machinery 0.9
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/MIT-LICENSE +20 -0
- data/README.markdown +208 -0
- data/lib/machinery/active_record.rb +48 -0
- data/lib/machinery.rb +77 -0
- metadata +58 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Lawrence Pit
|
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,208 @@
|
|
1
|
+
Machinery
|
2
|
+
=========
|
3
|
+
|
4
|
+
*Machinery to create object graphs and speed up tests.*
|
5
|
+
|
6
|
+
Machinery helps to create object graphs for use in tests without needing
|
7
|
+
fixtures, using pure ruby style. These object graphs are easily re-usable
|
8
|
+
in Test::Unit, RSpec, Cucumber and in rake tasks to populate a database.
|
9
|
+
|
10
|
+
Secondly, even if you don't need object graphs, Machinery can speed up
|
11
|
+
your tests significantly because you can set up any model objects in
|
12
|
+
a before(:all) instead of a before(:each), and at the end of the tests
|
13
|
+
all activerecord objects created in the before(:all) will be cleaned up
|
14
|
+
automatically.
|
15
|
+
|
16
|
+
Machinery works with plain ruby objects and currently supports ActiveRecord.
|
17
|
+
A DataMapper abstraction should be too easy.
|
18
|
+
|
19
|
+
|
20
|
+
Usage
|
21
|
+
-----
|
22
|
+
|
23
|
+
#### Object Graphs
|
24
|
+
|
25
|
+
In /spec/scenarios.rb
|
26
|
+
|
27
|
+
Machinery.define do
|
28
|
+
scenario :earth_ships do
|
29
|
+
@titanic = Ship.create!(:name => "Titanic")
|
30
|
+
@pirate = Ship.create!(:name => "Pirate")
|
31
|
+
end
|
32
|
+
|
33
|
+
scenario :space_ships do
|
34
|
+
@apollo = Ship.create!(:name => "Apollo")
|
35
|
+
@uss_enterprise = Ship.create!(:name => "USS Enterprise")
|
36
|
+
end
|
37
|
+
|
38
|
+
scenario :all_ships do
|
39
|
+
scenarios :earth_ships, :space_ships
|
40
|
+
@sunken_ship = Ship.create!(:name => "Sunk")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
In /spec/spec_helper.rb
|
46
|
+
|
47
|
+
require 'machinery'
|
48
|
+
require File.join(File.dirname(__FILE__) + 'scenarios')
|
49
|
+
|
50
|
+
|
51
|
+
In /spec/models/ship_spec.rb
|
52
|
+
|
53
|
+
describe Ship do
|
54
|
+
scenarios :all_ships
|
55
|
+
|
56
|
+
it "should create a pirate ship" do
|
57
|
+
@pirate.should_not be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have 5 ships" do
|
61
|
+
Ship.count.should == 5
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
#### Just the speed up please
|
67
|
+
|
68
|
+
If it's only speeding up your tests you're after you can use anonymous scenarios, like this:
|
69
|
+
|
70
|
+
In /spec/models/ship_spec.rb
|
71
|
+
|
72
|
+
describe Ship do
|
73
|
+
scenario do
|
74
|
+
@pirate = Ship.create!(:name => "Pirate")
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should update a pirate ship" do
|
78
|
+
@pirate.name = "Updated Pirate"
|
79
|
+
@pirate.save
|
80
|
+
@pirate.reload
|
81
|
+
@pirate.name.should == "Updated Pirate"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should have 1 ship" do
|
85
|
+
Ship.count.should == 1
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should be named 'Pirate'" do
|
89
|
+
@pirate.name.should == "Pirate"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
This will create one ship only in the database.
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
Explain
|
98
|
+
-------
|
99
|
+
|
100
|
+
When you use +scenario+ with a block you are defining a scenario. Any instance
|
101
|
+
variables you declare within a scenario will be available in your test when you
|
102
|
+
use that scenario.
|
103
|
+
|
104
|
+
When you use +scenario+ without a block then you indicate that the block for the
|
105
|
+
scenario you previously defined should be executed. Any instance variables you
|
106
|
+
defined within the block will now be available to you in your test methods. Note
|
107
|
+
that you can re-use scenarios within scenarios.
|
108
|
+
|
109
|
+
When you declare to use a scenario within an rspec +ExampleGroup+ (as in the first
|
110
|
+
ship_spec example above) then the instances will be created for you exactly once.
|
111
|
+
They will automatically be removed from the database after all the tests have run.
|
112
|
+
Any instance variables declared within a scenario will be cloned before each test.
|
113
|
+
If you make sure you use +use_transactional_fixtures+ then this means:
|
114
|
+
|
115
|
+
* speedy tests, because instances are created exactly once for all tests
|
116
|
+
* you can mess around with the instance variables any way you like within a test method as before each test they will always be in exactly the same initial state.
|
117
|
+
|
118
|
+
A special case is when you use an anonymous +scenario+ with a block, specified at the
|
119
|
+
top of your tests (as in the second ship_spec example above). It will behave as if it's
|
120
|
+
a named scenario *and* it is used immediately.
|
121
|
+
At most one anonymous +scenario+ per example group can be used.
|
122
|
+
|
123
|
+
|
124
|
+
Magic?
|
125
|
+
------
|
126
|
+
|
127
|
+
Why write stuff like:
|
128
|
+
|
129
|
+
users(:admin).should be_nil
|
130
|
+
|
131
|
+
when you can just as well write:
|
132
|
+
|
133
|
+
@user_admin.should be_nil
|
134
|
+
|
135
|
+
Use less magic. Less code. Makes software softer.
|
136
|
+
|
137
|
+
|
138
|
+
Installation
|
139
|
+
------------
|
140
|
+
|
141
|
+
### Rails installation (Test::Unit)
|
142
|
+
|
143
|
+
#### As a Gem
|
144
|
+
|
145
|
+
Use this if you prefer to use versioned releases of Machinery.
|
146
|
+
Specify the gem dependency in your config/environment.rb file:
|
147
|
+
|
148
|
+
Rails::Initializer.run do |config|
|
149
|
+
config.gem "lawrencepit-machinery", :lib => "machinery", :source => "http://gems.github.com"
|
150
|
+
end
|
151
|
+
|
152
|
+
Then:
|
153
|
+
|
154
|
+
$ rake gems:install
|
155
|
+
$ rake gems:unpack
|
156
|
+
|
157
|
+
#### As a Plugin
|
158
|
+
|
159
|
+
Use this if you prefer to use the edge version of Machinery:
|
160
|
+
|
161
|
+
$ script/plugin install git://github.com/lawrencepit/machinery.git
|
162
|
+
|
163
|
+
### Rails installation (RSpec)
|
164
|
+
|
165
|
+
If you're using Machinery with RSpec, it is recommended that you add config.gem lines
|
166
|
+
for RSpec and Machinery in your config/environment/test.rb file, but do not ask
|
167
|
+
Rails to load the RSpec and Machinery libraries:
|
168
|
+
|
169
|
+
config.gem 'rspec', :lib => false
|
170
|
+
config.gem 'rspec-rails', :lib => false
|
171
|
+
config.gem 'lawrencepit-machinery', :lib => false, :source => 'http://gems.github.com'
|
172
|
+
|
173
|
+
Then require machinery from your spec/spec_helper.rb file, before Spec::Runner is
|
174
|
+
configured:
|
175
|
+
|
176
|
+
# requires for RSpec
|
177
|
+
require 'machinery'
|
178
|
+
Spec::Runner.configure do |config|
|
179
|
+
# ...
|
180
|
+
|
181
|
+
You should not need to require anything besides the top-level machinery library.
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
Credits
|
186
|
+
-------
|
187
|
+
|
188
|
+
Written by [Lawrence Pit](http://lawrencepit.com).
|
189
|
+
|
190
|
+
Thanks to
|
191
|
+
[Machinist](http://github.com/notahat/machinist),
|
192
|
+
[ModelStubbing](http://github.com/technoweenie/model_stubbing),
|
193
|
+
[FactoryGirl](http://github.com/thoughtbot/factory_girl),
|
194
|
+
[FixtureScenarios](http://errtheblog.com/posts/61-fixin-fixtures) and
|
195
|
+
[Hornsby](http://github.com/lachie/hornsby)
|
196
|
+
for inspiration.
|
197
|
+
|
198
|
+
After using model_stubbing on a relatively large project for over a year I decided
|
199
|
+
a simpler solution was needed. Object graphs need to be easily re-usable by specs,
|
200
|
+
unit tests, cucumber stories and ad-hoc database loading. And tests need to be speedy.
|
201
|
+
|
202
|
+
Machinery works especially pleasurable in combination with
|
203
|
+
[Machinist](http://github.com/notahat/machinist) or
|
204
|
+
[FactoryGirl](http://github.com/thoughtbot/factory_girl).
|
205
|
+
|
206
|
+
|
207
|
+
----
|
208
|
+
Copyright (c) 2009 Lawrence Pit, released under the MIT license
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Machinery
|
2
|
+
module ActiveRecord
|
3
|
+
@@toys = []
|
4
|
+
@@recording_level = 0
|
5
|
+
|
6
|
+
def self.record
|
7
|
+
@@recording_level = @@recording_level + 1
|
8
|
+
yield
|
9
|
+
@@recording_level = @@recording_level - 1
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.clear
|
13
|
+
@@toys.each do |toy|
|
14
|
+
toy.connection.delete(
|
15
|
+
"DELETE FROM #{toy.class.quoted_table_name} " +
|
16
|
+
"WHERE #{toy.connection.quote_column_name(toy.class.primary_key)} = #{toy.quoted_id}",
|
17
|
+
"#{toy.class.name} Destroy"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
@@toys = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.created(obj)
|
24
|
+
@@toys << obj if @@recording_level > 0
|
25
|
+
end
|
26
|
+
|
27
|
+
module Extensions
|
28
|
+
private
|
29
|
+
|
30
|
+
def self.included(base)
|
31
|
+
base.class_eval do
|
32
|
+
alias_method :create_without_machinery, :create
|
33
|
+
alias_method :create, :create_with_machinery
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_with_machinery
|
38
|
+
id = create_without_machinery
|
39
|
+
Machinery::ActiveRecord.created(self)
|
40
|
+
id
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
ActiveRecord::Base.send(:include, Machinery::ActiveRecord::Extensions)
|
48
|
+
|
data/lib/machinery.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'machinery/active_record'
|
3
|
+
|
4
|
+
module Machinery
|
5
|
+
@@scenarios = {}
|
6
|
+
|
7
|
+
def self.scenario(name, &block)
|
8
|
+
@@scenarios[name] = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.load(file)
|
12
|
+
Machinery.instance_eval(File.read(file))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.define(&block)
|
16
|
+
Machinery.instance_eval(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def self.make_scenario(context, names, &block)
|
21
|
+
evaluated = context.instance_variable_get('@_machinery') || []
|
22
|
+
Machinery::ActiveRecord.record do
|
23
|
+
names.each do |name|
|
24
|
+
next if evaluated.include?(name)
|
25
|
+
context.instance_variable_set('@_machinery', evaluated << name)
|
26
|
+
context.instance_eval(&@@scenarios[name])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Helpers
|
32
|
+
def scenario(*names, &block)
|
33
|
+
Machinery.scenario(names.first, &block) and return if block_given?
|
34
|
+
Machinery.make_scenario(self, names, &block)
|
35
|
+
end
|
36
|
+
alias_method :scenarios, :scenario
|
37
|
+
|
38
|
+
def rollback_scenario
|
39
|
+
Machinery::ActiveRecord.clear
|
40
|
+
end
|
41
|
+
alias_method :rollback_scenarios, :rollback_scenario
|
42
|
+
|
43
|
+
private
|
44
|
+
def clone_instance_variables
|
45
|
+
self.instance_variables.each do |ivar|
|
46
|
+
obj = self.instance_variable_get(ivar)
|
47
|
+
next unless obj.is_a?(::ActiveRecord::Base)
|
48
|
+
clone = obj.clone
|
49
|
+
clone.id = obj.id
|
50
|
+
clone.instance_variable_set('@new_record', obj.new_record?)
|
51
|
+
self.instance_variable_set(ivar, clone)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module ExampleGroupMacros
|
57
|
+
def scenario(*names, &block)
|
58
|
+
scenario(:__anonymous__, &block) and scenario(:__anonymous__) and return if names.empty?
|
59
|
+
Machinery.scenario(names.first, &block) and return true if block_given?
|
60
|
+
self.before(:all) { scenarios(*names) }
|
61
|
+
self.after(:all) { rollback_scenarios }
|
62
|
+
self.before(:each) { clone_instance_variables }
|
63
|
+
end
|
64
|
+
alias_method :scenarios, :scenario
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if defined? Test::Unit::TestCase
|
69
|
+
Test::Unit::TestCase.extend Machinery::ExampleGroupMacros
|
70
|
+
Test::Unit::TestCase.send('include', Machinery::Helpers)
|
71
|
+
end
|
72
|
+
|
73
|
+
if defined? Spec::Example::ExampleGroup
|
74
|
+
Spec::Example::ExampleGroup.extend Machinery::ExampleGroupMacros
|
75
|
+
Spec::Example::ExampleGroupFactory[nil].send('include', Machinery::Helpers)
|
76
|
+
end
|
77
|
+
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: machinery
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.9"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lawrence Pit
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-09 00:00:00 +11:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: lawrence.pit@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README.markdown
|
26
|
+
- MIT-LICENSE
|
27
|
+
- lib/machinery.rb
|
28
|
+
- lib/machinery/active_record.rb
|
29
|
+
has_rdoc: true
|
30
|
+
homepage: http://github.com/lawrencepit/machinery
|
31
|
+
licenses: []
|
32
|
+
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: "0"
|
43
|
+
version:
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 1.3.5
|
54
|
+
signing_key:
|
55
|
+
specification_version: 3
|
56
|
+
summary: Machinery to create object graphs and speed up tests.
|
57
|
+
test_files: []
|
58
|
+
|