diff options
28 files changed, 261 insertions, 44 deletions
diff --git a/actioncable/app/assets/javascripts/action_cable.js b/actioncable/app/assets/javascripts/action_cable.js index 029c7567ce..8349361405 100644 --- a/actioncable/app/assets/javascripts/action_cable.js +++ b/actioncable/app/assets/javascripts/action_cable.js @@ -477,15 +477,17 @@ return Consumer; }(); function createWebSocketURL(url) { - var webSocketURL = typeof url === "function" ? url() : url; - if (webSocketURL && !/^wss?:/i.test(webSocketURL)) { + if (typeof url === "function") { + url = url(); + } + if (url && !/^wss?:/i.test(url)) { var a = document.createElement("a"); - a.href = webSocketURL; + a.href = url; a.href = a.href; a.protocol = a.protocol.replace("http", "ws"); return a.href; } else { - return webSocketURL; + return url; } } function createConsumer() { @@ -505,8 +507,8 @@ exports.Subscription = Subscription; exports.Subscriptions = Subscriptions; exports.adapters = adapters; - exports.logger = logger; exports.createWebSocketURL = createWebSocketURL; + exports.logger = logger; exports.createConsumer = createConsumer; exports.getConfig = getConfig; Object.defineProperty(exports, "__esModule", { diff --git a/actioncable/app/javascript/action_cable/consumer.js b/actioncable/app/javascript/action_cable/consumer.js index 51f3b60980..e2e0dea8b5 100644 --- a/actioncable/app/javascript/action_cable/consumer.js +++ b/actioncable/app/javascript/action_cable/consumer.js @@ -58,16 +58,18 @@ export default class Consumer { } export function createWebSocketURL(url) { - const webSocketURL = typeof url === "function" ? url() : url + if (typeof url === "function") { + url = url() + } - if (webSocketURL && !/^wss?:/i.test(webSocketURL)) { + if (url && !/^wss?:/i.test(url)) { const a = document.createElement("a") - a.href = webSocketURL + a.href = url // Fix populating Location properties in IE. Otherwise, protocol will be blank. a.href = a.href a.protocol = a.protocol.replace("http", "ws") return a.href } else { - return webSocketURL + return url } } diff --git a/actioncable/app/javascript/action_cable/index.js b/actioncable/app/javascript/action_cable/index.js index d484d99179..848b5631d6 100644 --- a/actioncable/app/javascript/action_cable/index.js +++ b/actioncable/app/javascript/action_cable/index.js @@ -15,8 +15,8 @@ export { Subscription, Subscriptions, adapters, - logger, createWebSocketURL, + logger, } export function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) { diff --git a/actioncable/test/javascript/src/unit/action_cable_test.js b/actioncable/test/javascript/src/unit/action_cable_test.js index 2181f955e3..c46f9878d2 100644 --- a/actioncable/test/javascript/src/unit/action_cable_test.js +++ b/actioncable/test/javascript/src/unit/action_cable_test.js @@ -42,14 +42,15 @@ module("ActionCable", () => { assert.equal(consumer.url, testURL) }) - test("uses function to generate URL", assert => { + test("dynamically computes URL from function", assert => { let dynamicURL = testURL const generateURL = () => { return dynamicURL } + const consumer = ActionCable.createConsumer(generateURL) + assert.equal(consumer.url, testURL) dynamicURL = `${testURL}foo` - const consumer = ActionCable.createConsumer(generateURL) assert.equal(consumer.url, `${testURL}foo`) }) }) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 2d2073de9a..08560ccf7d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1402,6 +1402,8 @@ module ActionDispatch # as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt> # to be shortened to just <tt>/comments/1234</tt>. # + # Set shallow: false on a child resource to ignore a parent's shallow parameter. + # # [:shallow_path] # Prefixes nested shallow routes with the specified path. # @@ -1672,7 +1674,8 @@ module ActionDispatch return true end - if options.delete(:shallow) + if options[:shallow] + options.delete(:shallow) shallow do send(method, resources.pop, options, &block) end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 4645ba476f..11772a36a3 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -2200,6 +2200,37 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal "cards#destroy", @response.body end + def test_shallow_false_inside_nested_shallow_resource + draw do + resources :blogs, shallow: true do + resources :posts do + resources :comments, shallow: false + resources :tags + end + end + end + + get "/posts/1/comments" + assert_equal "comments#index", @response.body + assert_equal "/posts/1/comments", post_comments_path("1") + + get "/posts/1/comments/new" + assert_equal "comments#new", @response.body + assert_equal "/posts/1/comments/new", new_post_comment_path("1") + + get "/posts/1/comments/2" + assert_equal "comments#show", @response.body + assert_equal "/posts/1/comments/2", post_comment_path("1", "2") + + get "/posts/1/comments/2/edit" + assert_equal "comments#edit", @response.body + assert_equal "/posts/1/comments/2/edit", edit_post_comment_path("1", "2") + + get "/tags/3" + assert_equal "tags#show", @response.body + assert_equal "/tags/3", tag_path("3") + end + def test_shallow_deeply_nested_resources draw do resources :blogs do diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index d73d9ade41..96e2a8683e 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Add `after_save_commit` callback as shortcut for `after_commit :hook, on: [ :create, :update ]`. + + *DHH* + +* Add `ActiveRecord::Relation#extract_associated` for extracting associated records from a relation. + * Assign all attributes before calling `build` to ensure the child record is visible in `before_add` and `after_add` callbacks for `has_many :through` associations. diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index f021a8f6c4..447def8d77 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -222,6 +222,16 @@ db_namespace = namespace :db do desc "Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)" task setup: ["db:schema:load_if_ruby", "db:structure:load_if_sql", :seed] + desc "Runs setup if database does not exist, or runs migrations if it does" + task prepare: :load_config do + ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config| + ActiveRecord::Base.establish_connection(db_config.config) + db_namespace["migrate"].invoke + rescue ActiveRecord::NoDatabaseError + db_namespace["setup"].invoke + end + end + desc "Loads the seed data from db/seeds.rb" task seed: :load_config do db_namespace["abort_if_pending_migrations"].invoke diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index fe3842b905..a45d228298 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -234,6 +234,12 @@ module ActiveRecord set_callback(:commit, :after, *args, &block) end + # Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>. + def after_save_commit(*args, &block) + set_options_for_callbacks!(args, on: [ :create, :update ]) + set_callback(:commit, :after, *args, &block) + end + # Shortcut for <tt>after_commit :hook, on: :create</tt>. def after_create_commit(*args, &block) set_options_for_callbacks!(args, on: :create) diff --git a/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt b/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt index cdd029735a..c1c03e2762 100644 --- a/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt +++ b/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt @@ -6,6 +6,12 @@ class <%= class_name %> < <%= parent_class_name.classify %> <% attributes.select(&:rich_text?).each do |attribute| -%> has_rich_text :<%= attribute.name %> <% end -%> +<% attributes.select(&:attachment?).each do |attribute| -%> + has_one_attached :<%= attribute.name %> +<% end -%> +<% attributes.select(&:attachments?).each do |attribute| -%> + has_many_attached :<%= attribute.name %> +<% end -%> <% attributes.select(&:token?).each do |attribute| -%> has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %> <% end -%> diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index aa6b7915a2..e88d20a453 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -38,6 +38,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase before_commit { |record| record.do_before_commit(nil) } after_commit { |record| record.do_after_commit(nil) } + after_save_commit { |record| record.do_after_commit(:save) } after_create_commit { |record| record.do_after_commit(:create) } after_update_commit { |record| record.do_after_commit(:update) } after_destroy_commit { |record| record.do_after_commit(:destroy) } @@ -110,6 +111,17 @@ class TransactionCallbacksTest < ActiveRecord::TestCase assert_equal [:after_commit], @first.history end + def test_only_call_after_commit_on_save_after_transaction_commits_for_saving_record + record = TopicWithCallbacks.new(title: "New topic", written_on: Date.today) + record.after_commit_block(:save) { |r| r.history << :after_save } + + record.save! + assert_equal [:after_save], record.history + + record.update!(title: "Another topic") + assert_equal [:after_save, :after_save], record.history + end + def test_only_call_after_commit_on_update_after_transaction_commits_for_existing_record add_transaction_execution_blocks @first diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb index 6258610c98..5013812460 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -10,7 +10,7 @@ class Hash # This is useful for limiting a set of parameters to everything but a few known toggles: # @person.update(params[:person].except(:admin)) def except(*keys) - dup.except!(*keys) + slice(*self.keys - keys) end # Removes the given keys from hash and returns it. diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 3a2b2652c4..42ae7e9b7b 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -225,8 +225,8 @@ module ActiveSupport # hash[:a] = 'x' # hash[:b] = 'y' # hash.values_at('a', 'b') # => ["x", "y"] - def values_at(*indices) - indices.collect { |key| self[convert_key(key)] } + def values_at(*keys) + super(*keys.map { |key| convert_key(key) }) end # Returns an array of the values at the specified indices, but also @@ -239,7 +239,7 @@ module ActiveSupport # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"] # hash.fetch_values('a', 'c') # => KeyError: key not found: "c" def fetch_values(*indices, &block) - indices.collect { |key| fetch(key, &block) } + super(*indices.map { |key| convert_key(key) }, &block) end # Returns a shallow copy of the hash. @@ -293,6 +293,9 @@ module ActiveSupport super(convert_key(key)) end + def except(*keys) + slice(*self.keys - keys.map { |key| convert_key(key) }) + end alias_method :without, :except def stringify_keys!; self end diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt index b80c1280ce..1dddc3d698 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt @@ -21,6 +21,9 @@ <div class="field"> <%%= form.label :password_confirmation %> <%%= form.password_field :password_confirmation %> +<% elsif attribute.attachments? -%> + <%%= form.label :<%= attribute.column_name %> %> + <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true %> <% else -%> <%%= form.label :<%= attribute.column_name %> %> <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %> %> diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt index 7deba07926..6b216001d2 100644 --- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt @@ -3,7 +3,15 @@ <% attributes.reject(&:password_digest?).each do |attribute| -%> <p> <strong><%= attribute.human_name %>:</strong> +<% if attribute.attachment? -%> + <%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %> %> +<% elsif attribute.attachments? -%> + <%% @<%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %> + <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div> + <%% end %> +<% else -%> <%%= @<%= singular_table_name %>.<%= attribute.column_name %> %> +<% end -%> </p> <% end -%> diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb index e801ab0c90..99c1bc4269 100644 --- a/railties/lib/rails/generators/generated_attribute.rb +++ b/railties/lib/rails/generators/generated_attribute.rb @@ -68,14 +68,15 @@ module Rails def field_type @field_type ||= case type - when :integer then :number_field - when :float, :decimal then :text_field - when :time then :time_select - when :datetime, :timestamp then :datetime_select - when :date then :date_select - when :text then :text_area - when :rich_text then :rich_text_area - when :boolean then :check_box + when :integer then :number_field + when :float, :decimal then :text_field + when :time then :time_select + when :datetime, :timestamp then :datetime_select + when :date then :date_select + when :text then :text_area + when :rich_text then :rich_text_area + when :boolean then :check_box + when :attachment, :attachments then :file_field else :text_field end @@ -83,15 +84,17 @@ module Rails def default @default ||= case type - when :integer then 1 - when :float then 1.5 - when :decimal then "9.99" - when :datetime, :timestamp, :time then Time.now.to_s(:db) - when :date then Date.today.to_s(:db) - when :string then name == "type" ? "" : "MyString" - when :text then "MyText" - when :boolean then false - when :references, :belongs_to, :rich_text then nil + when :integer then 1 + when :float then 1.5 + when :decimal then "9.99" + when :datetime, :timestamp, :time then Time.now.to_s(:db) + when :date then Date.today.to_s(:db) + when :string then name == "type" ? "" : "MyString" + when :text then "MyText" + when :boolean then false + when :references, :belongs_to, + :attachment, :attachments, + :rich_text then nil else "" end @@ -157,8 +160,16 @@ module Rails type == :rich_text end + def attachment? + type == :attachment + end + + def attachments? + type == :attachments + end + def virtual? - rich_text? + rich_text? || attachment? || attachments? end def inject_options diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index d6732f8ff1..42e64cd11f 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -187,6 +187,7 @@ module Rails def attributes_names # :doc: @attributes_names ||= attributes.each_with_object([]) do |a, names| + next if a.attachments? names << a.column_name names << "password_confirmation" if a.password_digest? names << "#{a.name}_type" if a.polymorphic? diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb index 7030561a33..8b46eb88ae 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -32,6 +32,14 @@ module Rails hook_for :helper, as: :scaffold do |invoked| invoke invoked, [ controller_name ] end + + private + + def permitted_params + params = attributes_names.map { |name| ":#{name}" }.join(", ") + params += attributes.select(&:attachments?).map { |a| ", #{a.name}: []" }.join + params + end end end end diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt index 400afec6dc..bb26370276 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt @@ -54,7 +54,7 @@ class <%= controller_class_name %>Controller < ApplicationController <%- if attributes_names.empty? -%> params.fetch(:<%= singular_table_name %>, {}) <%- else -%> - params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) + params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>) <%- end -%> end end diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt index 05f1c2b2d3..82b43987b4 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt @@ -61,7 +61,7 @@ class <%= controller_class_name %>Controller < ApplicationController <%- if attributes_names.empty? -%> params.fetch(:<%= singular_table_name %>, {}) <%- else -%> - params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) + params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>) <%- end -%> end end diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 75b9316593..258066a7e6 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -553,6 +553,22 @@ module ApplicationTests end end end + + test "db:prepare setup the database" do + Dir.chdir(app_path) do + rails "generate", "model", "book", "title:string" + output = rails("db:prepare") + assert_match(/CreateBooks: migrated/, output) + + output = rails("db:drop") + assert_match(/Dropped database/, output) + + rails "generate", "model", "recipe", "title:string" + output = rails("db:prepare") + assert_match(/CreateBooks: migrated/, output) + assert_match(/CreateRecipes: migrated/, output) + end + end end end end diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb index d676e7486e..147b8f94e1 100644 --- a/railties/test/application/rake/multi_dbs_test.rb +++ b/railties/test/application/rake/multi_dbs_test.rb @@ -137,6 +137,21 @@ module ApplicationTests end end + def db_prepare + Dir.chdir(app_path) do + generate_models_for_animals + output = rails("db:prepare") + + ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).each do |db_config| + if db_config.spec_name == "primary" + assert_match(/CreateBooks: migrated/, output) + else + assert_match(/CreateDogs: migrated/, output) + end + end + end + end + def write_models_for_animals # make a directory for the animals migration FileUtils.mkdir_p("#{app_path}/db/animals_migrate") @@ -226,6 +241,11 @@ module ApplicationTests require "#{app_path}/config/environment" db_migrate_and_schema_cache_dump_and_schema_cache_clear end + + test "db:prepare works on all databases" do + require "#{app_path}/config/environment" + db_prepare + end end end end diff --git a/railties/test/application/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb index 5d2e34433a..dc6db429a9 100644 --- a/railties/test/application/zeitwerk_integration_test.rb +++ b/railties/test/application/zeitwerk_integration_test.rb @@ -181,8 +181,8 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase boot("production") assert $zeitwerk_integration_test_user - assert !$zeitwerk_integration_test_lib - assert !$zeitwerk_integration_test_extras + assert_not $zeitwerk_integration_test_lib + assert_not $zeitwerk_integration_test_extras assert WebhookHacks assert WebsocketHacks diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb index 7a1a2ee96b..bf60d6bc22 100644 --- a/railties/test/generators/generated_attribute_test.rb +++ b/railties/test/generators/generated_attribute_test.rb @@ -42,6 +42,12 @@ class GeneratedAttributeTest < Rails::Generators::TestCase assert_field_type :rich_text, :rich_text_area end + def test_field_type_returns_file_field + %w(attachment attachments).each do |attribute_type| + assert_field_type attribute_type, :file_field + end + end + def test_field_type_with_unknown_type_returns_text_field %w(foo bar baz).each do |attribute_type| assert_field_type attribute_type, :text_field @@ -88,7 +94,7 @@ class GeneratedAttributeTest < Rails::Generators::TestCase end def test_default_value_is_nil - %w(references belongs_to rich_text).each do |attribute_type| + %w(references belongs_to rich_text attachment attachments).each do |attribute_type| assert_field_default_value attribute_type, nil end end diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb index 8a66956290..540bed551b 100644 --- a/railties/test/generators/migration_generator_test.rb +++ b/railties/test/generators/migration_generator_test.rb @@ -368,32 +368,38 @@ class MigrationGeneratorTest < Rails::Generators::TestCase def test_add_migration_ignores_virtual_attributes migration = "add_rich_text_content_to_messages" - run_generator [migration, "content:rich_text"] + run_generator [migration, "content:rich_text", "video:attachment", "photos:attachments"] assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :change, content do |change| assert_no_match(/add_column :messages, :content, :rich_text/, change) + assert_no_match(/add_column :messages, :video, :attachment/, change) + assert_no_match(/add_column :messages, :photos, :attachments/, change) end end end def test_create_table_migration_ignores_virtual_attributes - run_generator ["create_messages", "content:rich_text"] + run_generator ["create_messages", "content:rich_text", "video:attachment", "photos:attachments"] assert_migration "db/migrate/create_messages.rb" do |content| assert_method :change, content do |change| assert_match(/create_table :messages/, change) assert_no_match(/ t\.rich_text :content/, change) + assert_no_match(/ t\.attachment :video/, change) + assert_no_match(/ t\.attachments :photos/, change) end end end def test_remove_migration_with_virtual_attributes migration = "remove_content_from_messages" - run_generator [migration, "content:rich_text"] + run_generator [migration, "content:rich_text", "video:attachment", "photos:attachments"] assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :change, content do |change| assert_no_match(/remove_column :messages, :content, :rich_text/, change) + assert_no_match(/remove_column :messages, :video, :attachment/, change) + assert_no_match(/remove_column :messages, :photos, :attachments/, change) end end end diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 0eb8e9d270..c97cd17ec6 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -509,8 +509,28 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_file "app/models/message.rb", expected_file end + def test_model_with_attachment_attribute_adds_has_one_attached + run_generator ["message", "video:attachment"] + expected_file = <<~FILE + class Message < ApplicationRecord + has_one_attached :video + end + FILE + assert_file "app/models/message.rb", expected_file + end + + def test_model_with_attachments_attribute_adds_has_many_attached + run_generator ["message", "photos:attachments"] + expected_file = <<~FILE + class Message < ApplicationRecord + has_many_attached :photos + end + FILE + assert_file "app/models/message.rb", expected_file + end + def test_skip_virtual_fields_in_fixtures - run_generator ["message", "content:rich_text"] + run_generator ["message", "content:rich_text", "video:attachment", "photos:attachments"] assert_generated_fixture("test/fixtures/messages.yml", "one" => nil, "two" => nil) diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index fd5aa817b4..1348744b0b 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -80,6 +80,15 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase end end + def test_controller_permit_attachment_attributes + run_generator ["Message", "video:attachment", "photos:attachments"] + + assert_file "app/controllers/messages_controller.rb" do |content| + assert_match(/def message_params/, content) + assert_match(/params\.require\(:message\)\.permit\(:video, photos: \[\]\)/, content) + end + end + def test_helper_are_invoked_with_a_pluralized_name run_generator assert_file "app/helpers/users_helper.rb", /module UsersHelper/ @@ -276,4 +285,13 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_no_match(/assert_redirected_to/, content) end end + + def test_api_only_generates_params_for_attachments + run_generator ["Message", "video:attachment", "photos:attachments", "--api"] + + assert_file "app/controllers/messages_controller.rb" do |content| + assert_match(/def message_params/, content) + assert_match(/params\.require\(:message\)\.permit\(:video, photos: \[\]\)/, content) + end + end end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 715ad938f4..bfa52a1beb 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -471,6 +471,24 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end end + def test_scaffold_generator_attachments + run_generator ["message", "video:attachment", "photos:attachments", "images:attachments"] + + assert_file "app/models/message.rb", /has_one_attached :video/ + assert_file "app/models/message.rb", /has_many_attached :photos/ + + assert_file "app/controllers/messages_controller.rb" do |content| + assert_instance_method :message_params, content do |m| + assert_match(/permit\(:video, photos: \[\], images: \[\]\)/, m) + end + end + + assert_file "app/views/messages/_form.html.erb" do |content| + assert_match(/^\W{4}<%= form\.file_field :video %>/, content) + assert_match(/^\W{4}<%= form\.file_field :photos, multiple: true %>/, content) + end + end + def test_scaffold_generator_database with_secondary_database_configuration do run_generator ["posts", "--database=secondary"] |