diff options
42 files changed, 414 insertions, 138 deletions
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index a6fb0dbe1d..4ba2c26949 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -122,7 +122,7 @@ module AbstractController def _normalize_render(*args, &block) options = _normalize_args(*args, &block) #TODO: remove defined? when we restore AP <=> AV dependency - if defined?(request) && request.variant.present? + if defined?(request) && !request.nil? && request.variant.present? options[:variant] = request.variant end _normalize_options(options) diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb index 5ff4a658ad..a8c8d66682 100644 --- a/actionpack/lib/action_controller/renderer.rb +++ b/actionpack/lib/action_controller/renderer.rb @@ -1,7 +1,7 @@ require 'active_support/core_ext/hash/keys' module ActionController - # ActionController::Renderer allows to render arbitrary templates + # ActionController::Renderer allows you to render arbitrary templates # without requirement of being in controller actions. # # You get a concrete renderer class by invoking ActionController::Base#renderer. diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 8ff3b42a40..40b6500553 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -106,7 +106,7 @@ module ActionDispatch @ast = ast @anchor = anchor @via = via - @internal = options[:internal] + @internal = options.delete(:internal) path_params = ast.find_all(&:symbol?).map(&:to_sym) diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index f42efd35af..8d3134630d 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -564,6 +564,13 @@ class MetalRenderTest < ActionController::TestCase end end +class ActionControllerBaseRenderTest < ActionController::TestCase + def test_direct_render_to_string + ac = ActionController::Base.new() + assert_equal "Hello world!", ac.render_to_string(template: 'test/hello_world') + end +end + class ImplicitRenderTest < ActionController::TestCase tests ImplicitRenderTestController diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index dfcef14344..c7194cde4a 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -520,7 +520,7 @@ class CookiesTest < ActionController::TestCase def test_accessing_nonexistent_signed_cookie_should_not_raise_an_invalid_signature get :set_signed_cookie - assert_nil @controller.send(:cookies).signed[:non_existant_attribute] + assert_nil @controller.send(:cookies).signed[:non_existent_attribute] end def test_encrypted_cookie_using_default_serializer @@ -638,7 +638,7 @@ class CookiesTest < ActionController::TestCase def test_accessing_nonexistent_encrypted_cookie_should_not_raise_invalid_message get :set_encrypted_cookie - assert_nil @controller.send(:cookies).encrypted[:non_existant_attribute] + assert_nil @controller.send(:cookies).encrypted[:non_existent_attribute] end def test_setting_invalid_encrypted_cookie_should_return_nil_when_accessing_it diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index ade4b0c381..75fdc9469f 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -4795,3 +4795,33 @@ class TestPathParameters < ActionDispatch::IntegrationTest assert_equal "/ar | /ar/about", @response.body end end + +class TestInternalRoutingParams < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + get '/test_internal/:internal' => 'internal#internal' + end + end + + class ::InternalController < ActionController::Base + def internal + head :ok + end + end + + APP = build_app Routes + + def app + APP + end + + def test_paths_with_partial_dynamic_segments_are_recognised + get '/test_internal/123' + assert_equal 200, response.status + + assert_equal( + { controller: 'internal', action: 'internal', internal: '123' }, + request.path_parameters + ) + end +end diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 9042b9cffd..04dcf01bb7 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -3,6 +3,7 @@ require 'action_view/helpers/tag_helper' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/date/conversions' require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/object/acts_like' require 'active_support/core_ext/object/with_options' module ActionView diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb index f0222582c7..23081c5f07 100644 --- a/actionview/lib/action_view/helpers/number_helper.rb +++ b/actionview/lib/action_view/helpers/number_helper.rb @@ -263,8 +263,6 @@ module ActionView # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes # insignificant zeros after the decimal separator (defaults to # +true+) - # * <tt>:prefix</tt> - If +:si+ formats the number using the SI - # prefix (defaults to :binary) # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when # the argument is invalid. # diff --git a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb index 311109e958..163c8eb212 100644 --- a/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb +++ b/activejob/lib/active_job/queue_adapters/sucker_punch_adapter.rb @@ -5,7 +5,7 @@ module ActiveJob # == Sucker Punch adapter for Active Job # # Sucker Punch is a single-process Ruby asynchronous processing library. - # This reduces the cost of of hosting on a service like Heroku along + # This reduces the cost of hosting on a service like Heroku along # with the memory footprint of having to maintain additional jobs if # hosting on a dedicated server. All queues can run within a # single application (eg. Rails, Sinatra, etc.) process. diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb index 3feb82d432..e16af1947f 100644 --- a/activejob/lib/active_job/test_helper.rb +++ b/activejob/lib/active_job/test_helper.rb @@ -9,7 +9,7 @@ module ActiveJob to: :queue_adapter def before_setup # :nodoc: - test_adapter = ActiveJob::QueueAdapters::TestAdapter.new + test_adapter = queue_adapter_for_test @old_queue_adapters = (ActiveJob::Base.subclasses << ActiveJob::Base).select do |klass| # only override explicitly set adapters, a quirk of `class_attribute` @@ -32,6 +32,19 @@ module ActiveJob end end + # Specifies the queue adapter to use with all active job test helpers. + # + # Returns an instance of the queue adapter and defaults to + # <tt>ActiveJob::QueueAdapters::TestAdapter</tt>. + # + # Note: The adapter provided by this method must provide some additional + # methods from those expected of a standard <tt>ActiveJob::QueueAdapter</tt> + # in order to be used with the active job test helpers. Refer to + # <tt>ActiveJob::QueueAdapters::TestAdapter</tt>. + def queue_adapter_for_test + ActiveJob::QueueAdapters::TestAdapter.new + end + # Asserts that the number of enqueued jobs matches the given number. # # def test_jobs diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb index f7ee763e8a..3d863f5e65 100644 --- a/activejob/test/cases/test_helper_test.rb +++ b/activejob/test/cases/test_helper_test.rb @@ -509,3 +509,15 @@ class PerformedJobsTest < ActiveJob::TestCase assert_equal 2, ActiveJob::Base.queue_adapter.performed_jobs.count end end + +class OverrideQueueAdapterTest < ActiveJob::TestCase + class CustomQueueAdapter < ActiveJob::QueueAdapters::TestAdapter; end + + def queue_adapter_for_test + CustomQueueAdapter.new + end + + def test_assert_job_has_custom_queue_adapter_set + assert_instance_of CustomQueueAdapter, HelloJob.queue_adapter + end +end diff --git a/activerecord/.codeclimate.yml b/activerecord/.codeclimate.yml new file mode 100644 index 0000000000..877c67873d --- /dev/null +++ b/activerecord/.codeclimate.yml @@ -0,0 +1,27 @@ +engines: + rubocop: + enabled: true + +ratings: + paths: + - "**.rb" + +exclude_paths: + - actioncable/lib/rails/generators/ + - actioncable/test/ + - actionmailer/lib/rails/generators/ + - actionmailer/test/ + - actionpack/test/ + - actionview/test/ + - activejob/lib/rails/generators/ + - activejob/test/ + - activemodel/test/ + - activerecord/lib/rails/generators/ + - activerecord/test/ + - activesupport/test/ + - railties/lib/rails/generators/ + - railties/test/ + - ci/ + - guides/ + - tasks/ + - tools/ diff --git a/activerecord/.rubocop.yml b/activerecord/.rubocop.yml new file mode 100644 index 0000000000..dd8db6af3a --- /dev/null +++ b/activerecord/.rubocop.yml @@ -0,0 +1,27 @@ +AllCops: + TargetRubyVersion: 2.3 + DisabledByDefault: true + +# Two spaces, no tabs (for indentation). +Style/IndentationWidth: + enabled: true + +# No trailing whitespace. +Style/TrailingWhitespace: + enabled: true + +# Blank lines should not have any spaces. +Style/TrailingBlankLines: + enabled: true + +# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }. +Style/HashSyntax: + enabled: true + +# Prefer &&/|| over and/or. +Style/AndOr: + enabled: true + +# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg. +Lint/RequireParentheses: + enabled: true diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index ed1bbf5dcd..51bf12d0bf 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -66,7 +66,7 @@ module ActiveRecord # By default, +test_helper.rb+ will load all of your fixtures into your test # database, so this test will succeed. # - # The testing environment will automatically load the all fixtures into the database before each + # The testing environment will automatically load all the fixtures into the database before each # test. To ensure consistent data, the environment deletes the fixtures before running the load. # # In addition to being available in the database, the fixture's data may also be accessed by diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 87eea8277a..882605de31 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -398,15 +398,7 @@ module ActiveRecord end def construct_relation_for_association_calculations - from = arel.froms.first - if Arel::Table === from - apply_join_dependency(self, construct_join_dependency(joins_values)) - else - # FIXME: as far as I can tell, `from` will always be an Arel::Table. - # There are no tests that test this branch, but presumably it's - # possible for `from` to be a list? - apply_join_dependency(self, construct_join_dependency(from)) - end + apply_join_dependency(self, construct_join_dependency(joins_values)) end def apply_join_dependency(relation, join_dependency) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 2a831c2017..8a87015e44 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -658,7 +658,7 @@ module ActiveRecord # present). Neither relation may have a #limit, #offset, or #distinct set. # # Post.where("id = 1").or(Post.where("author_id = 3")) - # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'author_id = 3')) + # # SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3)) # def or(other) unless other.is_a? Relation diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index f7bb3e54d5..80d9a6083b 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -90,6 +90,10 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_no_queries { authors.map(&:post) } end + def test_calculate_with_string_in_from_and_eager_loading + assert_equal 10, Post.from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").count + end + def test_with_two_tables_in_from_without_getting_double_quoted posts = Post.select("posts.*").from("authors, posts").eager_load(:comments).where("posts.author_id = authors.id").order("posts.id").to_a assert_equal 2, posts.first.comments.size diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb index 4af791b758..eee135cfb8 100644 --- a/activerecord/test/cases/associations/left_outer_join_association_test.rb +++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb @@ -50,7 +50,7 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase def test_join_conditions_added_to_join_clause queries = capture_sql { Author.left_outer_joins(:essays).to_a } - assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1)/i =~ sql } + assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1|\:a1)/i =~ sql } assert queries.none? { |sql| /WHERE/i =~ sql } end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 80dcba1cf4..00e4e50ea1 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -14,7 +14,6 @@ require 'models/auto_id' require 'models/boolean' require 'models/column_name' require 'models/subscriber' -require 'models/keyboard' require 'models/comment' require 'models/minimalistic' require 'models/warehouse_thing' @@ -25,7 +24,6 @@ require 'models/joke' require 'models/bird' require 'models/car' require 'models/bulb' -require 'rexml/document' require 'concurrent/atomic/count_down_latch' class FirstAbstractClass < ActiveRecord::Base diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 91ff5146fd..db71840658 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -170,8 +170,8 @@ class EachTest < ActiveRecord::TestCase end end - def test_find_in_batches_should_not_error_if_config_overriden - # Set the config option which will be overriden + def test_find_in_batches_should_not_error_if_config_overridden + # Set the config option which will be overridden prev = ActiveRecord::Base.error_on_ignored_order_or_limit ActiveRecord::Base.error_on_ignored_order_or_limit = true assert_nothing_raised do diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 7ad9ff1f57..f9794518c7 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -5,23 +5,6 @@ require 'models/parrot' require 'models/person' # For optimistic locking require 'models/aircraft' -class Pirate # Just reopening it, not defining it - attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected - attr_accessor :changes_detected_in_after_update # Actual changes - - after_update :check_changes - -private - # after_save/update and the model itself - # can end up checking dirty status and acting on the results - def check_changes - if self.changed? - self.detected_changes_in_after_update = true - self.changes_detected_in_after_update = self.changes - end - end -end - class NumericData < ActiveRecord::Base self.table_name = 'numeric_data' end diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb index ae18573126..d05cb22740 100644 --- a/activerecord/test/cases/multiparameter_attributes_test.rb +++ b/activerecord/test/cases/multiparameter_attributes_test.rb @@ -11,15 +11,13 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase topic.attributes = attributes # note that extra #to_date call allows test to pass for Oracle, which # treats dates/times the same - assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date + assert_equal Date.new(2004, 6, 24), topic.last_read.to_date end def test_multiparameter_attributes_on_date_with_empty_year attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" } topic = Topic.find(1) topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same assert_nil topic.last_read end @@ -27,8 +25,6 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" } topic = Topic.find(1) topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same assert_nil topic.last_read end @@ -36,8 +32,6 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" } topic = Topic.find(1) topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same assert_nil topic.last_read end @@ -45,8 +39,6 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" } topic = Topic.find(1) topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same assert_nil topic.last_read end @@ -54,8 +46,6 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" } topic = Topic.find(1) topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same assert_nil topic.last_read end @@ -63,8 +53,6 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" } topic = Topic.find(1) topic.attributes = attributes - # note that extra #to_date call allows test to pass for Oracle, which - # treats dates/times the same assert_nil topic.last_read end diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index 87299c0dab..c8adc21bbc 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -12,10 +12,6 @@ module ActiveRecord SQLCounter.clear_log end - def assert_date_from_db(expected, actual, message = nil) - assert_equal expected.to_s, actual.to_s, message - end - def capture_sql SQLCounter.clear_log yield diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 904d3f0eb0..1559fe641c 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -736,8 +736,13 @@ module ActiveSupport # # would call <tt>Audit#save</tt>. # - # NOTE: +method_name+ passed to `define_model_callbacks` must not end with + # ===== Notes + # + # +names+ passed to `define_callbacks` must not end with # `!`, `?` or `=`. + # + # Calling `define_callbacks` multiple times with the same +names+ will + # overwrite previous callbacks registered with `set_callback`. def define_callbacks(*names) options = names.extract_options! diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index 7f968d10b5..3f6e8bd26c 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -164,8 +164,8 @@ class Module '' end - file, line = caller(1, 1).first.split(':'.freeze, 2) - line = line.to_i + location = caller_locations(1, 1).first + file, line = location.path, location.lineno to = to.to_s to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to) diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb index de48e717b6..92b890dbb0 100644 --- a/activesupport/lib/active_support/logger.rb +++ b/activesupport/lib/active_support/logger.rb @@ -55,6 +55,24 @@ module ActiveSupport logger.local_level = level if logger.respond_to?(:local_level=) super(level) if respond_to?(:local_level=) end + + define_method(:silence) do |level = Logger::ERROR, &block| + if logger.respond_to?(:silence) + logger.silence(level) do + if respond_to?(:silence) + super(level, &block) + else + block.call(self) + end + end + else + if respond_to?(:silence) + super(level, &block) + else + block.call(self) + end + end + end end end diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index 29305e0082..ad83638572 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/object/blank' - module ActiveSupport module Testing module Assertions diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index edf8b30a0a..7dd03ce65d 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -13,17 +13,6 @@ module ActiveSupport !ENV["NO_FORK"] && Process.respond_to?(:fork) end - @@class_setup_mutex = Mutex.new - - def _run_class_setup # class setup method should only happen in parent - @@class_setup_mutex.synchronize do - unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST'] - self.class.setup if self.class.respond_to?(:setup) - @@ran_class_setup = true - end - end - end - def run serialized = run_in_isolation do super diff --git a/activesupport/test/broadcast_logger_test.rb b/activesupport/test/broadcast_logger_test.rb index 6d4e3b74f7..f95b3e5ad3 100644 --- a/activesupport/test/broadcast_logger_test.rb +++ b/activesupport/test/broadcast_logger_test.rb @@ -3,75 +3,147 @@ require 'abstract_unit' module ActiveSupport class BroadcastLoggerTest < TestCase attr_reader :logger, :log1, :log2 - def setup + + setup do @log1 = FakeLogger.new @log2 = FakeLogger.new @log1.extend Logger.broadcast @log2 @logger = @log1 end - def test_debug - logger.debug "foo" - assert_equal 'foo', log1.adds.first[2] - assert_equal 'foo', log2.adds.first[2] + Logger::Severity.constants.each do |level_name| + method = level_name.downcase + level = Logger::Severity.const_get(level_name) + + test "##{method} adds the message to all loggers" do + logger.send(method, "msg") + + assert_equal [level, "msg", nil], log1.adds.first + assert_equal [level, "msg", nil], log2.adds.first + end end - def test_close + test "#close broadcasts to all loggers" do logger.close + assert log1.closed, 'should be closed' assert log2.closed, 'should be closed' end - def test_chevrons + test "#<< shovels the value into all loggers" do logger << "foo" + assert_equal %w{ foo }, log1.chevrons assert_equal %w{ foo }, log2.chevrons end - def test_level - assert_nil logger.level - logger.level = 10 - assert_equal 10, log1.level - assert_equal 10, log2.level + test "#level= assigns the level to all loggers" do + assert_equal ::Logger::DEBUG, logger.level + logger.level = ::Logger::FATAL + + assert_equal ::Logger::FATAL, log1.level + assert_equal ::Logger::FATAL, log2.level end - def test_progname + test "#progname= assigns to all the loggers" do assert_nil logger.progname - logger.progname = 10 - assert_equal 10, log1.progname - assert_equal 10, log2.progname + logger.progname = ::Logger::FATAL + + assert_equal ::Logger::FATAL, log1.progname + assert_equal ::Logger::FATAL, log2.progname end - def test_formatter + test "#formatter= assigns to all the loggers" do assert_nil logger.formatter - logger.formatter = 10 - assert_equal 10, log1.formatter - assert_equal 10, log2.formatter + logger.formatter = ::Logger::FATAL + + assert_equal ::Logger::FATAL, log1.formatter + assert_equal ::Logger::FATAL, log2.formatter + end + + test "#local_level= assigns the local_level to all loggers" do + assert_equal ::Logger::DEBUG, logger.local_level + logger.local_level = ::Logger::FATAL + + assert_equal ::Logger::FATAL, log1.local_level + assert_equal ::Logger::FATAL, log2.local_level + end + + test "#silence silences all loggers below the default level of ERROR" do + logger.silence do + logger.debug "test" + end + + assert_equal [], log1.adds + assert_equal [], log2.adds + end + + test "#silence does not silence at or above ERROR" do + logger.silence do + logger.error "from error" + logger.unknown "from unknown" + end + + assert_equal [[::Logger::ERROR, "from error", nil], [::Logger::UNKNOWN, "from unknown", nil]], log1.adds + assert_equal [[::Logger::ERROR, "from error", nil], [::Logger::UNKNOWN, "from unknown", nil]], log2.adds + end + + test "#silence allows you to override the silence level" do + logger.silence(::Logger::FATAL) do + logger.error "unseen" + logger.fatal "seen" + end + + assert_equal [[::Logger::FATAL, "seen", nil]], log1.adds + assert_equal [[::Logger::FATAL, "seen", nil]], log2.adds end class FakeLogger + include LoggerSilence + attr_reader :adds, :closed, :chevrons - attr_accessor :level, :progname, :formatter + attr_accessor :level, :progname, :formatter, :local_level def initialize - @adds = [] - @closed = false - @chevrons = [] - @level = nil - @progname = nil - @formatter = nil + @adds = [] + @closed = false + @chevrons = [] + @level = ::Logger::DEBUG + @local_level = ::Logger::DEBUG + @progname = nil + @formatter = nil + end + + def debug(message, &block) + add(::Logger::DEBUG, message, &block) + end + + def info(message, &block) + add(::Logger::INFO, message, &block) + end + + def warn(message, &block) + add(::Logger::WARN, message, &block) + end + + def error(message, &block) + add(::Logger::ERROR, message, &block) + end + + def fatal(message, &block) + add(::Logger::FATAL, message, &block) end - def debug msg, &block - add(:omg, nil, msg, &block) + def unknown(message, &block) + add(::Logger::UNKNOWN, message, &block) end def << x @chevrons << x end - def add(*args) - @adds << args + def add(message_level, message=nil, progname=nil, &block) + @adds << [message_level, message, progname] if message_level >= local_level end def close diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index e70680c773..e8099baa35 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -1042,7 +1042,7 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 3, new_hash[2] new_hash.default = 2 - assert_equal 2, new_hash[:non_existant] + assert_equal 2, new_hash[:non_existent] end def test_to_hash_with_raising_default_proc diff --git a/activesupport/test/logger_test.rb b/activesupport/test/logger_test.rb index 5a91420f1e..dfc5f3fdf4 100644 --- a/activesupport/test/logger_test.rb +++ b/activesupport/test/logger_test.rb @@ -143,12 +143,13 @@ class LoggerTest < ActiveSupport::TestCase def test_logger_silencing_works_for_broadcast another_output = StringIO.new - another_logger = Logger.new(another_output) + another_logger = ActiveSupport::Logger.new(another_output) - @logger.extend Logger.broadcast(another_logger) + @logger.extend ActiveSupport::Logger.broadcast(another_logger) @logger.debug "CORRECT DEBUG" - @logger.silence do + @logger.silence do |logger| + assert_kind_of ActiveSupport::Logger, logger @logger.debug "FAILURE" @logger.error "CORRECT ERROR" end @@ -166,10 +167,11 @@ class LoggerTest < ActiveSupport::TestCase another_output = StringIO.new another_logger = ::Logger.new(another_output) - @logger.extend Logger.broadcast(another_logger) + @logger.extend ActiveSupport::Logger.broadcast(another_logger) @logger.debug "CORRECT DEBUG" - @logger.silence do + @logger.silence do |logger| + assert_kind_of ActiveSupport::Logger, logger @logger.debug "FAILURE" @logger.error "CORRECT ERROR" end diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md index a07ce7e859..8bd2074e82 100644 --- a/guides/source/5_0_release_notes.md +++ b/guides/source/5_0_release_notes.md @@ -37,9 +37,20 @@ Major Features -------------- ### Action Cable -[Pull Request](https://github.com/rails/rails/pull/22586) -ToDo... +Action Cable is a new framework in Rails 5. It seamlessly integrates +[WebSockets](https://en.wikipedia.org/wiki/WebSocket) with the rest of your +Rails application. + +Action Cable allows for real-time features to be written in Ruby in the +same style and form as the rest of your Rails application, while still being +performant and scalable. It's a full-stack offering that provides both a +client-side JavaScript framework and a server-side Ruby framework. You have +access to your full domain model written with Active Record or your ORM of +choice. + +See the [Active Cable Overview](action_cable_overview.html) guide for more +information. ### Rails API [Pull Request](https://github.com/rails/rails/pull/19832) @@ -48,7 +59,7 @@ ToDo... ### Active Record attributes API -Defines an attribute with a type on a model. It will override the type of existing attributes if needed. +Defines an attribute with a type on a model. It will override the type of existing attributes if needed. This allows control over how values are converted to and from SQL when assigned to a model. It also changes the behavior of values passed to `ActiveRecord::Base.where`, which lets use our domain objects across much of Active Record, without having to rely on implementation details or monkey patching. @@ -80,7 +91,7 @@ class StoreListing < ActiveRecord::Base attribute :price_in_cents, :integer # custom type attribute :my_string, :string, default: "new default" # default value attribute :my_default_proc, :datetime, default: -> { Time.now } # default value - attribute :field_without_db_column, :integer, array: true + attribute :field_without_db_column, :integer, array: true end # after @@ -96,22 +107,22 @@ model.attributes #=> {field_without_db_column: [1, 2, 3]} You can define your own custom types, as long as they respond to the methods defined on the value type. The method +deserialize+ or +cast+ will be called on your type object, with raw input from the -database or from your controllers. This is useful, for example, when doing custom conversion, +database or from your controllers. This is useful, for example, when doing custom conversion, like Money data. **Querying:** When `ActiveRecord::Base.where` is called, it will use the type defined by the model class to convert the value to SQL, -calling +serialize+ on your type object. +calling +serialize+ on your type object. -This gives the objects ability to specify, how to convert values when performing SQL queries. +This gives the objects ability to specify, how to convert values when performing SQL queries. **Dirty Tracking:** The type of an attribute is given the opportunity to change how dirty -tracking is performed. - +tracking is performed. + See its [documentation](http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html) for a detailed write up. @@ -209,6 +220,15 @@ Please refer to the [Changelog][railties] for detailed changes. ([commit](https://github.com/rails/rails/commit/b04d07337fd7bc17e88500e9d6bcd361885a45f8)) +* Added `--skip-action-mailer` to skip Action Mailer while generating new app. + ([Pull Request](https://github.com/rails/rails/pull/18288)) + +* Removed `tmp/sessions` directory and the clear rake task associated with it. + ([Pull Request](https://github.com/rails/rails/pull/18314)) + +* Changed `_form.html.erb` generated by scaffold generator to use local variables. + ([Pull Request](https://github.com/rails/rails/pull/13434)) + Action Pack ----------- @@ -371,6 +391,9 @@ Please refer to the [Changelog][action-pack] for detailed changes. at the controller level. ([Pull Request](https://github.com/rails/rails/pull/24866)) +* Discarded flash messages get removed before storing into session. + ([Pull Request](https://github.com/rails/rails/pull/18721)) + Action View ------------- @@ -413,6 +436,9 @@ Please refer to the [Changelog][action-view] for detailed changes. button on submit to prevent double submits. ([Pull Request](https://github.com/rails/rails/pull/21135)) +* Partial template name no longer has to be a valid Ruby identifier. + ([commit](https://github.com/rails/rails/commit/da9038e)) + Action Mailer ------------- @@ -507,6 +533,9 @@ Please refer to the [Changelog][active-record] for detailed changes. * Removed support for PostgreSQL versions below 9.1. ([Pull Request](https://github.com/rails/rails/pull/23434)) +* Removed support for `activerecord-deprecated_finders` gem. + ([commit](https://github.com/rails/rails/commit/78dab2a8569408658542e462a957ea5a35aa4679)) + ### Deprecations * Deprecated passing a class as a value in a query. Users should pass strings @@ -671,10 +700,24 @@ Please refer to the [Changelog][active-record] for detailed changes. with comments stored in database metadata for PostgreSQL & MySQL. ([Pull Request](https://github.com/rails/rails/pull/22911)) -* Added prepared statements support to `mysql2` adapter, for mysql2 0.4.4+, - Previously this was only supported on the deprecated `mysql` legacy adapter. +* Added prepared statements support to `mysql2` adapter, for mysql2 0.4.4+, + Previously this was only supported on the deprecated `mysql` legacy adapter. To enable, set `prepared_statements: true` in config/database.yml. - ([Pull Request](https://github.com/rails/rails/pull/23461)) + ([Pull Request](https://github.com/rails/rails/pull/23461)) + +* Added ability to call `ActionRecord::Relation#update` on relation objects + which will run validations on callbacks on all objects in the relation. + ([Pull Request](https://github.com/rails/rails/pull/11898)) + +* Added `:touch` option to the `save` method so that records can be saved without + updating timestamps. + ([Pull Request](https://github.com/rails/rails/pull/18225)) + +* Added expression indexes and operator classes support for PostgreSQL. + ([commit](https://github.com/rails/rails/commit/edc2b7718725016e988089b5fb6d6fb9d6e16882)) + +* Added `:index_errors` option to add indexes to errors of nested attributes. + ([Pull Request](https://github.com/rails/rails/pull/19686)) Active Model ------------ @@ -691,6 +734,9 @@ Please refer to the [Changelog][active-model] for detailed changes. [activemodel-serializers-xml](https://github.com/rails/activemodel-serializers-xml) gem. ([Pull Request](https://github.com/rails/rails/pull/21161)) +* Removed `ActionController::ModelNaming` module. + ([Pull Request](https://github.com/rails/rails/pull/18194)) + ### Deprecations * Deprecated returning `false` as a way to halt Active Model and @@ -797,6 +843,9 @@ Please refer to the [Changelog][active-support] for detailed changes. * Removed deprecated `ThreadSafe::Cache`. Use `Concurrent::Map` instead. ([Pull Request](https://github.com/rails/rails/pull/21679)) +* Removed `Object#itself` as it is implemented in Ruby 2.2. + ([Pull Request](https://github.com/rails/rails/pull/18244)) + ### Deprecations * Deprecated `MissingSourceFile` in favor of `LoadError`. @@ -918,6 +967,9 @@ Please refer to the [Changelog][active-support] for detailed changes. * `ActiveSupport::Duration` now supports ISO8601 formatting and parsing. ([Pull Request](https://github.com/rails/rails/pull/16917)) +* Added ability to `TaggedLogging` to allow loggers to be instantiated multiple + times so that they don't share tags with each other. + ([Pull Request](https://github.com/rails/rails/pull/9065)) Credits ------- diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index d6de92ace6..c9f70dc87b 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -62,12 +62,12 @@ $ bin/rails generate job guests_cleanup --queue urgent ``` If you don't want to use a generator, you could create your own file inside of -`app/jobs`, just make sure that it inherits from `ActiveJob::Base`. +`app/jobs`, just make sure that it inherits from `ApplicationJob`. Here's what a job looks like: ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :default def perform(*guests) @@ -141,7 +141,7 @@ end You can also configure your backend on a per job basis. ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob self.queue_adapter = :resque #.... end @@ -171,7 +171,7 @@ Most of the adapters support multiple queues. With Active Job you can schedule the job to run on a specific queue: ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :low_priority #.... end @@ -189,7 +189,7 @@ module YourApp end # app/jobs/guests_cleanup_job.rb -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :low_priority #.... end @@ -212,7 +212,7 @@ module YourApp end # app/jobs/guests_cleanup_job.rb -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :low_priority #.... end @@ -234,7 +234,7 @@ block will be executed in the job context (so you can access `self.arguments`) and you must return the queue name: ```ruby -class ProcessVideoJob < ActiveJob::Base +class ProcessVideoJob < ApplicationJob queue_as do video = self.arguments.first if video.owner.premium? @@ -274,7 +274,7 @@ trigger logic during the life cycle of a job. ### Usage ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :default before_enqueue do |job| @@ -331,7 +331,7 @@ Active Record objects to your job instead of class/id pairs, which you then have to manually deserialize. Before, jobs would look like this: ```ruby -class TrashableCleanupJob < ActiveJob::Base +class TrashableCleanupJob < ApplicationJob def perform(trashable_class, trashable_id, depth) trashable = trashable_class.constantize.find(trashable_id) trashable.cleanup(depth) @@ -342,7 +342,7 @@ end Now you can simply do: ```ruby -class TrashableCleanupJob < ActiveJob::Base +class TrashableCleanupJob < ApplicationJob def perform(trashable, depth) trashable.cleanup(depth) end @@ -360,7 +360,7 @@ Active Job provides a way to catch exceptions raised during the execution of the job: ```ruby -class GuestsCleanupJob < ActiveJob::Base +class GuestsCleanupJob < ApplicationJob queue_as :default rescue_from(ActiveRecord::RecordNotFound) do |exception| diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index 936d6a30b8..2737237c1a 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -392,7 +392,8 @@ The `exclusion` helper has an option `:in` that receives the set of values that will not be accepted for the validated attributes. The `:in` option has an alias called `:within` that you can use for the same purpose, if you'd like to. This example uses the `:message` option to show how you can include the -attribute's value. +attribute's value. For full options to the message argument please see the +[message documentation](#message). The default error message is _"is reserved"_. @@ -427,7 +428,8 @@ end The `inclusion` helper has an option `:in` that receives the set of values that will be accepted. The `:in` option has an alias called `:within` that you can use for the same purpose, if you'd like to. The previous example uses the -`:message` option to show how you can include the attribute's value. +`:message` option to show how you can include the attribute's value. For full +options please see the [message documentation](#message). The default error message for this helper is _"is not included in the list"_. @@ -768,6 +770,9 @@ class Coffee < ApplicationRecord end ``` +For full options to the message argument please see the +[message documentation](#message). + ### `:allow_blank` The `:allow_blank` option is similar to the `:allow_nil` option. This option @@ -792,7 +797,8 @@ for each validation helper. The `:message` option accepts a `String` or `Proc`. A `String` `:message` value can optionally contain any/all of `%{value}`, `%{attribute}`, and `%{model}` which will be dynamically replaced when -validation fails. +validation fails. This replacement is done using the I18n gem, and the +placeholders must match exactly, no spaces are allowed. A `Proc` `:message` value is given two arguments: the object being validated, and a hash with `:model`, `:attribute`, and `:value` key-value pairs. diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index a5b8a75509..a06a53b250 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -195,6 +195,11 @@ url: upgrading_ruby_on_rails.html description: This guide helps in upgrading applications to latest Ruby on Rails versions. - + name: Ruby on Rails 5.0 Release Notes + url: 5_0_release_notes.html + description: Release notes for Rails 5.0. + work_in_progress: true + - name: Ruby on Rails 4.2 Release Notes url: 4_2_release_notes.html description: Release notes for Rails 4.2. diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb index 7f00943d80..97f3657070 100644 --- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb +++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb @@ -9,6 +9,13 @@ module Erb # :nodoc: view_base_path = File.join("app/views", class_path, file_name + '_mailer') empty_directory view_base_path + if self.behavior == :invoke + formats.each do |format| + layout_path = File.join('app/views/layouts', class_path, filename_with_extensions('mailer', format)) + template filename_with_extensions(:layout, format), layout_path + end + end + actions.each do |action| @action = action diff --git a/railties/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt b/railties/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt new file mode 100644 index 0000000000..55f3675d49 --- /dev/null +++ b/railties/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <style> + /* Email styles need to be inline */ + </style> + </head> + + <body> + <%%= yield %> + </body> +</html> diff --git a/railties/lib/rails/generators/erb/mailer/templates/layout.text.erb.tt b/railties/lib/rails/generators/erb/mailer/templates/layout.text.erb.tt new file mode 100644 index 0000000000..6363733e6e --- /dev/null +++ b/railties/lib/rails/generators/erb/mailer/templates/layout.text.erb.tt @@ -0,0 +1 @@ +<%%= yield %> 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 01ef3e6630..fa63e48fdf 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 @@ -9,3 +9,6 @@ Rails.application.config.assets.version = '1.0' # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. # Rails.application.config.assets.precompile += %w( search.js ) + +# Suppress logger output for asset requests. +Rails.application.config.assets.quiet = true diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb index 8728b39dae..6a4951840d 100644 --- a/railties/test/generators/mailer_generator_test.rb +++ b/railties/test/generators/mailer_generator_test.rb @@ -84,6 +84,10 @@ class MailerGeneratorTest < Rails::Generators::TestCase assert_match(%r(\sapp/views/notifier_mailer/bar\.text\.erb), view) assert_match(/<%= @greeting %>/, view) end + + assert_file "app/views/layouts/mailer.text.erb" do |view| + assert_match(/<%= yield %>/, view) + end end def test_invokes_default_html_template_engine @@ -97,6 +101,10 @@ class MailerGeneratorTest < Rails::Generators::TestCase assert_match(%r(\sapp/views/notifier_mailer/bar\.html\.erb), view) assert_match(/<%= @greeting %>/, view) end + + assert_file "app/views/layouts/mailer.html.erb" do |view| + assert_match(%r{<body>\n <%= yield %>\n </body>}, view) + end end def test_invokes_default_template_engine_even_with_no_action diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 3cc8e1de55..5dd4cce28a 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -669,6 +669,21 @@ class PluginGeneratorTest < Rails::Generators::TestCase end end + def test_generate_mailer_layouts_when_does_not_exist_in_mountable_engine + run_generator [destination_root, '--mountable'] + capture(:stdout) do + `#{destination_root}/bin/rails g mailer User` + end + + assert_file "#{destination_root}/app/views/layouts/bukkits/mailer.text.erb" do |view| + assert_match(/<%= yield %>/, view) + end + + assert_file "#{destination_root}/app/views/layouts/bukkits/mailer.html.erb" do |view| + assert_match(%r{<body>\n <%= yield %>\n </body>}, view) + end + end + def test_generate_application_job_when_does_not_exist_in_mountable_engine run_generator [destination_root, '--mountable'] FileUtils.rm "#{destination_root}/app/jobs/bukkits/application_job.rb" diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb index c51503c2b7..2e10d63599 100644 --- a/railties/test/rails_info_controller_test.rb +++ b/railties/test/rails_info_controller_test.rb @@ -78,4 +78,10 @@ class InfoControllerTest < ActionController::TestCase get :routes, params: { path: 'rails/info/routes.html' } assert fuzzy_count.call == 0, 'should match optional parts of route literally' end + + test "internal routes do not have a default params[:internal] value" do + get :properties + assert_response :success + assert_nil @controller.params[:internal] + end end |