aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/metal.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb3
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb77
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb6
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb6
-rw-r--r--activemodel/lib/active_model/naming.rb2
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb55
-rw-r--r--activerecord/CHANGELOG.md5
-rw-r--r--activerecord/lib/active_record/attribute_mutation_tracker.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
-rw-r--r--activerecord/test/cases/sanitize_test.rb8
-rw-r--r--activerecord/test/cases/validations_test.rb11
-rw-r--r--activesupport/test/core_ext/hash/transform_keys_test.rb12
-rw-r--r--activesupport/test/core_ext/hash/transform_values_test.rb12
-rw-r--r--guides/source/engines.md20
-rw-r--r--railties/test/isolation/abstract_unit.rb1
18 files changed, 154 insertions, 85 deletions
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 3d72755f1d..beeaae9d0c 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -187,6 +187,7 @@ module ActionController
set_request!(request)
set_response!(response)
process(name)
+ request.commit_flash
to_a
end
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index a4dfe72c63..36e90e5855 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -131,7 +131,7 @@ to:
exchange_xml_items if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
delete_at(text_xml_idx) # delete text_xml from the list
elsif text_xml_idx
- text_xml.name = Mime::XML.to_s
+ text_xml.name = Mime::Type[:XML].to_s
end
# Look for more specific XML-based types and sort them ahead of app/xml
@@ -255,11 +255,11 @@ to:
parse_data_with_trailing_star($1) if accept_header =~ TRAILING_STAR_REGEXP
end
- # For an input of <tt>'text'</tt>, returns <tt>[Mime::JSON, Mime::XML, Mime::ICS,
- # Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]</tt>.
+ # For an input of <tt>'text'</tt>, returns <tt>[Mime::Type[:JSON], Mime::Type[:XML], Mime::Type[:ICS],
+ # Mime::Type[:HTML], Mime::Type[:CSS], Mime::Type[:CSV], Mime::Type[:JS], Mime::Type[:YAML], Mime::Type[:TEXT]</tt>.
#
- # For an input of <tt>'application'</tt>, returns <tt>[Mime::HTML, Mime::JS,
- # Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]</tt>.
+ # For an input of <tt>'application'</tt>, returns <tt>[Mime::Type[:HTML], Mime::Type[:JS],
+ # Mime::Type[:XML], Mime::Type[:YAML], Mime::Type[:ATOM], Mime::Type[:JSON], Mime::Type[:RSS], Mime::Type[:URL_ENCODED_FORM]</tt>.
def parse_data_with_trailing_star(input)
Mime::SET.select { |m| m =~ input }
end
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 0af3e5c0df..04828f7c87 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -32,5 +32,5 @@ Mime::Type.register "application/json", :json, %w( text/x-json application/jsonr
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
Mime::Type.register "application/zip", :zip, [], %w(zip)
-# Create Mime::ALL but do not add it to the SET.
+# Create Mime::Type[:ALL] but do not add it to the SET.
Mime::Type.add_type :ALL, Mime::Type::All.new("*/*", :all, [])
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index eaa7e88b34..bf20a33d36 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -384,6 +384,9 @@ module ActionDispatch
get_header("action_dispatch.logger".freeze)
end
+ def commit_flash
+ end
+
private
def check_method(name)
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 014b7bee61..c51dcd542a 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -1,25 +1,6 @@
require 'active_support/core_ext/hash/keys'
module ActionDispatch
- class Request
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
- # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
- # to put a new one.
- def flash
- flash = flash_hash
- return flash if flash
- self.flash = Flash::FlashHash.from_session_value(session["flash"])
- end
-
- def flash=(flash)
- set_header Flash::KEY, flash
- end
-
- def flash_hash # :nodoc:
- get_header Flash::KEY
- end
- end
-
# The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
@@ -57,6 +38,40 @@ module ActionDispatch
class Flash
KEY = 'action_dispatch.request.flash_hash'.freeze
+ module RequestMethods
+ # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
+ # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
+ # to put a new one.
+ def flash
+ flash = flash_hash
+ return flash if flash
+ self.flash = Flash::FlashHash.from_session_value(session["flash"])
+ end
+
+ def flash=(flash)
+ set_header Flash::KEY, flash
+ end
+
+ def flash_hash # :nodoc:
+ get_header Flash::KEY
+ end
+
+ def commit_flash # :nodoc:
+ session = self.session || {}
+ flash_hash = self.flash_hash
+
+ if flash_hash && (flash_hash.present? || session.key?('flash'))
+ session["flash"] = flash_hash.to_session_value
+ self.flash = flash_hash.dup
+ end
+
+ if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
+ session.key?('flash') && session['flash'].nil?
+ session.delete('flash')
+ end
+ end
+ end
+
class FlashNow #:nodoc:
attr_accessor :flash
@@ -268,26 +283,10 @@ module ActionDispatch
end
end
- def initialize(app)
- @app = app
- end
-
- def call(env)
- req = ActionDispatch::Request.new env
- @app.call(env)
- ensure
- session = req.session || {}
- flash_hash = req.flash_hash
-
- if flash_hash && (flash_hash.present? || session.key?('flash'))
- session["flash"] = flash_hash.to_session_value
- req.flash = flash_hash.dup
- end
+ def self.new(app) app; end
+ end
- if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
- session.key?('flash') && session['flash'].nil?
- session.delete('flash')
- end
- end
+ class Request
+ prepend Flash::RequestMethods
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 02b6cfe727..0e636b8257 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -62,11 +62,7 @@ module ActionDispatch
# would set the session cookie to expire automatically 14 days after creation.
# Other useful options include <tt>:key</tt>, <tt>:secure</tt> and
# <tt>:httponly</tt>.
- class CookieStore < Rack::Session::Abstract::Persisted
- include Compatibility
- include StaleSessionCheck
- include SessionObject
-
+ class CookieStore < AbstractStore
def initialize(app, options={})
super(app, options.merge!(:cookie_only => true))
end
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index d9fb44f06d..91f6f66fe3 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -154,10 +154,10 @@ class MimeTypeTest < ActiveSupport::TestCase
end
test "type convenience methods" do
- # Don't test Mime::ALL, since it Mime::ALL#html? == true
+ # Don't test Mime::Type[:ALL], since it Mime::Type[:ALL].html? == true
types = Mime::SET.symbols.uniq - [:all, :iphone]
- # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE
+ # Remove custom Mime::Type instances set in other tests, like Mime::Type[:GIF] and Mime::Type[:IPHONE]
types.delete_if { |type| !Mime::Type.registered?(type.upcase) }
types.each do |type|
@@ -186,7 +186,7 @@ class MimeTypeTest < ActiveSupport::TestCase
test "verifiable mime types" do
all_types = Mime::SET.symbols
all_types.uniq!
- # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE
+ # Remove custom Mime::Type instances set in other tests, like Mime::Type[:GIF] and Mime::Type[:IPHONE]
all_types.delete_if { |type| !Mime::Type.registered?(type.upcase) }
end
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 213f2d5b6a..de5fb27467 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -164,7 +164,7 @@ module ActiveModel
@route_key << "_index" if @plural == @singular
end
- # Transform the model name into a more humane format, using I18n. By default,
+ # Transform the model name into a more human format, using I18n. By default,
# it will underscore then humanize the class name.
#
# class BlogPost
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 1bcfedb35d..c5c0cd4636 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -14,16 +14,63 @@ module ActiveModel
end
private
+
def setup!(klass)
- attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
- attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
- klass.send(:attr_reader, *attr_readers)
- klass.send(:attr_writer, *attr_writers)
+ klass.include(LazilyDefineAttributes.new(AttributeDefinition.new(attributes)))
end
def acceptable_option?(value)
Array(options[:accept]).include?(value)
end
+
+ class LazilyDefineAttributes < Module
+ def initialize(attribute_definition)
+ define_method(:respond_to_missing?) do |method_name, include_private=false|
+ super(method_name, include_private) || attribute_definition.matches?(method_name)
+ end
+
+ define_method(:method_missing) do |method_name, *args, &block|
+ if attribute_definition.matches?(method_name)
+ attribute_definition.define_on(self.class)
+ send(method_name, *args, &block)
+ else
+ super(method_name, *args, &block)
+ end
+ end
+ end
+ end
+
+ class AttributeDefinition
+ def initialize(attributes)
+ @attributes = attributes.map(&:to_s)
+ end
+
+ def matches?(method_name)
+ attr_name = convert_to_reader_name(method_name)
+ attributes.include?(attr_name)
+ end
+
+ def define_on(klass)
+ attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
+ attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
+ klass.send(:attr_reader, *attr_readers)
+ klass.send(:attr_writer, *attr_writers)
+ end
+
+ protected
+
+ attr_reader :attributes
+
+ private
+
+ def convert_to_reader_name(method_name)
+ attr_name = method_name.to_s
+ if attr_name.end_with?("=")
+ attr_name = attr_name[0..-2]
+ end
+ attr_name
+ end
+ end
end
module HelperMethods
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 45c330b8af..6c0722e43c 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Don't require a database connection to load a class which uses acceptance
+ validations.
+
+ *Sean Griffin*
+
* Correctly apply `unscope` when preloading through associations.
*Jimmy Bourassa*
diff --git a/activerecord/lib/active_record/attribute_mutation_tracker.rb b/activerecord/lib/active_record/attribute_mutation_tracker.rb
index 168794fcb4..08eb8ba7ac 100644
--- a/activerecord/lib/active_record/attribute_mutation_tracker.rb
+++ b/activerecord/lib/active_record/attribute_mutation_tracker.rb
@@ -1,5 +1,5 @@
module ActiveRecord
- class AttributeMutationTracker
+ class AttributeMutationTracker # :nodoc:
def initialize(attributes, original_attributes)
@attributes = attributes
@original_attributes = original_attributes
@@ -61,7 +61,7 @@ module ActiveRecord
end
end
- class NullMutationTracker
+ class NullMutationTracker # :nodoc:
def changed_values
{}
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 038280509d..eb53a18f0f 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -264,7 +264,7 @@ module ActiveRecord
# Returns an array with distinct records based on the +group+ attribute:
#
# User.select([:id, :name])
- # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
+ # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">]
#
# User.group(:name)
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
@@ -713,7 +713,7 @@ module ActiveRecord
#
# users = User.readonly
# users.first.save
- # => ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
+ # => ActiveRecord::ReadOnlyRecord: User is marked as readonly
def readonly(value = true)
spawn.readonly!(value)
end
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index 262e0abc22..14e392ac30 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -9,11 +9,11 @@ class SanitizeTest < ActiveRecord::TestCase
def test_sanitize_sql_array_handles_string_interpolation
quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
- assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi"])
- assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi".mb_chars])
+ assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"])
+ assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper"])
- assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper".mb_chars])
+ assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
+ assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
end
def test_sanitize_sql_array_handles_bind_variables
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index a429d06aad..d04f4f7ce7 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -168,4 +168,15 @@ class ValidationsTest < ActiveRecord::TestCase
ensure
Topic.reset_column_information
end
+
+ def test_acceptance_validator_doesnt_require_db_connection
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'posts'
+ end
+ klass.reset_column_information
+
+ assert_no_queries do
+ klass.validates_acceptance_of(:foo)
+ end
+ end
end
diff --git a/activesupport/test/core_ext/hash/transform_keys_test.rb b/activesupport/test/core_ext/hash/transform_keys_test.rb
index a7e12117f3..5a0b99e22c 100644
--- a/activesupport/test/core_ext/hash/transform_keys_test.rb
+++ b/activesupport/test/core_ext/hash/transform_keys_test.rb
@@ -24,9 +24,21 @@ class TransformKeysTest < ActiveSupport::TestCase
assert_equal Enumerator, enumerator.class
end
+ test "transform_keys! returns an Enumerator if no block is given" do
+ original = { a: 'a', b: 'b' }
+ enumerator = original.transform_keys!
+ assert_equal Enumerator, enumerator.class
+ end
+
test "transform_keys is chainable with Enumerable methods" do
original = { a: 'a', b: 'b' }
mapped = original.transform_keys.with_index { |k, i| [k, i].join.to_sym }
assert_equal({ a0: 'a', b1: 'b' }, mapped)
end
+
+ test "transform_keys! is chainable with Enumerable methods" do
+ original = { a: 'a', b: 'b' }
+ original.transform_keys!.with_index { |k, i| [k, i].join.to_sym }
+ assert_equal({ a0: 'a', b1: 'b' }, original)
+ end
end
diff --git a/activesupport/test/core_ext/hash/transform_values_test.rb b/activesupport/test/core_ext/hash/transform_values_test.rb
index 45ed11fef7..7c33227dc0 100644
--- a/activesupport/test/core_ext/hash/transform_values_test.rb
+++ b/activesupport/test/core_ext/hash/transform_values_test.rb
@@ -53,9 +53,21 @@ class TransformValuesTest < ActiveSupport::TestCase
assert_equal Enumerator, enumerator.class
end
+ test "transform_values! returns an Enumerator if no block is given" do
+ original = { a: 'a', b: 'b' }
+ enumerator = original.transform_values!
+ assert_equal Enumerator, enumerator.class
+ end
+
test "transform_values is chainable with Enumerable methods" do
original = { a: 'a', b: 'b' }
mapped = original.transform_values.with_index { |v, i| [v, i].join }
assert_equal({ a: 'a0', b: 'b1' }, mapped)
end
+
+ test "transform_values! is chainable with Enumerable methods" do
+ original = { a: 'a', b: 'b' }
+ original.transform_values!.with_index { |v, i| [v, i].join }
+ assert_equal({ a: 'a0', b: 'b1' }, original)
+ end
end
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 6bbd68ff78..71844b7990 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -843,28 +843,10 @@ above the "Title" output inside `app/views/blorgh/articles/show.html.erb`:
```html+erb
<p>
<b>Author:</b>
- <%= @article.author %>
+ <%= @article.author.name %>
</p>
```
-By outputting `@article.author` using the `<%=` tag, the `to_s` method will be
-called on the object. By default, this will look quite ugly:
-
-```
-#<User:0x00000100ccb3b0>
-```
-
-This is undesirable. It would be much better to have the user's name there. To
-do this, add a `to_s` method to the `User` class within the application:
-
-```ruby
-def to_s
- name
-end
-```
-
-Now instead of the ugly Ruby object output, the author's name will be displayed.
-
#### Using a Controller Provided by the Application
Because Rails controllers generally share code for things like authentication
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 65d8a55421..41d36680e0 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -159,6 +159,7 @@ module TestHelpers
require "rails"
require "action_controller/railtie"
require "action_view/railtie"
+ require 'action_dispatch/middleware/flash'
app = Class.new(Rails::Application)
app.config.eager_load = false