diff options
Diffstat (limited to 'activerecord')
18 files changed, 181 insertions, 47 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 32bcf02139..502a7e43de 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,17 @@ *Rails 3.1.0 (unreleased)* +* Add block setting of attributes to singular associations: + + class User < ActiveRecord::Base + has_one :account + end + + user.build_account{ |a| a.credit_limit => 100.0 } + + The block is called after the instance has been initialized. [Andrew White] + +* Add ActiveRecord::Base.attribute_names to return a list of attribute names. This will return an empty array if the model is abstract or table does not exists. [Prem Sichanugrist] + * CSV Fixtures are deprecated and support will be removed in Rails 3.2.0 * AR#new, AR#create, AR#create!, AR#update_attributes and AR#update_attributes! all accept a second hash as option that allows you diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 3a5035305b..335127f38e 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -22,6 +22,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('arel', '~> 2.1.0') + s.add_dependency('arel', '~> 2.1.1') s.add_dependency('tzinfo', '~> 0.3.27') end diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index 634dee2289..44e2ee141e 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -50,7 +50,7 @@ module ActiveRecord end def pluralize(table_name) - ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name + ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name.to_s end private diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb index 62d48d3a2c..638a2ec72a 100644 --- a/activerecord/lib/active_record/associations/builder/singular_association.rb +++ b/activerecord/lib/active_record/associations/builder/singular_association.rb @@ -29,16 +29,16 @@ module ActiveRecord::Associations::Builder def define_constructors name = self.name - model.redefine_method("build_#{name}") do |*params| - association(name).build(*params) + model.redefine_method("build_#{name}") do |*params, &block| + association(name).build(*params, &block) end - model.redefine_method("create_#{name}") do |*params| - association(name).create(*params) + model.redefine_method("create_#{name}") do |*params, &block| + association(name).create(*params, &block) end - model.redefine_method("create_#{name}!") do |*params| - association(name).create!(*params) + model.redefine_method("create_#{name}!") do |*params, &block| + association(name).create!(*params, &block) end end end diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 525ac65722..902ad8cb64 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -423,9 +423,13 @@ module ActiveRecord raise NotImplementedError end + def create_scope + scoped.scope_for_create.stringify_keys + end + def build_record(attributes, options) - record = reflection.build_association - record.assign_attributes(scoped.scope_for_create, :without_protection => true) + record = reflection.build_association(attributes, options) + record.assign_attributes(create_scope.except(*record.changed), :without_protection => true) record.assign_attributes(attributes, options) record end diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index 877ddf3ee1..ce1f2a5543 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -17,27 +17,28 @@ module ActiveRecord replace(record) end - def create(attributes = {}, options = {}) - build(attributes, options).tap { |record| record.save } + def create(attributes = {}, options = {}, &block) + build(attributes, options, &block).tap { |record| record.save } end - def create!(attributes = {}, options = {}) - build(attributes, options).tap { |record| record.save! } + def create!(attributes = {}, options = {}, &block) + build(attributes, options, &block).tap { |record| record.save! } end def build(attributes = {}, options = {}) - record = reflection.build_association - record.assign_attributes( - scoped.scope_for_create.except(klass.primary_key), - :without_protection => true - ) - record.assign_attributes(attributes, options) + record = reflection.build_association(attributes, options) + record.assign_attributes(create_scope.except(*record.changed), :without_protection => true) + yield(record) if block_given? set_new_record(record) record end private + def create_scope + scoped.scope_for_create.stringify_keys.except(klass.primary_key) + end + def find_target scoped.first.tap { |record| set_inverse_instance(record) } end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e1bf2ccc8a..cd16b8d3ca 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -767,6 +767,17 @@ module ActiveRecord #:nodoc: super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, ''))) end + # Returns an array of column names as strings if it's not + # an abstract class and table exists. + # Otherwise it returns an empty array. + def attribute_names + @attribute_names ||= if !abstract_class? && table_exists? + column_names + else + [] + end + end + # Set the lookup ancestors for ActiveModel. def lookup_ancestors #:nodoc: klass = self diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index f4beeceb61..0a460bc086 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -701,11 +701,11 @@ module ActiveRecord schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') result = query(<<-SQL, name) SELECT distinct i.relname, d.indisunique, d.indkey, t.oid - FROM pg_class t, pg_class i, pg_index d + FROM pg_class t + INNER JOIN pg_index d ON t.oid = d.indrelid + INNER JOIN pg_class i ON d.indexrelid = i.oid WHERE i.relkind = 'i' - AND d.indexrelid = i.oid AND d.indisprimary = 'f' - AND t.oid = d.indrelid AND t.relname = '#{table_name}' AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) ) ORDER BY i.relname @@ -820,19 +820,13 @@ module ActiveRecord # given table's primary key. result = exec_query(<<-end_sql, 'SCHEMA').rows.first SELECT attr.attname, seq.relname - FROM pg_class seq, - pg_attribute attr, - pg_depend dep, - pg_namespace name, - pg_constraint cons - WHERE seq.oid = dep.objid - AND seq.relkind = 'S' - AND attr.attrelid = dep.refobjid - AND attr.attnum = dep.refobjsubid - AND attr.attrelid = cons.conrelid - AND attr.attnum = cons.conkey[1] - AND cons.contype = 'p' - AND dep.refobjid = '#{quote_table_name(table)}'::regclass + FROM pg_class seq + INNER JOIN pg_depend dep ON seq.oid = dep.objid + INNER JOIN pg_attribute attr ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid + INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] + WHERE seq.relkind = 'S' + AND cons.contype = 'p' + AND dep.refobjid = '#{quote_table_name(table)}'::regclass end_sql # [primary_key, sequence] @@ -845,16 +839,11 @@ module ActiveRecord def primary_key(table) row = exec_query(<<-end_sql, 'SCHEMA', [[nil, table]]).rows.first SELECT DISTINCT(attr.attname) - FROM pg_attribute attr, - pg_depend dep, - pg_namespace name, - pg_constraint cons - WHERE attr.attrelid = dep.refobjid - AND attr.attnum = dep.refobjsubid - AND attr.attrelid = cons.conrelid - AND attr.attnum = cons.conkey[1] - AND cons.contype = 'p' - AND dep.refobjid = $1::regclass + FROM pg_attribute attr + INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid + INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] + WHERE cons.contype = 'p' + AND dep.refobjid = $1::regclass end_sql row && row.first diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index 9e3b3429e4..fb3fd34665 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/module/attr_internal' +require 'active_record/log_subscriber' module ActiveRecord module Railties diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index ddcc36c841..b993bf6e90 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -626,4 +626,25 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal "Bob", firm.name end + + def test_build_with_block + client = Client.create(:name => 'Client Company') + + firm = client.build_firm{ |f| f.name = 'Agency Company' } + assert_equal 'Agency Company', firm.name + end + + def test_create_with_block + client = Client.create(:name => 'Client Company') + + firm = client.create_firm{ |f| f.name = 'Agency Company' } + assert_equal 'Agency Company', firm.name + end + + def test_create_bang_with_block + client = Client.create(:name => 'Client Company') + + firm = client.create_firm!{ |f| f.name = 'Agency Company' } + assert_equal 'Agency Company', firm.name + end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 0e33fa9c8e..522ac56d82 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1455,4 +1455,17 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal "RED!", car.bulbs.to_a.first.color end + + def test_new_is_called_with_attributes_and_options + car = Car.create(:name => 'honda') + + bulb = car.bulbs.build + assert_equal Bulb, bulb.class + + bulb = car.bulbs.build(:bulb_type => :custom) + assert_equal Bulb, bulb.class + + bulb = car.bulbs.build({ :bulb_type => :custom }, :as => :admin) + assert_equal CustomBulb, bulb.class + end end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 356a4a7a09..f3bf5baa95 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -410,4 +410,41 @@ class HasOneAssociationsTest < ActiveRecord::TestCase bulb = car.create_frickinawesome_bulb(:frickinawesome => false) assert_equal true, bulb.frickinawesome? end + + def test_new_is_called_with_attributes_and_options + car = Car.create(:name => 'honda') + + bulb = car.build_bulb + assert_equal Bulb, bulb.class + + bulb = car.build_bulb + assert_equal Bulb, bulb.class + + bulb = car.build_bulb(:bulb_type => :custom) + assert_equal Bulb, bulb.class + + bulb = car.build_bulb({ :bulb_type => :custom }, :as => :admin) + assert_equal CustomBulb, bulb.class + end + + def test_build_with_block + car = Car.create(:name => 'honda') + + bulb = car.build_bulb{ |b| b.color = 'Red' } + assert_equal 'RED!', bulb.color + end + + def test_create_with_block + car = Car.create(:name => 'honda') + + bulb = car.create_bulb{ |b| b.color = 'Red' } + assert_equal 'RED!', bulb.color + end + + def test_create_bang_with_block + car = Car.create(:name => 'honda') + + bulb = car.create_bulb!{ |b| b.color = 'Red' } + assert_equal 'RED!', bulb.color + end end diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 55d9a328a7..e5e9ca6131 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -10,7 +10,7 @@ require 'models/tagging' require 'models/tag' class InnerJoinAssociationTest < ActiveRecord::TestCase - fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations, + fixtures :authors, :essays, :posts, :comments, :categories, :categories_posts, :categorizations, :taggings, :tags def test_construct_finder_sql_applies_aliases_tables_on_association_conditions diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 49a1c117bc..8e23ab78be 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -13,6 +13,9 @@ require 'models/vertex' require 'models/edge' require 'models/book' require 'models/citation' +require 'models/aircraft' +require 'models/engine' +require 'models/car' class AssociationsJoinModelTest < ActiveRecord::TestCase self.use_transactional_fixtures = false unless supports_savepoints? @@ -704,6 +707,15 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort end + def test_has_many_with_pluralize_table_names_false + engine = Engine.create(:car_id => 1) + Aircraft.pluralize_table_names = false + aircraft = Aircraft.create!(:name => "Airbus 380", :id => 1) + assert_equal aircraft.engines, [engine] + ensure + ActiveRecord::Base.pluralize_table_names = true + end + private # create dynamic Post models to allow different dependency options def find_post_with_dependency(post_id, association, association_name, dependency) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 9bc04ed29c..bfb66f07da 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1790,4 +1790,17 @@ class BasicsTest < ActiveRecord::TestCase assert_equal expected.attributes, actual.attributes end + + def test_attribute_names + assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id"], + Company.attribute_names + end + + def test_attribute_names_on_table_not_exists + assert_equal [], NonExistentTable.attribute_names + end + + def test_attribtue_names_on_abstract_class + assert_equal [], AbstractCompany.attribute_names + end end diff --git a/activerecord/test/models/aircraft.rb b/activerecord/test/models/aircraft.rb new file mode 100644 index 0000000000..0c47aab539 --- /dev/null +++ b/activerecord/test/models/aircraft.rb @@ -0,0 +1,3 @@ +class Aircraft < ActiveRecord::Base + has_many :engines, :foreign_key => "car_id" +end diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb index 7d90963720..0dcc8d5970 100644 --- a/activerecord/test/models/bulb.rb +++ b/activerecord/test/models/bulb.rb @@ -15,4 +15,17 @@ class Bulb < ActiveRecord::Base self[:color] = color.upcase + "!" end + def self.new(attributes = {}, options = {}, &block) + bulb_type = (attributes || {}).delete(:bulb_type) + + if options && options[:as] == :admin && bulb_type.present? + bulb_class = "#{bulb_type.to_s.camelize}Bulb".constantize + bulb_class.new(attributes, options, &block) + else + super + end + end end + +class CustomBulb < Bulb +end
\ No newline at end of file diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index d56cdd57b2..4fe311b441 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -40,6 +40,10 @@ ActiveRecord::Schema.define do t.references :account end + create_table :aircraft, :force => true do |t| + t.string :name + end + create_table :audit_logs, :force => true do |t| t.column :message, :string, :null=>false t.column :developer_id, :integer, :null=>false |