From 44b752bea148b9df8f4d806e410e30fff26f680e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 9 Jul 2010 16:39:34 -0400 Subject: expanding on :uniq option in has_many --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 65daa8ffbe..11555b88e0 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -889,7 +889,7 @@ module ActiveRecord # Specifies type of the source association used by has_many :through queries where the source # association is a polymorphic +belongs_to+. # [:uniq] - # If true, duplicates will be omitted from the collection. Useful in conjunction with :through. + # If true, duplicates will be omitted from the collection. Works only in conjunction with :through. # [:readonly] # If true, all the associated objects are readonly through the association. # [:validate] -- cgit v1.2.3 From 86d5c728fbbce6ba8ae73f8926084f5c0eee9a41 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 10 Jul 2010 00:50:59 +0200 Subject: revises recent commit related to :uniq => true --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 11555b88e0..65daa8ffbe 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -889,7 +889,7 @@ module ActiveRecord # Specifies type of the source association used by has_many :through queries where the source # association is a polymorphic +belongs_to+. # [:uniq] - # If true, duplicates will be omitted from the collection. Works only in conjunction with :through. + # If true, duplicates will be omitted from the collection. Useful in conjunction with :through. # [:readonly] # If true, all the associated objects are readonly through the association. # [:validate] -- cgit v1.2.3 From 51783c0298d3789d49682f2ea94039ff2c1f1633 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 10 Jul 2010 13:21:56 -0700 Subject: behavioral tests for sqlite adapter --- .../cases/adapters/sqlite/sqlite_adapter_test.rb | 81 ++++++++++++++++++++-- 1 file changed, 77 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb b/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb index 2505372b7e..389bcdfa8e 100644 --- a/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb @@ -103,17 +103,90 @@ module ActiveRecord end end + def test_columns + columns = @ctx.columns('items').sort_by { |x| x.name } + assert_equal 2, columns.length + assert_equal %w{ id number }.sort, columns.map { |x| x.name } + assert_equal [nil, nil], columns.map { |x| x.default } + assert_equal [true, true], columns.map { |x| x.null } + end + + def test_columns_with_default + @ctx.execute <<-eosql + CREATE TABLE columns_with_default ( + id integer PRIMARY KEY AUTOINCREMENT, + number integer default 10 + ) + eosql + column = @ctx.columns('columns_with_default').find { |x| + x.name == 'number' + } + assert_equal 10, column.default + end + + def test_columns_with_not_null + @ctx.execute <<-eosql + CREATE TABLE columns_with_default ( + id integer PRIMARY KEY AUTOINCREMENT, + number integer not null + ) + eosql + column = @ctx.columns('columns_with_default').find { |x| + x.name == 'number' + } + assert !column.null, "column should not be null" + end + + def test_indexes_logs + intercept_logs_on @ctx + assert_difference('@ctx.logged.length') do + @ctx.indexes('items') + end + assert_match(/items/, @ctx.logged.last.first) + end + + def test_no_indexes + assert_equal [], @ctx.indexes('items') + end + + def test_index + @ctx.add_index 'items', 'id', :unique => true, :name => 'fun' + index = @ctx.indexes('items').find { |idx| idx.name == 'fun' } + + assert_equal 'items', index.table + assert index.unique, 'index is unique' + assert_equal ['id'], index.columns + end + + def test_non_unique_index + @ctx.add_index 'items', 'id', :name => 'fun' + index = @ctx.indexes('items').find { |idx| idx.name == 'fun' } + assert !index.unique, 'index is not unique' + end + + def test_compound_index + @ctx.add_index 'items', %w{ id number }, :name => 'fun' + index = @ctx.indexes('items').find { |idx| idx.name == 'fun' } + assert_equal %w{ id number }.sort, index.columns.sort + end + + private + def assert_logged logs + intercept_logs_on @ctx + yield + assert_equal logs, @ctx.logged + end + + def intercept_logs_on ctx @ctx.extend(Module.new { - attr_reader :logged + attr_accessor :logged def log sql, name - @logged ||= [] @logged << [sql, name] yield end }) - yield - assert_equal logs, @ctx.logged + @ctx.logged = [] end end end -- cgit v1.2.3 From 0bac4c7e0287faa81461c94e6f3a38041b4181ee Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 10 Jul 2010 13:29:12 -0700 Subject: primary key behavioral tests --- .../test/cases/adapters/sqlite/sqlite_adapter_test.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb b/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb index 389bcdfa8e..ce0b2f5f5b 100644 --- a/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite/sqlite_adapter_test.rb @@ -170,6 +170,23 @@ module ActiveRecord assert_equal %w{ id number }.sort, index.columns.sort end + def test_primary_key + assert_equal 'id', @ctx.primary_key('items') + + @ctx.execute <<-eosql + CREATE TABLE foos ( + internet integer PRIMARY KEY AUTOINCREMENT, + number integer not null + ) + eosql + assert_equal 'internet', @ctx.primary_key('foos') + end + + def test_no_primary_key + @ctx.execute 'CREATE TABLE failboat (number integer not null)' + assert_nil @ctx.primary_key('failboat') + end + private def assert_logged logs -- cgit v1.2.3 From f33ee69d59d2b06da3c3c1b24fc9bb640bf6ed25 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 10 Jul 2010 13:30:49 -0700 Subject: reducing funcalls, cleaning up primary key methods for sqlite adapter --- .../active_record/connection_adapters/sqlite_adapter.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 117cf447df..b7f730d06a 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -190,16 +190,21 @@ module ActiveRecord def indexes(table_name, name = nil) #:nodoc: execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row| - index = IndexDefinition.new(table_name, row['name']) - index.unique = row['unique'].to_i != 0 - index.columns = execute("PRAGMA index_info('#{index.name}')").map { |col| col['name'] } - index + IndexDefinition.new( + table_name, + row['name'], + row['unique'].to_i != 0, + execute("PRAGMA index_info('#{row['name']}')").map { |col| + col['name'] + }) end end def primary_key(table_name) #:nodoc: - column = table_structure(table_name).find {|field| field['pk'].to_i == 1} - column ? column['name'] : nil + column = table_structure(table_name).find { |field| + field['pk'].to_i == 1 + } + column && column['name'] end def remove_index!(table_name, index_name) #:nodoc: -- cgit v1.2.3 From da84c9d0d243bb3b27748e250cb7ef71df70bb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 12 Jul 2010 20:18:04 +0200 Subject: Remove old rubygems require. --- .../connection_adapters/abstract/connection_specification.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index 23c42d670b..8e74eff0ab 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -66,15 +66,9 @@ module ActiveRecord unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end begin - require 'rubygems' - gem "activerecord-#{spec[:adapter]}-adapter" require "active_record/connection_adapters/#{spec[:adapter]}_adapter" rescue LoadError - begin - require "active_record/connection_adapters/#{spec[:adapter]}_adapter" - rescue LoadError - raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})" - end + raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})" end adapter_method = "#{spec[:adapter]}_connection" -- cgit v1.2.3 From 6b29dc876fe185881d46731c3ae170478a3828fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 12 Jul 2010 20:18:54 +0200 Subject: Just add connection management middleware if running in a concurrent environment. --- activerecord/lib/active_record/railtie.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 2808e199fe..eff51a7c87 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -16,11 +16,7 @@ module ActiveRecord config.generators.orm :active_record, :migration => true, :timestamps => true - config.app_middleware.insert_after "::ActionDispatch::Callbacks", - "ActiveRecord::QueryCache" - - config.app_middleware.insert_after "::ActionDispatch::Callbacks", - "ActiveRecord::ConnectionAdapters::ConnectionManagement" + config.app_middleware.insert_after "::ActionDispatch::Callbacks", "ActiveRecord::QueryCache" rake_tasks do load "active_record/railties/databases.rake" @@ -72,6 +68,13 @@ module ActiveRecord end end + initializer "active_record.add_concurrency_middleware" do |app| + if app.config.allow_concurrency + app.config.middleware.insert_after "::ActionDispatch::Callbacks", + "ActiveRecord::ConnectionAdapters::ConnectionManagement" + end + end + config.after_initialize do ActiveSupport.on_load(:active_record) do instantiate_observers -- cgit v1.2.3 From 8fb838ed1690fb38ea3dfced1827ef5e7683cc3b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 16:32:08 -0700 Subject: use Hash#each to avoid a second hash lookup --- .../lib/active_record/connection_adapters/sqlite_adapter.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index b7f730d06a..7ccd50bd8c 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -283,10 +283,8 @@ module ActiveRecord def select(sql, name = nil) #:nodoc: execute(sql, name).map do |row| record = {} - row.each_key do |key| - if key.is_a?(String) - record[key.sub(/^"?\w+"?\./, '')] = row[key] - end + row.each do |key, value| + record[key.sub(/^"?\w+"?\./, '')] = value if key.is_a?(String) end record end -- cgit v1.2.3 From cee2ff2768589cb81781fbbdafb9925ba203902f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 16:43:20 -0700 Subject: these strings do not need to be frozen --- activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 7ccd50bd8c..e812a0371b 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -381,9 +381,9 @@ module ActiveRecord def default_primary_key_type if supports_autoincrement? - 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'.freeze + 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL' else - 'INTEGER PRIMARY KEY NOT NULL'.freeze + 'INTEGER PRIMARY KEY NOT NULL' end end -- cgit v1.2.3 From 7c4e0401d25e29b2960ae2adb31194d908cfd25b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 16:54:00 -0700 Subject: cleaning up PostgreSQLAdapter#select --- .../active_record/connection_adapters/postgresql_adapter.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 2fe2ae7136..004c6620df 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -941,15 +941,9 @@ module ActiveRecord # conversions that are required to be performed here instead of in PostgreSQLColumn. def select(sql, name = nil) fields, rows = select_raw(sql, name) - result = [] - for row in rows - row_hash = {} - fields.each_with_index do |f, i| - row_hash[f] = row[i] - end - result << row_hash + rows.map do |row| + Hash[*fields.zip(row).flatten] end - result end def select_raw(sql, name = nil) -- cgit v1.2.3 From c9710a43ff6cdc536f08983b007768ae6a66dd10 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 17:00:29 -0700 Subject: refactor PostgreSQLAdapter#result_as_array to create fewer Range objects --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 004c6620df..9bf08a7671 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -431,15 +431,15 @@ module ActiveRecord def result_as_array(res) #:nodoc: # check if we have any binary column and if they need escaping unescape_col = [] - for j in 0...res.nfields do + res.nfields.times do |j| # unescape string passed BYTEA field (OID == 17) unescape_col << ( res.ftype(j)==17 ) end ary = [] - for i in 0...res.ntuples do + res.ntuples.times do |i| ary << [] - for j in 0...res.nfields do + res.nfields.times do |j| data = res.getvalue(i,j) data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String) ary[i] << data -- cgit v1.2.3 From 8521cdf03d2b9761f21b593e10a31ca96d05092c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 17:44:15 -0700 Subject: PostgreSQLAdapter#select_raw fields and results are empty even if ntuples is 0 --- .../connection_adapters/postgresql_adapter.rb | 46 ++++++++++------------ 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 9bf08a7671..1429f44ff3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -949,34 +949,30 @@ module ActiveRecord def select_raw(sql, name = nil) res = execute(sql, name) results = result_as_array(res) - fields = [] - rows = [] - if res.ntuples > 0 - fields = res.fields - results.each do |row| - hashed_row = {} - row.each_index do |cell_index| - # If this is a money type column and there are any currency symbols, - # then strip them off. Indeed it would be prettier to do this in - # PostgreSQLColumn.string_to_decimal but would break form input - # fields that call value_before_type_cast. - if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID - # Because money output is formatted according to the locale, there are two - # cases to consider (note the decimal separators): - # (1) $12,345,678.12 - # (2) $12.345.678,12 - case column = row[cell_index] - when /^-?\D+[\d,]+\.\d{2}$/ # (1) - row[cell_index] = column.gsub(/[^-\d\.]/, '') - when /^-?\D+[\d\.]+,\d{2}$/ # (2) - row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.') - end + fields = res.fields + rows = results.map do |row| + hashed_row = {} + row.each_index do |cell_index| + # If this is a money type column and there are any currency symbols, + # then strip them off. Indeed it would be prettier to do this in + # PostgreSQLColumn.string_to_decimal but would break form input + # fields that call value_before_type_cast. + if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID + # Because money output is formatted according to the locale, there are two + # cases to consider (note the decimal separators): + # (1) $12,345,678.12 + # (2) $12.345.678,12 + case column = row[cell_index] + when /^-?\D+[\d,]+\.\d{2}$/ # (1) + row[cell_index] = column.gsub(/[^-\d\.]/, '') + when /^-?\D+[\d\.]+,\d{2}$/ # (2) + row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.') end - - hashed_row[fields[cell_index]] = column end - rows << row + + hashed_row[fields[cell_index]] = column end + row end res.clear return fields, rows -- cgit v1.2.3 From 79e9f4a3183064aa5486cda0e7cbddfea6e08c33 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 17:49:12 -0700 Subject: PostgreSQLAdapter#select_raw use each_with_index to avoid multiple array lookups --- .../active_record/connection_adapters/postgresql_adapter.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 1429f44ff3..f4756ed18d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -952,7 +952,7 @@ module ActiveRecord fields = res.fields rows = results.map do |row| hashed_row = {} - row.each_index do |cell_index| + row.each_with_index do |cell, cell_index| # If this is a money type column and there are any currency symbols, # then strip them off. Indeed it would be prettier to do this in # PostgreSQLColumn.string_to_decimal but would break form input @@ -962,15 +962,15 @@ module ActiveRecord # cases to consider (note the decimal separators): # (1) $12,345,678.12 # (2) $12.345.678,12 - case column = row[cell_index] + case cell when /^-?\D+[\d,]+\.\d{2}$/ # (1) - row[cell_index] = column.gsub(/[^-\d\.]/, '') + row[cell_index] = cell.gsub(/[^-\d\.]/, '') when /^-?\D+[\d\.]+,\d{2}$/ # (2) - row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.') + row[cell_index] = cell.gsub(/[^-\d,]/, '').sub(/,/, '.') end end - hashed_row[fields[cell_index]] = column + hashed_row[fields[cell_index]] = cell end row end -- cgit v1.2.3 From 2c3aab32dd064c2812088c1226562ee522d55d48 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 17:52:53 -0700 Subject: PostgreSQLAdapter#select_raw removing dead code --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 3 --- 1 file changed, 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index f4756ed18d..87ae23d104 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -951,7 +951,6 @@ module ActiveRecord results = result_as_array(res) fields = res.fields rows = results.map do |row| - hashed_row = {} row.each_with_index do |cell, cell_index| # If this is a money type column and there are any currency symbols, # then strip them off. Indeed it would be prettier to do this in @@ -969,8 +968,6 @@ module ActiveRecord row[cell_index] = cell.gsub(/[^-\d,]/, '').sub(/,/, '.') end end - - hashed_row[fields[cell_index]] = cell end row end -- cgit v1.2.3 From 2aed63eb526b5493df63151e2bf451d55114c49a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 12 Jul 2010 17:58:48 -0700 Subject: our method is modifying the original array, so refactor to use destructive methods --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 87ae23d104..dd623def2e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -950,7 +950,7 @@ module ActiveRecord res = execute(sql, name) results = result_as_array(res) fields = res.fields - rows = results.map do |row| + results.each do |row| row.each_with_index do |cell, cell_index| # If this is a money type column and there are any currency symbols, # then strip them off. Indeed it would be prettier to do this in @@ -963,16 +963,15 @@ module ActiveRecord # (2) $12.345.678,12 case cell when /^-?\D+[\d,]+\.\d{2}$/ # (1) - row[cell_index] = cell.gsub(/[^-\d\.]/, '') + cell.gsub!(/[^-\d\.]/, '') when /^-?\D+[\d\.]+,\d{2}$/ # (2) - row[cell_index] = cell.gsub(/[^-\d,]/, '').sub(/,/, '.') + cell.gsub!(/[^-\d,]/, '').sub!(/,/, '.') end end end - row end res.clear - return fields, rows + return fields, results end # Returns the list of a table's column names, data types, and default values. -- cgit v1.2.3 From 1d45ea081493cd4eca95cd75cce7be7b8d9cb07c Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 9 Jul 2010 14:11:51 -0400 Subject: with this fix touch method - does not call validations - doest not call callbacks - updates updated_at/on along with attribute if attribute is provided - marks udpated_at/on and attribute as NOT changed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#2520 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/persistence.rb | 1 + activerecord/lib/active_record/timestamp.rb | 16 +++++++--------- activerecord/test/cases/timestamp_test.rb | 10 ++++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 828a8b41b6..44264bec7f 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -111,6 +111,7 @@ module ActiveRecord if record_update_timestamps timestamp_attributes_for_update_in_model.each do |column| hash[column] = read_attribute(column) + @changed_attributes.delete(column.to_s) end end diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 1075a60f07..5cddd07e82 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -21,24 +21,22 @@ module ActiveRecord end # Saves the record with the updated_at/on attributes set to the current time. - # If the save fails because of validation errors, an - # ActiveRecord::RecordInvalid exception is raised. If an attribute name is passed, - # that attribute is used for the touch instead of the updated_at/on attributes. + # Please note that no validation is performed and no callbacks are executed. + # If an attribute name is passed, that attribute is updated along with + # updated_at/on attributes. # # Examples: # - # product.touch # updates updated_at - # product.touch(:designed_at) # updates the designed_at attribute + # product.touch # updates updated_at/on + # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on def touch(attribute = nil) current_time = current_time_from_proper_timezone if attribute - write_attribute(attribute, current_time) + self.update_attribute(attribute, current_time) else - timestamp_attributes_for_update_in_model.each { |column| write_attribute(column.to_s, current_time) } + timestamp_attributes_for_update_in_model.each { |column| self.update_attribute(column, current_time) } end - - save! end private diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 549c4af6b1..f765540808 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -25,16 +25,26 @@ class TimestampTest < ActiveRecord::TestCase end def test_touching_a_record_updates_its_timestamp + previous_salary = @developer.salary + @developer.salary = previous_salary + 10000 @developer.touch assert_not_equal @previously_updated_at, @developer.updated_at + assert_equal previous_salary + 10000, @developer.salary + assert @developer.salary_changed?, 'developer salary should have changed' + assert @developer.changed?, 'developer should be marked as changed' + @developer.reload + assert_equal previous_salary, @developer.salary end def test_touching_a_different_attribute previously_created_at = @developer.created_at @developer.touch(:created_at) + assert !@developer.created_at_changed? , 'created_at should not be changed' + assert !@developer.changed?, 'record should not be changed' assert_not_equal previously_created_at, @developer.created_at + assert_not_equal @previously_updated_at, @developer.updated_at end def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at -- cgit v1.2.3 From 34013115626b2632ffe8ec357e2c5f47cfa059b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 13 Jul 2010 08:05:09 +0200 Subject: Tidying up a bit, so update_attribute is not called twice on touch. --- activerecord/lib/active_record/persistence.rb | 18 ++++++++---------- activerecord/lib/active_record/timestamp.rb | 24 +++++++++--------------- 2 files changed, 17 insertions(+), 25 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 44264bec7f..e53cc5ee8c 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -105,19 +105,17 @@ module ActiveRecord # Updates a single attribute and saves the record without going through the normal validation procedure # or callbacks. This is especially useful for boolean flags on existing records. def update_attribute(name, value) - send("#{name}=", value) - hash = { name => read_attribute(name) } - - if record_update_timestamps - timestamp_attributes_for_update_in_model.each do |column| - hash[column] = read_attribute(column) - @changed_attributes.delete(column.to_s) - end + changes = record_update_timestamps || {} + + if name + name = name.to_s + send("#{name}=", value) + changes[name] = read_attribute(name) end - @changed_attributes.delete(name.to_s) + @changed_attributes.except!(*changes.keys) primary_key = self.class.primary_key - self.class.update_all(hash, { primary_key => self[primary_key] }) == 1 + self.class.update_all(changes, { primary_key => self[primary_key] }) == 1 end # Updates all the attributes from the passed-in Hash and saves the record. diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 5cddd07e82..341cc87be5 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -30,16 +30,11 @@ module ActiveRecord # product.touch # updates updated_at/on # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on def touch(attribute = nil) - current_time = current_time_from_proper_timezone - - if attribute - self.update_attribute(attribute, current_time) - else - timestamp_attributes_for_update_in_model.each { |column| self.update_attribute(column, current_time) } - end + update_attribute(attribute, current_time_from_proper_timezone) end private + def create #:nodoc: if record_timestamps current_time = current_time_from_proper_timezone @@ -56,17 +51,16 @@ module ActiveRecord end def update(*args) #:nodoc: - record_update_timestamps + record_update_timestamps if !partial_updates? || changed? super end - def record_update_timestamps - if record_timestamps && (!partial_updates? || changed?) - current_time = current_time_from_proper_timezone - timestamp_attributes_for_update_in_model.each { |column| write_attribute(column.to_s, current_time) } - true - else - false + def record_update_timestamps #:nodoc: + return unless record_timestamps + current_time = current_time_from_proper_timezone + timestamp_attributes_for_update_in_model.inject({}) do |hash, column| + hash[column.to_s] = write_attribute(column.to_s, current_time) + hash end end -- cgit v1.2.3 From 571cb1ddc6692ed96e04a14e670beb01ba8c93ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Thu, 8 Jul 2010 22:17:21 +0200 Subject: enable AS::JSON.encode to encode AR::Relation by providing `as_json` method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5073 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/relation.rb | 4 +++- activerecord/test/cases/json_serialization_test.rb | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index d9fc1b4940..7499100f55 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -10,7 +10,7 @@ module ActiveRecord include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches - delegate :to_xml, :to_json, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a + delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a delegate :insert, :to => :arel attr_reader :table, :klass @@ -74,6 +74,8 @@ module ActiveRecord @records end + def as_json(options = nil) to_a end #:nodoc: + # Returns size of the records. def size loaded? ? @records.length : count diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index c275557da8..2bc746c0b8 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -201,4 +201,11 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase } assert_equal %({"1":{"author":{"name":"David"}}}), ActiveSupport::JSON.encode(authors_hash, :only => [1, :name]) end + + def test_should_be_able_to_encode_relation + authors_relation = Author.where(:id => [@david.id, @mary.id]) + + json = ActiveSupport::JSON.encode authors_relation, :only => :name + assert_equal '[{"author":{"name":"David"}},{"author":{"name":"Mary"}}]', json + end end -- cgit v1.2.3 From 44e7fba59e251c1eb12d8f793a643b5804cb3977 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Fri, 9 Jul 2010 04:01:58 -0400 Subject: renaming test name to fix accidently override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5076 state:resolved] Signed-off-by: José Valim --- activerecord/test/cases/associations/has_many_associations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index a52cedd8c2..f1440804d2 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -549,7 +549,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert the_client.new_record? end - def test_find_or_create + def test_find_or_create_updates_size number_of_clients = companies(:first_firm).clients.size the_client = companies(:first_firm).clients.find_or_create_by_name("Yet another client") assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size -- cgit v1.2.3 From b520d602ffb85f3816f4407ff9dd5c7721a2da7d Mon Sep 17 00:00:00 2001 From: "Will St. Clair + Neeraj Singh" Date: Sat, 10 Jul 2010 14:19:25 -0500 Subject: string IDs are now quoted correctly [#5064 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../associations/through_association_scope.rb | 2 +- .../associations/has_one_through_associations_test.rb | 12 +++++++++++- activerecord/test/fixtures/dashboards.yml | 3 +++ activerecord/test/fixtures/minivans.yml | 4 ++++ activerecord/test/fixtures/speedometers.yml | 4 ++++ activerecord/test/models/dashboard.rb | 3 +++ activerecord/test/models/minivan.rb | 6 ++++++ activerecord/test/models/speedometer.rb | 4 ++++ activerecord/test/schema/schema.rb | 17 +++++++++++++++++ 9 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 activerecord/test/fixtures/dashboards.yml create mode 100644 activerecord/test/fixtures/minivans.yml create mode 100644 activerecord/test/fixtures/speedometers.yml create mode 100644 activerecord/test/models/dashboard.rb create mode 100644 activerecord/test/models/minivan.rb create mode 100644 activerecord/test/models/speedometer.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 22e1033a9d..cabb33c4a8 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -35,7 +35,7 @@ module ActiveRecord @owner.class.base_class.name.to_s, reflection.klass.columns_hash["#{as}_type"]) } elsif reflection.macro == :belongs_to - { reflection.klass.primary_key => @owner[reflection.primary_key_name] } + { reflection.klass.primary_key => @owner.class.quote_value(@owner[reflection.primary_key_name]) } else { reflection.primary_key_name => owner_quoted_id } end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 178c57435b..3fcd150422 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -6,9 +6,12 @@ require 'models/membership' require 'models/sponsor' require 'models/organization' require 'models/member_detail' +require 'models/minivan' +require 'models/dashboard' +require 'models/speedometer' class HasOneThroughAssociationsTest < ActiveRecord::TestCase - fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations + fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans, :dashboards, :speedometers def setup @member = members(:groucho) @@ -202,4 +205,11 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase Club.find(@club.id, :include => :sponsored_member).save! end end + + def test_value_is_properly_quoted + minivan = Minivan.find('m1') + assert_nothing_raised do + minivan.dashboard + end + end end diff --git a/activerecord/test/fixtures/dashboards.yml b/activerecord/test/fixtures/dashboards.yml new file mode 100644 index 0000000000..e75bf46e6c --- /dev/null +++ b/activerecord/test/fixtures/dashboards.yml @@ -0,0 +1,3 @@ +cool_first: + dashboard_id: d1 + name: my_dashboard \ No newline at end of file diff --git a/activerecord/test/fixtures/minivans.yml b/activerecord/test/fixtures/minivans.yml new file mode 100644 index 0000000000..e7a2ab77eb --- /dev/null +++ b/activerecord/test/fixtures/minivans.yml @@ -0,0 +1,4 @@ +cool_first: + minivan_id: m1 + name: my_minivan + speedometer_id: s1 diff --git a/activerecord/test/fixtures/speedometers.yml b/activerecord/test/fixtures/speedometers.yml new file mode 100644 index 0000000000..6a471aba0a --- /dev/null +++ b/activerecord/test/fixtures/speedometers.yml @@ -0,0 +1,4 @@ +cool_first: + speedometer_id: s1 + name: my_speedometer + dashboard_id: d1 \ No newline at end of file diff --git a/activerecord/test/models/dashboard.rb b/activerecord/test/models/dashboard.rb new file mode 100644 index 0000000000..a8a25834b1 --- /dev/null +++ b/activerecord/test/models/dashboard.rb @@ -0,0 +1,3 @@ +class Dashboard < ActiveRecord::Base + set_primary_key :dashboard_id +end \ No newline at end of file diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb new file mode 100644 index 0000000000..c753319a20 --- /dev/null +++ b/activerecord/test/models/minivan.rb @@ -0,0 +1,6 @@ +class Minivan < ActiveRecord::Base + set_primary_key :minivan_id + + belongs_to :speedometer + has_one :dashboard, :through => :speedometer +end \ No newline at end of file diff --git a/activerecord/test/models/speedometer.rb b/activerecord/test/models/speedometer.rb new file mode 100644 index 0000000000..94743eff8e --- /dev/null +++ b/activerecord/test/models/speedometer.rb @@ -0,0 +1,4 @@ +class Speedometer < ActiveRecord::Base + set_primary_key :speedometer_id + belongs_to :dashboard +end \ No newline at end of file diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index bea351b95a..641726b43f 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -164,6 +164,11 @@ ActiveRecord::Schema.define do t.string :address_country t.string :gps_location end + + create_table :dashboards, :force => true, :id => false do |t| + t.string :dashboard_id + t.string :name + end create_table :developers, :force => true do |t| t.string :name @@ -290,6 +295,12 @@ ActiveRecord::Schema.define do t.boolean :favourite t.integer :lock_version, :default => 0 end + + create_table :minivans, :force => true, :id => false do |t| + t.string :minivan_id + t.string :name + t.string :speedometer_id + end create_table :minimalistics, :force => true do |t| end @@ -452,6 +463,12 @@ ActiveRecord::Schema.define do t.string :name t.integer :ship_id end + + create_table :speedometers, :force => true, :id => false do |t| + t.string :speedometer_id + t.string :name + t.string :dashboard_id + end create_table :sponsors, :force => true do |t| t.integer :club_id -- cgit v1.2.3 From b75fca9e57423eec81384ff3e35f38fd4f7fe47f Mon Sep 17 00:00:00 2001 From: Vitalii Khustochka Date: Mon, 12 Jul 2010 14:17:48 +0300 Subject: Added reorder delegation for ActiveRecord::Base(to be able to overwrite the default_scope ordering in the named scope [#5093 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/relation_scoping_test.rb | 6 ++++++ activerecord/test/models/developer.rb | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c78060c956..400a0adbcf 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -398,7 +398,7 @@ module ActiveRecord #:nodoc: delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped - delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped + delegate :select, :group, :order, :reorder, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped # Executes a custom SQL query against your database and returns all the results. The results will diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index 41dcdbcd37..a5a3b3ef38 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -364,6 +364,12 @@ class DefaultScopingTest < ActiveRecord::TestCase 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 } + assert_equal expected, received + end + def test_nested_exclusive_scope expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary } received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index de68fd7f24..c61c583c1d 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -88,6 +88,7 @@ class DeveloperOrderedBySalary < ActiveRecord::Base self.table_name = 'developers' default_scope :order => 'salary DESC' scope :by_name, :order => 'name DESC' + scope :reordered_by_name, reorder('name DESC') def self.all_ordered_by_name with_scope(:find => { :order => 'name DESC' }) do -- cgit v1.2.3 From 7e075e62479a0eccad6eaf7a7c20f45347860c83 Mon Sep 17 00:00:00 2001 From: Benjamin Quorning Date: Fri, 9 Jul 2010 15:58:58 +0200 Subject: Fixed many references to the old config/environment.rb and Rails::Initializer --- activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 2 +- activerecord/lib/active_record/migration.rb | 2 +- activerecord/lib/active_record/observer.rb | 2 +- activerecord/lib/active_record/session_store.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index aa3626a37e..b403443d8e 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -125,7 +125,7 @@ module ActiveRecord # By default, the MysqlAdapter will consider all columns of type tinyint(1) # as boolean. If you wish to disable this emulation (which was the default # behavior in versions 0.13.1 and earlier) you can add the following line - # to your environment.rb file: + # to your application.rb file: # # ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false cattr_accessor :emulate_booleans diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 4c5e1ae218..5e272f0ba4 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -284,7 +284,7 @@ module ActiveRecord # # config.active_record.timestamped_migrations = false # - # In environment.rb. + # In application.rb. # class Migration @@verbose = true diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index d2ed643f35..ce002f5e1a 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -68,7 +68,7 @@ module ActiveRecord # == Configuration # # In order to activate an observer, list it in the config.active_record.observers configuration setting in your - # config/environment.rb file. + # config/application.rb file. # # config.active_record.observers = :comment_observer, :signup_observer # diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index b88d550086..df2f429c5d 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -16,7 +16,7 @@ module ActiveRecord # ActionController::SessionOverflowError will be raised. # # You may configure the table name, primary key, and data column. - # For example, at the end of config/environment.rb: + # For example, at the end of config/application.rb: # # ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table' # ActiveRecord::SessionStore::Session.primary_key = 'session_id' -- cgit v1.2.3 From bc35631dc7d67ec9c918ec1967c708a5388a76e4 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Jul 2010 10:20:47 -0700 Subject: use constants instead of magic numbers. meow --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index dd623def2e..4baf99610e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -433,7 +433,7 @@ module ActiveRecord unescape_col = [] res.nfields.times do |j| # unescape string passed BYTEA field (OID == 17) - unescape_col << ( res.ftype(j)==17 ) + unescape_col << ( res.ftype(j) == BYTEA_COLUMN_TYPE_OID ) end ary = [] @@ -889,6 +889,8 @@ module ActiveRecord private # The internal PostgreSQL identifier of the money data type. MONEY_COLUMN_TYPE_OID = 790 #:nodoc: + # The internal PostgreSQL identifier of the BYTEA data type. + BYTEA_COLUMN_TYPE_OID = 17 #:nodoc: # Connects to a PostgreSQL server and sets up the adapter depending on the # connected server's characteristics. -- cgit v1.2.3 From 35e304193b8513e1da523a9c1c34bf323e52b9e6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Jul 2010 10:57:15 -0700 Subject: only loop through all rows and columns once in the postgres adapter --- .../connection_adapters/postgresql_adapter.rb | 46 +++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 4baf99610e..d773be0ca6 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -432,8 +432,7 @@ module ActiveRecord # check if we have any binary column and if they need escaping unescape_col = [] res.nfields.times do |j| - # unescape string passed BYTEA field (OID == 17) - unescape_col << ( res.ftype(j) == BYTEA_COLUMN_TYPE_OID ) + unescape_col << res.ftype(j) end ary = [] @@ -441,7 +440,28 @@ module ActiveRecord ary << [] res.nfields.times do |j| data = res.getvalue(i,j) - data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String) + case unescape_col[j] + + # unescape string passed BYTEA field (OID == 17) + when BYTEA_COLUMN_TYPE_OID + data = unescape_bytea(data) if String === data + + # If this is a money type column and there are any currency symbols, + # then strip them off. Indeed it would be prettier to do this in + # PostgreSQLColumn.string_to_decimal but would break form input + # fields that call value_before_type_cast. + when MONEY_COLUMN_TYPE_OID + # Because money output is formatted according to the locale, there are two + # cases to consider (note the decimal separators): + # (1) $12,345,678.12 + # (2) $12.345.678,12 + case data + when /^-?\D+[\d,]+\.\d{2}$/ # (1) + data.gsub!(/[^-\d\.]/, '') + when /^-?\D+[\d\.]+,\d{2}$/ # (2) + data.gsub!(/[^-\d,]/, '').sub!(/,/, '.') + end + end ary[i] << data end end @@ -952,26 +972,6 @@ module ActiveRecord res = execute(sql, name) results = result_as_array(res) fields = res.fields - results.each do |row| - row.each_with_index do |cell, cell_index| - # If this is a money type column and there are any currency symbols, - # then strip them off. Indeed it would be prettier to do this in - # PostgreSQLColumn.string_to_decimal but would break form input - # fields that call value_before_type_cast. - if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID - # Because money output is formatted according to the locale, there are two - # cases to consider (note the decimal separators): - # (1) $12,345,678.12 - # (2) $12.345.678,12 - case cell - when /^-?\D+[\d,]+\.\d{2}$/ # (1) - cell.gsub!(/[^-\d\.]/, '') - when /^-?\D+[\d\.]+,\d{2}$/ # (2) - cell.gsub!(/[^-\d,]/, '').sub!(/,/, '.') - end - end - end - end res.clear return fields, results end -- cgit v1.2.3 From e35e6171bb27c26b13469ccf188c25bb324e38ab Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Jul 2010 11:56:00 -0700 Subject: reducing range comparisons when converting types to sql --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index d773be0ca6..e213eae026 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -848,11 +848,12 @@ module ActiveRecord # Maps logical Rails types to PostgreSQL-specific data types. def type_to_sql(type, limit = nil, precision = nil, scale = nil) return super unless type.to_s == 'integer' + return 'integer' unless limit case limit - when 1..2; 'smallint' - when 3..4, nil; 'integer' - when 5..8; 'bigint' + when 1, 2; 'smallint' + when 3, 4; 'integer' + when 5..8; 'bigint' else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.") end end -- cgit v1.2.3 From f4fbc2c1f943ff11776b2c7c34df6bcbe655a4e5 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 13 Jul 2010 15:30:23 -0400 Subject: update_attributes and update_attributes! are now wrapped in a transaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#922 state:resovled] Signed-off-by: José Valim --- activerecord/lib/active_record/persistence.rb | 12 ++++++++---- activerecord/test/cases/transactions_test.rb | 23 ++++++++++++++++++++++- activerecord/test/models/author.rb | 2 ++ 3 files changed, 32 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index e53cc5ee8c..3681a63e03 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -121,15 +121,19 @@ module ActiveRecord # Updates all the attributes from the passed-in Hash and saves the record. # If the object is invalid, the saving will fail and false will be returned. def update_attributes(attributes) - self.attributes = attributes - save + with_transaction_returning_status do + self.attributes = attributes + save + end end # Updates an object just like Base.update_attributes but calls save! instead # of save so an exception is raised if the record is invalid. def update_attributes!(attributes) - self.attributes = attributes - save! + with_transaction_returning_status do + self.attributes = attributes + save! + end end # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1). diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 958a4e4f94..9255190613 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -3,10 +3,12 @@ require 'models/topic' require 'models/reply' require 'models/developer' require 'models/book' +require 'models/author' +require 'models/post' class TransactionTest < ActiveRecord::TestCase self.use_transactional_fixtures = false - fixtures :topics, :developers + fixtures :topics, :developers, :authors, :posts def setup @first, @second = Topic.find(1, 2).sort_by { |t| t.id } @@ -103,6 +105,25 @@ class TransactionTest < ActiveRecord::TestCase end end + def test_update_attributes_should_rollback_on_failure + author = Author.find(1) + posts_count = author.posts.size + assert posts_count > 0 + status = author.update_attributes(:name => nil, :post_ids => []) + assert !status + assert_equal posts_count, author.posts(true).size + end + + def test_update_attributes_should_rollback_on_failure! + author = Author.find(1) + posts_count = author.posts.size + assert posts_count > 0 + assert_raise(ActiveRecord::RecordInvalid) do + author.update_attributes!(:name => nil, :post_ids => []) + end + assert_equal posts_count, author.posts(true).size + end + def test_cancellation_from_before_destroy_rollbacks_in_destroy add_cancelling_before_destroy_with_db_side_effect_to_topic begin diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 655b45bf57..727978431c 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -108,6 +108,8 @@ class Author < ActiveRecord::Base %w(twitter github) end + validates_presence_of :name + private def log_before_adding(object) @post_log << "before_adding#{object.id || ''}" -- cgit v1.2.3 From edb5401039ee15c37b201244c5dbf660bed51fb4 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 1 Jul 2010 21:41:41 -0400 Subject: count method should not take options if it is operated on has_many association which has finder_sql or counter_sql MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#2395 state:resolved] Signed-off-by: José Valim --- .../associations/association_collection.rb | 7 ++++-- .../associations/has_many_associations_test.rb | 26 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 615b7d2719..1c3a6b56f3 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -183,10 +183,13 @@ module ActiveRecord # descendant's +construct_sql+ method will have set :counter_sql automatically. # Otherwise, construct options and pass them with scope to the target class's +count+. def count(column_name = nil, options = {}) - if @reflection.options[:counter_sql] + column_name, options = nil, column_name if column_name.is_a?(Hash) + + if @reflection.options[:counter_sql] && !options.blank? + raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed" + elsif @reflection.options[:counter_sql] @reflection.klass.count_by_sql(@counter_sql) else - column_name, options = nil, column_name if column_name.is_a?(Hash) if @reflection.options[:uniq] # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL. diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index f1440804d2..6218cdd344 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -11,6 +11,32 @@ require 'models/comment' require 'models/person' require 'models/reader' require 'models/tagging' +require 'models/invoice' +require 'models/line_item' + +class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase + class Invoice < ActiveRecord::Base + has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.* from line_items" + end + def test_should_fail + assert_raise(ArgumentError) do + Invoice.create.custom_line_items.count(:conditions => {:amount => 0}) + end + end +end + +class HasManyAssociationsTestForCountWithCountSql < ActiveRecord::TestCase + class Invoice < ActiveRecord::Base + has_many :custom_line_items, :class_name => 'LineItem', :counter_sql => "SELECT COUNT(*) line_items.* from line_items" + end + def test_should_fail + assert_raise(ArgumentError) do + Invoice.create.custom_line_items.count(:conditions => {:amount => 0}) + end + end +end + + class HasManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :categories, :companies, :developers, :projects, -- cgit v1.2.3 From d10ecfefb8bd4461127f552f14970ad6b3df507f Mon Sep 17 00:00:00 2001 From: Jacob Lewallen Date: Wed, 7 Jul 2010 10:55:40 +0900 Subject: Set destroyed=true in opt locking's destroy [#5058 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/locking/optimistic.rb | 1 + activerecord/test/cases/locking_test.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index ceb0902fde..b6f87a57b8 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -124,6 +124,7 @@ module ActiveRecord end end + @destroyed = true freeze end diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 66874cdad1..e7126964cd 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -53,7 +53,8 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_raises(ActiveRecord::StaleObjectError) { p2.destroy } assert p1.destroy - assert_equal true, p1.frozen? + assert p1.frozen? + assert p1.destroyed? assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) } end -- cgit v1.2.3 From b7944e1b211c37fd9db8d72bef9f12c42b68a0a4 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 14 Jul 2010 17:42:48 +0200 Subject: revises the rdoc of update_attributes and update_attributes! to document they are wrapped in a transaction, and adds code comments explaining why --- activerecord/lib/active_record/persistence.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 3681a63e03..7ec443ccc7 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -118,18 +118,23 @@ module ActiveRecord self.class.update_all(changes, { primary_key => self[primary_key] }) == 1 end - # Updates all the attributes from the passed-in Hash and saves the record. - # If the object is invalid, the saving will fail and false will be returned. + # Updates the attributes of the model from the passed-in hash and saves the + # record, all wrapped in a transaction. If the object is invalid, the saving + # will fail and false will be returned. def update_attributes(attributes) + # The following transaction covers any possible database side-effects of the + # attributes assignment. For example, setting the IDs of a child collection. with_transaction_returning_status do self.attributes = attributes save end end - # Updates an object just like Base.update_attributes but calls save! instead - # of save so an exception is raised if the record is invalid. + # Updates its receiver just like +update_attributes+ but calls save! instead + # of +save+, so an exception is raised if the record is invalid. def update_attributes!(attributes) + # The following transaction covers any possible database side-effects of the + # attributes assignment. For example, setting the IDs of a child collection. with_transaction_returning_status do self.attributes = attributes save! -- cgit v1.2.3 From 4a06489525809755bcb8fd4c05394dbfb900f237 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 14 Jul 2010 16:50:18 +0100 Subject: Style fixes --- activerecord/lib/active_record/base.rb | 2 +- .../lib/active_record/relation/query_methods.rb | 40 +++++++++++----------- activerecord/test/cases/base_test.rb | 4 +-- 3 files changed, 22 insertions(+), 24 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 400a0adbcf..19ccf75b00 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1471,7 +1471,7 @@ MSG # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false) # user.is_admin? # => true def attributes=(new_attributes, guard_protected_attributes = true) - return unless new_attributes.is_a? Hash + return unless new_attributes.is_a?(Hash) attributes = new_attributes.stringify_keys multi_parameter_attributes = [] diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4692271266..0593897fa5 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -11,84 +11,84 @@ module ActiveRecord def includes(*args) args.reject! { |a| a.blank? } - clone.tap { |r| r.includes_values += args if args.present? } + clone.tap {|r| r.includes_values += args if args.present? } end def eager_load(*args) - clone.tap { |r| r.eager_load_values += args if args.present? } + clone.tap {|r| r.eager_load_values += args if args.present? } end def preload(*args) - clone.tap { |r| r.preload_values += args if args.present? } + clone.tap {|r| r.preload_values += args if args.present? } end def select(*args) if block_given? - to_a.select { |*block_args| yield(*block_args) } + to_a.select {|*block_args| yield(*block_args) } else - clone.tap { |r| r.select_values += args if args.present? } + clone.tap {|r| r.select_values += args if args.present? } end end def group(*args) - clone.tap { |r| r.group_values += args if args.present? } + clone.tap {|r| r.group_values += args if args.present? } end def order(*args) - clone.tap { |r| r.order_values += args if args.present? } + clone.tap {|r| r.order_values += args if args.present? } end def reorder(*args) - clone.tap { |r| r.order_values = args if args.present? } + clone.tap {|r| r.order_values = args if args.present? } end def joins(*args) args.flatten! - clone.tap { |r| r.joins_values += args if args.present? } + clone.tap {|r| r.joins_values += args if args.present? } end def where(*args) value = build_where(*args) - clone.tap { |r| r.where_values += Array.wrap(value) if value.present? } + clone.tap {|r| r.where_values += Array.wrap(value) if value.present? } end def having(*args) value = build_where(*args) - clone.tap { |r| r.having_values += Array.wrap(value) if value.present? } + clone.tap {|r| r.having_values += Array.wrap(value) if value.present? } end def limit(value = true) - clone.tap { |r| r.limit_value = value } + clone.tap {|r| r.limit_value = value } end def offset(value = true) - clone.tap { |r| r.offset_value = value } + clone.tap {|r| r.offset_value = value } end def lock(locks = true) case locks when String, TrueClass, NilClass - clone.tap { |r| r.lock_value = locks || true } + clone.tap {|r| r.lock_value = locks || true } else - clone.tap { |r| r.lock_value = false } + clone.tap {|r| r.lock_value = false } end end def readonly(value = true) - clone.tap { |r| r.readonly_value = value } + clone.tap {|r| r.readonly_value = value } end def create_with(value = true) - clone.tap { |r| r.create_with_value = value } + clone.tap {|r| r.create_with_value = value } end def from(value = true) - clone.tap { |r| r.from_value = value } + clone.tap {|r| r.from_value = value } end def extending(*modules, &block) modules << Module.new(&block) if block_given? - clone.tap { |r| r.send(:apply_modules, *modules) } + clone.tap {|r| r.send(:apply_modules, *modules) } end def reverse_order @@ -230,7 +230,7 @@ module ActiveRecord @implicit_readonly = false # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array. # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array - if selects.all? { |s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) + if selects.all? {|s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) arel.project(*selects) else arel.project(selects.last) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index a4cf5120e1..87f46d2992 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -63,9 +63,7 @@ class BasicsTest < ActiveRecord::TestCase def test_set_attributes_without_hash topic = Topic.new - assert_nothing_raised do - topic.attributes = '' - end + assert_nothing_raised { topic.attributes = '' } end def test_integers_as_nil -- cgit v1.2.3 From d5921cdb7a00b393d0ba5f2f795da60a33b11cf1 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 14 Jul 2010 18:32:24 +0100 Subject: Remove unintentional API changes. [#1108] --- .../associations/association_collection.rb | 13 ++---- .../lib/active_record/dynamic_finder_match.rb | 4 ++ .../associations/has_many_associations_test.rb | 50 ++++------------------ 3 files changed, 17 insertions(+), 50 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 1c3a6b56f3..f4e34657a5 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -418,15 +418,10 @@ module ActiveRecord end def method_missing(method, *args) - case method.to_s - when 'find_or_create' - return find(:first, :conditions => args.first) || create(args.first) - when /^find_or_create_by_(.*)$/ - rest = $1 - return send("find_by_#{rest}", *args) || - method_missing("create_by_#{rest}", *args) - when /^create_by_(.*)$/ - return create Hash[$1.split('_and_').zip(args)] + match = DynamicFinderMatch.match(method) + if match && match.creator? + attributes = match.attribute_names + return send(:"find_by_#{attributes.join('and')}", *args) || create(Hash[attributes.zip(args)]) end if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb index b39b291352..dfb8a3ba60 100644 --- a/activerecord/lib/active_record/dynamic_finder_match.rb +++ b/activerecord/lib/active_record/dynamic_finder_match.rb @@ -42,6 +42,10 @@ module ActiveRecord @finder == :first && !@instantiator.nil? end + def creator? + @finder == :first && @instantiator == :create + end + def bang? @bang end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 6218cdd344..6fe737a817 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -47,14 +47,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase Client.destroyed_client_ids.clear end - def test_create_by - person = Person.create! :first_name => 'tenderlove' - post = Post.find :first + def test_create_resets_cached_counters + person = Person.create!(:first_name => 'tenderlove') + post = Post.first assert_equal [], person.readers - assert_nil person.readers.find_by_post_id post.id + assert_nil person.readers.find_by_post_id(post.id) - reader = person.readers.create_by_post_id post.id + reader = person.readers.create(:post_id => post.id) assert_equal 1, person.readers.count assert_equal 1, person.readers.length @@ -62,13 +62,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal person, person.readers.first.person end - def test_create_by_multi + def test_find_or_create_by_resets_cached_counters person = Person.create! :first_name => 'tenderlove' - post = Post.find :first + post = Post.first assert_equal [], person.readers + assert_nil person.readers.find_by_post_id(post.id) - reader = person.readers.create_by_post_id_and_skimmer post.id, false + reader = person.readers.find_or_create_by_post_id(post.id) assert_equal 1, person.readers.count assert_equal 1, person.readers.length @@ -76,39 +77,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal person, person.readers.first.person end - def test_find_or_create_by - person = Person.create! :first_name => 'tenderlove' - post = Post.find :first - - assert_equal [], person.readers - assert_nil person.readers.find_by_post_id post.id - - reader = person.readers.find_or_create_by_post_id post.id - - assert_equal 1, person.readers.count - assert_equal 1, person.readers.length - assert_equal post, person.readers.first.post - assert_equal person, person.readers.first.person - end - - def test_find_or_create - person = Person.create! :first_name => 'tenderlove' - post = Post.find :first - - assert_equal [], person.readers - assert_nil person.readers.find(:first, :conditions => { - :post_id => post.id - }) - - reader = person.readers.find_or_create :post_id => post.id - - assert_equal 1, person.readers.count - assert_equal 1, person.readers.length - assert_equal post, person.readers.first.post - assert_equal person, person.readers.first.person - end - - def force_signal37_to_load_all_clients_of_firm companies(:first_firm).clients_of_firm.each {|f| } end -- cgit v1.2.3 From 36150c902b3253101aaa2e14ed31d8d8b9a2e0d2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 15:04:14 -0700 Subject: Let's initialize instance variables in the postgres adapter. --- .../connection_adapters/postgresql_adapter.rb | 3 +++ .../adapters/postgresql/postgresql_adapter_test.rb | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e213eae026..e9443fc237 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -218,6 +218,9 @@ module ActiveRecord # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil + @table_alias_length = nil + @postgresql_version = nil + connect @local_tz = execute('SHOW TIME ZONE').first["TimeZone"] end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb new file mode 100644 index 0000000000..7b72151b57 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -0,0 +1,17 @@ +require "cases/helper" + +module ActiveRecord + module ConnectionAdapters + class PostgreSQLAdapterTest < ActiveRecord::TestCase + def setup + @connection = ActiveRecord::Base.connection + end + + def test_table_alias_length + assert_nothing_raised do + @connection.table_alias_length + end + end + end + end +end -- cgit v1.2.3 From 51c9b666e634e2d9551410cbc393bfd83bba7856 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 15:10:45 -0700 Subject: PostgreSQLAdapter#query bail early if there is no column and dry up our conditionals --- .../active_record/connection_adapters/postgresql_adapter.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e9443fc237..cf0dc64d2b 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -311,14 +311,16 @@ module ActiveRecord # Quotes PostgreSQL-specific data types for SQL input. def quote(value, column = nil) #:nodoc: - if value.kind_of?(String) && column && column.type == :binary + return super unless column + + if value.kind_of?(String) && column.type == :binary "'#{escape_bytea(value)}'" - elsif value.kind_of?(String) && column && column.sql_type == 'xml' + elsif value.kind_of?(String) && column.sql_type == 'xml' "xml '#{quote_string(value)}'" - elsif value.kind_of?(Numeric) && column && column.sql_type == 'money' + elsif value.kind_of?(Numeric) && column.sql_type == 'money' # Not truly string input, so doesn't require (or allow) escape string syntax. "'#{value.to_s}'" - elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/ + elsif value.kind_of?(String) && column.sql_type =~ /^bit/ case value when /^[01]*$/ "B'#{value}'" # Bit-string notation -- cgit v1.2.3 From 8ba14c238e62e5f34846b4f82c5b787e33161511 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 15:18:48 -0700 Subject: PostgreSQLAdapter#query string interpolation calls to_s for us --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index cf0dc64d2b..121785bc15 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -319,7 +319,7 @@ module ActiveRecord "xml '#{quote_string(value)}'" elsif value.kind_of?(Numeric) && column.sql_type == 'money' # Not truly string input, so doesn't require (or allow) escape string syntax. - "'#{value.to_s}'" + "'#{value}'" elsif value.kind_of?(String) && column.sql_type =~ /^bit/ case value when /^[01]*$/ -- cgit v1.2.3 From 6a534455f66a24e807d75dccc847981fdf43fa18 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 15:44:15 -0700 Subject: remove useless ternary in PostgreSQL adapter --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 121785bc15..37e94502a4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -375,7 +375,7 @@ module ActiveRecord def supports_disable_referential_integrity?() #:nodoc: version = query("SHOW server_version")[0][0].split('.') - (version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false + version[0].to_i >= 8 && version[1].to_i >= 1 rescue return false end -- cgit v1.2.3 From 1db8ed0f4b134dfe6d1f3f3c440a536b2f7d2824 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 16:35:52 -0700 Subject: activerecord tests should inherit from ActiveRecord::TestCase --- activerecord/test/cases/adapters/postgresql/active_schema_test.rb | 2 +- activerecord/test/cases/i18n_test.rb | 2 +- activerecord/test/cases/invalid_date_test.rb | 2 +- activerecord/test/cases/log_subscriber_test.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb index f106e14319..e4746d4aa3 100644 --- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -1,6 +1,6 @@ require 'cases/helper' -class PostgresqlActiveSchemaTest < Test::Unit::TestCase +class PostgresqlActiveSchemaTest < ActiveRecord::TestCase def setup ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do alias_method :real_execute, :execute diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb index ae4dcfb81e..3287626378 100644 --- a/activerecord/test/cases/i18n_test.rb +++ b/activerecord/test/cases/i18n_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require 'models/topic' require 'models/reply' -class ActiveRecordI18nTests < Test::Unit::TestCase +class ActiveRecordI18nTests < ActiveRecord::TestCase def setup I18n.backend = I18n::Backend::Simple.new diff --git a/activerecord/test/cases/invalid_date_test.rb b/activerecord/test/cases/invalid_date_test.rb index 99af7d2986..2de50b224c 100644 --- a/activerecord/test/cases/invalid_date_test.rb +++ b/activerecord/test/cases/invalid_date_test.rb @@ -1,7 +1,7 @@ require 'cases/helper' require 'models/topic' -class InvalidDateTest < Test::Unit::TestCase +class InvalidDateTest < ActiveRecord::TestCase def test_assign_valid_dates valid_dates = [[2007, 11, 30], [1993, 2, 28], [2008, 2, 29]] diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 4aeae1fe45..cde383783b 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require "models/developer" require "active_support/log_subscriber/test_helper" -class LogSubscriberTest < ActiveSupport::TestCase +class LogSubscriberTest < ActiveRecord::TestCase include ActiveSupport::LogSubscriber::TestHelper def setup -- cgit v1.2.3 From d41a28bcca521222594a70d237935a4415a85230 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 17:09:14 -0700 Subject: use array intersection for dry / faster code --- activerecord/lib/active_record/fixtures.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 82270c56b3..a2328663fd 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -669,9 +669,8 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) end def timestamp_column_names - @timestamp_column_names ||= %w(created_at created_on updated_at updated_on).select do |name| - column_names.include?(name) - end + @timestamp_column_names ||= + %w(created_at created_on updated_at updated_on) & column_names end def inheritance_column_name -- cgit v1.2.3 From 955b26ac6e729d7197f9c5134ad7d438c08293c0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 17:11:52 -0700 Subject: primary_key_name already checks for the model_class --- activerecord/lib/active_record/fixtures.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index a2328663fd..5bf43b3a72 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -664,8 +664,8 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) end def has_primary_key_column? - @has_primary_key_column ||= model_class && primary_key_name && - model_class.columns.find { |c| c.name == primary_key_name } + @has_primary_key_column ||= primary_key_name && + model_class.columns.any? { |c| c.name == primary_key_name } end def timestamp_column_names -- cgit v1.2.3 From 3c300b31219f2af3ecd46ef22b04e5c5548db899 Mon Sep 17 00:00:00 2001 From: wycats Date: Wed, 14 Jul 2010 17:33:07 -0700 Subject: Style fixes --- activerecord/lib/active_record/base.rb | 2 +- .../lib/active_record/relation/query_methods.rb | 40 +++++++++++----------- activerecord/test/cases/base_test.rb | 4 ++- 3 files changed, 24 insertions(+), 22 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 19ccf75b00..400a0adbcf 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1471,7 +1471,7 @@ MSG # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false) # user.is_admin? # => true def attributes=(new_attributes, guard_protected_attributes = true) - return unless new_attributes.is_a?(Hash) + return unless new_attributes.is_a? Hash attributes = new_attributes.stringify_keys multi_parameter_attributes = [] diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0593897fa5..4692271266 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -11,84 +11,84 @@ module ActiveRecord def includes(*args) args.reject! { |a| a.blank? } - clone.tap {|r| r.includes_values += args if args.present? } + clone.tap { |r| r.includes_values += args if args.present? } end def eager_load(*args) - clone.tap {|r| r.eager_load_values += args if args.present? } + clone.tap { |r| r.eager_load_values += args if args.present? } end def preload(*args) - clone.tap {|r| r.preload_values += args if args.present? } + clone.tap { |r| r.preload_values += args if args.present? } end def select(*args) if block_given? - to_a.select {|*block_args| yield(*block_args) } + to_a.select { |*block_args| yield(*block_args) } else - clone.tap {|r| r.select_values += args if args.present? } + clone.tap { |r| r.select_values += args if args.present? } end end def group(*args) - clone.tap {|r| r.group_values += args if args.present? } + clone.tap { |r| r.group_values += args if args.present? } end def order(*args) - clone.tap {|r| r.order_values += args if args.present? } + clone.tap { |r| r.order_values += args if args.present? } end def reorder(*args) - clone.tap {|r| r.order_values = args if args.present? } + clone.tap { |r| r.order_values = args if args.present? } end def joins(*args) args.flatten! - clone.tap {|r| r.joins_values += args if args.present? } + clone.tap { |r| r.joins_values += args if args.present? } end def where(*args) value = build_where(*args) - clone.tap {|r| r.where_values += Array.wrap(value) if value.present? } + clone.tap { |r| r.where_values += Array.wrap(value) if value.present? } end def having(*args) value = build_where(*args) - clone.tap {|r| r.having_values += Array.wrap(value) if value.present? } + clone.tap { |r| r.having_values += Array.wrap(value) if value.present? } end def limit(value = true) - clone.tap {|r| r.limit_value = value } + clone.tap { |r| r.limit_value = value } end def offset(value = true) - clone.tap {|r| r.offset_value = value } + clone.tap { |r| r.offset_value = value } end def lock(locks = true) case locks when String, TrueClass, NilClass - clone.tap {|r| r.lock_value = locks || true } + clone.tap { |r| r.lock_value = locks || true } else - clone.tap {|r| r.lock_value = false } + clone.tap { |r| r.lock_value = false } end end def readonly(value = true) - clone.tap {|r| r.readonly_value = value } + clone.tap { |r| r.readonly_value = value } end def create_with(value = true) - clone.tap {|r| r.create_with_value = value } + clone.tap { |r| r.create_with_value = value } end def from(value = true) - clone.tap {|r| r.from_value = value } + clone.tap { |r| r.from_value = value } end def extending(*modules, &block) modules << Module.new(&block) if block_given? - clone.tap {|r| r.send(:apply_modules, *modules) } + clone.tap { |r| r.send(:apply_modules, *modules) } end def reverse_order @@ -230,7 +230,7 @@ module ActiveRecord @implicit_readonly = false # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array. # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array - if selects.all? {|s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) + if selects.all? { |s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) arel.project(*selects) else arel.project(selects.last) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 87f46d2992..a4cf5120e1 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -63,7 +63,9 @@ class BasicsTest < ActiveRecord::TestCase def test_set_attributes_without_hash topic = Topic.new - assert_nothing_raised { topic.attributes = '' } + assert_nothing_raised do + topic.attributes = '' + end end def test_integers_as_nil -- cgit v1.2.3 From 62dd1d8d23c843fa4839e47b9e67d84c15a61f35 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 14 Jul 2010 17:53:03 -0700 Subject: clean up ||= with an initialized variable and attr_reader --- activerecord/lib/active_record/associations.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 65daa8ffbe..9fe8d54f02 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1965,7 +1965,7 @@ module ActiveRecord end class JoinAssociation < JoinBase # :nodoc: - attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name + attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name, :join_class delegate :options, :klass, :through_reflection, :source_reflection, :to => :reflection def initialize(reflection, join_dependency, parent = nil) @@ -1982,6 +1982,7 @@ module ActiveRecord @parent_table_name = parent.active_record.table_name @aliased_table_name = aliased_table_name_for(table_name) @join = nil + @join_class = Arel::InnerJoin if reflection.macro == :has_and_belongs_to_many @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join") @@ -2004,10 +2005,6 @@ module ActiveRecord end end - def join_class - @join_class ||= Arel::InnerJoin - end - def with_join_class(join_class) @join_class = join_class self -- cgit v1.2.3 From 0c2c8934345ec8a3f2eb7896e252496167fdf652 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 15 Jul 2010 13:17:35 +0100 Subject: Revert "Style fixes" This reverts commit 3c300b31219f2af3ecd46ef22b04e5c5548db899. --- activerecord/lib/active_record/base.rb | 2 +- .../lib/active_record/relation/query_methods.rb | 40 +++++++++++----------- activerecord/test/cases/base_test.rb | 4 +-- 3 files changed, 22 insertions(+), 24 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 400a0adbcf..19ccf75b00 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1471,7 +1471,7 @@ MSG # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false) # user.is_admin? # => true def attributes=(new_attributes, guard_protected_attributes = true) - return unless new_attributes.is_a? Hash + return unless new_attributes.is_a?(Hash) attributes = new_attributes.stringify_keys multi_parameter_attributes = [] diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4692271266..0593897fa5 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -11,84 +11,84 @@ module ActiveRecord def includes(*args) args.reject! { |a| a.blank? } - clone.tap { |r| r.includes_values += args if args.present? } + clone.tap {|r| r.includes_values += args if args.present? } end def eager_load(*args) - clone.tap { |r| r.eager_load_values += args if args.present? } + clone.tap {|r| r.eager_load_values += args if args.present? } end def preload(*args) - clone.tap { |r| r.preload_values += args if args.present? } + clone.tap {|r| r.preload_values += args if args.present? } end def select(*args) if block_given? - to_a.select { |*block_args| yield(*block_args) } + to_a.select {|*block_args| yield(*block_args) } else - clone.tap { |r| r.select_values += args if args.present? } + clone.tap {|r| r.select_values += args if args.present? } end end def group(*args) - clone.tap { |r| r.group_values += args if args.present? } + clone.tap {|r| r.group_values += args if args.present? } end def order(*args) - clone.tap { |r| r.order_values += args if args.present? } + clone.tap {|r| r.order_values += args if args.present? } end def reorder(*args) - clone.tap { |r| r.order_values = args if args.present? } + clone.tap {|r| r.order_values = args if args.present? } end def joins(*args) args.flatten! - clone.tap { |r| r.joins_values += args if args.present? } + clone.tap {|r| r.joins_values += args if args.present? } end def where(*args) value = build_where(*args) - clone.tap { |r| r.where_values += Array.wrap(value) if value.present? } + clone.tap {|r| r.where_values += Array.wrap(value) if value.present? } end def having(*args) value = build_where(*args) - clone.tap { |r| r.having_values += Array.wrap(value) if value.present? } + clone.tap {|r| r.having_values += Array.wrap(value) if value.present? } end def limit(value = true) - clone.tap { |r| r.limit_value = value } + clone.tap {|r| r.limit_value = value } end def offset(value = true) - clone.tap { |r| r.offset_value = value } + clone.tap {|r| r.offset_value = value } end def lock(locks = true) case locks when String, TrueClass, NilClass - clone.tap { |r| r.lock_value = locks || true } + clone.tap {|r| r.lock_value = locks || true } else - clone.tap { |r| r.lock_value = false } + clone.tap {|r| r.lock_value = false } end end def readonly(value = true) - clone.tap { |r| r.readonly_value = value } + clone.tap {|r| r.readonly_value = value } end def create_with(value = true) - clone.tap { |r| r.create_with_value = value } + clone.tap {|r| r.create_with_value = value } end def from(value = true) - clone.tap { |r| r.from_value = value } + clone.tap {|r| r.from_value = value } end def extending(*modules, &block) modules << Module.new(&block) if block_given? - clone.tap { |r| r.send(:apply_modules, *modules) } + clone.tap {|r| r.send(:apply_modules, *modules) } end def reverse_order @@ -230,7 +230,7 @@ module ActiveRecord @implicit_readonly = false # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array. # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array - if selects.all? { |s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) + if selects.all? {|s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) arel.project(*selects) else arel.project(selects.last) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index a4cf5120e1..87f46d2992 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -63,9 +63,7 @@ class BasicsTest < ActiveRecord::TestCase def test_set_attributes_without_hash topic = Topic.new - assert_nothing_raised do - topic.attributes = '' - end + assert_nothing_raised { topic.attributes = '' } end def test_integers_as_nil -- cgit v1.2.3 From fcb230144b041a95c8e743921e22f8df56949993 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 15 Jul 2010 16:37:12 -0300 Subject: Bump up nokogiri, memcache-client, sqlite3-ruby, fcgi, mail and tzinfo --- activerecord/activerecord.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 5aea992801..ce10404feb 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -24,5 +24,5 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) s.add_dependency('arel', '~> 0.4.0') - s.add_dependency('tzinfo', '~> 0.3.16') + s.add_dependency('tzinfo', '~> 0.3.22') end -- cgit v1.2.3 From 1fcf4e8ecbe4db2443a88d1e06c06667cff5b762 Mon Sep 17 00:00:00 2001 From: David Genord II Date: Tue, 13 Jul 2010 18:14:26 -0400 Subject: JoinDependency#graft does not properly set parent join [#5124 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 2 +- .../test/cases/associations/cascaded_eager_loading_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9fe8d54f02..d67df64f59 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1761,7 +1761,7 @@ module ActiveRecord def graft(*associations) associations.each do |association| join_associations.detect {|a| association == a} || - build(association.reflection.name, association.find_parent_in(self), association.join_class) + build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_class) end self end diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 9c5dcc2ad9..67eb6403c8 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -46,6 +46,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').first end + def test_eager_associatoin_loading_with_join_for_count + authors = Author.joins(:special_posts).includes([:posts, :categorizations]) + + assert_nothing_raised { authors.count } + assert_queries(3) { authors.all } + end + def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id") assert_equal 2, authors.size -- cgit v1.2.3 From 130bf3c9edf89de78203c02c5f76f9ea2b7b46a5 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 15 Jul 2010 23:49:17 +0100 Subject: Fix data loading from the performance script --- activerecord/examples/performance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index f7d358337c..a985cfcb66 100644 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -58,7 +58,7 @@ end sqlfile = File.expand_path("../performance.sql", __FILE__) if File.exists?(sqlfile) - mysql_bin = %w[mysql mysql5].select { |bin| `which #{bin}`.length > 0 } + mysql_bin = %w[mysql mysql5].detect { |bin| `which #{bin}`.length > 0 } `#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}` else puts 'Generating data...' -- cgit v1.2.3 From 1f2a5199cb06b9898c9ba0a10350f4d522035a56 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 15 Jul 2010 08:57:24 -0400 Subject: moving persistence related tests to a new file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before refactoring test result from AR: 2291 tests, 7180 assertions, 0 failures, 0 errors After the refactoring test result from AR: 2291 tests, 7180 assertions, 0 failures, 0 errors Signed-off-by: José Valim --- activerecord/test/cases/base_test.rb | 327 ------------------------- activerecord/test/cases/persistence_test.rb | 357 ++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+), 327 deletions(-) create mode 100644 activerecord/test/cases/persistence_test.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 87f46d2992..831dd446ad 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -161,48 +161,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes end - def test_create - topic = Topic.new - topic.title = "New Topic" - topic.save - topic_reloaded = Topic.find(topic.id) - assert_equal("New Topic", topic_reloaded.title) - end - - def test_save! - topic = Topic.new(:title => "New Topic") - assert topic.save! - - reply = WrongReply.new - assert_raise(ActiveRecord::RecordInvalid) { reply.save! } - end - - def test_save_null_string_attributes - topic = Topic.find(1) - topic.attributes = { "title" => "null", "author_name" => "null" } - topic.save! - topic.reload - assert_equal("null", topic.title) - assert_equal("null", topic.author_name) - end - - def test_save_nil_string_attributes - topic = Topic.find(1) - topic.title = nil - topic.save! - topic.reload - assert_nil topic.title - end - - def test_save_for_record_with_only_primary_key - minimalistic = Minimalistic.new - assert_nothing_raised { minimalistic.save } - end - - def test_save_for_record_with_only_primary_key_that_is_provided - assert_nothing_raised { Minimalistic.create!(:id => 2) } - end - def test_hashes_not_mangled new_topic = { :title => "New Topic" } new_topic_values = { :title => "AnotherTopic" } @@ -214,78 +172,12 @@ class BasicsTest < ActiveRecord::TestCase assert_equal new_topic_values[:title], topic.title end - def test_create_many - topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) - assert_equal 2, topics.size - assert_equal "first", topics.first.title - end - - def test_create_columns_not_equal_attributes - topic = Topic.new - topic.title = 'Another New Topic' - topic.send :write_attribute, 'does_not_exist', 'test' - assert_nothing_raised { topic.save } - end - def test_create_through_factory topic = Topic.create("title" => "New Topic") topicReloaded = Topic.find(topic.id) assert_equal(topic, topicReloaded) end - def test_create_through_factory_with_block - topic = Topic.create("title" => "New Topic") do |t| - t.author_name = "David" - end - topicReloaded = Topic.find(topic.id) - assert_equal("New Topic", topic.title) - assert_equal("David", topic.author_name) - end - - def test_create_many_through_factory_with_block - topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t| - t.author_name = "David" - end - assert_equal 2, topics.size - topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id) - assert_equal "first", topic1.title - assert_equal "David", topic1.author_name - assert_equal "second", topic2.title - assert_equal "David", topic2.author_name - end - - def test_update - topic = Topic.new - topic.title = "Another New Topic" - topic.written_on = "2003-12-12 23:23:00" - topic.save - topicReloaded = Topic.find(topic.id) - assert_equal("Another New Topic", topicReloaded.title) - - topicReloaded.title = "Updated topic" - topicReloaded.save - - topicReloadedAgain = Topic.find(topic.id) - - assert_equal("Updated topic", topicReloadedAgain.title) - end - - def test_update_columns_not_equal_attributes - topic = Topic.new - topic.title = "Still another topic" - topic.save - - topicReloaded = Topic.find(topic.id) - topicReloaded.title = "A New Topic" - topicReloaded.send :write_attribute, 'does_not_exist', 'test' - assert_nothing_raised { topicReloaded.save } - end - - def test_update_for_record_with_only_primary_key - minimalistic = minimalistics(:first) - assert_nothing_raised { minimalistic.save } - end - def test_write_attribute topic = Topic.new topic.send(:write_attribute, :title, "Still another topic") @@ -393,7 +285,6 @@ class BasicsTest < ActiveRecord::TestCase assert !object.int_value? end - def test_non_attribute_access_and_assignment topic = Topic.new assert !topic.respond_to?("mumbo") @@ -497,29 +388,6 @@ class BasicsTest < ActiveRecord::TestCase assert topic.instance_variable_get("@custom_approved") end - def test_delete - topic = Topic.find(1) - assert_equal topic, topic.delete, 'topic.delete did not return self' - assert topic.frozen?, 'topic not frozen after delete' - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } - end - - def test_delete_doesnt_run_callbacks - Topic.find(1).delete - assert_not_nil Topic.find(2) - end - - def test_destroy - topic = Topic.find(1) - assert_equal topic, topic.destroy, 'topic.destroy did not return self' - assert topic.frozen?, 'topic not frozen after destroy' - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } - end - - def test_record_not_found_exception - assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) } - end - def test_initialize_with_attributes topic = Topic.new({ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23" @@ -688,33 +556,6 @@ class BasicsTest < ActiveRecord::TestCase assert Topic.find(2).approved? end - def test_update_all - assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") - assert_equal "bulk updated!", Topic.find(1).content - assert_equal "bulk updated!", Topic.find(2).content - - assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!']) - assert_equal "bulk updated again!", Topic.find(1).content - assert_equal "bulk updated again!", Topic.find(2).content - - assert_equal Topic.count, Topic.update_all(['content = ?', nil]) - assert_nil Topic.find(1).content - end - - def test_update_all_with_hash - assert_not_nil Topic.find(1).last_read - assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil) - assert_equal "bulk updated with hash!", Topic.find(1).content - assert_equal "bulk updated with hash!", Topic.find(2).content - assert_nil Topic.find(1).last_read - assert_nil Topic.find(2).last_read - end - - def test_update_all_with_non_standard_table_name - assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1]) - assert_equal 0, WarehouseThing.find(1).value - end - if current_adapter?(:MysqlAdapter) def test_update_all_with_order_and_limit assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC') @@ -861,119 +702,7 @@ class BasicsTest < ActiveRecord::TestCase assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ] end - def test_delete_new_record - client = Client.new - client.delete - assert client.frozen? - end - - def test_delete_record_with_associations - client = Client.find(3) - client.delete - assert client.frozen? - assert_kind_of Firm, client.firm - assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } - end - - def test_destroy_new_record - client = Client.new - client.destroy - assert client.frozen? - end - - def test_destroy_record_with_associations - client = Client.find(3) - client.destroy - assert client.frozen? - assert_kind_of Firm, client.firm - assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } - end - - def test_update_attribute - assert !Topic.find(1).approved? - Topic.find(1).update_attribute("approved", true) - assert Topic.find(1).approved? - - Topic.find(1).update_attribute(:approved, false) - assert !Topic.find(1).approved? - end - - def test_update_attribute_with_one_changed_and_one_updated - t = Topic.order('id').limit(1).first - title, author_name = t.title, t.author_name - t.author_name = 'John' - t.update_attribute(:title, 'super_title') - assert_equal 'John', t.author_name - assert_equal 'super_title', t.title - assert t.changed?, "topic should have changed" - assert t.author_name_changed?, "author_name should have changed" - assert !t.title_changed?, "title should not have changed" - assert_nil t.title_change, 'title change should be nil' - assert_equal ['author_name'], t.changed - - t.reload - assert_equal 'David', t.author_name - assert_equal 'super_title', t.title - end - - def test_update_attribute_with_one_updated - t = Topic.first - title = t.title - t.update_attribute(:title, 'super_title') - assert_equal 'super_title', t.title - assert !t.changed?, "topic should not have changed" - assert !t.title_changed?, "title should not have changed" - assert_nil t.title_change, 'title change should be nil' - - t.reload - assert_equal 'super_title', t.title - end - - def test_update_attribute_for_udpated_at_on - developer = Developer.find(1) - updated_at = developer.updated_at - developer.update_attribute(:salary, 80001) - assert_not_equal updated_at, developer.updated_at - developer.reload - assert_not_equal updated_at, developer.updated_at - end - - def test_update_attributes - topic = Topic.find(1) - assert !topic.approved? - assert_equal "The First Topic", topic.title - - topic.update_attributes("approved" => true, "title" => "The First Topic Updated") - topic.reload - assert topic.approved? - assert_equal "The First Topic Updated", topic.title - - topic.update_attributes(:approved => false, :title => "The First Topic") - topic.reload - assert !topic.approved? - assert_equal "The First Topic", topic.title - end - - def test_update_attributes! - Reply.validates_presence_of(:title) - reply = Reply.find(2) - assert_equal "The Second Topic of the day", reply.title - assert_equal "Have a nice day", reply.content - reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening") - reply.reload - assert_equal "The Second Topic of the day updated", reply.title - assert_equal "Have a nice evening", reply.content - - reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day") - reply.reload - assert_equal "The Second Topic of the day", reply.title - assert_equal "Have a nice day", reply.content - - assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") } - ensure - Reply.reset_callbacks(:validate) - end def test_readonly_attributes assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes @@ -1234,35 +963,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal false, Topic.find(1).new_record? end - def test_destroyed_returns_boolean - developer = Developer.first - assert_equal false, developer.destroyed? - developer.destroy - assert_equal true, developer.destroyed? - - developer = Developer.last - assert_equal false, developer.destroyed? - developer.delete - assert_equal true, developer.destroyed? - end - - def test_persisted_returns_boolean - developer = Developer.new(:name => "Jose") - assert_equal false, developer.persisted? - developer.save! - assert_equal true, developer.persisted? - - developer = Developer.first - assert_equal true, developer.persisted? - developer.destroy - assert_equal false, developer.persisted? - - developer = Developer.last - assert_equal true, developer.persisted? - developer.delete - assert_equal false, developer.persisted? - end - def test_clone topic = Topic.find(1) cloned_topic = nil @@ -1605,24 +1305,6 @@ class BasicsTest < ActiveRecord::TestCase end end - def test_class_level_destroy - should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") - Topic.find(1).replies << should_be_destroyed_reply - - Topic.destroy(1) - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } - assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) } - end - - def test_class_level_delete - should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") - Topic.find(1).replies << should_be_destroyed_reply - - Topic.delete(1) - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } - assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) } - end - def test_increment_attribute assert_equal 50, accounts(:signals37).credit_limit accounts(:signals37).increment! :credit_limit @@ -2235,15 +1917,6 @@ class BasicsTest < ActiveRecord::TestCase ActiveRecord::Base.logger = original_logger end - def test_create_with_custom_timestamps - custom_datetime = 1.hour.ago.beginning_of_day - - %w(created_at created_on updated_at updated_on).each do |attribute| - parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime) - assert_equal custom_datetime, parrot[attribute] - end - end - def test_dup assert !Minimalistic.new.freeze.dup.frozen? end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb new file mode 100644 index 0000000000..fbfdd7b041 --- /dev/null +++ b/activerecord/test/cases/persistence_test.rb @@ -0,0 +1,357 @@ +require "cases/helper" +require 'models/post' +require 'models/author' +require 'models/topic' +require 'models/reply' +require 'models/category' +require 'models/company' +require 'models/customer' +require 'models/developer' +require 'models/project' +require 'models/default' +require 'models/auto_id' +require 'models/column_name' +require 'models/subscriber' +require 'models/keyboard' +require 'models/comment' +require 'models/minimalistic' +require 'models/warehouse_thing' +require 'models/parrot' +require 'models/loose_person' +require 'rexml/document' +require 'active_support/core_ext/exception' + +class PersistencesTest < ActiveRecord::TestCase + + fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts + + def test_create + topic = Topic.new + topic.title = "New Topic" + topic.save + topic_reloaded = Topic.find(topic.id) + assert_equal("New Topic", topic_reloaded.title) + end + + def test_save! + topic = Topic.new(:title => "New Topic") + assert topic.save! + + reply = WrongReply.new + assert_raise(ActiveRecord::RecordInvalid) { reply.save! } + end + + def test_save_null_string_attributes + topic = Topic.find(1) + topic.attributes = { "title" => "null", "author_name" => "null" } + topic.save! + topic.reload + assert_equal("null", topic.title) + assert_equal("null", topic.author_name) + end + + def test_save_nil_string_attributes + topic = Topic.find(1) + topic.title = nil + topic.save! + topic.reload + assert_nil topic.title + end + + def test_save_for_record_with_only_primary_key + minimalistic = Minimalistic.new + assert_nothing_raised { minimalistic.save } + end + + def test_save_for_record_with_only_primary_key_that_is_provided + assert_nothing_raised { Minimalistic.create!(:id => 2) } + end + + def test_create_many + topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) + assert_equal 2, topics.size + assert_equal "first", topics.first.title + end + + def test_create_columns_not_equal_attributes + topic = Topic.new + topic.title = 'Another New Topic' + topic.send :write_attribute, 'does_not_exist', 'test' + assert_nothing_raised { topic.save } + end + + def test_create_through_factory_with_block + topic = Topic.create("title" => "New Topic") do |t| + t.author_name = "David" + end + topicReloaded = Topic.find(topic.id) + assert_equal("New Topic", topic.title) + assert_equal("David", topic.author_name) + end + + def test_create_many_through_factory_with_block + topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t| + t.author_name = "David" + end + assert_equal 2, topics.size + topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id) + assert_equal "first", topic1.title + assert_equal "David", topic1.author_name + assert_equal "second", topic2.title + assert_equal "David", topic2.author_name + end + + def test_update + topic = Topic.new + topic.title = "Another New Topic" + topic.written_on = "2003-12-12 23:23:00" + topic.save + topicReloaded = Topic.find(topic.id) + assert_equal("Another New Topic", topicReloaded.title) + + topicReloaded.title = "Updated topic" + topicReloaded.save + + topicReloadedAgain = Topic.find(topic.id) + + assert_equal("Updated topic", topicReloadedAgain.title) + end + + def test_update_columns_not_equal_attributes + topic = Topic.new + topic.title = "Still another topic" + topic.save + + topicReloaded = Topic.find(topic.id) + topicReloaded.title = "A New Topic" + topicReloaded.send :write_attribute, 'does_not_exist', 'test' + assert_nothing_raised { topicReloaded.save } + end + + def test_update_for_record_with_only_primary_key + minimalistic = minimalistics(:first) + assert_nothing_raised { minimalistic.save } + end + + def test_delete + topic = Topic.find(1) + assert_equal topic, topic.delete, 'topic.delete did not return self' + assert topic.frozen?, 'topic not frozen after delete' + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } + end + + def test_delete_doesnt_run_callbacks + Topic.find(1).delete + assert_not_nil Topic.find(2) + end + + def test_destroy + topic = Topic.find(1) + assert_equal topic, topic.destroy, 'topic.destroy did not return self' + assert topic.frozen?, 'topic not frozen after destroy' + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } + end + + def test_record_not_found_exception + assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) } + end + + def test_update_all + assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") + assert_equal "bulk updated!", Topic.find(1).content + assert_equal "bulk updated!", Topic.find(2).content + + assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!']) + assert_equal "bulk updated again!", Topic.find(1).content + assert_equal "bulk updated again!", Topic.find(2).content + + assert_equal Topic.count, Topic.update_all(['content = ?', nil]) + assert_nil Topic.find(1).content + end + + def test_update_all_with_hash + assert_not_nil Topic.find(1).last_read + assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil) + assert_equal "bulk updated with hash!", Topic.find(1).content + assert_equal "bulk updated with hash!", Topic.find(2).content + assert_nil Topic.find(1).last_read + assert_nil Topic.find(2).last_read + end + + def test_update_all_with_non_standard_table_name + assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1]) + assert_equal 0, WarehouseThing.find(1).value + end + + def test_delete_new_record + client = Client.new + client.delete + assert client.frozen? + end + + def test_delete_record_with_associations + client = Client.find(3) + client.delete + assert client.frozen? + assert_kind_of Firm, client.firm + assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } + end + + def test_destroy_new_record + client = Client.new + client.destroy + assert client.frozen? + end + + def test_destroy_record_with_associations + client = Client.find(3) + client.destroy + assert client.frozen? + assert_kind_of Firm, client.firm + assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } + end + + def test_update_attribute + assert !Topic.find(1).approved? + Topic.find(1).update_attribute("approved", true) + assert Topic.find(1).approved? + + Topic.find(1).update_attribute(:approved, false) + assert !Topic.find(1).approved? + end + + def test_update_attribute_with_one_changed_and_one_updated + t = Topic.order('id').limit(1).first + title, author_name = t.title, t.author_name + t.author_name = 'John' + t.update_attribute(:title, 'super_title') + assert_equal 'John', t.author_name + assert_equal 'super_title', t.title + assert t.changed?, "topic should have changed" + assert t.author_name_changed?, "author_name should have changed" + assert !t.title_changed?, "title should not have changed" + assert_nil t.title_change, 'title change should be nil' + assert_equal ['author_name'], t.changed + + t.reload + assert_equal 'David', t.author_name + assert_equal 'super_title', t.title + end + + def test_update_attribute_with_one_updated + t = Topic.first + title = t.title + t.update_attribute(:title, 'super_title') + assert_equal 'super_title', t.title + assert !t.changed?, "topic should not have changed" + assert !t.title_changed?, "title should not have changed" + assert_nil t.title_change, 'title change should be nil' + + t.reload + assert_equal 'super_title', t.title + end + + def test_update_attribute_for_udpated_at_on + developer = Developer.find(1) + updated_at = developer.updated_at + developer.update_attribute(:salary, 80001) + assert_not_equal updated_at, developer.updated_at + developer.reload + assert_not_equal updated_at, developer.updated_at + end + + + def test_update_attributes + topic = Topic.find(1) + assert !topic.approved? + assert_equal "The First Topic", topic.title + + topic.update_attributes("approved" => true, "title" => "The First Topic Updated") + topic.reload + assert topic.approved? + assert_equal "The First Topic Updated", topic.title + + topic.update_attributes(:approved => false, :title => "The First Topic") + topic.reload + assert !topic.approved? + assert_equal "The First Topic", topic.title + end + + def test_update_attributes! + Reply.validates_presence_of(:title) + reply = Reply.find(2) + assert_equal "The Second Topic of the day", reply.title + assert_equal "Have a nice day", reply.content + + reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening") + reply.reload + assert_equal "The Second Topic of the day updated", reply.title + assert_equal "Have a nice evening", reply.content + + reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day") + reply.reload + assert_equal "The Second Topic of the day", reply.title + assert_equal "Have a nice day", reply.content + + assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") } + ensure + Reply.reset_callbacks(:validate) + end + + def test_destroyed_returns_boolean + developer = Developer.first + assert_equal false, developer.destroyed? + developer.destroy + assert_equal true, developer.destroyed? + + developer = Developer.last + assert_equal false, developer.destroyed? + developer.delete + assert_equal true, developer.destroyed? + end + + def test_persisted_returns_boolean + developer = Developer.new(:name => "Jose") + assert_equal false, developer.persisted? + developer.save! + assert_equal true, developer.persisted? + + developer = Developer.first + assert_equal true, developer.persisted? + developer.destroy + assert_equal false, developer.persisted? + + developer = Developer.last + assert_equal true, developer.persisted? + developer.delete + assert_equal false, developer.persisted? + end + + def test_class_level_destroy + should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") + Topic.find(1).replies << should_be_destroyed_reply + + Topic.destroy(1) + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } + assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) } + end + + def test_class_level_delete + should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") + Topic.find(1).replies << should_be_destroyed_reply + + Topic.delete(1) + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } + assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) } + end + + def test_create_with_custom_timestamps + custom_datetime = 1.hour.ago.beginning_of_day + + %w(created_at created_on updated_at updated_on).each do |attribute| + parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime) + assert_equal custom_datetime, parrot[attribute] + end + end + +end -- cgit v1.2.3 From 1b2824b6591deee1211f799dc5ce71a7a718953b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 16 Jul 2010 09:13:51 -0700 Subject: fixing typeo. thanks bamnet! --- activerecord/test/cases/associations/cascaded_eager_loading_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 67eb6403c8..f5d59c9a43 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -46,7 +46,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').first end - def test_eager_associatoin_loading_with_join_for_count + def test_eager_association_loading_with_join_for_count authors = Author.joins(:special_posts).includes([:posts, :categorizations]) assert_nothing_raised { authors.count } -- cgit v1.2.3 From 79d9e0f15522c981fcd1005cec9d441ff96725f5 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 17 Jul 2010 02:35:02 +0800 Subject: Makes this test don't trust on the speed of execution --- activerecord/test/cases/persistence_test.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index fbfdd7b041..4ea5df0945 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -253,14 +253,15 @@ class PersistencesTest < ActiveRecord::TestCase def test_update_attribute_for_udpated_at_on developer = Developer.find(1) - updated_at = developer.updated_at + prev_month = Time.now.prev_month + developer.update_attribute(:updated_at, prev_month) + assert_equal prev_month, developer.updated_at developer.update_attribute(:salary, 80001) - assert_not_equal updated_at, developer.updated_at + assert_not_equal prev_month, developer.updated_at developer.reload - assert_not_equal updated_at, developer.updated_at + assert_not_equal prev_month, developer.updated_at end - def test_update_attributes topic = Topic.find(1) assert !topic.approved? -- cgit v1.2.3 From b952470cc228ce4438226e180454bb141063b0ca Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 16 Jul 2010 11:15:55 -0700 Subject: use an attr_reader for performance --- .../active_record/connection_adapters/abstract/connection_pool.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index c2d79a421d..5dcc625ade 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -290,14 +290,12 @@ module ActiveRecord # ActiveRecord::Base.connection_handler. Active Record models use this to # determine that connection pool that they should use. class ConnectionHandler + attr_reader :connection_pools + def initialize(pools = {}) @connection_pools = pools end - def connection_pools - @connection_pools ||= {} - end - def establish_connection(name, spec) @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec) end -- cgit v1.2.3 From 04ef434b21f2143c3bea330d6861eac3696666ea Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 16 Jul 2010 11:26:02 -0700 Subject: only test for existence of +pool+ once --- .../active_record/connection_adapters/abstract/connection_pool.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 5dcc625ade..1b4ee0368e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -343,9 +343,11 @@ module ActiveRecord # re-establishing the connection. def remove_connection(klass) pool = @connection_pools[klass.name] + return nil unless pool + @connection_pools.delete_if { |key, value| value == pool } - pool.disconnect! if pool - pool.spec.config if pool + pool.disconnect! + pool.spec.config end def retrieve_connection_pool(klass) -- cgit v1.2.3 From 2244bb00131f2f5ec4d1912f7dd6378ba0f3b58f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 16 Jul 2010 13:50:58 -0700 Subject: fixing whitespace errors --- activerecord/test/cases/connection_pool_test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index cc9b2a45f4..c535119972 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -5,21 +5,21 @@ class ConnectionManagementTest < ActiveRecord::TestCase @env = {} @app = stub('App') @management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app) - + @connections_cleared = false ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true } end - + test "clears active connections after each call" do @app.expects(:call).with(@env) @management.call(@env) assert @connections_cleared end - + test "doesn't clear active connections when running in a test case" do @env['rack.test'] = true @app.expects(:call).with(@env) @management.call(@env) assert !@connections_cleared end -end \ No newline at end of file +end -- cgit v1.2.3 From 7ba54ff71177e97cac16ec37fd1cb562c4bbb2bb Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 16 Jul 2010 13:54:14 -0700 Subject: fixing test file name --- .../test/cases/connection_management_test.rb | 25 ++++++++++++++++++++++ activerecord/test/cases/connection_pool_test.rb | 25 ---------------------- 2 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 activerecord/test/cases/connection_management_test.rb delete mode 100644 activerecord/test/cases/connection_pool_test.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb new file mode 100644 index 0000000000..c535119972 --- /dev/null +++ b/activerecord/test/cases/connection_management_test.rb @@ -0,0 +1,25 @@ +require "cases/helper" + +class ConnectionManagementTest < ActiveRecord::TestCase + def setup + @env = {} + @app = stub('App') + @management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app) + + @connections_cleared = false + ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true } + end + + test "clears active connections after each call" do + @app.expects(:call).with(@env) + @management.call(@env) + assert @connections_cleared + end + + test "doesn't clear active connections when running in a test case" do + @env['rack.test'] = true + @app.expects(:call).with(@env) + @management.call(@env) + assert !@connections_cleared + end +end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb deleted file mode 100644 index c535119972..0000000000 --- a/activerecord/test/cases/connection_pool_test.rb +++ /dev/null @@ -1,25 +0,0 @@ -require "cases/helper" - -class ConnectionManagementTest < ActiveRecord::TestCase - def setup - @env = {} - @app = stub('App') - @management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app) - - @connections_cleared = false - ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true } - end - - test "clears active connections after each call" do - @app.expects(:call).with(@env) - @management.call(@env) - assert @connections_cleared - end - - test "doesn't clear active connections when running in a test case" do - @env['rack.test'] = true - @app.expects(:call).with(@env) - @management.call(@env) - assert !@connections_cleared - end -end -- cgit v1.2.3 From 8d17f533187a791b4a42f45542c56c5f965e6fad Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 16 Jul 2010 14:39:40 -0700 Subject: adding a test for clearing stale connections --- activerecord/test/cases/connection_pool_test.rb | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 activerecord/test/cases/connection_pool_test.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb new file mode 100644 index 0000000000..82b3c36ed2 --- /dev/null +++ b/activerecord/test/cases/connection_pool_test.rb @@ -0,0 +1,31 @@ +require "cases/helper" + +module ActiveRecord + module ConnectionAdapters + class ConnectionPoolTest < ActiveRecord::TestCase + def test_clear_stale_cached_connections! + pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec + + threads = [ + Thread.new { pool.connection }, + Thread.new { pool.connection }] + + threads.map { |t| t.join } + + pool.extend Module.new { + attr_accessor :checkins + def checkin conn + @checkins << conn + conn.object_id + end + } + pool.checkins = [] + + cleared_threads = pool.clear_stale_cached_connections! + assert((cleared_threads - threads.map { |x| x.object_id }).empty?, + "threads should have been removed") + assert_equal pool.checkins.length, threads.length + end + end + end +end -- cgit v1.2.3 From 42be67e8d4470b0bdc6ba62fceaa6cdb1c21a097 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 16 Jul 2010 14:48:50 -0700 Subject: unfactoring clear_stale_cached_connections! --- .../abstract/connection_pool.rb | 23 +++++++--------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 1b4ee0368e..9d0251dda3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -161,8 +161,13 @@ module ActiveRecord # Return any checked-out connections back to the pool by threads that # are no longer alive. def clear_stale_cached_connections! - remove_stale_cached_threads!(@reserved_connections) do |name, conn| - checkin conn + keys = @reserved_connections.keys - Thread.list.find_all { |t| + t.alive? + }.map { |thread| thread.object_id } + + keys.each do |key| + checkin @reserved_connections[key] + @reserved_connections.delete(key) end end @@ -232,20 +237,6 @@ module ActiveRecord Thread.current.object_id end - # Remove stale threads from the cache. - def remove_stale_cached_threads!(cache, &block) - keys = Set.new(cache.keys) - - Thread.list.each do |thread| - keys.delete(thread.object_id) if thread.alive? - end - keys.each do |key| - next unless cache.has_key?(key) - block.call(key, cache[key]) - cache.delete(key) - end - end - def checkout_new_connection c = new_connection @connections << c -- cgit v1.2.3 From fa98eca75bd8666719bf3d061c87638850a20fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 17 Jul 2010 10:59:41 +0200 Subject: Add console hook to force ActiveRecord::Base to be loaded when console starts avoiding reference loops. --- activerecord/lib/active_record/railtie.rb | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index eff51a7c87..78fdb77216 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -22,6 +22,12 @@ module ActiveRecord load "active_record/railties/databases.rake" end + # When loading console, force ActiveRecord to be loaded to avoid cross + # references when loading a constant for the first time. + console do + ActiveRecord::Base + end + initializer "active_record.initialize_timezone" do ActiveSupport.on_load(:active_record) do self.time_zone_aware_attributes = true -- cgit v1.2.3 From e210895ba95e498b9debbf43a3e5ae588bca81f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 18 Jul 2010 11:01:32 +0200 Subject: Avoid uneeded queries in session stores if sid is not given. --- activerecord/lib/active_record/session_store.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index df2f429c5d..7ea7fb5c51 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -294,7 +294,6 @@ module ActiveRecord private def get_session(env, sid) Base.silence do - sid ||= generate_sid session = find_session(sid) env[SESSION_RECORD_KEY] = session [sid, session.data] -- cgit v1.2.3 From 4a0d7c1a439c6ad8d35bf514761824e51fa07df2 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Wed, 14 Jul 2010 04:39:54 -0400 Subject: save on parent should not cascade to child unless child changed [#3353 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/autosave_association.rb | 4 +++- activerecord/test/cases/autosave_association_test.rb | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 7517896235..6af384367f 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -372,7 +372,9 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - saved = association.save(:validate => !autosave) if association.new_record? || autosave + if association.new_record? || ( autosave && association.changed? ) + saved = association.save(:validate => !autosave) + end if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 3b89c12a3f..48479bb429 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -632,6 +632,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent + #association save method only trigged when association is changed + @ship.pirate.catchphrase = "new catch phrase" # Stub the save method of the @ship.pirate instance to destroy and then raise an exception class << @ship.pirate def save(*args) @@ -880,6 +882,22 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?") end + def test_should_not_call_belongs_to_after_save_callbacks_if_no_changes + @ship.attributes = { :name => "Titanic", :pirate_attributes => {:id => @pirate.id} } + #here there are no changes to pirate so if save on ship causes save on pirate + #this callback will fail pirate save.(pirate save shouldn't happen) + @ship.pirate.cancel_save_from_callback = true + @ship.save + assert_equal 'Titanic', @ship.reload.name + end + + def test_should_call_belongs_to_save_if_belongs_to_has_changes + @ship.attributes = { :name => "Titanic", :pirate_attributes => { :catchphrase => 'Jack', :id => @pirate.id} } + @ship.save + assert_equal 'Titanic', @ship.reload.name + assert_equal 'Jack', @pirate.reload.catchphrase + end + def test_should_still_work_without_an_associated_model @pirate.destroy @ship.reload.name = "The Vile Insanity" -- cgit v1.2.3 From df6aa8e246bed1961ee042aa9ab8a5209e2ce7f3 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:18:50 -0400 Subject: removing extra whitespaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/aggregations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb index 74588b4f47..9e285e57dc 100644 --- a/activerecord/test/cases/aggregations_test.rb +++ b/activerecord/test/cases/aggregations_test.rb @@ -125,7 +125,7 @@ class OverridingAggregationsTest < ActiveRecord::TestCase class Name; end class DifferentName; end - class Person < ActiveRecord::Base + class Person < ActiveRecord::Base composed_of :composed_of, :mapping => %w(person_first_name first_name) end -- cgit v1.2.3 From b58e1c52f75130a237b53c0e488341832487dd54 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:19:29 -0400 Subject: fixing typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/ar_schema_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 665c387d5d..588adc38e3 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -28,7 +28,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal 7, ActiveRecord::Migrator::current_version end - def test_schema_raises_an_error_for_invalid_column_ntype + def test_schema_raises_an_error_for_invalid_column_type assert_raise NoMethodError do ActiveRecord::Schema.define(:version => 8) do create_table :vegetables do |t| -- cgit v1.2.3 From 6caf943ace63c5d53ad95f4835ea58caea7eb22e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:21:05 -0400 Subject: primary_keys_test reads better than pk_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/pk_test.rb | 139 --------------------------- activerecord/test/cases/primary_keys_test.rb | 139 +++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 139 deletions(-) delete mode 100644 activerecord/test/cases/pk_test.rb create mode 100644 activerecord/test/cases/primary_keys_test.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/pk_test.rb b/activerecord/test/cases/pk_test.rb deleted file mode 100644 index 73f4b3848c..0000000000 --- a/activerecord/test/cases/pk_test.rb +++ /dev/null @@ -1,139 +0,0 @@ -require "cases/helper" -require 'models/topic' -require 'models/reply' -require 'models/subscriber' -require 'models/movie' -require 'models/keyboard' -require 'models/mixed_case_monkey' - -class PrimaryKeysTest < ActiveRecord::TestCase - fixtures :topics, :subscribers, :movies, :mixed_case_monkeys - - def test_to_key_with_default_primary_key - topic = Topic.new - assert topic.to_key.nil? - topic = Topic.find(1) - assert_equal topic.to_key, [1] - end - - def test_to_key_with_customized_primary_key - keyboard = Keyboard.new - assert_nil keyboard.to_key - keyboard.save - assert_equal keyboard.to_key, [keyboard.id] - end - - def test_to_key_with_primary_key_after_destroy - topic = Topic.find(1) - topic.destroy - assert_equal topic.to_key, [1] - end - - def test_integer_key - topic = Topic.find(1) - assert_equal(topics(:first).author_name, topic.author_name) - topic = Topic.find(2) - assert_equal(topics(:second).author_name, topic.author_name) - - topic = Topic.new - topic.title = "New Topic" - assert_nil topic.id - assert_nothing_raised { topic.save! } - id = topic.id - - topicReloaded = Topic.find(id) - assert_equal("New Topic", topicReloaded.title) - end - - def test_customized_primary_key_auto_assigns_on_save - Keyboard.delete_all - keyboard = Keyboard.new(:name => 'HHKB') - assert_nothing_raised { keyboard.save! } - assert_equal keyboard.id, Keyboard.find_by_name('HHKB').id - end - - def test_customized_primary_key_can_be_get_before_saving - keyboard = Keyboard.new - assert_nil keyboard.id - assert_nothing_raised { assert_nil keyboard.key_number } - end - - def test_customized_string_primary_key_settable_before_save - subscriber = Subscriber.new - assert_nothing_raised { subscriber.id = 'webster123' } - assert_equal 'webster123', subscriber.id - assert_equal 'webster123', subscriber.nick - end - - def test_string_key - subscriber = Subscriber.find(subscribers(:first).nick) - assert_equal(subscribers(:first).name, subscriber.name) - subscriber = Subscriber.find(subscribers(:second).nick) - assert_equal(subscribers(:second).name, subscriber.name) - - subscriber = Subscriber.new - subscriber.id = "jdoe" - assert_equal("jdoe", subscriber.id) - subscriber.name = "John Doe" - assert_nothing_raised { subscriber.save! } - assert_equal("jdoe", subscriber.id) - - subscriberReloaded = Subscriber.find("jdoe") - assert_equal("John Doe", subscriberReloaded.name) - end - - def test_find_with_more_than_one_string_key - assert_equal 2, Subscriber.find(subscribers(:first).nick, subscribers(:second).nick).length - end - - def test_primary_key_prefix - ActiveRecord::Base.primary_key_prefix_type = :table_name - Topic.reset_primary_key - assert_equal "topicid", Topic.primary_key - - ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore - Topic.reset_primary_key - assert_equal "topic_id", Topic.primary_key - - ActiveRecord::Base.primary_key_prefix_type = nil - Topic.reset_primary_key - assert_equal "id", Topic.primary_key - end - - def test_delete_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.delete(1) } - end - def test_update_counters_should_quote_pkey_and_quote_counter_columns - assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) } - end - def test_find_with_one_id_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find(1) } - end - def test_find_with_multiple_ids_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find([1,2]) } - end - def test_instance_update_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find(1).save } - end - def test_instance_destroy_should_quote_pkey - assert_nothing_raised { MixedCaseMonkey.find(1).destroy } - end - - def test_supports_primary_key - assert_nothing_raised NoMethodError do - ActiveRecord::Base.connection.supports_primary_key? - end - end - - def test_primary_key_returns_value_if_it_exists - if ActiveRecord::Base.connection.supports_primary_key? - assert_equal 'id', ActiveRecord::Base.connection.primary_key('developers') - end - end - - def test_primary_key_returns_nil_if_it_does_not_exist - if ActiveRecord::Base.connection.supports_primary_key? - assert_nil ActiveRecord::Base.connection.primary_key('developers_projects') - end - end -end diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb new file mode 100644 index 0000000000..73f4b3848c --- /dev/null +++ b/activerecord/test/cases/primary_keys_test.rb @@ -0,0 +1,139 @@ +require "cases/helper" +require 'models/topic' +require 'models/reply' +require 'models/subscriber' +require 'models/movie' +require 'models/keyboard' +require 'models/mixed_case_monkey' + +class PrimaryKeysTest < ActiveRecord::TestCase + fixtures :topics, :subscribers, :movies, :mixed_case_monkeys + + def test_to_key_with_default_primary_key + topic = Topic.new + assert topic.to_key.nil? + topic = Topic.find(1) + assert_equal topic.to_key, [1] + end + + def test_to_key_with_customized_primary_key + keyboard = Keyboard.new + assert_nil keyboard.to_key + keyboard.save + assert_equal keyboard.to_key, [keyboard.id] + end + + def test_to_key_with_primary_key_after_destroy + topic = Topic.find(1) + topic.destroy + assert_equal topic.to_key, [1] + end + + def test_integer_key + topic = Topic.find(1) + assert_equal(topics(:first).author_name, topic.author_name) + topic = Topic.find(2) + assert_equal(topics(:second).author_name, topic.author_name) + + topic = Topic.new + topic.title = "New Topic" + assert_nil topic.id + assert_nothing_raised { topic.save! } + id = topic.id + + topicReloaded = Topic.find(id) + assert_equal("New Topic", topicReloaded.title) + end + + def test_customized_primary_key_auto_assigns_on_save + Keyboard.delete_all + keyboard = Keyboard.new(:name => 'HHKB') + assert_nothing_raised { keyboard.save! } + assert_equal keyboard.id, Keyboard.find_by_name('HHKB').id + end + + def test_customized_primary_key_can_be_get_before_saving + keyboard = Keyboard.new + assert_nil keyboard.id + assert_nothing_raised { assert_nil keyboard.key_number } + end + + def test_customized_string_primary_key_settable_before_save + subscriber = Subscriber.new + assert_nothing_raised { subscriber.id = 'webster123' } + assert_equal 'webster123', subscriber.id + assert_equal 'webster123', subscriber.nick + end + + def test_string_key + subscriber = Subscriber.find(subscribers(:first).nick) + assert_equal(subscribers(:first).name, subscriber.name) + subscriber = Subscriber.find(subscribers(:second).nick) + assert_equal(subscribers(:second).name, subscriber.name) + + subscriber = Subscriber.new + subscriber.id = "jdoe" + assert_equal("jdoe", subscriber.id) + subscriber.name = "John Doe" + assert_nothing_raised { subscriber.save! } + assert_equal("jdoe", subscriber.id) + + subscriberReloaded = Subscriber.find("jdoe") + assert_equal("John Doe", subscriberReloaded.name) + end + + def test_find_with_more_than_one_string_key + assert_equal 2, Subscriber.find(subscribers(:first).nick, subscribers(:second).nick).length + end + + def test_primary_key_prefix + ActiveRecord::Base.primary_key_prefix_type = :table_name + Topic.reset_primary_key + assert_equal "topicid", Topic.primary_key + + ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore + Topic.reset_primary_key + assert_equal "topic_id", Topic.primary_key + + ActiveRecord::Base.primary_key_prefix_type = nil + Topic.reset_primary_key + assert_equal "id", Topic.primary_key + end + + def test_delete_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.delete(1) } + end + def test_update_counters_should_quote_pkey_and_quote_counter_columns + assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) } + end + def test_find_with_one_id_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find(1) } + end + def test_find_with_multiple_ids_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find([1,2]) } + end + def test_instance_update_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find(1).save } + end + def test_instance_destroy_should_quote_pkey + assert_nothing_raised { MixedCaseMonkey.find(1).destroy } + end + + def test_supports_primary_key + assert_nothing_raised NoMethodError do + ActiveRecord::Base.connection.supports_primary_key? + end + end + + def test_primary_key_returns_value_if_it_exists + if ActiveRecord::Base.connection.supports_primary_key? + assert_equal 'id', ActiveRecord::Base.connection.primary_key('developers') + end + end + + def test_primary_key_returns_nil_if_it_does_not_exist + if ActiveRecord::Base.connection.supports_primary_key? + assert_nil ActiveRecord::Base.connection.primary_key('developers_projects') + end + end +end -- cgit v1.2.3 From 0fce4ae57fa356e9eca7059a4ff6a67e68c37961 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 16 Jul 2010 22:24:18 -0400 Subject: expected value should come first in assert_equal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/primary_keys_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 73f4b3848c..1e44237e0a 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -13,7 +13,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase topic = Topic.new assert topic.to_key.nil? topic = Topic.find(1) - assert_equal topic.to_key, [1] + assert_equal [1], topic.to_key end def test_to_key_with_customized_primary_key @@ -26,7 +26,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase def test_to_key_with_primary_key_after_destroy topic = Topic.find(1) topic.destroy - assert_equal topic.to_key, [1] + assert_equal [1], topic.to_key end def test_integer_key -- cgit v1.2.3 From b22c11fa533fd523e8cadd36e75dd76b6a9f0488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 19 Jul 2010 15:14:26 +0200 Subject: Add missing entries and tidy up CHANGELOG. --- activerecord/CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index a1a82fdff5..679fdafae8 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [RC1] (unreleased)* +* Changed update_attribute to not run callbacks and update the record directly in the database [Neeraj Singh] + * Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope [José Valim] * New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund] -- cgit v1.2.3 From 247886e1b4256aeebc6b5fde0549400240b04e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Thu, 15 Jul 2010 03:43:59 +0800 Subject: Removed warnings when a variable is shadowed --- activerecord/lib/active_record/association_preload.rb | 4 ++-- .../lib/active_record/associations/association_collection.rb | 6 +++--- activerecord/test/cases/associations/inverse_associations_test.rb | 4 ++-- activerecord/test/cases/base_test.rb | 4 ++-- activerecord/test/cases/fixtures_test.rb | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index cbec5789fd..9172ab2a20 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -112,13 +112,13 @@ module ActiveRecord # Not all records have the same class, so group then preload # group on the reflection itself so that if various subclass share the same association then we do not split them # unnecessarily - records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records| + records.group_by { |record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, _records| raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection # 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus, # the following could call 'preload_belongs_to_association', # 'preload_has_many_association', etc. - send("preload_#{reflection.macro}_association", records, reflection, preload_options) + send("preload_#{reflection.macro}_association", _records, reflection, preload_options) end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index f4e34657a5..7abb738a74 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -218,9 +218,9 @@ module ActiveRecord # are actually removed from the database, that depends precisely on # +delete_records+. They are in any case removed from the collection. def delete(*records) - remove_records(records) do |records, old_records| + remove_records(records) do |_records, old_records| delete_records(old_records) if old_records.any? - records.each { |record| @target.delete(record) } + _records.each { |record| @target.delete(record) } end end @@ -231,7 +231,7 @@ module ActiveRecord # ignoring the +:dependent+ option. def destroy(*records) records = find(records) if records.any? {|record| record.kind_of?(Fixnum) || record.kind_of?(String)} - remove_records(records) do |records, old_records| + remove_records(records) do |_records, old_records| old_records.each { |record| record.destroy } end diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index 34d24a2948..fa5c2e49df 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -412,7 +412,7 @@ class InverseBelongsToTests < ActiveRecord::TestCase i = interests(:trainspotting) m = i.man assert_not_nil m.interests - iz = m.interests.detect {|iz| iz.id == i.id} + iz = m.interests.detect { |_iz| _iz.id == i.id} assert_not_nil iz assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child" i.topic = 'Eating cheese with a spoon' @@ -516,7 +516,7 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase i = interests(:llama_wrangling) m = i.polymorphic_man assert_not_nil m.polymorphic_interests - iz = m.polymorphic_interests.detect {|iz| iz.id == i.id} + iz = m.polymorphic_interests.detect { |_iz| _iz.id == i.id} assert_not_nil iz assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child" i.topic = 'Eating cheese with a spoon' diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 831dd446ad..709e22a648 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1816,8 +1816,8 @@ class BasicsTest < ActiveRecord::TestCase def test_to_xml_with_block value = "Rockin' the block" - xml = Company.new.to_xml(:skip_instruct => true) do |xml| - xml.tag! "arbitrary-element", value + xml = Company.new.to_xml(:skip_instruct => true) do |_xml| + _xml.tag! "arbitrary-element", value end assert_equal "", xml.first(9) assert xml.include?(%(#{value})) diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 8008b86f81..93f8749255 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -36,7 +36,7 @@ class FixturesTest < ActiveRecord::TestCase fixtures = nil assert_nothing_raised { fixtures = create_fixtures(name) } assert_kind_of(Fixtures, fixtures) - fixtures.each { |name, fixture| + fixtures.each { |_name, fixture| fixture.each { |key, value| assert_match(MATCH_ATTRIBUTE_NAME, key) } @@ -229,9 +229,9 @@ if Account.connection.respond_to?(:reset_pk_sequence!) def test_create_fixtures_resets_sequences_when_not_cached @instances.each do |instance| - max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)| + max_id = create_fixtures(instance.class.table_name).inject(0) do |_max_id, (name, fixture)| fixture_id = fixture['id'].to_i - fixture_id > max_id ? fixture_id : max_id + fixture_id > _max_id ? fixture_id : _max_id end # Clone the last fixture to check that it gets the next greatest id. -- cgit v1.2.3 From 010eda2eb516d237a7b5cf022b7dd2213ff77bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Sat, 17 Jul 2010 03:21:14 +0800 Subject: Removed warnings about method redefining --- activerecord/lib/active_record/associations.rb | 33 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d67df64f59..a9d256a771 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1354,6 +1354,7 @@ module ActiveRecord end def association_accessor_methods(reflection, association_proxy_class) + remove_possible_method(reflection.name) define_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1371,12 +1372,16 @@ module ActiveRecord association.target.nil? ? nil : association end - define_method("loaded_#{reflection.name}?") do + method = "loaded_#{reflection.name}?" + remove_possible_method(method) + define_method(method) do association = association_instance_get(reflection.name) association && association.loaded? end - define_method("#{reflection.name}=") do |new_value| + method = "#{reflection.name}=" + remove_possible_method(method) + define_method(method) do |new_value| association = association_instance_get(reflection.name) if association.nil? || association.target != new_value @@ -1386,8 +1391,10 @@ module ActiveRecord association.replace(new_value) association_instance_set(reflection.name, new_value.nil? ? nil : association) end - - define_method("set_#{reflection.name}_target") do |target| + + method = "set_#{reflection.name}_target" + remove_possible_method(method) + define_method(method) do |target| return if target.nil? and association_proxy_class == BelongsToAssociation association = association_proxy_class.new(self, reflection) association.target = target @@ -1396,6 +1403,7 @@ module ActiveRecord end def collection_reader_method(reflection, association_proxy_class) + remove_possible_method(reflection.name) define_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1409,8 +1417,10 @@ module ActiveRecord association end - - define_method("#{reflection.name.to_s.singularize}_ids") do + + method = "#{reflection.name.to_s.singularize}_ids" + remove_possible_method(method) + define_method(method) do if send(reflection.name).loaded? || reflection.options[:finder_sql] send(reflection.name).map(&:id) else @@ -1436,8 +1446,10 @@ module ActiveRecord association.replace(new_value) association end - - define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| + + method = "#{reflection.name.to_s.singularize}_ids=" + remove_possible_method(method) + define_method(method) do |new_value| ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i) send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end @@ -1445,7 +1457,9 @@ module ActiveRecord end def association_constructor_method(constructor, reflection, association_proxy_class) - define_method("#{constructor}_#{reflection.name}") do |*params| + method = "#{constructor}_#{reflection.name}" + remove_possible_method(method) + define_method(method) do |*params| attributees = params.first unless params.empty? replace_existing = params[1].nil? ? true : params[1] association = association_instance_get(reflection.name) @@ -1487,6 +1501,7 @@ module ActiveRecord def add_touch_callbacks(reflection, touch_attribute) method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym + remove_possible_method(method_name) define_method(method_name) do association = send(reflection.name) -- cgit v1.2.3 From 7637b7184a990ae51f3ab10e2af99ff88cb633ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Thu, 15 Jul 2010 04:40:17 +0800 Subject: Test for behaviour of befeore_type_cast when operating on datetime colmun --- activerecord/test/cases/base_test.rb | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 709e22a648..df6895f0d0 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -123,6 +123,14 @@ class BasicsTest < ActiveRecord::TestCase assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] else assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) + + developer.created_at = "345643456" + assert_equal developer.created_at_before_type_cast, "345643456" + assert_equal developer.created_at, nil + + developer.created_at = "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") end end -- cgit v1.2.3 From 661fd98aad027b6171253828ed89115f30fac46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Sat, 17 Jul 2010 17:42:17 +0800 Subject: Make use of redefine_method, removed some more redefining warnings --- activerecord/lib/active_record/associations.rb | 39 +++++++--------------- activerecord/lib/active_record/fixtures.rb | 2 +- activerecord/lib/active_record/named_scope.rb | 2 +- .../cases/adapters/mysql/active_schema_test.rb | 1 + 4 files changed, 15 insertions(+), 29 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a9d256a771..7cddb9c601 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1354,8 +1354,7 @@ module ActiveRecord end def association_accessor_methods(reflection, association_proxy_class) - remove_possible_method(reflection.name) - define_method(reflection.name) do |*params| + redefine_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1372,16 +1371,12 @@ module ActiveRecord association.target.nil? ? nil : association end - method = "loaded_#{reflection.name}?" - remove_possible_method(method) - define_method(method) do + redefine_method("loaded_#{reflection.name}?") do association = association_instance_get(reflection.name) association && association.loaded? end - - method = "#{reflection.name}=" - remove_possible_method(method) - define_method(method) do |new_value| + + redefine_method("#{reflection.name}=") do |new_value| association = association_instance_get(reflection.name) if association.nil? || association.target != new_value @@ -1392,9 +1387,7 @@ module ActiveRecord association_instance_set(reflection.name, new_value.nil? ? nil : association) end - method = "set_#{reflection.name}_target" - remove_possible_method(method) - define_method(method) do |target| + redefine_method("set_#{reflection.name}_target") do |target| return if target.nil? and association_proxy_class == BelongsToAssociation association = association_proxy_class.new(self, reflection) association.target = target @@ -1403,8 +1396,7 @@ module ActiveRecord end def collection_reader_method(reflection, association_proxy_class) - remove_possible_method(reflection.name) - define_method(reflection.name) do |*params| + redefine_method(reflection.name) do |*params| force_reload = params.first unless params.empty? association = association_instance_get(reflection.name) @@ -1418,9 +1410,7 @@ module ActiveRecord association end - method = "#{reflection.name.to_s.singularize}_ids" - remove_possible_method(method) - define_method(method) do + redefine_method("#{reflection.name.to_s.singularize}_ids") do if send(reflection.name).loaded? || reflection.options[:finder_sql] send(reflection.name).map(&:id) else @@ -1440,16 +1430,14 @@ module ActiveRecord collection_reader_method(reflection, association_proxy_class) if writer - define_method("#{reflection.name}=") do |new_value| + redefine_method("#{reflection.name}=") do |new_value| # Loads proxy class instance (defined in collection_reader_method) if not already loaded association = send(reflection.name) association.replace(new_value) association end - method = "#{reflection.name.to_s.singularize}_ids=" - remove_possible_method(method) - define_method(method) do |new_value| + redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i) send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end @@ -1457,9 +1445,7 @@ module ActiveRecord end def association_constructor_method(constructor, reflection, association_proxy_class) - method = "#{constructor}_#{reflection.name}" - remove_possible_method(method) - define_method(method) do |*params| + redefine_method("#{constructor}_#{reflection.name}") do |*params| attributees = params.first unless params.empty? replace_existing = params[1].nil? ? true : params[1] association = association_instance_get(reflection.name) @@ -1500,9 +1486,8 @@ module ActiveRecord end def add_touch_callbacks(reflection, touch_attribute) - method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym - remove_possible_method(method_name) - define_method(method_name) do + method_name = :"belongs_to_touch_after_save_or_destroy_for_#{reflection.name}" + redefine_method(method_name) do association = send(reflection.name) if touch_attribute == true diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 5bf43b3a72..657303fd14 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -871,7 +871,7 @@ module ActiveRecord table_names.each do |table_name| table_name = table_name.to_s.tr('./', '_') - define_method(table_name) do |*fixtures| + redefine_method(table_name) do |*fixtures| force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload @fixture_cache[table_name] ||= {} diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 849ec9c884..6596c695e2 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -105,7 +105,7 @@ module ActiveRecord extension ? relation.extending(extension) : relation end - singleton_class.send :define_method, name, &scopes[name] + singleton_class.send(:redefine_method, name, &scopes[name]) end def named_scope(*args, &block) diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb index 6e6645511c..ed4efdc1c0 100644 --- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -101,6 +101,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase #we need to actually modify some data, so we make execute point to the original method ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do alias_method :execute_with_stub, :execute + remove_method :execute alias_method :execute, :execute_without_stub end yield -- cgit v1.2.3 From bd4b3d8b2fc6b657ca2981582f8736842ac500d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Sat, 17 Jul 2010 23:53:05 +0800 Subject: Added missing require of remove_method --- activerecord/lib/active_record/associations.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7cddb9c601..1b9b725dd4 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/enumerable' require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/conversions' +require 'active_support/core_ext/module/remove_method' module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: -- cgit v1.2.3 From ad4ef4226fd5d47252d30effa41a3ab2f55dbc8d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 10:31:24 -0700 Subject: avoid const lookup. we know what these constants are in advance --- activerecord/lib/active_record/log_subscriber.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 71065f9908..89c79055fe 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -10,10 +10,10 @@ module ActiveRecord sql = event.payload[:sql].squeeze(' ') if odd? - name = color(name, :cyan, true) + name = color(name, CYAN, true) sql = color(sql, nil, true) else - name = color(name, :magenta, true) + name = color(name, MAGENTA, true) end debug " #{name} #{sql}" @@ -29,4 +29,4 @@ module ActiveRecord end end -ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file +ActiveRecord::LogSubscriber.attach_to :active_record -- cgit v1.2.3 From 38f0161aabb302550e1522cb62d19e54d448be9b Mon Sep 17 00:00:00 2001 From: Daniel Guettler Date: Sun, 18 Jul 2010 07:30:48 -0400 Subject: Minor performance improvment in notifications/fanout and active_record/log_subscriber [#5098 state:open] --- activerecord/lib/active_record/log_subscriber.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 89c79055fe..278e192e59 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -6,6 +6,8 @@ module ActiveRecord end def sql(event) + return unless logger.debug? + name = '%s (%.1fms)' % [event.payload[:name], event.duration] sql = event.payload[:sql].squeeze(' ') -- cgit v1.2.3 From 1b26c66ce470ce68674bbdce738c6f68467cff7d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 13:19:28 -0700 Subject: mocking out debing? call in the MockLogger --- activerecord/test/cases/log_subscriber_test.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index cde383783b..fa53fc7477 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -22,6 +22,7 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_logging + @logger.debugging = true Developer.all wait assert_equal 1, @logger.logged(:debug).size @@ -30,6 +31,7 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_cached_queries + @logger.debugging = true ActiveRecord::Base.cache do Developer.all Developer.all -- cgit v1.2.3 From d39c3b179c2c0d31099033b3de4a866e19ce144b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Jul 2010 04:28:51 +0800 Subject: Test added, we shouldn't log sql calls when logger is not on debug? mode --- activerecord/test/cases/log_subscriber_test.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index fa53fc7477..2a207bed8a 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -41,4 +41,21 @@ class LogSubscriberTest < ActiveRecord::TestCase assert_match(/CACHE/, @logger.logged(:debug).last) assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last) end + + def test_basic_query_doesnt_log_when_level_is_not_debug + @logger.debugging = false + Developer.all + wait + assert_equal 0, @logger.logged(:debug).size + end + + def test_cached_queries_doesnt_log_when_level_is_not_debug + @logger.debugging = false + ActiveRecord::Base.cache do + Developer.all + Developer.all + end + wait + assert_equal 0, @logger.logged(:debug).size + end end -- cgit v1.2.3 From 202fb79e8686ee127fe49497c979cfc9c9d985d5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 13:44:11 -0700 Subject: reusing the time instrumentation from the instrumenter rather than Benchmark. [#5098 state:open] --- .../lib/active_record/connection_adapters/abstract_adapter.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index be8d1bd76b..6072481411 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -199,11 +199,14 @@ module ActiveRecord def log(sql, name) name ||= "SQL" - result = nil - ActiveSupport::Notifications.instrument("sql.active_record", - :sql => sql, :name => name, :connection_id => self.object_id) do - @runtime += Benchmark.ms { result = yield } + instrumenter = ActiveSupport::Notifications.instrumenter + + result = instrumenter.instrument("sql.active_record", + :sql => sql, :name => name, :connection_id => object_id) do + yield end + @runtime += instrumenter.elapsed + result rescue Exception => e message = "#{e.class.name}: #{e.message}: #{sql}" -- cgit v1.2.3 From e1df4b956882f0c10a310088c1c13dcaa655a3b1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 19 Jul 2010 21:50:11 -0700 Subject: adding a reader for loaded, initializing @loaded to false --- activerecord/lib/active_record/named_scope.rb | 2 +- activerecord/lib/active_record/relation.rb | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 6596c695e2..417ff4b5eb 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -26,7 +26,7 @@ module ActiveRecord # You can define a \scope that applies to all finders using # ActiveRecord::Base.default_scope. def scoped(options = nil) - if options.present? + if options scoped.apply_finder_options(options) else current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7499100f55..86a210d2be 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -13,14 +13,15 @@ module ActiveRecord delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a delegate :insert, :to => :arel - attr_reader :table, :klass + attr_reader :table, :klass, :loaded attr_accessor :extensions + alias :loaded? :loaded def initialize(klass, table) @klass, @table = klass, table @implicit_readonly = nil - @loaded = nil + @loaded = false SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)} (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])} @@ -292,10 +293,6 @@ module ActiveRecord where(@klass.primary_key => id_or_array).delete_all end - def loaded? - @loaded - end - def reload reset to_a # force reload -- cgit v1.2.3 From d4151d7f0ac4a0823e788c0beed9ec2476e72386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 20 Jul 2010 21:20:19 +0200 Subject: Fix a failing test in Railtie and properly define all severity levels in MockLogger for LogSubscriber. --- activerecord/test/cases/log_subscriber_test.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 2a207bed8a..91ba852ecd 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -22,7 +22,6 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_logging - @logger.debugging = true Developer.all wait assert_equal 1, @logger.logged(:debug).size @@ -31,7 +30,6 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_cached_queries - @logger.debugging = true ActiveRecord::Base.cache do Developer.all Developer.all @@ -43,14 +41,14 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_doesnt_log_when_level_is_not_debug - @logger.debugging = false + @logger.level = 1 Developer.all wait assert_equal 0, @logger.logged(:debug).size end def test_cached_queries_doesnt_log_when_level_is_not_debug - @logger.debugging = false + @logger.level = 1 ActiveRecord::Base.cache do Developer.all Developer.all -- cgit v1.2.3 From 978c49ea6a969fe040ad15dbda78e43ca5afc069 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Jul 2010 17:00:12 -0300 Subject: Make use of severity levels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/log_subscriber_test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 91ba852ecd..342daa19df 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -4,6 +4,7 @@ require "active_support/log_subscriber/test_helper" class LogSubscriberTest < ActiveRecord::TestCase include ActiveSupport::LogSubscriber::TestHelper + include ActiveSupport::BufferedLogger::Severity def setup @old_logger = ActiveRecord::Base.logger @@ -41,14 +42,14 @@ class LogSubscriberTest < ActiveRecord::TestCase end def test_basic_query_doesnt_log_when_level_is_not_debug - @logger.level = 1 + @logger.level = INFO Developer.all wait assert_equal 0, @logger.logged(:debug).size end def test_cached_queries_doesnt_log_when_level_is_not_debug - @logger.level = 1 + @logger.level = INFO ActiveRecord::Base.cache do Developer.all Developer.all -- cgit v1.2.3 From f576d7cf848717384799a9e9669b253ccc94deb5 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 19 Jul 2010 15:32:00 -0400 Subject: Ensure that primary_keys of HABTM records is not double quoted [#5152 state:reslved] --- .../has_and_belongs_to_many_association.rb | 4 ++-- .../has_and_belongs_to_many_associations_test.rb | 18 ++++++++++++++++++ activerecord/test/models/country.rb | 7 +++++++ activerecord/test/models/treaty.rb | 7 +++++++ activerecord/test/schema/schema.rb | 13 +++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 activerecord/test/models/country.rb create mode 100644 activerecord/test/models/treaty.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index c989c3536d..aba66d5a96 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -49,9 +49,9 @@ module ActiveRecord attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s - attrs[relation[column.name]] = owner_quoted_id + attrs[relation[column.name]] = @owner.send(:id) when @reflection.association_foreign_key.to_s - attrs[relation[column.name]] = record.quoted_id + attrs[relation[column.name]] = record.send(:id) else if record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) 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 b11969a841..d4d3d8e43e 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 @@ -24,6 +24,8 @@ require 'models/club' require 'models/member' require 'models/membership' require 'models/sponsor' +require 'models/country' +require 'models/treaty' require 'active_support/core_ext/string/conversions' class ProjectWithAfterCreateHook < ActiveRecord::Base @@ -83,6 +85,22 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings + def test_should_property_quote_string_primary_keys + country = Country.new(:name => 'India') + country.country_id = 'c1' + country.save! + + treaty = Treaty.new(:name => 'peace') + treaty.treaty_id = 't1' + country.treaties << treaty + + con = ActiveRecord::Base.connection + sql = 'select * from countries_treaties' + record = con.select_rows(sql).last + assert_equal 'c1', record[0] + assert_equal 't1', record[1] + end + def test_has_and_belongs_to_many david = Developer.find(1) diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb new file mode 100644 index 0000000000..15e3a1de0b --- /dev/null +++ b/activerecord/test/models/country.rb @@ -0,0 +1,7 @@ +class Country < ActiveRecord::Base + + set_primary_key :country_id + + has_and_belongs_to_many :treaties + +end diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb new file mode 100644 index 0000000000..b46537f0d2 --- /dev/null +++ b/activerecord/test/models/treaty.rb @@ -0,0 +1,7 @@ +class Treaty < ActiveRecord::Base + + set_primary_key :treaty_id + + has_and_belongs_to_many :countries + +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 641726b43f..c4eed256bf 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -600,6 +600,19 @@ ActiveRecord::Schema.define do t.string :title end + create_table :countries, :force => true, :id => false, :primary_key => 'country_id' do |t| + t.string :country_id + t.string :name + end + create_table :treaties, :force => true, :id => false, :primary_key => 'treaty_id' do |t| + t.string :treaty_id + t.string :name + end + create_table :countries_treaties, :force => true, :id => false do |t| + t.string :country_id, :null => false + t.string :treaty_id, :null => false + end + except 'SQLite' do # fk_test_has_fk should be before fk_test_has_pk create_table :fk_test_has_fk, :force => true do |t| -- cgit v1.2.3 From fa8b290496789eb037d4fad89acea1cb0a534f35 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Jul 2010 08:13:31 +0800 Subject: id is a public method --- .../active_record/associations/has_and_belongs_to_many_association.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index aba66d5a96..9ec63e3fca 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -49,9 +49,9 @@ module ActiveRecord attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s - attrs[relation[column.name]] = @owner.send(:id) + attrs[relation[column.name]] = @owner.id when @reflection.association_foreign_key.to_s - attrs[relation[column.name]] = record.send(:id) + attrs[relation[column.name]] = record.id else if record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) -- cgit v1.2.3 From f1082bd51eb575f22f86b78f347ab75a4b2bff1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 11:56:28 +0200 Subject: Remove old install.rb files. --- activerecord/README | 21 +++------------------ activerecord/install.rb | 30 ------------------------------ 2 files changed, 3 insertions(+), 48 deletions(-) delete mode 100644 activerecord/install.rb (limited to 'activerecord') diff --git a/activerecord/README b/activerecord/README index d68eb28a64..0446180207 100644 --- a/activerecord/README +++ b/activerecord/README @@ -309,28 +309,13 @@ Admit the Database: == Download -The latest version of Active Record can be found at +The latest version of Active Record can be installed with Rubygems: -* http://rubyforge.org/project/showfiles.php?group_id=182 +* gem install activerecord Documentation can be found at -* http://ar.rubyonrails.com - - -== Installation - -The prefered method of installing Active Record is through its GEM file. You'll need to have -RubyGems[http://rubygems.rubyforge.org/wiki/wiki.pl] installed for that, though. If you have, -then use: - - % [sudo] gem install activerecord-1.10.0.gem - -You can also install Active Record the old-fashioned way with the following command: - - % [sudo] ruby install.rb - -from its distribution directory. +* http://api.rubyonrails.org == License diff --git a/activerecord/install.rb b/activerecord/install.rb deleted file mode 100644 index c87398b1f4..0000000000 --- a/activerecord/install.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rbconfig' -require 'find' -require 'ftools' - -include Config - -# this was adapted from rdoc's install.rb by ways of Log4r - -$sitedir = CONFIG["sitelibdir"] -unless $sitedir - version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"] - $libdir = File.join(CONFIG["libdir"], "ruby", version) - $sitedir = $:.find {|x| x =~ /site_ruby/ } - if !$sitedir - $sitedir = File.join($libdir, "site_ruby") - elsif $sitedir !~ Regexp.quote(version) - $sitedir = File.join($sitedir, version) - end -end - -# the actual gruntwork -Dir.chdir("lib") - -Find.find("active_record", "active_record.rb") { |f| - if f[-3..-1] == ".rb" - File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true) - else - File::makedirs(File.join($sitedir, *f.split(/\//))) - end -} -- cgit v1.2.3 From 508fba9e070e09f0a321f2dd7acf7938967468f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 12:51:14 +0200 Subject: Add .rdoc extension to README files. --- activerecord/README | 336 ----------------------------------------------- activerecord/README.rdoc | 336 +++++++++++++++++++++++++++++++++++++++++++++++ activerecord/Rakefile | 2 +- 3 files changed, 337 insertions(+), 337 deletions(-) delete mode 100644 activerecord/README create mode 100644 activerecord/README.rdoc (limited to 'activerecord') diff --git a/activerecord/README b/activerecord/README deleted file mode 100644 index 0446180207..0000000000 --- a/activerecord/README +++ /dev/null @@ -1,336 +0,0 @@ -= Active Record -- Object-relation mapping put on rails - -Active Record connects business objects and database tables to create a persistable -domain model where logic and data are presented in one wrapping. It's an implementation -of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] -by the same name as described by Martin Fowler: - - "An object that wraps a row in a database table or view, encapsulates - the database access, and adds domain logic on that data." - -Active Record's main contribution to the pattern is to relieve the original of two stunting problems: -lack of associations and inheritance. By adding a simple domain language-like set of macros to describe -the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the -gap of functionality between the data mapper and active record approach. - -A short rundown of the major features: - -* Automated mapping between classes and tables, attributes and columns. - - class Product < ActiveRecord::Base; end - - ...is automatically mapped to the table named "products", such as: - - CREATE TABLE products ( - id int(11) NOT NULL auto_increment, - name varchar(255), - PRIMARY KEY (id) - ); - - ...which again gives Product#name and Product#name=(new_name) - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Associations between objects controlled by simple meta-programming macros. - - class Firm < ActiveRecord::Base - has_many :clients - has_one :account - belongs_to :conglomorate - end - - {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] - - -* Aggregations of value objects controlled by simple meta-programming macros. - - class Account < ActiveRecord::Base - composed_of :balance, :class_name => "Money", - :mapping => %w(balance amount) - composed_of :address, - :mapping => [%w(address_street street), %w(address_city city)] - end - - {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html] - - -* Validation rules that can differ for new or existing objects. - - class Account < ActiveRecord::Base - validates_presence_of :subdomain, :name, :email_address, :password - validates_uniqueness_of :subdomain - validates_acceptance_of :terms_of_service, :on => :create - validates_confirmation_of :password, :email_address, :on => :create - end - - {Learn more}[link:classes/ActiveRecord/Validations.html] - -* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc). - - class Person < ActiveRecord::Base - def before_destroy # is called just before Person#destroy - CreditCard.find(credit_card_id).destroy - end - end - - class Account < ActiveRecord::Base - after_find :eager_load, 'self.class.announce(#{id})' - end - - {Learn more}[link:classes/ActiveRecord/Callbacks.html] - - -* Observers for the entire lifecycle - - class CommentObserver < ActiveRecord::Observer - def after_create(comment) # is called just after Comment#save - Notifications.deliver_new_comment("david@loudthinking.com", comment) - end - end - - {Learn more}[link:classes/ActiveRecord/Observer.html] - - -* Inheritance hierarchies - - class Company < ActiveRecord::Base; end - class Firm < Company; end - class Client < Company; end - class PriorityClient < Client; end - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Transactions - - # Database transaction - Account.transaction do - david.withdrawal(100) - mary.deposit(100) - end - - {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html] - - -* Reflections on columns, associations, and aggregations - - reflection = Firm.reflect_on_association(:clients) - reflection.klass # => Client (class) - Firm.columns # Returns an array of column descriptors for the firms table - - {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] - - -* Direct manipulation (instead of service invocation) - - So instead of (Hibernate[http://www.hibernate.org/] example): - - long pkId = 1234; - DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); - // something interesting involving a cat... - sess.save(cat); - sess.flush(); // force the SQL INSERT - - Active Record lets you: - - pkId = 1234 - cat = Cat.find(pkId) - # something even more interesting involving the same cat... - cat.save - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Database abstraction through simple adapters (~100 lines) with a shared connector - - ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - - {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for - MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. - - -* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc] - - ActiveRecord::Base.logger = Logger.new(STDOUT) - ActiveRecord::Base.logger = Log4r::Logger.new("Application Log") - - -* Database agnostic schema management with Migrations - - class AddSystemSettings < ActiveRecord::Migration - def self.up - create_table :system_settings do |t| - t.string :name - t.string :label - t.text :value - t.string :type - t.integer :position - end - - SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1 - end - - def self.down - drop_table :system_settings - end - end - - {Learn more}[link:classes/ActiveRecord/Migration.html] - -== Simple example (1/2): Defining tables and classes (using MySQL) - -Data definitions are specified only in the database. Active Record queries the database for -the column names (that then serves to determine which attributes are valid) on regular -object instantiation through the new constructor and relies on the column names in the rows -with the finders. - - # CREATE TABLE companies ( - # id int(11) unsigned NOT NULL auto_increment, - # client_of int(11), - # name varchar(255), - # type varchar(100), - # PRIMARY KEY (id) - # ) - -Active Record automatically links the "Company" object to the "companies" table - - class Company < ActiveRecord::Base - has_many :people, :class_name => "Person" - end - - class Firm < Company - has_many :clients - - def people_with_all_clients - clients.inject([]) { |people, client| people + client.people } - end - end - -The foreign_key is only necessary because we didn't use "firm_id" in the data definition - - class Client < Company - belongs_to :firm, :foreign_key => "client_of" - end - - # CREATE TABLE people ( - # id int(11) unsigned NOT NULL auto_increment, - # name text, - # company_id text, - # PRIMARY KEY (id) - # ) - -Active Record will also automatically link the "Person" object to the "people" table - - class Person < ActiveRecord::Base - belongs_to :company - end - -== Simple example (2/2): Using the domain - -Picking a database connection for all the Active Records - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - -Create some fixtures - - firm = Firm.new("name" => "Next Angle") - # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") - firm.save - - client = Client.new("name" => "37signals", "client_of" => firm.id) - # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") - client.save - -Lots of different finders - - # SQL: SELECT * FROM companies WHERE id = 1 - next_angle = Company.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' - next_angle = Firm.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' - next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") - - next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first - -The supertype, Company, will return subtype instances - - Firm === next_angle - -All the dynamic methods added by the has_many macro - - next_angle.clients.empty? # true - next_angle.clients.size # total number of clients - all_clients = next_angle.clients - -Constrained finds makes access security easier when ID comes from a web-app - - # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 - thirty_seven_signals = next_angle.clients.find(2) - -Bi-directional associations thanks to the "belongs_to" macro - - thirty_seven_signals.firm.nil? # true - - -== Philosophy - -Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is -object-relational mapping. The prime directive for this mapping has been to minimize -the amount of code needed to build a real-world domain model. This is made possible -by relying on a number of conventions that make it easy for Active Record to infer -complex relations and structures from a minimal amount of explicit direction. - -Convention over Configuration: -* No XML-files! -* Lots of reflection and run-time extension -* Magic is not inherently a bad word - -Admit the Database: -* Lets you drop down to SQL for odd cases and performance -* Doesn't attempt to duplicate or replace data definitions - - -== Download - -The latest version of Active Record can be installed with Rubygems: - -* gem install activerecord - -Documentation can be found at - -* http://api.rubyonrails.org - - -== License - -Active Record is released under the MIT license. - - -== Support - -The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record -RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says: - - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. - -For other information, feel free to ask on the rubyonrails-talk -(http://groups.google.com/group/rubyonrails-talk) mailing list. diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc new file mode 100644 index 0000000000..0446180207 --- /dev/null +++ b/activerecord/README.rdoc @@ -0,0 +1,336 @@ += Active Record -- Object-relation mapping put on rails + +Active Record connects business objects and database tables to create a persistable +domain model where logic and data are presented in one wrapping. It's an implementation +of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] +by the same name as described by Martin Fowler: + + "An object that wraps a row in a database table or view, encapsulates + the database access, and adds domain logic on that data." + +Active Record's main contribution to the pattern is to relieve the original of two stunting problems: +lack of associations and inheritance. By adding a simple domain language-like set of macros to describe +the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the +gap of functionality between the data mapper and active record approach. + +A short rundown of the major features: + +* Automated mapping between classes and tables, attributes and columns. + + class Product < ActiveRecord::Base; end + + ...is automatically mapped to the table named "products", such as: + + CREATE TABLE products ( + id int(11) NOT NULL auto_increment, + name varchar(255), + PRIMARY KEY (id) + ); + + ...which again gives Product#name and Product#name=(new_name) + + {Learn more}[link:classes/ActiveRecord/Base.html] + + +* Associations between objects controlled by simple meta-programming macros. + + class Firm < ActiveRecord::Base + has_many :clients + has_one :account + belongs_to :conglomorate + end + + {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] + + +* Aggregations of value objects controlled by simple meta-programming macros. + + class Account < ActiveRecord::Base + composed_of :balance, :class_name => "Money", + :mapping => %w(balance amount) + composed_of :address, + :mapping => [%w(address_street street), %w(address_city city)] + end + + {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html] + + +* Validation rules that can differ for new or existing objects. + + class Account < ActiveRecord::Base + validates_presence_of :subdomain, :name, :email_address, :password + validates_uniqueness_of :subdomain + validates_acceptance_of :terms_of_service, :on => :create + validates_confirmation_of :password, :email_address, :on => :create + end + + {Learn more}[link:classes/ActiveRecord/Validations.html] + +* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc). + + class Person < ActiveRecord::Base + def before_destroy # is called just before Person#destroy + CreditCard.find(credit_card_id).destroy + end + end + + class Account < ActiveRecord::Base + after_find :eager_load, 'self.class.announce(#{id})' + end + + {Learn more}[link:classes/ActiveRecord/Callbacks.html] + + +* Observers for the entire lifecycle + + class CommentObserver < ActiveRecord::Observer + def after_create(comment) # is called just after Comment#save + Notifications.deliver_new_comment("david@loudthinking.com", comment) + end + end + + {Learn more}[link:classes/ActiveRecord/Observer.html] + + +* Inheritance hierarchies + + class Company < ActiveRecord::Base; end + class Firm < Company; end + class Client < Company; end + class PriorityClient < Client; end + + {Learn more}[link:classes/ActiveRecord/Base.html] + + +* Transactions + + # Database transaction + Account.transaction do + david.withdrawal(100) + mary.deposit(100) + end + + {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html] + + +* Reflections on columns, associations, and aggregations + + reflection = Firm.reflect_on_association(:clients) + reflection.klass # => Client (class) + Firm.columns # Returns an array of column descriptors for the firms table + + {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] + + +* Direct manipulation (instead of service invocation) + + So instead of (Hibernate[http://www.hibernate.org/] example): + + long pkId = 1234; + DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); + // something interesting involving a cat... + sess.save(cat); + sess.flush(); // force the SQL INSERT + + Active Record lets you: + + pkId = 1234 + cat = Cat.find(pkId) + # something even more interesting involving the same cat... + cat.save + + {Learn more}[link:classes/ActiveRecord/Base.html] + + +* Database abstraction through simple adapters (~100 lines) with a shared connector + + ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") + + ActiveRecord::Base.establish_connection( + :adapter => "mysql", + :host => "localhost", + :username => "me", + :password => "secret", + :database => "activerecord" + ) + + {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for + MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. + + +* Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc] + + ActiveRecord::Base.logger = Logger.new(STDOUT) + ActiveRecord::Base.logger = Log4r::Logger.new("Application Log") + + +* Database agnostic schema management with Migrations + + class AddSystemSettings < ActiveRecord::Migration + def self.up + create_table :system_settings do |t| + t.string :name + t.string :label + t.text :value + t.string :type + t.integer :position + end + + SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1 + end + + def self.down + drop_table :system_settings + end + end + + {Learn more}[link:classes/ActiveRecord/Migration.html] + +== Simple example (1/2): Defining tables and classes (using MySQL) + +Data definitions are specified only in the database. Active Record queries the database for +the column names (that then serves to determine which attributes are valid) on regular +object instantiation through the new constructor and relies on the column names in the rows +with the finders. + + # CREATE TABLE companies ( + # id int(11) unsigned NOT NULL auto_increment, + # client_of int(11), + # name varchar(255), + # type varchar(100), + # PRIMARY KEY (id) + # ) + +Active Record automatically links the "Company" object to the "companies" table + + class Company < ActiveRecord::Base + has_many :people, :class_name => "Person" + end + + class Firm < Company + has_many :clients + + def people_with_all_clients + clients.inject([]) { |people, client| people + client.people } + end + end + +The foreign_key is only necessary because we didn't use "firm_id" in the data definition + + class Client < Company + belongs_to :firm, :foreign_key => "client_of" + end + + # CREATE TABLE people ( + # id int(11) unsigned NOT NULL auto_increment, + # name text, + # company_id text, + # PRIMARY KEY (id) + # ) + +Active Record will also automatically link the "Person" object to the "people" table + + class Person < ActiveRecord::Base + belongs_to :company + end + +== Simple example (2/2): Using the domain + +Picking a database connection for all the Active Records + + ActiveRecord::Base.establish_connection( + :adapter => "mysql", + :host => "localhost", + :username => "me", + :password => "secret", + :database => "activerecord" + ) + +Create some fixtures + + firm = Firm.new("name" => "Next Angle") + # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") + firm.save + + client = Client.new("name" => "37signals", "client_of" => firm.id) + # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") + client.save + +Lots of different finders + + # SQL: SELECT * FROM companies WHERE id = 1 + next_angle = Company.find(1) + + # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' + next_angle = Firm.find(1) + + # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' + next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") + + next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first + +The supertype, Company, will return subtype instances + + Firm === next_angle + +All the dynamic methods added by the has_many macro + + next_angle.clients.empty? # true + next_angle.clients.size # total number of clients + all_clients = next_angle.clients + +Constrained finds makes access security easier when ID comes from a web-app + + # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 + thirty_seven_signals = next_angle.clients.find(2) + +Bi-directional associations thanks to the "belongs_to" macro + + thirty_seven_signals.firm.nil? # true + + +== Philosophy + +Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is +object-relational mapping. The prime directive for this mapping has been to minimize +the amount of code needed to build a real-world domain model. This is made possible +by relying on a number of conventions that make it easy for Active Record to infer +complex relations and structures from a minimal amount of explicit direction. + +Convention over Configuration: +* No XML-files! +* Lots of reflection and run-time extension +* Magic is not inherently a bad word + +Admit the Database: +* Lets you drop down to SQL for odd cases and performance +* Doesn't attempt to duplicate or replace data definitions + + +== Download + +The latest version of Active Record can be installed with Rubygems: + +* gem install activerecord + +Documentation can be found at + +* http://api.rubyonrails.org + + +== License + +Active Record is released under the MIT license. + + +== Support + +The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record +RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says: + + Feel free to submit commits or feature requests. If you send a patch, + remember to update the corresponding unit tests. If fact, I prefer + new feature to be submitted in the form of new unit tests. + +For other information, feel free to ask on the rubyonrails-talk +(http://groups.google.com/group/rubyonrails-talk) mailing list. diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 392b717e0a..d9124c9776 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -172,7 +172,7 @@ Rake::RDocTask.new { |rdoc| rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' - rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG') + rdoc.rdoc_files.include('README.rdoc', 'RUNNING_UNIT_TESTS', 'CHANGELOG') rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.exclude('lib/active_record/vendor/*') rdoc.rdoc_files.include('dev-utils/*.rb') -- cgit v1.2.3 From 6ba7d5e6544d636a763a40d1543f96d0e0bd77d5 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 9 Jul 2010 06:07:00 -0400 Subject: - without the id test is passing even if I change :allow_destroy from 'false' - adding more tests to strengthen the test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/nested_attributes_test.rb | 49 ++++++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index c9ea0d8c40..84ab61f591 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -59,6 +59,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase pirate.save! assert_equal 1, pirate.birds_with_reject_all_blank.count + assert_equal 'Tweetie', pirate.birds_with_reject_all_blank.first.name end def test_should_raise_an_ArgumentError_for_non_existing_associations @@ -74,7 +75,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase ship = pirate.create_ship(:name => 'Nights Dirty Lightning') assert_no_difference('Ship.count') do - pirate.update_attributes(:ship_attributes => { '_destroy' => true }) + pirate.update_attributes(:ship_attributes => { '_destroy' => true, :id => ship.id }) end end @@ -100,7 +101,8 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true } assert_no_difference('Ship.count') { pirate.save! } - # pirate.reject_empty_ships_on_create returns false for saved records + # pirate.reject_empty_ships_on_create returns false for saved pirate records + # in the previous step note that pirate gets saved but ship fails pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true } assert_difference('Ship.count') { pirate.save! } end @@ -266,6 +268,28 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase end assert_equal 'Mayflower', @ship.reload.name end + + def test_should_update_existing_when_update_only_is_true_and_id_is_given + @ship.delete + @ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning') + + assert_no_difference('Ship.count') do + @pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower', :id => @ship.id }) + end + assert_equal 'Mayflower', @ship.reload.name + end + + def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction + Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => true + @ship.delete + @ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning') + + assert_difference('Ship.count', -1) do + @pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower', :id => @ship.id, :_destroy => true }) + end + Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => false + end + end class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase @@ -411,6 +435,27 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase end assert_equal 'Arr', @pirate.reload.catchphrase end + + def test_should_update_existing_when_update_only_is_true_and_id_is_given + @pirate.delete + @pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye') + + assert_no_difference('Pirate.count') do + @ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr', :id => @pirate.id }) + end + assert_equal 'Arr', @pirate.reload.catchphrase + end + + def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction + Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => true + @pirate.delete + @pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye') + + assert_difference('Pirate.count', -1) do + @ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr', :id => @pirate.id, :_destroy => true }) + end + Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => false + end end module NestedAttributesOnACollectionAssociationTests -- cgit v1.2.3 From c0bfa0bfc17f4aa615cd9d1006509e0d84b5692d Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Sat, 10 Jul 2010 12:29:09 -0400 Subject: In nested_attributes when association is not loaded and association record is saved and then in memory record attributes should be saved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5053 state:resolved] Signed-off-by: José Valim --- .../lib/active_record/associations/association_collection.rb | 9 ++++++++- activerecord/test/cases/nested_attributes_test.rb | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 7abb738a74..7100be0245 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -477,7 +477,14 @@ module ActiveRecord callback(:before_add, record) yield(record) if block_given? @target ||= [] unless loaded? - @target << record unless @reflection.options[:uniq] && @target.include?(record) + index = @target.index(record) + unless @reflection.options[:uniq] && index + if index + @target[index] = record + else + @target << record + end + end callback(:after_add, record) set_inverse_instance(record, @owner) record diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 84ab61f591..20bd4f6a76 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -856,6 +856,12 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR @part = @ship.parts.create!(:name => "Mast") @trinket = @part.trinkets.create!(:name => "Necklace") end + + test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do + @ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}] + assert_equal 1, @ship.parts.proxy_target.size + assert_equal 'Deck', @ship.parts[0].name + end test "when grandchild changed in memory, saving parent should save grandchild" do @trinket.name = "changed" -- cgit v1.2.3 From 0057d2df71ec7c2a788acd3b4bade263fc0fe361 Mon Sep 17 00:00:00 2001 From: Szetobo Date: Mon, 12 Jul 2010 09:47:47 +0800 Subject: association load target shouldn't replace records from db if it is already loaded by nested attributes assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5053 state:resolved] Signed-off-by: José Valim --- .../active_record/associations/association_collection.rb | 7 ++++--- activerecord/test/cases/nested_attributes_test.rb | 14 +++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 7100be0245..692badcc5c 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -396,11 +396,12 @@ module ActiveRecord if @target.is_a?(Array) && @target.any? @target = find_target.map do |f| i = @target.index(f) - t = @target.delete_at(i) if i - if t && t.changed? + if i + t = @target.delete_at(i) + keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names) + t.attributes = f.attributes.except(*keys) t else - f.mark_for_destruction if t && t.marked_for_destruction? f end end + @target diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 20bd4f6a76..df09bbd46a 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -862,7 +862,19 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR assert_equal 1, @ship.parts.proxy_target.size assert_equal 'Deck', @ship.parts[0].name end - + + test "if association is not loaded and child doesn't change and I am saving a grandchild then in memory record should be used" do + @ship.parts_attributes=[{:id => @part.id,:trinkets_attributes =>[{:id => @trinket.id, :name => 'Ruby'}]}] + assert_equal 1, @ship.parts.proxy_target.size + assert_equal 'Mast', @ship.parts[0].name + assert_no_difference("@ship.parts[0].trinkets.proxy_target.size") do + @ship.parts[0].trinkets.proxy_target.size + end + assert_equal 'Ruby', @ship.parts[0].trinkets[0].name + @ship.save + assert_equal 'Ruby', @ship.parts[0].trinkets[0].name + end + test "when grandchild changed in memory, saving parent should save grandchild" do @trinket.name = "changed" @ship.save -- cgit v1.2.3 From 96b2516c3cbcf900f2e84163baba3db7cb0e37d9 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Wed, 14 Jul 2010 23:13:56 -0400 Subject: Strengthening the test for nested_attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loading the associate target in nested_attributes should load most recent attributes for child records marked for destruction Signed-off-by: José Valim --- activerecord/test/cases/associations_test.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 4ae776c35a..ff9e9a472a 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -17,7 +17,8 @@ require 'models/tagging' require 'models/person' require 'models/reader' require 'models/parrot' -require 'models/pirate' +require 'models/ship_part' +require 'models/ship' require 'models/treasure' require 'models/price_estimate' require 'models/club' @@ -29,6 +30,24 @@ class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, :computers, :people, :readers + def test_loading_the_association_target_should_keep_child_records_marked_for_destruction + ship = Ship.create!(:name => "The good ship Dollypop") + part = ship.parts.create!(:name => "Mast") + part.mark_for_destruction + ship.parts.send(:load_target) + assert ship.parts[0].marked_for_destruction? + end + + def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction + ship = Ship.create!(:name => "The good ship Dollypop") + part = ship.parts.create!(:name => "Mast") + part.mark_for_destruction + ShipPart.find(part.id).update_attribute(:name, 'Deck') + ship.parts.send(:load_target) + assert_equal 'Deck', ship.parts[0].name + end + + def test_include_with_order_works assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)} assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)} -- cgit v1.2.3 From 01add55d6a6a67fb487afa040998575111511b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 14:44:46 +0200 Subject: Revert "save on parent should not cascade to child unless child changed [#3353 state:open]" Please check Lighthouse for more information. This reverts commit 4a0d7c1a439c6ad8d35bf514761824e51fa07df2. --- activerecord/lib/active_record/autosave_association.rb | 4 +--- activerecord/test/cases/autosave_association_test.rb | 18 ------------------ 2 files changed, 1 insertion(+), 21 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 6af384367f..7517896235 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -372,9 +372,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - if association.new_record? || ( autosave && association.changed? ) - saved = association.save(:validate => !autosave) - end + saved = association.save(:validate => !autosave) if association.new_record? || autosave if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 48479bb429..3b89c12a3f 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -632,8 +632,6 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase end def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent - #association save method only trigged when association is changed - @ship.pirate.catchphrase = "new catch phrase" # Stub the save method of the @ship.pirate instance to destroy and then raise an exception class << @ship.pirate def save(*args) @@ -882,22 +880,6 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?") end - def test_should_not_call_belongs_to_after_save_callbacks_if_no_changes - @ship.attributes = { :name => "Titanic", :pirate_attributes => {:id => @pirate.id} } - #here there are no changes to pirate so if save on ship causes save on pirate - #this callback will fail pirate save.(pirate save shouldn't happen) - @ship.pirate.cancel_save_from_callback = true - @ship.save - assert_equal 'Titanic', @ship.reload.name - end - - def test_should_call_belongs_to_save_if_belongs_to_has_changes - @ship.attributes = { :name => "Titanic", :pirate_attributes => { :catchphrase => 'Jack', :id => @pirate.id} } - @ship.save - assert_equal 'Titanic', @ship.reload.name - assert_equal 'Jack', @pirate.reload.catchphrase - end - def test_should_still_work_without_an_associated_model @pirate.destroy @ship.reload.name = "The Vile Insanity" -- cgit v1.2.3 From 8bb3b634c07a327b9f1c7e21b4d1fcc407737a9f Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Jul 2010 23:52:24 -0400 Subject: Timestamp columns of HABTM join table should record timestamps [#5161 state:resolved] --- .../has_and_belongs_to_many_association.rb | 6 +++- activerecord/lib/active_record/timestamp.rb | 19 ++++++++++-- .../has_and_belongs_to_many_associations_test.rb | 35 +++++++++++++++++++++- activerecord/test/schema/schema.rb | 2 ++ 4 files changed, 57 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 9ec63e3fca..177d7905c7 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -53,7 +53,11 @@ module ActiveRecord when @reflection.association_foreign_key.to_s attrs[relation[column.name]] = record.id else - if record.has_attribute?(column.name) + if record.send(:all_timestamp_attributes).include?(column.name.to_sym) + if record.record_timestamps + attrs[relation[column.name]] = record.send(:current_time_from_proper_timezone) + end + elsif record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) attrs[relation[column.name]] = value unless value.nil? end diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 341cc87be5..6c1e376745 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -39,8 +39,9 @@ module ActiveRecord if record_timestamps current_time = current_time_from_proper_timezone - write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil? - write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil? + timestamp_attributes_for_create.each do |column| + write_attribute(column.to_s, current_time) if respond_to?(column) && self.send(column).nil? + end timestamp_attributes_for_update_in_model.each do |column| write_attribute(column.to_s, current_time) if self.send(column).nil? @@ -65,7 +66,19 @@ module ActiveRecord end def timestamp_attributes_for_update_in_model #:nodoc: - [:updated_at, :updated_on].select { |elem| respond_to?(elem) } + timestamp_attributes_for_update.select { |elem| respond_to?(elem) } + end + + def timestamp_attributes_for_update #:nodoc: + [:updated_at, :updated_on] + end + + def timestamp_attributes_for_create #:nodoc: + [:created_at, :created_on] + end + + def all_timestamp_attributes #:nodoc: + timestamp_attributes_for_update + timestamp_attributes_for_create end def current_time_from_proper_timezone #:nodoc: 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 d4d3d8e43e..d7e9ca0bcb 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 @@ -85,7 +85,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings - def test_should_property_quote_string_primary_keys + def setup_data_for_habtm_case + ActiveRecord::Base.connection.execute('delete from countries_treaties') + country = Country.new(:name => 'India') country.country_id = 'c1' country.save! @@ -93,6 +95,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase treaty = Treaty.new(:name => 'peace') treaty.treaty_id = 't1' country.treaties << treaty + end + + def test_should_property_quote_string_primary_keys + setup_data_for_habtm_case con = ActiveRecord::Base.connection sql = 'select * from countries_treaties' @@ -101,6 +107,33 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal 't1', record[1] end + def test_should_record_timestamp_for_join_table + setup_data_for_habtm_case + + con = ActiveRecord::Base.connection + sql = 'select * from countries_treaties' + record = con.select_rows(sql).last + assert_not_nil record[2] + assert_not_nil record[3] + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2] + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3] + end + + def test_should_record_timestamp_for_join_table_only_if_timestamp_should_be_recorded + begin + Treaty.record_timestamps = false + setup_data_for_habtm_case + + con = ActiveRecord::Base.connection + sql = 'select * from countries_treaties' + record = con.select_rows(sql).last + assert_nil record[2] + assert_nil record[3] + ensure + Treaty.record_timestamps = true + end + end + def test_has_and_belongs_to_many david = Developer.find(1) diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index c4eed256bf..a69e38f414 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -611,6 +611,8 @@ ActiveRecord::Schema.define do create_table :countries_treaties, :force => true, :id => false do |t| t.string :country_id, :null => false t.string :treaty_id, :null => false + t.datetime :created_at + t.datetime :updated_at end except 'SQLite' do -- cgit v1.2.3 From f3e42292a5792f77681a3fbf03bc8cfc75aeb887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 14:50:04 +0200 Subject: Ensure all join table attributes will be in the same timestamp. --- .../has_and_belongs_to_many_association.rb | 32 ++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 177d7905c7..e61af93d1e 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -45,21 +45,23 @@ module ActiveRecord if @reflection.options[:insert_sql] @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record)) else - relation = Arel::Table.new(@reflection.options[:join_table]) + relation = Arel::Table.new(@reflection.options[:join_table]) + timestamps = record_timestamp_columns(record) + timezone = record.send(:current_time_from_proper_timezone) if timestamps.any? + attributes = columns.inject({}) do |attrs, column| - case column.name.to_s + name = column.name + case name.to_s when @reflection.primary_key_name.to_s - attrs[relation[column.name]] = @owner.id + attrs[relation[name]] = @owner.id when @reflection.association_foreign_key.to_s - attrs[relation[column.name]] = record.id + attrs[relation[name]] = record.id + when *timestamps + attrs[relation[name]] = timezone else - if record.send(:all_timestamp_attributes).include?(column.name.to_sym) - if record.record_timestamps - attrs[relation[column.name]] = record.send(:current_time_from_proper_timezone) - end - elsif record.has_attribute?(column.name) - value = @owner.send(:quote_value, record[column.name], column) - attrs[relation[column.name]] = value unless value.nil? + if record.has_attribute?(name) + value = @owner.send(:quote_value, record[name], column) + attrs[relation[name]] = value unless value.nil? end end attrs @@ -121,6 +123,14 @@ module ActiveRecord build_record(attributes, &block) end end + + def record_timestamp_columns(record) + if record.record_timestamps + record.send(:all_timestamp_attributes).map(&:to_s) + else + [] + end + end end end end -- cgit v1.2.3 From c96a50539121c4a722f354220c7f0e314b24804d Mon Sep 17 00:00:00 2001 From: Thiago Pradi Date: Tue, 20 Jul 2010 14:59:10 -0300 Subject: rake db:seed should check if the database have pending migrations [#5163 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 5024787c3c..2c17c74ab4 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -274,7 +274,7 @@ namespace :db do task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] desc 'Load the seed data from db/seeds.rb' - task :seed => :environment do + task :seed => 'db:abort_if_pending_migrations' do seed_file = File.join(Rails.root, 'db', 'seeds.rb') load(seed_file) if File.exist?(seed_file) end -- cgit v1.2.3 From 992711a86bc4ddd4460f9067e49eea36b37ca94f Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sun, 18 Jul 2010 07:54:48 -0400 Subject: update_attribute should not update readonly attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5106 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/persistence.rb | 2 ++ activerecord/test/cases/persistence_test.rb | 8 +++++++- activerecord/test/fixtures/minivans.yml | 1 + activerecord/test/models/minivan.rb | 5 ++++- activerecord/test/schema/schema.rb | 1 + 5 files changed, 15 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 7ec443ccc7..e2d92c860c 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -105,6 +105,8 @@ module ActiveRecord # Updates a single attribute and saves the record without going through the normal validation procedure # or callbacks. This is especially useful for boolean flags on existing records. def update_attribute(name, value) + raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s + changes = record_update_timestamps || {} if name diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 4ea5df0945..54fe991648 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -17,13 +17,14 @@ require 'models/comment' require 'models/minimalistic' require 'models/warehouse_thing' require 'models/parrot' +require 'models/minivan' require 'models/loose_person' require 'rexml/document' require 'active_support/core_ext/exception' class PersistencesTest < ActiveRecord::TestCase - fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts + fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans def test_create topic = Topic.new @@ -220,6 +221,11 @@ class PersistencesTest < ActiveRecord::TestCase assert !Topic.find(1).approved? end + def test_update_attribute_for_readonly_attribute + minivan = Minivan.find('m1') + assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') } + end + def test_update_attribute_with_one_changed_and_one_updated t = Topic.order('id').limit(1).first title, author_name = t.title, t.author_name diff --git a/activerecord/test/fixtures/minivans.yml b/activerecord/test/fixtures/minivans.yml index e7a2ab77eb..f1224a4c1a 100644 --- a/activerecord/test/fixtures/minivans.yml +++ b/activerecord/test/fixtures/minivans.yml @@ -2,3 +2,4 @@ cool_first: minivan_id: m1 name: my_minivan speedometer_id: s1 + color: blue diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb index c753319a20..602438d16f 100644 --- a/activerecord/test/models/minivan.rb +++ b/activerecord/test/models/minivan.rb @@ -3,4 +3,7 @@ class Minivan < ActiveRecord::Base belongs_to :speedometer has_one :dashboard, :through => :speedometer -end \ No newline at end of file + + attr_readonly :color + +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a69e38f414..f3fd37cd61 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -300,6 +300,7 @@ ActiveRecord::Schema.define do t.string :minivan_id t.string :name t.string :speedometer_id + t.string :color end create_table :minimalistics, :force => true do |t| -- cgit v1.2.3 From d77c3b669ce052234868b3d8e4f066e1baf9dbd5 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Mon, 19 Jul 2010 21:26:57 -0400 Subject: eagerly loaded association records should respect default_scope [#2931 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/association_preload.rb | 2 +- activerecord/test/cases/relation_scoping_test.rb | 18 ++++++++++++++++-- activerecord/test/models/person.rb | 1 + activerecord/test/models/reference.rb | 5 +++++ 4 files changed, 23 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 9172ab2a20..08601f8ef9 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -378,7 +378,7 @@ module ActiveRecord :order => preload_options[:order] || options[:order] } - reflection.klass.unscoped.apply_finder_options(find_options).to_a + reflection.klass.scoped.apply_finder_options(find_options).to_a end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index a5a3b3ef38..fdf4536bc0 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -5,6 +5,8 @@ require 'models/developer' require 'models/project' require 'models/comment' require 'models/category' +require 'models/person' +require 'models/reference' class RelationScopingTest < ActiveRecord::TestCase fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects @@ -218,7 +220,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase end class HasManyScopingTest< ActiveRecord::TestCase - fixtures :comments, :posts + fixtures :comments, :posts, :people, :references def setup @welcome = Post.find(1) @@ -250,6 +252,18 @@ class HasManyScopingTest< ActiveRecord::TestCase assert_equal 'a comment...', @welcome.comments.what_are_you end end + + def test_should_maintain_default_scope_on_associations + person = people(:michael) + magician = BadReference.find(1) + assert_equal [magician], people(:michael).bad_references + end + + def test_should_maintain_default_scope_on_eager_loaded_associations + michael = Person.where(:id => people(:michael).id).includes(:bad_references).first + magician = BadReference.find(1) + assert_equal [magician], michael.bad_references + end end class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase @@ -399,4 +413,4 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary end -end \ No newline at end of file +end diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 2a73b1ee01..be7463f7c8 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -4,6 +4,7 @@ class Person < ActiveRecord::Base has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments, :conditions => 'comments.id is null' has_many :references + has_many :bad_references has_many :jobs, :through => :references has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true] has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id' diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb index 479e8b72c6..4a17c936f5 100644 --- a/activerecord/test/models/reference.rb +++ b/activerecord/test/models/reference.rb @@ -2,3 +2,8 @@ class Reference < ActiveRecord::Base belongs_to :person belongs_to :job end + +class BadReference < ActiveRecord::Base + self.table_name ='references' + default_scope :conditions => {:favourite => false } +end -- cgit v1.2.3 From 71312443133c6bbcc518d594789f19a213369f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 21 Jul 2010 15:06:23 +0200 Subject: Ensure default_scope can be overwriten by association conditions. --- activerecord/test/cases/relation_scoping_test.rb | 5 +++++ activerecord/test/models/person.rb | 1 + 2 files changed, 6 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index fdf4536bc0..a50a4d4165 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -259,6 +259,11 @@ class HasManyScopingTest< ActiveRecord::TestCase assert_equal [magician], people(:michael).bad_references end + def test_should_default_scope_on_associations_is_overriden_by_association_conditions + person = people(:michael) + assert_equal [], people(:michael).fixed_bad_references + end + def test_should_maintain_default_scope_on_eager_loaded_associations michael = Person.where(:id => people(:michael).id).includes(:bad_references).first magician = BadReference.find(1) diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index be7463f7c8..951ec93c53 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -5,6 +5,7 @@ class Person < ActiveRecord::Base has_many :references has_many :bad_references + has_many :fixed_bad_references, :conditions => { :favourite => true }, :class_name => 'BadReference' has_many :jobs, :through => :references has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true] has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id' -- cgit v1.2.3 From 659e3b02ab98c8f043dfb02d997758d122034181 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Jul 2010 09:51:06 -0400 Subject: renaming tests by removing proxy_options from names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/named_scope_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index dc85b395d3..7c037b20c5 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -270,27 +270,27 @@ class NamedScopeTest < ActiveRecord::TestCase assert Topic.base.many? end - def test_should_build_with_proxy_options + def test_should_build_on_top_of_named_scope topic = Topic.approved.build({}) assert topic.approved end - def test_should_build_new_with_proxy_options + def test_should_build_new_on_top_of_named_scope topic = Topic.approved.new assert topic.approved end - def test_should_create_with_proxy_options + def test_should_create_on_top_of_named_scope topic = Topic.approved.create({}) assert topic.approved end - def test_should_create_with_bang_with_proxy_options + def test_should_create_with_bang_on_top_of_named_scope topic = Topic.approved.create!({}) assert topic.approved end - def test_should_build_with_proxy_options_chained + def test_should_build_on_top_of_chained_named_scopes topic = Topic.approved.by_lifo.build({}) assert topic.approved assert_equal 'lifo', topic.author_name -- cgit v1.2.3 From ef5ae60a07c7d45855a9a2a4b695f153ef9faa79 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Jul 2010 11:04:21 -0300 Subject: Make use of tap to return a previously used var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/associations/association_collection.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 692badcc5c..f346a19a3a 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -397,10 +397,10 @@ module ActiveRecord @target = find_target.map do |f| i = @target.index(f) if i - t = @target.delete_at(i) - keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names) - t.attributes = f.attributes.except(*keys) - t + @target.delete_at(i).tap do |t| + keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names) + t.attributes = f.attributes.except(*keys) + end else f end -- cgit v1.2.3 From d16c5cc99b4ac5a5517b643aabb3b31bf0f0f1b6 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 01:00:01 +0800 Subject: Change some missing README -> README.rdoc --- activerecord/activerecord.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index ce10404feb..67d521d56b 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -14,12 +14,12 @@ Gem::Specification.new do |s| s.homepage = 'http://www.rubyonrails.org' s.rubyforge_project = 'activerecord' - s.files = Dir['CHANGELOG', 'README', 'examples/**/*', 'lib/**/*'] + s.files = Dir['CHANGELOG', 'README.rdoc', 'examples/**/*', 'lib/**/*'] s.require_path = 'lib' s.has_rdoc = true - s.extra_rdoc_files = %w( README ) - s.rdoc_options.concat ['--main', 'README'] + s.extra_rdoc_files = %w( README.rdoc ) + s.rdoc_options.concat ['--main', 'README.rdoc'] s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) -- cgit v1.2.3 From b0c7dee4f2744286069c5ff4e65c3d081a4cb24a Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Mon, 19 Jul 2010 22:53:54 -0400 Subject: removing unused models from tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5153 state:resolved] Signed-off-by: José Valim --- .../test/cases/associations/belongs_to_associations_test.rb | 2 -- activerecord/test/cases/associations/callbacks_test.rb | 2 -- .../test/cases/associations/cascaded_eager_loading_test.rb | 1 - .../associations/has_and_belongs_to_many_associations_test.rb | 6 ------ activerecord/test/cases/associations_test.rb | 11 ----------- activerecord/test/cases/finder_test.rb | 1 - activerecord/test/cases/method_scoping_test.rb | 3 +-- activerecord/test/cases/persistence_test.rb | 7 ------- activerecord/test/cases/query_cache_test.rb | 2 -- activerecord/test/cases/relations_test.rb | 1 - activerecord/test/cases/transaction_callbacks_test.rb | 1 - .../validations/i18n_generate_message_validation_test.rb | 1 - activerecord/test/cases/validations_test.rb | 5 ----- activerecord/test/cases/xml_serialization_test.rb | 1 - 14 files changed, 1 insertion(+), 43 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index fb1e6e7e70..4d5769d173 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -5,8 +5,6 @@ require 'models/company' require 'models/topic' require 'models/reply' require 'models/computer' -require 'models/customer' -require 'models/order' require 'models/post' require 'models/author' require 'models/tag' diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb index 91b1af125e..15537d6940 100644 --- a/activerecord/test/cases/associations/callbacks_test.rb +++ b/activerecord/test/cases/associations/callbacks_test.rb @@ -1,8 +1,6 @@ require "cases/helper" require 'models/post' -require 'models/comment' require 'models/author' -require 'models/category' require 'models/project' require 'models/developer' diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index f5d59c9a43..b93e49613d 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -2,7 +2,6 @@ require "cases/helper" require 'models/post' require 'models/comment' require 'models/author' -require 'models/category' require 'models/categorization' require 'models/company' require 'models/topic' 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 d7e9ca0bcb..6b4a1d9408 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 @@ -2,20 +2,14 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' -require 'models/topic' -require 'models/reply' -require 'models/computer' require 'models/customer' require 'models/order' require 'models/categorization' require 'models/category' require 'models/post' require 'models/author' -require 'models/comment' require 'models/tag' require 'models/tagging' -require 'models/person' -require 'models/reader' require 'models/parrot' require 'models/pirate' require 'models/treasure' diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index ff9e9a472a..a1c794c084 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -2,11 +2,6 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' -require 'models/topic' -require 'models/reply' -require 'models/computer' -require 'models/customer' -require 'models/order' require 'models/categorization' require 'models/category' require 'models/post' @@ -19,12 +14,6 @@ require 'models/reader' require 'models/parrot' require 'models/ship_part' require 'models/ship' -require 'models/treasure' -require 'models/price_estimate' -require 'models/club' -require 'models/member' -require 'models/membership' -require 'models/sponsor' class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 860d330a7f..a107c1a474 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -10,7 +10,6 @@ require 'models/entrant' require 'models/project' require 'models/developer' require 'models/customer' -require 'models/job' class DynamicFinderMatchTest < ActiveRecord::TestCase def test_find_no_match diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 4e8ce1dac1..774b50e2e4 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -8,7 +8,6 @@ require 'models/author' require 'models/developer' require 'models/project' require 'models/comment' -require 'models/category' class MethodScopingTest < ActiveRecord::TestCase fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects @@ -543,4 +542,4 @@ class NestedScopingTest < ActiveRecord::TestCase assert_equal 1, scoped_authors.size assert_equal authors(:david).attributes, scoped_authors.first.attributes end -end \ No newline at end of file +end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 54fe991648..1cc3a337c3 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -5,15 +5,8 @@ require 'models/topic' require 'models/reply' require 'models/category' require 'models/company' -require 'models/customer' require 'models/developer' require 'models/project' -require 'models/default' -require 'models/auto_id' -require 'models/column_name' -require 'models/subscriber' -require 'models/keyboard' -require 'models/comment' require 'models/minimalistic' require 'models/warehouse_thing' require 'models/parrot' diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 68abca70b3..f0d97a00d0 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -1,8 +1,6 @@ require "cases/helper" require 'models/topic' -require 'models/reply' require 'models/task' -require 'models/course' require 'models/category' require 'models/post' diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ffde8daa07..cb252d56fe 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1,5 +1,4 @@ require "cases/helper" -require 'models/tag' require 'models/tagging' require 'models/post' require 'models/topic' diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index df123c9de8..ffc2cd638f 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -1,6 +1,5 @@ require "cases/helper" require 'models/topic' -require 'models/reply' class TransactionCallbacksTest < ActiveRecord::TestCase self.use_transactional_fixtures = false diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb index 454e42ed37..628029f8df 100644 --- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb @@ -1,6 +1,5 @@ require "cases/helper" require 'models/topic' -require 'models/reply' class I18nGenerateMessageValidationTest < ActiveRecord::TestCase def setup diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 3f1b0e333f..fd771ef4be 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -4,11 +4,6 @@ require 'models/topic' require 'models/reply' require 'models/person' require 'models/developer' -require 'models/warehouse_thing' -require 'models/guid' -require 'models/owner' -require 'models/pet' -require 'models/event' require 'models/parrot' require 'models/company' diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb index 751946ffc5..b11b340e94 100644 --- a/activerecord/test/cases/xml_serialization_test.rb +++ b/activerecord/test/cases/xml_serialization_test.rb @@ -2,7 +2,6 @@ require "cases/helper" require 'models/contact' require 'models/post' require 'models/author' -require 'models/tagging' require 'models/comment' require 'models/company_in_module' -- cgit v1.2.3 From 5c137939a7b9d32a46eb07c90d6b535c7c67db64 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Jul 2010 06:56:20 -0400 Subject: expanded comment for update_attribute method --- activerecord/lib/active_record/persistence.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index e2d92c860c..b587abd5d0 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -102,8 +102,15 @@ module ActiveRecord became end - # Updates a single attribute and saves the record without going through the normal validation procedure - # or callbacks. This is especially useful for boolean flags on existing records. + # Updates a single attribute and saves the record. + # This is especially useful for boolean flags on existing records. Also note that + # + # * validation is skipped + # * No callbacks are invoked + # * updated_at/updated_on column is updated if that column is available + # * does not work on associations + # * does not work on attr_accessor attributes. The attribute that is being updated must be column name. + # def update_attribute(name, value) raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s -- cgit v1.2.3 From 1f8edb996b97da0df6ed0f0e0ceffef9d84119ce Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Jul 2010 15:34:43 -0700 Subject: adding tests for sessions and clearing up warnings --- activerecord/lib/active_record/session_store.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 7ea7fb5c51..0c2db4156b 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -62,7 +62,7 @@ module ActiveRecord class << self def data_column_size_limit - @data_column_size_limit ||= columns_hash[@@data_column_name].limit + @data_column_size_limit ||= columns_hash[data_column_name].limit end # Hook to set up sessid compatibility. @@ -83,8 +83,8 @@ module ActiveRecord connection.execute <<-end_sql CREATE TABLE #{table_name} ( id INTEGER PRIMARY KEY, - #{connection.quote_column_name('session_id')} TEXT UNIQUE, - #{connection.quote_column_name(@@data_column_name)} TEXT(255) + #{connection.quote_column_name(session_id_column)} TEXT UNIQUE, + #{connection.quote_column_name(data_column_name)} TEXT(255) ) end_sql end @@ -94,6 +94,10 @@ module ActiveRecord end private + def session_id_column + 'session_id' + end + # Compatibility with tables using sessid instead of session_id. def setup_sessid_compatibility! # Reset column info since it may be stale. @@ -106,6 +110,8 @@ module ActiveRecord define_method(:session_id) { sessid } define_method(:session_id=) { |session_id| self.sessid = session_id } else + class << self; remove_method :find_by_session_id; end + def self.find_by_session_id(session_id) find :first, :conditions => {:session_id=>session_id} end -- cgit v1.2.3 From bdbe390a98acbebcfd48dca28c79d3296c8d3981 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Jul 2010 16:01:41 -0700 Subject: initializing instance variables --- activerecord/lib/active_record/session_store.rb | 7 ++- .../test/cases/session_store/session_test.rb | 63 ++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 activerecord/test/cases/session_store/session_test.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 0c2db4156b..feb9e2e8f6 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -119,6 +119,11 @@ module ActiveRecord end end + def initialize(attributes = nil) + @data = nil + super + end + # Lazy-unmarshal session state. def data @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {} @@ -128,7 +133,7 @@ module ActiveRecord # Has the session been loaded yet? def loaded? - !!@data + @data end private diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb new file mode 100644 index 0000000000..bb2e1ec97a --- /dev/null +++ b/activerecord/test/cases/session_store/session_test.rb @@ -0,0 +1,63 @@ +require 'cases/helper' +require 'action_dispatch' +require 'active_record/session_store' + +module ActiveRecord + class SessionStore + class SessionTest < ActiveRecord::TestCase + def setup + super + Session.drop_table! if Session.table_exists? + end + + def test_data_column_name + # default column name is 'data' + assert_equal 'data', Session.data_column_name + end + + def test_table_name + assert_equal 'sessions', Session.table_name + end + + def test_create_table! + assert !Session.table_exists? + Session.create_table! + assert Session.table_exists? + Session.drop_table! + assert !Session.table_exists? + end + + def test_find_by_sess_id_compat + klass = Class.new(Session) do + def self.session_id_column + 'sessid' + end + end + klass.create_table! + + assert klass.columns_hash['sessid'], 'sessid column exists' + session = klass.new(:data => 'hello') + session.sessid = 100 + session.save! + + found = klass.find_by_session_id(100) + assert_equal session, found + assert_equal session.sessid, found.session_id.to_i + ensure + klass.drop_table! + end + + def test_find_by_session_id + Session.create_table! + s = Session.create!(:data => 'world', :session_id => 10) + assert_equal s, Session.find_by_session_id(10) + Session.drop_table! + end + + def test_loaded? + s = Session.new + assert !s.loaded?, 'session is not loaded' + end + end + end +end -- cgit v1.2.3 From ba0d2a9ce374df69647a8280459d59000ce43188 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Jul 2010 16:36:22 -0700 Subject: starting sql bypass test, fixing create_table and drop_table! --- activerecord/lib/active_record/session_store.rb | 8 ++++---- .../test/cases/session_store/sql_bypass.rb | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 activerecord/test/cases/session_store/sql_bypass.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index feb9e2e8f6..cc8d697dde 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -217,17 +217,17 @@ module ActiveRecord end def create_table! - @@connection.execute <<-end_sql + connection.execute <<-end_sql CREATE TABLE #{table_name} ( id INTEGER PRIMARY KEY, - #{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE, - #{@@connection.quote_column_name(data_column)} TEXT + #{connection.quote_column_name(session_id_column)} TEXT UNIQUE, + #{connection.quote_column_name(data_column)} TEXT ) end_sql end def drop_table! - @@connection.execute "DROP TABLE #{table_name}" + connection.execute "DROP TABLE #{table_name}" end end diff --git a/activerecord/test/cases/session_store/sql_bypass.rb b/activerecord/test/cases/session_store/sql_bypass.rb new file mode 100644 index 0000000000..5484c34f7d --- /dev/null +++ b/activerecord/test/cases/session_store/sql_bypass.rb @@ -0,0 +1,22 @@ +require 'cases/helper' +require 'action_dispatch' +require 'active_record/session_store' + +module ActiveRecord + class SessionStore + class SqlBypassTest < ActiveRecord::TestCase + def setup + super + Session.drop_table! if Session.table_exists? + end + + def test_create_table + assert !Session.table_exists? + SqlBypass.create_table! + assert Session.table_exists? + SqlBypass.drop_table! + assert !Session.table_exists? + end + end + end +end -- cgit v1.2.3 From 4e6cf429a16ce824b18bb8f494dbcbf5931a615d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Jul 2010 16:45:38 -0700 Subject: readability is hip --- activerecord/lib/active_record/session_store.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index cc8d697dde..a0a9368c12 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -238,8 +238,10 @@ module ActiveRecord # telling us to postpone unmarshaling until the data is requested. # We need to handle a normal data attribute in case of a new record. def initialize(attributes) - @session_id, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data] - @new_record = @marshaled_data.nil? + @session_id = attributes[:session_id] + @data = attributes[:data] + @marshaled_data = attributes[:marshaled_data] + @new_record = @marshaled_data.nil? end def new_record? -- cgit v1.2.3 From f5de56f53768bf07a337c9bf32b397a9d3e61cf0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Jul 2010 16:52:15 -0700 Subject: reducing function calls in the session store --- activerecord/lib/active_record/session_store.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index a0a9368c12..5de812962a 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -138,17 +138,17 @@ module ActiveRecord private def marshal_data! - return false if !loaded? - write_attribute(@@data_column_name, self.class.marshal(self.data)) + return false unless loaded? + write_attribute(@@data_column_name, self.class.marshal(data)) end # Ensures that the data about to be stored in the database is not # larger than the data storage column. Raises # ActionController::SessionOverflowError. def raise_on_session_data_overflow! - return false if !loaded? + return false unless loaded? limit = self.class.data_column_size_limit - if loaded? and limit and read_attribute(@@data_column_name).size > limit + if limit and read_attribute(@@data_column_name).size > limit raise ActionController::SessionOverflowError end end @@ -265,7 +265,7 @@ module ActiveRecord end def save - return false if !loaded? + return false unless loaded? marshaled_data = self.class.marshal(data) if @new_record -- cgit v1.2.3 From 87a319cd0c51cbc8f6bc8e3d11ab9e30a96ac628 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Jul 2010 16:55:04 -0700 Subject: testing new_record? and converting to an attribute --- activerecord/lib/active_record/session_store.rb | 8 +++----- activerecord/test/cases/session_store/sql_bypass.rb | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 5de812962a..17f09d64b0 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -231,7 +231,9 @@ module ActiveRecord end end - attr_reader :session_id + attr_reader :session_id, :new_record + alias :new_record? :new_record + attr_writer :data # Look for normal and marshaled data, self.find_by_session_id's way of @@ -244,10 +246,6 @@ module ActiveRecord @new_record = @marshaled_data.nil? end - def new_record? - @new_record - end - # Lazy-unmarshal session state. def data unless @data diff --git a/activerecord/test/cases/session_store/sql_bypass.rb b/activerecord/test/cases/session_store/sql_bypass.rb index 5484c34f7d..cea5e4eabe 100644 --- a/activerecord/test/cases/session_store/sql_bypass.rb +++ b/activerecord/test/cases/session_store/sql_bypass.rb @@ -17,6 +17,11 @@ module ActiveRecord SqlBypass.drop_table! assert !Session.table_exists? end + + def test_new_record? + s = SqlBypass.new :data => 'foo', :session_id => 10 + assert s.new_record?, 'this is a new record!' + end end end end -- cgit v1.2.3 From a78547a9c66eeaa3dbf5e2612483ca172fb137e9 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Jul 2010 16:59:23 -0700 Subject: testing loaded? method and reducing funcalls --- activerecord/lib/active_record/session_store.rb | 2 +- activerecord/test/cases/session_store/sql_bypass.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 17f09d64b0..b352f9529d 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -259,7 +259,7 @@ module ActiveRecord end def loaded? - !!@data + @data end def save diff --git a/activerecord/test/cases/session_store/sql_bypass.rb b/activerecord/test/cases/session_store/sql_bypass.rb index cea5e4eabe..6c4a70b9fe 100644 --- a/activerecord/test/cases/session_store/sql_bypass.rb +++ b/activerecord/test/cases/session_store/sql_bypass.rb @@ -22,6 +22,16 @@ module ActiveRecord s = SqlBypass.new :data => 'foo', :session_id => 10 assert s.new_record?, 'this is a new record!' end + + def test_not_loaded? + s = SqlBypass.new({}) + assert !s.loaded?, 'it is not loaded' + end + + def test_loaded? + s = SqlBypass.new :data => 'hello' + assert s.loaded?, 'it is loaded' + end end end end -- cgit v1.2.3 From 89b5e79632c4f0b18099faa846e45741b7c5e700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Sun, 18 Jul 2010 14:58:40 +0200 Subject: revise download/installation/support sections in READMEs - don't reference ancient gem versions - don't link to old API doc subdomains - point to GitHub instead of RubyForge - point to Lighthouse account for support --- activerecord/README.rdoc | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index 0446180207..efa3c7e7ac 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -307,15 +307,15 @@ Admit the Database: * Doesn't attempt to duplicate or replace data definitions -== Download +== Download and installation The latest version of Active Record can be installed with Rubygems: -* gem install activerecord + % [sudo] gem install activerecord -Documentation can be found at +Source code can be downloaded as part of the Rails project on GitHub -* http://api.rubyonrails.org +* http://github.com/rails/rails/tree/master/activerecord/ == License @@ -325,12 +325,10 @@ Active Record is released under the MIT license. == Support -The Active Record homepage is http://www.rubyonrails.com. You can find the Active Record -RubyForge page at http://rubyforge.org/projects/activerecord. And as Jim from Rake says: +API documentation is at - Feel free to submit commits or feature requests. If you send a patch, - remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests. +* http://api.rubyonrails.com -For other information, feel free to ask on the rubyonrails-talk -(http://groups.google.com/group/rubyonrails-talk) mailing list. +Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here: + +* https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets -- cgit v1.2.3 From d5ee17ed200a4cb0481e99c4d40d9b584418a520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 19 Jul 2010 13:55:18 +0200 Subject: improve Active Record README - revise introductory text - improve examples - don't claim that Oracle, SQL Server, or DB2 are supported - remove lengthy "simple example" --- activerecord/README.rdoc | 222 ++++++++++++----------------------------------- 1 file changed, 55 insertions(+), 167 deletions(-) (limited to 'activerecord') diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index efa3c7e7ac..8dbd6c82b5 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -1,49 +1,52 @@ -= Active Record -- Object-relation mapping put on rails += Active Record -- Object-relational mapping put on rails -Active Record connects business objects and database tables to create a persistable -domain model where logic and data are presented in one wrapping. It's an implementation -of the object-relational mapping (ORM) pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] -by the same name as described by Martin Fowler: +Active Record connects classes to relational database tables to establish an +almost zero-configuration persistence layer for applications. The library +provides a base class that, when subclassed, sets up a mapping between the new +class and an existing table in the database. In context of an application, +these classes are commonly referred to as *models*. Models can also be +connected to other models; this is done by defining *associations*. - "An object that wraps a row in a database table or view, encapsulates - the database access, and adds domain logic on that data." +Active Record relies heavily on naming in that it uses class and association +names to establish mappings between respective database tables and foreign key +columns. Although these mappings can be defined explicitly, it's recommended +to follow naming conventions, especially when getting started with the +library. -Active Record's main contribution to the pattern is to relieve the original of two stunting problems: -lack of associations and inheritance. By adding a simple domain language-like set of macros to describe -the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the -gap of functionality between the data mapper and active record approach. - -A short rundown of the major features: +A short rundown of some of the major features: * Automated mapping between classes and tables, attributes and columns. - class Product < ActiveRecord::Base; end + class Product < ActiveRecord::Base + end - ...is automatically mapped to the table named "products", such as: + The Product class is automatically mapped to the table named "products", + which might look like this: CREATE TABLE products ( id int(11) NOT NULL auto_increment, name varchar(255), PRIMARY KEY (id) ); - - ...which again gives Product#name and Product#name=(new_name) + + This would also define the following accessors: `Product#name` and + `Product#name=(new_name)` {Learn more}[link:classes/ActiveRecord/Base.html] -* Associations between objects controlled by simple meta-programming macros. +* Associations between objects defined by simple class methods. class Firm < ActiveRecord::Base has_many :clients has_one :account - belongs_to :conglomorate + belongs_to :conglomerate end {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html] -* Aggregations of value objects controlled by simple meta-programming macros. +* Aggregations of value objects. class Account < ActiveRecord::Base composed_of :balance, :class_name => "Money", @@ -65,23 +68,19 @@ A short rundown of the major features: end {Learn more}[link:classes/ActiveRecord/Validations.html] - -* Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc). - class Person < ActiveRecord::Base - def before_destroy # is called just before Person#destroy - CreditCard.find(credit_card_id).destroy - end - end - class Account < ActiveRecord::Base - after_find :eager_load, 'self.class.announce(#{id})' +* Callbacks available for the entire lifecycle (instantiation, saving, destroying, validating, etc.) + + class Person < ActiveRecord::Base + before_destroy :invalidate_payment_plan + # the `invalidate_payment_plan` method gets called just before Person#destroy end {Learn more}[link:classes/ActiveRecord/Callbacks.html] -* Observers for the entire lifecycle +* Observers that react to changes in a model class CommentObserver < ActiveRecord::Observer def after_create(comment) # is called just after Comment#save @@ -122,40 +121,24 @@ A short rundown of the major features: {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html] -* Direct manipulation (instead of service invocation) +* Database abstraction through simple adapters - So instead of (Hibernate[http://www.hibernate.org/] example): + # connect to SQLite3 + ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "dbfile.sqlite3") - long pkId = 1234; - DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) ); - // something interesting involving a cat... - sess.save(cat); - sess.flush(); // force the SQL INSERT + # connect to MySQL with authentication + ActiveRecord::Base.establish_connection( + :adapter => "mysql", + :host => "localhost", + :username => "me", + :password => "secret", + :database => "activerecord" + ) - Active Record lets you: - - pkId = 1234 - cat = Cat.find(pkId) - # something even more interesting involving the same cat... - cat.save - - {Learn more}[link:classes/ActiveRecord/Base.html] - - -* Database abstraction through simple adapters (~100 lines) with a shared connector - - ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile") - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - - {Learn more}[link:classes/ActiveRecord/Base.html#M000081] and read about the built-in support for - MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], SQLite[link:classes/ActiveRecord/ConnectionAdapters/SQLiteAdapter.html], Oracle[link:classes/ActiveRecord/ConnectionAdapters/OracleAdapter.html], SQLServer[link:classes/ActiveRecord/ConnectionAdapters/SQLServerAdapter.html], and DB2[link:classes/ActiveRecord/ConnectionAdapters/DB2Adapter.html]. + {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for + MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html], + PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and + SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html]. * Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc] @@ -169,11 +152,11 @@ A short rundown of the major features: class AddSystemSettings < ActiveRecord::Migration def self.up create_table :system_settings do |t| - t.string :name - t.string :label - t.text :value - t.string :type - t.integer :position + t.string :name + t.string :label + t.text :value + t.string :type + t.integer :position end SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1 @@ -186,110 +169,15 @@ A short rundown of the major features: {Learn more}[link:classes/ActiveRecord/Migration.html] -== Simple example (1/2): Defining tables and classes (using MySQL) - -Data definitions are specified only in the database. Active Record queries the database for -the column names (that then serves to determine which attributes are valid) on regular -object instantiation through the new constructor and relies on the column names in the rows -with the finders. - - # CREATE TABLE companies ( - # id int(11) unsigned NOT NULL auto_increment, - # client_of int(11), - # name varchar(255), - # type varchar(100), - # PRIMARY KEY (id) - # ) - -Active Record automatically links the "Company" object to the "companies" table - - class Company < ActiveRecord::Base - has_many :people, :class_name => "Person" - end - - class Firm < Company - has_many :clients - - def people_with_all_clients - clients.inject([]) { |people, client| people + client.people } - end - end - -The foreign_key is only necessary because we didn't use "firm_id" in the data definition - - class Client < Company - belongs_to :firm, :foreign_key => "client_of" - end - - # CREATE TABLE people ( - # id int(11) unsigned NOT NULL auto_increment, - # name text, - # company_id text, - # PRIMARY KEY (id) - # ) -Active Record will also automatically link the "Person" object to the "people" table - - class Person < ActiveRecord::Base - belongs_to :company - end - -== Simple example (2/2): Using the domain - -Picking a database connection for all the Active Records - - ActiveRecord::Base.establish_connection( - :adapter => "mysql", - :host => "localhost", - :username => "me", - :password => "secret", - :database => "activerecord" - ) - -Create some fixtures - - firm = Firm.new("name" => "Next Angle") - # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") - firm.save - - client = Client.new("name" => "37signals", "client_of" => firm.id) - # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") - client.save - -Lots of different finders - - # SQL: SELECT * FROM companies WHERE id = 1 - next_angle = Company.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' - next_angle = Firm.find(1) - - # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' - next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") - - next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first - -The supertype, Company, will return subtype instances - - Firm === next_angle - -All the dynamic methods added by the has_many macro - - next_angle.clients.empty? # true - next_angle.clients.size # total number of clients - all_clients = next_angle.clients - -Constrained finds makes access security easier when ID comes from a web-app - - # SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 - thirty_seven_signals = next_angle.clients.find(2) - -Bi-directional associations thanks to the "belongs_to" macro - - thirty_seven_signals.firm.nil? # true +== Philosophy +Active Record is an implementation of the object-relational mapping (ORM) +pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] by the same +name described by Martin Fowler: -== Philosophy + "An object that wraps a row in a database table or view, + encapsulates the database access, and adds domain logic on that data." Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is object-relational mapping. The prime directive for this mapping has been to minimize -- cgit v1.2.3 From 38734aff2005f1739ce7229db9d12363106f68c6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 22 Jul 2010 09:35:50 -0700 Subject: adding a test for session save --- activerecord/test/cases/session_store/sql_bypass.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/session_store/sql_bypass.rb b/activerecord/test/cases/session_store/sql_bypass.rb index 6c4a70b9fe..0059467bc0 100644 --- a/activerecord/test/cases/session_store/sql_bypass.rb +++ b/activerecord/test/cases/session_store/sql_bypass.rb @@ -32,6 +32,16 @@ module ActiveRecord s = SqlBypass.new :data => 'hello' assert s.loaded?, 'it is loaded' end + + def test_save + SqlBypass.create_table! unless Session.table_exists? + session_id = 20 + s = SqlBypass.new :data => 'hello', :session_id => session_id + s.save + t = SqlBypass.find_by_session_id session_id + assert_equal s.session_id, t.session_id + assert_equal s.data, t.data + end end end end -- cgit v1.2.3 From 97f3c7387e22c7752310a6b9c74ddb9fd3a8eb2d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 22 Jul 2010 09:38:56 -0700 Subject: refactor to use instance methods and be kind to subclasses --- activerecord/lib/active_record/session_store.rb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index b352f9529d..c6cf91c15d 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -265,22 +265,23 @@ module ActiveRecord def save return false unless loaded? marshaled_data = self.class.marshal(data) + connect = connection if @new_record @new_record = false - @@connection.update <<-end_sql, 'Create session' - INSERT INTO #{@@table_name} ( - #{@@connection.quote_column_name(@@session_id_column)}, - #{@@connection.quote_column_name(@@data_column)} ) + connect.update <<-end_sql, 'Create session' + INSERT INTO #{table_name} ( + #{connect.quote_column_name(session_id_column)}, + #{connect.quote_column_name(data_column)} ) VALUES ( - #{@@connection.quote(session_id)}, - #{@@connection.quote(marshaled_data)} ) + #{connect.quote(session_id)}, + #{connect.quote(marshaled_data)} ) end_sql else - @@connection.update <<-end_sql, 'Update session' - UPDATE #{@@table_name} - SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)} - WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)} + connect.update <<-end_sql, 'Update session' + UPDATE #{table_name} + SET #{connect.quote_column_name(data_column)}=#{connect.quote(marshaled_data)} + WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id)} end_sql end end -- cgit v1.2.3 From 1b8cd5fb53d0414b2dbbcaf0bc4f8851b8550e34 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 12:07:16 -0300 Subject: MySQL can't index a TEXT column --- activerecord/lib/active_record/session_store.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index c6cf91c15d..bb472f7ab6 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -83,7 +83,7 @@ module ActiveRecord connection.execute <<-end_sql CREATE TABLE #{table_name} ( id INTEGER PRIMARY KEY, - #{connection.quote_column_name(session_id_column)} TEXT UNIQUE, + #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, #{connection.quote_column_name(data_column_name)} TEXT(255) ) end_sql @@ -220,7 +220,7 @@ module ActiveRecord connection.execute <<-end_sql CREATE TABLE #{table_name} ( id INTEGER PRIMARY KEY, - #{connection.quote_column_name(session_id_column)} TEXT UNIQUE, + #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, #{connection.quote_column_name(data_column)} TEXT ) end_sql -- cgit v1.2.3 From 7113f207c34e41ffcbbd0b3570f3fe41bbfbbcaf Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 12:08:37 -0300 Subject: This is a VARCHAR not a TEXT --- activerecord/lib/active_record/session_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index bb472f7ab6..bd976acdbb 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -84,7 +84,7 @@ module ActiveRecord CREATE TABLE #{table_name} ( id INTEGER PRIMARY KEY, #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, - #{connection.quote_column_name(data_column_name)} TEXT(255) + #{connection.quote_column_name(data_column_name)} VARCHAR(255) ) end_sql end -- cgit v1.2.3 From 24f303b677e4a8f0ecf8c335d95c63c3f7f1bdcc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 13:00:29 -0300 Subject: Makes current adapter decide the syntax of PRIMARY KEY column definition --- activerecord/lib/active_record/session_store.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index bd976acdbb..365fcd6222 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -82,7 +82,7 @@ module ActiveRecord def create_table! connection.execute <<-end_sql CREATE TABLE #{table_name} ( - id INTEGER PRIMARY KEY, + id #{connection.type_to_sql(:primary_key)}, #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, #{connection.quote_column_name(data_column_name)} VARCHAR(255) ) @@ -219,7 +219,7 @@ module ActiveRecord def create_table! connection.execute <<-end_sql CREATE TABLE #{table_name} ( - id INTEGER PRIMARY KEY, + id #{connection.type_to_sql(:primary_key)}, #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, #{connection.quote_column_name(data_column)} TEXT ) -- cgit v1.2.3 From 3ec67595522b573f800ad4f8df3ed8436eb670c7 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 22 Jul 2010 13:34:07 -0700 Subject: Session data will exceed 255 bytes. Use TEXT. --- activerecord/lib/active_record/session_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 365fcd6222..923fab27b0 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -84,7 +84,7 @@ module ActiveRecord CREATE TABLE #{table_name} ( id #{connection.type_to_sql(:primary_key)}, #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, - #{connection.quote_column_name(data_column_name)} VARCHAR(255) + #{connection.quote_column_name(data_column_name)} TEXT ) end_sql end -- cgit v1.2.3 From 227680f4152004b6f373192aa03bba6a89224a59 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 17:24:00 -0300 Subject: session_id is a VARCHAR --- activerecord/test/cases/session_store/session_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb index bb2e1ec97a..4f67073675 100644 --- a/activerecord/test/cases/session_store/session_test.rb +++ b/activerecord/test/cases/session_store/session_test.rb @@ -37,20 +37,20 @@ module ActiveRecord assert klass.columns_hash['sessid'], 'sessid column exists' session = klass.new(:data => 'hello') - session.sessid = 100 + session.sessid = "100" session.save! - found = klass.find_by_session_id(100) + found = klass.find_by_session_id("100") assert_equal session, found - assert_equal session.sessid, found.session_id.to_i + assert_equal session.sessid, found.session_id ensure klass.drop_table! end def test_find_by_session_id Session.create_table! - s = Session.create!(:data => 'world', :session_id => 10) - assert_equal s, Session.find_by_session_id(10) + s = Session.create!(:data => 'world', :session_id => "10") + assert_equal s, Session.find_by_session_id("10") Session.drop_table! end -- cgit v1.2.3 From b378b19430a4d2ee54cbe7f1935fbd4f8b3b331b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 22 Jul 2010 18:56:00 -0300 Subject: Makes Rakefile activate rdoc >= 2.5.9 Signed-off-by: Xavier Noria --- activerecord/Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index d9124c9776..8d81622760 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -1,4 +1,4 @@ -gem 'rdoc', '= 2.2' +gem 'rdoc', '>= 2.5.9' require 'rdoc' require 'rake' require 'rake/testtask' -- cgit v1.2.3 From da37716c35ec5007c028c533d64cea0dc80f574b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 22 Jul 2010 23:40:39 -0700 Subject: refactoring class methods to a module --- activerecord/lib/active_record/session_store.rb | 42 ++++++++++------------ .../test/cases/session_store/session_test.rb | 7 ++-- 2 files changed, 23 insertions(+), 26 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 923fab27b0..143c1fec13 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -49,8 +49,24 @@ module ActiveRecord # The example SqlBypass class is a generic SQL session store. You may # use it as a basis for high-performance database-specific stores. class SessionStore < ActionDispatch::Session::AbstractStore + module ClassMethods # :nodoc: + def marshal(data) + ActiveSupport::Base64.encode64(Marshal.dump(data)) if data + end + + def unmarshal(data) + Marshal.load(ActiveSupport::Base64.decode64(data)) if data + end + + def drop_table! + connection.execute "DROP TABLE #{table_name}" + end + end + # The default Active Record class. class Session < ActiveRecord::Base + extend ClassMethods + ## # :singleton-method: # Customizable data column name. Defaults to 'data'. @@ -71,14 +87,6 @@ module ActiveRecord find_by_session_id(session_id) end - def marshal(data) - ActiveSupport::Base64.encode64(Marshal.dump(data)) if data - end - - def unmarshal(data) - Marshal.load(ActiveSupport::Base64.decode64(data)) if data - end - def create_table! connection.execute <<-end_sql CREATE TABLE #{table_name} ( @@ -89,10 +97,6 @@ module ActiveRecord end_sql end - def drop_table! - connection.execute "DROP TABLE #{table_name}" - end - private def session_id_column 'session_id' @@ -173,6 +177,8 @@ module ActiveRecord # binary session data in a +text+ column. For higher performance, # store in a +blob+ column instead and forgo the Base64 encoding. class SqlBypass + extend ClassMethods + ## # :singleton-method: # Use the ActiveRecord::Base.connection by default. @@ -208,14 +214,6 @@ module ActiveRecord end end - def marshal(data) - ActiveSupport::Base64.encode64(Marshal.dump(data)) if data - end - - def unmarshal(data) - Marshal.load(ActiveSupport::Base64.decode64(data)) if data - end - def create_table! connection.execute <<-end_sql CREATE TABLE #{table_name} ( @@ -225,10 +223,6 @@ module ActiveRecord ) end_sql end - - def drop_table! - connection.execute "DROP TABLE #{table_name}" - end end attr_reader :session_id, :new_record diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb index 4f67073675..6ba31028de 100644 --- a/activerecord/test/cases/session_store/session_test.rb +++ b/activerecord/test/cases/session_store/session_test.rb @@ -49,8 +49,11 @@ module ActiveRecord def test_find_by_session_id Session.create_table! - s = Session.create!(:data => 'world', :session_id => "10") - assert_equal s, Session.find_by_session_id("10") + session_id = "10" + s = Session.create!(:data => 'world', :session_id => session_id) + t = Session.find_by_session_id(session_id) + assert_equal s, t + assert_equal s.data, t.data Session.drop_table! end -- cgit v1.2.3 From 34ff74f44819f6b5c07f5a2c17402010f414695a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 22 Jul 2010 23:44:58 -0700 Subject: refactoring more methods to a class methods module --- activerecord/lib/active_record/session_store.rb | 32 ++++++++++--------------- 1 file changed, 12 insertions(+), 20 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 143c1fec13..5ca38fb9bd 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -61,6 +61,16 @@ module ActiveRecord def drop_table! connection.execute "DROP TABLE #{table_name}" end + + def create_table! + connection.execute <<-end_sql + CREATE TABLE #{table_name} ( + id #{connection.type_to_sql(:primary_key)}, + #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, + #{connection.quote_column_name(data_column_name)} TEXT + ) + end_sql + end end # The default Active Record class. @@ -87,16 +97,6 @@ module ActiveRecord find_by_session_id(session_id) end - def create_table! - connection.execute <<-end_sql - CREATE TABLE #{table_name} ( - id #{connection.type_to_sql(:primary_key)}, - #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, - #{connection.quote_column_name(data_column_name)} TEXT - ) - end_sql - end - private def session_id_column 'session_id' @@ -203,6 +203,8 @@ module ActiveRecord @@data_column = 'data' class << self + alias :data_column_name :data_column + def connection @@connection ||= ActiveRecord::Base.connection end @@ -213,16 +215,6 @@ module ActiveRecord new(:session_id => session_id, :marshaled_data => record['data']) end end - - def create_table! - connection.execute <<-end_sql - CREATE TABLE #{table_name} ( - id #{connection.type_to_sql(:primary_key)}, - #{connection.quote_column_name(session_id_column)} VARCHAR(255) UNIQUE, - #{connection.quote_column_name(data_column)} TEXT - ) - end_sql - end end attr_reader :session_id, :new_record -- cgit v1.2.3 From 5e6382e60708bb64c00a4c1fb1ecc915011739ab Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 22 Jul 2010 23:45:07 -0700 Subject: fixing space error --- activerecord/lib/active_record/session_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 5ca38fb9bd..4a6f6c0936 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -314,7 +314,7 @@ module ActiveRecord sid end - + def destroy(env) if sid = current_session_id(env) Base.silence do -- cgit v1.2.3 From 9b48a94c0ae1d128d819e2523a813738c1662489 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 23 Jul 2010 08:30:14 -0700 Subject: brrrrr! freeze is not needed --- activerecord/lib/active_record/session_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 4a6f6c0936..04f059fb05 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -287,7 +287,7 @@ module ActiveRecord cattr_accessor :session_class self.session_class = Session - SESSION_RECORD_KEY = 'rack.session.record'.freeze + SESSION_RECORD_KEY = 'rack.session.record' private def get_session(env, sid) -- cgit v1.2.3 From 5ebc1d88d3c80f8773d39b155a4e6f66544ec46b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 23 Jul 2010 08:36:58 -0700 Subject: testing sqlbypass destroy. caching connection during destroy --- activerecord/lib/active_record/session_store.rb | 13 +++++++------ activerecord/test/cases/session_store/sql_bypass.rb | 9 +++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 04f059fb05..766e63edc7 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -273,12 +273,13 @@ module ActiveRecord end def destroy - unless @new_record - @@connection.delete <<-end_sql, 'Destroy session' - DELETE FROM #{@@table_name} - WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)} - end_sql - end + return if @new_record + + connect = connection + connect.delete <<-end_sql, 'Destroy session' + DELETE FROM #{table_name} + WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id)} + end_sql end end diff --git a/activerecord/test/cases/session_store/sql_bypass.rb b/activerecord/test/cases/session_store/sql_bypass.rb index 0059467bc0..f0ba166465 100644 --- a/activerecord/test/cases/session_store/sql_bypass.rb +++ b/activerecord/test/cases/session_store/sql_bypass.rb @@ -42,6 +42,15 @@ module ActiveRecord assert_equal s.session_id, t.session_id assert_equal s.data, t.data end + + def test_destroy + SqlBypass.create_table! unless Session.table_exists? + session_id = 20 + s = SqlBypass.new :data => 'hello', :session_id => session_id + s.save + s.destroy + assert_nil SqlBypass.find_by_session_id session_id + end end end end -- cgit v1.2.3 From b50635a59f7d2fab2cdba348c4169536fbbb77f4 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 23 Jul 2010 21:11:29 +0200 Subject: update Rakefiles for RDoc 2.5 --- activerecord/Rakefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 8d81622760..66ebb6344c 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -2,7 +2,7 @@ gem 'rdoc', '>= 2.5.9' require 'rdoc' require 'rake' require 'rake/testtask' -require 'rake/rdoctask' +require 'rdoc/task' require 'rake/packagetask' require 'rake/gempackagetask' @@ -166,12 +166,12 @@ task :rebuild_frontbase_databases => 'frontbase:rebuild_databases' # Generate the RDoc documentation -Rake::RDocTask.new { |rdoc| +RDoc::Task.new { |rdoc| rdoc.rdoc_dir = 'doc' rdoc.title = "Active Record -- Object-relation mapping put on rails" - rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object' + rdoc.options << '-f' << 'horo' + rdoc.options << '--main' << 'README.rdoc' rdoc.options << '--charset' << 'utf-8' - rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README.rdoc', 'RUNNING_UNIT_TESTS', 'CHANGELOG') rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.exclude('lib/active_record/vendor/*') -- cgit v1.2.3 From 082ca51d65c988333c9afbda73cec857687bd4d9 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 24 Jul 2010 00:45:53 -0400 Subject: bringing consistency in comments --- activerecord/lib/active_record/reflection.rb | 31 +++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a82e5d7ed1..09d7f8699e 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -50,7 +50,7 @@ module ActiveRecord # Returns an array of AssociationReflection objects for all the # associations in the class. If you only want to reflect on a certain # association type, pass in the symbol (:has_many, :has_one, - # :belongs_to) for that as the first parameter. + # :belongs_to) as the first parameter. # # Example: # @@ -62,9 +62,9 @@ module ActiveRecord macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections end - # Returns the AssociationReflection object for the named +association+ (use the symbol). Example: + # Returns the AssociationReflection object for the +association+ (use the symbol). # - # Account.reflect_on_association(:owner) # returns the owner AssociationReflection + # Account.reflect_on_association(:owner) # returns the owner AssociationReflection # Invoice.reflect_on_association(:line_items).macro # returns :has_many # def reflect_on_association(association) @@ -88,34 +88,37 @@ module ActiveRecord @macro, @name, @options, @active_record = macro, name, options, active_record end - # Returns the name of the macro. For example, composed_of :balance, - # :class_name => 'Money' will return :balance or for - # has_many :clients it will return :clients. + # Returns the name of the macro. + # composed_of :balance, :class_name => 'Money' will return :balance + # has_many :clients will return :clients def name @name end - # Returns the macro type. For example, + # Returns the macro type. # composed_of :balance, :class_name => 'Money' will return :composed_of - # or for has_many :clients will return :has_many. + # has_many :clients will return :has_many def macro @macro end - # Returns the hash of options used for the macro. For example, it would return { :class_name => "Money" } for - # composed_of :balance, :class_name => 'Money' or +{}+ for has_many :clients. + # Returns the hash of options used for the macro. + # composed_of :balance, :class_name => 'Money' will return { :class_name => "Money" } + # has_many :clients will return +{}+ def options @options end - # Returns the class for the macro. For example, composed_of :balance, :class_name => 'Money' returns the Money - # class and has_many :clients returns the Client class. + # Returns the class for the macro. + # composed_of :balance, :class_name => 'Money' will return the Money class + # has_many :clients will return the Client class def klass @klass ||= class_name.constantize end - # Returns the class name for the macro. For example, composed_of :balance, :class_name => 'Money' returns 'Money' - # and has_many :clients returns 'Client'. + # Returns the class name for the macro. + # composed_of :balance, :class_name => 'Money' will return 'Money' + # has_many :clients will return 'Client' def class_name @class_name ||= options[:class_name] || derive_class_name end -- cgit v1.2.3 From 834bd23a07a84ff631da1ded37c643a3a371cb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 24 Jul 2010 10:22:22 +0200 Subject: Get rid of instrumenter.elapsed. --- .../lib/active_record/connection_adapters/abstract_adapter.rb | 10 ++-------- activerecord/lib/active_record/log_subscriber.rb | 5 +++++ 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 6072481411..c103fcccf7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -34,10 +34,9 @@ module ActiveRecord include QueryCache include ActiveSupport::Callbacks + attr_accessor :runtime define_callbacks :checkout, :checkin - @@row_even = true - def initialize(connection, logger = nil) #:nodoc: @active = nil @connection, @logger = connection, logger @@ -199,15 +198,10 @@ module ActiveRecord def log(sql, name) name ||= "SQL" - instrumenter = ActiveSupport::Notifications.instrumenter - - result = instrumenter.instrument("sql.active_record", + ActiveSupport::Notifications.instrument("sql.active_record", :sql => sql, :name => name, :connection_id => object_id) do yield end - @runtime += instrumenter.elapsed - - result rescue Exception => e message = "#{e.class.name}: #{e.message}: #{sql}" @logger.debug message if @logger diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 278e192e59..d7b99d796d 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -6,6 +6,7 @@ module ActiveRecord end def sql(event) + connection.runtime += event.duration return unless logger.debug? name = '%s (%.1fms)' % [event.payload[:name], event.duration] @@ -25,6 +26,10 @@ module ActiveRecord @odd_or_even = !@odd_or_even end + def connection + ActiveRecord::Base.connection + end + def logger ActiveRecord::Base.logger end -- cgit v1.2.3 From 7fb7a2bd69b86ee0a0e832f597f11d8cc1c787c9 Mon Sep 17 00:00:00 2001 From: Arkadiusz Holko Date: Sun, 25 Jul 2010 05:29:40 -0700 Subject: Fixes syntax error in "with_scope" example --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 19ccf75b00..c589b32dd8 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1030,7 +1030,7 @@ module ActiveRecord #:nodoc: # class Article < ActiveRecord::Base # def self.find_with_exclusive_scope # with_scope(:find => where(:blog_id => 1).limit(1)) do - # with_exclusive_scope(:find => limit(10)) + # with_exclusive_scope(:find => limit(10)) do # all # => SELECT * from articles LIMIT 10 # end # end -- cgit v1.2.3 From 82c7ef837d32897717b7c6e54cfa6f00d218a242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 25 Jul 2010 16:42:05 +0200 Subject: Move runtime to log subscriber. --- .../connection_adapters/abstract_adapter.rb | 7 ------- activerecord/lib/active_record/log_subscriber.rb | 22 ++++++++++++++++------ .../active_record/railties/controller_runtime.rb | 4 ++-- 3 files changed, 18 insertions(+), 15 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c103fcccf7..c07ba2ac74 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -34,13 +34,11 @@ module ActiveRecord include QueryCache include ActiveSupport::Callbacks - attr_accessor :runtime define_callbacks :checkout, :checkin def initialize(connection, logger = nil) #:nodoc: @active = nil @connection, @logger = connection, logger - @runtime = 0 @query_cache_enabled = false @query_cache = {} end @@ -91,11 +89,6 @@ module ActiveRecord false end - def reset_runtime #:nodoc: - rt, @runtime = @runtime, 0 - rt - end - # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index d7b99d796d..74b5893bfb 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -1,12 +1,25 @@ module ActiveRecord class LogSubscriber < ActiveSupport::LogSubscriber + def self.runtime=(value) + Thread.current["active_record_sql_runtime"] = value + end + + def self.runtime + Thread.current["active_record_sql_runtime"] + end + + def self.reset_runtime + rt, self.runtime = runtime, 0 + rt + end + def initialize super @odd_or_even = false end def sql(event) - connection.runtime += event.duration + self.class.runtime += event.duration return unless logger.debug? name = '%s (%.1fms)' % [event.payload[:name], event.duration] @@ -26,14 +39,11 @@ module ActiveRecord @odd_or_even = !@odd_or_even end - def connection - ActiveRecord::Base.connection - end - def logger ActiveRecord::Base.logger end end end -ActiveRecord::LogSubscriber.attach_to :active_record +ActiveRecord::LogSubscriber.runtime = 0 +ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index aed1c59b00..bc6ca936c0 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -11,9 +11,9 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? - db_rt_before_render = ActiveRecord::Base.connection.reset_runtime + db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime runtime = super - db_rt_after_render = ActiveRecord::Base.connection.reset_runtime + db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime self.db_runtime = db_rt_before_render + db_rt_after_render runtime - db_rt_after_render else -- cgit v1.2.3 From d02f2d2fb213584ce52a231063c948def856f657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 25 Jul 2010 18:26:42 +0200 Subject: runtime should be properly initialized. --- activerecord/lib/active_record/log_subscriber.rb | 3 +-- activerecord/test/cases/log_subscriber_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 74b5893bfb..c7ae12977a 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -5,7 +5,7 @@ module ActiveRecord end def self.runtime - Thread.current["active_record_sql_runtime"] + Thread.current["active_record_sql_runtime"] ||= 0 end def self.reset_runtime @@ -45,5 +45,4 @@ module ActiveRecord end end -ActiveRecord::LogSubscriber.runtime = 0 ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 342daa19df..cbaaca764b 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -57,4 +57,8 @@ class LogSubscriberTest < ActiveRecord::TestCase wait assert_equal 0, @logger.logged(:debug).size end + + def test_initializes_runtime + Thread.new { assert_equal 0, ActiveRecord::LogSubscriber.runtime }.join + end end -- cgit v1.2.3 From b7e0408ca922cf51228818edbfdcd5c63e3cb84e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 25 Jul 2010 11:11:23 -0700 Subject: use a hash to collect optional statistics about the instrumentation --- .../connection_adapters/abstract_adapter.rb | 18 ++++++++++++++++-- activerecord/lib/active_record/log_subscriber.rb | 16 +--------------- .../lib/active_record/railties/controller_runtime.rb | 4 ++-- activerecord/test/cases/log_subscriber_test.rb | 4 ---- 4 files changed, 19 insertions(+), 23 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c07ba2ac74..4799b693c5 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -36,9 +36,12 @@ module ActiveRecord define_callbacks :checkout, :checkin + @@row_even = true + def initialize(connection, logger = nil) #:nodoc: @active = nil @connection, @logger = connection, logger + @runtime = 0 @query_cache_enabled = false @query_cache = {} end @@ -89,6 +92,11 @@ module ActiveRecord false end + def reset_runtime #:nodoc: + rt, @runtime = @runtime, 0 + rt + end + # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. @@ -191,10 +199,16 @@ module ActiveRecord def log(sql, name) name ||= "SQL" - ActiveSupport::Notifications.instrument("sql.active_record", - :sql => sql, :name => name, :connection_id => object_id) do + instrumenter = ActiveSupport::Notifications.instrumenter + info = {} + + result = instrumenter.instrument("sql.active_record", + {:sql => sql, :name => name, :connection_id => object_id}, info) do yield end + @runtime += info[:elapsed] + + result rescue Exception => e message = "#{e.class.name}: #{e.message}: #{sql}" @logger.debug message if @logger diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index c7ae12977a..278e192e59 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -1,25 +1,11 @@ module ActiveRecord class LogSubscriber < ActiveSupport::LogSubscriber - def self.runtime=(value) - Thread.current["active_record_sql_runtime"] = value - end - - def self.runtime - Thread.current["active_record_sql_runtime"] ||= 0 - end - - def self.reset_runtime - rt, self.runtime = runtime, 0 - rt - end - def initialize super @odd_or_even = false end def sql(event) - self.class.runtime += event.duration return unless logger.debug? name = '%s (%.1fms)' % [event.payload[:name], event.duration] @@ -45,4 +31,4 @@ module ActiveRecord end end -ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file +ActiveRecord::LogSubscriber.attach_to :active_record diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index bc6ca936c0..aed1c59b00 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -11,9 +11,9 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? - db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime + db_rt_before_render = ActiveRecord::Base.connection.reset_runtime runtime = super - db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime + db_rt_after_render = ActiveRecord::Base.connection.reset_runtime self.db_runtime = db_rt_before_render + db_rt_after_render runtime - db_rt_after_render else diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index cbaaca764b..342daa19df 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -57,8 +57,4 @@ class LogSubscriberTest < ActiveRecord::TestCase wait assert_equal 0, @logger.logged(:debug).size end - - def test_initializes_runtime - Thread.new { assert_equal 0, ActiveRecord::LogSubscriber.runtime }.join - end end -- cgit v1.2.3 From fc088d4e8fdc7fcc710df094ce4ae6faa27d8c8d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 25 Jul 2010 11:11:51 -0700 Subject: cache the instrumentor for a speed gain --- .../lib/active_record/connection_adapters/abstract_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 4799b693c5..618560087e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -44,6 +44,7 @@ module ActiveRecord @runtime = 0 @query_cache_enabled = false @query_cache = {} + @instrumenter = ActiveSupport::Notifications.instrumenter end # Returns the human-readable name of the adapter. Use mixed case - one @@ -199,10 +200,9 @@ module ActiveRecord def log(sql, name) name ||= "SQL" - instrumenter = ActiveSupport::Notifications.instrumenter info = {} - result = instrumenter.instrument("sql.active_record", + result = @instrumenter.instrument("sql.active_record", {:sql => sql, :name => name, :connection_id => object_id}, info) do yield end -- cgit v1.2.3 From 0d0e79398308e6147e59d99a48c7e6d952e5848c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 25 Jul 2010 11:16:13 -0700 Subject: look up connection once --- activerecord/lib/active_record/railties/controller_runtime.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index aed1c59b00..cf74fa1655 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -11,9 +11,10 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? - db_rt_before_render = ActiveRecord::Base.connection.reset_runtime + connection = ActiveRecord::Base.connection + db_rt_before_render = connection.reset_runtime runtime = super - db_rt_after_render = ActiveRecord::Base.connection.reset_runtime + db_rt_after_render = connection.reset_runtime self.db_runtime = db_rt_before_render + db_rt_after_render runtime - db_rt_after_render else -- cgit v1.2.3 From ff0d842454571d78addd1fe9d4f232b600881b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 25 Jul 2010 20:46:42 +0200 Subject: Revert the previous three commits. * AS::Notifications#instrument should not measure anything, it is not its responsibility; * Adding another argument to AS::Notifications#instrument API needs to be properly discussed; --- .../connection_adapters/abstract_adapter.rb | 18 ++---------------- activerecord/lib/active_record/log_subscriber.rb | 16 +++++++++++++++- .../lib/active_record/railties/controller_runtime.rb | 5 ++--- activerecord/test/cases/log_subscriber_test.rb | 4 ++++ 4 files changed, 23 insertions(+), 20 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 618560087e..c07ba2ac74 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -36,15 +36,11 @@ module ActiveRecord define_callbacks :checkout, :checkin - @@row_even = true - def initialize(connection, logger = nil) #:nodoc: @active = nil @connection, @logger = connection, logger - @runtime = 0 @query_cache_enabled = false @query_cache = {} - @instrumenter = ActiveSupport::Notifications.instrumenter end # Returns the human-readable name of the adapter. Use mixed case - one @@ -93,11 +89,6 @@ module ActiveRecord false end - def reset_runtime #:nodoc: - rt, @runtime = @runtime, 0 - rt - end - # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. @@ -200,15 +191,10 @@ module ActiveRecord def log(sql, name) name ||= "SQL" - info = {} - - result = @instrumenter.instrument("sql.active_record", - {:sql => sql, :name => name, :connection_id => object_id}, info) do + ActiveSupport::Notifications.instrument("sql.active_record", + :sql => sql, :name => name, :connection_id => object_id) do yield end - @runtime += info[:elapsed] - - result rescue Exception => e message = "#{e.class.name}: #{e.message}: #{sql}" @logger.debug message if @logger diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 278e192e59..c7ae12977a 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -1,11 +1,25 @@ module ActiveRecord class LogSubscriber < ActiveSupport::LogSubscriber + def self.runtime=(value) + Thread.current["active_record_sql_runtime"] = value + end + + def self.runtime + Thread.current["active_record_sql_runtime"] ||= 0 + end + + def self.reset_runtime + rt, self.runtime = runtime, 0 + rt + end + def initialize super @odd_or_even = false end def sql(event) + self.class.runtime += event.duration return unless logger.debug? name = '%s (%.1fms)' % [event.payload[:name], event.duration] @@ -31,4 +45,4 @@ module ActiveRecord end end -ActiveRecord::LogSubscriber.attach_to :active_record +ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index cf74fa1655..bc6ca936c0 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -11,10 +11,9 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? - connection = ActiveRecord::Base.connection - db_rt_before_render = connection.reset_runtime + db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime runtime = super - db_rt_after_render = connection.reset_runtime + db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime self.db_runtime = db_rt_before_render + db_rt_after_render runtime - db_rt_after_render else diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 342daa19df..cbaaca764b 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -57,4 +57,8 @@ class LogSubscriberTest < ActiveRecord::TestCase wait assert_equal 0, @logger.logged(:debug).size end + + def test_initializes_runtime + Thread.new { assert_equal 0, ActiveRecord::LogSubscriber.runtime }.join + end end -- cgit v1.2.3 From 304c49b1468ce71beb7e3ea0ca4c5add73a1534e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 25 Jul 2010 20:59:37 +0200 Subject: Move runtime back to connection. --- .../connection_adapters/abstract_adapter.rb | 7 +++++++ activerecord/lib/active_record/log_subscriber.rb | 21 ++++++--------------- .../active_record/railties/controller_runtime.rb | 4 ++-- activerecord/test/cases/log_subscriber_test.rb | 4 ---- 4 files changed, 15 insertions(+), 21 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c07ba2ac74..c103fcccf7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -34,11 +34,13 @@ module ActiveRecord include QueryCache include ActiveSupport::Callbacks + attr_accessor :runtime define_callbacks :checkout, :checkin def initialize(connection, logger = nil) #:nodoc: @active = nil @connection, @logger = connection, logger + @runtime = 0 @query_cache_enabled = false @query_cache = {} end @@ -89,6 +91,11 @@ module ActiveRecord false end + def reset_runtime #:nodoc: + rt, @runtime = @runtime, 0 + rt + end + # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index c7ae12977a..d7b99d796d 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -1,25 +1,12 @@ module ActiveRecord class LogSubscriber < ActiveSupport::LogSubscriber - def self.runtime=(value) - Thread.current["active_record_sql_runtime"] = value - end - - def self.runtime - Thread.current["active_record_sql_runtime"] ||= 0 - end - - def self.reset_runtime - rt, self.runtime = runtime, 0 - rt - end - def initialize super @odd_or_even = false end def sql(event) - self.class.runtime += event.duration + connection.runtime += event.duration return unless logger.debug? name = '%s (%.1fms)' % [event.payload[:name], event.duration] @@ -39,10 +26,14 @@ module ActiveRecord @odd_or_even = !@odd_or_even end + def connection + ActiveRecord::Base.connection + end + def logger ActiveRecord::Base.logger end end end -ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file +ActiveRecord::LogSubscriber.attach_to :active_record diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index bc6ca936c0..aed1c59b00 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -11,9 +11,9 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? - db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime + db_rt_before_render = ActiveRecord::Base.connection.reset_runtime runtime = super - db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime + db_rt_after_render = ActiveRecord::Base.connection.reset_runtime self.db_runtime = db_rt_before_render + db_rt_after_render runtime - db_rt_after_render else diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index cbaaca764b..342daa19df 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -57,8 +57,4 @@ class LogSubscriberTest < ActiveRecord::TestCase wait assert_equal 0, @logger.logged(:debug).size end - - def test_initializes_runtime - Thread.new { assert_equal 0, ActiveRecord::LogSubscriber.runtime }.join - end end -- cgit v1.2.3 From 331df84cc943e08459463d322ca2510490c73b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 25 Jul 2010 21:01:49 +0200 Subject: Cache connection access. --- activerecord/lib/active_record/log_subscriber.rb | 5 +---- activerecord/lib/active_record/railties/controller_runtime.rb | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index d7b99d796d..f7ae5587ed 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -6,6 +6,7 @@ module ActiveRecord end def sql(event) + connection = ActiveRecord::Base.connection connection.runtime += event.duration return unless logger.debug? @@ -26,10 +27,6 @@ module ActiveRecord @odd_or_even = !@odd_or_even end - def connection - ActiveRecord::Base.connection - end - def logger ActiveRecord::Base.logger end diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index aed1c59b00..cf74fa1655 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -11,9 +11,10 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? - db_rt_before_render = ActiveRecord::Base.connection.reset_runtime + connection = ActiveRecord::Base.connection + db_rt_before_render = connection.reset_runtime runtime = super - db_rt_after_render = ActiveRecord::Base.connection.reset_runtime + db_rt_after_render = connection.reset_runtime self.db_runtime = db_rt_before_render + db_rt_after_render runtime - db_rt_after_render else -- cgit v1.2.3 From f4200b0cd5c21d7bd37a9b5c9effc2006485ed46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 25 Jul 2010 22:59:50 +0200 Subject: Cache instrumenter again as per Aaron's patch. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c103fcccf7..69a963c3d3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -43,6 +43,7 @@ module ActiveRecord @runtime = 0 @query_cache_enabled = false @query_cache = {} + @instrumenter = ActiveSupport::Notifications.instrumenter end # Returns the human-readable name of the adapter. Use mixed case - one @@ -198,7 +199,7 @@ module ActiveRecord def log(sql, name) name ||= "SQL" - ActiveSupport::Notifications.instrument("sql.active_record", + @instrumenter.instrument("sql.active_record", :sql => sql, :name => name, :connection_id => object_id) do yield end -- cgit v1.2.3 From b0b9bf320409b66c6c6b680371aca590297cd4cc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 25 Jul 2010 18:12:20 -0300 Subject: Object#returning removed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/models/company_in_module.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 83d71b6909..2c8c30efb4 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/object/misc' +require 'active_support/core_ext/object/with_options' module MyApplication module Business -- cgit v1.2.3 From dcb7832ed51b2aeb2ea7386e2f07fcb5d7eb0c6d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 26 Jul 2010 00:01:23 +0200 Subject: APIs for individual components are no longer published --- activerecord/Rakefile | 6 ------ 1 file changed, 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 66ebb6344c..36cd7e3e6c 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -224,9 +224,3 @@ task :release => :package do Rake::Gemcutter::Tasks.new(spec).define Rake::Task['gem:push'].invoke end - -desc "Publish the API documentation" -task :pdoc => [:rdoc] do - require 'rake/contrib/sshpublisher' - Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/ar", "doc").upload -end -- cgit v1.2.3 From df95948d455b3a5f75fc01d41375dcd840549633 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 25 Jul 2010 15:16:57 -0700 Subject: do not execute the session tests within transactions --- activerecord/test/cases/session_store/session_test.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb index 6ba31028de..6f1c170a0c 100644 --- a/activerecord/test/cases/session_store/session_test.rb +++ b/activerecord/test/cases/session_store/session_test.rb @@ -5,6 +5,8 @@ require 'active_record/session_store' module ActiveRecord class SessionStore class SessionTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + def setup super Session.drop_table! if Session.table_exists? -- cgit v1.2.3 From 963c71edfaed3c9942caa3027b2397dd9c403f26 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 26 Jul 2010 05:52:27 +0800 Subject: Don't interpolate a blank string, avoid warnings --- activerecord/lib/active_record/base.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c589b32dd8..5898ec3732 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1255,6 +1255,8 @@ MSG replace_named_bind_variables(statement, values.first) elsif statement.include?('?') replace_bind_variables(statement, values) + elsif statement.blank? + statement else statement % values.collect { |value| connection.quote_string(value.to_s) } end -- cgit v1.2.3 From f70c0b348d2f703d763a8136df7837567f7cb691 Mon Sep 17 00:00:00 2001 From: Thiago Pradi Date: Sun, 25 Jul 2010 21:38:15 -0300 Subject: Removing unnecessary code [#5192 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/autosave_association.rb | 4 ---- activerecord/test/cases/autosave_association_test.rb | 8 +++++++- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 7517896235..062b010436 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -377,10 +377,6 @@ module ActiveRecord if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) self[reflection.primary_key_name] = association_id - # TODO: Removing this code doesn't seem to matter... - if reflection.options[:polymorphic] - self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s - end end saved if autosave diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 3b89c12a3f..08694526af 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -171,7 +171,7 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas end class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase - fixtures :companies + fixtures :companies, :posts, :tags, :taggings def test_should_save_parent_but_not_invalid_child client = Client.new(:name => 'Joe (the Plumber)') @@ -312,6 +312,12 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test assert_equal num_orders +1, Order.count assert_equal num_customers +2, Customer.count end + + def test_store_association_with_a_polymorphic_relationship + num_tagging = Tagging.count + tags(:misc).create_tagging(:taggable => posts(:thinking)) + assert_equal num_tagging +1, Tagging.count + end end class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase -- cgit v1.2.3 From a44652baed1d26a4f63380c406e05f7a2ddd4a12 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 22 Jul 2010 15:20:53 -0400 Subject: No need to check for :uniq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#5170 state:resolved] Signed-off-by: José Valim --- .../lib/active_record/associations/association_collection.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index f346a19a3a..4ce3b34819 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -478,13 +478,10 @@ module ActiveRecord callback(:before_add, record) yield(record) if block_given? @target ||= [] unless loaded? - index = @target.index(record) - unless @reflection.options[:uniq] && index - if index - @target[index] = record - else + if index = @target.index(record) + @target[index] = record + else @target << record - end end callback(:after_add, record) set_inverse_instance(record, @owner) -- cgit v1.2.3 From dd61a817dea85990f13664d74bca15bc5796b76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 26 Jul 2010 19:14:24 +0200 Subject: Move runtime back to the Thread.current --- .../connection_adapters/abstract_adapter.rb | 7 ------- activerecord/lib/active_record/log_subscriber.rb | 18 +++++++++++++++--- .../lib/active_record/railties/controller_runtime.rb | 5 ++--- activerecord/test/cases/log_subscriber_test.rb | 4 ++++ 4 files changed, 21 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 69a963c3d3..d8c92d0ad3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -34,13 +34,11 @@ module ActiveRecord include QueryCache include ActiveSupport::Callbacks - attr_accessor :runtime define_callbacks :checkout, :checkin def initialize(connection, logger = nil) #:nodoc: @active = nil @connection, @logger = connection, logger - @runtime = 0 @query_cache_enabled = false @query_cache = {} @instrumenter = ActiveSupport::Notifications.instrumenter @@ -92,11 +90,6 @@ module ActiveRecord false end - def reset_runtime #:nodoc: - rt, @runtime = @runtime, 0 - rt - end - # QUOTING ================================================== # Override to return the quoted table name. Defaults to column quoting. diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index f7ae5587ed..c7ae12977a 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -1,13 +1,25 @@ module ActiveRecord class LogSubscriber < ActiveSupport::LogSubscriber + def self.runtime=(value) + Thread.current["active_record_sql_runtime"] = value + end + + def self.runtime + Thread.current["active_record_sql_runtime"] ||= 0 + end + + def self.reset_runtime + rt, self.runtime = runtime, 0 + rt + end + def initialize super @odd_or_even = false end def sql(event) - connection = ActiveRecord::Base.connection - connection.runtime += event.duration + self.class.runtime += event.duration return unless logger.debug? name = '%s (%.1fms)' % [event.payload[:name], event.duration] @@ -33,4 +45,4 @@ module ActiveRecord end end -ActiveRecord::LogSubscriber.attach_to :active_record +ActiveRecord::LogSubscriber.attach_to :active_record \ No newline at end of file diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index cf74fa1655..bc6ca936c0 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -11,10 +11,9 @@ module ActiveRecord def cleanup_view_runtime if ActiveRecord::Base.connected? - connection = ActiveRecord::Base.connection - db_rt_before_render = connection.reset_runtime + db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime runtime = super - db_rt_after_render = connection.reset_runtime + db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime self.db_runtime = db_rt_before_render + db_rt_after_render runtime - db_rt_after_render else diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 342daa19df..cbaaca764b 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -57,4 +57,8 @@ class LogSubscriberTest < ActiveRecord::TestCase wait assert_equal 0, @logger.logged(:debug).size end + + def test_initializes_runtime + Thread.new { assert_equal 0, ActiveRecord::LogSubscriber.runtime }.join + end end -- cgit v1.2.3 From 856fc4bbc379b330d11702adbc2b26850dca6206 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 26 Jul 2010 12:52:34 -0500 Subject: Prep for RC --- activerecord/CHANGELOG | 2 +- activerecord/lib/active_record/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 679fdafae8..20b2286fc0 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*Rails 3.0.0 [RC1] (unreleased)* +*Rails 3.0.0 [release candidate] (July 26th, 2010)* * Changed update_attribute to not run callbacks and update the record directly in the database [Neeraj Singh] diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index d18fed0131..a467ffa960 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -3,7 +3,7 @@ module ActiveRecord MAJOR = 3 MINOR = 0 TINY = 0 - BUILD = "beta4" + BUILD = "rc" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end -- cgit v1.2.3 From 4ac3ae0685492977d36f8e71e3c3a5565c2fdd43 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 26 Jul 2010 16:25:21 -0300 Subject: missing requires added --- activerecord/test/cases/autosave_association_test.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 08694526af..49e7147773 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -13,6 +13,8 @@ require 'models/post' require 'models/reader' require 'models/ship' require 'models/ship_part' +require 'models/tag' +require 'models/tagging' require 'models/treasure' require 'models/company' -- cgit v1.2.3 From c9fe3c3bd0ee5010a36a157fe850ef6e8ffb147d Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 27 Jul 2010 13:55:00 -0400 Subject: adding comments to update_attribute method --- activerecord/lib/active_record/persistence.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index b587abd5d0..869cbf5b2e 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -110,6 +110,10 @@ module ActiveRecord # * updated_at/updated_on column is updated if that column is available # * does not work on associations # * does not work on attr_accessor attributes. The attribute that is being updated must be column name. + # * does not work on new record. record.new_record? should return false for this method to work. + # * updates only the attribute that is input to the method. If there are other changed attributes then + # those attributes are left alone. In that case even after this method has done its work record.changed? + # will return true. # def update_attribute(name, value) raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s -- cgit v1.2.3 From 1793b21479d29ec29bd47c37ee68cb6ebdb71617 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 27 Jul 2010 14:27:15 -0700 Subject: Frameworks are loaded in application.rb rather than config.rb according to Rails 3 conventions. --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 2c17c74ab4..d61cc520b7 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -333,7 +333,7 @@ namespace :db do if File.exists?(file) load(file) else - abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/boot.rb to limit the frameworks that will be loaded} + abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded} end end end -- cgit v1.2.3 From 67e79cf96d6bd27779a659703253984ac47a90ee Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 27 Jul 2010 23:31:59 +0200 Subject: Revert "Frameworks are loaded in application.rb rather than config.rb according to Rails 3 conventions." This reverts commit 1793b21479d29ec29bd47c37ee68cb6ebdb71617. This commit had no risk, but docrails has a hard rule about touching code. The contribution is appreciated, I hope you do not mind this technical revert. I'll apply that to master with proper credit. --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index d61cc520b7..2c17c74ab4 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -333,7 +333,7 @@ namespace :db do if File.exists?(file) load(file) else - abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded} + abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/boot.rb to limit the frameworks that will be loaded} end end end -- cgit v1.2.3 From 9d109302f139a23d18de605454074777b995e4c4 Mon Sep 17 00:00:00 2001 From: Jon Buda Date: Tue, 27 Jul 2010 20:04:36 -0500 Subject: fixed joining of attributes when using find_or_create_by with multiple attributes through an association Signed-off-by: Santiago Pastorino --- .../lib/active_record/associations/association_collection.rb | 2 +- .../test/cases/associations/has_many_associations_test.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 4ce3b34819..b5159eead3 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -422,7 +422,7 @@ module ActiveRecord match = DynamicFinderMatch.match(method) if match && match.creator? attributes = match.attribute_names - return send(:"find_by_#{attributes.join('and')}", *args) || create(Hash[attributes.zip(args)]) + return send(:"find_by_#{attributes.join('_and_')}", *args) || create(Hash[attributes.zip(args)]) end if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 6fe737a817..ac2021c369 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -167,6 +167,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase companies(:first_firm).readonly_clients.find(:all).each { |c| assert c.readonly? } end + def test_dynamic_find_or_create_from_two_attributes_using_an_association + author = authors(:david) + number_of_posts = Post.count + another = author.posts.find_or_create_by_title_and_body("Another Post", "This is the Body") + assert_equal number_of_posts + 1, Post.count + assert_equal another, author.posts.find_or_create_by_title_and_body("Another Post", "This is the Body") + assert !another.new_record? + end + def test_cant_save_has_many_readonly_association authors(:david).readonly_comments.each { |c| assert_raise(ActiveRecord::ReadOnlyRecord) { c.save! } } authors(:david).readonly_comments.each { |c| assert c.readonly? } -- cgit v1.2.3 From fe9d7abb3983605464b74aab58a25beeb40d7d78 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 27 Jul 2010 21:05:11 -0700 Subject: slight performance improvement when grabbing an arel table --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 5898ec3732..391c287fe4 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -803,7 +803,7 @@ module ActiveRecord #:nodoc: end def arel_table - @arel_table ||= Arel::Table.new(table_name, :engine => arel_engine) + @arel_table ||= Arel::Table.new(table_name, arel_engine) end def arel_engine -- cgit v1.2.3 From 51924d49af18635cab46720e208c34d75ed8363e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 28 Jul 2010 12:51:09 -0400 Subject: more documentation for update_attribute --- activerecord/lib/active_record/persistence.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 869cbf5b2e..36530d2b63 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -105,13 +105,14 @@ module ActiveRecord # Updates a single attribute and saves the record. # This is especially useful for boolean flags on existing records. Also note that # - # * validation is skipped + # * The attribute being updated must be a column name. + # * Validation is skipped # * No callbacks are invoked # * updated_at/updated_on column is updated if that column is available - # * does not work on associations - # * does not work on attr_accessor attributes. The attribute that is being updated must be column name. - # * does not work on new record. record.new_record? should return false for this method to work. - # * updates only the attribute that is input to the method. If there are other changed attributes then + # * Does not work on associations + # * Does not work on attr_accessor attributes. + # * Does not work on new record. record.new_record? should return false for this method to work. + # * Updates only the attribute that is input to the method. If there are other changed attributes then # those attributes are left alone. In that case even after this method has done its work record.changed? # will return true. # -- cgit v1.2.3 From 31f8a0cd23ca3d1137968e95bce47a431555bdd6 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 28 Jul 2010 13:10:04 -0400 Subject: adding punctuations --- activerecord/lib/active_record/persistence.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 36530d2b63..38b91652ee 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -106,10 +106,10 @@ module ActiveRecord # This is especially useful for boolean flags on existing records. Also note that # # * The attribute being updated must be a column name. - # * Validation is skipped - # * No callbacks are invoked - # * updated_at/updated_on column is updated if that column is available - # * Does not work on associations + # * Validation is skipped. + # * No callbacks are invoked. + # * updated_at/updated_on column is updated if that column is available. + # * Does not work on associations. # * Does not work on attr_accessor attributes. # * Does not work on new record. record.new_record? should return false for this method to work. # * Updates only the attribute that is input to the method. If there are other changed attributes then -- cgit v1.2.3 From f46e841f7217dd839ce166ec9a592f0dec127673 Mon Sep 17 00:00:00 2001 From: Simon Tokumine Date: Wed, 28 Jul 2010 14:56:05 -0700 Subject: removed ambiguity from autosave description --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 1b9b725dd4..f540aa7f25 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -303,7 +303,7 @@ module ActiveRecord # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be # aware of, mostly involving the saving of associated objects. # - # Unless you set the :autosave option on a has_one, belongs_to, + # You can set the :autosave option on a has_one, belongs_to, # has_many, or has_and_belongs_to_many association. Setting it # to +true+ will _always_ save the members, whereas setting it to +false+ will # _never_ save the members. -- cgit v1.2.3 From 1c030a3c3c61d6d6262785bf67e1d8f44da87ea5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 28 Jul 2010 17:01:19 -0700 Subject: avoid calling to_sql twice --- activerecord/lib/active_record/relation/query_methods.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0593897fa5..cba22c2207 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -135,11 +135,14 @@ module ActiveRecord next if where.blank? case where - when Arel::SqlLiteral - arel = arel.where(where) + when Arel::Predicates::In + # FIXME: this needs to go away + # when an IN is part of a larger query, the SQL seems to be different + arel = arel.where(Arel::SqlLiteral.new("(#{where.to_sql})")) + when String + arel = arel.where(Arel::SqlLiteral.new("(#{where})")) else - sql = where.is_a?(String) ? where : where.to_sql - arel = arel.where(Arel::SqlLiteral.new("(#{sql})")) + arel = arel.where where end end -- cgit v1.2.3 From d69949ed0d63ba19051a24cba992bd4655d0f5be Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 28 Jul 2010 17:03:52 -0700 Subject: Shouldn't have committed this! Revert "avoid calling to_sql twice" This reverts commit 1c030a3c3c61d6d6262785bf67e1d8f44da87ea5. --- activerecord/lib/active_record/relation/query_methods.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index cba22c2207..0593897fa5 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -135,14 +135,11 @@ module ActiveRecord next if where.blank? case where - when Arel::Predicates::In - # FIXME: this needs to go away - # when an IN is part of a larger query, the SQL seems to be different - arel = arel.where(Arel::SqlLiteral.new("(#{where.to_sql})")) - when String - arel = arel.where(Arel::SqlLiteral.new("(#{where})")) + when Arel::SqlLiteral + arel = arel.where(where) else - arel = arel.where where + sql = where.is_a?(String) ? where : where.to_sql + arel = arel.where(Arel::SqlLiteral.new("(#{sql})")) end end -- cgit v1.2.3 From 0f0bdaea7bf46c316ee63a9a66c8668354f142f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 29 Jul 2010 09:10:01 +0200 Subject: Revert "Avoid uneeded queries in session stores if sid is not given." First step to merge Rails and Rack session stores. Rack always expects to receive the SID since it may have different behavior if the SID is nil. This reverts commit e210895ba95e498b9debbf43a3e5ae588bca81f0. --- activerecord/lib/active_record/session_store.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 766e63edc7..becde0fbfd 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -293,6 +293,7 @@ module ActiveRecord private def get_session(env, sid) Base.silence do + sid ||= generate_sid session = find_session(sid) env[SESSION_RECORD_KEY] = session [sid, session.data] -- cgit v1.2.3 From 873c5a9e5924a7776692b9a17b03d0becec6e513 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 29 Jul 2010 10:29:33 -0400 Subject: adding documentation regarding time_zone_aware_attributes and skip_time_zone_conversion_for_attributes --- activerecord/lib/active_record/base.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 5898ec3732..99043af1a5 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -220,6 +220,19 @@ module ActiveRecord #:nodoc: # user = User.create(:preferences => %w( one two three )) # User.find(user.id).preferences # raises SerializationTypeMismatch # + # == Time Zone aware attributes + # + # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code. + # + # ActiveRecord::Base.time_zone_aware_attributes = true + # + # This feature can easily be turned off by assigning value false . + # + # If your attributes are time zone aware and you desire to skip time zone conversion for certain attributes then you can do following: + # + # Topic.skip_time_zone_conversion_for_attributes = [:written_on] + # + # # == Single table inheritance # # Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed -- cgit v1.2.3 From e42945333b1cb8e387699524b19fbad7ae23dcb7 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Thu, 29 Jul 2010 10:37:19 -0400 Subject: moving time zone related documentation to timestamp.rb from base.rb --- activerecord/lib/active_record/base.rb | 13 ------------- activerecord/lib/active_record/timestamp.rb | 12 ++++++++++++ 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 99043af1a5..5898ec3732 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -220,19 +220,6 @@ module ActiveRecord #:nodoc: # user = User.create(:preferences => %w( one two three )) # User.find(user.id).preferences # raises SerializationTypeMismatch # - # == Time Zone aware attributes - # - # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code. - # - # ActiveRecord::Base.time_zone_aware_attributes = true - # - # This feature can easily be turned off by assigning value false . - # - # If your attributes are time zone aware and you desire to skip time zone conversion for certain attributes then you can do following: - # - # Topic.skip_time_zone_conversion_for_attributes = [:written_on] - # - # # == Single table inheritance # # Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 6c1e376745..92f7a7753d 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -12,6 +12,18 @@ module ActiveRecord # Timestamps are in the local timezone by default but you can use UTC by setting: # # ActiveRecord::Base.default_timezone = :utc + # + # == Time Zone aware attributes + # + # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code. + # + # ActiveRecord::Base.time_zone_aware_attributes = true + # + # This feature can easily be turned off by assigning value false . + # + # If your attributes are time zone aware and you desire to skip time zone conversion for certain attributes then you can do following: + # + # Topic.skip_time_zone_conversion_for_attributes = [:written_on] module Timestamp extend ActiveSupport::Concern -- cgit v1.2.3 From f171bc64a2fcb321284b10c0c454f16569505439 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 29 Jul 2010 14:55:57 -0700 Subject: PERF: avoiding splat args and reducing function calls --- activerecord/lib/active_record/relation/query_methods.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0593897fa5..05962f409f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -47,9 +47,9 @@ module ActiveRecord clone.tap {|r| r.joins_values += args if args.present? } end - def where(*args) - value = build_where(*args) - clone.tap {|r| r.where_values += Array.wrap(value) if value.present? } + def where(opts, other = nil) + value = build_where(opts, other) + value ? clone : clone.tap {|r| r.where_values += Array.wrap(value) } end def having(*args) @@ -166,13 +166,10 @@ module ActiveRecord arel end - def build_where(*args) - return if args.blank? - - opts = args.first + def build_where(opts, other = nil) case opts when String, Array - @klass.send(:sanitize_sql, args.size > 1 ? args : opts) + @klass.send(:sanitize_sql, other ? [opts, other] : opts) when Hash attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts) PredicateBuilder.new(table.engine).build_from_hash(attributes, table) -- cgit v1.2.3 From a7a6a2ff46b173b420bd493d727772531d72658f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 29 Jul 2010 16:08:30 -0700 Subject: got the ternary backwards. somehow the sqlite tests passed! --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 05962f409f..716e7275a5 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -49,7 +49,7 @@ module ActiveRecord def where(opts, other = nil) value = build_where(opts, other) - value ? clone : clone.tap {|r| r.where_values += Array.wrap(value) } + value ? clone.tap {|r| r.where_values += Array.wrap(value) } : clone end def having(*args) -- cgit v1.2.3 From 755af497555fde16db86f7e51f6462b0aca79b49 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 30 Jul 2010 02:30:04 +0200 Subject: edit pass to apply API guideline wrt the use of "# =>" in example code --- activerecord/lib/active_record/nested_attributes.rb | 4 ++-- activerecord/lib/active_record/relation.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index cf8c5aaf84..e652296e2c 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -78,7 +78,7 @@ module ActiveRecord # member.avatar_attributes = { :id => '2', :_destroy => '1' } # member.avatar.marked_for_destruction? # => true # member.save - # member.reload.avatar #=> nil + # member.reload.avatar # => nil # # Note that the model will _not_ be destroyed until the parent is saved. # @@ -180,7 +180,7 @@ module ActiveRecord # # member.attributes = params['member'] # member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true - # member.posts.length #=> 2 + # member.posts.length # => 2 # member.save # member.reload.posts.length # => 1 # diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 86a210d2be..a8cea44c78 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -108,7 +108,7 @@ module ActiveRecord # ==== Example # # Comment.where(:post_id => 1).scoping do - # Comment.first #=> SELECT * FROM comments WHERE post_id = 1 + # Comment.first # SELECT * FROM comments WHERE post_id = 1 # end # # Please check unscoped if you want to remove all previous scopes (including -- cgit v1.2.3 From 2ea922bcec333b6484a295b4fee1f4fa75c5704d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Wed, 10 Jun 2009 16:24:32 +0300 Subject: Don't increment and then decrement the same counter when re-assigning a belongs_to association [#2786 state:committed] Signed-off-by: Santiago Pastorino --- activerecord/lib/active_record/associations/belongs_to_association.rb | 2 +- activerecord/test/cases/associations/belongs_to_associations_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index c2a6495db5..4558872a2b 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -22,7 +22,7 @@ module ActiveRecord else raise_on_type_mismatch(record) - if counter_cache_name && !@owner.new_record? + if counter_cache_name && !@owner.new_record? && record.id != @owner[@reflection.primary_key_name] @reflection.klass.increment_counter(counter_cache_name, record.id) @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name] end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 4d5769d173..046433820d 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -215,6 +215,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase r1.topic = Topic.find(t2.id) + assert_no_queries do + r1.topic = t2 + end + assert r1.save assert_equal 0, Topic.find(t1.id).replies.size assert_equal 1, Topic.find(t2.id).replies.size -- cgit v1.2.3 From 0e20e3ebc20250174867f33e0ad3972f7e7c76d0 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 30 Jul 2010 14:43:13 -0400 Subject: returns not returned . fixing documentation for reflection --- activerecord/lib/active_record/reflection.rb | 58 ++++++++++++++-------------- 1 file changed, 30 insertions(+), 28 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 09d7f8699e..03a932f642 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -3,14 +3,14 @@ module ActiveRecord module Reflection # :nodoc: extend ActiveSupport::Concern - # Reflection allows you to interrogate Active Record classes and objects + # Reflection enables to interrogate Active Record classes and objects # about their associations and aggregations. This information can, - # for example, be used in a form builder that took an Active Record object - # and created input fields for all of the attributes depending on their type - # and displayed the associations to other objects. + # for example, be used in a form builder that takes an Active Record object + # and creates input fields for all of the attributes depending on their type + # and displays the associations to other objects. # - # You can find the interface for the AggregateReflection and AssociationReflection - # classes in the abstract MacroReflection class. + # MacroReflection class has info for AggregateReflection and AssociationReflection + # classes. module ClassMethods def create_reflection(macro, name, options, active_record) case macro @@ -24,7 +24,7 @@ module ActiveRecord reflection end - # Returns a hash containing all AssociationReflection objects for the current class + # Returns a hash containing all AssociationReflection objects for the current class. # Example: # # Invoice.reflections @@ -39,9 +39,9 @@ module ActiveRecord reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) } end - # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example: + # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). # - # Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection + # Account.reflect_on_aggregation(:balance) #=> the balance AggregateReflection # def reflect_on_aggregation(aggregation) reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil @@ -78,8 +78,7 @@ module ActiveRecord end - # Abstract base class for AggregateReflection and AssociationReflection that - # describes the interface available for both of those classes. Objects of + # Abstract base class for AggregateReflection and AssociationReflection. Objects of # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods. class MacroReflection attr_reader :active_record @@ -89,36 +88,41 @@ module ActiveRecord end # Returns the name of the macro. - # composed_of :balance, :class_name => 'Money' will return :balance - # has_many :clients will return :clients + # + # composed_of :balance, :class_name => 'Money' returns :balance + # has_many :clients returns :clients def name @name end # Returns the macro type. - # composed_of :balance, :class_name => 'Money' will return :composed_of - # has_many :clients will return :has_many + # + # composed_of :balance, :class_name => 'Money' returns :composed_of + # has_many :clients returns :has_many def macro @macro end # Returns the hash of options used for the macro. - # composed_of :balance, :class_name => 'Money' will return { :class_name => "Money" } - # has_many :clients will return +{}+ + # + # composed_of :balance, :class_name => 'Money' returns { :class_name => "Money" } + # has_many :clients returns +{}+ def options @options end # Returns the class for the macro. - # composed_of :balance, :class_name => 'Money' will return the Money class - # has_many :clients will return the Client class + # + # composed_of :balance, :class_name => 'Money' returns the Money class + # has_many :clients returns the Client class def klass @klass ||= class_name.constantize end # Returns the class name for the macro. - # composed_of :balance, :class_name => 'Money' will return 'Money' - # has_many :clients will return 'Client' + # + # composed_of :balance, :class_name => 'Money' returns 'Money' + # has_many :clients returns 'Client' def class_name @class_name ||= options[:class_name] || derive_class_name end @@ -153,7 +157,7 @@ module ActiveRecord # Holds all the meta-data about an association as it was specified in the # Active Record class. class AssociationReflection < MacroReflection #:nodoc: - # Returns the target association's class: + # Returns the target association's class. # # class Author < ActiveRecord::Base # has_many :books @@ -162,7 +166,7 @@ module ActiveRecord # Author.reflect_on_association(:books).klass # # => Book # - # Note: do not call +klass.new+ or +klass.create+ to instantiate + # Note: Do not call +klass.new+ or +klass.create+ to instantiate # a new association object. Use +build_association+ or +create_association+ # instead. This allows plugins to hook into association object creation. def klass @@ -273,7 +277,7 @@ module ActiveRecord end # Returns whether or not this association reflection is for a collection - # association. Returns +true+ if the +macro+ is one of +has_many+ or + # association. Returns +true+ if the +macro+ is either +has_many+ or # +has_and_belongs_to_many+, +false+ otherwise. def collection? @collection @@ -283,7 +287,7 @@ module ActiveRecord # the parent's validation. # # Unless you explicitly disable validation with - # :validate => false, it will take place when: + # :validate => false, validation will take place when: # # * you explicitly enable validation; :validate => true # * you use autosave; :autosave => true @@ -327,8 +331,6 @@ module ActiveRecord # Gets the source of the through reflection. It checks both a singularized # and pluralized form for :belongs_to or :has_many. # - # (The :tags association on Tagging below.) - # # class Post < ActiveRecord::Base # has_many :taggings # has_many :tags, :through => :taggings @@ -339,7 +341,7 @@ module ActiveRecord end # Returns the AssociationReflection object specified in the :through option - # of a HasManyThrough or HasOneThrough association. Example: + # of a HasManyThrough or HasOneThrough association. # # class Post < ActiveRecord::Base # has_many :taggings -- cgit v1.2.3 From d1c53a9ad273f31618adcfbcabb39fd74bb2b91f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Jul 2010 13:38:09 -0700 Subject: reducing function calls and using faster methods for testing --- activerecord/lib/active_record/relation.rb | 14 +++++++------- activerecord/lib/active_record/relation/query_methods.rb | 12 +++--------- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a8cea44c78..7962f52738 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -99,7 +99,7 @@ module ActiveRecord if block_given? to_a.many? { |*block_args| yield(*block_args) } else - @limit_value.present? ? to_a.many? : size > 1 + @limit_value ? to_a.many? : size > 1 end end @@ -316,12 +316,12 @@ module ActiveRecord def scope_for_create @scope_for_create ||= begin - @create_with_value || @where_values.inject({}) do |hash, where| - if where.is_a?(Arel::Predicates::Equality) - hash[where.operand1.name] = where.operand2.respond_to?(:value) ? where.operand2.value : where.operand2 - end - hash - end + @create_with_value || Hash[ + @where_values.grep(Arel::Predicates::Equality).map { |where| + [where.operand1.name, + where.operand2.respond_to?(:value) ? + where.operand2.value : where.operand2] + }] end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 716e7275a5..4342dde5c8 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -129,7 +129,7 @@ module ActiveRecord def build_arel arel = table - arel = build_joins(arel, @joins_values) if @joins_values.present? + arel = build_joins(arel, @joins_values) unless @joins_values.empty? @where_values.uniq.each do |where| next if where.blank? @@ -145,7 +145,7 @@ module ActiveRecord arel = arel.having(*@having_values.uniq.select{|h| h.present?}) if @having_values.present? - arel = arel.take(@limit_value) if @limit_value.present? + arel = arel.take(@limit_value) if @limit_value arel = arel.skip(@offset_value) if @offset_value.present? arel = arel.group(*@group_values.uniq.select{|g| g.present?}) if @group_values.present? @@ -155,13 +155,7 @@ module ActiveRecord arel = build_select(arel, @select_values.uniq) arel = arel.from(@from_value) if @from_value.present? - - case @lock_value - when TrueClass - arel = arel.lock - when String - arel = arel.lock(@lock_value) - end if @lock_value.present? + arel = arel.lock(@lock_value) if @lock_value arel end -- cgit v1.2.3 From ecfb252e7a99bac6569f744064ef7f1ee1b31556 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Jul 2010 14:05:26 -0700 Subject: fewer method calls, using faster methods when possible --- activerecord/lib/active_record/relation/query_methods.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4342dde5c8..b941231a65 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -143,18 +143,18 @@ module ActiveRecord end end - arel = arel.having(*@having_values.uniq.select{|h| h.present?}) if @having_values.present? + arel = arel.having(*@having_values.uniq.select{|h| h.present?}) unless @having_values.empty? arel = arel.take(@limit_value) if @limit_value - arel = arel.skip(@offset_value) if @offset_value.present? + arel = arel.skip(@offset_value) if @offset_value - arel = arel.group(*@group_values.uniq.select{|g| g.present?}) if @group_values.present? + arel = arel.group(*@group_values.uniq.select{|g| g.present?}) unless @group_values.empty? - arel = arel.order(*@order_values.uniq.select{|o| o.present?}) if @order_values.present? + arel = arel.order(*@order_values.uniq.select{|o| o.present?}) unless @order_values.empty? arel = build_select(arel, @select_values.uniq) - arel = arel.from(@from_value) if @from_value.present? + arel = arel.from(@from_value) if @from_value arel = arel.lock(@lock_value) if @lock_value arel -- cgit v1.2.3 From 834e304b223723d63596034ef338d9af6655d35b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Jul 2010 14:12:01 -0700 Subject: reverting where() functionality, adding a test --- activerecord/lib/active_record/relation/query_methods.rb | 8 ++++---- activerecord/test/cases/relations_test.rb | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index b941231a65..a92d180442 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -47,8 +47,8 @@ module ActiveRecord clone.tap {|r| r.joins_values += args if args.present? } end - def where(opts, other = nil) - value = build_where(opts, other) + def where(opts, *rest) + value = build_where(opts, rest) value ? clone.tap {|r| r.where_values += Array.wrap(value) } : clone end @@ -160,10 +160,10 @@ module ActiveRecord arel end - def build_where(opts, other = nil) + def build_where(opts, other = []) case opts when String, Array - @klass.send(:sanitize_sql, other ? [opts, other] : opts) + @klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other)) when Hash attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts) PredicateBuilder.new(table.engine).build_from_hash(attributes, table) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index cb252d56fe..c9313fe7b6 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -22,6 +22,11 @@ class RelationTest < ActiveRecord::TestCase assert_equal 5, Post.where(:id => post_authors).size end + def test_multivalue_where + posts = Post.where('author_id = ? AND id = ?', 1, 1) + assert_equal 1, posts.to_a.size + end + def test_scoped topics = Topic.scoped assert_kind_of ActiveRecord::Relation, topics -- cgit v1.2.3 From 4f63294b0f4a428ed4dff172067ec114b86a1c25 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 31 Jul 2010 06:55:25 -0400 Subject: fixing documentation just a little bit --- activerecord/lib/active_record/associations.rb | 4 +-- .../lib/active_record/autosave_association.rb | 34 +++++++++------------- 2 files changed, 16 insertions(+), 22 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index f540aa7f25..9b9b843b42 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -114,7 +114,7 @@ module ActiveRecord autoload :HasOneAssociation, 'active_record/associations/has_one_association' autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association' - # Clears out the association cache + # Clears out the association cache. def clear_association_cache #:nodoc: self.class.reflect_on_all_associations.to_a.each do |assoc| instance_variable_set "@#{assoc.name}", nil @@ -122,7 +122,7 @@ module ActiveRecord end private - # Gets the specified association instance if it responds to :loaded?, nil otherwise. + # Returns the specified association instance if it responds to :loaded?, nil otherwise. def association_instance_get(name) ivar = "@#{name}" if instance_variable_defined?(ivar) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 062b010436..3129e8de47 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -4,14 +4,13 @@ module ActiveRecord # = Active Record Autosave Association # # AutosaveAssociation is a module that takes care of automatically saving - # your associations when the parent is saved. In addition to saving, it - # also destroys any associations that were marked for destruction. + # associacted records when parent is saved. In addition to saving, it + # also destroys any associated records that were marked for destruction. # (See mark_for_destruction and marked_for_destruction?) # # Saving of the parent, its associations, and the destruction of marked # associations, all happen inside 1 transaction. This should never leave the - # database in an inconsistent state after, for instance, mass assigning - # attributes and saving them. + # database in an inconsistent state. # # If validations for any of the associations fail, their error messages will # be applied to the parent. @@ -21,8 +20,6 @@ module ActiveRecord # # === One-to-one Example # - # Consider a Post model with one Author: - # # class Post # has_one :author, :autosave => true # end @@ -155,11 +152,12 @@ module ActiveRecord CODE end - # Adds a validate and save callback for the association as specified by + # Adds validation and save callbacks for the association as specified by # the +reflection+. # - # For performance reasons, we don't check whether to validate at runtime, - # but instead only define the method and callback when needed. However, + # For performance reasons, we don't check whether to validate at runtime. + # However the validation and callback methods are lazy and those methods + # get created when they are invoked for the very first time. However, # this can change, for instance, when using nested attributes, which is # called _after_ the association has been defined. Since we don't want # the callbacks to get defined multiple times, there are guards that @@ -197,14 +195,15 @@ module ActiveRecord end end - # Reloads the attributes of the object as usual and removes a mark for destruction. + # Reloads the attributes of the object as usual and clears marked_for_destruction/tt> flag. def reload(options = nil) @marked_for_destruction = false super end # Marks this record to be destroyed as part of the parents save transaction. - # This does _not_ actually destroy the record yet, rather it will be destroyed when parent.save is called. + # This does _not_ actually destroy the record instantly, rather child record will be destroyed + # when parent.save is called. # # Only useful if the :autosave option on the parent is enabled for this associated model. def mark_for_destruction @@ -249,7 +248,7 @@ module ActiveRecord end # Validate the association if :validate or :autosave is - # turned on for the association specified by +reflection+. + # turned on for the association. def validate_single_association(reflection) if (association = association_instance_get(reflection.name)) && !association.target.nil? association_valid?(reflection, association) @@ -357,14 +356,9 @@ module ActiveRecord end end - # Saves the associated record if it's new or :autosave is enabled - # on the association. - # - # In addition, it will destroy the association if it was marked for - # destruction with mark_for_destruction. + # Saves the associated record if it's new or :autosave is enabled. # - # This all happens inside a transaction, _if_ the Transactions module is included into - # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. + # In addition, it will destroy the association if it was marked for destruction. def save_belongs_to_association(reflection) if (association = association_instance_get(reflection.name)) && !association.destroyed? autosave = reflection.options[:autosave] @@ -384,4 +378,4 @@ module ActiveRecord end end end -end \ No newline at end of file +end -- cgit v1.2.3 From 69b401220c5b38b9e80292556e409c8459172375 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 31 Jul 2010 07:04:08 -0400 Subject: adding the missing closing tag --- activerecord/lib/active_record/autosave_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 3129e8de47..2c7afe3c9f 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -195,7 +195,7 @@ module ActiveRecord end end - # Reloads the attributes of the object as usual and clears marked_for_destruction/tt> flag. + # Reloads the attributes of the object as usual and clears marked_for_destruction flag. def reload(options = nil) @marked_for_destruction = false super -- cgit v1.2.3 From 6ac94829050534bff5b5e77b4f6518441afcf3d7 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 31 Jul 2010 07:12:29 -0400 Subject: ensuring that documentation does not exceed 100 columns --- activerecord/lib/active_record/aggregations.rb | 116 ++++++++++++++----------- 1 file changed, 65 insertions(+), 51 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index c45400d3d9..087462a5bb 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -9,11 +9,13 @@ module ActiveRecord end unless self.new_record? end - # Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes - # as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is] - # composed of [an] address". Each call to the macro adds a description of how the value objects are created from the - # attributes of the entity object (when the entity is initialized either as a new object or from finding an existing object) - # and how it can be turned back into attributes (when the entity is saved to the database). Example: + # Active Record implements aggregation through a macro-like class method called +composed_of+ + # for representing attributes as value objects. It expresses relationships like "Account [is] + # composed of Money [among other things]" or "Person [is] + # composed of [an] address". Each call to the macro adds a description of how the value objects + # are created from the attributes of the entity object (when the entity is initialized either + # as a new object or from finding an existing object) and how it can be turned back into attributes + # (when the entity is saved to the database). # # class Customer < ActiveRecord::Base # composed_of :balance, :class_name => "Money", :mapping => %w(balance amount) @@ -68,9 +70,10 @@ module ActiveRecord # end # end # - # Now it's possible to access attributes from the database through the value objects instead. If you choose to name the - # composition the same as the attribute's name, it will be the only way to access that attribute. That's the case with our - # +balance+ attribute. You interact with the value objects just like you would any other attribute, though: + # Now it's possible to access attributes from the database through the value objects instead. If + # you choose to name the composition the same as the attribute's name, it will be the only way to + # access that attribute. That's the case with our +balance+ attribute. You interact with the value + # objects just like you would any other attribute, though: # # customer.balance = Money.new(20) # sets the Money value object and the attribute # customer.balance # => Money value object @@ -79,8 +82,9 @@ module ActiveRecord # customer.balance == Money.new(20) # => true # customer.balance < Money.new(5) # => false # - # Value objects can also be composed of multiple attributes, such as the case of Address. The order of the mappings will - # determine the order of the parameters. Example: + # Value objects can also be composed of multiple attributes, such as the case of Address. The order + # of the mappings will + # determine the order of the parameters. # # customer.address_street = "Hyancintvej" # customer.address_city = "Copenhagen" @@ -91,38 +95,43 @@ module ActiveRecord # # == Writing value objects # - # Value objects are immutable and interchangeable objects that represent a given value, such as a Money object representing - # $5. Two Money objects both representing $5 should be equal (through methods such as == and <=> from Comparable if ranking - # makes sense). This is unlike entity objects where equality is determined by identity. An entity class such as Customer can - # easily have two different objects that both have an address on Hyancintvej. Entity identity is determined by object or - # relational unique identifiers (such as primary keys). Normal ActiveRecord::Base classes are entity objects. + # Value objects are immutable and interchangeable objects that represent a given value, such as + # a Money object representing $5. Two Money objects both representing $5 should be equal (through + # methods such as == and <=> from Comparable if ranking makes sense). This is + # unlike entity objects where equality is determined by identity. An entity class such as Customer can + # easily have two different objects that both have an address on Hyancintvej. Entity identity is + # determined by object or relational unique identifiers (such as primary keys). Normal + # ActiveRecord::Base classes are entity objects. # - # It's also important to treat the value objects as immutable. Don't allow the Money object to have its amount changed after - # creation. Create a new Money object with the new value instead. This is exemplified by the Money#exchange_to method that - # returns a new value object instead of changing its own values. Active Record won't persist value objects that have been - # changed through means other than the writer method. + # It's also important to treat the value objects as immutable. Don't allow the Money object to have + # its amount changed after creation. Create a new Money object with the new value instead. This + # is exemplified by the Money#exchange_to method that returns a new value object instead of changing + # its own values. Active Record won't persist value objects that have been changed through means + # other than the writer method. # - # The immutable requirement is enforced by Active Record by freezing any object assigned as a value object. Attempting to - # change it afterwards will result in a ActiveSupport::FrozenObjectError. + # The immutable requirement is enforced by Active Record by freezing any object assigned as a value + # object. Attempting to change it afterwards will result in a ActiveSupport::FrozenObjectError. # - # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not keeping value objects - # immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable + # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not + # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable # # == Custom constructors and converters # - # By default value objects are initialized by calling the new constructor of the value class passing each of the - # mapped attributes, in the order specified by the :mapping option, as arguments. If the value class doesn't support - # this convention then +composed_of+ allows a custom constructor to be specified. + # By default value objects are initialized by calling the new constructor of the value + # class passing each of the mapped attributes, in the order specified by the :mapping + # option, as arguments. If the value class doesn't support this convention then +composed_of+ allows + # a custom constructor to be specified. # - # When a new value is assigned to the value object the default assumption is that the new value is an instance of the value - # class. Specifying a custom converter allows the new value to be automatically converted to an instance of value class if - # necessary. + # When a new value is assigned to the value object the default assumption is that the new value + # is an instance of the value class. Specifying a custom converter allows the new value to be automatically + # converted to an instance of value class if necessary. # - # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be aggregated using the - # NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor for the value class is called +create+ and it - # expects a CIDR address string as a parameter. New values can be assigned to the value object using either another - # NetAddr::CIDR object, a string or an array. The :constructor and :converter options can be used to - # meet these requirements: + # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that + # should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor + # for the value class is called +create+ and it expects a CIDR address string as a parameter. New + # values can be assigned to the value object using either another NetAddr::CIDR object, a string + # or an array. The :constructor and :converter options can be used to meet + # these requirements: # # class NetworkResource < ActiveRecord::Base # composed_of :cidr, @@ -149,9 +158,9 @@ module ActiveRecord # # == Finding records by a value object # - # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database by specifying an instance - # of the value object in the conditions hash. The following example finds all customers with +balance_amount+ equal to 20 and - # +balance_currency+ equal to "USD": + # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database + # by specifying an instance of the value object in the conditions hash. The following example + # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD": # # Customer.find(:all, :conditions => {:balance => Money.new(20, "USD")}) # @@ -160,23 +169,28 @@ module ActiveRecord # composed_of :address adds address and address=(new_address) methods. # # Options are: - # * :class_name - Specifies the class name of the association. Use it only if that name can't be inferred - # from the part id. So composed_of :address will by default be linked to the Address class, but - # if the real class name is CompanyAddress, you'll have to specify it with this option. - # * :mapping - Specifies the mapping of entity attributes to attributes of the value object. Each mapping - # is represented as an array where the first item is the name of the entity attribute and the second item is the - # name the attribute in the value object. The order in which mappings are defined determine the order in which - # attributes are sent to the value class constructor. + # * :class_name - Specifies the class name of the association. Use it only if that name + # can't be inferred from the part id. So composed_of :address will by default be linked + # to the Address class, but if the real class name is CompanyAddress, you'll have to specify it + # with this option. + # * :mapping - Specifies the mapping of entity attributes to attributes of the value + # object. Each mapping is represented as an array where the first item is the name of the + # entity attribute and the second item is the name the attribute in the value object. The + # order in which mappings are defined determine the order in which attributes are sent to the + # value class constructor. # * :allow_nil - Specifies that the value object will not be instantiated when all mapped - # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all mapped attributes. + # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all + # mapped attributes. # This defaults to +false+. - # * :constructor - A symbol specifying the name of the constructor method or a Proc that is called to - # initialize the value object. The constructor is passed all of the mapped attributes, in the order that they - # are defined in the :mapping option, as arguments and uses them to instantiate a :class_name object. + # * :constructor - A symbol specifying the name of the constructor method or a Proc that + # is called to initialize the value object. The constructor is passed all of the mapped attributes, + # in the order that they are defined in the :mapping option, as arguments and uses them + # to instantiate a :class_name object. # The default is :new. - # * :converter - A symbol specifying the name of a class method of :class_name or a Proc that is - # called when a new value is assigned to the value object. The converter is passed the single value that is used - # in the assignment and is only called if the new value is not an instance of :class_name. + # * :converter - A symbol specifying the name of a class method of :class_name + # or a Proc that is called when a new value is assigned to the value object. The converter is + # passed the single value that is used in the assignment and is only called if the new value is + # not an instance of :class_name. # # Option examples: # composed_of :temperature, :mapping => %w(reading celsius) -- cgit v1.2.3 From b29c23a618731a4e7e49f79c617c5eb3714d7d8d Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sun, 1 Aug 2010 21:32:36 -0400 Subject: ensuring that documentation does not exceed 100 columns --- activerecord/lib/active_record/aggregations.rb | 13 +- .../lib/active_record/association_preload.rb | 16 +- activerecord/lib/active_record/associations.rb | 447 ++++++++++++--------- 3 files changed, 282 insertions(+), 194 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 087462a5bb..83a9ab46c5 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -11,11 +11,11 @@ module ActiveRecord # Active Record implements aggregation through a macro-like class method called +composed_of+ # for representing attributes as value objects. It expresses relationships like "Account [is] - # composed of Money [among other things]" or "Person [is] - # composed of [an] address". Each call to the macro adds a description of how the value objects - # are created from the attributes of the entity object (when the entity is initialized either - # as a new object or from finding an existing object) and how it can be turned back into attributes - # (when the entity is saved to the database). + # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call + # to the macro adds a description of how the value objects are created from the attributes of + # the entity object (when the entity is initialized either as a new object or from finding an + # existing object) and how it can be turned back into attributes (when the entity is saved to + # the database). # # class Customer < ActiveRecord::Base # composed_of :balance, :class_name => "Money", :mapping => %w(balance amount) @@ -83,8 +83,7 @@ module ActiveRecord # customer.balance < Money.new(5) # => false # # Value objects can also be composed of multiple attributes, such as the case of Address. The order - # of the mappings will - # determine the order of the parameters. + # of the mappings will determine the order of the parameters. # # customer.address_street = "Hyancintvej" # customer.address_city = "Copenhagen" diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 08601f8ef9..0f0fdc2e21 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -9,8 +9,8 @@ module ActiveRecord # Implements the details of eager loading of Active Record associations. # Application developers should not use this module directly. # - # ActiveRecord::Base is extended with this module. The source code in - # ActiveRecord::Base references methods defined in this module. + # ActiveRecord::Base is extended with this module. The source code in + # ActiveRecord::Base references methods defined in this module. # # Note that 'eager loading' and 'preloading' are actually the same thing. # However, there are two different eager loading strategies. @@ -55,7 +55,7 @@ module ActiveRecord # == Parameters # +records+ is an array of ActiveRecord::Base. This array needs not be flat, # i.e. +records+ itself may also contain arrays of records. In any case, - # +preload_associations+ will preload the associations all records by + # +preload_associations+ will preload the all associations records by # flattening +records+. # # +associations+ specifies one or more associations that you want to @@ -110,8 +110,8 @@ module ActiveRecord def preload_one_association(records, association, preload_options={}) class_to_reflection = {} # Not all records have the same class, so group then preload - # group on the reflection itself so that if various subclass share the same association then we do not split them - # unnecessarily + # group on the reflection itself so that if various subclass share the same association then + # we do not split them unnecessarily records.group_by { |record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, _records| raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection @@ -149,7 +149,8 @@ module ActiveRecord seen_keys = {} associated_records.each do |associated_record| #this is a has_one or belongs_to: there should only be one record. - #Unfortunately we can't (in portable way) ask the database for 'all records where foo_id in (x,y,z), but please + #Unfortunately we can't (in portable way) ask the database for + #'all records where foo_id in (x,y,z), but please # only one row per distinct foo_id' so this where we enforce that next if seen_keys[associated_record[key].to_s] seen_keys[associated_record[key].to_s] = true @@ -304,7 +305,8 @@ module ActiveRecord polymorph_type = options[:foreign_type] klasses_and_ids = {} - # Construct a mapping from klass to a list of ids to load and a mapping of those ids back to their parent_records + # Construct a mapping from klass to a list of ids to load and a mapping of those ids back + # to their parent_records records.each do |record| if klass = record.send(polymorph_type) klass_id = record.send(primary_key_name) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9b9b843b42..05d0200f90 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -136,10 +136,12 @@ module ActiveRecord instance_variable_set("@#{name}", association) end - # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like - # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are - # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own attr* - # methods. Example: + # Associations are a set of macro-like class methods for tying objects together through + # foreign keys. They express relationships like "Project has one Project Manager" + # or "Project belongs to a Portfolio". Each macro adds a number of methods to the + # class which are specialized according to the collection or association symbol and the + # options hash. It works much the same way as Ruby's own attr* + # methods. # # class Project < ActiveRecord::Base # belongs_to :portfolio @@ -148,7 +150,8 @@ module ActiveRecord # has_and_belongs_to_many :categories # end # - # The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships: + # The project class now has the following methods (and more) to ease the traversal and + # manipulation of its relationships: # * Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil? # * Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?, # * Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone), @@ -159,8 +162,9 @@ module ActiveRecord # # === A word of warning # - # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association - # adds a method with that name to its model, it will override the inherited method and break things. + # Don't create associations that have the same name as instance methods of + # ActiveRecord::Base. Since the association adds a method with that name to + # its model, it will override the inherited method and break things. # For instance, +attributes+ and +connection+ would be bad choices for association names. # # == Auto-generated methods @@ -270,8 +274,8 @@ module ActiveRecord # # == Is it a +belongs_to+ or +has_one+ association? # - # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class - # declaring the +belongs_to+ relationship. Example: + # Both express a 1-1 relationship. The difference is mostly where to place the foreign + # key, which goes on the table for the class declaring the +belongs_to+ relationship. # # class User < ActiveRecord::Base # # I reference an account. @@ -300,8 +304,9 @@ module ActiveRecord # # == Unsaved objects and associations # - # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be - # aware of, mostly involving the saving of associated objects. + # You can manipulate objects and associations before they are saved to the database, but + # there is some special behavior you should be aware of, mostly involving the saving of + # associated objects. # # You can set the :autosave option on a has_one, belongs_to, # has_many, or has_and_belongs_to_many association. Setting it @@ -310,26 +315,33 @@ module ActiveRecord # # === One-to-one associations # - # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in - # order to update their primary keys - except if the parent object is unsaved (new_record? == true). - # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment - # is cancelled. - # * If you wish to assign an object to a +has_one+ association without saving it, use the association.build method (documented below). - # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It - # does not save the parent either. + # * Assigning an object to a +has_one+ association automatically saves that object and + # the object being replaced (if there is one), in order to update their primary + # keys - except if the parent object is unsaved (new_record? == true). + # * If either of these saves fail (due to one of the objects being invalid) the assignment + # statement returns +false+ and the assignment is cancelled. + # * If you wish to assign an object to a +has_one+ association without saving it, + # use the association.build method (documented below). + # * Assigning an object to a +belongs_to+ association does not save the object, since + # the foreign key field belongs on the parent. It does not save the parent either. # # === Collections # - # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object - # (the owner of the collection) is not yet stored in the database. - # * If saving any of the objects being added to a collection (via push or similar) fails, then push returns +false+. - # * You can add an object to a collection without automatically saving it by using the collection.build method (documented below). - # * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved. + # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically + # saves that object, except if the parent object (the owner of the collection) is not yet + # stored in the database. + # * If saving any of the objects being added to a collection (via push or similar) + # fails, then push returns +false+. + # * You can add an object to a collection without automatically saving it by using the + # collection.build method (documented below). + # * All unsaved (new_record? == true) members of the collection are automatically + # saved when the parent is saved. # # === Association callbacks # - # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get - # triggered when you add an object to or remove an object from an association collection. Example: + # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, + # you can also define callbacks that get triggered when you add an object to or remove an + # object from an association collection. # # class Project # has_and_belongs_to_many :developers, :after_add => :evaluate_velocity @@ -342,19 +354,21 @@ module ActiveRecord # It's possible to stack callbacks by passing them as an array. Example: # # class Project - # has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}] + # has_and_belongs_to_many :developers, + # :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}] # end # # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+. # - # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with - # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed. + # Should any of the +before_add+ callbacks throw an exception, the object does not get + # added to the collection. Same with the +before_remove+ callbacks; if an exception is + # thrown the object doesn't get removed. # # === Association extensions # - # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially - # beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association. - # Example: + # The proxy objects that control the access to associations can be extended through anonymous + # modules. This is especially beneficial for adding new finders, creators, and other + # factory-type methods that are only used as part of this association. # # class Account < ActiveRecord::Base # has_many :people do @@ -369,7 +383,8 @@ module ActiveRecord # person.first_name # => "David" # person.last_name # => "Heinemeier Hansson" # - # If you need to share the same extensions between many associations, you can use a named extension module. Example: + # If you need to share the same extensions between many associations, you can use a named + # extension module. # # module FindOrCreateByNameExtension # def find_or_create_by_name(name) @@ -386,9 +401,10 @@ module ActiveRecord # has_many :people, :extend => FindOrCreateByNameExtension # end # - # If you need to use multiple named extension modules, you can specify an array of modules with the :extend option. - # In the case of name conflicts between methods in the modules, methods in modules later in the array supercede - # those earlier in the array. Example: + # If you need to use multiple named extension modules, you can specify an array of modules + # with the :extend option. + # In the case of name conflicts between methods in the modules, methods in modules later + # in the array supercede those earlier in the array. # # class Account < ActiveRecord::Base # has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension] @@ -399,12 +415,14 @@ module ActiveRecord # # * +proxy_owner+ - Returns the object the association is part of. # * +proxy_reflection+ - Returns the reflection object that describes the association. - # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+. + # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or + # the collection of associated objects for +has_many+ and +has_and_belongs_to_many+. # # === Association Join Models # - # Has Many associations can be configured with the :through option to use an explicit join model to retrieve the data. This - # operates similarly to a +has_and_belongs_to_many+ association. The advantage is that you're able to add validations, + # Has Many associations can be configured with the :through option to use an + # explicit join model to retrieve the data. This operates similarly to a + # +has_and_belongs_to_many+ association. The advantage is that you're able to add validations, # callbacks, and extra attributes on the join model. Consider the following schema: # # class Author < ActiveRecord::Base @@ -418,7 +436,7 @@ module ActiveRecord # end # # @author = Author.find :first - # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to. + # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to # @author.books # selects all books by using the Authorship join model # # You can also go through a +has_many+ association on the join model: @@ -439,7 +457,7 @@ module ActiveRecord # # @firm = Firm.find :first # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm - # @firm.invoices # selects all invoices by going through the Client join model. + # @firm.invoices # selects all invoices by going through the Client join model # # Similarly you can go through a +has_one+ association on the join model: # @@ -461,16 +479,18 @@ module ActiveRecord # @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group # @group.avatars # selects all avatars by going through the User join model. # - # An important caveat with going through +has_one+ or +has_many+ associations on the join model is that these associations are - # *read-only*. For example, the following would not work following the previous example: + # An important caveat with going through +has_one+ or +has_many+ associations on the + # join model is that these associations are *read-only*. For example, the following + # would not work following the previous example: # - # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around. + # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around # @group.avatars.delete(@group.avatars.last) # so would this # # === Polymorphic Associations # - # Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they - # specify an interface that a +has_many+ association must adhere to. + # Polymorphic associations on models are not restricted on what types of models they + # can be associated with. Rather, they specify an interface that a +has_many+ association + # must adhere to. # # class Asset < ActiveRecord::Base # belongs_to :attachable, :polymorphic => true @@ -482,13 +502,16 @@ module ActiveRecord # # @asset.attachable = @post # - # This works by using a type column in addition to a foreign key to specify the associated record. In the Asset example, you'd need - # an +attachable_id+ integer column and an +attachable_type+ string column. + # This works by using a type column in addition to a foreign key to specify the associated + # record. In the Asset example, you'd need an +attachable_id+ integer column and an + # +attachable_type+ string column. # - # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order - # for the associations to work as expected, ensure that you store the base model for the STI models in the - # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts - # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table. + # Using polymorphic associations in combination with single table inheritance (STI) is + # a little tricky. In order for the associations to work as expected, ensure that you + # store the base model for the STI models in the type column of the polymorphic + # association. To continue with the asset example above, suppose there are guest posts + # and member posts that use the posts table for STI. In this case, there must be a +type+ + # column in the posts table. # # class Asset < ActiveRecord::Base # belongs_to :attachable, :polymorphic => true @@ -511,9 +534,10 @@ module ActiveRecord # # == Caching # - # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically - # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without - # worrying too much about performance at the first go. Example: + # All of the methods are built on a simple caching principle that will keep the result + # of the last query around unless specifically instructed not to. The cache is even + # shared across methods to make it even cheaper to use the macro-added methods without + # worrying too much about performance at the first go. # # project.milestones # fetches milestones from the database # project.milestones.size # uses the milestone cache @@ -523,9 +547,10 @@ module ActiveRecord # # == Eager loading of associations # - # Eager loading is a way to find objects of a certain class and a number of named associations. This is - # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author - # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 2. Example: + # Eager loading is a way to find objects of a certain class and a number of named associations. + # This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 + # posts that each need to display their author triggers 101 database queries. Through the + # use of eager loading, the 101 queries can be reduced to 2. # # class Post < ActiveRecord::Base # belongs_to :author @@ -540,44 +565,55 @@ module ActiveRecord # puts "Last comment on: " + post.comments.first.created_on # end # - # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author: + # To iterate over these one hundred posts, we'll generate 201 database queries. Let's + # first just optimize it for retrieving the author: # # for post in Post.find(:all, :include => :author) # - # This references the name of the +belongs_to+ association that also used the :author symbol. After loading the posts, find - # will collect the +author_id+ from each one and load all the referenced authors with one query. Doing so will cut down the number of queries from 201 to 102. + # This references the name of the +belongs_to+ association that also used the :author + # symbol. After loading the posts, find will collect the +author_id+ from each one and load + # all the referenced authors with one query. Doing so will cut down the number of queries + # from 201 to 102. # # We can improve upon the situation further by referencing both associations in the finder with: # # for post in Post.find(:all, :include => [ :author, :comments ]) # - # This will load all comments with a single query. This reduces the total number of queries to 3. More generally the number of queries - # will be 1 plus the number of associations named (except if some of the associations are polymorphic +belongs_to+ - see below). + # This will load all comments with a single query. This reduces the total number of queries + # to 3. More generally the number of queries will be 1 plus the number of associations + # named (except if some of the associations are polymorphic +belongs_to+ - see below). # # To include a deep hierarchy of associations, use a hash: # # for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ]) # - # That'll grab not only all the comments but all their authors and gravatar pictures. You can mix and match - # symbols, arrays and hashes in any combination to describe the associations you want to load. + # That'll grab not only all the comments but all their authors and gravatar pictures. + # You can mix and match symbols, arrays and hashes in any combination to describe the + # associations you want to load. # - # All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced - # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no - # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above. + # All of this power shouldn't fool you into thinking that you can pull out huge amounts + # of data with no performance penalty just because you've reduced the number of queries. + # The database still needs to send all the data to Active Record and it still needs to + # be processed. So it's no catch-all for performance problems, but it's a great way to + # cut down on the number of queries in a situation as the one described above. # - # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case - # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example + # Since only one table is loaded at a time, conditions or orders cannot reference tables + # other than the main one. If this is the case Active Record falls back to the previously + # used LEFT OUTER JOIN based strategy. For example # # Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true]) # - # This will result in a single SQL query with joins along the lines of: LEFT OUTER JOIN comments ON comments.post_id = posts.id and - # LEFT OUTER JOIN authors ON authors.id = posts.author_id. Note that using conditions like this can have unintended consequences. - # In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole - # and not just to the association. You must disambiguate column references for this fallback to happen, for example + # This will result in a single SQL query with joins along the lines of: + # LEFT OUTER JOIN comments ON comments.post_id = posts.id and + # LEFT OUTER JOIN authors ON authors.id = posts.author_id. Note that using conditions + # like this can have unintended consequences. + # In the above example posts with no approved comments are not returned at all, because + # the conditions apply to the SQL statement as a whole and not just to the association. + # You must disambiguate column references for this fallback to happen, for example # :order => "author.name DESC" will work but :order => "name DESC" will not. # - # If you do want eager load only some members of an association it is usually more natural to :include an association - # which has conditions defined on it: + # If you do want eager load only some members of an association it is usually more natural + # to :include an association which has conditions defined on it: # # class Post < ActiveRecord::Base # has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true] @@ -585,9 +621,11 @@ module ActiveRecord # # Post.find(:all, :include => :approved_comments) # - # This will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved. + # This will load posts and eager load the +approved_comments+ association, which contains + # only those comments that have been approved. # - # If you eager load an association with a specified :limit option, it will be ignored, returning all the associated objects: + # If you eager load an association with a specified :limit option, it will be ignored, + # returning all the associated objects: # # class Picture < ActiveRecord::Base # has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10 @@ -595,8 +633,8 @@ module ActiveRecord # # Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments. # - # When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated - # before the actual model exists. + # When eager loaded, conditions are interpolated in the context of the model class, not + # the model instance. Conditions are lazily interpolated before the actual model exists. # # Eager loading is supported with polymorphic associations. # @@ -608,17 +646,21 @@ module ActiveRecord # # Address.find(:all, :include => :addressable) # - # This will execute one query to load the addresses and load the addressables with one query per addressable type. - # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of - # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback - # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent - # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query. + # This will execute one query to load the addresses and load the addressables with one + # query per addressable type. + # For example if all the addressables are either of class Person or Company then a total + # of 3 queries will be executed. The list of addressable types to load is determined on + # the back of the addresses loaded. This is not supported if Active Record has to fallback + # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. + # The reason is that the parent model's type is a column value so its corresponding table + # name cannot be put in the +FROM+/+JOIN+ clauses of that query. # # == Table Aliasing # - # Active Record uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once, - # the standard table name is used. The second time, the table is aliased as #{reflection_name}_#{parent_table_name}. Indexes are appended - # for any more successive uses of the table name. + # Active Record uses table aliasing in the case that a table is referenced multiple times + # in a join. If a table is referenced only once, the standard table name is used. The + # second time, the table is aliased as #{reflection_name}_#{parent_table_name}. + # Indexes are appended for any more successive uses of the table name. # # Post.find :all, :joins => :comments # # => SELECT ... FROM posts INNER JOIN comments ON ... @@ -651,7 +693,8 @@ module ActiveRecord # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2 # - # If you wish to specify your own custom joins using a :joins option, those table names will take precedence over the eager associations: + # If you wish to specify your own custom joins using a :joins option, those table + # names will take precedence over the eager associations: # # Post.find :all, :joins => :comments, :joins => "inner join comments ..." # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ... @@ -660,7 +703,8 @@ module ActiveRecord # INNER JOIN comments special_comments_posts ... # INNER JOIN comments ... # - # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database. + # Table aliases are automatically truncated according to the maximum length of table identifiers + # according to the specific database. # # == Modules # @@ -676,9 +720,10 @@ module ActiveRecord # end # end # - # When Firm#clients is called, it will in turn call MyApplication::Business::Client.find_all_by_firm_id(firm.id). - # If you want to associate with a class in another module scope, this can be done by specifying the complete class name. - # Example: + # When Firm#clients is called, it will in turn call + # MyApplication::Business::Client.find_all_by_firm_id(firm.id). + # If you want to associate with a class in another module scope, this can be done by + # specifying the complete class name. # # module MyApplication # module Business @@ -694,8 +739,8 @@ module ActiveRecord # # == Bi-directional associations # - # When you specify an association there is usually an association on the associated model that specifies the same - # relationship in reverse. For example, with the following models: + # When you specify an association there is usually an association on the associated model + # that specifies the same relationship in reverse. For example, with the following models: # # class Dungeon < ActiveRecord::Base # has_many :traps @@ -710,9 +755,11 @@ module ActiveRecord # belongs_to :dungeon # end # - # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are the inverse of each other and the - # inverse of the +dungeon+ association on +EvilWizard+ is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, - # Active Record doesn't know anything about these inverse relationships and so no object loading optimisation is possible. For example: + # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are + # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+ + # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, + # Active Record doesn't know anything about these inverse relationships and so no object + # loading optimisation is possible. For example: # # d = Dungeon.first # t = d.traps.first @@ -720,9 +767,11 @@ module ActiveRecord # d.level = 10 # d.level == t.dungeon.level # => false # - # The +Dungeon+ instances +d+ and t.dungeon in the above example refer to the same object data from the database, but are - # actually different in-memory copies of that data. Specifying the :inverse_of option on associations lets you tell - # Active Record about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to: + # The +Dungeon+ instances +d+ and t.dungeon in the above example refer to + # the same object data from the database, but are actually different in-memory copies + # of that data. Specifying the :inverse_of option on associations lets you tell + # Active Record about inverse relationships and it will optimise object loading. For + # example, if we changed our model definitions to: # # class Dungeon < ActiveRecord::Base # has_many :traps, :inverse_of => :dungeon @@ -737,8 +786,8 @@ module ActiveRecord # belongs_to :dungeon, :inverse_of => :evil_wizard # end # - # Then, from our code snippet above, +d+ and t.dungeon are actually the same in-memory instance and our final d.level == t.dungeon.level - # will return +true+. + # Then, from our code snippet above, +d+ and t.dungeon are actually the same + # in-memory instance and our final d.level == t.dungeon.level will return +true+. # # There are limitations to :inverse_of support: # @@ -748,13 +797,13 @@ module ActiveRecord # # == Type safety with ActiveRecord::AssociationTypeMismatch # - # If you attempt to assign an object to an association that doesn't match the inferred or specified :class_name, you'll - # get an ActiveRecord::AssociationTypeMismatch. + # If you attempt to assign an object to an association that doesn't match the inferred + # or specified :class_name, you'll get an ActiveRecord::AssociationTypeMismatch. # # == Options # - # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones - # possible. + # All of the association macros can be specialized through options. This makes cases + # more complex than the simple and guessable ones possible. module ClassMethods # Specifies a one-to-many association. The following methods for retrieval and query of # collections of associated objects will be added: @@ -828,20 +877,22 @@ module ActiveRecord # === Supported options # [:class_name] # Specify the class name of the association. Use it only if that name can't be inferred - # from the association name. So has_many :products will by default be linked to the Product class, but - # if the real class name is SpecialProduct, you'll have to specify it with this option. + # from the association name. So has_many :products will by default be linked + # to the Product class, but if the real class name is SpecialProduct, you'll have to + # specify it with this option. # [:conditions] # Specify the conditions that the associated objects must meet in order to be included as a +WHERE+ - # SQL fragment, such as price > 5 AND name LIKE 'B%'. Record creations from the association are scoped if a hash - # is used. has_many :posts, :conditions => {:published => true} will create published posts with @blog.posts.create - # or @blog.posts.build. + # SQL fragment, such as price > 5 AND name LIKE 'B%'. Record creations from + # the association are scoped if a hash is used. + # has_many :posts, :conditions => {:published => true} will create published + # posts with @blog.posts.create or @blog.posts.build. # [:order] # Specify the order in which the associated objects are returned as an ORDER BY SQL fragment, # such as last_name, first_name DESC. # [:foreign_key] # Specify the foreign key used for the association. By default this is guessed to be the name - # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id" - # as the default :foreign_key. + # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ + # association will use "person_id" as the default :foreign_key. # [:primary_key] # Specify the method that returns the primary key used for the association. By default this is +id+. # [:dependent] @@ -855,10 +906,12 @@ module ActiveRecord # # [:finder_sql] # Specify a complete SQL statement to fetch the association. This is a good way to go for complex - # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added. + # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ + # is _not_ added. # [:counter_sql] # Specify a complete SQL statement to fetch the size of the association. If :finder_sql is - # specified but not :counter_sql, :counter_sql will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM. + # specified but not :counter_sql, :counter_sql will be generated by + # replacing SELECT ... FROM with SELECT COUNT(*) FROM. # [:extend] # Specify a named module for extending the proxy. See "Association extensions". # [:include] @@ -866,25 +919,31 @@ module ActiveRecord # [:group] # An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. # [:having] - # Combined with +:group+ this can be used to filter the records that a GROUP BY returns. Uses the HAVING SQL-clause. + # Combined with +:group+ this can be used to filter the records that a GROUP BY + # returns. Uses the HAVING SQL-clause. # [:limit] # An integer determining the limit on the number of rows that should be returned. # [:offset] - # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. + # An integer determining the offset from where the rows should be fetched. So at 5, + # it would skip the first 4 rows. # [:select] - # By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join - # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error. + # By default, this is * as in SELECT * FROM, but can be changed if + # you, for example, want to do a join but not include the joined columns. Do not forget + # to include the primary and foreign keys, otherwise it will raise an error. # [:as] # Specifies a polymorphic interface (See belongs_to). # [:through] - # Specifies a join model through which to perform the query. Options for :class_name and :foreign_key - # are ignored, as the association uses the source reflection. You can only use a :through query through a belongs_to - # has_one or has_many association on the join model. The collection of join models can be managed via the collection - # API. For example, new join models are created for newly associated objects, and if some are gone their rows are deleted (directly, + # Specifies a join model through which to perform the query. Options for :class_name + # and :foreign_key are ignored, as the association uses the source reflection. You + # can only use a :through query through a belongs_to, has_one + # or has_many association on the join model. The collection of join models + # can be managed via the collection API. For example, new join models are created for + # newly associated objects, and if some are gone their rows are deleted (directly, # no destroy callbacks are triggered). # [:source] - # Specifies the source association name used by has_many :through queries. Only use it if the name cannot be - # inferred from the association. has_many :subscribers, :through => :subscriptions will look for either :subscribers or + # Specifies the source association name used by has_many :through queries. + # Only use it if the name cannot be inferred from the association. + # has_many :subscribers, :through => :subscriptions will look for either :subscribers or # :subscriber on Subscription, unless a :source is given. # [:source_type] # Specifies type of the source association used by has_many :through queries where the source @@ -896,12 +955,14 @@ module ActiveRecord # [:validate] # If false, don't validate the associated objects when saving the parent object. true by default. # [:autosave] - # If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object. + # If true, always save the associated objects or destroy them if marked for destruction, + # when saving the parent object. # If false, never save or destroy the associated objects. # By default, only save associated objects that are new records. # [:inverse_of] - # Specifies the name of the belongs_to association on the associated object that is the inverse of this has_many - # association. Does not work in combination with :through or :as options. + # Specifies the name of the belongs_to association on the associated object + # that is the inverse of this has_many association. Does not work in combination + # with :through or :as options. # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail. # # Option examples: @@ -975,19 +1036,20 @@ module ActiveRecord # [:conditions] # Specify the conditions that the associated object must meet in order to be included as a +WHERE+ # SQL fragment, such as rank = 5. Record creation from the association is scoped if a hash - # is used. has_one :account, :conditions => {:enabled => true} will create an enabled account with @company.create_account - # or @company.build_account. + # is used. has_one :account, :conditions => {:enabled => true} will create + # an enabled account with @company.create_account or @company.build_account. # [:order] # Specify the order in which the associated objects are returned as an ORDER BY SQL fragment, # such as last_name, first_name DESC. # [:dependent] # If set to :destroy, the associated object is destroyed when this object is. If set to - # :delete, the associated object is deleted *without* calling its destroy method. If set to :nullify, the associated - # object's foreign key is set to +NULL+. Also, association is assigned. + # :delete, the associated object is deleted *without* calling its destroy method. + # If set to :nullify, the associated object's foreign key is set to +NULL+. + # Also, association is assigned. # [:foreign_key] # Specify the foreign key used for the association. By default this is guessed to be the name - # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association will use "person_id" - # as the default :foreign_key. + # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association + # will use "person_id" as the default :foreign_key. # [:primary_key] # Specify the method that returns the primary key used for the association. By default this is +id+. # [:include] @@ -995,15 +1057,18 @@ module ActiveRecord # [:as] # Specifies a polymorphic interface (See belongs_to). # [:select] - # By default, this is * as in SELECT * FROM, but can be changed if, for example, you want to do a join - # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error. + # By default, this is * as in SELECT * FROM, but can be changed if, for example, + # you want to do a join but not include the joined columns. Do not forget to include the + # primary and foreign keys, otherwise it will raise an error. # [:through] - # Specifies a Join Model through which to perform the query. Options for :class_name and :foreign_key - # are ignored, as the association uses the source reflection. You can only use a :through query through a - # has_one or belongs_to association on the join model. + # Specifies a Join Model through which to perform the query. Options for :class_name + # and :foreign_key are ignored, as the association uses the source reflection. You + # can only use a :through query through a has_one or belongs_to + # association on the join model. # [:source] - # Specifies the source association name used by has_one :through queries. Only use it if the name cannot be - # inferred from the association. has_one :favorite, :through => :favorites will look for a + # Specifies the source association name used by has_one :through queries. + # Only use it if the name cannot be inferred from the association. + # has_one :favorite, :through => :favorites will look for a # :favorite on Favorite, unless a :source is given. # [:source_type] # Specifies type of the source association used by has_one :through queries where the source @@ -1013,17 +1078,19 @@ module ActiveRecord # [:validate] # If false, don't validate the associated object when saving the parent object. +false+ by default. # [:autosave] - # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. - # If false, never save or destroy the associated object. + # If true, always save the associated object or destroy it if marked for destruction, + # when saving the parent object. If false, never save or destroy the associated object. # By default, only save the associated object if it's a new record. # [:inverse_of] - # Specifies the name of the belongs_to association on the associated object that is the inverse of this has_one - # association. Does not work in combination with :through or :as options. + # Specifies the name of the belongs_to association on the associated object + # that is the inverse of this has_one association. Does not work in combination + # with :through or :as options. # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail. # # Option examples: # has_one :credit_card, :dependent => :destroy # destroys the associated credit card - # has_one :credit_card, :dependent => :nullify # updates the associated records foreign key value to NULL rather than destroying it + # has_one :credit_card, :dependent => :nullify # updates the associated records foreign + # # key value to NULL rather than destroying it # has_one :last_comment, :class_name => "Comment", :order => "posted_on" # has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'" # has_one :attachment, :as => :attachable @@ -1085,27 +1152,34 @@ module ActiveRecord # Specify the conditions that the associated object must meet in order to be included as a +WHERE+ # SQL fragment, such as authorized = 1. # [:select] - # By default, this is * as in SELECT * FROM, but can be changed if, for example, you want to do a join - # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error. + # By default, this is * as in SELECT * FROM, but can be changed + # if, for example, you want to do a join but not include the joined columns. Do not + # forget to include the primary and foreign keys, otherwise it will raise an error. # [:foreign_key] # Specify the foreign key used for the association. By default this is guessed to be the name - # of the association with an "_id" suffix. So a class that defines a belongs_to :person association will use - # "person_id" as the default :foreign_key. Similarly, belongs_to :favorite_person, :class_name => "Person" - # will use a foreign key of "favorite_person_id". + # of the association with an "_id" suffix. So a class that defines a belongs_to :person + # association will use "person_id" as the default :foreign_key. Similarly, + # belongs_to :favorite_person, :class_name => "Person" will use a foreign key + # of "favorite_person_id". # [:primary_key] - # Specify the method that returns the primary key of associated object used for the association. By default this is id. + # Specify the method that returns the primary key of associated object used for the association. + # By default this is id. # [:dependent] # If set to :destroy, the associated object is destroyed when this object is. If set to - # :delete, the associated object is deleted *without* calling its destroy method. This option should not be specified when - # belongs_to is used in conjunction with a has_many relationship on another class because of the potential to leave + # :delete, the associated object is deleted *without* calling its destroy method. + # This option should not be specified when belongs_to is used in conjunction with + # a has_many relationship on another class because of the potential to leave # orphaned records behind. # [:counter_cache] # Caches the number of belonging objects on the associate class through the use of +increment_counter+ - # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's - # destroyed. This requires that a column named #{table_name}_count (such as +comments_count+ for a belonging Comment class) - # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing - # a column name instead of a +true+/+false+ value to this option (e.g., :counter_cache => :my_custom_counter.) - # Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+. + # and +decrement_counter+. The counter cache is incremented when an object of this + # class is created and decremented when it's destroyed. This requires that a column + # named #{table_name}_count (such as +comments_count+ for a belonging Comment class) + # is used on the associate class (such as a Post class). You can also specify a custom counter + # cache column by providing a column name instead of a +true+/+false+ value to this + # option (e.g., :counter_cache => :my_custom_counter.) + # Note: Specifying a counter cache will add it to that model's list of readonly attributes + # using +attr_readonly+. # [:include] # Specify second-order associations that should be eager loaded when this object is loaded. # [:polymorphic] @@ -1117,15 +1191,18 @@ module ActiveRecord # [:validate] # If false, don't validate the associated objects when saving the parent object. +false+ by default. # [:autosave] - # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. + # If true, always save the associated object or destroy it if marked for destruction, when + # saving the parent object. # If false, never save or destroy the associated object. # By default, only save the associated object if it's a new record. # [:touch] - # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or - # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute. + # If true, the associated object will be touched (the updated_at/on attributes set to now) + # when this record is either saved or destroyed. If you specify a symbol, that attribute + # will be updated with the current time instead of the updated_at/on attribute. # [:inverse_of] - # Specifies the name of the has_one or has_many association on the associated object that is the inverse of this belongs_to - # association. Does not work in combination with the :polymorphic options. + # Specifies the name of the has_one or has_many association on the associated + # object that is the inverse of this belongs_to association. Does not work in + # combination with the :polymorphic options. # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail. # # Option examples: @@ -1159,9 +1236,10 @@ module ActiveRecord # Specifies a many-to-many relationship with another class. This associates two classes via an # intermediate join table. Unless the join table is explicitly specified as an option, it is # guessed using the lexical order of the class names. So a join between Developer and Project - # will give the default join table name of "developers_projects" because "D" outranks "P". Note that this precedence - # is calculated using the < operator for String. This means that if the strings are of different lengths, - # and the strings are equal when compared up to the shortest length, then the longer string is considered of higher + # will give the default join table name of "developers_projects" because "D" outranks "P". + # Note that this precedence is calculated using the < operator for String. This + # means that if the strings are of different lengths, and the strings are equal when compared + # up to the shortest length, then the longer string is considered of higher # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the @@ -1183,9 +1261,10 @@ module ActiveRecord # end # end # - # Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through - # +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as - # readonly (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any + # Deprecated: Any additional fields added to the join table will be placed as attributes when + # pulling records out through +has_and_belongs_to_many+ associations. Records returned from join + # tables with additional attributes will be marked as readonly (because we can't save changes + # to the additional attributes). It's strongly recommended that you upgrade any # associations with attributes to a real join model (see introduction). # # Adds the following methods for retrieval and query: @@ -1225,7 +1304,8 @@ module ActiveRecord # with +attributes+ and linked to this object through the join table, but has not yet been saved. # [collection.create(attributes = {})] # Returns a new object of the collection type that has been instantiated - # with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation). + # with +attributes+, linked to this object through the join table, and that has already been + # saved (if it passed the validation). # # (+collection+ is replaced with the symbol passed as the first argument, so # has_and_belongs_to_many :categories would add among others categories.empty?.) @@ -1260,8 +1340,9 @@ module ActiveRecord # MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work. # [:foreign_key] # Specify the foreign key used for the association. By default this is guessed to be the name - # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_and_belongs_to_many+ association - # to Project will use "person_id" as the default :foreign_key. + # of this class in lower-case and "_id" suffixed. So a Person class that makes + # a +has_and_belongs_to_many+ association to Project will use "person_id" as the + # default :foreign_key. # [:association_foreign_key] # Specify the foreign key used for the association on the receiving side of the association. # By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed. @@ -1269,7 +1350,8 @@ module ActiveRecord # the association will use "project_id" as the default :association_foreign_key. # [:conditions] # Specify the conditions that the associated object must meet in order to be included as a +WHERE+ - # SQL fragment, such as authorized = 1. Record creations from the association are scoped if a hash is used. + # SQL fragment, such as authorized = 1. Record creations from the association are + # scoped if a hash is used. # has_many :posts, :conditions => {:published => true} will create published posts with @blog.posts.create # or @blog.posts.build. # [:order] @@ -1281,7 +1363,8 @@ module ActiveRecord # Overwrite the default generated SQL statement used to fetch the association with a manual statement # [:counter_sql] # Specify a complete SQL statement to fetch the size of the association. If :finder_sql is - # specified but not :counter_sql, :counter_sql will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM. + # specified but not :counter_sql, :counter_sql will be generated by + # replacing SELECT ... FROM with SELECT COUNT(*) FROM. # [:delete_sql] # Overwrite the default generated SQL statement used to remove links between the associated # classes with a manual statement. @@ -1295,20 +1378,24 @@ module ActiveRecord # [:group] # An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. # [:having] - # Combined with +:group+ this can be used to filter the records that a GROUP BY returns. Uses the HAVING SQL-clause. + # Combined with +:group+ this can be used to filter the records that a GROUP BY returns. + # Uses the HAVING SQL-clause. # [:limit] # An integer determining the limit on the number of rows that should be returned. # [:offset] - # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. + # An integer determining the offset from where the rows should be fetched. So at 5, + # it would skip the first 4 rows. # [:select] - # By default, this is * as in SELECT * FROM, but can be changed if, for example, you want to do a join - # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error. + # By default, this is * as in SELECT * FROM, but can be changed if, for example, + # you want to do a join but not include the joined columns. Do not forget to include the primary + # and foreign keys, otherwise it will raise an error. # [:readonly] # If true, all the associated objects are readonly through the association. # [:validate] # If false, don't validate the associated objects when saving the parent object. +true+ by default. # [:autosave] - # If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object. + # If true, always save the associated objects or destroy them if marked for destruction, when + # saving the parent object. # If false, never save or destroy the associated objects. # By default, only save associated objects that are new records. # -- cgit v1.2.3 From 21e81da33570ba9453831cfc59c4f42ff9cdc1b2 Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Mon, 2 Aug 2010 01:37:57 -0700 Subject: update tests for mysql2 support --- activerecord/Rakefile | 14 +- .../cases/adapters/mysql/active_schema_test.rb | 2 +- .../cases/adapters/mysql2/active_schema_test.rb | 125 +++++++++++++++ .../test/cases/adapters/mysql2/connection_test.rb | 42 +++++ .../cases/adapters/mysql2/reserved_word_test.rb | 176 +++++++++++++++++++++ .../associations/belongs_to_associations_test.rb | 2 +- .../has_and_belongs_to_many_associations_test.rb | 9 +- activerecord/test/cases/base_test.rb | 17 +- activerecord/test/cases/calculations_test.rb | 2 +- activerecord/test/cases/column_definition_test.rb | 34 ++++ activerecord/test/cases/defaults_test.rb | 2 +- activerecord/test/cases/migration_test.rb | 12 +- activerecord/test/cases/query_cache_test.rb | 2 +- activerecord/test/cases/schema_dumper_test.rb | 4 +- .../test/connections/native_mysql2/connection.rb | 25 +++ activerecord/test/schema/mysql2_specific_schema.rb | 24 +++ 16 files changed, 466 insertions(+), 26 deletions(-) create mode 100644 activerecord/test/cases/adapters/mysql2/active_schema_test.rb create mode 100644 activerecord/test/cases/adapters/mysql2/connection_test.rb create mode 100644 activerecord/test/cases/adapters/mysql2/reserved_word_test.rb create mode 100644 activerecord/test/connections/native_mysql2/connection.rb create mode 100644 activerecord/test/schema/mysql2_specific_schema.rb (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 36cd7e3e6c..c1e90cc099 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -24,14 +24,14 @@ def run_without_aborting(*tasks) abort "Errors running #{errors.join(', ')}" if errors.any? end -desc 'Run mysql, sqlite, and postgresql tests by default' +desc 'Run mysql, mysql2, sqlite, and postgresql tests by default' task :default => :test -desc 'Run mysql, sqlite, and postgresql tests' +desc 'Run mysql, mysql2, sqlite, and postgresql tests' task :test do tasks = defined?(JRUBY_VERSION) ? %w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) : - %w(test_mysql test_sqlite3 test_postgresql) + %w(test_mysql test_mysql2 test_sqlite3 test_postgresql) run_without_aborting(*tasks) end @@ -39,15 +39,15 @@ namespace :test do task :isolated do tasks = defined?(JRUBY_VERSION) ? %w(isolated_test_jdbcmysql isolated_test_jdbcsqlite3 isolated_test_jdbcpostgresql) : - %w(isolated_test_mysql isolated_test_sqlite3 isolated_test_postgresql) + %w(isolated_test_mysql isolated_test_mysql2 isolated_test_sqlite3 isolated_test_postgresql) run_without_aborting(*tasks) end end -%w( mysql postgresql sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter| +%w( mysql mysql2 postgresql sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter| Rake::TestTask.new("test_#{adapter}") { |t| connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}" - adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/] + adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/] t.libs << "test" << connection_path t.test_files = (Dir.glob( "test/cases/**/*_test.rb" ).reject { |x| x =~ /\/adapters\// @@ -59,7 +59,7 @@ end task "isolated_test_#{adapter}" do connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}" - adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/] + adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/] puts [adapter, adapter_short, connection_path].inspect ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) (Dir["test/cases/**/*_test.rb"].reject { diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb index ed4efdc1c0..509baacaef 100644 --- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -42,7 +42,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase assert_equal "DROP TABLE `people`", drop_table(:people) end - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) def test_create_mysql_database_with_encoding assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb new file mode 100644 index 0000000000..a83399d0cd --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -0,0 +1,125 @@ +require "cases/helper" + +class ActiveSchemaTest < ActiveRecord::TestCase + def setup + ActiveRecord::ConnectionAdapters::Mysql2Adapter.class_eval do + alias_method :execute_without_stub, :execute + remove_method :execute + def execute(sql, name = nil) return sql end + end + end + + def teardown + ActiveRecord::ConnectionAdapters::Mysql2Adapter.class_eval do + remove_method :execute + alias_method :execute, :execute_without_stub + end + end + + def test_add_index + # add_index calls index_name_exists? which can't work since execute is stubbed + ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(:define_method, :index_name_exists?) do |*| + false + end + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" + assert_equal expected, add_index(:people, :last_name, :length => nil) + + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" + assert_equal expected, add_index(:people, :last_name, :length => 10) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) + ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(:remove_method, :index_name_exists?) + end + + def test_drop_table + assert_equal "DROP TABLE `people`", drop_table(:people) + end + + if current_adapter?(:Mysql2Adapter) + def test_create_mysql_database_with_encoding + assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) + assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) + end + + def test_recreate_mysql_database_with_encoding + create_database(:luca, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) + end + end + + def test_add_column + assert_equal "ALTER TABLE `people` ADD `last_name` varchar(255)", add_column(:people, :last_name, :string) + end + + def test_add_column_with_limit + assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32) + end + + def test_drop_table_with_specific_database + assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people') + end + + def test_add_timestamps + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + end + ActiveRecord::Base.connection.add_timestamps :delete_me + assert column_present?('delete_me', 'updated_at', 'datetime') + assert column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end + end + end + + def test_remove_timestamps + with_real_execute do + begin + ActiveRecord::Base.connection.create_table :delete_me do |t| + t.timestamps + end + ActiveRecord::Base.connection.remove_timestamps :delete_me + assert !column_present?('delete_me', 'updated_at', 'datetime') + assert !column_present?('delete_me', 'created_at', 'datetime') + ensure + ActiveRecord::Base.connection.drop_table :delete_me rescue nil + end + end + end + + private + def with_real_execute + #we need to actually modify some data, so we make execute point to the original method + ActiveRecord::ConnectionAdapters::Mysql2Adapter.class_eval do + alias_method :execute_with_stub, :execute + remove_method :execute + alias_method :execute, :execute_without_stub + end + yield + ensure + #before finishing, we restore the alias to the mock-up method + ActiveRecord::ConnectionAdapters::Mysql2Adapter.class_eval do + remove_method :execute + alias_method :execute, :execute_with_stub + end + end + + + def method_missing(method_symbol, *arguments) + ActiveRecord::Base.connection.send(method_symbol, *arguments) + end + + def column_present?(table_name, column_name, type) + results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'") + results.first && results.first['Type'] == type + end +end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb new file mode 100644 index 0000000000..b973da621b --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -0,0 +1,42 @@ +require "cases/helper" + +class MysqlConnectionTest < ActiveRecord::TestCase + def setup + super + @connection = ActiveRecord::Base.connection + end + + def test_no_automatic_reconnection_after_timeout + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + assert !@connection.active? + end + + def test_successful_reconnection_after_timeout_with_manual_reconnect + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + @connection.reconnect! + assert @connection.active? + end + + def test_successful_reconnection_after_timeout_with_verify + assert @connection.active? + @connection.update('set @@wait_timeout=1') + sleep 2 + @connection.verify! + assert @connection.active? + end + + private + + def run_without_connection + original_connection = ActiveRecord::Base.remove_connection + begin + yield original_connection + ensure + ActiveRecord::Base.establish_connection(original_connection) + end + end +end diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb new file mode 100644 index 0000000000..90d8b0d923 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -0,0 +1,176 @@ +require "cases/helper" + +class Group < ActiveRecord::Base + Group.table_name = 'group' + belongs_to :select, :class_name => 'Select' + has_one :values +end + +class Select < ActiveRecord::Base + Select.table_name = 'select' + has_many :groups +end + +class Values < ActiveRecord::Base + Values.table_name = 'values' +end + +class Distinct < ActiveRecord::Base + Distinct.table_name = 'distinct' + has_and_belongs_to_many :selects + has_many :values, :through => :groups +end + +# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with +# reserved word names (ie: group, order, values, etc...) +class MysqlReservedWordTest < ActiveRecord::TestCase + def setup + @connection = ActiveRecord::Base.connection + + # we call execute directly here (and do similar below) because ActiveRecord::Base#create_table() + # will fail with these table names if these test cases fail + + create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int', + 'select'=>'id int auto_increment primary key', + 'values'=>'id int auto_increment primary key, group_id int', + 'distinct'=>'id int auto_increment primary key', + 'distincts_selects'=>'distinct_id int, select_id int' + end + + def teardown + drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order'] + end + + # create tables with reserved-word names and columns + def test_create_tables + assert_nothing_raised { + @connection.create_table :order do |t| + t.column :group, :string + end + } + end + + # rename tables with reserved-word names + def test_rename_tables + assert_nothing_raised { @connection.rename_table(:group, :order) } + end + + # alter column with a reserved-word name in a table with a reserved-word name + def test_change_columns + assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') } + #the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter + assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) } + assert_nothing_raised { @connection.rename_column(:group, :order, :values) } + end + + # dump structure of table with reserved word name + def test_structure_dump + assert_nothing_raised { @connection.structure_dump } + end + + # introspect table with reserved word name + def test_introspect + assert_nothing_raised { @connection.columns(:group) } + assert_nothing_raised { @connection.indexes(:group) } + end + + #fixtures + self.use_instantiated_fixtures = true + self.use_transactional_fixtures = false + + #fixtures :group + + def test_fixtures + f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + + assert_nothing_raised { + f.each do |x| + x.delete_existing_fixtures + end + } + + assert_nothing_raised { + f.each do |x| + x.insert_fixtures + end + } + end + + #activerecord model class with reserved-word table name + def test_activerecord_model + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + x = nil + assert_nothing_raised { x = Group.new } + x.order = 'x' + assert_nothing_raised { x.save } + x.order = 'y' + assert_nothing_raised { x.save } + assert_nothing_raised { y = Group.find_by_order('y') } + assert_nothing_raised { y = Group.find(1) } + x = Group.find(1) + end + + # has_one association with reserved-word table name + def test_has_one_associations + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + v = nil + assert_nothing_raised { v = Group.find(1).values } + assert_equal 2, v.id + end + + # belongs_to association with reserved-word table name + def test_belongs_to_associations + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + gs = nil + assert_nothing_raised { gs = Select.find(2).groups } + assert_equal gs.length, 2 + assert(gs.collect{|x| x.id}.sort == [2, 3]) + end + + # has_and_belongs_to_many with reserved-word table name + def test_has_and_belongs_to_many + create_test_fixtures :select, :distinct, :group, :values, :distincts_selects + s = nil + assert_nothing_raised { s = Distinct.find(1).selects } + assert_equal s.length, 2 + assert(s.collect{|x|x.id}.sort == [1, 2]) + end + + # activerecord model introspection with reserved-word table and column names + def test_activerecord_introspection + assert_nothing_raised { Group.table_exists? } + assert_nothing_raised { Group.columns } + end + + # Calculations + def test_calculations_work_with_reserved_words + assert_nothing_raised { Group.count } + end + + def test_associations_work_with_reserved_words + assert_nothing_raised { Select.find(:all, :include => [:groups]) } + end + + #the following functions were added to DRY test cases + + private + # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path + def create_test_fixtures(*fixture_names) + Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names) + end + + # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name + def drop_tables_directly(table_names, connection = @connection) + table_names.each do |name| + connection.execute("DROP TABLE IF EXISTS `#{name}`") + end + end + + # custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns + def create_tables_directly (tables, connection = @connection) + tables.each do |table_name, column_properties| + connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )") + end + end + +end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 046433820d..a1ce9b1689 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -32,7 +32,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase def test_belongs_to_with_primary_key_joins_on_correct_column sql = Client.joins(:firm_with_primary_key).to_sql - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) assert_no_match(/`firm_with_primary_keys_companies`\.`id`/, sql) assert_match(/`firm_with_primary_keys_companies`\.`name`/, sql) elsif current_adapter?(:OracleAdapter) 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 6b4a1d9408..ed7d9a782c 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 @@ -109,8 +109,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase record = con.select_rows(sql).last assert_not_nil record[2] assert_not_nil record[3] - assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2] - assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3] + if current_adapter?(:Mysql2Adapter) + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2].to_s(:db) + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3].to_s(:db) + else + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2] + assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3] + end end def test_should_record_timestamp_for_join_table_only_if_timestamp_should_be_recorded diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index df6895f0d0..34cce04794 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -116,12 +116,17 @@ class BasicsTest < ActiveRecord::TestCase end end + if current_adapter?(:Mysql2Adapter) + def test_read_attributes_before_type_cast_on_boolean + bool = Booleantest.create({ "value" => false }) + assert_equal 0, bool.reload.attributes_before_type_cast["value"] + end + end + def test_read_attributes_before_type_cast_on_datetime developer = Developer.find(:first) # Oracle adapter returns Time before type cast - unless current_adapter?(:OracleAdapter) - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] - else + if current_adapter?(:OracleAdapter) assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) developer.created_at = "345643456" @@ -131,6 +136,10 @@ class BasicsTest < ActiveRecord::TestCase developer.created_at = "2010-03-21T21:23:32+01:00" assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") + elsif current_adapter?(:Mysql2Adapter) + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) + else + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] end end @@ -564,7 +573,7 @@ class BasicsTest < ActiveRecord::TestCase assert Topic.find(2).approved? end - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) def test_update_all_with_order_and_limit assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC') end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 2c9d23c80f..afef31396e 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -325,7 +325,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_from_option_with_specified_index - if Edge.connection.adapter_name == 'MySQL' + if Edge.connection.adapter_name == 'MySQL' or Edge.connection.adapter_name == 'Mysql2' assert_equal Edge.count(:all), Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)') assert_equal Edge.count(:all, :conditions => 'sink_id < 5'), Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)', :conditions => 'sink_id < 5') diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb index b5767344cd..cc6a6b44f2 100644 --- a/activerecord/test/cases/column_definition_test.rb +++ b/activerecord/test/cases/column_definition_test.rb @@ -68,6 +68,40 @@ class ColumnDefinitionTest < ActiveRecord::TestCase end end + if current_adapter?(:Mysql2Adapter) + def test_should_set_default_for_mysql_binary_data_types + binary_column = ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", "a", "binary(1)") + assert_equal "a", binary_column.default + + varbinary_column = ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", "a", "varbinary(1)") + assert_equal "a", varbinary_column.default + end + + def test_should_not_set_default_for_blob_and_text_data_types + assert_raise ArgumentError do + ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", "a", "blob") + end + + assert_raise ArgumentError do + ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", "Hello", "text") + end + + text_column = ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", nil, "text") + assert_equal nil, text_column.default + + not_null_text_column = ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", nil, "text", false) + assert_equal "", not_null_text_column.default + end + + def test_has_default_should_return_false_for_blog_and_test_data_types + blob_column = ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", nil, "blob") + assert !blob_column.has_default? + + text_column = ActiveRecord::ConnectionAdapters::Mysql2Column.new("title", nil, "text") + assert !text_column.has_default? + end + end + if current_adapter?(:PostgreSQLAdapter) def test_bigint_column_should_map_to_integer bigint_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new('number', nil, "bigint") diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb index ef29422824..0e90128907 100644 --- a/activerecord/test/cases/defaults_test.rb +++ b/activerecord/test/cases/defaults_test.rb @@ -39,7 +39,7 @@ class DefaultTest < ActiveRecord::TestCase end end -if current_adapter?(:MysqlAdapter) +if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase # ActiveRecord::Base#create! (and #save and other related methods) will # open a new transaction. When in transactional fixtures mode, this will diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 2c3fc46831..0cf3979694 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -256,7 +256,7 @@ if ActiveRecord::Base.connection.supports_migrations? def test_create_table_with_defaults # MySQL doesn't allow defaults on TEXT or BLOB columns. - mysql = current_adapter?(:MysqlAdapter) + mysql = current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter) Person.connection.create_table :testings do |t| t.column :one, :string, :default => "hello" @@ -313,7 +313,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal 'integer', four.sql_type assert_equal 'bigint', eight.sql_type assert_equal 'integer', eleven.sql_type - elsif current_adapter?(:MysqlAdapter) + elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) assert_match 'int(11)', default.sql_type assert_match 'tinyint', one.sql_type assert_match 'int', four.sql_type @@ -581,7 +581,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_kind_of BigDecimal, bob.wealth end - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) def test_unabstracted_database_dependent_types Person.delete_all @@ -621,7 +621,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert !Person.column_methods_hash.include?(:last_name) end - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) def testing_table_for_positioning Person.connection.create_table :testings, :id => false do |t| t.column :first, :integer @@ -1447,7 +1447,7 @@ if ActiveRecord::Base.connection.supports_migrations? columns = Person.connection.columns(:binary_testings) data_column = columns.detect { |c| c.name == "data" } - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) assert_equal '', data_column.default else assert_nil data_column.default @@ -1748,7 +1748,7 @@ if ActiveRecord::Base.connection.supports_migrations? end def integer_column - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) 'int(11)' elsif current_adapter?(:OracleAdapter) 'NUMBER(38)' diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index f0d97a00d0..594db1d0ab 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -57,7 +57,7 @@ class QueryCacheTest < ActiveRecord::TestCase # Oracle adapter returns count() as Fixnum or Float if current_adapter?(:OracleAdapter) assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") - elsif current_adapter?(:SQLite3Adapter) && SQLite3::Version::VERSION > '1.2.5' + elsif current_adapter?(:SQLite3Adapter) && SQLite3::Version::VERSION > '1.2.5' or current_adapter?(:Mysql2Adapter) # Future versions of the sqlite3 adapter will return numeric assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 1c43e3c5b5..66446b6b7e 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -93,7 +93,7 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{c_int_4.*}, output assert_no_match %r{c_int_4.*:limit}, output - elsif current_adapter?(:MysqlAdapter) + elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) assert_match %r{c_int_1.*:limit => 1}, output assert_match %r{c_int_2.*:limit => 2}, output assert_match %r{c_int_3.*:limit => 3}, output @@ -169,7 +169,7 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved" end - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter) def test_schema_dump_should_not_add_default_value_for_mysql_text_field output = standard_dump assert_match %r{t.text\s+"body",\s+:null => false$}, output diff --git a/activerecord/test/connections/native_mysql2/connection.rb b/activerecord/test/connections/native_mysql2/connection.rb new file mode 100644 index 0000000000..c6f198b1ac --- /dev/null +++ b/activerecord/test/connections/native_mysql2/connection.rb @@ -0,0 +1,25 @@ +print "Using native Mysql2\n" +require_dependency 'models/course' +require 'logger' + +ActiveRecord::Base.logger = Logger.new("debug.log") + +# GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost'; +# GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost'; + +ActiveRecord::Base.configurations = { + 'arunit' => { + :adapter => 'mysql2', + :username => 'rails', + :encoding => 'utf8', + :database => 'activerecord_unittest', + }, + 'arunit2' => { + :adapter => 'mysql2', + :username => 'rails', + :database => 'activerecord_unittest2' + } +} + +ActiveRecord::Base.establish_connection 'arunit' +Course.establish_connection 'arunit2' diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb new file mode 100644 index 0000000000..c78d99f4af --- /dev/null +++ b/activerecord/test/schema/mysql2_specific_schema.rb @@ -0,0 +1,24 @@ +ActiveRecord::Schema.define do + create_table :binary_fields, :force => true, :options => 'CHARACTER SET latin1' do |t| + t.binary :tiny_blob, :limit => 255 + t.binary :normal_blob, :limit => 65535 + t.binary :medium_blob, :limit => 16777215 + t.binary :long_blob, :limit => 2147483647 + t.text :tiny_text, :limit => 255 + t.text :normal_text, :limit => 65535 + t.text :medium_text, :limit => 16777215 + t.text :long_text, :limit => 2147483647 + end + + ActiveRecord::Base.connection.execute <<-SQL +DROP PROCEDURE IF EXISTS ten; +SQL + + ActiveRecord::Base.connection.execute <<-SQL +CREATE PROCEDURE ten() SQL SECURITY INVOKER +BEGIN + select 10; +END +SQL + +end -- cgit v1.2.3 From 311ea94f73c95be87f9a474da122719eebee3f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Mon, 2 Aug 2010 11:07:43 +0200 Subject: added failing touch propagation test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/timestamp_test.rb | 17 +++++++++++++++++ activerecord/test/schema/schema.rb | 2 ++ 2 files changed, 19 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index f765540808..e3d12f6214 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -82,4 +82,21 @@ class TimestampTest < ActiveRecord::TestCase ensure Pet.belongs_to :owner, :touch => true end + + def test_touching_a_record_touches_parent_record_and_grandparent_record + Toy.belongs_to :pet, :touch => true + Pet.belongs_to :owner, :touch => true + + toy = Toy.first + pet = toy.pet + owner = pet.owner + + previously_owner_updated_at = owner.updated_at + + toy.touch + + assert_not_equal previously_owner_updated_at, owner.updated_at + ensure + Toy.belongs_to :pet + end end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index f3fd37cd61..a0e620c2ef 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -398,6 +398,7 @@ ActiveRecord::Schema.define do create_table :pets, :primary_key => :pet_id ,:force => true do |t| t.string :name t.integer :owner_id, :integer + t.timestamps end create_table :pirates, :force => true do |t| @@ -530,6 +531,7 @@ ActiveRecord::Schema.define do create_table :toys, :primary_key => :toy_id ,:force => true do |t| t.string :name t.integer :pet_id, :integer + t.timestamps end create_table :traffic_lights, :force => true do |t| -- cgit v1.2.3 From b613c3cc7b08b00595e33d1b5302cd5d42687d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 Aug 2010 16:16:02 +0200 Subject: Add an internal (private API) after_touch callback. [#5271 state:resolved] --- activerecord/lib/active_record/associations.rb | 1 + activerecord/lib/active_record/callbacks.rb | 8 ++++++-- activerecord/lib/active_record/persistence.rb | 13 +++++++++++++ activerecord/lib/active_record/timestamp.rb | 13 ------------- activerecord/test/cases/timestamp_test.rb | 8 ++++---- 5 files changed, 24 insertions(+), 19 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index f540aa7f25..9663a36edf 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1498,6 +1498,7 @@ module ActiveRecord end end after_save(method_name) + after_touch(method_name) after_destroy(method_name) end diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 637dac450b..82c45a41b0 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -228,7 +228,7 @@ module ActiveRecord extend ActiveSupport::Concern CALLBACKS = [ - :after_initialize, :after_find, :before_validation, :after_validation, + :after_initialize, :after_find, :after_touch, :before_validation, :after_validation, :before_save, :around_save, :after_save, :before_create, :around_create, :after_create, :before_update, :around_update, :after_update, :before_destroy, :around_destroy, :after_destroy @@ -238,7 +238,7 @@ module ActiveRecord extend ActiveModel::Callbacks include ActiveModel::Validations::Callbacks - define_model_callbacks :initialize, :find, :only => :after + define_model_callbacks :initialize, :find, :touch, :only => :after define_model_callbacks :save, :create, :update, :destroy end @@ -256,6 +256,10 @@ module ActiveRecord _run_destroy_callbacks { super } end + def touch(*) #:nodoc: + _run_touch_callbacks { super } + end + def deprecated_callback_method(symbol) #:nodoc: if respond_to?(symbol, true) ActiveSupport::Deprecation.warn("Overwriting #{symbol} in your models has been deprecated, please use Base##{symbol} :method_name instead") diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 38b91652ee..cbc2220e96 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -218,6 +218,19 @@ module ActiveRecord self end + # Saves the record with the updated_at/on attributes set to the current time. + # Please note that no validation is performed and no callbacks are executed. + # If an attribute name is passed, that attribute is updated along with + # updated_at/on attributes. + # + # Examples: + # + # product.touch # updates updated_at/on + # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on + def touch(attribute = nil) + update_attribute(attribute, current_time_from_proper_timezone) + end + private def create_or_update raise ReadOnlyRecord if readonly? diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 92f7a7753d..32b3f03f13 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -31,19 +31,6 @@ module ActiveRecord class_inheritable_accessor :record_timestamps, :instance_writer => false self.record_timestamps = true end - - # Saves the record with the updated_at/on attributes set to the current time. - # Please note that no validation is performed and no callbacks are executed. - # If an attribute name is passed, that attribute is updated along with - # updated_at/on attributes. - # - # Examples: - # - # product.touch # updates updated_at/on - # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on - def touch(attribute = nil) - update_attribute(attribute, current_time_from_proper_timezone) - end private diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index e3d12f6214..06ab7aa9c7 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -2,9 +2,10 @@ require 'cases/helper' require 'models/developer' require 'models/owner' require 'models/pet' +require 'models/toy' class TimestampTest < ActiveRecord::TestCase - fixtures :developers, :owners, :pets + fixtures :developers, :owners, :pets, :toys def setup @developer = Developer.first @@ -91,11 +92,10 @@ class TimestampTest < ActiveRecord::TestCase pet = toy.pet owner = pet.owner - previously_owner_updated_at = owner.updated_at - + owner.update_attribute(:updated_at, (time = 3.days.ago)) toy.touch - assert_not_equal previously_owner_updated_at, owner.updated_at + assert_not_equal time, owner.updated_at ensure Toy.belongs_to :pet end -- cgit v1.2.3 From f8b53f35b9cbf2a134a7d9184a044ce95764acfa Mon Sep 17 00:00:00 2001 From: Robert Pankowecki Date: Tue, 27 Jul 2010 22:57:27 +0200 Subject: test and fix collection_singular_ids= with string primary keys [#5125 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/associations.rb | 4 ++- .../has_many_through_associations_test.rb | 40 +++++++++++++++++++++- activerecord/test/fixtures/subscriptions.yml | 2 +- activerecord/test/models/book.rb | 3 ++ 4 files changed, 46 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9663a36edf..bd90cfc5d5 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1439,7 +1439,9 @@ module ActiveRecord end redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| - ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i) + pk_column = reflection.klass.columns.find{|c| c.name == reflection.klass.primary_key } + ids = (new_value || []).reject { |nid| nid.blank? } + ids.map!{|i| pk_column.type_cast(i)} send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index e4dd810732..3940e75ad6 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -14,9 +14,14 @@ require 'models/toy' require 'models/contract' require 'models/company' require 'models/developer' +require 'models/subscriber' +require 'models/book' +require 'models/subscription' class HasManyThroughAssociationsTest < ActiveRecord::TestCase - fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references, :companies + fixtures :posts, :readers, :people, :comments, :authors, + :owners, :pets, :toys, :jobs, :references, :companies, + :subscribers, :books, :subscriptions # Dummies to force column loads so query counts are clean. def setup @@ -383,4 +388,37 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) }, ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) } end + + def test_collection_singular_ids_getter_with_string_primary_keys + book = books(:awdr) + assert_equal 2, book.subscriber_ids.size + assert_equal [subscribers(:first).nick, subscribers(:second).nick].sort, book.subscriber_ids.sort + end + + def test_collection_singular_ids_setter + company = companies(:rails_core) + dev = Developer.find(:first) + + company.developer_ids = [dev.id] + assert_equal [dev], company.developers + end + + def test_collection_singular_ids_setter_with_string_primary_keys + assert_nothing_raised do + book = books(:awdr) + book.subscriber_ids = [subscribers(:second).nick] + assert_equal [subscribers(:second)], book.subscribers(true) + + book.subscriber_ids = [] + assert_equal [], book.subscribers(true) + end + + end + + def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set + company = companies(:rails_core) + ids = [Developer.find(:first).id, -9999] + assert_raises(ActiveRecord::RecordNotFound) {company.developer_ids= ids} + end + end diff --git a/activerecord/test/fixtures/subscriptions.yml b/activerecord/test/fixtures/subscriptions.yml index 371bfd3422..5a93c12193 100644 --- a/activerecord/test/fixtures/subscriptions.yml +++ b/activerecord/test/fixtures/subscriptions.yml @@ -9,4 +9,4 @@ webster_rfr: alterself_awdr: id: 3 subscriber_id: alterself - book_id: 3 \ No newline at end of file + book_id: 1 diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb index cfd07abddc..1e030b4f59 100644 --- a/activerecord/test/models/book.rb +++ b/activerecord/test/models/book.rb @@ -1,4 +1,7 @@ class Book < ActiveRecord::Base has_many :citations, :foreign_key => 'book1_id' has_many :references, :through => :citations, :source => :reference_of, :uniq => true + + has_many :subscriptions + has_many :subscribers, :through => :subscriptions end -- cgit v1.2.3 From e1344bf5048934b0f231974c3597dfbc1c76154f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 Aug 2010 16:51:08 +0200 Subject: Tidy up previous commit. --- activerecord/lib/active_record/associations.rb | 6 ++--- activerecord/lib/active_record/reflection.rb | 26 ++++++++++------------ .../has_many_through_associations_test.rb | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index bd90cfc5d5..1dc094b893 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1437,11 +1437,11 @@ module ActiveRecord association.replace(new_value) association end - + redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| - pk_column = reflection.klass.columns.find{|c| c.name == reflection.klass.primary_key } + pk_column = reflection.primary_key_column ids = (new_value || []).reject { |nid| nid.blank? } - ids.map!{|i| pk_column.type_cast(i)} + ids.map!{ |i| pk_column.type_cast(i) } send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids)) end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 03a932f642..7f47a812eb 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -91,25 +91,19 @@ module ActiveRecord # # composed_of :balance, :class_name => 'Money' returns :balance # has_many :clients returns :clients - def name - @name - end + attr_reader :name # Returns the macro type. # # composed_of :balance, :class_name => 'Money' returns :composed_of # has_many :clients returns :has_many - def macro - @macro - end + attr_reader :macro # Returns the hash of options used for the macro. # # composed_of :balance, :class_name => 'Money' returns { :class_name => "Money" } # has_many :clients returns +{}+ - def options - @options - end + attr_reader :options # Returns the class for the macro. # @@ -137,11 +131,6 @@ module ActiveRecord @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions] end - # Returns +true+ if +self+ is a +belongs_to+ reflection. - def belongs_to? - macro == :belongs_to - end - private def derive_class_name name.to_s.camelize @@ -213,6 +202,10 @@ module ActiveRecord @primary_key_name ||= options[:foreign_key] || derive_primary_key_name end + def primary_key_column + @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key } + end + def association_foreign_key @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key end @@ -307,6 +300,11 @@ module ActiveRecord dependent_conditions end + # Returns +true+ if +self+ is a +belongs_to+ reflection. + def belongs_to? + macro == :belongs_to + end + private def derive_class_name class_name = name.to_s.camelize diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 3940e75ad6..0eaadac5ae 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -21,7 +21,7 @@ require 'models/subscription' class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references, :companies, - :subscribers, :books, :subscriptions + :subscribers, :books, :subscriptions, :developers # Dummies to force column loads so query counts are clean. def setup -- cgit v1.2.3 From b8d9d9ce0a72218fa0891485063d3fcb3e77cae8 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 2 Aug 2010 11:09:31 -0400 Subject: updating documentation to ensure line does not exceed 100 columns --- activerecord/lib/active_record/base.rb | 216 +++++++++++++++++------------ activerecord/lib/active_record/relation.rb | 14 +- 2 files changed, 136 insertions(+), 94 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 391c287fe4..5535079cd8 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -26,17 +26,19 @@ require 'active_record/log_subscriber' module ActiveRecord #:nodoc: # = Active Record # - # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with - # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change - # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain + # Active Record objects don't specify their attributes directly, but rather infer them from + # the table definition with which they're linked. Adding, removing, and changing attributes + # and their type is done directly in the database. Any change is instantly reflected in the + # Active Record objects. The mapping that binds a given Active Record class to a certain # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones. # # See the mapping rules in table_name and the full example in link:files/README.html for more insight. # # == Creation # - # Active Records accept constructor parameters either in a hash or as a block. The hash method is especially useful when - # you're receiving the data from somewhere else, like an HTTP request. It works like this: + # Active Records accept constructor parameters either in a hash or as a block. The hash + # method is especially useful when you're receiving the data from somewhere else, like an + # HTTP request. It works like this: # # user = User.new(:name => "David", :occupation => "Code Artist") # user.name # => "David" @@ -75,14 +77,17 @@ module ActiveRecord #:nodoc: # end # end # - # The authenticate_unsafely method inserts the parameters directly into the query and is thus susceptible to SQL-injection - # attacks if the user_name and +password+ parameters come directly from an HTTP request. The authenticate_safely and - # authenticate_safely_simply both will sanitize the user_name and +password+ before inserting them in the query, - # which will ensure that an attacker can't escape the query and fake the login (or worse). + # The authenticate_unsafely method inserts the parameters directly into the query + # and is thus susceptible to SQL-injection attacks if the user_name and +password+ + # parameters come directly from an HTTP request. The authenticate_safely and + # authenticate_safely_simply both will sanitize the user_name and +password+ + # before inserting them in the query, which will ensure that an attacker can't escape the + # query and fake the login (or worse). # - # When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth - # question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing - # the question marks with symbols and supplying a hash with values for the matching symbol keys: + # When using multiple parameters in the conditions, it can easily become hard to read exactly + # what the fourth or fifth question mark is supposed to represent. In those cases, you can + # resort to named bind variables instead. That's done by replacing the question marks with + # symbols and supplying a hash with values for the matching symbol keys: # # Company.where( # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date", @@ -103,18 +108,19 @@ module ActiveRecord #:nodoc: # # Student.where(:grade => [9,11,12]) # - # When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a - # particular condition. For instance: + # When joining tables, nested hashes or keys written in the form 'table_name.column_name' + # can be used to qualify the table name of a particular condition. For instance: # # Student.joins(:schools).where(:schools => { :type => 'public' }) # Student.joins(:schools).where('schools.type' => 'public' ) # # == Overwriting default accessors # - # All column values are automatically available through basic accessors on the Active Record object, but sometimes you - # want to specialize this behavior. This can be done by overwriting the default accessors (using the same - # name as the attribute) and calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things. - # Example: + # All column values are automatically available through basic accessors on the Active Record + # object, but sometimes you want to specialize this behavior. This can be done by overwriting + # the default accessors (using the same name as the attribute) and calling + # read_attribute(attr_name) and write_attribute(attr_name, value) to actually + # change things. # # class Song < ActiveRecord::Base # # Uses an integer of seconds to hold the length of the song @@ -128,8 +134,8 @@ module ActiveRecord #:nodoc: # end # end # - # You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, value) and - # read_attribute(:attribute) as a shorter form. + # You can alternatively use self[:attribute]=(value) and self[:attribute] + # instead of write_attribute(:attribute, value) and read_attribute(:attribute). # # == Attribute query methods # @@ -147,24 +153,29 @@ module ActiveRecord #:nodoc: # # == Accessing attributes before they have been typecasted # - # Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first. - # That can be done by using the _before_type_cast accessors that all attributes have. For example, if your Account model - # has a balance attribute, you can call account.balance_before_type_cast or account.id_before_type_cast. + # Sometimes you want to be able to read the raw attribute data without having the column-determined + # typecast run its course first. That can be done by using the _before_type_cast + # accessors that all attributes have. For example, if your Account model has a balance attribute, + # you can call account.balance_before_type_cast or account.id_before_type_cast. # - # This is especially useful in validation situations where the user might supply a string for an integer field and you want to display - # the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn't what you - # want. + # This is especially useful in validation situations where the user might supply a string for an + # integer field and you want to display the original string back in an error message. Accessing the + # attribute normally would typecast the string to 0, which isn't what you want. # # == Dynamic attribute-based finders # - # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by - # appending the name of an attribute to find_by_, find_last_by_, or find_all_by_, so you get finders like Person.find_by_user_name, - # Person.find_all_by_last_name, and Payment.find_by_transaction_id. So instead of writing + # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects + # by simple queries without turning to SQL. They work by appending the name of an attribute + # to find_by_, find_last_by_, or find_all_by_, so you get finders + # like Person.find_by_user_name, Person.find_all_by_last_name, and + # Payment.find_by_transaction_id. So instead of writing # Person.where(:user_name => user_name).first, you just do Person.find_by_user_name(user_name). - # And instead of writing Person.where(:last_name => last_name).all, you just do Person.find_all_by_last_name(last_name). + # And instead of writing Person.where(:last_name => last_name).all, you just do + # Person.find_all_by_last_name(last_name). # - # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like - # Person.find_by_user_name_and_password or even Payment.find_by_purchaser_and_state_and_country. So instead of writing + # It's also possible to use multiple attributes in the same find by separating them with "_and_", + # so you get finders like Person.find_by_user_name_and_password or even + # Payment.find_by_purchaser_and_state_and_country. So instead of writing # Person.where(:user_name => user_name, :password => password).first, you just do # Person.find_by_user_name_and_password(user_name, password). # @@ -173,8 +184,10 @@ module ActiveRecord #:nodoc: # Payment.order("created_on").find_all_by_amount(50) # Payment.pending.find_last_by_amount(100) # - # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with - # find_or_create_by_ and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example: + # The same dynamic finder style can be used to create the object if it doesn't already exist. + # This dynamic finder is called with find_or_create_by_ and will return the object if + # it already exists and otherwise creates it, then returns it. Protected attributes won't be set + # unless they are given in a block. For example: # # # No 'Summer' tag exists # Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") @@ -185,23 +198,27 @@ module ActiveRecord #:nodoc: # # Now 'Bob' exist and is an 'admin' # User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true } # - # Use the find_or_initialize_by_ finder if you want to return a new record without saving it first. Protected attributes won't be set unless they are given in a block. For example: + # Use the find_or_initialize_by_ finder if you want to return a new record without + # saving it first. Protected attributes won't be set unless they are given in a block. # # # No 'Winter' tag exists # winter = Tag.find_or_initialize_by_name("Winter") # winter.new_record? # true # # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of - # a list of parameters. For example: + # a list of parameters. # # Tag.find_or_create_by_name(:name => "rails", :creator => current_user) # - # That will either find an existing tag named "rails", or create a new one while setting the user that created it. + # That will either find an existing tag named "rails", or create a new one while setting the + # user that created it. # # == Saving arrays, hashes, and other non-mappable objects in text columns # - # Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method +serialize+. - # This makes it possible to store arrays, hashes, and other non-mappable objects without doing any additional work. Example: + # Active Record can serialize any object in text columns using YAML. To do so, you must + # specify this with a call to the class method +serialize+. + # This makes it possible to store arrays, hashes, and other non-mappable objects without doing + # any additional work. # # class User < ActiveRecord::Base # serialize :preferences @@ -210,8 +227,8 @@ module ActiveRecord #:nodoc: # user = User.create(:preferences => { "background" => "black", "display" => large }) # User.find(user.id).preferences # => { "background" => "black", "display" => large } # - # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a - # descendant of a class not in the hierarchy. Example: + # You can also specify a class option as the second parameter that'll raise an exception + # if a serialized object is retrieved as a descendant of a class not in the hierarchy. # # class User < ActiveRecord::Base # serialize :preferences, Hash @@ -222,52 +239,63 @@ module ActiveRecord #:nodoc: # # == Single table inheritance # - # Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed - # by overwriting Base.inheritance_column). This means that an inheritance looking like this: + # Active Record allows inheritance by storing the name of the class in a column that by + # default is named "type" (can be changed by overwriting Base.inheritance_column). + # This means that an inheritance looking like this: # # class Company < ActiveRecord::Base; end # class Firm < Company; end # class Client < Company; end # class PriorityClient < Client; end # - # When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = "Firm". You can then - # fetch this row again using Company.where(:name => '37signals').first and it will return a Firm object. + # When you do Firm.create(:name => "37signals"), this record will be saved in + # the companies table with type = "Firm". You can then fetch this row again using + # Company.where(:name => '37signals').first and it will return a Firm object. # - # If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just - # like normal subclasses with no special magic for differentiating between them or reloading the right type with find. + # If you don't have a type column defined in your table, single-table inheritance won't + # be triggered. In that case, it'll work just like normal subclasses with no special magic + # for differentiating between them or reloading the right type with find. # # Note, all the attributes for all the cases are kept in the same table. Read more: # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html # # == Connection to multiple databases in different models # - # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection. - # All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection. - # For example, if Course is an ActiveRecord::Base, but resides in a different database, you can just say Course.establish_connection + # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved + # by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this + # connection. But you can also set a class-specific connection. For example, if Course is an + # ActiveRecord::Base, but resides in a different database, you can just say Course.establish_connection # and Course and all of its subclasses will use this connection instead. # - # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is - # requested, the retrieve_connection method will go up the class-hierarchy until a connection is found in the connection pool. + # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is + # a Hash indexed by the class. If a connection is requested, the retrieve_connection method + # will go up the class-hierarchy until a connection is found in the connection pool. # # == Exceptions # # * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record. # * AdapterNotSpecified - The configuration hash used in establish_connection didn't include an # :adapter key. - # * AdapterNotFound - The :adapter key used in establish_connection specified a non-existent adapter + # * AdapterNotFound - The :adapter key used in establish_connection specified a + # non-existent adapter # (or a bad spelling of an existing one). - # * AssociationTypeMismatch - The object assigned to the association wasn't of the type specified in the association definition. + # * AssociationTypeMismatch - The object assigned to the association wasn't of the type + # specified in the association definition. # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter. - # * ConnectionNotEstablished+ - No connection has been established. Use establish_connection before querying. + # * ConnectionNotEstablished+ - No connection has been established. Use establish_connection + # before querying. # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal # nothing was found, please check its documentation for further details. # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message. # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the - # attributes= method. The +errors+ property of this exception contains an array of AttributeAssignmentError + # attributes= method. The +errors+ property of this exception contains an array of + # AttributeAssignmentError # objects that should be inspected to determine which attributes triggered the errors. - # * AttributeAssignmentError - An error occurred while doing a mass assignment through the attributes= method. - # You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error. + # * AttributeAssignmentError - An error occurred while doing a mass assignment through the + # attributes= method. + # You can inspect the +attribute+ property of the exception object to determine which attribute + # triggered the error. # # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level). # So it's possible to assign a logger to the class through Base.logger= which will then be used by all @@ -275,8 +303,9 @@ module ActiveRecord #:nodoc: class Base ## # :singleton-method: - # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed - # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+. + # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, + # which is then passed on to any new database connections made and which can be retrieved on both + # a class and instance level by calling +logger+. cattr_accessor :logger, :instance_writer => false class << self @@ -323,21 +352,24 @@ module ActiveRecord #:nodoc: ## # :singleton-method: - # Accessor for the prefix type that will be prepended to every primary key column name. The options are :table_name and - # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as - # the primary column. If the latter is specified, the Product class will look for "product_id" instead of "id". Remember + # Accessor for the prefix type that will be prepended to every primary key column name. + # The options are :table_name and :table_name_with_underscore. If the first is specified, + # the Product class will look for "productid" instead of "id" as the primary column. If the + # latter is specified, the Product class will look for "product_id" instead of "id". Remember # that this is a global setting for all Active Records. cattr_accessor :primary_key_prefix_type, :instance_writer => false @@primary_key_prefix_type = nil ## # :singleton-method: - # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all - # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace - # for tables in a shared database. By default, the prefix is the empty string. + # Accessor for the name of the prefix string to prepend to every table name. So if set + # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people", + # etc. This is a convenient way of creating a namespace for tables in a shared database. + # By default, the prefix is the empty string. # - # If you are organising your models within modules you can add a prefix to the models within a namespace by defining - # a singleton method in the parent module called table_name_prefix which returns your chosen prefix. + # If you are organising your models within modules you can add a prefix to the models within + # a namespace by defining a singleton method in the parent module called table_name_prefix which + # returns your chosen prefix. class_attribute :table_name_prefix, :instance_writer => false self.table_name_prefix = "" @@ -358,8 +390,8 @@ module ActiveRecord #:nodoc: ## # :singleton-method: - # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database. - # This is set to :local by default. + # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling + # dates and times from the database. This is set to :local by default. cattr_accessor :default_timezone, :instance_writer => false @@default_timezone = :local @@ -505,15 +537,18 @@ module ActiveRecord #:nodoc: serialized_attributes[attr_name.to_s] = class_name end - # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values. + # Returns a hash of all the attributes that have been specified for serialization as + # keys and their class restriction as values. def serialized_attributes read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {}) end - # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending - # directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used - # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class - # in Active Support, which knows almost all common English inflections. You can add new inflections in config/initializers/inflections.rb. + # Guesses the table name (in forced lower-case) based on the name of the class in the + # inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy + # looks like: Reply < Message < ActiveRecord::Base, then Message is used + # to guess the table name even when called on Reply. The rules used to do the guess + # are handled by the Inflector class in Active Support, which knows almost all common + # English inflections. You can add new inflections in config/initializers/inflections.rb. # # Nested classes are given table names prefixed by the singular form of # the parent's table name. Enclosing modules are not considered. @@ -923,15 +958,18 @@ module ActiveRecord #:nodoc: end end - # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) - # that are turned into where(:user_name => user_name).first and where(:user_name => user_name, :password => :password).first - # respectively. Also works for all by using find_all_by_amount(50) that is turned into where(:amount => 50).all. + # Enables dynamic finders like find_by_user_name(user_name) and + # find_by_user_name_and_password(user_name, password) that are turned into + # where(:user_name => user_name).first and + # where(:user_name => user_name, :password => :password).first + # respectively. Also works for all by using find_all_by_amount(50) + # that is turned into where(:amount => 50).all. # - # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+ - # is actually find_all_by_amount(amount, options). + # It's even possible to use all the additional parameters to +find+. For example, the + # full interface for +find_all_by_amount+ is actually find_all_by_amount(amount, options). # - # Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future - # attempts to use it do not run through method_missing. + # Each dynamic finder, scope or initializer/creator is also defined in the class after it + # is first invoked, so that future attempts to use it do not run through method_missing. def method_missing(method_id, *arguments, &block) if match = DynamicFinderMatch.match(method_id) attribute_names = match.attribute_names @@ -1607,10 +1645,11 @@ MSG private - # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendant. - # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to do Reply.new without having to - # set Reply[Reply.inheritance_column] = "Reply" yourself. No such attribute would be set for objects of the - # Message class in that example. + # Sets the attribute used for single table inheritance to this class name if this is not the + # ActiveRecord::Base descendant. + # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to + # do Reply.new without having to set Reply[Reply.inheritance_column] = "Reply" yourself. + # No such attribute would be set for objects of the Message class in that example. def ensure_proper_type unless self.class.descends_from_active_record? write_attribute(self.class.inheritance_column, self.class.sti_name) @@ -1659,8 +1698,9 @@ MSG # by calling new on the column type or aggregation type (through composed_of) object with these parameters. # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the - # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float, - # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil. + # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, + # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the + # attribute will be set to nil. def assign_multiparameter_attributes(pairs) execute_callstack_for_multiparameter_attributes( extract_callstack_for_multiparameter_attributes(pairs) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7962f52738..deacced627 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -67,7 +67,8 @@ module ActiveRecord preload += @includes_values unless eager_loading? preload.each {|associations| @klass.send(:preload_associations, @records, associations) } - # @readonly_value is true only if set explicitly. @implicit_readonly is true if there are JOINS and no explicit SELECT. + # @readonly_value is true only if set explicitly. @implicit_readonly is true if there + # are JOINS and no explicit SELECT. readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value @records.each { |record| record.readonly! } if readonly @@ -130,7 +131,8 @@ module ActiveRecord # ==== Parameters # # * +updates+ - A string, array, or hash representing the SET part of an SQL statement. - # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement. See conditions in the intro. + # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement. + # See conditions in the intro. # * +options+ - Additional options are :limit and :order, see the examples for usage. # # ==== Examples @@ -144,7 +146,7 @@ module ActiveRecord # # Update all avatars migrated more than a week ago # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago] # - # # Update all books that match our conditions, but limit it to 5 ordered by date + # # Update all books that match conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) if conditions || options.present? @@ -165,14 +167,14 @@ module ActiveRecord # ==== Parameters # # * +id+ - This should be the id or an array of ids to be updated. - # * +attributes+ - This should be a hash of attributes to be set on the object, or an array of hashes. + # * +attributes+ - This should be a hash of attributes or an array of hashes. # # ==== Examples # - # # Updating one record: + # # Updates one record # Person.update(15, :user_name => 'Samuel', :group => 'expert') # - # # Updating multiple records: + # # Updates multiple records # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } } # Person.update(people.keys, people.values) def update(id, attributes) -- cgit v1.2.3 From 009aa8825b6932b006f005ac351b82ad8100d7f1 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 26 Jul 2010 15:12:36 -0400 Subject: Eager loading an association should not change the count of children MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4971 state:resolved] Signed-off-by: José Valim --- activerecord/lib/active_record/associations.rb | 4 ++++ activerecord/test/cases/associations_test.rb | 13 +++++++++++++ activerecord/test/models/electron.rb | 3 +++ activerecord/test/models/liquid.rb | 5 +++++ activerecord/test/models/molecule.rb | 4 ++++ activerecord/test/schema/schema.rb | 13 +++++++++++++ 6 files changed, 42 insertions(+) create mode 100644 activerecord/test/models/electron.rb create mode 100644 activerecord/test/models/liquid.rb create mode 100644 activerecord/test/models/molecule.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 1dc094b893..9cd3b9662f 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1815,6 +1815,10 @@ module ActiveRecord when Hash associations.keys.each do |name| reflection = base.reflections[name] + + if records.any? && reflection.options && reflection.options[:uniq] + records.each { |record| record.send(reflection.name).target.uniq! } + end parent_records = [] records.each do |record| diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index a1c794c084..d328ca630b 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -14,11 +14,24 @@ require 'models/reader' require 'models/parrot' require 'models/ship_part' require 'models/ship' +require 'models/liquid' +require 'models/molecule' +require 'models/electron' class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, :computers, :people, :readers + def test_eager_loading_should_not_change_count_of_children + liquid = Liquid.create(:name => 'salty') + molecule = liquid.molecules.create(:name => 'molecule_1') + molecule.electrons.create(:name => 'electron_1') + molecule.electrons.create(:name => 'electron_2') + + liquids = Liquid.includes(:molecules => :electrons).where('molecules.id is not null') + assert_equal 1, liquids[0].molecules.length + end + def test_loading_the_association_target_should_keep_child_records_marked_for_destruction ship = Ship.create!(:name => "The good ship Dollypop") part = ship.parts.create!(:name => "Mast") diff --git a/activerecord/test/models/electron.rb b/activerecord/test/models/electron.rb new file mode 100644 index 0000000000..35af9f679b --- /dev/null +++ b/activerecord/test/models/electron.rb @@ -0,0 +1,3 @@ +class Electron < ActiveRecord::Base + belongs_to :molecule +end diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb new file mode 100644 index 0000000000..b96c054f6c --- /dev/null +++ b/activerecord/test/models/liquid.rb @@ -0,0 +1,5 @@ +class Liquid < ActiveRecord::Base + set_table_name :liquid + has_many :molecules, :uniq => true +end + diff --git a/activerecord/test/models/molecule.rb b/activerecord/test/models/molecule.rb new file mode 100644 index 0000000000..69325b8d29 --- /dev/null +++ b/activerecord/test/models/molecule.rb @@ -0,0 +1,4 @@ +class Molecule < ActiveRecord::Base + belongs_to :liquid + has_many :electrons +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index a0e620c2ef..fc3810f82b 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -618,6 +618,19 @@ ActiveRecord::Schema.define do t.datetime :updated_at end + create_table :liquid, :force => true do |t| + t.string :name + end + create_table :molecules, :force => true do |t| + t.integer :liquid_id + t.string :name + end + create_table :electrons, :force => true do |t| + t.integer :molecule_id + t.string :name + end + + except 'SQLite' do # fk_test_has_fk should be before fk_test_has_pk create_table :fk_test_has_fk, :force => true do |t| -- cgit v1.2.3 From 9effe3cc18182abbcf0a8b01635a34ca77b67e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 Aug 2010 17:20:17 +0200 Subject: Remove duplicated logic. --- activerecord/lib/active_record/associations.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9cd3b9662f..fdc203e298 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1805,9 +1805,7 @@ module ActiveRecord case associations when Symbol, String reflection = base.reflections[associations] - if reflection && reflection.collection? - records.each { |record| record.send(reflection.name).target.uniq! } - end + remove_uniq_by_reflection(reflection, records) when Array associations.each do |association| remove_duplicate_results!(base, records, association) @@ -1815,10 +1813,7 @@ module ActiveRecord when Hash associations.keys.each do |name| reflection = base.reflections[name] - - if records.any? && reflection.options && reflection.options[:uniq] - records.each { |record| record.send(reflection.name).target.uniq! } - end + remove_uniq_by_reflection(reflection, records) parent_records = [] records.each do |record| @@ -1837,6 +1832,7 @@ module ActiveRecord end protected + def build(associations, parent = nil, join_class = Arel::InnerJoin) parent ||= @joins.last case associations @@ -1859,6 +1855,12 @@ module ActiveRecord end end + def remove_uniq_by_reflection(reflection, records) + if reflection && reflection.collection? + records.each { |record| record.send(reflection.name).target.uniq! } + end + end + def build_join_association(reflection, parent) JoinAssociation.new(reflection, self, parent) end -- cgit v1.2.3 From 1ce40ca56216ae76e93cde78ec2752de110400c0 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 2 Aug 2010 12:25:26 -0400 Subject: ensuring that description does not exceed 100 columns --- .../has_and_belongs_to_many_association.rb | 7 +- .../associations/has_many_through_association.rb | 7 +- .../attribute_methods/time_zone_conversion.rb | 3 +- .../lib/active_record/attribute_methods/write.rb | 4 +- activerecord/lib/active_record/callbacks.rb | 66 +++++++++--------- .../abstract/schema_definitions.rb | 10 +-- .../connection_adapters/mysql_adapter.rb | 3 +- .../connection_adapters/postgresql_adapter.rb | 12 ++-- .../connection_adapters/sqlite_adapter.rb | 4 +- activerecord/lib/active_record/errors.rb | 14 ++-- activerecord/lib/active_record/fixtures.rb | 50 ++++++++------ activerecord/lib/active_record/named_scope.rb | 21 +++--- activerecord/lib/active_record/observer.rb | 4 +- activerecord/lib/active_record/persistence.rb | 4 +- .../lib/active_record/relation/calculations.rb | 79 ++++++++++++++-------- .../lib/active_record/relation/finder_methods.rb | 23 ++++--- activerecord/lib/active_record/timestamp.rb | 3 +- .../lib/active_record/validations/associated.rb | 7 +- .../lib/active_record/validations/uniqueness.rb | 22 +++--- 19 files changed, 205 insertions(+), 138 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index e61af93d1e..bec123e7a2 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -106,9 +106,10 @@ module ActiveRecord :limit => @reflection.options[:limit] } } end - # Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select - # clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has - # an id column. This will then overwrite the id column of the records coming back. + # Join tables with additional columns on top of the two foreign keys must be considered + # ambiguous unless a select clause has been explicitly defined. Otherwise you can get + # broken records back, if, for example, the join column also has an id column. This will + # then overwrite the id column of the records coming back. def finding_with_ambiguous_select?(select_clause) !select_clause && columns.size != 2 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 17f850756f..608b1c741a 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -24,9 +24,10 @@ module ActiveRecord end end - # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and - # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero, - # and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length. + # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been + # loaded and calling collection.size if it has. If it's more likely than not that the collection does + # have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer + # SELECT query if you use #length. def size return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter? return @target.size if loaded? diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 783d61383b..8f0aacba42 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -14,7 +14,8 @@ module ActiveRecord module ClassMethods protected # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled. - # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone. + # This enhanced read method automatically converts the UTC time stored in the database to the time + # zone stored in Time.zone. def define_method_attribute(attr_name) if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) method_body, line = <<-EOV, __LINE__ + 1 diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index e31acac050..7a2de3bf80 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -14,8 +14,8 @@ module ActiveRecord end end - # Updates the attribute identified by attr_name with the specified +value+. Empty strings for fixnum and float - # columns are turned into +nil+. + # Updates the attribute identified by attr_name with the specified +value+. Empty strings + # 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' diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 637dac450b..b2b1374838 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -26,8 +26,8 @@ module ActiveRecord # after_rollback. # # That's a total of ten callbacks, which gives you immense power to react and prepare for each state in the - # Active Record lifecycle. The sequence for calling Base#save for an existing record is similar, except that each - # _on_create callback is replaced by the corresponding _on_update callback. + # Active Record lifecycle. The sequence for calling Base#save for an existing record is similar, + # except that each _on_create callback is replaced by the corresponding _on_update callback. # # Examples: # class CreditCard < ActiveRecord::Base @@ -55,9 +55,9 @@ module ActiveRecord # # == Inheritable callback queues # - # Besides the overwritable callback methods, it's also possible to register callbacks through the use of the callback macros. - # Their main advantage is that the macros add behavior into a callback queue that is kept intact down through an inheritance - # hierarchy. Example: + # Besides the overwritable callback methods, it's also possible to register callbacks through the + # use of the callback macros. Their main advantage is that the macros add behavior into a callback + # queue that is kept intact down through an inheritance hierarchy. # # class Topic < ActiveRecord::Base # before_destroy :destroy_author @@ -67,9 +67,9 @@ module ActiveRecord # before_destroy :destroy_readers # end # - # Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is run, both +destroy_author+ and - # +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable - # methods: + # Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is + # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the situation where + # we've implemented the save behavior through overwriteable methods: # # class Topic < ActiveRecord::Base # def before_destroy() destroy_author end @@ -79,20 +79,21 @@ module ActiveRecord # def before_destroy() destroy_readers end # end # - # In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when - # you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods - # when you want to leave it up to each descendant to decide whether they want to call +super+ and trigger the inherited callbacks. + # In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. + # So, use the callback macros when you want to ensure that a certain callback is called for the entire + # hierarchy, and use the regular overwriteable methods when you want to leave it up to each descendant + # to decide whether they want to call +super+ and trigger the inherited callbacks. # - # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the - # associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't - # be inherited. + # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the + # callbacks before specifying the associations. Otherwise, you might trigger the loading of a + # child before the parent has registered the callbacks and they won't be inherited. # # == Types of callbacks # # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects, - # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the - # recommended approaches, inline methods using a proc are sometimes appropriate (such as for creating mix-ins), and inline - # eval methods are deprecated. + # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects + # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for + # creating mix-ins), and inline eval methods are deprecated. # # The method reference callbacks work by specifying a protected or private method available in the object, like this: # @@ -169,15 +170,15 @@ module ActiveRecord # end # end # - # The callback macros usually accept a symbol for the method they're supposed to run, but you can also pass a "method string", - # which will then be evaluated within the binding of the callback. Example: + # The callback macros usually accept a symbol for the method they're supposed to run, but you can also + # pass a "method string", which will then be evaluated within the binding of the callback. Example: # # class Topic < ActiveRecord::Base # before_destroy 'self.class.delete_all "parent_id = #{id}"' # end # - # Notice that single quotes (') are used so the #{id} part isn't evaluated until the callback is triggered. Also note that these - # inline callbacks can be stacked just like the regular ones: + # Notice that single quotes (') are used so the #{id} part isn't evaluated until the callback + # is triggered. Also note that these inline callbacks can be stacked just like the regular ones: # # class Topic < ActiveRecord::Base # before_destroy 'self.class.delete_all "parent_id = #{id}"', @@ -186,22 +187,24 @@ module ActiveRecord # # == The +after_find+ and +after_initialize+ exceptions # - # Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, such as Base.find(:all), we've had - # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and - # +after_initialize+ will only be run if an explicit implementation is defined (def after_find). In that case, all of the + # Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, + # such as Base.find(:all), we've had to implement a simple performance constraint (50% more speed + # on a simple test case). Unlike all the other callbacks, +after_find+ and +after_initialize+ will only be + # run if an explicit implementation is defined (def after_find). In that case, all of the # callback types will be called. # # == before_validation* returning statements # - # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and Base#save will return +false+. - # If Base#save! is called it will raise a ActiveRecord::RecordInvalid exception. - # Nothing will be appended to the errors object. + # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be + # aborted and Base#save will return +false+. If Base#save! is called it will raise a + # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object. # # == Canceling callbacks # - # If a before_* callback returns +false+, all the later callbacks and the associated action are cancelled. If an after_* callback returns - # +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks - # defined as methods on the model, which are called last. + # If a before_* callback returns +false+, all the later callbacks and the associated action are + # cancelled. If an after_* callback returns +false+, all the later callbacks are cancelled. + # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as + # methods on the model, which are called last. # # == Transactions # @@ -217,7 +220,8 @@ module ActiveRecord # # == Debugging callbacks # - # To list the methods and procs registered with a particular callback, append _callback_chain to the callback name that you wish to list and send that to your class from the Rails console: + # To list the methods and procs registered with a particular callback, append _callback_chain to + # the callback name that you wish to list and send that to your class from the Rails console: # # >> Topic.after_save_callback_chain # => [#supplier_id in supplier_id int(11). # +default+ is the type-casted default value, such as +new+ in sales_stage varchar(20) default 'new'. - # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in company_name varchar(60). + # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in + # company_name varchar(60). # It will be mapped to one of the standard Rails SQL types in the type attribute. # +null+ determines if this column allows +NULL+ values. def initialize(name, default, sql_type = nil, null = true) @@ -359,7 +360,8 @@ module ActiveRecord # # Available options are (none of these exists by default): # * :limit - - # Requests a maximum column length. This is number of characters for :string and :text columns and number of bytes for :binary and :integer columns. + # Requests a maximum column length. This is number of characters for :string and + # :text columns and number of bytes for :binary and :integer columns. # * :default - # The column's default value. Use nil for NULL. # * :null - @@ -462,8 +464,8 @@ module ActiveRecord # TableDefinition#timestamps that'll add created_at and +updated_at+ as datetimes. # # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type - # column if the :polymorphic option is supplied. If :polymorphic is a hash of options, these will be - # used when creating the _type column. So what can be written like this: + # column if the :polymorphic option is supplied. If :polymorphic is a hash of + # options, these will be used when creating the _type column. So what can be written like this: # # create_table :taggings do |t| # t.integer :tag_id, :tagger_id, :taggable_id diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index b403443d8e..cc7c07dc35 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -278,7 +278,8 @@ module ActiveRecord rows end - # Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it. + # Executes a SQL query and returns a MySQL::Result object. Note that you have to free + # the Result object after you're done using it. def execute(sql, name = nil) #:nodoc: if name == :skip_logging @connection.query(sql) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 37e94502a4..6fae899e87 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -183,10 +183,14 @@ module ActiveRecord # * :username - Defaults to nothing. # * :password - Defaults to nothing. # * :database - The name of the database. No default, must be provided. - # * :schema_search_path - An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the :schema_order option. - # * :encoding - An optional client encoding that is used in a SET client_encoding TO call on the connection. - # * :min_messages - An optional client min messages that is used in a SET client_min_messages TO call on the connection. - # * :allow_concurrency - If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods. + # * :schema_search_path - An optional schema search path for the connection given + # as a string of comma-separated schema names. This is backward-compatible with the :schema_order option. + # * :encoding - An optional client encoding that is used in a SET client_encoding TO + # call on the connection. + # * :min_messages - An optional client min messages that is used in a + # SET client_min_messages TO call on the connection. + # * :allow_concurrency - If true, use async query methods so Ruby threads don't deadlock; + # otherwise, use blocking query methods. class PostgreSQLAdapter < AbstractAdapter ADAPTER_NAME = 'PostgreSQL'.freeze diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index e812a0371b..82ad0a3b8e 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -29,8 +29,8 @@ module ActiveRecord end end - # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby drivers (available both as gems and - # from http://rubyforge.org/projects/sqlite-ruby/). + # The SQLite adapter works with both the 2.x and 3.x series of SQLite with the sqlite-ruby + # drivers (available both as gems and from http://rubyforge.org/projects/sqlite-ruby/). # # Options: # diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 7aa725d095..e9ac5516ec 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -30,7 +30,8 @@ module ActiveRecord class SerializationTypeMismatch < ActiveRecordError end - # Raised when adapter not specified on connection (or configuration file config/database.yml misses adapter field). + # Raised when adapter not specified on connection (or configuration file config/database.yml + # misses adapter field). class AdapterNotSpecified < ActiveRecordError end @@ -38,7 +39,8 @@ module ActiveRecord class AdapterNotFound < ActiveRecordError end - # Raised when connection to the database could not been established (for example when connection= is given a nil object). + # Raised when connection to the database could not been established (for example when connection= + # is given a nil object). class ConnectionNotEstablished < ActiveRecordError end @@ -51,7 +53,8 @@ module ActiveRecord class RecordNotSaved < ActiveRecordError end - # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old). + # Raised when SQL statement cannot be executed by the database (for example, it's often the case for + # MySQL when Ruby driver used is too old). class StatementInvalid < ActiveRecordError end @@ -78,7 +81,8 @@ module ActiveRecord class InvalidForeignKey < WrappedDatabaseException end - # Raised when number of bind variables in statement given to :condition key (for example, when using +find+ method) + # Raised when number of bind variables in statement given to :condition key (for example, + # when using +find+ method) # does not match number of expected variables. # # For example, in @@ -165,4 +169,4 @@ module ActiveRecord @errors = errors end end -end \ No newline at end of file +end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 657303fd14..e44102b538 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -39,9 +39,10 @@ end # This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures # in a non-verbose, human-readable format. It ships with Ruby 1.8.1+. # -# Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed -# by ActiveSupport::TestCase.fixture_path=(path) (this is automatically configured for Rails, so you can just -# put your files in /test/fixtures/). The fixture file ends with the .yml file extension (Rails example: +# Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed +# in the directory appointed by ActiveSupport::TestCase.fixture_path=(path) (this is +# automatically configured for Rails, so you can just put your files in /test/fixtures/). +# The fixture file ends with the .yml file extension (Rails example: # /test/fixtures/web_sites.yml). The format of a YAML fixture file looks like this: # # rubyonrails: @@ -58,7 +59,8 @@ end # indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing # pleasure. # -# Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. See http://yaml.org/type/omap.html +# Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. +# See http://yaml.org/type/omap.html # for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table. # This is commonly needed for tree structures. Example: # @@ -79,7 +81,8 @@ end # (Rails example: /test/fixtures/web_sites.csv). # # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us -# humans. The first line of the CSV file is a comma-separated list of field names. The rest of the file is then comprised +# humans. The first line of the CSV file is a comma-separated list of field names. The rest of the +# file is then comprised # of the actual data (1 per line). Here's an example: # # id, name, url @@ -99,15 +102,16 @@ end # # == Single-file fixtures # -# This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats. -# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory -# appointed by ActiveSupport::TestCase.fixture_path=(path) (this is automatically configured for Rails, so you can just -# put your files in /test/fixtures// -- +# This type of fixture was the original format for Active Record that has since been deprecated in +# favor of the YAML and CSV formats. +# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) +# to the directory appointed by ActiveSupport::TestCase.fixture_path=(path) (this is automatically +# configured for Rails, so you can just put your files in /test/fixtures// -- # like /test/fixtures/web_sites/ for the WebSite model). # # Each text file placed in this directory represents a "record". Usually these types of fixtures are named without -# extensions, but if you are on a Windows machine, you might consider adding .txt as the extension. Here's what the -# above example might look like: +# extensions, but if you are on a Windows machine, you might consider adding .txt as the extension. +# Here's what the above example might look like: # # web_sites/google # web_sites/yahoo.txt @@ -133,7 +137,8 @@ end # end # end # -# By default, the test_helper module will load all of your fixtures into your test database, so this test will succeed. +# By default, the test_helper module will load all of your fixtures into your test database, +# so this test will succeed. # The testing environment will automatically load the all fixtures into the database before each test. # To ensure consistent data, the environment deletes the fixtures before running the load. # @@ -182,13 +187,15 @@ end # This will create 1000 very simple YAML fixtures. # # Using ERb, you can also inject dynamic values into your fixtures with inserts like <%= Date.today.strftime("%Y-%m-%d") %>. -# This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable -# sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application -# is properly testable. Hence, dynamic values in fixtures are to be considered a code smell. +# This is however a feature to be used with some caution. The point of fixtures are that they're +# stable units of predictable sample data. If you feel that you need to inject dynamic values, then +# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values +# in fixtures are to be considered a code smell. # # = Transactional fixtures # -# TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case. +# TestCases can use begin+rollback to isolate their changes to the database instead of having to +# delete+insert for every test case. # # class FooTest < ActiveSupport::TestCase # self.use_transactional_fixtures = true @@ -205,15 +212,18 @@ end # end # # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures, -# then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes. +# then you may omit all fixtures declarations in your test cases since all the data's already there +# and every case rolls back its changes. # # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide -# access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+) +# access to fixture data for every table that has been loaded through fixtures (depending on the +# value of +use_instantiated_fixtures+) # # When *not* to use transactional fixtures: # -# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit, -# particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify +# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until +# all parent transactions commit, particularly, the fixtures transaction which is begun in setup +# and rolled back in teardown. Thus, you won't be able to verify # the results of your transaction until Active Record supports nested transactions or savepoints (in progress). # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM. # Use InnoDB, MaxDB, or NDB instead. diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 417ff4b5eb..0e560418dc 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -48,18 +48,21 @@ module ActiveRecord # The above calls to scope define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red, # in effect, represents the query Shirt.where(:color => 'red'). # - # Unlike Shirt.find(...), however, the object returned by Shirt.red is not an Array; it resembles the association object - # constructed by a has_many declaration. For instance, you can invoke Shirt.red.first, Shirt.red.count, - # Shirt.red.where(:size => 'small'). Also, just as with the association objects, named \scopes act like an Array, - # implementing Enumerable; Shirt.red.each(&block), Shirt.red.first, and Shirt.red.inject(memo, &block) + # Unlike Shirt.find(...), however, the object returned by Shirt.red is not an Array; it + # resembles the association object constructed by a has_many declaration. For instance, + # you can invoke Shirt.red.first, Shirt.red.count, Shirt.red.where(:size => 'small'). + # Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable; + # Shirt.red.each(&block), Shirt.red.first, and Shirt.red.inject(memo, &block) # all behave as if Shirt.red really was an Array. # - # These named \scopes are composable. For instance, Shirt.red.dry_clean_only will produce all shirts that are both red and dry clean only. - # Nested finds and calculations also work with these compositions: Shirt.red.dry_clean_only.count returns the number of garments - # for which these criteria obtain. Similarly with Shirt.red.dry_clean_only.average(:thread_count). + # These named \scopes are composable. For instance, Shirt.red.dry_clean_only will produce + # all shirts that are both red and dry clean only. + # Nested finds and calculations also work with these compositions: Shirt.red.dry_clean_only.count + # returns the number of garments for which these criteria obtain. Similarly with + # Shirt.red.dry_clean_only.average(:thread_count). # - # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which the \scopes were defined. But they are also available to - # has_many associations. If, + # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which + # the \scopes were defined. But they are also available to has_many associations. If, # # class Person < ActiveRecord::Base # has_many :shirts diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index ce002f5e1a..78bac55bf2 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -67,8 +67,8 @@ module ActiveRecord # # == Configuration # - # In order to activate an observer, list it in the config.active_record.observers configuration setting in your - # config/application.rb file. + # In order to activate an observer, list it in the config.active_record.observers configuration + # setting in your config/application.rb file. # # config.active_record.observers = :comment_observer, :signup_observer # diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 38b91652ee..d5a0295b4b 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -91,8 +91,8 @@ module ActiveRecord # like render :partial => @client.becomes(Company) to render that # instance using the companies/company partial instead of clients/client. # - # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either - # instance will affect the other. + # Note: The new instance will share a link to the same attributes as the original class. + # So any change to the attributes in either instance will affect the other. def becomes(klass) became = klass.new became.instance_variable_set("@attributes", @attributes) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 44baeb6c84..f8412bc604 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -5,26 +5,33 @@ module ActiveRecord # Count operates using three different approaches. # # * Count all: By not passing any parameters to count, it will return a count of all the rows for the model. - # * Count using column: By passing a column name to count, it will return a count of all the rows for the model with supplied column present + # * Count using column: By passing a column name to count, it will return a count of all the + # rows for the model with supplied column present # * Count using options will find the row count matched by the options used. # # The third approach, count using options, accepts an option hash as the only parameter. The options are: # - # * :conditions: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro to ActiveRecord::Base. + # * :conditions: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. + # See conditions in the intro to ActiveRecord::Base. # * :joins: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed) - # or named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s). - # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns. + # or named associations in the same form used for the :include option, which will + # perform an INNER JOIN on the associated table(s). + # If the value is a string, then the records will be returned read-only since they will have + # attributes that do not correspond to the table's columns. # Pass :readonly => false to override. - # * :include: Named associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer - # to already defined associations. When using named associations, count returns the number of DISTINCT items for the model you're counting. + # * :include: Named associations that should be loaded alongside using LEFT OUTER JOINs. + # The symbols named refer to already defined associations. When using named associations, count + # returns the number of DISTINCT items for the model you're counting. # See eager loading under Associations. # * :order: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations). # * :group: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. - # * :select: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not + # * :select: By default, this is * as in SELECT * FROM, but can be changed if you, for example, + # want to do a join but not # include the joined columns. - # * :distinct: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ... - # * :from - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name - # of a database view). + # * :distinct: Set this to true to make this a distinct calculation, such as + # SELECT COUNT(DISTINCT posts.id) ... + # * :from - By default, this is the table name of the class, but can be changed to an + # alternate table name (or even the name of a database view). # # Examples for counting all: # Person.count # returns the total count of all people @@ -34,12 +41,19 @@ module ActiveRecord # # Examples for count with options: # Person.count(:conditions => "age > 26") - # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN. - # Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins. + # + # # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN. + # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) + # + # # finds the number of rows matching the conditions and joins. + # Person.count(:conditions => "age > 26 AND job.salary > 60000", + # :joins => "LEFT JOIN jobs on jobs.person_id = person.id") + # # Person.count('id', :conditions => "age > 26") # Performs a COUNT(id) # Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*') # - # Note: Person.count(:all) will not work because it will use :all as the condition. Use Person.count instead. + # Note: Person.count(:all) will not work because it will use :all as the condition. + # Use Person.count instead. def count(column_name = nil, options = {}) column_name, options = nil, column_name if column_name.is_a?(Hash) calculate(:count, column_name, options) @@ -80,13 +94,15 @@ module ActiveRecord calculate(:sum, column_name, options) end - # This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts. - # Options such as :conditions, :order, :group, :having, and :joins can be passed to customize the query. + # This calculates aggregate values in the given column. Methods for count, sum, average, + # minimum, and maximum have been added as shortcuts. Options such as :conditions, + # :order, :group, :having, and :joins can be passed to customize the query. # # There are two basic forms of output: - # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float for AVG, and the given column's type for everything else. - # * Grouped values: This returns an ordered hash of the values and groups them by the :group option. It takes either a column name, or the name - # of a belongs_to association. + # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float + # for AVG, and the given column's type for everything else. + # * Grouped values: This returns an ordered hash of the values and groups them by the + # :group option. It takes either a column name, or the name of a belongs_to association. # # values = Person.maximum(:age, :group => 'last_name') # puts values["Drake"] @@ -102,21 +118,30 @@ module ActiveRecord # end # # Options: - # * :conditions - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro to ActiveRecord::Base. - # * :include: Eager loading, see Associations for details. Since calculations don't load anything, the purpose of this is to access fields on joined tables in your conditions, order, or group clauses. - # * :joins - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed). - # The records will be returned read-only since they will have attributes that do not correspond to the table's columns. + # * :conditions - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. + # See conditions in the intro to ActiveRecord::Base. + # * :include: Eager loading, see Associations for details. Since calculations don't load anything, + # the purpose of this is to access fields on joined tables in your conditions, order, or group clauses. + # * :joins - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". + # (Rarely needed). + # The records will be returned read-only since they will have attributes that do not correspond to the + # table's columns. # * :order - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations). # * :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. - # * :select - By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not - # include the joined columns. - # * :distinct - Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ... + # * :select - By default, this is * as in SELECT * FROM, but can be changed if you for example + # want to do a join, but not include the joined columns. + # * :distinct - Set this to true to make this a distinct calculation, such as + # SELECT COUNT(DISTINCT posts.id) ... # # Examples: # Person.calculate(:count, :all) # The same as Person.count # Person.average(:age) # SELECT AVG(age) FROM people... - # Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for everyone with a last name other than 'Drake' - # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) # Selects the minimum age for any family without any minors + # Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for + # # everyone with a last name other than 'Drake' + # + # # Selects the minimum age for any family without any minors + # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) + # # Person.sum("2 * age") def calculate(operation, column_name, options = {}) if options.except(:distinct).present? diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3bf4c5bdd1..a192e044ea 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -21,23 +21,28 @@ module ActiveRecord # # ==== Parameters # - # * :conditions - An SQL fragment like "administrator = 1", [ "user_name = ?", username ], or ["user_name = :user_name", { :user_name => user_name }]. See conditions in the intro. + # * :conditions - An SQL fragment like "administrator = 1", [ "user_name = ?", username ], + # or ["user_name = :user_name", { :user_name => user_name }]. See conditions in the intro. # * :order - An SQL fragment like "created_at DESC, name". # * :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. - # * :having - Combined with +:group+ this can be used to filter the records that a GROUP BY returns. Uses the HAVING SQL-clause. + # * :having - Combined with +:group+ this can be used to filter the records that a + # GROUP BY returns. Uses the HAVING SQL-clause. # * :limit - An integer determining the limit on the number of rows that should be returned. - # * :offset - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4. + # * :offset - An integer determining the offset from where the rows should be fetched. So at 5, + # it would skip rows 0 through 4. # * :joins - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed), - # named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s), + # named associations in the same form used for the :include option, which will perform an + # INNER JOIN on the associated table(s), # or an array containing a mixture of both strings and named associations. - # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns. + # If the value is a string, then the records will be returned read-only since they will + # have attributes that do not correspond to the table's columns. # Pass :readonly => false to override. # * :include - Names associations that should be loaded alongside. The symbols named refer # to already defined associations. See eager loading under Associations. - # * :select - By default, this is "*" as in "SELECT * FROM", but can be changed if you, for example, want to do a join but not - # include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name"). - # * :from - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name - # of a database view). + # * :select - By default, this is "*" as in "SELECT * FROM", but can be changed if you, + # for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name"). + # * :from - By default, this is the table name of the class, but can be changed + # to an alternate table name (or even the name of a database view). # * :readonly - Mark the returned records read-only so they cannot be saved or updated. # * :lock - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE". # :lock => true gives connection's default exclusive lock, usually "FOR UPDATE". diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 92f7a7753d..83137433e8 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -21,7 +21,8 @@ module ActiveRecord # # This feature can easily be turned off by assigning value false . # - # If your attributes are time zone aware and you desire to skip time zone conversion for certain attributes then you can do following: + # If your attributes are time zone aware and you desire to skip time zone conversion for certain + # attributes then you can do following: # # Topic.skip_time_zone_conversion_for_attributes = [:written_on] module Timestamp diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb index 0b0f5682aa..15b587de45 100644 --- a/activerecord/lib/active_record/validations/associated.rb +++ b/activerecord/lib/active_record/validations/associated.rb @@ -27,8 +27,9 @@ module ActiveRecord # # this would specify a circular dependency and cause infinite recursion. # - # NOTE: This validation will not fail if the association hasn't been assigned. If you want to ensure that the association - # is both present and guaranteed to be valid, you also need to use +validates_presence_of+. + # NOTE: This validation will not fail if the association hasn't been assigned. If you want to + # ensure that the association is both present and guaranteed to be valid, you also need to + # use +validates_presence_of+. # # Configuration options: # * :message - A custom error message (default is: "is invalid") @@ -44,4 +45,4 @@ module ActiveRecord end end end -end \ No newline at end of file +end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 1c9ecc7b1b..bf863c7063 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -78,22 +78,25 @@ module ActiveRecord end module ClassMethods - # Validates whether the value of the specified attributes are unique across the system. Useful for making sure that only one user + # Validates whether the value of the specified attributes are unique across the system. + # Useful for making sure that only one user # can be named "davidhh". # # class Person < ActiveRecord::Base # validates_uniqueness_of :user_name, :scope => :account_id # end # - # It can also validate whether the value of the specified attributes are unique based on multiple scope parameters. For example, - # making sure that a teacher can only be on the schedule once per semester for a particular class. + # It can also validate whether the value of the specified attributes are unique based on multiple + # scope parameters. For example, making sure that a teacher can only be on the schedule once + # per semester for a particular class. # # class TeacherSchedule < ActiveRecord::Base # validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id] # end # - # When the record is created, a check is performed to make sure that no record exists in the database with the given value for the specified - # attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself. + # When the record is created, a check is performed to make sure that no record exists in the database + # with the given value for the specified attribute (that maps to a column). When the record is updated, + # the same check is made but disregarding the record itself. # # Configuration options: # * :message - Specifies a custom error message (default is: "has already been taken"). @@ -102,11 +105,12 @@ module ActiveRecord # * :allow_nil - If set to true, skips this validation if the attribute is +nil+ (default is +false+). # * :allow_blank - If set to true, skips this validation if the attribute is blank (default is +false+). # * :if - Specifies a method, proc or string to call to determine if the validation should - # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The - # method, proc or string should return or evaluate to a true or false value. + # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). + # The method, proc or string should return or evaluate to a true or false value. # * :unless - Specifies a method, proc or string to call to determine if the validation should - # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The - # method, proc or string should return or evaluate to a true or false value. + # not occur (e.g. :unless => :skip_validation, or + # :unless => Proc.new { |user| user.signup_step <= 2 }). The method, proc or string should + # return or evaluate to a true or false value. # # === Concurrency and integrity # -- cgit v1.2.3 From d3eacf9352ee34e2965c0b1781cdb8a1799686ec Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 2 Aug 2010 13:07:18 -0400 Subject: Adding to AR::Base documentation about dynamically scopeded_by query User.scoped_by_user_name('David') --- activerecord/lib/active_record/base.rb | 26 ++++++++++++++-------- .../lib/active_record/dynamic_finder_match.rb | 4 ++-- 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 5535079cd8..335f26d221 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -166,20 +166,22 @@ module ActiveRecord #:nodoc: # # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects # by simple queries without turning to SQL. They work by appending the name of an attribute - # to find_by_, find_last_by_, or find_all_by_, so you get finders + # to find_by_, find_last_by_, or find_all_by_ and thus produces finders # like Person.find_by_user_name, Person.find_all_by_last_name, and - # Payment.find_by_transaction_id. So instead of writing + # Payment.find_by_transaction_id. Instead of writing # Person.where(:user_name => user_name).first, you just do Person.find_by_user_name(user_name). # And instead of writing Person.where(:last_name => last_name).all, you just do # Person.find_all_by_last_name(last_name). # - # It's also possible to use multiple attributes in the same find by separating them with "_and_", - # so you get finders like Person.find_by_user_name_and_password or even - # Payment.find_by_purchaser_and_state_and_country. So instead of writing - # Person.where(:user_name => user_name, :password => password).first, you just do - # Person.find_by_user_name_and_password(user_name, password). + # It's also possible to use multiple attributes in the same find by separating them with "_and_". + # + # Person.where(:user_name => user_name, :password => password).first + # Person.find_by_user_name_and_password #with dynamic finder + # + # Person.where(:user_name => user_name, :password => password, :gender => 'male').first + # Payment.find_by_user_name_and_password_and_gender # - # It's even possible to call these dynamic finder methods on relations and named scopes. For example : + # It's even possible to call these dynamic finder methods on relations and named scopes. # # Payment.order("created_on").find_all_by_amount(50) # Payment.pending.find_last_by_amount(100) @@ -187,7 +189,7 @@ module ActiveRecord #:nodoc: # The same dynamic finder style can be used to create the object if it doesn't already exist. # This dynamic finder is called with find_or_create_by_ and will return the object if # it already exists and otherwise creates it, then returns it. Protected attributes won't be set - # unless they are given in a block. For example: + # unless they are given in a block. # # # No 'Summer' tag exists # Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") @@ -213,6 +215,12 @@ module ActiveRecord #:nodoc: # That will either find an existing tag named "rails", or create a new one while setting the # user that created it. # + # Just like find_by_*, you can also use scoped_by_* to retrieve data. The good thing about + # using this feature is that the very first time result is returned using method_missing technique + # but after that the method is declared on the class. Henceforth method_missing will not be hit. + # + # User.scoped_by_user_name('David') + # # == Saving arrays, hashes, and other non-mappable objects in text columns # # Active Record can serialize any object in text columns using YAML. To do so, you must diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb index dfb8a3ba60..0dc965bd26 100644 --- a/activerecord/lib/active_record/dynamic_finder_match.rb +++ b/activerecord/lib/active_record/dynamic_finder_match.rb @@ -2,8 +2,8 @@ module ActiveRecord # = Active Record Dynamic Finder Match # - # Provides dynamic attribute-based finders such as find_by_country - # if, for example, the Person has an attribute with that name. + # Refer to ActiveRecord::Base documentation for Dynamic attribute-based finders for detailed info + # class DynamicFinderMatch def self.match(method) df_match = self.new(method) -- cgit v1.2.3 From db0e3e5ad1db9bc0204db1dbc4075180bce3bc93 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 2 Aug 2010 13:10:32 -0400 Subject: Correcting the documentation which wrongly states that each dynamic finder creates method on the class. Only dynamic finders using scoped_by_* creates new methods. --- activerecord/lib/active_record/base.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 335f26d221..12736d3d5b 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -966,17 +966,14 @@ module ActiveRecord #:nodoc: end end - # Enables dynamic finders like find_by_user_name(user_name) and - # find_by_user_name_and_password(user_name, password) that are turned into - # where(:user_name => user_name).first and - # where(:user_name => user_name, :password => :password).first - # respectively. Also works for all by using find_all_by_amount(50) - # that is turned into where(:amount => 50).all. + # Enables dynamic finders like User.find_by_user_name(user_name) and + # User.scoped_by_user_name(user_name). Refer to Dynamic attribute-based finders + # section at the top of this file for more detailed information. # # It's even possible to use all the additional parameters to +find+. For example, the # full interface for +find_all_by_amount+ is actually find_all_by_amount(amount, options). # - # Each dynamic finder, scope or initializer/creator is also defined in the class after it + # Each dynamic finder using scoped_by_* is also defined in the class after it # is first invoked, so that future attempts to use it do not run through method_missing. def method_missing(method_id, *arguments, &block) if match = DynamicFinderMatch.match(method_id) -- cgit v1.2.3 From ab64499911184faee11147bc1c88dd2616e848ab Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Mon, 2 Aug 2010 14:51:57 -0700 Subject: reload the owner model after update --- activerecord/test/cases/timestamp_test.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 06ab7aa9c7..a57b3dfba1 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -93,6 +93,7 @@ class TimestampTest < ActiveRecord::TestCase owner = pet.owner owner.update_attribute(:updated_at, (time = 3.days.ago)) + owner.reload toy.touch assert_not_equal time, owner.updated_at -- cgit v1.2.3 From 42035fd112fb8034a706d033bfc63ee2a2797a96 Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Mon, 2 Aug 2010 15:10:34 -0700 Subject: move reload after touch --- activerecord/test/cases/timestamp_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index a57b3dfba1..401439994d 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -93,8 +93,8 @@ class TimestampTest < ActiveRecord::TestCase owner = pet.owner owner.update_attribute(:updated_at, (time = 3.days.ago)) - owner.reload toy.touch + owner.reload assert_not_equal time, owner.updated_at ensure -- cgit v1.2.3 From d8b90114ddd2a432280d114b9fe4ef1fdb38d132 Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Mon, 2 Aug 2010 15:11:00 -0700 Subject: skip the before_type_cast_on_datetime test entirely for mysql2 --- activerecord/test/cases/base_test.rb | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 34cce04794..0992b2fefb 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -123,23 +123,24 @@ class BasicsTest < ActiveRecord::TestCase end end - def test_read_attributes_before_type_cast_on_datetime - developer = Developer.find(:first) - # Oracle adapter returns Time before type cast - if current_adapter?(:OracleAdapter) - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) - - developer.created_at = "345643456" - assert_equal developer.created_at_before_type_cast, "345643456" - assert_equal developer.created_at, nil - - developer.created_at = "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") - elsif current_adapter?(:Mysql2Adapter) - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) - else - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] + unless current_adapter?(:Mysql2Adapter) + def test_read_attributes_before_type_cast_on_datetime + developer = Developer.find(:first) + # Oracle adapter returns Time before type cast + if current_adapter?(:OracleAdapter) + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) + + developer.created_at = "345643456" + assert_equal developer.created_at_before_type_cast, "345643456" + assert_equal developer.created_at, nil + + developer.created_at = "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) + else + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] + end end end -- cgit v1.2.3 From 74f7e172c7a660286bfd2b265e299c55078fc68e Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 3 Aug 2010 04:14:01 -0400 Subject: fixing documentation --- activerecord/lib/active_record/base.rb | 18 +++++++++--------- activerecord/lib/active_record/callbacks.rb | 4 ++-- activerecord/lib/active_record/counter_cache.rb | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 12736d3d5b..7371a1ecb2 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -516,7 +516,8 @@ module ActiveRecord #:nodoc: connection.select_value(sql, "#{name} Count").to_i end - # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards. + # Attributes listed as readonly will be used to create a new record but update operations will + # ignore these fields. def attr_readonly(*attributes) write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&:to_s)) + (readonly_attributes || [])) end @@ -604,8 +605,8 @@ module ActiveRecord #:nodoc: (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix end - # Defines the column name for use with single table inheritance - # -- can be set in subclasses like so: self.inheritance_column = "type_id" + # Defines the column name for use with single table inheritance. Use + # set_inheritance_column to set a different value. def inheritance_column @inheritance_column ||= "type".freeze end @@ -622,8 +623,8 @@ module ActiveRecord #:nodoc: default end - # Sets the table name to use to the given value, or (if the value - # is nil or false) to the value returned by the given block. + # Sets the table name. If the value is nil or false then the value returned by the given + # block is used. # # class Project < ActiveRecord::Base # set_table_name "project" @@ -1034,8 +1035,8 @@ module ActiveRecord #:nodoc: end protected - # Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash. - # method_name may be :find or :create. :find parameter is Relation while + # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be + # :find or :create. :find parameter is Relation while # :create parameters are an attributes hash. # # class Article < ActiveRecord::Base @@ -1080,8 +1081,7 @@ module ActiveRecord #:nodoc: # end # end # - # *Note*: the +:find+ scope also has effect on update and deletion methods, - # like +update_all+ and +delete_all+. + # *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+. def with_scope(method_scoping = {}, action = :merge, &block) method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping) diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 7f10fbf8b0..aa92bf999f 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -68,8 +68,8 @@ module ActiveRecord # end # # Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is - # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the situation where - # we've implemented the save behavior through overwriteable methods: + # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation + # where the +before_destroy+ methis is overriden: # # class Topic < ActiveRecord::Base # def before_destroy() destroy_author end diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 237cd56683..b0e0b45e16 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -1,7 +1,7 @@ module ActiveRecord # = Active Record Counter Cache module CounterCache - # Resets one or more counter caches to their correct value using an SQL + # Resets one or more counter caches to their correct value using a SQL # count query. This is useful when adding new counter caches, or if the # counter has been corrupted or modified directly by SQL. # -- cgit v1.2.3 From 807239f5a191205080b43c8d7316d4de60b13f6b Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 2 Aug 2010 19:43:32 -0400 Subject: Making Active Record base_test.rb thinner by moving tests to relevant files. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Number of assertions before refactoring: 2391 tests, 7579 assertions, 0 failures, 0 errors Number of assertions after refactoring: 2391 tests, 7579 assertions, 0 failures, 0 errors Signed-off-by: José Valim --- activerecord/test/cases/associations_test.rb | 22 + activerecord/test/cases/attribute_methods_test.rb | 278 ++++++++++- activerecord/test/cases/base_test.rb | 532 ---------------------- activerecord/test/cases/persistence_test.rb | 113 +++++ activerecord/test/cases/serialization_test.rb | 134 ++++++ 5 files changed, 545 insertions(+), 534 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index d328ca630b..b31611e27a 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -32,6 +32,28 @@ class AssociationsTest < ActiveRecord::TestCase assert_equal 1, liquids[0].molecules.length end + def test_clear_association_cache_stored + firm = Firm.find(1) + assert_kind_of Firm, firm + + firm.clear_association_cache + assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort + end + + def test_clear_association_cache_new_record + firm = Firm.new + client_stored = Client.find(3) + client_new = Client.new + client_new.name = "The Joneses" + clients = [ client_stored, client_new ] + + firm.clients << clients + assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set + + firm.clear_association_cache + assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set + end + def test_loading_the_association_target_should_keep_child_records_marked_for_destruction ship = Ship.create!(:name => "The good ship Dollypop") part = ship.parts.create!(:name => "Mast") diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index d59fa0a632..d20b762853 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -1,9 +1,15 @@ require "cases/helper" -require 'models/topic' require 'models/minimalistic' +require 'models/developer' +require 'models/auto_id' +require 'models/computer' +require 'models/topic' +require 'models/company' +require 'models/category' +require 'models/reply' class AttributeMethodsTest < ActiveRecord::TestCase - fixtures :topics + fixtures :topics, :developers, :companies, :computers def setup @old_matchers = ActiveRecord::Base.send(:attribute_method_matchers).dup @@ -16,6 +22,274 @@ class AttributeMethodsTest < ActiveRecord::TestCase ActiveRecord::Base.send(:attribute_method_matchers).concat(@old_matchers) end + def test_attribute_present + t = Topic.new + t.title = "hello there!" + t.written_on = Time.now + assert t.attribute_present?("title") + assert t.attribute_present?("written_on") + assert !t.attribute_present?("content") + end + + def test_attribute_keys_on_new_instance + t = Topic.new + assert_equal nil, t.title, "The topics table has a title column, so it should be nil" + assert_raise(NoMethodError) { t.title2 } + end + + def test_boolean_attributes + assert ! Topic.find(1).approved? + assert Topic.find(2).approved? + end + + def test_set_attributes + topic = Topic.find(1) + topic.attributes = { "title" => "Budget", "author_name" => "Jason" } + topic.save + assert_equal("Budget", topic.title) + assert_equal("Jason", topic.author_name) + assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address) + end + + def test_set_attributes_without_hash + topic = Topic.new + assert_nothing_raised { topic.attributes = '' } + end + + def test_integers_as_nil + test = AutoId.create('value' => '') + assert_nil AutoId.find(test.id).value + end + + def test_set_attributes_with_block + topic = Topic.new do |t| + t.title = "Budget" + t.author_name = "Jason" + end + + assert_equal("Budget", topic.title) + assert_equal("Jason", topic.author_name) + end + + def test_respond_to? + topic = Topic.find(1) + assert_respond_to topic, "title" + assert_respond_to topic, "title?" + assert_respond_to topic, "title=" + assert_respond_to topic, :title + assert_respond_to topic, :title? + assert_respond_to topic, :title= + assert_respond_to topic, "author_name" + assert_respond_to topic, "attribute_names" + assert !topic.respond_to?("nothingness") + assert !topic.respond_to?(:nothingness) + end + + def test_array_content + topic = Topic.new + topic.content = %w( one two three ) + topic.save + + assert_equal(%w( one two three ), Topic.find(topic.id).content) + end + + def test_read_attributes_before_type_cast + category = Category.new({:name=>"Test categoty", :type => nil}) + category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil} + assert_equal category_attrs , category.attributes_before_type_cast + end + + if current_adapter?(:MysqlAdapter) + def test_read_attributes_before_type_cast_on_boolean + bool = Booleantest.create({ "value" => false }) + assert_equal "0", bool.reload.attributes_before_type_cast["value"] + end + end + + def test_read_attributes_before_type_cast_on_datetime + developer = Developer.find(:first) + # Oracle adapter returns Time before type cast + unless current_adapter?(:OracleAdapter) + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] + else + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) + + developer.created_at = "345643456" + assert_equal developer.created_at_before_type_cast, "345643456" + assert_equal developer.created_at, nil + + developer.created_at = "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") + end + end + + def test_hash_content + topic = Topic.new + topic.content = { "one" => 1, "two" => 2 } + topic.save + + assert_equal 2, Topic.find(topic.id).content["two"] + + topic.content_will_change! + topic.content["three"] = 3 + topic.save + + assert_equal 3, Topic.find(topic.id).content["three"] + end + + def test_update_array_content + topic = Topic.new + topic.content = %w( one two three ) + + topic.content.push "four" + assert_equal(%w( one two three four ), topic.content) + + topic.save + + topic = Topic.find(topic.id) + topic.content << "five" + assert_equal(%w( one two three four five ), topic.content) + end + + def test_case_sensitive_attributes_hash + # DB2 is not case-sensitive + return true if current_adapter?(:DB2Adapter) + + assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes + end + + def test_hashes_not_mangled + new_topic = { :title => "New Topic" } + new_topic_values = { :title => "AnotherTopic" } + + topic = Topic.new(new_topic) + assert_equal new_topic[:title], topic.title + + topic.attributes= new_topic_values + assert_equal new_topic_values[:title], topic.title + end + + def test_create_through_factory + topic = Topic.create("title" => "New Topic") + topicReloaded = Topic.find(topic.id) + assert_equal(topic, topicReloaded) + end + + def test_write_attribute + topic = Topic.new + topic.send(:write_attribute, :title, "Still another topic") + assert_equal "Still another topic", topic.title + + topic.send(:write_attribute, "title", "Still another topic: part 2") + assert_equal "Still another topic: part 2", topic.title + end + + def test_read_attribute + topic = Topic.new + topic.title = "Don't change the topic" + assert_equal "Don't change the topic", topic.send(:read_attribute, "title") + assert_equal "Don't change the topic", topic["title"] + + assert_equal "Don't change the topic", topic.send(:read_attribute, :title) + assert_equal "Don't change the topic", topic[:title] + end + + def test_read_attribute_when_false + topic = topics(:first) + topic.approved = false + assert !topic.approved?, "approved should be false" + topic.approved = "false" + assert !topic.approved?, "approved should be false" + end + + def test_read_attribute_when_true + topic = topics(:first) + topic.approved = true + assert topic.approved?, "approved should be true" + topic.approved = "true" + assert topic.approved?, "approved should be true" + end + + def test_read_write_boolean_attribute + topic = Topic.new + # puts "" + # puts "New Topic" + # puts topic.inspect + topic.approved = "false" + # puts "Expecting false" + # puts topic.inspect + assert !topic.approved?, "approved should be false" + topic.approved = "false" + # puts "Expecting false" + # puts topic.inspect + assert !topic.approved?, "approved should be false" + topic.approved = "true" + # puts "Expecting true" + # puts topic.inspect + assert topic.approved?, "approved should be true" + topic.approved = "true" + # puts "Expecting true" + # puts topic.inspect + assert topic.approved?, "approved should be true" + # puts "" + end + + def test_query_attribute_string + [nil, "", " "].each do |value| + assert_equal false, Topic.new(:author_name => value).author_name? + end + + assert_equal true, Topic.new(:author_name => "Name").author_name? + end + + def test_query_attribute_number + [nil, 0, "0"].each do |value| + assert_equal false, Developer.new(:salary => value).salary? + end + + assert_equal true, Developer.new(:salary => 1).salary? + assert_equal true, Developer.new(:salary => "1").salary? + end + + def test_query_attribute_boolean + [nil, "", false, "false", "f", 0].each do |value| + assert_equal false, Topic.new(:approved => value).approved? + end + + [true, "true", "1", 1].each do |value| + assert_equal true, Topic.new(:approved => value).approved? + end + end + + def test_query_attribute_with_custom_fields + object = Company.find_by_sql(<<-SQL).first + SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value + FROM companies c1, companies c2 + WHERE c1.firm_id = c2.id + AND c1.id = 2 + SQL + + assert_equal "Firm", object.string_value + assert object.string_value? + + object.string_value = " " + assert !object.string_value? + + assert_equal 1, object.int_value.to_i + assert object.int_value? + + object.int_value = "0" + assert !object.int_value? + end + + def test_non_attribute_access_and_assignment + topic = Topic.new + assert !topic.respond_to?("mumbo") + assert_raise(NoMethodError) { topic.mumbo } + assert_raise(NoMethodError) { topic.mumbo = 5 } + end + def test_undeclared_attribute_method_does_not_affect_respond_to_and_method_missing topic = @target.new(:title => 'Budget') assert topic.respond_to?('title') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index df6895f0d0..56ec4ca58c 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -52,254 +52,6 @@ class BasicsTest < ActiveRecord::TestCase assert Topic.table_exists? end - def test_set_attributes - topic = Topic.find(1) - topic.attributes = { "title" => "Budget", "author_name" => "Jason" } - topic.save - assert_equal("Budget", topic.title) - assert_equal("Jason", topic.author_name) - assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address) - end - - def test_set_attributes_without_hash - topic = Topic.new - assert_nothing_raised { topic.attributes = '' } - end - - def test_integers_as_nil - test = AutoId.create('value' => '') - assert_nil AutoId.find(test.id).value - end - - def test_set_attributes_with_block - topic = Topic.new do |t| - t.title = "Budget" - t.author_name = "Jason" - end - - assert_equal("Budget", topic.title) - assert_equal("Jason", topic.author_name) - end - - def test_respond_to? - topic = Topic.find(1) - assert_respond_to topic, "title" - assert_respond_to topic, "title?" - assert_respond_to topic, "title=" - assert_respond_to topic, :title - assert_respond_to topic, :title? - assert_respond_to topic, :title= - assert_respond_to topic, "author_name" - assert_respond_to topic, "attribute_names" - assert !topic.respond_to?("nothingness") - assert !topic.respond_to?(:nothingness) - end - - def test_array_content - topic = Topic.new - topic.content = %w( one two three ) - topic.save - - assert_equal(%w( one two three ), Topic.find(topic.id).content) - end - - def test_read_attributes_before_type_cast - category = Category.new({:name=>"Test categoty", :type => nil}) - category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil} - assert_equal category_attrs , category.attributes_before_type_cast - end - - if current_adapter?(:MysqlAdapter) - def test_read_attributes_before_type_cast_on_boolean - bool = Booleantest.create({ "value" => false }) - assert_equal "0", bool.reload.attributes_before_type_cast["value"] - end - end - - def test_read_attributes_before_type_cast_on_datetime - developer = Developer.find(:first) - # Oracle adapter returns Time before type cast - unless current_adapter?(:OracleAdapter) - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] - else - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) - - developer.created_at = "345643456" - assert_equal developer.created_at_before_type_cast, "345643456" - assert_equal developer.created_at, nil - - developer.created_at = "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") - end - end - - def test_hash_content - topic = Topic.new - topic.content = { "one" => 1, "two" => 2 } - topic.save - - assert_equal 2, Topic.find(topic.id).content["two"] - - topic.content_will_change! - topic.content["three"] = 3 - topic.save - - assert_equal 3, Topic.find(topic.id).content["three"] - end - - def test_update_array_content - topic = Topic.new - topic.content = %w( one two three ) - - topic.content.push "four" - assert_equal(%w( one two three four ), topic.content) - - topic.save - - topic = Topic.find(topic.id) - topic.content << "five" - assert_equal(%w( one two three four five ), topic.content) - end - - def test_case_sensitive_attributes_hash - # DB2 is not case-sensitive - return true if current_adapter?(:DB2Adapter) - - assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes - end - - def test_hashes_not_mangled - new_topic = { :title => "New Topic" } - new_topic_values = { :title => "AnotherTopic" } - - topic = Topic.new(new_topic) - assert_equal new_topic[:title], topic.title - - topic.attributes= new_topic_values - assert_equal new_topic_values[:title], topic.title - end - - def test_create_through_factory - topic = Topic.create("title" => "New Topic") - topicReloaded = Topic.find(topic.id) - assert_equal(topic, topicReloaded) - end - - def test_write_attribute - topic = Topic.new - topic.send(:write_attribute, :title, "Still another topic") - assert_equal "Still another topic", topic.title - - topic.send(:write_attribute, "title", "Still another topic: part 2") - assert_equal "Still another topic: part 2", topic.title - end - - def test_read_attribute - topic = Topic.new - topic.title = "Don't change the topic" - assert_equal "Don't change the topic", topic.send(:read_attribute, "title") - assert_equal "Don't change the topic", topic["title"] - - assert_equal "Don't change the topic", topic.send(:read_attribute, :title) - assert_equal "Don't change the topic", topic[:title] - end - - def test_read_attribute_when_false - topic = topics(:first) - topic.approved = false - assert !topic.approved?, "approved should be false" - topic.approved = "false" - assert !topic.approved?, "approved should be false" - end - - def test_read_attribute_when_true - topic = topics(:first) - topic.approved = true - assert topic.approved?, "approved should be true" - topic.approved = "true" - assert topic.approved?, "approved should be true" - end - - def test_read_write_boolean_attribute - topic = Topic.new - # puts "" - # puts "New Topic" - # puts topic.inspect - topic.approved = "false" - # puts "Expecting false" - # puts topic.inspect - assert !topic.approved?, "approved should be false" - topic.approved = "false" - # puts "Expecting false" - # puts topic.inspect - assert !topic.approved?, "approved should be false" - topic.approved = "true" - # puts "Expecting true" - # puts topic.inspect - assert topic.approved?, "approved should be true" - topic.approved = "true" - # puts "Expecting true" - # puts topic.inspect - assert topic.approved?, "approved should be true" - # puts "" - end - - def test_query_attribute_string - [nil, "", " "].each do |value| - assert_equal false, Topic.new(:author_name => value).author_name? - end - - assert_equal true, Topic.new(:author_name => "Name").author_name? - end - - def test_query_attribute_number - [nil, 0, "0"].each do |value| - assert_equal false, Developer.new(:salary => value).salary? - end - - assert_equal true, Developer.new(:salary => 1).salary? - assert_equal true, Developer.new(:salary => "1").salary? - end - - def test_query_attribute_boolean - [nil, "", false, "false", "f", 0].each do |value| - assert_equal false, Topic.new(:approved => value).approved? - end - - [true, "true", "1", 1].each do |value| - assert_equal true, Topic.new(:approved => value).approved? - end - end - - def test_query_attribute_with_custom_fields - object = Company.find_by_sql(<<-SQL).first - SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value - FROM companies c1, companies c2 - WHERE c1.firm_id = c2.id - AND c1.id = 2 - SQL - - assert_equal "Firm", object.string_value - assert object.string_value? - - object.string_value = " " - assert !object.string_value? - - assert_equal 1, object.int_value.to_i - assert object.int_value? - - object.int_value = "0" - assert !object.int_value? - end - - def test_non_attribute_access_and_assignment - topic = Topic.new - assert !topic.respond_to?("mumbo") - assert_raise(NoMethodError) { topic.mumbo } - assert_raise(NoMethodError) { topic.mumbo = 5 } - end - def test_preserving_date_objects if current_adapter?(:SybaseAdapter) # Sybase ctlib does not (yet?) support the date type; use datetime instead. @@ -531,38 +283,6 @@ class BasicsTest < ActiveRecord::TestCase GUESSED_CLASSES.each(&:reset_table_name) end - def test_destroy_all - conditions = "author_name = 'Mary'" - topics_by_mary = Topic.all(:conditions => conditions, :order => 'id') - assert ! topics_by_mary.empty? - - assert_difference('Topic.count', -topics_by_mary.size) do - destroyed = Topic.destroy_all(conditions).sort_by(&:id) - assert_equal topics_by_mary, destroyed - assert destroyed.all? { |topic| topic.frozen? }, "destroyed topics should be frozen" - end - end - - def test_destroy_many - clients = Client.find([2, 3], :order => 'id') - - assert_difference('Client.count', -2) do - destroyed = Client.destroy([2, 3]).sort_by(&:id) - assert_equal clients, destroyed - assert destroyed.all? { |client| client.frozen? }, "destroyed clients should be frozen" - end - end - - def test_delete_many - original_count = Topic.count - Topic.delete(deleting = [1, 2]) - assert_equal original_count - deleting.size, Topic.count - end - - def test_boolean_attributes - assert ! Topic.find(1).approved? - assert Topic.find(2).approved? - end if current_adapter?(:MysqlAdapter) def test_update_all_with_order_and_limit @@ -570,63 +290,6 @@ class BasicsTest < ActiveRecord::TestCase end end - # Oracle UPDATE does not support ORDER BY - unless current_adapter?(:OracleAdapter) - def test_update_all_ignores_order_without_limit_from_association - author = authors(:david) - assert_nothing_raised do - assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ]) - end - end - - def test_update_all_with_order_and_limit_updates_subset_only - author = authors(:david) - assert_nothing_raised do - assert_equal 1, author.posts_sorted_by_id_limited.size - assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size - assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ]) - assert_equal "bulk update!", posts(:welcome).body - assert_not_equal "bulk update!", posts(:thinking).body - end - end - end - - def test_update_many - topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } - updated = Topic.update(topic_data.keys, topic_data.values) - - assert_equal 2, updated.size - assert_equal "1 updated", Topic.find(1).content - assert_equal "2 updated", Topic.find(2).content - end - - def test_delete_all - assert Topic.count > 0 - - assert_equal Topic.count, Topic.delete_all - end - - def test_update_by_condition - Topic.update_all "content = 'bulk updated!'", ["approved = ?", true] - assert_equal "Have a nice day", Topic.find(1).content - assert_equal "bulk updated!", Topic.find(2).content - end - - def test_attribute_present - t = Topic.new - t.title = "hello there!" - t.written_on = Time.now - assert t.attribute_present?("title") - assert t.attribute_present?("written_on") - assert !t.attribute_present?("content") - end - - def test_attribute_keys_on_new_instance - t = Topic.new - assert_equal nil, t.title, "The topics table has a title column, so it should be nil" - assert_raise(NoMethodError) { t.title2 } - end - def test_null_fields assert_nil Topic.find(1).parent_id assert_nil Topic.create("title" => "Hey you").parent_id @@ -710,8 +373,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ] end - - def test_readonly_attributes assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes @@ -1313,49 +974,6 @@ class BasicsTest < ActiveRecord::TestCase end end - def test_increment_attribute - assert_equal 50, accounts(:signals37).credit_limit - accounts(:signals37).increment! :credit_limit - assert_equal 51, accounts(:signals37, :reload).credit_limit - - accounts(:signals37).increment(:credit_limit).increment!(:credit_limit) - assert_equal 53, accounts(:signals37, :reload).credit_limit - end - - def test_increment_nil_attribute - assert_nil topics(:first).parent_id - topics(:first).increment! :parent_id - assert_equal 1, topics(:first).parent_id - end - - def test_increment_attribute_by - assert_equal 50, accounts(:signals37).credit_limit - accounts(:signals37).increment! :credit_limit, 5 - assert_equal 55, accounts(:signals37, :reload).credit_limit - - accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3) - assert_equal 59, accounts(:signals37, :reload).credit_limit - end - - def test_decrement_attribute - assert_equal 50, accounts(:signals37).credit_limit - - accounts(:signals37).decrement!(:credit_limit) - assert_equal 49, accounts(:signals37, :reload).credit_limit - - accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit) - assert_equal 47, accounts(:signals37, :reload).credit_limit - end - - def test_decrement_attribute_by - assert_equal 50, accounts(:signals37).credit_limit - accounts(:signals37).decrement! :credit_limit, 5 - assert_equal 45, accounts(:signals37, :reload).credit_limit - - accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3) - assert_equal 41, accounts(:signals37, :reload).credit_limit - end - def test_toggle_attribute assert !topics(:first).approved? topics(:first).toggle!(:approved) @@ -1482,28 +1100,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal res6, res7 end - def test_clear_association_cache_stored - firm = Firm.find(1) - assert_kind_of Firm, firm - - firm.clear_association_cache - assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort - end - - def test_clear_association_cache_new_record - firm = Firm.new - client_stored = Client.find(3) - client_new = Client.new - client_new.name = "The Joneses" - clients = [ client_stored, client_new ] - - firm.clients << clients - assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set - - firm.clear_association_cache - assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set - end - def test_interpolate_sql assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') } assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') } @@ -1703,134 +1299,6 @@ class BasicsTest < ActiveRecord::TestCase assert_no_queries { assert true } end - def test_to_xml - xml = REXML::Document.new(topics(:first).to_xml(:indent => 0)) - bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema - written_on_in_current_timezone = topics(:first).written_on.xmlschema - last_read_in_current_timezone = topics(:first).last_read.xmlschema - - assert_equal "topic", xml.root.name - assert_equal "The First Topic" , xml.elements["//title"].text - assert_equal "David" , xml.elements["//author-name"].text - assert_match "Have a nice day", xml.elements["//content"].text - - assert_equal "1", xml.elements["//id"].text - assert_equal "integer" , xml.elements["//id"].attributes['type'] - - assert_equal "1", xml.elements["//replies-count"].text - assert_equal "integer" , xml.elements["//replies-count"].attributes['type'] - - assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text - assert_equal "datetime" , xml.elements["//written-on"].attributes['type'] - - assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text - - assert_equal nil, xml.elements["//parent-id"].text - assert_equal "integer", xml.elements["//parent-id"].attributes['type'] - assert_equal "true", xml.elements["//parent-id"].attributes['nil'] - - if current_adapter?(:SybaseAdapter) - assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text - assert_equal "datetime" , xml.elements["//last-read"].attributes['type'] - else - # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb) - assert_equal "2004-04-15", xml.elements["//last-read"].text - assert_equal "date" , xml.elements["//last-read"].attributes['type'] - end - - # Oracle and DB2 don't have true boolean or time-only fields - unless current_adapter?(:OracleAdapter, :DB2Adapter) - assert_equal "false", xml.elements["//approved"].text - assert_equal "boolean" , xml.elements["//approved"].attributes['type'] - - assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text - assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type'] - end - end - - def test_to_xml_skipping_attributes - xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count]) - assert_equal "", xml.first(7) - assert !xml.include?(%(The First Topic)) - assert xml.include?(%(David)) - - xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count]) - assert !xml.include?(%(The First Topic)) - assert !xml.include?(%(David)) - end - - def test_to_xml_including_has_many_association - xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count) - assert_equal "", xml.first(7) - assert xml.include?(%()) - assert xml.include?(%(The Second Topic of the day)) - end - - def test_array_to_xml_including_has_many_association - xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies) - assert xml.include?(%()) - end - - def test_array_to_xml_including_methods - xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ]) - assert xml.include?(%(#{topics(:first).topic_id})), xml - assert xml.include?(%(#{topics(:second).topic_id})), xml - end - - def test_array_to_xml_including_has_one_association - xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account) - assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true)) - assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true)) - end - - def test_array_to_xml_including_belongs_to_association - xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm) - assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true)) - assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true)) - assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true)) - end - - def test_to_xml_including_belongs_to_association - xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm) - assert !xml.include?("") - - xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm) - assert xml.include?("") - end - - def test_to_xml_including_multiple_associations - xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ]) - assert_equal "", xml.first(6) - assert xml.include?(%()) - assert xml.include?(%()) - end - - def test_to_xml_including_multiple_associations_with_options - xml = companies(:first_firm).to_xml( - :indent => 0, :skip_instruct => true, - :include => { :clients => { :only => :name } } - ) - - assert_equal "", xml.first(6) - assert xml.include?(%(Summit)) - assert xml.include?(%()) - end - - def test_to_xml_including_methods - xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true) - assert_equal "", xml.first(9) - assert xml.include?(%(I am Jack's profound disappointment)) - end - - def test_to_xml_with_block - value = "Rockin' the block" - xml = Company.new.to_xml(:skip_instruct => true) do |_xml| - _xml.tag! "arbitrary-element", value - end - assert_equal "", xml.first(9) - assert xml.include?(%(#{value})) - end - def test_to_param_should_return_string assert_kind_of String, Client.find(:first).to_param end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 1cc3a337c3..d7666b19f6 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -19,6 +19,119 @@ class PersistencesTest < ActiveRecord::TestCase fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans + # Oracle UPDATE does not support ORDER BY + unless current_adapter?(:OracleAdapter) + def test_update_all_ignores_order_without_limit_from_association + author = authors(:david) + assert_nothing_raised do + assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ]) + end + end + + def test_update_all_with_order_and_limit_updates_subset_only + author = authors(:david) + assert_nothing_raised do + assert_equal 1, author.posts_sorted_by_id_limited.size + assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size + assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ]) + assert_equal "bulk update!", posts(:welcome).body + assert_not_equal "bulk update!", posts(:thinking).body + end + end + end + + def test_update_many + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } + updated = Topic.update(topic_data.keys, topic_data.values) + + assert_equal 2, updated.size + assert_equal "1 updated", Topic.find(1).content + assert_equal "2 updated", Topic.find(2).content + end + + def test_delete_all + assert Topic.count > 0 + + assert_equal Topic.count, Topic.delete_all + end + + def test_update_by_condition + Topic.update_all "content = 'bulk updated!'", ["approved = ?", true] + assert_equal "Have a nice day", Topic.find(1).content + assert_equal "bulk updated!", Topic.find(2).content + end + + def test_increment_attribute + assert_equal 50, accounts(:signals37).credit_limit + accounts(:signals37).increment! :credit_limit + assert_equal 51, accounts(:signals37, :reload).credit_limit + + accounts(:signals37).increment(:credit_limit).increment!(:credit_limit) + assert_equal 53, accounts(:signals37, :reload).credit_limit + end + + def test_increment_nil_attribute + assert_nil topics(:first).parent_id + topics(:first).increment! :parent_id + assert_equal 1, topics(:first).parent_id + end + + def test_increment_attribute_by + assert_equal 50, accounts(:signals37).credit_limit + accounts(:signals37).increment! :credit_limit, 5 + assert_equal 55, accounts(:signals37, :reload).credit_limit + + accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3) + assert_equal 59, accounts(:signals37, :reload).credit_limit + end + + def test_destroy_all + conditions = "author_name = 'Mary'" + topics_by_mary = Topic.all(:conditions => conditions, :order => 'id') + assert ! topics_by_mary.empty? + + assert_difference('Topic.count', -topics_by_mary.size) do + destroyed = Topic.destroy_all(conditions).sort_by(&:id) + assert_equal topics_by_mary, destroyed + assert destroyed.all? { |topic| topic.frozen? }, "destroyed topics should be frozen" + end + end + + def test_destroy_many + clients = Client.find([2, 3], :order => 'id') + + assert_difference('Client.count', -2) do + destroyed = Client.destroy([2, 3]).sort_by(&:id) + assert_equal clients, destroyed + assert destroyed.all? { |client| client.frozen? }, "destroyed clients should be frozen" + end + end + + def test_delete_many + original_count = Topic.count + Topic.delete(deleting = [1, 2]) + assert_equal original_count - deleting.size, Topic.count + end + + def test_decrement_attribute + assert_equal 50, accounts(:signals37).credit_limit + + accounts(:signals37).decrement!(:credit_limit) + assert_equal 49, accounts(:signals37, :reload).credit_limit + + accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit) + assert_equal 47, accounts(:signals37, :reload).credit_limit + end + + def test_decrement_attribute_by + assert_equal 50, accounts(:signals37).credit_limit + accounts(:signals37).decrement! :credit_limit, 5 + assert_equal 45, accounts(:signals37, :reload).credit_limit + + accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3) + assert_equal 41, accounts(:signals37, :reload).credit_limit + end + def test_create topic = Topic.new topic.title = "New Topic" diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb index 8c385af97c..dab81530af 100644 --- a/activerecord/test/cases/serialization_test.rb +++ b/activerecord/test/cases/serialization_test.rb @@ -1,7 +1,13 @@ require "cases/helper" require 'models/contact' +require 'models/topic' +require 'models/reply' +require 'models/company' class SerializationTest < ActiveRecord::TestCase + + fixtures :topics, :companies, :accounts + FORMATS = [ :xml, :json ] def setup @@ -17,6 +23,134 @@ class SerializationTest < ActiveRecord::TestCase @contact = Contact.new(@contact_attributes) end + def test_to_xml + xml = REXML::Document.new(topics(:first).to_xml(:indent => 0)) + bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema + written_on_in_current_timezone = topics(:first).written_on.xmlschema + last_read_in_current_timezone = topics(:first).last_read.xmlschema + + assert_equal "topic", xml.root.name + assert_equal "The First Topic" , xml.elements["//title"].text + assert_equal "David" , xml.elements["//author-name"].text + assert_match "Have a nice day", xml.elements["//content"].text + + assert_equal "1", xml.elements["//id"].text + assert_equal "integer" , xml.elements["//id"].attributes['type'] + + assert_equal "1", xml.elements["//replies-count"].text + assert_equal "integer" , xml.elements["//replies-count"].attributes['type'] + + assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text + assert_equal "datetime" , xml.elements["//written-on"].attributes['type'] + + assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text + + assert_equal nil, xml.elements["//parent-id"].text + assert_equal "integer", xml.elements["//parent-id"].attributes['type'] + assert_equal "true", xml.elements["//parent-id"].attributes['nil'] + + if current_adapter?(:SybaseAdapter) + assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text + assert_equal "datetime" , xml.elements["//last-read"].attributes['type'] + else + # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb) + assert_equal "2004-04-15", xml.elements["//last-read"].text + assert_equal "date" , xml.elements["//last-read"].attributes['type'] + end + + # Oracle and DB2 don't have true boolean or time-only fields + unless current_adapter?(:OracleAdapter, :DB2Adapter) + assert_equal "false", xml.elements["//approved"].text + assert_equal "boolean" , xml.elements["//approved"].attributes['type'] + + assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text + assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type'] + end + end + + def test_to_xml_skipping_attributes + xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count]) + assert_equal "", xml.first(7) + assert !xml.include?(%(The First Topic)) + assert xml.include?(%(David)) + + xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count]) + assert !xml.include?(%(The First Topic)) + assert !xml.include?(%(David)) + end + + def test_to_xml_including_has_many_association + xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count) + assert_equal "", xml.first(7) + assert xml.include?(%()) + assert xml.include?(%(The Second Topic of the day)) + end + + def test_array_to_xml_including_has_many_association + xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies) + assert xml.include?(%()) + end + + def test_array_to_xml_including_methods + xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ]) + assert xml.include?(%(#{topics(:first).topic_id})), xml + assert xml.include?(%(#{topics(:second).topic_id})), xml + end + + def test_array_to_xml_including_has_one_association + xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account) + assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true)) + assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true)) + end + + def test_array_to_xml_including_belongs_to_association + xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm) + assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true)) + assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true)) + assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true)) + end + + def test_to_xml_including_belongs_to_association + xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm) + assert !xml.include?("") + + xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm) + assert xml.include?("") + end + + def test_to_xml_including_multiple_associations + xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ]) + assert_equal "", xml.first(6) + assert xml.include?(%()) + assert xml.include?(%()) + end + + def test_to_xml_including_multiple_associations_with_options + xml = companies(:first_firm).to_xml( + :indent => 0, :skip_instruct => true, + :include => { :clients => { :only => :name } } + ) + + assert_equal "", xml.first(6) + assert xml.include?(%(Summit)) + assert xml.include?(%()) + end + + def test_to_xml_including_methods + xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true) + assert_equal "", xml.first(9) + assert xml.include?(%(I am Jack's profound disappointment)) + end + + def test_to_xml_with_block + value = "Rockin' the block" + xml = Company.new.to_xml(:skip_instruct => true) do |_xml| + _xml.tag! "arbitrary-element", value + end + assert_equal "", xml.first(9) + assert xml.include?(%(#{value})) + end + def test_serialize_should_be_reversible for format in FORMATS @serialized = Contact.new.send("to_#{format}") -- cgit v1.2.3 From db1c484c55758e25c56615d6efdab8a22cff4a46 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 2 Aug 2010 21:22:02 -0400 Subject: Dynamic finder method like scoped_by_* create methods so that method_missing is not hit next time. Adding a test for this scenario. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/named_scope_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 7c037b20c5..c42dda2ccb 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -478,4 +478,10 @@ class DynamicScopeTest < ActiveRecord::TestCase assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1) assert_equal Post.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, Post.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"}) end + + def test_dynamic_scope_should_create_methods_after_hitting_method_missing + assert Developer.methods.grep(/scoped_by_created_at/).blank? + Developer.scoped_by_created_at(nil) + assert Developer.methods.grep(/scoped_by_created_at/).present? + end end -- cgit v1.2.3 From 41c1aa607dc9633c05e7946b7d2cf6f5c6954317 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 3 Aug 2010 09:16:01 -0700 Subject: order query is already a string, no need to to_s --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index a92d180442..5ef00206dd 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -95,7 +95,7 @@ module ActiveRecord order_clause = arel.send(:order_clauses).join(', ') relation = except(:order) - if order_clause.present? + unless order_clauses.blank? relation.order(reverse_sql_order(order_clause)) else relation.order("#{@klass.table_name}.#{@klass.primary_key} DESC") @@ -238,7 +238,7 @@ module ActiveRecord end def reverse_sql_order(order_query) - order_query.to_s.split(/,/).each { |s| + order_query.split(',').each { |s| if s.match(/\s(asc|ASC)$/) s.gsub!(/\s(asc|ASC)$/, ' DESC') elsif s.match(/\s(desc|DESC)$/) -- cgit v1.2.3 From c0fa4de65b32fc5e8251dafab1330f044d682a1e Mon Sep 17 00:00:00 2001 From: wycats Date: Tue, 3 Aug 2010 12:22:10 -0700 Subject: Allow :name to be a Symbol (was this removed by accident?) --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index ffc3847a31..7dee68502f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -327,6 +327,8 @@ module ActiveRecord # # Note: SQLite doesn't support index length def add_index(table_name, column_name, options = {}) + options[:name] = options[:name].to_s if options.key?(:name) + column_names = Array.wrap(column_name) index_name = index_name(table_name, :column => column_names) -- cgit v1.2.3 From 7e4e1f0ca5722008b288224221405eba486345c1 Mon Sep 17 00:00:00 2001 From: RainerBlessing Date: Tue, 3 Aug 2010 20:37:46 +0200 Subject: query value is converted to_s instead of to_yaml Signed-off-by: Santiago Pastorino --- .../lib/active_record/connection_adapters/abstract/quoting.rb | 2 +- activerecord/test/cases/finder_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index d7b5bf8e31..e2b3773a99 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -30,7 +30,7 @@ module ActiveRecord if value.acts_like?(:date) || value.acts_like?(:time) "'#{quoted_date(value)}'" else - "'#{quote_string(value.to_yaml)}'" + "'#{quote_string(value.to_s)}'" end end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index a107c1a474..4f3e43d77d 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -693,6 +693,14 @@ class FinderTest < ActiveRecord::TestCase assert_equal [], Topic.find_all_by_title("The First Topic!!") end + def test_find_all_by_one_attribute_which_is_a_symbol + topics = Topic.find_all_by_content("Have a nice day".to_sym) + assert_equal 2, topics.size + assert topics.include?(topics(:first)) + + assert_equal [], Topic.find_all_by_title("The First Topic!!") + end + def test_find_all_by_one_attribute_that_is_an_aggregate balance = customers(:david).balance assert_kind_of Money, balance -- cgit v1.2.3 From 6d6ed5532416aa75889cd34a669696ba640abeb5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 3 Aug 2010 17:57:36 -0700 Subject: avoid passing AR::Base objects to Arel when we can --- activerecord/lib/active_record/relation/finder_methods.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index a192e044ea..bad9af250d 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -169,6 +169,8 @@ module ActiveRecord # Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists? def exists?(id = nil) + id = id.id if ActiveRecord::Base === id + case id when Array, Hash where(id).exists? -- cgit v1.2.3 From c25c3879ce27f5e834defd71409ea3cb2378aac6 Mon Sep 17 00:00:00 2001 From: wycats Date: Wed, 4 Aug 2010 04:05:28 -0700 Subject: I'm unsure how cloning was working in Rails 3 before --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 12736d3d5b..7710aa7c54 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1400,7 +1400,7 @@ MSG # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is # application specific and is therefore left to the application to implement according to its need. def initialize_copy(other) - callback(:after_initialize) if respond_to_without_attributes?(:after_initialize) + _run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks) cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) cloned_attributes.delete(self.class.primary_key) -- cgit v1.2.3 From cff7f53fbcce14f190cc2d993467ba77fe8fa03d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 4 Aug 2010 08:44:35 -0700 Subject: do not pass AR objects to ARel --- activerecord/lib/active_record/relation/finder_methods.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index bad9af250d..b34c11973b 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -286,6 +286,8 @@ module ActiveRecord end def find_one(id) + id = id.id if ActiveRecord::Base === id + record = where(primary_key.eq(id)).first unless record -- cgit v1.2.3 From 30abb01d073a5cadf1e7f38e08ba20dfe468bda0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 4 Aug 2010 14:11:09 -0700 Subject: fisting indentation --- activerecord/lib/active_record/relation/batches.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 412be895c4..ecdf7c54b1 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -50,9 +50,9 @@ module ActiveRecord def find_in_batches(options = {}) relation = self - if orders.present? || taken.present? - ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") - end + if orders.present? || taken.present? + ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") + end if (finder_options = options.except(:start, :batch_size)).present? raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present? -- cgit v1.2.3 From ec736dff7b0a05d58d4c8780863afc47e2bb74a3 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 4 Aug 2010 14:22:20 -0700 Subject: call to_a since we are not passing anything to all() --- activerecord/lib/active_record/relation/batches.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index ecdf7c54b1..d7494ebb5a 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -73,7 +73,7 @@ module ActiveRecord break if records.size < batch_size if primary_key_offset = records.last.id - records = relation.where(primary_key.gt(primary_key_offset)).all + records = relation.where(primary_key.gt(primary_key_offset)).to_a else raise "Primary key not included in the custom select clause" end -- cgit v1.2.3 From d191db76e04f065e1b0cff3766c818f9b8e2f43a Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 5 Aug 2010 01:09:09 +0200 Subject: standarizes the use of the article "an" for "SQL" and "SQLite" --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- .../lib/active_record/connection_adapters/abstract/database_limits.rb | 2 +- .../active_record/connection_adapters/abstract/schema_definitions.rb | 2 +- activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 2 +- activerecord/lib/active_record/counter_cache.rb | 2 +- activerecord/lib/active_record/persistence.rb | 2 +- activerecord/lib/active_record/railties/databases.rake | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index d74fb7c702..c33bc6aa47 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -24,7 +24,7 @@ module ActiveRecord # If the association has a counter cache it gets that value. Otherwise # 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. + # to do an 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 diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb index 4118ea7b31..a130c330dd 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb @@ -42,7 +42,7 @@ module ActiveRecord 65535 end - # the maximum length of a SQL query + # the maximum length of an SQL query def sql_query_length 1048575 end 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 9fc0e6d403..9118ceb33c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -537,7 +537,7 @@ module ActiveRecord end end - # Represents a SQL table in an abstract way for updating a table. + # Represents an SQL table in an abstract way for updating a table. # Also see TableDefinition and SchemaStatements#create_table # # Available transformations are: diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index cc7c07dc35..ba0051de05 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -278,7 +278,7 @@ module ActiveRecord rows end - # Executes a SQL query and returns a MySQL::Result object. Note that you have to free + # Executes an SQL query and returns a MySQL::Result object. Note that you have to free # the Result object after you're done using it. def execute(sql, name = nil) #:nodoc: if name == :skip_logging diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index b0e0b45e16..237cd56683 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -1,7 +1,7 @@ module ActiveRecord # = Active Record Counter Cache module CounterCache - # Resets one or more counter caches to their correct value using a SQL + # Resets one or more counter caches to their correct value using an SQL # count query. This is useful when adding new counter caches, or if the # counter has been corrupted or modified directly by SQL. # diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 8f44f03d56..71b46beaef 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -60,7 +60,7 @@ module ActiveRecord # reflect that no changes should be made (since they can't be # persisted). Returns the frozen instance. # - # The row is simply removed with a SQL +DELETE+ statement on the + # The row is simply removed with an SQL +DELETE+ statement on the # record's primary key, and no callbacks are executed. # # To enforce the object's +before_destroy+ and +after_destroy+ diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 2c17c74ab4..ae605d3e7a 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -339,7 +339,7 @@ namespace :db do end namespace :structure do - desc "Dump the database structure to a SQL file" + desc "Dump the database structure to an SQL file" task :dump => :environment do abcs = ActiveRecord::Base.configurations case abcs[Rails.env]["adapter"] -- cgit v1.2.3 From 117b096d0a91ff7a8fb704b359a6fdf1ccc20e7c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 4 Aug 2010 16:22:16 -0700 Subject: avoid passing lists of lists to the group clause --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 5ef00206dd..e71f1cca72 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -31,7 +31,7 @@ module ActiveRecord end def group(*args) - clone.tap {|r| r.group_values += args if args.present? } + clone.tap {|r| r.group_values += args.flatten if args.present? } end def order(*args) -- cgit v1.2.3 From 1ca18a6f858a257cf495e9dec7573374b1f3b01e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 5 Aug 2010 08:15:07 -0700 Subject: fixing whitespace errors --- activerecord/lib/active_record/schema.rb | 2 +- activerecord/lib/active_record/schema_dumper.rb | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index e2783087ec..c1bc3214ea 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -2,7 +2,7 @@ require 'active_support/core_ext/object/blank' module ActiveRecord # = Active Record Schema - # + # # Allows programmers to programmatically define a schema in a portable # DSL. This means you can define tables, indexes, etc. without using SQL # directly, so your applications can more easily support multiple diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index a4757773d8..e9af20e1b6 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -8,13 +8,13 @@ module ActiveRecord # output format (i.e., ActiveRecord::Schema). class SchemaDumper #:nodoc: private_class_method :new - + ## # :singleton-method: - # A list of tables which should not be dumped to the schema. + # A list of tables which should not be dumped to the schema. # Acceptable values are strings as well as regexp. # This setting is only used if ActiveRecord::Base.schema_format == :ruby - cattr_accessor :ignore_tables + cattr_accessor :ignore_tables @@ignore_tables = [] def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT) @@ -71,7 +71,7 @@ HEADER else raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.' end - end + end table(tbl, stream) end end @@ -87,7 +87,7 @@ HEADER elsif @connection.respond_to?(:primary_key) pk = @connection.primary_key(table) end - + tbl.print " create_table #{table.inspect}" if columns.detect { |c| c.name == pk } if pk != 'id' @@ -105,7 +105,7 @@ HEADER next if column.name == pk spec = {} spec[:name] = column.name.inspect - + # AR has an optimisation which handles zero-scale decimals as integers. This # code ensures that the dumper still dumps the column as a decimal. spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) } @@ -148,7 +148,7 @@ HEADER tbl.puts " end" tbl.puts - + indexes(table, tbl) tbl.rewind @@ -158,7 +158,7 @@ HEADER stream.puts "# #{e.message}" stream.puts end - + stream end @@ -172,7 +172,7 @@ HEADER value.inspect end end - + def indexes(table, stream) if (indexes = @connection.indexes(table)).any? add_index_statements = indexes.map do |index| -- cgit v1.2.3 From 12b3eca420c2de46d34a363c91b2263d366f4d6c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 6 Aug 2010 11:31:05 -0700 Subject: do not rely on arel class structure --- activerecord/lib/active_record/relation.rb | 4 +++- activerecord/test/cases/method_scoping_test.rb | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index deacced627..30be723291 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -319,7 +319,9 @@ module ActiveRecord def scope_for_create @scope_for_create ||= begin @create_with_value || Hash[ - @where_values.grep(Arel::Predicates::Equality).map { |where| + @where_values.find_all { |w| + w.respond_to?(:operator) && w.operator == :== + }.map { |where| [where.operand1.name, where.operand2.respond_to?(:value) ? where.operand2.value : where.operand2] diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 774b50e2e4..5256ab8d11 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -208,6 +208,13 @@ class MethodScopingTest < ActiveRecord::TestCase end end + def test_scope_for_create_only_uses_equal + table = VerySpecialComment.arel_table + relation = VerySpecialComment.scoped + relation.where_values << table[:id].not_eq(1) + assert_equal({:type => "VerySpecialComment"}, relation.send(:scope_for_create)) + end + def test_scoped_create new_comment = nil -- cgit v1.2.3 From 74dde5951af9f7cbea080087c49f2654e619a0fd Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Fri, 6 Aug 2010 15:24:38 -0400 Subject: updating documentation for method configure_dependency_for_has_many --- activerecord/lib/active_record/associations.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 2556d243f6..73c0900c8b 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1593,15 +1593,12 @@ module ActiveRecord # Creates before_destroy callback methods that nullify, delete or destroy # has_many associated objects, according to the defined :dependent rule. - # If the association is marked as :dependent => :restrict, create a callback - # that prevents deleting entirely. # - # See HasManyAssociation#delete_records. Dependent associations - # delete children, otherwise foreign key is set to NULL. - # See HasManyAssociation#delete_records. Dependent associations - # delete children if the option is set to :destroy or :delete_all, set the - # foreign key to NULL if the option is set to :nullify, and do not touch the - # child records if the option is set to :restrict. + # See HasManyAssociation#delete_records for more information. In general + # - delete children if the option is set to :destroy or :delete_all + # - set the foreign key to NULL if the option is set to :nullify + # - do not delete the parent record if there is any child record if the + # option is set to :restrict # # The +extra_conditions+ parameter, which is not used within the main # Active Record codebase, is meant to allow plugins to define extra -- cgit v1.2.3 From d082a9a2b803c2cf530c6b6bdb8a5c4f19f36982 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 6 Aug 2010 14:52:33 -0700 Subject: sorry AR, my privates are none of your business --- activerecord/test/cases/relations_test.rb | 7 ------- 1 file changed, 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index c9313fe7b6..02c8c60873 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -192,13 +192,6 @@ class RelationTest < ActiveRecord::TestCase end end - def test_respond_to_private_arel_methods - relation = Topic.scoped - - assert ! relation.respond_to?(:matching_attributes) - assert relation.respond_to?(:matching_attributes, true) - end - def test_respond_to_dynamic_finders relation = Topic.scoped -- cgit v1.2.3 From e1596be32363122f12777ce09e654ae58f262eb4 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 6 Aug 2010 15:23:11 -0700 Subject: test to ensure that respond_to? delegates to arel --- activerecord/test/cases/relations_test.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 02c8c60873..ac7b501bb7 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -192,6 +192,25 @@ class RelationTest < ActiveRecord::TestCase end end + def test_respond_to_delegates_to_relation + relation = Topic.scoped + fake_arel = Struct.new(:responds) { + def respond_to? method, access = false + responds << [method, access] + end + }.new [] + + relation.extend(Module.new { attr_accessor :arel }) + relation.arel = fake_arel + + relation.respond_to?(:matching_attributes) + assert_equal [:matching_attributes, false], fake_arel.responds.first + + fake_arel.responds = [] + relation.respond_to?(:matching_attributes, true) + assert_equal [:matching_attributes, true], fake_arel.responds.first + end + def test_respond_to_dynamic_finders relation = Topic.scoped -- cgit v1.2.3 From 334452098e593bb514a9ea2bd74435f2c7b0628e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 6 Aug 2010 16:38:53 -0700 Subject: reduce the number of times current_connection_id is called in with_connection() --- .../connection_adapters/abstract/connection_pool.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 9d0251dda3..02a8f4e214 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -103,8 +103,8 @@ module ActiveRecord # Signal that the thread is finished with the current connection. # #release_connection releases the connection-thread association # and returns the connection to the pool. - def release_connection - conn = @reserved_connections.delete(current_connection_id) + def release_connection(with_id = current_connection_id) + conn = @reserved_connections.delete(with_id) checkin conn if conn end @@ -112,10 +112,11 @@ module ActiveRecord # exists checkout a connection, yield it to the block, and checkin the # connection when finished. def with_connection - fresh_connection = true unless @reserved_connections[current_connection_id] + connection_id = current_connection_id + fresh_connection = true unless @reserved_connections[connection_id] yield connection ensure - release_connection if fresh_connection + release_connection(connection_id) if fresh_connection end # Returns true if a connection has already been opened. -- cgit v1.2.3 From 4f7565c4de1f877547a6b901a8416b18c613acc9 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 9 Aug 2010 15:14:00 +0200 Subject: adds missing requires for Object#try --- activerecord/lib/active_record/relation/calculations.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index f8412bc604..a679c444cf 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/object/try' module ActiveRecord module Calculations -- cgit v1.2.3 From 94cff67bd1125fb98abe8b642ce2dc0ea26bb6dd Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Mon, 9 Aug 2010 12:50:09 -0700 Subject: ignore this test for mysql2 --- activerecord/test/cases/attribute_methods_test.rb | 28 ++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index d20b762853..2c069cd8a5 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -106,21 +106,23 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end - def test_read_attributes_before_type_cast_on_datetime - developer = Developer.find(:first) - # Oracle adapter returns Time before type cast - unless current_adapter?(:OracleAdapter) - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] - else - assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) + unless current_adapter?(:Mysql2Adapter) + def test_read_attributes_before_type_cast_on_datetime + developer = Developer.find(:first) + # Oracle adapter returns Time before type cast + unless current_adapter?(:OracleAdapter) + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] + else + assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db) - developer.created_at = "345643456" - assert_equal developer.created_at_before_type_cast, "345643456" - assert_equal developer.created_at, nil + developer.created_at = "345643456" + assert_equal developer.created_at_before_type_cast, "345643456" + assert_equal developer.created_at, nil - developer.created_at = "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" - assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") + developer.created_at = "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at_before_type_cast, "2010-03-21T21:23:32+01:00" + assert_equal developer.created_at, Time.parse("2010-03-21T21:23:32+01:00") + end end end -- cgit v1.2.3 From 188855501b454d6732fb1fd1d76cc26d6119f2eb Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Mon, 9 Aug 2010 14:52:00 -0700 Subject: move mysql2 adapter into core --- .../connection_adapters/mysql2_adapter.rb | 639 +++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb new file mode 100644 index 0000000000..568759775b --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -0,0 +1,639 @@ +# encoding: utf-8 + +require 'mysql2' unless defined? Mysql2 + +module ActiveRecord + class Base + def self.mysql2_connection(config) + config[:username] = 'root' if config[:username].nil? + client = Mysql2::Client.new(config.symbolize_keys) + options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0] + ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config) + end + end + + module ConnectionAdapters + class Mysql2Column < Column + BOOL = "tinyint(1)" + def extract_default(default) + if sql_type =~ /blob/i || type == :text + if default.blank? + return null ? nil : '' + else + raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}" + end + elsif missing_default_forged_as_empty_string?(default) + nil + else + super + end + end + + def has_default? + return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns + super + end + + # Returns the Ruby class that corresponds to the abstract data type. + def klass + case type + when :integer then Fixnum + when :float then Float + when :decimal then BigDecimal + when :datetime then Time + when :date then Date + when :timestamp then Time + when :time then Time + when :text, :string then String + when :binary then String + when :boolean then Object + end + end + + def type_cast(value) + return nil if value.nil? + case type + when :string then value + when :text then value + when :integer then value.to_i rescue value ? 1 : 0 + when :float then value.to_f # returns self if it's already a Float + when :decimal then self.class.value_to_decimal(value) + when :datetime, :timestamp then value.class == Time ? value : self.class.string_to_time(value) + when :time then value.class == Time ? value : self.class.string_to_dummy_time(value) + when :date then value.class == Date ? value : self.class.string_to_date(value) + when :binary then value + when :boolean then self.class.value_to_boolean(value) + else value + end + end + + def type_cast_code(var_name) + case type + when :string then nil + when :text then nil + when :integer then "#{var_name}.to_i rescue #{var_name} ? 1 : 0" + when :float then "#{var_name}.to_f" + when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})" + when :datetime, :timestamp then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_time(#{var_name})" + when :time then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_dummy_time(#{var_name})" + when :date then "#{var_name}.class == Date ? #{var_name} : #{self.class.name}.string_to_date(#{var_name})" + when :binary then nil + when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})" + else nil + end + end + + private + def simplified_type(field_type) + return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL) + return :string if field_type =~ /enum/i or field_type =~ /set/i + return :integer if field_type =~ /year/i + return :binary if field_type =~ /bit/i + super + end + + def extract_limit(sql_type) + case sql_type + when /blob|text/i + case sql_type + when /tiny/i + 255 + when /medium/i + 16777215 + when /long/i + 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases + else + super # we could return 65535 here, but we leave it undecorated by default + end + when /^bigint/i; 8 + when /^int/i; 4 + when /^mediumint/i; 3 + when /^smallint/i; 2 + when /^tinyint/i; 1 + else + super + end + end + + # MySQL misreports NOT NULL column default when none is given. + # We can't detect this for columns which may have a legitimate '' + # default (string) but we can for others (integer, datetime, boolean, + # and the rest). + # + # Test whether the column has default '', is not null, and is not + # a type allowing default ''. + def missing_default_forged_as_empty_string?(default) + type != :string && !null && default == '' + end + end + + class Mysql2Adapter < AbstractAdapter + cattr_accessor :emulate_booleans + self.emulate_booleans = true + + ADAPTER_NAME = 'Mysql2' + PRIMARY = "PRIMARY" + + LOST_CONNECTION_ERROR_MESSAGES = [ + "Server shutdown in progress", + "Broken pipe", + "Lost connection to MySQL server during query", + "MySQL server has gone away" ] + + QUOTED_TRUE, QUOTED_FALSE = '1', '0' + + NATIVE_DATABASE_TYPES = { + :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", + :string => { :name => "varchar", :limit => 255 }, + :text => { :name => "text" }, + :integer => { :name => "int", :limit => 4 }, + :float => { :name => "float" }, + :decimal => { :name => "decimal" }, + :datetime => { :name => "datetime" }, + :timestamp => { :name => "datetime" }, + :time => { :name => "time" }, + :date => { :name => "date" }, + :binary => { :name => "blob" }, + :boolean => { :name => "tinyint", :limit => 1 } + } + + def initialize(connection, logger, connection_options, config) + super(connection, logger) + @connection_options, @config = connection_options, config + @quoted_column_names, @quoted_table_names = {}, {} + configure_connection + end + + def adapter_name + ADAPTER_NAME + end + + def supports_migrations? + true + end + + def supports_primary_key? + true + end + + def supports_savepoints? + true + end + + def native_database_types + NATIVE_DATABASE_TYPES + end + + # QUOTING ================================================== + + def quote(value, column = nil) + if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary) + s = column.class.string_to_binary(value).unpack("H*")[0] + "x'#{s}'" + elsif value.kind_of?(BigDecimal) + value.to_s("F") + else + super + end + end + + def quote_column_name(name) #:nodoc: + @quoted_column_names[name] ||= "`#{name}`" + end + + def quote_table_name(name) #:nodoc: + @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`') + end + + def quote_string(string) + @connection.escape(string) + end + + def quoted_true + QUOTED_TRUE + end + + def quoted_false + QUOTED_FALSE + end + + # REFERENTIAL INTEGRITY ==================================== + + def disable_referential_integrity(&block) #:nodoc: + old = select_value("SELECT @@FOREIGN_KEY_CHECKS") + + begin + update("SET FOREIGN_KEY_CHECKS = 0") + yield + ensure + update("SET FOREIGN_KEY_CHECKS = #{old}") + end + end + + # CONNECTION MANAGEMENT ==================================== + + def active? + return false unless @connection + @connection.query 'select 1' + true + rescue Mysql2::Error + false + end + + def reconnect! + disconnect! + connect + end + + # this is set to true in 2.3, but we don't want it to be + def requires_reloading? + false + end + + def disconnect! + unless @connection.nil? + @connection.close + @connection = nil + end + end + + def reset! + disconnect! + connect + end + + # DATABASE STATEMENTS ====================================== + + # FIXME: re-enable the following once a "better" query_cache solution is in core + # + # The overrides below perform much better than the originals in AbstractAdapter + # because we're able to take advantage of mysql2's lazy-loading capabilities + # + # # Returns a record hash with the column names as keys and column values + # # as values. + # def select_one(sql, name = nil) + # result = execute(sql, name) + # result.each(:as => :hash) do |r| + # return r + # end + # end + # + # # Returns a single value from a record + # def select_value(sql, name = nil) + # result = execute(sql, name) + # if first = result.first + # first.first + # end + # end + # + # # Returns an array of the values of the first column in a select: + # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] + # def select_values(sql, name = nil) + # execute(sql, name).map { |row| row.first } + # end + + # Returns an array of arrays containing the field values. + # Order is the same as that returned by +columns+. + def select_rows(sql, name = nil) + execute(sql, name).to_a + end + + # Executes the SQL statement in the context of this connection. + def execute(sql, name = nil) + # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been + # made since we established the connection + @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone + if name == :skip_logging + @connection.query(sql) + else + log(sql, name) { @connection.query(sql) } + end + rescue ActiveRecord::StatementInvalid => exception + if exception.message.split(":").first =~ /Packets out of order/ + raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings." + else + raise + end + end + + def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) + super + id_value || @connection.last_id + end + alias :create :insert_sql + + def update_sql(sql, name = nil) + super + @connection.affected_rows + end + + def begin_db_transaction + execute "BEGIN" + rescue Exception + # Transactions aren't supported + end + + def commit_db_transaction + execute "COMMIT" + rescue Exception + # Transactions aren't supported + end + + def rollback_db_transaction + execute "ROLLBACK" + rescue Exception + # Transactions aren't supported + end + + def create_savepoint + execute("SAVEPOINT #{current_savepoint_name}") + end + + def rollback_to_savepoint + execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}") + end + + def release_savepoint + execute("RELEASE SAVEPOINT #{current_savepoint_name}") + end + + def add_limit_offset!(sql, options) + limit, offset = options[:limit], options[:offset] + if limit && offset + sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}" + elsif limit + sql << " LIMIT #{sanitize_limit(limit)}" + elsif offset + sql << " OFFSET #{offset.to_i}" + end + sql + end + + # SCHEMA STATEMENTS ======================================== + + def structure_dump + if supports_views? + sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'" + else + sql = "SHOW TABLES" + end + + select_all(sql).inject("") do |structure, table| + table.delete('Table_type') + structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n" + end + end + + def recreate_database(name, options = {}) + drop_database(name) + create_database(name, options) + end + + # Create a new MySQL database with optional :charset and :collation. + # Charset defaults to utf8. + # + # Example: + # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin' + # create_database 'matt_development' + # create_database 'matt_development', :charset => :big5 + def create_database(name, options = {}) + if options[:collation] + execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`" + else + execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`" + end + end + + def drop_database(name) #:nodoc: + execute "DROP DATABASE IF EXISTS `#{name}`" + end + + def current_database + select_value 'SELECT DATABASE() as db' + end + + # Returns the database character set. + def charset + show_variable 'character_set_database' + end + + # Returns the database collation strategy. + def collation + show_variable 'collation_database' + end + + def tables(name = nil) + tables = [] + execute("SHOW TABLES", name).each do |field| + tables << field.first + end + tables + end + + def drop_table(table_name, options = {}) + super(table_name, options) + end + + def indexes(table_name, name = nil) + indexes = [] + current_index = nil + result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name) + result.each(:symbolize_keys => true, :as => :hash) do |row| + if current_index != row[:Key_name] + next if row[:Key_name] == PRIMARY # skip the primary key + current_index = row[:Key_name] + indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique] == 0, []) + end + + indexes.last.columns << row[:Column_name] + end + indexes + end + + def columns(table_name, name = nil) + sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" + columns = [] + result = execute(sql, :skip_logging) + result.each(:symbolize_keys => true, :as => :hash) { |field| + columns << Mysql2Column.new(field[:Field], field[:Default], field[:Type], field[:Null] == "YES") + } + columns + end + + def create_table(table_name, options = {}) + super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB")) + end + + def rename_table(table_name, new_name) + execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}" + end + + def add_column(table_name, column_name, type, options = {}) + add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + add_column_options!(add_column_sql, options) + add_column_position!(add_column_sql, options) + execute(add_column_sql) + end + + def change_column_default(table_name, column_name, default) + column = column_for(table_name, column_name) + change_column table_name, column_name, column.sql_type, :default => default + end + + def change_column_null(table_name, column_name, null, default = nil) + column = column_for(table_name, column_name) + + unless null || default.nil? + execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") + end + + change_column table_name, column_name, column.sql_type, :null => null + end + + def change_column(table_name, column_name, type, options = {}) + column = column_for(table_name, column_name) + + unless options_include_default?(options) + options[:default] = column.default + end + + unless options.has_key?(:null) + options[:null] = column.null + end + + change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + add_column_options!(change_column_sql, options) + add_column_position!(change_column_sql, options) + execute(change_column_sql) + end + + def rename_column(table_name, column_name, new_column_name) + options = {} + if column = columns(table_name).find { |c| c.name == column_name.to_s } + options[:default] = column.default + options[:null] = column.null + else + raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" + end + current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"] + rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}" + add_column_options!(rename_column_sql, options) + execute(rename_column_sql) + end + + # Maps logical Rails types to MySQL-specific data types. + def type_to_sql(type, limit = nil, precision = nil, scale = nil) + return super unless type.to_s == 'integer' + + case limit + when 1; 'tinyint' + when 2; 'smallint' + when 3; 'mediumint' + when nil, 4, 11; 'int(11)' # compatibility with MySQL default + when 5..8; 'bigint' + else raise(ActiveRecordError, "No integer type has byte size #{limit}") + end + end + + def add_column_position!(sql, options) + if options[:first] + sql << " FIRST" + elsif options[:after] + sql << " AFTER #{quote_column_name(options[:after])}" + end + end + + def show_variable(name) + variables = select_all("SHOW VARIABLES LIKE '#{name}'") + variables.first['Value'] unless variables.empty? + end + + def pk_and_sequence_for(table) + keys = [] + result = execute("describe #{quote_table_name(table)}") + result.each(:symbolize_keys => true, :as => :hash) do |row| + keys << row[:Field] if row[:Key] == "PRI" + end + keys.length == 1 ? [keys.first, nil] : nil + end + + # Returns just a table's primary key + def primary_key(table) + pk_and_sequence = pk_and_sequence_for(table) + pk_and_sequence && pk_and_sequence.first + end + + def case_sensitive_equality_operator + "= BINARY" + end + + def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) + where_sql + end + + protected + def quoted_columns_for_index(column_names, options = {}) + length = options[:length] if options.is_a?(Hash) + + quoted_column_names = case length + when Hash + column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) } + when Fixnum + column_names.map {|name| "#{quote_column_name(name)}(#{length})"} + else + column_names.map {|name| quote_column_name(name) } + end + end + + def translate_exception(exception, message) + return super unless exception.respond_to?(:error_number) + + case exception.error_number + when 1062 + RecordNotUnique.new(message, exception) + when 1452 + InvalidForeignKey.new(message, exception) + else + super + end + end + + private + def connect + @connection = Mysql2::Client.new(@config) + configure_connection + end + + def configure_connection + @connection.query_options.merge!(:as => :array) + encoding = @config[:encoding] + execute("SET NAMES '#{encoding}'", :skip_logging) if encoding + + # By default, MySQL 'where id is null' selects the last inserted id. + # Turn this off. http://dev.rubyonrails.org/ticket/6778 + execute("SET SQL_AUTO_IS_NULL=0", :skip_logging) + end + + # Returns an array of record hashes with the column names as keys and + # column values as values. + def select(sql, name = nil) + execute(sql, name).each(:as => :hash) + end + + def supports_views? + version[0] >= 5 + end + + def version + @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } + end + + def column_for(table_name, column_name) + unless column = columns(table_name).find { |c| c.name == column_name.to_s } + raise "No such column: #{table_name}.#{column_name}" + end + column + end + end + end +end -- cgit v1.2.3 From cb9295c8a1c97f07a7a41af420831794fe9c1b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=BCtke?= Date: Mon, 9 Aug 2010 21:33:02 -0400 Subject: Added test case to verify that transaction callbacks are correctly propagated to class observers --- .../test/cases/transaction_callbacks_test.rb | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index ffc2cd638f..d72c4bf7c4 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -245,3 +245,44 @@ class TransactionCallbacksTest < ActiveRecord::TestCase assert_equal [:after_rollback], @second.history end end + + +class TransactionObserverCallbacksTest < ActiveRecord::TestCase + self.use_transactional_fixtures = false + fixtures :topics + + class TopicWithObserverAttached < ActiveRecord::Base + set_table_name :topics + def history + @history ||= [] + end + end + + class TopicWithObserverAttachedObserver < ActiveRecord::Observer + def after_commit(record) + record.history.push :"TopicWithObserverAttachedObserver#after_commit" + end + + def after_rollback(record) + record.history.push :"TopicWithObserverAttachedObserver#after_rollback" + end + end + + def test_after_commit_called + topic = TopicWithObserverAttached.new + topic.save! + + assert topic.history, [:"TopicWithObserverAttachedObserver#after_commit"] + end + + def test_after_rollback_called + topic = TopicWithObserverAttached.new + + Topic.transaction do + topic.save! + raise ActiveRecord::Rollback + end + + assert topic.history, [:"TopicWithObserverObserver#after_rollback"] + end +end -- cgit v1.2.3