blueprints 0.2.4 → 0.3.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.
@@ -80,16 +80,10 @@ And if you also need it to depend on other blueprints:
80
80
  === Blueprints file ===
81
81
 
82
82
  Blueprints searches for blueprints files in this particular order in Rails (Merb) root:
83
- * blueprints.rb
84
- * blueprints/*.rb
85
83
  * blueprint.rb
86
84
  * blueprint/*.rb
87
- * spec/blueprints.rb
88
- * spec/blueprints/*.rb
89
85
  * spec/blueprint.rb
90
86
  * spec/blueprint/*.rb
91
- * test/blueprints.rb
92
- * test/blueprints/*.rb
93
87
  * test/blueprint.rb
94
88
  * test/blueprint/*.rb
95
89
  You can pass :root option to override framework root and :filename option to pass custom filename pattern
@@ -1,4 +1,7 @@
1
1
  require 'activesupport'
2
+ require File.join(File.dirname(__FILE__), 'blueprints/buildable')
3
+ require File.join(File.dirname(__FILE__), 'blueprints/namespace')
4
+ require File.join(File.dirname(__FILE__), 'blueprints/root_namespace')
2
5
  require File.join(File.dirname(__FILE__), 'blueprints/plan')
3
6
  require File.join(File.dirname(__FILE__), 'blueprints/file_context')
4
7
  require File.join(File.dirname(__FILE__), 'blueprints/helper')
@@ -11,7 +14,7 @@ end
11
14
 
12
15
  module Blueprints
13
16
  PLAN_FILES = [nil, "spec", "test"].map do |dir|
14
- ["blueprint", "blueprints"].map do |file|
17
+ ["blueprint"].map do |file|
15
18
  path = File.join([dir, file].compact)
16
19
  ["#{path}.rb", File.join(path, "*.rb")]
17
20
  end
@@ -25,8 +28,8 @@ module Blueprints
25
28
  end
26
29
 
27
30
  def self.setup(current_context)
28
- Plan.setup
29
- Plan.copy_ivars(current_context)
31
+ Namespace.root.setup
32
+ Namespace.root.copy_ivars(current_context)
30
33
  unless @@orm == :none
31
34
  ActiveRecord::Base.connection.increment_open_transactions
32
35
  ActiveRecord::Base.connection.transaction_joinable = false
@@ -44,7 +47,7 @@ module Blueprints
44
47
  def self.load(options = {})
45
48
  options.assert_valid_keys(:delete_policy, :filename, :prebuild, :root, :orm)
46
49
  options.symbolize_keys!
47
- return unless Plan.plans.empty?
50
+ return unless Namespace.root.empty?
48
51
 
49
52
  @@orm = (options.delete(:orm) || :active_record).to_sym
50
53
  raise ArgumentError, "Unsupported ORM #{@@orm}. Blueprints supports only #{SUPPORTED_ORMS.join(', ')}" unless SUPPORTED_ORMS.include?(@@orm)
@@ -56,7 +59,7 @@ module Blueprints
56
59
  @@framework_root = options[:root] if options[:root]
57
60
  load_scenarios_files(options[:filename] || PLAN_FILES)
58
61
 
59
- Plan.prebuild(options[:prebuild])
62
+ Namespace.root.prebuild(options[:prebuild])
60
63
  end
61
64
 
62
65
  def self.load_scenarios_files(*patterns)
@@ -1,4 +1,4 @@
1
- require 'active_record'
1
+ require 'activerecord'
2
2
 
3
3
  module ActiveRecord
4
4
  class Base
@@ -12,7 +12,7 @@ module ActiveRecord
12
12
  else
13
13
  returning(self.new) do |object|
14
14
  options.each do |attr, value|
15
- value = Blueprints::Plan.context.instance_variable_get(value) if value.is_a? Symbol and value.to_s =~ /^@.+$/
15
+ value = Blueprints::Namespace.root.context.instance_variable_get(value) if value.is_a? Symbol and value.to_s =~ /^@.+$/
16
16
  object.send("#{attr}=", value)
17
17
  end
18
18
  object.save!
@@ -0,0 +1,50 @@
1
+ module Blueprints
2
+ class Buildable
3
+ attr_reader :name
4
+ attr_accessor :namespace
5
+
6
+ def initialize(name)
7
+ @name, parents = parse_name(name)
8
+ depends_on(*parents)
9
+
10
+ Namespace.root.add_child(self) if Namespace.root
11
+ end
12
+
13
+ def depends_on(*scenarios)
14
+ @parents = (@parents || []) + scenarios.map{|s| s.to_sym}
15
+ end
16
+
17
+ def build
18
+ namespace.build_parent_plans
19
+ build_parent_plans
20
+ build_plan
21
+ end
22
+
23
+ protected
24
+
25
+ def build_parent_plans
26
+ @parents.each do |p|
27
+ parent = begin
28
+ namespace[p]
29
+ rescue PlanNotFoundError
30
+ Namespace.root[p]
31
+ end
32
+
33
+ parent.build_parent_plans
34
+ parent.build_plan
35
+ end
36
+ end
37
+
38
+ def parse_name(name)
39
+ case name
40
+ when Hash
41
+ return name.keys.first.to_sym, [name.values.first].flatten.map{|sc| parse_name(sc).first}
42
+ when Symbol, String
43
+ name = name.to_sym unless name == ''
44
+ return name, []
45
+ else
46
+ raise TypeError, "Pass plan names as strings or symbols only, cannot build plan #{name.inspect}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -5,7 +5,7 @@ module Blueprints
5
5
  end
6
6
 
7
7
  def to_s
8
- "Plan(s) not found '#{@plans.join(',')}'"
8
+ "Plan/namespace not found '#{@plans.join(',')}'"
9
9
  end
10
10
  end
11
11
  end
@@ -3,5 +3,14 @@ module Blueprints
3
3
  def self.blueprint(plan, &block)
4
4
  Plan.new(plan, &block)
5
5
  end
6
+
7
+ def self.namespace(name)
8
+ old_namespace = Namespace.root
9
+ namespace = Namespace.new(name)
10
+ Namespace.root = namespace
11
+ yield
12
+ old_namespace.add_child(namespace)
13
+ Namespace.root = old_namespace
14
+ end
6
15
  end
7
16
  end
@@ -1,8 +1,8 @@
1
1
  module Blueprints
2
2
  module Helper
3
3
  def build_plan(*names)
4
- Plan.build(*names)
5
- Plan.copy_ivars(self)
4
+ Namespace.root.build(*names)
5
+ Namespace.root.copy_ivars(self)
6
6
  end
7
7
 
8
8
  alias :build :build_plan
@@ -12,13 +12,13 @@ module Blueprints
12
12
  Blueprints.delete_tables(*args)
13
13
 
14
14
  if options[:undo] == :all
15
- Plan.executed_plans.clear
15
+ Namespace.root.executed_plans.clear
16
16
  else
17
17
  undo = [options[:undo]].flatten.compact
18
- unless (not_found = undo - Plan.executed_plans.to_a).blank?
18
+ unless (not_found = undo - Namespace.root.executed_plans.to_a).blank?
19
19
  raise(ArgumentError, "Scenario(s) #{not_found} not found")
20
20
  end
21
- Plan.executed_plans -= undo
21
+ Namespace.root.executed_plans -= undo
22
22
  end
23
23
  end
24
24
  end
@@ -0,0 +1,31 @@
1
+ module Blueprints
2
+ class Namespace < Buildable
3
+ cattr_accessor :root
4
+ delegate :empty?, :size, :to => :@children
5
+
6
+ def initialize(name)
7
+ super(name)
8
+ @children = {}
9
+ end
10
+
11
+ def add_child(child)
12
+ #TODO: Raise error for duplicate children!
13
+ @children[child.name] = child
14
+ child.namespace = self
15
+ end
16
+
17
+ def [](path)
18
+ child_name, path = path.to_s.split('.', 2)
19
+ child = @children[child_name.to_sym] or raise PlanNotFoundError, child_name
20
+ if path
21
+ child[path]
22
+ else
23
+ child
24
+ end
25
+ end
26
+
27
+ def build_plan
28
+ Namespace.root.add_variable(name, @children.collect {|p| p.last.build }.uniq)
29
+ end
30
+ end
31
+ end
@@ -1,102 +1,27 @@
1
1
  module Blueprints
2
- class Plan
3
- cattr_reader :plans, :context
4
- cattr_accessor :executed_plans
5
- @@plans = {}
6
- @@executed_plans = Set.new
7
- @@global_executed_plans = Set.new
8
-
9
- @@global_context = Module.new
10
- @@context = nil
11
-
12
- def self.setup
13
- @@context = YAML.load(@@global_context)
14
- @@executed_plans = @@global_executed_plans.clone
15
- end
16
-
17
- def self.copy_ivars(to)
18
- @@context.instance_variables.each do |iv|
19
- to.instance_variable_set(iv, @@context.instance_variable_get(iv))
20
- end
21
- end
22
-
23
- def self.prebuild(plans)
24
- @@context = @@global_context
25
- @@global_scenarios = Plan.build(plans) if plans
26
- @@global_executed_plans = @@executed_plans
27
- @@global_context = YAML.dump(@@global_context)
28
- end
29
-
30
- def self.build(*names)
31
- names.map {|name| @@plans[name.to_sym] or raise PlanNotFoundError, name}.each {|p| p.build}
32
- end
33
-
34
- # Instance
35
-
36
- attr_reader :name
37
-
38
- def initialize(*scenario, &block)
39
- @name, parents = parse_name(*scenario)
40
- depends_on(*parents)
2
+ class Plan < Buildable
3
+ def initialize(name, &block)
4
+ super(name)
41
5
  @block = block
42
-
43
- @@plans[@name] = self
44
- end
45
-
46
- def build
47
- build_parent_plans(@@context)
48
- build_plan(@@context)
49
- end
50
-
51
- def depends_on(*scenarios)
52
- @parents = (@parents || []) + scenarios.map{|s| s.to_sym}
53
- end
54
-
55
- protected
56
-
57
- def parse_name(name)
58
- case name
59
- when Hash
60
- return name.keys.first.to_sym, [name.values.first].flatten.map{|sc| parse_name(sc).first}
61
- when Symbol, String
62
- return name.to_sym, []
63
- else
64
- raise TypeError, "Pass plan names as strings or symbols only, cannot build plan #{name.inspect}"
65
- end
66
- end
67
-
68
- def say(*messages)
69
- puts messages.map { |message| "=> #{message}" }
70
6
  end
71
7
 
72
- def build_plan(context)
8
+ def build_plan
73
9
  surface_errors do
74
10
  if @block
75
- result = context.module_eval(&@block)
76
- iv_name = :"@#{@name}"
77
- context.instance_variable_set(iv_name, result) unless context.instance_variable_get(iv_name)
11
+ @result = Namespace.root.context.module_eval(&@block)
12
+ Namespace.root.add_variable(@name, @result)
78
13
  end
79
- end unless @@executed_plans.include?(@name)
80
- @@executed_plans << @name
14
+ end unless Namespace.root.executed_plans.include?(@name)
15
+ Namespace.root.executed_plans << @name
16
+ @result
81
17
  end
82
18
 
83
- def build_parent_plans(context)
84
- @parents.each do |p|
85
- parent = @@plans[p] or raise PlanNotFoundError, p
86
-
87
- parent.build_parent_plans(context)
88
- parent.build_plan(context)
89
- end
90
- end
19
+ private
91
20
 
92
21
  def surface_errors
93
22
  yield
94
23
  rescue StandardError => error
95
- puts
96
- say "There was an error building scenario '#{@name}'", error.inspect
97
- puts
98
- puts error.backtrace
99
- puts
24
+ puts "\n=> There was an error building scenario '#{@name}'", error.inspect, '', error.backtrace
100
25
  raise error
101
26
  end
102
27
  end
@@ -0,0 +1,43 @@
1
+ module Blueprints
2
+ class RootNamespace < Namespace
3
+ attr_reader :context, :plans
4
+ attr_accessor :executed_plans
5
+
6
+ def initialize
7
+ @executed_plans = Set.new
8
+ @global_executed_plans = Set.new
9
+ @global_context = Module.new
10
+
11
+ super ''
12
+ end
13
+
14
+ def setup
15
+ @context = YAML.load(@global_context)
16
+ @executed_plans = @global_executed_plans.clone
17
+ end
18
+
19
+ def copy_ivars(to)
20
+ @context.instance_variables.each do |iv|
21
+ to.instance_variable_set(iv, @context.instance_variable_get(iv))
22
+ end
23
+ end
24
+
25
+ def prebuild(plans)
26
+ @context = @global_context
27
+ @global_scenarios = build(plans) if plans
28
+ @global_executed_plans = @executed_plans
29
+ @global_context = YAML.dump(@global_context)
30
+ end
31
+
32
+ def build(*names)
33
+ names.map {|name| self[name].build}
34
+ end
35
+
36
+ def add_variable(name, value)
37
+ name = "@#{name}" unless name.to_s[0, 1] == "@"
38
+ @context.instance_variable_set(name, value) unless @context.instance_variable_get(name)
39
+ end
40
+
41
+ @@root = RootNamespace.new
42
+ end
43
+ end
@@ -40,4 +40,14 @@ blueprint :pine do
40
40
  @the_pine = Tree.blueprint :name => 'Pine', :size => 'medium'
41
41
  end
42
42
 
43
- Fruit.blueprint(:acorn, :species => 'Acorn', :tree => :@oak).depends_on(:oak)
43
+ Fruit.blueprint(:acorn, :species => 'Acorn', :tree => :@oak).depends_on(:oak)
44
+
45
+ namespace :pitted => :pine do
46
+ Tree.blueprint :peach_tree, :name => 'pitted peach tree'
47
+ Fruit.blueprint(:peach, :species => 'pitted peach', :tree => :@peach_tree).depends_on(:peach_tree)
48
+ Fruit.blueprint(:acorn, :species => 'pitted acorn', :tree => :@oak).depends_on(:oak)
49
+
50
+ namespace :red do
51
+ Fruit.blueprint(:apple, :species => 'pitted red apple')
52
+ end
53
+ end
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/spec_helper'
3
3
  describe Blueprints do
4
4
  describe "constants" do
5
5
  it "should be loaded from specified dirs" do
6
- Blueprints::PLAN_FILES.should == ["blueprint.rb", "blueprint/*.rb", "blueprints.rb", "blueprints/*.rb", "spec/blueprint.rb", "spec/blueprint/*.rb", "spec/blueprints.rb", "spec/blueprints/*.rb", "test/blueprint.rb", "test/blueprint/*.rb", "test/blueprints.rb", "test/blueprints/*.rb"]
6
+ Blueprints::PLAN_FILES.should == ["blueprint.rb", "blueprint/*.rb", "spec/blueprint.rb", "spec/blueprint/*.rb", "test/blueprint.rb", "test/blueprint/*.rb"]
7
7
  end
8
8
 
9
9
  it "should support required ORMS" do
@@ -129,9 +129,9 @@ describe Blueprints do
129
129
 
130
130
  describe 'delete policies' do
131
131
  before do
132
- Blueprints::Plan.plans.expects(:empty?).returns(true)
132
+ Blueprints::Namespace.root.expects(:empty?).returns(true)
133
133
  Blueprints.expects(:load_scenarios_files).with(Blueprints::PLAN_FILES)
134
- Blueprints::Plan.expects(:prebuild).with(nil)
134
+ Blueprints::Namespace.root.expects(:prebuild).with(nil)
135
135
  end
136
136
 
137
137
  it "should allow using custom delete policy" do
@@ -191,13 +191,13 @@ describe Blueprints do
191
191
  it 'should raise ScenarioNotFoundError when scenario could not be found' do
192
192
  lambda {
193
193
  build :not_existing
194
- }.should raise_error(Blueprints::PlanNotFoundError, "Plan(s) not found 'not_existing'")
194
+ }.should raise_error(Blueprints::PlanNotFoundError, "Plan/namespace not found 'not_existing'")
195
195
  end
196
196
 
197
197
  it 'should raise ScenarioNotFoundError when scenario parent could not be found' do
198
198
  lambda {
199
199
  build :parent_not_existing
200
- }.should raise_error(Blueprints::PlanNotFoundError, "Plan(s) not found 'not_existing'")
200
+ }.should raise_error(Blueprints::PlanNotFoundError, "Plan/namespace not found 'not_existing'")
201
201
  end
202
202
 
203
203
  it 'should raise TypeError when scenario name is not symbol or string' do
@@ -207,7 +207,7 @@ describe Blueprints do
207
207
  end
208
208
 
209
209
  it "should raise ArgumentError when unknown ORM specified" do
210
- Blueprints::Plan.plans.expects(:empty?).returns(true)
210
+ Blueprints::Namespace.root.expects(:empty?).returns(true)
211
211
  lambda {
212
212
  Blueprints.load(:orm => :unknown)
213
213
  }.should raise_error(ArgumentError, "Unsupported ORM unknown. Blueprints supports only none, active_record")
@@ -237,14 +237,44 @@ describe Blueprints do
237
237
  end
238
238
  end
239
239
 
240
- #describe "with pitted namespace" do
241
- # before do
242
- # Hornsby.build('pitted:peach').copy_ivars(self)
243
- # end
240
+ describe "with pitted namespace" do
241
+ it "should allow building namespaced scenarios" do
242
+ build 'pitted.peach_tree'
243
+ @peach_tree.name.should == 'pitted peach tree'
244
+ end
245
+
246
+ it "should allow adding dependencies from same namespace" do
247
+ build 'pitted.peach'
248
+ @peach.species.should == 'pitted peach'
249
+ @peach_tree.should_not be_nil
250
+ end
251
+
252
+ it "should allow adding dependencies from root namespace" do
253
+ build 'pitted.acorn'
254
+ @acorn.species.should == 'pitted acorn'
255
+ @oak.should_not be_nil
256
+ end
244
257
 
245
- # it "should have @peach" do
246
- # @peach.species.should == 'peach'
247
- # end
248
- #end
258
+ it "should allow building whole namespace" do
259
+ build :pitted
260
+ @peach_tree.should_not be_nil
261
+ @peach.should_not be_nil
262
+ @acorn.should_not be_nil
263
+ @apple.should_not be_nil
264
+ @pitted.should =~ [@peach_tree, @peach, @acorn, [@apple]]
265
+ end
266
+
267
+ it "should load dependencies when building namespaced blueprint if namespace has any" do
268
+ build 'pitted.peach'
269
+ @the_pine.should_not be_nil
270
+ end
271
+
272
+ describe "with red namespace" do
273
+ it "should allow building nested namespaces scenarios" do
274
+ build 'pitted.red.apple'
275
+ @apple.species.should == 'pitted red apple'
276
+ end
277
+ end
278
+ end
249
279
  end
250
280
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blueprints
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrius Chamentauskas
@@ -9,11 +9,11 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-11 00:00:00 +03:00
12
+ date: 2009-10-17 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: activerecord
16
+ name: activesupport
17
17
  type: :runtime
18
18
  version_requirement:
19
19
  version_requirements: !ruby/object:Gem::Requirement
@@ -22,7 +22,7 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 2.0.0
24
24
  version:
25
- description:
25
+ description: Another replacement for factories and fixtures. The library that lazy typists will love
26
26
  email: sinsiliux@gmail.com
27
27
  executables: []
28
28
 
@@ -31,14 +31,17 @@ extensions: []
31
31
  extra_rdoc_files: []
32
32
 
33
33
  files:
34
- - lib/blueprints.rb
35
- - lib/blueprints/errors.rb
36
34
  - lib/blueprints/file_context.rb
37
35
  - lib/blueprints/helper.rb
36
+ - lib/blueprints/buildable.rb
37
+ - lib/blueprints/root_namespace.rb
38
38
  - lib/blueprints/plan.rb
39
- - lib/blueprints/ar_extensions.rb
40
39
  - lib/blueprints/rspec_extensions.rb
40
+ - lib/blueprints/ar_extensions.rb
41
41
  - lib/blueprints/test_unit_extensions.rb
42
+ - lib/blueprints/namespace.rb
43
+ - lib/blueprints/errors.rb
44
+ - lib/blueprints.rb
42
45
  - README.rdoc
43
46
  - LICENSE
44
47
  has_rdoc: true
@@ -72,11 +75,11 @@ summary: Another replacement for factories and fixtures
72
75
  test_files:
73
76
  - spec/no_db/spec_helper.rb
74
77
  - spec/no_db/blueprints_spec.rb
75
- - spec/no_db/blueprints.rb
78
+ - spec/no_db/blueprint.rb
76
79
  - spec/no_db/fixtures/fruit.rb
77
80
  - spec/active_record/spec_helper.rb
78
81
  - spec/active_record/blueprints_spec.rb
79
- - spec/active_record/blueprints.rb
82
+ - spec/active_record/blueprint.rb
80
83
  - spec/active_record/fixtures/fruit.rb
81
84
  - spec/active_record/fixtures/tree.rb
82
85
  - spec/active_record/fixtures/database.yml.example