caesars 0.5.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|