Skip to content
This repository
Browse code

Simplify AR configuration code.

Get rid of ActiveModel::Configuration, make better use of
ActiveSupport::Concern + class_attribute, etc.
  • Loading branch information...
commit e030f26ad3de98205edec9d8b59ecca9508cb57d 1 parent 61826bc
Jon Leighton authored May 25, 2012

Showing 31 changed files with 356 additions and 567 deletions. Show diff stats Hide diff stats

  1. 1  activemodel/lib/active_model.rb
  2. 3  activemodel/lib/active_model/attribute_methods.rb
  3. 134  activemodel/lib/active_model/configuration.rb
  4. 10  activemodel/lib/active_model/mass_assignment_security.rb
  5. 5  activemodel/lib/active_model/serializers/json.rb
  6. 3  activemodel/lib/active_model/validations.rb
  7. 154  activemodel/test/cases/configuration_test.rb
  8. 9  activerecord/lib/active_record/attribute_methods/dirty.rb
  9. 8  activerecord/lib/active_record/attribute_methods/read.rb
  10. 2  activerecord/lib/active_record/attribute_methods/serialization.rb
  11. 13  activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
  12. 24  activerecord/lib/active_record/callbacks.rb
  13. 14  activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
  14. 4  activerecord/lib/active_record/connection_handling.rb
  15. 137  activerecord/lib/active_record/core.rb
  16. 200  activerecord/lib/active_record/counter_cache.rb
  17. 11  activerecord/lib/active_record/explain.rb
  18. 10  activerecord/lib/active_record/inheritance.rb
  19. 8  activerecord/lib/active_record/locking/optimistic.rb
  20. 103  activerecord/lib/active_record/model.rb
  21. 23  activerecord/lib/active_record/model_schema.rb
  22. 6  activerecord/lib/active_record/nested_attributes.rb
  23. 4  activerecord/lib/active_record/railtie.rb
  24. 2  activerecord/lib/active_record/readonly_attributes.rb
  25. 3  activerecord/lib/active_record/reflection.rb
  26. 2  activerecord/lib/active_record/scoping/default.rb
  27. 12  activerecord/lib/active_record/serialization.rb
  28. 8  activerecord/lib/active_record/timestamp.rb
  29. 4  activerecord/test/active_record/connection_adapters/fake_adapter.rb
  30. 1  activerecord/test/cases/attribute_methods/read_test.rb
  31. 5  activerecord/test/cases/connection_adapters/connection_handler_test.rb
1  activemodel/lib/active_model.rb
@@ -30,7 +30,6 @@ module ActiveModel
30 30
   autoload :AttributeMethods
31 31
   autoload :BlockValidator, 'active_model/validator'
32 32
   autoload :Callbacks
33  
-  autoload :Configuration
34 33
   autoload :Conversion
35 34
   autoload :Dirty
36 35
   autoload :EachValidator, 'active_model/validator'
3  activemodel/lib/active_model/attribute_methods.rb
@@ -61,8 +61,7 @@ module AttributeMethods
61 61
     CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
62 62
 
63 63
     included do
64  
-      extend ActiveModel::Configuration
65  
-      config_attribute :attribute_method_matchers
  64
+      class_attribute :attribute_method_matchers, instance_writer: false
66 65
       self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
67 66
     end
68 67
 
