1
0
Fork 0
mirror of https://github.com/thoughtbot/factory_bot.git synced 2022-11-09 11:43:51 -05:00
thoughtbot--factory_bot/lib/factory_girl/sequence.rb
Derek Prior and Josh Clayton bf29843d15 Evaluate sequences within the context of the Evaluator when possible
This fixes weird issues where methods invoked within sequences
(like `sprintf`) fail because these methods are being evaluated within
the context of the DefinitionProxy. With this change, invoking `#next`
on a sequence happens from the evaluator so if the scope is provided (it
usually will be), it'll run in the proper context. This means a couple
of oddities are fixed:

1. Developers can now refer to methods on the object instance, just like
in dynamic attributes:

    class User
      def company
        # company lookup
      end
    end

    FactoryGirl.define do
      factory :user do
        sequence(:job_title) {|n| "{title} #{n} at #{company.name}" }
      end
    end

2. Invoke methods typically available because the method is available on
Object (e.g. Kernel methods):

    FactoryGirl.define do
      factory :user do
        sequence(:last_4_ssn) {|n| sprintf '%04d', n }
      end
    end

[#466]
2013-01-18 09:33:54 -05:00

62 lines
1.1 KiB
Ruby

module FactoryGirl
# Sequences are defined using sequence within a FactoryGirl.define block.
# Sequence values are generated using next.
# @api private
class Sequence
attr_reader :name
def initialize(name, *args, &proc)
@name = name
@proc = proc
options = args.extract_options!
@value = args.first || 1
@aliases = options.fetch(:aliases) { [] }
if !@value.respond_to?(:peek)
@value = EnumeratorAdapter.new(@value)
end
end
def next(scope = nil)
if @proc && scope
scope.instance_exec(value, &@proc)
elsif @proc
@proc.call(value)
else
value
end
ensure
increment_value
end
def names
[@name] + @aliases
end
private
def value
@value.peek
end
def increment_value
@value.next
end
class EnumeratorAdapter
def initialize(value)
@value = value
end
def peek
@value
end
def next
@value = @value.next
end
end
end
end