Skip to content

undefined method times for #<FactoryGirl::Declaration::Static (NoMethodError) #229

Closed
radosch opened this Issue Nov 5, 2011 · 12 comments

3 participants

@radosch
radosch commented Nov 5, 2011

f.sequence(:tag_list) { rand(1..5).times.map{ tags[rand(tags.size)]}.join(", ") }

somehow, you cannot eval methods.
works not anymore, any idea?

@joshuaclayton
thoughtbot, inc. member

Depending on your version of Ruby, you'll want to use Kernel.rand or Random.rand instead.

@radosch
@joshuaclayton
thoughtbot, inc. member

when you use 5.times.map, what error do you see? Also, rand(1..5) doesn't even work (it results in a TypeError). Please paste the full backtrace as well as the actual sequence you're using.

@radosch
radosch commented Nov 7, 2011

hi joshua,

thnx for taking care of this! Here a few more infos...

sequence:

tags = %w(tag1 tag2 tag3 tag4 tag6 tag9 tag10)
clue_types = Clue.const_get("TYPES_#{I18n.locale.to_s}")

FactoryGirl.define do
  factory :clue do |f|
    f.sequence(:title){ |n| "A random title set here, with a random number like: #{n}"}
    f.sequence(:description){ Faker::Lorem.sentence(10)}
    f.clue_type{clue_types.values[rand(clue_types.size)].to_s}
    f.sequence(:tag_list) { rand(1..5).times.map{ tags[rand(tags.size)]}.join(", ") }
    f.sequence(:user_id) { User.all.map(&:id).shuffle.first }
    f.sequence(:visibility_mask) { (1..4).to_a.shuffle.first }
  end
end

Here is the full backtrace

  NoMethodError: undefined method `times' for #<FactoryGirl::Declaration::Static:0x0000012cdcbc98>
  /Users/rafael/Sites/testing.com/testing/spec/factories/clue.rb:9:in `block (3 levels) in <top (required)>'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/sequence.rb:18:in `call'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/sequence.rb:18:in `next'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/definition_proxy.rb:111:in `block in sequence'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/attribute/dynamic.rb:10:in `instance_exec'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/attribute/dynamic.rb:10:in `add_to'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/factory.rb:61:in `block in run'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/attribute_list.rb:32:in `each'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/attribute_list.rb:32:in `each'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/factory.rb:58:in `run'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/syntax/methods.rb:60:in `create'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/syntax/vintage.rb:50:in `default_strategy'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/factory_girl-2.2.0/lib/factory_girl/syntax/vintage.rb:143:in `Factory'
  /Users/rafael/Sites/testing.com/testing/features/step_definitions/clue_steps.rb.rb:2:in `block in <top (required)>'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/core_ext/instance_exec.rb:48:in `instance_exec'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/core_ext/instance_exec.rb:48:in `block in cucumber_instance_exec'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/core_ext/instance_exec.rb:69:in `cucumber_run_with_backtrace_filtering'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/core_ext/instance_exec.rb:36:in `cucumber_instance_exec'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/rb_support/rb_step_definition.rb:62:in `invoke'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/step_match.rb:25:in `invoke'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/step_invocation.rb:59:in `invoke'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/step_invocation.rb:38:in `accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:99:in `block in visit_step'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:163:in `broadcast'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:98:in `visit_step'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/step_collection.rb:15:in `block in accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/step_collection.rb:14:in `each'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/step_collection.rb:14:in `accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:93:in `block in visit_steps'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:163:in `broadcast'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:92:in `visit_steps'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/scenario.rb:55:in `block (2 levels) in accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime.rb:79:in `block (2 levels) in with_hooks'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime.rb:95:in `before_and_after'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime.rb:78:in `block in with_hooks'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime/support_code.rb:120:in `call'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime/support_code.rb:120:in `block (3 levels) in around'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/language_support/language_methods.rb:13:in `block in around'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/language_support/language_methods.rb:95:in `call'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/language_support/language_methods.rb:95:in `execute_around'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/language_support/language_methods.rb:12:in `around'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime/support_code.rb:119:in `block (2 levels) in around'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime/support_code.rb:117:in `call'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime/support_code.rb:117:in `around'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime.rb:90:in `around'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime.rb:77:in `with_hooks'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/scenario.rb:53:in `block in accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/scenario.rb:108:in `with_visitor'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/scenario.rb:47:in `accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:51:in `block in visit_feature_element'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:163:in `broadcast'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:50:in `visit_feature_element'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/feature.rb:43:in `block in accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/feature.rb:42:in `each'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/feature.rb:42:in `accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:20:in `block in visit_feature'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:163:in `broadcast'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:19:in `visit_feature'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/features.rb:29:in `block in accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/features.rb:17:in `each'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/features.rb:17:in `each'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/features.rb:28:in `accept'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:14:in `block in visit_features'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:163:in `broadcast'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/ast/tree_walker.rb:13:in `visit_features'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/runtime.rb:45:in `run!'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/cli/main.rb:43:in `execute!'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/lib/cucumber/cli/main.rb:20:in `execute'
  /Users/rafael/.rvm/gems/ruby-1.9.2-p290@testing/gems/cucumber-1.1.1/bin/cucumber:14:in `<top (required)>'
  /Users/rafael/Sites/testing.com/testing/script/cucumber:9:in `load'
  /Users/rafael/Sites/testing.com/testing/script/cucumber:9:in `<top (required)>'
  -e:1:in `load'
  -e:1:in `<main>'