134  activemodel/lib/active_model/configuration.rb
... ...
@@ -1,134 +0,0 @@
1  
-require 'active_support/concern'
2  
-require 'active_support/core_ext/class/attribute'
3  
-require 'active_support/core_ext/class/attribute_accessors'
4  
-
5  
-module ActiveModel
6  
-  # This API is for Rails' internal use and is not currently considered 'public', so
7  
-  # it may change in the future without warning.
8  
-  #
9  
-  # It creates configuration attributes that can be inherited from a module down
10  
-  # to a class that includes the module. E.g.
11  
-  #
12  
-  #   module MyModel
13  
-  #     extend ActiveModel::Configuration
14  
-  #     config_attribute :awesome
15  
-  #     self.awesome = true
16  
-  #   end
17  
-  #
18  
-  #   class Post
19  
-  #     include MyModel
20  
-  #   end
21  
-  #
22  
-  #   Post.awesome # => true
23  
-  #
24  
-  #   Post.awesome = false
25  
-  #   Post.awesome    # => false
26  
-  #   MyModel.awesome # => true
27  
-  #
28  
-  # We assume that the module will have a ClassMethods submodule containing methods
29  
-  # to be transferred to the including class' singleton class.
30  
-  #
31  
-  # Config options can also be defined directly on a class:
32  
-  #
33  
-  #   class Post
34  
-  #     extend ActiveModel::Configuration
35  
-  #     config_attribute :awesome
36  
-  #   end
37  
-  #
38  
-  # So this allows us to define a module that doesn't care about whether it is being
39  
-  # included in a class or a module:
40  
-  #
41  
-  #   module Awesomeness
42  
-  #     extend ActiveSupport::Concern
43  
-  #
44  
-  #     included do
45  
-  #       extend ActiveModel::Configuration
46  
-  #       config_attribute :awesome
47  
-  #       self.awesome = true
48  
-  #     end
49  
-  #   end
50  
-  #
51  
-  #   class Post
52  
-  #     include Awesomeness
53  
-  #   end
54  
-  #
55  
-  #   module AwesomeModel
56  
-  #     include Awesomeness
57  
-  #   end
58  
-  module Configuration #:nodoc:
59  
-    def config_attribute(name, options = {})
60  
-      klass = self.is_a?(Class) ? ClassAttribute : ModuleAttribute
61  
-      klass.new(self, name, options).define
62  
-    end
63  
-
64  
-    class Attribute
65  
-      attr_reader :host, :name, :options
66  
-
67  
-      def initialize(host, name, options)
68  
-        @host, @name, @options = host, name, options
69  
-      end
70  
-
71  
-      def instance_writer?
72  
-        options.fetch(:instance_writer, false)
73  
-      end
74  
-    end
75  
-
76  
-    class ClassAttribute < Attribute
77  
-      def define
78  
-        if options[:global]
79  
-          host.cattr_accessor name, :instance_writer => instance_writer?
80  
-        else
81  
-          host.class_attribute name, :instance_writer => instance_writer?
82  
-        end
83  
-      end
84  
-    end
85  
-
86  
-    class ModuleAttribute < Attribute
87  
-      def class_methods
88  
-        @class_methods ||= begin
89  
-          if host.const_defined?(:ClassMethods, false)
90  
-            host.const_get(:ClassMethods)
91  
-          else
92  
-            host.const_set(:ClassMethods, Module.new)
93  
-          end
94  
-        end
95  
-      end
96  
-
97  
-      def define
98  
-        host.singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
99  
-          attr_accessor :#{name}
100  
-          def #{name}?; !!#{name}; end
101  
-        CODE
102  
-
103  
-        name, host = self.name, self.host
104  
-
105  
-        class_methods.class_eval do
106  
-          define_method(name) { host.send(name) }
107  
-          define_method("#{name}?") { !!send(name) }
108  
-        end
109  
-
110  
-        host.class_eval <<-CODE, __FILE__, __LINE__ + 1
111  
-          def #{name};  defined?(@#{name}) ? @#{name} : self.class.#{name}; end
112  
-          def #{name}?; !!#{name}; end
113  
-        CODE
114  
-
115  
-        if options[:global]
116  
-          class_methods.class_eval do
117  
-            define_method("#{name}=") { |val| host.send("#{name}=", val) }
118  
-          end
119  
-        else
120  
-          class_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
121  
-            def #{name}=(val)
122  
-              singleton_class.class_eval do
123  
-                remove_possible_method(:#{name})
124  
-                define_method(:#{name}) { val }
125  
-              end
126  
-            end
127  
-          CODE
128  
-        end
129  
-
130  
-        host.send(:attr_writer, name) if instance_writer?
131  
-      end
132  
-    end
133  
-  end
134  
-end
10  activemodel/lib/active_model/mass_assignment_security.rb
@@ -9,13 +9,11 @@ module MassAssignmentSecurity
9 9
     extend ActiveSupport::Concern
10 10
 
11 11
     included do
12  
-      extend ActiveModel::Configuration
  12
+      class_attribute :_accessible_attributes, instance_writer: false
  13
+      class_attribute :_protected_attributes,  instance_writer: false
  14
+      class_attribute :_active_authorizer,     instance_writer: false
13 15
 
14  
-      config_attribute :_accessible_attributes
15  
-      config_attribute :_protected_attributes
16  
-      config_attribute :_active_authorizer
17  
-
18  
-      config_attribute :_mass_assignment_sanitizer
  16
+      class_attribute :_mass_assignment_sanitizer, instance_writer: false
19 17
       self.mass_assignment_sanitizer = :logger
20 18
     end
21 19
 
5  activemodel/lib/active_model/serializers/json.rb
@@ -10,9 +10,8 @@ module JSON
10 10
 
11 11
       included do
12 12
         extend ActiveModel::Naming
13  
-        extend ActiveModel::Configuration
14 13
 
15  
-        config_attribute :include_root_in_json
  14
+        class_attribute :include_root_in_json
16 15
         self.include_root_in_json = false
17 16
       end
18 17
 
@@ -106,4 +105,4 @@ def from_json(json, include_root=include_root_in_json)
106 105
       end
107 106
     end
108 107
   end
109  
-end
  108
+end
3  activemodel/lib/active_model/validations.rb
@@ -52,8 +52,7 @@ module Validations
52 52
       attr_accessor :validation_context
53 53
       define_callbacks :validate, :scope => :name
54 54
 
55  
-      extend ActiveModel::Configuration
56  
-      config_attribute :_validators
  55
+      class_attribute :_validators
57 56
       self._validators = Hash.new { |h,k| h[k] = [] }
58 57
     end
59 58
 
154  activemodel/test/cases/configuration_test.rb
... ...
@@ -1,154 +0,0 @@
1  
-require 'cases/helper'
2  
-
3  
-class ConfigurationOnModuleTest < ActiveModel::TestCase
4  
-  def setup
5  
-    @mod = mod = Module.new do
6  
-      extend ActiveSupport::Concern
7  
-      extend ActiveModel::Configuration
8  
-
9  
-      config_attribute :omg
10  
-      self.omg = "default"
11  
-
12  
-      config_attribute :wtf, global: true
13  
-      self.wtf = "default"
14  
-
15  
-      config_attribute :boolean
16  
-
17  
-      config_attribute :lol, instance_writer: true
18  
-    end
19  
-
20  
-    @klass = Class.new do
21  
-      include mod
22  
-    end
23  
-
24  
-    @subklass = Class.new(@klass)
25  
-  end
26  
-
27  
-  test "default" do
28  
-    assert_equal "default", @mod.omg
29  
-    assert_equal "default", @klass.omg
30  
-    assert_equal "default", @klass.new.omg
31  
-  end
32  
-
33  
-  test "setting" do
34  
-    @mod.omg = "lol"
35  
-    assert_equal "lol", @mod.omg
36  
-  end
37  
-
38  
-  test "setting on class including the module" do
39  
-    @klass.omg = "lol"
40  
-    assert_equal "lol", @klass.omg
41  
-    assert_equal "lol", @klass.new.omg
42  
-    assert_equal "default", @mod.omg
43  
-  end
44  
-
45  
-  test "setting on subclass of class including the module" do
46  
-    @subklass.omg = "lol"
47  
-    assert_equal "lol", @subklass.omg
48  
-    assert_equal "default", @klass.omg
49  
-    assert_equal "default", @mod.omg
50  
-  end
51  
-
52  
-  test "setting on instance" do
53  
-    assert !@klass.new.respond_to?(:omg=)
54  
-
55  
-    @klass.lol = "lol"
56  
-    obj = @klass.new
57  
-    assert_equal "lol", obj.lol
58  
-    obj.lol = "omg"
59  
-    assert_equal "omg", obj.lol
60  
-    assert_equal "lol", @klass.lol
61  
-    assert_equal "lol", @klass.new.lol
62  
-    obj.lol = false
63  
-    assert !obj.lol?
64  
-  end
65  
-
66  
-  test "global attribute" do
67  
-    assert_equal "default", @mod.wtf
68  
-    assert_equal "default", @klass.wtf
69  
-
70  
-    @mod.wtf = "wtf"
71  
-
72  
-    assert_equal "wtf", @mod.wtf
73  
-    assert_equal "wtf", @klass.wtf
74  
-
75  
-    @klass.wtf = "lol"
76  
-
77  
-    assert_equal "lol", @mod.wtf
78  
-    assert_equal "lol", @klass.wtf
79  
-  end
80  
-
81  
-  test "boolean" do
82  
-    assert_equal false, @mod.boolean?
83  
-    assert_equal false, @klass.new.boolean?
84  
-    @mod.boolean = true
85  
-    assert_equal true, @mod.boolean?
86  
-    assert_equal true, @klass.new.boolean?
87  
-  end
88  
-end
89  
-
90  
-class ConfigurationOnClassTest < ActiveModel::TestCase
91  
-  def setup
92  
-    @klass = Class.new do
93  
-      extend ActiveModel::Configuration
94  
-
95  
-      config_attribute :omg
96  
-      self.omg = "default"
97  
-
98  
-      config_attribute :wtf, global: true
99  
-      self.wtf = "default"
100  
-
101  
-      config_attribute :omg2, instance_writer: true
102  
-      config_attribute :wtf2, instance_writer: true, global: true
103  
-    end
104  
-
105  
-    @subklass = Class.new(@klass)
106  
-  end
107  
-
108  
-  test "defaults" do
109  
-    assert_equal "default", @klass.omg
110  
-    assert_equal "default", @klass.wtf
111  
-    assert_equal "default", @subklass.omg
112  
-    assert_equal "default", @subklass.wtf
113  
-  end
114  
-
115  
-  test "changing" do
116  
-    @klass.omg = "lol"
117  
-    assert_equal "lol", @klass.omg
118  
-    assert_equal "lol", @subklass.omg
119  
-  end
120  
-
121  
-  test "changing in subclass" do
122  
-    @subklass.omg = "lol"
123  
-    assert_equal "lol", @subklass.omg
124  
-    assert_equal "default", @klass.omg
125  
-  end
126  
-
127  
-  test "changing global" do
128  
-    @klass.wtf = "wtf"
129  
-    assert_equal "wtf", @klass.wtf
130  
-    assert_equal "wtf", @subklass.wtf
131  
-
132  
-    @subklass.wtf = "lol"
133  
-    assert_equal "lol", @klass.wtf
134  
-    assert_equal "lol", @subklass.wtf
135  
-  end
136  
-
137  
-  test "instance_writer" do
138  
-    obj = @klass.new
139  
-
140  
-    @klass.omg2 = "omg"
141  
-    @klass.wtf2 = "wtf"
142  
-
143  
-    assert_equal "omg", obj.omg2
144  
-    assert_equal "wtf", obj.wtf2
145  
-
146  
-    obj.omg2 = "lol"
147  
-    obj.wtf2 = "lol"
148  
-
149  
-    assert_equal "lol", obj.omg2
150  
-    assert_equal "lol", obj.wtf2
151  
-    assert_equal "omg", @klass.omg2
152  
-    assert_equal "lol", @klass.wtf2
153  
-  end
154  
-end
9  activerecord/lib/active_record/attribute_methods/dirty.rb
... ...
@@ -1,12 +1,18 @@
1 1
 require 'active_support/core_ext/class/attribute'
2 2
 require 'active_support/core_ext/object/blank'
  3
+require 'active_support/core_ext/module/attribute_accessors'
3 4
 
4 5
 module ActiveRecord
  6
+  ActiveSupport.on_load(:active_record_config) do
  7
+    mattr_accessor :partial_updates, instance_accessor: false
  8
+    self.partial_updates = true
  9
+  end
  10
+
5 11
   module AttributeMethods
6 12
     module Dirty
7 13
       extend ActiveSupport::Concern
  14
+
8 15
       include ActiveModel::Dirty
9  
-      include AttributeMethods::Write
10 16
 
11 17
       included do
12 18
         if self < ::ActiveRecord::Timestamp
@@ -14,7 +20,6 @@ module Dirty
14 20
         end
15 21
 
16 22
         config_attribute :partial_updates
17  
-        self.partial_updates = true
18 23
       end
19 24
 
20 25
       # Attempts to +save+ the record and clears changed attributes if successful.
8  activerecord/lib/active_record/attribute_methods/read.rb
... ...
@@ -1,13 +1,17 @@
1 1
 module ActiveRecord
  2
+  ActiveSupport.on_load(:active_record_config) do
  3
+    mattr_accessor :attribute_types_cached_by_default, instance_accessor: false
  4
+  end
  5
+
2 6
   module AttributeMethods
3 7
     module Read
4 8
       extend ActiveSupport::Concern
5 9
 
6 10
       ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
  11
+      ActiveRecord::Model.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
7 12
 
8 13
       included do
9  
-        config_attribute :attribute_types_cached_by_default, :global => true
10  
-        self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
  14
+        config_attribute :attribute_types_cached_by_default
11 15
       end
12 16
 
13 17
       module ClassMethods
2  activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -6,7 +6,7 @@ module Serialization
6 6
       included do
7 7
         # Returns a hash of all the attributes that have been specified for serialization as
8 8
         # keys and their class restriction as values.
9  
-        config_attribute :serialized_attributes
  9
+        class_attribute :serialized_attributes
10 10
         self.serialized_attributes = {}
11 11
       end
12 12
 
13  activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -2,6 +2,14 @@
2 2
 require 'active_support/core_ext/object/inclusion'
3 3
 
4 4
 module ActiveRecord
  5
+  ActiveSupport.on_load(:active_record_config) do
  6
+    mattr_accessor :time_zone_aware_attributes, instance_accessor: false
  7
+    self.time_zone_aware_attributes = false
  8
+
  9
+    mattr_accessor :skip_time_zone_conversion_for_attributes, instance_accessor: false
  10
+    self.skip_time_zone_conversion_for_attributes = []
  11
+  end
  12
+
5 13
   module AttributeMethods
6 14
     module TimeZoneConversion
7 15
       class Type # :nodoc:
@@ -22,11 +30,8 @@ def type
22 30
       extend ActiveSupport::Concern
23 31
 
24 32
       included do
25  
-        config_attribute :time_zone_aware_attributes, :global => true
26  
-        self.time_zone_aware_attributes = false
27  
-
  33
+        config_attribute :time_zone_aware_attributes, global: true
28 34
         config_attribute :skip_time_zone_conversion_for_attributes
29  
-        self.skip_time_zone_conversion_for_attributes = []
30 35
       end
31 36
 
32 37
       module ClassMethods
24  activerecord/lib/active_record/callbacks.rb
@@ -231,30 +231,6 @@ module ActiveRecord
231 231
   # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
232 232
   #
233 233
   module Callbacks
234  
-    # We can't define callbacks directly on ActiveRecord::Model because
235  
-    # it is a module. So we queue up the definitions and execute them
236  
-    # when ActiveRecord::Model is included.
237  
-    module Register #:nodoc:
238  
-      def self.extended(base)
239  
-        base.config_attribute :_callbacks_register
240  
-        base._callbacks_register = []
241  
-      end
242  
-
243  
-      def self.setup(base)
244  
-        base._callbacks_register.each do |item|
245  
-          base.send(*item)
246  
-        end
247  
-      end
248  
-
249  
-      def define_callbacks(*args)
250  
-        self._callbacks_register << [:define_callbacks, *args]
251  
-      end
252  
-
253  
-      def define_model_callbacks(*args)
254  
-        self._callbacks_register << [:define_model_callbacks, *args]
255  
-      end
256  
-    end
257  
-
258 234
     extend ActiveSupport::Concern
259 235
 
260 236
     CALLBACKS = [
14  activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -448,11 +448,11 @@ def release(conn)
448 448
       end
449 449
 
450 450
       def new_connection
451  
-        ActiveRecord::Base.send(spec.adapter_method, spec.config)
  451
+        ActiveRecord::Model.send(spec.adapter_method, spec.config)
452 452
       end
453 453
 
454 454
       def current_connection_id #:nodoc:
455  
-        ActiveRecord::Base.connection_id ||= Thread.current.object_id
  455
+        ActiveRecord::Model.connection_id ||= Thread.current.object_id
456 456
       end
457 457
 
458 458
       def checkout_new_connection
@@ -563,10 +563,12 @@ def remove_connection(klass)
563 563
       end
564 564
 
565 565
       def retrieve_connection_pool(klass)
566  
-        pool = get_pool_for_class klass.name
567  
-        return pool if pool
568  
-        return nil if ActiveRecord::Model == klass
569  
-        retrieve_connection_pool klass.active_record_super
  566
+        if !(klass < Model::Tag)
  567
+          get_pool_for_class('ActiveRecord::Model') # default connection
  568
+        else
  569
+          pool = get_pool_for_class(klass.name)
  570
+          pool || retrieve_connection_pool(klass.superclass)
  571
+        end
570 572
       end
571 573
 
572 574
       private
4  activerecord/lib/active_record/connection_handling.rb
@@ -90,6 +90,10 @@ def remove_connection(klass = self)
90 90
       connection_handler.remove_connection(klass)
91 91
     end
92 92
 
  93
+    def clear_cache! # :nodoc:
  94
+      connection.schema_cache.clear!
  95
+    end
  96
+
93 97
     delegate :clear_active_connections!, :clear_reloadable_connections!,
94 98
       :clear_all_connections!, :to => :connection_handler
95 99
   end
137  activerecord/lib/active_record/core.rb
... ...
@@ -1,80 +1,88 @@
1 1
 require 'active_support/concern'
2 2
 require 'active_support/core_ext/hash/indifferent_access'
3 3
 require 'active_support/core_ext/object/deep_dup'
  4
+require 'active_support/core_ext/module/delegation'
4 5
 require 'thread'
5 6
 
6 7
 module ActiveRecord
7  
-  module Core
8  
-    extend ActiveSupport::Concern
  8
+  ActiveSupport.on_load(:active_record_config) do
  9
+    ##
  10
+    # :singleton-method:
  11
+    #
  12
+    # Accepts a logger conforming to the interface of Log4r which is then
  13
+    # passed on to any new database connections made and which can be
  14
+    # retrieved on both a class and instance level by calling +logger+.
  15
+    mattr_accessor :logger, instance_accessor: false
9 16
 
10  
-    included do
11  
-      ##
12  
-      # :singleton-method:
13  
-      #
14  
-      # Accepts a logger conforming to the interface of Log4r which is then
15  
-      # passed on to any new database connections made and which can be
16  
-      # retrieved on both a class and instance level by calling +logger+.
17  
-      config_attribute :logger, :global => true
  17
+    ##
  18
+    # :singleton-method:
  19
+    # Contains the database configuration - as is typically stored in config/database.yml -
  20
+    # as a Hash.
  21
+    #
  22
+    # For example, the following database.yml...
  23
+    #
  24
+    #   development:
  25
+    #     adapter: sqlite3
  26
+    #     database: db/development.sqlite3
  27
+    #
  28
+    #   production:
  29
+    #     adapter: sqlite3
  30
+    #     database: db/production.sqlite3
  31
+    #
  32
+    # ...would result in ActiveRecord::Base.configurations to look like this:
  33
+    #
  34
+    #   {
  35
+    #      'development' => {
  36
+    #         'adapter'  => 'sqlite3',
  37
+    #         'database' => 'db/development.sqlite3'
  38
+    #      },
  39
+    #      'production' => {
  40
+    #         'adapter'  => 'sqlite3',
  41
+    #         'database' => 'db/production.sqlite3'
  42
+    #      }
  43
+    #   }
  44
+    mattr_accessor :configurations, instance_accessor: false
  45
+    self.configurations = {}
18 46
 
19  
-      ##
20  
-      # :singleton-method:
21  
-      # Contains the database configuration - as is typically stored in config/database.yml -
22  
-      # as a Hash.
23  
-      #
24  
-      # For example, the following database.yml...
25  
-      #
26  
-      #   development:
27  
-      #     adapter: sqlite3
28  
-      #     database: db/development.sqlite3
29  
-      #
30  
-      #   production:
31  
-      #     adapter: sqlite3
32  
-      #     database: db/production.sqlite3
33  
-      #
34  
-      # ...would result in ActiveRecord::Base.configurations to look like this:
35  
-      #
36  
-      #   {
37  
-      #      'development' => {
38  
-      #         'adapter'  => 'sqlite3',
39  
-      #         'database' => 'db/development.sqlite3'
40  
-      #      },
41  
-      #      'production' => {
42  
-      #         'adapter'  => 'sqlite3',
43  
-      #         'database' => 'db/production.sqlite3'
44  
-      #      }
45  
-      #   }
46  
-      config_attribute :configurations, :global => true
47  
-      self.configurations = {}
  47
+    ##
  48
+    # :singleton-method:
  49
+    # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
  50
+    # dates and times from the database. This is set to :utc by default.
  51
+    mattr_accessor :default_timezone, instance_accessor: false
  52
+    self.default_timezone = :utc
48 53
 
49  
-      ##
50  
-      # :singleton-method:
51  
-      # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
52  
-      # dates and times from the database. This is set to :utc by default.
53  
-      config_attribute :default_timezone, :global => true
54  
-      self.default_timezone = :utc
  54
+    ##
  55
+    # :singleton-method:
  56
+    # Specifies the format to use when dumping the database schema with Rails'
  57
+    # Rakefile. If :sql, the schema is dumped as (potentially database-
  58
+    # specific) SQL statements. If :ruby, the schema is dumped as an
  59
+    # ActiveRecord::Schema file which can be loaded into any database that
  60
+    # supports migrations. Use :ruby if you want to have different database
  61
+    # adapters for, e.g., your development and test environments.
  62
+    mattr_accessor :schema_format, instance_accessor: false
  63
+    self.schema_format = :ruby
55 64
 
56  
-      ##
57  
-      # :singleton-method:
58  
-      # Specifies the format to use when dumping the database schema with Rails'
59  
-      # Rakefile. If :sql, the schema is dumped as (potentially database-
60  
-      # specific) SQL statements. If :ruby, the schema is dumped as an
61  
-      # ActiveRecord::Schema file which can be loaded into any database that
62  
-      # supports migrations. Use :ruby if you want to have different database
63  
-      # adapters for, e.g., your development and test environments.
64  
-      config_attribute :schema_format, :global => true
65  
-      self.schema_format = :ruby
  65
+    ##
  66
+    # :singleton-method:
  67
+    # Specify whether or not to use timestamps for migration versions
  68
+    mattr_accessor :timestamped_migrations, instance_accessor: false
  69
+    self.timestamped_migrations = true
66 70
 
67  
-      ##
68  
-      # :singleton-method:
69  
-      # Specify whether or not to use timestamps for migration versions
70  
-      config_attribute :timestamped_migrations, :global => true
71  
-      self.timestamped_migrations = true
  71
+    mattr_accessor :connection_handler, instance_accessor: false
  72
+    self.connection_handler = ConnectionAdapters::ConnectionHandler.new
  73
+
  74
+    mattr_accessor :dependent_restrict_raises, instance_accessor: false
  75
+    self.dependent_restrict_raises = true
  76
+  end
72 77
 
  78
+  module Core
  79
+    extend ActiveSupport::Concern
  80
+
  81
+    included do
73 82
       ##
74 83
       # :singleton-method:
75 84
       # The connection handler
76 85
       config_attribute :connection_handler
77  
-      self.connection_handler = ConnectionAdapters::ConnectionHandler.new
78 86
 
79 87
       ##
80 88
       # :singleton-method:
@@ -83,8 +91,11 @@ module Core
83 91
       # ActiveRecord::DeleteRestrictionError exception will be raised
84 92
       # along with a DEPRECATION WARNING. If set to false, an error would
85 93
       # be added to the model instead.
86  
-      config_attribute :dependent_restrict_raises, :global => true
87  
-      self.dependent_restrict_raises = true
  94
+      config_attribute :dependent_restrict_raises
  95
+
  96
+      %w(logger configurations default_timezone schema_format timestamped_migrations).each do |name|
  97
+        config_attribute name, global: true
  98
+      end
88 99
     end
89 100
 
90 101
     module ClassMethods
200  activerecord/lib/active_record/counter_cache.rb
... ...
@@ -1,111 +1,115 @@
1 1
 module ActiveRecord
2 2
   # = Active Record Counter Cache
3 3
   module CounterCache
4  
-    # Resets one or more counter caches to their correct value using an SQL
5  
-    # count query. This is useful when adding new counter caches, or if the
6  
-    # counter has been corrupted or modified directly by SQL.
7  
-    #
8  
-    # ==== Parameters
9  
-    #
10  
-    # * +id+ - The id of the object you wish to reset a counter on.
11  
-    # * +counters+ - One or more counter names to reset
12  
-    #
13  
-    # ==== Examples
14  
-    #
15  
-    #   # For Post with id #1 records reset the comments_count
16  
-    #   Post.reset_counters(1, :comments)
17  
-    def reset_counters(id, *counters)
18  
-      object = find(id)
19  
-      counters.each do |association|
20  
-        has_many_association = reflect_on_association(association.to_sym)
  4
+    extend ActiveSupport::Concern
21 5
 
22  
-        foreign_key  = has_many_association.foreign_key.to_s
23  
-        child_class  = has_many_association.klass
24  
-        belongs_to   = child_class.reflect_on_all_associations(:belongs_to)
25  
-        reflection   = belongs_to.find { |e| e.foreign_key.to_s == foreign_key }
26  
-        counter_name = reflection.counter_cache_column
  6
+    module ClassMethods
  7
+      # Resets one or more counter caches to their correct value using an SQL
  8
+      # count query. This is useful when adding new counter caches, or if the
  9
+      # counter has been corrupted or modified directly by SQL.
  10
+      #
  11
+      # ==== Parameters
  12
+      #
  13
+      # * +id+ - The id of the object you wish to reset a counter on.
  14
+      # * +counters+ - One or more counter names to reset
  15
+      #
  16
+      # ==== Examples
  17
+      #
  18
+      #   # For Post with id #1 records reset the comments_count
  19
+      #   Post.reset_counters(1, :comments)
  20
+      def reset_counters(id, *counters)
  21
+        object = find(id)
  22
+        counters.each do |association|
  23
+          has_many_association = reflect_on_association(association.to_sym)
27 24
 
28  
-        stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
29  
-          arel_table[counter_name] => object.send(association).count
30  
-        })
31  
-        connection.update stmt
32  
-      end
33  
-      return true
34  
-    end
  25
+          foreign_key  = has_many_association.foreign_key.to_s
  26
+          child_class  = has_many_association.klass
  27
+          belongs_to   = child_class.reflect_on_all_associations(:belongs_to)
  28
+          reflection   = belongs_to.find { |e| e.foreign_key.to_s == foreign_key }
  29
+          counter_name = reflection.counter_cache_column
35 30
 
36  
-    # A generic "counter updater" implementation, intended primarily to be
37  
-    # used by increment_counter and decrement_counter, but which may also
38  
-    # be useful on its own. It simply does a direct SQL update for the record
39  
-    # with the given ID, altering the given hash of counters by the amount
40  
-    # given by the corresponding value:
41  
-    #
42  
-    # ==== Parameters
43  
-    #
44  
-    # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
45  
-    # * +counters+ - An Array of Hashes containing the names of the fields
46  
-    #   to update as keys and the amount to update the field by as values.
47  
-    #
48  
-    # ==== Examples
49  
-    #
50  
-    #   # For the Post with id of 5, decrement the comment_count by 1, and
51  
-    #   # increment the action_count by 1
52  
-    #   Post.update_counters 5, :comment_count => -1, :action_count => 1
53  
-    #   # Executes the following SQL:
54  
-    #   # UPDATE posts
55  
-    #   #    SET comment_count = COALESCE(comment_count, 0) - 1,
56  
-    #   #        action_count = COALESCE(action_count, 0) + 1
57  
-    #   #  WHERE id = 5
58  
-    #
59  
-    #   # For the Posts with id of 10 and 15, increment the comment_count by 1
60  
-    #   Post.update_counters [10, 15], :comment_count => 1
61  
-    #   # Executes the following SQL:
62  
-    #   # UPDATE posts
63  
-    #   #    SET comment_count = COALESCE(comment_count, 0) + 1
64  
-    #   #  WHERE id IN (10, 15)
65  
-    def update_counters(id, counters)
66  
-      updates = counters.map do |counter_name, value|
67  
-        operator = value < 0 ? '-' : '+'
68  
-        quoted_column = connection.quote_column_name(counter_name)
69  
-        "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
  31
+          stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
  32
+            arel_table[counter_name] => object.send(association).count
  33
+          })
  34
