pickle 0.2.12 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.3.0
2
+
3
+ * 2 major improvements
4
+ * Mechanism for registering ORM adapters for pickle [Daniel Neighman]
5
+ * Adapters for ActiveRecord and DataMapper [Josh Bassett, Daniel Neighman]
6
+
7
+
1
8
  == 0.2.12
2
9
 
3
10
  * 1 bugfix
data/README.rdoc CHANGED
@@ -1,7 +1,10 @@
1
1
  = pickle
2
2
 
3
3
  Pickle gives you cucumber steps that create your models easily from factory-girl or
4
- machinist factories/blueprints. You can also just use ActiveRecord but it's not as cool.
4
+ machinist factories/blueprints. You can also just use ActiveRecord as a factory but it's not as cool.
5
+
6
+ Pickle can make use of different ORMs for finding records. Currently ActiveRecord and DataMapper adapters are
7
+ provided. More adapters welcome!
5
8
 
6
9
  References to the models are stored in the current world, not necessarily for the purpose of checking the db
7
10
  (although you could use it for that), but for enabling easy reference to urls, and for
@@ -35,7 +38,7 @@ Install pickle either as a rails plugin, or a gem
35
38
 
36
39
  # plugin
37
40
  script/plugin install git://github.com/ianwhite/pickle.git
38
-
41
+
39
42
  # or, plugin as submodule
40
43
  git submodule add git://github.com/ianwhite/pickle.git vendor/plugins/pickle
41
44
 
@@ -48,7 +51,7 @@ It's tested against all stable branches of 2.x rails, and edge, with the latest
48
51
  To run the specs do:
49
52
 
50
53
  rake spec
51
-
54
+
52
55
  To run the features (rails 2.3 only ATM):
53
56
 
54
57
  rake cucumber
@@ -57,6 +60,8 @@ To run the features (rails 2.3 only ATM):
57
60
 
58
61
  The following people have made Pickle better:
59
62
 
