fixture_dependencies 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2007-2008 Jeremy Evans
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,254 @@
1
+ = fixture_dependencies
2
+
3
+ fixture_dependencies is an advanced fixture loader, allowing the loading of
4
+ models from YAML fixtures, along with their entire dependency graph. It has
5
+ the following features:
6
+
7
+ - Fixtures specify association names instead of foreign keys
8
+ - Support both Sequel and ActiveRecord
9
+ - Supports many_to_one/belongs_to, one_to_many/has_many,
10
+ many_to_many/has_and_belongs_to_many, and has_one associations
11
+ - Loads a fixture's dependency graph in such a manner that foreign key
12
+ constraints aren't violated
13
+ - Has a very simple API (FixtureDependencies.load(:model__fixture))
14
+ - Handles almost all cyclic dependencies
15
+ - Includes Rails and Sequel test helpers for Test::Unit (and a Sequel test
16
+ helper for RSpec) that load fixtures for every test inside a transaction,
17
+ so fixture data is never left in your database
18
+
19
+ == Installation
20
+
21
+ sudo gem install jeremyevans-fixture_dependencies \
22
+ --source http://gems.github.com
23
+
24
+ == Source
25
+
26
+ Source is available via github:
27
+
28
+ http://github.com/jeremyevans/fixture_dependencies
29
+
30
+ You can check it out with git:
31
+
32
+ git clone git://github.com/jeremyevans/fixture_dependencies.git
33
+
34
+ == Usage
35
+
36
+ === With Rails/ActiveRecord/Test::Unit:
37
+
38
+ Add the following to test/test_helper.rb after "require 'test_help'":
39
+
40
+ require 'fixture_dependencies/test_unit/rails'
41
+
42
+ This overrides the default test helper to load the fixtures inside transactions
43
+ and to use FixtureDependencies to load the fixtures.
44
+
45
+ === With Sequel/Test::Unit:
46
+
47
+ Somewhere before the test code is loaded:
48
+
49
+ require 'fixture_dependencies/test_unit/sequel'
50
+
51
+ Make sure the test case classes use FixtureDependencies::SequelTestCase:
52
+
53
+ class ModelTest < FixtureDependencies::SequelTestCase
54
+
55
+ This runs the test cases inside a Sequel transaction.
56
+
57
+ === With Sequel/RSpec:
58
+
59
+ Somewhere before the test code is loaded:
60
+
61
+ require 'fixture_dependencies/rspec/sequel'
62
+
63
+ This runs each spec inside a separate Sequel transaction.
64
+
65
+ === With other testing libraries:
66
+
67
+ You can just use FixtureDependencies.load to handle the loading of fixtures.
68
+ The use of transactions is up to you. One thing you must do if you are
69
+ not using the rails test helper is to set the fixture path for
70
+ FixtureDependencies:
71
+
72
+ FixtureDependencies.fixture_path = '/path/to/fixtures'
73
+
74
+ == Changes to Rails default fixtures:
75
+
76
+ fixture_dependencies is designed to require the least possible changes to
77
+ the default YAML fixtures used by Rails (well, at least Rails 1.2 and earlier).
78
+ For example, see the following changes:
79
+
80
+ OLD NEW
81
+ asset1: asset1:
82
+ id: 1 id: 1
83
+ employee_id: 2 employee: jeremy
84
+ product_id: 3 product: nx7010
85
+ vendor_id: 2 vendor: lxg_computers
86
+ note: in working order note: in working order
87
+
88
+ As you can see, you just replace the foreign key attribute and value with the
89
+ name of the association and the associations name. This assumes you have an
90
+ employee fixture with a name of jeremy, and products fixture with the name of
91
+ nx7010, and a vendors fixture with the name lxg_computers.
92
+
93
+ Fixture files still use the table_name of the model.
94
+
95
+ == Changes to the fixtures Class Method:
96
+
97
+ fixture_dependencies can still use the fixtures class method in your test:
98
+
99
+ class EmployeeTest < Test::Unit::TestCase
100
+ fixtures :assets
101
+ end
102
+
103
+ In Rails default testing practices, the arguments to fixtures are table names.
104
+ fixture_dependencies changes this to underscored model names. If you are using
105
+ Rails' recommended table practices, this shouldn't make a difference.
106
+
107
+ It is recommended that you do not use the fixtures method, and instead load
108
+ individual fixtures as needed (see below). This makes your tests much more
109
+ robust, in case you want to add or remove individual fixtures at a later date.
110
+
111
+ == Loading individual fixtures with fixtures class method
112
+
113
+ There is support for loading individual fixtures (and just their dependencies),
114
+ using the following syntax:
115
+
116
+ class EmployeeTest < Test::Unit::TestCase
117
+ fixtures :employee__jeremy # Note the double underscore
118
+ end
119
+
120
+ This would load just the jeremy fixture and its dependencies. I find this is
121
+ much better than loading all fixtures in most of my test suites. Even better
122
+ is loading just the fixtures you want instead every test method (see below).
123
+ This leads to the most robust testing.
124
+
125
+ == Loading fixtures inside test methods
126
+
127
+ I find that it is often better to skip the use of the fixtures method entirely,
128
+ and load the fixtures I want manually in each test method. This provides for
129
+ the loosest coupling possible. Here's an example:
130
+
131
+ class EmployeeTest < Test::Unit::TestCase
132
+ def test_employee_name
133
+ # Load the fixture and return the Employee object
134
+ employee = load(:employee__jeremy)
135
+ # Test the employee
136
+ end
137
+
138
+ def test_employees
139
+ # Load the fixtures and return two Employee objects
140
+ employee1, employee2 = load(:employees=>[:jeremy, :karl])
141
+ # Test the employees
142
+ end
143
+
144
+ def test_award_statistics
145
+ # Load all fixtures in both tables
146
+ load(:employee_award__jeremy_first, :award__first)
147
+ # Test the award_statistics method
148
+ # (which pulls data from the tables loaded above)
149
+ end
150
+ end
151
+
152
+ Don't worry about loading the same fixture twice, if a fixture is already
153
+ loaded, it won't attempt to load it again.
154
+
155
+ == one_to_many/many_to_many/has_many/has_and_belongs_to_many assocations
156
+
157
+ Here's an example of using has_one (logon_information), has_many (assets), and
158
+ has_and_belongs_to_many (groups) associations.
159
+
160
+ jeremy:
161
+ id: 2
162
+ name: Jeremy Evans
163
+ logon_information: jeremy
164
+ assets: [asset1, asset2, asset3]
165
+ groups: [group1]
166
+
167
+ logon_information is a has_one association to another table which was split
168
+ from the employees table due to database security requirements. Assets is a
169
+ has_many association, where one employee is responsible for the asset.
170
+ Employees can be a member of multiple groups, and each group can have multiple
171
+ employees.
172
+
173
+ For has_* associations, after fixture_dependencies saves jeremy, it will load
174
+ and save logon_information (and its dependencies...), it will load each asset
175
+ in the order specified (and their dependencies...), and it will load all of the
176
+ groups in the order specified (and their dependencies...). Note that there
177
+ is only a load order inside a specific association, associations are stored
178
+ in the same hash as attributes and are loaded in an arbitrary order.
179
+
180
+ == many_to_many/has_and_belongs_to_many join table fixtures
181
+
182
+ Another change is that Rails defaults allow you to specify habtm join tables in
183
+ fixtures. That doesn't work with fixture dependencies, as there is no
184
+ associated model. Instead, you use a has_and_belongs_to_many association name
185
+ in the the appropriate model fixtures (see above).
186
+
187
+ == Cyclic dependencies
188
+
189
+ fixture_dependencies handles almost all cyclic dependencies. It handles all
190
+ has_many, has_one, and habtm cyclic dependencies. It handles all
191
+ self-referential cyclic dependencies. It handles all belongs_to cyclic
192
+ dependencies except the case where there is a NOT NULL or validates_presence of
193
+ constraint on the cyclic dependency's foreign key.
194
+
195
+ For example, a case that won't work is when employee belongs_to supervisor
196
+ (with a NOT NULL or validates_presence_of constraint on supervisor_id), and
197
+ john is karl's supervisor and karl is john's supervisor. Since you can't create
198
+ john without a valid supervisor_id, you need to create karl first, but you
199
+ can't create karl for the same reason (as john doesn't exist yet).
200
+
201
+ There isn't a generic way to handle the belongs_to cyclic dependency, as far as
202
+ I know. Deferring foreign key checks could work, but may not be enabled (and
203
+ one of the main reasons to use the plugin is that it doesn't require them).
204
+ For associations like the example above (employee's supervisor is also an
205
+ employee), setting the foreign_key to the primary key and then changing it
206
+ later is an option, but database checks may prevent it. For more complex
207
+ cyclic dependencies involving multiple model classes (employee belongs_to
208
+ division belongs_to head_of_division when the employee is a member of the
209
+ division and also the head of the division), even that approach is not
210
+ possible.
211
+
212
+ == Known issues
213
+
214
+ Currently, the plugin only supports YAML fixtures, but other types of fixtures
215
+ would be fairly easy to add (send me a patch if you add support for another
216
+ fixture type).
217
+
218
+ The plugin is significantly slower than the default testing method, because it
219
+ loads all fixtures inside of a transaction (one per test method), where Rails
220
+ defaults to loading the fixtures once per test suite (outside of a
221
+ transaction), and only deletes fixtures from a table when overwriting it with
222
+ new fixtures.
223
+
224
+ Instantiated fixtures are not available with this plugin. Instead, you should
225
+ use load(:model__fixture_name).
226
+
227
+ == Troubleshooting
228
+
229
+ If you run into problems with loading your fixtures, it can be difficult to see
230
+ where the problems are. To aid in debugging an error, add the following to
231
+ test/test_helper.rb:
232
+
233
+ FixtureDependencies.verbose = 3
234
+
235
+ This will give a verbose description of the loading and saving of fixtures for
236
+ every test, including the recursive loading of the dependency graph.
237
+
238
+ == Similar Ideas
239
+
240
+ Rails now supports something similar by default. Honestly, I'm not sure what
241
+ the differences are.
242
+
243
+ fixture_references is a similar plugin. It uses erb inside yaml, and uses the
244
+ foreign key numbers inside of the association names, which leads me to believe
245
+ it doesn't support has_* associations.
246
+
247
+ == License
248
+
249
+ fixture_dependencies is released under the MIT License. See the LICENSE file
250
+ for details.
251
+
252
+ == Author
253
+
254
+ Jeremy Evans <code@jeremyevans.net>
@@ -0,0 +1,43 @@
1
+ class << FixtureDependencies
2
+ private
3
+
4
+ def add_associated_object_AR(reflection, attr, object, assoc)
5
+ if reflection.macro == :has_one
6
+ object.send("#{attr}=", assoc)
7
+ elsif !object.send(attr).include?(assoc)
8
+ object.send(attr) << assoc
9
+ end
10
+ end
11
+
12
+ def model_find_AR(model, pk)
13
+ model.find(pk)
14
+ end
15
+
16
+ def model_find_by_pk_AR(model, pk)
17
+ model.send("find_by_#{model.primary_key}", pk)
18
+ end
19
+
20
+ def model_save_AR(object)
21
+ object.save || raise(ActiveRecord::ActiveRecordError)
22
+ end
23
+
24
+ def raise_model_error_AR(message)
25
+ ActiveRecord::RecordNotFound
26
+ end
27
+
28
+ def reflection_AR(model, attr)
29
+ model.reflect_on_association(attr)
30
+ end
31
+
32
+ def reflection_class_AR(reflection)
33
+ reflection.klass
34
+ end
35
+
36
+ def reflection_key_AR(reflection)
37
+ reflection.options[:foreign_key] || reflection.klass.table_name.classify.foreign_key
38
+ end
39
+
40
+ def reflection_type_AR(reflection)
41
+ reflection.macro
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ class Spec::Example::ExampleGroup
2
+ def execute(*args, &block)
3
+ Sequel::Model.db.transaction{super(*args, &block); raise Sequel::Error::Rollback}
4
+ end
5
+
6
+ def load(*args)
7
+ FixtureDependencies.load(*args)
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ class << FixtureDependencies
2
+ private
3
+
4
+ def add_associated_object_S(reflection, attr, object, assoc)
5
+ object.send("add_#{attr.to_s.singularize}", assoc) unless object.send(attr).include?(assoc)
6
+ end
7
+
8
+ def model_find_S(model, pk)
9
+ model[pk] || raise(Sequel::Error)
10
+ end
11
+
12
+ def model_find_by_pk_S(model, pk)
13
+ model[pk]
14
+ end
15
+
16
+ def model_save_S(object)
17
+ object.raise_on_save_failure = true
18
+ object.save
19
+ end
20
+
21
+ def raise_model_error_S(message)
22
+ Sequel::Error
23
+ end
24
+
25
+ def reflection_S(model, attr)
26
+ model.association_reflection(attr)
27
+ end
28
+
29
+ def reflection_class_S(reflection)
30
+ reflection.associated_class
31
+ end
32
+
33
+ def reflection_key_S(reflection)
34
+ reflection[:key]
35
+ end
36
+
37
+ def reflection_type_S(reflection)
38
+ reflection[:type]
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ require 'fixture_dependencies/test_unit'
2
+ FixtureDependencies.fixture_path = Test::Unit::TestCase.fixture_path
3
+
4
+ module Test
5
+ module Unit
6
+ class TestCase
7
+ class << self
8
+ alias_method :stupid_method_added, :method_added
9
+ end
10
+ def self.method_added(x)
11
+ end
12
+
13
+ # Load fixtures using FixtureDependencies inside a transaction
14
+ def setup_with_fixtures
15
+ ActiveRecord::Base.send :increment_open_transactions
16
+ ActiveRecord::Base.connection.begin_db_transaction
17
+ load_fixtures
18
+ end
19
+ alias_method :setup, :setup_with_fixtures
20
+
21
+ class << self
22
+ alias_method :method_added, :stupid_method_added
23
+ end
24
+
25
+ private
26
+
27
+ # Load fixtures named with the fixtures class method
28
+ def load_fixtures
29
+ load(*fixture_table_names)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ require 'fixture_dependencies/test_unit'
2
+
3
+ class FixtureDependencies::SequelTestCase < Test::Unit::TestCase
4
+ # Work around for Rails stupidity
5
+ undef_method :default_test if method_defined?(:default_test)
6
+
7
+ def run(*args, &block)
8
+ Sequel::Model.db.transaction do
9
+ super
10
+ raise Sequel::Error::Rollback
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'fixture_dependencies'
2
+
3
+ module Test
4
+ module Unit
5
+ class TestCase
6
+ private
7
+
8
+ # Load given fixtures using FixtureDependencies
9
+ def load(*fixture)
10
+ FixtureDependencies.load(*fixture)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,213 @@
1
+ require('sequel/extensions/inflector') unless [:singularize, :camelize, :underscore, :constantize].all?{|meth| "".respond_to?(meth)}
2
+
3
+ class FixtureDependencies
4
+ @fixtures = {}
5
+ @loaded = {}
6
+ @verbose = 0
7
+
8
+ # Load all record arguments into the database. If a single argument is
9
+ # given and it corresponds to a single fixture, return the the model
10
+ # instance corresponding to that fixture. If a single argument if given
11
+ # and it corresponds to a model, return all model instances corresponding
12
+ # to that model. If multiple arguments are given, return a list of
13
+ # model instances (for single fixture arguments) or list of model instances
14
+ # (for model fixture arguments). If no arguments, return the empty list.
15
+ # If any of the arguments is a hash, assume the key specifies the model
16
+ # and the values specify the fixture, and treat it as though individual
17
+ # symbols specifying both model and fixture were given.
18
+ #
19
+ # Examples:
20
+ # * load(:posts) # All post fixtures, not recommended
21
+ # * load(:posts, :comments) # All post and comment fixtures, again not recommended
22
+ # * load(:post__post1) # Just the post fixture named post1
23
+ # * load(:post__post1, :post__post2) # Post fixtures named post1 and post2
24
+ # * load(:posts=>[:post1, :post2]) # Post fixtures named post1 and post2
25
+ # * load(:post__post1, :comment__comment2) # Post fixture named post1 and comment fixture named comment2
26
+ # * load({:posts=>[:post1, :post2]}, :comment__comment2) # Post fixtures named post1 and post2 and comment fixture named comment2
27
+ #
28
+ # This will load the data from the yaml files for each argument whose model
29
+ # is not already in the fixture hash.
30
+ def self.load(*records)
31
+ ret = records.map do |record|
32
+ if record.is_a?(Hash)
33
+ record.map do |k, vals|
34
+ model = k.to_s.singularize
35
+ vals.map{|v| :"#{model}__#{v}"}
36
+ end
37
+ else
38
+ record
39
+ end
40
+ end.flatten.compact.map do |record|
41
+ model_name, name = split_name(record)
42
+ if name
43
+ use(record.to_sym)
44
+ else
45
+ model_name = model_name.singularize
46
+ unless loaded[model_name.to_sym]
47
+ puts "loading #{model_name}.yml" if verbose > 0
48
+ load_yaml(model_name)
49
+ end
50
+ fixtures[model_name.to_sym].keys.map{|name| use(:"#{model_name}__#{name}")}
51
+ end
52
+ end
53
+ ret.length == 1 ? ret[0] : ret
54
+ end
55
+ end
56
+
57
+ require 'fixture_dependencies/active_record' if defined?(ActiveRecord::Base)
58
+ require 'fixture_dependencies/sequel' if defined?(Sequel::Model)
59
+
60
+
61
+ class << FixtureDependencies
62
+ attr_reader :fixtures, :loaded
63
+ attr_accessor :verbose, :fixture_path
64
+
65
+ private
66
+
67
+ # Add a fixture to the fixture hash (does not add to the database,
68
+ # just makes it available to be add to the database via use).
69
+ def add(model_name, name, attributes)
70
+ (fixtures[model_name.to_sym]||={})[name.to_sym] = attributes
71
+ end
72
+
73
+ # Get the model instance that already exists in the database using
74
+ # the fixture name.
75
+ def get(record)
76
+ model_name, name = split_name(record)
77
+ model = model_name.camelize.constantize
78
+ model_method(:model_find, model_type(model), model, fixtures[model_name.to_sym][name.to_sym][model.primary_key.to_sym])
79
+ end
80
+
81
+ # Adds all fixtures in the yaml fixture file for the model to the fixtures
82
+ # hash (does not add them to the database, see add).
83
+ def load_yaml(model_name)
84
+ raise(ArgumentError, "No fixture_path set. Use FixtureDependencies.fixture_path = ...") unless fixture_path
85
+ YAML.load(File.read(File.join(fixture_path, "#{model_name.camelize.constantize.table_name}.yml"))).each do |name, attributes|
86
+ symbol_attrs = {}
87
+ attributes.each{|k,v| symbol_attrs[k.to_sym] = v}
88
+ add(model_name.to_sym, name, symbol_attrs)
89
+ end
90
+ loaded[model_name.to_sym] = true
91
+ end
92
+
93
+ # Delegate to the correct method based on mtype
94
+ def model_method(meth, mtype, *args, &block)
95
+ send("#{meth}_#{mtype}", *args, &block)
96
+ end
97
+
98
+ # A symbol representing the base class of the model, currently
99
+ # ActiveRecord::Base and Sequel::Model are supported.
100
+ def model_type(model)
101
+ if model.ancestors.map{|x| x.to_s}.include?('ActiveRecord::Base')
102
+ :AR
103
+ elsif model.ancestors.map{|x| x.to_s}.include?('Sequel::Model')
104
+ :S
105
+ else
106
+ raise TypeError, 'not ActiveRecord or Sequel model'
107
+ end
108
+ end
109
+
110
+ # Split the fixture name into the name of the model and the name of
111
+ # the individual fixture.
112
+ def split_name(name)
113
+ name.to_s.split('__', 2)
114
+ end
115
+
116
+ # Load the individual fixture into the database, by loading all necessary
117
+ # belongs_to dependencies before saving the model, and all has_*
118
+ # dependencies after saving the model. If the model already exists in
119
+ # the database, return it. Will check the yaml file for fixtures if no
120
+ # fixtures yet exist for the model. If the fixture isn't in the fixture
121
+ # hash, raise an error.
122
+ def use(record, loading = [], procs = {})
123
+ spaces = " " * loading.length
124
+ puts "#{spaces}using #{record}" if verbose > 0
125
+ puts "#{spaces}load stack:#{loading.inspect}" if verbose > 1
126
+ loading.push(record)
127
+ model_name, name = split_name(record)
128
+ model = model_name.camelize.constantize
129
+ unless loaded[model_name.to_sym]
130
+ puts "#{spaces}loading #{model.table_name}.yml" if verbose > 0
131
+ load_yaml(model_name)
132
+ end
133
+ mtype = model_type(model)
134
+ model_method(:raise_model_error, mtype, "Couldn't use fixture #{record.inspect}") unless attributes = fixtures[model_name.to_sym][name.to_sym]
135
+ # return if object has already been loaded into the database
136
+ if existing_obj = model_method(:model_find_by_pk, mtype, model, attributes[model.primary_key.to_sym])
137
+ puts "#{spaces}using #{record}: already in database" if verbose > 2
138
+ loading.pop
139
+ return existing_obj
140
+ end
141
+ obj = model.new
142
+ many_associations = []
143
+ attributes.each do |attr, value|
144
+ if reflection = model_method(:reflection, mtype, model, attr.to_sym)
145
+ if [:belongs_to, :many_to_one].include?(model_method(:reflection_type, mtype, reflection))
146
+ dep_name = "#{model_method(:reflection_class, mtype, reflection).name.underscore}__#{value}".to_sym
147
+ if dep_name == record
148
+ # Self referential record, use primary key
149
+ puts "#{spaces}#{record}.#{attr}: belongs_to self-referential" if verbose > 1
150
+ attr = model_method(:reflection_key, mtype, reflection)
151
+ value = attributes[model.primary_key.to_sym]
152
+ elsif loading.include?(dep_name)
153
+ # Association cycle detected, set foreign key for this model afterward using procs
154
+ # This is will fail if the column is set to not null or validates_presence_of
155
+ puts "#{spaces}#{record}.#{attr}: belongs-to cycle detected:#{dep_name}" if verbose > 1
156
+ (procs[dep_name] ||= []) << Proc.new do |assoc|
157
+ m = model_method(:model_find, mtype, model, attributes[model.primary_key.to_sym])
158
+ m.send("#{attr}=", assoc)
159
+ model_method(:model_save, mtype, m)
160
+ end
161
+ value = nil
162
+ else
163
+ # Regular assocation, load it
164
+ puts "#{spaces}#{record}.#{attr}: belongs_to:#{dep_name}" if verbose > 1
165
+ use(dep_name, loading, procs)
166
+ value = get(dep_name)
167
+ end
168
+ elsif
169
+ many_associations << [attr, reflection, model_method(:reflection_type, mtype, reflection) == :has_one ? [value] : value]
170
+ next
171
+ end
172
+ end
173
+ puts "#{spaces}#{record}.#{attr} = #{value.inspect}" if verbose > 2
174
+ obj.send("#{attr}=", value)
175
+ end
176
+
177
+ puts "#{spaces}saving #{record}" if verbose > 1
178
+
179
+ model_method(:model_save, mtype, obj)
180
+ # after saving the model, we set the primary key within the fixture hash, in case it was not explicitly specified in the fixture and was generated by an auto_increment / serial field
181
+ fixtures[model_name.to_sym][name.to_sym][model.primary_key.to_sym] ||= obj[model.primary_key.to_sym]
182
+
183
+ loading.pop
184
+ # Update the circular references
185
+ if procs[record]
186
+ procs[record].each{|p| p.call(obj)}
187
+ procs.delete(record)
188
+ end
189
+ # Update the has_many and habtm associations
190
+ many_associations.each do |attr, reflection, values|
191
+ values.each do |value|
192
+ dep_name = "#{model_method(:reflection_class, mtype, reflection).name.underscore}__#{value}".to_sym
193
+ rtype = model_method(:reflection_type, mtype, reflection) if verbose > 1
194
+ if dep_name == record
195
+ # Self referential, add association
196
+ puts "#{spaces}#{record}.#{attr}: #{rtype} self-referential" if verbose > 1
197
+ model_method(:add_associated_object, mtype, reflection, attr, obj, obj)
198
+ elsif loading.include?(dep_name)
199
+ # Cycle Detected, add association to this object after saving other object
200
+ puts "#{spaces}#{record}.#{attr}: #{rtype} cycle detected:#{dep_name}" if verbose > 1
201
+ (procs[dep_name] ||= []) << Proc.new do |assoc|
202
+ model_method(:add_associated_object, mtype, reflection, attr, obj, assoc)
203
+ end
204
+ else
205
+ # Regular association, add it
206
+ puts "#{spaces}#{record}.#{attr}: #{rtype}:#{dep_name}" if verbose > 1
207
+ model_method(:add_associated_object, mtype, reflection, attr, obj, use(dep_name, loading, procs))
208
+ end
209
+ end
210
+ end
211
+ obj
212
+ end
213
+ end
@@ -0,0 +1 @@
1
+ require 'fixture_dependencies/test_unit/rails'
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fixture_dependencies
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Evans
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-06 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: code@jeremyevans.net
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ files:
25
+ - README
26
+ - LICENSE
27
+ - lib/fixture_dependencies.rb
28
+ - lib/fixture_dependencies_test_help.rb
29
+ - lib/fixture_dependencies/sequel.rb
30
+ - lib/fixture_dependencies/active_record.rb
31
+ - lib/fixture_dependencies/test_unit.rb
32
+ - lib/fixture_dependencies/test_unit/rails.rb
33
+ - lib/fixture_dependencies/test_unit/sequel.rb
34
+ - lib/fixture_dependencies/rspec/sequel.rb
35
+ has_rdoc: true
36
+ homepage:
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --inline-source
40
+ - --line-numbers
41
+ - README
42
+ - lib
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.3.1
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: Sequel/ActiveRecord fixture loader that handles dependency graphs
64
+ test_files: []
65
+