├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── CHANGELOG.md
├── Gemfile
├── MIT-LICENSE
├── README.md
├── Rakefile
├── action-draft.gemspec
├── app
└── models
│ └── action_draft
│ ├── application_record.rb
│ └── content.rb
├── bin
└── rails
├── config
└── routes.rb
├── db
└── migrate
│ └── 20190724102032_create_action_draft_contents.rb
├── gemfiles
├── Gemfile-5-2
├── Gemfile-6-0
└── Gemfile-6-1
├── lib
├── action-draft.rb
└── action-draft
│ ├── attribute.rb
│ ├── engine.rb
│ └── version.rb
└── test
├── action_draft_test.rb
├── active_record_test_case_helper.rb
├── dummy
├── Rakefile
├── app
│ ├── assets
│ │ ├── config
│ │ │ └── manifest.js
│ │ ├── images
│ │ │ └── .keep
│ │ └── stylesheets
│ │ │ └── application.css
│ ├── channels
│ │ └── application_cable
│ │ │ ├── channel.rb
│ │ │ └── connection.rb
│ ├── controllers
│ │ ├── application_controller.rb
│ │ └── concerns
│ │ │ └── .keep
│ ├── helpers
│ │ └── application_helper.rb
│ ├── javascript
│ │ └── packs
│ │ │ └── application.js
│ ├── jobs
│ │ └── application_job.rb
│ ├── mailers
│ │ └── application_mailer.rb
│ ├── models
│ │ ├── application_record.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ ├── group.rb
│ │ └── message.rb
│ └── views
│ │ └── layouts
│ │ ├── application.html.erb
│ │ ├── mailer.html.erb
│ │ └── mailer.text.erb
├── bin
│ ├── rails
│ ├── rake
│ └── setup
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── application_controller_renderer.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── content_security_policy.rb
│ │ ├── cookies_serializer.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ └── en.yml
│ ├── puma.rb
│ ├── routes.rb
│ ├── spring.rb
│ └── storage.yml
├── db
│ ├── migrate
│ │ ├── 20190724105053_create_messages.rb
│ │ └── 20190726083445_create_groups.rb
│ └── schema.rb
├── lib
│ └── assets
│ │ └── .keep
├── log
│ └── .keep
├── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── apple-touch-icon-precomposed.png
│ ├── apple-touch-icon.png
│ └── favicon.ico
└── test
│ ├── fixtures
│ └── groups.yml
│ └── models
│ └── group_test.rb
├── test_helper.rb
└── unit
└── model_test.rb
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | if: "!contains(github.event.head_commit.message, '[skip ci]')"
8 | runs-on: ubuntu-latest
9 | strategy:
10 | fail-fast: false
11 | matrix:
12 | include:
13 | - ruby: 3.1.0
14 | gemfile: Gemfile
15 | postgres: 10
16 | - ruby: 3.0.0
17 | gemfile: gemfiles/Gemfile-6-1
18 | postgres: 10
19 | - ruby: 2.6
20 | gemfile: gemfiles/Gemfile-6-0
21 | postgres: 10
22 | - ruby: 2.6
23 | gemfile: gemfiles/Gemfile-5-2
24 | postgres: 10
25 | env:
26 | BUNDLE_GEMFILE: ${{ matrix.gemfile }}
27 | USE_OFFICIAL_GEM_SOURCE: 1
28 | steps:
29 | - uses: actions/checkout@v2
30 | - uses: ruby/setup-ruby@v1
31 | with:
32 | ruby-version: ${{ matrix.ruby }}
33 | bundler-cache: true
34 | - uses: ankane/setup-postgres@v1
35 | with:
36 | postgres-version: ${{ matrix.postgres }}
37 | - run: createdb action-draft-test
38 | - run: bundle exec rails test
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | log/*.log
3 | pkg/
4 | test/dummy/log/*.log
5 | test/dummy/storage/
6 | test/dummy/tmp/
7 | Gemfile.lock
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.3.0
2 |
3 | - Remove `publish`, instead of use `apply_draft` without save record.
4 |
5 | ## 0.2.0
6 |
7 | - A `publish` method for assignment the draft values to actual attributes.
8 | - Fallback to actual attribute value when draft is nil.
9 |
10 | ## 0.1.0
11 |
12 | First commit.
13 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5 |
6 | gemspec
7 | gem "rails", "~> 7.0.0"
8 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 Jason Lee
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Action Draft
2 |
3 | Action Draft brings your ActiveRecord model to storage multiple draft attributes without add columns to the business table.
4 |
5 | 让你的 ActiveRecord Model 能轻易的支持草稿存储功能,而无需在业务表里面增加字段。
6 |
7 | [](https://rubygems.org/gems/action-draft) [](https://travis-ci.org/rails-engine/action-draft)
8 |
9 | ## Features
10 |
11 | - Save drafts without add columns to the business table.
12 | - Work any ActiveRecord model, just add `has_draft :field_name`.
13 | - A `apply_draft` method for assignment the draft values to actual attributes.
14 | - Fallback to actual attribute value when draft is nil.
15 |
16 | ## Installation
17 |
18 | ```ruby
19 | gem "action-draft"
20 | ```
21 |
22 | And then execute:
23 | ```bash
24 | $ bundle
25 | $ rails action_draft:install:migrations
26 | ```
27 |
28 | ## Usage
29 |
30 | In your ActiveRecord model:
31 |
32 | ```rb
33 | # app/models/message.rb
34 | class Message < ApplicationRecord
35 | has_draft :title, :content
36 | end
37 | ```
38 |
39 | Now you have `draft_title`, `draft_content` attributes.
40 |
41 | Then refer to this field in the form for the model:
42 |
43 | ```erb
44 | <%# app/views/messages/_form.html.erb %>
45 | <%= form_with(model: message) do |form| %>
46 | …
47 |
48 | <%= form.label :draft_title %>
49 | <%= form.textarea :draft_title %>
50 |
51 |
52 |
53 | <%= form.label :draft_content %>
54 | <%= form.textarea :draft_content %>
55 |
56 | …
57 | <% end %>
58 | ```
59 |
60 | In your controller
61 |
62 | ```rb
63 | class MessagesController < ApplicationController
64 | def new
65 | @message = Message.new
66 | end
67 |
68 | def create
69 | @message = Message.new(message_params)
70 | message.apply_draft if message_params[:publish]
71 | if message.save
72 | redirect_to messages_path, notice: "Message has created successfully"
73 | else
74 | render :new
75 | end
76 | end
77 |
78 | def update
79 | @message.assign_attributes(message_params)
80 | message.apply_draft if message_params[:publish]
81 | if message.save
82 | redirect_to messages_path, notice: "Message has updated successfully"
83 | else
84 | render :edit
85 | end
86 | end
87 |
88 | private
89 | def set_message
90 | @message = Message.find(params[:id])
91 | end
92 |
93 | def message_params
94 | params.require(:message).perrmit(:draft_title, :draft_content, :publish)
95 | end
96 | end
97 | ```
98 |
99 | Save draft attributes:
100 |
101 | ```rb
102 | irb> message = Message.new
103 | irb> message.draft_title = "Draft title"
104 | irb> message.draft_title.to_s
105 | "Draft title"
106 | irb> message.draft_content = "Draft message content"
107 | irb> message.draft_content.to_s
108 | "Draft message content"
109 | irb> message.save
110 |
111 | irb> message.reload
112 | irb> message.draft_title.to_s
113 | "Draft title"
114 | irb> message.draft_content.to_s
115 | "Draft message content"
116 | ```
117 |
118 | Apply draft content:
119 |
120 | ```rb
121 | irb> message = Message.new
122 | irb> message.draft_title = "Message title"
123 | irb> message.apply_draft
124 |
125 | irb> message.title
126 | "Message title"
127 | irb> message.draft_title
128 | "Message title"
129 | irb> message.save
130 | ```
131 |
132 | ## License
133 |
134 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
135 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | begin
4 | require "bundler/setup"
5 | rescue LoadError
6 | puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7 | end
8 |
9 | require "rdoc/task"
10 |
11 | RDoc::Task.new(:rdoc) do |rdoc|
12 | rdoc.rdoc_dir = "rdoc"
13 | rdoc.title = "ActionDraft"
14 | rdoc.options << "--line-numbers"
15 | rdoc.rdoc_files.include("README.md")
16 | rdoc.rdoc_files.include("lib/**/*.rb")
17 | end
18 |
19 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
20 | load "rails/tasks/engine.rake"
21 |
22 | load "rails/tasks/statistics.rake"
23 |
24 | require "bundler/gem_tasks"
25 |
26 | require "rake/testtask"
27 |
28 | Rake::TestTask.new(:test) do |t|
29 | t.libs << "test"
30 | t.pattern = "test/**/*_test.rb"
31 | t.verbose = false
32 | end
33 |
34 | task default: :test
35 |
--------------------------------------------------------------------------------
/action-draft.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | $:.push File.expand_path("lib", __dir__)
4 |
5 | # Maintain your gem's version:
6 | require "action-draft/version"
7 |
8 | # Describe your gem and declare its dependencies:
9 | Gem::Specification.new do |spec|
10 | spec.name = "action-draft"
11 | spec.version = ActionDraft::VERSION
12 | spec.authors = ["Jason Lee"]
13 | spec.email = ["huacnlee@gmail.com"]
14 | spec.homepage = "http://github.com/rails-engine/action-draft"
15 | spec.summary = "Action Draft brings your ActiveRecord model to storage multiple draft attributes without add columns to the business table."
16 | spec.description = "Action Draft brings your ActiveRecord model to storage multiple draft attributes without add columns to the business table."
17 | spec.license = "MIT"
18 |
19 | spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "CHANGELOG.md", "README.md"]
20 |
21 | spec.add_dependency "rails", ">= 5.2"
22 |
23 | spec.add_development_dependency "pg"
24 | end
25 |
--------------------------------------------------------------------------------
/app/models/action_draft/application_record.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ActionDraft
4 | class ApplicationRecord < ActiveRecord::Base
5 | self.abstract_class = true
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/models/action_draft/content.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ActionDraft::Content < ApplicationRecord
4 | self.table_name = "action_draft_contents"
5 |
6 | belongs_to :record, polymorphic: true, touch: true
7 |
8 | delegate :to_s, :nil?, :blank?, :present?, to: :content
9 | end
10 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails gems
3 | # installed from the root of your application.
4 |
5 | ENGINE_ROOT = File.expand_path('..', __dir__)
6 | ENGINE_PATH = File.expand_path('../lib/action/draft/engine', __dir__)
7 | APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8 |
9 | # Set up gems listed in the Gemfile.
10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
12 |
13 | require 'rails/all'
14 | require 'rails/engine/commands'
15 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ActionDraft::Engine.routes.draw do
4 | resources :drafts
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20190724102032_create_action_draft_contents.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateActionDraftContents < ActiveRecord::Migration[6.0]
4 | def change
5 | create_table :action_draft_contents do |t|
6 | t.references :record, null: false, polymorphic: true, index: false
7 | t.string :name
8 | t.text :content, limit: 16777215
9 |
10 | t.timestamps
11 |
12 | t.index [ :record_type, :record_id, :name ], name: "index_action_drafts_uniqueness", unique: true
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile-5-2:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec path: '..'
4 |
5 | gem 'rails', '~> 5.2.0'
6 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile-6-0:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec path: '..'
4 |
5 | gem 'rails', '~> 6.0.0'
6 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile-6-1:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gemspec path: '..'
4 |
5 | gem 'rails', '~> 6.1.0'
6 |
--------------------------------------------------------------------------------
/lib/action-draft.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "action-draft/version"
4 | require "action-draft/attribute"
5 | require "action-draft/engine"
6 |
7 | module ActionDraft
8 | # Your code goes here...
9 | end
10 |
--------------------------------------------------------------------------------
/lib/action-draft/attribute.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ActionDraft
4 | module Attribute
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | end
9 |
10 | def apply_draft
11 | self.class.action_draft_attributes ||= []
12 | self.class.action_draft_attributes.each do |attr_name|
13 | send("#{attr_name}=", send("draft_#{attr_name}").to_s)
14 | end
15 | end
16 |
17 | class_methods do
18 | attr_accessor :action_draft_attributes
19 |
20 | # Provides access to a dependent ActionDraft::Content model that holds the draft attributes.
21 | # This dependent attribute is lazily instantiated and will be auto-saved when it's been changed. Example:
22 | #
23 | # class Message < ActiveRecord::Base
24 | # has_draft :title, :content
25 | # end
26 | #
27 | # message = Message.create!(draft_title: "This is draft title", draft_content: "Funny times!
")
28 | # message.draft_title # => "This is draft title"
29 | # message.draft_content # => "Funny times!
"
30 | #
31 | # If you wish to preload the dependent ActionDraft::Content model, you can use the named scope:
32 | #
33 | # Message.all.with_draft_title # Avoids N+1 queries when you just want the draft fields.
34 | def has_draft(*names)
35 | self.action_draft_attributes ||= []
36 | names = Array(names)
37 | names.each do |name|
38 | self.action_draft_attributes << name.to_sym
39 |
40 | class_eval <<-CODE, __FILE__, __LINE__ + 1
41 | def draft_#{name}
42 | self.draft_#{name}_content ||= ActionDraft::Content.new(name: "#{name}", record: self, content: self.send("#{name}"))
43 | end
44 |
45 | def draft_#{name}=(content)
46 | self.draft_#{name}.content = content
47 | end
48 | CODE
49 |
50 | has_one :"draft_#{name}_content", -> { where(name: name) }, class_name: "ActionDraft::Content", as: :record, inverse_of: :record, dependent: :destroy
51 | end
52 |
53 | scope :with_drafts, -> do
54 | names = self.action_draft_attributes.map { |name| "draft_#{name}_content" }
55 | includes(*names)
56 | end
57 |
58 | after_save do
59 | self.class.action_draft_attributes.each do |name|
60 | public_send("draft_#{name}").save if public_send("draft_#{name}").changed?
61 | end
62 | end
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/action-draft/engine.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ActionDraft
4 | class Engine < ::Rails::Engine
5 | isolate_namespace ActionDraft
6 |
7 | initializer "action_draft.attribute" do
8 | ActiveSupport.on_load(:active_record) do
9 | include ActionDraft::Attribute
10 | end
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/action-draft/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ActionDraft
4 | VERSION = "0.4.0"
5 | end
6 |
--------------------------------------------------------------------------------
/test/action_draft_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "test_helper"
4 |
5 | class ActionDraft::Test < ActiveSupport::TestCase
6 | test "truth" do
7 | assert_kind_of Module, ActionDraft
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/test/active_record_test_case_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "active_support/test_case"
4 |
5 | module ActiveRecordTestCaseHelper
6 | def teardown
7 | SQLCounter.clear_log
8 | end
9 |
10 | def assert_date_from_db(expected, actual, message = nil)
11 | assert_equal expected.to_s, actual.to_s, message
12 | end
13 |
14 | def capture(stream)
15 | stream = stream.to_s
16 | captured_stream = Tempfile.new(stream)
17 | stream_io = eval("$#{stream}")
18 | origin_stream = stream_io.dup
19 | stream_io.reopen(captured_stream)
20 |
21 | yield
22 |
23 | stream_io.rewind
24 | captured_stream.read
25 | ensure
26 | captured_stream.close
27 | captured_stream.unlink
28 | stream_io.reopen(origin_stream)
29 | end
30 |
31 | def capture_sql
32 | SQLCounter.clear_log
33 | yield
34 | SQLCounter.log_all.dup
35 | end
36 |
37 | def assert_sql(*patterns_to_match)
38 | capture_sql { yield }
39 | ensure
40 | failed_patterns = []
41 | patterns_to_match.each do |pattern|
42 | failed_patterns << pattern unless SQLCounter.log_all.any? { |sql| pattern === sql }
43 | end
44 | assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(", ")} not found.#{SQLCounter.log.empty? ? "" : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
45 | end
46 |
47 | def assert_queries(num = 1, options = {})
48 | ignore_none = options.fetch(:ignore_none) { num == :any }
49 | SQLCounter.clear_log
50 | x = yield
51 | the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
52 | if num == :any
53 | assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
54 | else
55 | mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.empty? ? "" : "\nQueries:\n#{the_log.join("\n")}"}"
56 | assert_equal num, the_log.size, mesg
57 | end
58 | x
59 | end
60 |
61 | def assert_no_queries(options = {}, &block)
62 | options.reverse_merge! ignore_none: true
63 | assert_queries(0, options, &block)
64 | end
65 |
66 | def assert_column(model, column_name, msg = nil)
67 | assert exists_column?(model, column_name), msg
68 | end
69 |
70 | def assert_no_column(model, column_name, msg = nil)
71 | assert_not exists_column?(model, column_name), msg
72 | end
73 |
74 | def exists_column?(model, column_name)
75 | model.reset_column_information
76 | model.column_names.include?(column_name.to_s)
77 | end
78 |
79 | class SQLCounter
80 | class << self
81 | attr_accessor :ignored_sql, :log, :log_all
82 | def clear_log
83 | self.log = []
84 | self.log_all = []
85 | end
86 | end
87 |
88 | clear_log
89 |
90 | self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
91 |
92 | # FIXME: this needs to be refactored so specific database can add their own
93 | # ignored SQL, or better yet, use a different notification for the queries
94 | # instead examining the SQL content.
95 | oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
96 | mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /]
97 | postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
98 | sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im]
99 |
100 | [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
101 | ignored_sql.concat db_ignored_sql
102 | end
103 |
104 | attr_reader :ignore
105 |
106 | def initialize(ignore = Regexp.union(self.class.ignored_sql))
107 | @ignore = ignore
108 | end
109 |
110 | def call(_name, _start, _finish, _message_id, values)
111 | sql = values[:sql]
112 |
113 | # FIXME: this seems bad. we should probably have a better way to indicate
114 | # the query was cached
115 | return if values[:name] == "CACHE"
116 |
117 | self.class.log_all << sql
118 | self.class.log << sql unless ignore.match?(sql)
119 | end
120 |
121 | ActiveSupport::Notifications.subscribe("sql.active_record", SQLCounter.new)
122 | end
123 | end
124 |
125 | ActiveSupport::TestCase.send(:include, ActiveRecordTestCaseHelper)
126 |
--------------------------------------------------------------------------------
/test/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Add your own tasks in files placed in lib/tasks ending in .rake,
4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5 |
6 | require_relative "config/application"
7 |
8 | Rails.application.load_tasks
9 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../stylesheets .css
3 | //= link action_draft_manifest.js
4 |
--------------------------------------------------------------------------------
/test/dummy/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/app/assets/images/.keep
--------------------------------------------------------------------------------
/test/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationCable
4 | class Channel < ActionCable::Channel::Base
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/test/dummy/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationCable
4 | class Connection < ActionCable::Connection::Base
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationController < ActionController::Base
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/test/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ApplicationHelper
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require activestorage
15 | //= require_tree .
16 |
--------------------------------------------------------------------------------
/test/dummy/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationJob < ActiveJob::Base
4 | # Automatically retry jobs that encountered a deadlock
5 | # retry_on ActiveRecord::Deadlocked
6 |
7 | # Most jobs are safe to ignore if the underlying records are no longer available
8 | # discard_on ActiveJob::DeserializationError
9 | end
10 |
--------------------------------------------------------------------------------
/test/dummy/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationMailer < ActionMailer::Base
4 | default from: "from@example.com"
5 | layout "mailer"
6 | end
7 |
--------------------------------------------------------------------------------
/test/dummy/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ApplicationRecord < ActiveRecord::Base
4 | self.abstract_class = true
5 | end
6 |
--------------------------------------------------------------------------------
/test/dummy/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/app/models/concerns/.keep
--------------------------------------------------------------------------------
/test/dummy/app/models/group.rb:
--------------------------------------------------------------------------------
1 | class Group < ApplicationRecord
2 | has_draft :name, :description
3 | end
4 |
--------------------------------------------------------------------------------
/test/dummy/app/models/message.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class Message < ApplicationRecord
4 | validates :title, presence: true
5 |
6 | has_draft :title, :content
7 | end
8 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dummy
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= stylesheet_link_tag 'application', media: 'all' %>
9 |
10 |
11 |
12 | <%= yield %>
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/test/dummy/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/test/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | APP_PATH = File.expand_path("../config/application", __dir__)
5 | require_relative "../config/boot"
6 | require "rails/commands"
7 |
--------------------------------------------------------------------------------
/test/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require_relative "../config/boot"
5 | require "rake"
6 | Rake.application.run
7 |
--------------------------------------------------------------------------------
/test/dummy/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require "fileutils"
5 |
6 | # path to your application root.
7 | APP_ROOT = File.expand_path("..", __dir__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | FileUtils.chdir APP_ROOT do
14 | # This script is a way to setup or update your development environment automatically.
15 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
16 | # Add necessary setup steps to this file.
17 |
18 | puts "== Installing dependencies =="
19 | system! "gem install bundler --conservative"
20 | system("bundle check") || system!("bundle install")
21 |
22 | # puts "\n== Copying sample files =="
23 | # unless File.exist?('config/database.yml')
24 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
25 | # end
26 |
27 | puts "\n== Preparing database =="
28 | system! "bin/rails db:prepare"
29 |
30 | puts "\n== Removing old logs and tempfiles =="
31 | system! "bin/rails log:clear tmp:clear"
32 |
33 | puts "\n== Restarting application server =="
34 | system! "bin/rails restart"
35 | end
36 |
--------------------------------------------------------------------------------
/test/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is used by Rack-based servers to start the application.
4 |
5 | require_relative "config/environment"
6 |
7 | run Rails.application
8 |
--------------------------------------------------------------------------------
/test/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "boot"
4 |
5 | require "rails/all"
6 |
7 | Bundler.require(*Rails.groups)
8 | require "action-draft"
9 |
10 | module Dummy
11 | class Application < Rails::Application
12 | # Initialize configuration defaults for originally generated Rails version.
13 | config.load_defaults 5.0
14 |
15 | # Settings in config/environments/* take precedence over those specified here.
16 | # Application configuration can go into files in config/initializers
17 | # -- all .rb files in that directory are automatically loaded after loading
18 | # the framework and any gems in your application.
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/test/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Set up gems listed in the Gemfile.
4 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
5 |
6 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
7 | $LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
8 |
--------------------------------------------------------------------------------
/test/dummy/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: dummy_production
11 |
--------------------------------------------------------------------------------
/test/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | adapter: postgresql
3 | timeout: 5000
4 | encoding: utf-8
5 |
6 | development:
7 | <<: *default
8 | database: action-draft-development
9 |
10 | # Warning: The database defined as "test" will be erased and
11 | # re-generated from your development database when you run "rake".
12 | # Do not set this db to the same as development or production.
13 | test:
14 | <<: *default
15 | database: action-draft-test
16 |
17 | production:
18 | <<: *default
19 |
--------------------------------------------------------------------------------
/test/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Load the Rails application.
4 | require_relative "application"
5 |
6 | # Initialize the Rails application.
7 | Rails.application.initialize!
8 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # In the development environment your application's code is reloaded on
7 | # every request. This slows down response time but is perfect for development
8 | # since you don't have to restart the web server when you make code changes.
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot.
12 | config.eager_load = false
13 |
14 | # Show full error reports.
15 | config.consider_all_requests_local = true
16 |
17 | # Enable/disable caching. By default caching is disabled.
18 | # Run rails dev:cache to toggle caching.
19 | if Rails.root.join("tmp", "caching-dev.txt").exist?
20 | config.action_controller.perform_caching = true
21 | config.action_controller.enable_fragment_cache_logging = true
22 |
23 | config.cache_store = :memory_store
24 | config.public_file_server.headers = {
25 | "Cache-Control" => "public, max-age=#{2.days.to_i}"
26 | }
27 | else
28 | config.action_controller.perform_caching = false
29 |
30 | config.cache_store = :null_store
31 | end
32 |
33 | # Store uploaded files on the local file system (see config/storage.yml for options).
34 | config.active_storage.service = :local
35 |
36 | # Don't care if the mailer can't send.
37 | config.action_mailer.raise_delivery_errors = false
38 |
39 | config.action_mailer.perform_caching = false
40 |
41 | # Print deprecation notices to the Rails logger.
42 | config.active_support.deprecation = :log
43 |
44 | # Raise an error on page load if there are pending migrations.
45 | config.active_record.migration_error = :page_load
46 |
47 | # Highlight code that triggered database queries in logs.
48 | config.active_record.verbose_query_logs = true
49 |
50 | # Raises error for missing translations.
51 | # config.action_view.raise_on_missing_translations = true
52 |
53 | # Use an evented file watcher to asynchronously detect changes in source code,
54 | # routes, locales, etc. This feature depends on the listen gem.
55 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
56 | end
57 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.cache_classes = true
8 |
9 | # Eager load code on boot. This eager loads most of Rails and
10 | # your application in memory, allowing both threaded web servers
11 | # and those relying on copy on write to perform better.
12 | # Rake tasks automatically ignore this option for performance.
13 | config.eager_load = true
14 |
15 | # Full error reports are disabled and caching is turned on.
16 | config.consider_all_requests_local = false
17 | config.action_controller.perform_caching = true
18 |
19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
21 | # config.require_master_key = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
26 |
27 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
28 | # config.action_controller.asset_host = 'http://assets.example.com'
29 |
30 | # Specifies the header that your server uses for sending files.
31 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
32 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
33 |
34 | # Store uploaded files on the local file system (see config/storage.yml for options).
35 | config.active_storage.service = :local
36 |
37 | # Mount Action Cable outside main process or domain.
38 | # config.action_cable.mount_path = nil
39 | # config.action_cable.url = 'wss://example.com/cable'
40 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
41 |
42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
43 | # config.force_ssl = true
44 |
45 | # Use the lowest log level to ensure availability of diagnostic information
46 | # when problems arise.
47 | config.log_level = :debug
48 |
49 | # Prepend all log lines with the following tags.
50 | config.log_tags = [ :request_id ]
51 |
52 | # Use a different cache store in production.
53 | # config.cache_store = :mem_cache_store
54 |
55 | # Use a real queuing backend for Active Job (and separate queues per environment).
56 | # config.active_job.queue_adapter = :resque
57 | # config.active_job.queue_name_prefix = "dummy_production"
58 |
59 | config.action_mailer.perform_caching = false
60 |
61 | # Ignore bad email addresses and do not raise email delivery errors.
62 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
63 | # config.action_mailer.raise_delivery_errors = false
64 |
65 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
66 | # the I18n.default_locale when a translation cannot be found).
67 | config.i18n.fallbacks = true
68 |
69 | # Send deprecation notices to registered listeners.
70 | config.active_support.deprecation = :notify
71 |
72 | # Use default logging formatter so that PID and timestamp are not suppressed.
73 | config.log_formatter = ::Logger::Formatter.new
74 |
75 | # Use a different logger for distributed setups.
76 | # require 'syslog/logger'
77 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
78 |
79 | if ENV["RAILS_LOG_TO_STDOUT"].present?
80 | logger = ActiveSupport::Logger.new(STDOUT)
81 | logger.formatter = config.log_formatter
82 | config.logger = ActiveSupport::TaggedLogging.new(logger)
83 | end
84 |
85 | # Do not dump schema after migrations.
86 | config.active_record.dump_schema_after_migration = false
87 |
88 | # Inserts middleware to perform automatic connection switching.
89 | # The `database_selector` hash is used to pass options to the DatabaseSelector
90 | # middleware. The `delay` is used to determine how long to wait after a write
91 | # to send a subsequent read to the primary.
92 | #
93 | # The `database_resolver` class is used by the middleware to determine which
94 | # database is appropriate to use based on the time delay.
95 | #
96 | # The `database_resolver_context` class is used by the middleware to set
97 | # timestamps for the last write to the primary. The resolver uses the context
98 | # class timestamps to determine how long to wait before reading from the
99 | # replica.
100 | #
101 | # By default Rails will store a last write timestamp in the session. The
102 | # DatabaseSelector middleware is designed as such you can define your own
103 | # strategy for connection switching and pass that into the middleware through
104 | # these configuration options.
105 | # config.active_record.database_selector = { delay: 2.seconds }
106 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
107 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
108 | end
109 |
--------------------------------------------------------------------------------
/test/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # The test environment is used exclusively to run your application's
4 | # test suite. You never need to work with it otherwise. Remember that
5 | # your test database is "scratch space" for the test suite and is wiped
6 | # and recreated between test runs. Don't rely on the data there!
7 |
8 | Rails.application.configure do
9 | # Settings specified here will take precedence over those in config/application.rb.
10 |
11 | config.cache_classes = false
12 |
13 | # Do not eager load code on boot. This avoids loading your whole application
14 | # just for the purpose of running a single test. If you are using a tool that
15 | # preloads Rails for running tests, you may have to set it to true.
16 | config.eager_load = false
17 |
18 | # Configure public file server for tests with Cache-Control for performance.
19 | config.public_file_server.enabled = true
20 | config.public_file_server.headers = {
21 | "Cache-Control" => "public, max-age=#{1.hour.to_i}"
22 | }
23 |
24 | # Show full error reports and disable caching.
25 | config.consider_all_requests_local = true
26 | config.action_controller.perform_caching = false
27 | config.cache_store = :null_store
28 |
29 | # Raise exceptions instead of rendering exception templates.
30 | config.action_dispatch.show_exceptions = false
31 |
32 | # Disable request forgery protection in test environment.
33 | config.action_controller.allow_forgery_protection = false
34 |
35 | # Store uploaded files on the local file system in a temporary directory.
36 | config.active_storage.service = :test
37 |
38 | config.action_mailer.perform_caching = false
39 |
40 | # Tell Action Mailer not to deliver emails to the real world.
41 | # The :test delivery method accumulates sent emails in the
42 | # ActionMailer::Base.deliveries array.
43 | config.action_mailer.delivery_method = :test
44 |
45 | # Print deprecation notices to the stderr.
46 | config.active_support.deprecation = :stderr
47 |
48 | # Raises error for missing translations.
49 | # config.action_view.raise_on_missing_translations = true
50 | end
51 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # ActiveSupport::Reloader.to_prepare do
6 | # ApplicationController.renderer.defaults.merge!(
7 | # http_host: 'example.org',
8 | # https: false
9 | # )
10 | # end
11 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
6 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
7 |
8 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
9 | # Rails.backtrace_cleaner.remove_silencers!
10 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Define an application-wide content security policy
6 | # For further information see the following documentation
7 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
8 |
9 | # Rails.application.config.content_security_policy do |policy|
10 | # policy.default_src :self, :https
11 | # policy.font_src :self, :https, :data
12 | # policy.img_src :self, :https, :data
13 | # policy.object_src :none
14 | # policy.script_src :self, :https
15 | # policy.style_src :self, :https
16 |
17 | # # Specify URI for violation reports
18 | # # policy.report_uri "/csp-violation-report-endpoint"
19 | # end
20 |
21 | # If you are using UJS then enable automatic nonce generation
22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
23 |
24 | # Report CSP violations to a specified URI
25 | # For further information see the following documentation:
26 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
27 | # Rails.application.config.content_security_policy_report_only = true
28 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Specify a serializer for the signed and encrypted cookie jars.
6 | # Valid options are :json, :marshal, and :hybrid.
7 | Rails.application.config.action_dispatch.cookies_serializer = :json
8 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Configure sensitive parameters which will be filtered from the log file.
6 | Rails.application.config.filter_parameters += [:password]
7 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Add new inflection rules using the following format. Inflections
6 | # are locale specific, and you may define rules for as many different
7 | # locales as you wish. All of these examples are active by default:
8 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
9 | # inflect.plural /^(ox)$/i, '\1en'
10 | # inflect.singular /^(ox)en/i, '\1'
11 | # inflect.irregular 'person', 'people'
12 | # inflect.uncountable %w( fish sheep )
13 | # end
14 |
15 | # These inflection rules are supported but not enabled by default:
16 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
17 | # inflect.acronym 'RESTful'
18 | # end
19 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # Add new mime types for use in respond_to blocks:
6 | # Mime::Type.register "text/richtext", :rtf
7 |
--------------------------------------------------------------------------------
/test/dummy/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Be sure to restart your server when you modify this file.
4 |
5 | # This file contains settings for ActionController::ParamsWrapper which
6 | # is enabled by default.
7 |
8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
9 | ActiveSupport.on_load(:action_controller) do
10 | wrap_parameters format: [:json]
11 | end
12 |
13 | # To enable root element in JSON for ActiveRecord objects.
14 | # ActiveSupport.on_load(:active_record) do
15 | # self.include_root_in_json = true
16 | # end
17 |
--------------------------------------------------------------------------------
/test/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/test/dummy/config/puma.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Puma can serve each request in a thread from an internal thread pool.
4 | # The `threads` method setting takes two numbers: a minimum and maximum.
5 | # Any libraries that use thread pools should be configured to match
6 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
7 | # and maximum; this matches the default thread size of Active Record.
8 | #
9 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
10 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
11 | threads min_threads_count, max_threads_count
12 |
13 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
14 | #
15 | port ENV.fetch("PORT") { 3000 }
16 |
17 | # Specifies the `environment` that Puma will run in.
18 | #
19 | environment ENV.fetch("RAILS_ENV") { "development" }
20 |
21 | # Specifies the number of `workers` to boot in clustered mode.
22 | # Workers are forked web server processes. If using threads and workers together
23 | # the concurrency of the application would be max `threads` * `workers`.
24 | # Workers do not work on JRuby or Windows (both of which do not support
25 | # processes).
26 | #
27 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
28 |
29 | # Use the `preload_app!` method when specifying a `workers` number.
30 | # This directive tells Puma to first boot the application and load code
31 | # before forking the application. This takes advantage of Copy On Write
32 | # process behavior so workers use less memory.
33 | #
34 | # preload_app!
35 |
36 | # Allow puma to be restarted by `rails restart` command.
37 | plugin :tmp_restart
38 |
--------------------------------------------------------------------------------
/test/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Rails.application.routes.draw do
4 | end
5 |
--------------------------------------------------------------------------------
/test/dummy/config/spring.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Spring.watch(
4 | ".ruby-version",
5 | ".rbenv-vars",
6 | "tmp/restart.txt",
7 | "tmp/caching-dev.txt"
8 | )
9 |
--------------------------------------------------------------------------------
/test/dummy/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/test/dummy/db/migrate/20190724105053_create_messages.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class CreateMessages < ActiveRecord::Migration[6.0]
4 | def change
5 | create_table :messages do |t|
6 | t.string :title
7 | t.text :content
8 | t.string :to
9 |
10 | t.timestamps
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/dummy/db/migrate/20190726083445_create_groups.rb:
--------------------------------------------------------------------------------
1 | class CreateGroups < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :groups do |t|
4 | t.string :name
5 | t.string :description
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/test/dummy/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `rails
6 | # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2019_07_26_083445) do
14 |
15 | # These are extensions that must be enabled in order to support this database
16 | enable_extension "plpgsql"
17 |
18 | create_table "action_draft_contents", force: :cascade do |t|
19 | t.string "record_type", null: false
20 | t.bigint "record_id", null: false
21 | t.string "name"
22 | t.text "content"
23 | t.datetime "created_at", precision: 6, null: false
24 | t.datetime "updated_at", precision: 6, null: false
25 | t.index ["record_type", "record_id", "name"], name: "index_action_drafts_uniqueness", unique: true
26 | end
27 |
28 | create_table "groups", force: :cascade do |t|
29 | t.string "name"
30 | t.string "description"
31 | t.datetime "created_at", precision: 6, null: false
32 | t.datetime "updated_at", precision: 6, null: false
33 | end
34 |
35 | create_table "messages", force: :cascade do |t|
36 | t.string "title"
37 | t.text "content"
38 | t.string "to"
39 | t.datetime "created_at", precision: 6, null: false
40 | t.datetime "updated_at", precision: 6, null: false
41 | end
42 |
43 | end
44 |
--------------------------------------------------------------------------------
/test/dummy/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/lib/assets/.keep
--------------------------------------------------------------------------------
/test/dummy/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/log/.keep
--------------------------------------------------------------------------------
/test/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/dummy/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/dummy/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/test/dummy/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/test/dummy/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/test/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rails-engine/action-draft/7b586fa5856303532e8a09cddb092558b73246d2/test/dummy/public/favicon.ico
--------------------------------------------------------------------------------
/test/dummy/test/fixtures/groups.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | name: MyString
5 | description: MyString
6 |
7 | two:
8 | name: MyString
9 | description: MyString
10 |
--------------------------------------------------------------------------------
/test/dummy/test/models/group_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class GroupTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Configure Rails Environment
4 | ENV["RAILS_ENV"] = "test"
5 |
6 | require_relative "../test/dummy/config/environment"
7 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
8 | ActiveRecord::Migrator.migrations_paths << File.expand_path("../db/migrate", __dir__)
9 | require "rails/test_help"
10 | require "active_record_test_case_helper"
11 |
12 | # Filter out the backtrace from minitest while preserving the one from other libraries.
13 | Minitest.backtrace_filter = Minitest::BacktraceFilter.new
14 |
15 |
16 | # Load fixtures from the engine
17 | if ActiveSupport::TestCase.respond_to?(:fixture_path=)
18 | ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__)
19 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
20 | ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
21 | ActiveSupport::TestCase.fixtures :all
22 | end
23 |
--------------------------------------------------------------------------------
/test/unit/model_test.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "test_helper"
4 |
5 | class ActionDraft::ModelTest < ActiveSupport::TestCase
6 | test "action_draft_attributes" do
7 | assert_equal [:title, :content], Message.action_draft_attributes
8 | assert_equal [:name, :description], Group.action_draft_attributes
9 | end
10 |
11 | test "saving attributes" do
12 | message = Message.create(
13 | title: "Hello world",
14 | content: "This is message content.",
15 | draft_title: "Hello world draft",
16 | draft_content: "This is draft message content.",
17 | )
18 |
19 | assert_equal "Hello world", message.title
20 | assert_equal "Hello world draft", message.draft_title.to_s
21 | assert_equal "This is message content.", message.content
22 | assert_equal "This is draft message content.", message.draft_content.to_s
23 |
24 | drafts = ActionDraft::Content.where(record: message)
25 | assert_equal 2, drafts.size
26 |
27 | assert_equal message.draft_title, ActionDraft::Content.where(record: message, name: "title").take
28 | assert_equal message.draft_content, ActionDraft::Content.where(record: message, name: "content").take
29 | end
30 |
31 | test "apply_draft" do
32 | message = Message.new(draft_title: "Hello draft title", draft_content: "This is draft message content.")
33 | message.apply_draft
34 |
35 | assert_equal "Hello draft title", message.draft_title.to_s
36 | assert_equal "This is draft message content.", message.draft_content.to_s
37 | assert_equal message.draft_title.to_s, message.title
38 | assert_equal message.draft_content.to_s, message.content
39 | end
40 |
41 | test "fallback actual attribute value" do
42 | message = Message.create(title: "Hello world", content: "This is message content.")
43 | assert_equal "Hello world", message.title
44 | assert_equal message.title, message.draft_title.to_s
45 | assert_equal "This is message content.", message.content
46 | assert_equal message.content, message.draft_content.to_s
47 | end
48 |
49 | test "without draft" do
50 | message = Message.new
51 | message.save(validate: false)
52 | assert_equal false, message.new_record?
53 |
54 | assert message.draft_title.nil?
55 | assert message.draft_title.blank?
56 | assert_not message.draft_title.present?
57 | end
58 |
59 | test "with blank content" do
60 | message = Message.create!(title: "Hello world", draft_content: "")
61 | assert_not message.draft_content.nil?
62 | assert message.draft_content.blank?
63 | assert_not message.draft_content.present?
64 | end
65 |
66 | test "with_drafts" do
67 | 10.times do |i|
68 | Message.create(
69 | title: "Hello world #{i}",
70 | content: "This is message content #{i}.",
71 | draft_title: "Hello world draft #{i}",
72 | draft_content: "This is draft message content #{i}.",
73 | )
74 | end
75 |
76 | assert_equal 10, Message.count
77 | # SELECT * FROM messages
78 | # SELECT * FROM action_draft_contents WHERE name = "draft_title" AND record_id IN (?)
79 | # SELECT * FROM action_draft_contents WHERE name = "draft_content" AND record_id IN (?)
80 | assert_queries(3) do
81 | messages = Message.with_drafts.all.to_a
82 | draft_titles = messages.collect(&:draft_title)
83 | draft_contents = messages.collect(&:draft_content)
84 | end
85 | end
86 | end
87 |
--------------------------------------------------------------------------------