From 2e6b798c101cb130b34dab326be939be1b8815f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Schr=C3=B6der?= Date: Fri, 11 Jul 2014 19:42:09 -0400 Subject: don't log errors when all is fine --- activerecord/lib/active_record/fixtures.rb | 10 +++++----- activerecord/test/cases/fixtures_test.rb | 15 ++++++++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 4306b36ae1..727f12103a 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -870,11 +870,11 @@ module ActiveRecord def try_to_load_dependency(file_name) require_dependency file_name rescue LoadError => e - # Let's hope the developer has included it - # Let's warn in case this is a subdependency, otherwise - # subdependency error messages are totally cryptic - if ActiveRecord::Base.logger - ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}") + unless fixture_class_names.key?(file_name.pluralize) + if ActiveRecord::Base.logger + ActiveRecord::Base.logger.warn("Unable to load #{file_name}, make sure you added it to ActiveSupport::TestCase.set_fixture_class") + ActiveRecord::Base.logger.warn("underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}") + end end end diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 042fdaf0bb..d7bbe0df62 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -823,15 +823,20 @@ end class FixtureLoadingTest < ActiveRecord::TestCase def test_logs_message_for_failed_dependency_load - ActiveRecord::TestCase.expects(:require_dependency).with(:does_not_exist).raises(LoadError) - ActiveRecord::Base.logger.expects(:warn) - ActiveRecord::TestCase.try_to_load_dependency(:does_not_exist) + ActiveRecord::Base.logger.expects(:warn).twice + ActiveRecord::TestCase.try_to_load_dependency('does_not_exist') + end + + def test_does_not_logs_message_for_dependency_that_has_been_defined_with_set_fixture_class + ActiveRecord::TestCase.set_fixture_class unknown_dead_parrots: DeadParrot + ActiveRecord::Base.logger.expects(:warn).never + ActiveRecord::TestCase.try_to_load_dependency('unknown_dead_parrot') end def test_does_not_logs_message_for_successful_dependency_load - ActiveRecord::TestCase.expects(:require_dependency).with(:works_out_fine) + ActiveRecord::TestCase.expects(:require_dependency).with('works_out_fine') ActiveRecord::Base.logger.expects(:warn).never - ActiveRecord::TestCase.try_to_load_dependency(:works_out_fine) + ActiveRecord::TestCase.try_to_load_dependency('works_out_fine') end end -- cgit v1.2.3 From 5c2926590711c36fdbd1c09cb929292a404ccd27 Mon Sep 17 00:00:00 2001 From: Alan Oliver Date: Thu, 24 Jul 2014 00:16:03 +0100 Subject: Small Action View Overview edits [ci skip] Some small grammar and readability edits --- guides/source/action_view_overview.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index ef7ef5a50e..d1e3df8d05 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -44,18 +44,18 @@ $ bin/rails generate scaffold article There is a naming convention for views in Rails. Typically, the views share their name with the associated controller action, as you can see above. For example, the index controller action of the `articles_controller.rb` will use the `index.html.erb` view file in the `app/views/articles` directory. -The complete HTML returned to the client is composed of a combination of this ERB file, a layout template that wraps it, and all the partials that the view may reference. Later on this guide you can find a more detailed documentation of each one of these three components. +The complete HTML returned to the client is composed of a combination of this ERB file, a layout template that wraps it, and all the partials that the view may reference. Within this guide you will find more detailed documentation about each of these three components. Templates, Partials and Layouts ------------------------------- -As mentioned before, the final HTML output is a composition of three Rails elements: `Templates`, `Partials` and `Layouts`. -Below is a brief overview of each one of them. +As mentioned, the final HTML output is a composition of three Rails elements: `Templates`, `Partials` and `Layouts`. +Below is a brief overview of each of them. ### Templates -Action View templates can be written in several ways. If the template file has a `.erb` extension then it uses a mixture of ERB (included in Ruby) and HTML. If the template file has a `.builder` extension then a fresh instance of `Builder::XmlMarkup` library is used. +Action View templates can be written in several ways. If the template file has a `.erb` extension then it uses a mixture of ERB (Embedded Ruby) and HTML. If the template file has a `.builder` extension then the `Builder::XmlMarkup` library is used. Rails supports multiple template systems and uses a file extension to distinguish amongst them. For example, an HTML file using the ERB template system will have `.html.erb` as a file extension. @@ -72,7 +72,7 @@ Consider the following loop for names: <% end %> ``` -The loop is set up in regular embedding tags (`<% %>`) and the name is written using the output embedding tags (`<%= %>`). Note that this is not just a usage suggestion, for regular output functions like `print` or `puts` won't work with ERB templates. So this would be wrong: +The loop is set up using regular embedding tags (`<% %>`) and the name is inserted using the output embedding tags (`<%= %>`). Note that this is not just a usage suggestion: regular output functions such as `print` and `puts` won't be rendered to the view with ERB templates. So this would be wrong: ```html+erb <%# WRONG %> @@ -231,7 +231,7 @@ The `object` and `as` options can also be used together: #### Rendering Collections -It is very common that a template needs to iterate over a collection and render a sub-template for each of the elements. This pattern has been implemented as a single method that accepts an array and renders a partial for each one of the elements in the array. +It is very common that a template will need to iterate over a collection and render a sub-template for each of the elements. This pattern has been implemented as a single method that accepts an array and renders a partial for each one of the elements in the array. So this example for rendering all the products: @@ -247,7 +247,7 @@ can be rewritten in a single line: <%= render partial: "product", collection: @products %> ``` -When a partial is called like this (eg. with a collection), the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is `_product`, and within it you can refer to `product` to get the instance that is being rendered. +When a partial is called with a collection, the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is `_product`, and within it you can refer to `product` to get the collection member that is being rendered. You can use a shorthand syntax for rendering collections. Assuming `@products` is a collection of `Product` instances, you can simply write the following to produce the same result: @@ -255,7 +255,7 @@ You can use a shorthand syntax for rendering collections. Assuming `@products` i <%= render @products %> ``` -Rails determines the name of the partial to use by looking at the model name in the collection, `Product` in this case. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection. +Rails determines the name of the partial to use by looking at the model name in the collection, `Product` in this case. In fact, you can even render a collection made up of instances of different models using this shorthand, and Rails will choose the proper partial for each member of the collection. #### Spacer Templates @@ -269,14 +269,14 @@ Rails will render the `_product_ruler` partial (with no data passed to it) betwe ### Layouts -Layouts can be used to render a common view template around the results of Rails controller actions. Typically, every Rails application has a couple of overall layouts that most pages are rendered within. For example, a site might have a layout for a logged in user, and a layout for the marketing or sales side of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us." You would expect each layout to have a different look and feel. You can read more details about Layouts in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide. +Layouts can be used to render a common view template around the results of Rails controller actions. Typically, a Rails application will have a couple of layouts that pages will be rendered within. For example, a site might have one layout for a logged in user and another for the marketing or sales side of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us" pages. You would expect each layout to have a different look and feel. You can read about layouts in more detail in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide. Partial Layouts --------------- -Partials can have their own layouts applied to them. These layouts are different than the ones that are specified globally for the entire action, but they work in a similar fashion. +Partials can have their own layouts applied to them. These layouts are different from those applied to a controller action, but they work in a similar fashion. -Let's say we're displaying an article on a page, that should be wrapped in a `div` for display purposes. First, we'll create a new `Article`: +Let's say we're displaying an article on a page which should be wrapped in a `div` for display purposes. Firstly, we'll create a new `Article`: ```ruby Article.create(body: 'Partial Layouts are cool!') -- cgit v1.2.3 From eb73d7dafa343507a60f765c43c748d6987ec652 Mon Sep 17 00:00:00 2001 From: Robin Dupret Date: Sun, 17 Aug 2014 16:31:00 +0200 Subject: Define the Duration#instance_of? method Since Duration is extending from ProxyObject which extends itself from BasicObject, the Duration object doesn't respond to the #instance_of? method. Thus, the #method_missing hook get triggered, delegating the method to its `value` attribute. However, Rubinius' #eql? definition relies on #instance_of?, thus this will equal to true with a Fixnum (since its `value` attribute is a Fixnum) while it should not. The previous behavior was wrong anyway, no matter the implementation. --- activesupport/CHANGELOG.md | 7 ++++++- activesupport/lib/active_support/duration.rb | 6 +++++- activesupport/test/core_ext/duration_test.rb | 5 +++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 96bce53999..88d6e09d4b 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,6 +1,11 @@ +* Add the `Duration#instance_of?` method that was previously delegated to the + internal `value` attribute. + + *Robin Dupret* + * Fix rounding errors with #travel_to by resetting the usec on any passed time to zero, so we only travel with per-second precision, not anything deeper than that. - + *DHH* * Fix ActiveSupport::TestCase not to order users' test cases by default. diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 0ae641d05b..084d13a9e3 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -35,10 +35,14 @@ module ActiveSupport end def is_a?(klass) #:nodoc: - Duration == klass || value.is_a?(klass) + instance_of?(klass) || value.is_a?(klass) end alias :kind_of? :is_a? + def instance_of?(klass) # :nodoc: + Duration == klass + end + # Returns +true+ if +other+ is also a Duration instance with the # same +value+, or if other == value. def ==(other) diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 31af3c4521..330d995b7c 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -41,6 +41,11 @@ class DurationTest < ActiveSupport::TestCase assert !1.eql?(1.second) end + def test_instance_of + assert !1.minute.instance_of?(Fixnum) + assert !2.days.instance_of?(Fixnum) + end + def test_inspect assert_equal '0 seconds', 0.seconds.inspect assert_equal '1 month', 1.month.inspect -- cgit v1.2.3 From 20f7c767e5e1b6ca6619cd078be10406164efd56 Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Thu, 17 Jul 2014 11:36:17 +0200 Subject: Grammar pass [ci skip] Add improvements from @eileencodes [skip ci] --- activerecord/lib/active_record/associations.rb | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7ec7eb1b24..6ac457a2cd 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -447,9 +447,11 @@ module ActiveRecord # # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+. # - # Should any of the +before_add+ callbacks throw an exception, the object does not get - # added to the collection. Same with the +before_remove+ callbacks; if an exception is - # thrown the object doesn't get removed. + # If any of the +before_add+ callbacks throw an exception, the object will not be + # added to the collection. + # + # Similarly, if any of the +before_remove+ callbacks throw an exception, the object + # will not be removed from the collection. # # == Association extensions # @@ -647,7 +649,7 @@ module ActiveRecord # belongs_to :commenter # end # - # When using nested association, you will not be able to modify the association because there + # When using a nested association, you will not be able to modify the association because there # is not enough information to know what modification to make. For example, if you tried to # add a Commenter in the example above, there would be no way to tell how to set up the # intermediate Post and Comment objects. @@ -717,7 +719,7 @@ module ActiveRecord # == Eager loading of associations # # Eager loading is a way to find objects of a certain class and a number of named associations. - # This is one of the easiest ways of to prevent the dreaded N+1 problem in which fetching 100 + # It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100 # posts that each need to display their author triggers 101 database queries. Through the # use of eager loading, the number of queries will be reduced from 101 to 2. # @@ -749,16 +751,16 @@ module ActiveRecord # Post.includes(:author, :comments).each do |post| # # This will load all comments with a single query. This reduces the total number of queries - # to 3. More generally the number of queries will be 1 plus the number of associations + # to 3. In general, the number of queries will be 1 plus the number of associations # named (except if some of the associations are polymorphic +belongs_to+ - see below). # # To include a deep hierarchy of associations, use a hash: # - # Post.includes(:author, {comments: {author: :gravatar}}).each do |post| + # Post.includes(:author, { comments: { author: :gravatar } }).each do |post| # - # That'll grab not only all the comments but all their authors and gravatar pictures. - # You can mix and match symbols, arrays and hashes in any combination to describe the - # associations you want to load. + # The above code will load all the comments and all of their associated + # authors and gravatars. You can mix and match any combination of symbols, + # arrays, and hashes to retrieve the associations you want to load. # # All of this power shouldn't fool you into thinking that you can pull out huge amounts # of data with no performance penalty just because you've reduced the number of queries. @@ -767,8 +769,8 @@ module ActiveRecord # cut down on the number of queries in a situation as the one described above. # # Since only one table is loaded at a time, conditions or orders cannot reference tables - # other than the main one. If this is the case Active Record falls back to the previously - # used LEFT OUTER JOIN based strategy. For example + # other than the main one. If this is the case, Active Record falls back to the previously + # used LEFT OUTER JOIN based strategy. For example: # # Post.includes([:author, :comments]).where(['comments.approved = ?', true]) # -- cgit v1.2.3 From 63f7b22a4edb4d35f8a209733e44c015d12f55a3 Mon Sep 17 00:00:00 2001 From: Lucas Mazza Date: Tue, 19 Aug 2014 23:53:19 -0300 Subject: Remove global helpers from the ActionMailer test suite. --- actionmailer/test/abstract_unit.rb | 9 --------- actionmailer/test/asset_host_test.rb | 5 +---- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index c549545674..c4dbbe25f6 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -32,15 +32,6 @@ module Rails end end -def set_delivery_method(method) - @old_delivery_method = ActionMailer::Base.delivery_method - ActionMailer::Base.delivery_method = method -end - -def restore_delivery_method - ActionMailer::Base.delivery_method = @old_delivery_method -end - # Skips the current run on Rubinius using Minitest::Assertions#skip def rubinius_skip(message = '') skip message if RUBY_ENGINE == 'rbx' diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb index 9ba67c2842..dd3b38544f 100644 --- a/actionmailer/test/asset_host_test.rb +++ b/actionmailer/test/asset_host_test.rb @@ -9,11 +9,8 @@ class AssetHostMailer < ActionMailer::Base end end -class AssetHostTest < ActiveSupport::TestCase +class AssetHostTest < ActionMailer::TestCase def setup - set_delivery_method :test - ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries.clear AssetHostMailer.configure do |c| c.asset_host = "http://www.example.com" end -- cgit v1.2.3 From 431f8e01196044877c2acea4271410b1033ec915 Mon Sep 17 00:00:00 2001 From: Agis- Date: Tue, 15 Jul 2014 03:25:18 +0300 Subject: Only merge scopes with zero arity in has_many through with dynamic conditions. Fixes #16128 This bug was introduced in https://github.com/rails/rails/commit/c35e438620f2d56562251571377995359546393d so it's present from 4.1.2-rc1 and after. https://github.com/rails/rails/commit/c35e438620f2d56562251571377995359546393d merges any relation scopes passed as proc objects to the relation, but does *not* take into account the arity of the lambda. To reproduce: https://gist.github.com/Agis-/5f1f0d664d2cd08dfb9b --- activerecord/CHANGELOG.md | 7 +++++++ .../lib/active_record/associations/through_association.rb | 6 +++++- activerecord/test/cases/relation/merging_test.rb | 13 +++++++++++++ activerecord/test/models/comment.rb | 1 + activerecord/test/models/developer.rb | 2 ++ activerecord/test/schema/schema.rb | 1 + 6 files changed, 29 insertions(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 74f802f3f7..c2e79e9f02 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fix has_many :through relation merging failing when dynamic conditions are + passed as a lambda with an arity of one. + + Fixes #16128 + + *Agis Anastasopoulos* + * Fixed the `Relation#exists?` to work with polymorphic associations. Fixes #15821. diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index 611d471e62..e47e81aa0f 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -15,7 +15,11 @@ module ActiveRecord scope = super reflection.chain.drop(1).each do |reflection| relation = reflection.klass.all - relation.merge!(reflection.scope) if reflection.scope + + reflection_scope = reflection.scope + if reflection_scope && reflection_scope.arity.zero? + relation.merge!(reflection_scope) + end scope.merge!( relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load) diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 2b5c2fd5a4..54a581e416 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -4,6 +4,7 @@ require 'models/comment' require 'models/developer' require 'models/post' require 'models/project' +require 'models/rating' class RelationMergingTest < ActiveRecord::TestCase fixtures :developers, :comments, :authors, :posts @@ -144,4 +145,16 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase assert_equal ["Mary", "Mary", "Mary", "David"], posts_by_author_name end + + test "relation merging (using a proc argument)" do + dev = Developer.where(name: "Jamis").first + + comment_1 = dev.comments.create!(body: "I'm Jamis", post: Post.first) + rating_1 = comment_1.ratings.create! + + comment_2 = dev.comments.create!(body: "I'm John", post: Post.first) + rating_2 = comment_2.ratings.create! + + assert_equal dev.ratings, [rating_1] + end end diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index 15970758db..7a88299d08 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -9,6 +9,7 @@ class Comment < ActiveRecord::Base belongs_to :post, :counter_cache => true belongs_to :author, polymorphic: true belongs_to :resource, polymorphic: true + belongs_to :developer has_many :ratings diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 5bd2f00129..3627cfdd09 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -46,6 +46,8 @@ class Developer < ActiveRecord::Base has_many :audit_logs has_many :contracts has_many :firms, :through => :contracts, :source => :firm + has_many :comments, ->(developer) { where(body: "I'm #{developer.name}") } + has_many :ratings, through: :comments scope :jamises, -> { where(:name => 'Jamis') } diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 98f2492ef8..0584df87c6 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -198,6 +198,7 @@ ActiveRecord::Schema.define do t.references :author, polymorphic: true t.string :resource_id t.string :resource_type + t.integer :developer_id end create_table :companies, force: true do |t| -- cgit v1.2.3 From 4b7ac2e83eb5776855b4abe02ca6060e73290e42 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 20 Aug 2014 14:26:22 +0900 Subject: Drop schema_migrations table only when exists --- activerecord/test/cases/migration_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index f9d1edc340..034a0f567e 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -89,7 +89,7 @@ class MigrationTest < ActiveRecord::TestCase end def test_migration_detection_without_schema_migration_table - ActiveRecord::Base.connection.drop_table :schema_migrations + ActiveRecord::Base.connection.drop_table('schema_migrations') if ActiveRecord::Base.connection.table_exists?('schema_migrations') migrations_path = MIGRATIONS_ROOT + "/valid" old_path = ActiveRecord::Migrator.migrations_paths -- cgit v1.2.3 From 161382fd04e89011ff05e8749b4feefb96ca1de6 Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Tue, 19 Aug 2014 23:35:52 -0700 Subject: [ci skip] "..enables interrorgating of [thing].." would be correct --- activerecord/lib/active_record/reflection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 1547c8e3f4..331b6b0698 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -38,7 +38,7 @@ module ActiveRecord ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection) end - # \Reflection enables interrogating Active Record classes and objects + # \Reflection enables interrogating of Active Record classes and objects # about their associations and aggregations. This information can, # for example, be used in a form builder that takes an Active Record object # and creates input fields for all of the attributes depending on their type -- cgit v1.2.3 From 5efcf3800ef427ac58cd3e3946f7dca71df3e068 Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Tue, 19 Aug 2014 23:40:42 -0700 Subject: Move as/test_test to as/test_case_test --- activesupport/test/test_case_test.rb | 174 +++++++++++++++++++++++++++++++++++ activesupport/test/test_test.rb | 174 ----------------------------------- 2 files changed, 174 insertions(+), 174 deletions(-) create mode 100644 activesupport/test/test_case_test.rb delete mode 100644 activesupport/test/test_test.rb diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb new file mode 100644 index 0000000000..c93192f207 --- /dev/null +++ b/activesupport/test/test_case_test.rb @@ -0,0 +1,174 @@ +require 'abstract_unit' + +class AssertDifferenceTest < ActiveSupport::TestCase + def setup + @object = Class.new do + attr_accessor :num + def increment + self.num += 1 + end + + def decrement + self.num -= 1 + end + end.new + @object.num = 0 + end + + def test_assert_not + assert_equal true, assert_not(nil) + assert_equal true, assert_not(false) + + e = assert_raises(Minitest::Assertion) { assert_not true } + assert_equal 'Expected true to be nil or false', e.message + + e = assert_raises(Minitest::Assertion) { assert_not true, 'custom' } + assert_equal 'custom', e.message + end + + def test_assert_no_difference_pass + assert_no_difference '@object.num' do + # ... + end + end + + def test_assert_no_difference_fail + error = assert_raises(Minitest::Assertion) do + assert_no_difference '@object.num' do + @object.increment + end + end + assert_equal "\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message + end + + def test_assert_no_difference_with_message_fail + error = assert_raises(Minitest::Assertion) do + assert_no_difference '@object.num', 'Object Changed' do + @object.increment + end + end + assert_equal "Object Changed.\n\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message + end + + def test_assert_difference + assert_difference '@object.num', +1 do + @object.increment + end + end + + def test_assert_difference_with_implicit_difference + assert_difference '@object.num' do + @object.increment + end + end + + def test_arbitrary_expression + assert_difference '@object.num + 1', +2 do + @object.increment + @object.increment + end + end + + def test_negative_differences + assert_difference '@object.num', -1 do + @object.decrement + end + end + + def test_expression_is_evaluated_in_the_appropriate_scope + silence_warnings do + local_scope = local_scope = 'foo' + assert_difference('local_scope; @object.num') { @object.increment } + end + end + + def test_array_of_expressions + assert_difference [ '@object.num', '@object.num + 1' ], +1 do + @object.increment + end + end + + def test_array_of_expressions_identify_failure + assert_raises(Minitest::Assertion) do + assert_difference ['@object.num', '1 + 1'] do + @object.increment + end + end + end + + def test_array_of_expressions_identify_failure_when_message_provided + assert_raises(Minitest::Assertion) do + assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do + @object.increment + end + end + end +end + +class AlsoDoingNothingTest < ActiveSupport::TestCase +end + +# Setup and teardown callbacks. +class SetupAndTeardownTest < ActiveSupport::TestCase + setup :reset_callback_record, :foo + teardown :foo, :sentinel + + def test_inherited_setup_callbacks + assert_equal [:reset_callback_record, :foo], self.class._setup_callbacks.map(&:raw_filter) + assert_equal [:foo], @called_back + assert_equal [:foo, :sentinel], self.class._teardown_callbacks.map(&:raw_filter) + end + + def setup + end + + def teardown + end + + protected + + def reset_callback_record + @called_back = [] + end + + def foo + @called_back << :foo + end + + def sentinel + assert_equal [:foo], @called_back + end +end + +class SubclassSetupAndTeardownTest < SetupAndTeardownTest + setup :bar + teardown :bar + + def test_inherited_setup_callbacks + assert_equal [:reset_callback_record, :foo, :bar], self.class._setup_callbacks.map(&:raw_filter) + assert_equal [:foo, :bar], @called_back + assert_equal [:foo, :sentinel, :bar], self.class._teardown_callbacks.map(&:raw_filter) + end + + protected + def bar + @called_back << :bar + end + + def sentinel + assert_equal [:foo, :bar, :bar], @called_back + end +end + +class TestCaseTaggedLoggingTest < ActiveSupport::TestCase + def before_setup + require 'stringio' + @out = StringIO.new + self.tagged_logger = ActiveSupport::TaggedLogging.new(Logger.new(@out)) + super + end + + def test_logs_tagged_with_current_test_case + assert_match "#{self.class}: #{name}\n", @out.string + end +end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb deleted file mode 100644 index c93192f207..0000000000 --- a/activesupport/test/test_test.rb +++ /dev/null @@ -1,174 +0,0 @@ -require 'abstract_unit' - -class AssertDifferenceTest < ActiveSupport::TestCase - def setup - @object = Class.new do - attr_accessor :num - def increment - self.num += 1 - end - - def decrement - self.num -= 1 - end - end.new - @object.num = 0 - end - - def test_assert_not - assert_equal true, assert_not(nil) - assert_equal true, assert_not(false) - - e = assert_raises(Minitest::Assertion) { assert_not true } - assert_equal 'Expected true to be nil or false', e.message - - e = assert_raises(Minitest::Assertion) { assert_not true, 'custom' } - assert_equal 'custom', e.message - end - - def test_assert_no_difference_pass - assert_no_difference '@object.num' do - # ... - end - end - - def test_assert_no_difference_fail - error = assert_raises(Minitest::Assertion) do - assert_no_difference '@object.num' do - @object.increment - end - end - assert_equal "\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message - end - - def test_assert_no_difference_with_message_fail - error = assert_raises(Minitest::Assertion) do - assert_no_difference '@object.num', 'Object Changed' do - @object.increment - end - end - assert_equal "Object Changed.\n\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message - end - - def test_assert_difference - assert_difference '@object.num', +1 do - @object.increment - end - end - - def test_assert_difference_with_implicit_difference - assert_difference '@object.num' do - @object.increment - end - end - - def test_arbitrary_expression - assert_difference '@object.num + 1', +2 do - @object.increment - @object.increment - end - end - - def test_negative_differences - assert_difference '@object.num', -1 do - @object.decrement - end - end - - def test_expression_is_evaluated_in_the_appropriate_scope - silence_warnings do - local_scope = local_scope = 'foo' - assert_difference('local_scope; @object.num') { @object.increment } - end - end - - def test_array_of_expressions - assert_difference [ '@object.num', '@object.num + 1' ], +1 do - @object.increment - end - end - - def test_array_of_expressions_identify_failure - assert_raises(Minitest::Assertion) do - assert_difference ['@object.num', '1 + 1'] do - @object.increment - end - end - end - - def test_array_of_expressions_identify_failure_when_message_provided - assert_raises(Minitest::Assertion) do - assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do - @object.increment - end - end - end -end - -class AlsoDoingNothingTest < ActiveSupport::TestCase -end - -# Setup and teardown callbacks. -class SetupAndTeardownTest < ActiveSupport::TestCase - setup :reset_callback_record, :foo - teardown :foo, :sentinel - - def test_inherited_setup_callbacks - assert_equal [:reset_callback_record, :foo], self.class._setup_callbacks.map(&:raw_filter) - assert_equal [:foo], @called_back - assert_equal [:foo, :sentinel], self.class._teardown_callbacks.map(&:raw_filter) - end - - def setup - end - - def teardown - end - - protected - - def reset_callback_record - @called_back = [] - end - - def foo - @called_back << :foo - end - - def sentinel - assert_equal [:foo], @called_back - end -end - -class SubclassSetupAndTeardownTest < SetupAndTeardownTest - setup :bar - teardown :bar - - def test_inherited_setup_callbacks - assert_equal [:reset_callback_record, :foo, :bar], self.class._setup_callbacks.map(&:raw_filter) - assert_equal [:foo, :bar], @called_back - assert_equal [:foo, :sentinel, :bar], self.class._teardown_callbacks.map(&:raw_filter) - end - - protected - def bar - @called_back << :bar - end - - def sentinel - assert_equal [:foo, :bar, :bar], @called_back - end -end - -class TestCaseTaggedLoggingTest < ActiveSupport::TestCase - def before_setup - require 'stringio' - @out = StringIO.new - self.tagged_logger = ActiveSupport::TaggedLogging.new(Logger.new(@out)) - super - end - - def test_logs_tagged_with_current_test_case - assert_match "#{self.class}: #{name}\n", @out.string - end -end -- cgit v1.2.3 From 24a9bc4fc8d0885c64e27a62fb2703416c6ab95d Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Tue, 19 Aug 2014 23:45:43 -0700 Subject: Add documentation intro to example for `Object#itself`. Also moved comment for removal above `unless()` to not confuse RDoc with the documentation for this method. --- activesupport/lib/active_support/core_ext/object/itself.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/object/itself.rb b/activesupport/lib/active_support/core_ext/object/itself.rb index adedc20169..d71cea6674 100644 --- a/activesupport/lib/active_support/core_ext/object/itself.rb +++ b/activesupport/lib/active_support/core_ext/object/itself.rb @@ -1,6 +1,9 @@ class Object - unless respond_to?(:itself) # TODO: Remove this file when we drop support for Ruby < 2.2 - # Returns the object itself. Useful when dealing with a chaining scenario, like Active Record scopes: + # TODO: Remove this file when we drop support for Ruby < 2.2 + unless respond_to?(:itself) + # Returns the object itself. + # + # Useful for chaining methods, such as Active Record scopes: # # Event.public_send(state.presence_in([ :trashed, :drafted ]) || :itself).order(:created_at) # -- cgit v1.2.3 From df773ca7c2258f1ae68e43314570ee9544d2cc7c Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Wed, 20 Aug 2014 11:02:10 +0300 Subject: Updated the deprecation warnings to 5.0 --- activesupport/lib/active_support/deprecation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index ab16977bda..46e9996d59 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -32,7 +32,7 @@ module ActiveSupport # and the second is a library name # # ActiveSupport::Deprecation.new('2.0', 'MyLibrary') - def initialize(deprecation_horizon = '4.2', gem_name = 'Rails') + def initialize(deprecation_horizon = '5.0', gem_name = 'Rails') self.gem_name = gem_name self.deprecation_horizon = deprecation_horizon # By default, warnings are not silenced and debugging is off. -- cgit v1.2.3 From 9ea80288f69eb775f095d74a644bdc432984cc85 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 20 Aug 2014 17:49:03 +0900 Subject: Ignore Postgresql "SELECT tablename FROM pg_tables" when counting queries --- activerecord/test/cases/test_case.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index 4070216733..eb44c4a83c 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -94,7 +94,7 @@ module ActiveRecord # instead examining the SQL content. oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im] mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /] - postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] + postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im] [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql| -- cgit v1.2.3 From 31c98ecc1d32bb1cc375640984775f7789944b67 Mon Sep 17 00:00:00 2001 From: Nick Sinenko Date: Wed, 20 Aug 2014 19:21:58 +1000 Subject: remove end-of-line spacing in development.rb, production.rb --- .../rails/app/templates/config/environments/development.rb.tt | 2 +- .../generators/rails/app/templates/config/environments/production.rb.tt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index 35e3035a0b..d8326d1728 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -30,7 +30,7 @@ Rails.application.configure do # number of complex assets. config.assets.debug = true - # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # Asset digests allow you to set far-future HTTP expiration dates on all assets, # yet still be able to expire them through the digest params. config.assets.digest = true diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index bec7716a9b..bbfd9505bc 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -30,7 +30,7 @@ Rails.application.configure do # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false - # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # Asset digests allow you to set far-future HTTP expiration dates on all assets, # yet still be able to expire them through the digest params. config.assets.digest = true -- cgit v1.2.3 From 0df2a83b7f3989aa987e55ac85fd2e189220fd57 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 20 Aug 2014 19:07:03 +0900 Subject: Clear deliveries in order not to affect other tests --- actionmailer/test/message_delivery_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb index a097d0e84d..1c7406b520 100644 --- a/actionmailer/test/message_delivery_test.rb +++ b/actionmailer/test/message_delivery_test.rb @@ -45,9 +45,11 @@ class MessageDeliveryTest < ActiveSupport::TestCase assert_respond_to @mail, :deliver_later! end - test 'should enqueue and run correctly in activejob' do + def test_should_enqueue_and_run_correctly_in_activejob @mail.deliver_later! - assert_equal 1 , ActionMailer::Base.deliveries.size + assert_equal 1, ActionMailer::Base.deliveries.size + ensure + ActionMailer::Base.deliveries.clear end test 'should enqueue the email with :deliver delivery method' do -- cgit v1.2.3 From 597a6666015d72c79dce4efb8d001a5fda18bcc1 Mon Sep 17 00:00:00 2001 From: Juanito Fatas Date: Wed, 20 Aug 2014 19:18:10 +0800 Subject: Revert "Do not gsub non ASCII characters in header anchor.". This reverts commit 699babe. Also change the upgrading_ruby_on_rails link back to original in 4_2_release_notes. --- guides/rails_guides/markdown.rb | 2 +- guides/source/4_2_release_notes.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb index 1ea18ba9f5..b422a80759 100644 --- a/guides/rails_guides/markdown.rb +++ b/guides/rails_guides/markdown.rb @@ -47,7 +47,7 @@ module RailsGuides end def dom_id_text(text) - text.downcase.gsub(/\?/, '-questionmark').gsub(/!/, '-bang').gsub(/\s+/, '-') + text.downcase.gsub(/\?/, '-questionmark').gsub(/!/, '-bang').gsub(/[^a-z0-9]+/, ' ').strip.gsub(/\s+/, '-') end def engine diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index d362c8cd58..aa056f5844 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -26,7 +26,7 @@ coverage before going in. You should also first upgrade to Rails 4.1 in case you haven't and make sure your application still runs as expected before attempting to upgrade to Rails 4.2. A list of things to watch out for when upgrading is available in the -[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-4.1-to-rails-4.2) +[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-4-1-to-rails-4-2) guide. -- cgit v1.2.3 From 5833163faf31aea07476b4ee9f8b82bc725bbe96 Mon Sep 17 00:00:00 2001 From: Juanito Fatas Date: Wed, 20 Aug 2014 21:33:06 +0800 Subject: [ci skip] Format pass of Active Job Basics guide. --- guides/source/active_job_basics.md | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index ae5d21d546..f7864f4961 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -13,6 +13,7 @@ After reading this guide, you will know: -------------------------------------------------------------------------------- + Introduction ------------ @@ -40,7 +41,7 @@ This section will provide a step-by-step guide to creating a job and enqueue it. ### Create the Job Active Job provides a Rails generator to create jobs. The following will create a -job in app/jobs: +job in `app/jobs`: ```bash $ bin/rails generate job guests_cleanup @@ -58,7 +59,7 @@ As you can see, you can generate jobs just like you use other generators with Rails. 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 `ActiveJob::Base`. Here's how a job looks like: @@ -135,11 +136,12 @@ You can easy change your adapter: YourApp::Application.config.active_job.queue_adapter = :sidekiq ``` + Queues ------ -Most of the adapters supports multiple queues. With Active Job you can schedule the job -to run on a specific queue: +Most of the adapters supports multiple queues. With Active Job you can schedule +the job to run on a specific queue: ```ruby class GuestsCleanupJob < ActiveJob::Base @@ -155,17 +157,17 @@ you need to specify the queues to listen to. Callbacks --------- -Active Job provides hooks during the lifecycle of a job. Callbacks allows you to trigger -logic during the lifecycle of a job. +Active Job provides hooks during the lifecycle of a job. Callbacks allows you to +trigger logic during the lifecycle of a job. ### Available callbacks -* before_enqueue -* around_enqueue -* after_enqueue -* before_perform -* around_perform -* after_perform +* `before_enqueue` +* `around_enqueue` +* `after_enqueue` +* `before_perform` +* `around_perform` +* `after_perform` ### Usage @@ -189,8 +191,10 @@ class GuestsCleanupJob < ActiveJob::Base end ``` + ActionMailer ------------ + One of the most common jobs in a modern web application is sending emails outside of the request-response cycle, so the user doesn't have to wait on it. Active Job is integrated with Action Mailer so you can easily send emails async: @@ -203,11 +207,12 @@ UserMailer.welcome(@user).deliver UserMailer.welcome(@user).deliver_later ``` + GlobalID -------- -Active Job supports GlobalID for parameters. This makes it possible -to pass live Active Record objects to your job instead of class/id pairs, which -you then have to manually deserialize. Before, jobs would look like this: +Active Job supports GlobalID for parameters. This makes it possible to pass live +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 @@ -228,12 +233,13 @@ class TrashableCleanupJob end ``` -This works with any class that mixes in ActiveModel::GlobalIdentification, which +This works with any class that mixes in `ActiveModel::GlobalIdentification`, which by default has been mixed into Active Model classes. Exceptions ---------- + Active Job provides a way to catch exceptions raised during the execution of the job: -- cgit v1.2.3 From ce8e4a43b353b7e36a1c9f98561dc7ed23e16c93 Mon Sep 17 00:00:00 2001 From: Alex Robbin Date: Tue, 12 Aug 2014 23:43:24 -0400 Subject: provide a builder for form labels to customize wrapping around I18n content --- actionview/CHANGELOG.md | 7 +++ actionview/lib/action_view/helpers/tags/label.rb | 68 ++++++++++++++++-------- actionview/test/template/form_helper_test.rb | 9 ++++ 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 552a902349..396249ac37 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,10 @@ +* Provide a `builder` object when using the `label` form helper in block form. + + The new `builder` object responds to `translation`, allowing I18n fallback support + when you want to customize how a particular label is presented. + + *Alex Robbin* + * Add I18n support for input/textarea placeholder text. Placeholder I18n follows the same convention as `label` I18n. diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb index 39b2f48c39..08a23e497e 100644 --- a/actionview/lib/action_view/helpers/tags/label.rb +++ b/actionview/lib/action_view/helpers/tags/label.rb @@ -2,6 +2,39 @@ module ActionView module Helpers module Tags # :nodoc: class Label < Base # :nodoc: + class LabelBuilder # :nodoc: + attr_reader :object + + def initialize(template_object, object_name, method_name, object, tag_value) + @template_object = template_object + @object_name = object_name + @method_name = method_name + @object = object + @tag_value = tag_value + end + + def translation + method_and_value = @tag_value.present? ? "#{@method_name}.#{@tag_value}" : @method_name + @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1') + + if object.respond_to?(:to_model) + key = object.model_name.i18n_key + i18n_default = ["#{key}.#{method_and_value}".to_sym, ""] + end + + i18n_default ||= "" + content = I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence + + content ||= if object && object.class.respond_to?(:human_attribute_name) + object.class.human_attribute_name(method_and_value) + end + + content ||= @method_name.humanize + + content + end + end + def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil) options ||= {} @@ -32,33 +65,24 @@ module ActionView options.delete("namespace") options["for"] = name_and_id["id"] unless options.key?("for") - if block_given? - content = @template_object.capture(&block) - else - method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name - content = if @content.blank? - @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1') - - if object.respond_to?(:to_model) - key = object.model_name.i18n_key - i18n_default = ["#{key}.#{method_and_value}".to_sym, ""] - end - - i18n_default ||= "" - I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence - else - @content.to_s - end - - content ||= if object && object.class.respond_to?(:human_attribute_name) - object.class.human_attribute_name(method_and_value) - end + builder = LabelBuilder.new(@template_object, @object_name, @method_name, @object, tag_value) - content ||= @method_name.humanize + content = if block_given? + @template_object.capture(builder, &block) + elsif @content.present? + @content.to_s + else + render_component(builder) end label_tag(name_and_id["id"], content, options) end + + private + + def render_component(builder) + builder.translation + end end end end diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index d944214961..4cb2abbd85 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -319,6 +319,15 @@ class FormHelperTest < ActionView::TestCase ) end + def test_label_with_block_and_builder + with_locale :label do + assert_dom_equal( + '', + label(:post, :body) { |b| "#{b.translation}".html_safe } + ) + end + end + def test_label_with_block_in_erb assert_equal( %{}, -- cgit v1.2.3 From 500deece9ef511c59a8c6edc7fb8682b13c4cd7e Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 20 Aug 2014 16:10:37 +0200 Subject: Fixes the digits counter of AS's NumberToRoundedConverter Zero has one digit, but Math.log10(0) returns -Infinity. The method needs to special-case zero. The patch adds a regression test that is not clearly related to the underlying issue because digit_count is private and has no coverage. Gray area. This bug was uncovered by 60062cf. --- .../lib/active_support/number_helper/number_to_rounded_converter.rb | 2 +- activesupport/test/number_helper_test.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb index 01597b288a..47028e9ecf 100644 --- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb +++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb @@ -59,7 +59,7 @@ module ActiveSupport end def digit_count(number) - (Math.log10(absolute_number(number)) + 1).floor + number.zero? ? 1 : (Math.log10(absolute_number(number)) + 1).floor end def strip_insignificant_zeros diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb index bb51cc68f2..50d84a9470 100644 --- a/activesupport/test/number_helper_test.rb +++ b/activesupport/test/number_helper_test.rb @@ -135,6 +135,7 @@ module ActiveSupport assert_equal("111.23460000000000000000", number_helper.number_to_rounded(BigDecimal(111.2346, Float::DIG), :precision => 20)) assert_equal("111.2346" + "0"*96, number_helper.number_to_rounded('111.2346', :precision => 100)) assert_equal("111.2346", number_helper.number_to_rounded(Rational(1112346, 10000), :precision => 4)) + assert_equal('0.00', number_helper.number_to_rounded(Rational(0, 1), :precision => 2)) end end -- cgit v1.2.3 From f4ee114746ddc68db606f63e17e6de28274fc2bd Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Wed, 20 Aug 2014 11:34:57 +0300 Subject: Deprecated .deliver / .deliver! to .deliver_now / .deliver_now! --- actionmailer/lib/action_mailer/message_delivery.rb | 24 +++++++++++++-- actionmailer/test/assert_select_email_test.rb | 4 +-- actionmailer/test/base_test.rb | 36 +++++++++++----------- actionmailer/test/delivery_methods_test.rb | 24 +++++++-------- actionmailer/test/i18n_with_controller_test.rb | 2 +- actionmailer/test/log_subscriber_test.rb | 2 +- actionmailer/test/message_delivery_test.rb | 29 ++++++++++++++--- actionmailer/test/test_helper_test.rb | 22 ++++++------- actionmailer/test/url_test.rb | 2 +- 9 files changed, 92 insertions(+), 53 deletions(-) diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index 80a0517bff..8136483725 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -21,11 +21,31 @@ module ActionMailer end def deliver_later!(options={}) - enqueue_delivery :deliver!, options + enqueue_delivery :deliver_now!, options end def deliver_later(options={}) - enqueue_delivery :deliver, options + enqueue_delivery :deliver_now, options + end + + def deliver_now! + message.deliver! + end + + def deliver_now + message.deliver + end + + def deliver! + ActiveSupport::Deprecation.warn "#deliver! is deprecated and will be removed on Rails 5. " \ + "Use #deliver_now! to deliver immediately or #deliver_later! to deliver through ActiveJob" + deliver_now! + end + + def deliver + ActiveSupport::Deprecation.warn "#deliver is deprecated and will be removed on Rails 5. " \ + "Use #deliver_now to deliver immediately or #deliver_later to deliver through ActiveJob" + deliver_now end private diff --git a/actionmailer/test/assert_select_email_test.rb b/actionmailer/test/assert_select_email_test.rb index 57ae3436e1..cae2e20abd 100644 --- a/actionmailer/test/assert_select_email_test.rb +++ b/actionmailer/test/assert_select_email_test.rb @@ -26,7 +26,7 @@ class AssertSelectEmailTest < ActionMailer::TestCase assert_select_email {} end - AssertSelectMailer.test("

