Permalink
Browse files

Sequences support Enumerators

This introduces an EnumeratorAdapter so everything coming in looks like
an Enumerator. It doesn't use the adapter if the #peek method exists on
the object.

This allows for sequences like:

    sequence(:cities, %w[Boston Atlanta Detroit Seattle].cycle)

This also makes it easier to cycle through arrays and ranges, since they
can be converted to Enumerators rather easily.

    sequence(:month, (1..12).to_enum)
    sequence(:month, %w[foo bar baz].to_enum)

This doesn't handle when calling Range#step out of the box, because
Ruby returns an Enumerator but ActiveSupport 3.x returns an array,
meaning #to_enum still needs to be called.

Closes #339, #378
  • Loading branch information...
1 parent d0e56fe commit 7b382214b3bf29aa7eb6d12b215ac9ba66b03f9d @joshuaclayton joshuaclayton committed May 13, 2012
Showing with 32 additions and 2 deletions.
  1. +22 −2 lib/factory_girl/sequence.rb
  2. +10 −0 spec/factory_girl/sequence_spec.rb
@@ -13,16 +13,36 @@ def initialize(name, *args, &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
- @proc ? @proc.call(@value) : @value
+ @proc ? @proc.call(@value.peek) : @value.peek
ensure
- @value = @value.next
+ @value.next
end
def names
[@name] + @aliases
end
+
+ private
+
+ class EnumeratorAdapter
+ def initialize(value)
+ @value = value
+ end
+
+ def peek
+ @value
+ end
+
+ def next
+ @value = @value.next
+ end
+ end
end
end
@@ -65,4 +65,14 @@
its(:next) { should == "B" }
end
end
+
+ describe "iterating over items in an enumerator" do
+ subject { FactoryGirl::Sequence.new(:name, %w[foo bar].to_enum) {|n| "=#{n}" } }
+
+ it "navigates to the next items until no items remain" do
+ subject.next.should == "=foo"
+ subject.next.should == "=bar"
+ expect { subject.next }.to raise_error(StopIteration)
+ end
+ end
end

0 comments on commit 7b38221

Please sign in to comment.