+          connection.update stmt
  35
+        end
  36
+        return true
70 37
       end
71 38
 
72  
-      where(primary_key => id).update_all updates.join(', ')
73  
-    end
  39
+      # A generic "counter updater" implementation, intended primarily to be
  40
+      # used by increment_counter and decrement_counter, but which may also
  41
+      # be useful on its own. It simply does a direct SQL update for the record
  42
+      # with the given ID, altering the given hash of counters by the amount
  43
+      # given by the corresponding value:
  44
+      #
  45
+      # ==== Parameters
  46
+      #
  47
+      # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
  48
+      # * +counters+ - An Array of Hashes containing the names of the fields
  49
+      #   to update as keys and the amount to update the field by as values.
  50
+      #
  51
+      # ==== Examples
  52
+      #
  53
+      #   # For the Post with id of 5, decrement the comment_count by 1, and
  54
+      #   # increment the action_count by 1
  55
+      #   Post.update_counters 5, :comment_count => -1, :action_count => 1
  56
+      #   # Executes the following SQL:
  57
+      #   # UPDATE posts
  58
+      #   #    SET comment_count = COALESCE(comment_count, 0) - 1,
  59
+      #   #        action_count = COALESCE(action_count, 0) + 1
  60
+      #   #  WHERE id = 5
  61
