caesars 0.4.2 → 0.5.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/CHANGES.txt +23 -2
- data/README.rdoc +8 -4
- data/bin/example +10 -5
- data/caesars.gemspec +2 -2
- data/lib/caesars.rb +156 -50
- metadata +2 -2
data/CHANGES.txt
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
CAESAR -- CHANGES
|
2
2
|
|
3
3
|
|
4
|
+
#### 0.5.1 (2009-03-11) ###############################
|
5
|
+
|
6
|
+
* FIX: Method-syntax was broken for attributes of top level method
|
7
|
+
* FIX: Caesars::Hash#refresh was setting @options to nil
|
8
|
+
* UPDATED: docs and bin/example to reflect Caesars::Hash changes.
|
9
|
+
* FIX: instance_variables in Ruby 1.9.1 returns Symbols
|
10
|
+
|
11
|
+
#### 0.5.0 (2009-03-11) ###############################
|
12
|
+
|
13
|
+
* FIX: find_deferred now gracefully handles nil errors
|
14
|
+
* NEW: empty? method in Caesars::Config
|
15
|
+
* NEW: post processing hook in Caesars::Config#refresh
|
16
|
+
* NEW: Caesars::Hash#to_hash now recursively casts children to ::Hash.
|
17
|
+
* FIX: Added Array support to Caesars::Hash
|
18
|
+
* NEW: Setters for Caesars attributes
|
19
|
+
* NEW: Caesars::Config supports multiple config files
|
20
|
+
* NEW: Top level methods used more than once now merges values
|
21
|
+
rather than overwrites.
|
22
|
+
* NEW: Caesars::Config supports reloading config files on the fly
|
23
|
+
|
24
|
+
|
4
25
|
#### 0.4.2 (2009-03-05) ###############################
|
5
26
|
|
6
27
|
* FIX: missing bin/party.conf in gem release
|
@@ -14,11 +35,11 @@ food :extra do; end; # => food_extra
|
|
14
35
|
|
15
36
|
* CHANGE: Removed bloody method. We now parse blocks immediately.
|
16
37
|
* CHANGE: Renamed virgin method to chill.
|
17
|
-
* NEW:
|
38
|
+
* NEW: Caesars::Config class for loading DSLs as config files.
|
18
39
|
See Example 3.
|
19
40
|
* NEW: Added find_deferred method to automatically jump up the
|
20
41
|
heirarchy when looking for a specific attribute.
|
21
|
-
* NEW: Added to_hash and [] methods to
|
42
|
+
* NEW: Added to_hash and [] methods to Caesars to make it
|
22
43
|
more hashlike.
|
23
44
|
* FIX: "chilled" attributes weren't available by method name
|
24
45
|
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Caesars - v0.
|
1
|
+
= Caesars - v0.5
|
2
2
|
|
3
3
|
A simple class for rapid DSL prototyping in Ruby.
|
4
4
|
|
@@ -123,13 +123,17 @@ Or for GitHub fans:
|
|
123
123
|
end
|
124
124
|
|
125
125
|
conffile = File.join(File.dirname(__FILE__), 'party.conf')
|
126
|
-
@config = PartyConfig.new(
|
126
|
+
@config = PartyConfig.new(conffile)
|
127
127
|
|
128
|
-
p @config.food.order.call # =>
|
128
|
+
p @config.food.order.call # => 10kg
|
129
129
|
p @config[:drink][:wine] # => 12L
|
130
130
|
p @config # => <PartyConfig:0x3f780c ...>
|
131
131
|
p @config.keys # => [:food, :drink]
|
132
|
-
|
132
|
+
|
133
|
+
# [... make changes to party.conf ...]
|
134
|
+
|
135
|
+
@config.refresh
|
136
|
+
|
133
137
|
== More Info
|
134
138
|
|
135
139
|
* GitHub[http://github.com/delano/caesar]
|
data/bin/example
CHANGED
@@ -108,23 +108,28 @@ p @staff_fte.splashdown.keys
|
|
108
108
|
# EXAMPLE 3 -- External Config file
|
109
109
|
#
|
110
110
|
|
111
|
-
|
111
|
+
require 'caesars'
|
112
|
+
|
113
|
+
class Food < Caesars
|
112
114
|
chill :order
|
113
115
|
end
|
114
|
-
class Drink < Caesars
|
116
|
+
class Drink < Caesars
|
115
117
|
end
|
116
118
|
|
117
|
-
class PartyConfig < Caesars::Config
|
119
|
+
class PartyConfig < Caesars::Config
|
118
120
|
dsl Food::DSL
|
119
121
|
dsl Drink::DSL
|
120
122
|
end
|
121
123
|
|
122
124
|
conffile = File.join(File.dirname(__FILE__), 'party.conf')
|
123
|
-
@config = PartyConfig.new(
|
125
|
+
@config = PartyConfig.new(conffile)
|
124
126
|
|
125
|
-
p @config.food.order.call # =>
|
127
|
+
p @config.food.order.call # => 10kg
|
126
128
|
p @config[:drink][:wine] # => 12L
|
127
129
|
p @config # => <PartyConfig:0x3f780c ...>
|
128
130
|
p @config.keys # => [:food, :drink]
|
129
131
|
|
132
|
+
# [... make changes to party.conf ...]
|
133
|
+
|
134
|
+
@config.refresh
|
130
135
|
|
data/caesars.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
2
|
s.name = %q{caesars}
|
3
|
-
s.version = "0.
|
4
|
-
s.date = %q{2009-03-
|
3
|
+
s.version = "0.5.1"
|
4
|
+
s.date = %q{2009-03-11}
|
5
5
|
s.specification_version = 1 if s.respond_to? :specification_version=
|
6
6
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
7
7
|
|
data/lib/caesars.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# See bin/example
|
8
8
|
#
|
9
9
|
class Caesars
|
10
|
-
VERSION = "0.
|
10
|
+
VERSION = "0.5.1"
|
11
11
|
# A subclass of ::Hash that provides method names for hash parameters.
|
12
12
|
# It's like a lightweight OpenStruct.
|
13
13
|
# ch = Caesars::Hash[:tabasco => :lots!]
|
@@ -17,13 +17,29 @@ class Caesars
|
|
17
17
|
def method_missing(meth)
|
18
18
|
self[meth] if self.has_key?(meth)
|
19
19
|
end
|
20
|
+
|
21
|
+
# Returns a clone of itself and all children cast as ::Hash objects
|
22
|
+
def to_hash(hash=self)
|
23
|
+
return hash unless hash.is_a?(Caesars::Hash) # nothing to do
|
24
|
+
target = ::Hash[dup]
|
25
|
+
hash.keys.each do |key|
|
26
|
+
if hash[key].is_a? Caesars::Hash
|
27
|
+
target[key] = hash[key].to_hash
|
28
|
+
next
|
29
|
+
elsif hash[key].is_a? Array
|
30
|
+
target[key] = hash[key].collect { |h| to_hash(h) }
|
31
|
+
next
|
32
|
+
end
|
33
|
+
target[key] = hash[key]
|
34
|
+
end
|
35
|
+
target
|
36
|
+
end
|
37
|
+
|
20
38
|
end
|
21
39
|
|
22
40
|
# An instance of Caesars::Hash which contains the data specified by your DSL
|
23
41
|
attr_accessor :caesars_properties
|
24
42
|
|
25
|
-
@@caesars_chilled = []
|
26
|
-
|
27
43
|
|
28
44
|
def initialize(name=nil)
|
29
45
|
@caesars_name = name if name
|
@@ -37,12 +53,13 @@ class Caesars
|
|
37
53
|
end
|
38
54
|
|
39
55
|
def to_hash
|
40
|
-
@caesars_properties
|
56
|
+
@caesars_properties.to_hash
|
41
57
|
end
|
42
58
|
|
43
59
|
# Look for an attribute, bubbling up to the parent if it's not found
|
44
60
|
# +criteria+ is an array of attribute names, orders according to their
|
45
|
-
# relationship.
|
61
|
+
# relationship. The last element is considered to the desired attribute.
|
62
|
+
# It can be an array.
|
46
63
|
#
|
47
64
|
# # Looking for 'attribute'.
|
48
65
|
# # First checks at @caesars_properties[grandparent][parent][attribute]
|
@@ -50,7 +67,7 @@ class Caesars
|
|
50
67
|
# # Finally, @caesars_properties[attribute]
|
51
68
|
# find_deferred('grandparent', 'parent', 'attribute')
|
52
69
|
#
|
53
|
-
# Returns the attribute if found or nil
|
70
|
+
# Returns the attribute if found or nil.
|
54
71
|
#
|
55
72
|
def find_deferred(*criteria)
|
56
73
|
# This is a nasty implementation. Sorry me! I'll enjoy a few
|
@@ -58,8 +75,7 @@ class Caesars
|
|
58
75
|
att = criteria.pop
|
59
76
|
val = nil
|
60
77
|
while !criteria.empty?
|
61
|
-
|
62
|
-
val = eval "@caesars_properties#{str} if defined?(@caesars_properties#{str})"
|
78
|
+
val = find(criteria, att)
|
63
79
|
break if val
|
64
80
|
criteria.pop
|
65
81
|
end
|
@@ -67,19 +83,43 @@ class Caesars
|
|
67
83
|
val = @caesars_properties[att.to_sym] if defined?(@caesars_properties[att.to_sym]) && !val
|
68
84
|
val
|
69
85
|
end
|
70
|
-
|
86
|
+
|
87
|
+
# Looks for the specific attribute specified.
|
88
|
+
# +criteria+ is an array of attribute names, orders according to their
|
89
|
+
# relationship. The last element is considered to the desired attribute.
|
90
|
+
# It can be an array.
|
91
|
+
#
|
92
|
+
# Unlike find_deferred, it will return only the value specified, otherwise nil.
|
93
|
+
def find(*criteria)
|
94
|
+
criteria.flatten! if criteria.first.is_a?(Array)
|
95
|
+
str = criteria.collect { |v| "[:'#{v}']" if v }.join
|
96
|
+
val = eval "@caesars_properties#{str} if defined?(@caesars_properties#{str})"
|
97
|
+
val
|
98
|
+
end
|
99
|
+
|
71
100
|
# Act a bit like a hash for the case:
|
72
101
|
# @subclass[:property]
|
73
102
|
def [](name)
|
74
103
|
return @caesars_properties[name] if @caesars_properties.has_key?(name)
|
75
104
|
return @caesars_properties[name.to_sym] if @caesars_properties.has_key?(name.to_sym)
|
76
105
|
end
|
77
|
-
|
106
|
+
|
107
|
+
# Act a bit like a hash for the case:
|
108
|
+
# @subclass[:property] = value
|
109
|
+
def []=(name, value)
|
110
|
+
@caesars_properties[name] = value
|
111
|
+
end
|
112
|
+
|
78
113
|
# This method handles all of the attributes that do not contain blocks.
|
79
114
|
# It's used in the DSL for handling attributes dyanamically (that weren't defined
|
80
115
|
# previously) and also in subclasses of Caesar for returning the appropriate
|
81
116
|
# attribute values.
|
82
117
|
def method_missing(meth, *args, &b)
|
118
|
+
# Handle the setter, attribute=
|
119
|
+
if meth.to_s =~ /=$/ && @caesars_properties.has_key?(meth.to_s.chop.to_sym)
|
120
|
+
return @caesars_properties[meth.to_s.chop.to_sym] = (args.size == 1) ? args.first : args
|
121
|
+
end
|
122
|
+
|
83
123
|
return @caesars_properties[meth] if @caesars_properties.has_key?(meth) && args.empty? && b.nil?
|
84
124
|
return nil if args.empty? && b.nil?
|
85
125
|
|
@@ -88,13 +128,15 @@ class Caesars
|
|
88
128
|
args << meth if args.empty?
|
89
129
|
args.each do |name|
|
90
130
|
prev = @caesars_pointer
|
131
|
+
#(@caesars_pointer[:"#{meth}_values"] ||= []) << name
|
91
132
|
@caesars_pointer[name] ||= Caesars::Hash.new
|
92
133
|
@caesars_pointer = @caesars_pointer[name]
|
93
134
|
b.call if b
|
94
135
|
@caesars_pointer = prev
|
95
136
|
end
|
96
137
|
|
97
|
-
elsif @caesars_pointer[meth]
|
138
|
+
elsif @caesars_pointer.kind_of?(Hash) && @caesars_pointer[meth]
|
139
|
+
|
98
140
|
@caesars_pointer[meth] = [@caesars_pointer[meth]] unless @caesars_pointer[meth].is_a?(Array)
|
99
141
|
@caesars_pointer[meth] += args
|
100
142
|
elsif !args.empty?
|
@@ -102,25 +144,43 @@ class Caesars
|
|
102
144
|
end
|
103
145
|
|
104
146
|
end
|
105
|
-
|
106
|
-
|
147
|
+
|
148
|
+
# A class method which can be used by subclasses to specify which methods
|
149
|
+
# should delay execution of their blocks. Here's an example:
|
150
|
+
#
|
151
|
+
# class Food < Caesars
|
152
|
+
# chill :count
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# food do
|
156
|
+
# taste :delicious
|
157
|
+
# count do |items|
|
158
|
+
# puts items + 2
|
159
|
+
# end
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# @config.food.order.call(3) # => 5
|
163
|
+
#
|
164
|
+
def self.chill(caesars_meth)
|
107
165
|
module_eval %Q{
|
108
|
-
def #{
|
109
|
-
#
|
110
|
-
return @
|
111
|
-
|
166
|
+
def #{caesars_meth}(*caesars_names,&b)
|
167
|
+
# caesars.toplevel.unnamed_chilled_attribute
|
168
|
+
return @caesars_properties[:'#{caesars_meth}'] if @caesars_properties.has_key?(:'#{caesars_meth}') && caesars_names.empty? && b.nil?
|
169
|
+
|
112
170
|
# Use the name of the bloody method if no name is supplied.
|
113
|
-
|
114
|
-
|
115
|
-
|
171
|
+
caesars_names << :'#{caesars_meth}' if caesars_names.empty?
|
172
|
+
|
173
|
+
caesars_names.each do |name|
|
116
174
|
@caesars_pointer[name] = b
|
117
175
|
end
|
118
176
|
|
119
|
-
@caesars_pointer[:'#{
|
177
|
+
@caesars_pointer[:'#{caesars_meth}']
|
120
178
|
end
|
121
179
|
}
|
122
180
|
nil
|
123
181
|
end
|
182
|
+
|
183
|
+
|
124
184
|
# Executes automatically when Caesars is subclassed. This creates the
|
125
185
|
# YourClass::DSL module which contains a single method named after YourClass
|
126
186
|
# that is used to catch the top level DSL method.
|
@@ -129,7 +189,7 @@ class Caesars
|
|
129
189
|
# would be: highball.
|
130
190
|
#
|
131
191
|
# highball :mine do
|
132
|
-
# volume
|
192
|
+
# volume "9oz"
|
133
193
|
# end
|
134
194
|
#
|
135
195
|
def self.inherited(modname)
|
@@ -140,16 +200,16 @@ class Caesars
|
|
140
200
|
name = !args.empty? ? args.first.to_s : nil
|
141
201
|
varname = "@#{meth.to_s}"
|
142
202
|
varname << "_\#{name}" if name
|
203
|
+
inst = instance_variable_get(varname)
|
143
204
|
|
144
205
|
# When the top level DSL method is called without a block
|
145
206
|
# it will return the appropriate instance variable name
|
146
|
-
if b.nil?
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
i
|
207
|
+
return inst if b.nil?
|
208
|
+
|
209
|
+
# Add to existing instance, if it exists. Otherwise create one anew.
|
210
|
+
inst = instance_variable_set(varname, inst || #{modname.to_s}.new(name))
|
211
|
+
inst.instance_eval(&b)
|
212
|
+
inst
|
153
213
|
end
|
154
214
|
|
155
215
|
def self.methname
|
@@ -175,56 +235,102 @@ end
|
|
175
235
|
# p @config.staff # => <Staff:0x7ea450 ... >
|
176
236
|
#
|
177
237
|
class Caesars::Config
|
178
|
-
attr_accessor :
|
238
|
+
attr_accessor :paths
|
239
|
+
attr_accessor :options
|
179
240
|
attr_accessor :verbose
|
180
241
|
|
181
242
|
@@glasses = []
|
182
243
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
244
|
+
# +args+ is a last of config file paths to load into this instance.
|
245
|
+
# If the last argument is a hash, it's assumed to be a list of
|
246
|
+
# options. The available options are:
|
247
|
+
#
|
248
|
+
# <li>:verbose => true or false</li>
|
249
|
+
#
|
250
|
+
def initialize(*args)
|
251
|
+
# We store the options hash b/c we reapply them when we refresh.
|
252
|
+
@options = args.last.is_a?(Hash) ? args.pop : {}
|
253
|
+
@paths = args.empty? ? [] : args
|
254
|
+
@options = {}
|
187
255
|
|
188
256
|
refresh
|
189
257
|
end
|
190
|
-
|
191
|
-
def self.dsl(glass)
|
192
|
-
@@glasses << glass
|
193
|
-
end
|
194
258
|
|
195
|
-
def
|
196
|
-
|
197
|
-
|
259
|
+
def init
|
260
|
+
# Remove instance variables used to populate DSL data
|
261
|
+
instance_variables.each do |varname|
|
262
|
+
next if varname == :'@options' || varname == :'@paths' # Ruby 1.9.1
|
263
|
+
next if varname == '@options' || varname == '@paths' # Ruby 1.8
|
264
|
+
instance_variable_set(varname, nil)
|
265
|
+
end
|
198
266
|
|
199
|
-
|
200
|
-
|
267
|
+
# Re-apply options
|
268
|
+
@options.each_pair do |n,v|
|
269
|
+
self.send("#{n}=", v) if respond_to?("#{n}=")
|
270
|
+
end
|
271
|
+
|
272
|
+
check_paths # make sure files exist
|
273
|
+
end
|
274
|
+
|
275
|
+
# This method is a stub. It gets called by refresh after the
|
276
|
+
# config file has be loaded. You can use it to do some post
|
277
|
+
# processing on the configuration before it's used elsewhere.
|
278
|
+
def postprocess
|
201
279
|
end
|
202
280
|
|
203
281
|
def refresh
|
282
|
+
init
|
204
283
|
|
205
|
-
|
206
|
-
puts "Loading config from #{
|
284
|
+
@paths.each do |path|
|
285
|
+
puts "Loading config from #{path}" if @verbose
|
207
286
|
|
208
287
|
begin
|
209
288
|
@@glasses.each { |glass| extend glass }
|
210
|
-
dsl = File.read
|
289
|
+
dsl = File.read path
|
211
290
|
|
212
291
|
# We're using eval so the DSL code can be executed in this
|
213
292
|
# namespace.
|
214
293
|
eval %Q{
|
215
294
|
#{dsl}
|
216
|
-
}
|
295
|
+
}, binding, __FILE__, __LINE__
|
296
|
+
|
297
|
+
postprocess
|
217
298
|
|
218
299
|
rescue SyntaxError => ex
|
219
|
-
puts "Syntax error in #{
|
300
|
+
puts "Syntax error in #{path}."
|
301
|
+
puts ex.message
|
220
302
|
exit 1
|
221
303
|
end
|
222
304
|
end
|
223
305
|
end
|
224
306
|
|
225
|
-
def
|
226
|
-
|
307
|
+
def check_paths
|
308
|
+
@paths.each do |path|
|
309
|
+
raise "You provided a nil value" unless path
|
310
|
+
raise "Config file #{path} does not exist!" unless File.exists?(path)
|
311
|
+
end
|
227
312
|
end
|
313
|
+
|
314
|
+
|
315
|
+
def empty?
|
316
|
+
keys.each do |obj|
|
317
|
+
return false if self.respond_to?(obj.to_sym)
|
318
|
+
end
|
319
|
+
true
|
320
|
+
end
|
321
|
+
|
322
|
+
def self.dsl(glass)
|
323
|
+
@@glasses << glass
|
324
|
+
end
|
325
|
+
|
326
|
+
def [](name)
|
327
|
+
self.send(name) if respond_to?(name)
|
328
|
+
end
|
329
|
+
|
330
|
+
def keys
|
331
|
+
@@glasses.collect { |glass| glass.methname }
|
332
|
+
end
|
333
|
+
|
228
334
|
end
|
229
335
|
|
230
336
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caesars
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-11 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|