From a17027d13a48e1e64b14a28e7d58e341812f8cb4 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 13 Sep 2008 20:28:01 +0100 Subject: Merge docrails --- .../abstract/schema_definitions.rb | 38 ++++++++- activerecord/lib/active_record/locale/en-US.yml | 29 ++++++- activerecord/lib/active_record/validations.rb | 97 ++++++++++++---------- 3 files changed, 116 insertions(+), 48 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 75032efe57..22304edfc9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -252,6 +252,10 @@ module ActiveRecord class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc: end + # Abstract representation of a column definition. Instances of this type + # are typically created by methods in TableDefinition, and added to the + # +columns+ attribute of said TableDefinition object, in order to be used + # for generating a number of table creation or table changing SQL statements. class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc: def sql_type @@ -275,9 +279,29 @@ module ActiveRecord end end - # Represents a SQL table in an abstract way. - # Columns are stored as a ColumnDefinition in the +columns+ attribute. + # Represents the schema of an SQL table in an abstract way. This class + # provides methods for manipulating the schema representation. + # + # Inside migration files, the +t+ object in +create_table+ and + # +change_table+ is actually of this type: + # + # class SomeMigration < ActiveRecord::Migration + # def self.up + # create_table :foo do |t| + # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition" + # end + # end + # + # def self.down + # ... + # end + # end + # + # The table definitions + # The Columns are stored as a ColumnDefinition in the +columns+ attribute. class TableDefinition + # An array of ColumnDefinition objects, representing the column changes + # that have been defined. attr_accessor :columns def initialize(base) @@ -321,6 +345,12 @@ module ActiveRecord # * :scale - # Specifies the scale for a :decimal column. # + # For clarity's sake: the precision is the number of significant digits, + # while the scale is the number of digits that can be stored following + # the decimal point. For example, the number 123.45 has a precision of 5 + # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can + # range from -999.99 to 999.99. + # # Please be aware of different RDBMS implementations behavior with # :decimal columns: # * The SQL standard says the default scale should be 0, :scale <= @@ -374,6 +404,10 @@ module ActiveRecord # td.column(:huge_integer, :decimal, :precision => 30) # # => huge_integer DECIMAL(30) # + # # Defines a column with a database-specific type. + # td.column(:foo, 'polygon') + # # => foo polygon + # # == Short-hand examples # # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types. diff --git a/activerecord/lib/active_record/locale/en-US.yml b/activerecord/lib/active_record/locale/en-US.yml index 8148f31a81..421f0ebd60 100644 --- a/activerecord/lib/active_record/locale/en-US.yml +++ b/activerecord/lib/active_record/locale/en-US.yml @@ -25,9 +25,30 @@ en-US: even: "must be even" # Append your own errors here or at the model/attributes scope. + # You can define own errors for models or model attributes. + # The values :model, :attribute and :value are always available for interpolation. + # + # For example, + # models: + # user: + # blank: "This is a custom blank message for {{model}}: {{attribute}}" + # attributes: + # login: + # blank: "This is a custom blank message for User login" + # Will define custom blank validation message for User model and + # custom blank validation message for login attribute of User model. models: - # Overrides default messages - - attributes: - # Overrides model and default messages. + + # Translate model names. Used in Model.human_name(). + #models: + # For example, + # user: "Dude" + # will translate User model name to "Dude" + + # Translate model attribute names. Used in Model.human_attribute_name(attribute). + #attributes: + # For example, + # user: + # login: "Handle" + # will translate User attribute "login" as "Handle" diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 577e30ec86..518b59e433 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -259,6 +259,8 @@ module ActiveRecord end + # Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations. + # # Active Records implement validation by overwriting Base#validate (or the variations, +validate_on_create+ and # +validate_on_update+). Each of these methods can inspect the state of the object, which usually means ensuring # that a number of attributes have a certain value (such as not empty, within a given range, matching a certain regular expression). @@ -297,8 +299,6 @@ module ActiveRecord # person.save # => true (and person is now saved in the database) # # An Errors object is automatically created for every Active Record. - # - # Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations. module Validations VALIDATIONS = %w( validate validate_on_create validate_on_update ) @@ -313,9 +313,50 @@ module ActiveRecord base.define_callbacks *VALIDATIONS end - # All of the following validations are defined in the class scope of the model that you're interested in validating. - # They offer a more declarative way of specifying when the model is valid and when it is not. It is recommended to use - # these over the low-level calls to +validate+ and +validate_on_create+ when possible. + # Active Record classes can implement validations in several ways. The highest level, easiest to read, + # and recommended approach is to use the declarative validates_..._of class methods (and + # +validates_associated+) documented below. These are sufficient for most model validations. + # + # Slightly lower level is +validates_each+. It provides some of the same options as the purely declarative + # validation methods, but like all the lower-level approaches it requires manually adding to the errors collection + # when the record is invalid. + # + # At a yet lower level, a model can use the class methods +validate+, +validate_on_create+ and +validate_on_update+ + # to add validation methods or blocks. These are ActiveSupport::Callbacks and follow the same rules of inheritance + # and chaining. + # + # The lowest level style is to define the instance methods +validate+, +validate_on_create+ and +validate_on_update+ + # as documented in ActiveRecord::Validations. + # + # == +validate+, +validate_on_create+ and +validate_on_update+ Class Methods + # + # Calls to these methods add a validation method or block to the class. Again, this approach is recommended + # only when the higher-level methods documented below (validates_..._of and +validates_associated+) are + # insufficient to handle the required validation. + # + # This can be done with a symbol pointing to a method: + # + # class Comment < ActiveRecord::Base + # validate :must_be_friends + # + # def must_be_friends + # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee) + # end + # end + # + # Or with a block which is passed the current record to be validated: + # + # class Comment < ActiveRecord::Base + # validate do |comment| + # comment.must_be_friends + # end + # + # def must_be_friends + # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee) + # end + # end + # + # This usage applies to +validate_on_create+ and +validate_on_update+ as well. module ClassMethods DEFAULT_VALIDATION_OPTIONS = { :on => :save, @@ -329,34 +370,6 @@ module ActiveRecord :equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=', :odd => 'odd?', :even => 'even?' }.freeze - # Adds a validation method or block to the class. This is useful when - # overriding the +validate+ instance method becomes too unwieldy and - # you're looking for more descriptive declaration of your validations. - # - # This can be done with a symbol pointing to a method: - # - # class Comment < ActiveRecord::Base - # validate :must_be_friends - # - # def must_be_friends - # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee) - # end - # end - # - # Or with a block which is passed the current record to be validated: - # - # class Comment < ActiveRecord::Base - # validate do |comment| - # comment.must_be_friends - # end - # - # def must_be_friends - # errors.add_to_base("Must be friends to leave a comment") unless commenter.friend_of?(commentee) - # end - # end - # - # This usage applies to +validate_on_create+ and +validate_on_update+ as well. - # Validates each attribute against a block. # # class Person < ActiveRecord::Base @@ -509,13 +522,13 @@ module ActiveRecord # # class Person < ActiveRecord::Base # validates_length_of :first_name, :maximum=>30 - # validates_length_of :last_name, :maximum=>30, :message=>"less than %d if you don't mind" + # validates_length_of :last_name, :maximum=>30, :message=>"less than {{count}} if you don't mind" # validates_length_of :fax, :in => 7..32, :allow_nil => true # validates_length_of :phone, :in => 7..32, :allow_blank => true # validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name" - # validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least %d character" - # validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with %d characters... don't play me." - # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) } + # validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least {{count}} character" + # validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with {{count}} characters... don't play me." + # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least {{count}} words."), :tokenizer => lambda {|str| str.scan(/\w+/) } # end # # Configuration options: @@ -526,9 +539,9 @@ module ActiveRecord # * :in - A synonym(or alias) for :within. # * :allow_nil - Attribute may be +nil+; skip validation. # * :allow_blank - Attribute may be blank; skip validation. - # * :too_long - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)"). - # * :too_short - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)"). - # * :wrong_length - The error message if using the :is method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)"). + # * :too_long - The error message if the attribute goes over the maximum (default is: "is too long (maximum is {{count}} characters)"). + # * :too_short - The error message if the attribute goes under the minimum (default is: "is too short (min is {{count}} characters)"). + # * :wrong_length - The error message if using the :is method and the attribute is the wrong size (default is: "is the wrong length (should be {{count}} characters)"). # * :message - The error message to use for a :minimum, :maximum, or :is violation. An alias of the appropriate too_long/too_short/wrong_length message. # * :on - Specifies when this validation is active (default is :save, other options :create, :update). # * :if - Specifies a method, proc or string to call to determine if the validation should @@ -731,7 +744,7 @@ module ActiveRecord # class Person < ActiveRecord::Base # validates_inclusion_of :gender, :in => %w( m f ), :message => "woah! what are you then!??!!" # validates_inclusion_of :age, :in => 0..99 - # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %s is not included in the list" + # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list" # end # # Configuration options: @@ -765,7 +778,7 @@ module ActiveRecord # class Person < ActiveRecord::Base # validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here" # validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60" - # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %s is not allowed" + # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension {{value}} is not allowed" # end # # Configuration options: -- cgit v1.2.3 From 9c4730d01e892df8d5c5493a08e0cddf0de5d575 Mon Sep 17 00:00:00 2001 From: miloops Date: Fri, 12 Sep 2008 11:28:47 -0300 Subject: Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] --- activerecord/lib/active_record/attribute_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 0a1baff87d..020da01871 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -10,7 +10,7 @@ module ActiveRecord base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false base.time_zone_aware_attributes = false - base.cattr_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false + base.class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false base.skip_time_zone_conversion_for_attributes = [] end -- cgit v1.2.3 From d95943b276d52c5bc4f033e532376667badbad9f Mon Sep 17 00:00:00 2001 From: gbuesing Date: Sun, 14 Sep 2008 18:16:50 -0500 Subject: Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 91b69747e0..b20da512eb 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2730,7 +2730,7 @@ module ActiveRecord #:nodoc: end def instantiate_time_object(name, values) - if self.class.time_zone_aware_attributes && !self.class.skip_time_zone_conversion_for_attributes.include?(name.to_sym) + if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name)) Time.zone.local(*values) else Time.time_with_datetime_fallback(@@default_timezone, *values) -- cgit v1.2.3 From d51a39ff500d94ea4a81fbc22f0d1c540e83f4e1 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Sun, 14 Sep 2008 12:06:10 +0100 Subject: Deal with MySQL's quirky handling of defaults and blob/text columns [#1043 state:committed] Signed-off-by: Jeremy Kemper --- .../connection_adapters/abstract/schema_definitions.rb | 4 ++++ .../lib/active_record/connection_adapters/mysql_adapter.rb | 7 ++++++- activerecord/lib/active_record/schema_dumper.rb | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 22304edfc9..58992f91da 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -40,6 +40,10 @@ module ActiveRecord type == :integer || type == :float || type == :decimal end + def has_default? + !default.nil? + end + # Returns the Ruby class that corresponds to the abstract data type. def klass case type diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index c2a0fb72bf..a26fd02b90 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -80,7 +80,7 @@ module ActiveRecord def extract_default(default) if type == :binary || type == :text if default.blank? - nil + return null ? nil : '' else raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}" end @@ -91,6 +91,11 @@ module ActiveRecord end end + def has_default? + return false if type == :binary || type == :text #mysql forbids defaults on blob and text columns + super + end + private def simplified_type(field_type) return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)") diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index b90ed88c6b..4f96e225c1 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -102,7 +102,7 @@ HEADER spec[:precision] = column.precision.inspect if !column.precision.nil? spec[:scale] = column.scale.inspect if !column.scale.nil? spec[:null] = 'false' if !column.null - spec[:default] = default_string(column.default) if !column.default.nil? + spec[:default] = default_string(column.default) if column.has_default? (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")} spec end.compact -- cgit v1.2.3 From dc8bf7515de85f5bc28d17e96edf4a3e74a858da Mon Sep 17 00:00:00 2001 From: miloops Date: Mon, 15 Sep 2008 13:23:50 -0300 Subject: When counting grouped records the target should be loaded to return a valid groups count result. Without this change count_records will group for the count in the query and return erroneous results. Signed-off-by: Michael Koziarski [#937 state:committed] --- activerecord/lib/active_record/associations/association_collection.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 8de528f638..afb817f8ae 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -238,6 +238,8 @@ module ActiveRecord def size if @owner.new_record? || (loaded? && !@reflection.options[:uniq]) @target.size + elsif !loaded? && @reflection.options[:group] + load_target.size elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array) unsaved_records = @target.select { |r| r.new_record? } unsaved_records.size + count_records -- cgit v1.2.3 From 4db7e8de1160de6c813a33266fa415849e25fba6 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 16 Sep 2008 18:50:36 +0200 Subject: Update the documentation to reflect the change handling :group earlier --- activerecord/lib/active_record/associations/has_many_association.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index dda22668c6..3b2f306637 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -17,7 +17,10 @@ module ActiveRecord # Returns the number of records in this collection. # # If the association has a counter cache it gets that value. Otherwise - # a count via SQL is performed, bounded to :limit if there's one. + # it will attempt to do a count via SQL, bounded to :limit if + # there's one. Some configuration options like :group make it impossible + # to do a SQL count, in those cases the array count will be used. + # # That does not depend on whether the collection has already been loaded # or not. The +size+ method is the one that takes the loaded flag into # account and delegates to +count_records+ if needed. -- cgit v1.2.3 From 9d7f186f746f38367851355fcd301ac24d6f6a51 Mon Sep 17 00:00:00 2001 From: Nathaniel Talbott Date: Fri, 19 Sep 2008 16:42:18 -0400 Subject: Fixed an error triggered by a reload followed by a foreign key assignment. Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/associations.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 5d91315aad..d7aa4bfa98 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1268,7 +1268,11 @@ module ActiveRecord if association_proxy_class == BelongsToAssociation define_method("#{reflection.primary_key_name}=") do |target_id| - instance_variable_get(ivar).reset if instance_variable_defined?(ivar) + if instance_variable_defined?(ivar) + if association = instance_variable_get(ivar) + association.reset + end + end write_attribute(reflection.primary_key_name, target_id) end end -- cgit v1.2.3 From 8cb7d460439a9b20a80a77b6370c1107233d1cbd Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 14 Sep 2008 13:36:06 +0200 Subject: I18n: move old-style interpolation syntax deprecation to Active Record. [#1044 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record.rb | 2 +- .../i18n_interpolation_deprecation.rb | 26 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 activerecord/lib/active_record/i18n_interpolation_deprecation.rb (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index a6bbd6fc82..3f24170ce1 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -77,5 +77,5 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' +require 'active_record/i18n_interpolation_deprecation' I18n.load_translations File.dirname(__FILE__) + '/active_record/locale/en-US.yml' - diff --git a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb new file mode 100644 index 0000000000..cd634e1b8d --- /dev/null +++ b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb @@ -0,0 +1,26 @@ +# Deprecates the use of the former message interpolation syntax in activerecord +# as in "must have %d characters". The new syntax uses explicit variable names +# as in "{{value}} must have {{count}} characters". + +require 'i18n/backend/simple' +module I18n + module Backend + class Simple + DEPRECATED_INTERPOLATORS = { '%d' => '{{count}}', '%s' => '{{value}}' } + + protected + def interpolate_with_deprecated_syntax(locale, string, values = {}) + return string unless string.is_a?(String) + + string = string.gsub(/%d|%s/) do |s| + instead = DEPRECATED_INTERPOLATORS[s] + ActiveSupport::Deprecation.warn "using #{s} in messages is deprecated; use #{instead} instead." + instead + end + + interpolate_without_deprecated_syntax(locale, string, values) + end + alias_method_chain :interpolate, :deprecated_syntax + end + end +end \ No newline at end of file -- cgit v1.2.3 From a3b7fa78bfdc33e45e39c095b67e02d50a2c7bea Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 15 Sep 2008 10:26:50 +0200 Subject: I18n: Introduce I18n.load_path in favor of I18n.load_translations and change Simple backend to load translations lazily. [#1048 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 3f24170ce1..219cd30f94 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -78,4 +78,4 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' require 'active_record/i18n_interpolation_deprecation' -I18n.load_translations File.dirname(__FILE__) + '/active_record/locale/en-US.yml' +I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en-US.yml' -- cgit v1.2.3 From de96a8666d8edc9be57f6146e587a71d23dbeb41 Mon Sep 17 00:00:00 2001 From: Adeh DeSandies Date: Fri, 12 Sep 2008 18:02:40 +0800 Subject: applied patch to fix the associations with blocks in modules bug from an old trac ticket --- activerecord/lib/active_record/associations.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d7aa4bfa98..e6491cebd6 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1026,7 +1026,7 @@ module ActiveRecord # Create the callbacks to update counter cache if options[:counter_cache] cache_column = options[:counter_cache] == true ? - "#{self.to_s.underscore.pluralize}_count" : + "#{self.to_s.demodulize.underscore.pluralize}_count" : options[:counter_cache] method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym @@ -1755,12 +1755,12 @@ module ActiveRecord def create_extension_modules(association_id, block_extension, extensions) if block_extension - extension_module_name = "#{self.to_s}#{association_id.to_s.camelize}AssociationExtension" + extension_module_name = "#{self.to_s.demodulize}#{association_id.to_s.camelize}AssociationExtension" silence_warnings do - Object.const_set(extension_module_name, Module.new(&block_extension)) + self.parent.const_set(extension_module_name, Module.new(&block_extension)) end - Array(extensions).push(extension_module_name.constantize) + Array(extensions).push("#{self.parent}::#{extension_module_name}".constantize) else Array(extensions) end -- cgit v1.2.3 From 1585a7ed0228c2d4688e986c24fc221a4a9e5104 Mon Sep 17 00:00:00 2001 From: Manfred Stienstra Date: Sun, 21 Sep 2008 18:01:15 +0200 Subject: Change all calls to String#chars to String#mb_chars. --- activerecord/lib/active_record/validations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 518b59e433..684fb1de88 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -678,7 +678,7 @@ module ActiveRecord condition_params = [value] else condition_sql = "LOWER(#{sql_attribute}) #{comparison_operator}" - condition_params = [value.chars.downcase] + condition_params = [value.mb_chars.downcase] end if scope = configuration[:scope] -- cgit v1.2.3 From 46939a9b5a0098fddeac99a8a4331f66bdd0710e Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion" Date: Sun, 21 Sep 2008 23:01:32 +0200 Subject: Add Model#delete instance method, similar to Model.delete class method. [#1086 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 4 ++-- activerecord/lib/active_record/base.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index e6491cebd6..6f4be9391b 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1470,7 +1470,7 @@ module ActiveRecord method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - association.class.delete(association.id) unless association.nil? + association.delete unless association.nil? end before_destroy method_name when :nullify @@ -1500,7 +1500,7 @@ module ActiveRecord method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - association.class.delete(association.id) unless association.nil? + association.delete unless association.nil? end before_destroy method_name else diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b20da512eb..3aa8e5541d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2304,6 +2304,16 @@ module ActiveRecord #:nodoc: create_or_update || raise(RecordNotSaved) end + # Deletes the record in the database and freezes this instance to reflect that no changes should + # be made (since they can't be persisted). + # + # Unlike #destroy, this method doesn't run any +before_delete+ and +after_delete+ + # callbacks, nor will it enforce any association +:dependent+ rules. + def delete + self.class.delete(id) unless new_record? + freeze + end + # Deletes the record in the database and freezes this instance to reflect that no changes should # be made (since they can't be persisted). def destroy -- cgit v1.2.3 From 050e58441bf7e60d167f6708072f8fa7aee2ce76 Mon Sep 17 00:00:00 2001 From: Jan De Poorter Date: Mon, 22 Sep 2008 18:14:42 +0100 Subject: Association#first and last should not load the association if not needed. [#1091 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/associations/association_collection.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index afb817f8ae..47a09510c8 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -63,7 +63,7 @@ module ActiveRecord # Fetches the first one using SQL if possible. def first(*args) - if fetch_first_or_last_using_find? args + if fetch_first_or_last_using_find?(args) find(:first, *args) else load_target unless loaded? @@ -73,7 +73,7 @@ module ActiveRecord # Fetches the last one using SQL if possible. def last(*args) - if fetch_first_or_last_using_find? args + if fetch_first_or_last_using_find?(args) find(:last, *args) else load_target unless loaded? @@ -420,7 +420,8 @@ module ActiveRecord end def fetch_first_or_last_using_find?(args) - args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] || !@target.blank? || args.first.kind_of?(Integer)) + args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] || + @target.any? { |record| record.new_record? } || args.first.kind_of?(Integer)) end end end -- cgit v1.2.3 From 5f86451a4c5d0beca5a746c4708be48b13f665be Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 22 Sep 2008 17:14:54 +0200 Subject: Bump the Version constants to align with the *next* release rather than the previous release. This allows people tracking non-release gems or git submodules to use the constants. --- activerecord/lib/active_record/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index aaadef9979..2479b75789 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -1,7 +1,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 - MINOR = 1 + MINOR = 2 TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') -- cgit v1.2.3 From 70b8ea4fa6f432919340345ae0d5af6aa8f87ec8 Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion)" Date: Sat, 20 Sep 2008 21:59:49 +0200 Subject: Make AssociationCollection start transactions in the correct database. AssociationCollection now starts transactions by calling AssociationCollection#transaction instead of @owner.transaction or @reflection.klass.transaction. Signed-off-by: Michael Koziarski [#1081 state:committed] --- .../associations/association_collection.rb | 23 ++++++++++++++++++---- .../associations/has_many_through_association.rb | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 47a09510c8..463de9d819 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -108,7 +108,7 @@ module ActiveRecord result = true load_target if @owner.new_record? - @owner.transaction do + transaction do flatten_deeper(records).each do |record| raise_on_type_mismatch(record) add_record_to_target_with_callbacks(record) do |r| @@ -123,6 +123,21 @@ module ActiveRecord alias_method :push, :<< alias_method :concat, :<< + # Starts a transaction in the association class's database connection. + # + # class Author < ActiveRecord::Base + # has_many :books + # end + # + # Author.find(:first).books.transaction do + # # same effect as calling Book.transaction + # end + def transaction(*args) + @reflection.klass.transaction(*args) do + yield + end + end + # Remove all records from this association def delete_all load_target @@ -173,7 +188,7 @@ module ActiveRecord records = flatten_deeper(records) records.each { |record| raise_on_type_mismatch(record) } - @owner.transaction do + transaction do records.each { |record| callback(:before_remove, record) } old_records = records.reject {|r| r.new_record? } @@ -200,7 +215,7 @@ module ActiveRecord end def destroy_all - @owner.transaction do + transaction do each { |record| record.destroy } end @@ -292,7 +307,7 @@ module ActiveRecord other = other_array.size < 100 ? other_array : other_array.to_set current = @target.size < 100 ? @target : @target.to_set - @owner.transaction do + transaction do delete(@target.select { |v| !other.include?(v) }) concat(other_array.select { |v| !current.include?(v) }) end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index ebd2bf768c..3171665e19 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -9,14 +9,14 @@ module ActiveRecord alias_method :new, :build def create!(attrs = nil) - @reflection.klass.transaction do + transaction do self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association! } : @reflection.create_association!) object end end def create(attrs = nil) - @reflection.klass.transaction do + transaction do self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association } : @reflection.create_association) object end -- cgit v1.2.3 From 487758b3b88a38da3a75900839aea03774904fe1 Mon Sep 17 00:00:00 2001 From: Pivotal Labs Date: Tue, 23 Sep 2008 13:32:17 -0700 Subject: Allowed passing arrays-of-strings to :join everywhere. Merge duplicate join strings to avoid table aliasing problems. Signed-off-by: Michael Koziarski [#1077 state:committed] --- activerecord/lib/active_record/base.rb | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3aa8e5541d..69ea155ace 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1576,19 +1576,19 @@ module ActiveRecord #:nodoc: (safe_to_array(first) + safe_to_array(second)).uniq end - def merge_joins(first, second) - if first.is_a?(String) && second.is_a?(String) - "#{first} #{second}" - elsif first.is_a?(String) || second.is_a?(String) - if first.is_a?(String) - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, second, nil) - "#{first} #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join}" - else - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, first, nil) - "#{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} #{second}" + def merge_joins(*joins) + if joins.any?{|j| j.is_a?(String) || array_of_strings?(j) } + joins = joins.collect do |join| + join = [join] if join.is_a?(String) + unless array_of_strings?(join) + join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, join, nil) + join = join_dependency.join_associations.collect { |assoc| assoc.association_join } + end + join end + joins.flatten.uniq else - (safe_to_array(first) + safe_to_array(second)).uniq + joins.collect{|j| safe_to_array(j)}.flatten.uniq end end @@ -1604,6 +1604,10 @@ module ActiveRecord #:nodoc: end end + def array_of_strings?(o) + o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)} + end + def add_order!(sql, order, scope = :auto) scope = scope(:find) if :auto == scope scoped_order = scope[:order] if scope @@ -1652,8 +1656,12 @@ module ActiveRecord #:nodoc: merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) case merged_joins when Symbol, Hash, Array - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) - sql << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} " + if array_of_strings?(merged_joins) + sql << merged_joins.join(' ') + " " + else + join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) + sql << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} " + end when String sql << " #{merged_joins} " end -- cgit v1.2.3 From 72b772ae9b692add0359574b0da7038bd1420a5a Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion)" Date: Sun, 21 Sep 2008 23:51:02 +0200 Subject: Refactor configure_dependency_for_has_many to use a few more methods. Add an additional conditions option to make it slightly easier for certain plugins. Signed-off-by: Michael Koziarski [#1087 state:committed] --- activerecord/lib/active_record/associations.rb | 37 +++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 6f4be9391b..3f8ec4d09c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1428,15 +1428,23 @@ module ActiveRecord [] end + # Creates before_destroy callback methods that nullify, delete or destroy + # has_many associated objects, according to the defined :dependent rule. + # # See HasManyAssociation#delete_records. Dependent associations # delete children, otherwise foreign key is set to NULL. - def configure_dependency_for_has_many(reflection) + # + # The +extra_conditions+ parameter, which is not used within the main + # Active Record codebase, is meant to allow plugins to define extra + # finder conditions. + def configure_dependency_for_has_many(reflection, extra_conditions = nil) if reflection.options.include?(:dependent) # Add polymorphic type if the :as option is present dependent_conditions = [] dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}" dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as] dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions] + dependent_conditions << extra_conditions if extra_conditions dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") case reflection.options[:dependent] @@ -1447,9 +1455,24 @@ module ActiveRecord end before_destroy method_name when :delete_all - module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }" + module_eval %Q{ + before_destroy do |record| + delete_all_has_many_dependencies(record, + "#{reflection.name}", + #{reflection.class_name}, + "#{dependent_conditions}") + end + } when :nullify - module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{dependent_conditions})) }" + module_eval %Q{ + before_destroy do |record| + nullify_has_many_dependencies(record, + "#{reflection.name}", + #{reflection.class_name}, + "#{reflection.primary_key_name}", + "#{dependent_conditions}") + end + } else raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})" end @@ -1509,6 +1532,14 @@ module ActiveRecord end end + def delete_all_has_many_dependencies(record, reflection_name, association_class, dependent_conditions) + association_class.delete_all(dependent_conditions) + end + + def nullify_has_many_dependencies(record, reflection_name, association_class, primary_key_name, dependent_conditions) + association_class.update_all("#{primary_key_name} = NULL", dependent_conditions) + end + mattr_accessor :valid_keys_for_has_many_association @@valid_keys_for_has_many_association = [ :class_name, :table_name, :foreign_key, :primary_key, -- cgit v1.2.3 From 4d9a7ab5f5c28820e0b076f9ca44bdd20e19e6ea Mon Sep 17 00:00:00 2001 From: Adam Milligan Date: Sun, 21 Sep 2008 01:03:09 -0700 Subject: Changed ActiveRecord attributes to respect access control. Signed-off-by: Michael Koziarski [#1084 state:committed] --- activerecord/lib/active_record/attribute_methods.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 020da01871..e5486738f0 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -232,6 +232,10 @@ module ActiveRecord def method_missing(method_id, *args, &block) method_name = method_id.to_s + if self.class.private_method_defined?(method_name) + raise NoMethodError("Attempt to call private method", method_name, args) + end + # 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.generated_methods? @@ -334,10 +338,12 @@ module ActiveRecord # person.respond_to?(:name=), and person.respond_to?(:name?) # which will all return +true+. alias :respond_to_without_attributes? :respond_to? - def respond_to?(method, include_priv = false) + def respond_to?(method, include_private_methods = false) method_name = method.to_s if super return true + elsif self.private_methods.include?(method_name) && !include_private_methods + return false elsif !self.class.generated_methods? self.class.define_attribute_methods if self.class.generated_methods.include?(method_name) -- cgit v1.2.3 From ea609b265ffc30cac00bf09a262027f96964ed6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Fri, 26 Sep 2008 20:57:56 +0300 Subject: Ignore all exceptions for validates_acceptance_of columns fetch so it can run even without a database connection Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/validations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 684fb1de88..ed366527ce 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -472,7 +472,7 @@ module ActiveRecord db_cols = begin column_names - rescue ActiveRecord::StatementInvalid + rescue Exception # To ignore both statement and connection errors [] end names = attr_names.reject { |name| db_cols.include?(name.to_s) } -- cgit v1.2.3