blueprints 0.8.2 → 0.9.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 (61) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +82 -18
  4. data/LICENSE +1 -1
  5. data/README.rdoc +38 -8
  6. data/Rakefile +11 -35
  7. data/blueprints.gemspec +29 -123
  8. data/features/support/env.rb +7 -10
  9. data/lib/blueprints.rb +68 -12
  10. data/lib/blueprints/blueprint.rb +39 -19
  11. data/lib/blueprints/buildable.rb +92 -74
  12. data/lib/blueprints/configuration.rb +18 -4
  13. data/lib/blueprints/context.rb +148 -5
  14. data/lib/blueprints/database_cleaner_fix.rb +9 -0
  15. data/lib/blueprints/dependency.rb +32 -21
  16. data/lib/blueprints/eval_context.rb +51 -0
  17. data/lib/blueprints/extensions.rb +115 -0
  18. data/lib/blueprints/extensions/rspec.rb +3 -1
  19. data/lib/blueprints/helper.rb +52 -20
  20. data/lib/blueprints/namespace.rb +31 -12
  21. data/lib/blueprints/root_namespace.rb +24 -25
  22. data/lib/blueprints/version.rb +3 -0
  23. data/spec/{active_record/blueprint.rb → blueprint.rb} +14 -17
  24. data/spec/{active_record/blueprints_spec.rb → blueprints_spec.rb} +40 -59
  25. data/spec/spec_helper.rb +34 -0
  26. data/spec/support/active_record/database.yml.example +7 -0
  27. data/spec/support/active_record/initializer.rb +15 -0
  28. data/spec/{active_record/fixtures → support/active_record}/schema.rb +0 -0
  29. data/spec/support/dm-core/initializer.rb +31 -0
  30. data/spec/support/mongo_mapper/database.yml.example +2 -0
  31. data/spec/support/mongo_mapper/initializer.rb +20 -0
  32. data/spec/support/mongoid/database.yml.example +2 -0
  33. data/spec/support/mongoid/initializer.rb +23 -0
  34. data/spec/support/none/initializer.rb +63 -0
  35. data/spec/unit/active_record_spec.rb +1 -6
  36. data/spec/unit/blueprint_spec.rb +91 -20
  37. data/spec/unit/blueprints_spec.rb +44 -0
  38. data/spec/unit/buildable_spec.rb +37 -6
  39. data/spec/unit/configuration_spec.rb +11 -0
  40. data/spec/unit/context_spec.rb +100 -0
  41. data/spec/unit/dependency_spec.rb +24 -19
  42. data/spec/unit/eval_context_spec.rb +56 -0
  43. data/spec/unit/fixtures.rb +61 -0
  44. data/spec/unit/namespace_spec.rb +59 -11
  45. data/spec/unit/spec_helper.rb +8 -16
  46. data/test/blueprints_test.rb +40 -59
  47. data/test/test_helper.rb +6 -16
  48. data/test_all.sh +45 -0
  49. metadata +178 -61
  50. data/VERSION +0 -1
  51. data/lib/blueprints/core_ext.rb +0 -69
  52. data/lib/blueprints/file_context.rb +0 -37
  53. data/spec/active_record/fixtures/database.yml.example +0 -8
  54. data/spec/active_record/fixtures/fruit.rb +0 -3
  55. data/spec/active_record/fixtures/tree.rb +0 -4
  56. data/spec/active_record/spec_helper.rb +0 -37
  57. data/spec/no_db/blueprint.rb +0 -9
  58. data/spec/no_db/blueprints_spec.rb +0 -45
  59. data/spec/no_db/fixtures/fruit.rb +0 -15
  60. data/spec/no_db/spec_helper.rb +0 -14
  61. data/spec/test_all.sh +0 -39
