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.
Files changed (39) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +14 -1
  3. data/README.rdoc +3 -173
  4. data/Rakefile +32 -15
  5. data/blueprints.gemspec +2 -16
  6. data/lib/blueprints.rb +17 -10
  7. data/lib/blueprints/blueprint.rb +85 -41
  8. data/lib/blueprints/blueprint_name_proxy.rb +34 -0
  9. data/lib/blueprints/buildable.rb +53 -26
  10. data/lib/blueprints/context.rb +8 -0
  11. data/lib/blueprints/extensions.rb +22 -4
  12. data/lib/blueprints/extensions/rspec.rb +28 -14
  13. data/lib/blueprints/helper.rb +17 -9
  14. data/lib/blueprints/namespace.rb +20 -14
  15. data/lib/blueprints/railtie.rb +3 -0
  16. data/lib/blueprints/root_namespace.rb +16 -23
  17. data/lib/blueprints/version.rb +1 -1
  18. data/lib/generators/blueprints/model/model_generator.rb +29 -0
  19. data/spec/blueprints_spec.rb +18 -1
  20. data/spec/support/active_record/initializer.rb +9 -5
  21. data/spec/support/none/initializer.rb +4 -0
  22. data/spec/unit/active_record_spec.rb +58 -4
  23. data/spec/unit/blueprint_name_proxy_spec.rb +31 -0
  24. data/spec/unit/blueprint_spec.rb +160 -22
  25. data/spec/unit/blueprints_spec.rb +4 -4
  26. data/spec/unit/context_spec.rb +8 -1
  27. data/spec/unit/dependency_spec.rb +1 -5
  28. data/spec/unit/fixtures.rb +69 -47
  29. data/spec/unit/namespace_spec.rb +23 -5
  30. data/spec/unit/root_namespace_spec.rb +9 -0
  31. data/spec/unit/spec_helper.rb +3 -4
  32. data/test/blueprints_test.rb +18 -1
  33. data/test/test_helper.rb +1 -0
  34. data/test_all.sh +6 -33
  35. metadata +43 -276
  36. data/Gemfile.lock +0 -108
  37. data/lib/blueprints/database_cleaner_fix.rb +0 -9
  38. data/lib/blueprints/eval_context.rb +0 -51
  39. data/spec/unit/eval_context_spec.rb +0 -56
@@ -1,3 +1,3 @@
1
1
  module Blueprints
2
- VERSION = '0.9.0'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -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)
@@ -1,4 +1,8 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
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
- end
11
+ has_many :fruits, :after_add => :fruit_after_add
12
+ belongs_to :main_fruit, :class_name => 'Fruit'
9
13
 
10
- db_config = YAML::load(Root.join("spec/support/active_record/database.yml").read)
11
- ActiveRecord::Base.establish_connection(db_config)
12
- ActiveRecord::Base.logger = @logger
14
+ def fruit_after_add(_)
15
+ end
16
+ end
13
17
 
14
18
  @rspec1 = @version.to_s[0, 1] == '2'
15
19
  @transactions = true
@@ -44,6 +44,10 @@ class NoneOrm
44
44
  def destroy
45
45
  self.class.destroy(self)
46
46
  end
47
+
48
+ def new_record?
49
+ true
50
+ end
47
51
  end
48
52
 
49
53
  class Fruit < NoneOrm
@@ -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
- tree = Tree.blueprint(:name => 'tree')
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
@@ -1,13 +1,37 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe Blueprints::Blueprint do
4
- it "should rewrite trace" do
5
- context = Blueprints::Context.new(:file => __FILE__)
6
- error_blueprint = Blueprints::Blueprint.new(:error, context) { raise 'error' }
7
- begin
8
- error_blueprint.build(stage)
9
- rescue RuntimeError => e
10
- e.backtrace[0].should =~ %r{spec/unit/blueprint_spec.rb:#{__LINE__ - 4}:in blueprint 'error'}
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, false)
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, false)
78
+ blueprint.build(stage, :rebuild => true)
55
79
  stage.instance_variable_get(:@blueprint).should == mock1
56
80
  end
57
81
  end
58
82
 
59
- it "should allow passing options" do
60
- (result = mock).expects(:options=).with(:option => 'value')
61
- blueprint2 { result.options = options }.build(stage, true, :option => 'value')
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
- it "should include attributes for blueprint" do
65
- (result = mock).expects(:attributes=).with(:option => 'value')
66
- blueprint2 { result.attributes = attributes }.attributes(:option => 'value').build(stage)
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
- it "should automatically build dependencies" do
70
- blueprint
71
- blueprint2.depends_on(:blueprint).build(stage)
72
- blueprint.should be_built
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, true, :option => 'value'
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, true, :option => 'value'
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, false, :option => 'value'
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