Changes coming in pickle

21 views
Skip to first unread message

Ian White

unread,
Aug 19, 2010, 12:09:29 PM8/19/10
to pickle
Hi pickle peeps,

(warning - a long post)

First off, pickle 0.4.x adds Mongoid support, using the ORM adapter
functionality added in 0.3.x (thanks to the many contributors for the
many contributions, see the bottom of the README for details).

pickle 0.4.x also replaces the default active_record 'factory' adapter
with an agnostic orm adapter. This means that if you use, say mongoid
as your orm, and *dont* use machinist or factory_girl, then pickle
will default to using Document.create as the factory create method.

On to the primary business of the pickle changes...

A gripe I have with pickle at the moment is the freakin huge regexps.
They are there to try and achieve both readable, but not conflicting,
default pickle_steps.rb. Not only are the regexps really long, but
their construction requires all models to be loaded at startup, which
means you can;t dynamically add models/predicates at runtime, and have
pickle be aware of them.

So this needs to change.

A solution I am working on is to make the pickle_refs (capture_model)
and predicate refs (capture_predicate) have a more restricted
signature, ie. have quotes around them for predicates, and have a very
set format for pickle refs. This may make the default steps less
readable, but will enable some nice features. This may also encourage
people to use pickle's api to write their own steps.

These steps would be what pickle 0.5 would look like this (subject to
comment below):

Given an admin_user "fred" exists
Then the admin_user "fred" should be "activated"
Then "fred" should be "activated"

Notice the admin_user has no space (underscore is required) and the
2nd & 3rd lines have quotes around the predicate. On the plus side,
0.5 enables reference to models by label only (line 3)

For BC, and to enable people to not write too many steps, I'm
extending the config.map facility as follows:

Pickle.configure do |config|
config.predicates = ['activated'] # this already exists in 0.4
config.alias 'admin', :as => 'admin_user'
end

With the above config, the default pickle steps will parse:

Given an admin "fred" exists
Then "fred" should be activated

The bonus is that you can add to the pickle regexps when you need
them, and pickle doesn't need to do all the model/predicate
introspection.

Another feature that I'm experimenting with is being able to define
default labels...

The following sort of step often trips-up/annoys pickle users:

Given a person "Fred" exists with name: "Fred"

I'm proposing a label config option:

Pickle.configure do |config|
config.label "person", :with => "name"
end

Which means the following step:

Given a person "Fred" exists

will create a person with :name => "Fred"

Penultimately, the field parsing will happen entirely inside pickle,
which will enable association traversal, for example.

Finally, work is being done on the API to make it easier for devs to
store and retrieve models using pickle. The method names we're
looking at are longer, but more explicit. e.g.

retrieve_from_scenario("3rd user") #=> raises ModelUnknown if it
can't be found
store_in_scenario(User.make, :label => "Fred") #=> stores the model
with the label


Putting all of the together, the following scenario should be
achievable with default pickle steps, and some config

Given a website owner "Fred" exists
And "Fred"'s site has 11 customers
Then the 10th customer should be a lucky winner
But the 11th customer should not be a lucky winner

(default) pickle_steps.rb:

Given(/^#{pickle_ref} exists(?: with #{pickle_fields}$/) do |ref,
fields|
create_and_store_in_scenario(ref, fields)
end

Given(/^#{pickle_ref}'s (\w+) has (\d+) (\w+)$/ do |ref, assoc,
count, new_models|
model = retrieve_from_scenario(ref)
assoc = model.send(assoc)
new_model = new_models.singularize
count.to_i.times do
create_and_store_in_scenario(new_model, :on => assoc)
end
end

Then(/^#{pickle_ref} should(?: not)? be #{pickle_predicate}$/) do |
ref, expectation, predicate|
retreieve_from_scenario(ref).send(expectation.sub(' ','_'),
"be_#{predicate.gsub(' ','_')}")
end

pickle.rb:

Pickle.configure do |config|
config.predicates = ['a lucky winner']
end

I'd love to hear opinions about any (or all) of this stuff.

Cheers,
Ian

Jason Bucki

unread,
Aug 19, 2010, 12:53:55 PM8/19/10
to pickle-...@googlegroups.com
>
> Another feature that I'm experimenting with is being able to define
> default labels...
>
> The following sort of step often trips-up/annoys pickle users:
>
> Given a person "Fred" exists with name: "Fred"
>
> I'm proposing a label config option:
>
> Pickle.configure do |config|
> config.label "person", :with => "name"
> end
>
> Which means the following step:
>
> Given a person "Fred" exists
>
> will create a person with :name => "Fred"

Great idea. Would love to see this implemented.

>
> Finally, work is being done on the API to make it easier for devs to
> store and retrieve models using pickle. The method names we're
> looking at are longer, but more explicit. e.g.
>
> retrieve_from_scenario("3rd user") #=> raises ModelUnknown if it
> can't be found
> store_in_scenario(User.make, :label => "Fred") #=> stores the model
> with the label


I've been meaning to email you about this (storing models) for weeks now as we've been hacking around this in our own step definitions that don't utilize the standard pickle "Given X exists" step(s). Having true support for storing models inside the pickle scenario/session would be wonderful.

Excited about the new ideas you've got. Let me know if you want/need help on any of this.

- Jason

> --
> You received this message because you are subscribed to the Google Groups "pickle" group.
> To post to this group, send email to pickle-...@googlegroups.com.
> To unsubscribe from this group, send email to pickle-cucumb...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/pickle-cucumber?hl=en.
>


Ian White

unread,
Aug 22, 2010, 10:12:34 AM8/22/10
to pickle
Here's the Api I'm working on at the moment (for the pickle session
object)

http://github.com/ianwhite/pickle/blob/0.5-unstable/lib/pickle/session/api.rb

gives stuff like:

# this does User.make, and stores result in pickle.jar labelled as
fred
pickle.create_and_store 'user: "fred"'

# these all get fred out (and reload, to make sure it's still in the
db
pickle.retrieve_and_reload 'last user'
pickle.retrieve_and_reload '"fred"'
pickle.retrieve_and_reload :label => 'fred'

# this does User.find :conditions => {:status => 'activated'}, and
stores result in pickle.jar
pickle.find_and_store 'user', 'status: "activated"'

pickle.retrieve_and_reload 'last user' # => the
found user
pickle.retrieve_and_reload :factory => 'user', :index => 0 # => fred
user

# You can skip the whole make/find orm stuff with #retrieve and
#store
# Pickle uses the class of the object as its 'factory_name', but you
can provide another one

pickle.store "Gday"
pickle.store "Mate"

pickle.retrieve '1st string' # => "Gday"
pickle.retrieve 'last string' # => "Mate"

pickle.store "Gday Mate", 'saying'
pickle.store "A bird in the hand", 'saying'

pickle.retrieve '1st saying" # => "Gday Mate"
pickle.retrieve 'last string, # => "A bird in the hand"
pickle.retrieve 'last saying' # => "A bird in the hand"

I'm toying with this: http://github.com/ianwhite/pickle/blob/0.5-unstable/lib/pickle/dsl.rb
as being the module included into cucumber scenarios.

This would mean, in addition to the above, in step_defs you could
write

make 'user'
# => a made user object, stored

find 'user: "fred"'
# => a found user object, stored

model '2nd user'
# => a known object, the 2nd user

model? '4th user'
# => false (because 'the 4th user' isn't known in this secnario)

I've also cleaned up the adpater/orm_adapter stuff, hopefully making
it a bit clearer what's going on.

Any thoughts on this stuff appreciated.

Cheers,
Ian
Reply all
Reply to author
Forward
0 new messages