activerecord 3.1.0.beta1 → 3.1.0.rc1
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 +123 -30
- data/README.rdoc +2 -2
- data/lib/active_record/associations.rb +10 -9
- data/lib/active_record/associations/alias_tracker.rb +2 -2
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +19 -6
- data/lib/active_record/associations/collection_association.rb +61 -52
- data/lib/active_record/associations/collection_proxy.rb +30 -3
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -3
- data/lib/active_record/associations/join_helper.rb +1 -1
- data/lib/active_record/associations/singular_association.rb +13 -12
- data/lib/active_record/associations/through_association.rb +4 -1
- data/lib/active_record/base.rb +95 -46
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -9
- data/lib/active_record/connection_adapters/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -3
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +28 -26
- data/lib/active_record/fixtures.rb +294 -339
- data/lib/active_record/identity_map.rb +34 -8
- data/lib/active_record/locking/optimistic.rb +16 -16
- data/lib/active_record/locking/pessimistic.rb +2 -2
- data/lib/active_record/observer.rb +3 -3
- data/lib/active_record/persistence.rb +1 -1
- data/lib/active_record/railties/controller_runtime.rb +10 -1
- data/lib/active_record/railties/databases.rake +9 -9
- data/lib/active_record/relation/calculations.rb +5 -6
- data/lib/active_record/relation/finder_methods.rb +9 -4
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/session_store.rb +4 -2
- data/lib/active_record/test_case.rb +7 -0
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +1 -1
- metadata +6 -6
@@ -39,6 +39,16 @@ module ActiveRecord
|
|
39
39
|
# :stopdoc:
|
40
40
|
class << self
|
41
41
|
attr_accessor :money_precision
|
42
|
+
def string_to_time(string)
|
43
|
+
return string unless String === string
|
44
|
+
|
45
|
+
case string
|
46
|
+
when 'infinity' then 1.0 / 0.0
|
47
|
+
when '-infinity' then -1.0 / 0.0
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
42
52
|
end
|
43
53
|
# :startdoc:
|
44
54
|
|
@@ -349,6 +359,9 @@ module ActiveRecord
|
|
349
359
|
return super unless column
|
350
360
|
|
351
361
|
case value
|
362
|
+
when Float
|
363
|
+
return super unless value.infinite? && column.type == :datetime
|
364
|
+
"'#{value.to_s.downcase}'"
|
352
365
|
when Numeric
|
353
366
|
return super unless column.sql_type == 'money'
|
354
367
|
# Not truly string input, so doesn't require (or allow) escape string syntax.
|
@@ -688,11 +701,11 @@ module ActiveRecord
|
|
688
701
|
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
|
689
702
|
result = query(<<-SQL, name)
|
690
703
|
SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
|
691
|
-
|
704
|
+
FROM pg_class t
|
705
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
706
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
692
707
|
WHERE i.relkind = 'i'
|
693
|
-
AND d.indexrelid = i.oid
|
694
708
|
AND d.indisprimary = 'f'
|
695
|
-
AND t.oid = d.indrelid
|
696
709
|
AND t.relname = '#{table_name}'
|
697
710
|
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
|
698
711
|
ORDER BY i.relname
|
@@ -807,19 +820,13 @@ module ActiveRecord
|
|
807
820
|
# given table's primary key.
|
808
821
|
result = exec_query(<<-end_sql, 'SCHEMA').rows.first
|
809
822
|
SELECT attr.attname, seq.relname
|
810
|
-
FROM pg_class
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
AND
|
817
|
-
AND attr.attrelid = dep.refobjid
|
818
|
-
AND attr.attnum = dep.refobjsubid
|
819
|
-
AND attr.attrelid = cons.conrelid
|
820
|
-
AND attr.attnum = cons.conkey[1]
|
821
|
-
AND cons.contype = 'p'
|
822
|
-
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
823
|
+
FROM pg_class seq
|
824
|
+
INNER JOIN pg_depend dep ON seq.oid = dep.objid
|
825
|
+
INNER JOIN pg_attribute attr ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
|
826
|
+
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
|
827
|
+
WHERE seq.relkind = 'S'
|
828
|
+
AND cons.contype = 'p'
|
829
|
+
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
823
830
|
end_sql
|
824
831
|
|
825
832
|
# [primary_key, sequence]
|
@@ -832,16 +839,11 @@ module ActiveRecord
|
|
832
839
|
def primary_key(table)
|
833
840
|
row = exec_query(<<-end_sql, 'SCHEMA', [[nil, table]]).rows.first
|
834
841
|
SELECT DISTINCT(attr.attname)
|
835
|
-
FROM pg_attribute
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
AND attr.attnum = dep.refobjsubid
|
841
|
-
AND attr.attrelid = cons.conrelid
|
842
|
-
AND attr.attnum = cons.conkey[1]
|
843
|
-
AND cons.contype = 'p'
|
844
|
-
AND dep.refobjid = $1::regclass
|
842
|
+
FROM pg_attribute attr
|
843
|
+
INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
|
844
|
+
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
|
845
|
+
WHERE cons.contype = 'p'
|
846
|
+
AND dep.refobjid = $1::regclass
|
845
847
|
end_sql
|
846
848
|
|
847
849
|
row && row.first
|
@@ -13,6 +13,7 @@ require 'active_support/core_ext/array/wrap'
|
|
13
13
|
require 'active_support/core_ext/object/blank'
|
14
14
|
require 'active_support/core_ext/logger'
|
15
15
|
require 'active_support/ordered_hash'
|
16
|
+
require 'active_support/core_ext/module/deprecation'
|
16
17
|
|
17
18
|
if defined? ActiveRecord
|
18
19
|
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
|
@@ -28,11 +29,9 @@ class FixturesFileNotFound < StandardError; end
|
|
28
29
|
#
|
29
30
|
# = Fixture formats
|
30
31
|
#
|
31
|
-
# Fixtures come in
|
32
|
+
# Fixtures come in 1 flavor:
|
32
33
|
#
|
33
34
|
# 1. YAML fixtures
|
34
|
-
# 2. CSV fixtures
|
35
|
-
# 3. Single-file fixtures
|
36
35
|
#
|
37
36
|
# == YAML fixtures
|
38
37
|
#
|
@@ -74,56 +73,6 @@ class FixturesFileNotFound < StandardError; end
|
|
74
73
|
# parent_id: 1
|
75
74
|
# title: Child
|
76
75
|
#
|
77
|
-
# == CSV fixtures
|
78
|
-
#
|
79
|
-
# Fixtures can also be kept in the Comma Separated Value (CSV) format. Akin to YAML fixtures, CSV fixtures are stored
|
80
|
-
# in a single file, but instead end with the <tt>.csv</tt> file extension
|
81
|
-
# (Rails example: <tt><your-rails-app>/test/fixtures/web_sites.csv</tt>).
|
82
|
-
#
|
83
|
-
# The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
|
84
|
-
# humans. The first line of the CSV file is a comma-separated list of field names. The rest of the
|
85
|
-
# file is then comprised
|
86
|
-
# of the actual data (1 per line). Here's an example:
|
87
|
-
#
|
88
|
-
# id, name, url
|
89
|
-
# 1, Ruby On Rails, http://www.rubyonrails.org
|
90
|
-
# 2, Google, http://www.google.com
|
91
|
-
#
|
92
|
-
# Should you have a piece of data with a comma character in it, you can place double quotes around that value. If you
|
93
|
-
# need to use a double quote character, you must escape it with another double quote.
|
94
|
-
#
|
95
|
-
# Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats. Instead, the
|
96
|
-
# fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
|
97
|
-
# number to the end. In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
|
98
|
-
# "web_site_2".
|
99
|
-
#
|
100
|
-
# Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
|
101
|
-
# have existing data somewhere already.
|
102
|
-
#
|
103
|
-
# == Single-file fixtures
|
104
|
-
#
|
105
|
-
# This type of fixture was the original format for Active Record that has since been deprecated in
|
106
|
-
# favor of the YAML and CSV formats.
|
107
|
-
# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model)
|
108
|
-
# to the directory appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
|
109
|
-
# configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
|
110
|
-
# like <tt><your-rails-app>/test/fixtures/web_sites/</tt> for the WebSite model).
|
111
|
-
#
|
112
|
-
# Each text file placed in this directory represents a "record". Usually these types of fixtures are named without
|
113
|
-
# extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension.
|
114
|
-
# Here's what the above example might look like:
|
115
|
-
#
|
116
|
-
# web_sites/google
|
117
|
-
# web_sites/yahoo.txt
|
118
|
-
# web_sites/ruby-on-rails
|
119
|
-
#
|
120
|
-
# The file format of a standard fixture is simple. Each line is a property (or column in db speak) and has the syntax
|
121
|
-
# of "name => value". Here's an example of the ruby-on-rails fixture above:
|
122
|
-
#
|
123
|
-
# id => 1
|
124
|
-
# name => Ruby on Rails
|
125
|
-
# url => http://www.rubyonrails.org
|
126
|
-
#
|
127
76
|
# = Using fixtures in testcases
|
128
77
|
#
|
129
78
|
# Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
|
@@ -176,7 +125,7 @@ class FixturesFileNotFound < StandardError; end
|
|
176
125
|
# = Dynamic fixtures with ERB
|
177
126
|
#
|
178
127
|
# Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
|
179
|
-
# mix ERB in with your YAML
|
128
|
+
# mix ERB in with your YAML fixtures to create a bunch of fixtures for load testing, like:
|
180
129
|
#
|
181
130
|
# <% for i in 1..1000 %>
|
182
131
|
# fix_<%= i %>:
|
@@ -423,8 +372,8 @@ class FixturesFileNotFound < StandardError; end
|
|
423
372
|
# to the rescue:
|
424
373
|
#
|
425
374
|
# george_reginald:
|
426
|
-
# monkey_id: <%= Fixtures.identify(:reginald) %>
|
427
|
-
# pirate_id: <%= Fixtures.identify(:george) %>
|
375
|
+
# monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
|
376
|
+
# pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
|
428
377
|
#
|
429
378
|
# == Support for YAML defaults
|
430
379
|
#
|
@@ -444,369 +393,375 @@ class FixturesFileNotFound < StandardError; end
|
|
444
393
|
#
|
445
394
|
# Any fixture labeled "DEFAULTS" is safely ignored.
|
446
395
|
|
447
|
-
|
448
|
-
|
396
|
+
Fixture = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixture', 'ActiveRecord::Fixture')
|
397
|
+
Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixtures', 'ActiveRecord::Fixtures')
|
449
398
|
|
450
|
-
|
399
|
+
module ActiveRecord
|
400
|
+
class Fixtures
|
401
|
+
MAX_ID = 2 ** 30 - 1
|
451
402
|
|
452
|
-
|
453
|
-
ActiveRecord::Base.pluralize_table_names ?
|
454
|
-
table_name.to_s.singularize.camelize :
|
455
|
-
table_name.to_s.camelize
|
456
|
-
end
|
403
|
+
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
|
457
404
|
|
458
|
-
|
459
|
-
|
460
|
-
|
405
|
+
def self.find_table_name(table_name) # :nodoc:
|
406
|
+
ActiveRecord::Base.pluralize_table_names ?
|
407
|
+
table_name.to_s.singularize.camelize :
|
408
|
+
table_name.to_s.camelize
|
409
|
+
end
|
461
410
|
|
462
|
-
|
463
|
-
|
464
|
-
|
411
|
+
def self.reset_cache
|
412
|
+
@@all_cached_fixtures.clear
|
413
|
+
end
|
465
414
|
|
466
|
-
|
467
|
-
|
468
|
-
|
415
|
+
def self.cache_for_connection(connection)
|
416
|
+
@@all_cached_fixtures[connection]
|
417
|
+
end
|
469
418
|
|
470
|
-
|
471
|
-
|
472
|
-
cache_for_connection(connection).values_at(*keys_to_fetch)
|
473
|
-
else
|
474
|
-
cache_for_connection(connection).values
|
419
|
+
def self.fixture_is_cached?(connection, table_name)
|
420
|
+
cache_for_connection(connection)[table_name]
|
475
421
|
end
|
476
|
-
end
|
477
422
|
|
478
|
-
|
479
|
-
|
480
|
-
|
423
|
+
def self.cached_fixtures(connection, keys_to_fetch = nil)
|
424
|
+
if keys_to_fetch
|
425
|
+
cache_for_connection(connection).values_at(*keys_to_fetch)
|
426
|
+
else
|
427
|
+
cache_for_connection(connection).values
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
def self.cache_fixtures(connection, fixtures_map)
|
432
|
+
cache_for_connection(connection).update(fixtures_map)
|
433
|
+
end
|
481
434
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
435
|
+
def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
|
436
|
+
if load_instances
|
437
|
+
fixtures.each do |name, fixture|
|
438
|
+
begin
|
439
|
+
object.instance_variable_set "@#{name}", fixture.find
|
440
|
+
rescue FixtureClassNotFound
|
441
|
+
nil
|
442
|
+
end
|
489
443
|
end
|
490
444
|
end
|
491
445
|
end
|
492
|
-
end
|
493
446
|
|
494
|
-
|
495
|
-
|
496
|
-
|
447
|
+
def self.instantiate_all_loaded_fixtures(object, load_instances = true)
|
448
|
+
all_loaded_fixtures.each do |table_name, fixtures|
|
449
|
+
ActiveRecord::Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
|
450
|
+
end
|
497
451
|
end
|
498
|
-
end
|
499
452
|
|
500
|
-
|
501
|
-
|
453
|
+
cattr_accessor :all_loaded_fixtures
|
454
|
+
self.all_loaded_fixtures = {}
|
502
455
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
456
|
+
def self.create_fixtures(fixtures_directory, table_names, class_names = {})
|
457
|
+
table_names = [table_names].flatten.map { |n| n.to_s }
|
458
|
+
table_names.each { |n|
|
459
|
+
class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
|
460
|
+
}
|
508
461
|
|
509
|
-
|
510
|
-
|
462
|
+
# FIXME: Apparently JK uses this.
|
463
|
+
connection = block_given? ? yield : ActiveRecord::Base.connection
|
511
464
|
|
512
|
-
|
513
|
-
|
514
|
-
|
465
|
+
files_to_read = table_names.reject { |table_name|
|
466
|
+
fixture_is_cached?(connection, table_name)
|
467
|
+
}
|
515
468
|
|
516
|
-
|
517
|
-
|
518
|
-
|
469
|
+
unless files_to_read.empty?
|
470
|
+
connection.disable_referential_integrity do
|
471
|
+
fixtures_map = {}
|
519
472
|
|
520
|
-
|
521
|
-
|
473
|
+
fixture_files = files_to_read.map do |path|
|
474
|
+
table_name = path.tr '/', '_'
|
522
475
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
476
|
+
fixtures_map[path] = ActiveRecord::Fixtures.new(
|
477
|
+
connection,
|
478
|
+
table_name,
|
479
|
+
class_names[table_name.to_sym] || table_name.classify,
|
480
|
+
File.join(fixtures_directory, path))
|
481
|
+
end
|
529
482
|
|
530
|
-
|
483
|
+
all_loaded_fixtures.update(fixtures_map)
|
531
484
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
485
|
+
connection.transaction(:requires_new => true) do
|
486
|
+
fixture_files.each do |ff|
|
487
|
+
conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
|
488
|
+
table_rows = ff.table_rows
|
536
489
|
|
537
|
-
|
538
|
-
|
539
|
-
|
490
|
+
table_rows.keys.each do |table|
|
491
|
+
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
|
492
|
+
end
|
540
493
|
|
541
|
-
|
542
|
-
|
543
|
-
|
494
|
+
table_rows.each do |table_name,rows|
|
495
|
+
rows.each do |row|
|
496
|
+
conn.insert_fixture(row, table_name)
|
497
|
+
end
|
544
498
|
end
|
545
499
|
end
|
546
|
-
end
|
547
500
|
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
501
|
+
# Cap primary key sequences to max(pk).
|
502
|
+
if connection.respond_to?(:reset_pk_sequence!)
|
503
|
+
table_names.each do |table_name|
|
504
|
+
connection.reset_pk_sequence!(table_name.tr('/', '_'))
|
505
|
+
end
|
552
506
|
end
|
553
507
|
end
|
554
|
-
end
|
555
508
|
|
556
|
-
|
509
|
+
cache_fixtures(connection, fixtures_map)
|
510
|
+
end
|
557
511
|
end
|
512
|
+
cached_fixtures(connection, table_names)
|
558
513
|
end
|
559
|
-
cached_fixtures(connection, table_names)
|
560
|
-
end
|
561
|
-
|
562
|
-
# Returns a consistent, platform-independent identifier for +label+.
|
563
|
-
# Identifiers are positive integers less than 2^32.
|
564
|
-
def self.identify(label)
|
565
|
-
Zlib.crc32(label.to_s) % MAX_ID
|
566
|
-
end
|
567
514
|
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
@table_name = table_name
|
573
|
-
@fixture_path = fixture_path
|
574
|
-
@name = table_name # preserve fixture base name
|
575
|
-
@class_name = class_name
|
576
|
-
|
577
|
-
@fixtures = ActiveSupport::OrderedHash.new
|
578
|
-
@table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
|
579
|
-
|
580
|
-
# Should be an AR::Base type class
|
581
|
-
if class_name.is_a?(Class)
|
582
|
-
@table_name = class_name.table_name
|
583
|
-
@connection = class_name.connection
|
584
|
-
@model_class = class_name
|
585
|
-
else
|
586
|
-
@model_class = class_name.constantize rescue nil
|
515
|
+
# Returns a consistent, platform-independent identifier for +label+.
|
516
|
+
# Identifiers are positive integers less than 2^32.
|
517
|
+
def self.identify(label)
|
518
|
+
Zlib.crc32(label.to_s) % MAX_ID
|
587
519
|
end
|
588
520
|
|
589
|
-
|
590
|
-
end
|
521
|
+
attr_reader :table_name, :name, :fixtures, :model_class
|
591
522
|
|
592
|
-
|
593
|
-
|
594
|
-
|
523
|
+
def initialize(connection, table_name, class_name, fixture_path)
|
524
|
+
@connection = connection
|
525
|
+
@table_name = table_name
|
526
|
+
@fixture_path = fixture_path
|
527
|
+
@name = table_name # preserve fixture base name
|
528
|
+
@class_name = class_name
|
595
529
|
|
596
|
-
|
597
|
-
|
598
|
-
end
|
530
|
+
@fixtures = ActiveSupport::OrderedHash.new
|
531
|
+
@table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
|
599
532
|
|
600
|
-
|
601
|
-
|
602
|
-
|
533
|
+
# Should be an AR::Base type class
|
534
|
+
if class_name.is_a?(Class)
|
535
|
+
@table_name = class_name.table_name
|
536
|
+
@connection = class_name.connection
|
537
|
+
@model_class = class_name
|
538
|
+
else
|
539
|
+
@model_class = class_name.constantize rescue nil
|
540
|
+
end
|
603
541
|
|
604
|
-
|
605
|
-
|
606
|
-
end
|
542
|
+
read_fixture_files
|
543
|
+
end
|
607
544
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
|
612
|
-
now = now.to_s(:db)
|
545
|
+
def [](x)
|
546
|
+
fixtures[x]
|
547
|
+
end
|
613
548
|
|
614
|
-
|
615
|
-
|
549
|
+
def []=(k,v)
|
550
|
+
fixtures[k] = v
|
551
|
+
end
|
616
552
|
|
617
|
-
|
618
|
-
|
553
|
+
def each(&block)
|
554
|
+
fixtures.each(&block)
|
555
|
+
end
|
619
556
|
|
620
|
-
|
621
|
-
|
557
|
+
def size
|
558
|
+
fixtures.size
|
559
|
+
end
|
622
560
|
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
end
|
629
|
-
end
|
561
|
+
# Return a hash of rows to be inserted. The key is the table, the value is
|
562
|
+
# a list of rows to insert to that table.
|
563
|
+
def table_rows
|
564
|
+
now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
|
565
|
+
now = now.to_s(:db)
|
630
566
|
|
631
|
-
|
632
|
-
|
633
|
-
row[key] = label if value == "$LABEL"
|
634
|
-
end
|
567
|
+
# allow a standard key to be used for doing defaults in YAML
|
568
|
+
fixtures.delete('DEFAULTS')
|
635
569
|
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
570
|
+
# track any join tables we need to insert later
|
571
|
+
rows = Hash.new { |h,table| h[table] = [] }
|
572
|
+
|
573
|
+
rows[table_name] = fixtures.map do |label, fixture|
|
574
|
+
row = fixture.to_hash
|
640
575
|
|
641
|
-
|
642
|
-
|
643
|
-
if
|
644
|
-
|
645
|
-
|
646
|
-
|
576
|
+
if model_class && model_class < ActiveRecord::Base
|
577
|
+
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
|
578
|
+
if model_class.record_timestamps
|
579
|
+
timestamp_column_names.each do |name|
|
580
|
+
row[name] = now unless row.key?(name)
|
581
|
+
end
|
647
582
|
end
|
648
583
|
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
|
584
|
+
# interpolate the fixture label
|
585
|
+
row.each do |key, value|
|
586
|
+
row[key] = label if value == "$LABEL"
|
587
|
+
end
|
654
588
|
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
end
|
589
|
+
# generate a primary key if necessary
|
590
|
+
if has_primary_key_column? && !row.include?(primary_key_name)
|
591
|
+
row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
|
592
|
+
end
|
660
593
|
|
661
|
-
|
594
|
+
# If STI is used, find the correct subclass for association reflection
|
595
|
+
reflection_class =
|
596
|
+
if row.include?(inheritance_column_name)
|
597
|
+
row[inheritance_column_name].constantize rescue model_class
|
598
|
+
else
|
599
|
+
model_class
|
662
600
|
end
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
601
|
+
|
602
|
+
reflection_class.reflect_on_all_associations.each do |association|
|
603
|
+
case association.macro
|
604
|
+
when :belongs_to
|
605
|
+
# Do not replace association name with association foreign key if they are named the same
|
606
|
+
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
|
607
|
+
|
608
|
+
if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
|
609
|
+
if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
|
610
|
+
# support polymorphic belongs_to as "label (Type)"
|
611
|
+
row[association.foreign_type] = $1
|
612
|
+
end
|
613
|
+
|
614
|
+
row[fk_name] = ActiveRecord::Fixtures.identify(value)
|
615
|
+
end
|
616
|
+
when :has_and_belongs_to_many
|
617
|
+
if (targets = row.delete(association.name.to_s))
|
618
|
+
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
619
|
+
table_name = association.options[:join_table]
|
620
|
+
rows[table_name].concat targets.map { |target|
|
621
|
+
{ association.foreign_key => row[primary_key_name],
|
622
|
+
association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
|
623
|
+
}
|
624
|
+
end
|
671
625
|
end
|
672
626
|
end
|
673
627
|
end
|
674
|
-
end
|
675
|
-
|
676
|
-
row
|
677
|
-
end
|
678
|
-
rows
|
679
|
-
end
|
680
628
|
|
681
|
-
|
682
|
-
|
683
|
-
|
629
|
+
row
|
630
|
+
end
|
631
|
+
rows
|
684
632
|
end
|
685
633
|
|
686
|
-
|
687
|
-
|
688
|
-
model_class
|
689
|
-
|
634
|
+
private
|
635
|
+
def primary_key_name
|
636
|
+
@primary_key_name ||= model_class && model_class.primary_key
|
637
|
+
end
|
690
638
|
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
639
|
+
def has_primary_key_column?
|
640
|
+
@has_primary_key_column ||= primary_key_name &&
|
641
|
+
model_class.columns.any? { |c| c.name == primary_key_name }
|
642
|
+
end
|
695
643
|
|
696
|
-
|
697
|
-
|
698
|
-
|
644
|
+
def timestamp_column_names
|
645
|
+
@timestamp_column_names ||=
|
646
|
+
%w(created_at created_on updated_at updated_on) & column_names
|
647
|
+
end
|
699
648
|
|
700
|
-
|
701
|
-
|
702
|
-
|
649
|
+
def inheritance_column_name
|
650
|
+
@inheritance_column_name ||= model_class && model_class.inheritance_column
|
651
|
+
end
|
703
652
|
|
704
|
-
|
705
|
-
|
706
|
-
read_yaml_fixture_files
|
707
|
-
elsif File.file?(csv_file_path)
|
708
|
-
read_csv_fixture_files
|
709
|
-
else
|
710
|
-
raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
|
653
|
+
def column_names
|
654
|
+
@column_names ||= @connection.columns(@table_name).collect { |c| c.name }
|
711
655
|
end
|
712
|
-
end
|
713
656
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
yaml.value
|
724
|
-
else
|
725
|
-
[yaml]
|
726
|
-
end
|
657
|
+
def read_fixture_files
|
658
|
+
if File.file?(yaml_file_path)
|
659
|
+
read_yaml_fixture_files
|
660
|
+
elsif File.file?(csv_file_path)
|
661
|
+
read_csv_fixture_files
|
662
|
+
else
|
663
|
+
raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
|
664
|
+
end
|
665
|
+
end
|
727
666
|
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
667
|
+
def read_yaml_fixture_files
|
668
|
+
yaml_string = (Dir["#{@fixture_path}/**/*.yml"].select { |f|
|
669
|
+
File.file?(f)
|
670
|
+
} + [yaml_file_path]).map { |file_path| IO.read(file_path) }.join
|
671
|
+
|
672
|
+
if yaml = parse_yaml_string(yaml_string)
|
673
|
+
# If the file is an ordered map, extract its children.
|
674
|
+
yaml_value =
|
675
|
+
if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
|
676
|
+
yaml.value
|
677
|
+
else
|
678
|
+
[yaml]
|
733
679
|
end
|
734
680
|
|
735
|
-
|
681
|
+
yaml_value.each do |fixture|
|
682
|
+
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
|
683
|
+
fixture.each do |name, data|
|
684
|
+
unless data
|
685
|
+
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
|
686
|
+
end
|
687
|
+
|
688
|
+
fixtures[name] = ActiveRecord::Fixture.new(data, model_class)
|
689
|
+
end
|
736
690
|
end
|
737
691
|
end
|
738
692
|
end
|
739
|
-
end
|
740
693
|
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
694
|
+
def read_csv_fixture_files
|
695
|
+
reader = CSV.parse(erb_render(IO.read(csv_file_path)))
|
696
|
+
header = reader.shift
|
697
|
+
i = 0
|
698
|
+
reader.each do |row|
|
699
|
+
data = {}
|
700
|
+
row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
|
701
|
+
fixtures["#{@class_name.to_s.underscore}_#{i+=1}"] = ActiveRecord::Fixture.new(data, model_class)
|
702
|
+
end
|
749
703
|
end
|
750
|
-
|
704
|
+
deprecate :read_csv_fixture_files
|
751
705
|
|
752
|
-
|
753
|
-
|
754
|
-
|
706
|
+
def yaml_file_path
|
707
|
+
"#{@fixture_path}.yml"
|
708
|
+
end
|
755
709
|
|
756
|
-
|
757
|
-
|
758
|
-
|
710
|
+
def csv_file_path
|
711
|
+
@fixture_path + ".csv"
|
712
|
+
end
|
759
713
|
|
760
|
-
|
761
|
-
|
762
|
-
|
714
|
+
def yaml_fixtures_key(path)
|
715
|
+
File.basename(@fixture_path).split(".").first
|
716
|
+
end
|
763
717
|
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
718
|
+
def parse_yaml_string(fixture_content)
|
719
|
+
YAML::load(erb_render(fixture_content))
|
720
|
+
rescue => error
|
721
|
+
raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}"
|
722
|
+
end
|
769
723
|
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
end
|
724
|
+
def erb_render(fixture_content)
|
725
|
+
ERB.new(fixture_content).result
|
726
|
+
end
|
727
|
+
end
|
774
728
|
|
775
|
-
class Fixture #:nodoc:
|
776
|
-
|
729
|
+
class Fixture #:nodoc:
|
730
|
+
include Enumerable
|
777
731
|
|
778
|
-
|
779
|
-
|
732
|
+
class FixtureError < StandardError #:nodoc:
|
733
|
+
end
|
780
734
|
|
781
|
-
|
782
|
-
|
735
|
+
class FormatError < FixtureError #:nodoc:
|
736
|
+
end
|
783
737
|
|
784
|
-
|
738
|
+
attr_reader :model_class, :fixture
|
785
739
|
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
740
|
+
def initialize(fixture, model_class)
|
741
|
+
@fixture = fixture
|
742
|
+
@model_class = model_class
|
743
|
+
end
|
790
744
|
|
791
|
-
|
792
|
-
|
793
|
-
|
745
|
+
def class_name
|
746
|
+
model_class.name if model_class
|
747
|
+
end
|
794
748
|
|
795
|
-
|
796
|
-
|
797
|
-
|
749
|
+
def each
|
750
|
+
fixture.each { |item| yield item }
|
751
|
+
end
|
798
752
|
|
799
|
-
|
800
|
-
|
801
|
-
|
753
|
+
def [](key)
|
754
|
+
fixture[key]
|
755
|
+
end
|
802
756
|
|
803
|
-
|
757
|
+
alias :to_hash :fixture
|
804
758
|
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
759
|
+
def find
|
760
|
+
if model_class
|
761
|
+
model_class.find(fixture[model_class.primary_key])
|
762
|
+
else
|
763
|
+
raise FixtureClassNotFound, "No class attached to find."
|
764
|
+
end
|
810
765
|
end
|
811
766
|
end
|
812
767
|
end
|
@@ -832,7 +787,7 @@ module ActiveRecord
|
|
832
787
|
self.pre_loaded_fixtures = false
|
833
788
|
|
834
789
|
self.fixture_class_names = Hash.new do |h, table_name|
|
835
|
-
h[table_name] = Fixtures.find_table_name(table_name)
|
790
|
+
h[table_name] = ActiveRecord::Fixtures.find_table_name(table_name)
|
836
791
|
end
|
837
792
|
end
|
838
793
|
|
@@ -944,7 +899,7 @@ module ActiveRecord
|
|
944
899
|
ActiveRecord::Base.connection.begin_db_transaction
|
945
900
|
# Load fixtures for every test.
|
946
901
|
else
|
947
|
-
Fixtures.reset_cache
|
902
|
+
ActiveRecord::Fixtures.reset_cache
|
948
903
|
@@already_loaded_fixtures[self.class] = nil
|
949
904
|
@loaded_fixtures = load_fixtures
|
950
905
|
end
|
@@ -957,7 +912,7 @@ module ActiveRecord
|
|
957
912
|
return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
|
958
913
|
|
959
914
|
unless run_in_transaction?
|
960
|
-
Fixtures.reset_cache
|
915
|
+
ActiveRecord::Fixtures.reset_cache
|
961
916
|
end
|
962
917
|
|
963
918
|
# Rollback changes if a transaction is active.
|
@@ -970,7 +925,7 @@ module ActiveRecord
|
|
970
925
|
|
971
926
|
private
|
972
927
|
def load_fixtures
|
973
|
-
fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
|
928
|
+
fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
|
974
929
|
Hash[fixtures.map { |f| [f.name, f] }]
|
975
930
|
end
|
976
931
|
|
@@ -979,16 +934,16 @@ module ActiveRecord
|
|
979
934
|
|
980
935
|
def instantiate_fixtures
|
981
936
|
if pre_loaded_fixtures
|
982
|
-
raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
|
937
|
+
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty?
|
983
938
|
unless @@required_fixture_classes
|
984
|
-
self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
|
939
|
+
self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
|
985
940
|
@@required_fixture_classes = true
|
986
941
|
end
|
987
|
-
Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
|
942
|
+
ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
|
988
943
|
else
|
989
944
|
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
|
990
945
|
@loaded_fixtures.each do |fixture_name, fixtures|
|
991
|
-
Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
|
946
|
+
ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
|
992
947
|
end
|
993
948
|
end
|
994
949
|
end
|