object-daddy 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", "~> 2.8.0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.8.3"
13
+ gem "rails", "~> 3.1.4"
14
+ gem "generator_spec"
15
+ gem "sqlite3"
16
+ gem "rspec-rails"
17
+ gem "pry"
18
+ gem "mocha"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,130 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionmailer (3.1.4)
5
+ actionpack (= 3.1.4)
6
+ mail (~> 2.3.0)
7
+ actionpack (3.1.4)
8
+ activemodel (= 3.1.4)
9
+ activesupport (= 3.1.4)
10
+ builder (~> 3.0.0)
11
+ erubis (~> 2.7.0)
12
+ i18n (~> 0.6)
13
+ rack (~> 1.3.6)
14
+ rack-cache (~> 1.1)
15
+ rack-mount (~> 0.8.2)
16
+ rack-test (~> 0.6.1)
17
+ sprockets (~> 2.0.3)
18
+ activemodel (3.1.4)
19
+ activesupport (= 3.1.4)
20
+ builder (~> 3.0.0)
21
+ i18n (~> 0.6)
22
+ activerecord (3.1.4)
23
+ activemodel (= 3.1.4)
24
+ activesupport (= 3.1.4)
25
+ arel (~> 2.2.3)
26
+ tzinfo (~> 0.3.29)
27
+ activeresource (3.1.4)
28
+ activemodel (= 3.1.4)
29
+ activesupport (= 3.1.4)
30
+ activesupport (3.1.4)
31
+ multi_json (~> 1.0)
32
+ arel (2.2.3)
33
+ builder (3.0.0)
34
+ coderay (1.0.6)
35
+ diff-lcs (1.1.3)
36
+ erubis (2.7.0)
37
+ generator_spec (0.8.5)
38
+ rails (>= 3.0, < 4.0)
39
+ rspec-rails
40
+ git (1.2.5)
41
+ hike (1.2.1)
42
+ i18n (0.6.0)
43
+ jeweler (1.8.3)
44
+ bundler (~> 1.0)
45
+ git (>= 1.2.5)
46
+ rake
47
+ rdoc
48
+ json (1.6.6)
49
+ mail (2.3.3)
50
+ i18n (>= 0.4.0)
51
+ mime-types (~> 1.16)
52
+ treetop (~> 1.4.8)
53
+ metaclass (0.0.1)
54
+ method_source (0.7.1)
55
+ mime-types (1.18)
56
+ mocha (0.10.5)
57
+ metaclass (~> 0.0.1)
58
+ multi_json (1.2.0)
59
+ polyglot (0.3.3)
60
+ pry (0.9.8.4)
61
+ coderay (~> 1.0.5)
62
+ method_source (~> 0.7.1)
63
+ slop (>= 2.4.4, < 3)
64
+ rack (1.3.6)
65
+ rack-cache (1.2)
66
+ rack (>= 0.4)
67
+ rack-mount (0.8.3)
68
+ rack (>= 1.0.0)
69
+ rack-ssl (1.3.2)
70
+ rack
71
+ rack-test (0.6.1)
72
+ rack (>= 1.0)
73
+ rails (3.1.4)
74
+ actionmailer (= 3.1.4)
75
+ actionpack (= 3.1.4)
76
+ activerecord (= 3.1.4)
77
+ activeresource (= 3.1.4)
78
+ activesupport (= 3.1.4)
79
+ bundler (~> 1.0)
80
+ railties (= 3.1.4)
81
+ railties (3.1.4)
82
+ actionpack (= 3.1.4)
83
+ activesupport (= 3.1.4)
84
+ rack-ssl (~> 1.3.2)
85
+ rake (>= 0.8.7)
86
+ rdoc (~> 3.4)
87
+ thor (~> 0.14.6)
88
+ rake (0.9.2.2)
89
+ rdoc (3.12)
90
+ json (~> 1.4)
91
+ rspec (2.8.0)
92
+ rspec-core (~> 2.8.0)
93
+ rspec-expectations (~> 2.8.0)
94
+ rspec-mocks (~> 2.8.0)
95
+ rspec-core (2.8.0)
96
+ rspec-expectations (2.8.0)
97
+ diff-lcs (~> 1.1.2)
98
+ rspec-mocks (2.8.0)
99
+ rspec-rails (2.8.1)
100
+ actionpack (>= 3.0)
101
+ activesupport (>= 3.0)
102
+ railties (>= 3.0)
103
+ rspec (~> 2.8.0)
104
+ slop (2.4.4)
105
+ sprockets (2.0.3)
106
+ hike (~> 1.2)
107
+ rack (~> 1.0)
108
+ tilt (~> 1.1, != 1.3.0)
109
+ sqlite3 (1.3.5)
110
+ thor (0.14.6)
111
+ tilt (1.3.3)
112
+ treetop (1.4.10)
113
+ polyglot
114
+ polyglot (>= 0.3.1)
115
+ tzinfo (0.3.32)
116
+
117
+ PLATFORMS
118
+ ruby
119
+
120
+ DEPENDENCIES
121
+ bundler (~> 1.0.0)
122
+ generator_spec
123
+ jeweler (~> 1.8.3)
124
+ mocha
125
+ pry
126
+ rails (~> 3.1.4)
127
+ rdoc (~> 3.12)
128
+ rspec (~> 2.8.0)
129
+ rspec-rails
130
+ sqlite3
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Flawed Logic, OG Consulting, Rick Bradley, Yossef Mendelssohn
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,312 @@
1
+ Object Daddy
2
+ ============
3
+ _Version 1.0.0 (April 6, 2012)_
4
+
5
+ __Authors:__ [Rick Bradley](mailto:blogicx@rickbradley.com), [Yossef Mendelssohn](mailto:ymendel@pobox.com), [Jeremy Holland](mailto:jeremy@jeremypholland.com)
6
+
7
+ __Gem Maintainer:__ [Jeremy Holland](mailto:jeremy@jeremypholland.com)
8
+
9
+ __Copyright:__ Copyright (c) 2007, Flawed Logic, OG Consulting, Rick Bradley, Yossef Mendelssohn
10
+
11
+ __License:__ MIT License. See LICENSE.txt file for more details.
12
+
13
+ Object Daddy is a library (as well as a Ruby on Rails plugin) designed to
14
+ assist in automating testing of large collections of objects, especially webs
15
+ of ActiveRecord models. It is a descendent of the "Object Mother" pattern for
16
+ creating objects for testing, and is related to the concept of an "object
17
+ exemplar" or _stereotype_.
18
+
19
+ **WARNING** This code is very much at an _alpha_ development stage. Usage, APIs,
20
+ etc., are all subject to change.
21
+
22
+ See [http://b.logi.cx/2007/11/26/object-daddy](http://b.logi.cx/2007/11/26/object-daddy) for inspiration, historical drama, and too much reading.
23
+
24
+ ## Installation
25
+
26
+ Add the following to your Gemfile and run `bundle install`
27
+
28
+ group :development, :test do
29
+ gem 'object-daddy'
30
+ end
31
+
32
+ Once installed, to set up your exemplars directory, run `rails g object-daddy`
33
+
34
+ ## Using Object Daddy
35
+
36
+ Object Daddy adds a `.generate` method to every ActiveRecord model which can be
37
+ called to generate a valid instance object of that model class, for use in
38
+ testing:
39
+
40
+ it "should have a comment for every forum the user posts to" do
41
+ @user = User.generate
42
+ @post = Post.generate
43
+ @post.comments << Comment.generate
44
+ @user.should have(1).comments
45
+ end
46
+
47
+ This allows us to generate custom model objects without relying on fixtures,
48
+ and without knowing, in our various widespread tests and specs, the details of
49
+ creating a User, Post, Comment, etc. Not having to know this information means
50
+ the information isn't coded into dozens (or hundreds) of tests, and won't need
51
+ to be changed when the User (Post, Comment, ...) model is refactored later.
52
+
53
+ Object Daddy will identify associated classes that need to be instantiated to
54
+ make the main model valid. E.g., given the following models:
55
+
56
+ class User < ActiveRecord::Base
57
+ belongs_to :login
58
+ validates :login, :presence => true
59
+ end
60
+
61
+ class Login < ActiveRecord::Base
62
+ has_one :user
63
+ end
64
+
65
+ A call to `User.generate` will also make a call to `Login.generate` so that
66
+ `User#login` is present, and therefore valid.
67
+
68
+ If all models were able to be created in a valid form by the default Model.new
69
+ call with no knowledge of the model itself, there'd be no need for Object
70
+ Daddy. So, when we deal with models which have validity requirements,
71
+ requiring fields which have format constraints, we need a means of expressing
72
+ how to create those models -- how to satisfy those validity constraints.
73
+
74
+ Object Daddy provides a `generator_for` method which allows the developer to
75
+ specify, for a specific model attribute, how to make a valid value. Note that
76
+ `validates_uniqueness_of` can require that, even if we make 100,000 instances
77
+ of a model that unique attributes cannot have the same values.
78
+
79
+ Object Daddy's `generator_for` method can take three main forms corresponding to
80
+ the means of finding a value for the associated attribute: a block, a method
81
+ call, or using a generator class.
82
+
83
+ class User < ActiveRecord::Base
84
+ validates :email,
85
+ :presence => true,
86
+ :uniqueness => true,
87
+ :format => {
88
+ :with => /^[-a-z_+0-9.]+@(?:[-a-z_+0-9.]\.)+[a-z]+$/i
89
+ }
90
+ validates :username,
91
+ :presence => true,
92
+ :format => {
93
+ :with => /^[a-z0-9_]{4,12}$/i
94
+ }
95
+
96
+ generator_for :email, :start => 'test@domain.com' do |prev|
97
+ user, domain = prev.split('@')
98
+ user.succ + '@' + domain
99
+ end
100
+
101
+ generator_for :username, :method => :next_user
102
+
103
+ generator_for :ssn, :class => SSNGenerator
104
+
105
+ def self.next_user
106
+ @last_username ||= 'testuser'
107
+ @last_username.succ
108
+ end
109
+ end
110
+
111
+ class SSNGenerator
112
+ def self.next
113
+ @last ||= '000-00-0000'
114
+ @last = ("%09d" % (@last.gsub('-', '').to_i + 1)).sub(/^(\d{3})(\d{2})(\d{4})$/, '\1-\2-\3')
115
+ end
116
+ end
117
+
118
+ Note that the block method of invocation (as used with _:email_ above) takes an
119
+ optional _:start_ argument, to specify the value of that attribute on the first
120
+ run. The block will be called thereafter with the previous value of the
121
+ attribute and will generate the next attribute value to be used.
122
+
123
+ A simple default block is provided for any generator with a :start value.
124
+
125
+ class User < ActiveRecord::Base
126
+ generator_for :name, :start => 'Joe' do |prev|
127
+ prev.succ
128
+ end
129
+
130
+ generator_for :name, :start => 'Joe' # equivalent to the above
131
+ end
132
+
133
+ The _:method_ form takes a symbol naming a class method in the model class to be
134
+ called to generate a new value for the attribute in question. If the method
135
+ takes a single argument, it will act much like the block method of invocation,
136
+ being called with the previous value and generating the next.
137
+
138
+ The _:class_ form calls the .next class method on the named class to generate a
139
+ new value for the attribute in question.
140
+
141
+ The argument (previous value) to the block invocation form can be omitted if
142
+ it's going to be ignored, and simple invocation forms are provided for literal
143
+ values.
144
+
145
+ class User < ActiveRecord::Base
146
+ generator_for(:start_time) { Time.now }
147
+ generator_for :name, 'Joe'
148
+ generator_for :age => 25
149
+ end
150
+
151
+ The developer would then simply call `User.generate` when testing.
152
+
153
+ If some attribute values are known (or are being controlled during testing)
154
+ then these can simply be passed in to `.generate`:
155
+
156
+ @bad_login = Login.generate(:expiry => 1.week.ago)
157
+ @expired_user = User.generate(:login => @bad_login)
158
+
159
+ A `.generate!` method is also provided. The _generate/generate!_ pair of methods
160
+ can be thought of as analogs to create/create!, one merely providing an instance
161
+ that may or may not be valid and the other raising an exception if any
162
+ problem comes up.
163
+
164
+ Finally, a `.spawn` method is provided that only gives a new, unsaved object. Note
165
+ that this is the only method of the three that is available if you happen to be
166
+ using Object Daddy outside of Rails.
167
+
168
+ ## Exemplars
169
+
170
+ In the examples given above we are using `generator_for` in the bodies of the
171
+ models themselves. Given that Object Daddy is primarily geared towards
172
+ annotating models with information useful for testing, we anticipate that
173
+ `generator_for` should not normally be included inline in models. Rather, we
174
+ will provide a place where model classes can be re-opened and `generator_for`
175
+ calls (and support methods) can be written without polluting the model files
176
+ with Object Daddy information.
177
+
178
+ when the Object Daddy generator is run, it will create
179
+ *RAILS_ROOT/spec/exemplars/* as a place to hold __exemplar__ files for Rails model
180
+ classes. (We are seeking perhaps some better terminology)
181
+
182
+ An __exemplar__ for the User model would then be found in
183
+ *RAILS_ROOT/spec/exemplars/user_exemplar.rb* (when you are using a testing tool
184
+ which works from *RAILS_ROOT/test*, Object Daddy will create
185
+ *RAILS_ROOT/test/exemplars* and look for your exemplars in that directory
186
+ instead). Exemplar files are completely optional, and no model need have
187
+ exemplar files. The `.generate` method will still exist and be callable, and
188
+ `generator_for` can be declared in the model files themselves. If an exemplar
189
+ file is available when `.generate` is called on a model, the exemplar file will
190
+ be loaded and used. An example *user_exemplar.rb* appears below:
191
+
192
+ require 'ssn_generator'
193
+
194
+ class User < ActiveRecord::Base
195
+ generator_for :email, :start => 'test@domain.com' do |prev|
196
+ user, domain = prev.split('@')
197
+ user.succ + '@' + domain
198
+ end
199
+
200
+ generator_for :username, :method => :next_user
201
+
202
+ generator_for :ssn, :class => SSNGenerator
203
+
204
+ def self.next_user
205
+ @last_username ||= 'testuser'
206
+ @last_username.succ
207
+ end
208
+ end
209
+
210
+ ## Blocks
211
+
212
+ The `spawn`, `generate` and `generate!` methods can all accept a block, to which
213
+ they'll yield the generated object. This provides a nice scoping mechanism in
214
+ your code examples. Consider:
215
+
216
+ describe "admin user" do
217
+ it "should be authorized to create company profiles"
218
+ admin_user = User.generate!
219
+ admin_user.activate!
220
+ admin_user.add_role("admin")
221
+
222
+ admin_user.should be_authorized(:create, Company)
223
+ end
224
+ end
225
+
226
+ This could be refactored to:
227
+
228
+ describe "admin user" do
229
+ it "should be authorized to create company profiles" do
230
+ admin_user = User.generate! do |user|
231
+ user.activate!
232
+ user.add_role("admin")
233
+ end
234
+
235
+ admin_user.should be_authorized(:create, Company)
236
+ end
237
+ end
238
+
239
+ Or:
240
+
241
+ describe "admin user" do
242
+ it "should be authorized to create company profiles"
243
+ User.generate! do |user|
244
+ user.activate!
245
+ user.add_role("admin")
246
+ end.should be_authorized(:create, Company)
247
+ end
248
+ end
249
+
250
+ Or even:
251
+
252
+ describe "admin user" do
253
+ def admin_user
254
+ @admin_user ||= User.generate! do |user|
255
+ user.activate!
256
+ user.add_role("admin")
257
+ end
258
+ end
259
+
260
+ it "should be authorized to create company profiles"
261
+ admin_user.should be_authorized(:create, Company)
262
+ end
263
+ end
264
+
265
+ This last refactoring allows you to reuse the admin_user method across
266
+ multiple code examples, balancing DRY with local data.
267
+
268
+ ## Object Daddy and Fixtures
269
+
270
+ While Object Daddy is meant to obviate the hellish devilspawn that are test
271
+ fixtures, Object Daddy should work alongside fixtures just fine. To each his
272
+ own, I suppose.
273
+
274
+ ## Known Issues
275
+
276
+ The simple invocation forms for `generator_for` when using literal values do not
277
+ work if the literal value is a Hash. Don't do that.
278
+
279
+ class User < ActiveRecord::Base
280
+ generator_for :thing_hash, { 'some key' => 'some value' }
281
+ generator_for :other_hash => { 'other key' => 'other value' }
282
+ end
283
+
284
+ I'm not sure why this would even ever come up, but seriously, don't.
285
+
286
+ Required `belongs_to` associations are automatically generated when generating an instance,
287
+ but only if necessary.
288
+
289
+ class Category < ActiveRecord::Base
290
+ has_many :items
291
+ end
292
+
293
+ class Item < ActiveRecord::Base
294
+ belongs_to :category
295
+ validates :category, :presence => true
296
+ end
297
+
298
+ `Item.generate` will generate a new category, but `some_category.items.generate` will not.
299
+ Unless, of course, you are foolish enough to define a generator in the exemplar.
300
+
301
+ class Item
302
+ generator_for(:category) { Category.generate }
303
+ end
304
+
305
+ Once again, don't do that.
306
+
307
+ ## Rails _surprises_
308
+
309
+ Due to the way Rails handles associations, cascading generations (as a result of
310
+ required associations) are always generated-and-saved, even if the original generation
311
+ call was a mere `spawn` (`new`). This may come as a surprise, but it would probably be more
312
+ of a surprise if `User.spawn.save` and `User.generate` weren't comparable.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "object-daddy"
18
+ gem.homepage = "http://github.com/awebneck/object_daddy"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Kill Fixtures}
21
+ gem.description = %Q{Object Daddy is a library (as well as a Ruby on Rails plugin) designed to assist in automating testing of large collections of objects, especially webs of ActiveRecord models. It is a descendent of the "Object Mother" pattern for creating objects for testing, and is related to the concept of an "object exemplar" or stereotype.}
22
+ gem.email = ["blogicx@rickbradley.com", "ymendel@pobox.com", "jeremy@jeremypholland.com"]
23
+ gem.authors = ["Rick Bradley", "Yossef Mendelssohn", "Jeremy Holland"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,8 @@
1
+ class ObjectDaddyGenerator < Rails::Generators::Base
2
+ desc "create exemplars directory"
3
+ def create_exemplars_dir
4
+ testfw = File.exists?("#{destination_root}/test") ? "test" : "spec"
5
+ empty_directory "#{testfw}/exemplars"
6
+ create_file "#{testfw}/exemplars/.gitkeep", "git sucks"
7
+ end
8
+ end
@@ -0,0 +1 @@
1
+ require 'object_daddy'
@@ -0,0 +1,19 @@
1
+ module ObjectDaddy
2
+ class Railtie < Rails::Railtie
3
+ railtie_name :object_daddy
4
+ initializer 'object_daddy.extend.activerecord' do
5
+ ::ActiveSupport.on_load :active_record do
6
+ class ::ActiveRecord::Base
7
+ def self.inherited_with_object_daddy(subclass)
8
+ self.inherited_without_object_daddy(subclass)
9
+ subclass.send(:include, ObjectDaddy) unless subclass < ObjectDaddy
10
+ end
11
+
12
+ class << self
13
+ alias_method_chain :inherited, :object_daddy
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end