+      #
  62
+      #   # For the Posts with id of 10 and 15, increment the comment_count by 1
  63
+      #   Post.update_counters [10, 15], :comment_count => 1
  64
+      #   # Executes the following SQL:
  65
+      #   # UPDATE posts
  66
+      #   #    SET comment_count = COALESCE(comment_count, 0) + 1
  67
+      #   #  WHERE id IN (10, 15)
  68
+      def update_counters(id, counters)
  69
+        updates = counters.map do |counter_name, value|
  70
+          operator = value < 0 ? '-' : '+'
  71
+          quoted_column = connection.quote_column_name(counter_name)
  72
+          "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
  73
+        end
74 74
 
75  
-    # Increment a number field by one, usually representing a count.
76  
-    #
77  
-    # This is used for caching aggregate values, so that they don't need to be computed every time.
78  
-    # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
79  
-    # shown it would have to run an SQL query to find how many posts and comments there are.
80  
-    #
81  
-    # ==== Parameters
82  
-    #
83  
-    # * +counter_name+ - The name of the field that should be incremented.
84  
-    # * +id+ - The id of the object that should be incremented.
85  
-    #
86  
-    # ==== Examples
87  
-    #
88  
-    #   # Increment the post_count column for the record with an id of 5
89  
-    #   DiscussionBoard.increment_counter(:post_count, 5)
90  
-    def increment_counter(counter_name, id)
91  
-      update_counters(id, counter_name => 1)
92  
-    end
  75
