diff options
14 files changed, 198 insertions, 105 deletions
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index ed83658140..6c3d2cf1b8 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -519,16 +519,18 @@ module ActionView # Delete: <%= project_fields.check_box :_destroy %> # <% end %> # <% end %> - def fields_for(record_or_name_or_array, *args, &block) + def fields_for(record, record_object = nil, options = nil, &block) raise ArgumentError, "Missing block" unless block_given? - options = args.extract_options! - case record_or_name_or_array + options, record_object = record_object, nil if record_object.is_a?(Hash) + options ||= {} + + case record when String, Symbol - object_name = record_or_name_or_array - object = args.first + object = record_object + object_name = record else - object = record_or_name_or_array + object = record object_name = ActiveModel::Naming.singular(object) end diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index e4a9210cde..4d300a1469 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -52,9 +52,9 @@ module ActionView # # <li id="person_123" class="person bar">... # - def content_tag_for(tag_name, record, *args, &block) - prefix = args.first.is_a?(Hash) ? nil : args.shift - options = args.extract_options! + def content_tag_for(tag_name, record, prefix = nil, options = nil, &block) + options, prefix = prefix, nil if prefix.is_a?(Hash) + options ||= {} options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) }) content_tag(tag_name, options, &block) end diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb new file mode 100644 index 0000000000..8cc1ccb1e7 --- /dev/null +++ b/activemodel/test/cases/serialization_test.rb @@ -0,0 +1,45 @@ +require "cases/helper" + +class SerializationTest < ActiveModel::TestCase + class User + include ActiveModel::Serialization + + attr_accessor :name, :email, :gender + + def attributes + @attributes ||= {'name' => 'nil', 'email' => 'nil', 'gender' => 'nil'} + end + + def foo + 'i_am_foo' + end + end + + setup do + @user = User.new + @user.name = 'David' + @user.email = 'david@example.com' + @user.gender = 'male' + end + + def test_method_serializable_hash_should_work + expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"} + assert_equal expected , @user.serializable_hash + end + + def test_method_serializable_hash_should_work_with_only_option + expected = {"name"=>"David"} + assert_equal expected , @user.serializable_hash(:only => [:name]) + end + + def test_method_serializable_hash_should_work_with_except_option + expected = {"gender"=>"male", "email"=>"david@example.com"} + assert_equal expected , @user.serializable_hash(:except => [:name]) + end + + def test_method_serializable_hash_should_work_with_methods_option + expected = {"name"=>"David", "gender"=>"male", :foo=>"i_am_foo", "email"=>"david@example.com"} + assert_equal expected , @user.serializable_hash(:methods => [:foo]) + end + +end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index cf4594ad7f..439880c1fa 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -12,7 +12,7 @@ module ActiveRecord raise "You cannot include Dirty after Timestamp" end - superclass_delegating_accessor :partial_updates + class_attribute :partial_updates self.partial_updates = true end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index a73a9d5fef..f465a38dbe 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -421,7 +421,7 @@ module ActiveRecord #:nodoc: @@timestamped_migrations = true # Determine whether to store the full constant name including namespace when using STI - superclass_delegating_accessor :store_full_sti_class + class_attribute :store_full_sti_class self.store_full_sti_class = true # Stores the default scope for the class diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 666d18215e..826031a3e3 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -827,12 +827,12 @@ module ActiveRecord setup :setup_fixtures teardown :teardown_fixtures - superclass_delegating_accessor :fixture_path - superclass_delegating_accessor :fixture_table_names - superclass_delegating_accessor :fixture_class_names - superclass_delegating_accessor :use_transactional_fixtures - superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances - superclass_delegating_accessor :pre_loaded_fixtures + class_attribute :fixture_path + class_attribute :fixture_table_names + class_attribute :fixture_class_names + class_attribute :use_transactional_fixtures + class_attribute :use_instantiated_fixtures # true, false, or :no_instances + class_attribute :pre_loaded_fixtures self.fixture_table_names = [] self.use_transactional_fixtures = true diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index bb9cdaaefe..78fa85e737 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -10,89 +10,133 @@ module ActiveRecord :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value def includes(*args) - args.reject! { |a| a.blank? } - clone.tap {|r| r.includes_values = (r.includes_values + args).flatten.uniq if args.present? } + args.reject! {|a| a.blank? } + + relation = clone + relation.includes_values = (relation.includes_values + args).flatten.uniq if args.present? + relation end def eager_load(*args) - clone.tap {|r| r.eager_load_values += args if args.present? } + relation = clone + relation.eager_load_values += args if args.present? + relation end def preload(*args) - clone.tap {|r| r.preload_values += args if args.present? } + relation = clone + relation.preload_values += args if args.present? + relation end def select(*args) if block_given? to_a.select {|*block_args| yield(*block_args) } else - clone.tap {|r| r.select_values += args if args.present? } + relation = clone + relation.select_values += args if args.present? + relation end end def group(*args) - clone.tap {|r| r.group_values += args.flatten if args.present? } + relation = clone + relation.group_values += args.flatten if args.present? + relation end def order(*args) - clone.tap {|r| r.order_values += args if args.present? } + relation = clone + relation.order_values += args.flatten if args.present? + relation end def reorder(*args) - clone.tap {|r| r.order_values = args if args.present? } + relation = clone + relation.order_values = args if args.present? + relation end def joins(*args) - args.flatten! - clone.tap {|r| r.joins_values += args if args.present? } + relation = clone + + if args.present? + args.flatten! + relation.joins_values += args if args.present? + end + + relation end def where(opts, *rest) - value = build_where(opts, rest) - copy = clone - copy.where_values += Array.wrap(value) if value - copy + relation = clone + + if opts.present? && value = build_where(opts, rest) + relation.where_values += Array.wrap(value) + end + + relation end def having(*args) - value = build_where(*args) - clone.tap {|r| r.having_values += Array.wrap(value) if value.present? } + relation = clone + + if args.present? && value = build_where(*args) + relation.having_values += Array.wrap(value) + end + + relation end - def limit(value = true) - copy = clone - copy.limit_value = value - copy + def limit(value) + relation = clone + relation.limit_value = value + relation end - def offset(value = true) - clone.tap {|r| r.offset_value = value } + def offset(value) + relation = clone + relation.offset_value = value + relation end def lock(locks = true) + relation = clone + case locks when String, TrueClass, NilClass - clone.tap {|r| r.lock_value = locks || true } + relation.lock_value = locks || true else - clone.tap {|r| r.lock_value = false } + relation.lock_value = false end + + relation end def readonly(value = true) - clone.tap {|r| r.readonly_value = value } + relation = clone + relation.readonly_value = value + relation end - def create_with(value = true) - clone.tap {|r| r.create_with_value = value } + def create_with(value) + relation = clone + relation.create_with_value = value + relation end - def from(value = true) - clone.tap {|r| r.from_value = value } + def from(value) + relation = clone + relation.from_value = value + relation end def extending(*modules, &block) modules << Module.new(&block) if block_given? - clone.tap {|r| r.send(:apply_modules, *modules) } + + relation = clone + relation.send(:apply_modules, *modules) + relation end def reverse_order @@ -103,7 +147,7 @@ module ActiveRecord "#{@klass.table_name}.#{@klass.primary_key} DESC" : reverse_sql_order(order_clause) - relation.order Arel::SqlLiteral.new order + relation.order(Arel::SqlLiteral.new(order)) end def arel @@ -112,6 +156,7 @@ module ActiveRecord def custom_join_sql(*joins) arel = table + joins.each do |join| next if join.blank? @@ -129,6 +174,7 @@ module ActiveRecord arel = arel.join(join) end end + arel.joins(arel) end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index e7e5f26ce0..4b2c647a26 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -6,8 +6,9 @@ module ActiveRecord merged_relation = clone return merged_relation unless r - ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - [:joins, :where]).each do |method| + Relation::ASSOCIATION_METHODS.each do |method| value = r.send(:"#{method}_values") + unless value.empty? if method == :includes merged_relation = merged_relation.includes(value) @@ -17,15 +18,18 @@ module ActiveRecord end end + (Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method| + value = r.send(:"#{method}_values") + merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present? + end + merged_relation = merged_relation.joins(r.joins_values) merged_wheres = @where_values r.where_values.each do |w| if w.respond_to?(:operator) && w.operator == :== - merged_wheres = merged_wheres.reject { |p| - p.respond_to?(:operator) && p.operator == :== && p.operand1.name == w.operand1.name - } + merged_wheres = merged_wheres.reject {|p| p.respond_to?(:operator) && p.operator == :== && p.operand1.name == w.operand1.name } end merged_wheres += [w] @@ -34,9 +38,8 @@ module ActiveRecord merged_relation.where_values = merged_wheres Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method| - unless (value = r.send(:"#{method}_value")).nil? - merged_relation.send(:"#{method}_value=", value) - end + value = r.send(:"#{method}_value") + merged_relation.send(:"#{method}_value=", value) unless value.nil? end merged_relation.lock_value = r.lock_value unless merged_relation.lock_value diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index c726fedd13..8bb8edde0e 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -567,16 +567,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis') end - def test_dynamic_find_order_should_override_association_order - # Developers are ordered 'name DESC, id DESC' - low_id_jamis = developers(:jamis) - middle_id_jamis = developers(:poor_jamis) - high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis') - - assert_equal low_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'", :order => 'id') - assert_equal low_id_jamis, projects(:active_record).developers.find_by_name('Jamis', :order => 'id') - end - def test_dynamic_find_all_should_respect_association_order # Developers are ordered 'name DESC, id DESC' low_id_jamis = developers(:jamis) @@ -587,14 +577,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis') end - def test_dynamic_find_all_order_should_override_association_order - # Developers are ordered 'name DESC, id DESC' - low_id_jamis = developers(:jamis) - middle_id_jamis = developers(:poor_jamis) - high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis') - - assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'", :order => 'id') - assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis', :order => 'id') + def test_find_should_append_to_association_order + ordered_developers = projects(:active_record).developers.order('projects.id') + assert_equal ['developers.name desc, developers.id desc', 'projects.id'], ordered_developers.order_values end def test_dynamic_find_all_should_respect_association_limit @@ -625,11 +610,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase end def test_find_in_association_with_options - developers = projects(:active_record).developers.find(:all) + developers = projects(:active_record).developers.all assert_equal 3, developers.size - assert_equal developers(:poor_jamis), projects(:active_record).developers.find(:first, :conditions => "salary < 10000") - assert_equal developers(:jamis), projects(:active_record).developers.find(:first, :order => "salary DESC") + assert_equal developers(:poor_jamis), projects(:active_record).developers.where("salary < 10000").first end def test_replace_with_less diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 63fc15bca3..efabf74e13 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -147,6 +147,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 2, companies(:first_firm).limited_clients.find(:all, :limit => nil).size end + def test_find_should_append_to_association_order + ordered_clients = companies(:first_firm).clients_sorted_desc.order('companies.id') + assert_equal ['id DESC', 'companies.id'], ordered_clients.order_values + end + def test_dynamic_find_last_without_specified_order assert_equal companies(:second_client), companies(:first_firm).unsorted_clients.find_last_by_type('Client') end @@ -156,21 +161,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client') end - def test_dynamic_find_order_should_override_association_order - assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'", :order => 'id') - assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client', :order => 'id') - end - def test_dynamic_find_all_should_respect_association_order assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'") assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client') end - def test_dynamic_find_all_order_should_override_association_order - assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'", :order => 'id') - assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client', :order => 'id') - end - def test_dynamic_find_all_should_respect_association_limit assert_equal 1, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'").length assert_equal 1, companies(:first_firm).limited_clients.find_all_by_type('Client').length @@ -1018,21 +1013,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment') end - def test_dynamic_find_order_should_override_association_order_for_through - assert_equal Comment.find(3), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id') - assert_equal Comment.find(3), authors(:david).comments_desc.find_by_type('SpecialComment', :order => 'comments.id') - end - def test_dynamic_find_all_should_respect_association_order_for_through assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'") assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find_all_by_type('SpecialComment') end - def test_dynamic_find_all_order_should_override_association_order_for_through - assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id') - assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find_all_by_type('SpecialComment', :order => 'comments.id') - end - def test_dynamic_find_all_should_respect_association_limit_for_through assert_equal 1, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'").length assert_equal 1, authors(:david).limited_comments.find_all_by_type('SpecialComment').length diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index d56e5a7999..cdfd62a675 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -363,14 +363,15 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal "David", klass.first.name assert_equal 100000, klass.first.salary end + def test_method_scope - expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary } + expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary } received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary } assert_equal expected, received end def test_nested_scope - expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary } + expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary } received = DeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } end @@ -378,14 +379,14 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_named_scope_overwrites_default - expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name } + expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.name } received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name } assert_equal expected, received end - def test_named_scope_reorders_default - expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.reordered_by_name.find(:all).collect { |dev| dev.name } + def test_reorder_overrides_default_scope_order + expected = Developer.order('name DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.reorder('name DESC').collect { |dev| dev.name } assert_equal expected, received end diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index a140fb8e57..f0d40e741b 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -87,7 +87,7 @@ end class DeveloperOrderedBySalary < ActiveRecord::Base self.table_name = 'developers' default_scope :order => 'salary DESC' - scope :by_name, :order => 'name DESC' + scope :by_name, order('name DESC') scope :reordered_by_name, reorder('name DESC') def self.all_ordered_by_name diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 70a2950f9b..292383e3d8 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -524,4 +524,31 @@ module CallbacksTest assert_equal "ACTION", obj.stuff end end + + class WriterSkipper < Person + attr_accessor :age + skip_callback :save, :before, :before_save_method, :if => lambda {self.age > 21} + end + + class WriterCallbacksTest < Test::Unit::TestCase + def test_skip_writer + writer = WriterSkipper.new + writer.age = 18 + assert_equal [], writer.history + writer.save + assert_equal [ + [:before_save, :symbol], + [:before_save, :string], + [:before_save, :proc], + [:before_save, :object], + [:before_save, :block], + [:after_save, :block], + [:after_save, :object], + [:after_save, :proc], + [:after_save, :string], + [:after_save, :symbol] + ], writer.history + end + end + end diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 07ef975f80..b998066f4c 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -1375,10 +1375,10 @@ the _version_ file contains this code (comments stripped): module VERSION #:nodoc: MAJOR = 3 MINOR = 1 - MINOR = 0 TINY = 0 + BUILD = "beta" - STRING = [MAJOR, MINOR, TINY].join('.') + STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end end </ruby> |