freegenie-factory_girl 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +9 -0
  2. data/Changelog +29 -0
  3. data/LICENSE +19 -0
  4. data/README.rdoc +236 -0
  5. data/Rakefile +81 -0
  6. data/lib/factory_girl/aliases.rb +50 -0
  7. data/lib/factory_girl/attribute/association.rb +20 -0
  8. data/lib/factory_girl/attribute/dynamic.rb +20 -0
  9. data/lib/factory_girl/attribute/static.rb +17 -0
  10. data/lib/factory_girl/attribute.rb +29 -0
  11. data/lib/factory_girl/factory.rb +412 -0
  12. data/lib/factory_girl/proxy/attributes_for.rb +21 -0
  13. data/lib/factory_girl/proxy/build.rb +29 -0
  14. data/lib/factory_girl/proxy/create.rb +10 -0
  15. data/lib/factory_girl/proxy/stub.rb +49 -0
  16. data/lib/factory_girl/proxy.rb +62 -0
  17. data/lib/factory_girl/sequence.rb +63 -0
  18. data/lib/factory_girl/syntax/blueprint.rb +42 -0
  19. data/lib/factory_girl/syntax/generate.rb +68 -0
  20. data/lib/factory_girl/syntax/make.rb +39 -0
  21. data/lib/factory_girl/syntax/sham.rb +42 -0
  22. data/lib/factory_girl/syntax.rb +12 -0
  23. data/lib/factory_girl.rb +39 -0
  24. data/spec/factory_girl/aliases_spec.rb +29 -0
  25. data/spec/factory_girl/attribute/association_spec.rb +29 -0
  26. data/spec/factory_girl/attribute/dynamic_spec.rb +49 -0
  27. data/spec/factory_girl/attribute/static_spec.rb +29 -0
  28. data/spec/factory_girl/attribute_spec.rb +30 -0
  29. data/spec/factory_girl/factory_spec.rb +525 -0
  30. data/spec/factory_girl/proxy/attributes_for_spec.rb +52 -0
  31. data/spec/factory_girl/proxy/build_spec.rb +73 -0
  32. data/spec/factory_girl/proxy/create_spec.rb +83 -0
  33. data/spec/factory_girl/proxy/stub_spec.rb +69 -0
  34. data/spec/factory_girl/proxy_spec.rb +28 -0
  35. data/spec/factory_girl/sequence_spec.rb +66 -0
  36. data/spec/factory_girl/syntax/blueprint_spec.rb +35 -0
  37. data/spec/factory_girl/syntax/generate_spec.rb +57 -0
  38. data/spec/factory_girl/syntax/make_spec.rb +35 -0
  39. data/spec/factory_girl/syntax/sham_spec.rb +35 -0
  40. data/spec/integration_spec.rb +265 -0
  41. data/spec/models.rb +43 -0
  42. data/spec/spec_helper.rb +18 -0
  43. data/test/factory_girl_test.rb +28 -0
  44. metadata +118 -0