+        where(primary_key => id).update_all updates.join(', ')
  76
+      end
  77
+
  78
+      # Increment a number field by one, usually representing a count.
  79
+      #
  80
+      # This is used for caching aggregate values, so that they don't need to be computed every time.
  81
+      # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
  82
+      # shown it would have to run an SQL query to find how many posts and comments there are.
  83
+      #
  84
+      # ==== Parameters
  85
+      #
  86
+      # * +counter_name+ - The name of the field that should be incremented.
  87
+      # * +id+ - The id of the object that should be incremented.
  88
+      #
  89
+      # ==== Examples
  90
+      #
  91
+      #   # Increment the post_count column for the record with an id of 5
  92
+      #   DiscussionBoard.increment_counter(:post_count, 5)
  93
+      def increment_counter(counter_name, id)
  94
+        update_counters(id, counter_name => 1)
  95
+      end
93 96
 
94  
-    # Decrement a number field by one, usually representing a count.
95  
-    #
96  
-    # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
97  
-    #
98  
-    # ==== Parameters
99  
-    #
100  
-    # * +counter_name+ - The name of the field that should be decremented.
101  
-    # * +id+ - The id of the object that should be decremented.
102  
-    #
103  
-    # ==== Examples
104  
-    #
105  
-    #   # Decrement the post_count column for the record with an id of 5
106  
-    #   DiscussionBoard.decrement_counter(:post_count, 5)
107  
-    def decrement_counter(counter_name, id)
108  
-      update_counters(id, counter_name => -1)
  97
