module ObjectDaddy::ClassMethods

Attributes

exemplar_path[RW]
exemplars_generated[RW]
generators[RW]
presence_validated_attributes[R]

Public Instance Methods

gather_exemplars() click to toggle source
# File lib/object_daddy.rb, line 94
def gather_exemplars
  return if exemplars_generated
  
  self.generators ||= {}
  if superclass.respond_to?(:gather_exemplars)
    superclass.gather_exemplars
    self.generators = (superclass.generators).merge(generators).dup
  end

  exemplar_path.each do |raw_path|
    path = File.join(raw_path, "#{underscore(name)}_exemplar.rb")
    load(path) if File.exists?(path)
  end
  
  self.exemplars_generated = true
end
generates_subclass(subclass_name) click to toggle source
# File lib/object_daddy.rb, line 90
def generates_subclass(subclass_name)
  @concrete_subclass_name = subclass_name.to_s
end
generator_for(handle, args = {}, &block) click to toggle source

register a generator for an attribute of this class #generator_for :foo do |prev| … end #generator_for :foo do … end #generator_for :foo, value #generator_for :foo => value #generator_for :foo, :class => GeneratorClass #generator_for :foo, :method => :method_name

# File lib/object_daddy.rb, line 51
def generator_for(handle, args = {}, &block)
  if handle.is_a?(Hash)
    raise ArgumentError, "only specify one attr => value pair at a time" unless handle.keys.length == 1
    gen_data = handle
    handle = gen_data.keys.first
    args = gen_data[handle]
  end
  
  raise ArgumentError, "an attribute name must be specified" unless handle = handle.to_sym
  
  unless args.is_a?(Hash)
    unless block
      retval = args
      block = lambda { retval }  # lambda { args } results in returning the empty hash that args gets changed to
    end
    args = {}  # args is assumed to be a hash for the rest of the method
  end
  
  if args[:start]
    block ||= lambda { |prev|  prev.succ }
  end
  
  if args[:method]
    h = { :method => args[:method].to_sym }
    h[:start] = args[:start] if args[:start]
    record_generator_for(handle, h)
  elsif args[:class]
    raise ArgumentError, "generator class [#{args[:class].name}] does not have a :next method" unless args[:class].respond_to?(:next)
    record_generator_for(handle, :class => args[:class])
  elsif block
    raise ArgumentError, "generator block must take an optional single argument" unless (-1..1).include?(block.arity)  # NOTE: lambda {} has an arity of -1, while lambda {||} has an arity of 0
    h = { :block => block }
    h[:start] = args[:start] if args[:start]
    record_generator_for(handle, h)
  else
    raise ArgumentError, "a block, :class generator, :method generator, or value must be specified to generator_for"
  end
end
spawn() click to toggle source
spawn() do |obj| ... end
spawn(args)
spawn(args) do |obj| ... end

Creates a valid instance of this class, using any known generators. The generated instance is yielded to a block if provided.

# File lib/object_daddy.rb, line 28
def spawn(args = {})
  gather_exemplars
  if @concrete_subclass_name
    return block_given?            ? const_get(@concrete_subclass_name).spawn(args) {|instance| yield instance}            : const_get(@concrete_subclass_name).spawn(args)
  end
  generate_values(args)
  instance = new
  args.each_pair do |attribute, value|
    instance.send("#{attribute}=", value)  # support setting of mass-assignment protected attributes
  end
  yield instance if block_given?
  instance
end

Protected Instance Methods

record_generator_for(handle, generator) click to toggle source
# File lib/object_daddy.rb, line 127
def record_generator_for(handle, generator)
  self.generators ||= {}
  raise ArgumentError, "a generator for attribute [:#{handle}] has already been specified" if (generators[handle] || {})[:source] == self
  generators[handle] = { :generator => generator, :source => self }
end
underscore(string) click to toggle source

we define an underscore helper ourselves since the ActiveSupport isn't available if we're not using Rails

# File lib/object_daddy.rb, line 123
def underscore(string)
  string.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
end

Private Instance Methods

generate_missing(args) click to toggle source
# File lib/object_daddy.rb, line 171
def generate_missing(args)
  if presence_validated_attributes and !presence_validated_attributes.empty?
    req = {}
    (presence_validated_attributes.keys - args.keys).each {|a| req[a.to_s] = true } # find attributes required by validates_presence_of not already set
    
    belongs_to_associations = reflect_on_all_associations(:belongs_to).to_a
    missing = belongs_to_associations.select { |a|  req[a.name.to_s] or req[a.primary_key_name.to_s] }
    if create_scope = scope(:create)
      missing.reject! { |a|   create_scope.include?(a.primary_key_name) }
    end
    missing.reject! { |a|  [a.name, a.primary_key_name].any? { |n|  args.stringify_keys.include?(n.to_s) } }
    missing.each {|a| args[a.name] = a.class_name.constantize.generate }
  end
end
generate_values(args) click to toggle source
# File lib/object_daddy.rb, line 135
def generate_values(args)
  (generators || {}).each_pair do |handle, gen_data|
    next if args.include?(handle) or args.include?(handle.to_s)
    
    generator = gen_data[:generator]
    if generator[:block]
      process_generated_value(args, handle, generator, generator[:block])
    elsif generator[:method]
      method = method(generator[:method])
      if method.arity == 1
        process_generated_value(args, handle, generator, method)
      else
        args[handle] = method.call
      end
    elsif generator[:class]
      args[handle] = generator[:class].next
    end
  end
  
  generate_missing(args)
end
process_generated_value(args, handle, generator, block) click to toggle source
# File lib/object_daddy.rb, line 157
def process_generated_value(args, handle, generator, block)
  if generator[:start]
    value = generator[:start]
    generator.delete(:start)
  else
    if block.arity == 0
      value = block.call
    else
      value = block.call(generator[:prev])
    end
  end
  generator[:prev] = args[handle] = value
end