@@ -0,0 +1,412 @@
1
+ class Factory
2
+
3
+ # Raised when a factory is defined that attempts to instantiate itself.
4
+ class AssociationDefinitionError < RuntimeError
5
+ end
6
+
7
+ class << self
8
+ attr_accessor :factories #:nodoc:
9
+
10
+ # An Array of strings specifying locations that should be searched for
11
+ # factory definitions. By default, factory_girl will attempt to require
12
+ # "factories," "test/factories," and "spec/factories." Only the first
13
+ # existing file will be loaded.
14
+ attr_accessor :definition_file_paths
15
+ attr_accessor :live_registry
16
+ end
17
+
18
+ self.factories = {}
19
+ self.definition_file_paths = %w(factories test/factories spec/factories)
20
+
21
+ self.live_registry = []
22
+
23
+ attr_reader :factory_name #:nodoc:
24
+ attr_reader :attributes #:nodoc:
25
+
26
+ # Defines a new factory that can be used by the build strategies (create and
27
+ # build) to build new objects.
28
+ #
29
+ # Arguments:
30
+ # * name: +Symbol+ or +String+
31
+ # A unique name used to identify this factory.
32
+ # * options: +Hash+
33
+ #
34
+ # Options:
35
+ # * class: +Symbol+, +Class+, or +String+
36
+ # The class that will be used when generating instances for this factory. If not specified, the class will be guessed from the factory name.
37
+ # * parent: +Symbol+
38
+ # The parent factory. If specified, the attributes from the parent
39
+ # factory will be copied to the current one with an ability to override
40
+ # them.
41
+ # * default_strategy: +Symbol+
42
+ # The strategy that will be used by the Factory shortcut method.
43
+ # Defaults to :create.
44
+ #
45
+ # Yields: +Factory+
46
+ # The newly created factory.
47
+ def self.define (name, options = {})
48
+ instance = Factory.new(name, options)
49
+ yield(instance)
50
+ if parent = options.delete(:parent)
51
+ instance.inherit_from(Factory.factory_by_name(parent))
52
+ end
53
+ self.factories[instance.factory_name] = instance
54
+ end
55
+
56
+
57
+ def self.find_in_live_registry(name)
58
+ live_registry.each do |value|
59
+ if value[0] == name
60
+ return value
61
+ end
62
+ end
63
+ return false
64
+ end
65
+
66
+ def self.delete_from_live_registry(name, id)
67
+ live_registry.each_with_index do |value, index|
68
+ if value[1] == name && value[2] == id
69
+ live_registry.slice!(index, 1)
70
+ end
71
+ end
72
+ true
73
+ end
74
+
75
+ def class_name #:nodoc:
76
+ @options[:class] || factory_name
77
+ end
78
+
79
+ def build_class #:nodoc:
80
+ @build_class ||= class_for(class_name)
81
+ end
82
+
83
+ def default_strategy #:nodoc:
84
+ @options[:default_strategy] || :create
85
+ # @options[:default_strategy] || :find_or_create
86
+ end
87
+
88
+ def initialize (name, options = {}) #:nodoc:
89
+ assert_valid_options(options)
90
+ @factory_name = factory_name_for(name)
91
+ @options = options
92
+ @attributes = []
93
+ end
94
+
95
+ def inherit_from(parent) #:nodoc:
96
+ @options[:class] ||= parent.class_name
97
+ parent.attributes.each do |attribute|
98
+ unless attribute_defined?(attribute.name)
99
+ @attributes << attribute.clone
100
+ end
101
+ end
102
+ end
103
+
104
+ # Adds an attribute that should be assigned on generated instances for this
105
+ # factory.
106
+ #
107
+ # This method should be called with either a value or block, but not both. If
108
+ # called with a block, the attribute will be generated "lazily," whenever an
109
+ # instance is generated. Lazy attribute blocks will not be called if that
110
+ # attribute is overriden for a specific instance.
111
+ #
112
+ # When defining lazy attributes, an instance of Factory::Proxy will
113
+ # be yielded, allowing associations to be built using the correct build
114
+ # strategy.
115
+ #
116
+ # Arguments:
117
+ # * name: +Symbol+ or +String+
118
+ # The name of this attribute. This will be assigned using :"#{name}=" for
119
+ # generated instances.
120
+ # * value: +Object+
121
+ # If no block is given, this value will be used for this attribute.
122
+ def add_attribute (name, value = nil, &block)
123
+ if block_given?
124
+ if value
125
+ raise AttributeDefinitionError, "Both value and block given"
126
+ else
127
+ attribute = Attribute::Dynamic.new(name, block)
128
+ end
129
+ else
130
+ attribute = Attribute::Static.new(name, value)
131
+ end
132
+
133
+ if attribute_defined?(attribute.name)
134
+ raise AttributeDefinitionError, "Attribute already defined: #{name}"
135
+ end
136
+
137
+ @attributes << attribute
138
+ end
139
+
140
+ # Calls add_attribute using the missing method name as the name of the
141
+ # attribute, so that:
142
+ #
143
+ # Factory.define :user do |f|
144
+ # f.name 'Billy Idol'
145
+ # end
146
+ #
147
+ # and:
148
+ #
149
+ # Factory.define :user do |f|
150
+ # f.add_attribute :name, 'Billy Idol'
151
+ # end
152
+ #
153
+ # are equivilent.
154
+ def method_missing (name, *args, &block)
155
+ add_attribute(name, *args, &block)
156
+ end
157
+
158
+ # Adds an attribute that builds an association. The associated instance will
159
+ # be built using the same build strategy as the parent instance.
160
+ #
161
+ # Example:
162
+ # Factory.define :user do |f|
163
+ # f.name 'Joey'
164
+ # end
165
+ #
166
+ # Factory.define :post do |f|
167
+ # f.association :author, :factory => :user
168
+ # end
169
+ #
170
+ # Arguments:
171
+ # * name: +Symbol+
172
+ # The name of this attribute.
173
+ # * options: +Hash+
174
+ #
175
+ # Options:
176
+ # * factory: +Symbol+ or +String+
177
+ # The name of the factory to use when building the associated instance.
178
+ # If no name is given, the name of the attribute is assumed to be the
179
+ # name of the factory. For example, a "user" association will by
180
+ # default use the "user" factory.
181
+ def association (name, options = {})
182
+ factory_name = options.delete(:factory) || name
183
+ if factory_name_for(factory_name) == self.factory_name
184
+ raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.factory_name}'"
185
+ end
186
+ @attributes << Attribute::Association.new(name, factory_name, options)
187
+ end
188
+
189
+ # Adds an attribute that will have unique values generated by a sequence with
190
+ # a specified format.
191
+ #
192
+ # The result of:
193
+ # Factory.define :user do |f|
194
+ # f.sequence(:email) { |n| "person#{n}@example.com" }
195
+ # end
196
+ #
197
+ # Is equal to:
198
+ # Factory.sequence(:email) { |n| "person#{n}@example.com" }
199
+ #
200
+ # Factory.define :user do |f|
201
+ # f.email { Factory.next(:email) }
202
+ # end
203
+ #
204
+ # Except that no globally available sequence will be defined.
205
+ def sequence (name, &block)
206
+ s = Sequence.new(&block)
207
+ add_attribute(name) { s.next }
208
+ end
209
+
210
+ # Generates and returns a Hash of attributes from this factory. Attributes
211
+ # can be individually overridden by passing in a Hash of attribute => value
212
+ # pairs.
213
+ #
214
+ # Arguments:
215
+ # * name: +Symbol+ or +String+
216
+ # The name of the factory that should be used.
217
+ # * overrides: +Hash+
218
+ # Attributes to overwrite for this set.
219
+ #
220
+ # Returns: +Hash+
221
+ # A set of attributes that can be used to build an instance of the class
222
+ # this factory generates.
223
+ def self.attributes_for (name, overrides = {})
224
+ factory_by_name(name).run(Proxy::AttributesFor, overrides)
225
+ end
226
+
227
+ # Generates and returns an instance from this factory. Attributes can be
228
+ # individually overridden by passing in a Hash of attribute => value pairs.
229
+ #
230
+ # Arguments:
231
+ # * name: +Symbol+ or +String+
232
+ # The name of the factory that should be used.
233
+ # * overrides: +Hash+
234
+ # Attributes to overwrite for this instance.
235
+ #
236
+ # Returns: +Object+
237
+ # An instance of the class this factory generates, with generated attributes
238
+ # assigned.
239
+ def self.build (name, overrides = {})
240
+ factory_by_name(name).run(Proxy::Build, overrides)
241
+ end
242
+
243
+ # Generates, saves, and returns an instance from this factory. Attributes can
244
+ # be individually overridden by passing in a Hash of attribute => value
245
+ # pairs.
246
+ #
247
+ # Instances are saved using the +save!+ method, so ActiveRecord models will
248
+ # raise ActiveRecord::RecordInvalid exceptions for invalid attribute sets.
249
+ #
250
+ # Arguments:
251
+ # * name: +Symbol+ or +String+
252
+ # The name of the factory that should be used.
253
+ # * overrides: +Hash+
254
+ # Attributes to overwrite for this instance.
255
+ #
256
+ # Returns: +Object+
257
+ # A saved instance of the class this factory generates, with generated
258
+ # attributes assigned.
259
+ def self.create (name, overrides = {})
260
+ factory_by_name(name).run(Proxy::Create, overrides)
261
+ end
262
+
263
+ def self.find_or_create (name, overrides = {})
264
+ # check if the item is present in the registry
265
+ just_created = nil
266
+ item = find_in_live_registry(name)
267
+ if item.is_a? Array
268
+ if item[1].methods.include? 'find'
269
+ just_created = item[1].constantize.find(item[2])
270
+ end
271
+ end
272
+ if just_created.nil?
273
+ just_created = factory_by_name(name).run(Proxy::FindOrCreate, overrides)
274
+ live_registry << [name, just_created.class.name, just_created.id]
275
+ end
276
+ just_created
277
+ end
278
+
279
+
280
+ # Generates and returns an object with all attributes from this factory
281
+ # stubbed out. Attributes can be individually overridden by passing in a Hash
282
+ # of attribute => value pairs.
283
+ #
284
+ # Arguments:
285
+ # * name: +Symbol+ or +String+
286
+ # The name of the factory that should be used.
287
+ # * overrides: +Hash+
288
+ # Attributes to overwrite for this instance.
289
+ #
290
+ # Returns: +Object+
291
+ # An object with generated attributes stubbed out.
292
+ def self.stub (name, overrides = {})
293
+ factory_by_name(name).run(Proxy::Stub, overrides)
294
+ end
295
+
296
+ # Executes the default strategy for the given factory. This is usually create,
297
+ # but it can be overridden for each factory.
298
+ #
299
+ # Arguments:
300
+ # * name: +Symbol+ or +String+
301
+ # The name of the factory that should be used.
302
+ # * overrides: +Hash+
303
+ # Attributes to overwrite for this instance.
304
+ #
305
+ # Returns: +Object+
306
+ # The result of the default strategy.
307
+ def self.default_strategy (name, overrides = {})
308
+ self.send(factory_by_name(name).default_strategy, name, overrides)
309
+ end
310
+
311
+ def self.find_definitions #:nodoc:
312
+ definition_file_paths.each do |path|
313
+ require("#{path}.rb") if File.exists?("#{path}.rb")
314
+
315
+ if File.directory? path
316
+ Dir[File.join(path, '*.rb')].each do |file|
317
+ require file
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ def run (proxy_class, overrides) #:nodoc:
324
+ proxy = proxy_class.new(build_class)
325
+ overrides = symbolize_keys(overrides)
326
+ overrides.each {|attr, val| proxy.set(attr, val) }
327
+ passed_keys = overrides.keys.collect {|k| Factory.aliases_for(k) }.flatten
328
+ @attributes.each do |attribute|
329
+ unless passed_keys.include?(attribute.name)
330
+ attribute.add_to(proxy)
331
+ end
332
+ end
333
+ proxy.result
334
+ end
335
+
336
+ def self.factory_by_name (name)
337
+ factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
338
+ end
339
+
340
+ def human_name(*args, &block)
341
+ if args.size == 0 && block.nil?
342
+ factory_name.to_s.gsub('_', ' ')
343
+ else
344
+ add_attribute(:human_name, *args, &block)
345
+ end
346
+ end
347
+
348
+ def associations
349
+ attributes.select {|attribute| attribute.is_a?(Attribute::Association) }
350
+ end
351
+
352
+ private
353
+
354
+ def class_for (class_or_to_s)
355
+ if class_or_to_s.respond_to?(:to_sym)
356
+ Object.const_get(variable_name_to_class_name(class_or_to_s))
357
+ else
358
+ class_or_to_s
359
+ end
360
+ end
361
+
362
+ def factory_name_for (class_or_to_s)
363
+ if class_or_to_s.respond_to?(:to_sym)
364
+ class_or_to_s.to_sym
365
+ else
366
+ class_name_to_variable_name(class_or_to_s).to_sym
367
+ end
368
+ end
369
+
370
+ def attribute_defined? (name)
371
+ !@attributes.detect {|attr| attr.name == name }.nil?
372
+ end
373
+
374
+ def assert_valid_options(options)
375
+ invalid_keys = options.keys - [:class, :parent, :default_strategy]
376
+ unless invalid_keys == []
377
+ raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
378
+ end
379
+ assert_valid_strategy(options[:default_strategy]) if options[:default_strategy]
380
+ end
381
+
382
+ def assert_valid_strategy(strategy)
383
+ unless Factory::Proxy.const_defined? variable_name_to_class_name(strategy)
384
+ raise ArgumentError, "Unknown strategy: #{strategy}"
385
+ end
386
+ end
387
+
388
+ # Based on ActiveSupport's underscore inflector
389
+ def class_name_to_variable_name(name)
390
+ name.to_s.gsub(/::/, '/').
391
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
392
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
393
+ tr("-", "_").
394
+ downcase
395
+ end
396
+
397
+ # Based on ActiveSupport's camelize inflector
398
+ def variable_name_to_class_name(name)
399
+ name.to_s.
400
+ gsub(/\/(.?)/) { "::#{$1.upcase}" }.
401
+ gsub(/(?:^|_)(.)/) { $1.upcase }
402
+ end
403
+
404
+ # From ActiveSupport
405
+ def symbolize_keys(hash)
406
+ hash.inject({}) do |options, (key, value)|
407
+ options[(key.to_sym rescue key) || key] = value
408
+ options
409
+ end
410
+ end
411
+
412
+ end
@@ -0,0 +1,21 @@
1
+ class Factory
2
+ class Proxy #:nodoc:
3
+ class AttributesFor < Proxy #:nodoc:
4
+ def initialize(klass)
5
+ @hash = {}
6
+ end
7
+
8
+ def get(attribute)
9
+ @hash[attribute]
10
+ end
11
+
12
+ def set(attribute, value)
13
+ @hash[attribute] = value
14
+ end
15
+
16
+ def result
17
+ @hash
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ class Factory
2
+ class Proxy #:nodoc:
3
+ class Build < Proxy #:nodoc:
4
+ def initialize(klass)
5
+ @instance = klass.new
6
+ end
7
+
8
+ def get(attribute)
9
+ @instance.send(attribute)
10
+ end
11
+
12
+ def set(attribute, value)
13
+ @instance.send(:"#{attribute}=", value)
14
+ end
15
+
16
+ def associate(name, factory, attributes)
17
+ set(name, Factory.create(factory, attributes))
18
+ end
19
+
20
+ def association(factory, overrides = {})
21
+ Factory.create(factory, overrides)
22
+ end
23
+
24
+ def result
25
+ @instance
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ class Factory
2
+ class Proxy #:nodoc:
3
+ class Create < Build #:nodoc:
4
+ def result
5
+ @instance.save!
6
+ @instance
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,49 @@
1
+ class Factory
2
+ class Proxy
3
+ class Stub < Proxy #:nodoc:
4
+ @@next_id = 1000
5
+
6
+ def initialize(klass)
7
+ @stub = klass.new
8
+ @stub.id = next_id
9
+ @stub.instance_eval do
10
+ def new_record?
11
+ id.nil?
12
+ end
13
+
14
+ def connection
15
+ raise "stubbed models are not allowed to access the database"
16
+ end
17
+
18
+ def reload
19
+ raise "stubbed models are not allowed to access the database"
20
+ end
21
+ end
22
+ end
23
+
24
+ def next_id
25
+ @@next_id += 1
26
+ end
27
+
28
+ def get(attribute)
29
+ @stub.send(attribute)
30
+ end
31
+
32
+ def set(attribute, value)
33
+ @stub.send(:"#{attribute}=", value)
34
+ end
35
+
36
+ def associate(name, factory, attributes)
37
+ set(name, Factory.stub(factory, attributes))
38
+ end
39
+
40
+ def association(factory, overrides = {})
41
+ Factory.stub(factory, overrides)
42
+ end
43
+
44
+ def result
45
+ @stub
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,62 @@
1
+ class Factory
2
+
3
+ class Proxy #:nodoc:
4
+ def initialize(klass)
5
+ end
6
+
7
+ def get(attribute)
8
+ nil
9
+ end
10
+
11
+ def set(attribute, value)
12
+ end
13
+
14
+ def associate(name, factory, attributes)
15
+ end
16
+
17
+ # Generates an association using the current build strategy.
18
+ #
19
+ # Arguments:
20
+ # name: (Symbol)
21
+ # The name of the factory that should be used to generate this
22
+ # association.
23
+ # attributes: (Hash)
24
+ # A hash of attributes that should be overridden for this association.
25
+ #
26
+ # Returns:
27
+ # The generated association for the current build strategy. Note that
28
+ # assocaitions are not generated for the attributes_for strategy. Returns
29
+ # nil in this case.
30
+ #
31
+ # Example:
32
+ #
33
+ # Factory.define :user do |f|
34
+ # # ...
35
+ # end
36
+ #
37
+ # Factory.define :post do |f|
38
+ # # ...
39
+ # f.author {|a| a.association :user, :name => 'Joe' }
40
+ # end
41
+ #
42
+ # # Builds (but doesn't save) a Post and a User
43
+ # Factory.build(:post)
44
+ #
45
+ # # Builds and saves a User, builds a Post, assigns the User to the
46
+ # # author association, and saves the User.
47
+ # Factory.create(:post)
48
+ #
49
+ def association(name, overrides = {})
50
+ nil
51
+ end
52
+
53
+ def method_missing(method, *args, &block)
54
+ get(method)
55
+ end
56
+
57
+ def result
58
+ raise NotImplementedError, "Strategies must return a result"
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,63 @@
1
+ class Factory
2
+
3
+ # Raised when calling Factory.sequence from a dynamic attribute block
4
+ class SequenceAbuseError < StandardError; end
5
+
6
+ # Sequences are defined using Factory.sequence. Sequence values are generated
7
+ # using next.
8
+ class Sequence
9
+
10
+ def initialize (&proc) #:nodoc:
11
+ @proc = proc
12
+ @value = 0
13
+ end
14
+
15
+ # Returns the next value for this sequence
16
+ def next
17
+ @value += 1
18
+ @proc.call(@value)
19
+ end
20
+
21
+ end
22
+
23
+ class << self
24
+ attr_accessor :sequences #:nodoc:
25
+ end
26
+ self.sequences = {}
27
+
28
+ # Defines a new sequence that can be used to generate unique values in a specific format.
29
+ #
30
+ # Arguments:
31
+ # name: (Symbol)
32
+ # A unique name for this sequence. This name will be referenced when
33
+ # calling next to generate new values from this sequence.
34
+ # block: (Proc)
35
+ # The code to generate each value in the sequence. This block will be
36
+ # called with a unique number each time a value in the sequence is to be
37
+ # generated. The block should return the generated value for the
38
+ # sequence.
39
+ #
40
+ # Example:
41
+ #
42
+ # Factory.sequence(:email) {|n| "somebody_#{n}@example.com" }
43
+ def self.sequence (name, &block)
44
+ self.sequences[name] = Sequence.new(&block)
45
+ end
46
+
47
+ # Generates and returns the next value in a sequence.
48
+ #
49
+ # Arguments:
50
+ # name: (Symbol)
51
+ # The name of the sequence that a value should be generated for.
52
+ #
53
+ # Returns:
54
+ # The next value in the sequence. (Object)
55
+ def self.next (sequence)
56
+ unless self.sequences.key?(sequence)
57
+ raise "No such sequence: #{sequence}"
58
+ end
59
+
60
+ self.sequences[sequence].next
61
+ end
62
+
63
+ end
@@ -0,0 +1,42 @@
1
+ class Factory
2
+ module Syntax
3
+
4
+ # Extends ActiveRecord::Base to provide a make class method, which is an
5
+ # alternate syntax for defining factories.
6
+ #
7
+ # Usage:
8
+ #
9
+ # require 'factory_girl/syntax/blueprint'
10
+ #
11
+ # User.blueprint do
12
+ # name { 'Billy Bob' }
13
+ # email { 'billy@bob.example.com' }
14
+ # end
15
+ #
16
+ # Factory(:user, :name => 'Johnny')
17
+ #
18
+ # This syntax was derived from Pete Yandell's machinist.
19
+ module Blueprint
20
+ module ActiveRecord #:nodoc:
21
+
22
+ def self.included(base) # :nodoc:
23
+ base.extend ClassMethods
24
+ end
25
+
26
+ module ClassMethods #:nodoc:
27
+
28
+ def blueprint(&block)
29
+ instance = Factory.new(name.underscore, :class => self)
30
+ instance.instance_eval(&block)
31
+ Factory.factories[instance.factory_name] = instance
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ActiveRecord::Base.send(:include, Factory::Syntax::Blueprint::ActiveRecord)
42
+