machinery 0.9

Sign up to get free protection for your applications and to get access to all the features.
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
+