blueprints 0.9.0 → 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/.gitignore +1 -0
- data/Gemfile +14 -1
- data/README.rdoc +3 -173
- data/Rakefile +32 -15
- data/blueprints.gemspec +2 -16
- data/lib/blueprints.rb +17 -10
- data/lib/blueprints/blueprint.rb +85 -41
- data/lib/blueprints/blueprint_name_proxy.rb +34 -0
- data/lib/blueprints/buildable.rb +53 -26
- data/lib/blueprints/context.rb +8 -0
- data/lib/blueprints/extensions.rb +22 -4
- data/lib/blueprints/extensions/rspec.rb +28 -14
- data/lib/blueprints/helper.rb +17 -9
- data/lib/blueprints/namespace.rb +20 -14
- data/lib/blueprints/railtie.rb +3 -0
- data/lib/blueprints/root_namespace.rb +16 -23
- data/lib/blueprints/version.rb +1 -1
- data/lib/generators/blueprints/model/model_generator.rb +29 -0
- data/spec/blueprints_spec.rb +18 -1
- data/spec/support/active_record/initializer.rb +9 -5
- data/spec/support/none/initializer.rb +4 -0
- data/spec/unit/active_record_spec.rb +58 -4
- data/spec/unit/blueprint_name_proxy_spec.rb +31 -0
- data/spec/unit/blueprint_spec.rb +160 -22
- data/spec/unit/blueprints_spec.rb +4 -4
- data/spec/unit/context_spec.rb +8 -1
- data/spec/unit/dependency_spec.rb +1 -5
- data/spec/unit/fixtures.rb +69 -47
- data/spec/unit/namespace_spec.rb +23 -5
- data/spec/unit/root_namespace_spec.rb +9 -0
- data/spec/unit/spec_helper.rb +3 -4
- data/test/blueprints_test.rb +18 -1
- data/test/test_helper.rb +1 -0
- data/test_all.sh +6 -33
- metadata +43 -276
- data/Gemfile.lock +0 -108
- data/lib/blueprints/database_cleaner_fix.rb +0 -9
- data/lib/blueprints/eval_context.rb +0 -51
- data/spec/unit/eval_context_spec.rb +0 -56
data/lib/blueprints/version.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Blueprints
|
2
|
+
class ModelGenerator < Rails::Generators::NamedBase
|
3
|
+
argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
|
4
|
+
|
5
|
+
def create_blueprint
|
6
|
+
blueprint = "#{class_name}.blueprint :#{singular_name}"
|
7
|
+
attributes.each do |attribute|
|
8
|
+
blueprint << ", :#{attribute.name} => #{default_for(attribute)}"
|
9
|
+
end
|
10
|
+
|
11
|
+
dir = File.exists?('spec') ? 'spec' : 'test'
|
12
|
+
file = "#{dir}/blueprint.rb"
|
13
|
+
create_file file unless File.exists?(file)
|
14
|
+
append_file file, "#{blueprint}\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def default_for(attribute)
|
20
|
+
if attribute.reference?
|
21
|
+
"d(:#{attribute.name})"
|
22
|
+
else
|
23
|
+
attribute.default.inspect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Rails::Generators.hide_namespace(:blueprints)
|
data/spec/blueprints_spec.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
if RUBY_VERSION.start_with?('1.9')
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
else
|
4
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
5
|
+
end
|
2
6
|
|
3
7
|
describe Blueprints do
|
4
8
|
it "should return result of built scenario when calling build" do
|
@@ -70,6 +74,7 @@ describe Blueprints do
|
|
70
74
|
describe "build per describe" do
|
71
75
|
unless File.dirname(__FILE__) =~ /test$/
|
72
76
|
build_blueprint :apple
|
77
|
+
build :acorn => {:tree => d(:pine)}
|
73
78
|
|
74
79
|
it "should have cherry" do
|
75
80
|
@apple.should_not be_nil
|
@@ -78,6 +83,10 @@ describe Blueprints do
|
|
78
83
|
it "should have correct cherry species" do
|
79
84
|
@apple.species.should == 'apple'
|
80
85
|
end
|
86
|
+
|
87
|
+
it "should set tree to pine" do
|
88
|
+
@acorn.tree.should == @pine
|
89
|
+
end
|
81
90
|
end
|
82
91
|
end
|
83
92
|
|
@@ -371,4 +380,12 @@ describe Blueprints do
|
|
371
380
|
it "should allow inferring blueprint name" do
|
372
381
|
build(:infered).name.should == 'infered'
|
373
382
|
end
|
383
|
+
|
384
|
+
it "should allow building with :new strategy" do
|
385
|
+
build_with(:new, :oak)
|
386
|
+
@oak.should be_instance_of(Tree)
|
387
|
+
@oak.should be_new_record
|
388
|
+
@oak.name.should == 'Oak'
|
389
|
+
@oak.size.should == 'large'
|
390
|
+
end
|
374
391
|
end
|
@@ -1,15 +1,19 @@
|
|
1
|
+
db_config = YAML::load_file(File.expand_path("../database.yml", __FILE__))
|
2
|
+
ActiveRecord::Base.establish_connection(db_config)
|
3
|
+
ActiveRecord::Base.logger = @logger
|
4
|
+
|
1
5
|
class Fruit < ActiveRecord::Base
|
2
6
|
belongs_to :tree
|
3
7
|
end
|
4
8
|
|
5
9
|
class Tree < ActiveRecord::Base
|
6
10
|
attr_protected :size
|
7
|
-
has_many :fruits
|
8
|
-
|
11
|
+
has_many :fruits, :after_add => :fruit_after_add
|
12
|
+
belongs_to :main_fruit, :class_name => 'Fruit'
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
def fruit_after_add(_)
|
15
|
+
end
|
16
|
+
end
|
13
17
|
|
14
18
|
@rspec1 = @version.to_s[0, 1] == '2'
|
15
19
|
@transactions = true
|
@@ -1,12 +1,66 @@
|
|
1
|
-
require 'active_record'
|
2
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
|
-
require File.dirname(__FILE__) + '/../support/active_record/initializer'
|
4
2
|
|
5
3
|
describe ActiveRecord::Base do
|
4
|
+
subject do
|
5
|
+
Tree.blueprint(:name => 'tree')
|
6
|
+
end
|
7
|
+
|
6
8
|
it "should allow calling blueprint on associations" do
|
7
|
-
|
8
|
-
fruit = tree.fruits.blueprint(:species => 'fruit')
|
9
|
+
fruit = subject.fruits.blueprint(:species => 'fruit')
|
9
10
|
fruit.should be_instance_of(Fruit)
|
10
11
|
fruit.species.should == 'fruit'
|
12
|
+
fruit.tree.should == subject
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should update object on belongs to association" do
|
16
|
+
subject.create_main_fruit(:species => 'fruit')
|
17
|
+
subject.main_fruit.blueprint(:species => 'apple')
|
18
|
+
subject.main_fruit.reload.species.should == 'apple'
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "association callbacks" do
|
22
|
+
after do
|
23
|
+
class Tree
|
24
|
+
def fruit_after_add(fruit)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should call associations specific callbacks when calling blueprint on association" do
|
30
|
+
class Tree
|
31
|
+
def fruit_after_add(fruit)
|
32
|
+
fruit.average_diameter = -1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
fruit = subject.fruits.blueprint(:species => 'fruit')
|
37
|
+
fruit.average_diameter.should == -1
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should have fields set in callback" do
|
41
|
+
Tree.class_eval do
|
42
|
+
define_method(:fruit_after_add) do |fruit|
|
43
|
+
fruit.species.should == 'fruit'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
subject.fruits.blueprint(:species => 'fruit')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "defining blueprints" do
|
52
|
+
describe "inferring name" do
|
53
|
+
it "should infer name from class name" do
|
54
|
+
blueprint = nil
|
55
|
+
Blueprints::Context.eval_within_context({}) { blueprint = Tree.blueprint :attr => 'val' }
|
56
|
+
blueprint.name.should == :tree
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should still infer name from name default attributes first" do
|
60
|
+
blueprint = nil
|
61
|
+
Blueprints::Context.eval_within_context({}) { blueprint = Tree.blueprint :name => 'my_tree' }
|
62
|
+
blueprint.name.should == :my_tree
|
63
|
+
end
|
64
|
+
end
|
11
65
|
end
|
12
66
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Blueprints::BlueprintNameProxy do
|
4
|
+
subject do
|
5
|
+
Blueprints::BlueprintNameProxy.new(:regexp_blueprint, namespace_regexp_blueprint)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should pass matched groups as options" do
|
9
|
+
subject.build(stage)
|
10
|
+
stage.instance_variable_get(:@namespace_regexp_blueprint).should == {:arg0 => 'blueprint'}
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should pass any other options" do
|
14
|
+
namespace_regexp_blueprint.expects(:build).with(stage, :options => {:passed => 1, :arg0 => 'blueprint'}, :rebuild => true, :name => :regexp_blueprint)
|
15
|
+
subject.build(stage, :options => {:passed => 1}, :rebuild => true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should allow capturing named groups" do
|
19
|
+
if RUBY_VERSION.start_with?('1.9')
|
20
|
+
regexp = 'regexp_(?<name>.*)'
|
21
|
+
namespace_regexp_blueprint(Regexp.new(regexp))
|
22
|
+
subject.build(stage)
|
23
|
+
stage.instance_variable_get(:@namespace_regexp_blueprint).should == {:name => 'blueprint'}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should allow demolishing buildable" do
|
28
|
+
subject.build(stage).expects(:destroy)
|
29
|
+
subject.demolish(stage)
|
30
|
+
end
|
31
|
+
end
|
data/spec/unit/blueprint_spec.rb
CHANGED
@@ -1,13 +1,37 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe Blueprints::Blueprint do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
describe "backtrace" do
|
5
|
+
before do
|
6
|
+
blueprint { raise 'error' }
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should rewrite trace" do
|
10
|
+
begin
|
11
|
+
blueprint.build(stage)
|
12
|
+
rescue RuntimeError => e
|
13
|
+
e.backtrace[0].should == "While building blueprint 'blueprint'"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should set correct order of trace" do
|
18
|
+
blueprint2.depends_on(:blueprint)
|
19
|
+
begin
|
20
|
+
blueprint2.build(stage)
|
21
|
+
rescue RuntimeError => e
|
22
|
+
e.backtrace[0].should == "While building blueprint 'blueprint'"
|
23
|
+
e.backtrace[1].should == "While building blueprint 'blueprint2'"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should set correct order of trace when building blueprint within blueprint" do
|
28
|
+
blueprint2 { build :blueprint }
|
29
|
+
begin
|
30
|
+
blueprint2.build(stage)
|
31
|
+
rescue RuntimeError => e
|
32
|
+
e.backtrace[0].should == "While building blueprint 'blueprint'"
|
33
|
+
e.backtrace[1].should == "While building blueprint 'blueprint2'"
|
34
|
+
end
|
11
35
|
end
|
12
36
|
end
|
13
37
|
|
@@ -22,7 +46,7 @@ describe Blueprints::Blueprint do
|
|
22
46
|
it "should not increase build count if blueprint was already built" do
|
23
47
|
blueprint.build(stage)
|
24
48
|
lambda {
|
25
|
-
blueprint.build(stage,
|
49
|
+
blueprint.build(stage, :rebuild => true)
|
26
50
|
}.should_not change(blueprint, :uses)
|
27
51
|
end
|
28
52
|
end
|
@@ -51,25 +75,129 @@ describe Blueprints::Blueprint do
|
|
51
75
|
it "should reset auto variable" do
|
52
76
|
blueprint.build(stage)
|
53
77
|
stage.instance_variable_set(:@blueprint, :false_result)
|
54
|
-
blueprint.build(stage,
|
78
|
+
blueprint.build(stage, :rebuild => true)
|
55
79
|
stage.instance_variable_get(:@blueprint).should == mock1
|
56
80
|
end
|
57
81
|
end
|
58
82
|
|
59
|
-
|
60
|
-
|
61
|
-
|
83
|
+
describe "dependencies" do
|
84
|
+
before do
|
85
|
+
blueprint
|
86
|
+
blueprint2
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should automatically build dependencies" do
|
90
|
+
blueprint3.depends_on(:blueprint).build(stage)
|
91
|
+
blueprint.should be_built
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should set return value to array of all dependent blueprints" do
|
95
|
+
blueprint3.depends_on(:blueprint, :blueprint2).build(stage)
|
96
|
+
stage.instance_variable_get(:@blueprint3).should == [mock1, mock1]
|
97
|
+
end
|
62
98
|
end
|
63
99
|
|
64
|
-
|
65
|
-
|
66
|
-
|
100
|
+
describe 'options and attributes' do
|
101
|
+
it "should allow passing options" do
|
102
|
+
(result = mock).expects(:options=).with(:option => 'value')
|
103
|
+
blueprint2 { result.options = options }.build(stage, :options => {:option => 'value'})
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should include attributes for blueprint" do
|
107
|
+
(result = mock).expects(:attributes=).with(:option => 'value')
|
108
|
+
blueprint2 { result.attributes = attributes }.attributes(:option => 'value').build(stage)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should not overwrite options and attributes methods" do
|
112
|
+
def stage.options
|
113
|
+
:options
|
114
|
+
end
|
115
|
+
|
116
|
+
def stage.attributes
|
117
|
+
:attributes
|
118
|
+
end
|
119
|
+
|
120
|
+
blueprint2.build(stage, :options => {:option => 'value'})
|
121
|
+
|
122
|
+
stage.options.should == :options
|
123
|
+
stage.attributes.should == :attributes
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should not try to remove options and attributes methods if they're not defined by singleton class" do
|
127
|
+
mod = Module.new do
|
128
|
+
def options
|
129
|
+
:options
|
130
|
+
end
|
131
|
+
def attributes
|
132
|
+
:attributes
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
stage_class = Class.new do
|
137
|
+
include mod
|
138
|
+
end
|
139
|
+
stage = stage_class.new
|
140
|
+
|
141
|
+
blueprint2.build(stage, :options => {:option => 'value'})
|
142
|
+
stage.options.should == :options
|
143
|
+
stage.attributes.should == :attributes
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should normalize options and attributes" do
|
147
|
+
blueprint
|
148
|
+
stage.instance_variable_set(:@value, 2)
|
149
|
+
blueprint2 { [options, attributes] }.attributes(:attr => Blueprints::Dependency.new(:blueprint))
|
150
|
+
options, attributes = blueprint2.build(stage, :options => {:attr2 => lambda { @value + 2 }, :attr3 => :value})
|
151
|
+
|
152
|
+
options.should == {:attr2 => 4, :attr3 => :value}
|
153
|
+
attributes.should == {:attr => mock1, :attr2 => 4, :attr3 => :value}
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should return normalized attributes" do
|
157
|
+
blueprint2
|
158
|
+
blueprint.attributes(:attr => Blueprints::Dependency.new(:blueprint2))
|
159
|
+
blueprint.normalized_attributes(stage, :attr2 => 1).should == {:attr => mock1, :attr2 => 1}
|
160
|
+
end
|
67
161
|
end
|
68
162
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
163
|
+
describe "strategies" do
|
164
|
+
it "should allow defining different strategies" do
|
165
|
+
new_result = mock('new_result')
|
166
|
+
blueprint.blueprint(:new) { new_result }
|
167
|
+
blueprint.build(stage, :strategy => 'new').should == new_result
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should return blueprint itself" do
|
171
|
+
blueprint.blueprint(:new) { 1 }.should == blueprint
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "on error" do
|
176
|
+
it "should allow rebuilding blueprint" do
|
177
|
+
blueprint do
|
178
|
+
unless @is_built
|
179
|
+
@is_built = true
|
180
|
+
raise 'Failure'
|
181
|
+
end
|
182
|
+
:success
|
183
|
+
end
|
184
|
+
|
185
|
+
expect {
|
186
|
+
blueprint.build(stage)
|
187
|
+
}.to raise_error
|
188
|
+
blueprint.build(stage).should == :success
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should restore overwritten methods" do
|
192
|
+
def stage.options
|
193
|
+
:options
|
194
|
+
end
|
195
|
+
|
196
|
+
expect {
|
197
|
+
blueprint { raise 'error' }.build(stage)
|
198
|
+
}.to raise_error
|
199
|
+
stage.options.should == :options
|
200
|
+
end
|
73
201
|
end
|
74
202
|
end
|
75
203
|
|
@@ -108,17 +236,27 @@ describe Blueprints::Blueprint do
|
|
108
236
|
|
109
237
|
it "should allow building blueprint with different parameters" do
|
110
238
|
@result.expects(:blueprint).with(:option => 'value')
|
111
|
-
blueprint.build stage,
|
239
|
+
blueprint.build stage, :options => {:option => 'value'}
|
112
240
|
end
|
113
241
|
|
114
242
|
it "should allow customizing update block" do
|
115
243
|
blueprint.update { @blueprint.update_attributes(options) }
|
116
244
|
@result.expects(:update_attributes).with(:option => 'value')
|
117
|
-
blueprint.build stage,
|
245
|
+
blueprint.build stage, :options => {:option => 'value'}
|
118
246
|
end
|
119
247
|
|
120
248
|
it "should not update if build_once is false" do
|
121
|
-
blueprint.build stage,
|
249
|
+
blueprint.build stage, :options => {:option => 'value'}, :rebuild => true
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "extending" do
|
254
|
+
before do
|
255
|
+
blueprint
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should allow extending with nothing" do
|
259
|
+
blueprint2.extends(:blueprint)
|
122
260
|
end
|
123
261
|
end
|
124
262
|
end
|
@@ -10,17 +10,17 @@ describe Blueprints do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should allow getting unused blueprints" do
|
13
|
-
Blueprints::Namespace.root.build [:blueprint, :"namespace.blueprint2"]
|
13
|
+
Blueprints::Namespace.root.build [:blueprint, :"namespace.blueprint2"], stage
|
14
14
|
Blueprints.unused.should =~ ['blueprint2', 'namespace.blueprint']
|
15
15
|
end
|
16
16
|
|
17
17
|
describe "most used" do
|
18
18
|
before do
|
19
|
-
Blueprints::Namespace.root.build [:blueprint, :blueprint2, :"namespace.blueprint"]
|
19
|
+
Blueprints::Namespace.root.build [:blueprint, :blueprint2, :"namespace.blueprint"], stage
|
20
20
|
Blueprints::Namespace.root.executed_blueprints.clear
|
21
|
-
Blueprints::Namespace.root.build [:blueprint2, :"namespace.blueprint"]
|
21
|
+
Blueprints::Namespace.root.build [:blueprint2, :"namespace.blueprint"], stage
|
22
22
|
Blueprints::Namespace.root.executed_blueprints.clear
|
23
|
-
Blueprints::Namespace.root.build [:"namespace.blueprint"]
|
23
|
+
Blueprints::Namespace.root.build [:"namespace.blueprint"], stage
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should return all blueprints with their usages" do
|