63
+ * {Daniel Neighman}[http://github.com/hassox]
64
+ * {Josh Bassett}[http://github.com/nullobject]
60
65
  * {Nick Rutherford}[http://github.com/nruth]
61
66
  * {Tobi Knaup}[http://github.com/guenter]
62
67
  * {Michael MacDonald}[http://github.com/schlick]
@@ -77,9 +82,11 @@ If you want path steps and email steps then just add 'paths' and/or 'email'. Th
77
82
  written to <tt>features/env/paths.rb</tt> and
78
83
  <tt>features/step_definitions/email_steps.rb</tt> respectively.
79
84
 
80
- === Using with plain ole Active Record
85
+ === Using with plain ole Active Record or DataMapper
86
+
87
+ Pickle comes with adapters for Active Record and DataMapper.
81
88
 
82
- If you have an AR called 'Post', with required fields 'title', and 'body', then you can now write
89
+ If you have an AR/DM called 'Post', with required fields 'title', and 'body', then you can now write
83
90
  steps like this
84
91
 
85
92
  Given a post exists with title: "My Post", body: "My body"
@@ -98,7 +105,7 @@ you've written, you can just do stuff like
98
105
 
99
106
  ==== Machinst: require your blueprints and reset Shams
100
107
 
101
- (The latest version of pickle supports {multiple blueprints}[http://github.com/notahat/machinist/commit/d6492e6927a8aa1819926e48b22377171fd20496], for
108
+ (The latest version of pickle supports {multiple blueprints}[http://github.com/notahat/machinist/commit/d6492e6927a8aa1819926e48b22377171fd20496], for
102
109
  earlier versions of machinist use pickle <= 0.1.10)
103
110
 
104
111
  In your <tt>features/support/env.rb</tt> add the following lines at the bottom
@@ -115,6 +122,19 @@ If that doesn't solve loading issues then require your factories.rb file directl
115
122
  # example features/support/factory_girl.rb
116
123
  require File.dirname(__FILE__) + '/../../spec/factories'
117
124
 
125
+ === Using with an ORM other than ActiveRecord or DataMapper
126
+
127
+ Pickle can be used with any Modeling library provided there is an adapter written for it.
128
+
129
+ Adapters are very simple and exist a module or class with the name "PickleAdapter" available to the class. For example
130
+
131
+ User.const_get(:PickleAdapter) #=> should return a pickle adapter
132
+
133
+ The Active Record and DataMapper ones can be found at
134
+ ActiveRecord::Base::PickleAdapter and DataMapper::Resource::PickleAdapter respectively.
135
+
136
+ See how to implement one by looking at the ones provided in the pickle source in lib/pickle/adapters/*
137
+
118
138
  === Configuring Pickle
119
139
 
120
140
  You can tell pickle to use another factory adapter (see Pickle::Adapter), or
@@ -129,7 +149,7 @@ In: <tt>features/support/pickle.rb</tt>
129
149
  config.adapters = [:machinist, YourOwnAdapterClass]
130
150
  config.map 'me', 'myself', 'my', 'I', :to => 'user: "me"'
131
151
  end
132
-
152
+
133
153
  Out of the box pickle looks for machinist, then factory-girl, then finally active-record 'factories'.
134
154
  If you find that your steps aren't working with your factories, it's probably the case that your factory
135
155
  setup is not being included in your cucumber environment (see comments above regarding machinist and factory-girl).
@@ -147,29 +167,29 @@ When you run <tt>script/generate pickle</tt> you get the following steps
147
167
  Given a user exists
148
168
  Given a user: "fred" exists
149
169
  Given the user exists
150
-
170
+
151
171
  "Given <b>a model</b> exists with <b>fields</b>", e.g.
152
172
 
153
173
  Given a user exists with name: "Fred"
154
174
  Given a user exists with name: "Fred", activated: false
155
-
175
+
156
176
  You can refer to other models in the fields
157
177
 
158
178
  Given a user exists
159
179
  And a post exists with author: the user
160
-
180
+
161
181
  Given a person: "fred" exists
162
182
  And a person: "ethel" exists
163
183
  And a fatherhood exists with parent: user "fred", child: user "ethel"
164
-
184
+
165
185
  "Given <b>n models</b> exist", e.g.
166
-
186
+
167
187
  Given 10 users exist
168
-
188
+
169
189
  "Given <b>n models</b> exist with <b>fields</b>", examples:
170
190
 
171
191
  Given 10 users exist with activated: false
172
-
192
+
173
193
  "Given the following <b>models</b> exist:", examples:
174
194
 
175
195
  Given the following users exist
@@ -188,21 +208,21 @@ You can refer to other models in the fields
188
208
  "Then <b>a model</b> should exist with <b>fields</b>", e.g.
189
209
 
190
210
  Then a user: "fred" should exist with name: "Fred" # we can label the found user for later use
191
-
211
+
192
212
  You can use other models, booleans, numerics, and strings as fields
193
213
 
194
214
  Then a person should exist with child: person "ethel"
195
215
  Then a user should exist with activated: false
196
216
  Then a user should exist with activated: true, email: "fred@gmail.com"
197
-
217
+
198
218
  "Then <b>n models</b> should exist", e.g.
199
219
 
200
220
  Then 10 events should exist
201
-
221
+
202
222
  "Then <b>n models</b> should exist with <b>fields</b>", e.g.
203
223
 
204
224
  Then 2 people should exist with father: person "fred"
205
-
225
+
206
226
  "Then the following <b>models</b> exist". This allows the creation of multiple models
207
227
  using a table syntax. Using a column with the singularized name of the model creates a referenceable model. E.g.
208
228
 
@@ -213,30 +233,30 @@ using a table syntax. Using a column with the singularized name of the model cre
213
233
  Then the following users exist:
214
234
  | user | name | activated |
215
235
  | Fred | Freddy | false |
216
-
236
+
217
237
  ===== Asserting associations
218
238
 
219
239
  One-to-one assocs: "Then <b>a model</b> should be <b>other model</b>'s <b>association</b>", e.g.
220
240
 
221
241
  Then the person: "fred" should be person: "ethel"'s father
222
-
242
+
223
243
  Many-to-one assocs: "Then <b>a model</b> should be [in|one of] <b>other model</b>'s <b>association</b>", e.g.
224
244
 
225
245
  Then the person: "ethel" should be one of person: "fred"'s children
226
246
  Then the comment should be in the post's comments
227
-
247
+
228
248
  ===== Asserting predicate methods
229
249
 
230
250
  "Then <b>a model</b> should [be|have] [a|an] <b>predicate</b>", e.g.
231
-
251
+
232
252
  Then the user should have a status # => user.status.should be_present
233
253
  Then the user should have a stale password # => user.should have_stale_password
234
254
  Then the car: "batmobile" should be fast # => car.should be_fast
235
-
255
+
236
256
  "Then <b>a model</b> should not [be|have] [a|an] <b>predicate</b>", e.g.
237
257
 
238
258
  Then person: "fred" should not be childless # => fred.should_not be_childless
239
-
259
+
240
260
  === Regexps for use in your own steps
241
261
 
242
262
  By default you get some regexps available in the main namespace for use
@@ -263,7 +283,7 @@ Pickle::Parser::Matchers for the methods available)
263
283
  post = model!(post)
264
284
  forum = model!(forum)
265
285
  forum.posts.should include(post)
266
- end
286
+ end
267
287
 
268
288
  *capture_fields*
269
289
 
@@ -276,4 +296,4 @@ can build up composite objects with ease
276
296
 
277
297
  # example of use
278
298
  Given a user exists
279
- And a post exists with author: the user # this step will assign the above user as :author on the post
299
+ And a post exists with author: the user # this step will assign the above user as :author on the post
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.12
1
+ 0.3.0
@@ -4,7 +4,7 @@ module Pickle
4
4
  # Abstract Factory adapter class, if you have a factory type setup, you
5
5
  # can easily create an adaptor to make it work with Pickle.
6
6
  #
7
- # The factory adaptor must have a #factories class method that returns
7
+ # The factory adaptor must have a #factories class method that returns
8
8
  # its instances, and each instance must respond to:
9
9
  #
10
10
  # #name : identifies the factory by name (default is attr_reader)
@@ -12,40 +12,65 @@ module Pickle
12
12
  # #create(attrs = {}) : returns a newly created object
13
13
  class Adapter
14
14
  attr_reader :name, :klass
15
-
15
+
16
16
  def create(attrs = {})
17
17
  raise NotImplementedError, "create and return an object with the given attributes"
18
18
  end
19
-
19
+
20
20
  if respond_to?(:class_attribute)
21
21
  class_attribute :model_classes
22
22
  else
23
23
  cattr_writer :model_classes
24
24
  end
25
-
25
+
26
26
  self.model_classes = nil
27
-
27
+
28
+ # Include this module into your adapter
29
+ # this will register the adapter with pickle and it will be picked up for you
30
+ # To create an adapter you should create an inner constant "PickleAdapter"
31
+ #
32
+ # e.g. ActiveRecord::Base::PickleAdapter
33
+ #
34
+ # @see pickle/adapters/active_record
35
+ # @see pickle/adapters/datamapper
36
+ module Base
37
+ def self.included(base)
38
+ adapters << base
39
+ end
40
+
41
+ # A collection of registered adapters
42
+ def self.adapters
43
+ @@adapters ||= []
44
+ end
45
+ end
46
+
28
47
  class << self
29
48
  def factories
30
49
  raise NotImplementedError, "return an array of factory adapter objects"
31
50
  end
32
51
 
33
52
  def model_classes
34
- @@model_classes ||= ::ActiveRecord::Base.send(:subclasses).select {|klass| suitable_for_pickle?(klass)}
53
+ @@model_classes ||= self::Base.adapters.map{ |a| a.model_classes }.flatten
35
54
  end
36
-
37
- # return true if a klass should be used by pickle
38
- def suitable_for_pickle?(klass)
39
- !klass.abstract_class? && klass.table_exists? && !framework_class?(klass)
55
+
56
+ # Returns the column names for the given ORM model class.
57
+ def column_names(klass)
58
+ klass.const_get(:PickleAdapter).column_names(klass)
40
59
  end
41
60
 
42
- # return true if the passed class is a special framework class
43
- def framework_class?(klass)
44
- ((defined?(CGI::Session::ActiveRecordStore::Session) && klass == CGI::Session::ActiveRecordStore::Session)) ||
45
- ((defined?(::ActiveRecord::SessionStore::Session) && klass == ::ActiveRecord::SessionStore::Session))
61
+ def get_model(klass, id)
62
+ klass.const_get(:PickleAdapter).get_model(klass, id)
63
+ end
64
+
65
+ def find_first_model(klass, conditions)
66
+ klass.const_get(:PickleAdapter).find_first_model(klass, conditions)
67
+ end
68
+
69
+ def find_all_models(klass, conditions)
70
+ klass.const_get(:PickleAdapter).find_all_models(klass, conditions)
46
71
  end
47
72
  end
48
-
73
+
49
74
  # machinist adapter
50
75
  class Machinist < Adapter
51
76
  def self.factories
@@ -57,37 +82,37 @@ module Pickle
57
82
  end
58
83
  factories
59
84
  end
60
-
85
+
61
86
  def initialize(klass, blueprint)
62
87
  @klass, @blueprint = klass, blueprint
63
88
  @name = @klass.name.underscore.gsub('/','_')
64
89
  @name = "#{@blueprint}_#{@name}" unless @blueprint == :master
65
90
  end
66
-
91
+
67
92
  def create(attrs = {})
68
93
  @klass.send(:make, @blueprint, attrs)
69
94
  end
70
95
  end
71
-
96
+
72
97
  # factory-girl adapter
73
98
  class FactoryGirl < Adapter
74
99
  def self.factories
75
100
  (::Factory.factories.values rescue []).map {|factory| new(factory)}
76
101
  end
77
-
102
+
78
103
  def initialize(factory)
79
104
  @klass, @name = factory.build_class, factory.factory_name.to_s
80
105
  end
81
-
106
+
82
107
  def create(attrs = {})
83
108
  Factory.create(@name, attrs)
84
109
  end
85
110
  end
86
-
111
+
87
112
  # fallback active record adapter
88
113
  class ActiveRecord < Adapter
89
114
  def self.factories
90
- model_classes.map {|klass| new(klass) }
115
+ ::ActiveRecord::Base::PickleAdapter.model_classes.map{|k| new(k)}
91
116
  end
92
117
 
93
118
  def initialize(klass)
@@ -99,4 +124,4 @@ module Pickle
99
124
  end
100
125
  end
101
126
  end
102
- end
127
+ end
@@ -0,0 +1,46 @@
1
+ begin
2
+ require 'activerecord'
3
+ rescue LoadError
4
+ require 'active_record'
5
+ end
6
+
7
+ class ActiveRecord::Base
8
+ module PickleAdapter
9
+ include Pickle::Adapter::Base
10
+
11
+ # Do not consider these to be part of the class list
12
+ def self.except_classes
13
+ @@except_classes ||= [
14
+ "CGI::Session::ActiveRecordStore::Session",
15
+ "ActiveRecord::SessionStore::Session"
16
+ ]
17
+ end
18
+
19
+ # Gets a list of the available models for this adapter
20
+ def self.model_classes
21
+ ::ActiveRecord::Base.__send__(:subclasses).select do |klass|
22
+ !klass.abstract_class? && klass.table_exists? && !except_classes.include?(klass.name)
23
+ end
24
+ end
25
+
26
+ # get a list of column names for a given class
27
+ def self.column_names(klass)
28
+ klass.column_names
29
+ end
30
+
31
+ # Get an instance by id of the model
32
+ def self.get_model(klass, id)
33
+ klass.find(id)
34
+ end
35
+
36
+ # Find the first instance matching conditions
37
+ def self.find_first_model(klass, conditions)
38
+ klass.find(:first, :conditions => conditions)
39
+ end
40
+
41
+ # Find all models matching conditions
42
+ def self.find_all_models(klass, conditions)
43
+ klass.find(:all, :conditions => conditions)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,37 @@
1
+ require 'dm-core'
2
+
3
+ module DataMapper::Resource
4
+ module PickleAdapter
5
+ include Pickle::Adapter::Base
6
+
7
+ # Do not consider these to be part of the class list
8
+ def self.except_classes
9
+ @@except_classes ||= []
10
+ end
11
+
12
+ # Gets a list of the available models for this adapter
13
+ def self.model_classes
14
+ ::DataMapper::Model.descendants.to_a.select{|k| !except_classes.include?(k.name)}
15
+ end
16
+
17
+ # get a list of column names for a given class
18
+ def self.column_names(klass)
19
+ klass.properties.map(&:name)
20
+ end
21
+
22
+ # Get an instance by id of the model
23
+ def self.get_model(klass, id)
24
+ klass.get(id)
25
+ end
26
+
27
+ # Find the first instance matching conditions
28
+ def self.find_first_model(klass, conditions)
29
+ klass.first(conditions)
30
+ end
31
+
32
+ # Find all models matching conditions
33
+ def self.find_all_models(klass, conditions)
34
+ klass.all(conditions)
35
+ end
36
+ end
37
+ end
data/lib/pickle/config.rb CHANGED
@@ -28,7 +28,7 @@ module Pickle
28
28
 
29
29
  def predicates
30
30
  @predicates ||= Pickle::Adapter.model_classes.map do |k|
31
- k.public_instance_methods.select{|m| m =~ /\?$/} + k.column_names
31
+ k.public_instance_methods.select {|m| m =~ /\?$/} + Pickle::Adapter.column_names(k)
32
32
  end.flatten.uniq
33
33
  end
34
34
 
@@ -45,4 +45,4 @@ module Pickle
45
45
  end
46
46
  end
47
47
  end
48
- end
48
+ end