@@ -0,0 +1,34 @@
1
+ @orm, @version = (ENV['ORM'] || 'active_record').split('.', 2)
2
+ gem_mappings = {'active_record' => 'activerecord'}
3
+ gem gem_mappings[@orm], "~> #{@version}" if @version
4
+ require @orm unless @orm == 'none'
5
+
6
+ Root = Pathname.new(__FILE__).dirname.join('..')
7
+ $: << Root.to_s
8
+
9
+ require 'logger'
10
+ @logger_file = Root.join('debug.log')
11
+ @logger = Logger.new(@logger_file)
12
+
13
+ require 'lib/blueprints'
14
+ require "spec/support/#{@orm}/initializer"
15
+
16
+ config_class = if @rspec1
17
+ gem 'rspec', '~> 1.0'
18
+ require 'spec'
19
+ Spec::Runner
20
+ else
21
+ gem 'rspec', '>= 2.0.0.beta'
22
+ require 'rspec'
23
+ RSpec
24
+ end
25
+
26
+ config_class.configure do |config|
27
+ config.mock_with :mocha
28
+ end
29
+
30
+ Blueprints.enable do |config|
31
+ config.root = Root.join('spec')
32
+ config.prebuild = :big_cherry
33
+ config.transactions = @transactions
34
+ end
@@ -0,0 +1,7 @@
1
+ adapter: mysql2
2
+ socket: /var/run/mysqld/mysqld.sock
3
+ encoding: utf8
4
+ database: blueprints_test
5
+ username: root
6
+ password:
7
+ host: localhost
@@ -0,0 +1,15 @@
1
+ class Fruit < ActiveRecord::Base
2
+ belongs_to :tree
3
+ end
4
+
5
+ class Tree < ActiveRecord::Base
6
+ attr_protected :size
7
+ has_many :fruits
8
+ end
9
+
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
13
+
14
+ @rspec1 = @version.to_s[0, 1] == '2'
15
+ @transactions = true
@@ -0,0 +1,31 @@
1
+ class Fruit
2
+ include DataMapper::Resource
3
+
4
+ property :id, Serial
5
+ property :species, String
6
+ property :average_diameter, Integer
7
+
8
+ belongs_to :tree, :required => false, :default => nil
9
+ end
10
+
11
+ class Tree
12
+ include DataMapper::Resource
13
+
14
+ property :id, Serial
15
+ property :name, String
16
+ property :size, String, :writer => :private
17
+
18
+ has n, :fruits
19
+ end
20
+
21
+ DataMapper::Model.raise_on_save_failure = true
22
+
23
+ require 'dm-transactions'
24
+ require 'dm-migrations'
25
+
26
+ DataMapper::Logger.new(@logger_file, :debug)
27
+ DataMapper.setup(:default, 'mysql://localhost/blueprints_test')
28
+ DataMapper.finalize
29
+ DataMapper.auto_migrate!
30
+
31
+ @transactions = true
@@ -0,0 +1,2 @@
1
+ database: blueprints_test
2
+ host: localhost
@@ -0,0 +1,20 @@
1
+ class Fruit
2
+ include MongoMapper::Document
3
+
4
+ key :species, String
5
+ key :average_diameter, Integer
6
+
7
+ belongs_to :tree
8
+ end
9
+
10
+ class Tree
11
+ include MongoMapper::Document
12
+
13
+ key :size
14
+
15
+ attr_protected :size
16
+ many :fruits
17
+ end
18
+
19
+ MongoMapper.connection = Mongo::Connection.new('localhost')
20
+ MongoMapper.database = 'blueprints_test'
@@ -0,0 +1,2 @@
1
+ database: blueprints_test
2
+ host: localhost
@@ -0,0 +1,23 @@
1
+ class Fruit
2
+ include Mongoid::Document
3
+
4
+ field :species
5
+ field :average_diameter, :type => Integer
6
+
7
+ referenced_in :tree, :inverse_of => :fruits
8
+ end
9
+
10
+ class Tree
11
+ include Mongoid::Document
12
+
13
+ field :size
14
+
15
+ attr_protected :size
16
+ references_many :fruits
17
+ end
18
+
19
+ Mongoid.configure do |config|
20
+ config.logger = @logger
21
+ config.from_hash YAML.load_file(Root.join('spec/support/mongoid/database.yml'))
22
+ end
23
+
@@ -0,0 +1,63 @@
1
+ class NoneOrm
2
+ include Blueprints::Extensions::Blueprintable
3
+
4
+ class << self
5
+ def add (record)
6
+ @all ||= []
7
+ @all << record
8
+ end
9
+
10
+ def destroy(record)
11
+ @all.delete record
12
+ end
13
+
14
+ def count
15
+ all.size
16
+ end
17
+
18
+ def all(options = {})
19
+ Array(@all).clone.tap do |result|
20
+ result.reject! do |record|
21
+ options[:conditions].any? { |key, value| record.send(key) != value }
22
+ end if options[:conditions]
23
+ end
24
+ end
25
+
26
+ def first(options = {})
27
+ all(options).first
28
+ end
29
+
30
+ def last(options = {})
31
+ all(options).last
32
+ end
33
+ end
34
+
35
+ def initialize #(params)
36
+ # params.each { |param, value| instance_variable_set("@#{param}", value) }
37
+ self.class.add(self)
38
+ end
39
+
40
+ def reload
41
+ self
42
+ end
43
+
44
+ def destroy
45
+ self.class.destroy(self)
46
+ end
47
+ end
48
+
49
+ class Fruit < NoneOrm
50
+ attr_accessor :species, :average_diameter, :tree
51
+ end
52
+
53
+ class Tree < NoneOrm
54
+ attr_accessor :name, :size, :fruits
55
+ end
56
+
57
+ RSpec.configure do |config|
58
+ config.before do
59
+ [Fruit, Tree].each do |klass|
60
+ klass.instance_variables.each { |iv| klass.send(:remove_instance_variable, iv) }
61
+ end
62
+ end
63
+ end
@@ -1,11 +1,6 @@
1
1
  require 'active_record'