+      # Decrement a number field by one, usually representing a count.
  98
+      #
  99
+      # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
  100
+      #
  101
+      # ==== Parameters
  102
+      #
  103
+      # * +counter_name+ - The name of the field that should be decremented.
  104
+      # * +id+ - The id of the object that should be decremented.
  105
+      #
  106
+      # ==== Examples
  107
+      #
  108
+      #   # Decrement the post_count column for the record with an id of 5
  109
+      #   DiscussionBoard.decrement_counter(:post_count, 5)
  110
+      def decrement_counter(counter_name, id)
  111
+        update_counters(id, counter_name => -1)
  112
+      end
109 113
     end
110 114
   end
111 115
 end
11  activerecord/lib/active_record/explain.rb
... ...
@@ -1,12 +1,13 @@
  1
+require 'active_support/lazy_load_hooks'
1 2
 require 'active_support/core_ext/class/attribute'
2 3
 
3 4
 module ActiveRecord
  5
+  ActiveSupport.on_load(:active_record_config) do
  6
+    mattr_accessor :auto_explain_threshold_in_seconds, instance_accessor: false
  7
+  end
  8
+
4 9
   module Explain
5  
-    def self.extended(base)
6  
-      # If a query takes longer than these many seconds we log its query plan
7  
-      # automatically. nil disables this feature.
8  
-      base.config_attribute :auto_explain_threshold_in_seconds, :global => true
9  
-    end
  10
