Module: Stepford::FactoryGirl
- Defined in:
- lib/stepford/factory_girl/methods.rb,
lib/stepford/factory_girl/config.rb,
lib/stepford/factory_girl/generator.rb,
lib/stepford/factory_girl/rspec_helpers.rb
Overview
Automatically recursively creates/builds/stubbed associations using FactoryGirl.
You can specify the method name and arguments/options to factory girl for the associations. e.g. if the following is required:
-
Bar has a required association called house_special which uses the :beer factory, and we have a block we want to send into it
-
Beer has specials that you want to build as a list of 3 using the :tuesday_special_offer factory
you could do that with:
Stepford::FactoryGirl.create_list(:bar, with_factory_options: {
house_special: [:create, :beer, {blk: ->(beer) do; beer.bubbles.create(attributes_for(:bubbles)); end}],
specials: [:build_list, :tuesday_special_offer, 3]
}) do
# the block you would send to FactoryGirl.create_list(:foo) would go here
end
Defined Under Namespace
Modules: RspecHelpers Classes: Generator
Constant Summary collapse
- OPTIONS =
[:debug, :column_overrides, :config_loaded, :column_overrides_tree]
Class Method Summary collapse
- .build(*args, &block) ⇒ Object
- .build_list(*args, &block) ⇒ Object
- .build_stubbed(*args, &block) ⇒ Object
- .column_overrides=(args) ⇒ Object
- .configure(&blk) ⇒ Object
-
.create(*args, &block) ⇒ Object
switched to this from method_missing to avoid method trying to handle mistaken calls.
- .create_list(*args, &block) ⇒ Object
- .debugargs(args) ⇒ Object
- .deep_dup(o) ⇒ Object
- .force_configure(pathname) ⇒ Object
- .handle_factory_girl_method(m, *args, &block) ⇒ Object
-
.load_config(pathname = nil) ⇒ Object
Loads the configuration from config/stepford.rb or a specified pathname unless has already been loaded.
-
.method_missing(m, *args, &block) ⇒ Object
pass everything else to FactoryGirl to try to handle (can’t reflect in current version to find what it handles).
Class Method Details
.build(*args, &block) ⇒ Object
161 |
# File 'lib/stepford/factory_girl/methods.rb', line 161 def build(*args, &block); handle_factory_girl_method(:build, *args, &block); end |
.build_list(*args, &block) ⇒ Object
162 |
# File 'lib/stepford/factory_girl/methods.rb', line 162 def build_list(*args, &block); handle_factory_girl_method(:build_list, *args, &block); end |
.build_stubbed(*args, &block) ⇒ Object
163 |
# File 'lib/stepford/factory_girl/methods.rb', line 163 def build_stubbed(*args, &block); handle_factory_girl_method(:build_stubbed, *args, &block); end |
.column_overrides=(args) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/stepford/factory_girl/config.rb', line 34 def column_overrides=(args) # to avoid a lot of processing overhead, we preprocess the arrays into a hash that would look ugly to the user, e.g. # {:model_name => {:attribute_name => {options or just empty}}} result = {} args.each do |k,v| if k.is_a?(Array) && k.size == 2 && v.is_a?(Hash) table_columns = (result[k[0].to_sym] ||= {}) = (table_columns[k[1].to_sym] ||= {}) .merge(v) else 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." end end if args @column_overrides = args @column_overrides_tree = result end |
.configure(&blk) ⇒ Object
7 |
# File 'lib/stepford/factory_girl/config.rb', line 7 def configure(&blk); class_eval(&blk); end |
.create(*args, &block) ⇒ Object
switched to this from method_missing to avoid method trying to handle mistaken calls
159 |
# File 'lib/stepford/factory_girl/methods.rb', line 159 def create(*args, &block); handle_factory_girl_method(:create, *args, &block); end |
.create_list(*args, &block) ⇒ Object
160 |
# File 'lib/stepford/factory_girl/methods.rb', line 160 def create_list(*args, &block); handle_factory_girl_method(:create_list, *args, &block); end |
.debugargs(args) ⇒ Object
197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/stepford/factory_girl/methods.rb', line 197 def debugargs(args) result = [] args.each do |arg| if arg.is_a?(Hash) result << "{#{arg.keys.collect{|key|"#{key} = (#{arg[key].class.name})"}.join(', ')}}" else result << "#{arg.inspect}," end end result.join('') end |
.deep_dup(o) ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/stepford/factory_girl/methods.rb', line 167 def deep_dup(o) result = nil if o.is_a?(Hash) result = {} o.keys.each do |key| result[deep_dup(key)] = deep_dup(o[key]) end elsif o.is_a?(Array) result = [] o.each do |value| result << deep_dup(value) end elsif [NilClass,FalseClass,TrueClass,Symbol,Numeric,Class,Module].any?{|c|o.is_a?(c)} result = o elsif o.is_a?(BigDecimal) # ActiveSupport v3.2.8 checks duplicable? for BigDecimal by testing it, so we'll just try to dup the value itself result = o begin result = o.dup rescue TypeError # can't dup end elsif o.is_a?(Object) result = o.dup else result = o end result end |
.force_configure(pathname) ⇒ Object
28 29 30 31 32 |
# File 'lib/stepford/factory_girl/config.rb', line 28 def force_configure(pathname) load pathname puts "Loaded #{pathname}" ::Stepford::FactoryGirl.config_loaded = pathname end |
.handle_factory_girl_method(m, *args, &block) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/stepford/factory_girl/methods.rb', line 22 def handle_factory_girl_method(m, *args, &block) if args && args.size > 0 # call Stepford::FactoryGirl.* on any not null associations recursively model_name = args[0] begin model_class = model_name.to_s.camelize.constantize rescue => e puts "Problem in #{model_name.to_s.camelize}" if model_name raise e end args = args.dup # need local version because we'll be dup'ing the options hash to add things to set prior to create/build = args.last if .is_a?(Hash) # keep them separate = = deep_dup() args[(args.size - 1)] = # need to set the dup'd options else # keep them separate = {} = {} args << # need to add options to set associations end [:with_factory_options] = {} unless [:with_factory_options] = [:with_factory_options] [:nesting_breadcrumbs] = [] unless [:nesting_breadcrumbs] = [:nesting_breadcrumbs] << [args[0]] [:to_reload] = [] unless [:to_reload] to_reload = [:to_reload] if ::Stepford::FactoryGirl.debug? puts "#{.join('>')} start. args=#{debugargs(args)}" end model_class.reflections.each do |association_name, reflection| assc_sym = reflection.name.to_sym next if [assc_sym] || [reflection.foreign_key.to_sym] # || reflection.macro != :belongs_to begin reflection.class_name rescue puts "#{model_class.name}.#{association_name} failed when attempting to call reflection's class_name method, so skipped association" next end clas_sym = reflection.class_name.underscore.to_sym has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(::ActiveModel::Validations::PresenceValidator) required = reflection.foreign_key ? (has_presence_validator || model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym}) : false = ? ([[clas_sym, assc_sym]] || [clas_sym]) : nil has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator) required = false if reflection.macro == :belongs_to # note: supports composite_primary_keys gem which stores primary_key as an array foreign_key_is_also_primary_key = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}.include?(reflection.foreign_key.to_sym) 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} required = is_not_null_fkey_that_is_not_primary_key || has_presence_validator else # 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 required = has_presence_validator end if required || Array.wrap(::Stepford::FactoryGirl.column_overrides).compact.include?([model_name.to_sym, assc_sym]) << ["a:#{assc_sym}"] if = .dup = args.last blk = .is_a?(Hash) ? .delete(:blk) : nil begin if blk [assc_sym] = ::FactoryGirl.__send__(*, &blk) else [assc_sym] = ::FactoryGirl.__send__(*) end to_reload << [assc_sym] rescue => e puts "#{.join('>')}: FactoryGirl.__send__(#{.inspect}): #{e}#{::Stepford::FactoryGirl.debug? ? "\n#{e.backtrace.join("\n")}" : ''}" raise e end else if reflection.macro == :has_many [assc_sym] = ::Stepford::FactoryGirl.create_list(clas_sym, 2, ) else [assc_sym] = ::Stepford::FactoryGirl.create(clas_sym, ) end end .pop end end if defined?() if ::Stepford::FactoryGirl.debug? puts "#{.join('>')} end" puts "#{.join('>')} FactoryGirl.#{m}(#{debugargs(args)})" end .pop end # clean-up before sending to FactoryGirl if args.last.is_a?(Hash) (args.last).delete(:with_factory_options) (args.last).delete(:nesting_breadcrumbs) (args.last).delete(:to_reload) end end begin raise "#{.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)) result = ::FactoryGirl.__send__(m, *args, &block) rescue => e puts "#{.join('>')}: FactoryGirl.#{m}(#{args.inspect}): #{e}#{::Stepford::FactoryGirl.debug? ? "\n#{e.backtrace.join("\n")}" : ''}" if defined?() raise e end if args.last.is_a?(Hash) && defined?() && .size > 0 # still handling association/subassociation args.last[:nesting_breadcrumbs] = args.last[:to_reload] = to_reload [:to_reload] << result else # ready to return the initially requested instances, so reload children with their parents, in reverse order added [:to_reload].each do |i| begin i.reload rescue => e puts "#{i} reload failed: #{e}\n#{e.backtrace.join("\n")}" if ::Stepford::FactoryGirl.debug? end end end result end |
.load_config(pathname = nil) ⇒ Object
Loads the configuration from config/stepford.rb or a specified pathname unless has already been loaded.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/stepford/factory_girl/config.rb', line 10 def load_config(pathname = nil) if !(pathname || ::Stepford::FactoryGirl.config_loaded) || (pathname && ::Stepford::FactoryGirl.config_loaded.to_sym != pathname.to_sym) begin if pathname # load without checking if exists to raise error if user-specified file is missing force_configure(pathname) else pathname = Rails.root.join('config', 'stepford.rb').to_s if File.exist?(pathname) force_configure(pathname) end end rescue => e puts "Failed to load #{pathname}:\n#{e.}#{e.backtrace.join("\n")}" end end end |
.method_missing(m, *args, &block) ⇒ Object
pass everything else to FactoryGirl to try to handle (can’t reflect in current version to find what it handles)
165 |
# File 'lib/stepford/factory_girl/methods.rb', line 165 def method_missing(m, *args, &block); ::FactoryGirl.__send__(m, *args, &block); end |