2
2
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
- require 'spec/active_record/fixtures/fruit'
4
- require 'spec/active_record/fixtures/tree'
5
-
6
- databases = YAML::load(IO.read("spec/active_record/fixtures/database.yml"))
7
- db_info = databases[ENV["DB"] || "test"]
8
- ActiveRecord::Base.establish_connection(db_info)
3
+ require File.dirname(__FILE__) + '/../support/active_record/initializer'
9
4
 
10
5
  describe ActiveRecord::Base do
11
6
  it "should allow calling blueprint on associations" do
@@ -1,53 +1,124 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe Blueprints::Blueprint do
4
- before do
5
- mock = @mock
6
- @blueprint = Blueprints::Blueprint.new(:blueprint, __FILE__) { mock }
7
- Blueprints::Namespace.root.build :blueprint
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'}
11
+ end
12
+ end
13
+
14
+ describe "building" do
15
+ describe "build count" do
16
+ it "should increase build count" do
17
+ lambda {
18
+ blueprint.build(stage)
19
+ }.should change(blueprint, :uses).by(1)
20
+ end
21
+
22
+ it "should not increase build count if blueprint was already built" do
23
+ blueprint.build(stage)
24
+ lambda {
25
+ blueprint.build(stage, false)
26
+ }.should_not change(blueprint, :uses)
27
+ end
28
+ end
29
+
30
+ it "should copy instance variables to container" do
31
+ result = mock
32
+ blueprint { @bl = result }.build(stage)
33
+ stage.instance_variable_get(:@bl).should == result
34
+ end
35
+
36
+ describe "auto set variable" do
37
+ it "should be set" do
38
+ blueprint.build(stage)
39
+ stage.instance_variable_get(:@blueprint).should == mock1
40
+ end
41
+
42
+ it "should not be set if blueprint defines same variable" do
43
+ result = mock
44
+ blueprint do
45
+ @blueprint = result
46
+ :false_result
47
+ end.build(stage)
48
+ stage.instance_variable_get(:@blueprint).should == result
49
+ end
50
+
51
+ it "should reset auto variable" do
52
+ blueprint.build(stage)
53
+ stage.instance_variable_set(:@blueprint, :false_result)
54
+ blueprint.build(stage, false)
55
+ stage.instance_variable_get(:@blueprint).should == mock1
56
+ end
57
+ end
58
+
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')
62
+ end
63
+
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)
67
+ end
68
+
69
+ it "should automatically build dependencies" do
70
+ blueprint
71
+ blueprint2.depends_on(:blueprint).build(stage)
72
+ blueprint.should be_built
73
+ end
8
74
  end
