aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md10
-rw-r--r--actionpack/lib/action_dispatch/routing.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_view.rb4
-rw-r--r--actionpack/test/controller/routing_test.rb5
-rw-r--r--actionpack/test/dispatch/routing_test.rb4
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb3
-rw-r--r--activemodel/lib/active_model/configuration.rb134
-rw-r--r--activemodel/lib/active_model/mass_assignment_security.rb10
-rw-r--r--activemodel/lib/active_model/serializers/json.rb5
-rw-r--r--activemodel/lib/active_model/validations.rb3
-rw-r--r--activemodel/test/cases/configuration_test.rb154
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb19
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb26
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb13
-rw-r--r--activerecord/lib/active_record/callbacks.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb14
-rw-r--r--activerecord/lib/active_record/connection_handling.rb4
-rw-r--r--activerecord/lib/active_record/core.rb137
-rw-r--r--activerecord/lib/active_record/counter_cache.rb200
-rw-r--r--activerecord/lib/active_record/explain.rb11
-rw-r--r--activerecord/lib/active_record/inheritance.rb10
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb8
-rw-r--r--activerecord/lib/active_record/model.rb104
-rw-r--r--activerecord/lib/active_record/model_schema.rb23
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb26
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb3
-rw-r--r--activerecord/lib/active_record/scoping/default.rb2
-rw-r--r--activerecord/lib/active_record/serialization.rb12
-rw-r--r--activerecord/lib/active_record/timestamp.rb8
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb4
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb1
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb5
-rw-r--r--activerecord/test/cases/mass_assignment_security_test.rb59
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb2
-rw-r--r--activesupport/lib/active_support/notifications.rb2
-rw-r--r--guides/source/routing.textile8
-rw-r--r--guides/source/upgrading_ruby_on_rails.textile2
-rw-r--r--railties/test/application/configuration_test.rb39
45 files changed, 530 insertions, 605 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 8c2d887cb3..345b5aa330 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,15 @@
## Rails 4.0.0 (unreleased) ##
+* Support unicode characters in routes. Route will be automatically escaped, so instead of manually escaping:
+
+ get Rack::Utils.escape('こんにちは') => 'home#index'
+
+ You just have to write the unicode route:
+
+ get 'こんにちは' => 'home#index'
+
+ *kennyj*
+
* Return proper format on exceptions. *Santiago Pastorino*
* Allow to use mounted_helpers (helpers for accessing mounted engines) in ActionView::TestCase. *Piotr Sarnacki*
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 38a0270151..29090882a5 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -1,3 +1,4 @@
+# encoding: UTF-8
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/regexp'
@@ -218,6 +219,12 @@ module ActionDispatch
#
# match "/stories" => redirect("/posts")
#
+ # == Unicode character routes
+ #
+ # You can specify unicode character routes in your router:
+ #
+ # match "こんにちは" => "welcome#index"
+ #
# == Routing to Rack Applications
#
# Instead of a String, like <tt>posts#index</tt>, which corresponds to the
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 25d099d83e..94242ad962 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1387,7 +1387,7 @@ module ActionDispatch
options[:as] = name_for_action(options[:as], action)
end
- mapping = Mapping.new(@set, @scope, path, options)
+ mapping = Mapping.new(@set, @scope, URI.parser.escape(path), options)
app, conditions, requirements, defaults, as, anchor = mapping.to_route
@set.add_route(app, conditions, requirements, defaults, as, anchor)
end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 3823f87027..03dfa110e3 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -36,7 +36,7 @@ module ActionView
autoload :LookupContext
autoload :PathSet
autoload :Template
- autoload :TestCase
+
autoload_under "renderer" do
autoload :Renderer
@@ -73,6 +73,8 @@ module ActionView
end
end
+ autoload :TestCase
+
ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*'
end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 306b8a93da..6cc1370105 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -1760,6 +1760,7 @@ class RackMountIntegrationTests < ActiveSupport::TestCase
get 'account(/:action)' => "account#subscription"
get 'pages/:page_id/:controller(/:action(/:id))'
get ':controller/ping', :action => 'ping'
+ get 'こんにちは/世界', :controller => 'news', :action => 'index'
match ':controller(/:action(/:id))(.:format)', :via => :all
root :to => "news#index"
}
@@ -1876,6 +1877,10 @@ class RackMountIntegrationTests < ActiveSupport::TestCase
assert_equal({:controller => 'people', :action => 'create', :person => { :name => 'Josh'}}, params)
end
+ def test_unicode_path
+ assert_equal({:controller => 'news', :action => 'index'}, @routes.recognize_path(URI.parser.escape('こんにちは/世界'), :method => :get))
+ end
+
private
def sort_extras!(extras)
if extras.length == 2
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index fa4cb301eb..f15dd7214b 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -2472,7 +2472,7 @@ end
class TestUnicodePaths < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
- get "/#{Rack::Utils.escape("ほげ")}" => lambda { |env|
+ get "/ほげ" => lambda { |env|
[200, { 'Content-Type' => 'text/plain' }, []]
}, :as => :unicode_path
end
@@ -2727,4 +2727,4 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
assert_response :bad_request
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index ded1b752df..f6bacf5ec0 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -30,7 +30,6 @@ module ActiveModel
autoload :AttributeMethods
autoload :BlockValidator, 'active_model/validator'
autoload :Callbacks
- autoload :Configuration
autoload :Conversion
autoload :Dirty
autoload :EachValidator, 'active_model/validator'
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 846d0d7f86..b5b50847c2 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -61,8 +61,7 @@ module ActiveModel
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
included do
- extend ActiveModel::Configuration
- config_attribute :attribute_method_matchers
+ class_attribute :attribute_method_matchers, instance_writer: false
self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
end
diff --git a/activemodel/lib/active_model/configuration.rb b/activemodel/lib/active_model/configuration.rb
deleted file mode 100644
index ba5a6a2075..0000000000
--- a/activemodel/lib/active_model/configuration.rb
+++ /dev/null
@@ -1,134 +0,0 @@
-require 'active_support/concern'
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/class/attribute_accessors'
-
-module ActiveModel
- # This API is for Rails' internal use and is not currently considered 'public', so
- # it may change in the future without warning.
- #
- # It creates configuration attributes that can be inherited from a module down
- # to a class that includes the module. E.g.
- #
- # module MyModel
- # extend ActiveModel::Configuration
- # config_attribute :awesome
- # self.awesome = true
- # end
- #
- # class Post
- # include MyModel
- # end
- #
- # Post.awesome # => true
- #
- # Post.awesome = false
- # Post.awesome # => false
- # MyModel.awesome # => true
- #
- # We assume that the module will have a ClassMethods submodule containing methods
- # to be transferred to the including class' singleton class.
- #
- # Config options can also be defined directly on a class:
- #
- # class Post
- # extend ActiveModel::Configuration
- # config_attribute :awesome
- # end
- #
- # So this allows us to define a module that doesn't care about whether it is being
- # included in a class or a module:
- #
- # module Awesomeness
- # extend ActiveSupport::Concern
- #
- # included do
- # extend ActiveModel::Configuration
- # config_attribute :awesome
- # self.awesome = true
- # end
- # end
- #
- # class Post
- # include Awesomeness
- # end
- #
- # module AwesomeModel
- # include Awesomeness
- # end
- module Configuration #:nodoc:
- def config_attribute(name, options = {})
- klass = self.is_a?(Class) ? ClassAttribute : ModuleAttribute
- klass.new(self, name, options).define
- end
-
- class Attribute
- attr_reader :host, :name, :options
-
- def initialize(host, name, options)
- @host, @name, @options = host, name, options
- end
-
- def instance_writer?
- options.fetch(:instance_writer, false)
- end
- end
-
- class ClassAttribute < Attribute
- def define
- if options[:global]
- host.cattr_accessor name, :instance_writer => instance_writer?
- else
- host.class_attribute name, :instance_writer => instance_writer?
- end
- end
- end
-
- class ModuleAttribute < Attribute
- def class_methods
- @class_methods ||= begin
- if host.const_defined?(:ClassMethods, false)
- host.const_get(:ClassMethods)
- else
- host.const_set(:ClassMethods, Module.new)
- end
- end
- end
-
- def define
- host.singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
- attr_accessor :#{name}
- def #{name}?; !!#{name}; end
- CODE
-
- name, host = self.name, self.host
-
- class_methods.class_eval do
- define_method(name) { host.send(name) }
- define_method("#{name}?") { !!send(name) }
- end
-
- host.class_eval <<-CODE, __FILE__, __LINE__ + 1
- def #{name}; defined?(@#{name}) ? @#{name} : self.class.#{name}; end
- def #{name}?; !!#{name}; end
- CODE
-
- if options[:global]
- class_methods.class_eval do
- define_method("#{name}=") { |val| host.send("#{name}=", val) }
- end
- else
- class_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
- def #{name}=(val)
- singleton_class.class_eval do
- remove_possible_method(:#{name})
- define_method(:#{name}) { val }
- end
- end
- CODE
- end
-
- host.send(:attr_writer, name) if instance_writer?
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb
index cfce1542b1..8f2c0bf00a 100644
--- a/activemodel/lib/active_model/mass_assignment_security.rb
+++ b/activemodel/lib/active_model/mass_assignment_security.rb
@@ -9,13 +9,11 @@ module ActiveModel
extend ActiveSupport::Concern
included do
- extend ActiveModel::Configuration
+ class_attribute :_accessible_attributes, instance_writer: false
+ class_attribute :_protected_attributes, instance_writer: false
+ class_attribute :_active_authorizer, instance_writer: false
- config_attribute :_accessible_attributes
- config_attribute :_protected_attributes
- config_attribute :_active_authorizer
-
- config_attribute :_mass_assignment_sanitizer
+ class_attribute :_mass_assignment_sanitizer, instance_writer: false
self.mass_assignment_sanitizer = :logger
end
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index e668b4a009..b4baf3a946 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -10,9 +10,8 @@ module ActiveModel
included do
extend ActiveModel::Naming
- extend ActiveModel::Configuration
- config_attribute :include_root_in_json
+ class_attribute :include_root_in_json
self.include_root_in_json = false
end
@@ -106,4 +105,4 @@ module ActiveModel
end
end
end
-end \ No newline at end of file
+end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 611e9ffd55..06eebf79d9 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -52,8 +52,7 @@ module ActiveModel
attr_accessor :validation_context
define_callbacks :validate, :scope => :name
- extend ActiveModel::Configuration
- config_attribute :_validators
+ class_attribute :_validators
self._validators = Hash.new { |h,k| h[k] = [] }
end
diff --git a/activemodel/test/cases/configuration_test.rb b/activemodel/test/cases/configuration_test.rb
deleted file mode 100644
index a172fa26a3..0000000000
--- a/activemodel/test/cases/configuration_test.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-require 'cases/helper'
-
-class ConfigurationOnModuleTest < ActiveModel::TestCase
- def setup
- @mod = mod = Module.new do
- extend ActiveSupport::Concern
- extend ActiveModel::Configuration
-
- config_attribute :omg
- self.omg = "default"
-
- config_attribute :wtf, global: true
- self.wtf = "default"
-
- config_attribute :boolean
-
- config_attribute :lol, instance_writer: true
- end
-
- @klass = Class.new do
- include mod
- end
-
- @subklass = Class.new(@klass)
- end
-
- test "default" do
- assert_equal "default", @mod.omg
- assert_equal "default", @klass.omg
- assert_equal "default", @klass.new.omg
- end
-
- test "setting" do
- @mod.omg = "lol"
- assert_equal "lol", @mod.omg
- end
-
- test "setting on class including the module" do
- @klass.omg = "lol"
- assert_equal "lol", @klass.omg
- assert_equal "lol", @klass.new.omg
- assert_equal "default", @mod.omg
- end
-
- test "setting on subclass of class including the module" do
- @subklass.omg = "lol"
- assert_equal "lol", @subklass.omg
- assert_equal "default", @klass.omg
- assert_equal "default", @mod.omg
- end
-
- test "setting on instance" do
- assert !@klass.new.respond_to?(:omg=)
-
- @klass.lol = "lol"
- obj = @klass.new
- assert_equal "lol", obj.lol
- obj.lol = "omg"
- assert_equal "omg", obj.lol
- assert_equal "lol", @klass.lol
- assert_equal "lol", @klass.new.lol
- obj.lol = false
- assert !obj.lol?
- end
-
- test "global attribute" do
- assert_equal "default", @mod.wtf
- assert_equal "default", @klass.wtf
-
- @mod.wtf = "wtf"
-
- assert_equal "wtf", @mod.wtf
- assert_equal "wtf", @klass.wtf
-
- @klass.wtf = "lol"
-
- assert_equal "lol", @mod.wtf
- assert_equal "lol", @klass.wtf
- end
-
- test "boolean" do
- assert_equal false, @mod.boolean?
- assert_equal false, @klass.new.boolean?
- @mod.boolean = true
- assert_equal true, @mod.boolean?
- assert_equal true, @klass.new.boolean?
- end
-end
-
-class ConfigurationOnClassTest < ActiveModel::TestCase
- def setup
- @klass = Class.new do
- extend ActiveModel::Configuration
-
- config_attribute :omg
- self.omg = "default"
-
- config_attribute :wtf, global: true
- self.wtf = "default"
-
- config_attribute :omg2, instance_writer: true
- config_attribute :wtf2, instance_writer: true, global: true
- end
-
- @subklass = Class.new(@klass)
- end
-
- test "defaults" do
- assert_equal "default", @klass.omg
- assert_equal "default", @klass.wtf
- assert_equal "default", @subklass.omg
- assert_equal "default", @subklass.wtf
- end
-
- test "changing" do
- @klass.omg = "lol"
- assert_equal "lol", @klass.omg
- assert_equal "lol", @subklass.omg
- end
-
- test "changing in subclass" do
- @subklass.omg = "lol"
- assert_equal "lol", @subklass.omg
- assert_equal "default", @klass.omg
- end
-
- test "changing global" do
- @klass.wtf = "wtf"
- assert_equal "wtf", @klass.wtf
- assert_equal "wtf", @subklass.wtf
-
- @subklass.wtf = "lol"
- assert_equal "lol", @klass.wtf
- assert_equal "lol", @subklass.wtf
- end
-
- test "instance_writer" do
- obj = @klass.new
-
- @klass.omg2 = "omg"
- @klass.wtf2 = "wtf"
-
- assert_equal "omg", obj.omg2
- assert_equal "wtf", obj.wtf2
-
- obj.omg2 = "lol"
- obj.wtf2 = "lol"
-
- assert_equal "lol", obj.omg2
- assert_equal "lol", obj.wtf2
- assert_equal "omg", @klass.omg2
- assert_equal "lol", @klass.wtf2
- end
-end
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index abc2fa546a..df4de8ac35 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -1,11 +1,25 @@
require 'active_support/concern'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :whitelist_attributes, instance_accessor: false
+ mattr_accessor :mass_assignment_sanitizer, instance_accessor: false
+ end
+
module AttributeAssignment
extend ActiveSupport::Concern
include ActiveModel::MassAssignmentSecurity
+ included do
+ initialize_mass_assignment_sanitizer
+ end
+
module ClassMethods
+ def inherited(child) # :nodoc:
+ child.send :initialize_mass_assignment_sanitizer if self == Base
+ super
+ end
+
private
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
@@ -14,6 +28,11 @@ module ActiveRecord
default << 'id' unless primary_key.eql? 'id'
default
end
+
+ def initialize_mass_assignment_sanitizer
+ attr_accessible(nil) if Model.whitelist_attributes
+ self.mass_assignment_sanitizer = Model.mass_assignment_sanitizer if Model.mass_assignment_sanitizer
+ end
end
# Allows you to set all the attributes at once by passing in a hash with keys
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 172026d150..f36df4a444 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -16,19 +16,6 @@ module ActiveRecord
include TimeZoneConversion
include Dirty
include Serialization
-
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
- # (Alias for the protected read_attribute method).
- def [](attr_name)
- read_attribute(attr_name)
- end
-
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
- # (Alias for the protected write_attribute method).
- def []=(attr_name, value)
- write_attribute(attr_name, value)
- end
end
module ClassMethods
@@ -192,6 +179,19 @@ module ActiveRecord
self.class.columns_hash[name.to_s]
end
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+ # (Alias for the protected read_attribute method).
+ def [](attr_name)
+ read_attribute(attr_name)
+ end
+
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
+ # (Alias for the protected write_attribute method).
+ def []=(attr_name, value)
+ write_attribute(attr_name, value)
+ end
+
protected
def clone_attributes(reader_method = :read_attribute, attributes = {})
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 11c63591e3..f8a40ad520 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -1,12 +1,18 @@
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/module/attribute_accessors'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :partial_updates, instance_accessor: false
+ self.partial_updates = true
+ end
+
module AttributeMethods
module Dirty
extend ActiveSupport::Concern
+
include ActiveModel::Dirty
- include AttributeMethods::Write
included do
if self < ::ActiveRecord::Timestamp
@@ -14,7 +20,6 @@ module ActiveRecord
end
config_attribute :partial_updates
- self.partial_updates = true
end
# Attempts to +save+ the record and clears changed attributes if successful.
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index dcc3d79de9..a7af086e43 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -1,13 +1,17 @@
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :attribute_types_cached_by_default, instance_accessor: false
+ end
+
module AttributeMethods
module Read
extend ActiveSupport::Concern
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
+ ActiveRecord::Model.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
included do
- config_attribute :attribute_types_cached_by_default, :global => true
- self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+ config_attribute :attribute_types_cached_by_default
end
module ClassMethods
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 706fbf0546..4af4d28b74 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -6,7 +6,7 @@ module ActiveRecord
included do
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
- config_attribute :serialized_attributes
+ class_attribute :serialized_attributes
self.serialized_attributes = {}
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 58a5d82e14..e300c9721f 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -2,6 +2,14 @@ require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/object/inclusion'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :time_zone_aware_attributes, instance_accessor: false
+ self.time_zone_aware_attributes = false
+
+ mattr_accessor :skip_time_zone_conversion_for_attributes, instance_accessor: false
+ self.skip_time_zone_conversion_for_attributes = []
+ end
+
module AttributeMethods
module TimeZoneConversion
class Type # :nodoc:
@@ -22,11 +30,8 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- config_attribute :time_zone_aware_attributes, :global => true
- self.time_zone_aware_attributes = false
-
+ config_attribute :time_zone_aware_attributes, global: true
config_attribute :skip_time_zone_conversion_for_attributes
- self.skip_time_zone_conversion_for_attributes = []
end
module ClassMethods
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index a050fabf35..cc11567506 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -231,30 +231,6 @@ module ActiveRecord
# Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
#
module Callbacks
- # We can't define callbacks directly on ActiveRecord::Model because
- # it is a module. So we queue up the definitions and execute them
- # when ActiveRecord::Model is included.
- module Register #:nodoc:
- def self.extended(base)
- base.config_attribute :_callbacks_register
- base._callbacks_register = []
- end
-
- def self.setup(base)
- base._callbacks_register.each do |item|
- base.send(*item)
- end
- end
-
- def define_callbacks(*args)
- self._callbacks_register << [:define_callbacks, *args]
- end
-
- def define_model_callbacks(*args)
- self._callbacks_register << [:define_model_callbacks, *args]
- end
- end
-
extend ActiveSupport::Concern
CALLBACKS = [
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 61ff603006..347d794fa3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -448,11 +448,11 @@ module ActiveRecord
end
def new_connection
- ActiveRecord::Base.send(spec.adapter_method, spec.config)
+ ActiveRecord::Model.send(spec.adapter_method, spec.config)
end
def current_connection_id #:nodoc:
- ActiveRecord::Base.connection_id ||= Thread.current.object_id
+ ActiveRecord::Model.connection_id ||= Thread.current.object_id
end
def checkout_new_connection
@@ -563,10 +563,12 @@ module ActiveRecord
end
def retrieve_connection_pool(klass)
- pool = get_pool_for_class klass.name
- return pool if pool
- return nil if ActiveRecord::Model == klass
- retrieve_connection_pool klass.active_record_super
+ if !(klass < Model::Tag)
+ get_pool_for_class('ActiveRecord::Model') # default connection
+ else
+ pool = get_pool_for_class(klass.name)
+ pool || retrieve_connection_pool(klass.superclass)
+ end
end
private
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 7b218a5570..bda41df80f 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -90,6 +90,10 @@ module ActiveRecord
connection_handler.remove_connection(klass)
end
+ def clear_cache! # :nodoc:
+ connection.schema_cache.clear!
+ end
+
delegate :clear_active_connections!, :clear_reloadable_connections!,
:clear_all_connections!, :to => :connection_handler
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index dbad561ca2..f5d60d11b6 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -1,80 +1,88 @@
require 'active_support/concern'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/object/deep_dup'
+require 'active_support/core_ext/module/delegation'
require 'thread'
module ActiveRecord
- module Core
- extend ActiveSupport::Concern
+ ActiveSupport.on_load(:active_record_config) do
+ ##
+ # :singleton-method:
+ #
+ # Accepts a logger conforming to the interface of Log4r which is then
+ # passed on to any new database connections made and which can be
+ # retrieved on both a class and instance level by calling +logger+.
+ mattr_accessor :logger, instance_accessor: false
- included do
- ##
- # :singleton-method:
- #
- # Accepts a logger conforming to the interface of Log4r which is then
- # passed on to any new database connections made and which can be
- # retrieved on both a class and instance level by calling +logger+.
- config_attribute :logger, :global => true
+ ##
+ # :singleton-method:
+ # Contains the database configuration - as is typically stored in config/database.yml -
+ # as a Hash.
+ #
+ # For example, the following database.yml...
+ #
+ # development:
+ # adapter: sqlite3
+ # database: db/development.sqlite3
+ #
+ # production:
+ # adapter: sqlite3
+ # database: db/production.sqlite3
+ #
+ # ...would result in ActiveRecord::Base.configurations to look like this:
+ #
+ # {
+ # 'development' => {
+ # 'adapter' => 'sqlite3',
+ # 'database' => 'db/development.sqlite3'
+ # },
+ # 'production' => {
+ # 'adapter' => 'sqlite3',
+ # 'database' => 'db/production.sqlite3'
+ # }
+ # }
+ mattr_accessor :configurations, instance_accessor: false
+ self.configurations = {}
- ##
- # :singleton-method:
- # Contains the database configuration - as is typically stored in config/database.yml -
- # as a Hash.
- #
- # For example, the following database.yml...
- #
- # development:
- # adapter: sqlite3
- # database: db/development.sqlite3
- #
- # production:
- # adapter: sqlite3
- # database: db/production.sqlite3
- #
- # ...would result in ActiveRecord::Base.configurations to look like this:
- #
- # {
- # 'development' => {
- # 'adapter' => 'sqlite3',
- # 'database' => 'db/development.sqlite3'
- # },
- # 'production' => {
- # 'adapter' => 'sqlite3',
- # 'database' => 'db/production.sqlite3'
- # }
- # }
- config_attribute :configurations, :global => true
- self.configurations = {}
+ ##
+ # :singleton-method:
+ # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
+ # dates and times from the database. This is set to :utc by default.
+ mattr_accessor :default_timezone, instance_accessor: false
+ self.default_timezone = :utc
- ##
- # :singleton-method:
- # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
- # dates and times from the database. This is set to :utc by default.
- config_attribute :default_timezone, :global => true
- self.default_timezone = :utc
+ ##
+ # :singleton-method:
+ # Specifies the format to use when dumping the database schema with Rails'
+ # Rakefile. If :sql, the schema is dumped as (potentially database-
+ # specific) SQL statements. If :ruby, the schema is dumped as an
+ # ActiveRecord::Schema file which can be loaded into any database that
+ # supports migrations. Use :ruby if you want to have different database
+ # adapters for, e.g., your development and test environments.
+ mattr_accessor :schema_format, instance_accessor: false
+ self.schema_format = :ruby
- ##
- # :singleton-method:
- # Specifies the format to use when dumping the database schema with Rails'
- # Rakefile. If :sql, the schema is dumped as (potentially database-
- # specific) SQL statements. If :ruby, the schema is dumped as an
- # ActiveRecord::Schema file which can be loaded into any database that
- # supports migrations. Use :ruby if you want to have different database
- # adapters for, e.g., your development and test environments.
- config_attribute :schema_format, :global => true
- self.schema_format = :ruby
+ ##
+ # :singleton-method:
+ # Specify whether or not to use timestamps for migration versions
+ mattr_accessor :timestamped_migrations, instance_accessor: false
+ self.timestamped_migrations = true
- ##
- # :singleton-method:
- # Specify whether or not to use timestamps for migration versions
- config_attribute :timestamped_migrations, :global => true
- self.timestamped_migrations = true
+ mattr_accessor :connection_handler, instance_accessor: false
+ self.connection_handler = ConnectionAdapters::ConnectionHandler.new
+
+ mattr_accessor :dependent_restrict_raises, instance_accessor: false
+ self.dependent_restrict_raises = true
+ end
+ module Core
+ extend ActiveSupport::Concern
+
+ included do
##
# :singleton-method:
# The connection handler
config_attribute :connection_handler
- self.connection_handler = ConnectionAdapters::ConnectionHandler.new
##
# :singleton-method:
@@ -83,8 +91,11 @@ module ActiveRecord
# ActiveRecord::DeleteRestrictionError exception will be raised
# along with a DEPRECATION WARNING. If set to false, an error would
# be added to the model instead.
- config_attribute :dependent_restrict_raises, :global => true
- self.dependent_restrict_raises = true
+ config_attribute :dependent_restrict_raises
+
+ %w(logger configurations default_timezone schema_format timestamped_migrations).each do |name|
+ config_attribute name, global: true
+ end
end
module ClassMethods
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index b163ef3c12..b27a19f89a 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -1,111 +1,115 @@
module ActiveRecord
# = Active Record Counter Cache
module CounterCache
- # Resets one or more counter caches to their correct value using an SQL
- # count query. This is useful when adding new counter caches, or if the
- # counter has been corrupted or modified directly by SQL.
- #
- # ==== Parameters
- #
- # * +id+ - The id of the object you wish to reset a counter on.
- # * +counters+ - One or more counter names to reset
- #
- # ==== Examples
- #
- # # For Post with id #1 records reset the comments_count
- # Post.reset_counters(1, :comments)
- def reset_counters(id, *counters)
- object = find(id)
- counters.each do |association|
- has_many_association = reflect_on_association(association.to_sym)
+ extend ActiveSupport::Concern
- foreign_key = has_many_association.foreign_key.to_s
- child_class = has_many_association.klass
- belongs_to = child_class.reflect_on_all_associations(:belongs_to)
- reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key }
- counter_name = reflection.counter_cache_column
+ module ClassMethods
+ # Resets one or more counter caches to their correct value using an SQL
+ # count query. This is useful when adding new counter caches, or if the
+ # counter has been corrupted or modified directly by SQL.
+ #
+ # ==== Parameters
+ #
+ # * +id+ - The id of the object you wish to reset a counter on.
+ # * +counters+ - One or more counter names to reset
+ #
+ # ==== Examples
+ #
+ # # For Post with id #1 records reset the comments_count
+ # Post.reset_counters(1, :comments)
+ def reset_counters(id, *counters)
+ object = find(id)
+ counters.each do |association|
+ has_many_association = reflect_on_association(association.to_sym)
- stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
- arel_table[counter_name] => object.send(association).count
- })
- connection.update stmt
- end
- return true
- end
+ foreign_key = has_many_association.foreign_key.to_s
+ child_class = has_many_association.klass
+ belongs_to = child_class.reflect_on_all_associations(:belongs_to)
+ reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key }
+ counter_name = reflection.counter_cache_column
- # A generic "counter updater" implementation, intended primarily to be
- # used by increment_counter and decrement_counter, but which may also
- # be useful on its own. It simply does a direct SQL update for the record
- # with the given ID, altering the given hash of counters by the amount
- # given by the corresponding value:
- #
- # ==== Parameters
- #
- # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
- # * +counters+ - An Array of Hashes containing the names of the fields
- # to update as keys and the amount to update the field by as values.
- #
- # ==== Examples
- #
- # # For the Post with id of 5, decrement the comment_count by 1, and
- # # increment the action_count by 1
- # Post.update_counters 5, :comment_count => -1, :action_count => 1
- # # Executes the following SQL:
- # # UPDATE posts
- # # SET comment_count = COALESCE(comment_count, 0) - 1,
- # # action_count = COALESCE(action_count, 0) + 1
- # # WHERE id = 5
- #
- # # For the Posts with id of 10 and 15, increment the comment_count by 1
- # Post.update_counters [10, 15], :comment_count => 1
- # # Executes the following SQL:
- # # UPDATE posts
- # # SET comment_count = COALESCE(comment_count, 0) + 1
- # # WHERE id IN (10, 15)
- def update_counters(id, counters)
- updates = counters.map do |counter_name, value|
- operator = value < 0 ? '-' : '+'
- quoted_column = connection.quote_column_name(counter_name)
- "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
+ stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
+ arel_table[counter_name] => object.send(association).count
+ })
+ connection.update stmt
+ end
+ return true
end
- where(primary_key => id).update_all updates.join(', ')
- end
+ # A generic "counter updater" implementation, intended primarily to be
+ # used by increment_counter and decrement_counter, but which may also
+ # be useful on its own. It simply does a direct SQL update for the record
+ # with the given ID, altering the given hash of counters by the amount
+ # given by the corresponding value:
+ #
+ # ==== Parameters
+ #
+ # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
+ # * +counters+ - An Array of Hashes containing the names of the fields
+ # to update as keys and the amount to update the field by as values.
+ #
+ # ==== Examples
+ #
+ # # For the Post with id of 5, decrement the comment_count by 1, and
+ # # increment the action_count by 1
+ # Post.update_counters 5, :comment_count => -1, :action_count => 1
+ # # Executes the following SQL:
+ # # UPDATE posts
+ # # SET comment_count = COALESCE(comment_count, 0) - 1,
+ # # action_count = COALESCE(action_count, 0) + 1
+ # # WHERE id = 5
+ #
+ # # For the Posts with id of 10 and 15, increment the comment_count by 1
+ # Post.update_counters [10, 15], :comment_count => 1
+ # # Executes the following SQL:
+ # # UPDATE posts
+ # # SET comment_count = COALESCE(comment_count, 0) + 1
+ # # WHERE id IN (10, 15)
+ def update_counters(id, counters)
+ updates = counters.map do |counter_name, value|
+ operator = value < 0 ? '-' : '+'
+ quoted_column = connection.quote_column_name(counter_name)
+ "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
+ end
- # Increment a number field by one, usually representing a count.
- #
- # This is used for caching aggregate values, so that they don't need to be computed every time.
- # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
- # shown it would have to run an SQL query to find how many posts and comments there are.
- #
- # ==== Parameters
- #
- # * +counter_name+ - The name of the field that should be incremented.
- # * +id+ - The id of the object that should be incremented.
- #
- # ==== Examples
- #
- # # Increment the post_count column for the record with an id of 5
- # DiscussionBoard.increment_counter(:post_count, 5)
- def increment_counter(counter_name, id)
- update_counters(id, counter_name => 1)
- end
+ where(primary_key => id).update_all updates.join(', ')
+ end
+
+ # Increment a number field by one, usually representing a count.
+ #
+ # This is used for caching aggregate values, so that they don't need to be computed every time.
+ # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
+ # shown it would have to run an SQL query to find how many posts and comments there are.
+ #
+ # ==== Parameters
+ #
+ # * +counter_name+ - The name of the field that should be incremented.
+ # * +id+ - The id of the object that should be incremented.
+ #
+ # ==== Examples
+ #
+ # # Increment the post_count column for the record with an id of 5
+ # DiscussionBoard.increment_counter(:post_count, 5)
+ def increment_counter(counter_name, id)
+ update_counters(id, counter_name => 1)
+ end
- # Decrement a number field by one, usually representing a count.
- #
- # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
- #
- # ==== Parameters
- #
- # * +counter_name+ - The name of the field that should be decremented.
- # * +id+ - The id of the object that should be decremented.
- #
- # ==== Examples
- #
- # # Decrement the post_count column for the record with an id of 5
- # DiscussionBoard.decrement_counter(:post_count, 5)
- def decrement_counter(counter_name, id)
- update_counters(id, counter_name => -1)
+ # Decrement a number field by one, usually representing a count.
+ #
+ # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
+ #
+ # ==== Parameters
+ #
+ # * +counter_name+ - The name of the field that should be decremented.
+ # * +id+ - The id of the object that should be decremented.
+ #
+ # ==== Examples
+ #
+ # # Decrement the post_count column for the record with an id of 5
+ # DiscussionBoard.decrement_counter(:post_count, 5)
+ def decrement_counter(counter_name, id)
+ update_counters(id, counter_name => -1)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index b0eda8ef34..7ade385c70 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -1,12 +1,13 @@
+require 'active_support/lazy_load_hooks'
require 'active_support/core_ext/class/attribute'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false
+ end
+
module Explain
- def self.extended(base)
- # If a query takes longer than these many seconds we log its query plan
- # automatically. nil disables this feature.
- base.config_attribute :auto_explain_threshold_in_seconds, :global => true
- end
+ delegate :auto_explain_threshold_in_seconds, :auto_explain_threshold_in_seconds=, to: 'ActiveRecord::Model'
# If auto explain is enabled, this method triggers EXPLAIN logging for the
# queries triggered by the block if it takes more than the threshold as a
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 46d253b0a7..770083ac13 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -1,13 +1,17 @@
require 'active_support/concern'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ # Determine whether to store the full constant name including namespace when using STI
+ mattr_accessor :store_full_sti_class, instance_accessor: false
+ self.store_full_sti_class = true
+ end
+
module Inheritance
extend ActiveSupport::Concern
included do
- # Determine whether to store the full constant name including namespace when using STI
config_attribute :store_full_sti_class
- self.store_full_sti_class = true
end
module ClassMethods
@@ -95,7 +99,7 @@ module ActiveRecord
# Returns the class descending directly from ActiveRecord::Base or an
# abstract class, if any, in the inheritance hierarchy.
def class_of_active_record_descendant(klass)
- unless klass < Model
+ unless klass < Model::Tag
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 05e052b953..4ce42feb74 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -1,4 +1,9 @@
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :lock_optimistically, instance_accessor: false
+ self.lock_optimistically = true
+ end
+
module Locking
# == What is Optimistic Locking
#
@@ -51,8 +56,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- config_attribute :lock_optimistically, :global => true
- self.lock_optimistically = true
+ config_attribute :lock_optimistically
end
def locking_enabled? #:nodoc:
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index 105d1e0e2b..d51cb30ec1 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -1,6 +1,34 @@
require 'active_support/deprecation'
+require 'active_support/concern'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/attribute_accessors'
module ActiveRecord
+ module Configuration # :nodoc:
+ # This just abstracts out how we define configuration options in AR. Essentially we
+ # have mattr_accessors on the ActiveRecord:Model constant that define global defaults.
+ # Classes that then use AR get class_attributes defined, which means that when they
+ # are assigned the default will be overridden for that class and subclasses. (Except
+ # when options[:global] == true, in which case there is one global value always.)
+ def config_attribute(name, options = {})
+ if options[:global]
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def self.#{name}; ActiveRecord::Model.#{name}; end
+ def #{name}; ActiveRecord::Model.#{name}; end
+ def self.#{name}=(val); ActiveRecord::Model.#{name} = val; end
+ CODE
+ else
+ options[:instance_writer] ||= false
+ class_attribute name, options
+
+ singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ remove_method :#{name}
+ def #{name}; ActiveRecord::Model.#{name}; end
+ CODE
+ end
+ end
+ end
+
# <tt>ActiveRecord::Model</tt> can be included into a class to add Active Record persistence.
# This is an alternative to inheriting from <tt>ActiveRecord::Base</tt>. Example:
#
@@ -9,41 +37,35 @@ module ActiveRecord
# end
#
module Model
- module ClassMethods #:nodoc:
- include ActiveSupport::Callbacks::ClassMethods
- include ActiveModel::Naming
- include QueryCache::ClassMethods
- include ActiveSupport::Benchmarkable
- include ActiveSupport::DescendantsTracker
-
- include Querying
- include Translation
- include DynamicMatchers
- include CounterCache
- include Explain
- include ConnectionHandling
- end
+ extend ActiveSupport::Concern
+ extend ConnectionHandling
+ extend ActiveModel::Observing::ClassMethods
- def self.included(base)
- return if base.singleton_class < ClassMethods
+ # This allows us to detect an ActiveRecord::Model while it's in the process of being included.
+ module Tag; end
+ def self.append_features(base)
base.class_eval do
- extend ClassMethods
- Callbacks::Register.setup(self)
- initialize_generated_modules unless self == Base
+ include Tag
+ extend Configuration
end
+
+ super
end
- extend ActiveModel::Configuration
- extend ActiveModel::Callbacks
- extend ActiveModel::MassAssignmentSecurity::ClassMethods
- extend ActiveModel::AttributeMethods::ClassMethods
- extend Callbacks::Register
- extend Explain
- extend ConnectionHandling
+ included do
+ extend ActiveModel::Naming
+ extend ActiveSupport::Benchmarkable
+ extend ActiveSupport::DescendantsTracker
+
+ extend QueryCache::ClassMethods
+ extend Querying
+ extend Translation
+ extend DynamicMatchers
+ extend Explain
+ extend ConnectionHandling
- def self.extend(*modules)
- ClassMethods.send(:include, *modules)
+ initialize_generated_modules unless self == Base
end
include Persistence
@@ -56,13 +78,22 @@ module ActiveRecord
include AttributeAssignment
include ActiveModel::Conversion
include Validations
- include Locking::Optimistic, Locking::Pessimistic
+ include CounterCache
+ include Locking::Optimistic
+ include Locking::Pessimistic
include AttributeMethods
- include Callbacks, ActiveModel::Observing, Timestamp
+ include Callbacks
+ include ActiveModel::Observing
+ include Timestamp
include Associations
include ActiveModel::SecurePassword
- include AutosaveAssociation, NestedAttributes
- include Aggregations, Transactions, Reflection, Serialization, Store
+ include AutosaveAssociation
+ include NestedAttributes
+ include Aggregations
+ include Transactions
+ include Reflection
+ include Serialization
+ include Store
include Core
class << self
@@ -103,6 +134,15 @@ module ActiveRecord
end
end
+ # This hook is where config accessors on Model get defined.
+ #
+ # We don't want to just open the Model module and add stuff to it in other files, because
+ # that would cause Model to load, which causes all sorts of loading order issues.
+ #
+ # We need this hook rather than just using the :active_record one, because users of the
+ # :active_record hook may need to use config options.
+ ActiveSupport.run_load_hooks(:active_record_config, Model)
+
# Load Base at this point, because the active_record load hook is run in that file.
Base
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 7f38dda11e..e6b76ddc4c 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -1,7 +1,19 @@
require 'active_support/concern'
-require 'active_support/core_ext/class/attribute_accessors'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :primary_key_prefix_type, instance_accessor: false
+
+ mattr_accessor :table_name_prefix, instance_accessor: false
+ self.table_name_prefix = ""
+
+ mattr_accessor :table_name_suffix, instance_accessor: false
+ self.table_name_suffix = ""
+
+ mattr_accessor :pluralize_table_names, instance_accessor: false
+ self.pluralize_table_names = true
+ end
+
module ModelSchema
extend ActiveSupport::Concern
@@ -13,7 +25,7 @@ module ActiveRecord
# the Product class will look for "productid" instead of "id" as the primary column. If the
# latter is specified, the Product class will look for "product_id" instead of "id". Remember
# that this is a global setting for all Active Records.
- config_attribute :primary_key_prefix_type, :global => true
+ config_attribute :primary_key_prefix_type, global: true
##
# :singleton-method:
@@ -26,14 +38,12 @@ module ActiveRecord
# a namespace by defining a singleton method in the parent module called table_name_prefix which
# returns your chosen prefix.
config_attribute :table_name_prefix
- self.table_name_prefix = ""
##
# :singleton-method:
# Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
# "people_basecamp"). By default, the suffix is the empty string.
config_attribute :table_name_suffix
- self.table_name_suffix = ""
##
# :singleton-method:
@@ -41,7 +51,6 @@ module ActiveRecord
# If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
# See table_name for the full rules on table/class naming. This is true, by default.
config_attribute :pluralize_table_names
- self.pluralize_table_names = true
end
module ClassMethods
@@ -308,10 +317,6 @@ module ActiveRecord
@relation = nil
end
- def clear_cache! # :nodoc:
- connection.schema_cache.clear!
- end
-
private
# Guesses the table name, but does not decorate it with prefix and suffix information.
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 95a2ddcc11..841681e542 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -5,6 +5,11 @@ require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/class/attribute'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :nested_attributes_options, instance_accessor: false
+ self.nested_attributes_options = {}
+ end
+
module NestedAttributes #:nodoc:
class TooManyRecords < ActiveRecordError
end
@@ -13,7 +18,6 @@ module ActiveRecord
included do
config_attribute :nested_attributes_options
- self.nested_attributes_options = {}
end
# = Active Record Nested Attributes
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 319516413b..9432a70c41 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -68,9 +68,6 @@ module ActiveRecord
initializer "active_record.set_configs" do |app|
ActiveSupport.on_load(:active_record) do
- if app.config.active_record.delete(:whitelist_attributes)
- attr_accessible(nil)
- end
app.config.active_record.each do |k,v|
send "#{k}=", v
end
@@ -97,18 +94,12 @@ module ActiveRecord
end
initializer "active_record.set_reloader_hooks" do |app|
- hook = lambda do
- ActiveRecord::Base.clear_reloadable_connections!
- ActiveRecord::Base.clear_cache!
- end
+ hook = app.config.reload_classes_only_on_change ? :to_prepare : :to_cleanup
- if app.config.reload_classes_only_on_change
- ActiveSupport.on_load(:active_record) do
- ActionDispatch::Reloader.to_prepare(&hook)
- end
- else
- ActiveSupport.on_load(:active_record) do
- ActionDispatch::Reloader.to_cleanup(&hook)
+ ActiveSupport.on_load(:active_record) do
+ ActionDispatch::Reloader.send(hook) do
+ ActiveRecord::Model.clear_reloadable_connections!
+ ActiveRecord::Model.clear_cache!
end
end
end
@@ -119,20 +110,21 @@ module ActiveRecord
config.after_initialize do |app|
ActiveSupport.on_load(:active_record) do
- ActiveRecord::Base.instantiate_observers
+ ActiveRecord::Model.instantiate_observers
ActionDispatch::Reloader.to_prepare do
- ActiveRecord::Base.instantiate_observers
+ ActiveRecord::Model.instantiate_observers
end
end
ActiveSupport.on_load(:active_record) do
if app.config.use_schema_cache_dump
filename = File.join(app.config.paths["db"].first, "schema_cache.dump")
+
if File.file?(filename)
cache = Marshal.load File.binread filename
if cache.version == ActiveRecord::Migrator.current_version
- ActiveRecord::Base.connection.schema_cache = cache
+ ActiveRecord::Model.connection.schema_cache = cache
else
warn "schema_cache.dump is expired. Current version is #{ActiveRecord::Migrator.current_version}, but cache version is #{cache.version}."
end
diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb
index 836b15e2ce..960b78dc38 100644
--- a/activerecord/lib/active_record/readonly_attributes.rb
+++ b/activerecord/lib/active_record/readonly_attributes.rb
@@ -6,7 +6,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- config_attribute :_attr_readonly
+ class_attribute :_attr_readonly, instance_writer: false
self._attr_readonly = []
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index c380b5c029..ec13d27323 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -7,8 +7,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- extend ActiveModel::Configuration
- config_attribute :reflections
+ class_attribute :reflections
self.reflections = {}
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index db833fc7f1..af51c803a7 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -8,7 +8,7 @@ module ActiveRecord
included do
# Stores the default scope for the class
- config_attribute :default_scopes
+ class_attribute :default_scopes, instance_writer: false
self.default_scopes = []
end
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index 41e3b92499..e8dd312a47 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -1,9 +1,21 @@
module ActiveRecord #:nodoc:
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :include_root_in_json, instance_accessor: false
+ self.include_root_in_json = true
+ end
+
# = Active Record Serialization
module Serialization
extend ActiveSupport::Concern
include ActiveModel::Serializers::JSON
+ included do
+ singleton_class.class_eval do
+ remove_method :include_root_in_json
+ delegate :include_root_in_json, to: 'ActiveRecord::Model'
+ end
+ end
+
def serializable_hash(options = nil)
options = options.try(:clone) || {}
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index c717fdea47..e5b7a6bfba 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -1,6 +1,11 @@
require 'active_support/core_ext/class/attribute'
module ActiveRecord
+ ActiveSupport.on_load(:active_record_config) do
+ mattr_accessor :record_timestamps, instance_accessor: false
+ self.record_timestamps = true
+ end
+
# = Active Record Timestamp
#
# Active Record automatically timestamps create and update operations if the
@@ -33,8 +38,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- config_attribute :record_timestamps, :instance_writer => true
- self.record_timestamps = true
+ config_attribute :record_timestamps, instance_writer: true
end
def initialize_dup(other)
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index 69dfd2503e..1199be68eb 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -1,6 +1,6 @@
module ActiveRecord
- class Base
- def self.fake_connection(config)
+ module ConnectionHandling
+ def fake_connection(config)
ConnectionAdapters::FakeAdapter.new nil, logger
end
end
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index 98c38535a6..f4c40b8b97 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -15,6 +15,7 @@ module ActiveRecord
def self.active_record_super; Base; end
def self.base_class; self; end
+ extend ActiveRecord::Configuration
include ActiveRecord::AttributeMethods
def self.define_attribute_methods
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index dc99ac665c..17cb447105 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -7,12 +7,11 @@ module ActiveRecord
@handler = ConnectionHandler.new
@handler.establish_connection 'america', Base.connection_pool.spec
@klass = Class.new do
+ include Model::Tag
def self.name; 'america'; end
- class << self
- alias active_record_super superclass
- end
end
@subklass = Class.new(@klass) do
+ include Model::Tag
def self.name; 'north america'; end
end
end
diff --git a/activerecord/test/cases/mass_assignment_security_test.rb b/activerecord/test/cases/mass_assignment_security_test.rb
index 214546802a..73a01906b9 100644
--- a/activerecord/test/cases/mass_assignment_security_test.rb
+++ b/activerecord/test/cases/mass_assignment_security_test.rb
@@ -251,6 +251,65 @@ class MassAssignmentSecurityTest < ActiveRecord::TestCase
assert !Task.new.respond_to?("#{method}=")
end
end
+
+ test "ActiveRecord::Model.whitelist_attributes works for models which include Model" do
+ begin
+ prev, ActiveRecord::Model.whitelist_attributes = ActiveRecord::Model.whitelist_attributes, true
+
+ klass = Class.new { include ActiveRecord::Model }
+ assert_equal ActiveModel::MassAssignmentSecurity::WhiteList, klass.active_authorizers[:default].class
+ assert_equal [], klass.active_authorizers[:default].to_a
+ ensure
+ ActiveRecord::Model.whitelist_attributes = prev
+ end
+ end
+
+ test "ActiveRecord::Model.whitelist_attributes works for models which inherit Base" do
+ begin
+ prev, ActiveRecord::Model.whitelist_attributes = ActiveRecord::Model.whitelist_attributes, true
+
+ klass = Class.new(ActiveRecord::Base)
+ assert_equal ActiveModel::MassAssignmentSecurity::WhiteList, klass.active_authorizers[:default].class
+ assert_equal [], klass.active_authorizers[:default].to_a
+
+ klass.attr_accessible 'foo'
+ assert_equal ['foo'], Class.new(klass).active_authorizers[:default].to_a
+ ensure
+ ActiveRecord::Model.whitelist_attributes = prev
+ end
+ end
+
+ test "ActiveRecord::Model.mass_assignment_sanitizer works for models which include Model" do
+ begin
+ sanitizer = Object.new
+ prev, ActiveRecord::Model.mass_assignment_sanitizer = ActiveRecord::Model.mass_assignment_sanitizer, sanitizer
+
+ klass = Class.new { include ActiveRecord::Model }
+ assert_equal sanitizer, klass._mass_assignment_sanitizer
+
+ ActiveRecord::Model.mass_assignment_sanitizer = nil
+ klass = Class.new { include ActiveRecord::Model }
+ assert_not_nil klass._mass_assignment_sanitizer
+ ensure
+ ActiveRecord::Model.mass_assignment_sanitizer = prev
+ end
+ end
+
+ test "ActiveRecord::Model.mass_assignment_sanitizer works for models which inherit Base" do
+ begin
+ sanitizer = Object.new
+ prev, ActiveRecord::Model.mass_assignment_sanitizer = ActiveRecord::Model.mass_assignment_sanitizer, sanitizer
+
+ klass = Class.new(ActiveRecord::Base)
+ assert_equal sanitizer, klass._mass_assignment_sanitizer
+
+ sanitizer2 = Object.new
+ klass.mass_assignment_sanitizer = sanitizer2
+ assert_equal sanitizer2, Class.new(klass)._mass_assignment_sanitizer
+ ensure
+ ActiveRecord::Model.mass_assignment_sanitizer = prev
+ end
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 070bfd7af6..efa2d43f20 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -107,7 +107,7 @@ class String
# Replaces underscores with dashes in the string.
#
- # 'puni_puni' # => "puni-puni"
+ # 'puni_puni'.dasherize # => "puni-puni"
def dasherize
ActiveSupport::Inflector.dasherize(self)
end
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 6735c561d3..e3d8cf48ce 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -33,7 +33,7 @@ module ActiveSupport
# end
#
# That code returns right away, you are just subscribing to "render" events.
- # The block will be called asynchronously whenever someone instruments "render":
+ # The block is saved and will be called whenever someone instruments "render":
#
# ActiveSupport::Notifications.instrument("render", :extra => :information) do
# render :text => "Foo"
diff --git a/guides/source/routing.textile b/guides/source/routing.textile
index 7941e655bb..dae25853cd 100644
--- a/guides/source/routing.textile
+++ b/guides/source/routing.textile
@@ -644,6 +644,14 @@ You should put the +root+ route at the top of the file, because it is the most p
NOTE: The +root+ route only routes +GET+ requests to the action.
+h4. Unicode character routes
+
+You can specify unicode character routes directly. For example
+
+<ruby>
+match 'こんにちは' => 'welcome#index'
+</ruby>
+
h3. Customizing Resourceful Routes
While the default routes and helpers generated by +resources :posts+ will usually serve you well, you may want to customize them in some way. Rails allows you to customize virtually any generic part of the resourceful helpers.
diff --git a/guides/source/upgrading_ruby_on_rails.textile b/guides/source/upgrading_ruby_on_rails.textile
index 6cdc6ab289..4bf4751127 100644
--- a/guides/source/upgrading_ruby_on_rails.textile
+++ b/guides/source/upgrading_ruby_on_rails.textile
@@ -50,6 +50,8 @@ h4(#action_pack4_0). Action Pack
Rails 4.0 changed how <tt>assert_generates</tt>, <tt>assert_recognizes</tt>, and <tt>assert_routing</tt> work. Now all these assertions raise <tt>Assertion</tt> instead of <tt>ActionController::RoutingError</tt>.
+Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, e.g. <tt>get Rack::Utils.escape('こんにちは'), :controller => 'welcome', :action => 'index'</tt> to <tt>get 'こんにちは', :controller => 'welcome', :action => 'index'</tt>.
+
h4(#helpers_order). Helpers Loading Order
The loading order of helpers from more than one directory has changed in Rails 4.0. Previously, helpers from all directories were gathered and then sorted alphabetically. After upgrade to Rails 4.0 helpers will preserve the order of loaded directories and will be sorted alphabetically only within each directory. Unless you explicitly use <tt>helpers_path</tt> parameter, this change will only impact the way of loading helpers from engines. If you rely on the fact that particular helper from engine loads before or after another helper from application or another engine, you should check if correct methods are available after upgrade. If you would like to change order in which engines are loaded, you can use <tt>config.railties_order=</tt> method.
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 7193dfdef5..c1aa98e481 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -148,6 +148,15 @@ module ApplicationTests
assert AppTemplate::Application.config.allow_concurrency
end
+ test "initialize a threadsafe app" do
+ add_to_config <<-RUBY
+ config.threadsafe!
+ RUBY
+
+ require "#{app_path}/config/application"
+ assert AppTemplate::Application.initialize!
+ end
+
test "asset_path defaults to nil for application" do
require "#{app_path}/config/environment"
assert_equal nil, AppTemplate::Application.config.asset_path
@@ -179,7 +188,7 @@ module ApplicationTests
require "#{app_path}/config/environment"
- assert !ActionController.autoload?(:Caching)
+ assert !ActionView.autoload?(:AssetPaths)
end
test "filter_parameters should be able to set via config.filter_parameters" do
@@ -374,9 +383,10 @@ module ApplicationTests
require "#{app_path}/config/environment"
- assert_equal ActiveModel::MassAssignmentSecurity::WhiteList,
- ActiveRecord::Base.active_authorizers[:default].class
- assert_equal [], ActiveRecord::Base.active_authorizers[:default].to_a
+ klass = Class.new(ActiveRecord::Base)
+
+ assert_equal ActiveModel::MassAssignmentSecurity::WhiteList, klass.active_authorizers[:default].class
+ assert_equal [], klass.active_authorizers[:default].to_a
end
test "registers interceptors with ActionMailer" do
@@ -617,5 +627,26 @@ module ApplicationTests
make_basic_app
assert app.config.colorize_logging
end
+
+ test "config.active_record.observers" do
+ add_to_config <<-RUBY
+ config.active_record.observers = :foo_observer
+ RUBY
+
+ app_file 'app/models/foo.rb', <<-RUBY
+ class Foo < ActiveRecord::Base
+ end
+ RUBY
+
+ app_file 'app/models/foo_observer.rb', <<-RUBY
+ class FooObserver < ActiveRecord::Observer
+ end
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ ActiveRecord::Base
+ assert defined?(FooObserver)
+ end
end
end