@jferris
thoughtbot, inc. member
@radosch
radosch commented Nov 8, 2011
This is not the problem. "times" can not be invoked in the Factory define block.
I think, there will be other methods too.


FactoryGirl.define do
  factory :clue do |f|
    f.sequence(:title){ |n| "Example Title #{n}"}
    f.sequence(:description){ Faker::Lorem.sentence(10)}
    f.clue_type{clue_types.values[rand(clue_types.size)].to_s}
    f.sequence(:tag_list) { 3.times.map{ tags[rand(tags.size)]}.join(", ") }
    f.sequence(:user_id) { User.all.map(&:id).shuffle.first }
    f.sequence(:visibility_mask) { (1..4).to_a.shuffle.first }
  end
end


TypeError: can't convert FactoryGirl::Declaration::Static into Integer
~/clueapp/spec/factories/clue.rb:9:in `[]'
~/clueapp/spec/factories/clue.rb:9:in `block (4 levels) in <top (required)>'
~/clueapp/spec/factories/clue.rb:9:in `times'
~/clueapp/spec/factories/clue.rb:9:in `each'
~/clueapp/spec/factories/clue.rb:9:in `map'
~/clueapp/spec/factories/clue.rb:9:in `block (3 levels) in <top (required)>'
@joshuaclayton
thoughtbot, inc. member

times absolutely positively can be executed in the block. I even created a spec in the FG test suite to ensure it works.

  it "works with times and rand" do
    FactoryGirl.define do
      sequence(:letters) { 3.times.map { rand(5) } }
    end

    p generate(:letters)
  end

This test prints out [2, 3, 1], then [4, 4, 1], then [4, 1, 2]... etc. rand works (even without being called on Kernel or Random), as does times. So, now that we know those work, let's move on to figure out what the real issue is.

It looks like what you're seeing here with [] is accessing tags, which I remember you mentioning in a comment earlier. You can't access variables like tags within the sequence block, which is why this breaks.

@joshuaclayton
thoughtbot, inc. member

Disregard my last comment. You can access tags or any other variables inside the sequence just fine.

Here's a failure case:

  it "rand and a variable inside a factory" do
    define_model("User", :letters => :string)

    FactoryGirl.define do
      factory :user do
        tags = %w(one two three four five)

        sequence(:letters) do
          3.times.map { tags[rand(tags.length)] }
        end
      end
    end

    p FactoryGirl.build(:user).letters
  end

This fails with:

1) sequences rand and a variable inside a factory
     Failure/Error: 3.times.map { tags[rand(tags.length)] }
     TypeError:
       can't convert FactoryGirl::Declaration::Static into Integer
     # /Users/joshuaclayton/dev/gems/factory_girl/spec/acceptance/sequence_spec.rb:42:in `[]'
     # /Users/joshuaclayton/dev/gems/factory_girl/spec/acceptance/sequence_spec.rb:42
     # /Users/joshuaclayton/.rvm/gems/ruby-1.8.7-p352/gems/activemodel-3.1.0/lib/active_model/attribute_methods.rb:102:in `map'
     # /Users/joshuaclayton/.rvm/gems/ruby-1.8.7-p352/gems/activemodel-3.1.0/lib/active_model/attribute_methods.rb:102:in `each'
     # /Users/joshuaclayton/dev/gems/factory_girl/spec/acceptance/sequence_spec.rb:42:in `times'
     # /Users/joshuaclayton/dev/gems/factory_girl/spec/acceptance/sequence_spec.rb:42:in `each'
     # /Users/joshuaclayton/dev/gems/factory_girl/spec/acceptance/sequence_spec.rb:42:in `map'
     # /Users/joshuaclayton/dev/gems/factory_girl/spec/acceptance/sequence_spec.rb:42

That's what you were seeing.

Here's the fix:

  it "rand and a variable inside a factory" do
    define_model("User", :letters => :string)

    FactoryGirl.define do
      factory :user do
        tags = %w(one two three four five)

        sequence(:letters) do
          3.times.map { tags[Kernel.rand(tags.length)] }
        end
      end
    end

    p FactoryGirl.build(:user).letters
  end

Notice that the only difference is that I called Kernel.rand. Problem solved.

@radosch
radosch commented Nov 9, 2011

ah, ok. Kernel.rand saves the deal.

May I ask, why this is like that, I try to understand the scope better.

Thnx a lot !!! For spending so much time on this.
It's strange, that once it doesn't work anymore as before.

@radosch
radosch commented Nov 9, 2011

Actually, I can run it like that as well:

f.sequence(:tag_list) { Random.new.rand(1..5).times.map{ tags[Random.new.rand(tags.length)]}.join(", ") }

I even thought that was replaced by rand() to simplify

@jferris
thoughtbot, inc. member
@radosch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.