activerecord 4.0.13 → 4.1.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +745 -2700
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record.rb +2 -6
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +0 -4
- data/lib/active_record/associations.rb +87 -43
- data/lib/active_record/associations/alias_tracker.rb +1 -3
- data/lib/active_record/associations/association.rb +8 -16
- data/lib/active_record/associations/association_scope.rb +5 -16
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +78 -54
- data/lib/active_record/associations/builder/belongs_to.rb +91 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
- data/lib/active_record/associations/builder/has_many.rb +2 -2
- data/lib/active_record/associations/builder/has_one.rb +5 -7
- data/lib/active_record/associations/builder/singular_association.rb +6 -7
- data/lib/active_record/associations/collection_association.rb +68 -105
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/has_many_association.rb +11 -9
- data/lib/active_record/associations/has_many_through_association.rb +16 -12
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +204 -165
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
- data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
- data/lib/active_record/associations/join_helper.rb +2 -11
- data/lib/active_record/associations/preloader.rb +89 -34
- data/lib/active_record/associations/preloader/association.rb +43 -25
- data/lib/active_record/associations/preloader/collection_association.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +58 -26
- data/lib/active_record/associations/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +5 -2
- data/lib/active_record/attribute_methods.rb +45 -40
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +8 -22
- data/lib/active_record/attribute_methods/primary_key.rb +1 -7
- data/lib/active_record/attribute_methods/read.rb +55 -28
- data/lib/active_record/attribute_methods/serialization.rb +12 -33
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
- data/lib/active_record/attribute_methods/write.rb +37 -12
- data/lib/active_record/autosave_association.rb +207 -207
- data/lib/active_record/base.rb +5 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
- data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
- data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
- data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +22 -43
- data/lib/active_record/counter_cache.rb +7 -7
- data/lib/active_record/enum.rb +100 -0
- data/lib/active_record/errors.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +171 -74
- data/lib/active_record/inheritance.rb +16 -22
- data/lib/active_record/integration.rb +52 -1
- data/lib/active_record/locking/optimistic.rb +7 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -12
- data/lib/active_record/migration.rb +62 -46
- data/lib/active_record/migration/command_recorder.rb +7 -13
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +10 -8
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +3 -3
- data/lib/active_record/persistence.rb +16 -34
- data/lib/active_record/querying.rb +14 -12
- data/lib/active_record/railtie.rb +0 -50
- data/lib/active_record/railties/databases.rake +12 -15
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +189 -75
- data/lib/active_record/relation.rb +69 -94
- data/lib/active_record/relation/batches.rb +57 -23
- data/lib/active_record/relation/calculations.rb +36 -43
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +107 -62
- data/lib/active_record/relation/merger.rb +7 -20
- data/lib/active_record/relation/predicate_builder.rb +57 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +110 -98
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +6 -8
- data/lib/active_record/schema_dumper.rb +16 -5
- data/lib/active_record/schema_migration.rb +24 -25
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +8 -29
- data/lib/active_record/store.rb +56 -28
- data/lib/active_record/tasks/database_tasks.rb +8 -4
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +8 -10
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -6
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +2 -8
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- metadata +32 -45
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
- data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
- data/lib/active_record/test_case.rb +0 -102
@@ -0,0 +1,100 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# Declare an enum attribute where the values map to integers in the database, but can be queried by name. Example:
|
3
|
+
#
|
4
|
+
# class Conversation < ActiveRecord::Base
|
5
|
+
# enum status: [ :active, :archived ]
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# # conversation.update! status: 0
|
9
|
+
# conversation.active!
|
10
|
+
# conversation.active? # => true
|
11
|
+
# conversation.status # => "active"
|
12
|
+
#
|
13
|
+
# # conversation.update! status: 1
|
14
|
+
# conversation.archived!
|
15
|
+
# conversation.archived? # => true
|
16
|
+
# conversation.status # => "archived"
|
17
|
+
#
|
18
|
+
# # conversation.update! status: 1
|
19
|
+
# conversation.status = "archived"
|
20
|
+
#
|
21
|
+
# You can set the default value from the database declaration, like:
|
22
|
+
#
|
23
|
+
# create_table :conversations do |t|
|
24
|
+
# t.column :status, :integer, default: 0
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# Good practice is to let the first declared status be the default.
|
28
|
+
#
|
29
|
+
# Finally, it's also possible to explicitly map the relation between attribute and
|
30
|
+
# database integer with a +Hash+:
|
31
|
+
#
|
32
|
+
# class Conversation < ActiveRecord::Base
|
33
|
+
# enum status: { active: 0, archived: 1 }
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Note that when an +Array+ is used, the implicit mapping from the values to database
|
37
|
+
# integers is derived from the order the values appear in the array. In the example,
|
38
|
+
# <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
|
39
|
+
# is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
|
40
|
+
# database.
|
41
|
+
#
|
42
|
+
# Therefore, once a value is added to the enum array, its position in the array must
|
43
|
+
# be maintained, and new values should only be added to the end of the array. To
|
44
|
+
# remove unused values, the explicit +Hash+ syntax should be used.
|
45
|
+
#
|
46
|
+
# In rare circumstances you might need to access the mapping directly.
|
47
|
+
# The mappings are exposed through a constant with the attributes name:
|
48
|
+
#
|
49
|
+
# Conversation::STATUS # => { "active" => 0, "archived" => 1 }
|
50
|
+
#
|
51
|
+
# Use that constant when you need to know the ordinal value of an enum:
|
52
|
+
#
|
53
|
+
# Conversation.where("status <> ?", Conversation::STATUS[:archived])
|
54
|
+
module Enum
|
55
|
+
def enum(definitions)
|
56
|
+
klass = self
|
57
|
+
definitions.each do |name, values|
|
58
|
+
# DIRECTION = { }
|
59
|
+
enum_values = _enum_methods_module.const_set name.to_s.upcase, ActiveSupport::HashWithIndifferentAccess.new
|
60
|
+
name = name.to_sym
|
61
|
+
|
62
|
+
_enum_methods_module.module_eval do
|
63
|
+
# def direction=(value) self[:direction] = DIRECTION[value] end
|
64
|
+
define_method("#{name}=") { |value|
|
65
|
+
unless enum_values.has_key?(value)
|
66
|
+
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
67
|
+
end
|
68
|
+
self[name] = enum_values[value]
|
69
|
+
}
|
70
|
+
|
71
|
+
# def direction() DIRECTION.key self[:direction] end
|
72
|
+
define_method(name) { enum_values.key self[name] }
|
73
|
+
|
74
|
+
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
75
|
+
pairs.each do |value, i|
|
76
|
+
enum_values[value] = i
|
77
|
+
|
78
|
+
# scope :incoming, -> { where direction: 0 }
|
79
|
+
klass.scope value, -> { klass.where name => i }
|
80
|
+
|
81
|
+
# def incoming?() direction == 0 end
|
82
|
+
define_method("#{value}?") { self[name] == i }
|
83
|
+
|
84
|
+
# def incoming! update! direction: :incoming end
|
85
|
+
define_method("#{value}!") { update! name => value }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def _enum_methods_module
|
93
|
+
@_enum_methods_module ||= begin
|
94
|
+
mod = Module.new
|
95
|
+
include mod
|
96
|
+
mod
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/active_record/errors.rb
CHANGED
@@ -69,10 +69,6 @@ module ActiveRecord
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
# Raised when SQL statement is invalid and the application gets a blank result.
|
73
|
-
class ThrowResult < ActiveRecordError
|
74
|
-
end
|
75
|
-
|
76
72
|
# Defunct wrapper class kept for compatibility.
|
77
73
|
# +StatementInvalid+ wraps the original exception now.
|
78
74
|
class WrappedDatabaseException < StatementInvalid
|
@@ -159,6 +155,15 @@ module ActiveRecord
|
|
159
155
|
|
160
156
|
# Raised when unknown attributes are supplied via mass assignment.
|
161
157
|
class UnknownAttributeError < NoMethodError
|
158
|
+
|
159
|
+
attr_reader :record, :attribute
|
160
|
+
|
161
|
+
def initialize(record, attribute)
|
162
|
+
@record = record
|
163
|
+
@attribute = attribute.to_s
|
164
|
+
super("unknown attribute: #{attribute}")
|
165
|
+
end
|
166
|
+
|
162
167
|
end
|
163
168
|
|
164
169
|
# Raised when an error occurred while doing a mass assignment to an attribute through the
|
@@ -183,7 +188,7 @@ module ActiveRecord
|
|
183
188
|
end
|
184
189
|
end
|
185
190
|
|
186
|
-
# Raised when a primary key is needed, but
|
191
|
+
# Raised when a primary key is needed, but not specified in the schema or model.
|
187
192
|
class UnknownPrimaryKey < ActiveRecordError
|
188
193
|
attr_reader :model
|
189
194
|
|
@@ -119,6 +119,23 @@ module ActiveRecord
|
|
119
119
|
# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
|
120
120
|
# in fixtures are to be considered a code smell.
|
121
121
|
#
|
122
|
+
# Helper methods defined in a fixture will not be available in other fixtures, to prevent against
|
123
|
+
# unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
|
124
|
+
# that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
|
125
|
+
#
|
126
|
+
# - define a helper method in `test_helper.rb`
|
127
|
+
# class FixtureFileHelpers
|
128
|
+
# def file_sha(path)
|
129
|
+
# Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
# ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers
|
133
|
+
#
|
134
|
+
# - use the helper method in a fixture
|
135
|
+
# photo:
|
136
|
+
# name: kitten.png
|
137
|
+
# sha: <%= file_sha 'files/kitten.png' %>
|
138
|
+
#
|
122
139
|
# = Transactional Fixtures
|
123
140
|
#
|
124
141
|
# Test cases can use begin+rollback to isolate their changes to the database instead of having to
|
@@ -379,22 +396,16 @@ module ActiveRecord
|
|
379
396
|
|
380
397
|
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
|
381
398
|
|
382
|
-
def self.
|
383
|
-
|
384
|
-
"ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases. Use ActiveRecord::Fixtures.default_fixture_model_name instead.")
|
385
|
-
default_fixture_model_name(fixture_set_name)
|
386
|
-
end
|
387
|
-
|
388
|
-
def self.default_fixture_model_name(fixture_set_name) # :nodoc:
|
389
|
-
ActiveRecord::Base.pluralize_table_names ?
|
399
|
+
def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
|
400
|
+
config.pluralize_table_names ?
|
390
401
|
fixture_set_name.singularize.camelize :
|
391
402
|
fixture_set_name.camelize
|
392
403
|
end
|
393
404
|
|
394
|
-
def self.default_fixture_table_name(fixture_set_name) # :nodoc:
|
395
|
-
"#{
|
405
|
+
def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
|
406
|
+
"#{ config.table_name_prefix }"\
|
396
407
|
"#{ fixture_set_name.tr('/', '_') }"\
|
397
|
-
"#{
|
408
|
+
"#{ config.table_name_suffix }".to_sym
|
398
409
|
end
|
399
410
|
|
400
411
|
def self.reset_cache
|
@@ -442,9 +453,47 @@ module ActiveRecord
|
|
442
453
|
cattr_accessor :all_loaded_fixtures
|
443
454
|
self.all_loaded_fixtures = {}
|
444
455
|
|
445
|
-
|
456
|
+
class ClassCache
|
457
|
+
def initialize(class_names, config)
|
458
|
+
@class_names = class_names.stringify_keys
|
459
|
+
@config = config
|
460
|
+
|
461
|
+
# Remove string values that aren't constants or subclasses of AR
|
462
|
+
@class_names.delete_if { |k,klass|
|
463
|
+
unless klass.is_a? Class
|
464
|
+
klass = klass.safe_constantize
|
465
|
+
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
|
466
|
+
end
|
467
|
+
!insert_class(@class_names, k, klass)
|
468
|
+
}
|
469
|
+
end
|
470
|
+
|
471
|
+
def [](fs_name)
|
472
|
+
@class_names.fetch(fs_name) {
|
473
|
+
klass = default_fixture_model(fs_name, @config).safe_constantize
|
474
|
+
insert_class(@class_names, fs_name, klass)
|
475
|
+
}
|
476
|
+
end
|
477
|
+
|
478
|
+
private
|
479
|
+
|
480
|
+
def insert_class(class_names, name, klass)
|
481
|
+
# We only want to deal with AR objects.
|
482
|
+
if klass && klass < ActiveRecord::Base
|
483
|
+
class_names[name] = klass
|
484
|
+
else
|
485
|
+
class_names[name] = nil
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
def default_fixture_model(fs_name, config)
|
490
|
+
ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
|
446
495
|
fixture_set_names = Array(fixture_set_names).map(&:to_s)
|
447
|
-
class_names = class_names
|
496
|
+
class_names = ClassCache.new class_names, config
|
448
497
|
|
449
498
|
# FIXME: Apparently JK uses this.
|
450
499
|
connection = block_given? ? yield : ActiveRecord::Base.connection
|
@@ -458,10 +507,12 @@ module ActiveRecord
|
|
458
507
|
fixtures_map = {}
|
459
508
|
|
460
509
|
fixture_sets = files_to_read.map do |fs_name|
|
510
|
+
klass = class_names[fs_name]
|
511
|
+
conn = klass ? klass.connection : connection
|
461
512
|
fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
|
462
|
-
|
513
|
+
conn,
|
463
514
|
fs_name,
|
464
|
-
|
515
|
+
klass,
|
465
516
|
::File.join(fixtures_directory, fs_name))
|
466
517
|
end
|
467
518
|
|
@@ -470,16 +521,8 @@ module ActiveRecord
|
|
470
521
|
connection.transaction(:requires_new => true) do
|
471
522
|
fixture_sets.each do |fs|
|
472
523
|
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
|
473
|
-
|
474
|
-
|
475
|
-
table_rows.keys.each do |table|
|
476
|
-
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
|
477
|
-
end
|
478
|
-
|
479
|
-
table_rows.each do |fixture_set_name, rows|
|
480
|
-
rows.each do |row|
|
481
|
-
conn.insert_fixture(row, fixture_set_name)
|
482
|
-
end
|
524
|
+
fs.fixture_sql(conn).each do |stmt|
|
525
|
+
conn.execute stmt
|
483
526
|
end
|
484
527
|
end
|
485
528
|
|
@@ -503,27 +546,36 @@ module ActiveRecord
|
|
503
546
|
Zlib.crc32(label.to_s) % MAX_ID
|
504
547
|
end
|
505
548
|
|
506
|
-
|
549
|
+
# Superclass for the evaluation contexts used by ERB fixtures.
|
550
|
+
def self.context_class
|
551
|
+
@context_class ||= Class.new
|
552
|
+
end
|
553
|
+
|
554
|
+
attr_reader :table_name, :name, :fixtures, :model_class, :config
|
507
555
|
|
508
|
-
def initialize(connection, name, class_name, path)
|
509
|
-
@fixtures = {} # Ordered hash
|
556
|
+
def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
|
510
557
|
@name = name
|
511
558
|
@path = path
|
559
|
+
@config = config
|
560
|
+
@model_class = nil
|
561
|
+
|
562
|
+
if class_name.is_a?(String)
|
563
|
+
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
|
564
|
+
end
|
512
565
|
|
513
566
|
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
|
514
567
|
@model_class = class_name
|
515
568
|
else
|
516
|
-
@model_class = class_name.
|
569
|
+
@model_class = class_name.safe_constantize if class_name
|
517
570
|
end
|
518
571
|
|
519
|
-
@connection =
|
520
|
-
model_class.connection : connection )
|
572
|
+
@connection = connection
|
521
573
|
|
522
574
|
@table_name = ( model_class.respond_to?(:table_name) ?
|
523
575
|
model_class.table_name :
|
524
|
-
self.class.default_fixture_table_name(name) )
|
576
|
+
self.class.default_fixture_table_name(name, config) )
|
525
577
|
|
526
|
-
read_fixture_files
|
578
|
+
@fixtures = read_fixture_files path, @model_class
|
527
579
|
end
|
528
580
|
|
529
581
|
def [](x)
|
@@ -542,10 +594,20 @@ module ActiveRecord
|
|
542
594
|
fixtures.size
|
543
595
|
end
|
544
596
|
|
597
|
+
def fixture_sql(conn)
|
598
|
+
table_rows = self.table_rows
|
599
|
+
|
600
|
+
table_rows.keys.map { |table|
|
601
|
+
"DELETE FROM #{conn.quote_table_name(table)}"
|
602
|
+
}.concat table_rows.flat_map { |fixture_set_name, rows|
|
603
|
+
rows.map { |row| conn.fixture_sql(row, fixture_set_name) }
|
604
|
+
}
|
605
|
+
end
|
606
|
+
|
545
607
|
# Return a hash of rows to be inserted. The key is the table, the value is
|
546
608
|
# a list of rows to insert to that table.
|
547
609
|
def table_rows
|
548
|
-
now =
|
610
|
+
now = config.default_timezone == :utc ? Time.now.utc : Time.now
|
549
611
|
now = now.to_s(:db)
|
550
612
|
|
551
613
|
# allow a standard key to be used for doing defaults in YAML
|
@@ -557,7 +619,7 @@ module ActiveRecord
|
|
557
619
|
rows[table_name] = fixtures.map do |label, fixture|
|
558
620
|
row = fixture.to_hash
|
559
621
|
|
560
|
-
if model_class
|
622
|
+
if model_class
|
561
623
|
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
|
562
624
|
if model_class.record_timestamps
|
563
625
|
timestamp_column_names.each do |c_name|
|
@@ -567,7 +629,7 @@ module ActiveRecord
|
|
567
629
|
|
568
630
|
# interpolate the fixture label
|
569
631
|
row.each do |key, value|
|
570
|
-
row[key] = label if
|
632
|
+
row[key] = label if "$LABEL" == value
|
571
633
|
end
|
572
634
|
|
573
635
|
# generate a primary key if necessary
|
@@ -597,14 +659,9 @@ module ActiveRecord
|
|
597
659
|
|
598
660
|
row[fk_name] = ActiveRecord::FixtureSet.identify(value)
|
599
661
|
end
|
600
|
-
when :
|
601
|
-
if
|
602
|
-
|
603
|
-
table_name = association.join_table
|
604
|
-
rows[table_name].concat targets.map { |target|
|
605
|
-
{ association.foreign_key => row[primary_key_name],
|
606
|
-
association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
|
607
|
-
}
|
662
|
+
when :has_many
|
663
|
+
if association.options[:through]
|
664
|
+
add_join_records(rows, row, HasManyThroughProxy.new(association))
|
608
665
|
end
|
609
666
|
end
|
610
667
|
end
|
@@ -615,11 +672,50 @@ module ActiveRecord
|
|
615
672
|
rows
|
616
673
|
end
|
617
674
|
|
675
|
+
class ReflectionProxy # :nodoc:
|
676
|
+
def initialize(association)
|
677
|
+
@association = association
|
678
|
+
end
|
679
|
+
|
680
|
+
def join_table
|
681
|
+
@association.join_table
|
682
|
+
end
|
683
|
+
|
684
|
+
def name
|
685
|
+
@association.name
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
class HasManyThroughProxy < ReflectionProxy # :nodoc:
|
690
|
+
def rhs_key
|
691
|
+
@association.foreign_key
|
692
|
+
end
|
693
|
+
|
694
|
+
def lhs_key
|
695
|
+
@association.through_reflection.foreign_key
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
618
699
|
private
|
619
700
|
def primary_key_name
|
620
701
|
@primary_key_name ||= model_class && model_class.primary_key
|
621
702
|
end
|
622
703
|
|
704
|
+
def add_join_records(rows, row, association)
|
705
|
+
# This is the case when the join table has no fixtures file
|
706
|
+
if (targets = row.delete(association.name.to_s))
|
707
|
+
table_name = association.join_table
|
708
|
+
lhs_key = association.lhs_key
|
709
|
+
rhs_key = association.rhs_key
|
710
|
+
|
711
|
+
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
712
|
+
rows[table_name].concat targets.map { |target|
|
713
|
+
{ lhs_key => row[primary_key_name],
|
714
|
+
rhs_key => ActiveRecord::FixtureSet.identify(target) }
|
715
|
+
}
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
623
719
|
def has_primary_key_column?
|
624
720
|
@has_primary_key_column ||= primary_key_name &&
|
625
721
|
model_class.columns.any? { |c| c.name == primary_key_name }
|
@@ -638,12 +734,12 @@ module ActiveRecord
|
|
638
734
|
@column_names ||= @connection.columns(@table_name).collect { |c| c.name }
|
639
735
|
end
|
640
736
|
|
641
|
-
def read_fixture_files
|
642
|
-
yaml_files = Dir["#{
|
737
|
+
def read_fixture_files(path, model_class)
|
738
|
+
yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
|
643
739
|
::File.file?(f)
|
644
|
-
} + [yaml_file_path]
|
740
|
+
} + [yaml_file_path(path)]
|
645
741
|
|
646
|
-
yaml_files.
|
742
|
+
yaml_files.each_with_object({}) do |file, fixtures|
|
647
743
|
FixtureSet::File.open(file) do |fh|
|
648
744
|
fh.each do |fixture_name, row|
|
649
745
|
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
|
@@ -652,8 +748,8 @@ module ActiveRecord
|
|
652
748
|
end
|
653
749
|
end
|
654
750
|
|
655
|
-
def yaml_file_path
|
656
|
-
"#{
|
751
|
+
def yaml_file_path(path)
|
752
|
+
"#{path}.yml"
|
657
753
|
end
|
658
754
|
|
659
755
|
end
|
@@ -696,7 +792,7 @@ module ActiveRecord
|
|
696
792
|
|
697
793
|
def find
|
698
794
|
if model_class
|
699
|
-
model_class.
|
795
|
+
model_class.find(fixture[model_class.primary_key])
|
700
796
|
else
|
701
797
|
raise FixtureClassNotFound, "No class attached to find."
|
702
798
|
end
|
@@ -725,14 +821,16 @@ module ActiveRecord
|
|
725
821
|
class_attribute :use_transactional_fixtures
|
726
822
|
class_attribute :use_instantiated_fixtures # true, false, or :no_instances
|
727
823
|
class_attribute :pre_loaded_fixtures
|
824
|
+
class_attribute :config
|
728
825
|
|
729
826
|
self.fixture_table_names = []
|
730
827
|
self.use_transactional_fixtures = true
|
731
828
|
self.use_instantiated_fixtures = false
|
732
829
|
self.pre_loaded_fixtures = false
|
830
|
+
self.config = ActiveRecord::Base
|
733
831
|
|
734
832
|
self.fixture_class_names = Hash.new do |h, fixture_set_name|
|
735
|
-
h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
|
833
|
+
h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name, self.config)
|
736
834
|
end
|
737
835
|
end
|
738
836
|
|
@@ -745,13 +843,6 @@ module ActiveRecord
|
|
745
843
|
# 'namespaced/fixture' => Another::Model
|
746
844
|
#
|
747
845
|
# The keys must be the fixture names, that coincide with the short paths to the fixture files.
|
748
|
-
#--
|
749
|
-
# It is also possible to pass the class name instead of the class:
|
750
|
-
# set_fixture_class 'some_fixture' => 'SomeModel'
|
751
|
-
# I think this option is redundant, i propose to deprecate it.
|
752
|
-
# Isn't it easier to always pass the class itself?
|
753
|
-
# (2011-12-20 alexeymuranov)
|
754
|
-
#++
|
755
846
|
def set_fixture_class(class_names = {})
|
756
847
|
self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
|
757
848
|
end
|
@@ -765,7 +856,7 @@ module ActiveRecord
|
|
765
856
|
end
|
766
857
|
|
767
858
|
self.fixture_table_names |= fixture_set_names
|
768
|
-
require_fixture_classes(fixture_set_names)
|
859
|
+
require_fixture_classes(fixture_set_names, self.config)
|
769
860
|
setup_fixture_accessors(fixture_set_names)
|
770
861
|
end
|
771
862
|
|
@@ -780,7 +871,7 @@ module ActiveRecord
|
|
780
871
|
end
|
781
872
|
end
|
782
873
|
|
783
|
-
def require_fixture_classes(fixture_set_names = nil)
|
874
|
+
def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
|
784
875
|
if fixture_set_names
|
785
876
|
fixture_set_names = fixture_set_names.map { |n| n.to_s }
|
786
877
|
else
|
@@ -788,7 +879,7 @@ module ActiveRecord
|
|
788
879
|
end
|
789
880
|
|
790
881
|
fixture_set_names.each do |file_name|
|
791
|
-
file_name = file_name.singularize if
|
882
|
+
file_name = file_name.singularize if config.pluralize_table_names
|
792
883
|
try_to_load_dependency(file_name)
|
793
884
|
end
|
794
885
|
end
|
@@ -840,9 +931,7 @@ module ActiveRecord
|
|
840
931
|
!self.class.uses_transaction?(method_name)
|
841
932
|
end
|
842
933
|
|
843
|
-
def setup_fixtures
|
844
|
-
return if ActiveRecord::Base.configurations.blank?
|
845
|
-
|
934
|
+
def setup_fixtures(config = ActiveRecord::Base)
|
846
935
|
if pre_loaded_fixtures && !use_transactional_fixtures
|
847
936
|
raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
|
848
937
|
end
|
@@ -856,7 +945,7 @@ module ActiveRecord
|
|
856
945
|
if @@already_loaded_fixtures[self.class]
|
857
946
|
@loaded_fixtures = @@already_loaded_fixtures[self.class]
|
858
947
|
else
|
859
|
-
@loaded_fixtures = load_fixtures
|
948
|
+
@loaded_fixtures = load_fixtures(config)
|
860
949
|
@@already_loaded_fixtures[self.class] = @loaded_fixtures
|
861
950
|
end
|
862
951
|
@fixture_connections = enlist_fixture_connections
|
@@ -867,16 +956,14 @@ module ActiveRecord
|
|
867
956
|
else
|
868
957
|
ActiveRecord::FixtureSet.reset_cache
|
869
958
|
@@already_loaded_fixtures[self.class] = nil
|
870
|
-
@loaded_fixtures = load_fixtures
|
959
|
+
@loaded_fixtures = load_fixtures(config)
|
871
960
|
end
|
872
961
|
|
873
962
|
# Instantiate fixtures for every test if requested.
|
874
|
-
instantiate_fixtures if use_instantiated_fixtures
|
963
|
+
instantiate_fixtures(config) if use_instantiated_fixtures
|
875
964
|
end
|
876
965
|
|
877
966
|
def teardown_fixtures
|
878
|
-
return if ActiveRecord::Base.configurations.blank?
|
879
|
-
|
880
967
|
# Rollback changes if a transaction is active.
|
881
968
|
if run_in_transaction?
|
882
969
|
@fixture_connections.each do |connection|
|
@@ -895,19 +982,19 @@ module ActiveRecord
|
|
895
982
|
end
|
896
983
|
|
897
984
|
private
|
898
|
-
def load_fixtures
|
899
|
-
fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
|
985
|
+
def load_fixtures(config)
|
986
|
+
fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
|
900
987
|
Hash[fixtures.map { |f| [f.name, f] }]
|
901
988
|
end
|
902
989
|
|
903
990
|
# for pre_loaded_fixtures, only require the classes once. huge speed improvement
|
904
991
|
@@required_fixture_classes = false
|
905
992
|
|
906
|
-
def instantiate_fixtures
|
993
|
+
def instantiate_fixtures(config)
|
907
994
|
if pre_loaded_fixtures
|
908
995
|
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
|
909
996
|
unless @@required_fixture_classes
|
910
|
-
self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
|
997
|
+
self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
|
911
998
|
@@required_fixture_classes = true
|
912
999
|
end
|
913
1000
|
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
|
@@ -924,3 +1011,13 @@ module ActiveRecord
|
|
924
1011
|
end
|
925
1012
|
end
|
926
1013
|
end
|
1014
|
+
|
1015
|
+
class ActiveRecord::FixtureSet::RenderContext # :nodoc:
|
1016
|
+
def self.create_subclass
|
1017
|
+
Class.new ActiveRecord::FixtureSet.context_class do
|
1018
|
+
def get_binding
|
1019
|
+
binding()
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
end
|