mongoid_rails_fixtures 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .bundle
2
+ .DS_Store
3
+ .rvmrc
4
+ .gem
5
+ log/
6
+ .log
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # Use `bundle install` in order to install these gems
2
+ # Use `bundle exec rake` in order to run the specs using the bundle
3
+ source :rubygems
4
+
5
+ gem "rails", ">=3.0.0.rc"
6
+ gem "railties", ">=3.0.0.rc"
7
+ gem "activesupport", ">=3.0.0.rc"
8
+ gem "mongoid", ">=2.0.0.beta.7"
9
+ gem "mongoid_rails_migrations", ">=0.0.4"
data/README.rdoc ADDED
@@ -0,0 +1,61 @@
1
+ == SYNOPSIS
2
+ * A case of the fixtures for the mongoid
3
+
4
+ == WHY?
5
+ * Fixtures can be kinda nice for classic rails workflow
6
+ * Some of us don't like factories
7
+ * Factories can't test db server side code, e.g. Mongodb server.js
8
+
9
+ == REQUIREMENTS
10
+ * rails >= 3.0.0.rc
11
+ * mongoid >= 2.0.0.beta.7
12
+
13
+ == INSTALL
14
+ * gem install mongoid_rails_fixtures
15
+ * In your Gemfile, include:
16
+ gem "mongoid_rails_fixtures", ">=0.0.1" # or current version
17
+
18
+ == OPTIONS
19
+ to use, set these attributes after the Bunlder.require in your application.rb
20
+ * to migrate before test runs, optionally include
21
+ +typewriter+Mongoid.configure.migrate_before_tests = true+typewriter+
22
+ * to stop the deletion of existing fixtures before create (a rails default), set
23
+ +typewriter+Mongoid.configure.delete_existing_fixtures_before_create = false+typewriter+
24
+
25
+ == FEATURES AND HOW TO USE
26
+ * you can simply create your own fixture files
27
+ * I've been thinking about creating a generator but it seems to be overkill to automate the process
28
+ * place your fixtures as pluralized models either in .yml or .csv (e.g. users.yml) in your rails/test/fixtures directory
29
+ * execute with a standard call to
30
+ +typewriter+rake test:units+typewriter+
31
+
32
+ == NOTES
33
+ * Original active_record/fixtures.rb is unchanged for association detection (so that likely doesn't work)
34
+ * Only tested with ruby 1.9.1
35
+
36
+ == TODO
37
+ * make association detection functional in fixtures.rb
38
+ *
39
+
40
+ == CREDITS TO
41
+ * rails
42
+ * mongoid
43
+
44
+ Much of this gem simply modifies existing code from both projects.
45
+ With that out of the way, on to the license.
46
+
47
+ == LICENSE (MIT)
48
+
49
+ Copyright © 2010: Alan Da Costa
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'),
52
+ to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
54
+ the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
57
+
58
+ The software is provided 'as is', without warranty of any kind, express or implied, including but not limited to the warranties of
59
+ merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any
60
+ claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the
61
+ software or the use or other dealings in the software.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ namespace :test do
2
+ namespace :mongoid do
3
+ desc "Test mongoid rails migrations"
4
+ task :fixtures do
5
+ require File.dirname(__FILE__) + '/test/fixture_test'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ module MongoidRailsFixtures
3
+ def self.dirname; File.dirname(__FILE__); end
4
+ end
5
+
6
+ require 'rubygems'
7
+ # Set up gems listed in the Gemfile.
8
+ if File.exist?("#{MongoidRailsFixtures.dirname}/../../Gemfile")
9
+ require 'bundler'
10
+ Bundler.setup
11
+ end
12
+
13
+ Bundler.require(:default) if defined?(Bundler)
14
+
15
+ # action_controller is bundled inside rails, we can't gem require
16
+ require 'action_controller'
17
+
18
+ # Add base to path incase not included as a gem
19
+ $:.unshift(MongoidRailsFixtures.dirname) unless
20
+ $:.include?(MongoidRailsFixtures.dirname) || $:.include?(File.expand_path(MongoidRailsFixtures.dirname))
21
+
22
+ %w[ timestamps_ext railtie_ext fixtures test_help mongoid_ext ].each do |file|
23
+ require "#{MongoidRailsFixtures.dirname}/mongoid_rails_fixtures/mongoid_ext/#{file}"
24
+ end
@@ -0,0 +1,982 @@
1
+ # extensions for compatibility with existing rails setup
2
+ module Mongoid
3
+ class << self
4
+ # Silence deprecation warnings within the block.
5
+ def silence
6
+ old_silenced, @silenced = @silenced, true
7
+ yield
8
+ ensure
9
+ @silenced = old_silenced
10
+ end
11
+ end
12
+ end
13
+
14
+ require 'erb'
15
+ require 'yaml'
16
+ require 'csv'
17
+ require 'zlib'
18
+ require 'active_support/dependencies'
19
+ require 'active_support/core_ext/array/wrap'
20
+ require 'active_support/core_ext/object/blank'
21
+ require 'active_support/core_ext/logger'
22
+ require 'active_support/core_ext/class/delegating_attributes'
23
+
24
+ if RUBY_VERSION < '1.9'
25
+ module YAML #:nodoc:
26
+ class Omap #:nodoc:
27
+ def keys; map { |k, v| k } end
28
+ def values; map { |k, v| v } end
29
+ end
30
+ end
31
+ end
32
+
33
+ class FixtureClassNotFound < StandardError #:nodoc:
34
+ end
35
+
36
+ # Fixtures are a way of organizing data that you want to test against; in short, sample data.
37
+ #
38
+ # = Fixture formats
39
+ #
40
+ # Fixtures come in 3 flavors:
41
+ #
42
+ # 1. YAML fixtures
43
+ # 2. CSV fixtures
44
+ # 3. Single-file fixtures
45
+ #
46
+ # == YAML fixtures
47
+ #
48
+ # This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
49
+ # in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
50
+ #
51
+ # Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
52
+ # by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
53
+ # put your files in <tt><your-rails-app>/test/fixtures/</tt>). The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
54
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a YAML fixture file looks like this:
55
+ #
56
+ # rubyonrails:
57
+ # id: 1
58
+ # name: Ruby on Rails
59
+ # url: http://www.rubyonrails.org
60
+ #
61
+ # google:
62
+ # id: 2
63
+ # name: Google
64
+ # url: http://www.google.com
65
+ #
66
+ # This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
67
+ # indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
68
+ # pleasure.
69
+ #
70
+ # Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. See http://yaml.org/type/omap.html
71
+ # for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
72
+ # This is commonly needed for tree structures. Example:
73
+ #
74
+ # --- !omap
75
+ # - parent:
76
+ # id: 1
77
+ # parent_id: NULL
78
+ # title: Parent
79
+ # - child:
80
+ # id: 2
81
+ # parent_id: 1
82
+ # title: Child
83
+ #
84
+ # == CSV fixtures
85
+ #
86
+ # Fixtures can also be kept in the Comma Separated Value (CSV) format. Akin to YAML fixtures, CSV fixtures are stored
87
+ # in a single file, but instead end with the <tt>.csv</tt> file extension
88
+ # (Rails example: <tt><your-rails-app>/test/fixtures/web_sites.csv</tt>).
89
+ #
90
+ # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
91
+ # humans. The first line of the CSV file is a comma-separated list of field names. The rest of the file is then comprised
92
+ # of the actual data (1 per line). Here's an example:
93
+ #
94
+ # id, name, url
95
+ # 1, Ruby On Rails, http://www.rubyonrails.org
96
+ # 2, Google, http://www.google.com
97
+ #
98
+ # Should you have a piece of data with a comma character in it, you can place double quotes around that value. If you
99
+ # need to use a double quote character, you must escape it with another double quote.
100
+ #
101
+ # Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats. Instead, the
102
+ # fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
103
+ # number to the end. In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
104
+ # "web_site_2".
105
+ #
106
+ # Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
107
+ # have existing data somewhere already.
108
+ #
109
+ # == Single-file fixtures
110
+ #
111
+ # This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
112
+ # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
113
+ # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
114
+ # put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
115
+ # like <tt><your-rails-app>/test/fixtures/web_sites/</tt> for the WebSite model).
116
+ #
117
+ # Each text file placed in this directory represents a "record". Usually these types of fixtures are named without
118
+ # extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension. Here's what the
119
+ # above example might look like:
120
+ #
121
+ # web_sites/google
122
+ # web_sites/yahoo.txt
123
+ # web_sites/ruby-on-rails
124
+ #
125
+ # The file format of a standard fixture is simple. Each line is a property (or column in db speak) and has the syntax
126
+ # of "name => value". Here's an example of the ruby-on-rails fixture above:
127
+ #
128
+ # id => 1
129
+ # name => Ruby on Rails
130
+ # url => http://www.rubyonrails.org
131
+ #
132
+ # = Using fixtures in testcases
133
+ #
134
+ # Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
135
+ # fixtures, but first let's take a look at a sample unit test:
136
+ #
137
+ # require 'test_helper'
138
+ #
139
+ # class WebSiteTest < ActiveSupport::TestCase
140
+ # test "web_site_count" do
141
+ # assert_equal 2, WebSite.count
142
+ # end
143
+ # end
144
+ #
145
+ # By default, the <tt>test_helper module</tt> will load all of your fixtures into your test database, so this test will succeed.
146
+ # The testing environment will automatically load the all fixtures into the database before each test.
147
+ # To ensure consistent data, the environment deletes the fixtures before running the load.
148
+ #
149
+ # In addition to being available in the database, the fixture's data may also be accessed by
150
+ # using a special dynamic method, which has the same name as the model, and accepts the
151
+ # name of the fixture to instantiate:
152
+ #
153
+ # test "find" do
154
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
155
+ # end
156
+ #
157
+ # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the following tests:
158
+ #
159
+ # test "find_alt_method_1" do
160
+ # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
161
+ # end
162
+ #
163
+ # test "find_alt_method_2" do
164
+ # assert_equal "Ruby on Rails", @rubyonrails.news
165
+ # end
166
+ #
167
+ # In order to use these methods to access fixtured data within your testcases, you must specify one of the
168
+ # following in your <tt>ActiveSupport::TestCase</tt>-derived class:
169
+ #
170
+ # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
171
+ # self.use_instantiated_fixtures = true
172
+ #
173
+ # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
174
+ # self.use_instantiated_fixtures = :no_instances
175
+ #
176
+ # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
177
+ # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
178
+ # large sets of fixtured data.
179
+ #
180
+ # = Dynamic fixtures with ERb
181
+ #
182
+ # 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
183
+ # mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
184
+ #
185
+ # <% for i in 1..1000 %>
186
+ # fix_<%= i %>:
187
+ # id: <%= i %>
188
+ # name: guy_<%= 1 %>
189
+ # <% end %>
190
+ #
191
+ # This will create 1000 very simple YAML fixtures.
192
+ #
193
+ # Using ERb, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
194
+ # This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
195
+ # sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
196
+ # is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
197
+ #
198
+ # = Transactional fixtures
199
+ #
200
+ # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
201
+ #
202
+ # class FooTest < ActiveSupport::TestCase
203
+ # self.use_transactional_fixtures = true
204
+ #
205
+ # test "godzilla" do
206
+ # assert !Foo.find(:all).empty?
207
+ # Foo.destroy_all
208
+ # assert Foo.find(:all).empty?
209
+ # end
210
+ #
211
+ # test "godzilla aftermath" do
212
+ # assert !Foo.find(:all).empty?
213
+ # end
214
+ # end
215
+ #
216
+ # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
217
+ # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
218
+ #
219
+ # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
220
+ # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
221
+ #
222
+ # When *not* to use transactional fixtures:
223
+ #
224
+ # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
225
+ # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
226
+ # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
227
+ # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
228
+ # Use InnoDB, MaxDB, or NDB instead.
229
+ #
230
+ # = Advanced YAML Fixtures
231
+ #
232
+ # YAML fixtures that don't specify an ID get some extra features:
233
+ #
234
+ # * Stable, autogenerated IDs
235
+ # * Label references for associations (belongs_to, has_one, has_many)
236
+ # * HABTM associations as inline lists
237
+ # * Autofilled timestamp columns
238
+ # * Fixture label interpolation
239
+ # * Support for YAML defaults
240
+ #
241
+ # == Stable, autogenerated IDs
242
+ #
243
+ # Here, have a monkey fixture:
244
+ #
245
+ # george:
246
+ # id: 1
247
+ # name: George the Monkey
248
+ #
249
+ # reginald:
250
+ # id: 2
251
+ # name: Reginald the Pirate
252
+ #
253
+ # Each of these fixtures has two unique identifiers: one for the database
254
+ # and one for the humans. Why don't we generate the primary key instead?
255
+ # Hashing each fixture's label yields a consistent ID:
256
+ #
257
+ # george: # generated id: 503576764
258
+ # name: George the Monkey
259
+ #
260
+ # reginald: # generated id: 324201669
261
+ # name: Reginald the Pirate
262
+ #
263
+ # Active Record looks at the fixture's model class, discovers the correct
264
+ # primary key, and generates it right before inserting the fixture
265
+ # into the database.
266
+ #
267
+ # The generated ID for a given label is constant, so we can discover
268
+ # any fixture's ID without loading anything, as long as we know the label.
269
+ #
270
+ # == Label references for associations (belongs_to, has_one, has_many)
271
+ #
272
+ # Specifying foreign keys in fixtures can be very fragile, not to
273
+ # mention difficult to read. Since Active Record can figure out the ID of
274
+ # any fixture from its label, you can specify FK's by label instead of ID.
275
+ #
276
+ # === belongs_to
277
+ #
278
+ # Let's break out some more monkeys and pirates.
279
+ #
280
+ # ### in pirates.yml
281
+ #
282
+ # reginald:
283
+ # id: 1
284
+ # name: Reginald the Pirate
285
+ # monkey_id: 1
286
+ #
287
+ # ### in monkeys.yml
288
+ #
289
+ # george:
290
+ # id: 1
291
+ # name: George the Monkey
292
+ # pirate_id: 1
293
+ #
294
+ # Add a few more monkeys and pirates and break this into multiple files,
295
+ # and it gets pretty hard to keep track of what's going on. Let's
296
+ # use labels instead of IDs:
297
+ #
298
+ # ### in pirates.yml
299
+ #
300
+ # reginald:
301
+ # name: Reginald the Pirate
302
+ # monkey: george
303
+ #
304
+ # ### in monkeys.yml
305
+ #
306
+ # george:
307
+ # name: George the Monkey
308
+ # pirate: reginald
309
+ #
310
+ # Pow! All is made clear. Active Record reflects on the fixture's model class,
311
+ # finds all the +belongs_to+ associations, and allows you to specify
312
+ # a target *label* for the *association* (monkey: george) rather than
313
+ # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
314
+ #
315
+ # ==== Polymorphic belongs_to
316
+ #
317
+ # Supporting polymorphic relationships is a little bit more complicated, since
318
+ # Active Record needs to know what type your association is pointing at. Something
319
+ # like this should look familiar:
320
+ #
321
+ # ### in fruit.rb
322
+ #
323
+ # belongs_to :eater, :polymorphic => true
324
+ #
325
+ # ### in fruits.yml
326
+ #
327
+ # apple:
328
+ # id: 1
329
+ # name: apple
330
+ # eater_id: 1
331
+ # eater_type: Monkey
332
+ #
333
+ # Can we do better? You bet!
334
+ #
335
+ # apple:
336
+ # eater: george (Monkey)
337
+ #
338
+ # Just provide the polymorphic target type and Active Record will take care of the rest.
339
+ #
340
+ # === has_and_belongs_to_many
341
+ #
342
+ # Time to give our monkey some fruit.
343
+ #
344
+ # ### in monkeys.yml
345
+ #
346
+ # george:
347
+ # id: 1
348
+ # name: George the Monkey
349
+ #
350
+ # ### in fruits.yml
351
+ #
352
+ # apple:
353
+ # id: 1
354
+ # name: apple
355
+ #
356
+ # orange:
357
+ # id: 2
358
+ # name: orange
359
+ #
360
+ # grape:
361
+ # id: 3
362
+ # name: grape
363
+ #
364
+ # ### in fruits_monkeys.yml
365
+ #
366
+ # apple_george:
367
+ # fruit_id: 1
368
+ # monkey_id: 1
369
+ #
370
+ # orange_george:
371
+ # fruit_id: 2
372
+ # monkey_id: 1
373
+ #
374
+ # grape_george:
375
+ # fruit_id: 3
376
+ # monkey_id: 1
377
+ #
378
+ # Let's make the HABTM fixture go away.
379
+ #
380
+ # ### in monkeys.yml
381
+ #
382
+ # george:
383
+ # id: 1
384
+ # name: George the Monkey
385
+ # fruits: apple, orange, grape
386
+ #
387
+ # ### in fruits.yml
388
+ #
389
+ # apple:
390
+ # name: apple
391
+ #
392
+ # orange:
393
+ # name: orange
394
+ #
395
+ # grape:
396
+ # name: grape
397
+ #
398
+ # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
399
+ # on George's fixture, but we could've just as easily specified a list
400
+ # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
401
+ # the fixture's model class and discovers the +has_and_belongs_to_many+
402
+ # associations.
403
+ #
404
+ # == Autofilled timestamp columns
405
+ #
406
+ # If your table/model specifies any of Active Record's
407
+ # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
408
+ # they will automatically be set to <tt>Time.now</tt>.
409
+ #
410
+ # If you've set specific values, they'll be left alone.
411
+ #
412
+ # == Fixture label interpolation
413
+ #
414
+ # The label of the current fixture is always available as a column value:
415
+ #
416
+ # geeksomnia:
417
+ # name: Geeksomnia's Account
418
+ # subdomain: $LABEL
419
+ #
420
+ # Also, sometimes (like when porting older join table fixtures) you'll need
421
+ # to be able to get a hold of the identifier for a given label. ERB
422
+ # to the rescue:
423
+ #
424
+ # george_reginald:
425
+ # monkey_id: <%= Fixtures.identify(:reginald) %>
426
+ # pirate_id: <%= Fixtures.identify(:george) %>
427
+ #
428
+ # == Support for YAML defaults
429
+ #
430
+ # You probably already know how to use YAML to set and reuse defaults in
431
+ # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
432
+ #
433
+ # DEFAULTS: &DEFAULTS
434
+ # created_on: <%= 3.weeks.ago.to_s(:db) %>
435
+ #
436
+ # first:
437
+ # name: Smurf
438
+ # <<: *DEFAULTS
439
+ #
440
+ # second:
441
+ # name: Fraggle
442
+ # <<: *DEFAULTS
443
+ #
444
+ # Any fixture labeled "DEFAULTS" is safely ignored.
445
+
446
+ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
447
+ MAX_ID = 2 ** 30 - 1
448
+ DEFAULT_FILTER_RE = /\.ya?ml$/
449
+
450
+ @@all_cached_fixtures = {}
451
+
452
+ def self.reset_cache(connection = nil)
453
+ connection ||= Mongoid.database
454
+ @@all_cached_fixtures[connection.object_id] = {}
455
+ end
456
+
457
+ def self.cache_for_connection(connection)
458
+ @@all_cached_fixtures[connection.object_id] ||= {}
459
+ @@all_cached_fixtures[connection.object_id]
460
+ end
461
+
462
+ def self.fixture_is_cached?(connection, table_name)
463
+ cache_for_connection(connection)[table_name]
464
+ end
465
+
466
+ def self.cached_fixtures(connection, keys_to_fetch = nil)
467
+ if keys_to_fetch
468
+ fixtures = cache_for_connection(connection).values_at(*keys_to_fetch)
469
+ else
470
+ fixtures = cache_for_connection(connection).values
471
+ end
472
+ fixtures.size > 1 ? fixtures : fixtures.first
473
+ end
474
+
475
+ def self.cache_fixtures(connection, fixtures_map)
476
+ cache_for_connection(connection).update(fixtures_map)
477
+ end
478
+
479
+ def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true)
480
+ object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures
481
+ if load_instances
482
+ Mongoid.silence do
483
+ fixtures.each do |name, fixture|
484
+ begin
485
+ object.instance_variable_set "@#{name}", fixture.find
486
+ rescue FixtureClassNotFound
487
+ nil
488
+ end
489
+ end
490
+ end
491
+ end
492
+ end
493
+
494
+ def self.instantiate_all_loaded_fixtures(object, load_instances = true)
495
+ all_loaded_fixtures.each do |table_name, fixtures|
496
+ Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
497
+ end
498
+ end
499
+
500
+ cattr_accessor :all_loaded_fixtures
501
+ self.all_loaded_fixtures = {}
502
+
503
+ def self.create_fixtures(fixtures_directory, table_names, class_names = {})
504
+ table_names = [table_names].flatten.map { |n| n.to_s }
505
+ connection = block_given? ? yield : Mongoid.database
506
+
507
+ table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) }
508
+ unless table_names_to_fetch.empty?
509
+ Mongoid.silence do
510
+ fixtures_map = {}
511
+ fixtures = table_names_to_fetch.map do |table_name|
512
+ fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s))
513
+ end
514
+
515
+ all_loaded_fixtures.update(fixtures_map)
516
+
517
+ # connection.transaction(:requires_new => true) do
518
+ fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } if Mongoid.configure.delete_existing_fixtures_before_create
519
+ fixtures.each { |fixture| fixture.insert_fixtures }
520
+ # end
521
+
522
+ cache_fixtures(connection, fixtures_map)
523
+ end
524
+ end
525
+ cached_fixtures(connection, table_names)
526
+ end
527
+
528
+ # Returns a consistent, platform-independent identifier for +label+.
529
+ # Identifiers are positive integers less than 2^32.
530
+ def self.identify(label)
531
+ Zlib.crc32(label.to_s) % MAX_ID
532
+ end
533
+
534
+ attr_reader :table_name, :name
535
+
536
+ def initialize(connection, table_name, class_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
537
+ @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
538
+ @name = table_name # preserve fixture base name
539
+ @class_name = class_name || @table_name.singularize.camelize
540
+ @table_name = @table_name
541
+ @table_name = class_name.table_name if class_name.respond_to?(:table_name)
542
+ @connection = class_name.connection if class_name.respond_to?(:connection)
543
+ read_fixture_files
544
+ end
545
+
546
+ def delete_existing_fixtures
547
+ Mongoid.master.collections.each{|col| col.drop if col.name == table_name }
548
+ end
549
+
550
+ def insert_fixtures
551
+ begin
552
+ now = Rails.configuration.time_zone.downcase.to_sym == :utc ? Time.now.utc : Time.now
553
+ rescue
554
+ # default to UTC if rails no existy
555
+ now = Time.now.utc
556
+ end
557
+
558
+ now = now.to_s(:db)
559
+
560
+ # allow a standard key to be used for doing defaults in YAML
561
+ if is_a?(Hash)
562
+ delete('DEFAULTS')
563
+ else
564
+ delete(assoc('DEFAULTS'))
565
+ end
566
+
567
+ # track any join tables we need to insert later
568
+ habtm_fixtures = Hash.new do |h, habtm|
569
+ h[habtm] = HabtmFixtures.new(@connection, habtm.options[:join_table], nil, nil)
570
+ end
571
+
572
+ each do |label, fixture|
573
+ row = fixture.to_hash
574
+
575
+ if model_class && model_class < Mongoid
576
+ # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
577
+ if model_class.record_timestamps
578
+ timestamp_column_names.each do |name|
579
+ row[name] = now unless row.key?(name)
580
+ end
581
+ end
582
+
583
+ # interpolate the fixture label
584
+ row.each do |key, value|
585
+ row[key] = label if value == "$LABEL"
586
+ end
587
+
588
+ # generate a primary key if necessary
589
+ if has_primary_key_column? && !row.include?(primary_key_name)
590
+ row[primary_key_name] = Fixtures.identify(label)
591
+ end
592
+
593
+ # If STI is used, find the correct subclass for association reflection
594
+ # reflection_class =
595
+ # if row.include?(inheritance_column_name)
596
+ # row[inheritance_column_name].constantize rescue model_class
597
+ # else
598
+ # model_class
599
+ # end
600
+ reflection_class = model_class
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]
610
+ if value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
611
+ target_type = $1
612
+ target_type_name = (association.options[:foreign_type] || "#{association.name}_type").to_s
613
+
614
+ # support polymorphic belongs_to as "label (Type)"
615
+ row[target_type_name] = target_type
616
+ end
617
+ end
618
+
619
+ row[fk_name] = Fixtures.identify(value)
620
+ end
621
+ when :has_and_belongs_to_many
622
+ if (targets = row.delete(association.name.to_s))
623
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
624
+ join_fixtures = habtm_fixtures[association]
625
+
626
+ targets.each do |target|
627
+ join_fixtures["#{label}_#{target}"] = Fixture.new(
628
+ { association.primary_key_name => row[primary_key_name],
629
+ association.association_foreign_key => Fixtures.identify(target) },
630
+ nil, @connection)
631
+ end
632
+ end
633
+ end
634
+ end
635
+ end
636
+
637
+ Mongoid.insert_fixture(fixture, @table_name)
638
+ end
639
+
640
+ # insert any HABTM join tables we discovered
641
+ habtm_fixtures.values.each do |fixture|
642
+ fixture.delete_existing_fixtures
643
+ fixture.insert_fixtures
644
+ end
645
+ end
646
+
647
+ private
648
+ class HabtmFixtures < ::Fixtures #:nodoc:
649
+ def read_fixture_files; end
650
+ end
651
+
652
+ def model_class
653
+ unless defined?(@model_class)
654
+ @model_class =
655
+ if @class_name.nil? || @class_name.is_a?(Class)
656
+ @class_name
657
+ else
658
+ @class_name.constantize rescue nil
659
+ end
660
+ end
661
+
662
+ @model_class
663
+ end
664
+
665
+ def primary_key_name
666
+ @primary_key_name ||= (model_class && model_class.primary_key ? model_class.primary_key : 'id')
667
+ end
668
+
669
+ def has_primary_key_column?
670
+ @has_primary_key_column ||= model_class && primary_key_name && true
671
+ end
672
+
673
+ def timestamp_column_names
674
+ model_class.respond_to? :record_timestamps
675
+ end
676
+
677
+ def inheritance_column_name
678
+ @inheritance_column_name ||= nil
679
+ # @inheritance_column_name ||= model_class && model_class.inheritance_column
680
+ end
681
+
682
+ def column_names
683
+ # @column_names ||= @connection.columns(@table_name).collect(&:name)
684
+ @column_names ||= []
685
+ end
686
+
687
+ def read_fixture_files
688
+ if File.file?(yaml_file_path)
689
+ read_yaml_fixture_files
690
+ elsif File.file?(csv_file_path)
691
+ read_csv_fixture_files
692
+ end
693
+ end
694
+
695
+ def read_yaml_fixture_files
696
+ yaml_string = ""
697
+ Dir["#{@fixture_path}/**/*.yml"].select { |f| test(?f, f) }.each do |subfixture_path|
698
+ yaml_string << IO.read(subfixture_path)
699
+ end
700
+ yaml_string << IO.read(yaml_file_path)
701
+
702
+ if yaml = parse_yaml_string(yaml_string)
703
+ # If the file is an ordered map, extract its children.
704
+ yaml_value =
705
+ if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
706
+ yaml.value
707
+ else
708
+ [yaml]
709
+ end
710
+
711
+ yaml_value.each do |fixture|
712
+ raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
713
+ fixture.each do |name, data|
714
+ unless data
715
+ raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
716
+ end
717
+
718
+ self[name] = Fixture.new(data, model_class, @connection)
719
+ end
720
+ end
721
+ end
722
+ end
723
+
724
+ def read_csv_fixture_files
725
+ reader = CSV.parse(erb_render(IO.read(csv_file_path)))
726
+ header = reader.shift
727
+ i = 0
728
+ reader.each do |row|
729
+ data = {}
730
+ row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
731
+ self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class, @connection)
732
+ end
733
+ end
734
+
735
+ def yaml_file_path
736
+ "#{@fixture_path}.yml"
737
+ end
738
+
739
+ def csv_file_path
740
+ @fixture_path + ".csv"
741
+ end
742
+
743
+ def yaml_fixtures_key(path)
744
+ File.basename(@fixture_path).split(".").first
745
+ end
746
+
747
+ def parse_yaml_string(fixture_content)
748
+ YAML::load(erb_render(fixture_content))
749
+ rescue => error
750
+ 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}"
751
+ end
752
+
753
+ def erb_render(fixture_content)
754
+ ERB.new(fixture_content).result
755
+ end
756
+ end
757
+
758
+ class Fixture #:nodoc:
759
+ include Enumerable
760
+
761
+ class FixtureError < StandardError #:nodoc:
762
+ end
763
+
764
+ class FormatError < FixtureError #:nodoc:
765
+ end
766
+
767
+ attr_reader :model_class
768
+
769
+ def initialize(fixture, model_class, connection = Mongoid.database)
770
+ @connection = connection
771
+ @fixture = fixture
772
+ @model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil
773
+ end
774
+
775
+ def class_name
776
+ @model_class.name if @model_class
777
+ end
778
+
779
+ def each
780
+ @fixture.each { |item| yield item }
781
+ end
782
+
783
+ def [](key)
784
+ @fixture[key]
785
+ end
786
+
787
+ def to_hash
788
+ @fixture
789
+ end
790
+
791
+ def key_list
792
+ columns = @fixture.keys.collect{ |column_name| @connection.quote_column_name(column_name) }
793
+ columns.join(", ")
794
+ end
795
+
796
+ def value_list
797
+ list = @fixture.inject([]) do |fixtures, (key, value)|
798
+ col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(Mongoid)
799
+ fixtures << @connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
800
+ end
801
+ list * ', '
802
+ end
803
+
804
+ def find
805
+ forced_primary_key = model_class.primary_key ? model_class.primary_key : 'id'
806
+ if model_class
807
+ model_class.find(forced_primary_key)
808
+ else
809
+ raise FixtureClassNotFound, "No class attached to find."
810
+ end
811
+ end
812
+ end
813
+
814
+ module Mongoid
815
+ module TestFixtures
816
+ extend ActiveSupport::Concern
817
+
818
+ included do
819
+ setup :setup_fixtures
820
+ teardown :teardown_fixtures
821
+
822
+ superclass_delegating_accessor :fixture_path
823
+ superclass_delegating_accessor :fixture_table_names
824
+ superclass_delegating_accessor :fixture_class_names
825
+ superclass_delegating_accessor :use_transactional_fixtures
826
+ superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances
827
+ superclass_delegating_accessor :pre_loaded_fixtures
828
+
829
+ self.fixture_table_names = []
830
+ self.use_transactional_fixtures = false
831
+ self.use_instantiated_fixtures = true
832
+ self.pre_loaded_fixtures = false
833
+
834
+ self.fixture_class_names = {}
835
+ end
836
+
837
+ module ClassMethods
838
+ def set_fixture_class(class_names = {})
839
+ self.fixture_class_names = self.fixture_class_names.merge(class_names)
840
+ end
841
+
842
+ def fixtures(*table_names)
843
+ if table_names.first == :all
844
+ table_names = Dir["#{Rails.root}#{fixture_path}/*.yml"] + Dir["#{Rails.root}#{fixture_path}/*.csv"]
845
+ table_names.map! { |f| File.basename(f).split('.')[0..-2].join('.') }
846
+ else
847
+ table_names = table_names.flatten.map { |n| n.to_s }
848
+ end
849
+
850
+ self.fixture_table_names |= table_names
851
+ require_fixture_classes(table_names)
852
+ setup_fixture_accessors(table_names)
853
+ end
854
+
855
+ def try_to_load_dependency(file_name)
856
+ require_dependency file_name
857
+ rescue LoadError => e
858
+ # Let's hope the developer has included it himself
859
+
860
+ # Let's warn in case this is a subdependency, otherwise
861
+ # subdependency error messages are totally cryptic
862
+ mongoid_logger = Mongoid.config.send :logger
863
+ if mongoid_logger
864
+ mongoid_logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
865
+ end
866
+ end
867
+
868
+ def require_fixture_classes(table_names = nil)
869
+ (table_names || fixture_table_names).each do |table_name|
870
+ file_name = table_name.to_s
871
+ try_to_load_dependency(file_name.singularize) # assume singularized model name and pluralized fixture file name
872
+ end
873
+ end
874
+
875
+ def setup_fixture_accessors(table_names = nil)
876
+ table_names = [table_names] if table_names && !table_names.respond_to?(:each)
877
+ (table_names || fixture_table_names).each do |table_name|
878
+ table_name = table_name.to_s.tr('.', '_')
879
+
880
+ define_method(table_name) do |*fixtures|
881
+ force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
882
+
883
+ @fixture_cache[table_name] ||= {}
884
+
885
+ instances = fixtures.map do |fixture|
886
+ @fixture_cache[table_name].delete(fixture) if force_reload
887
+
888
+ if @loaded_fixtures[table_name][fixture.to_s]
889
+ @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find
890
+ else
891
+ raise StandardError, "No fixture with name '#{fixture}' found for table '#{table_name}'"
892
+ end
893
+ end
894
+
895
+ instances.size == 1 ? instances.first : instances
896
+ end
897
+ end
898
+ end
899
+
900
+ def uses_transaction(*methods)
901
+ @uses_transaction = [] unless defined?(@uses_transaction)
902
+ @uses_transaction.concat methods.map(&:to_s)
903
+ end
904
+
905
+ def uses_transaction?(method)
906
+ @uses_transaction = [] unless defined?(@uses_transaction)
907
+ @uses_transaction.include?(method.to_s)
908
+ end
909
+ end
910
+
911
+ def run_in_transaction?
912
+ # use_transactional_fixtures &&
913
+ # !self.class.uses_transaction?(method_name)
914
+ false
915
+ end
916
+
917
+ def setup_fixtures
918
+ return unless defined?(Mongoid) && !Mongoid.config.blank?
919
+
920
+ if pre_loaded_fixtures && !use_transactional_fixtures
921
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
922
+ end
923
+
924
+ @fixture_cache = {}
925
+ @@already_loaded_fixtures ||= {}
926
+
927
+ # Load fixtures files once for all tests.
928
+ if use_instantiated_fixtures && Fixtures.class_variable_get(:@@all_cached_fixtures).size == 0
929
+ Fixtures.reset_cache
930
+ @@already_loaded_fixtures[self.class] = nil
931
+ load_fixtures && instantiate_fixtures
932
+ end
933
+
934
+ end
935
+
936
+ def teardown_fixtures
937
+ return unless defined?(Mongoid) && !Mongoid.config.blank?
938
+
939
+ unless run_in_transaction?
940
+ Fixtures.reset_cache
941
+ end
942
+
943
+ # ActiveRecord::Base.clear_active_connections!
944
+ end
945
+
946
+ private
947
+ def load_fixtures
948
+ @loaded_fixtures = {}
949
+ fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
950
+ unless fixtures.nil?
951
+ if fixtures.instance_of?(Fixtures)
952
+ @loaded_fixtures[fixtures.name] = fixtures
953
+ else
954
+ fixtures.each { |f| @loaded_fixtures[f.name] = f }
955
+ end
956
+ end
957
+ end
958
+
959
+ # for pre_loaded_fixtures, only require the classes once. huge speed improvement
960
+ @@required_fixture_classes = false
961
+
962
+ def instantiate_fixtures
963
+ if pre_loaded_fixtures
964
+ raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
965
+ unless @@required_fixture_classes
966
+ self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
967
+ @@required_fixture_classes = true
968
+ end
969
+ Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
970
+ else
971
+ raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
972
+ @loaded_fixtures.each do |table_name, fixtures|
973
+ Fixtures.instantiate_fixtures(self, table_name, fixtures, load_instances?)
974
+ end
975
+ end
976
+ end
977
+
978
+ def load_instances?
979
+ use_instantiated_fixtures != :no_instances
980
+ end
981
+ end
982
+ end