diff options
Diffstat (limited to 'activerecord/lib')
-rw-r--r-- | activerecord/lib/active_record/attribute_methods.rb | 84 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/read.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/write.rb | 14 | ||||
-rw-r--r-- | activerecord/lib/active_record/autosave_association.rb | 5 | ||||
-rw-r--r-- | activerecord/lib/active_record/base.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/persistence.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/railties/databases.rake | 12 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 3 | ||||
-rw-r--r-- | activerecord/lib/active_record/timestamp.rb | 4 |
9 files changed, 85 insertions, 45 deletions
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index d0687ed0b6..d7bfaa5655 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/enumerable' +require 'active_support/deprecation' module ActiveRecord # = Active Record Attribute Methods @@ -11,56 +12,83 @@ module ActiveRecord # accessors, mutators and query methods. def define_attribute_methods return if attribute_methods_generated? - super(column_names) - @attribute_methods_generated = true + + if base_class == self + super(column_names) + @attribute_methods_generated = true + else + base_class.define_attribute_methods + end end def attribute_methods_generated? - @attribute_methods_generated ||= false + if base_class == self + @attribute_methods_generated ||= false + else + base_class.attribute_methods_generated? + end end def undefine_attribute_methods(*args) - super - @attribute_methods_generated = false + if base_class == self + super + @attribute_methods_generated = false + else + base_class.undefine_attribute_methods(*args) + end end - # Checks whether the method is defined in the model or any of its subclasses - # that also derive from Active Record. Raises DangerousAttributeError if the - # method is defined by Active Record though. def instance_method_already_implemented?(method_name) - method_name = method_name.to_s - index = ancestors.index(ActiveRecord::Base) || ancestors.length - @_defined_class_methods ||= ancestors.first(index).map { |m| - m.instance_methods(false) | m.private_instance_methods(false) - }.flatten.map {|m| m.to_s }.to_set + if dangerous_attribute_method?(method_name) + raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" + end - @@_defined_activerecord_methods ||= defined_activerecord_methods - raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name) - @_defined_class_methods.include?(method_name) + super end - def defined_activerecord_methods + # A method name is 'dangerous' if it is already defined by Active Record, but + # not by any ancestors. (So 'puts' is not dangerous but 'save' is.) + def dangerous_attribute_method?(method_name) active_record = ActiveRecord::Base - super_klass = ActiveRecord::Base.superclass - methods = (active_record.instance_methods - super_klass.instance_methods) + - (active_record.private_instance_methods - super_klass.private_instance_methods) - methods.map {|m| m.to_s }.to_set + superclass = ActiveRecord::Base.superclass + + (active_record.method_defined?(method_name) || + active_record.private_method_defined?(method_name)) && + !superclass.method_defined?(method_name) && + !superclass.private_method_defined?(method_name) end end - 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. - if !self.class.attribute_methods_generated? + # If we haven't generated any methods yet, generate them, then + # see if we've created the method we're looking for. + def method_missing(method, *args, &block) + unless self.class.attribute_methods_generated? self.class.define_attribute_methods - method_name = method_id.to_s - guard_private_attribute_method!(method_name, args) - send(method_id, *args, &block) + + if respond_to_without_attributes?(method) + send(method, *args, &block) + else + super + end else super end end + def attribute_missing(match, *args, &block) + if self.class.columns_hash[match.attr_name] + ActiveSupport::Deprecation.warn( + "The method `#{match.method_name}', matching the attribute `#{match.attr_name}' has " \ + "dispatched through method_missing. This shouldn't happen, because `#{match.attr_name}' " \ + "is a column of the table. If this error has happened through normal usage of Active " \ + "Record (rather than through your own code or external libraries), please report it as " \ + "a bug." + ) + end + + super + end + def respond_to?(name, include_private = false) self.class.define_attribute_methods unless self.class.attribute_methods_generated? super diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 9a50a20fbc..4174e4da09 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -6,8 +6,6 @@ module ActiveRecord ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date] included do - attribute_method_suffix "" - cattr_accessor :attribute_types_cached_by_default, :instance_writer => false self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index c77a3ac145..e9cdb130db 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -17,6 +17,10 @@ module ActiveRecord write_attribute(attr_name, new_value) end end + + if attr_name == primary_key && attr_name != "id" + generated_attribute_methods.module_eval("alias :id= :'#{primary_key}='") + end end end @@ -24,12 +28,16 @@ module ActiveRecord # for fixnum and float columns are turned into +nil+. def write_attribute(attr_name, value) attr_name = attr_name.to_s - attr_name = self.class.primary_key if attr_name == 'id' + attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key @attributes_cache.delete(attr_name) - if (column = column_for_attribute(attr_name)) && column.number? + column = column_for_attribute(attr_name) + + if column && column.number? @attributes[attr_name] = convert_number_column_value(value) - else + elsif column || @attributes.has_key?(attr_name) @attributes[attr_name] = value + else + raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" end end alias_method :raw_write_attribute, :write_attribute diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 085fdba639..056170d82a 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -370,7 +370,10 @@ module ActiveRecord else key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave) - record[reflection.foreign_key] = key + unless reflection.through_reflection + record[reflection.foreign_key] = key + end + saved = record.save(:validate => !autosave) raise ActiveRecord::Rollback if !saved && autosave saved diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2979ad1cb3..558b341c06 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -442,7 +442,7 @@ module ActiveRecord #:nodoc: class << self # Class methods delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped - delegate :first_or_create, :first_or_create!, :first_or_new, :first_or_build, :to => :scoped + delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :scoped delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped @@ -1332,7 +1332,7 @@ MSG # 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) - if klass.superclass == Base || klass.superclass.abstract_class? + if klass == Base || klass.superclass == Base || klass.superclass.abstract_class? klass elsif klass.superclass.nil? raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord" diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 2dac9ea0fb..5e65e46a7d 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -314,7 +314,7 @@ module ActiveRecord new_id = self.class.unscoped.insert attributes_values - self.id ||= new_id + self.id ||= new_id if self.class.primary_key IdentityMap.add(self) if IdentityMap.enabled? @new_record = false diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 13c41350fb..b3316fd1a2 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -203,11 +203,13 @@ db_namespace = namespace :db do end db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}") file_list = [] - Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file| - # only files matching "20091231235959_some_name.rb" pattern - if match_data = /^(\d{14})_(.+)\.rb$/.match(file) - status = db_list.delete(match_data[1]) ? 'up' : 'down' - file_list << [status, match_data[1], match_data[2].humanize] + ActiveRecord::Migrator.migrations_paths.each do |path| + Dir.foreach(path) do |file| + # only files matching "20091231235959_some_name.rb" pattern + if match_data = /^(\d{14})_(.+)\.rb$/.match(file) + status = db_list.delete(match_data[1]) ? 'up' : 'down' + file_list << [status, match_data[1], match_data[2].humanize] + end end end db_list.map! do |version| diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index d3f1347e03..ecefaa633c 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -132,10 +132,9 @@ module ActiveRecord # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>. # # Expects arguments in the same format as <tt>Base.new</tt>. - def first_or_new(attributes = nil, options = {}, &block) + def first_or_initialize(attributes = nil, options = {}, &block) first || new(attributes, options, &block) end - alias :first_or_build :first_or_new def respond_to?(method, include_private = false) arel.respond_to?(method, include_private) || diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index cccac6ffd7..4d5e469a7f 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -48,7 +48,9 @@ module ActiveRecord current_time = current_time_from_proper_timezone all_timestamp_attributes.each do |column| - write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil? + if respond_to?(column) && respond_to?("#{column}=") && self.send(column).nil? + write_attribute(column.to_s, current_time) + end end end |