diff options
33 files changed, 399 insertions, 295 deletions
diff --git a/.travis.yml b/.travis.yml index 95a9099901..6c7380f050 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,7 +114,8 @@ notifications: on_success: change on_failure: always channels: - - "irc.freenode.org#rails-contrib" + # "irc.freenode.org#rails-contrib" + - secure: "QFKSOK7xQiWWqTzYfYm0XWoW7idzuxT57MBW9i9EASyRLEPuDwZEubKRP40Y7wPx7ylQd9lp6kJheeLnrDvvTjFbW3sWv9GDRl4WlOU8sG/Kv7MXAASXlDqzyJxxXTtzLeXz2iwY296kOBuKxKxl923eTvEGeocwH02QGo14LpQ=" campfire: on_success: change on_failure: always @@ -40,7 +40,7 @@ gem "rubocop", ">= 0.47", require: false gem "rb-inotify", github: "matthewd/rb-inotify", branch: "close-handling", require: false group :doc do - gem "sdoc", "> 1.0.0.rc1", "< 2.0" + gem "sdoc", github: "robin850/sdoc", branch: "upgrade" gem "redcarpet", "~> 3.2.3", platforms: :ruby gem "w3c_validators" gem "kindlerb", "~> 1.2.0" diff --git a/Gemfile.lock b/Gemfile.lock index 2096465a8b..516b4155c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,6 +41,14 @@ GIT sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) +GIT + remote: https://github.com/robin850/sdoc.git + revision: 0e340352f3ab2f196c8a8743f83c2ee286e4f71c + branch: upgrade + specs: + sdoc (1.0.0.rc2) + rdoc (~> 5.0) + PATH remote: . specs: @@ -135,7 +143,7 @@ GEM bcrypt (3.1.11-x86-mingw32) beaneater (1.0.0) benchmark-ips (2.7.2) - blade (0.7.0) + blade (0.7.1) activesupport (>= 3.0.0) blade-qunit_adapter (~> 2.0.1) coffee-script @@ -145,7 +153,7 @@ GEM faye sprockets (>= 3.0) thin (>= 1.6.0) - thor (~> 0.19.1) + thor (>= 0.19.1) useragent (~> 0.16.7) blade-qunit_adapter (2.0.1) blade-sauce_labs_plugin (0.7.2) @@ -389,8 +397,6 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sdoc (1.0.0.rc2) - rdoc (~> 5.0) selenium-webdriver (3.5.1) childprocess (~> 0.5) rubyzip (~> 1.0) @@ -437,7 +443,7 @@ GEM daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (0.19.4) + thor (0.20.0) thread (0.1.7) thread_safe (0.3.6) tilt (2.0.8) @@ -521,7 +527,7 @@ DEPENDENCIES resque-scheduler rubocop (>= 0.47) sass-rails! - sdoc (> 1.0.0.rc1, < 2.0) + sdoc! sequel sidekiq sneakers diff --git a/actioncable/lib/action_cable/subscription_adapter/redis.rb b/actioncable/lib/action_cable/subscription_adapter/redis.rb index c64f55f5b7..facea944ff 100644 --- a/actioncable/lib/action_cable/subscription_adapter/redis.rb +++ b/actioncable/lib/action_cable/subscription_adapter/redis.rb @@ -10,7 +10,7 @@ module ActionCable class Redis < Base # :nodoc: prepend ChannelPrefix - # Overwrite this factory method for redis connections if you want to use a different Redis library than Redis. + # Overwrite this factory method for Redis connections if you want to use a different Redis library than the redis gem. # This is needed, for example, when using Makara proxies for distributed Redis. cattr_accessor :redis_connector, default: ->(config) do ::Redis.new(config.slice(:url, :host, :port, :db, :password)) diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb index 9629876a6d..f02a32a38e 100644 --- a/activejob/test/support/integration/test_case_helpers.rb +++ b/activejob/test/support/integration/test_case_helpers.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "active_support/core_ext/string/inflections" require "support/integration/jobs_manager" module TestCaseHelpers @@ -30,8 +29,7 @@ module TestCaseHelpers end def adapter_is?(*adapter_class_symbols) - adapter = ActiveJob::Base.queue_adapter.class.name.demodulize.chomp("Adapter").underscore - adapter_class_symbols.map(&:to_s).include? adapter + adapter_class_symbols.map(&:to_s).include? ActiveJob::Base.queue_adapter_name end def wait_for_jobs_to_finish_for(seconds = 60) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 5febb5a59f..4078345abf 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -10,6 +10,11 @@ module ActiveRecord # Converts an arel AST to SQL def to_sql(arel_or_sql_string, binds = []) + sql, _ = to_sql_and_binds(arel_or_sql_string, binds) + sql + end + + def to_sql_and_binds(arel_or_sql_string, binds = []) # :nodoc: if arel_or_sql_string.respond_to?(:ast) unless binds.empty? raise "Passing bind parameters with an arel AST is forbidden. " \ @@ -21,6 +26,7 @@ module ActiveRecord [arel_or_sql_string.dup.freeze, binds] end end + private :to_sql_and_binds # This is used in the StatementCache object. It returns an object that # can be used to query the database repeatedly. @@ -39,7 +45,7 @@ module ActiveRecord # Returns an ActiveRecord::Result instance. def select_all(arel, name = nil, binds = [], preparable: nil) arel = arel_from_relation(arel) - sql, binds = to_sql(arel, binds) + sql, binds = to_sql_and_binds(arel, binds) if !prepared_statements || (arel.is_a?(String) && preparable.nil?) preparable = false else @@ -138,22 +144,22 @@ module ActiveRecord # # If the next id was calculated in advance (as in Oracle), it should be # passed in as +id_value+. - def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil) - sql, binds = to_sql(arel) + def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) + sql, binds = to_sql_and_binds(arel, binds) value = exec_insert(sql, name, binds, pk, sequence_name) id_value || last_inserted_id(value) end alias create insert # Executes the update statement and returns the number of rows affected. - def update(arel, name = nil) - sql, binds = to_sql(arel) + def update(arel, name = nil, binds = []) + sql, binds = to_sql_and_binds(arel, binds) exec_update(sql, name, binds) end # Executes the delete statement and returns the number of rows affected. - def delete(arel, name = nil) - sql, binds = to_sql(arel) + def delete(arel, name = nil, binds = []) + sql, binds = to_sql_and_binds(arel, binds) exec_delete(sql, name, binds) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 41d93c4322..25622e34c8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -95,7 +95,7 @@ module ActiveRecord def select_all(arel, name = nil, binds = [], preparable: nil) if @query_cache_enabled && !locked?(arel) arel = arel_from_relation(arel) - sql, binds = to_sql(arel, binds) + sql, binds = to_sql_and_binds(arel, binds) cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) } else super 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 8a9c497918..5915f0db2d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -177,8 +177,7 @@ module ActiveRecord #++ def explain(arel, binds = []) - sql, binds = to_sql(arel, binds) - sql = "EXPLAIN #{sql}" + sql = "EXPLAIN #{to_sql(arel, binds)}" start = Time.now result = exec_query(sql, "EXPLAIN", binds) elapsed = Time.now - start diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 0dd4aac463..8db2a645af 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -5,8 +5,7 @@ module ActiveRecord module PostgreSQL module DatabaseStatements def explain(arel, binds = []) - sql, binds = to_sql(arel, binds) - sql = "EXPLAIN #{sql}" + sql = "EXPLAIN #{to_sql(arel, binds)}" PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds)) end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 8c12cb09bd..10e80179ac 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -203,8 +203,7 @@ module ActiveRecord #++ def explain(arel, binds = []) - sql, binds = to_sql(arel, binds) - sql = "EXPLAIN QUERY PLAN #{sql}" + sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}" SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", [])) end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index caabad6055..d319978930 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -571,8 +571,7 @@ module ActiveRecord conn = klass.connection conn.unprepared_statement { - sql, _ = conn.to_sql(relation.arel) - sql + conn.to_sql(relation.arel) } end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 27a1c89bd1..fd644225ca 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -243,7 +243,9 @@ HEADER end def remove_prefix_and_suffix(table) - table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2") + prefix = Regexp.escape(@options[:table_name_prefix].to_s) + suffix = Regexp.escape(@options[:table_name_suffix].to_s) + table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1") end def ignored?(table_name) diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 6e04578576..d7551cd658 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -227,6 +227,38 @@ module ActiveRecord result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new(nil).to_sql}", nil, [[nil, post.id]]) assert_equal expected.to_hash, result.to_hash end + + def test_insert_update_delete_with_legacy_binds + binds = [[nil, 1]] + bind_param = Arel::Nodes::BindParam.new(nil) + + id = @connection.insert("INSERT INTO events(id) VALUES (#{bind_param.to_sql})", nil, nil, nil, nil, binds) + assert_equal 1, id + + @connection.update("UPDATE events SET title = 'foo' WHERE id = #{bind_param.to_sql}", nil, binds) + result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds) + assert_equal({ "id" => 1, "title" => "foo" }, result.first) + + @connection.delete("DELETE FROM events WHERE id = #{bind_param.to_sql}", nil, binds) + result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds) + assert_nil result.first + end + + def test_insert_update_delete_with_binds + binds = [Relation::QueryAttribute.new("id", 1, Type.default_value)] + bind_param = Arel::Nodes::BindParam.new(nil) + + id = @connection.insert("INSERT INTO events(id) VALUES (#{bind_param.to_sql})", nil, nil, nil, nil, binds) + assert_equal 1, id + + @connection.update("UPDATE events SET title = 'foo' WHERE id = #{bind_param.to_sql}", nil, binds) + result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds) + assert_equal({ "id" => 1, "title" => "foo" }, result.first) + + @connection.delete("DELETE FROM events WHERE id = #{bind_param.to_sql}", nil, binds) + result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds) + assert_nil result.first + end end def test_select_methods_passing_a_association_relation diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 383c7314e1..6bd11a5d81 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -576,8 +576,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_taking_with_a_number + klass = Class.new(Author) do + has_many :posts, -> { order(:id) } + + def self.name + "Author" + end + end + # taking from unloaded Relation - bob = Author.find(authors(:bob).id) + bob = klass.find(authors(:bob).id) new_post = bob.posts.build assert_not bob.posts.loaded? assert_equal [posts(:misc_by_bob)], bob.posts.take(1) diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb index 9200d32caf..be6dc2acb1 100644 --- a/activerecord/test/cases/migration/column_attributes_test.rb +++ b/activerecord/test/cases/migration/column_attributes_test.rb @@ -45,11 +45,11 @@ module ActiveRecord assert_nil TestModel.columns_hash["description"].limit end - if current_adapter?(:Mysql2Adapter) + if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter) def test_unabstracted_database_dependent_types - add_column :test_models, :intelligence_quotient, :tinyint + add_column :test_models, :intelligence_quotient, :smallint TestModel.reset_column_information - assert_match(/tinyint/, TestModel.columns_hash["intelligence_quotient"].sql_type) + assert_match(/smallint/, TestModel.columns_hash["intelligence_quotient"].sql_type) end end @@ -99,7 +99,21 @@ module ActiveRecord assert_equal 7, wealth_column.scale end + # Test SQLite3 adapter specifically for decimal types with precision and scale + # attributes, since these need to be maintained in schema but aren't actually + # used in SQLite3 itself if current_adapter?(:SQLite3Adapter) + def test_change_column_with_new_precision_and_scale + connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7 + + connection.change_column "test_models", "wealth", :decimal, precision: 12, scale: 8 + TestModel.reset_column_information + + wealth_column = TestModel.columns_hash["wealth"] + assert_equal 12, wealth_column.precision + assert_equal 8, wealth_column.scale + end + def test_change_column_preserve_other_column_precision_and_scale connection.add_column "test_models", "last_name", :string connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7 diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index c887f54560..170fd02b6f 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -78,6 +78,16 @@ class PersistenceTest < ActiveRecord::TestCase assert_equal "2 updated", Topic.find(2).content end + def test_class_level_update_is_affected_by_scoping + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } + + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + end + assert_not_equal "1 updated", Topic.find(1).content + assert_not_equal "2 updated", Topic.find(2).content + end + def test_delete_all assert Topic.count > 0 @@ -912,13 +922,33 @@ class PersistenceTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) } end + def test_class_level_destroy_is_affected_by_scoping + should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") + Topic.find(1).replies << should_not_be_destroyed_reply + + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.destroy(1) } + end + assert_nothing_raised { Topic.find(1) } + assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) } + end + def test_class_level_delete - should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") - Topic.find(1).replies << should_be_destroyed_reply + should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") + Topic.find(1).replies << should_not_be_destroyed_reply Topic.delete(1) assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } - assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) } + assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) } + end + + def test_class_level_delete_is_affected_by_scoping + should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") + Topic.find(1).replies << should_not_be_destroyed_reply + + Topic.where("1=0").scoping { Topic.delete(1) } + assert_nothing_raised { Topic.find(1) } + assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) } end def test_create_with_custom_timestamps diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 2fe2a67b1c..d95a54a2fe 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -109,6 +109,15 @@ module ActiveRecord assert_equal expected.to_sql, actual.to_sql end + def test_polymorphic_shallow_where_not + treasure = treasures(:sapphire) + + expected = [price_estimates(:diamond), price_estimates(:honda)] + actual = PriceEstimate.where.not(estimate_of: treasure) + + assert_equal expected.sort_by(&:id), actual.sort_by(&:id) + end + def test_polymorphic_nested_array_where treasure = Treasure.new treasure.id = 1 @@ -121,6 +130,16 @@ module ActiveRecord assert_equal expected.to_sql, actual.to_sql end + def test_polymorphic_nested_array_where_not + treasure = treasures(:diamond) + car = cars(:honda) + + expected = [price_estimates(:sapphire_1), price_estimates(:sapphire_2)] + actual = PriceEstimate.where.not(estimate_of: [treasure, car]) + + assert_equal expected.sort_by(&:id), actual.sort_by(&:id) + end + def test_polymorphic_array_where_multiple_types treasure_1 = treasures(:diamond) treasure_2 = treasures(:sapphire) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 01ec3e06ad..eb9b257da9 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -400,6 +400,31 @@ class SchemaDumperTest < ActiveRecord::TestCase $stdout = original end + def test_schema_dump_with_table_name_prefix_and_suffix_regexp_escape + original, $stdout = $stdout, StringIO.new + ActiveRecord::Base.table_name_prefix = "foo$" + ActiveRecord::Base.table_name_suffix = "$bar" + + migration = CreateDogMigration.new + migration.migrate(:up) + + output = perform_schema_dump + assert_no_match %r{create_table "foo\$.+\$bar"}, output + assert_no_match %r{add_index "foo\$.+\$bar"}, output + assert_no_match %r{create_table "schema_migrations"}, output + assert_no_match %r{create_table "ar_internal_metadata"}, output + + if ActiveRecord::Base.connection.supports_foreign_keys? + assert_no_match %r{add_foreign_key "foo\$.+\$bar"}, output + assert_no_match %r{add_foreign_key "[^"]+", "foo\$.+\$bar"}, output + end + ensure + migration.migrate(:down) + + ActiveRecord::Base.table_name_suffix = ActiveRecord::Base.table_name_prefix = "" + $stdout = original + end + def test_schema_dump_with_table_name_prefix_and_ignoring_tables original, $stdout = $stdout, StringIO.new diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index ba0bc6c45d..5d3e2a175c 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -446,17 +446,6 @@ class TimestampTest < ActiveRecord::TestCase toy = Toy.first assert_equal ["created_at", "updated_at"], toy.send(:all_timestamp_attributes_in_model) end - - def test_index_is_created_for_both_timestamps - ActiveRecord::Base.connection.create_table(:foos, force: true) do |t| - t.timestamps null: true, index: true - end - - indexes = ActiveRecord::Base.connection.indexes("foos") - assert_equal ["created_at", "updated_at"], indexes.flat_map(&:columns).sort - ensure - ActiveRecord::Base.connection.drop_table(:foos) - end end class TimestampsWithoutTransactionTest < ActiveRecord::TestCase @@ -475,4 +464,15 @@ class TimestampsWithoutTransactionTest < ActiveRecord::TestCase assert_nil post.updated_at end end + + def test_index_is_created_for_both_timestamps + ActiveRecord::Base.connection.create_table(:foos, force: true) do |t| + t.timestamps null: true, index: true + end + + indexes = ActiveRecord::Base.connection.indexes("foos") + assert_equal ["created_at", "updated_at"], indexes.flat_map(&:columns).sort + ensure + ActiveRecord::Base.connection.drop_table(:foos) + end end diff --git a/activestorage/.codeclimate.yml b/activestorage/.codeclimate.yml deleted file mode 100644 index 18f3dbb7b5..0000000000 --- a/activestorage/.codeclimate.yml +++ /dev/null @@ -1,7 +0,0 @@ -engines: - rubocop: - enabled: true - -ratings: - paths: - - "**.rb" diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb index 1c50d18994..99f2fb20b0 100644 --- a/activestorage/test/models/blob_test.rb +++ b/activestorage/test/models/blob_test.rb @@ -41,6 +41,13 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase end end + test "purge removes from external service" do + blob = create_blob + + blob.purge + assert_not ActiveStorage::Blob.service.exist?(blob.key) + end + private def expected_url_for(blob, disposition: :inline) query_string = { content_type: blob.content_type, disposition: disposition }.to_param diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb index 0bb3a925b8..2a969fa326 100644 --- a/activestorage/test/test_helper.rb +++ b/activestorage/test/test_helper.rb @@ -32,7 +32,7 @@ end require "tmpdir" ActiveStorage::Blob.service = ActiveStorage::Service::DiskService.new(root: Dir.mktmpdir("active_storage_tests")) -ActiveStorage::Service.logger = ActiveSupport::Logger.new(STDOUT) +ActiveStorage::Service.logger = ActiveSupport::Logger.new(nil) ActiveStorage.verifier = ActiveSupport::MessageVerifier.new("Testing") diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb index f92a2f1c2a..9bc50b7bc6 100644 --- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb @@ -21,20 +21,14 @@ class Hash # Same as +deep_merge+, but modifies +self+. def deep_merge!(other_hash, &block) - other_hash.each_pair do |current_key, other_value| - this_value = self[current_key] - - self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash) - this_value.deep_merge(other_value, &block) + merge!(other_hash) do |key, this_val, other_val| + if this_val.is_a?(Hash) && other_val.is_a?(Hash) + this_val.deep_merge(other_val, &block) + elsif block_given? + block.call(key, this_val, other_val) else - if block_given? && key?(current_key) - block.call(current_key, this_value, other_value) - else - other_value - end + other_val end end - - self end end diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index c124416595..dc8080c469 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -40,7 +40,7 @@ module ActiveSupport # * <tt>:run_once</tt> - Given +block+ will run only once. def on_load(name, options = {}, &block) @loaded[name].each do |base| - execute_hook(base, options, block) + execute_hook(name, base, options, block) end @load_hooks[name] << [block, options] @@ -49,7 +49,7 @@ module ActiveSupport def run_load_hooks(name, base = Object) @loaded[name] << base @load_hooks[name].each do |hook, options| - execute_hook(base, options, hook) + execute_hook(name, base, options, hook) end end @@ -63,7 +63,7 @@ module ActiveSupport end end - def execute_hook(base, options, block) + def execute_hook(name, base, options, block) with_execution_control(name, block, options[:run_once]) do if options[:yield] block.call(base) diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb index c161005100..721d44d0c1 100644 --- a/activesupport/test/lazy_load_hooks_test.rb +++ b/activesupport/test/lazy_load_hooks_test.rb @@ -22,14 +22,19 @@ class LazyLoadHooksTest < ActiveSupport::TestCase def test_basic_hook_with_two_registrations_only_once i = 0 - ActiveSupport.on_load(:basic_hook_with_two_once, run_once: true) do + block = proc { i += incr } + ActiveSupport.on_load(:basic_hook_with_two_once, run_once: true, &block) + ActiveSupport.on_load(:basic_hook_with_two_once) do i += incr end - assert_equal 0, i - ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(2)) + + ActiveSupport.on_load(:different_hook, run_once: true, &block) + ActiveSupport.run_load_hooks(:different_hook, FakeContext.new(2)) assert_equal 2, i + ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(2)) + assert_equal 6, i ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(5)) - assert_equal 2, i + assert_equal 11, i end def test_hook_registered_after_run diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index a086363cf1..e9157f3db1 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -892,7 +892,7 @@ Conditional Validation Sometimes it will make sense to validate an object only when a given predicate is satisfied. You can do that by using the `:if` and `:unless` options, which -can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if` +can take a symbol, a `Proc` or an `Array`. You may use the `:if` option when you want to specify when the validation **should** happen. If you want to specify when the validation **should not** happen, then you may use the `:unless` option. diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index c57efd6362..77b036b20f 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -21,24 +21,25 @@ The easiest and recommended way to get a development environment ready to hack i The Hard Way ------------ -In case you can't use the Rails development box, see section below, these are the steps to manually build a development box for Ruby on Rails core development. +In case you can't use the Rails development box, see the steps below to manually +build a development box for Ruby on Rails core development. ### Install Git -Ruby on Rails uses Git for source code control. The [Git homepage](http://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git: +Ruby on Rails uses Git for source code control. The [Git homepage](https://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git: -* [Try Git course](http://try.github.io/) is an interactive course that will teach you the basics. -* The [official Documentation](http://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git. +* [Try Git course](https://try.github.io/) is an interactive course that will teach you the basics. +* The [official Documentation](https://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git. * [Everyday Git](http://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by. -* [GitHub](http://help.github.com) offers links to a variety of Git resources. -* [Pro Git](http://git-scm.com/book) is an entire book about Git with a Creative Commons license. +* [GitHub](https://help.github.com/) offers links to a variety of Git resources. +* [Pro Git](https://git-scm.com/book) is an entire book about Git with a Creative Commons license. ### Clone the Ruby on Rails Repository Navigate to the folder where you want the Ruby on Rails source code (it will create its own `rails` subdirectory) and run: ```bash -$ git clone git://github.com/rails/rails.git +$ git clone https://github.com/rails/rails.git $ cd rails ``` @@ -62,7 +63,7 @@ $ sudo apt-get install sqlite3 libsqlite3-dev If you are on Fedora or CentOS, you're done with ```bash -$ sudo yum install libsqlite3x libsqlite3x-devel +$ sudo yum install libsqlite3x libsqlite3x-devel ``` If you are on Arch Linux, you will need to run: @@ -96,7 +97,7 @@ This command will install all dependencies except the MySQL and PostgreSQL Ruby NOTE: If you would like to run the tests that use memcached, you need to ensure that you have it installed and running. -You can use [Homebrew](http://brew.sh/) to install memcached on OS X: +You can use [Homebrew](https://brew.sh/) to install memcached on macOS: ```bash $ brew install memcached @@ -181,7 +182,7 @@ The Active Record test suite requires a custom config file: `activerecord/test/c To be able to run the suite for MySQL and PostgreSQL we need their gems. Install first the servers, their client libraries, and their development files. -On OS X, you can run: +On macOS, you can run: ```bash $ brew install mysql @@ -190,7 +191,7 @@ $ brew install postgresql Follow the instructions given by Homebrew to start these. -In Ubuntu just run: +On Ubuntu, just run: ```bash $ sudo apt-get install mysql-server libmysqlclient-dev @@ -260,34 +261,34 @@ with your development account, on Linux or BSD, you just have to run: $ sudo -u postgres createuser --superuser $USER ``` -and for OS X: +and for macOS: ```bash $ createuser --superuser $USER ``` -Then you need to create the test databases with +Then, you need to create the test databases with: ```bash $ cd activerecord $ bundle exec rake db:postgresql:build ``` -It is possible to build databases for both PostgreSQL and MySQL with +It is possible to build databases for both PostgreSQL and MySQL with: ```bash $ cd activerecord $ bundle exec rake db:create ``` -You can cleanup the databases using +You can cleanup the databases using: ```bash $ cd activerecord $ bundle exec rake db:drop ``` -NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation. +NOTE: Using the Rake task to create the test databases ensures they have the correct character set and collation. NOTE: You'll see the following warning (or localized warning) during activating HStore extension in PostgreSQL 9.1.x or earlier: "WARNING: => is deprecated as an operator". @@ -299,11 +300,11 @@ Action Cable uses Redis as its default subscriptions adapter ([read more](action #### Install Redis From Source -Redis' documentation discourage installations with package managers as those are usually outdated. Installing from source and bringing the server up is straight forward and well documented on [Redis' documentation](http://redis.io/download#installation). +Redis' documentation discourage installations with package managers as those are usually outdated. Installing from source and bringing the server up is straight forward and well documented on [Redis' documentation](https://redis.io/download#installation). #### Install Redis From Package Manager -On OS X, you can run: +On macOS, you can run: ```bash $ brew install redis @@ -311,7 +312,7 @@ $ brew install redis Follow the instructions given by Homebrew to start these. -In Ubuntu just run: +On Ubuntu, just run: ```bash $ sudo apt-get install redis-server @@ -323,7 +324,7 @@ On Fedora or CentOS (requires EPEL enabled), just run: $ sudo yum install redis ``` -If you are running Arch Linux just run: +If you are running Arch Linux, just run: ```bash $ sudo pacman -S redis @@ -335,3 +336,43 @@ FreeBSD users will have to run the following: ```bash # portmaster databases/redis ``` + +### Active Storage Setup + +When working on Active Storage, it is important to note that you need to +install its JavaScript dependencies while working on that section of the +codebase. In order to install these dependencies, it is necessary to +have Yarn, a Node.js package manager, available on your system. A +prerequisite for installing this package manager is that +[Node.js](https://nodejs.org) is installed. + + +On macOS, you can run: + +```bash +brew install yarn +``` + +On Ubuntu, you can run: + +```bash +curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + +sudo apt-get update && sudo apt-get install yarn +``` + +On Fedora or CentOS, just run: + +``bash +sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo + +sudo yum install yarn +``` + +Finally, after installing Yarn, you will need to run the following +command inside of the `activestorage` directory to install the dependencies: + +```bash +yarn install +``` diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 49a0584d51..651b86275a 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -397,6 +397,15 @@ When using Ruby 2.4, you can preserve the timezone of the receiver when calling ActiveSupport.to_time_preserves_timezone = false +### Changes with JSON/JSONB serialization + +In Rails 5.0, how JSON/JSONB attributes are serialized and deserialized changed. Now, if +you set a column equal to a `String`, Active Record will no longer turn that string +into a `Hash`, and will instead only return the string. This is not limited to code +interacting with models, but also affects `:default` column settings in `db/schema.rb`. +It is recommended that you do not set columns equal to a `String`, but pass a `Hash` +instead, which will be converted to and from a JSON string automatically. + Upgrading from Rails 4.1 to Rails 4.2 ------------------------------------- diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore index 8c7cad74ed..e15863d860 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/gitignore +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore @@ -2,8 +2,10 @@ log/*.log pkg/ <% if with_dummy_app? -%> +<% if sqlite3? -%> <%= dummy_path %>/db/*.sqlite3 <%= dummy_path %>/db/*.sqlite3-journal +<% end -%> <%= dummy_path %>/log/*.log <% unless options[:skip_yarn] -%> <%= dummy_path %>/node_modules/ diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb index a0bab22764..2fa05a9146 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb @@ -17,6 +17,7 @@ Minitest.backtrace_filter = Minitest::BacktraceFilter.new Rails::TestUnitReporter.executable = 'bin/test' <% end -%> +<% unless options[:skip_active_record] -%> # Load fixtures from the engine if ActiveSupport::TestCase.respond_to?(:fixture_path=) ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__) @@ -24,3 +25,4 @@ if ActiveSupport::TestCase.respond_to?(:fixture_path=) ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" ActiveSupport::TestCase.fixtures :all end +<% end -%> diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index b50b9364d0..f64ebf5f1f 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -381,31 +381,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_without_skips - run_generator - assert_file "config/application.rb", /\s+require\s+["']rails\/all["']/ - assert_file "config/environments/development.rb" do |content| - assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content) - end - assert_file "config/environments/test.rb" do |content| - assert_match(/config\.action_mailer\.delivery_method = :test/, content) - end - assert_file "config/environments/production.rb" do |content| - assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content) - assert_match(/^ config\.read_encrypted_secrets = true/, content) - end - end - - def test_default_frameworks_are_required_when_others_are_removed - run_generator [destination_root, "--skip-active-record", "--skip-action-mailer", "--skip-action-cable", "--skip-sprockets", "--skip-test"] - assert_file "config/application.rb", /require\s+["']rails["']/ - assert_file "config/application.rb", /require\s+["']active_model\/railtie["']/ - assert_file "config/application.rb", /require\s+["']active_job\/railtie["']/ - assert_file "config/application.rb", /require\s+["']action_controller\/railtie["']/ - assert_file "config/application.rb", /require\s+["']action_view\/railtie["']/ - assert_file "config/application.rb", /require\s+["']active_storage\/engine["']/ - end - def test_generator_defaults_to_puma_version run_generator [destination_root] assert_gem "puma", "'~> 3.7'" @@ -419,39 +394,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_if_skip_active_record_is_given - run_generator [destination_root, "--skip-active-record"] - assert_no_directory "db/" - assert_no_file "config/database.yml" - assert_no_file "app/models/application_record.rb" - assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ - assert_file "test/test_helper.rb" do |helper_content| - assert_no_match(/fixtures :all/, helper_content) - end - assert_file "bin/setup" do |setup_content| - assert_no_match(/db:setup/, setup_content) - end - assert_file "bin/update" do |update_content| - assert_no_match(/db:migrate/, update_content) - end - end - - def test_generator_if_skip_action_mailer_is_given - run_generator [destination_root, "--skip-action-mailer"] - assert_file "config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ - assert_file "config/environments/development.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "config/environments/test.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "config/environments/production.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_no_directory "app/mailers" - assert_no_directory "test/mailers" - end - def test_generator_has_assets_gems run_generator @@ -459,43 +401,6 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_gem "uglifier" end - def test_generator_if_skip_sprockets_is_given - run_generator [destination_root, "--skip-sprockets"] - - assert_no_file "config/initializers/assets.rb" - - assert_file "config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/ - - assert_file "Gemfile" do |content| - assert_no_match(/sass-rails/, content) - assert_no_match(/uglifier/, content) - assert_no_match(/coffee-rails/, content) - end - - assert_file "config/environments/development.rb" do |content| - assert_no_match(/config\.assets\.debug/, content) - end - - assert_file "config/environments/production.rb" do |content| - assert_no_match(/config\.assets\.digest/, content) - assert_no_match(/config\.assets\.js_compressor/, content) - assert_no_match(/config\.assets\.css_compressor/, content) - assert_no_match(/config\.assets\.compile/, content) - end - end - - def test_generator_if_skip_action_cable_is_given - run_generator [destination_root, "--skip-action-cable"] - assert_file "config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ - assert_no_file "config/cable.yml" - assert_no_file "app/assets/javascripts/cable.js" - assert_no_directory "app/assets/javascripts/channels" - assert_no_directory "app/channels" - assert_file "Gemfile" do |content| - assert_no_match(/redis/, content) - end - end - def test_action_cable_redis_gems run_generator assert_file "Gemfile", /^# gem 'redis'/ @@ -770,30 +675,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_gitignore_when_sqlite3 - run_generator - - assert_file ".gitignore" do |content| - assert_match(/sqlite3/, content) - end - end - - def test_gitignore_when_no_active_record - run_generator [destination_root, "--skip-active-record"] - - assert_file ".gitignore" do |content| - assert_no_match(/sqlite/i, content) - end - end - - def test_gitignore_when_non_sqlite3_db - run_generator([destination_root, "-d", "mysql"]) - - assert_file ".gitignore" do |content| - assert_no_match(/sqlite/i, content) - end - end - def test_inclusion_of_ruby_version run_generator diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index e94b1ac8fe..38130ceb68 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -129,31 +129,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase end end - def test_generating_adds_dummy_app_without_sprockets - run_generator [destination_root, "--skip-sprockets"] - - assert_no_file "test/dummy/config/initializers/assets.rb" - - assert_file "test/dummy/config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/ - - assert_file "Gemfile" do |content| - assert_no_match(/sass-rails/, content) - assert_no_match(/uglifier/, content) - assert_no_match(/coffee-rails/, content) - end - - assert_file "test/dummy/config/environments/development.rb" do |content| - assert_no_match(/config\.assets\.debug/, content) - end - - assert_file "test/dummy/config/environments/production.rb" do |content| - assert_no_match(/config\.assets\.digest/, content) - assert_no_match(/config\.assets\.js_compressor/, content) - assert_no_match(/config\.assets\.css_compressor/, content) - assert_no_match(/config\.assets\.compile/, content) - end - end - def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files run_generator [destination_root, "-T", "--mountable", "--dummy-path", "my_dummy_app"] assert_file "Rakefile", /APP_RAKEFILE/ @@ -202,69 +177,13 @@ class PluginGeneratorTest < Rails::Generators::TestCase end end - def test_app_generator_without_skips - run_generator - assert_file "test/dummy/config/application.rb", /\s+require\s+["']rails\/all["']/ - assert_file "test/dummy/config/environments/development.rb" do |content| - assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content) - end - assert_file "test/dummy/config/environments/test.rb" do |content| - assert_match(/config\.action_mailer\.delivery_method = :test/, content) - end - assert_file "test/dummy/config/environments/production.rb" do |content| - assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content) - assert_match(/^ config\.read_encrypted_secrets = true/, content) - end - end - - def test_default_frameworks_are_required_when_others_are_removed - run_generator [destination_root, "--skip-active-record", "--skip-action-mailer", "--skip-action-cable", "--skip-sprockets"] - assert_file "test/dummy/config/application.rb", /require\s+["']rails["']/ - assert_file "test/dummy/config/application.rb", /require\s+["']active_model\/railtie["']/ - assert_file "test/dummy/config/application.rb", /require\s+["']active_job\/railtie["']/ - assert_file "test/dummy/config/application.rb", /require\s+["']action_controller\/railtie["']/ - assert_file "test/dummy/config/application.rb", /require\s+["']action_view\/railtie["']/ - end - - def test_active_record_is_removed_from_frameworks_if_skip_active_record_is_given - run_generator [destination_root, "--skip-active-record"] - assert_file "test/dummy/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ - end - def test_ensure_that_skip_active_record_option_is_passed_to_app_generator run_generator [destination_root, "--skip_active_record"] - assert_no_file "test/dummy/config/database.yml" assert_file "test/test_helper.rb" do |contents| assert_no_match(/ActiveRecord/, contents) end end - def test_action_mailer_is_removed_from_frameworks_if_skip_action_mailer_is_given - run_generator [destination_root, "--skip-action-mailer"] - assert_file "test/dummy/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ - assert_file "test/dummy/config/environments/development.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "test/dummy/config/environments/test.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "test/dummy/config/environments/production.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_no_directory "test/dummy/app/mailers" - end - - def test_action_cable_is_removed_from_frameworks_if_skip_action_cable_is_given - run_generator [destination_root, "--skip-action-cable"] - assert_file "test/dummy/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ - assert_no_file "test/dummy/config/cable.yml" - assert_no_file "test/dummy/app/assets/javascripts/cable.js" - assert_no_directory "test/dummy/app/channels" - assert_file "Gemfile" do |content| - assert_no_match(/redis/, content) - end - end - def test_ensure_that_database_option_is_passed_to_app_generator run_generator [destination_root, "--database", "postgresql"] assert_file "test/dummy/config/database.yml", /postgres/ diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index 6c0775b50e..56c9b37e1b 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -128,6 +128,120 @@ module SharedGeneratorTests assert_no_file("app/models/concerns/.keep") end + def test_default_frameworks_are_required_when_others_are_removed + run_generator [destination_root, "--skip-active-record", "--skip-action-mailer", "--skip-action-cable", "--skip-sprockets"] + assert_file "#{application_path}/config/application.rb", /require\s+["']rails["']/ + assert_file "#{application_path}/config/application.rb", /require\s+["']active_model\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /require\s+["']active_job\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /require\s+["']action_controller\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /require\s+["']action_view\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /require\s+["']active_storage\/engine["']/ + end + + def test_generator_without_skips + run_generator + assert_file "#{application_path}/config/application.rb", /\s+require\s+["']rails\/all["']/ + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content) + end + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_match(/config\.action_mailer\.delivery_method = :test/, content) + end + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content) + assert_match(/^ config\.read_encrypted_secrets = true/, content) + end + end + + def test_gitignore_when_sqlite3 + run_generator + + assert_file ".gitignore" do |content| + assert_match(/sqlite3/, content) + end + end + + def test_gitignore_when_non_sqlite3_db + run_generator([destination_root, "-d", "mysql"]) + + assert_file ".gitignore" do |content| + assert_no_match(/sqlite/i, content) + end + end + + def test_generator_if_skip_active_record_is_given + run_generator [destination_root, "--skip-active-record"] + assert_no_directory "#{application_path}/db/" + assert_no_file "#{application_path}/config/database.yml" + assert_no_file "#{application_path}/app/models/application_record.rb" + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ + assert_file "test/test_helper.rb" do |helper_content| + assert_no_match(/fixtures :all/, helper_content) + end + assert_file "#{application_path}/bin/setup" do |setup_content| + assert_no_match(/db:setup/, setup_content) + end + assert_file "#{application_path}/bin/update" do |update_content| + assert_no_match(/db:migrate/, update_content) + end + assert_file ".gitignore" do |content| + assert_no_match(/sqlite/i, content) + end + end + + def test_generator_if_skip_action_mailer_is_given + run_generator [destination_root, "--skip-action-mailer"] + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_no_directory "#{application_path}/app/mailers" + assert_no_directory "#{application_path}/test/mailers" + end + + def test_generator_if_skip_action_cable_is_given + run_generator [destination_root, "--skip-action-cable"] + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ + assert_no_file "#{application_path}/config/cable.yml" + assert_no_file "#{application_path}/app/assets/javascripts/cable.js" + assert_no_directory "#{application_path}/app/assets/javascripts/channels" + assert_no_directory "#{application_path}/app/channels" + assert_file "Gemfile" do |content| + assert_no_match(/redis/, content) + end + end + + def test_generator_if_skip_sprockets_is_given + run_generator [destination_root, "--skip-sprockets"] + + assert_no_file "#{application_path}/config/initializers/assets.rb" + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/ + + assert_file "Gemfile" do |content| + assert_no_match(/sass-rails/, content) + assert_no_match(/uglifier/, content) + assert_no_match(/coffee-rails/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.assets\.debug/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.assets\.digest/, content) + assert_no_match(/config\.assets\.js_compressor/, content) + assert_no_match(/config\.assets\.css_compressor/, content) + assert_no_match(/config\.assets\.compile/, content) + end + end + def test_generator_for_yarn run_generator assert_file "#{application_path}/package.json", /dependencies/ |