caesars 0.5.6 → 0.6.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/CHANGES.txt +9 -2
- data/README.rdoc +4 -4
- data/caesars.gemspec +8 -8
- data/lib/caesars.rb +147 -44
- metadata +5 -6
data/CHANGES.txt
CHANGED
@@ -1,12 +1,19 @@
|
|
1
|
-
|
1
|
+
CAESARS -- CHANGES
|
2
2
|
|
3
3
|
|
4
|
+
#### 0.6.0 (2009-04-30) ###############################
|
5
|
+
|
6
|
+
* ADDED: Forced reloading for Caesars::Config.refresh. This allows
|
7
|
+
one DSL to affect the parsing of another.
|
8
|
+
* ADDED: Caesars.forced_ignore
|
9
|
+
* ADDED: Better docs for Caesars::Config
|
10
|
+
* ADDED: Ceasars::Config.has_key?
|
11
|
+
|
4
12
|
|
5
13
|
#### 0.5.6 (2009-04-28) ###############################
|
6
14
|
|
7
15
|
* FIXED: Bug in fixed_hash which wasn't forcing the hash man!
|
8
16
|
|
9
|
-
|
10
17
|
#### 0.5.5 (2009-04-27) ###############################
|
11
18
|
|
12
19
|
* CHANGE: Caesars.chill and Caesars.forced_hash can now be used together.
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Caesars - v0.
|
1
|
+
= Caesars - v0.6
|
2
2
|
|
3
3
|
A simple class for rapid DSL prototyping in Ruby.
|
4
4
|
|
@@ -15,7 +15,7 @@ Or for GitHub fans:
|
|
15
15
|
* git clone git://github.com/delano/caesar.git
|
16
16
|
|
17
17
|
|
18
|
-
== EXAMPLE 1 --
|
18
|
+
== EXAMPLE 1 -- A Simple DSL
|
19
19
|
|
20
20
|
require 'caesars'
|
21
21
|
|
@@ -38,7 +38,7 @@ Or for GitHub fans:
|
|
38
38
|
|
39
39
|
|
40
40
|
|
41
|
-
== EXAMPLE 2 --
|
41
|
+
== EXAMPLE 2 -- Storing Blocks as Procs
|
42
42
|
|
43
43
|
require 'caesars'
|
44
44
|
|
@@ -111,7 +111,7 @@ Or for GitHub fans:
|
|
111
111
|
p @staff_fte.splashdown.sheila.salary.call(rand(100)) # => 549.77
|
112
112
|
|
113
113
|
|
114
|
-
== EXAMPLE 3 -- External Config
|
114
|
+
== EXAMPLE 3 -- External Config Files
|
115
115
|
|
116
116
|
require 'caesars'
|
117
117
|
|
data/caesars.gemspec
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
|
-
s.name =
|
3
|
-
s.
|
4
|
-
s.
|
2
|
+
s.name = "caesars"
|
3
|
+
s.rubyforge_project = "caesars"
|
4
|
+
s.version = "0.6.0"
|
5
|
+
s.date = "2009-04-30"
|
5
6
|
s.specification_version = 1 if s.respond_to? :specification_version=
|
6
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
7
8
|
|
8
9
|
s.authors = ["Delano Mandelbaum"]
|
9
|
-
s.description = %q{
|
10
|
-
s.summary = %q{Caesars:
|
10
|
+
s.description = %q{Rapid DSL prototyping in Ruby.}
|
11
|
+
s.summary = %q{Caesars: Rapid DSL prototyping in Ruby.}
|
11
12
|
s.email = %q{delano@solutious.com}
|
12
13
|
|
13
14
|
# = MANIFEST =
|
@@ -29,8 +30,7 @@
|
|
29
30
|
s.has_rdoc = true
|
30
31
|
s.homepage = %q{http://github.com/delano/caesar}
|
31
32
|
s.extra_rdoc_files = %w[README.rdoc LICENSE.txt CHANGES.txt]
|
32
|
-
s.rdoc_options = ["--line-numbers", "--
|
33
|
+
s.rdoc_options = ["--line-numbers", "--title", "Caesars: Rapid DSL prototyping in Ruby.", "--main", "README.rdoc"]
|
33
34
|
s.require_paths = ["lib"]
|
34
|
-
s.rubygems_version =
|
35
|
-
s.rubyforge_project = "caesars"
|
35
|
+
s.rubygems_version = "1.1.1"
|
36
36
|
end
|
data/lib/caesars.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
# Caesars --
|
2
|
+
# Caesars -- Rapid DSL prototyping in Ruby.
|
3
3
|
#
|
4
4
|
# Subclass Caesars and start drinking! I mean, start prototyping
|
5
5
|
# your own domain specific language!
|
@@ -7,10 +7,11 @@
|
|
7
7
|
# See bin/example
|
8
8
|
#
|
9
9
|
class Caesars
|
10
|
-
VERSION = 0.
|
10
|
+
VERSION = 0.6
|
11
11
|
@@debug = false
|
12
12
|
@@chilled = {}
|
13
13
|
@@forced_array = {}
|
14
|
+
@@forced_ignore = {}
|
14
15
|
|
15
16
|
def Caesars.enable_debug; @@debug = true; end
|
16
17
|
def Caesars.disable_debug; @@debug = false; end
|
@@ -27,7 +28,12 @@ class Caesars
|
|
27
28
|
return false unless name
|
28
29
|
@@forced_array.has_key?(name.to_sym)
|
29
30
|
end
|
30
|
-
|
31
|
+
# Is the given +name+ a forced ignore?
|
32
|
+
# See Caesars.forced_ignore
|
33
|
+
def Caesars.forced_ignore?(name)
|
34
|
+
return false unless name
|
35
|
+
@@forced_ignore.has_key?(name.to_sym)
|
36
|
+
end
|
31
37
|
|
32
38
|
# A subclass of ::Hash that provides method names for hash parameters.
|
33
39
|
# It's like a lightweight OpenStruct.
|
@@ -70,6 +76,7 @@ class Caesars
|
|
70
76
|
@caesars_name = name if name
|
71
77
|
@caesars_properties = Caesars::Hash.new
|
72
78
|
@caesars_pointer = @caesars_properties
|
79
|
+
init if respond_to?(:init)
|
73
80
|
end
|
74
81
|
|
75
82
|
# Returns an array of the available
|
@@ -231,6 +238,10 @@ class Caesars
|
|
231
238
|
# previously) and also in subclasses of Caesars for returning the appropriate
|
232
239
|
# attribute values.
|
233
240
|
def method_missing(meth, *args, &b)
|
241
|
+
if Caesars.forced_ignore?(meth)
|
242
|
+
STDERR.puts "Forced ignore: #{meth}" if Caesars.debug?
|
243
|
+
return
|
244
|
+
end
|
234
245
|
|
235
246
|
# Handle the setter, attribute=
|
236
247
|
if meth.to_s =~ /=$/ && @caesars_properties.has_key?(meth.to_s.chop.to_sym)
|
@@ -314,7 +325,11 @@ class Caesars
|
|
314
325
|
module_eval %Q{
|
315
326
|
def #{caesars_meth}(*caesars_names,&b)
|
316
327
|
this_meth = :'#{caesars_meth}'
|
317
|
-
|
328
|
+
if Caesars.forced_ignore?(this_meth)
|
329
|
+
STDERR.puts "Forced ignore: \#{this_meth}" if Caesars.debug?
|
330
|
+
return
|
331
|
+
end
|
332
|
+
|
318
333
|
if @caesars_properties.has_key?(this_meth) && caesars_names.empty? && b.nil?
|
319
334
|
return @caesars_properties[this_meth]
|
320
335
|
end
|
@@ -362,7 +377,7 @@ class Caesars
|
|
362
377
|
# end
|
363
378
|
# end
|
364
379
|
#
|
365
|
-
# @
|
380
|
+
# @food.count.call(3) # => 5
|
366
381
|
#
|
367
382
|
def self.chill(caesars_meth)
|
368
383
|
@@chilled[caesars_meth.to_sym] = true
|
@@ -378,15 +393,37 @@ class Caesars
|
|
378
393
|
#
|
379
394
|
# food do
|
380
395
|
# taste :delicious
|
381
|
-
# sauce
|
396
|
+
# sauce :tabasco, :worcester
|
397
|
+
# sauce :franks
|
382
398
|
# end
|
383
399
|
#
|
384
|
-
# @
|
400
|
+
# @food.sauce # => [[:tabasco, :worcester], [:franks]]
|
401
|
+
#
|
385
402
|
def self.forced_array(caesars_meth)
|
403
|
+
STDERR.puts "forced_array: #{caesars_meth}" if Caesars.debug?
|
386
404
|
@@forced_array[caesars_meth.to_sym] = true
|
387
405
|
nil
|
388
406
|
end
|
389
407
|
|
408
|
+
# Specify a method that should always be ignored.
|
409
|
+
# Here's an example:
|
410
|
+
#
|
411
|
+
# class Food < Caesars
|
412
|
+
# forced_ignore :taste
|
413
|
+
# end
|
414
|
+
#
|
415
|
+
# food do
|
416
|
+
# taste :delicious
|
417
|
+
# end
|
418
|
+
#
|
419
|
+
# @food.taste # => nil
|
420
|
+
#
|
421
|
+
def self.forced_ignore(caesars_meth)
|
422
|
+
STDERR.puts "forced_ignore: #{caesars_meth}" if Caesars.debug?
|
423
|
+
@@forced_ignore[caesars_meth.to_sym] = true
|
424
|
+
nil
|
425
|
+
end
|
426
|
+
|
390
427
|
# Executes automatically when Caesars is subclassed. This creates the
|
391
428
|
# YourClass::DSL module which contains a single method named after YourClass
|
392
429
|
# that is used to catch the top level DSL method.
|
@@ -399,7 +436,8 @@ class Caesars
|
|
399
436
|
# end
|
400
437
|
#
|
401
438
|
def self.inherited(modname)
|
402
|
-
|
439
|
+
# NOTE: We may be able to replace this without an eval using Module.nesting
|
440
|
+
meth = (modname.to_s.split(/::/))[-1].downcase # Some::HighBall => highball
|
403
441
|
module_eval %Q{
|
404
442
|
module #{modname}::DSL
|
405
443
|
def #{meth}(*args, &b)
|
@@ -448,6 +486,16 @@ class Caesars::Config
|
|
448
486
|
|
449
487
|
@@glasses = []
|
450
488
|
|
489
|
+
# Used by postprocess to tell refresh to reload all configs.
|
490
|
+
class ForceRefresh < RuntimeError
|
491
|
+
# The list of config types that need to be refreshed. This is currently
|
492
|
+
# for informational-purposes only. It does not affect which files/config
|
493
|
+
# types are refreshed. See Caesars::Config::ForcedRefresh
|
494
|
+
attr_reader :glasses
|
495
|
+
def initialize(*glasses); @glasses = glasses; end
|
496
|
+
def message; "Force refresh of: #{@glasses.join(',')}"; end
|
497
|
+
end
|
498
|
+
|
451
499
|
# +args+ is a last of config file paths to load into this instance.
|
452
500
|
# If the last argument is a hash, it's assumed to be a list of
|
453
501
|
# options. The available options are:
|
@@ -459,62 +507,80 @@ class Caesars::Config
|
|
459
507
|
@options = args.last.is_a?(Hash) ? args.pop : {}
|
460
508
|
@paths = args.empty? ? [] : args
|
461
509
|
@options = {}
|
462
|
-
|
510
|
+
@forced_refreshes = 0
|
463
511
|
refresh
|
464
512
|
end
|
465
513
|
|
514
|
+
# Reset all config instance variables to nil.
|
466
515
|
def caesars_init
|
467
516
|
# Remove instance variables used to populate DSL data
|
468
|
-
|
469
|
-
next if varname == :'@options' || varname == :'@paths' # Ruby 1.9.1
|
470
|
-
next if varname == '@options' || varname == '@paths' # Ruby 1.8
|
471
|
-
instance_variable_set(varname, nil)
|
472
|
-
end
|
473
|
-
|
517
|
+
keys.each { |confname| instance_variable_set("@#{confname}", nil) }
|
474
518
|
# Re-apply options
|
475
519
|
@options.each_pair do |n,v|
|
476
520
|
self.send("#{n}=", v) if respond_to?("#{n}=")
|
477
521
|
end
|
478
|
-
|
479
522
|
check_paths # make sure files exist
|
480
523
|
end
|
481
524
|
|
482
|
-
# This method is a stub. It gets called by refresh after
|
483
|
-
# config file has be loaded. You can use it to
|
525
|
+
# This method is a stub. It gets called by refresh after each
|
526
|
+
# config file has be loaded. You can use it to run file specific
|
484
527
|
# processing on the configuration before it's used elsewhere.
|
485
528
|
def postprocess
|
486
529
|
end
|
487
530
|
|
531
|
+
# Clear all current configuration (sets all config instance
|
532
|
+
# variables to nil) and reload all config files in +@paths+.
|
533
|
+
# After each path is loaded, Caesars::Config.postprocess is
|
534
|
+
# called. If a ForceRefresh exception is raise, refresh is
|
535
|
+
# run again from the start. This is useful in the case
|
536
|
+
# where one DSL can affect the parsing of another. Note that
|
537
|
+
# refresh only clears the instance variables, the class vars
|
538
|
+
# for each of the DSLs are not affected so all calls to
|
539
|
+
# +forced_array+, +forced_hash+, +chill+ and +forced_ignore+
|
540
|
+
# are unaffected.
|
541
|
+
#
|
542
|
+
# Rudy has an example of forced refreshing in action. See
|
543
|
+
# the files (http://github.com/solutious/rudy):
|
544
|
+
#
|
545
|
+
# * +lib/rudy/config.rb+
|
546
|
+
# * +lib/rudy/config/objects.rb+.
|
547
|
+
#
|
488
548
|
def refresh
|
489
|
-
caesars_init
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
@@glasses.each { |glass| extend glass }
|
549
|
+
caesars_init # Delete all current configuration
|
550
|
+
@@glasses.each { |glass| extend glass }
|
551
|
+
|
552
|
+
begin
|
553
|
+
@paths.each do |path|
|
554
|
+
puts "Loading config from #{path}" if @verbose || Caesars.debug?
|
496
555
|
dsl = File.read path
|
497
|
-
|
498
|
-
|
499
|
-
#
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
STDERR.puts ex.backtrace if Caesars.debug?
|
509
|
-
rescue ArgumentError, SyntaxError => ex
|
510
|
-
STDERR.puts "Syntax error in #{path}."
|
511
|
-
STDERR.puts ex.message
|
512
|
-
STDERR.puts ex.backtrace if Caesars.debug?
|
513
|
-
exit 1
|
556
|
+
# eval so the DSL code can be executed in this namespace.
|
557
|
+
eval dsl, binding, __FILE__, __LINE__
|
558
|
+
# Execute Caesars::Config.postprocesses every time a file is loaded
|
559
|
+
postprocess # Can raise ForceRefresh
|
560
|
+
end
|
561
|
+
|
562
|
+
rescue Caesars::Config::ForceRefresh => ex
|
563
|
+
@forced_refreshes += 1
|
564
|
+
if @forced_refreshes > 3
|
565
|
+
STDERR.puts "Too many forced refreshed (#{@forced_refreshes})"
|
566
|
+
exit 9
|
514
567
|
end
|
568
|
+
STDERR.puts ex.message if Caesars.debug?
|
569
|
+
refresh
|
570
|
+
|
571
|
+
rescue Caesars::Error => ex
|
572
|
+
STDERR.puts ex.message
|
573
|
+
STDERR.puts ex.backtrace if Caesars.debug?
|
574
|
+
rescue ArgumentError, SyntaxError => ex
|
575
|
+
STDERR.puts "Syntax error in #{path}."
|
576
|
+
STDERR.puts ex.message
|
577
|
+
STDERR.puts ex.backtrace if Caesars.debug?
|
578
|
+
exit 1
|
515
579
|
end
|
516
580
|
end
|
517
581
|
|
582
|
+
# Checks all values of +@paths+, raises an exception for nil
|
583
|
+
# values and file paths that don't exist.
|
518
584
|
def check_paths
|
519
585
|
@paths.each do |path|
|
520
586
|
raise "You provided a nil value" unless path
|
@@ -522,7 +588,7 @@ class Caesars::Config
|
|
522
588
|
end
|
523
589
|
end
|
524
590
|
|
525
|
-
|
591
|
+
# Do any of the known DSLs have config data?
|
526
592
|
def empty?
|
527
593
|
keys.each do |obj|
|
528
594
|
return false if self.respond_to?(obj.to_sym)
|
@@ -530,18 +596,55 @@ class Caesars::Config
|
|
530
596
|
true
|
531
597
|
end
|
532
598
|
|
599
|
+
# Specify a DSL class (+glass+) to include in this config.
|
600
|
+
#
|
601
|
+
# class CoolDrink < Caesars::Config
|
602
|
+
# dsl CoolDrink::Flavours::DSL
|
603
|
+
# end
|
604
|
+
#
|
533
605
|
def self.dsl(glass)
|
534
606
|
@@glasses << glass
|
535
607
|
end
|
536
608
|
|
609
|
+
# Provide a hash-like interface for Config classes.
|
610
|
+
# +name+ is the name of a DSL config.
|
611
|
+
#
|
612
|
+
# class CoolDrink < Caesars::Config
|
613
|
+
# dsl CoolDrink::Flavours::DSL
|
614
|
+
# end
|
615
|
+
#
|
616
|
+
# cd = CoolDrink.new('/path/2/config')
|
617
|
+
# cd[:flavours] # => {}
|
618
|
+
#
|
537
619
|
def [](name)
|
538
620
|
self.send(name) if respond_to?(name)
|
539
621
|
end
|
540
|
-
|
622
|
+
|
623
|
+
# Returns the list of known DSL config names.
|
624
|
+
# class CoolDrink < Caesars::Config
|
625
|
+
# dsl CoolDrink::Flavours::DSL
|
626
|
+
# end
|
627
|
+
#
|
628
|
+
# cd = CoolDrink.new('/path/2/config')
|
629
|
+
# cd.keys # => [:flavours]
|
630
|
+
#
|
541
631
|
def keys
|
542
632
|
@@glasses.collect { |glass| glass.methname }
|
543
633
|
end
|
544
634
|
|
635
|
+
# Is +name+ a known configuration type?
|
636
|
+
#
|
637
|
+
# class CoolDrink < Caesars::Config
|
638
|
+
# dsl CoolDrink::Flavours::DSL
|
639
|
+
# end
|
640
|
+
#
|
641
|
+
# cd = CoolDrink.new('/path/2/config')
|
642
|
+
# cd.has_key?(:taste) # => false
|
643
|
+
# cd.has_key?(:flavours) # => true
|
644
|
+
#
|
645
|
+
def has_key?(name)
|
646
|
+
respond_to?(name)
|
647
|
+
end
|
545
648
|
end
|
546
649
|
|
547
650
|
|
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.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-30 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: Rapid DSL prototyping in Ruby.
|
17
17
|
email: delano@solutious.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -40,9 +40,8 @@ licenses: []
|
|
40
40
|
post_install_message:
|
41
41
|
rdoc_options:
|
42
42
|
- --line-numbers
|
43
|
-
- --inline-source
|
44
43
|
- --title
|
45
|
-
- "Caesars:
|
44
|
+
- "Caesars: Rapid DSL prototyping in Ruby."
|
46
45
|
- --main
|
47
46
|
- README.rdoc
|
48
47
|
require_paths:
|
@@ -65,6 +64,6 @@ rubyforge_project: caesars
|
|
65
64
|
rubygems_version: 1.3.2
|
66
65
|
signing_key:
|
67
66
|
specification_version: 1
|
68
|
-
summary: "Caesars:
|
67
|
+
summary: "Caesars: Rapid DSL prototyping in Ruby."
|
69
68
|
test_files: []
|
70
69
|
|