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.
Files changed (5) hide show
  1. data/CHANGES.txt +9 -2
  2. data/README.rdoc +4 -4
  3. data/caesars.gemspec +8 -8
  4. data/lib/caesars.rb +147 -44
  5. metadata +5 -6
data/CHANGES.txt CHANGED
@@ -1,12 +1,19 @@
1
- CAESAR -- CHANGES
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.5
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 -- Flavour
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 -- Staff
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 file
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 = %q{caesars}
3
- s.version = "0.5.6"
4
- s.date = %q{2009-04-28}
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{A simple class for rapid DSL prototyping in Ruby.}
10
- s.summary = %q{Caesars: A simple class for rapid DSL prototyping in Ruby.}
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", "--inline-source", "--title", "Caesars: A simple class for rapid DSL prototyping in Ruby.", "--main", "README.rdoc"]
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 = %q{1.1.1}
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 -- A simple class for rapid DSL prototyping.
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.5
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
- # @config.food.count.call(3) # => 5
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
- # @config.food.count.call(3) # => 5
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
- meth = (modname.to_s.split(/::/))[-1].downcase # Some::ClassName => classname
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
- instance_variables.each do |varname|
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 the
483
- # config file has be loaded. You can use it to do some post
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
- @paths.each do |path|
492
- puts "Loading config from #{path}" if @verbose
493
-
494
- begin
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
- # We're using eval so the DSL code can be executed in this
499
- # namespace.
500
- eval %Q{
501
- #{dsl}
502
- }, binding, __FILE__, __LINE__
503
-
504
- postprocess
505
-
506
- rescue Caesars::Error => ex
507
- STDERR.puts ex.message
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.5.6
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-28 00:00:00 -04:00
12
+ date: 2009-04-30 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description: A simple class for rapid DSL prototyping in Ruby.
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: A simple class for rapid DSL prototyping in Ruby."
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: A simple class for rapid DSL prototyping in Ruby."
67
+ summary: "Caesars: Rapid DSL prototyping in Ruby."
69
68
  test_files: []
70
69