9
75
 
10
76
  describe "demolish" do
11
77
  before do
12
- @mock.stubs(:destroy)
78
+ @result = blueprint.build(stage)
79
+ @result.stubs(:destroy)
13
80
  end
14
81
 
15
82
  it "should allow to demolish blueprint" do
16
- @mock.expects(:destroy)
17
- @blueprint.demolish
83
+ @result.expects(:destroy)
84
+ blueprint.demolish(stage)
18
85
  end
19
86
 
20
87
  it "should raise error if blueprint is not built" do
21
- @blueprint.demolish
22
- lambda { @blueprint.demolish }.should raise_error(Blueprints::DemolishError)
88
+ blueprint.demolish(stage)
89
+ lambda { blueprint.demolish(stage) }.should raise_error(Blueprints::DemolishError)
23
90
  end
24
91
 
25
92
  it "should set blueprint as not built" do
26
- @blueprint.demolish
27
- Blueprints::Namespace.root.executed_blueprints.should_not include(@blueprint)
93
+ blueprint.demolish(stage)
94
+ Blueprints::Namespace.root.executed_blueprints.should_not include(blueprint)
28
95
  end
29
96
 
30
97
  it "should allow to customize demolishing" do
31
- @mock.expects(:demolish)
32
- @blueprint.demolish { @blueprint.demolish }
33
- @blueprint.demolish
98
+ @result.expects(:demolish)
99
+ blueprint.demolish { @blueprint.demolish }
100
+ blueprint.demolish(stage)
34
101
  end
35
102
  end
36
103
 
37
104
  describe "updating" do
105
+ before do
106
+ @result = blueprint.build(stage)
107
+ end
108
+
38
109
  it "should allow building blueprint with different parameters" do
39
- @mock.expects(:blueprint).with(:option => 'value')
40
- Blueprints::RootNamespace.root.build(:blueprint => {:option => 'value'})
110
+ @result.expects(:blueprint).with(:option => 'value')
111
+ blueprint.build stage, true, :option => 'value'
41
112
  end
42
113
 
43
114
  it "should allow customizing update block" do
44
- @blueprint.update { @blueprint.update_attributes(options) }
45
- @mock.expects(:update_attributes).with(:option => 'value')
46
- Blueprints::RootNamespace.root.build(:blueprint => {:option => 'value'})
115
+ blueprint.update { @blueprint.update_attributes(options) }
116
+ @result.expects(:update_attributes).with(:option => 'value')
117
+ blueprint.build stage, true, :option => 'value'
47
118
  end
48
119
 
49
120
  it "should not update if build_once is false" do
50
- Blueprints::RootNamespace.root.build({:blueprint => {:option => 'value'}}, nil, false)
121
+ blueprint.build stage, false, :option => 'value'
51
122
  end
52
123
  end
