bocuse 0.1.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/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: