machinist 1.0.6 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +3 -2
  2. data/Gemfile +8 -0
  3. data/MIT-LICENSE +2 -1
  4. data/README.markdown +39 -271
  5. data/Rakefile +22 -14
  6. data/lib/generators/machinist/install/USAGE +2 -0
  7. data/lib/generators/machinist/install/install_generator.rb +48 -0
  8. data/lib/generators/machinist/install/templates/blueprints.rb +9 -0
  9. data/lib/generators/machinist/install/templates/machinist.rb.erb +10 -0
  10. data/lib/generators/machinist/model/model_generator.rb +13 -0
  11. data/lib/machinist.rb +11 -105
  12. data/lib/machinist/active_record.rb +8 -93
  13. data/lib/machinist/active_record/blueprint.rb +41 -0
  14. data/lib/machinist/active_record/lathe.rb +24 -0
  15. data/lib/machinist/blueprint.rb +89 -0
  16. data/lib/machinist/exceptions.rb +32 -0
  17. data/lib/machinist/lathe.rb +69 -0
  18. data/lib/machinist/machinable.rb +97 -0
  19. data/lib/machinist/shop.rb +52 -0
  20. data/lib/machinist/warehouse.rb +36 -0
  21. data/spec/active_record_spec.rb +100 -169
  22. data/spec/blueprint_spec.rb +74 -0
  23. data/spec/exceptions_spec.rb +20 -0
  24. data/spec/inheritance_spec.rb +104 -0
  25. data/spec/machinable_spec.rb +101 -0
  26. data/spec/shop_spec.rb +94 -0
  27. data/spec/spec_helper.rb +4 -6
  28. data/spec/support/active_record_environment.rb +65 -0
  29. data/spec/warehouse_spec.rb +24 -0
  30. metadata +52 -40
  31. data/.autotest +0 -7
  32. data/FAQ.markdown +0 -18
  33. data/VERSION +0 -1
  34. data/init.rb +0 -2
  35. data/lib/machinist/blueprints.rb +0 -25
  36. data/lib/machinist/data_mapper.rb +0 -83
  37. data/lib/machinist/object.rb +0 -30
  38. data/lib/machinist/sequel.rb +0 -62
  39. data/lib/sham.rb +0 -77
  40. data/machinist.gemspec +0 -72
  41. data/spec/data_mapper_spec.rb +0 -134
  42. data/spec/db/.gitignore +0 -1
  43. data/spec/db/schema.rb +0 -20
  44. data/spec/log/.gitignore +0 -1
  45. data/spec/machinist_spec.rb +0 -190
  46. data/spec/sequel_spec.rb +0 -146
  47. data/spec/sham_spec.rb +0 -95
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
+ .bundle
2
+ .DS_Store
1
3
  coverage
2
4
  doc
3
- *.gem
4
- *.project
5
+ tags
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :gemcutter
2
+
3
+ gem "activerecord"
4
+ gem "mysql"
5
+ gem "rake"
6
+ gem "rcov"
7
+ gem "rspec", ">= 2.0.0.beta.12"
8
+ gem "jeweler"
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008 Peter Yandell
1
+ Copyright (c) 2008-2010 Peter Yandell
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -18,3 +18,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.markdown CHANGED
@@ -1,295 +1,58 @@
1
- Machinist
2
- =========
1
+ # Machinist 2
3
2
 
4
3
  *Fixtures aren't fun. Machinist is.*