foo

bar

").deliver + AssertSelectMailer.test("

foo

bar

").deliver_now assert_select_email do assert_select "div:root" do assert_select "p:first-child", "foo" @@ -36,7 +36,7 @@ class AssertSelectEmailTest < ActionMailer::TestCase end def test_assert_select_email_multipart - AssertMultipartSelectMailer.test(html: "

foo

bar

", text: 'foo bar').deliver + AssertMultipartSelectMailer.test(html: "

foo

bar

", text: 'foo bar').deliver_now assert_select_email do assert_select "div:root" do assert_select "p:first-child", "foo" diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index fc24639bf4..d00f5aea47 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -466,12 +466,12 @@ class BaseTest < ActiveSupport::TestCase test "calling deliver on the action should deliver the mail object" do BaseMailer.expects(:deliver_mail).once - mail = BaseMailer.welcome.deliver + mail = BaseMailer.welcome.deliver_now assert_equal 'The first email on new API!', mail.subject end test "calling deliver on the action should increment the deliveries collection if using the test mailer" do - BaseMailer.welcome.deliver + BaseMailer.welcome.deliver_now assert_equal(1, BaseMailer.deliveries.length) end @@ -484,35 +484,35 @@ class BaseTest < ActiveSupport::TestCase # Rendering test "you can specify a different template for implicit render" do - mail = BaseMailer.implicit_different_template('implicit_multipart').deliver + mail = BaseMailer.implicit_different_template('implicit_multipart').deliver_now assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded) assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded) end test "should raise if missing template in implicit render" do assert_raises ActionView::MissingTemplate do - BaseMailer.implicit_different_template('missing_template').deliver + BaseMailer.implicit_different_template('missing_template').deliver_now end assert_equal(0, BaseMailer.deliveries.length) end test "you can specify a different template for explicit render" do - mail = BaseMailer.explicit_different_template('explicit_multipart_templates').deliver + mail = BaseMailer.explicit_different_template('explicit_multipart_templates').deliver_now assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded) assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded) end test "you can specify a different layout" do - mail = BaseMailer.different_layout('different_layout').deliver + mail = BaseMailer.different_layout('different_layout').deliver_now assert_equal("HTML -- HTML", mail.html_part.body.decoded) assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded) end test "you can specify the template path for implicit lookup" do - mail = BaseMailer.welcome_from_another_path('another.path/base_mailer').deliver + mail = BaseMailer.welcome_from_another_path('another.path/base_mailer').deliver_now assert_equal("Welcome from another path", mail.body.encoded) - mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer']).deliver + mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer']).deliver_now assert_equal("Welcome from another path", mail.body.encoded) end @@ -542,13 +542,13 @@ class BaseTest < ActiveSupport::TestCase test 'the view is not rendered when mail was never called' do mail = BaseMailer.without_mail_call assert_equal('', mail.body.to_s.strip) - mail.deliver + mail.deliver_now end test 'the return value of mailer methods is not relevant' do mail = BaseMailer.with_nil_as_return_value assert_equal('Welcome', mail.body.to_s.strip) - mail.deliver + mail.deliver_now end # Before and After hooks @@ -568,7 +568,7 @@ class BaseTest < ActiveSupport::TestCase ActionMailer::Base.register_observer(MyObserver) mail = BaseMailer.welcome MyObserver.expects(:delivered_email).with(mail) - mail.deliver + mail.deliver_now end end @@ -577,7 +577,7 @@ class BaseTest < ActiveSupport::TestCase ActionMailer::Base.register_observer("BaseTest::MyObserver") mail = BaseMailer.welcome MyObserver.expects(:delivered_email).with(mail) - mail.deliver + mail.deliver_now end end @@ -586,7 +586,7 @@ class BaseTest < ActiveSupport::TestCase ActionMailer::Base.register_observer(:"base_test/my_observer") mail = BaseMailer.welcome MyObserver.expects(:delivered_email).with(mail) - mail.deliver + mail.deliver_now end end @@ -596,7 +596,7 @@ class BaseTest < ActiveSupport::TestCase mail = BaseMailer.welcome MyObserver.expects(:delivered_email).with(mail) MySecondObserver.expects(:delivered_email).with(mail) - mail.deliver + mail.deliver_now end end @@ -615,7 +615,7 @@ class BaseTest < ActiveSupport::TestCase ActionMailer::Base.register_interceptor(MyInterceptor) mail = BaseMailer.welcome MyInterceptor.expects(:delivering_email).with(mail) - mail.deliver + mail.deliver_now end end @@ -624,7 +624,7 @@ class BaseTest < ActiveSupport::TestCase ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor") mail = BaseMailer.welcome MyInterceptor.expects(:delivering_email).with(mail) - mail.deliver + mail.deliver_now end end @@ -633,7 +633,7 @@ class BaseTest < ActiveSupport::TestCase ActionMailer::Base.register_interceptor(:"base_test/my_interceptor") mail = BaseMailer.welcome MyInterceptor.expects(:delivering_email).with(mail) - mail.deliver + mail.deliver_now end end @@ -643,7 +643,7 @@ class BaseTest < ActiveSupport::TestCase mail = BaseMailer.welcome MyInterceptor.expects(:delivering_email).with(mail) MySecondInterceptor.expects(:delivering_email).with(mail) - mail.deliver + mail.deliver_now end end diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb index a76ac6d295..2e4e019bf7 100644 --- a/actionmailer/test/delivery_methods_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -104,21 +104,21 @@ class MailDeliveryTest < ActiveSupport::TestCase test "ActionMailer should be told when Mail gets delivered" do DeliveryMailer.expects(:deliver_mail).once - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now end test "delivery method can be customized per instance" do Mail::SMTP.any_instance.expects(:deliver!) - email = DeliveryMailer.welcome.deliver + email = DeliveryMailer.welcome.deliver_now assert_instance_of Mail::SMTP, email.delivery_method - email = DeliveryMailer.welcome(delivery_method: :test).deliver + email = DeliveryMailer.welcome(delivery_method: :test).deliver_now assert_instance_of Mail::TestMailer, email.delivery_method end test "delivery method can be customized in subclasses not changing the parent" do DeliveryMailer.delivery_method = :test assert_equal :smtp, ActionMailer::Base.delivery_method - email = DeliveryMailer.welcome.deliver + email = DeliveryMailer.welcome.deliver_now assert_instance_of Mail::TestMailer, email.delivery_method end @@ -162,14 +162,14 @@ class MailDeliveryTest < ActiveSupport::TestCase test "non registered delivery methods raises errors" do DeliveryMailer.delivery_method = :unknown assert_raise RuntimeError do - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now end end test "undefined delivery methods raises errors" do DeliveryMailer.delivery_method = nil assert_raise RuntimeError do - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now end end @@ -178,7 +178,7 @@ class MailDeliveryTest < ActiveSupport::TestCase begin DeliveryMailer.perform_deliveries = false Mail::Message.any_instance.expects(:deliver!).never - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now ensure DeliveryMailer.perform_deliveries = old_perform_deliveries end @@ -188,7 +188,7 @@ class MailDeliveryTest < ActiveSupport::TestCase old_perform_deliveries = DeliveryMailer.perform_deliveries begin DeliveryMailer.perform_deliveries = false - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now assert_equal [], DeliveryMailer.deliveries ensure DeliveryMailer.perform_deliveries = old_perform_deliveries @@ -198,14 +198,14 @@ class MailDeliveryTest < ActiveSupport::TestCase test "raise errors on bogus deliveries" do DeliveryMailer.delivery_method = BogusDelivery assert_raise RuntimeError do - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now end end test "does not increment the deliveries collection on error" do DeliveryMailer.delivery_method = BogusDelivery assert_raise RuntimeError do - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now end assert_equal [], DeliveryMailer.deliveries end @@ -216,7 +216,7 @@ class MailDeliveryTest < ActiveSupport::TestCase DeliveryMailer.delivery_method = BogusDelivery DeliveryMailer.raise_delivery_errors = false assert_nothing_raised do - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now end ensure DeliveryMailer.raise_delivery_errors = old_raise_delivery_errors @@ -228,7 +228,7 @@ class MailDeliveryTest < ActiveSupport::TestCase begin DeliveryMailer.delivery_method = BogusDelivery DeliveryMailer.raise_delivery_errors = false - DeliveryMailer.welcome.deliver + DeliveryMailer.welcome.deliver_now assert_equal [], DeliveryMailer.deliveries ensure DeliveryMailer.raise_delivery_errors = old_raise_delivery_errors diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb index ee36b89dd6..010e44d045 100644 --- a/actionmailer/test/i18n_with_controller_test.rb +++ b/actionmailer/test/i18n_with_controller_test.rb @@ -17,7 +17,7 @@ end class TestController < ActionController::Base def send_mail - email = I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver + email = I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver_now render text: "Mail sent - Subject: #{email.subject}" end end diff --git a/actionmailer/test/log_subscriber_test.rb b/actionmailer/test/log_subscriber_test.rb index e7a73d6c8e..3871b16840 100644 --- a/actionmailer/test/log_subscriber_test.rb +++ b/actionmailer/test/log_subscriber_test.rb @@ -22,7 +22,7 @@ class AMLogSubscriberTest < ActionMailer::TestCase end def test_deliver_is_notified - BaseMailer.welcome.deliver + BaseMailer.welcome.deliver_now wait assert_equal(1, @logger.logged(:info).size) diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb index 1c7406b520..49b63d7675 100644 --- a/actionmailer/test/message_delivery_test.rb +++ b/actionmailer/test/message_delivery_test.rb @@ -37,6 +37,17 @@ class MessageDeliveryTest < ActiveSupport::TestCase assert_respond_to @mail, :deliver! end + test '.deliver is deprecated' do + assert_deprecated do + @mail.deliver + end + end + test '.deliver! is deprecated' do + assert_deprecated do + @mail.deliver! + end + end + test 'should respond to .deliver_later' do assert_respond_to @mail, :deliver_later end @@ -45,7 +56,15 @@ class MessageDeliveryTest < ActiveSupport::TestCase assert_respond_to @mail, :deliver_later! end - def test_should_enqueue_and_run_correctly_in_activejob + test 'should respond to .deliver_now' do + assert_respond_to @mail, :deliver_now + end + + test 'should respond to .deliver_now!' do + assert_respond_to @mail, :deliver_now! + end + + test 'should enqueue and run correctly in activejob' do @mail.deliver_later! assert_equal 1, ActionMailer::Base.deliveries.size ensure @@ -56,21 +75,21 @@ class MessageDeliveryTest < ActiveSupport::TestCase ret = ActionMailer::DeliveryJob.stub :enqueue, ->(*args){ args } do @mail.deliver_later end - assert_equal ['DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret + assert_equal ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3], ret end test 'should enqueue the email with :deliver! delivery method' do ret = ActionMailer::DeliveryJob.stub :enqueue, ->(*args){ args } do @mail.deliver_later! end - assert_equal ['DelayedMailer', 'test_message', 'deliver!', 1, 2, 3], ret + assert_equal ['DelayedMailer', 'test_message', 'deliver_now!', 1, 2, 3], ret end test 'should enqueue a delivery with a delay' do ret = ActionMailer::DeliveryJob.stub :enqueue_in, ->(*args){ args } do @mail.deliver_later in: 600 end - assert_equal [600, 'DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret + assert_equal [600, 'DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3], ret end test 'should enqueue a delivery at a specific time' do @@ -78,7 +97,7 @@ class MessageDeliveryTest < ActiveSupport::TestCase ret = ActionMailer::DeliveryJob.stub :enqueue_at, ->(*args){ args } do @mail.deliver_later at: later_time end - assert_equal [later_time, 'DelayedMailer', 'test_message', 'deliver', 1, 2, 3], ret + assert_equal [later_time, 'DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3], ret end end diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb index 1ff08a3b6e..96b75ff2e0 100644 --- a/actionmailer/test/test_helper_test.rb +++ b/actionmailer/test/test_helper_test.rb @@ -48,7 +48,7 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_emails assert_nothing_raised do assert_emails 1 do - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now end end end @@ -56,27 +56,27 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_repeated_assert_emails_calls assert_nothing_raised do assert_emails 1 do - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now end end assert_nothing_raised do assert_emails 2 do - TestHelperMailer.test.deliver - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now + TestHelperMailer.test.deliver_now end end end def test_assert_emails_with_no_block assert_nothing_raised do - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now assert_emails 1 end assert_nothing_raised do - TestHelperMailer.test.deliver - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now + TestHelperMailer.test.deliver_now assert_emails 3 end end @@ -92,7 +92,7 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_emails_too_few_sent error = assert_raise ActiveSupport::TestCase::Assertion do assert_emails 2 do - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now end end @@ -102,8 +102,8 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_emails_too_many_sent error = assert_raise ActiveSupport::TestCase::Assertion do assert_emails 1 do - TestHelperMailer.test.deliver - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now + TestHelperMailer.test.deliver_now end end @@ -113,7 +113,7 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_no_emails_failure error = assert_raise ActiveSupport::TestCase::Assertion do assert_no_emails do - TestHelperMailer.test.deliver + TestHelperMailer.test.deliver_now end end diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb index 589944fa69..e79b2ca978 100644 --- a/actionmailer/test/url_test.rb +++ b/actionmailer/test/url_test.rb @@ -68,7 +68,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase created.message_id = '<123@456>' assert_equal expected.encoded, created.encoded - assert_nothing_raised { UrlTestMailer.signed_up_with_url(@recipient).deliver } + assert_nothing_raised { UrlTestMailer.signed_up_with_url(@recipient).deliver_now } assert_not_nil ActionMailer::Base.deliveries.first delivered = ActionMailer::Base.deliveries.first -- cgit v1.2.3 From 9e7f4a94caa9019b2ef0295ad242fbed0acf8bb6 Mon Sep 17 00:00:00 2001 From: Cristian Bica Date: Wed, 20 Aug 2014 15:34:37 +0300 Subject: Updated rdoc / guides / release notes related to ActiveJob / ActionMailer --- actionmailer/CHANGELOG.md | 10 ++- actionmailer/README.rdoc | 6 +- actionmailer/lib/action_mailer/base.rb | 21 +++-- actionmailer/lib/action_mailer/delivery_job.rb | 4 +- actionmailer/lib/action_mailer/message_delivery.rb | 89 +++++++++++++++++----- actionmailer/lib/action_mailer/test_helper.rb | 12 +-- guides/source/4_2_release_notes.md | 3 + guides/source/action_mailer_basics.md | 32 +++++++- guides/source/active_job_basics.md | 6 +- guides/source/active_record_querying.md | 10 +-- guides/source/testing.md | 2 +- 11 files changed, 141 insertions(+), 54 deletions(-) diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index a4923de045..1ff4db41a3 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,7 +1,9 @@ -* Added #deliver_later in addition to #deliver, which will enqueue a job to render and - deliver the mail instead of delivering it right at that moment. The job is enqueued - using the new Active Job framework in Rails, and will use whatever queue is configured for Rails. - +* Added #deliver_later, #deliver_now and deprecate #deliver in favour of + #deliver_now. #deliver_later will enqueue a job to render and deliver + the mail instead of delivering it right at that moment. The job is enqueued + using the new Active Job framework in Rails, and will use whatever queue is + configured for Rails. + *DHH/Abdelkader Boudih/Cristian Bica* * Make ActionMailer::Previews methods class methods. Previously they were diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc index ceca912ada..3b8e3ed749 100644 --- a/actionmailer/README.rdoc +++ b/actionmailer/README.rdoc @@ -65,12 +65,12 @@ In order to send mails, you simply call the method and then call +deliver+ on th Calling the method returns a Mail Message object: - message = Notifier.welcome("david@loudthinking.com") # => Returns a Mail::Message object - message.deliver # => delivers the email + message = Notifier.welcome("david@loudthinking.com") # => Returns a Mail::Message object + message.deliver_now # => delivers the email Or you can just chain the methods together like: - Notifier.welcome("david@loudthinking.com").deliver # Creates the email and sends it immediately + Notifier.welcome("david@loudthinking.com").deliver_now # Creates the email and sends it immediately == Setting defaults diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 9aae14ec8c..fc2d0936f9 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -138,9 +138,20 @@ module ActionMailer # Once a mailer action and template are defined, you can deliver your message or create it and save it # for delivery later: # - # Notifier.welcome(david).deliver # sends the email - # mail = Notifier.welcome(david) # => a Mail::Message object - # mail.deliver # sends the email + # Notifier.welcome(david).deliver_now # sends the email + # mail = Notifier.welcome(david) # => an ActionMailer::MessageDeliver object + # mail.deliver_now # sends the email + # + # The ActionMailer::MessageDeliver class is a wrapper around a Mail::Message object. If + # you want direct access to the Mail::Message object you can call the message method on + # the ActionMailer::MessageDeliver object. + # + # Notifier.welcome(david).message # => a Mail::Message object + # + # ActionMailer is nicely integrated with ActiveJob so you can send emails in the background (example: outside + # of the request-response cycle, so the user doesn't have to wait on it): + # + # Notifier.welcome(david).deliver_later # enqueue the email sending to ActiveJob # # You never instantiate your mailer class. Rather, you just call the method you defined on the class itself. # @@ -322,8 +333,8 @@ module ActionMailer # end # # Methods must return a Mail::Message object which can be generated by calling the mailer - # method without the additional deliver. The location of the mailer previews - # directory can be configured using the preview_path option which has a default + # method without the additional deliver_now / deliver_later. The location of the + # mailer previews directory can be configured using the preview_path option which has a default # of test/mailers/previews: # # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews" diff --git a/actionmailer/lib/action_mailer/delivery_job.rb b/actionmailer/lib/action_mailer/delivery_job.rb index b2cfa245fd..622f202695 100644 --- a/actionmailer/lib/action_mailer/delivery_job.rb +++ b/actionmailer/lib/action_mailer/delivery_job.rb @@ -1,10 +1,10 @@ require 'active_job' module ActionMailer - class DeliveryJob < ActiveJob::Base + class DeliveryJob < ActiveJob::Base #:nodoc: queue_as :mailers - def perform(mailer, mail_method, delivery_method, *args) + def perform(mailer, mail_method, delivery_method, *args) #:nodoc# mailer.constantize.public_send(mail_method, *args).send(delivery_method) end end diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index 8136483725..e65e79e741 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -1,65 +1,112 @@ require 'delegate' module ActionMailer + + # The ActionMailer::MessageDeliver class is used by ActionMailer::Base when + # creating a new mailer. MessageDeliver is a wrapper (Delegator subclass) + # around a lazy created Mail::Message. You can get direct access to the + # Mail::Message, deliver the email or schedule the email to be sent through ActiveJob. + # + # Notifier.welcome(david) # an ActionMailer::MessageDeliver object + # Notifier.welcome(david).deliver_now # sends the email + # Notifier.welcome(david).deliver_later # enqueue the deliver email job to ActiveJob + # Notifier.welcome(david).message # a Mail::Message object class MessageDelivery < Delegator - def initialize(mailer, mail_method, *args) + def initialize(mailer, mail_method, *args) #:nodoc: @mailer = mailer @mail_method = mail_method @args = args end - def __getobj__ + def __getobj__ #:nodoc: @obj ||= @mailer.send(:new, @mail_method, *@args).message end - def __setobj__(obj) + def __setobj__(obj) #:nodoc: @obj = obj end - def message #:nodoc: + # Returns the Mail::Message object + def message __getobj__ end + # Enqueues the message to be delivered through ActiveJob. When the + # ActiveJob job runs it will send the email using #deliver_now!. That + # means that the message will be sent bypassing checking perform_deliveries + # and raise_delivery_errors, so use with caution. + # + # ==== Examples + # + # Notifier.welcome(david).deliver_later + # Notifier.welcome(david).deliver_later(in: 1.hour) + # Notifier.welcome(david).deliver_later(at: 10.hours.from_now) + # + # ==== Options + # * in - Enqueue the message to be delivered with a delay + # * at - Enqueue the message to be delivered at (after) a specific date / time def deliver_later!(options={}) enqueue_delivery :deliver_now!, options end + # Enqueues the message to be delivered through ActiveJob. When the + # ActiveJob job runs it will send the email using #deliver_now. + # + # ==== Examples + # + # Notifier.welcome(david).deliver_later + # Notifier.welcome(david).deliver_later(in: 1.hour) + # Notifier.welcome(david).deliver_later(at: 10.hours.from_now) + # + # ==== Options + # * in - Enqueue the message to be delivered with a delay + # * at - Enqueue the message to be delivered at (after) a specific date / time def deliver_later(options={}) enqueue_delivery :deliver_now, options end + # Delivers a message. The message will be sent bypassing checking perform_deliveries + # and raise_delivery_errors, so use with caution. + # + # Notifier.welcome(david).deliver_now! + # def deliver_now! message.deliver! end + # Delivers a message: + # + # Notifier.welcome(david).deliver_now + # def deliver_now message.deliver end - def deliver! - ActiveSupport::Deprecation.warn "#deliver! is deprecated and will be removed on Rails 5. " \ - "Use #deliver_now! to deliver immediately or #deliver_later! to deliver through ActiveJob" + def deliver! #:nodoc: + ActiveSupport::Deprecation.warn "#deliver! is deprecated and will be removed in Rails 5. " \ + "Use #deliver_now! to deliver immediately or #deliver_later! to deliver through ActiveJob." deliver_now! end - def deliver - ActiveSupport::Deprecation.warn "#deliver is deprecated and will be removed on Rails 5. " \ - "Use #deliver_now to deliver immediately or #deliver_later to deliver through ActiveJob" + def deliver #:nodoc: + ActiveSupport::Deprecation.warn "#deliver is deprecated and will be removed in Rails 5. " \ + "Use #deliver_now to deliver immediately or #deliver_later to deliver through ActiveJob." deliver_now end private - def enqueue_delivery(delivery_method, options={}) - args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args - enqueue_method = :enqueue - if options[:at] - enqueue_method = :enqueue_at - args.unshift options[:at] - elsif options[:in] - enqueue_method = :enqueue_in - args.unshift options[:in] + + def enqueue_delivery(delivery_method, options={}) + args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args + enqueue_method = :enqueue + if options[:at] + enqueue_method = :enqueue_at + args.unshift options[:at] + elsif options[:in] + enqueue_method = :enqueue_in + args.unshift options[:in] + end + ActionMailer::DeliveryJob.send enqueue_method, *args end - ActionMailer::DeliveryJob.send enqueue_method, *args - end end end diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb index 06da0dd27e..6ddacf7b79 100644 --- a/actionmailer/lib/action_mailer/test_helper.rb +++ b/actionmailer/lib/action_mailer/test_helper.rb @@ -6,9 +6,9 @@ module ActionMailer # # def test_emails # assert_emails 0 - # ContactMailer.welcome.deliver + # ContactMailer.welcome.deliver_now # assert_emails 1 - # ContactMailer.welcome.deliver + # ContactMailer.welcome.deliver_now # assert_emails 2 # end # @@ -17,12 +17,12 @@ module ActionMailer # # def test_emails_again # assert_emails 1 do - # ContactMailer.welcome.deliver + # ContactMailer.welcome.deliver_now # end # # assert_emails 2 do - # ContactMailer.welcome.deliver - # ContactMailer.welcome.deliver + # ContactMailer.welcome.deliver_now + # ContactMailer.welcome.deliver_now # end # end def assert_emails(number) @@ -40,7 +40,7 @@ module ActionMailer # # def test_emails # assert_no_emails - # ContactMailer.welcome.deliver + # ContactMailer.welcome.deliver_now # assert_emails 1 # end # diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index aa056f5844..864026e9f2 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -311,6 +311,9 @@ Please refer to the [Changelog][action-mailer] for detailed changes. * Deprecated `*_path` helpers in mailers. Always use `*_url` helpers instead. ([Pull Request](https://github.com/rails/rails/pull/15840)) +* Deprecated `deliver` / `deliver!` in favour of `deliver_now` / `deliver_now!`. + ([Pull Request](https://github.com/rails/rails/pull/16582)) + ### Notable changes * Introduced `deliver_later` which enqueues a job on the application's queue diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index 9ad9319255..6b6ce145e4 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -159,7 +159,10 @@ $ bin/rake db:migrate Now that we have a user model to play with, we will just edit the `app/controllers/users_controller.rb` make it instruct the `UserMailer` to deliver an email to the newly created user by editing the create action and inserting a -call to `UserMailer.welcome_email` right after the user is successfully saved: +call to `UserMailer.welcome_email` right after the user is successfully saved. + +Action Mailer is nicely integrated with Active Job so you can send emails outside +of the request-response cycle, so the user doesn't have to wait on it: ```ruby class UsersController < ApplicationController @@ -171,7 +174,7 @@ class UsersController < ApplicationController respond_to do |format| if @user.save # Tell the UserMailer to send a welcome email after save - UserMailer.welcome_email(@user).deliver + UserMailer.welcome_email(@user).deliver_later format.html { redirect_to(@user, notice: 'User was successfully created.') } format.json { render json: @user, status: :created, location: @user } @@ -184,8 +187,29 @@ class UsersController < ApplicationController end ``` -The method `welcome_email` returns a `Mail::Message` object which can then just -be told `deliver` to send itself out. +NOTE: By default Active Job is configured to execute the job `:inline`. So you can +use `deliver_later` now to send the emails and when you decide to start sending the +email from a background job you'll just have to setup Active Job to use a queueing +backend (Sidekiq, Resque, etc). + +If you want to send the emails right away (from a cronjob for example) just +call `deliver_now`: + +```ruby +class SendWeeklySummary + def run + User.find_each do |user| + UserMailer.weekly_summary(user).deliver_now + end + end +end +``` + +The method `welcome_email` returns a `ActionMailer::MessageDelivery` object which +can then just be told `deliver_now` or `deliver_later` to send itself out. The +`ActionMailer::MessageDelivery` object is just a wrapper around a `Mail::Message`. If +you want to inspect, alter or do anything else with the `Mail::Message` object you can +access it with the `message` method on the `ActionMailer::MessageDelivery` object. ### Auto encoding header values diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index ae5d21d546..0df4320e36 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -196,10 +196,10 @@ of the request-response cycle, so the user doesn't have to wait on it. Active Jo is integrated with Action Mailer so you can easily send emails async: ```ruby -# Instead of the classic -UserMailer.welcome(@user).deliver +# If you want to send the email now use #deliver_now +UserMailer.welcome(@user).deliver_now -# use #deliver later to send the email async +# If you want to send the email through ActiveJob use #deliver_later UserMailer.welcome(@user).deliver_later ``` diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index f9b46286c1..cb243c95f5 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -276,7 +276,7 @@ This may appear straightforward: ```ruby # This is very inefficient when the users table has thousands of rows. User.all.each do |user| - NewsMailer.weekly(user).deliver + NewsMailer.weekly(user).deliver_now end ``` @@ -292,7 +292,7 @@ The `find_each` method retrieves a batch of records and then yields _each_ recor ```ruby User.find_each do |user| - NewsMailer.weekly(user).deliver + NewsMailer.weekly(user).deliver_now end ``` @@ -300,7 +300,7 @@ To add conditions to a `find_each` operation you can chain other Active Record m ```ruby User.where(weekly_subscriber: true).find_each do |user| - NewsMailer.weekly(user).deliver + NewsMailer.weekly(user).deliver_now end ``` @@ -316,7 +316,7 @@ The `:batch_size` option allows you to specify the number of records to be retri ```ruby User.find_each(batch_size: 5000) do |user| - NewsMailer.weekly(user).deliver + NewsMailer.weekly(user).deliver_now end ``` @@ -328,7 +328,7 @@ For example, to send newsletters only to users with the primary key starting fro ```ruby User.find_each(start: 2000, batch_size: 5000) do |user| - NewsMailer.weekly(user).deliver + NewsMailer.weekly(user).deliver_now end ``` diff --git a/guides/source/testing.md b/guides/source/testing.md index 2ecd560a87..d91f13699a 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -949,7 +949,7 @@ class UserMailerTest < ActionMailer::TestCase test "invite" do # Send the email, then test that it got queued email = UserMailer.create_invite('me@example.com', - 'friend@example.com', Time.now).deliver + 'friend@example.com', Time.now).deliver_now assert_not ActionMailer::Base.deliveries.empty? # Test the body of the sent email contains what we expect it to -- cgit v1.2.3 From 2eacdb0cbf558a46faeb42abc2df02adc4f677ae Mon Sep 17 00:00:00 2001 From: slainer68 Date: Wed, 20 Aug 2014 17:28:23 +0200 Subject: Require ActiveJob in case a skip_xxx option is given Change position of require active_job --- railties/lib/rails/generators/rails/app/templates/config/application.rb | 1 + railties/test/generators/app_generator_test.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 23db8b5153..80393a16ba 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -5,6 +5,7 @@ require 'rails/all' <% else -%> # Pick the frameworks you want: require "active_model/railtie" +require "active_job/railtie" <%= comment_if :skip_active_record %>require "active_record/railtie" require "action_controller/railtie" require "action_mailer/railtie" diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 70c439672f..d687a8f506 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -395,6 +395,7 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--skip-test-unit", "--skip-active-record"] assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/ assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ + assert_file "config/application.rb", /\s+require\s+["']active_job\/railtie["']/ end def test_new_hash_style -- cgit v1.2.3 From d1629346b9506a013315525afac113d9b2bfb47c Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 20 Aug 2014 16:22:41 +0000 Subject: Fix typo in rdoc [ci skip] --- actionmailer/lib/action_mailer/base.rb | 8 ++++---- actionmailer/lib/action_mailer/message_delivery.rb | 24 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index fc2d0936f9..0e4497536d 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -138,20 +138,20 @@ module ActionMailer # Once a mailer action and template are defined, you can deliver your message or create it and save it # for delivery later: # - # Notifier.welcome(david).deliver_now # sends the email - # mail = Notifier.welcome(david) # => an ActionMailer::MessageDeliver object + # Notifier.welcome('david').deliver_now # sends the email + # mail = Notifier.welcome('david') # => an ActionMailer::MessageDeliver object # mail.deliver_now # sends the email # # The ActionMailer::MessageDeliver class is a wrapper around a Mail::Message object. If # you want direct access to the Mail::Message object you can call the message method on # the ActionMailer::MessageDeliver object. # - # Notifier.welcome(david).message # => a Mail::Message object + # Notifier.welcome('david').message # => a Mail::Message object # # ActionMailer is nicely integrated with ActiveJob so you can send emails in the background (example: outside # of the request-response cycle, so the user doesn't have to wait on it): # - # Notifier.welcome(david).deliver_later # enqueue the email sending to ActiveJob + # Notifier.welcome('david').deliver_later # enqueue the email sending to ActiveJob # # You never instantiate your mailer class. Rather, you just call the method you defined on the class itself. # diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index e65e79e741..6b326719de 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -7,10 +7,10 @@ module ActionMailer # around a lazy created Mail::Message. You can get direct access to the # Mail::Message, deliver the email or schedule the email to be sent through ActiveJob. # - # Notifier.welcome(david) # an ActionMailer::MessageDeliver object - # Notifier.welcome(david).deliver_now # sends the email - # Notifier.welcome(david).deliver_later # enqueue the deliver email job to ActiveJob - # Notifier.welcome(david).message # a Mail::Message object + # Notifier.welcome('david') # an ActionMailer::MessageDeliver object + # Notifier.welcome('david').deliver_now # sends the email + # Notifier.welcome('david').deliver_later # enqueue the deliver email job to ActiveJob + # Notifier.welcome('david').message # a Mail::Message object class MessageDelivery < Delegator def initialize(mailer, mail_method, *args) #:nodoc: @mailer = mailer @@ -38,9 +38,9 @@ module ActionMailer # # ==== Examples # - # Notifier.welcome(david).deliver_later - # Notifier.welcome(david).deliver_later(in: 1.hour) - # Notifier.welcome(david).deliver_later(at: 10.hours.from_now) + # Notifier.welcome('david').deliver_later + # Notifier.welcome('david').deliver_later(in: 1.hour) + # Notifier.welcome('david').deliver_later(at: 10.hours.from_now) # # ==== Options # * in - Enqueue the message to be delivered with a delay @@ -54,9 +54,9 @@ module ActionMailer # # ==== Examples # - # Notifier.welcome(david).deliver_later - # Notifier.welcome(david).deliver_later(in: 1.hour) - # Notifier.welcome(david).deliver_later(at: 10.hours.from_now) + # Notifier.welcome('david').deliver_later + # Notifier.welcome('david').deliver_later(in: 1.hour) + # Notifier.welcome('david').deliver_later(at: 10.hours.from_now) # # ==== Options # * in - Enqueue the message to be delivered with a delay @@ -68,7 +68,7 @@ module ActionMailer # Delivers a message. The message will be sent bypassing checking perform_deliveries # and raise_delivery_errors, so use with caution. # - # Notifier.welcome(david).deliver_now! + # Notifier.welcome('david').deliver_now! # def deliver_now! message.deliver! @@ -76,7 +76,7 @@ module ActionMailer # Delivers a message: # - # Notifier.welcome(david).deliver_now + # Notifier.welcome('david').deliver_now # def deliver_now message.deliver -- cgit v1.2.3 From 6e75e7e3bc3f0a405dbaca486ecbe5cb00a6d783 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Wed, 20 Aug 2014 16:41:00 +0000 Subject: [Rdoc] Make clear that we are sending an AR object [ci skip] --- actionmailer/lib/action_mailer/base.rb | 8 ++++---- actionmailer/lib/action_mailer/message_delivery.rb | 24 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 0e4497536d..ad3aebbe9b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -138,20 +138,20 @@ module ActionMailer # Once a mailer action and template are defined, you can deliver your message or create it and save it # for delivery later: # - # Notifier.welcome('david').deliver_now # sends the email - # mail = Notifier.welcome('david') # => an ActionMailer::MessageDeliver object + # Notifier.welcome(User.first).deliver_now # sends the email + # mail = Notifier.welcome(User.first) # => an ActionMailer::MessageDeliver object # mail.deliver_now # sends the email # # The ActionMailer::MessageDeliver class is a wrapper around a Mail::Message object. If # you want direct access to the Mail::Message object you can call the message method on # the ActionMailer::MessageDeliver object. # - # Notifier.welcome('david').message # => a Mail::Message object + # Notifier.welcome(User.first).message # => a Mail::Message object # # ActionMailer is nicely integrated with ActiveJob so you can send emails in the background (example: outside # of the request-response cycle, so the user doesn't have to wait on it): # - # Notifier.welcome('david').deliver_later # enqueue the email sending to ActiveJob + # Notifier.welcome(User.first).deliver_later # enqueue the email sending to ActiveJob # # You never instantiate your mailer class. Rather, you just call the method you defined on the class itself. # diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index 6b326719de..db7724195c 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -7,10 +7,10 @@ module ActionMailer # around a lazy created Mail::Message. You can get direct access to the # Mail::Message, deliver the email or schedule the email to be sent through ActiveJob. # - # Notifier.welcome('david') # an ActionMailer::MessageDeliver object - # Notifier.welcome('david').deliver_now # sends the email - # Notifier.welcome('david').deliver_later # enqueue the deliver email job to ActiveJob - # Notifier.welcome('david').message # a Mail::Message object + # Notifier.welcome(User.first) # an ActionMailer::MessageDeliver object + # Notifier.welcome(User.first).deliver_now # sends the email + # Notifier.welcome(User.first).deliver_later # enqueue the deliver email job to ActiveJob + # Notifier.welcome(User.first).message # a Mail::Message object class MessageDelivery < Delegator def initialize(mailer, mail_method, *args) #:nodoc: @mailer = mailer @@ -38,9 +38,9 @@ module ActionMailer # # ==== Examples # - # Notifier.welcome('david').deliver_later - # Notifier.welcome('david').deliver_later(in: 1.hour) - # Notifier.welcome('david').deliver_later(at: 10.hours.from_now) + # Notifier.welcome(User.first).deliver_later + # Notifier.welcome(User.first).deliver_later(in: 1.hour) + # Notifier.welcome(User.first).deliver_later(at: 10.hours.from_now) # # ==== Options # * in - Enqueue the message to be delivered with a delay @@ -54,9 +54,9 @@ module ActionMailer # # ==== Examples # - # Notifier.welcome('david').deliver_later - # Notifier.welcome('david').deliver_later(in: 1.hour) - # Notifier.welcome('david').deliver_later(at: 10.hours.from_now) + # Notifier.welcome(User.first).deliver_later + # Notifier.welcome(User.first).deliver_later(in: 1.hour) + # Notifier.welcome(User.first).deliver_later(at: 10.hours.from_now) # # ==== Options # * in - Enqueue the message to be delivered with a delay @@ -68,7 +68,7 @@ module ActionMailer # Delivers a message. The message will be sent bypassing checking perform_deliveries # and raise_delivery_errors, so use with caution. # - # Notifier.welcome('david').deliver_now! + # Notifier.welcome(User.first).deliver_now! # def deliver_now! message.deliver! @@ -76,7 +76,7 @@ module ActionMailer # Delivers a message: # - # Notifier.welcome('david').deliver_now + # Notifier.welcome(User.first).deliver_now # def deliver_now message.deliver -- cgit v1.2.3 From f39cb2b25c1f0d9e7d577c3edf33b6bf9715805a Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Wed, 20 Aug 2014 10:29:27 -0700 Subject: Point release notes to stable branch for list of commits. [ci skip] --- guides/source/2_2_release_notes.md | 2 +- guides/source/2_3_release_notes.md | 2 +- guides/source/3_0_release_notes.md | 2 +- guides/source/3_1_release_notes.md | 5 ++++- guides/source/3_2_release_notes.md | 5 ++++- guides/source/4_2_release_notes.md | 8 ++++---- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md index 522f628a7e..b11aaa15a8 100644 --- a/guides/source/2_2_release_notes.md +++ b/guides/source/2_2_release_notes.md @@ -1,7 +1,7 @@ Ruby on Rails 2.2 Release Notes =============================== -Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. +Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/2-2-stable) in the main Rails repository on GitHub. Along with Rails, 2.2 marks the launch of the [Ruby on Rails Guides](http://guides.rubyonrails.org/), the first results of the ongoing [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide). This site will deliver high-quality documentation of the major features of Rails. diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md index 52eeb4c2bc..20566c9155 100644 --- a/guides/source/2_3_release_notes.md +++ b/guides/source/2_3_release_notes.md @@ -1,7 +1,7 @@ Ruby on Rails 2.3 Release Notes =============================== -Rails 2.3 delivers a variety of new and improved features, including pervasive Rack integration, refreshed support for Rails Engines, nested transactions for Active Record, dynamic and default scopes, unified rendering, more efficient routing, application templates, and quiet backtraces. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/master) in the main Rails repository on GitHub or review the `CHANGELOG` files for the individual Rails components. +Rails 2.3 delivers a variety of new and improved features, including pervasive Rack integration, refreshed support for Rails Engines, nested transactions for Active Record, dynamic and default scopes, unified rendering, more efficient routing, application templates, and quiet backtraces. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/2-3-stable) in the main Rails repository on GitHub or review the `CHANGELOG` files for the individual Rails components. -------------------------------------------------------------------------------- diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md index aec3a383d6..8122d6c235 100644 --- a/guides/source/3_0_release_notes.md +++ b/guides/source/3_0_release_notes.md @@ -15,7 +15,7 @@ Even if you don't give a hoot about any of our internal cleanups, Rails 3.0 is g On top of all that, we've tried our best to deprecate the old APIs with nice warnings. That means that you can move your existing application to Rails 3 without immediately rewriting all your old code to the latest best practices. -These release notes cover the major upgrades, but don't include every little bug fix and change. Rails 3.0 consists of almost 4,000 commits by more than 250 authors! If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. +These release notes cover the major upgrades, but don't include every little bug fix and change. Rails 3.0 consists of almost 4,000 commits by more than 250 authors! If you want to see everything, check out the [list of commits](http://github.com/rails/rails/commits/3-0-stable) in the main Rails repository on GitHub. -------------------------------------------------------------------------------- diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md index 7626296e7d..b7ed285b96 100644 --- a/guides/source/3_1_release_notes.md +++ b/guides/source/3_1_release_notes.md @@ -8,7 +8,10 @@ Highlights in Rails 3.1: * Assets Pipeline * jQuery as the default JavaScript library -This release notes cover the major changes, but don't include every little bug fix and change. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. +These release notes cover only the major changes. To learn about various bug +fixes and changes, please refer to the change logs or check out the [list of +commits](https://github.com/rails/rails/commits/3-1-stable) in the main Rails +repository on GitHub. -------------------------------------------------------------------------------- diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index 2416e1a228..c5db0262e9 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -8,7 +8,10 @@ Highlights in Rails 3.2: * Automatic Query Explains * Tagged Logging -These release notes cover the major changes, but do not include each bug-fix and changes. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/3-2-stable) in the main Rails repository on GitHub. +These release notes cover only the major changes. To learn about various bug +fixes and changes, please refer to the change logs or check out the [list of +commits](https://github.com/rails/rails/commits/3-2-stable) in the main Rails +repository on GitHub. -------------------------------------------------------------------------------- diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index 864026e9f2..913bb4fb5f 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -8,10 +8,10 @@ Highlights in Rails 4.2: * Web Console * Foreign key support -These release notes cover only the major changes. To know about various bug -fixes and changes, please refer to the change logs or check out the -[list of commits](https://github.com/rails/rails/commits/master) in the main -Rails repository on GitHub. +These release notes cover only the major changes. To learn about various bug +fixes and changes, please refer to the change logs or check out the [list of +commits](https://github.com/rails/rails/commits/master) in the main Rails +repository on GitHub. -------------------------------------------------------------------------------- -- cgit v1.2.3 From 0cc9848fafa0829144ac289becdd6e977a696427 Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Wed, 20 Aug 2014 10:31:38 -0700 Subject: Same as f39cb2b except backport friendly Point release notes to stable branch for list of commits. [ci skip] --- guides/source/4_1_release_notes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md index 5f4bdaaa8f..52a5acb75e 100644 --- a/guides/source/4_1_release_notes.md +++ b/guides/source/4_1_release_notes.md @@ -8,10 +8,10 @@ Highlights in Rails 4.1: * Action Pack variants * Action Mailer previews -These release notes cover only the major changes. To know about various bug -fixes and changes, please refer to the change logs or check out the -[list of commits](https://github.com/rails/rails/commits/master) in the main -Rails repository on GitHub. +These release notes cover only the major changes. To learn about various bug +fixes and changes, please refer to the change logs or check out the [list of +commits](https://github.com/rails/rails/commits/4-1-stable) in the main Rails +repository on GitHub. -------------------------------------------------------------------------------- -- cgit v1.2.3 From 2f194669ec8ac6445dcb4645077f07d292fd35c1 Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Wed, 20 Aug 2014 10:33:48 -0700 Subject: Same as f39cb2b except backport friendly Point release notes to stable branch for list of commits. [ci skip] --- guides/source/4_0_release_notes.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index 19c690233c..1aaf5ebc94 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -8,7 +8,10 @@ Highlights in Rails 4.0: * Turbolinks * Russian Doll Caching -These release notes cover only the major changes. To know about various bug fixes and changes, please refer to the change logs or check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. +These release notes cover only the major changes. To learn about various bug +fixes and changes, please refer to the change logs or check out the [list of +commits](https://github.com/rails/rails/commits/4-0-stable) in the main Rails +repository on GitHub. -------------------------------------------------------------------------------- -- cgit v1.2.3 From b64cfac9318b09cb6acbc31bdfd309d2e28f86d0 Mon Sep 17 00:00:00 2001 From: Tom Kadwill Date: Wed, 20 Aug 2014 07:58:59 +0100 Subject: [ci skip] Added documentation for has_many scope parameter --- activerecord/lib/active_record/associations.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 945f22d3c8..114e327926 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1133,6 +1133,17 @@ module ActiveRecord # * Firm#clients.create! (similar to c = Client.new("firm_id" => id); c.save!) # The declaration can also include an +options+ hash to specialize the behavior of the association. # + # === Scopes + # + # You can pass a second argument +scope+ as a callable (i.e. proc or + # lambda) to retrieve a specific set of records or customize the generated + # query when you access the associated collection. + # + # Scope examples: + # has_many :comments, -> { where(author_id: 1) } + # has_many :employees, -> { joins(:address) } + # has_many :posts, ->(post) { where("max_post_length > ?", post.length) } + # # === Options # [:class_name] # Specify the class name of the association. Use it only if that name can't be inferred -- cgit v1.2.3 From e569e12c66d430865a32ae67714d667da3e7c1d0 Mon Sep 17 00:00:00 2001 From: Logan Hasson Date: Wed, 20 Aug 2014 15:08:19 -0400 Subject: Fix a few typos/some grammar in Active Job Basics --- guides/source/active_job_basics.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index 5ce1dc43d1..a8e1f22b4d 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -19,7 +19,7 @@ Introduction Active Job is a framework for declaring jobs and making them run on a variety of queueing backends. These jobs can be everything from regularly scheduled -clean-ups, billing charges, or mailings. Anything that can be chopped up +clean-ups, to billing charges, to mailings. Anything that can be chopped up into small units of work and run in parallel, really. @@ -36,7 +36,7 @@ then. And you'll be able to switch between them without having to rewrite your j Creating a Job -------------- -This section will provide a step-by-step guide to creating a job and enqueue it. +This section will provide a step-by-step guide to creating a job and enqueuing it. ### Create the Job @@ -61,7 +61,7 @@ Rails. 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`. -Here's how a job looks like: +Here's what a job looks like: ```ruby class GuestsCleanupJob < ActiveJob::Base @@ -95,7 +95,7 @@ That's it! Job Execution ------------- -If not adapter is set, the job is immediately executed. +If no adapter is set, the job is immediately executed. ### Backends @@ -128,7 +128,7 @@ Active Job has adapters for the following queueing backends: ### Change Backends -You can easy change your adapter: +You can easily change your adapter: ```ruby # be sure to have the adapter gem in your Gemfile and follow the adapter specific @@ -140,7 +140,7 @@ YourApp::Application.config.active_job.queue_adapter = :sidekiq Queues ------ -Most of the adapters supports multiple queues. With Active Job you can schedule +Most of the adapters support multiple queues. With Active Job you can schedule the job to run on a specific queue: ```ruby @@ -157,7 +157,7 @@ you need to specify the queues to listen to. Callbacks --------- -Active Job provides hooks during the lifecycle of a job. Callbacks allows you to +Active Job provides hooks during the lifecycle of a job. Callbacks allow you to trigger logic during the lifecycle of a job. ### Available callbacks @@ -197,7 +197,7 @@ ActionMailer One of the most common jobs in a modern web application is sending emails outside of the request-response cycle, so the user doesn't have to wait on it. Active Job -is integrated with Action Mailer so you can easily send emails async: +is integrated with Action Mailer so you can easily send emails asynchronously: ```ruby # If you want to send the email now use #deliver_now -- cgit v1.2.3 From 2858e31387616f2a3a3b149571a3e09f2dabb7b8 Mon Sep 17 00:00:00 2001 From: Aditya Kapoor Date: Thu, 21 Aug 2014 00:53:26 +0530 Subject: [ci skip] correct docs about the migration generation --- activerecord/lib/active_record/migration.rb | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 659c5e3bbb..9219d70c65 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -161,21 +161,14 @@ module ActiveRecord # in the db/migrate/ directory where timestamp is the # UTC formatted date and time that the migration was generated. # - # You may then edit the up and down methods of - # MyNewMigration. - # # There is a special syntactic shortcut to generate migrations that add fields to a table. # # rails generate migration add_fieldname_to_tablename fieldname:string # # This will generate the file timestamp_add_fieldname_to_tablename, which will look like this: # class AddFieldnameToTablename < ActiveRecord::Migration - # def up - # add_column :tablenames, :fieldname, :string - # end - # - # def down - # remove_column :tablenames, :fieldname + # def change + # add_column :tablenames, :field, :string # end # end # -- cgit v1.2.3 From eb9f1ef84f5236c6a1d4d46d372bf8fe6e2fff58 Mon Sep 17 00:00:00 2001 From: Aditya Kapoor Date: Thu, 21 Aug 2014 01:01:45 +0530 Subject: [ci skip] add doc for STEP in migration --- activerecord/lib/active_record/migration.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 9219d70c65..d0d9304a36 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -181,9 +181,12 @@ module ActiveRecord # # To roll the database back to a previous migration version, use # rake db:migrate VERSION=X where X is the version to which - # you wish to downgrade. If any of the migrations throw an - # ActiveRecord::IrreversibleMigration exception, that step will fail and you'll - # have some manual work to do. + # you wish to downgrade. Alternatively, you can also use the STEP option if you + # wish to rollback last few migrations. rake db:migrate STEP=2 will rollback + # the latest two migrations. + # + # If any of the migrations throw an ActiveRecord::IrreversibleMigration exception, + # that step will fail and you'll have some manual work to do. # # == Database support # -- cgit v1.2.3 From d694ea259c755924ba54e8e3783630ae3d737543 Mon Sep 17 00:00:00 2001 From: Logan Hasson Date: Wed, 20 Aug 2014 15:36:58 -0400 Subject: [ci skip] Fix Active Job grammar in api docs --- activejob/README.md | 2 +- activejob/lib/active_job/callbacks.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activejob/README.md b/activejob/README.md index e48070bcfc..1f300fcf62 100644 --- a/activejob/README.md +++ b/activejob/README.md @@ -2,7 +2,7 @@ Active Job is a framework for declaring jobs and making them run on a variety of queueing backends. These jobs can be everything from regularly scheduled -clean-ups, billing charges, or mailings. Anything that can be chopped up into +clean-ups, to billing charges, to mailings. Anything that can be chopped up into small units of work and run in parallel, really. It also serves as the backend for ActionMailer's #deliver_later functionality diff --git a/activejob/lib/active_job/callbacks.rb b/activejob/lib/active_job/callbacks.rb index af92031bc9..8901fa77f2 100644 --- a/activejob/lib/active_job/callbacks.rb +++ b/activejob/lib/active_job/callbacks.rb @@ -3,7 +3,7 @@ require 'active_support/callbacks' module ActiveJob # = Active Job Callbacks # - # Active Job provides hooks during the lifecycle of a job. Callbacks allows you to trigger + # Active Job provides hooks during the lifecycle of a job. Callbacks allow you to trigger # logic during the lifecycle of a job. Available callbacks: # # * before_enqueue -- cgit v1.2.3 From 212fde6a1a8672cf8ab838c1d6e67569e5bc93e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 20 Aug 2014 16:50:06 -0300 Subject: Remove timestamp type from generator usage It was soft removed at 03035d69e14032a589e9653e3145237b8a9a09be --- railties/lib/rails/generators/rails/model/USAGE | 1 - 1 file changed, 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE index 2a6b8700e3..8c3b63c3b4 100644 --- a/railties/lib/rails/generators/rails/model/USAGE +++ b/railties/lib/rails/generators/rails/model/USAGE @@ -46,7 +46,6 @@ Available field types: date time datetime - timestamp You can also consider `references` as a kind of type. For instance, if you run: -- cgit v1.2.3 From f7851c61d835733d97257892b9a8bd9f48b37d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 20 Aug 2014 16:51:17 -0300 Subject: Make the explicit the Active Job dependency of Action Mailer --- actionmailer/actionmailer.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index bc72c20d87..089e12ebf1 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |s| s.add_dependency 'actionpack', version s.add_dependency 'actionview', version + s.add_dependency 'activejob', version s.add_dependency 'mail', ['~> 2.5', '>= 2.5.4'] s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.2' -- cgit v1.2.3 From f0772597f65fce4a69182731c1899270af7c64dd Mon Sep 17 00:00:00 2001 From: Tee Parham Date: Wed, 20 Aug 2014 14:56:30 -0600 Subject: Update configuring.md [ci skip] Remove ActiveRecord config instructions for active_record.attribute_types_cached_by_default, which was removed in https://github.com/rails/rails/commit/66736c8e50585 --- guides/source/configuring.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 801cef5ca6..38f9609287 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -290,8 +290,6 @@ All these configuration options are delegated to the `I18n` library. * `config.active_record.partial_writes` is a boolean value and controls whether or not partial writes are used (i.e. whether updates only set attributes that are dirty). Note that when using partial writes, you should also use optimistic locking `config.active_record.lock_optimistically` since concurrent updates may write attributes based on a possibly stale read state. The default value is `true`. -* `config.active_record.attribute_types_cached_by_default` sets the attribute types that `ActiveRecord::AttributeMethods` will cache by default on reads. The default is `[:datetime, :timestamp, :time, :date]`. - * `config.active_record.maintain_test_schema` is a boolean value which controls whether Active Record should try to keep your test database schema up-to-date with `db/schema.rb` (or `db/structure.sql`) when you run your tests. The default is true. * `config.active_record.dump_schema_after_migration` is a flag which -- cgit v1.2.3 From 31abbe5154f63175fb3bc49b5e9b6fa008a9ac73 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 21 Aug 2014 06:40:00 +0900 Subject: Fix SyntaxError --- actionmailer/test/message_delivery_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb index 49b63d7675..8402fc130f 100644 --- a/actionmailer/test/message_delivery_test.rb +++ b/actionmailer/test/message_delivery_test.rb @@ -64,7 +64,7 @@ class MessageDeliveryTest < ActiveSupport::TestCase assert_respond_to @mail, :deliver_now! end - test 'should enqueue and run correctly in activejob' do + def test_should_enqueue_and_run_correctly_in_activejob @mail.deliver_later! assert_equal 1, ActionMailer::Base.deliveries.size ensure -- cgit v1.2.3 From f02c9726f1e62335d6405a7460b3490b9e10c4d8 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 21 Aug 2014 06:52:54 +0900 Subject: "warning: assigned but unused variable" --- activerecord/test/cases/relation/merging_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 54a581e416..0d537fbfe3 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -153,7 +153,7 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase rating_1 = comment_1.ratings.create! comment_2 = dev.comments.create!(body: "I'm John", post: Post.first) - rating_2 = comment_2.ratings.create! + comment_2.ratings.create! assert_equal dev.ratings, [rating_1] end -- cgit v1.2.3 From d4f0f836f5341ce17765a9bb40b08434ae7970e9 Mon Sep 17 00:00:00 2001 From: Bradly Feeley Date: Wed, 20 Aug 2014 16:21:56 -0700 Subject: [ci skip] Fixing spelling typo in testing guide. --- guides/source/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/source/testing.md b/guides/source/testing.md index d91f13699a..29724ae011 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -99,7 +99,7 @@ one: Note: For associations to reference one another by name, you cannot specify the `id:` attribute on the fixtures. Rails will auto assign a primary key to be consistent between runs. If you manually specify an `id:` attribute, this behavior will not work. For more - information on this assocation behavior please read the + information on this association behavior please read the [fixture api documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). #### ERB'in It Up -- cgit v1.2.3 From cfaaacd9763642e91761de54c90669a88d772e5a Mon Sep 17 00:00:00 2001 From: schneems Date: Mon, 11 Aug 2014 13:29:25 -0500 Subject: Enable gzip compression by default If someone is using ActionDispatch::Static to serve assets and makes it past the `match?` then the file exists on disk and it will be served. This PR adds in logic that checks to see if the file being served is already compressed (via gzip) and on disk, if it is it will be served as long as the client can handle gzip encoding. If not, then a non gzip file will be served. This additional logic slows down an individual asset request but should speed up the consumer experience as compressed files are served and production applications should be delivered with a CDN. This PR allows a CDN to cache a gzip file by setting the `Vary` header appropriately. In net this should speed up a production application that are using Rails as an origin for a CDN. Non-asset request speed is not affected in this PR. --- actionpack/CHANGELOG.md | 6 +++ .../lib/action_dispatch/middleware/static.rb | 54 +++++++++++++++------ actionpack/test/dispatch/static_test.rb | 24 ++++++++- ...application-a71b3024f80aea3181c09774ca17e712.js | 4 ++ ...lication-a71b3024f80aea3181c09774ca17e712.js.gz | Bin 0 -> 38457 bytes ...pplication-a71b3024f80aea3181c09774ca17e712.js" | 4 ++ ...ication-a71b3024f80aea3181c09774ca17e712.js.gz" | Bin 0 -> 38457 bytes 7 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js create mode 100644 actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz create mode 100644 "actionpack/test/fixtures/\345\205\254\345\205\261/gzip/application-a71b3024f80aea3181c09774ca17e712.js" create mode 100644 "actionpack/test/fixtures/\345\205\254\345\205\261/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz" diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index e2731d0ee5..30986d0a6a 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,9 @@ +* Requests that hit `ActionDispatch::Static` can now take advantage + of gzipped assets on disk. By default a gzip asset will be served if + the client supports gzip and a compressed file is on disk. + + *Richard Schneeman* + * `ActionController::Parameters` will stop inheriting from `Hash` and `HashWithIndifferentAccess` in the next major release. If you use any method that is not available on `ActionController::Parameters` you should consider diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 25e32cdef8..a3e8126ace 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -16,8 +16,9 @@ module ActionDispatch def initialize(root, cache_control) @root = root.chomp('/') @compiled_root = /^#{Regexp.escape(root)}/ - headers = cache_control && { 'Cache-Control' => cache_control } - @file_server = ::Rack::File.new(@root, headers) + headers = {} + headers['Cache-Control'] = cache_control if cache_control + @file_server = ::Rack::File.new(@root, headers) end def match?(path) @@ -36,23 +37,48 @@ module ActionDispatch end def call(env) - @file_server.call(env) + path = env['PATH_INFO'] + gzip_file_exists = gzip_file_exists?(path) + if gzip_file_exists && gzip_encoding_accepted?(env) + env['PATH_INFO'] = "#{path}.gz" + status, headers, body = @file_server.call(env) + headers['Content-Encoding'] = 'gzip' + headers['Content-Type'] = content_type(path) + else + status, headers, body = @file_server.call(env) + end + + headers['Vary'] = 'Accept-Encoding' if gzip_file_exists + return [status, headers, body] end - def ext - @ext ||= begin - ext = ::ActionController::Base.default_static_extension - "{,#{ext},/index#{ext}}" + private + def ext + @ext ||= begin + ext = ::ActionController::Base.default_static_extension + "{,#{ext},/index#{ext}}" + end end - end - def unescape_path(path) - URI.parser.unescape(path) - end + def unescape_path(path) + URI.parser.unescape(path) + end - def escape_glob_chars(path) - path.gsub(/[*?{}\[\]]/, "\\\\\\&") - end + def escape_glob_chars(path) + path.gsub(/[*?{}\[\]]/, "\\\\\\&") + end + + def content_type(path) + ::Rack::Mime.mime_type(::File.extname(path), 'text/plain') + end + + def gzip_encoding_accepted?(env) + env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/ + end + + def gzip_file_exists?(path) + File.exist?(File.join(@root, "#{::Rack::Utils.unescape(path)}.gz")) + end end # This middleware will attempt to return the contents of a file's body from diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index afdda70748..f4b3a8cb93 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'abstract_unit' require 'rbconfig' +require 'zlib' module StaticTests def test_serves_dynamic_content @@ -106,6 +107,18 @@ module StaticTests end end + def test_serves_gzip_files_when_header_set + file_name = "/gzip/application-a71b3024f80aea3181c09774ca17e712.js" + response = get(file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip') + assert_gzip file_name, response + assert_equal 'application/javascript', response.headers['Content-Type'] + assert_equal 'Accept-Encoding', response.headers["Vary"] + assert_equal 'gzip', response.headers["Content-Encoding"] + + response = get(file_name, 'HTTP_ACCEPT_ENCODING' => '') + refute_equal 'gzip', response.headers["Content-Encoding"] + end + # Windows doesn't allow \ / : * ? " < > | in filenames unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ def test_serves_static_file_with_colon @@ -125,13 +138,20 @@ module StaticTests private + def assert_gzip(file_name, response) + expected = File.read("#{FIXTURE_LOAD_PATH}/#{public_path}" + file_name) + actual = Zlib::GzipReader.new(StringIO.new(response.body)).read + assert_equal expected, actual + end + def assert_html(body, response) assert_equal body, response.body assert_equal "text/html", response.headers["Content-Type"] + refute response.headers.key?("Vary") end - def get(path) - Rack::MockRequest.new(@app).request("GET", path) + def get(path, headers = {}) + Rack::MockRequest.new(@app).request("GET", path, headers) end def with_static_file(file) diff --git a/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js b/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js new file mode 100644 index 0000000000..1826a7660e --- /dev/null +++ b/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js @@ -0,0 +1,4 @@ +!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=it.type(e);return"function"===n||it.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(it.isFunction(t))return it.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return it.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(ft.test(t))return it.filter(t,e,n);t=it.filter(t,e)}return it.grep(e,function(e){return it.inArray(e,t)>=0!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t=xt[e]={};return it.each(e.match(bt)||[],function(e,n){t[n]=!0}),t}function a(){ht.addEventListener?(ht.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1)):(ht.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(ht.addEventListener||"load"===event.type||"complete"===ht.readyState)&&(a(),it.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(Ct,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:kt.test(n)?it.parseJSON(n):n}catch(i){}it.data(e,t,n)}else n=void 0}return n}function u(e){var t;for(t in e)if(("data"!==t||!it.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(it.acceptData(e)){var i,o,a=it.expando,s=e.nodeType,l=s?it.cache:e,u=s?e[a]:e[a]&&a;if(u&&l[u]&&(r||l[u].data)||void 0!==n||"string"!=typeof t)return u||(u=s?e[a]=G.pop()||it.guid++:a),l[u]||(l[u]=s?{}:{toJSON:it.noop}),("object"==typeof t||"function"==typeof t)&&(r?l[u]=it.extend(l[u],t):l[u].data=it.extend(l[u].data,t)),o=l[u],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[it.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[it.camelCase(t)])):i=o,i}}function d(e,t,n){if(it.acceptData(e)){var r,i,o=e.nodeType,a=o?it.cache:e,s=o?e[it.expando]:it.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){it.isArray(t)?t=t.concat(it.map(t,it.camelCase)):t in r?t=[t]:(t=it.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!u(r):!it.isEmptyObject(r))return}(n||(delete a[s].data,u(a[s])))&&(o?it.cleanData([e],!0):nt.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}function f(){return!0}function p(){return!1}function h(){try{return ht.activeElement}catch(e){}}function m(e){var t=Mt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function g(e,t){var n,r,i=0,o=typeof e.getElementsByTagName!==Et?e.getElementsByTagName(t||"*"):typeof e.querySelectorAll!==Et?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||it.nodeName(r,t)?o.push(r):it.merge(o,g(r,t));return void 0===t||t&&it.nodeName(e,t)?it.merge([e],o):o}function v(e){Dt.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t){return it.nodeName(e,"table")&&it.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function b(e){return e.type=(null!==it.find.attr(e,"type"))+"/"+e.type,e}function x(e){var t=Vt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function w(e,t){for(var n,r=0;null!=(n=e[r]);r++)it._data(n,"globalEval",!t||it._data(t[r],"globalEval"))}function T(e,t){if(1===t.nodeType&&it.hasData(e)){var n,r,i,o=it._data(e),a=it._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)it.event.add(t,n,s[n][r])}a.data&&(a.data=it.extend({},a.data))}}function E(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!nt.noCloneEvent&&t[it.expando]){i=it._data(t);for(r in i.events)it.removeEvent(t,r,i.handle);t.removeAttribute(it.expando)}"script"===n&&t.text!==e.text?(b(t).text=e.text,x(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),nt.html5Clone&&e.innerHTML&&!it.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Dt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function k(t,n){var r,i=it(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:it.css(i[0],"display");return i.detach(),o}function C(e){var t=ht,n=Zt[e];return n||(n=k(e,t),"none"!==n&&n||(Qt=(Qt||it("