diff options
20 files changed, 183 insertions, 73 deletions
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index c264c710f6..5a61746700 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -4,7 +4,11 @@ *Andrew White* * Add the ability to intercept emails before previewing in a similar fashion - to how emails can be intercepted before delivery, e.g: + to how emails can be intercepted before delivery. + + Fixes #13622. + + Example: class CSSInlineStyler def self.previewing_email(message) @@ -14,11 +18,9 @@ ActionMailer::Base.register_preview_interceptor CSSInlineStyler - Fixes #13622. - *Andrew White* -* Add mailer previews feature based on 37 Signals mail_view gem +* Add mailer previews feature based on 37 Signals mail_view gem. *Andrew White* diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index f836b69042..394b1473d3 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,16 +1,17 @@ * Add `:serializer` option for `config.session_store :cookie_store`. This - changes default serializer when using `:cookie_store` to - `ActionDispatch::Session::MarshalSerializer` which is wrapper on Marshal. + changes default serializer when using `:cookie_store`. - It is also possible to pass: + It is possible to pass: - * `:json_serializer` which is secure wrapper on JSON using `JSON.parse` and + * `:json` which is a secure wrapper on JSON using `JSON.parse` and `JSON.generate` methods with quirks mode; - * any other Symbol or String like `:my_custom_serializer` which will be - camelized and constantized in `ActionDispatch::Session` namespace; - * serializer object with `load` and `dump` methods defined. + * `:marshal` which is a wrapper on Marshal; + * serializer class with `load` and `dump` methods defined. - *Łukasz Sarnacki + Matt Aimonetti* + For new apps `:json` option is added by default and :marshal is used + when no option is specified. + + *Łukasz Sarnacki*, *Matt Aimonetti* * Ensure that `request.filtered_parameters` is reset between calls to `process` in `ActionController::TestCase`. diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index f9f034952e..23d0ecd529 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -466,10 +466,12 @@ module ActionDispatch end def serializer - serializer = @options[:session_serializer] || :marshal_serializer + serializer = @options[:session_serializer] || :marshal case serializer - when Symbol, String - ActionDispatch::Session.const_get(serializer.to_s.camelize) + when :marshal + ActionDispatch::Session::MarshalSerializer + when :json + ActionDispatch::Session::JsonSerializer else serializer end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index b19ce905f5..6101acdc25 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -379,7 +379,7 @@ class CookiesTest < ActionController::TestCase assert_equal 'bar', cookies.encrypted[:foo] end - class ActionDispatch::Session::CustomJsonSerializer + class CustomJsonSerializer def self.load(value) JSON.load(value) + " and loaded" end @@ -389,20 +389,14 @@ class CookiesTest < ActionController::TestCase end end - def test_encrypted_cookie_using_custom_json_serializer - @request.env["action_dispatch.session_serializer"] = :custom_json_serializer - get :set_encrypted_cookie - assert_equal 'bar was dumped and loaded', cookies.encrypted[:foo] - end - def test_encrypted_cookie_using_serializer_object - @request.env["action_dispatch.session_serializer"] = ActionDispatch::Session::CustomJsonSerializer + @request.env["action_dispatch.session_serializer"] = CustomJsonSerializer get :set_encrypted_cookie assert_equal 'bar was dumped and loaded', cookies.encrypted[:foo] end def test_encrypted_cookie_using_json_serializer - @request.env["action_dispatch.session_serializer"] = :json_serializer + @request.env["action_dispatch.session_serializer"] = :json get :set_encrypted_cookie cookies = @controller.send :cookies assert_not_equal 'bar', cookies[:foo] diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 960f867d99..c370f3df51 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,20 +1,20 @@ * Added `config.action_view.raise_on_missing_translations` to define whether an error should be raised for missing translations. - Fixes #13196 + Fixes #13196. *Kassio Borges* * Improved ERB dependency detection. New argument types and formattings for the `render` calls can be matched. - Fixes #13074 and #13116 + Fixes #13074, #13116. *João Britto* -* Use `display:none` instead of `display:inline` for hidden fields +* Use `display:none` instead of `display:inline` for hidden fields. - Fixes #6403 + Fixes #6403. *Gaelian Ditchburn* @@ -82,11 +82,11 @@ *Yves Senn* -* Use `set_backtrace` instead of instance variable `@backtrace` in ActionView exceptions +* Use `set_backtrace` instead of instance variable `@backtrace` in ActionView exceptions. *Shimpei Makimoto* -* Fix `simple_format` escapes own output when passing `sanitize: true` +* Fix `simple_format` escapes own output when passing `sanitize: true`. *Paul Seidemann* @@ -104,7 +104,9 @@ *Bogdan Gusiev* -* Ability to pass block to `select` helper +* Ability to pass a block to the `select` helper. + + Example: <%= select(report, "campaign_ids") do %> <% available_campaigns.each do |c| -%> @@ -184,7 +186,7 @@ * Fix default rendered format problem when calling `render` without :content_type option. It should return :html. Fix #11393. - *Gleb Mazovetskiy* *Oleg* *kennyj* + *Gleb Mazovetskiy*, *Oleg*, *kennyj* * Fix `link_to` with block and url hashes. diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 0148066bac..6585808fa2 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -18,9 +18,12 @@ *Vince Puzzella* -* `attribute_changed?` now accepts parameters which check the old and new value of the attribute +* `attribute_changed?` now accepts a hash to check if the attribute was + changed `:from` and/or `:to` a given value. - `model.name_changed?(from: "Pete", to: "Ringo")` + Example: + + model.name_changed?(from: "Pete", to: "Ringo") *Tejas Dinkar* diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index fe0d7b2b35..7df4720ea5 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,15 @@ +* Active Record objects can now be correctly dumped, loaded and dumped again without issues. + + Previously, if you did `YAML.dump`, `YAML.load` and then `YAML.dump` again + in an ActiveRecord model that used serialization it would fail at the last + dump due to the fields not being correctly serialized before being dumped + to YAML. Now it is possible to dump and load the same object as many times + as needed without any issues. + + Fixes #13861. + + *Maurício Linhares* + * `find_in_batches` now returns an `Enumerator` when called without a block, so that it can be chained with other `Enumerable` methods. diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index ccbff8d1ff..9326c9c117 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -278,6 +278,11 @@ module ActiveRecord } end + # Placeholder so it can be overriden when needed by serialization + def attributes_for_coder # :nodoc: + attributes + end + # Returns an <tt>#inspect</tt>-like string for the value of the # attribute +attr_name+. String attributes are truncated upto 50 # characters, Date and Time attributes are returned in the diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 3227464032..67abbbc2a0 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -164,6 +164,16 @@ module ActiveRecord super end end + + def attributes_for_coder + attribute_names.each_with_object({}) do |name, attrs| + attrs[name] = if self.class.serialized_attributes.include?(name) + @attributes[name].serialized_value + else + read_attribute(name) + end + end + end end end end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 6f02c763fe..6303fe5ee4 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -275,7 +275,7 @@ module ActiveRecord # Post.new.encode_with(coder) # coder # => {"attributes" => {"id" => nil, ... }} def encode_with(coder) - coder['attributes'] = attributes + coder['attributes'] = attributes_for_coder end # Returns true if +comparison_object+ is the same exact object, or +comparison_object+ diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 88fc47fada..14470f22aa 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -120,6 +120,9 @@ module ActiveRecord # Will throw an error, but this will work: # # User.includes(:posts).where('posts.name = ?', 'example').references(:posts) + # + # Note that +includes+ works with association names while +references+ needs + # the actual table name. def includes(*args) check_if_method_has_arguments!(:includes, args) spawn.includes!(*args) @@ -163,24 +166,26 @@ module ActiveRecord self end - # Used to indicate that an association is referenced by an SQL string, and should - # therefore be JOINed in any query rather than loaded separately. + # Use to indicate that the given +table_names+ are referenced by an SQL string, + # and should therefore be JOINed in any query rather than loaded separately. + # This method only works in conjuction with +includes+. + # See #includes for more details. # # User.includes(:posts).where("posts.name = 'foo'") # # => Doesn't JOIN the posts table, resulting in an error. # # User.includes(:posts).where("posts.name = 'foo'").references(:posts) # # => Query now knows the string references posts, so adds a JOIN - def references(*args) - check_if_method_has_arguments!(:references, args) - spawn.references!(*args) + def references(*table_names) + check_if_method_has_arguments!(:references, table_names) + spawn.references!(*table_names) end - def references!(*args) # :nodoc: - args.flatten! - args.map!(&:to_s) + def references!(*table_names) # :nodoc: + table_names.flatten! + table_names.map!(&:to_s) - self.references_values |= args + self.references_values |= table_names self end diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb index 6f632b4d8d..978cee9cfb 100644 --- a/activerecord/test/cases/store_test.rb +++ b/activerecord/test/cases/store_test.rb @@ -166,4 +166,28 @@ class StoreTest < ActiveRecord::TestCase test "YAML coder initializes the store when a Nil value is given" do assert_equal({}, @john.params) end + + test "attributes_for_coder should return stored fields already serialized" do + attributes = { + "id" => @john.id, + "name"=> @john.name, + "settings" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\ncolor: black\n", + "preferences" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nremember_login: true\n", + "json_data" => "{\"height\":\"tall\"}", "json_data_empty"=>"{\"is_a_good_guy\":true}", + "params" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess {}\n", + "account_id"=> @john.account_id + } + + assert_equal attributes, @john.attributes_for_coder + end + + test "dump, load and dump again a model" do + dumped = YAML.dump(@john) + loaded = YAML.load(dumped) + assert_equal @john, loaded + + second_dump = YAML.dump(loaded) + assert_equal dumped, second_dump + assert_equal @john, YAML.load(second_dump) + end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 95bf5601f2..008d71701c 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,10 @@ +* Remove the deprecation about the `#filter` method. + + Filter objects should now rely on method corresponding to the filter type + (e.g. `#before`). + + *Aaron Patterson* + * Add `ActiveSupport::JSON::Encoding.time_precision` as a way to configure the precision of encoded time values: diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb index 94230e56ba..0ee6332d6f 100644 --- a/activesupport/lib/active_support/testing/time_helpers.rb +++ b/activesupport/lib/active_support/testing/time_helpers.rb @@ -1,7 +1,51 @@ module ActiveSupport module Testing + class SimpleStubs # :nodoc: + Stub = Struct.new(:object, :method_name, :original_method) + + def initialize + @stubs = {} + end + + def stub_object(object, method_name, return_value) + key = [object.object_id, method_name] + + if (stub = @stubs[key]) + unstub_object(stub) + end + + new_name = "__simple_stub__#{method_name}" + + @stubs[key] = Stub.new(object, method_name, new_name) + + object.singleton_class.send :alias_method, new_name, method_name + object.define_singleton_method(method_name) { return_value } + end + + def unstub_all! + @stubs.each_value do |stub| + unstub_object(stub) + end + @stubs = {} + end + + private + + def unstub_object(stub) + singleton_class = stub.object.singleton_class + singleton_class.send :undef_method, stub.method_name + singleton_class.send :alias_method, stub.method_name, stub.original_method + singleton_class.send :undef_method, stub.original_method + end + end + # Containing helpers that helps you test passage of time. module TimeHelpers + def after_teardown #:nodoc: + simple_stubs.unstub_all! + super + end + # Change current time to the time in the future or in the past by a given time difference by # stubbing +Time.now+ and +Date.today+. Note that the stubs are automatically removed # at the end of each test. @@ -41,15 +85,20 @@ module ActiveSupport # end # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 def travel_to(date_or_time, &block) - Time.stubs now: date_or_time.to_time - Date.stubs today: date_or_time.to_date + simple_stubs.stub_object(Time, :now, date_or_time.to_time) + simple_stubs.stub_object(Date, :today, date_or_time.to_date) if block_given? block.call - Time.unstub :now - Date.unstub :today + simple_stubs.unstub_all! end end + + private + + def simple_stubs + @simple_stubs ||= SimpleStubs.new + end end end end diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md index 477268f4bc..7399bfb5de 100644 --- a/guides/source/4_1_release_notes.md +++ b/guides/source/4_1_release_notes.md @@ -567,6 +567,9 @@ for detailed changes. * Removed deprecated `assert_present` and `assert_blank` methods, use `assert object.blank?` and `assert object.present?` instead. +* Remove deprecated `#filter` method for filter objects, use the corresponding + method instead (e.g. `#before` for a before filter). + ### Deprecations * Deprecated `Numeric#{ago,until,since,from_now}`, the user is expected to diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 0234120b45..9eaf03dd82 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -384,20 +384,14 @@ YourApp::Application.config.session_store :cookie_store, key: '_your_app_session You can pass `:serializer` key to specify serializer for serializing session: ```ruby -YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', serializer: :json_serializer +YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', serializer: :json ``` -Default serializer is `:marshal_serializer`. When Symbol or String is passed it -will look for appropriate class in `ActionDispatch::Session` namespace, so -passing `:my_custom_serializer` would load -`ActionDispatch::Session::MyCustomSerializer`. +The default serializer for new application is `:json`. For compatibility with +old applications `:marshal` is used when `serializer` option is not specified. -```ruby -YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', serializer: :my_custom_serializer -``` - -It is also possible to pass serializer object with defined `load` and `dump` -public methods: +It is also possible to pass a custom serializer class with `load` and `dump` +public methods defined: ```ruby YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', serializer: MyCustomSerializer @@ -709,7 +703,7 @@ class ApplicationController < ActionController::Base end class LoginFilter - def self.filter(controller) + def self.before(controller) unless controller.send(:logged_in?) controller.flash[:error] = "You must be logged in to access this section" controller.redirect_to controller.new_login_url @@ -718,7 +712,7 @@ class LoginFilter end ``` -Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class has a class method `filter` which gets run before or after the action, depending on if it's a before or after filter. Classes used as around filters can also use the same `filter` method, which will get run in the same way. The method must `yield` to execute the action. Alternatively, it can have both a `before` and an `after` method that are run before and after the action. +Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class must implement a method with the same name as the filter, so for the `before_action` filter the class must implement a `before` method, and so on. The `around` method must `yield` to execute the action. Request Forgery Protection -------------------------- diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 40e0770177..0422dda0d8 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -496,16 +496,11 @@ In this example, `require_self` is used. This puts the CSS contained within the file (if any) at the precise location of the `require_self` call. If `require_self` is called more than once, only the last call is respected. -NOTE. If you want to use multiple Sass files, you should generally use the [Sass -`@import` -rule](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import) instead -of these Sprockets directives. Using Sprockets directives all Sass files exist -within their own scope, making variables or mixins only available within the -document they were defined in. You can do file globbing as well using -`@import "*"`, and `@import "**/*"` to add the whole tree equivalent to how -`require_tree` works. Check the [sass-rails -documentation](https://github.com/rails/sass-rails#features) for more info and -important caveats. +NOTE. If you want to use multiple Sass files, you should generally use the [Sass `@import` rule](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import) +instead of these Sprockets directives. Using Sprockets directives all Sass files exist within +their own scope, making variables or mixins only available within the document they were defined in. +You can do file globbing as well using `@import "*"`, and `@import "**/*"` to add the whole tree +equivalent to how `require_tree` works. Check the [sass-rails documentation](https://github.com/rails/sass-rails#features) for more info and important caveats. You can have as many manifest files as you need. For example, the `admin.css` and `admin.js` manifest could contain the JS and CSS files that are used for the diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index e902205a13..20e3de32aa 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -109,6 +109,8 @@ module Rails raise "YAML syntax error occurred while parsing #{paths["config/database"].first}. " \ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \ "Error: #{e.message}" + rescue => e + raise e, "Cannot load `Rails.application.database_configuration`:\n#{e.message}", e.backtrace end def log_level diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt index 923d423287..097fcb4bb0 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Rails.application.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %>, serializer: :json_serializer +Rails.application.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %>, serializer: :json diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 8aa306c8e0..700935fd8d 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -433,7 +433,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_new_hash_style run_generator [destination_root] assert_file "config/initializers/session_store.rb" do |file| - assert_match(/config.session_store :cookie_store, key: '_.+_session', serializer: :json_serializer/, file) + assert_match(/config.session_store :cookie_store, key: '_.+_session', serializer: :json/, file) end end |