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 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
+