diff options
54 files changed, 372 insertions, 205 deletions
diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb index dfee123ea2..e1da126d64 100644 --- a/actioncable/lib/action_cable/connection/base.rb +++ b/actioncable/lib/action_cable/connection/base.rb @@ -24,11 +24,8 @@ module ActionCable # # protected # def find_verified_user - # if current_user = User.find_by_identity cookies.signed[:identity_id] - # current_user - # else + # User.find_by_identity(cookies.signed[:identity_id]) || # reject_unauthorized_connection - # end # end # end # end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 357ca56036..f71c6afd6c 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -408,7 +408,7 @@ module ActionDispatch # :nodoc: def parse_content_type(content_type) if content_type type, charset = content_type.split(/;\s*charset=/) - type = nil if type.empty? + type = nil if type && type.empty? ContentTypeHeader.new(type, charset) else NullContentTypeHeader diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 1ab6158c90..021ffec862 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -69,7 +69,7 @@ module ActionDispatch DEFAULT_HOST = "www.example.com" include Minitest::Assertions - include RequestHelpers, Assertions + include TestProcess, RequestHelpers, Assertions %w( status status_message headers body redirect? ).each do |method| delegate method, to: :response, allow_nil: true @@ -598,7 +598,7 @@ module ActionDispatch # Consult the Rails Testing Guide for more. class IntegrationTest < ActiveSupport::TestCase - include TestProcess + include TestProcess::FixtureFile module UrlOptions extend ActiveSupport::Concern diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb index 8b03b776fa..0282eb15c3 100644 --- a/actionpack/lib/action_dispatch/testing/test_process.rb +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -3,6 +3,26 @@ require "action_dispatch/middleware/flash" module ActionDispatch module TestProcess + module FixtureFile + # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.fixture_path, path), type)</tt>: + # + # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png') + # + # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter. + # This will not affect other platforms: + # + # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary) + def fixture_file_upload(path, mime_type = nil, binary = false) + if self.class.respond_to?(:fixture_path) && self.class.fixture_path && + !File.exist?(path) + path = File.join(self.class.fixture_path, path) + end + Rack::Test::UploadedFile.new(path, mime_type, binary) + end + end + + include FixtureFile + def assigns(key = nil) raise NoMethodError, "assigns has been extracted to a gem. To continue using it, @@ -24,21 +44,5 @@ module ActionDispatch def redirect_to_url @response.redirect_url end - - # Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionDispatch::IntegrationTest.fixture_path, path), type)</tt>: - # - # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png') - # - # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter. - # This will not affect other platforms: - # - # post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary) - def fixture_file_upload(path, mime_type = nil, binary = false) - if self.class.respond_to?(:fixture_path) && self.class.fixture_path && - !File.exist?(path) - path = File.join(self.class.fixture_path, path) - end - Rack::Test::UploadedFile.new(path, mime_type, binary) - end end end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 400af42bac..2df70704a1 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -110,6 +110,11 @@ class ResponseTest < ActiveSupport::TestCase assert_equal "application/aaron", @response.content_type end + def test_empty_content_type_returns_nil + @response.headers['Content-Type'] = "" + assert_equal nil, @response.content_type + end + test "simple output" do @response.body = "Hello, World!" diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 558659dd77..8da2e4ae1d 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,11 @@ +* Use `ActionView::Resolver.caching?` (`config.action_view.cache_template_loading`) + to enable template recompilation. + + Before it was enabled by `consider_all_requests_local`, which caused + recompilation in tests. + + *Max Melentiev* + * Add `form_with` to unify `form_tag` and `form_for` usage. Used like `form_tag` (where just the open tag is output): diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb index ae72cea404..d344d98f4b 100644 --- a/actionview/lib/action_view/railtie.rb +++ b/actionview/lib/action_view/railtie.rb @@ -39,7 +39,7 @@ module ActionView initializer "action_view.per_request_digest_cache" do |app| ActiveSupport.on_load(:action_view) do - if app.config.consider_all_requests_local + unless ActionView::Resolver.caching? app.executor.to_run ActionView::Digestor::PerExecutionDigestCacheExpiry end end diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 2dcd6324db..0afdcd1def 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -326,7 +326,7 @@ module ActionView def locals_code #:nodoc: # Only locals with valid variable names get set directly. Others will # still be available in local_assigns. - locals = @locals.to_set - Module::DELEGATION_RESERVED_METHOD_NAMES + locals = @locals - Module::RUBY_RESERVED_KEYWORDS locals = locals.grep(/\A(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/) # Double assign to suppress the dreaded 'assigned but unused variable' warning diff --git a/actionview/test/fixtures/test/test_template_with_delegation_reserved_keywords.erb b/actionview/test/fixtures/test/test_template_with_delegation_reserved_keywords.erb new file mode 100644 index 0000000000..edfe52e422 --- /dev/null +++ b/actionview/test/fixtures/test/test_template_with_delegation_reserved_keywords.erb @@ -0,0 +1 @@ +<%= _ %> <%= arg %> <%= args %> <%= block %>
\ No newline at end of file diff --git a/actionview/test/template/compiled_templates_test.rb b/actionview/test/template/compiled_templates_test.rb index 3ecac46d34..40ac867b38 100644 --- a/actionview/test/template/compiled_templates_test.rb +++ b/actionview/test/template/compiled_templates_test.rb @@ -24,6 +24,16 @@ class CompiledTemplatesTest < ActiveSupport::TestCase assert_equal locals.inspect, render(file: "test/render_file_inspect_local_assigns", locals: locals) end + def test_template_with_delegation_reserved_keywords + locals = { + _: "one", + arg: "two", + args: "three", + block: "four", + } + assert_equal "one two three four", render(file: "test/test_template_with_delegation_reserved_keywords", locals: locals) + end + def test_template_with_unicode_identifier assert_equal "🎂", render(file: "test/render_file_unicode_local", locals: { 🎃: "🎂" }) end diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 9c53e2ecec..853a1e7d9d 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,3 +1,7 @@ +* Moved DecimalWithoutScale, Text, and UnsignedInteger from Active Model to Active Record + + *Iain Beeston* + * Allow indifferent access in `ActiveModel::Errors`. `#include?`, `#has_key?`, `#key?`, `#delete` and `#full_messages_for`. diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 5ee9413cff..9df4ca51fe 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -419,16 +419,23 @@ module ActiveModel I18n.translate(key, options) end - def marshal_dump + def marshal_dump # :nodoc: [@base, without_default_proc(@messages), without_default_proc(@details)] end - def marshal_load(array) + def marshal_load(array) # :nodoc: @base, @messages, @details = array apply_default_array(@messages) apply_default_array(@details) end + def init_with(coder) # :nodoc: + coder.map.each { |k, v| instance_variable_set(:"@#{k}", v) } + @details ||= {} + apply_default_array(@messages) + apply_default_array(@details) + end + private def normalize_message(attribute, message, options) case message diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb index e683050787..945a5402a3 100644 --- a/activemodel/lib/active_model/model.rb +++ b/activemodel/lib/active_model/model.rb @@ -2,10 +2,10 @@ module ActiveModel # == Active \Model \Basic \Model # # Includes the required interface for an object to interact with - # <tt>ActionPack</tt>, using different <tt>ActiveModel</tt> modules. + # Action Pack and Action View, using different Active Model modules. # It includes model name introspections, conversions, translations and # validations. Besides that, it allows you to initialize the object with a - # hash of attributes, pretty much like <tt>ActiveRecord</tt> does. + # hash of attributes, pretty much like Active Record does. # # A minimal implementation could be: # diff --git a/activemodel/lib/active_model/type.rb b/activemodel/lib/active_model/type.rb index 0d3349e236..b8e6d2376b 100644 --- a/activemodel/lib/active_model/type.rb +++ b/activemodel/lib/active_model/type.rb @@ -7,14 +7,11 @@ require "active_model/type/boolean" require "active_model/type/date" require "active_model/type/date_time" require "active_model/type/decimal" -require "active_model/type/decimal_without_scale" require "active_model/type/float" require "active_model/type/immutable_string" require "active_model/type/integer" require "active_model/type/string" -require "active_model/type/text" require "active_model/type/time" -require "active_model/type/unsigned_integer" require "active_model/type/registry" @@ -53,7 +50,6 @@ module ActiveModel register(:immutable_string, Type::ImmutableString) register(:integer, Type::Integer) register(:string, Type::String) - register(:text, Type::Text) register(:time, Type::Time) end end diff --git a/activemodel/lib/active_model/type/decimal_without_scale.rb b/activemodel/lib/active_model/type/decimal_without_scale.rb deleted file mode 100644 index 985e1038ed..0000000000 --- a/activemodel/lib/active_model/type/decimal_without_scale.rb +++ /dev/null @@ -1,11 +0,0 @@ -require "active_model/type/big_integer" - -module ActiveModel - module Type - class DecimalWithoutScale < BigInteger # :nodoc: - def type - :decimal - end - end - end -end diff --git a/activemodel/lib/active_model/type/helpers/time_value.rb b/activemodel/lib/active_model/type/helpers/time_value.rb index ad78fd49ec..721f9543ed 100644 --- a/activemodel/lib/active_model/type/helpers/time_value.rb +++ b/activemodel/lib/active_model/type/helpers/time_value.rb @@ -34,7 +34,7 @@ module ActiveModel return value unless precision && value.respond_to?(:usec) number_of_insignificant_digits = 6 - precision round_power = 10**number_of_insignificant_digits - value.change(usec: value.usec / round_power * round_power) + value.change(usec: value.usec - value.usec % round_power) end def type_cast_for_schema(value) diff --git a/activemodel/lib/active_model/type/text.rb b/activemodel/lib/active_model/type/text.rb deleted file mode 100644 index 7c0d647706..0000000000 --- a/activemodel/lib/active_model/type/text.rb +++ /dev/null @@ -1,11 +0,0 @@ -require "active_model/type/string" - -module ActiveModel - module Type - class Text < String # :nodoc: - def type - :text - end - end - end -end diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index fc840bf192..e868d20fc8 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -365,4 +365,24 @@ class ErrorsTest < ActiveModel::TestCase assert_equal errors.messages, serialized.messages assert_equal errors.details, serialized.details end + + test "errors are backward compatible with the Rails 4.2 format" do + yaml = <<-CODE.strip_heredoc + --- !ruby/object:ActiveModel::Errors + base: &1 !ruby/object:ErrorsTest::Person + errors: !ruby/object:ActiveModel::Errors + base: *1 + messages: {} + messages: {} + CODE + + errors = YAML.load(yaml) + errors.add(:name, :invalid) + assert_equal({ name: ["is invalid"] }, errors.messages) + assert_equal({ name: [{ error: :invalid }] }, errors.details) + + errors.clear + assert_equal({}, errors.messages) + assert_equal({}, errors.details) + end end diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 2182ee78bb..e377ad85c9 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,4 +1,33 @@ -* PostgreSQL & MySQL: Use big integer as primary key type for new tables +* Raise `ActiveRecord::NotNullViolation` when a record cannot be inserted + or updated because it would violate a not null constraint. + + *Ryuta Kamizono* + +* Emulate db trigger behaviour for after_commit :destroy, :update. + + Race conditions can occur when an ActiveRecord is destroyed + twice or destroyed and updated. The callbacks should only be + triggered once, similar to a SQL database trigger. + + *Stefan Budeanu* + +* Moved `DecimalWithoutScale`, `Text`, and `UnsignedInteger` from Active Model to Active Record. + + *Iain Beeston* + +* Fix `write_attribute` method to check whether an attribute is aliased or not, and + use the aliased attribute name if needed. + + *Prathamesh Sonpatki* + +* Fix `read_attribute` method to check whether an attribute is aliased or not, and + use the aliased attribute name if needed. + + Fixes #26417. + + *Prathamesh Sonpatki* + +* PostgreSQL & MySQL: Use big integer as primary key type for new tables. *Jon McCartie*, *Pavel Pravosud* @@ -18,7 +47,7 @@ *Sergey Alekseev* -* Raise ActiveRecord::RecordNotFound from collection `*_ids` setters +* Raise `ActiveRecord::RecordNotFound` from collection `*_ids` setters for unknown IDs with a better error message. Changes the collection `*_ids` setters to cast provided IDs the data @@ -58,7 +87,7 @@ *Jon Moss* -* Added `stat` method to `ActiveRecord::ConnectionAdapters::ConnectionPool` +* Added `stat` method to `ActiveRecord::ConnectionAdapters::ConnectionPool`. Example: @@ -105,7 +134,7 @@ * Fixed: Optimistic locking does not work well with `null` in the database. - Fixes #26024 + Fixes #26024. *bogdanvlviv* @@ -114,7 +143,7 @@ *Edho Arief* -* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null` +* Serialize JSON attribute value `nil` as SQL `NULL`, not JSON `null`. *Trung Duc Tran* @@ -218,7 +247,7 @@ successfully rolled back when the column was given and invalid column type. - Fixes #26087 + Fixes #26087. *Travis O'Neill* @@ -248,7 +277,7 @@ *Takeshi Akima* * Virtual attributes will no longer raise when read on models loaded from the - database + database. *Sean Griffin* diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 3d23fa1e46..46923f690a 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -75,7 +75,7 @@ module ActiveRecord r.send(reflection.association_primary_key) end.values_at(*ids).compact if records.size != ids.size - scope.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key) + klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key) else replace(records) end diff --git a/activerecord/lib/active_record/attribute_decorators.rb b/activerecord/lib/active_record/attribute_decorators.rb index 340dfe11cf..c39e9ce4c5 100644 --- a/activerecord/lib/active_record/attribute_decorators.rb +++ b/activerecord/lib/active_record/attribute_decorators.rb @@ -8,12 +8,34 @@ module ActiveRecord end module ClassMethods # :nodoc: + # This method is an internal API used to create class macros such as + # +serialize+, and features like time zone aware attributes. + # + # Used to wrap the type of an attribute in a new type. + # When the schema for a model is loaded, attributes with the same name as + # +column_name+ will have their type yielded to the given block. The + # return value of that block will be used instead. + # + # Subsequent calls where +column_name+ and +decorator_name+ are the same + # will override the previous decorator, not decorate twice. This can be + # used to create idempotent class macros like +serialize+ def decorate_attribute_type(column_name, decorator_name, &block) matcher = ->(name, _) { name == column_name.to_s } key = "_#{column_name}_#{decorator_name}" decorate_matching_attribute_types(matcher, key, &block) end + # This method is an internal API used to create higher level features like + # time zone aware attributes. + # + # When the schema for a model is loaded, +matcher+ will be called for each + # attribute with its name and type. If the matcher returns a truthy value, + # the type will then be yielded to the given block, and the return value + # of that block will replace the type. + # + # Subsequent calls to this method with the same value for +decorator_name+ + # will replace the previous decorator, not decorate twice. This can be + # used to ensure that class macros are idempotent. def decorate_matching_attribute_types(matcher, decorator_name, &block) reload_schema_from_cache decorator_name = decorator_name.to_s diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 30f7750884..5448ebc165 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -48,7 +48,12 @@ module ActiveRecord # it has been typecast (for example, "2004-12-12" in a date column is cast # to a date object, like Date.new(2004, 12, 12)). def read_attribute(attr_name, &block) - name = attr_name.to_s + name = if self.class.attribute_alias?(attr_name) + self.class.attribute_alias(attr_name).to_s + else + attr_name.to_s + end + name = self.class.primary_key if name == "id".freeze _read_attribute(name, &block) end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index f65c297e01..0022d526a4 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -29,7 +29,13 @@ module ActiveRecord # specified +value+. Empty strings for Integer and Float columns are # turned into +nil+. def write_attribute(attr_name, value) - write_attribute_with_type_cast(attr_name, value, true) + name = if self.class.attribute_alias?(attr_name) + self.class.attribute_alias(attr_name).to_s + else + attr_name.to_s + end + + write_attribute_with_type_cast(name, value, true) end def raw_write_attribute(attr_name, value) # :nodoc: diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index b343332bae..9d0b501862 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -383,6 +383,9 @@ module ActiveRecord if association = association_instance_get(reflection.name) autosave = reflection.options[:autosave] + # reconstruct the scope now that we know the owner's id + association.reset_scope if association.respond_to?(:reset_scope) + if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave) if autosave records_to_destroy = records.select(&:marked_for_destruction?) @@ -408,9 +411,6 @@ module ActiveRecord raise ActiveRecord::Rollback unless saved end end - - # reconstruct the scope now that we know the owner's id - association.reset_scope if association.respond_to?(:reset_scope) end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 84589bd5f2..6985d2c1b2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -39,7 +39,7 @@ module ActiveRecord self.emulate_booleans = true NATIVE_DATABASE_TYPES = { - primary_key: "BIGINT(8) UNSIGNED auto_increment PRIMARY KEY", + primary_key: "bigint auto_increment PRIMARY KEY", string: { name: "varchar", limit: 255 }, text: { name: "text", limit: 65535 }, integer: { name: "int", limit: 4 }, diff --git a/activerecord/lib/active_record/connection_adapters/mysql/column.rb b/activerecord/lib/active_record/connection_adapters/mysql/column.rb index 22b9df5309..c66d543752 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/column.rb @@ -5,6 +5,7 @@ module ActiveRecord delegate :extra, to: :sql_type_metadata, allow_nil: true def unsigned? + # enum and set types do not allow being defined as unsigned. !/\A(?:enum|set)\b/.match?(sql_type) && /\bunsigned\b/.match?(sql_type) end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 8e13ee3564..60d8e95b21 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -181,7 +181,11 @@ module ActiveRecord _raise_readonly_record_error if readonly? destroy_associations self.class.connection.add_transaction_record(self) - destroy_row if persisted? + @_trigger_destroy_callback = if persisted? + destroy_row > 0 + else + true + end @destroyed = true freeze end @@ -519,6 +523,7 @@ module ActiveRecord raise ActiveRecord::StaleObjectError.new(self, "touch") end + @_trigger_update_callback = result result else true @@ -550,10 +555,13 @@ module ActiveRecord def _update_record(attribute_names = self.attribute_names) attributes_values = arel_attributes_with_values_for_update(attribute_names) if attributes_values.empty? - 0 + rows_affected = 0 + @_trigger_update_callback = true else - self.class.unscoped._update_record attributes_values, id, id_in_database + rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database + @_trigger_update_callback = rows_affected > 0 end + rows_affected end # Creates a record with values matching those of the instance attributes diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 17751c9571..e1a3c59f08 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -850,6 +850,10 @@ module ActiveRecord actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass) end + def association_primary_key_type + klass.type_for_attribute(association_primary_key) + end + # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form. # # class Post < ActiveRecord::Base diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index af3fc88282..ce939c8b97 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -461,9 +461,10 @@ module ActiveRecord when :create transaction_record_state(:new_record) when :destroy - destroyed? + defined?(@_trigger_destroy_callback) && @_trigger_destroy_callback when :update - !(transaction_record_state(:new_record) || destroyed?) + !(transaction_record_state(:new_record) || destroyed?) && + (defined?(@_trigger_update_callback) && @_trigger_update_callback) end end end diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index 0b48d2186a..4f632660a8 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -5,7 +5,10 @@ require "active_record/type/internal/timezone" require "active_record/type/date" require "active_record/type/date_time" +require "active_record/type/decimal_without_scale" require "active_record/type/time" +require "active_record/type/text" +require "active_record/type/unsigned_integer" require "active_record/type/serialized" require "active_record/type/adapter_specific_registry" @@ -53,12 +56,9 @@ module ActiveRecord Binary = ActiveModel::Type::Binary Boolean = ActiveModel::Type::Boolean Decimal = ActiveModel::Type::Decimal - DecimalWithoutScale = ActiveModel::Type::DecimalWithoutScale Float = ActiveModel::Type::Float Integer = ActiveModel::Type::Integer String = ActiveModel::Type::String - Text = ActiveModel::Type::Text - UnsignedInteger = ActiveModel::Type::UnsignedInteger Value = ActiveModel::Type::Value register(:big_integer, Type::BigInteger, override: false) diff --git a/activerecord/lib/active_record/type/decimal_without_scale.rb b/activerecord/lib/active_record/type/decimal_without_scale.rb new file mode 100644 index 0000000000..7ce33e9cd3 --- /dev/null +++ b/activerecord/lib/active_record/type/decimal_without_scale.rb @@ -0,0 +1,9 @@ +module ActiveRecord + module Type + class DecimalWithoutScale < ActiveModel::Type::BigInteger # :nodoc: + def type + :decimal + end + end + end +end diff --git a/activerecord/lib/active_record/type/text.rb b/activerecord/lib/active_record/type/text.rb new file mode 100644 index 0000000000..cb1949700a --- /dev/null +++ b/activerecord/lib/active_record/type/text.rb @@ -0,0 +1,9 @@ +module ActiveRecord + module Type + class Text < ActiveModel::Type::String # :nodoc: + def type + :text + end + end + end +end diff --git a/activemodel/lib/active_model/type/unsigned_integer.rb b/activerecord/lib/active_record/type/unsigned_integer.rb index 288fa23efe..9ae0109f9f 100644 --- a/activemodel/lib/active_model/type/unsigned_integer.rb +++ b/activerecord/lib/active_record/type/unsigned_integer.rb @@ -1,6 +1,6 @@ -module ActiveModel +module ActiveRecord module Type - class UnsignedInteger < Integer # :nodoc: + class UnsignedInteger < ActiveModel::Type::Integer # :nodoc: private def max_value diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 8defb09db7..83b1f8d4d6 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -904,6 +904,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_match(/Couldn't find all Clients with 'name'/, e.message) end + def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set + author = authors(:david) + ids = [categories(:general).name, "Unknown"] + e = assert_raises(ActiveRecord::RecordNotFound) { author.essay_category_ids = ids } + assert_equal "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2)", e.message + end + def test_build_a_model_from_hm_through_association_with_where_clause assert_nothing_raised { books(:awdr).subscribers.where(nick: "marklazz").build } end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index ba6877a6a6..4ac604a164 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -319,6 +319,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal "Still another topic: part 4", topic.title end + test "write_attribute can write aliased attributes as well" do + topic = Topic.new(title: "Don't change the topic") + topic.write_attribute :heading, "New topic" + + assert_equal "New topic", topic.title + end + test "read_attribute" do topic = Topic.new topic.title = "Don't change the topic" @@ -329,6 +336,16 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal "Don't change the topic", topic[:title] end + test "read_attribute can read aliased attributes as well" do + topic = Topic.new(title: "Don't change the topic") + + assert_equal "Don't change the topic", topic.read_attribute("heading") + assert_equal "Don't change the topic", topic["heading"] + + assert_equal "Don't change the topic", topic.read_attribute(:heading) + assert_equal "Don't change the topic", topic[:heading] + end + test "read_attribute raises ActiveModel::MissingAttributeError when the attribute does not exist" do computer = Computer.select("id").first assert_raises(ActiveModel::MissingAttributeError) { computer[:developer] } diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index a3f82ed49d..77ee3ca2d7 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -1699,3 +1699,27 @@ class TestAutosaveAssociationWithTouch < ActiveRecord::TestCase assert_nothing_raised { invoice.line_items.create(amount: 10) } end end + +class TestAutosaveAssociationOnAHasManyAssociationWithInverse < ActiveRecord::TestCase + class Post < ActiveRecord::Base + has_many :comments, inverse_of: :post + end + + class Comment < ActiveRecord::Base + belongs_to :post, inverse_of: :comments + + attr_accessor :post_comments_count + after_save do + self.post_comments_count = post.comments.count + end + end + + def test_after_save_callback_with_autosave + post = Post.new(title: "Test", body: "...") + comment = post.comments.build(body: "...") + post.save! + + assert_equal 1, post.comments.count + assert_equal 1, comment.post_comments_count + end +end diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index dba100f5c9..391bbe8877 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -449,6 +449,51 @@ class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase end end +class CallbacksOnDestroyUpdateActionRaceTest < ActiveRecord::TestCase + class TopicWithHistory < ActiveRecord::Base + self.table_name = :topics + + def self.clear_history + @@history = [] + end + + def self.history + @@history ||= [] + end + end + + class TopicWithCallbacksOnDestroy < TopicWithHistory + after_commit(on: :destroy) { |record| record.class.history << :destroy } + end + + class TopicWithCallbacksOnUpdate < TopicWithHistory + after_commit(on: :update) { |record| record.class.history << :update } + end + + def test_trigger_once_on_multiple_deletions + TopicWithCallbacksOnDestroy.clear_history + topic = TopicWithCallbacksOnDestroy.new + topic.save + topic_clone = TopicWithCallbacksOnDestroy.find(topic.id) + topic.destroy + topic_clone.destroy + + assert_equal [:destroy], TopicWithCallbacksOnDestroy.history + end + + def test_trigger_on_update_where_row_was_deleted + TopicWithCallbacksOnUpdate.clear_history + topic = TopicWithCallbacksOnUpdate.new + topic.save + topic_clone = TopicWithCallbacksOnUpdate.find(topic.id) + topic.destroy + topic_clone.author_name = "Test Author" + topic_clone.save + + assert_equal [], TopicWithCallbacksOnUpdate.history + end +end + class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase class TopicWithoutTransactionalEnrollmentCallbacks < ActiveRecord::Base self.table_name = :topics diff --git a/activemodel/test/cases/type/unsigned_integer_test.rb b/activerecord/test/cases/type/unsigned_integer_test.rb index 026cb08a06..1cd4dbc2c5 100644 --- a/activemodel/test/cases/type/unsigned_integer_test.rb +++ b/activerecord/test/cases/type/unsigned_integer_test.rb @@ -1,9 +1,8 @@ require "cases/helper" -require "active_model/type" -module ActiveModel +module ActiveRecord module Type - class UnsignedIntegerTest < ActiveModel::TestCase + class UnsignedIntegerTest < ActiveRecord::TestCase test "unsigned int max value is in range" do assert_equal(4294967295, UnsignedInteger.new.serialize(4294967295)) end diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index f5f4ba61b7..19f692e943 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -6,11 +6,12 @@ class Module # option is not used. class DelegationError < NoMethodError; end + RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do + else elsif END end ensure false for if in module next nil not or redo rescue retry + return self super then true undef unless until when while yield) + DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block) DELEGATION_RESERVED_METHOD_NAMES = Set.new( - %w(_ arg args alias and BEGIN begin block break case class def defined? do - else elsif END end ensure false for if in module next nil not or redo - rescue retry return self super then true undef unless until when while - yield) + RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS ).freeze # Provides a +delegate+ class method to easily expose contained objects' diff --git a/guides/source/testing.md b/guides/source/testing.md index 29a9537141..6f783089a9 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -322,7 +322,6 @@ specify to make your test failure messages clearer. | `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.| | `assert_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is true, e.g. `assert_predicate str, :empty?`| | `assert_not_predicate ( obj, predicate, [msg] )` | Ensures that `obj.predicate` is false, e.g. `assert_not_predicate str, :empty?`| -| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true, e.g. assert_send [@user, :full_name, 'Sam Smith']. This one is weird eh?| | `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.| The above are a subset of assertions that minitest supports. For an exhaustive & diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index c445390cd3..0ba220104a 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,5 +1,4 @@ -* Add Yarn support in new apps using --yarn option. This adds a yarn binstub, vendor/package.json, - and the settings needed to get npm modules integrated in new apps. +* Add Yarn support in new apps with a yarn binstub and vendor/package.json. Skippable via --skip-yarn option. *Liceth Ovalles*, *Guillermo Iguaran*, *DHH* diff --git a/railties/Rakefile b/railties/Rakefile index 202644fb26..680ed03f75 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -18,6 +18,7 @@ namespace :test do "lib", "#{File.dirname(__FILE__)}/../activesupport/lib", "#{File.dirname(__FILE__)}/../actionpack/lib", + "#{File.dirname(__FILE__)}/../actionview/lib", "#{File.dirname(__FILE__)}/../activemodel/lib" ] ruby "-w", "-I#{dash_i.join ':'}", file diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 87f6f01750..576c36fc86 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -33,8 +33,11 @@ module Rails class_option :javascript, type: :string, aliases: "-j", desc: "Preconfigure for selected JavaScript library" - class_option :yarn, type: :boolean, default: false, - desc: "Preconfigure for assets management with Yarn" + class_option :webpack, type: :boolean, default: false, + desc: "Preconfigure for app-like JavaScript with Webpack" + + class_option :skip_yarn, type: :boolean, default: false, + desc: "Don't use Yarn for managing JavaScript dependencies" class_option :skip_gemfile, type: :boolean, default: false, desc: "Don't create a Gemfile" @@ -128,6 +131,7 @@ module Rails database_gemfile_entry, webserver_gemfile_entry, assets_gemfile_entry, + webpacker_gemfile_entry, javascript_gemfile_entry, jbuilder_gemfile_entry, psych_gemfile_entry, @@ -315,6 +319,13 @@ module Rails gems end + def webpacker_gemfile_entry + return [] unless options[:webpack] + + comment = "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker" + GemfileEntry.github "webpacker", "rails/webpacker", nil, comment + end + def jbuilder_gemfile_entry comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder" GemfileEntry.new "jbuilder", "~> 2.5", comment, {}, options[:api] @@ -414,53 +425,8 @@ module Rails bundle_command("install") if bundle_install? end - def run_yarn - if package_json_exist? - if yarn_path - say_status :run, "yarn install" - yarn_command("install") - else - say_status :warning, "yarn option passed but Yarn executable was not detected in the system.", :yellow - say_status :warning, "Download Yarn at https://yarnpkg.com/en/docs/install", :yellow - end - end - end - - def package_json_exist? - File.exist?("vendor/package.json") - end - - def yarn_path - commands = ["yarn"] - - if Gem.win_platform? - ENV["PATHEXT"].split(File::PATH_SEPARATOR).each do |ext| - commands << commands[0] + ext - end - end - - yarn_path = commands.find do |cmd| - paths = ENV["PATH"].split(File::PATH_SEPARATOR) - - path = paths.find do |p| - full_path = File.expand_path(cmd, p) - File.executable?(full_path) && File.file?(full_path) - end - - path && File.expand_path(cmd, path) - end - - yarn_path - end - - def yarn_command(command) - full_command = "#{yarn_path} #{command}" - - if options[:quiet] - system(full_command, out: File::NULL) - else - system(full_command) - end + def run_webpack + rails_command "webpacker:install" if options[:webpack] end def generate_spring_binstubs diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 49d23d682a..19d3ba2f0f 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -53,10 +53,6 @@ module Rails template "gitignore", ".gitignore" end - def packagejson - template "package.json", "vendor/package.json" - end - def app directory "app" @@ -155,23 +151,12 @@ module Rails end def vendor - if options[:yarn] - empty_directory_with_keep_file "vendor" - else - vendor_javascripts - vendor_stylesheets - end - end + empty_directory_with_keep_file "vendor" - def vendor_javascripts - unless options[:skip_javascript] - empty_directory_with_keep_file "vendor/assets/javascripts" + unless options[:skip_yarn] + template "package.json", "vendor/package.json" end end - - def vendor_stylesheets - empty_directory_with_keep_file "vendor/assets/stylesheets" - end end module Generators @@ -201,9 +186,11 @@ module Rails raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}." end - # Force sprockets to be skipped when generating API only apps. + # Force sprockets and yarn to be skipped when generating API only apps. # Can't modify options hash as it's frozen by default. - self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze if options[:api] + if options[:api] + self.options = options.merge(skip_sprockets: true, skip_javascript: true, skip_yarn: true).freeze + end end public_task :set_default_accessors! @@ -213,9 +200,8 @@ module Rails build(:readme) build(:rakefile) build(:configru) - build(:packagejson) if options[:yarn] - build(:gitignore) unless options[:skip_git] - build(:gemfile) unless options[:skip_gemfile] + build(:gitignore) unless options[:skip_git] + build(:gemfile) unless options[:skip_gemfile] end def create_app_files @@ -276,6 +262,10 @@ module Rails def create_vendor_files build(:vendor) + + if options[:skip_yarn] + remove_file "vendor/package.json" + end end def delete_app_assets_if_api_option @@ -283,7 +273,6 @@ module Rails remove_dir "app/assets" remove_dir "lib/assets" remove_dir "tmp/cache/assets" - remove_dir "vendor/assets" end end @@ -359,12 +348,16 @@ module Rails end end + def delete_bin_yarn_if_api_option + remove_file "bin/yarn" if options[:api] + end + def finish_template build(:leftovers) end public_task :apply_rails_template, :run_bundle - public_task :run_yarn, :generate_spring_binstubs + public_task :run_webpack, :generate_spring_binstubs def run_after_bundle_callbacks @after_bundle_callbacks.each(&:call) diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt index 8db5b7e075..25870f19c8 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt @@ -1,8 +1,8 @@ // This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's +// vendor/assets/javascripts directory can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. JavaScript code in this file should be added after the last require_* statement. diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css index 0ebd7fe829..865300bef9 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css +++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css @@ -2,8 +2,8 @@ * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, - * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the bottom of the * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt index 73b0732463..c6607dbb2b 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/setup.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/setup.tt @@ -16,8 +16,9 @@ chdir APP_ROOT do puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') -<% if options[:yarn] %> - system! 'bin/yarn' +<% unless options[:skip_yarn] %> + # Install JavaScript dependencies if using Yarn + # system('bin/yarn') <% end %> <% unless options.skip_active_record -%> diff --git a/railties/lib/rails/generators/rails/app/templates/bin/update.tt b/railties/lib/rails/generators/rails/app/templates/bin/update.tt index dcd80b6ce5..d23af018c7 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/update.tt +++ b/railties/lib/rails/generators/rails/app/templates/bin/update.tt @@ -16,10 +16,6 @@ chdir APP_ROOT do puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') -<% if options[:yarn] %> - system! 'bin/yarn' - -<% end %> <% unless options.skip_active_record -%> puts "\n== Updating database ==" system! 'bin/rails db:migrate' diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn index cf48e2e5b7..872438cecb 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/yarn +++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn @@ -1,2 +1,9 @@ VENDOR_PATH = File.expand_path('../vendor', __dir__) -Dir.chdir(VENDOR_PATH) { exec "yarnpkg #{ARGV.join(" ")}" } +Dir.chdir(VENDOR_PATH) do + begin + exec "yarnpkg #{ARGV.join(" ")}" + rescue Errno::ENOENT + puts "Yarn executable was not detected in the system." + puts "Download Yarn at https://yarnpkg.com/en/docs/install" + end +end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt index 29d9df6ad4..f5d03fb117 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt @@ -5,7 +5,7 @@ Rails.application.config.assets.version = '1.0' # Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path -<%- if options[:yarn] -%> +<%- unless options[:skip_yarn] -%> # Add Yarn node_modules folder to the asset load path. Rails.application.config.assets.paths << Rails.root.join('vendor/node_modules') <%- end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore index dee42ebb87..709b341387 100644 --- a/railties/lib/rails/generators/rails/app/templates/gitignore +++ b/railties/lib/rails/generators/rails/app/templates/gitignore @@ -21,7 +21,7 @@ !/tmp/.keep <% end -%> -<% if options[:yarn] -%> +<% unless options[:skip_yarn] -%> /vendor/node_modules <% end -%> diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb index 967e969f81..3a48c4c496 100644 --- a/railties/lib/rails/source_annotation_extractor.rb +++ b/railties/lib/rails/source_annotation_extractor.rb @@ -116,7 +116,7 @@ class SourceAnnotationExtractor # Otherwise it returns an empty hash. def extract_annotations_from(file, pattern) lineno = 0 - result = File.readlines(file).inject([]) do |list, line| + result = File.readlines(file, encoding: Encoding::BINARY).inject([]) do |list, line| lineno += 1 next list unless line =~ pattern list << Annotation.new(lineno, $1, $2) diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb index 7069a07bbe..c54d9cc599 100644 --- a/railties/test/generators/api_app_generator_test.rb +++ b/railties/test/generators/api_app_generator_test.rb @@ -105,10 +105,10 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase %w(app/assets app/helpers app/views/layouts/application.html.erb + bin/yarn config/initializers/assets.rb config/initializers/cookies_serializer.rb lib/assets - vendor/assets test/helpers tmp/cache/assets public/404.html @@ -116,6 +116,8 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase public/500.html public/apple-touch-icon-precomposed.png public/apple-touch-icon.png - public/favicon.ico) + public/favicon.icon + vendor/package.json + ) end end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 15a923676d..9285a9f091 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -43,9 +43,6 @@ DEFAULT_APP_FILES = %w( test/mailers test/integration vendor - vendor/assets - vendor/assets/stylesheets - vendor/assets/javascripts tmp tmp/cache tmp/cache/assets @@ -466,7 +463,6 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--skip-javascript"] assert_no_file "app/assets/javascripts" - assert_no_file "vendor/assets/javascripts" assert_file "app/views/layouts/application.html.erb" do |contents| assert_match(/stylesheet_link_tag\s+'application', media: 'all' %>/, contents) @@ -492,12 +488,21 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_if_yarn_option_is_given - run_generator([destination_root, "--yarn"]) + def test_generator_for_yarn + run_generator([destination_root]) assert_file "vendor/package.json", /dependencies/ assert_file "config/initializers/assets.rb", /node_modules/ end + def test_generator_for_yarn_skipped + run_generator([destination_root, "--skip-yarn"]) + assert_no_file "vendor/package.json" + + assert_file "config/initializers/assets.rb" do |content| + assert_no_match(/node_modules/, content) + end + end + def test_inclusion_of_jbuilder run_generator assert_gem "jbuilder" @@ -618,10 +623,6 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_generates_with_bundler end - def test_generation_runs_yarn_install_with_yarn_option - assert_generates_with_yarn yarn: true - end - def test_dev_option assert_generates_with_bundler dev: true rails_path = File.expand_path("../../..", Rails.root) @@ -740,7 +741,6 @@ class AppGeneratorTest < Rails::Generators::TestCase test/helpers test/integration tmp - vendor/assets/stylesheets ) folders_with_keep.each do |folder| assert_file("#{folder}/.keep") @@ -847,18 +847,4 @@ class AppGeneratorTest < Rails::Generators::TestCase quietly { generator.invoke_all } end end - - def assert_generates_with_yarn(options = {}) - generator([destination_root], options) - - command_check = -> command do - @install_called ||= 0 - @install_called += 1 - assert_equal 1, @install_called, "install expected to be called once, but was called #{@install_called} times" - end - - generator.stub :yarn_command, command_check do - quietly { generator.invoke_all } - end - end end |