attribute_methods.rb 2.4 KB
Newer Older
J
Jeremy Kemper 已提交
1 2
require 'active_support/core_ext/enumerable'

3
module ActiveRecord
4
  # = Active Record Attribute Methods
5
  module AttributeMethods #:nodoc:
6
    extend ActiveSupport::Concern
7
    include ActiveModel::AttributeMethods
8

9
    module ClassMethods
10 11
      # Generates all the attribute related methods for columns in the database
      # accessors, mutators and query methods.
12
      def define_attribute_methods
13
        super(columns_hash.keys)
14 15
      end

16
      # Checks whether the method is defined in the model or any of its subclasses
J
Joshua Peek 已提交
17 18
      # that also derive from Active Record. Raises DangerousAttributeError if the
      # method is defined by Active Record though.
19
      def instance_method_already_implemented?(method_name)
J
Jeremy Kemper 已提交
20
        method_name = method_name.to_s
J
Joshua Peek 已提交
21
        @_defined_class_methods         ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
22
        @@_defined_activerecord_methods ||= defined_activerecord_methods
J
Joshua Peek 已提交
23
        raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
24
        @_defined_class_methods.include?(method_name)
25
      end
26 27 28 29 30 31 32 33 34

      def defined_activerecord_methods
        active_record = ActiveRecord::Base
        super_klass   = ActiveRecord::Base.superclass
        methods =  active_record.public_instance_methods - super_klass.public_instance_methods
        methods += active_record.private_instance_methods - super_klass.private_instance_methods
        methods += active_record.protected_instance_methods - super_klass.protected_instance_methods
        methods.map {|m| m.to_s }.to_set
      end
35
    end
J
Joshua Peek 已提交
36

37 38 39
    def method_missing(method_id, *args, &block)
      # If we haven't generated any methods yet, generate them, then
      # see if we've created the method we're looking for.
40
      if !self.class.attribute_methods_generated?
41
        self.class.define_attribute_methods
42
        method_name = method_id.to_s
43
        guard_private_attribute_method!(method_name, args)
44 45 46
        send(method_id, *args, &block)
      else
        super
47 48 49
      end
    end

50 51
    def respond_to?(*args)
      self.class.define_attribute_methods
52 53
      super
    end
54

55 56 57
    protected
      def attribute_method?(attr_name)
        attr_name == 'id' || attributes.include?(attr_name)
58
      end
59 60
  end
end