confection 0.2.0 → 0.2.1
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/.ruby +1 -1
- data/Confile.rb +57 -0
- data/lib/confection/core_ext.rb +14 -0
- data/lib/confection/current.rb +79 -0
- data/lib/confection/hash_builder.rb +48 -0
- data/lib/confection/project.rb +161 -0
- data/lib/confection/store.rb +182 -0
- data/spec/00_concept.md +33 -0
- data/spec/01_dsl.md +69 -0
- data/spec/02_import.md +52 -0
- data/spec/03_store.md +20 -0
- data/spec/04_controller.md +68 -0
- data/spec/05_config.md +99 -0
- data/spec/06_manage.md +38 -0
- data/spec/applique/ae.rb +1 -0
- data/spec/applique/file.rb +8 -0
- data/spec/applique/fixture.rb +10 -0
- data/spec/applique/fixture/confile.rb +15 -0
- metadata +30 -13
data/.ruby
CHANGED
@@ -47,7 +47,7 @@ revision: 0
|
|
47
47
|
created: '2011-11-06'
|
48
48
|
summary: Multi-tenant configuration for Ruby
|
49
49
|
title: Confection
|
50
|
-
version: 0.2.
|
50
|
+
version: 0.2.1
|
51
51
|
name: confection
|
52
52
|
description: Confection is a multi-tenant configuration system for Ruby projects.
|
53
53
|
organization: Rubyworks
|
data/Confile.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# QED test coverage report using SimpleCov.
|
5
|
+
#
|
6
|
+
# coverage_folder - the directory in which to store coverage report
|
7
|
+
# this defaults to `log/coverage`.
|
8
|
+
#
|
9
|
+
config :qed, :cov do
|
10
|
+
require 'simplecov'
|
11
|
+
|
12
|
+
dir = $properties.coverage_folder
|
13
|
+
|
14
|
+
SimpleCov.start do
|
15
|
+
coverage_dir(dir || 'log/coverage')
|
16
|
+
#add_group "Label", "lib/qed/directory"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Example configuration.
|
22
|
+
#
|
23
|
+
config :example do
|
24
|
+
puts "Configuration Example!"
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Detroit assembly.
|
29
|
+
#
|
30
|
+
config :detroit do
|
31
|
+
email do
|
32
|
+
mailto 'ruby-talk@ruby-lang.org', 'rubyworks-mailinglist@googlegroups.com'
|
33
|
+
end
|
34
|
+
|
35
|
+
gem do
|
36
|
+
active true
|
37
|
+
end
|
38
|
+
|
39
|
+
github do
|
40
|
+
folder 'web'
|
41
|
+
end
|
42
|
+
|
43
|
+
dnote do
|
44
|
+
title 'Source Notes'
|
45
|
+
output 'log/notes.html'
|
46
|
+
end
|
47
|
+
|
48
|
+
locat do
|
49
|
+
output 'log/locat.html'
|
50
|
+
end
|
51
|
+
|
52
|
+
vclog do
|
53
|
+
output 'log/history.html',
|
54
|
+
'log/changes.html'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# All core extensions come from Ruby Facets to maintain high standard for
|
2
|
+
# careful core extensions.
|
3
|
+
|
4
|
+
require 'facets/string/tabto'
|
5
|
+
require 'facets/to_hash'
|
6
|
+
|
7
|
+
#require 'facets/ostruct/to_h' # TODO: Newer version of facets.
|
8
|
+
require 'ostruct'
|
9
|
+
class OpenStruct
|
10
|
+
def to_h
|
11
|
+
@table.dup
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Confection
|
2
|
+
|
3
|
+
#
|
4
|
+
# Global properties is set for parsing project configuration.
|
5
|
+
# It is *always* the properties of the current project.
|
6
|
+
#
|
7
|
+
$properties = nil
|
8
|
+
|
9
|
+
# Current mixin extends the Confection module. Primarily is provides
|
10
|
+
# class methods for working with the current project's configurations.
|
11
|
+
#
|
12
|
+
module Current
|
13
|
+
|
14
|
+
#
|
15
|
+
def controller(scope, tool, *options)
|
16
|
+
params = (Hash === options.last ? options.pop : {})
|
17
|
+
params[:profile] = options.shift unless options.empty?
|
18
|
+
|
19
|
+
if from = params[:from]
|
20
|
+
projects[from] ||= Project.load(from)
|
21
|
+
projects[from].controller(scope, tool, params)
|
22
|
+
else
|
23
|
+
bootstrap if $properties.nil? # TODO: better way to go about this?
|
24
|
+
current_project.controller(scope, tool, params)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
def bootstrap
|
30
|
+
$properties = current_project.properties
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
def projects
|
35
|
+
@projects ||= {}
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
def current_directory
|
40
|
+
@current_directory ||= Dir.pwd
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
def current_project
|
45
|
+
projects[current_directory] ||= Project.lookup(current_directory)
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
def clear!
|
50
|
+
current_project.store.clear!
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
def profiles(tool)
|
55
|
+
current_project.profiles(tool)
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
def each(&block)
|
60
|
+
current_project.each(&block)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
def size
|
65
|
+
current_project.size
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Project properties.
|
70
|
+
#
|
71
|
+
def properties
|
72
|
+
current_project.properties
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
extend Current
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Confection
|
2
|
+
|
3
|
+
# HashBuilder takes a procedure and builds a Hash out of it.
|
4
|
+
#
|
5
|
+
# The procedure must conform to a set of rules to be useful in this respect.
|
6
|
+
# They must either take an argument and use that argument to set values, or
|
7
|
+
# if no argument is taken then `#instance_eval` is used to evaluate the
|
8
|
+
# procedure such that each method represents a key.
|
9
|
+
#
|
10
|
+
class HashBuilder < BasicObject
|
11
|
+
|
12
|
+
#
|
13
|
+
def initialize(hash={}, &block)
|
14
|
+
@hash = hash
|
15
|
+
case block.arity
|
16
|
+
when 0
|
17
|
+
instance_eval(&block)
|
18
|
+
else
|
19
|
+
block.call(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
def to_h
|
25
|
+
@hash
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
def method_missing(s, *a, &b)
|
30
|
+
m = s.to_s
|
31
|
+
if a.empty? && !b
|
32
|
+
@hash[m.to_sym]
|
33
|
+
else
|
34
|
+
if b
|
35
|
+
@hash[m.chomp('=').to_sym] = HashBuilder.new(&b).to_h
|
36
|
+
else
|
37
|
+
if a.size > 1
|
38
|
+
@hash[m.chomp('=').to_sym] = a
|
39
|
+
else
|
40
|
+
@hash[m.chomp('=').to_sym] = a.first
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Confection
|
2
|
+
|
3
|
+
# Project configuration.
|
4
|
+
#
|
5
|
+
# @todo Rename to `ProjectConfig` or ?
|
6
|
+
#
|
7
|
+
class Project
|
8
|
+
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
#
|
12
|
+
# Configuration file pattern.
|
13
|
+
#
|
14
|
+
PATTERN = '{.,}confile{.rb,}'
|
15
|
+
|
16
|
+
#
|
17
|
+
# Per library cache.
|
18
|
+
#
|
19
|
+
def self.cache
|
20
|
+
@cache ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Get project configuration from another library.
|
25
|
+
#
|
26
|
+
# This method uses the Finder gem.
|
27
|
+
#
|
28
|
+
# @param [String] lib
|
29
|
+
# Library name.
|
30
|
+
#
|
31
|
+
# @return [Project,nil] Located project.
|
32
|
+
#
|
33
|
+
def self.load(lib=nil)
|
34
|
+
if lib
|
35
|
+
lib = lib.to_s
|
36
|
+
return cache[lib] if cache.key?(lib)
|
37
|
+
cache[lib] ||= (
|
38
|
+
config_path = Find.path(PATTERN, :from=>lib).first
|
39
|
+
config_path ? new(File.dirname(config_path)) : nil
|
40
|
+
)
|
41
|
+
else
|
42
|
+
lookup
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Lookup configuation file.
|
48
|
+
#
|
49
|
+
# @param dir [String]
|
50
|
+
# Optional directory to begin search.
|
51
|
+
#
|
52
|
+
# @return [String] file path
|
53
|
+
#
|
54
|
+
def self.lookup(dir=nil)
|
55
|
+
dir = dir || Dir.pwd
|
56
|
+
home = File.expand_path('~')
|
57
|
+
while dir != '/' && dir != home
|
58
|
+
if file = Dir.glob(File.join(dir, PATTERN), File::FNM_CASEFOLD).first
|
59
|
+
return new(File.dirname(file))
|
60
|
+
end
|
61
|
+
dir = File.dirname(dir)
|
62
|
+
end
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Initialize new ProjectConfig.
|
68
|
+
#
|
69
|
+
# @param [String] root
|
70
|
+
# Project root directory.
|
71
|
+
#
|
72
|
+
def initialize(root)
|
73
|
+
@root = root
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Project root directory.
|
78
|
+
#
|
79
|
+
# @return [String] project's root directory
|
80
|
+
#
|
81
|
+
attr :root
|
82
|
+
|
83
|
+
#
|
84
|
+
# Alias for #root.
|
85
|
+
#
|
86
|
+
alias :directory :root
|
87
|
+
|
88
|
+
#
|
89
|
+
# Configuration store tracks a project's confirguration entries.
|
90
|
+
#
|
91
|
+
def store
|
92
|
+
@store ||= Store.new(*source)
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# The file path of the project's configuration file.
|
97
|
+
#
|
98
|
+
# @return [String] path to configuration file
|
99
|
+
#
|
100
|
+
def source
|
101
|
+
Dir.glob(File.join(root, PATTERN), File::FNM_CASEFOLD)
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# List of configuration profiles.
|
106
|
+
#
|
107
|
+
# @return [Array] profile names
|
108
|
+
#
|
109
|
+
def profiles(tool)
|
110
|
+
store.profiles(tool)
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Project properties.
|
115
|
+
#
|
116
|
+
# @todo Use cascading class, e.g. Confstruct.
|
117
|
+
#
|
118
|
+
def properties
|
119
|
+
dotruby = File.join(directory,'.ruby')
|
120
|
+
if File.exist?(dotruby)
|
121
|
+
data = YAML.load_file(dotruby)
|
122
|
+
OpenStruct.new(data)
|
123
|
+
else
|
124
|
+
OpenStruct.new
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Create a configuration controller.
|
130
|
+
#
|
131
|
+
# @param [Object] scope
|
132
|
+
# Context for which controller is being created.
|
133
|
+
#
|
134
|
+
# @param [Symbol] tool
|
135
|
+
# The tool of the configuration to select.
|
136
|
+
#
|
137
|
+
def controller(scope, tool, options={})
|
138
|
+
profile = options[:profile]
|
139
|
+
configs = store.lookup(tool, profile)
|
140
|
+
Controller.new(scope, *configs)
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# Iterate over each configurations.
|
145
|
+
#
|
146
|
+
def each(&block)
|
147
|
+
store.each(&block)
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# The number of configurations.
|
152
|
+
#
|
153
|
+
# @return [Fixnum] config count
|
154
|
+
#
|
155
|
+
def size
|
156
|
+
store.size
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Confection
|
2
|
+
|
3
|
+
class Store
|
4
|
+
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
=begin
|
8
|
+
#
|
9
|
+
# Bootstrap the system, loading current configurations.
|
10
|
+
#
|
11
|
+
def bootstrap
|
12
|
+
if file
|
13
|
+
@config[root] = []
|
14
|
+
begin
|
15
|
+
DSL.load_file(file)
|
16
|
+
rescue => e
|
17
|
+
raise e if $DEBUG
|
18
|
+
warn e.message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@config[root]
|
22
|
+
end
|
23
|
+
=end
|
24
|
+
|
25
|
+
#
|
26
|
+
def initialize(*sources)
|
27
|
+
@sources = sources
|
28
|
+
|
29
|
+
@list = []
|
30
|
+
|
31
|
+
sources.each do |source|
|
32
|
+
if File.file?(source)
|
33
|
+
parse(source)
|
34
|
+
else
|
35
|
+
# ignore directories
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
attr :sources
|
42
|
+
|
43
|
+
#
|
44
|
+
def parse(file)
|
45
|
+
DSL.parse(self, file)
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Iterate over each configurations.
|
50
|
+
#
|
51
|
+
def each(&block)
|
52
|
+
@list.each(&block)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# The number of configurations.
|
57
|
+
#
|
58
|
+
# @return [Fixnum] config count
|
59
|
+
#
|
60
|
+
def size
|
61
|
+
@list.size
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Add as configuratio to the store.
|
66
|
+
#
|
67
|
+
def <<(conf)
|
68
|
+
raise TypeError, "not a configuration instance -- `#{conf}'" unless Config === conf
|
69
|
+
@list << conf
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Add a list of configs.
|
74
|
+
#
|
75
|
+
def concat(configs)
|
76
|
+
configs.each{ |c| self << c }
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Lookup configuration by tool and profile name.
|
81
|
+
#
|
82
|
+
# @todo Future versions should allow this to handle regex and fnmatches.
|
83
|
+
#
|
84
|
+
def lookup(tool, profile=nil)
|
85
|
+
if profile == '*'
|
86
|
+
select do |c|
|
87
|
+
c.tool.to_sym == tool.to_sym
|
88
|
+
end
|
89
|
+
else
|
90
|
+
profile = profile.to_sym if profile
|
91
|
+
|
92
|
+
select do |c|
|
93
|
+
c.tool.to_sym == tool.to_sym && c.profile == profile
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Returns list of profiles collected from all configs.
|
100
|
+
#
|
101
|
+
def profiles(tool)
|
102
|
+
names = []
|
103
|
+
each do |c|
|
104
|
+
names << c.profile if c.tool == tool.to_sym
|
105
|
+
end
|
106
|
+
names.uniq
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Clear configs.
|
111
|
+
#
|
112
|
+
def clear!
|
113
|
+
@list = []
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
def first
|
118
|
+
@list.first
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
def last
|
123
|
+
@list.last
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
#def config(*args, &block)
|
128
|
+
# dsl.config(*args, &block)
|
129
|
+
#end
|
130
|
+
|
131
|
+
#
|
132
|
+
#def controller(scope, name, profile=nil)
|
133
|
+
# configs = lookup(name, profile)
|
134
|
+
# Controller.new(scope, *configs)
|
135
|
+
#end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Import configuration from another project.
|
139
|
+
#
|
140
|
+
def import(tool, profile, options, &block)
|
141
|
+
from_tool = options[:tool] || tool
|
142
|
+
from_profile = options[:profile] || profile
|
143
|
+
|
144
|
+
case from = options[:from]
|
145
|
+
when String, Symbol
|
146
|
+
project = Project.load(from.to_s)
|
147
|
+
store = project ? project.store : nil
|
148
|
+
else
|
149
|
+
from = '(self)'
|
150
|
+
store = self
|
151
|
+
end
|
152
|
+
|
153
|
+
raise "no configuration found in `#{from}'" unless store
|
154
|
+
|
155
|
+
configs = store.lookup(from_tool, from_profile)
|
156
|
+
|
157
|
+
configs.each do |config|
|
158
|
+
new_config = config.copy(:tool=>tool, :profile=>profile)
|
159
|
+
|
160
|
+
#new_options = @_options.dup
|
161
|
+
#new_options[:tool] = tool
|
162
|
+
#new_options[:profile] = profile
|
163
|
+
#new_options[:block] = config.block
|
164
|
+
#new_options[:text] = config.text
|
165
|
+
|
166
|
+
# not so sure about this one
|
167
|
+
if String === new_config.value
|
168
|
+
new_config.value += ("\n" + options[:text].to_s) if options[:text]
|
169
|
+
end
|
170
|
+
|
171
|
+
self << new_config
|
172
|
+
end
|
173
|
+
|
174
|
+
#if block
|
175
|
+
# self << Config::Block.new(tool, profile, nil, &block)
|
176
|
+
#end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
data/spec/00_concept.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= Confection
|
2
|
+
|
3
|
+
The idea of confection is a unified configuration management across multiple
|
4
|
+
tools for Ruby. The structure of a confection configuration file is very simple.
|
5
|
+
It is a ruby script sectioned into named blocks:
|
6
|
+
|
7
|
+
config :rake do
|
8
|
+
# ... rake tasks ...
|
9
|
+
end
|
10
|
+
|
11
|
+
config :vclog do
|
12
|
+
# ... configure vclog ...
|
13
|
+
end
|
14
|
+
|
15
|
+
Utilization of the these configurations is strictly up to the consuming
|
16
|
+
application. For example, save native support in Rake itself, we can add
|
17
|
+
to a Rakefile:
|
18
|
+
|
19
|
+
require 'confection'
|
20
|
+
|
21
|
+
confection('rake').call
|
22
|
+
|
23
|
+
ANALYSIS:
|
24
|
+
|
25
|
+
config :qed, :coverage, -> do
|
26
|
+
name 'Tommy'
|
27
|
+
age 42
|
28
|
+
end
|
29
|
+
|
30
|
+
config :qed, :coverage do
|
31
|
+
puts "Scripted"
|
32
|
+
end
|
33
|
+
|
data/spec/01_dsl.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# DSL
|
2
|
+
|
3
|
+
The DSL class handle evaluation of a project configuration file.
|
4
|
+
|
5
|
+
store = Confection::Store.new
|
6
|
+
|
7
|
+
dsl = Confection::DSL.new(store)
|
8
|
+
|
9
|
+
The DSL instance will have a cached binding.
|
10
|
+
|
11
|
+
#dsl.__binding__ == dsl.__binding__
|
12
|
+
|
13
|
+
We can use the `#instance_eval` method to evaluate a configuration for our
|
14
|
+
demonstration.
|
15
|
+
|
16
|
+
dsl.instance_eval(<<-HERE)
|
17
|
+
config :sample1 do
|
18
|
+
"block code"
|
19
|
+
end
|
20
|
+
HERE
|
21
|
+
|
22
|
+
Evaluation of a configuration file, populate the Confection.config instance.
|
23
|
+
|
24
|
+
sample = store.last
|
25
|
+
sample.tool #=> :sample1
|
26
|
+
sample.profile #=> nil
|
27
|
+
sample.class #=> Confection::Config
|
28
|
+
|
29
|
+
A profile can be used as a means fo defining multiple configuration options
|
30
|
+
for a single tool. This can be done by setting the second argument to a Symbol.
|
31
|
+
|
32
|
+
dsl.instance_eval(<<-HERE)
|
33
|
+
config :sample2, :opt1 do
|
34
|
+
"block code"
|
35
|
+
end
|
36
|
+
HERE
|
37
|
+
|
38
|
+
sample = store.last
|
39
|
+
sample.tool #=> :sample2
|
40
|
+
sample.profile #=> :opt1
|
41
|
+
|
42
|
+
Or it can be done by using a `profile` block.
|
43
|
+
|
44
|
+
dsl.instance_eval(<<-HERE)
|
45
|
+
profile :opt1 do
|
46
|
+
config :sample2 do
|
47
|
+
"block code"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
HERE
|
51
|
+
|
52
|
+
sample = store.last
|
53
|
+
sample.tool #=> :sample2
|
54
|
+
sample.profile #=> :opt1
|
55
|
+
|
56
|
+
Different types of configuration can be defined. For instance, if a multi-line
|
57
|
+
string is passed to the `config` method will be a text-based configuration.
|
58
|
+
|
59
|
+
dsl.instance_eval(<<-HERE)
|
60
|
+
config :sample3, %{
|
61
|
+
text config
|
62
|
+
}
|
63
|
+
HERE
|
64
|
+
|
65
|
+
sample = store.last
|
66
|
+
sample.tool #=> :sample3
|
67
|
+
sample.profile #=> nil
|
68
|
+
sample.to_s.strip.assert == "text config"
|
69
|
+
|
data/spec/02_import.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Importing
|
2
|
+
|
3
|
+
## Configuration Importing
|
4
|
+
|
5
|
+
Configurations can be imported from another project
|
6
|
+
using the `:from` option.
|
7
|
+
|
8
|
+
store = Confection::Store.new
|
9
|
+
|
10
|
+
dsl = Confection::DSL.new(store)
|
11
|
+
|
12
|
+
dsl.config :qed, :profile=>'example', :from=>'qed'
|
13
|
+
|
14
|
+
store.size.assert == 1
|
15
|
+
|
16
|
+
The configuration can also be imported from a different profile.
|
17
|
+
|
18
|
+
dsl.config :qed, :coverage, :from=>'qed', :profile=>:simplecov
|
19
|
+
|
20
|
+
store.size.assert == 2
|
21
|
+
|
22
|
+
Although it will rarely be useful, it may also be imported from another tool.
|
23
|
+
|
24
|
+
dsl.config :example, :from=>'qed', :tool=>:sample
|
25
|
+
|
26
|
+
Imported configurations can also be augmented via a block.
|
27
|
+
|
28
|
+
store = Confection::Store.new
|
29
|
+
|
30
|
+
dsl = Confection::DSL.new(store)
|
31
|
+
|
32
|
+
dsl.config :qed, :from=>'qed', :profile=>:simplecov do
|
33
|
+
# additional code here
|
34
|
+
end
|
35
|
+
|
36
|
+
store.size.assert == 2
|
37
|
+
|
38
|
+
Technically this last form just creates two configurations for the same
|
39
|
+
tool and profile, but the ultimate effect is the same.
|
40
|
+
|
41
|
+
## Script Importing
|
42
|
+
|
43
|
+
Library files can be imported directly into configuration blocks via the
|
44
|
+
`#import` method.
|
45
|
+
|
46
|
+
dsl.config :example do
|
47
|
+
import "fruitbasket/example.rb"
|
48
|
+
end
|
49
|
+
|
50
|
+
This looks up the file via the `finder` gem and then evals it in the context
|
51
|
+
of the config block.
|
52
|
+
|
data/spec/03_store.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
## Store
|
2
|
+
|
3
|
+
A Confection::Store encapsulates the list of configurations that belong
|
4
|
+
to a project.
|
5
|
+
|
6
|
+
store = Confection::Store.new
|
7
|
+
|
8
|
+
Only Config instance can be added to a store. Any other type of object
|
9
|
+
will raise an error.
|
10
|
+
|
11
|
+
expect TypeError do
|
12
|
+
store << 1
|
13
|
+
end
|
14
|
+
|
15
|
+
We can get a list of profiles for a given tool.
|
16
|
+
|
17
|
+
profiles = store.profiles(:text)
|
18
|
+
|
19
|
+
profiles.assert == []
|
20
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Controller
|
2
|
+
|
3
|
+
When working with configurations, Confection wraps configurations in a
|
4
|
+
Controller instance.
|
5
|
+
|
6
|
+
An empty controller can be made simply enough.
|
7
|
+
|
8
|
+
Confection::Controller.new(self, *[])
|
9
|
+
|
10
|
+
The #confection method simple calls this with the configs it finds.
|
11
|
+
|
12
|
+
ctrl = config(:block)
|
13
|
+
|
14
|
+
Confection::Controller.assert === ctrl
|
15
|
+
|
16
|
+
A controller will most oftern encapsulate just a sigle configuration,
|
17
|
+
but it may contain more if the search parameters used to create it
|
18
|
+
returned more than one matching configuration. We can see how many
|
19
|
+
configurations the controller is harnessing using the `#size` method.
|
20
|
+
|
21
|
+
ctrl.size.assert == 1
|
22
|
+
|
23
|
+
With the controller, we can utilize the configuration(s) it encapsulates
|
24
|
+
through the handful of methods it provides for doing so. Probably the
|
25
|
+
most useful, which applies to Ruby-based configurations is the `#call`
|
26
|
+
methods. This will execute the configuration script in the context
|
27
|
+
in which it was defined.
|
28
|
+
|
29
|
+
result = ctrl.call
|
30
|
+
|
31
|
+
result.assert == "example block config"
|
32
|
+
|
33
|
+
Instead of being executed in the context in which a configuration was
|
34
|
+
defined, it can be executed in the current scope using the #exec method.
|
35
|
+
|
36
|
+
result = ctrl.exec
|
37
|
+
|
38
|
+
result.assert == "example block config"
|
39
|
+
|
40
|
+
Of course, as this example only outputs a string, we won't notice any
|
41
|
+
difference here.
|
42
|
+
|
43
|
+
Lastly, script based configurations can be executed in the TOPLEVEL
|
44
|
+
context by using `#main_exec`, or its alias `#load`.
|
45
|
+
|
46
|
+
result = ctrl.main_exec
|
47
|
+
|
48
|
+
result.assert == "example block config"
|
49
|
+
|
50
|
+
The controller can be converted to a Proc object, in which case the
|
51
|
+
underlying execution is equivalent to using `#exec`. This allows
|
52
|
+
the procedure to be evaluated in other bindings as needed.
|
53
|
+
|
54
|
+
proc = ctrl.to_proc
|
55
|
+
|
56
|
+
result = proc.call
|
57
|
+
|
58
|
+
result.assert == "example block config"
|
59
|
+
|
60
|
+
For text configuration, the controller will concat each configuration's
|
61
|
+
content.
|
62
|
+
|
63
|
+
ctrl = config(:text)
|
64
|
+
|
65
|
+
text = ctrl.to_s
|
66
|
+
|
67
|
+
text.strip.assert == 'example text config'
|
68
|
+
|
data/spec/05_config.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Config Classes
|
2
|
+
|
3
|
+
There are three classes of configuration: test, data and block. Block
|
4
|
+
configurations encapsulate a block of Ruby code. Text configurations
|
5
|
+
simply contain a text string --whatever that string may represent.
|
6
|
+
Data configuration contantain a table of key-value pairs.
|
7
|
+
|
8
|
+
## Block Configuration
|
9
|
+
|
10
|
+
File configuration come from separate files rather then from definitions
|
11
|
+
in a project's master configuration file.
|
12
|
+
|
13
|
+
config = confection(:block).first
|
14
|
+
|
15
|
+
Confection::Config.assert === config
|
16
|
+
|
17
|
+
A Ruby-based configuration file, like our example, can be called via the `#call`
|
18
|
+
method. This evaluates the code at the TOPLEVEL, like standard `Kernel.load`
|
19
|
+
would.
|
20
|
+
|
21
|
+
result = config.call
|
22
|
+
|
23
|
+
result.assert == "example block config"
|
24
|
+
|
25
|
+
The call can also be converted into a Proc object via `#to_proc`. This uses
|
26
|
+
`instance_eval` internally, so that the Proc object can be evaluated in
|
27
|
+
any context it may be needed.
|
28
|
+
|
29
|
+
proc = config.to_proc
|
30
|
+
|
31
|
+
Proc.assert === proc
|
32
|
+
|
33
|
+
result = proc.call
|
34
|
+
|
35
|
+
result.strip.assert == "example block config"
|
36
|
+
|
37
|
+
|
38
|
+
## Text Configuraiton
|
39
|
+
|
40
|
+
Text-based configurations
|
41
|
+
|
42
|
+
config = confection(:text).first
|
43
|
+
|
44
|
+
Confection::Config.assert === config
|
45
|
+
|
46
|
+
result = config.to_s
|
47
|
+
|
48
|
+
result.strip.assert == "example text config"
|
49
|
+
|
50
|
+
For a text-based configuration `#call` does the same thing as `#to_s`.
|
51
|
+
|
52
|
+
result = config.call
|
53
|
+
|
54
|
+
result.strip.assert == "example text config"
|
55
|
+
|
56
|
+
As with the other configuration classes, we may also convert this call
|
57
|
+
into a Proc instance.
|
58
|
+
|
59
|
+
proc = config.to_proc
|
60
|
+
|
61
|
+
Proc.assert === proc
|
62
|
+
|
63
|
+
The configuration object can be copied, with a special `#copy` method
|
64
|
+
that accepts option parameters for changing the tool or profile of the copy.
|
65
|
+
|
66
|
+
alt = config.copy(:profile=>:alt)
|
67
|
+
|
68
|
+
alt.profile.assert == :alt
|
69
|
+
|
70
|
+
|
71
|
+
## Data Configuration
|
72
|
+
|
73
|
+
Data-based configuration.
|
74
|
+
|
75
|
+
config = confection(:example, :data).first
|
76
|
+
|
77
|
+
Confection::Config.assert === config
|
78
|
+
|
79
|
+
result = config.to_h
|
80
|
+
|
81
|
+
result.assert == {:name=>'Tommy', :age=>42 }
|
82
|
+
|
83
|
+
For a Data-based configuration `#call` does the same thing as `#to_data`.
|
84
|
+
|
85
|
+
data = OpenStruct.new
|
86
|
+
|
87
|
+
result = config.call(data)
|
88
|
+
|
89
|
+
data.to_h.assert == {:name=>'Tommy', :age=>42 }
|
90
|
+
|
91
|
+
As with the other configuration classes, we may also convert this call
|
92
|
+
into a Proc instance.
|
93
|
+
|
94
|
+
proc = config.to_proc
|
95
|
+
|
96
|
+
Proc.assert === proc
|
97
|
+
|
98
|
+
This just encapsulates the `#to_data` call in a lambda.
|
99
|
+
|
data/spec/06_manage.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Manage
|
2
|
+
|
3
|
+
The Confection module has some convenice class methods for working
|
4
|
+
with the configuration of the *current* project --the one relative
|
5
|
+
to the current working directory.
|
6
|
+
|
7
|
+
The current project root directory can be had via the `current_directory`
|
8
|
+
method.
|
9
|
+
|
10
|
+
Confection.current_directory
|
11
|
+
|
12
|
+
The Project instance can be had via the `current_project` method.
|
13
|
+
|
14
|
+
project = Confection.current_project
|
15
|
+
|
16
|
+
Confection::Project.assert === project
|
17
|
+
|
18
|
+
The configuration properties of the current project can be
|
19
|
+
had via the `properties` method.
|
20
|
+
|
21
|
+
Confection.properties
|
22
|
+
|
23
|
+
The profile names can be looked up for any given tool via the `profiles`
|
24
|
+
method.
|
25
|
+
|
26
|
+
Confection.profiles(:file)
|
27
|
+
|
28
|
+
The number of configurations in the current project can be had via
|
29
|
+
the `size` method. (This is the number of configurations we have
|
30
|
+
defined in our test fixture.)
|
31
|
+
|
32
|
+
Confection.size.assert == 3
|
33
|
+
|
34
|
+
And we can loop through each configuration via the `each` method.
|
35
|
+
|
36
|
+
Confection.each{ |c| c }
|
37
|
+
|
38
|
+
|
data/spec/applique/ae.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ae'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: confection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-03-16 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: finder
|
16
|
-
requirement: &
|
16
|
+
requirement: &69827679742240 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *69827679742240
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: facets
|
27
|
-
requirement: &
|
27
|
+
requirement: &69827679741700 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *69827679741700
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: blankslate
|
38
|
-
requirement: &
|
38
|
+
requirement: &69827679741200 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *69827679741200
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: detroit
|
49
|
-
requirement: &
|
49
|
+
requirement: &69827679740700 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *69827679740700
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: qed
|
60
|
-
requirement: &
|
60
|
+
requirement: &69827679740200 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *69827679740200
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: ae
|
71
|
-
requirement: &
|
71
|
+
requirement: &69827679739700 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *69827679739700
|
80
80
|
description: Confection is a multi-tenant configuration system for Ruby projects.
|
81
81
|
email:
|
82
82
|
- transfire@gmail.com
|
@@ -92,11 +92,28 @@ files:
|
|
92
92
|
- lib/confection/basic_object.rb
|
93
93
|
- lib/confection/config.rb
|
94
94
|
- lib/confection/controller.rb
|
95
|
+
- lib/confection/core_ext.rb
|
96
|
+
- lib/confection/current.rb
|
95
97
|
- lib/confection/dsl.rb
|
98
|
+
- lib/confection/hash_builder.rb
|
99
|
+
- lib/confection/project.rb
|
100
|
+
- lib/confection/store.rb
|
96
101
|
- lib/confection.rb
|
102
|
+
- spec/00_concept.md
|
103
|
+
- spec/01_dsl.md
|
104
|
+
- spec/02_import.md
|
105
|
+
- spec/03_store.md
|
106
|
+
- spec/04_controller.md
|
107
|
+
- spec/05_config.md
|
108
|
+
- spec/06_manage.md
|
109
|
+
- spec/applique/ae.rb
|
110
|
+
- spec/applique/file.rb
|
111
|
+
- spec/applique/fixture/confile.rb
|
112
|
+
- spec/applique/fixture.rb
|
97
113
|
- LICENSE.txt
|
98
114
|
- HISTORY.md
|
99
115
|
- README.md
|
116
|
+
- Confile.rb
|
100
117
|
homepage: http://rubyworks.github.com/confection
|
101
118
|
licenses:
|
102
119
|
- BSD-2-Clause
|