diff options
25 files changed, 176 insertions, 122 deletions
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index e5e3a0164d..e2900c2d10 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,9 @@ +* Attachments can be added while rendering the mail template. + + Fixes #16974. + + *Christian Felder* + * 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 diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 09843ca70d..038f9e384b 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -51,9 +51,7 @@ module ActionView # The HTML generated for this would be (modulus formatting): # # <form action="/people" class="new_person" id="new_person" method="post"> - # <div style="display:none"> - # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> - # </div> + # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # <label for="person_first_name">First name</label>: # <input id="person_first_name" name="person[first_name]" type="text" /><br /> # @@ -81,10 +79,8 @@ module ActionView # the code above as is would yield instead: # # <form action="/people/256" class="edit_person" id="edit_person_256" method="post"> - # <div style="display:none"> - # <input name="_method" type="hidden" value="patch" /> - # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> - # </div> + # <input name="_method" type="hidden" value="patch" /> + # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # <label for="person_first_name">First name</label>: # <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br /> # @@ -315,9 +311,7 @@ module ActionView # The HTML generated for this would be: # # <form action='http://www.example.com' method='post' data-remote='true'> - # <div style='display:none'> - # <input name='_method' type='hidden' value='patch' /> - # </div> + # <input name='_method' type='hidden' value='patch' /> # ... # </form> # @@ -333,9 +327,7 @@ module ActionView # The HTML generated for this would be: # # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'> - # <div style='display:none'> - # <input name='_method' type='hidden' value='patch' /> - # </div> + # <input name='_method' type='hidden' value='patch' /> # ... # </form> # diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index c3be47133c..364414da05 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -229,59 +229,51 @@ module ActionView # ==== Examples # <%= button_to "New", action: "new" %> # # => "<form method="post" action="/controller/new" class="button_to"> - # # <div><input value="New" type="submit" /></div> + # # <input value="New" type="submit" /> # # </form>" # # <%= button_to "New", new_articles_path %> # # => "<form method="post" action="/articles/new" class="button_to"> - # # <div><input value="New" type="submit" /></div> + # # <input value="New" type="submit" /> # # </form>" # # <%= button_to [:make_happy, @user] do %> # Make happy <strong><%= @user.name %></strong> # <% end %> # # => "<form method="post" action="/users/1/make_happy" class="button_to"> - # # <div> - # # <button type="submit"> - # # Make happy <strong><%= @user.name %></strong> - # # </button> - # # </div> + # # <button type="submit"> + # # Make happy <strong><%= @user.name %></strong> + # # </button> # # </form>" # # <%= button_to "New", { action: "new" }, form_class: "new-thing" %> # # => "<form method="post" action="/controller/new" class="new-thing"> - # # <div><input value="New" type="submit" /></div> + # # <input value="New" type="submit" /> # # </form>" # # # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %> # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json"> - # # <div> - # # <input value="Create" type="submit" /> - # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/> - # # </div> + # # <input value="Create" type="submit" /> + # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/> # # </form>" # # # <%= button_to "Delete Image", { action: "delete", id: @image.id }, # method: :delete, data: { confirm: "Are you sure?" } %> # # => "<form method="post" action="/images/delete/1" class="button_to"> - # # <div> - # # <input type="hidden" name="_method" value="delete" /> - # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" /> - # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/> - # # </div> + # # <input type="hidden" name="_method" value="delete" /> + # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" /> + # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/> # # </form>" # # # <%= button_to('Destroy', 'http://www.example.com', # method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %> # # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'> - # # <div> - # # <input name='_method' value='delete' type='hidden' /> - # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' /> - # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/> - # # </div> + # # <input name='_method' value='delete' type='hidden' /> + # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' /> + # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/> # # </form>" # # def button_to(name = nil, options = nil, html_options = nil, &block) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 78fb60332f..c02de2e979 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -43,7 +43,7 @@ *Girish Sonawane* -* Introduce `connection.supports_views?` to check wether the current adapter +* Introduce `connection.supports_views?` to check whether the current adapter has support for SQL views. Connection adapters should define this method. *Yves Senn* @@ -91,17 +91,18 @@ *Agis Anastasopoulos* -* Fixed the `Relation#exists?` to work with polymorphic associations. +* Fixed `Relation#exists?` to work with polymorphic associations. Fixes #15821. *Kassio Borges* -* Currently, Active Record will rescue any errors raised within - after_rollback/after_create callbacks and print them to the logs. Next versions of rails - will not rescue those errors anymore, and just bubble them up, as the other callbacks. +* Currently, Active Record rescues any errors raised within + `after_rollback`/`after_create` callbacks and prints them to the logs. + Future versions of Rails will not rescue these errors anymore and + just bubble them up like the other callbacks. - This adds a opt-in flag to enable that behaviour, of not rescuing the errors. + This commit adds an opt-in flag to enable not rescuing the errors. Example: @@ -125,7 +126,7 @@ *Sean Griffin* -* Fix regression on after_commit that didnt fire when having nested transactions. +* Fix regression on `after_commit` that did not fire with nested transactions. Fixes #16425. @@ -161,9 +162,9 @@ * Define `id_was` to get the previous value of the primary key. - Currently when we call id_was and we have a custom primary key name + Currently when we call `id_was` and we have a custom primary key name, Active Record will return the current value of the primary key. This - make impossible to correctly do an update operation if you change the + makes it impossible to correctly do an update operation if you change the id. Fixes #16413. @@ -195,7 +196,7 @@ *Eileen M. Uchtitelle*, *Aaron Patterson* -* No verbose backtrace by db:drop when database does not exist. +* No verbose backtrace by `db:drop` when database does not exist. Fixes #16295. @@ -240,7 +241,7 @@ *Stefan Kanev* -* Dont swallow errors on compute_type when having a bad alias_method on +* Do not swallow errors on `compute_type` when having a bad `alias_method` on a class. *arthurnn* @@ -323,7 +324,7 @@ *Eileen M. Uchitelle, Aaron Patterson* -* Avoid type casting boolean and ActiveSupport::Duration values to numeric +* Avoid type casting boolean and `ActiveSupport::Duration` values to numeric values for string columns. Otherwise, in some database, the string column values will be coerced to a numeric allowing false or 0.seconds match any string starting with a non-digit. @@ -354,8 +355,8 @@ *Abdelkader Boudih* -* Move 'dependent: :destroy' handling for 'belongs_to' - from 'before_destroy' to 'after_destroy' callback chain +* Move 'dependent: :destroy' handling for `belongs_to` + from `before_destroy` to `after_destroy` callback chain Fixes #12380. @@ -363,8 +364,9 @@ * Detect in-place modifications on String attributes. - Before this change user have to mark the attribute as changed to it be persisted - in the database. Now it is not required anymore. + Before this change, an attribute modified in-place had to be marked as + changed in order for it to be persisted in the database. Now it is no longer + required. Before: @@ -476,7 +478,7 @@ *Sean Griffin* -* Pluck now works when selecting columns from different tables with the same +* `Pluck` now works when selecting columns from different tables with the same name. Fixes #15649. @@ -543,7 +545,7 @@ *Sean Griffin* -* Implemented ActiveRecord::Base#pretty_print to work with PP. +* Implemented `ActiveRecord::Base#pretty_print` to work with PP. *Ethan* @@ -584,16 +586,16 @@ *Arun Agrawal* -* Fix redefine a has_and_belongs_to_many inside inherited class - Fixing regression case, where redefining the same has_an_belongs_to_many +* Fix redefine a `has_and_belongs_to_many` inside inherited class + Fixing regression case, where redefining the same `has_and_belongs_to_many` definition into a subclass would raise. Fixes #14983. *arthurnn* -* Fix has_and_belongs_to_many public reflection. - When defining a has_and_belongs_to_many, internally we convert that to two has_many. +* Fix `has_and_belongs_to_many` public reflection. + When defining a `has_and_belongs_to_many`, internally we convert that to two has_many. But as `reflections` is a public API, people expect to see the right macro. Fixes #14682. @@ -630,7 +632,7 @@ *Brock Trappitt* -* Fixed the inferred table name of a has_and_belongs_to_many auxiliar +* Fixed the inferred table name of a `has_and_belongs_to_many` auxiliar table inside a schema. Fixes #14824. @@ -674,7 +676,7 @@ *Aaron Nelson* -* Fix how to calculate associated class name when using namespaced has_and_belongs_to_many +* Fix how to calculate associated class name when using namespaced `has_and_belongs_to_many` association. Fixes #14709. @@ -744,7 +746,7 @@ * Fixed has_and_belongs_to_many's CollectionAssociation size calculation. - has_and_belongs_to_many should fall back to using the normal CollectionAssociation's + `has_and_belongs_to_many` should fall back to using the normal CollectionAssociation's size calculation if the collection is not cached or loaded. Fixes #14913, #14914. @@ -804,7 +806,7 @@ *Timur Alperovich* -* Give ActiveRecord::PredicateBuilder private methods the privacy they deserve. +* Give `ActiveRecord::PredicateBuilder` private methods the privacy they deserve. *Hector Satre* @@ -860,7 +862,7 @@ *Kuldeep Aggarwal* -* Fixed has_many association to make it support irregular inflections. +* Fixed `has_many` association to make it support irregular inflections. Fixes #8928. @@ -974,7 +976,7 @@ * `to_sql` on an association now matches the query that is actually executed, where it could previously have incorrectly accrued additional conditions (e.g. as a result of - a previous query). CollectionProxy now always defers to the association scope's + a previous query). `CollectionProxy` now always defers to the association scope's `arel` method so the (incorrect) inherited one should be entirely concealed. Fixes #14003. @@ -1205,7 +1207,7 @@ The current solution of incrementing the beginning is not correct and is now deprecated. For subtypes where we don't know how to increment (e.g. `#succ` - is not defined) it will raise an ArgumentException for ranges with excluding + is not defined) it will raise an `ArgumentException` for ranges with excluding beginnings. *Yves Senn* diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 61f1a9aa27..069aa977bf 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -162,6 +162,9 @@ module ActiveRecord v.nil? || Array === v || Hash === v } + # We can't cache Post.find_by(author: david) ...yet + return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) } + key = hash.keys klass = self @@ -181,7 +184,7 @@ module ActiveRecord end def find_by!(*args) - find_by(*args) or raise RecordNotFound + find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}") end def initialize_generated_modules diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index f0b6afc4b4..73fd96f979 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -20,7 +20,7 @@ module ActiveRecord def reset_counters(id, *counters) object = find(id) counters.each do |counter_association| - has_many_association = _reflect_on_association(counter_association.to_sym) + has_many_association = _reflect_on_association(counter_association) unless has_many_association has_many = reflect_on_all_associations(:has_many) has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym } diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 4527452f1a..1724fa893d 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -181,6 +181,9 @@ module ActiveRecord # * Stable, autogenerated IDs # * Label references for associations (belongs_to, has_one, has_many) # * HABTM associations as inline lists + # + # There are some more advanced features available even if the id is specified: + # # * Autofilled timestamp columns # * Fixture label interpolation # * Support for YAML defaults diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 90e99957f6..eaaa409636 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -19,15 +19,15 @@ module ActiveRecord # # Person.group(:city).count # # => { 'Rome' => 5, 'Paris' => 3 } - # - # If +count+ is used with +group+ for multiple columns, it returns a Hash whose - # keys are an array containing the individual values of each column and the value + # + # If +count+ is used with +group+ for multiple columns, it returns a Hash whose + # keys are an array containing the individual values of each column and the value # of each key would be the +count+. - # + # # Article.group(:status, :category).count - # # => {["draft", "business"]=>10, ["draft", "technology"]=>4, + # # => {["draft", "business"]=>10, ["draft", "technology"]=>4, # ["published", "business"]=>0, ["published", "technology"]=>2} - # + # # If +count+ is used with +select+, it will count the selected columns: # # Person.select(:age).count @@ -274,7 +274,7 @@ module ActiveRecord group_attrs = group_values if group_attrs.first.respond_to?(:to_sym) - association = @klass._reflect_on_association(group_attrs.first.to_sym) + association = @klass._reflect_on_association(group_attrs.first) associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations group_fields = Array(associated ? association.foreign_key : group_attrs) else diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index b1753b27e1..ed56369f86 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -103,7 +103,7 @@ module ActiveRecord # Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. Note that <tt>take!</tt> accepts no arguments. def take! - take or raise RecordNotFound + take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Find the first record (or first N records if a parameter is supplied). @@ -138,7 +138,7 @@ module ActiveRecord # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. Note that <tt>first!</tt> accepts no arguments. def first! - first or raise RecordNotFound + first or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Find the last record (or last N records if a parameter is supplied). @@ -171,7 +171,7 @@ module ActiveRecord # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. Note that <tt>last!</tt> accepts no arguments. def last! - last or raise RecordNotFound + last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Find the second record. @@ -187,7 +187,7 @@ module ActiveRecord # Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. def second! - second or raise RecordNotFound + second or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Find the third record. @@ -203,7 +203,7 @@ module ActiveRecord # Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. def third! - third or raise RecordNotFound + third or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Find the fourth record. @@ -219,7 +219,7 @@ module ActiveRecord # Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. def fourth! - fourth or raise RecordNotFound + fourth or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Find the fifth record. @@ -235,7 +235,7 @@ module ActiveRecord # Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. def fifth! - fifth or raise RecordNotFound + fifth or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Find the forty-second record. Also known as accessing "the reddit". @@ -251,7 +251,7 @@ module ActiveRecord # Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record # is found. def forty_two! - forty_two or raise RecordNotFound + forty_two or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") end # Returns +true+ if a record exists in the table that matches the +id+ or diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index eff5c8f09c..3df0df40ee 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -26,7 +26,7 @@ module ActiveRecord queries << '1=0' else table = Arel::Table.new(column, default_table.engine) - association = klass._reflect_on_association(column.to_sym) + association = klass._reflect_on_association(column) value.each do |k, v| queries.concat expand(association && association.klass, table, k, v) @@ -55,7 +55,7 @@ module ActiveRecord # # For polymorphic relationships, find the foreign key and type: # PriceEstimate.where(estimate_of: treasure) - if klass && reflection = klass._reflect_on_association(column.to_sym) + if klass && reflection = klass._reflect_on_association(column) if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value) queries << build(table[reflection.foreign_type], base_class) end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 153f870e3d..7ca7349528 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -734,13 +734,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } } end - def test_bulk_update_raise_unknown_attribute_errro + def test_bulk_update_raise_unknown_attribute_error error = assert_raises(ActiveRecord::UnknownAttributeError) { @target.new(:hello => "world") } - assert @target, error.record - assert "hello", error.attribute - assert "unknown attribute: hello", error.message + assert_instance_of @target, error.record + assert_equal "hello", error.attribute + assert_equal "unknown attribute: hello", error.message end def test_methods_override_in_multi_level_subclass diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index fc91b728c2..ac570ecbe0 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -262,7 +262,7 @@ class FinderTest < ActiveRecord::TestCase end def test_take_bang_missing - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.where("title = 'This title does not exist'").take! end end @@ -282,7 +282,7 @@ class FinderTest < ActiveRecord::TestCase end def test_first_bang_missing - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.where("title = 'This title does not exist'").first! end end @@ -296,7 +296,7 @@ class FinderTest < ActiveRecord::TestCase def test_model_class_responds_to_first_bang assert Topic.first! Topic.delete_all - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.first! end end @@ -318,7 +318,7 @@ class FinderTest < ActiveRecord::TestCase def test_model_class_responds_to_second_bang assert Topic.second! Topic.delete_all - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.second! end end @@ -340,7 +340,7 @@ class FinderTest < ActiveRecord::TestCase def test_model_class_responds_to_third_bang assert Topic.third! Topic.delete_all - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.third! end end @@ -362,7 +362,7 @@ class FinderTest < ActiveRecord::TestCase def test_model_class_responds_to_fourth_bang assert Topic.fourth! Topic.delete_all - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.fourth! end end @@ -384,7 +384,7 @@ class FinderTest < ActiveRecord::TestCase def test_model_class_responds_to_fifth_bang assert Topic.fifth! Topic.delete_all - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.fifth! end end @@ -396,14 +396,14 @@ class FinderTest < ActiveRecord::TestCase end def test_last_bang_missing - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.where("title = 'This title does not exist'").last! end end def test_model_class_responds_to_last_bang assert_equal topics(:fifth), Topic.last! - assert_raises ActiveRecord::RecordNotFound do + assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do Topic.delete_all Topic.last! end @@ -802,7 +802,9 @@ class FinderTest < ActiveRecord::TestCase def test_find_by_one_attribute_bang assert_equal topics(:first), Topic.find_by_title!("The First Topic") - assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") } + assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do + Topic.find_by_title!("The First Topic!") + end end def test_find_by_on_attribute_that_is_a_reserved_word @@ -1071,6 +1073,11 @@ class FinderTest < ActiveRecord::TestCase assert_equal nil, Post.find_by("1 = 0") end + test "find_by with associations" do + assert_equal authors(:david), Post.find_by(author: authors(:david)).author + assert_equal authors(:mary) , Post.find_by(author: authors(:mary) ).author + end + test "find_by doesn't have implicit ordering" do assert_sql(/^((?!ORDER).)*$/) { Post.find_by(id: posts(:eager_other).id) } end @@ -1113,4 +1120,10 @@ class FinderTest < ActiveRecord::TestCase end end) end + + def assert_raises_with_message(exception_class, message, &block) + err = assert_raises(exception_class) { block.call } + assert_match message, err.message + end + end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 8f328335b2..4204057737 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,9 @@ +* Delegation now works with ruby reserved words passed to `:to` option. + + Fixes #16956. + + *Agis Anastasopoulos* + * Added method `#eql?` to `ActiveSupport::Duration`, in addition to `#==`. Currently, the following returns `false`, contrary to expectation: diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index 9d5cee54e3..342d3a9d52 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -95,7 +95,7 @@ module ActiveSupport # end # # class Host - # include Bar # works, Bar takes care now of its dependencies + # include Bar # It works, now Bar takes care of its dependencies # end module Concern class MultipleIncludedBlocks < StandardError #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index e926392952..570585b89a 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -1,8 +1,16 @@ +require 'set' + class Module # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+ # option is not used. class DelegationError < NoMethodError; end + RUBY_RESERVED_WORDS = Set.new( + %w(alias and BEGIN begin break case class def defined? do else elsif END + end ensure false for if in module next nil not or redo rescue retry + return self super then true undef unless until when while yield) + ).freeze + # Provides a +delegate+ class method to easily expose contained objects' # public methods as your own. # @@ -163,7 +171,7 @@ class Module line = line.to_i to = to.to_s - to = 'self.class' if to == 'class' + to = "self.#{to}" if RUBY_RESERVED_WORDS.include?(to) methods.each do |method| # Attribute writer methods only accept one argument. Makes sure []= diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 881faf93f5..f4d504914e 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -98,8 +98,8 @@ class DurationTest < ActiveSupport::TestCase def test_since_and_ago t = Time.local(2000) - assert t + 1, 1.second.since(t) - assert t - 1, 1.second.ago(t) + assert_equal t + 1, 1.second.since(t) + assert_equal t - 1, 1.second.ago(t) end def test_since_and_ago_without_argument diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 380f5ad42b..3c49c4d14f 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -56,8 +56,14 @@ Developer = Struct.new(:client) do delegate :name, :to => :client, :prefix => nil end +Event = Struct.new(:case) do + delegate :foo, :to => :case +end + Tester = Struct.new(:client) do delegate :name, :to => :client, :prefix => false + + def foo; 1; end end Product = Struct.new(:name) do @@ -495,4 +501,9 @@ class MethodAliasingTest < ActiveSupport::TestCase assert_equal 'duck_with_orange', @instance.duck assert FooClassWithBarMethod.public_method_defined?(:duck) end + + def test_delegate_with_case + event = Event.new(Tester.new) + assert_equal 1, event.foo + end end diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index 5aed0c9358..c59affea8f 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -25,9 +25,8 @@ If you're upgrading an existing application, it's a great idea to have good test 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) -guide. +available in the guide: [Upgrading Ruby on +Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-4-1-to-rails-4-2) Major Features @@ -36,9 +35,12 @@ Major Features ### Active Job, Action Mailer #deliver_later Active Job is a new framework in Rails 4.2. It is an adapter layer on top of -queuing systems like [Resque](https://github.com/resque/resque), [Delayed Job](https://github.com/collectiveidea/delayed_job), [Sidekiq](https://github.com/mperham/sidekiq), and more. You can write your -jobs with the Active Job API, and it'll run on all these queues with no changes -(it comes pre-configured with an inline runner). +queuing systems like [Resque](https://github.com/resque/resque), [Delayed +Job](https://github.com/collectiveidea/delayed_job), +[Sidekiq](https://github.com/mperham/sidekiq), and more. + +You can write your jobs with the Active Job API, and it'll run on all these +queues with no changes (it comes pre-configured with an inline runner). Building on top of Active Job, Action Mailer now comes with a `#deliver_later` method, which adds your email to be sent as a job to a queue, so it doesn't @@ -52,11 +54,16 @@ deserialize it at run time. ### Adequate Record -Adequate Record is a set of refactorings that make Active Record `find` and `find_by` methods and some association queries upto 2x faster. +Adequate Record is a set of refactorings that make Active Record `find` and +`find_by` methods and some association queries upto 2x faster. -It works by caching SQL query patterns while executing the Active Record calls. The cache helps skip parts of the computation involved in the transformation of the calls into SQL queries. More details in [Aaron Patterson's post](http://tenderlovemaking.com/2014/02/19/adequaterecord-pro-like-activerecord.html). +It works by caching SQL query patterns while executing the Active Record calls. +The cache helps skip parts of the computation involved in the transformation of +the calls into SQL queries. More details in [Aaron Patterson's +post](http://tenderlovemaking.com/2014/02/19/adequaterecord-pro-like-activerecord.html). -Nothing special has to be done to activate this feature. Most `find` and `find_by` calls and association queries will use it automatically. Examples: +Nothing special has to be done to activate this feature. Most `find` and +`find_by` calls and association queries will use it automatically. Examples: ```ruby Post.find 1 # caches query pattern @@ -177,6 +184,21 @@ class UsersController < ApplicationController end ``` +### Default host for `rails server` + +Due to a [change in Rack](https://github.com/rack/rack/commit/28b014484a8ac0bbb388e7eaeeef159598ec64fc), +`rails server` now listens on `localhost` instead of `0.0.0.0` by default. This +should have minimal impact on the standard development workflow as both http://127.0.0.1:3000 +and http://localhost:3000 would continue to work as before on your own machine. + +However, with this change you would no longer be able to access the Rails server +from a different machine (e.g. your development environment is in a virtual +machine and you would like to access it from the host machine), you would need +to start the server with `rails server -b 0.0.0.0` to restore the old behavior. + +If you do this, be sure to configure your firewall properly such that only +trusted machines on your network can access your development server. + ### Production logging The default log level in the `production` environment is now `:debug`. This @@ -322,7 +344,7 @@ Please refer to the [Changelog][railties] for detailed changes. namespace: my_app_development # config/production.rb - MyApp::Application.configure do + Rails.application.configure do config.middleware.use ExceptionNotifier, config_for(:exception_notification) end ``` diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index 7b3081993d..f2831defe6 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -138,7 +138,7 @@ You can easily change your adapter: ```ruby # be sure to have the adapter gem in your Gemfile and follow the adapter specific # installation and deployment instructions -YourApp::Application.config.active_job.queue_adapter = :sidekiq +Rails.application.config.active_job.queue_adapter = :sidekiq ``` diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 667281d1aa..220946e8d5 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -108,7 +108,7 @@ numbers. New applications filter out passwords by adding the following `config.f * `config.log_formatter` defines the formatter of the Rails logger. This option defaults to an instance of `ActiveSupport::Logger::SimpleFormatter` for all modes except production, where it defaults to `Logger::Formatter`. -* `config.log_level` defines the verbosity of the Rails logger. This option defaults to `:debug` for all modes except production, where it defaults to `:info`. +* `config.log_level` defines the verbosity of the Rails logger. This option defaults to `:debug` for all environments. * `config.log_tags` accepts a list of methods that the `request` object responds to. This makes it easy to tag log lines with debug information like subdomain and request id - both very helpful in debugging multi-user production applications. diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 88c6210296..1a647f8375 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -138,7 +138,7 @@ Rails.logger.level = 0 # at any time This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information. -TIP: The default Rails log level is `info` in production mode and `debug` in development and test mode. +TIP: The default Rails log level is `debug` in all environments. ### Sending Messages diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 0543b57ad4..2e7f134101 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -73,7 +73,7 @@ namespace: my_app_development # config/production.rb - MyApp::Application.configure do + Rails.application.configure do config.middleware.use ExceptionNotifier, config_for(:exception_notification) end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 67d5bac700..bc966e87c6 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -217,7 +217,7 @@ module Rails # namespace: my_app_development # # # config/production.rb - # MyApp::Application.configure do + # Rails.application.configure do # config.middleware.use ExceptionNotifier, config_for(:exception_notification) # end def config_for(name) diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index c479e92ae0..e39f0920af 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -24,7 +24,7 @@ module Rails opts.on("-p", "--port=port", Integer, "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } opts.on("-b", "--binding=IP", String, - "Binds Rails to the specified IP.", "Default: 0.0.0.0") { |v| options[:Host] = v } + "Binds Rails to the specified IP.", "Default: localhost") { |v| options[:Host] = v } opts.on("-c", "--config=file", String, "Uses a custom rackup configuration.") { |v| options[:config] = v } opts.on("-d", "--daemon", "Runs server as a Daemon.") { options[:daemonize] = true } @@ -126,10 +126,6 @@ module Rails puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}" puts "=> Run `rails server -h` for more startup options" - if options[:Host].to_s.match(/0\.0\.0\.0/) - puts "=> Notice: server is listening on all interfaces (#{options[:Host]}). Consider using 127.0.0.1 (--binding option)" - end - puts "=> Ctrl-C to shutdown server" unless options[:daemonize] end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index f8b4ee30d8..0eddf644d9 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -182,7 +182,7 @@ module ApplicationTests test "application is always added to eager_load namespaces" do require "#{app_path}/config/application" - assert Rails.application, Rails.application.config.eager_load_namespaces + assert_includes Rails.application.config.eager_load_namespaces, AppTemplate::Application end test "the application can be eager loaded even when there are no frameworks" do |