+    delegate :auto_explain_threshold_in_seconds, :auto_explain_threshold_in_seconds=, to: 'ActiveRecord::Model'
10 11
 
11 12
     # If auto explain is enabled, this method triggers EXPLAIN logging for the
12 13
     # queries triggered by the block if it takes more than the threshold as a
10  activerecord/lib/active_record/inheritance.rb
... ...
@@ -1,13 +1,17 @@
1 1
 require 'active_support/concern'
2 2
 
3 3
 module ActiveRecord
  4
+  ActiveSupport.on_load(:active_record_config) do
  5
+    # Determine whether to store the full constant name including namespace when using STI
  6
+    mattr_accessor :store_full_sti_class, instance_accessor: false
  7
+    self.store_full_sti_class = true
  8
+  end
  9
+
4 10
   module Inheritance
5 11
     extend ActiveSupport::Concern
6 12
 
7 13
     included do
8  
-      # Determine whether to store the full constant name including namespace when using STI
9 14
       config_attribute :store_full_sti_class
10  
-      self.store_full_sti_class = true
11 15
     end
12 16
 
13 17
     module ClassMethods
@@ -95,7 +99,7 @@ def active_record_super #:nodoc:
95 99
       # Returns the class descending directly from ActiveRecord::Base or an
96 100
       # abstract class, if any, in the inheritance hierarchy.