53
124
  end
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Blueprints do
4
+ describe "usage" do
5
+ before do
6
+ blueprint
7
+ blueprint2
8
+ namespace_blueprint
9
+ namespace_blueprint2
10
+ end
11
+
12
+ it "should allow getting unused blueprints" do
13
+ Blueprints::Namespace.root.build [:blueprint, :"namespace.blueprint2"]
14
+ Blueprints.unused.should =~ ['blueprint2', 'namespace.blueprint']
15
+ end
16
+
17
+ describe "most used" do
18
+ before do
19
+ Blueprints::Namespace.root.build [:blueprint, :blueprint2, :"namespace.blueprint"]
20
+ Blueprints::Namespace.root.executed_blueprints.clear
21
+ Blueprints::Namespace.root.build [:blueprint2, :"namespace.blueprint"]
22
+ Blueprints::Namespace.root.executed_blueprints.clear
23
+ Blueprints::Namespace.root.build [:"namespace.blueprint"]
24
+ end
25
+
26
+ it "should return all blueprints with their usages" do
27
+ Blueprints.most_used.should == [["namespace.blueprint", 3], ["blueprint2", 2], ["blueprint", 1], ["namespace.blueprint2", 0]]
28
+ end
29
+
30
+ it "should allow getting most used blueprints" do
31
+ Blueprints.most_used(:count => 2).should == [['namespace.blueprint', 3], ['blueprint2', 2]]
32
+ end
33
+
34
+ it "should allow getting list of blueprints used at least n times" do
35
+ Blueprints.most_used(:at_least => 1).should == [['namespace.blueprint', 3], ['blueprint2', 2], ["blueprint", 1]]
36
+ end
37
+
38
+ it "should allow mixing at least with count" do
39
+ Blueprints.most_used(:at_least => 2, :count => 3).should == [['namespace.blueprint', 3], ['blueprint2', 2]]
40
+ Blueprints.most_used(:at_least => 2, :count => 1).should == [['namespace.blueprint', 3]]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,14 +1,45 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe Blueprints::Buildable do
4
- before do
5
- mock = @mock
6
- @blueprint = Blueprints::Blueprint.new(:blueprint, __FILE__) { mock }
4
+ it "should inspect buildable nicely" do
5
+ namespace_blueprint.attributes(:attr => 'value').depends_on(:dependency)
6
+ namespace_blueprint.inspect.should == %q(<#Blueprints::Blueprint name: "namespace.blueprint", attributes: {:attr=>"value"}, dependencies: [:dependency]>)
7
7
  end
8
8
 
9
- describe "normalize attributes" do
10
- it "should allow passing dependent normalize Blueprints::Dependency object" do
11
- Blueprints::Buildable.normalize_attributes(:name => Blueprints::Dependency.new(:blueprint)).should == {:name => @mock}
9
+ describe "name" do
10
+ after do
11
+ Blueprints.config.default_attributes = :name
12
+ end
13
+
14
+ it 'should raise TypeError when scenario name is not symbol or string' do
15
+ lambda {
16
+ Blueprints::Blueprint.new([], context)
17
+ }.should raise_error(TypeError, "Pass blueprint names as strings or symbols only, cannot define blueprint []")
18
+ end
19
+
20
+ it "should return full name" do
21
+ namespace_blueprint.full_name.should == 'namespace.blueprint'
22
+ end
23
+
24
+ describe "inferring" do
25
+ it "should infer name using default attributes" do
26
+ Blueprints::Buildable.new(nil, context.attributes(:name => 'inferred_name')).name.should == :inferred_name
27
+ end
28
+
29
+ it "should infer name from third default attribute" do
30
+ Blueprints.config.default_attributes = :name, :to_s, :id
31
+ Blueprints::Buildable.new(nil, context.attributes(:id => 'third')).name.should == :third
32
+ end
33
+
34
+ it "should parameterize infered name" do
35
+ Blueprints::Buildable.new(nil, context.attributes(:name => 'my blueprint')).name.should == :my_blueprint
36
+ end
37
+
38
+ it "should raise error if name can't be inferred" do
39
+ lambda {
40
+ Blueprints::Buildable.new(nil, context.attributes(:id => 'third')).name.should == :third
41
+ }.should raise_error(TypeError, "Pass blueprint names as strings or symbols only, cannot define blueprint nil")
42
+ end
12
43
  end
13
44
  end
14
45
  end