Class: Stepford::FactoryGirlGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/stepford/factory_girl_generator.rb

Class Method Summary collapse

Class Method Details

.generate_factories(options = {}) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
# File 'lib/stepford/factory_girl_generator.rb', line 5

def self.generate_factories(options={})
  factories = {}
  expected = {}
  included_models = options[:models] ? options[:models].split(',').collect{|s|s.strip}.compact : nil
  Dir[File.join('app','models','*.rb').to_s].each do |filename|
    model_name = File.basename(filename).sub(/.rb$/, '')
    next if included_models && !included_models.include?(model_name)
    load File.join('app','models',"#{model_name}.rb")
    model_class = model_name.camelize.constantize
    next unless model_class.ancestors.include?(ActiveRecord::Base)
    factory = (factories[model_name.to_sym] ||= [])
    excluded_attributes = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym} + [:updated_at, :created_at, :object_id]
    model_class.reflections.collect {|association_name, reflection|
      (expected[reflection.class_name.underscore.to_sym] ||= []) << model_name
      excluded_attributes << reflection.foreign_key.to_sym if reflection.foreign_key
      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 == reflection.foreign_key.to_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}
    model_class.columns.sort_by {|c|[c.name]}.each {|c|
      if !excluded_attributes.include?(c.name.to_sym) && !(c.name.downcase.end_with?('_id') && options[:exclude_all_ids]) && (options[:attributes] || !c.null)
        factory << "#{is_reserved?(c.name) ? 'self.' : ''}#{c.name} #{Stepford::Common.value_for(c)}"
      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|
      unless factories[factory_name.to_sym]
        puts "#{File.join('app','models',"#{factory_name}.rb")} missing. Model(s) with associations to it: #{expected[factory_name].sort.join(', ')}"
        expected[factory_name].each do |model_name|
          (model_to_fixes_required[model_name.to_sym] ||= []) << factory_name.to_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|
        factory = factories[factory_name]
        write_factory(factory_name, 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|
      factory = factories[factory_name]
      File.open(File.join(path,"#{factory_name}.rb"), "w") do |f|
        write_header(f, options)
        write_factory(factory_name, factory, f)
        write_footer(f)
      end
    end
  end

  return true
end