97 101
       def class_of_active_record_descendant(klass)
98  
-        unless klass < Model
  102
+        unless klass < Model::Tag
99 103
           raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
100 104
         end
101 105
 
8  activerecord/lib/active_record/locking/optimistic.rb
... ...
@@ -1,4 +1,9 @@
1 1
 module ActiveRecord
  2
+  ActiveSupport.on_load(:active_record_config) do
  3
+    mattr_accessor :lock_optimistically, instance_accessor: false
  4
+    self.lock_optimistically = true
  5
+  end
  6
+
2 7
   module Locking
3 8
     # == What is Optimistic Locking
4 9
     #
@@ -51,8 +56,7 @@ module Optimistic
51 56
       extend ActiveSupport::Concern
52 57
 
53 58
       included do
54  
-        config_attribute :lock_optimistically, :global => true
55  
-        self.lock_optimistically = true
  59
+        config_attribute :lock_optimistically
56 60
       end
57 61
 
58 62
       def locking_enabled? #:nodoc:
103  activerecord/lib/active_record/model.rb
... ...
@@ -1,6 +1,34 @@
1 1
 require 'active_support/deprecation'
  2
+require 'active_support/concern'
  3
+require 'active_support/core_ext/module/delegation'
  4
+require 'active_support/core_ext/module/attribute_accessors'
2 5
 
3 6
 module ActiveRecord
  7
+  module Configuration # :nodoc:
  8
+    # This just abstracts out how we define configuration options in AR. Essentially we
  9
+    # have mattr_accessors on the ActiveRecord:Model constant that define global defaults.
  10
+    # Classes that then use AR get class_attributes defined, which means that when they
  11
+    # are assigned the default will be overridden for that class and subclasses. (Except
  12
+    # when options[:global] == true, in which case there is one global value always.)
  13
+    def config_attribute(name, options = {})
  14
+      if options[:global]
  15
+        class_eval <<-CODE, __FILE__, __LINE__ + 1
  16
+          def self.#{name};       ActiveRecord::Model.#{name};       end
  17
+          def #{name};            ActiveRecord::Model.#{name};       end
  18
+          def self.#{name}=(val); ActiveRecord::Model.#{name} = val; end
  19
+        CODE
  20
+      else
  21
+        options[:instance_writer] ||= false
  22
+        class_attribute name, options
  23
+
  24
+        singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
  25
+          remove_method :#{name}
  26
+          def #{name}; ActiveRecord::Model.#{name}; end
  27
+        CODE
  28
+      end
  29
+    end
  30
+  end
  31
+
4 32
   # <tt>ActiveRecord::Model</tt> can be included into a class to add Active Record persistence.
5 33
   # This is an alternative to inheriting from <tt>ActiveRecord::Base</tt>. Example:
6 34
   #
@@ -9,41 +37,34 @@ module ActiveRecord
9 37
   #     end
10 38
   #
11 39
   module Model
12  
-    module ClassMethods #:nodoc:
13  
-      include ActiveSupport::Callbacks::ClassMethods
14  
-      include ActiveModel::Naming
15  
-      include QueryCache::ClassMethods
16  
-      include ActiveSupport::Benchmarkable
17  
-      include ActiveSupport::DescendantsTracker
18  
-
19  
-      include Querying
20  
-      include Translation
21  
-      include DynamicMatchers
22  
-      include CounterCache
23  
-      include Explain
24  
-      include ConnectionHandling
25  
-    end
  40
+    extend ActiveSupport::Concern
  41
+    extend ConnectionHandling
26 42
 
27  
-    def self.included(base)
28  
-      return if base.singleton_class < ClassMethods
  43
+    # This allows us to detect an ActiveRecord::Model while it's in the process of being included.
  44
+    module Tag; end
29 45
 
  46
+    def self.append_features(base)
30 47
       base.class_eval do
31  
-        extend ClassMethods
32  
-        Callbacks::Register.setup(self)
33  
-        initialize_generated_modules unless self == Base
  48
+        include Tag
  49
+        extend Configuration
34 50
       end
  51
+
  52
+      super
35 53
     end
36 54
 
37  
-    extend ActiveModel::Configuration
38  
-    extend ActiveModel::Callbacks
39  
-    extend ActiveModel::MassAssignmentSecurity::ClassMethods
40  
-    extend ActiveModel::AttributeMethods::ClassMethods
41  
-    extend Callbacks::Register
42  
-    extend Explain
43  
-    extend ConnectionHandling
  55
+    included do
  56
+      extend ActiveModel::Naming
  57
+      extend ActiveSupport::Benchmarkable
  58
+      extend ActiveSupport::DescendantsTracker
  59
+
  60
+      extend QueryCache::ClassMethods
  61
+      extend Querying
  62
+      extend Translation
  63
+      extend DynamicMatchers
  64
+      extend Explain
  65
+      extend ConnectionHandling
44 66