activerecord 2.0.2 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +25 -0
- data/README +0 -0
- data/Rakefile +5 -3
- data/lib/active_record.rb +0 -0
- data/lib/active_record/associations.rb +232 -193
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +14 -14
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +25 -6
- data/lib/active_record/attribute_methods.rb +4 -3
- data/lib/active_record/base.rb +75 -41
- data/lib/active_record/calculations.rb +2 -1
- data/lib/active_record/callbacks.rb +0 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +12 -11
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +13 -6
- data/lib/active_record/fixtures.rb +29 -31
- data/lib/active_record/migration.rb +4 -5
- data/lib/active_record/observer.rb +1 -1
- data/lib/active_record/transactions.rb +3 -5
- data/lib/active_record/validations.rb +5 -3
- data/lib/active_record/version.rb +1 -1
- data/test/abstract_unit.rb +0 -0
- data/test/active_schema_test_mysql.rb +5 -2
- data/test/adapter_test.rb +1 -0
- data/test/all.sh +0 -0
- data/test/associations/callbacks_test.rb +1 -1
- data/test/associations/eager_test.rb +5 -0
- data/test/associations/join_model_test.rb +11 -3
- data/test/associations_test.rb +36 -6
- data/test/attribute_methods_test.rb +0 -0
- data/test/base_test.rb +92 -10
- data/test/calculations_test.rb +9 -1
- data/test/debug.log +358 -0
- data/test/deprecated_finder_test.rb +0 -0
- data/test/fixtures/author.rb +1 -1
- data/test/fixtures/company.rb +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/reply.rb +0 -0
- data/test/fixtures/topic.rb +0 -0
- data/test/fixtures_test.rb +20 -12
- data/test/inheritance_test.rb +0 -0
- data/test/lifecycle_test.rb +0 -0
- data/test/migration_test.rb +46 -0
- data/test/query_cache_test.rb +22 -2
- data/test/readonly_test.rb +0 -0
- data/test/unconnected_test.rb +0 -0
- data/test/validations_test.rb +50 -38
- metadata +8 -4
@@ -89,7 +89,7 @@ module ActiveRecord
|
|
89
89
|
# See also TableDefinition#column for details on how to create columns.
|
90
90
|
def create_table(table_name, options = {})
|
91
91
|
table_definition = TableDefinition.new(self)
|
92
|
-
table_definition.primary_key(options[:primary_key] ||
|
92
|
+
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name)) unless options[:id] == false
|
93
93
|
|
94
94
|
yield table_definition
|
95
95
|
|
@@ -255,26 +255,27 @@ module ActiveRecord
|
|
255
255
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
256
256
|
if native = native_database_types[type]
|
257
257
|
column_type_sql = native.is_a?(Hash) ? native[:name] : native
|
258
|
+
|
258
259
|
if type == :decimal # ignore limit, use precision and scale
|
259
|
-
precision ||= native[:precision]
|
260
260
|
scale ||= native[:scale]
|
261
|
-
|
261
|
+
|
262
|
+
if precision ||= native[:precision]
|
262
263
|
if scale
|
263
264
|
column_type_sql << "(#{precision},#{scale})"
|
264
265
|
else
|
265
266
|
column_type_sql << "(#{precision})"
|
266
267
|
end
|
267
|
-
|
268
|
-
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
|
268
|
+
elsif scale
|
269
|
+
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
|
269
270
|
end
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
column_type_sql << "(#{limit})" if limit
|
274
|
-
column_type_sql
|
271
|
+
|
272
|
+
elsif limit ||= native.is_a?(Hash) && native[:limit]
|
273
|
+
column_type_sql << "(#{limit})"
|
275
274
|
end
|
275
|
+
|
276
|
+
column_type_sql
|
276
277
|
else
|
277
|
-
|
278
|
+
type
|
278
279
|
end
|
279
280
|
end
|
280
281
|
|
File without changes
|
@@ -346,7 +346,7 @@ module ActiveRecord
|
|
346
346
|
# There are some incorrectly compiled postgres drivers out there
|
347
347
|
# that don't define PGconn.escape.
|
348
348
|
self.class.instance_eval do
|
349
|
-
|
349
|
+
remove_method(:quote_string)
|
350
350
|
end
|
351
351
|
end
|
352
352
|
quote_string(s)
|
@@ -386,8 +386,8 @@ module ActiveRecord
|
|
386
386
|
|
387
387
|
# Executes an INSERT query and returns the new record's ID
|
388
388
|
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
389
|
-
table = sql.split(" ", 4)[2]
|
390
|
-
super || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
|
389
|
+
table = sql.split(" ", 4)[2].gsub('"', '')
|
390
|
+
super || pk && last_insert_id(table, sequence_name || default_sequence_name(table, pk))
|
391
391
|
end
|
392
392
|
|
393
393
|
# Queries the database and returns the results in an Array or nil otherwise.
|
@@ -304,13 +304,13 @@ module ActiveRecord
|
|
304
304
|
yield @definition if block_given?
|
305
305
|
end
|
306
306
|
|
307
|
-
copy_table_indexes(from, to)
|
307
|
+
copy_table_indexes(from, to, options[:rename] || {})
|
308
308
|
copy_table_contents(from, to,
|
309
309
|
@definition.columns.map {|column| column.name},
|
310
310
|
options[:rename] || {})
|
311
311
|
end
|
312
312
|
|
313
|
-
def copy_table_indexes(from, to) #:nodoc:
|
313
|
+
def copy_table_indexes(from, to, rename = {}) #:nodoc:
|
314
314
|
indexes(from).each do |index|
|
315
315
|
name = index.name
|
316
316
|
if to == "altered_#{from}"
|
@@ -319,10 +319,17 @@ module ActiveRecord
|
|
319
319
|
name = name[5..-1]
|
320
320
|
end
|
321
321
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
322
|
+
to_column_names = columns(to).map(&:name)
|
323
|
+
columns = index.columns.map {|c| rename[c] || c }.select do |column|
|
324
|
+
to_column_names.include?(column)
|
325
|
+
end
|
326
|
+
|
327
|
+
unless columns.empty?
|
328
|
+
# index name can't be the same
|
329
|
+
opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
|
330
|
+
opts[:unique] = true if index.unique
|
331
|
+
add_index(to, columns, opts)
|
332
|
+
end
|
326
333
|
end
|
327
334
|
end
|
328
335
|
|
@@ -438,7 +438,7 @@ end
|
|
438
438
|
#
|
439
439
|
# Any fixture labeled "DEFAULTS" is safely ignored.
|
440
440
|
|
441
|
-
class Fixtures < YAML::Omap
|
441
|
+
class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
442
442
|
DEFAULT_FILTER_RE = /\.ya?ml$/
|
443
443
|
|
444
444
|
@@all_cached_fixtures = {}
|
@@ -467,7 +467,7 @@ class Fixtures < YAML::Omap
|
|
467
467
|
end
|
468
468
|
|
469
469
|
def self.cache_fixtures(connection, fixtures)
|
470
|
-
cache_for_connection(connection).update(fixtures.index_by
|
470
|
+
cache_for_connection(connection).update(fixtures.index_by { |f| f.table_name })
|
471
471
|
end
|
472
472
|
|
473
473
|
def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true)
|
@@ -644,8 +644,16 @@ class Fixtures < YAML::Omap
|
|
644
644
|
end
|
645
645
|
|
646
646
|
def model_class
|
647
|
-
|
648
|
-
@
|
647
|
+
unless defined?(@model_class)
|
648
|
+
@model_class =
|
649
|
+
if @class_name.nil? || @class_name.is_a?(Class)
|
650
|
+
@class_name
|
651
|
+
else
|
652
|
+
@class_name.constantize rescue nil
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
@model_class
|
649
657
|
end
|
650
658
|
|
651
659
|
def primary_key_name
|
@@ -681,7 +689,7 @@ class Fixtures < YAML::Omap
|
|
681
689
|
Dir.entries(@fixture_path).each do |file|
|
682
690
|
path = File.join(@fixture_path, file)
|
683
691
|
if File.file?(path) and file !~ @file_filter
|
684
|
-
self[file] = Fixture.new(path,
|
692
|
+
self[file] = Fixture.new(path, model_class)
|
685
693
|
end
|
686
694
|
end
|
687
695
|
end
|
@@ -709,20 +717,20 @@ class Fixtures < YAML::Omap
|
|
709
717
|
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
|
710
718
|
end
|
711
719
|
|
712
|
-
self[name] = Fixture.new(data,
|
720
|
+
self[name] = Fixture.new(data, model_class)
|
713
721
|
end
|
714
722
|
end
|
715
723
|
end
|
716
724
|
end
|
717
725
|
|
718
726
|
def read_csv_fixture_files
|
719
|
-
reader = CSV
|
727
|
+
reader = CSV.parse(erb_render(IO.read(csv_file_path)))
|
720
728
|
header = reader.shift
|
721
729
|
i = 0
|
722
730
|
reader.each do |row|
|
723
731
|
data = {}
|
724
732
|
row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
|
725
|
-
self["#{Inflector::underscore(@class_name)}_#{i+=1}"]= Fixture.new(data,
|
733
|
+
self["#{Inflector::underscore(@class_name)}_#{i+=1}"] = Fixture.new(data, model_class)
|
726
734
|
end
|
727
735
|
end
|
728
736
|
|
@@ -758,9 +766,9 @@ class Fixture #:nodoc:
|
|
758
766
|
class FormatError < FixtureError #:nodoc:
|
759
767
|
end
|
760
768
|
|
761
|
-
attr_reader :
|
769
|
+
attr_reader :model_class
|
762
770
|
|
763
|
-
def initialize(fixture,
|
771
|
+
def initialize(fixture, model_class)
|
764
772
|
case fixture
|
765
773
|
when Hash, YAML::Omap
|
766
774
|
@fixture = fixture
|
@@ -770,7 +778,11 @@ class Fixture #:nodoc:
|
|
770
778
|
raise ArgumentError, "Bad fixture argument #{fixture.inspect} during creation of #{class_name} fixture"
|
771
779
|
end
|
772
780
|
|
773
|
-
@
|
781
|
+
@model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil
|
782
|
+
end
|
783
|
+
|
784
|
+
def class_name
|
785
|
+
@model_class.name if @model_class
|
774
786
|
end
|
775
787
|
|
776
788
|
def each
|
@@ -791,21 +803,18 @@ class Fixture #:nodoc:
|
|
791
803
|
end
|
792
804
|
|
793
805
|
def value_list
|
794
|
-
klass = @class_name.constantize rescue nil
|
795
|
-
|
796
806
|
list = @fixture.inject([]) do |fixtures, (key, value)|
|
797
|
-
col =
|
807
|
+
col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(ActiveRecord::Base)
|
798
808
|
fixtures << ActiveRecord::Base.connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
|
799
809
|
end
|
800
810
|
list * ', '
|
801
811
|
end
|
802
812
|
|
803
813
|
def find
|
804
|
-
|
805
|
-
|
806
|
-
klass.find(self[klass.primary_key])
|
814
|
+
if model_class
|
815
|
+
model_class.find(self[model_class.primary_key])
|
807
816
|
else
|
808
|
-
raise FixtureClassNotFound, "
|
817
|
+
raise FixtureClassNotFound, "No class attached to find."
|
809
818
|
end
|
810
819
|
end
|
811
820
|
|
@@ -916,8 +925,6 @@ module Test #:nodoc:
|
|
916
925
|
end
|
917
926
|
|
918
927
|
def setup_with_fixtures
|
919
|
-
return if @fixtures_setup
|
920
|
-
@fixtures_setup = true
|
921
928
|
return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?
|
922
929
|
|
923
930
|
if pre_loaded_fixtures && !use_transactional_fixtures
|
@@ -949,8 +956,6 @@ module Test #:nodoc:
|
|
949
956
|
alias_method :setup, :setup_with_fixtures
|
950
957
|
|
951
958
|
def teardown_with_fixtures
|
952
|
-
return if @fixtures_teardown
|
953
|
-
@fixtures_teardown = true
|
954
959
|
return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank?
|
955
960
|
|
956
961
|
unless use_transactional_fixtures?
|
@@ -967,31 +972,24 @@ module Test #:nodoc:
|
|
967
972
|
alias_method :teardown, :teardown_with_fixtures
|
968
973
|
|
969
974
|
def self.method_added(method)
|
970
|
-
return if @__disable_method_added__
|
971
|
-
@__disable_method_added__ = true
|
972
|
-
|
973
975
|
case method.to_s
|
974
976
|
when 'setup'
|
975
977
|
unless method_defined?(:setup_without_fixtures)
|
976
978
|
alias_method :setup_without_fixtures, :setup
|
977
|
-
define_method(:
|
979
|
+
define_method(:setup) do
|
978
980
|
setup_with_fixtures
|
979
981
|
setup_without_fixtures
|
980
982
|
end
|
981
983
|
end
|
982
|
-
alias_method :setup, :full_setup
|
983
984
|
when 'teardown'
|
984
985
|
unless method_defined?(:teardown_without_fixtures)
|
985
986
|
alias_method :teardown_without_fixtures, :teardown
|
986
|
-
define_method(:
|
987
|
+
define_method(:teardown) do
|
987
988
|
teardown_without_fixtures
|
988
989
|
teardown_with_fixtures
|
989
990
|
end
|
990
991
|
end
|
991
|
-
alias_method :teardown, :full_teardown
|
992
992
|
end
|
993
|
-
|
994
|
-
@__disable_method_added__ = false
|
995
993
|
end
|
996
994
|
|
997
995
|
private
|
@@ -230,7 +230,7 @@ module ActiveRecord
|
|
230
230
|
# recursively. We use @ignore_new_methods as a guard to indicate whether
|
231
231
|
# it is safe for the call to proceed.
|
232
232
|
def singleton_method_added(sym) #:nodoc:
|
233
|
-
return if @ignore_new_methods
|
233
|
+
return if defined?(@ignore_new_methods) && @ignore_new_methods
|
234
234
|
|
235
235
|
begin
|
236
236
|
@ignore_new_methods = true
|
@@ -356,15 +356,14 @@ module ActiveRecord
|
|
356
356
|
|
357
357
|
private
|
358
358
|
def migration_classes
|
359
|
-
|
359
|
+
classes = migration_files.inject([]) do |migrations, migration_file|
|
360
360
|
load(migration_file)
|
361
361
|
version, name = migration_version_and_name(migration_file)
|
362
362
|
assert_unique_migration_version(migrations, version.to_i)
|
363
363
|
migrations << migration_class(name, version.to_i)
|
364
|
-
end
|
364
|
+
end.sort_by(&:version)
|
365
365
|
|
366
|
-
|
367
|
-
down? ? sorted.reverse : sorted
|
366
|
+
down? ? classes.reverse : classes
|
368
367
|
end
|
369
368
|
|
370
369
|
def assert_unique_migration_version(migrations, version)
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
# Transactions are protective blocks where SQL statements are only permanent if they can all succeed as one atomic action.
|
18
|
+
# Transactions are protective blocks where SQL statements are only permanent if they can all succeed as one atomic action.
|
19
19
|
# The classic example is a transfer between two accounts where you can only have a deposit if the withdrawal succeeded and
|
20
20
|
# vice versa. Transactions enforce the integrity of the database and guard the data against program errors or database break-downs.
|
21
21
|
# So basically you should use transaction blocks whenever you have a number of statements that must be executed together or
|
@@ -73,14 +73,12 @@ module ActiveRecord
|
|
73
73
|
# trigger a ROLLBACK when raised, but not be re-raised by the transaction block.
|
74
74
|
module ClassMethods
|
75
75
|
def transaction(&block)
|
76
|
-
previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" }
|
77
76
|
increment_open_transactions
|
78
77
|
|
79
78
|
begin
|
80
79
|
connection.transaction(Thread.current['start_db_transaction'], &block)
|
81
80
|
ensure
|
82
81
|
decrement_open_transactions
|
83
|
-
trap('TERM', previous_handler)
|
84
82
|
end
|
85
83
|
end
|
86
84
|
|
@@ -116,7 +114,7 @@ module ActiveRecord
|
|
116
114
|
def rollback_active_record_state!
|
117
115
|
id_present = has_attribute?(self.class.primary_key)
|
118
116
|
previous_id = id
|
119
|
-
previous_new_record =
|
117
|
+
previous_new_record = new_record?
|
120
118
|
yield
|
121
119
|
rescue Exception
|
122
120
|
@new_record = previous_new_record
|
@@ -125,7 +123,7 @@ module ActiveRecord
|
|
125
123
|
else
|
126
124
|
@attributes.delete(self.class.primary_key)
|
127
125
|
@attributes_cache.delete(self.class.primary_key)
|
128
|
-
end
|
126
|
+
end
|
129
127
|
raise
|
130
128
|
end
|
131
129
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
# Raised by save! and create! when the record is invalid. Use the
|
3
|
-
# record method to retrieve the record which did not validate.
|
3
|
+
# +record+ method to retrieve the record which did not validate.
|
4
4
|
# begin
|
5
5
|
# complex_operation_that_calls_save!_internally
|
6
6
|
# rescue ActiveRecord::RecordInvalid => invalid
|
7
7
|
# puts invalid.record.errors
|
8
8
|
# end
|
9
|
-
class RecordInvalid < ActiveRecordError
|
9
|
+
class RecordInvalid < ActiveRecordError
|
10
10
|
attr_reader :record
|
11
11
|
def initialize(record)
|
12
12
|
@record = record
|
@@ -633,7 +633,7 @@ module ActiveRecord
|
|
633
633
|
#
|
634
634
|
# Because this check is performed outside the database there is still a chance that duplicate values
|
635
635
|
# will be inserted in two parallel transactions. To guarantee against this you should create a
|
636
|
-
# unique index on the field. See +
|
636
|
+
# unique index on the field. See +add_index+ for more information.
|
637
637
|
#
|
638
638
|
# Configuration options:
|
639
639
|
# * <tt>message</tt> - Specifies a custom error message (default is: "has already been taken")
|
@@ -707,6 +707,8 @@ module ActiveRecord
|
|
707
707
|
#
|
708
708
|
# Configuration options:
|
709
709
|
# * <tt>message</tt> - A custom error message (default is: "is invalid")
|
710
|
+
# * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
|
711
|
+
# * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
|
710
712
|
# * <tt>with</tt> - The regular expression used to validate the format with (note: must be supplied!)
|
711
713
|
# * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
|
712
714
|
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
data/test/abstract_unit.rb
CHANGED
File without changes
|
@@ -3,13 +3,16 @@ require 'abstract_unit'
|
|
3
3
|
class ActiveSchemaTest < Test::Unit::TestCase
|
4
4
|
def setup
|
5
5
|
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
|
6
|
-
alias_method :
|
6
|
+
alias_method :execute_without_stub, :execute
|
7
7
|
def execute(sql, name = nil) return sql end
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
def teardown
|
12
|
-
ActiveRecord::ConnectionAdapters::MysqlAdapter.
|
12
|
+
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
|
13
|
+
remove_method :execute
|
14
|
+
alias_method :execute, :execute_without_stub
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
15
18
|
def test_drop_table
|
data/test/adapter_test.rb
CHANGED
data/test/all.sh
CHANGED
File without changes
|
@@ -128,7 +128,7 @@ class AssociationCallbacksTest < Test::Unit::TestCase
|
|
128
128
|
callback_log = ["before_adding<new>", "after_adding<new>"]
|
129
129
|
assert_equal callback_log, project.developers_log
|
130
130
|
assert project.save
|
131
|
-
assert_equal 1, project.developers_with_callbacks.
|
131
|
+
assert_equal 1, project.developers_with_callbacks.size
|
132
132
|
assert_equal callback_log, project.developers_log
|
133
133
|
end
|
134
134
|
|