bocuse 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/bocuse ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+
4
+ require 'thor'
5
+
6
+ $:.unshift File.expand_path '../../lib', __FILE__
7
+ require 'bocuse'
8
+
9
+ class BocuseCLI < Thor
10
+ desc "compile NODE_FQDN", "Compiles the node given and outputs its JSON configuration."
11
+ def compile(node_fqdn)
12
+ project = Bocuse::Project.new(Dir.pwd)
13
+ node_config = project.nodes[node_fqdn]
14
+
15
+ puts MultiJson.encode node_config.to_h
16
+ end
17
+ end
18
+
19
+ BocuseCLI.start
@@ -0,0 +1,103 @@
1
+ module Bocuse
2
+
3
+ # This is the core class of bocuse.
4
+ #
5
+ # Its functions are:
6
+ # * to be able to spit out a configuration hash when prompted.
7
+ # * to be configurable
8
+ #
9
+ # It will mainly return proxies that will lodge themselves in its internal
10
+ # hash. The proxies usually represent an internal hash value.
11
+ #
12
+ class Configuration
13
+
14
+ attr_reader :store,
15
+ :unresolved_block
16
+
17
+ def initialize(hash=nil, &block)
18
+ @store = hash && hash.dup || Hash.new
19
+
20
+ call(block) if block
21
+ end
22
+
23
+ # Explicit accessor for POROs.
24
+ #
25
+ # Note: Unwraps Values.
26
+ #
27
+ def [] key
28
+ value = @store[key]
29
+ value.to_h rescue value
30
+ end
31
+
32
+ # Sets key to value.
33
+ #
34
+ def []= key, value
35
+ @store[key] = value
36
+ end
37
+
38
+ # The main method.
39
+ #
40
+ # It will react to method calls, install the right keys on the internal
41
+ # store and return proxies on which callers can perform operations, e.g.
42
+ # <<.
43
+ #
44
+ # Note: The user only interacts with Configurations. Not with the internal
45
+ # values.
46
+ #
47
+ # Except when the user explicitly takes the
48
+ # values out using [].
49
+ #
50
+ def method_missing name, *args
51
+ case args.size
52
+ when 0
53
+ if block_given?
54
+ subconfig = Configuration.new &Proc.new
55
+ store[name] = subconfig
56
+ else
57
+ store[name] ||= Value.new
58
+ end
59
+ when 1
60
+ store[name] = Value.new args.first
61
+ end
62
+ end
63
+
64
+ # Returns a configuration hash.
65
+ #
66
+ # NOTE: Resolves any unresolved blocks.
67
+ #
68
+ # NOTE: Call to_json on this to get a JSON representation of the hash.
69
+ #
70
+ def to_h
71
+ copy = {}
72
+ store.each do |key, value|
73
+ value = value.to_h if value.respond_to?(:to_h)
74
+ copy[key] = value if value
75
+ end
76
+ copy
77
+ end
78
+
79
+ # Performs a deep duplication of this configuration object. This is mainly
80
+ # used by the user for quickly generating a lot of copies of a template.
81
+ #
82
+ def dup(&block)
83
+ dup_cfg = Configuration.new
84
+
85
+ store.each do |key, value|
86
+ dup_cfg[key] = value.dup
87
+ end
88
+
89
+ dup_cfg.call(block) if block
90
+ dup_cfg
91
+ end
92
+
93
+ # Executes the block such that it may modify this object.
94
+ #
95
+ def call(block)
96
+ fail ArgumentError unless block
97
+
98
+ block.call(self) if block.arity>0
99
+ instance_eval(&block) if block.arity == 0
100
+ end
101
+ end
102
+
103
+ end
@@ -0,0 +1,72 @@
1
+
2
+ # Represents a bocuse File.
3
+ #
4
+ # This is usually a file that contains one or more of the
5
+ # following directives:
6
+ # * node
7
+ # * template
8
+ #
9
+ # It is used to generate a config hash from the given files.
10
+ #
11
+ class Bocuse::File
12
+ attr_reader :path
13
+
14
+ # When inside a construct that has a configuration associated, this will
15
+ # return that configuration instance.
16
+ attr_reader :current_configuration
17
+
18
+ def initialize(path, context)
19
+ @path = path
20
+ @context = context
21
+ end
22
+
23
+ # Returns a config hash.
24
+ #
25
+ # Call to_h on the returned configuration to get the config hash.
26
+ #
27
+ # Will return nil if there is no configuration to speak of inside the file.
28
+ # Use "node" or "template" to define a configuration.
29
+ #
30
+ def evaluate
31
+ ::File.open path, 'r' do |file|
32
+ self.instance_eval file.read, file.path
33
+ end
34
+ end
35
+
36
+ # The files read by #evaluate will trigger these methods.
37
+ #
38
+ def node name
39
+ unit = Bocuse::Unit.new(Proc.new, @context)
40
+
41
+ configuration_block do |configuration|
42
+ unit.call(configuration)
43
+ @context.register_node name, configuration
44
+ end
45
+ end
46
+ def template
47
+ # Delay template evaluation until someone tries to call include_template.
48
+ # At that time, we'll have a configuration object to have the template
49
+ # manipulate.
50
+ unit = Bocuse::Unit.new(Proc.new, @context)
51
+ @context.register_template path, unit
52
+ unit
53
+ end
54
+ private
55
+ def configuration_block
56
+ # NOTE since thread-safety is not an issue here (who would use threads
57
+ # to define configuration?), we can use a simple file-global state to
58
+ # keep track of which configuration is in progress. We use the begin-end
59
+ # construct to make sure that configurations are not used beyond the end
60
+ # of the node block.
61
+
62
+ configuration = @current_configuration = Bocuse::Configuration.new
63
+
64
+ begin
65
+ yield configuration
66
+ ensure
67
+ @current_configuration = nil
68
+ end
69
+
70
+ configuration
71
+ end
72
+ end
@@ -0,0 +1,155 @@
1
+ require 'pathname'
2
+
3
+ module Bocuse
4
+ # A bocuse project comes in two flavours: simple and co-located with chef.
5
+ # The simple project is a directory with the subdirectories
6
+ # nodes/
7
+ # templates/
8
+ # lib/
9
+ #
10
+ # The nodes/ subdirectory contains all node descriptions, templates/
11
+ # contains templates and lib/ is added to the load path and can contain code
12
+ # to help with construction of the configuration.
13
+ #
14
+ # When you co-host bocuse with chef, normally you would store all these
15
+ # directories one level deeper. Here's what your hierarchy should look like
16
+ # in this case:
17
+ #
18
+ # config/
19
+ # nodes/
20
+ # templates/
21
+ # lib/
22
+ #
23
+ # This class does most of the base path handling and of the path
24
+ # manipulation.
25
+ #
26
+ class Project
27
+ attr_reader :base_path
28
+
29
+ # Initialize a bocuse project by specifying its base directory.
30
+ #
31
+ def initialize(base_directory=nil)
32
+ base_directory = base_directory || Dir.pwd
33
+
34
+ if chef_cohosted?(base_directory)
35
+ base_directory = ::File.join(base_directory, 'config')
36
+ end
37
+
38
+ @base_path = Pathname.new(base_directory)
39
+ @templates = Hash.new
40
+ @nodes = Hash.new
41
+
42
+ # Make sure that project libraries can be loaded:
43
+ lib_dir = base_path.join('lib')
44
+ $:.unshift lib_dir if ::File.directory?(lib_dir)
45
+ end
46
+
47
+ # Returns one of the projects files as a {File}.
48
+ #
49
+ # @param path [String] a relative or absolute file path
50
+ # @return [File] file at path below the project directory
51
+ #
52
+ def file path
53
+ path = Pathname.new(path)
54
+ path = base_path.join(path) unless path.absolute?
55
+
56
+ Bocuse::File.new(path, self)
57
+ end
58
+
59
+ # Evaluates a file given by path.
60
+ #
61
+ # @param path [String] file path, either absolute or relative to project
62
+ # base directory
63
+ # @return [void]
64
+ #
65
+ def evaluate path
66
+ file(path).evaluate
67
+ end
68
+
69
+ # Registers a template for project usage.
70
+ #
71
+ def register_template path, template
72
+ path = path.to_s
73
+ template_base = base_path.join('templates/').to_s
74
+
75
+ # Is this template path below base_path?
76
+ if path.start_with?(template_base)
77
+ path = path[template_base.size..-1]
78
+ end
79
+
80
+ # Does the path end in .rb? (most will)
81
+ if path.end_with?('.rb')
82
+ path = path.slice 0..-4
83
+ end
84
+
85
+ @templates.store path.to_sym, template
86
+ end
87
+
88
+ # Returns a template by name. Official template names can be either
89
+ # absolute paths or relative paths to template files, but do NOT end in
90
+ # .rb.
91
+ #
92
+ # foo -> templates/foo.rb
93
+ # /templates/bar -> /usr/templates/bar.rb
94
+ #
95
+ def template name
96
+ unless @templates.has_key?(name.to_sym)
97
+ file_name = name.to_s
98
+ file_name += '.rb' unless file_name.end_with?('.rb')
99
+
100
+ # Try to find and load this template:
101
+ template_path = base_path.join('templates', file_name)
102
+ file(template_path).evaluate
103
+
104
+ # A successful load will register the template.
105
+ end
106
+
107
+ @templates.fetch(name.to_sym)
108
+ end
109
+
110
+ # Registers a machine node in the project.
111
+ #
112
+ def register_node name, node
113
+ @nodes.store name, node
114
+ end
115
+
116
+ # Returns all nodes in this project as a hash.
117
+ #
118
+ # @return [Hash<name,Configuration>] A hash mapping node names to their
119
+ # configuration objects.
120
+ #
121
+ def nodes
122
+ evaluate_all unless @nodes.size > 0
123
+ @nodes
124
+ end
125
+
126
+ # Evaluates all files in the project.
127
+ #
128
+ # Retrieve the found configurations via
129
+ # Nodes.find pattern_or_name
130
+ #
131
+ # @param dir [String] directory to use as project base directory
132
+ # @return [void]
133
+ #
134
+ def evaluate_all
135
+ nodes_glob = base_path.join('nodes', '**', '*.rb')
136
+
137
+ Dir[nodes_glob].each do |filename|
138
+ file(filename).evaluate
139
+ end
140
+ end
141
+
142
+ class << self
143
+ def bocuse_path?(path)
144
+ %w(nodes).all? { |el|
145
+ ::File.directory?(::File.join(path, el)) }
146
+ end
147
+ end
148
+
149
+ private
150
+ def chef_cohosted?(base)
151
+ !self.class.bocuse_path?(base) &&
152
+ self.class.bocuse_path?(::File.join(base, 'config'))
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,44 @@
1
+ module Bocuse
2
+ # A configuration unit.
3
+ #
4
+ class Bocuse::Unit
5
+ # Returns the current configuration, but only during a call to this unit.
6
+ attr_reader :current_configuration
7
+
8
+ def initialize(block, context)
9
+ @block = block
10
+ @context = context
11
+ end
12
+
13
+ def call(configuration)
14
+ @current_configuration = configuration
15
+
16
+ # Fill in the config hash argument if the block cares at all.
17
+ instance_exec(configuration, &@block)
18
+ ensure
19
+ @current_configuration = nil
20
+ end
21
+
22
+ # Cook adds to the toplevel recipes of this file's configuration.
23
+ #
24
+ def cook recipe
25
+ current_configuration.recipes << recipe
26
+ end
27
+
28
+ # Make the given module a helper module for this node.
29
+ #
30
+ def helper_module(mod)
31
+ extend mod
32
+ end
33
+
34
+ # Include the given template name.
35
+ #
36
+ # Note: This could be pushed to the configuration.
37
+ #
38
+ def include_template identifier
39
+ template_block = @context.template identifier
40
+
41
+ template_block.call(current_configuration)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ module Bocuse
2
+
3
+ # This is a value object that by default starts out life as not having
4
+ # decided what exactly it is.
5
+ #
6
+ # On first adding another object, it assumes an internal form.
7
+ #
8
+ # It will complain if its internal form does not correspond to the assumed
9
+ # external one, ie. when you want to << something, but it has already
10
+ # assumed the internal form of a hash (<< does not exist on a hash).
11
+ #
12
+ class Value
13
+
14
+ module Empty; def self.empty?; true; end end
15
+
16
+ def initialize internal = Empty
17
+ @internal = internal
18
+ end
19
+
20
+ def empty?
21
+ @internal && @internal.empty?
22
+ end
23
+
24
+ def []= key, value
25
+ @internal = {} if empty?
26
+ @internal[key] = value
27
+ end
28
+
29
+ def << element
30
+ @internal = [] if empty?
31
+ @internal << element
32
+ end
33
+
34
+ def to_h
35
+ @internal unless Empty == @internal
36
+ end
37
+
38
+ def dup
39
+ Value.new(@internal.dup)
40
+ end
41
+ end
42
+
43
+ end
data/lib/bocuse.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'multi_json'
2
+
3
+ require 'bocuse/project'
4
+ require 'bocuse/value'
5
+ require 'bocuse/configuration'
6
+ require 'bocuse/file'
7
+ require 'bocuse/unit'
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ require 'multi_json'
4
+
5
+ describe "CLI:" do
6
+ describe 'compile NODE_FQDN' do
7
+ it "parses nodes and outputs a single nodes JSON" do
8
+ Dir.chdir File.expand_path('../../files/complex', __FILE__)
9
+
10
+ # Explicitly call the current binary.
11
+ #
12
+
13
+ MultiJson.load(`#{project_path('bin/bocuse')} \
14
+ compile complex.production.example.com`).should ==
15
+ {
16
+ "recipes"=>["nginx", "git", "app::install", "app::deploy"],
17
+ "key_location" =>
18
+ "/Users/kschiess/git/own/bocuse/spec/files/complex/config/nodes/production/key.txt"}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe 'complex example with templates' do
6
+ let(:project) { Bocuse::Project.new(fixture('complex')) }
7
+
8
+ describe 'hash of node "test.staging.example.com"' do
9
+ it 'looks right' do
10
+ configuration = project.evaluate('nodes/staging/complex.rb')
11
+
12
+ configuration.to_h.should == {
13
+ :user => "root",
14
+ :users => [
15
+ {
16
+ :username => "some_user",
17
+ :password => "toooootally_secret",
18
+ :authorized_keys => ["key1", "key2"],
19
+ :shell => "/bin/someshell",
20
+ :gid => 1000,
21
+ :uid => 1000,
22
+ :sudo => true
23
+ }
24
+ ],
25
+ :webserver => {
26
+ :domains => ["staging.example.com"],
27
+ :listen_ip => "1.2.3.4",
28
+ :ssl_support => true
29
+ },
30
+ :server => {
31
+ :ip => "11.22.33.44",
32
+ :external_net => 28,
33
+ :internal_ip => "1.1.1.1",
34
+ :internal_net => "10",
35
+ :context => 20
36
+ },
37
+ :cache1 => {
38
+ :address => "1.1.1.1"
39
+ },
40
+ :cache2 => {
41
+ :address => "1.1.1.1"
42
+ },
43
+ :empty => true,
44
+ :recipes => ["nginx", "git", "app::install", "app::deploy"]
45
+ }
46
+ end
47
+ end
48
+ describe 'hash of node "complex.production.example.com"' do
49
+ it "contains only recipes that are relevant to the node" do
50
+ config = project.nodes.
51
+ find { |name, _| name == 'complex.production.example.com' }.
52
+ last
53
+
54
+ config.to_h[:recipes].should =~ %w(nginx git app::install app::deploy)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'helpers example' do
4
+ let(:project) { Bocuse::Project.new(fixture('helpers')) }
5
+
6
+ describe 'loading "node"' do
7
+ it 'works' do
8
+ configuration = project.nodes['node']
9
+ configuration[:foo].should == 'bar'
10
+ configuration[:nested][:foo].should == 'bar'
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe 'finding nodes in files/many' do
6
+ let(:project) { Bocuse::Project.new(fixture('many')) }
7
+
8
+ describe 'loading all nodes' do
9
+ it 'works with defaults' do
10
+ project.should have(4).nodes
11
+ end
12
+ it 'works with specific string' do
13
+ nodes = project.nodes.select { |name, _| 'subfolder1' === name }
14
+ nodes.size.should == 1
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe 'Templates' do
6
+ let(:project) { Bocuse::Project.new(fixture('complex')) }
7
+
8
+ describe 'loading' do
9
+ it 'works' do
10
+ config = Bocuse::Configuration.new
11
+ project.template(:users).call(config)
12
+
13
+ config.to_h.should == {
14
+ :user => "root",
15
+ :users => [
16
+ {
17
+ :username => "some_user",
18
+ :password => "toooootally_secret",
19
+ :authorized_keys => ["key1", "key2"],
20
+ :shell => "/bin/someshell",
21
+ :gid => 1000,
22
+ :uid => 1000,
23
+ :sudo => true
24
+ }
25
+ ]
26
+ }
27
+ end
28
+ end
29
+
30
+ describe '#require_ingredients' do
31
+ it "complains if the current configuration doesn't contain the given keys"
32
+ end
33
+ end
@@ -0,0 +1,148 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Bocuse::Configuration do
6
+
7
+ let(:configuration) { described_class.new }
8
+
9
+ describe 'construction' do
10
+ it "constructs given a hash" do
11
+ h = { foo: 'bar' }
12
+
13
+ config = described_class.new(h)
14
+
15
+ config.to_h.should == h
16
+ config.to_h.should_not equal(h)
17
+ end
18
+ end
19
+
20
+ describe 'uninitialized values' do
21
+ it 'will be ignored' do
22
+ something = configuration.something
23
+
24
+ configuration.to_h.should == {}
25
+ end
26
+ end
27
+ describe '.something as a value' do
28
+ it 'returns the right value' do
29
+ configuration.something.to_h.should == nil # Bocuse::Value::Empty is returned as nil
30
+ end
31
+ it 'returns the right value' do
32
+ configuration.something nil # explicit nil
33
+
34
+ configuration.something.to_h.should == nil
35
+ end
36
+ it 'returns the right value' do
37
+ configuration.something 'value'
38
+
39
+ configuration.something.to_h.should == 'value' # <= This is the tested getter.
40
+ end
41
+ it 'is modifiable in place' do
42
+ something = configuration.something
43
+
44
+ something << "hello"
45
+
46
+ configuration.something.to_h.should == ["hello"]
47
+ end
48
+ it 'is modifiable in place' do
49
+ something = configuration.something
50
+
51
+ something[:key] = "value"
52
+
53
+ configuration.something.to_h.should == { :key => 'value' }
54
+ end
55
+ it 'is modifiable in place' do
56
+ something = configuration.something
57
+
58
+ something[:key] = nil # Explicit nil.
59
+
60
+ configuration.to_h.should == { :something => { :key => nil } }
61
+ end
62
+ end
63
+ describe '.something "value"' do
64
+ it 'stores the value correctly' do
65
+ configuration.something 'value'
66
+
67
+ configuration.to_h.should == { :something => 'value' }
68
+ end
69
+ end
70
+ describe '.something do ... end' do
71
+ it 'stores the value correctly' do
72
+ configuration.something do
73
+ key 'value'
74
+ end
75
+
76
+ configuration.to_h.should == { :something => { :key => 'value' } }
77
+ end
78
+ end
79
+ describe '.something do |param| ... end' do
80
+ it "doesn't allow implicit access" do
81
+ expect {
82
+ configuration.something do |something|
83
+ foo 'bar'
84
+ end
85
+ }.to raise_error
86
+ end
87
+ it "represents a configuration, one level deeper" do
88
+ result = nil
89
+ configuration.something do |something|
90
+ result = something
91
+ end
92
+
93
+ result.should be_instance_of(described_class)
94
+ end
95
+ it 'stores the value correctly' do
96
+ configuration.something do |cfg|
97
+ cfg.key 'value'
98
+ end
99
+
100
+ configuration.to_h.should == { :something => { :key => 'value' } }
101
+ end
102
+ end
103
+ describe '#[]' do
104
+ it 'offers a [] method' do
105
+ configuration[:something].should == nil
106
+ end
107
+ it 'returns the right value' do
108
+ configuration.something :value
109
+
110
+ configuration[:something].should == :value
111
+ end
112
+ it 'returns the right value' do
113
+ configuration.something do
114
+ key 'value'
115
+ end
116
+
117
+ configuration[:something].should == { :key => 'value' }
118
+ end
119
+ it 'is not modifiable in place as it is a PORO' do
120
+ something = configuration[:something]
121
+
122
+ expect { something << "hello" }.to raise_error
123
+
124
+ # configuration.to_h.should == { :something => ["hello"] } # This might be expected by a user.
125
+ end
126
+ end
127
+ describe '#dup' do
128
+ before(:each) {
129
+ configuration.foo do
130
+ bar []
131
+ end }
132
+ let(:dup) { configuration.dup }
133
+
134
+ it "is performed in depth" do
135
+ configuration[:foo][:bar].should_not equal(dup[:foo][:bar])
136
+ end
137
+ it "returns a duplicate" do
138
+ configuration.to_h.should == dup.to_h
139
+ end
140
+ it "takes a block, allowing modification of the result" do
141
+ dup = configuration.dup do |cfg|
142
+ cfg.baz 'this is new'
143
+ end
144
+
145
+ dup[:baz].should == 'this is new'
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ require 'bocuse/file'
4
+
5
+ describe Bocuse::File do
6
+ let(:context) { flexmock('context') }
7
+ let(:file) { described_class.new('path', context) }
8
+
9
+ before(:each) {
10
+ context.
11
+ should_receive(
12
+ :register_node, :register_template).by_default
13
+ }
14
+
15
+ it "checks that helper inclusion only works for the current file"
16
+
17
+ describe 'node' do
18
+ it 'works correctly' do
19
+ configuration = file.node 'name' do |cfg|
20
+ cfg.something :value
21
+ cfg.something_else do
22
+ key 'value'
23
+ end
24
+ end
25
+
26
+ configuration.to_h.should == { :something => :value, :something_else => { :key => "value" } }
27
+ end
28
+ it "registers the node in the context" do
29
+ context.should_receive(:register_node).
30
+ with('name', Bocuse::Configuration).once
31
+
32
+ file.node('name') { }
33
+ end
34
+ end
35
+
36
+ describe 'template' do
37
+ it 'works correctly' do
38
+ template = file.template do |cfg|
39
+ cfg.something :value
40
+ cfg.something_else do
41
+ key 'value'
42
+ end
43
+ end
44
+
45
+ config = Bocuse::Configuration.new
46
+ template.call config
47
+
48
+ config.to_h.should == { :something => :value, :something_else => { :key => "value" } }
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ require 'bocuse/project'
4
+
5
+ describe Bocuse::Project do
6
+ let(:project) { described_class.new(fixture('complex')) }
7
+
8
+ describe '#base_path' do
9
+ it "detects a simple project" do
10
+ described_class.new(fixture('complex/config')).
11
+ base_path.should == fixture('complex/config')
12
+ end
13
+ it "detects a co-hosted project" do
14
+ described_class.new(fixture('complex')).
15
+ base_path.should == fixture('complex/config')
16
+ end
17
+ end
18
+ describe '#file' do
19
+ let(:file) { project.file('nodes/production/complex') }
20
+
21
+ it "returns a Bocuse::Unit instance" do
22
+ file.should be_instance_of(Bocuse::File)
23
+ end
24
+ it "has the correct expanded path" do
25
+ file.path.should ==
26
+ fixture('complex/config/nodes/production/complex')
27
+ end
28
+ end
29
+ describe '#register_node' do
30
+
31
+ end
32
+ describe '#register_template / #template' do
33
+ before(:each) {
34
+ base = project.base_path
35
+ template = base.join('templates', 'foo', 'bar.rb')
36
+ project.register_template template, :configuration }
37
+
38
+ it "returns the template" do
39
+ project.template('foo/bar').should == :configuration
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Bocuse::Value do
6
+
7
+ describe 'to_h' do
8
+ it 'works correctly' do
9
+ Bocuse::Value.new.to_h.should == nil
10
+ end
11
+ it 'works correctly' do
12
+ value = Bocuse::Value.new
13
+ value << 1
14
+ value.to_h.should == [1]
15
+ end
16
+ it 'works correctly' do
17
+ value = Bocuse::Value.new
18
+ value[:address] = '1.2.3.4'
19
+ value.to_h.should == { :address => '1.2.3.4' }
20
+ end
21
+ end
22
+
23
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bocuse
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Florian Hanke
9
+ - Kaspar Schiess
10
+ - Jens-Christian Fischer
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-07-06 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: '0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: guard
34
+ requirement: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: guard-rspec
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ - !ruby/object:Gem::Dependency
65
+ name: flexmock
66
+ requirement: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ type: :development
73
+ prerelease: false
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ - !ruby/object:Gem::Dependency
81
+ name: multi_json
82
+ requirement: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: 1.0.0
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: 1.0.0
96
+ - !ruby/object:Gem::Dependency
97
+ name: thor
98
+ requirement: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '0.15'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ version: '0.15'
112
+ description: ! " bocuse teaches chef-solo a few tricks. A strict front-end to chef-solo,
113
+ \n it reads a configuration syntax that is under source control and \n generates
114
+ JSON for chef-solo.\n\n This library puts the full power of Ruby at your fingertips
115
+ when composing\n configuration for your nodes using templates and helpers. It
116
+ is the \n missing link between puppet and chef. \n"
117
+ email:
118
+ - florian.hanke@technologyastronauts.ch
119
+ - kaspar.schiess@technologyastronauts.ch
120
+ - jcf@mobino.com
121
+ executables:
122
+ - bocuse
123
+ extensions: []
124
+ extra_rdoc_files: []
125
+ files:
126
+ - lib/bocuse/configuration.rb
127
+ - lib/bocuse/file.rb
128
+ - lib/bocuse/project.rb
129
+ - lib/bocuse/unit.rb
130
+ - lib/bocuse/value.rb
131
+ - lib/bocuse.rb
132
+ - spec/integration/cli_spec.rb
133
+ - spec/integration/complex_spec.rb
134
+ - spec/integration/helpers_spec.rb
135
+ - spec/integration/node_finding_spec.rb
136
+ - spec/integration/templates_spec.rb
137
+ - spec/lib/bocuse/configuration_spec.rb
138
+ - spec/lib/bocuse/file_spec.rb
139
+ - spec/lib/bocuse/project_spec.rb
140
+ - spec/lib/bocuse/value_spec.rb
141
+ - bin/bocuse
142
+ homepage: http://github.com/mobino/bocuse
143
+ licenses: []
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ none: false
156
+ requirements:
157
+ - - ! '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubyforge_project:
162
+ rubygems_version: 1.8.24
163
+ signing_key:
164
+ specification_version: 3
165
+ summary: A front-end language to chef-solo.
166
+ test_files:
167
+ - spec/integration/cli_spec.rb
168
+ - spec/integration/complex_spec.rb
169
+ - spec/integration/helpers_spec.rb
170
+ - spec/integration/node_finding_spec.rb
171
+ - spec/integration/templates_spec.rb
172
+ - spec/lib/bocuse/configuration_spec.rb
173
+ - spec/lib/bocuse/file_spec.rb
174
+ - spec/lib/bocuse/project_spec.rb
175
+ - spec/lib/bocuse/value_spec.rb
176
+ has_rdoc: