class Stepford::FactoryGirl::Generator
Public Class Methods
generate_factories(options={})
click to toggle source
# File lib/stepford/factory_girl/generator.rb, line 8 def self.generate_factories(options={}) factories = {} expected = {} included_model_syms = options[:models] ? options[:models].split(',').collect{|s|s.strip.to_sym}.compact : nil puts "Rails.root=#{Rails.root}" Dir[Rails.root.join('app','models','*.rb').to_s].each do |filename| model_name = File.basename(filename).sub(/.rb$/, '') model_name_sym = model_name.to_sym next if included_model_syms && !included_model_syms.include?(model_name_sym) load filename begin model_class = model_name.camelize.constantize rescue => e puts "Problem in #{model_name.camelize}" raise e end next unless model_class.ancestors.include?(ActiveRecord::Base) factory = (factories[model_name_sym] ||= []) pkey_syms = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym} excluded_attribute_syms = [:updated_at, :created_at, :object_id] excluded_attribute_syms_and_pkeys = pkey_syms + [:updated_at, :created_at, :object_id] model_class.reflections.collect {|association_name, reflection| 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 (expected[reflection.class_name.underscore.to_sym] ||= []) << model_name_sym fkey_sym = reflection.foreign_key.try(:to_sym) excluded_attribute_syms_and_pkeys << fkey_sym if reflection.foreign_key && !(excluded_attribute_syms_and_pkeys.include?(fkey_sym)) assc_sym = reflection.name.to_sym clas_sym = reflection.class_name.underscore.to_sym # 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 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 == fkey_sym}) : false should_be_trait = !(options[:associations] || (options[:include_required_associations] && required)) && options[:association_traits] if options[:associations] || (options[:include_required_associations] && required) || should_be_trait if reflection.macro == :has_many # In factory girl v4.1.0: # create_list must be done in an after(:create) or you get Trait not registered or Factory not registered errors. # 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 # use build, not create: http://stackoverflow.com/questions/11209347/has-many-with-at-least-two-entries "#{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'}" elsif assc_sym != clas_sym "#{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'}" else "#{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'}" end else nil end }.compact.sort.each {|l|factory << l} sequenceless_table = false begin sequenceless_table = true unless m.sequence_name rescue => e # 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' sequenceless_table = true end model_column_name_sym_to_column_representation = {} model_class.columns.each {|c|model_column_name_sym_to_column_representation[c.name.to_sym] = ::Stepford::ColumnRepresentation.new(c)} ::Stepford::FactoryGirl.column_overrides_tree[model_name_sym].each do |column_override_attr_sym, column_override_options| (model_column_name_sym_to_column_representation[column_override_attr_sym] ||= ::Stepford::ColumnRepresentation.new(column_override_attr_sym)).merge_options(column_override_options) end if ::Stepford::FactoryGirl.column_overrides_tree && ::Stepford::FactoryGirl.column_overrides_tree[model_name_sym] model_column_name_sym_to_column_representation.values {|c|[c.name]}.each {|c| # without a sequence, FactoryGirl will need to have sequences for pkeys if sequenceless_table && pkey_syms.include?(c.name.to_sym) factory << ::Stepford::Common.sequence_for(c) 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) has_uniqueness_validator = model_class.validators_on(c.name.to_sym).collect{|v|v.class}.include?(ActiveRecord::Validations::UniquenessValidator) if has_uniqueness_validator #TODO: specialize for different data types factory << ::Stepford::Common.sequence_for(c) else factory << "#{is_reserved?(c.name) ? 'self.' : ''}#{c.name} #{Stepford::Common.value_for(c)}" end elsif options[:attribute_traits] if c.type == :boolean factory << "trait #{c.name.underscore.to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} true; end" factory << "trait #{"not_#{c.name.underscore}".to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} false; end" else factory << "trait #{"with_#{c.name.underscore}".to_sym.inspect} do; #{is_reserved?(c.name) ? 'self.' : ''}#{c.name} #{Stepford::Common.value_for(c)}; end" end end } end if options[:associations] || options[:validate_associations] failed = false model_to_fixes_required = {} expected.keys.sort.each do |factory_name_sym| unless factories[factory_name_sym] puts "#{Rails.root.join('app','models',"#{factory_name_sym}.rb")} missing. Model(s) with associations to it: #{expected[factory_name_sym].sort.join(', ')}" expected[factory_name_sym].each do |model_name| (model_to_fixes_required[model_name_sym] ||= []) << factory_name_sym end failed = true end end model_to_fixes_required.keys.each do |model_to_fix| puts "" puts "In #{model_to_fix}:" model_to_fixes_required[model_to_fix].each do |fix| puts "- comment/remove/fix broken association to #{fix}" end end return false if failed end path = get_factories_rb_pathname(options) if path.end_with?('.rb') dirpath = File.dirname(path) unless File.directory?(dirpath) puts "Please create this directory first: #{dirpath}" return false end File.open(path, "w") do |f| write_header(f, options) factories.keys.sort.each do |factory_name_sym| factory = factories[factory_name_sym] write_factory(factory_name_sym, factory, f) end write_footer(f) end else unless File.directory?(path) puts "Please create this directory first: #{path}" return false end factories.keys.sort.each do |factory_name_sym| factory = factories[factory_name_sym] File.open(File.join(path,"#{factory_name_sym}.rb"), "w") do |f| write_header(f, options) write_factory(factory_name_sym, factory, f) write_footer(f) end end end return true end
Private Class Methods
get_factories_rb_pathname(options)
click to toggle source
# File lib/stepford/factory_girl/generator.rb, line 166 def self.get_factories_rb_pathname(options) path = Rails.root.join('test','factories.rb') if options[:path] if options[:path].end_with?('.rb') path = options[:path] else if options[:multiple] path = options[:path] else path = Rails.root.join(options[:path],'factories.rb') end end end # convert Pathname to string path.to_s end
is_reserved?(s)
click to toggle source
# File lib/stepford/factory_girl/generator.rb, line 161 def self.is_reserved?(s) # modified from http://stackoverflow.com/questions/6461303/built-in-way-to-determine-whether-a-string-is-a-ruby-reserved-word/6461673#6461673 %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 end
write_factory(factory_name, factory, f)
click to toggle source
# File lib/stepford/factory_girl/generator.rb, line 190 def self.write_factory(factory_name, factory, f) f.puts " factory #{factory_name.inspect} do" factory.each do |line| f.puts " #{line}" end f.puts ' end' f.puts ' ' end
write_header(f, options)
click to toggle source
# File lib/stepford/factory_girl/generator.rb, line 183 def self.write_header(f, options) f.puts '# original version autogenerated by Stepford: https://github.com/garysweaver/stepford' f.puts '' f.puts 'FactoryGirl.define do' f.puts ' ' end