aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/model.rb
blob: 57553c29eb403b274fcf842f4eee1adf46e65c86 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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:
  #
  #     class Post
  #       include ActiveRecord::Model
  #     end
  #
  module Model
    extend ActiveSupport::Concern
    extend ConnectionHandling
    extend ActiveModel::Observing::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
        include Tag
        extend Configuration
      end

      super
    end

    included do
      extend ActiveModel::Naming
      extend ActiveSupport::Benchmarkable
      extend ActiveSupport::DescendantsTracker

      extend QueryCache::ClassMethods
      extend Querying
      extend Translation
      extend DynamicMatchers
      extend Explain
      extend ConnectionHandling

      initialize_generated_modules unless self == Base
    end

    include Persistence
    include ReadonlyAttributes
    include ModelSchema
    include Inheritance
    include Scoping
    include Sanitization
    include AttributeAssignment
    include ActiveModel::Conversion
    include Integration
    include Validations
    include CounterCache
    include Locking::Optimistic
    include Locking::Pessimistic
    include AttributeMethods
    include Callbacks
    include ActiveModel::Observing
    include Timestamp
    include Associations
    include ActiveModel::SecurePassword
    include AutosaveAssociation
    include NestedAttributes
    include Aggregations
    include Transactions
    include Reflection
    include Serialization
    include Store
    include Core

    class << self
      def arel_engine
        self
      end

      def abstract_class?
        false
      end
      
      # Defines the name of the table column which will store the class name on single-table
      # inheritance situations.
      def inheritance_column
        'type'
      end
    end

    class DeprecationProxy < BasicObject #:nodoc:
      def initialize(model = Model, base = Base)
        @model = model
        @base  = base
      end

      def method_missing(name, *args, &block)
        if @model.respond_to?(name, true)
          @model.send(name, *args, &block)
        else
          ::ActiveSupport::Deprecation.warn(
            "The object passed to the active_record load hook was previously ActiveRecord::Base " \
            "(a Class). Now it is ActiveRecord::Model (a Module). You have called `#{name}' which " \
            "is only defined on ActiveRecord::Base. Please change your code so that it works with " \
            "a module rather than a class. (Model is included in Base, so anything added to Model " \
            "will be available on Base as well.)"
          )
          @base.send(name, *args, &block)
        end
      end

      alias send method_missing

      def extend(*mods)
        ::ActiveSupport::Deprecation.warn(
          "The object passed to the active_record load hook was previously ActiveRecord::Base " \
          "(a Class). Now it is ActiveRecord::Model (a Module). You have called `extend' which " \
          "would add singleton methods to Model. This is presumably not what you want, since the " \
          "methods would not be inherited down to Base. Rather than using extend, please use " \
          "ActiveSupport::Concern + include, which will ensure that your class methods are " \
          "inherited."
        )
        @base.extend(*mods)
      end
    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