stepford 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -92,6 +92,30 @@ Then run:
92
92
 
93
93
  bundle install
94
94
 
95
+ ### Configuration
96
+
97
+ You don't have to use a `config/stepford.rb`, but if you have one, it will load it as needed both in CLI and via helpers, etc.
98
+
99
+ If you don't use the CLI, you can just put it into your `test/test_helper.rb`, `spec/spec_helper.rb`, or similar for the deep_* methods, and with the CLI you could just put it into a Rails environment file.
100
+
101
+ Debug option:
102
+
103
+ Stepford::FactoryGirl.debug = true
104
+
105
+ Make Stepford think that the schema looks different than it does to allow virtual attributes, etc.:
106
+
107
+ Stepford::FactoryGirl.column_overrides = {
108
+ [:bartender, :experience] => {null: false},
109
+ [:patron, :time_entered_bar] => {null: false},
110
+ [:patron, :some_virtual_attribute] => {null: false, virtual: true, type: :string, limit: 5} # if you specify a virtual attribute, be sure to include virtual: true and a valid type
111
+ }
112
+
113
+ Override options are: `:virtual`, `:type`, `:limit`, `:default`, `:null`, `:precision`, and `:scale`. Each is equivalent to their [ActiveRecord column equivalents][column_meta].
114
+
115
+ You can reconfigure it at runtime during tests if you'd like, and you can just call this if you want, but it doesn't have to be loaded this way. No magic involved, but it caches a little, so faster than just doing a `load`:
116
+
117
+ Stepford::FactoryGirl.load_config(pathname)
118
+
95
119
  ### Usage
96
120
 
97
121
  #### Require
@@ -123,22 +147,12 @@ But, you might want to specify traits, and certain attributes or associations or
123
147
 
124
148
  Put this in your `spec/spec_helper.rb`:
125
149
 
126
- require 'stepford/factory_girl_rspec_helpers'
150
+ require 'stepford/factory_girl/rspec_helpers'
127
151
 
128
152
  Then you can just use `deep_create`, `deep_create_list`, `deep_build`, `deep_build_list`, or `deep_build_stubbed` in your rspec tests (`deep_create` becomes a shortcut for `::Stepford::FactoryGirl.create`, etc.), e.g.:
129
153
 
130
154
  deep_create(:foo)
131
155
 
132
- ##### Forcing Attributes and Associations
133
-
134
- If you want to force attributes and associations to be set, use the not_null configuration setting, or hand-edit the factories.rb:
135
-
136
- # each entry looks like [:model_name, :association_or_column_name]
137
- Stepford::FactoryGirl.not_null = [
138
- [:bartender, :experience],
139
- [:patron, :time_entered_bar],
140
- ]
141
-
142
156
  ##### Cleaning Up
143
157
 
144
158
  If you just want to run rspec at command-line, want to be able to create in before hooks, and don't want to mess with database cleaner, here is some code that you can add to your spec_helper to remove all model instances.
@@ -328,7 +342,7 @@ See [Testing all Factories (with RSpec)][test_factories] in the FactoryGirl wiki
328
342
  Here is a version that tests the FactoryGirl factories and the Stepford deep_creates:
329
343
 
330
344
  require 'spec_helper'
331
- require 'stepford/factory_girl_rspec_helpers'
345
+ require 'stepford/factory_girl/rspec_helpers'
332
346
 
333
347
  describe 'validate factories build' do
334
348
  FactoryGirl.factories.each do |factory|
@@ -407,6 +421,7 @@ or referring to created objects through associations, though he said multiple ne
407
421
 
408
422
  Copyright (c) 2012 Gary S. Weaver, released under the [MIT license][lic].
409
423
 
424
+ [column_meta]: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-column
410
425
  [composite_primary_keys]: https://github.com/drnic/composite_primary_keys
411
426
  [test_factories]: https://github.com/thoughtbot/factory_girl/wiki/Testing-all-Factories-%28with-RSpec%29
412
427
  [factory_girl]: https://github.com/thoughtbot/factory_girl/