5
-
6
- Machinist makes it easy to create test data within your tests. It generates data for the fields you don't care about, and constructs any necessary associated objects, leaving you to only specify the fields you *do* care about in your tests. For example:
7
-
8
- describe Comment do
9
- before do
10
- # This will make a Comment, a Post, and a User (the author of
11
- # the Post), and generate values for all their attributes:
12
- @comment = Comment.make(:spam => true)
13
- end
14
-
15
- it "should not include comments marked as spam in the without_spam named scope" do
16
- Comment.without_spam.should_not include(@comment)
17
- end
18
- end
19
-
20
- You tell Machinist how to do this with blueprints:
21
-
22
- require 'machinist/active_record'
23
- require 'sham'
24
- require 'faker'
25
-
26
- Sham.name { Faker::Name.name }
27
- Sham.email { Faker::Internet.email }
28
- Sham.title { Faker::Lorem.sentence }
29
- Sham.body { Faker::Lorem.paragraph }
30
-
31
- User.blueprint do
32
- name
33
- email
34
- end
35
-
36
- Post.blueprint do
37
- title
38
- author
39
- body
40
- end
41
-
42
- Comment.blueprint do
43
- post
44
- author_name { Sham.name }
45
- author_email { Sham.email }
46
- body
47
- end
48
-
49
-
50
- Download & Install
51
- ==================
52
-
53
- ### Installing as a Rails plugin
54
-
55
- ./script/plugin install git://github.com/notahat/machinist.git
56
-
57
- ### Installing as a Gem
58
-
59
- sudo gem install machinist --source http://gemcutter.org
60
-
61
- ### Setting up your project
62
-
63
- Create a `blueprints.rb` file to hold your blueprints in your test (or spec) directory. It should start with:
64
-
65
- require 'machinist/active_record'
66
- require 'sham'
67
-
68
- Substitute `data_mapper` or `sequel` for `active_record` if that's your weapon of choice.
69
-
70
- Require `blueprints.rb` in your `test_helper.rb` (or `spec_helper.rb`):
71
-
72
- require File.expand_path(File.dirname(__FILE__) + "/blueprints")
73
-
74
- Set Sham to reset before each test. In the `class Test::Unit::TestCase` block in your `test_helper.rb`, add:
75
-
76
- setup { Sham.reset }
77
-
78
- or, if you're on RSpec, in the `Spec::Runner.configure` block in your `spec_helper.rb`, add:
79
-
80
- config.before(:all) { Sham.reset(:before_all) }
81
- config.before(:each) { Sham.reset(:before_each) }
82
-
83
-
84
- Documentation
85
- =============
86
-
87
- Sham - Generating Attribute Values
88
- ----------------------------------
89
-
90
- Sham lets you generate random but repeatable unique attributes values.
91
-
92
- For example, you could define a way to generate random names as:
93
-
94
- Sham.name { (1..10).map { ('a'..'z').to_a.rand }.join }
95
-
96
- Then, to generate a name, call:
97
-
98
- Sham.name
99
-
100
- So why not just define a helper method to do this? Sham ensures two things for you:
101
-
102
- 1. You get the same sequence of values each time your test is run
103
- 2. You don't get any duplicate values
104
-
105
- Sham works very well with the excellent [Faker gem](http://faker.rubyforge.org/) by Benjamin Curtis. Using this, a much nicer way to generate names is:
106
-
107
- Sham.name { Faker::Name.name }
108
-
109
- Sham also supports generating numbered sequences if you prefer.
110
-
111
- Sham.name {|index| "Name #{index}" }
112
-
113
- If you want to allow duplicate values for a sham, you can pass the `:unique` option:
114
-
115
- Sham.coin_toss(:unique => false) { rand(2) == 0 ? 'heads' : 'tails' }
116
-
117
- You can create a bunch of sham definitions in one hit like this:
118
-
119
- Sham.define do
120
- title { Faker::Lorem.words(5).join(' ') }
121
- name { Faker::Name.name }
122
- body { Faker::Lorem.paragraphs(3).join("\n\n") }
123
- end
124
-
125
-
126
- Blueprints - Generating Objects
127
- -------------------------------
128
-
129
- A blueprint describes how to generate an object. The idea is that you let the blueprint take care of making up values for attributes that you don't care about in your test, leaving you to focus on the just the things that you're testing.
130
-
131
- A simple blueprint might look like this:
132
-
133
- Post.blueprint do
134
- title { Sham.title }
135
- author { Sham.name }
136
- body { Sham.body }
137
- end
138
4
 
139
- You can then construct a Post from this blueprint with:
140
-
141
- Post.make
142
-
143
- When you call `make`, Machinist calls `Post.new`, then runs through the attributes in your blueprint, calling the block for each attribute to generate a value. The Post is then saved and reloaded. An exception is thrown if Post can't be saved.
5
+ - [Home page](http://github.com/notahat/machinist/tree/machinist2)
6
+ - [What's new in Machinist 2](http://wiki.github.com/notahat/machinist/machinist-2)
7
+ - [Installation](http://wiki.github.com/notahat/machinist/installation)
8
+ - [Documentation](http://wiki.github.com/notahat/machinist/getting-started)
9
+ - [Google group](http://groups.google.com/group/machinist-users)
10
+ - [Issue tracker](http://github.com/notahat/machinist/issues)
144
11
 
145
- You can override values defined in the blueprint by passing a hash to make:
146
12
 
147
- Post.make(:title => "A Specific Title")
148
-
149
- If you don't supply a block for an attribute in the blueprint, Machinist will look for a Sham definition with the same name as the attribute, so you can shorten the above blueprint to:
13
+ # Introduction
150
14
 
151
- Post.blueprint do
152
- title
153
- author { Sham.name }
154
- body
155
- end
156
-
157
- If you want to generate an object without saving it to the database, replace `make` with `make_unsaved`. (`make_unsaved` also ensures that any associated objects that need to be generated are not saved - although not if you are using Sequel. See the section on associations below.)
15
+ Machinist makes it easy to create objects within your tests. It generates data
16
+ for the attributes you don't care about, and constructs any necessary
17
+ associated objects, leaving you to specify only the attributes you *do* care
18
+ about in your tests. For example:
158
19
 
159
- You can refer to already assigned attributes when constructing a new attribute:
20
+ describe Comment do
21
+ it "should not include spam in the without_spam scope" do
22
+ # This will make a Comment, a Post, and a User (the author of the
23
+ # Post), generate values for all their attributes, and save them:
24
+ spam = Comment.make!(:spam => true)
160
25
 
161
- Post.blueprint do
162
- title
163
- author { Sham.name }
164
- body { "Post by #{author}" }
26
+ Comment.without_spam.should_not include(spam)
27
+ end
165
28
  end
166
-
167
29
 
168
- ### Named Blueprints
30
+ You tell Machinist how to do this with blueprints:
169
31
 
170
- Named blueprints let you define variations on an object. For example, suppose some of your Users are administrators:
32
+ require 'machinist/active_record'
171
33
 
172
34
  User.blueprint do
173
- name
174
- email
175
- end
176
-
177
- User.blueprint(:admin) do
178
- name { Sham.name + " (admin)" }
179
- admin { true }
35
+ username { "user#{sn}" } # Each user gets a unique serial number.
180
36
  end
181
-
182
- Calling:
183
-
184
- User.make(:admin)
185
-
186
- will use the `:admin` blueprint.
187
-
188
- Named blueprints call the default blueprint to set any attributes not specifically provided, so in this example the `email` attribute will still be generated even for an admin user.
189
-
190
-
191
- ### Belongs\_to Associations
192
-
193
- If you're generating an object that belongs to another object, you can generate the associated object like this:
194
-
195
- Comment.blueprint do
196
- post { Post.make }
37
+
38
+ Post.blueprint do
39
+ author
40
+ title { "Post #{sn}" }
41
+ body { "Lorem ipsum..." }
197
42
  end
198
-
199
- Calling `Comment.make` will construct a Comment and its associated Post, and save both.
200
43
 
201
- If you want to override the value for post when constructing the comment, you can do this:
202
-
203
- post = Post.make(:title => "A particular title)
204
- comment = Comment.make(:post => post)
205
-
206
- Machinist will not call the blueprint block for the post attribute, so this won't generate two posts.
207
-
208
- Machinist is smart enough to look at the association and work out what sort of object it needs to create, so you can shorten the above blueprint to:
209
-
210
44
  Comment.blueprint do
211
45
  post
46
+ email { "commenter-#{sn}@example.com" }
47
+ body { "Lorem ipsum..." }
212
48
  end
213
49
 
214
-
215
- ### Other Associations
216
-
217
- For has\_many and has\_and\_belongs\_to\_many associations, ActiveRecord insists that the object be saved before any associated objects can be saved. That means you can't generate the associated objects from within the blueprint.
218
-
219
- The simplest solution is to write a test helper:
220
-
221
- def make_post_with_comments(attributes = {})
222
- post = Post.make(attributes)
223
- 3.times { post.comments.make }
224
- post
225
- end
226
-
227
- Note here that you can call `make` on a has\_many association. (This isn't yet supported for DataMapper.)
228
-
229
- Make can take a block, into which it passes the constructed object, so the above can be written as:
230
-
231
- def make_post_with_comments
232
- Post.make(attributes) do |post|
233
- 3.times { post.comments.make }
234
- end
235
- end
236
-
237
-
238
- ### Using Blueprints in Rails Controller Tests
239
-
240
- The `plan` method behaves like `make`, except it returns a hash of attributes, and doesn't save the object. This is useful for passing in to controller tests:
241
-
242
- test "should create post" do
243
- assert_difference('Post.count') do
244
- post :create, :post => Post.plan
245
- end
246
- assert_redirected_to post_path(assigns(:post))
247
- end
248
-
249
- `plan` will save any associated objects. In this example, it will create an Author, and it knows that the controller expects an `author_id` attribute, rather than an `author` attribute, and makes this translation for you.
250
-
251
- You can also call plan on has\_many associations, making it easy to test nested controllers:
252
-
253
- test "should create comment" do
254
- post = Post.make
255
- assert_difference('Comment.count') do
256
- post :create, :post_id => post.id, :comment => post.comments.plan
257
- end
258
- assert_redirected_to post_comment_path(post, assigns(:comment))
259
- end
260
-
261
- (Calling plan on associations is not yet supported in DataMapper.)
262
-
50
+ Check out the
51
+ [documentation](http://wiki.github.com/notahat/machinist/getting-started) for
52
+ more info.
263
53
 
264
- ### Blueprints on Plain Old Ruby Objects
265
54
 
266
- Machinist also works with plain old Ruby objects. Let's say you have a class like:
267
-
268
- class Post
269
- attr_accessor :title
270
- attr_accessor :body
271
- end
272
-
273
- You can then do the following in your `blueprints.rb`:
274
-
275
- require 'machinist/object'
276
-
277
- Post.blueprint do
278
- title "A title!"
279
- body "A body!"
280
- end
281
-
282
- Community
283
- =========
284
-
285
- You can always find the [latest version on GitHub](http://github.com/notahat/machinist).
286
-
287
- If you have questions, check out the [Google Group](http://groups.google.com/group/machinist-users).
288
-
289
- File bug reports and feature requests in the [issue tracker](http://github.com/notahat/machinist/issues).
290
-
291
- Contributors
292
- ------------
55
+ ## Contributors
293
56
 
294
57
  Machinist is maintained by Pete Yandell ([pete@notahat.com](mailto:pete@notahat.com), [@notahat](http://twitter.com/notahat))
295
58
 
@@ -297,6 +60,7 @@ Other contributors include:
297
60
 
298
61
  [Marcos Arias](http://github.com/yizzreel),
299
62
  [Jack Dempsey](http://github.com/jackdempsey),
63
+ [Jeremy Durham](http://github.com/jeremydurham),
300
64
  [Clinton Forbes](http://github.com/clinton),
301
65
  [Perryn Fowler](http://github.com/perryn),
302
66
  [Niels Ganser](http://github.com/Nielsomat),
@@ -314,4 +78,8 @@ Other contributors include:
314
78
  [Matt Wastrodowski](http://github.com/towski),
315
79
  [Ian White](http://github.com/ianwhite)
316
80
 
317
- Thanks to Thoughtbot's [Factory Girl](http://github.com/thoughtbot/factory_girl/tree/master). Machinist was written because I loved the idea behind Factory Girl, but I thought the philosophy wasn't quite right, and I hated the syntax.
81
+ Thanks to Thoughtbot's [Factory
82
+ Girl](http://github.com/thoughtbot/factory_girl/tree/master). Machinist was
83
+ written because I loved the idea behind Factory Girl, but I thought the
84
+ philosophy wasn't quite right, and I hated the syntax.
85
+
data/Rakefile CHANGED
@@ -1,4 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
1
5
  require 'rake'
6
+ require 'rspec/core/rake_task'
7
+ require 'rake/rdoctask'
2
8
 
3
9
  begin
4
10
  require 'jeweler'
@@ -9,29 +15,31 @@ begin
9
15
  gem.homepage = "http://github.com/notahat/machinist"
10
16
  gem.authors = ["Pete Yandell"]
11
17
  gem.has_rdoc = false
12
- gem.add_development_dependency "rspec", ">= 1.2.8"
13
18
  end
14
19
  Jeweler::GemcutterTasks.new
15
20
  rescue LoadError
16
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ puts "Jeweler not available. Install it with: gem install jeweler"
17
22
  end
18
23
 
19
24
 
20
- require 'spec/rake/spectask'
21
- desc 'Run the specs.'
22
- Spec::Rake::SpecTask.new(:spec) do |spec|
23
- spec.libs << 'lib' << 'spec'
24
- spec.spec_files = FileList['spec/**/*_spec.rb']
25
- end
25
+ RSpec::Core::RakeTask.new
26
26
 
27
- desc 'Run the specs with rcov.'
28
- Spec::Rake::SpecTask.new(:rcov) do |spec|
29
- spec.libs << 'lib' << 'spec'
30
- spec.pattern = 'spec/**/*_spec.rb'
27
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
31
28
  spec.rcov = true
29
+ spec.rcov_opts = ['--exclude', 'spec', '--exclude', '.rvm']
32
30
  end
33
31
 
34
- task :spec => :check_dependencies
35
-
36
32
  desc 'Run the specs.'
37
33
  task :default => :spec
34
+
35
+
36
+ Rake::RDocTask.new(:rdoc) do |rdoc|
37
+ rdoc.rdoc_dir = 'doc'
38
+ rdoc.title = 'Machinist'
39
+ rdoc.options << '--line-numbers' << '--inline-source'
40
+ rdoc.rdoc_files.include('lib')
41
+ end
42
+
43
+ task :notes do
44
+ system "grep -n -r 'FIXME\\|TODO' lib spec"
45
+ end
@@ -0,0 +1,2 @@
1
+ Description:
2
+ Copy Machinist files to your application.
@@ -0,0 +1,48 @@
1
+ module Machinist
2
+ module Generators #:nodoc:
3
+ class InstallGenerator < Rails::Generators::Base #:nodoc:
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ class_option :test_framework, :type => :string, :aliases => "-t", :desc => "Test framework to use Machinist with"
7
+ class_option :cucumber, :type => :boolean, :desc => "Set up access to Machinist from Cucumber"
8
+
9
+ def blueprints_file
10
+ if rspec?
11
+ copy_file "blueprints.rb", "spec/support/blueprints.rb"
12
+ else
13
+ copy_file "blueprints.rb", "test/blueprints.rb"
14
+ end
15
+ end
16
+
17
+ def test_helper
18
+ if rspec?
19
+ inject_into_file("spec/spec_helper.rb", :after => "Rspec.configure do |config|\n") do
20
+ " # Reset the Machinist cache before each spec.\n" +
21
+ " config.before(:each) { Machinist.reset_before_test }\n\n"
22
+ end
23
+ else
24
+ inject_into_file("test/test_helper.rb", :after => "require 'rails/test_help'\n") do
25
+ "require File.expand_path(File.dirname(__FILE__) + '/blueprints')\n"
26
+ end
27
+ inject_into_class("test/test_helper.rb", ActiveSupport::TestCase) do
28
+ " # Reset the Machinist cache before each test.\n" +
29
+ " setup { Machinist.reset_before_test }\n\n"
30
+ end
31
+ end
32
+ end
33
+
34
+ def cucumber_support
35
+ if options[:cucumber]
36
+ template "machinist.rb.erb", "features/support/machinist.rb"
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def rspec?
43
+ options[:test_framework].to_sym == :rspec
44
+ end
45
+
46
+ end
47
+ end
48
+ end