@@ -76,7 +76,6 @@ module Stepford
76
76
  model_class.reflections.collect {|association_name, reflection|
77
77
  puts "warning: #{model_class}'s association #{reflection.name}'s foreign_key was nil. can't check." unless reflection.foreign_key
78
78
  assc_sym = reflection.name.to_sym
79
- clas_sym = reflection.class_name.underscore.to_sym
80
79
 
81
80
  begin
82
81
  next_class = reflection.class_name.constantize
data/lib/stepford/cli.rb CHANGED
@@ -17,8 +17,8 @@ module Stepford
17
17
  # load Rails environment
18
18
  require './config/environment'
19
19
  # load FactoryGirl and generate factories
20
- require 'stepford/factory_girl_generator'
21
- exit Stepford::FactoryGirlGenerator.generate_factories(options) ? 0 : 1
20
+ require 'stepford/factory_girl/generator'
21
+ exit ::Stepford::FactoryGirl::Generator.generate_factories(options) ? 0 : 1
22
22
  end
23
23
 
24
24
  desc "circular", "check for circular refs"
@@ -28,9 +28,9 @@ module Stepford
28
28
  require './config/environment'
29
29
  # load FactoryGirl and generate factories
30
30
  require 'stepford/circular_ref_checker'
31
- exit Stepford::CircularRefChecker.check_refs(options) ? 0 : 1
31
+ exit ::Stepford::CircularRefChecker.check_refs(options) ? 0 : 1
32
32
  end
33
33
  end
34
34
  end
35
35
 
36
- Stepford::CLI.start
36
+ ::Stepford::CLI.start
@@ -0,0 +1,28 @@
1
+ module Stepford
2
+ # Needed a column representation that would allow user to specify attributes that are used for sample data choice for virtual attributes
3
+ # e.g. if you have an object_id column in the schema, but in model you have virtual proxy attribute methods to set it like my_object_id/my_object_id=
4
+ # then you need a way to specify that it should set my_object_id= with a string vs. number, etc.
5
+ class ColumnRepresentation
6
+ attr_accessor :name, :type, :limit, :default, :null, :precision, :scale, :virtual
7
+
8
+ def initialize(args)
9
+ if args.is_a?(Symbol)
10
+ @name = args.to_sym
11
+ elsif !(args.nil?)
12
+ # assume initializing with column
13
+ # see: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-column
14
+ @name = args.name
15
+ @type = args.type
16
+ @limit = args.limit
17
+ @default = args.default
18
+ @null = args.null # should be called nullable, but using what Rails/AR calls it to be easier to work with as if were a "real" AR column
19
+ @precision = args.precision
20
+ @scale = args.scale
21
+ end
22
+ end
23
+
24
+ def merge_options(options)
25
+ options.each {|k,v|instance_variable_set("@#{k}", v)}
26
+ end
27
+ end
28
+ end
@@ -1,213 +1 @@
1
- require 'factory_girl'
2
- require 'bigdecimal'
3
-
4
- module Stepford
5
- # A proxy for FactoryGirl that automatically recursively creates/builds/stubbed factories for null=false and/or presence validated associations.
6
- #
7
- # Lets you specify method name and arguments/options to factory girl for associations.
8
- #
9
- # e.g. if the following is required:
10
- # * Bar has a required association called house_special which uses the :beer factory, and we have a block we want to send into it
11
- # * Beer has specials that you want to build as a list of 3 using the :tuesday_special_offer factory
12
- # then you could set that up like this:
13
- # Stepford::FactoryGirl.create_list(:bar, with_factory_options: {
14
- # house_special: [:create, :beer, {blk: ->(beer) do; beer.bubbles.create(attributes_for(:bubbles)); end}],
15
- # specials: [:build_list, :tuesday_special_offer, 3]
16
- # }) do
17
- # # the block you would send to FactoryGirl.create_list(:foo) would go here
18
- # end
19
- module FactoryGirl
20
- OPTIONS = [
21
- :debug,
22
- :not_null
23
- ]
24
-
25
- class << self
26
- OPTIONS.each{|o|attr_accessor o; define_method("#{o}?".to_sym){!!send("#{o}")}}
27
- def configure(&blk); class_eval(&blk); end
28
-
29
- def handle_factory_girl_method(m, *args, &block)
30
-
31
- if args && args.size > 0
32
- # call Stepford::FactoryGirl.* on any not null associations recursively
33
- model_name = args[0]
34
- begin
35
- model_class = model_name.to_s.camelize.constantize
36
- rescue => e
37
- puts "Problem in #{model_name.to_s.camelize}" if model_name
38
- raise e
39
- end
40
-
41
- args = args.dup # need local version because we'll be dup'ing the options hash to add things to set prior to create/build
42
- options = args.last
43
- if options.is_a?(Hash)
44
- # keep them separate
45
- orig_options = options
46
- options = deep_dup(options)
47
- args[(args.size - 1)] = options # need to set the dup'd options
48
- else
49
- # keep them separate
50
- orig_options = {}
51
- options = {}
52
- args << options # need to add options to set associations
53
- end
54
-
55
- options[:with_factory_options] = {} unless options[:with_factory_options]
56
- with_factory_options = options[:with_factory_options]
57
-
58
- orig_options[:nesting_breadcrumbs] = [] unless orig_options[:nesting_breadcrumbs]
59
- breadcrumbs = orig_options[:nesting_breadcrumbs]
60
- breadcrumbs << [args[0]]
61
-
62
- orig_options[:to_reload] = [] unless orig_options[:to_reload]
63
- to_reload = orig_options[:to_reload]
64
-
65
- if ::Stepford::FactoryGirl.debug?
66
- puts "#{breadcrumbs.join('>')} start. args=#{debugargs(args)}"
67
- end
68
-
69
- model_class.reflections.each do |association_name, reflection|
70
- assc_sym = reflection.name.to_sym
71
- next if options[assc_sym] || options[reflection.foreign_key.to_sym] # || reflection.macro != :belongs_to
72
-
73
- clas_sym = reflection.class_name.underscore.to_sym
74
- has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(::ActiveModel::Validations::PresenceValidator)
75
- required = reflection.foreign_key ? (has_presence_validator || model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym}) : false
76
- orig_method_args_and_options = with_factory_options ? (with_factory_options[[clas_sym, assc_sym]] || with_factory_options[clas_sym]) : nil
77
-
78
- has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator)
79
- required = false
80
- if reflection.macro == :belongs_to
81
- # note: supports composite_primary_keys gem which stores primary_key as an array
82
- foreign_key_is_also_primary_key = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}.include?(reflection.foreign_key.to_sym)
83
- is_not_null_fkey_that_is_not_primary_key = model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym && !foreign_key_is_also_primary_key}
84
- required = is_not_null_fkey_that_is_not_primary_key || has_presence_validator
85
- else
86
- # no nullable metadata on column if no foreign key in this table. we'd figure out the null requirement on the column if inspecting the child model
87
- required = has_presence_validator
88
- end
89
-
90
- if required || Array.wrap(::Stepford::FactoryGirl.not_null).compact.include?([model_name.to_sym, assc_sym])
91
- breadcrumbs << ["a:#{assc_sym}"]
92
- if orig_method_args_and_options
93
- method_args_and_options = orig_method_args_and_options.dup
94
- method_options = args.last
95
- blk = method_options.is_a?(Hash) ? method_args_and_options.delete(:blk) : nil
96
- begin
97
- if blk
98
- options[assc_sym] = ::FactoryGirl.__send__(*method_args_and_options, &blk)
99
- else
100
- options[assc_sym] = ::FactoryGirl.__send__(*method_args_and_options)
101
- end
102
- to_reload << options[assc_sym]
103
- rescue => e
104
- puts "#{breadcrumbs.join('>')}: FactoryGirl.__send__(#{method_args_and_options.inspect}): #{e}#{::Stepford::FactoryGirl.debug? ? "\n#{e.backtrace.join("\n")}" : ''}"
105
- raise e
106
- end
107
- else
108
- if reflection.macro == :has_many
109
- options[assc_sym] = ::Stepford::FactoryGirl.create_list(clas_sym, 2, orig_options)
110
- else
111
- options[assc_sym] = ::Stepford::FactoryGirl.create(clas_sym, orig_options)
112
- end
113
- end
114
- breadcrumbs.pop
115
- end
116
- end
117
- end
118
-
119
- if defined?(breadcrumbs)
120
- if ::Stepford::FactoryGirl.debug?
121
- puts "#{breadcrumbs.join('>')} end"
122
- puts "#{breadcrumbs.join('>')} FactoryGirl.#{m}(#{debugargs(args)})"
123
- end
124
- breadcrumbs.pop
125
- end
126
-
127
- # clean-up before sending to FactoryGirl
128
- if args.last.is_a?(Hash)
129
- (args.last).delete(:with_factory_options)
130
- (args.last).delete(:nesting_breadcrumbs)
131
- (args.last).delete(:to_reload)
132
- end
133
-
134
- begin
135
- raise "#{breadcrumbs.join('>')} - Huh? args[0] was #{args[0]}. m=#{m.inspect}, args=#{args.inspect}" if args && args.size > 1 && !(args[0].is_a?(String) || args[0].is_a?(Symbol))
136
- result = ::FactoryGirl.__send__(m, *args, &block)
137
- rescue => e
138
- puts "#{breadcrumbs.join('>')}: FactoryGirl.#{m}(#{args.inspect}): #{e}#{::Stepford::FactoryGirl.debug? ? "\n#{e.backtrace.join("\n")}" : ''}" if defined?(breadcrumbs)
139
- raise e
140
- end
141
-
142
- if args.last.is_a?(Hash) && defined?(breadcrumbs) && breadcrumbs.size > 0
143
- # still handling association/subassociation
144
- args.last[:nesting_breadcrumbs] = breadcrumbs
145
- args.last[:to_reload] = to_reload
146
- orig_options[:to_reload] << result
147
- else
148
- # ready to return the initially requested instances, so reload children with their parents, in reverse order added
149
- orig_options[:to_reload].each do |i|
150
- begin
151
- i.reload
152
- rescue => e
153
- puts "#{i} reload failed: #{e}\n#{e.backtrace.join("\n")}" if ::Stepford::FactoryGirl.debug?
154
- end
155
- end
156
- end
157
-
158
- result
159
- end
160
-
161
- # switched to this from method_missing to avoid method trying to handle mistaken calls
162
- def create(*args, &block); handle_factory_girl_method(:create, *args, &block); end
163
- def create_list(*args, &block); handle_factory_girl_method(:create_list, *args, &block); end
164
- def build(*args, &block); handle_factory_girl_method(:build, *args, &block); end
165
- def build_list(*args, &block); handle_factory_girl_method(:build_list, *args, &block); end
166
- def build_stubbed(*args, &block); handle_factory_girl_method(:build_stubbed, *args, &block); end
167
- # pass everything else to FactoryGirl to try to handle (can't reflect in current version to find what it handles)
168
- def method_missing(m, *args, &block); ::FactoryGirl.__send__(m, *args, &block); end
169
-
170
- def deep_dup(o)
171
- result = nil
172
- if o.is_a?(Hash)
173
- result = {}
174
- o.keys.each do |key|
175
- result[deep_dup(key)] = deep_dup(o[key])
176
- end
177
- elsif o.is_a?(Array)
178
- result = []
179
- o.each do |value|
180
- result << deep_dup(value)
181
- end
182
- elsif [NilClass,FalseClass,TrueClass,Symbol,Numeric,Class,Module].any?{|c|o.is_a?(c)}
183
- result = o
184
- elsif o.is_a?(BigDecimal)
185
- # ActiveSupport v3.2.8 checks duplicable? for BigDecimal by testing it, so we'll just try to dup the value itself
186
- result = o
187
- begin
188
- result = o.dup
189
- rescue TypeError
190
- # can't dup
191
- end
192
- elsif o.is_a?(Object)
193
- result = o.dup
194
- else
195
- result = o
196
- end
197
- result
198
- end
199
-
200
- def debugargs(args)
201
- result = []
202
- args.each do |arg|
203
- if arg.is_a?(Hash)
204
- result << "{#{arg.keys.collect{|key|"#{key} = (#{arg[key].class.name})"}.join(', ')}}"
205
- else
206
- result << "#{arg.inspect},"
207
- end
208
- end
209
- result.join('')
210
- end
211
- end
212
- end
213
- end
1
+ require 'stepford/factory_girl/methods'
@@ -0,0 +1,54 @@
1
+ module Stepford
2
+ module FactoryGirl
3
+ OPTIONS = [:debug, :column_overrides, :config_loaded, :column_overrides_tree]
4
+
5
+ class << self
6
+ OPTIONS.each{|o|attr_accessor o; define_method("#{o}?".to_sym){!!send("#{o}")}}
7
+ def configure(&blk); class_eval(&blk); end
8
+
9
+ # Loads the configuration from config/stepford.rb or a specified pathname unless has already been loaded.
10
+ def load_config(pathname = nil)
11
+ if !(pathname || ::Stepford::FactoryGirl.config_loaded) || (pathname && ::Stepford::FactoryGirl.config_loaded.to_sym != pathname.to_sym)
12
+ begin
13
+ if pathname
14
+ # load without checking if exists to raise error if user-specified file is missing
15
+ force_configure(pathname)
16
+ else
17
+ pathname = Rails.root.join('config', 'stepford.rb').to_s
18
+ if File.exist?(pathname)
19
+ force_configure(pathname)
20
+ end
21
+ end
22
+ rescue => e
23
+ puts "Failed to load #{pathname}:\n#{e.message}#{e.backtrace.join("\n")}"
24
+ end
25
+ end
26
+ end
27
+
28
+ def force_configure(pathname)
29
+ load pathname
30
+ puts "Loaded #{pathname}"
31
+ ::Stepford::FactoryGirl.config_loaded = pathname
32
+ end
33
+
34
+ def column_overrides=(args)
35
+ # to avoid a lot of processing overhead, we preprocess the arrays into a hash that would look ugly to the user, e.g.
36
+ # {:model_name => {:attribute_name => {options or just empty}}}
37
+ result = {}
38
+ args.each do |k,v|
39
+ if k.is_a?(Array) && k.size == 2 && v.is_a?(Hash)
40
+ table_columns = (result[k[0].to_sym] ||= {})
41
+ table_column_options = (table_columns[k[1].to_sym] ||= {})
42
+ table_column_options.merge(v)
43
+ else
44
+ puts "Ignoring bad Stepford::FactoryGirl.column_overrides array value: #{a.inspect}. Should look like [:model_name, :attribute_name, {}]. See documentation for information on defining options hash."
45
+ end
46
+ end if args
47
+ @column_overrides = args
48
+ @column_overrides_tree = result
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ ::Stepford::FactoryGirl.load_config
@@ -0,0 +1,198 @@
1
+ require 'stepford/column_representation'
2
+ require 'stepford/common'
3
+ require 'stepford/factory_girl/config'
4
+
5
+ module Stepford
6
+ module FactoryGirl
7
+ class Generator
8
+ def self.generate_factories(options={})
9
+ factories = {}
10
+ expected = {}
11
+ included_model_syms = options[:models] ? options[:models].split(',').collect{|s|s.strip.to_sym}.compact : nil
12
+ puts "Rails.root=#{Rails.root}"
13
+ Dir[Rails.root.join('app','models','*.rb').to_s].each do |filename|
14
+ model_name = File.basename(filename).sub(/.rb$/, '')
15
+ model_name_sym = model_name.to_sym
16
+ next if included_model_syms && !included_model_syms.include?(model_name_sym)
17
+ load filename
18
+
19
+ begin
20
+ model_class = model_name.camelize.constantize
21
+ rescue => e
22
+ puts "Problem in #{model_name.camelize}"
23
+ raise e
24
+ end
25
+
26
+ next unless model_class.ancestors.include?(ActiveRecord::Base)
27
+ factory = (factories[model_name_sym] ||= [])
28
+ pkey_syms = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}
29
+ excluded_attribute_syms = [:updated_at, :created_at, :object_id]
30
+ excluded_attribute_syms_and_pkeys = pkey_syms + [:updated_at, :created_at, :object_id]
31
+ model_class.reflections.collect {|association_name, reflection|
32
+ (expected[reflection.class_name.underscore.to_sym] ||= []) << model_name_sym
33
+ fkey_sym = reflection.foreign_key.try(:to_sym)
34
+ excluded_attribute_syms_and_pkeys << fkey_sym if reflection.foreign_key && !(excluded_attribute_syms_and_pkeys.include?(fkey_sym))
35
+ assc_sym = reflection.name.to_sym
36
+ clas_sym = reflection.class_name.underscore.to_sym
37
+ # if has a foreign key, then if NOT NULL or is a presence validate, the association is required and should be output. unfortunately this could mean a circular reference that will have to be manually fixed
38
+ has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator)
39
+ required = reflection.foreign_key ? (has_presence_validator || model_class.columns.any?{|c| !c.null && c.name.to_sym == fkey_sym}) : false
40
+ should_be_trait = !(options[:associations] || (options[:include_required_associations] && required)) && options[:association_traits]
41
+ if options[:associations] || (options[:include_required_associations] && required) || should_be_trait
42
+ if reflection.macro == :has_many
43
+ # In factory girl v4.1.0:
44
+ # create_list must be done in an after(:create) or you get Trait not registered or Factory not registered errors.
45
+ # this means that validators that verify presence or size > 0 in a association list will not work with this method, and you'll need to
46
+ # use build, not create: http://stackoverflow.com/questions/11209347/has-many-with-at-least-two-entries
47
+ "#{should_be_trait ? "trait #{"with_#{assc_sym}".to_sym.inspect} do; " : ''}#{should_be_trait || has_presence_validator ? '' : '#'}after(:create) do |user, evaluator|; FactoryGirl.create_list #{clas_sym.inspect}, 2; end#{should_be_trait ? '; end' : ''}#{should_be_trait ? '' : ' # commented to avoid circular reference'}"
48
+ elsif assc_sym != clas_sym
49
+ "#{should_be_trait ? "trait #{"with_#{assc_sym}".to_sym.inspect} do; " : ''}#{should_be_trait || reflection.macro == :belongs_to || has_presence_validator ? '' : '#'}association #{assc_sym.inspect}#{assc_sym != clas_sym ? ", factory: #{clas_sym.inspect}" : ''}#{should_be_trait ? '; end' : ''}#{should_be_trait || reflection.macro == :belongs_to ? '' : ' # commented to avoid circular reference'}"
50
+ else
51
+ "#{should_be_trait ? "trait #{"with_#{assc_sym}".to_sym.inspect} do; " : ''}#{should_be_trait || reflection.macro == :belongs_to || has_presence_validator ? '' : '#'}#{is_reserved?(assc_sym) ? 'self.' : ''}#{assc_sym}#{should_be_trait ? '; end' : ''}#{should_be_trait || reflection.macro == :belongs_to ? '' : ' # commented to avoid circular reference'}"
52
+ end
53
+ else
54
+ nil
55
+ end
56
+ }.compact.sort.each {|l|factory << l}
57
+
58
+ sequenceless_table = false
59
+ begin
60
+ sequenceless_table = true unless m.sequence_name
61
+ rescue => e
62
+ # bug in Rails 3.2.8, at least: undefined method `split' for nil:NilClass in activerecord-3.2.8/lib/active_record/connection_adapters/postgresql_adapter.rb:911:in `default_sequence_name'
63
+ sequenceless_table = true
64
+ end
65
+
66
+ model_column_name_sym_to_column_representation = {}
67
+ model_class.columns.each {|c|model_column_name_sym_to_column_representation[c.name.to_sym] = ::Stepford::ColumnRepresentation.new(c)}
68
+ ::Stepford::FactoryGirl.column_overrides_tree[model_name_sym].each do |column_override_attr_sym, column_override_options|
69
+ (model_column_name_sym_to_column_representation[column_override_attr_sym] ||= ::Stepford::ColumnRepresentation.new(column_override_attr_sym)).merge_options(column_override_options)
70
+ end if ::Stepford::FactoryGirl.column_overrides_tree[model_name_sym]
71
+
72
+ model_column_name_sym_to_column_representation.values {|c|[c.name]}.each {|c|
73
+ # without a sequence, FactoryGirl will need to have sequences for pkeys
74
+ if sequenceless_table && excluded_attribute_syms.include?(c.name.to_sym)
75
+ factory << ::Stepford::Common.sequence_for(c)
76
+ elsif !excluded_attribute_syms_and_pkeys.include?(c.name.to_sym) && !(c.name.to_s.downcase.end_with?('_id') && options[:exclude_all_ids]) && (options[:attributes] || !c.null)
77
+ has_uniqueness_validator = model_class.validators_on(c.name.to_sym).collect{|v|v.class}.include?(ActiveRecord::Validations::UniquenessValidator)
78
+ if has_uniqueness_validator
79
+ #TODO: specialize for different data types
80
+ factory << ::Stepford::Common.sequence_for(c)
81
+ else
82
+ factory << "#{is_reserved?(c.name) ? 'self.' : ''}#{c.name} #{Stepford::Common.value_for(c)}"
83
+ end
84
+ elsif options[:attribute_traits]
85
+ if c.type == :boolean
86
+ factory << "trait #{c.name.underscore.to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} true; end"
87
+ factory << "trait #{"not_#{c.name.underscore}".to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} false; end"
88
+ else
89
+ factory << "trait #{"with_#{c.name.underscore}".to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} #{Stepford::Common.value_for(c)}; end"
90
+ end
91
+ end
92
+ }
93
+ end
94
+
95
+ if options[:associations] || options[:validate_associations]
96
+ failed = false
97
+ model_to_fixes_required = {}
98
+ expected.keys.sort.each do |factory_name_sym|
99
+ unless factories[factory_name_sym]
100
+ puts "#{Rails.root.join('app','models',"#{factory_name_sym}.rb")} missing. Model(s) with associations to it: #{expected[factory_name_sym].sort.join(', ')}"
101
+ expected[factory_name_sym].each do |model_name|
102
+ (model_to_fixes_required[model_name_sym] ||= []) << factory_name_sym
103
+ end
104
+ failed = true
105
+ end
106
+ end
107
+ model_to_fixes_required.keys.each do |model_to_fix|
108
+ puts ""
109
+ puts "In #{model_to_fix}:"
110
+ model_to_fixes_required[model_to_fix].each do |fix|
111
+ puts "- comment/remove/fix broken association to #{fix}"
112
+ end
113
+ end
114
+ return false if failed
115
+ end
116
+
117
+ path = get_factories_rb_pathname(options)
118
+
119
+ if path.end_with?('.rb')
120
+ dirpath = File.dirname(path)
121
+ unless File.directory?(dirpath)
122
+ puts "Please create this directory first: #{dirpath}"
123
+ return false
124
+ end
125
+
126
+ File.open(path, "w") do |f|
127
+ write_header(f, options)
128
+ factories.keys.sort.each do |factory_name_sym|
129
+ factory = factories[factory_name_sym]
130
+ write_factory(factory_name_sym, factory, f)
131
+ end
132
+ write_footer(f)
133
+ end
134
+ else
135
+ unless File.directory?(path)
136
+ puts "Please create this directory first: #{path}"
137
+ return false
138
+ end
139
+
140
+ factories.keys.sort.each do |factory_name_sym|
141
+ factory = factories[factory_name_sym]
142
+ File.open(File.join(path,"#{factory_name_sym}.rb"), "w") do |f|
143
+ write_header(f, options)
144
+ write_factory(factory_name_sym, factory, f)
145
+ write_footer(f)
146
+ end
147
+ end
148
+ end
149
+
150
+ return true
151
+ end
152
+
153
+ private
154
+
155
+ def self.is_reserved?(s)
156
+ # modified from http://stackoverflow.com/questions/6461303/built-in-way-to-determine-whether-a-string-is-a-ruby-reserved-word/6461673#6461673
157
+ %w{__FILE__ __LINE__ alias and begin BEGIN break case class def defined? do else elsif end END ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield}.include? s.to_s
158
+ end
159
+
160
+ def self.get_factories_rb_pathname(options)
161
+ path = Rails.root.join('test','factories.rb')
162
+ if options[:path]
163
+ if options[:path].end_with?('.rb')
164
+ path = options[:path]
165
+ else
166
+ if options[:multiple]
167
+ path = options[:path]
168
+ else
169
+ path = Rails.root.join(options[:path],'factories.rb')
170
+ end
171
+ end
172
+ end
173
+ # convert Pathname to string
174
+ path.to_s
175
+ end
176
+
177
+ def self.write_header(f, options)
178
+ f.puts '# original version autogenerated by Stepford: https://github.com/garysweaver/stepford'
179
+ f.puts ''
180
+ f.puts 'FactoryGirl.define do'
181
+ f.puts ' '
182
+ end
183
+
184
+ def self.write_factory(factory_name, factory, f)
185
+ f.puts " factory #{factory_name.inspect} do"
186
+ factory.each do |line|
187
+ f.puts " #{line}"
188
+ end
189
+ f.puts ' end'
190
+ f.puts ' '
191
+ end
192
+
193
+ def self.write_footer(f)
194
+ f.puts 'end'
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,205 @@
1
+ require 'factory_girl'
2
+ require 'bigdecimal'
3
+ require 'stepford/factory_girl/config'
4
+
5
+ module Stepford
6
+ # Automatically recursively creates/builds/stubbed associations using FactoryGirl.
7
+ #
8
+ # You can specify the method name and arguments/options to factory girl for the associations. e.g. if the following is required:
9
+ # * Bar has a required association called house_special which uses the :beer factory, and we have a block we want to send into it
10
+ # * Beer has specials that you want to build as a list of 3 using the :tuesday_special_offer factory
11
+ # you could do that with:
12
+ # Stepford::FactoryGirl.create_list(:bar, with_factory_options: {
13
+ # house_special: [:create, :beer, {blk: ->(beer) do; beer.bubbles.create(attributes_for(:bubbles)); end}],
14
+ # specials: [:build_list, :tuesday_special_offer, 3]
15
+ # }) do
16
+ # # the block you would send to FactoryGirl.create_list(:foo) would go here
17
+ # end
18
+ module FactoryGirl
19
+
20
+ class << self
21
+
22
+ def handle_factory_girl_method(m, *args, &block)
23
+ if args && args.size > 0
24
+ # call Stepford::FactoryGirl.* on any not null associations recursively
25
+ model_name = args[0]
26
+ begin
27
+ model_class = model_name.to_s.camelize.constantize
28
+ rescue => e
29
+ puts "Problem in #{model_name.to_s.camelize}" if model_name
30
+ raise e
31
+ end
32
+
33
+ args = args.dup # need local version because we'll be dup'ing the options hash to add things to set prior to create/build
34
+ options = args.last
35
+ if options.is_a?(Hash)
36
+ # keep them separate
37
+ orig_options = options
38
+ options = deep_dup(options)
39
+ args[(args.size - 1)] = options # need to set the dup'd options
40
+ else
41
+ # keep them separate
42
+ orig_options = {}
43
+ options = {}
44
+ args << options # need to add options to set associations
45
+ end
46
+
47
+ options[:with_factory_options] = {} unless options[:with_factory_options]
48
+ with_factory_options = options[:with_factory_options]
49
+
50
+ orig_options[:nesting_breadcrumbs] = [] unless orig_options[:nesting_breadcrumbs]
51
+ breadcrumbs = orig_options[:nesting_breadcrumbs]
52
+ breadcrumbs << [args[0]]
53
+
54
+ orig_options[:to_reload] = [] unless orig_options[:to_reload]
55
+ to_reload = orig_options[:to_reload]
56
+
57
+ if ::Stepford::FactoryGirl.debug?
58
+ puts "#{breadcrumbs.join('>')} start. args=#{debugargs(args)}"
59
+ end
60
+
61
+ model_class.reflections.each do |association_name, reflection|
62
+ assc_sym = reflection.name.to_sym
63
+ next if options[assc_sym] || options[reflection.foreign_key.to_sym] # || reflection.macro != :belongs_to
64
+
65
+ clas_sym = reflection.class_name.underscore.to_sym
66
+ has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(::ActiveModel::Validations::PresenceValidator)
67
+ required = reflection.foreign_key ? (has_presence_validator || model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym}) : false
68
+ orig_method_args_and_options = with_factory_options ? (with_factory_options[[clas_sym, assc_sym]] || with_factory_options[clas_sym]) : nil
69
+
70
+ has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator)
71
+ required = false
72
+ if reflection.macro == :belongs_to
73
+ # note: supports composite_primary_keys gem which stores primary_key as an array
74
+ foreign_key_is_also_primary_key = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}.include?(reflection.foreign_key.to_sym)
75
+ is_not_null_fkey_that_is_not_primary_key = model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym && !foreign_key_is_also_primary_key}
76
+ required = is_not_null_fkey_that_is_not_primary_key || has_presence_validator
77
+ else
78
+ # no nullable metadata on column if no foreign key in this table. we'd figure out the null requirement on the column if inspecting the child model
79
+ required = has_presence_validator
80
+ end
81
+
82
+ if required || Array.wrap(::Stepford::FactoryGirl.column_overrides).compact.include?([model_name.to_sym, assc_sym])
83
+ breadcrumbs << ["a:#{assc_sym}"]
84
+ if orig_method_args_and_options
85
+ method_args_and_options = orig_method_args_and_options.dup
86
+ method_options = args.last
87
+ blk = method_options.is_a?(Hash) ? method_args_and_options.delete(:blk) : nil
88
+ begin
89
+ if blk
90
+ options[assc_sym] = ::FactoryGirl.__send__(*method_args_and_options, &blk)
91
+ else
92
+ options[assc_sym] = ::FactoryGirl.__send__(*method_args_and_options)
93
+ end
94
+ to_reload << options[assc_sym]
95
+ rescue => e
96
+ puts "#{breadcrumbs.join('>')}: FactoryGirl.__send__(#{method_args_and_options.inspect}): #{e}#{::Stepford::FactoryGirl.debug? ? "\n#{e.backtrace.join("\n")}" : ''}"
97
+ raise e
98
+ end
99
+ else
100
+ if reflection.macro == :has_many
101
+ options[assc_sym] = ::Stepford::FactoryGirl.create_list(clas_sym, 2, orig_options)
102
+ else
103
+ options[assc_sym] = ::Stepford::FactoryGirl.create(clas_sym, orig_options)
104
+ end
105
+ end
106
+ breadcrumbs.pop
107
+ end
108
+ end
109
+
110
+ if defined?(breadcrumbs)
111
+ if ::Stepford::FactoryGirl.debug?
112
+ puts "#{breadcrumbs.join('>')} end"
113
+ puts "#{breadcrumbs.join('>')} FactoryGirl.#{m}(#{debugargs(args)})"
114
+ end
115
+ breadcrumbs.pop
116
+ end
117
+
118
+ # clean-up before sending to FactoryGirl
119
+ if args.last.is_a?(Hash)
120
+ (args.last).delete(:with_factory_options)
121
+ (args.last).delete(:nesting_breadcrumbs)
122
+ (args.last).delete(:to_reload)
123
+ end
124
+ end
125
+
126
+ begin
127
+ raise "#{breadcrumbs.join('>')} - Huh? args[0] was #{args[0]}. m=#{m.inspect}, args=#{args.inspect}" if args && args.size > 1 && !(args[0].is_a?(String) || args[0].is_a?(Symbol))
128
+ result = ::FactoryGirl.__send__(m, *args, &block)
129
+ rescue => e
130
+ puts "#{breadcrumbs.join('>')}: FactoryGirl.#{m}(#{args.inspect}): #{e}#{::Stepford::FactoryGirl.debug? ? "\n#{e.backtrace.join("\n")}" : ''}" if defined?(breadcrumbs)
131
+ raise e
132
+ end
133
+
134
+ if args.last.is_a?(Hash) && defined?(breadcrumbs) && breadcrumbs.size > 0
135
+ # still handling association/subassociation
136
+ args.last[:nesting_breadcrumbs] = breadcrumbs
137
+ args.last[:to_reload] = to_reload
138
+ orig_options[:to_reload] << result
139
+ else
140
+ # ready to return the initially requested instances, so reload children with their parents, in reverse order added
141
+ orig_options[:to_reload].each do |i|
142
+ begin
143
+ i.reload
144
+ rescue => e
145
+ puts "#{i} reload failed: #{e}\n#{e.backtrace.join("\n")}" if ::Stepford::FactoryGirl.debug?
146
+ end
147
+ end
148
+ end
149
+
150
+ result
151
+ end
152
+
153
+ # switched to this from method_missing to avoid method trying to handle mistaken calls
154
+ def create(*args, &block); handle_factory_girl_method(:create, *args, &block); end
155
+ def create_list(*args, &block); handle_factory_girl_method(:create_list, *args, &block); end
156
+ def build(*args, &block); handle_factory_girl_method(:build, *args, &block); end
157
+ def build_list(*args, &block); handle_factory_girl_method(:build_list, *args, &block); end
158
+ def build_stubbed(*args, &block); handle_factory_girl_method(:build_stubbed, *args, &block); end
159
+ # pass everything else to FactoryGirl to try to handle (can't reflect in current version to find what it handles)
160
+ def method_missing(m, *args, &block); ::FactoryGirl.__send__(m, *args, &block); end
161
+
162
+ def deep_dup(o)
163
+ result = nil
164
+ if o.is_a?(Hash)
165
+ result = {}
166
+ o.keys.each do |key|
167
+ result[deep_dup(key)] = deep_dup(o[key])
168
+ end
169
+ elsif o.is_a?(Array)
170
+ result = []
171
+ o.each do |value|
172
+ result << deep_dup(value)
173
+ end
174
+ elsif [NilClass,FalseClass,TrueClass,Symbol,Numeric,Class,Module].any?{|c|o.is_a?(c)}
175
+ result = o
176
+ elsif o.is_a?(BigDecimal)
177
+ # ActiveSupport v3.2.8 checks duplicable? for BigDecimal by testing it, so we'll just try to dup the value itself
178
+ result = o
179
+ begin
180
+ result = o.dup
181
+ rescue TypeError
182
+ # can't dup
183
+ end
184
+ elsif o.is_a?(Object)
185
+ result = o.dup
186
+ else
187
+ result = o
188
+ end
189
+ result
190
+ end
191
+
192
+ def debugargs(args)
193
+ result = []
194
+ args.each do |arg|
195
+ if arg.is_a?(Hash)
196
+ result << "{#{arg.keys.collect{|key|"#{key} = (#{arg[key].class.name})"}.join(', ')}}"
197
+ else
198
+ result << "#{arg.inspect},"
199
+ end
200
+ end
201
+ result.join('')
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,19 @@
1
+ require 'stepford/factory_girl'
2
+
3
+ module Stepford
4
+ module FactoryGirl
5
+ module RspecHelpers
6
+ [:create, :create_list, :build, :build_list, :build_stubbed].each do |s|
7
+ class_eval %Q"
8
+ def deep_#{s}(*args, &block)
9
+ ::Stepford::FactoryGirl.#{s}(*args, &block)
10
+ end
11
+ "
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ ::RSpec.configure do |c|
18
+ c.include ::Stepford::FactoryGirl::RspecHelpers
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Stepford
2
- VERSION = '0.13.0'
2
+ VERSION = '0.14.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stepford
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-13 00:00:00.000000000 Z
12
+ date: 2012-11-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -55,10 +55,13 @@ extra_rdoc_files: []
55
55
  files:
56
56
  - lib/stepford/circular_ref_checker.rb
57
57
  - lib/stepford/cli.rb
58
+ - lib/stepford/column_representation.rb
58
59
  - lib/stepford/common.rb
60
+ - lib/stepford/factory_girl/config.rb
61
+ - lib/stepford/factory_girl/generator.rb
62
+ - lib/stepford/factory_girl/methods.rb
63
+ - lib/stepford/factory_girl/rspec_helpers.rb
59
64
  - lib/stepford/factory_girl.rb
60
- - lib/stepford/factory_girl_generator.rb
61
- - lib/stepford/factory_girl_rspec_helpers.rb
62
65
  - lib/stepford/version.rb
63
66
  - lib/stepford.rb
64
67
  - Rakefile
@@ -1,184 +0,0 @@
1
- require 'stepford/common'
2
-
3
- module Stepford
4
- class FactoryGirlGenerator
5
- def self.generate_factories(options={})
6
- factories = {}
7
- expected = {}
8
- included_models = options[:models] ? options[:models].split(',').collect{|s|s.strip}.compact : nil
9
- Dir[File.join('app','models','*.rb').to_s].each do |filename|
10
- model_name = File.basename(filename).sub(/.rb$/, '')
11
- next if included_models && !included_models.include?(model_name)
12
- load File.join('app','models',"#{model_name}.rb")
13
-
14
- begin
15
- model_class = model_name.camelize.constantize
16
- rescue => e
17
- puts "Problem in #{model_name.camelize}"
18
- raise e
19
- end
20
-
21
- next unless model_class.ancestors.include?(ActiveRecord::Base)
22
- factory = (factories[model_name.to_sym] ||= [])
23
- pk_syms = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}
24
- excluded_attributes = pk_syms + [:updated_at, :created_at, :object_id]
25
- model_class.reflections.collect {|association_name, reflection|
26
- (expected[reflection.class_name.underscore.to_sym] ||= []) << model_name
27
- fkey_sym = reflection.foreign_key.try(:to_sym)
28
- excluded_attributes << fkey_sym if reflection.foreign_key && !(excluded_attributes.include?(fkey_sym))
29
- assc_sym = reflection.name.to_sym
30
- clas_sym = reflection.class_name.underscore.to_sym
31
- # if has a foreign key, then if NOT NULL or is a presence validate, the association is required and should be output. unfortunately this could mean a circular reference that will have to be manually fixed
32
- has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator)
33
- required = reflection.foreign_key ? (has_presence_validator || model_class.columns.any?{|c| !c.null && c.name.to_sym == fkey_sym}) : false
34
- should_be_trait = !(options[:associations] || (options[:include_required_associations] && required)) && options[:association_traits]
35
- if options[:associations] || (options[:include_required_associations] && required) || should_be_trait
36
- if reflection.macro == :has_many
37
- # In factory girl v4.1.0:
38
- # create_list must be done in an after(:create) or you get Trait not registered or Factory not registered errors.
39
- # this means that validators that verify presence or size > 0 in a association list will not work with this method, and you'll need to
40
- # use build, not create: http://stackoverflow.com/questions/11209347/has-many-with-at-least-two-entries
41
- "#{should_be_trait ? "trait #{"with_#{assc_sym}".to_sym.inspect} do; " : ''}#{should_be_trait || has_presence_validator ? '' : '#'}after(:create) do |user, evaluator|; FactoryGirl.create_list #{clas_sym.inspect}, 2; end#{should_be_trait ? '; end' : ''}#{should_be_trait ? '' : ' # commented to avoid circular reference'}"
42
- elsif assc_sym != clas_sym
43
- "#{should_be_trait ? "trait #{"with_#{assc_sym}".to_sym.inspect} do; " : ''}#{should_be_trait || reflection.macro == :belongs_to || has_presence_validator ? '' : '#'}association #{assc_sym.inspect}#{assc_sym != clas_sym ? ", factory: #{clas_sym.inspect}" : ''}#{should_be_trait ? '; end' : ''}#{should_be_trait || reflection.macro == :belongs_to ? '' : ' # commented to avoid circular reference'}"
44
- else
45
- "#{should_be_trait ? "trait #{"with_#{assc_sym}".to_sym.inspect} do; " : ''}#{should_be_trait || reflection.macro == :belongs_to || has_presence_validator ? '' : '#'}#{is_reserved?(assc_sym) ? 'self.' : ''}#{assc_sym}#{should_be_trait ? '; end' : ''}#{should_be_trait || reflection.macro == :belongs_to ? '' : ' # commented to avoid circular reference'}"
46
- end
47
- else
48
- nil
49
- end
50
- }.compact.sort.each {|l|factory << l}
51
-
52
- sequenceless_table = false
53
- begin
54
- sequenceless_table = true unless m.sequence_name
55
- rescue => e
56
- # bug in Rails 3.2.8, at least: undefined method `split' for nil:NilClass in activerecord-3.2.8/lib/active_record/connection_adapters/postgresql_adapter.rb:911:in `default_sequence_name'
57
- sequenceless_table = true
58
- end
59
-
60
- model_class.columns.sort_by {|c|[c.name]}.each {|c|
61
- # intentional not checking excluded_attributes/exclude_all_ids when sequenceless. it needs these for create to work.
62
- if sequenceless_table && pk_syms.include?(c.name.to_sym)
63
- factory << Stepford::Common.sequence_for(c)
64
- elsif !excluded_attributes.include?(c.name.to_sym) && !(c.name.to_s.downcase.end_with?('_id') && options[:exclude_all_ids]) && (options[:attributes] || !c.null)
65
- has_uniqueness_validator = model_class.validators_on(c.name.to_sym).collect{|v|v.class}.include?(ActiveRecord::Validations::UniquenessValidator)
66
- if has_uniqueness_validator
67
- #TODO: specialize for different data types
68
- factory << Stepford::Common.sequence_for(c)
69
- else
70
- factory << "#{is_reserved?(c.name) ? 'self.' : ''}#{c.name} #{Stepford::Common.value_for(c)}"
71
- end
72
- elsif options[:attribute_traits]
73
- if c.type == :boolean
74
- factory << "trait #{c.name.underscore.to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} true; end"
75
- factory << "trait #{"not_#{c.name.underscore}".to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} false; end"
76
- else
77
- factory << "trait #{"with_#{c.name.underscore}".to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} #{Stepford::Common.value_for(c)}; end"
78
- end
79
- end
80
- }
81
- end
82
-
83
- if options[:associations] || options[:validate_associations]
84
- failed = false
85
- model_to_fixes_required = {}
86
- expected.keys.sort.each do |factory_name|
87
- unless factories[factory_name.to_sym]
88
- puts "#{File.join('app','models',"#{factory_name}.rb")} missing. Model(s) with associations to it: #{expected[factory_name].sort.join(', ')}"
89
- expected[factory_name].each do |model_name|
90
- (model_to_fixes_required[model_name.to_sym] ||= []) << factory_name.to_sym
91
- end
92
- failed = true
93
- end
94
- end
95
- model_to_fixes_required.keys.each do |model_to_fix|
96
- puts ""
97
- puts "In #{model_to_fix}:"
98
- model_to_fixes_required[model_to_fix].each do |fix|
99
- puts "- comment/remove/fix broken association to #{fix}"
100
- end
101
- end
102
- return false if failed
103
- end
104
-
105
- path = get_factories_rb_pathname(options)
106
-
107
- if path.end_with?('.rb')
108
- dirpath = File.dirname(path)
109
- unless File.directory?(dirpath)
110
- puts "Please create this directory first: #{dirpath}"
111
- return false
112
- end
113
-
114
- File.open(path, "w") do |f|
115
- write_header(f, options)
116
- factories.keys.sort.each do |factory_name|
117
- factory = factories[factory_name]
118
- write_factory(factory_name, factory, f)
119
- end
120
- write_footer(f)
121
- end
122
- else
123
- unless File.directory?(path)
124
- puts "Please create this directory first: #{path}"
125
- return false
126
- end
127
-
128
- factories.keys.sort.each do |factory_name|
129
- factory = factories[factory_name]
130
- File.open(File.join(path,"#{factory_name}.rb"), "w") do |f|
131
- write_header(f, options)
132
- write_factory(factory_name, factory, f)
133
- write_footer(f)
134
- end
135
- end
136
- end
137
-
138
- return true
139
- end
140
-
141
- private
142
-
143
- def self.is_reserved?(s)
144
- # modified from http://stackoverflow.com/questions/6461303/built-in-way-to-determine-whether-a-string-is-a-ruby-reserved-word/6461673#6461673
145
- %w{__FILE__ __LINE__ alias and begin BEGIN break case class def defined? do else elsif end END ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield}.include? s.to_s
146
- end
147
-
148
- def self.get_factories_rb_pathname(options)
149
- path = File.join('test','factories.rb')
150
- if options[:path]
151
- if options[:path].end_with?('.rb')
152
- path = options[:path]
153
- else
154
- if options[:multiple]
155
- path = options[:path]
156
- else
157
- path = File.join(options[:path],'factories.rb')
158
- end
159
- end
160
- end
161
- path
162
- end
163
-
164
- def self.write_header(f, options)
165
- f.puts '# original version autogenerated by Stepford: https://github.com/garysweaver/stepford'
166
- f.puts ''
167
- f.puts 'FactoryGirl.define do'
168
- f.puts ' '
169
- end
170
-
171
- def self.write_factory(factory_name, factory, f)
172
- f.puts " factory #{factory_name.inspect} do"
173
- factory.each do |line|
174
- f.puts " #{line}"
175
- end
176
- f.puts ' end'
177
- f.puts ' '
178
- end
179
-
180
- def self.write_footer(f)
181
- f.puts 'end'
182
- end
183
- end
184
- end
@@ -1,17 +0,0 @@
1
- require 'stepford/factory_girl'
2
-
3
- module Stepford
4
- module FactoryGirlRspecHelpers
5
- [:create, :create_list, :build, :build_list, :build_stubbed].each do |s|
6
- class_eval %Q"
7
- def deep_#{s}(*args, &block)
8
- ::Stepford::FactoryGirl.#{s}(*args, &block)
9
- end
10
- "
11
- end
12
- end
13
- end
14
-
15
- RSpec.configure do |c|
16
- c.include ::Stepford::FactoryGirlRspecHelpers
17
- end