caesars 0.3.2

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 ADDED
@@ -0,0 +1,25 @@
1
+ CAESAR -- CHANGES
2
+
3
+
4
+ #### 0.3.2 (2009-03-04) ###############################
5
+
6
+ * FIX: Added file and line info for eval code (better debugging).
7
+ * CHANGE: The top level DSL method names are now determined by
8
+ by the class name. Some::ClassName becomes classname.
9
+ This is less confusing than allowing it to be anything
10
+ and makes it possible to use several DSLs in the same
11
+ namespace.
12
+
13
+
14
+ #### 0.3.1 (2009-03-04) ###############################
15
+
16
+ * NEW: Accept instances without a name
17
+ * CHANGE: Updated examples.
18
+ * NEW: More rdocs.
19
+
20
+
21
+
22
+ #### 0.3 (2009-03-04) ###############################
23
+
24
+ Initial public release
25
+
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Delano Mandelbaum
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,139 @@
1
+ = Caesars - v0.3
2
+
3
+ A simple class for rapid DSL prototyping in Ruby.
4
+
5
+ NOTE: Currently runs only with Ruby 1.9+!
6
+
7
+ == Installation
8
+
9
+ One of:
10
+
11
+ * gem install caesars
12
+ * copy lib/caesars.rb into your lib directory.
13
+
14
+ Or for GitHub fans:
15
+
16
+ * git clone git://github.com/delano/caesar.git
17
+ * gem sources -a http://gems.github.com (you only have to do this once, ever...), then:
18
+ * gem install delano-caesar
19
+
20
+
21
+ == Usage
22
+
23
+
24
+ # ------------------------------------------------------------------
25
+ # EXAMPLE 1 -- Flavour
26
+ #
27
+
28
+ class Flavour < Caesars # Subclass Caesars.
29
+ end
30
+
31
+ extend Flavour::DSL # Bring the DSL into the current namespace.
32
+ # This module is created dynamically based
33
+ # on the name of the subclass.
34
+
35
+ flavour do # Start drinking! I mean, start writing your
36
+ spicy true # domain specific language!
37
+ clamy true # Use any attribute name you want.
38
+ salty true
39
+ vodka :very_true # And any value you want.
40
+ end
41
+
42
+ p @flavour # => #<Flavour:0x3f56b0 ...>
43
+ p @flavour.spicy # => true
44
+
45
+
46
+
47
+ # ------------------------------------------------------------------
48
+ # EXAMPLE 2 -- Staff
49
+ #
50
+
51
+ # Tell Caesars which attributes have children using Caesars#bloody and
52
+ # which have blocks that you want to execute later using Caesars#virgin.
53
+ class Staff < Caesars
54
+ bloody :location
55
+ bloody :person
56
+ virgin :calculate
57
+ end
58
+
59
+ extend Staff::DSL
60
+
61
+ # The top level method is the lower case name of the class. For deeper
62
+ # names like Class::SecondLevel it will use the final name
63
+ # (i.e. secondlevel). You can supply an optional modifier name which
64
+ # will be included in the instance variable (@staff_fte).
65
+ staff :fte do
66
+ desc 'Our hard-working, "full-time" staff'
67
+ location :splashdown do
68
+ town :tsawwassen
69
+ person :steve, :sheila do
70
+ role :manager
71
+ end
72
+ person :steve do
73
+ role :cook
74
+ anger :high
75
+ hours 25
76
+ catchphrase "Rah! [strokes goatee]"
77
+ end
78
+ person :sheila do
79
+ catchphrase "This gravy tastes like food I ate in a Mexican prison."
80
+ hours rand(20)
81
+ rate "9.35/h"
82
+ calculate :salary do |gumption|
83
+ ("%.2f" % [gumption * self.splashdown.sheila.rate.to_f]).to_f
84
+ end
85
+ end
86
+ person :delano do
87
+ role :cook
88
+ rate "8.35/h"
89
+ hours 57
90
+ satisfaction :low
91
+ calculate :salary do
92
+ self.splashdown.delano.rate.to_f * self.splashdown.delano.hours
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ p @staff_fte # => #<KitchenStaff: ...>
99
+ p @staff_fte.desc # => Our hard-working, "full-time" staff
100
+
101
+ # Deeper attributes are also available via instance methods
102
+ p @staff_fte.splashdown.delano # => {:role=>:cook, :rate=>"$8.35/h", :satisfaction=>:low}
103
+ p @staff_fte.splashdown.sheila # => {:role=>:manager, :catchphrase=>"This gravy tastes like food I ate in a Mexican prison."}
104
+ p @staff_fte.splashdown.steve # => {:role=>[:manager, :cook], :anger=>:high, :catchphrase=>"Rah! [strokes goatee]"}
105
+ p @staff_fte.splashdown.delano.satisfaction # => :low
106
+
107
+ # You can also access them using hash syntax
108
+ p @staff_fte.splashdown[:steve][:role] # => [:manager, :cook]
109
+
110
+ # The "bloody" attributes keep track of all values that are used. These are available as arrays
111
+ # via "NAME_values" methods. The goes for the virgin ones.
112
+ p @staff_fte.location_values # => [:splashdown]
113
+ p @staff_fte.person_values.uniq # => [:steve, :sheila, :delano, :angela]
114
+ p @staff_fte.calculate_values # => [:salary, :salary]
115
+
116
+ # The "virgin" methods store their blocks as Procs and are not executed automatically.
117
+ # You can call them manually and send arguments like you normally would.
118
+ p @staff_fte.splashdown.delano.salary.call # => 475.95
119
+ p @staff_fte.splashdown.sheila.salary.call(rand(100)) # => 549.77
120
+
121
+
122
+
123
+ == More Info
124
+
125
+ * GitHub[http://github.com/delano/caesar]
126
+ * Inspiration[http://www.youtube.com/watch?v=ycElb0tB9_U]
127
+ * Recipe[http://twitter.com/solutious/status/1264327499]
128
+
129
+ == Credits
130
+
131
+ * Delano Mandelbaum (delano@solutious.com)
132
+
133
+ == Thanks
134
+
135
+ * Clams, Tomatoes, Vodka, and the rest of the crew.
136
+
137
+ == License
138
+
139
+ See: LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'hanna/rdoctask'
5
+ require 'fileutils'
6
+ include FileUtils
7
+
8
+ task :default => :package
9
+
10
+ # SPECS ===============================================================
11
+
12
+ # None-yet!
13
+
14
+ # PACKAGE =============================================================
15
+
16
+ name = "caesars"
17
+ load "#{name}.gemspec"
18
+
19
+ version = @spec.version
20
+
21
+ Rake::GemPackageTask.new(@spec) do |p|
22
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
23
+ end
24
+
25
+ task :release => [ :rdoc, :package ]
26
+
27
+ task :install => [ :rdoc, :package ] do
28
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
29
+ end
30
+
31
+ task :uninstall => [ :clean ] do
32
+ sh %{sudo gem uninstall #{name}}
33
+ end
34
+
35
+
36
+ # Rubyforge Release / Publish Tasks ==================================
37
+
38
+ desc 'Publish website to rubyforge'
39
+ task 'publish:rdoc' => 'doc/index.html' do
40
+ sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/"
41
+ end
42
+
43
+ task 'publish:gem' => [:package] do |t|
44
+ sh <<-end
45
+ rubyforge add_release -o Any -a CHANGES.txt -f -n README.rdoc #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem &&
46
+ rubyforge add_file -o Any -a CHANGES.txt -f -n README.rdoc #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz
47
+ end
48
+ end
49
+
50
+
51
+ Rake::RDocTask.new do |t|
52
+ t.rdoc_dir = 'doc'
53
+ t.title = @spec.summary
54
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
55
+ t.options << '--charset' << 'utf-8'
56
+ t.rdoc_files.include('LICENSE.txt')
57
+ t.rdoc_files.include('README.rdoc')
58
+ t.rdoc_files.include('CHANGES.txt')
59
+ t.rdoc_files.include('bin/*')
60
+ t.rdoc_files.include('lib/*.rb')
61
+ end
62
+
63
+ CLEAN.include [ 'pkg', '*.gem', '.config', 'doc' ]
64
+
65
+
66
+
data/bin/example ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Caesars Example
5
+ #
6
+ # Usage: bin/example
7
+ #
8
+
9
+ $:.unshift(File.join(File.join(File.dirname(__FILE__), '..'), 'lib')) # Make sure our local lib is first in line
10
+
11
+ require 'caesars'
12
+
13
+ # ------------------------------------------------------------------
14
+ # EXAMPLE 1 -- Flavour
15
+ #
16
+
17
+ class Flavour < Caesars # Subclass Caesars.
18
+ end
19
+
20
+ extend Flavour::DSL # Bring the DSL into the current namespace.
21
+ # This module is created dynamically based
22
+ # on the name of the subclass.
23
+
24
+ flavour do # Start drinking! I mean, start writing your
25
+ spicy true # domain specific language!
26
+ clamy true # Use any attribute name you want.
27
+ salty true
28
+ vodka :very_true # And any value you want.
29
+ end
30
+
31
+ p @flavour # => #<Flavour:0x3f56b0 ...>
32
+ p @flavour.spicy # => true
33
+
34
+
35
+
36
+ # ------------------------------------------------------------------
37
+ # EXAMPLE 2 -- Staff
38
+ #
39
+
40
+ # Tell Caesars which attributes have children using Caesars#bloody and
41
+ # which have blocks that you want to execute later using Caesars#virgin.
42
+ class Staff < Caesars
43
+ bloody :location
44
+ bloody :person
45
+ virgin :calculate
46
+ end
47
+
48
+ extend Staff::DSL
49
+
50
+ # The top level method is the lower case name of the class. For deeper
51
+ # names like Class::SecondLevel it will use the final name
52
+ # (i.e. secondlevel). You can supply an optional modifier name which
53
+ # will be included in the instance variable (@staff_fte).
54
+ staff :fte do
55
+ desc 'Our hard-working, "full-time" staff'
56
+ location :splashdown do
57
+ town :tsawwassen
58
+ person :steve, :sheila do
59
+ role :manager
60
+ end
61
+ person :steve do
62
+ role :cook
63
+ anger :high
64
+ hours 25
65
+ catchphrase "Rah! [strokes goatee]"
66
+ end
67
+ person :sheila do
68
+ catchphrase "This gravy tastes like food I ate in a Mexican prison."
69
+ hours rand(20)
70
+ rate "9.35/h"
71
+ calculate :salary do |gumption|
72
+ ("%.2f" % [gumption * self.splashdown.sheila.rate.to_f]).to_f
73
+ end
74
+ end
75
+ person :delano do
76
+ role :cook
77
+ rate "8.35/h"
78
+ hours 57
79
+ satisfaction :low
80
+ calculate :salary do
81
+ self.splashdown.delano.rate.to_f * self.splashdown.delano.hours
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ p @staff_fte # => #<KitchenStaff: ...>
88
+ p @staff_fte.desc # => Our hard-working, "full-time" staff
89
+
90
+ # Deeper attributes are also available via instance methods
91
+ p @staff_fte.splashdown.delano # => {:role=>:cook, :rate=>"$8.35/h", :satisfaction=>:low}
92
+ p @staff_fte.splashdown.sheila # => {:role=>:manager, :catchphrase=>"This gravy tastes like food I ate in a Mexican prison."}
93
+ p @staff_fte.splashdown.steve # => {:role=>[:manager, :cook], :anger=>:high, :catchphrase=>"Rah! [strokes goatee]"}
94
+ p @staff_fte.splashdown.delano.satisfaction # => :low
95
+
96
+ # You can also access them using hash syntax
97
+ p @staff_fte.splashdown[:steve][:role] # => [:manager, :cook]
98
+
99
+ # The "bloody" attributes keep track of all values that are used. These are available as arrays
100
+ # via "NAME_values" methods. The goes for the virgin ones.
101
+ p @staff_fte.location_values # => [:splashdown]
102
+ p @staff_fte.person_values.uniq # => [:steve, :sheila, :delano, :angela]
103
+ p @staff_fte.calculate_values # => [:salary, :salary]
104
+
105
+ # The "virgin" methods store their blocks as Procs and are not executed automatically.
106
+ # You can call them manually and send arguments like you normally would.
107
+ p @staff_fte.splashdown.delano.salary.call # => 475.95
108
+ p @staff_fte.splashdown.sheila.salary.call(rand(100)) # => 549.77
109
+ p @staff_fte.splashdown.keys
data/bin/example.bat ADDED
@@ -0,0 +1,12 @@
1
+ @echo off
2
+
3
+ rem Check for funkiness when called from another batch script.
4
+ rem We want FULL_PATH to contain the full path to the stella bin directory.
5
+ IF EXIST "%~dp0example.bat" (set FULL_PATH=%~dp0) ELSE (set FULL_PATH=%~dp$PATH:0)
6
+
7
+ rem Check for JRuby, otherwise use Ruby.
8
+ rem We want EXECUTABLE to contain either "jruby" or "ruby"
9
+ IF EXIST "%JRUBY_HOME%" (set EXECUTABLE=jruby) ELSE (set EXECUTABLE=ruby)
10
+
11
+ rem Call the Ruby script, passing it all the arguments.
12
+ @%EXECUTABLE% %FULL_PATH%example %*
data/caesars.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ @spec = Gem::Specification.new do |s|
2
+ s.name = %q{caesars}
3
+ s.version = "0.3.2"
4
+ s.date = %q{2009-03-04}
5
+ s.specification_version = 1 if s.respond_to? :specification_version=
6
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
7
+
8
+ 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.}
11
+ s.email = %q{delano@solutious.com}
12
+
13
+ # = MANIFEST =
14
+ # git ls-files
15
+ s.files = %w(
16
+ CHANGES.txt
17
+ LICENSE.txt
18
+ README.rdoc
19
+ Rakefile
20
+ bin/example
21
+ bin/example.bat
22
+ caesars.gemspec
23
+ lib/caesars.rb
24
+ )
25
+
26
+ # s.add_dependency ''
27
+
28
+ s.has_rdoc = true
29
+ s.homepage = %q{http://github.com/delano/caesar}
30
+ s.extra_rdoc_files = %w[README.rdoc LICENSE.txt CHANGES.txt]
31
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Caesars: A simple class for rapid DSL prototyping in Ruby.", "--main", "README.rdoc"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.1.1}
34
+ s.rubyforge_project = "caesars"
35
+ end
data/lib/caesars.rb ADDED
@@ -0,0 +1,95 @@
1
+
2
+ # Caesars -- A simple class for rapid DSL prototyping.
3
+ #
4
+ # Subclass Caesars, then tell it which attributes have children using
5
+ # Caesars.bloody and which have blocks that you want to execute later
6
+ # using Caesars.virgin. That's it! Just start drinking! I mean, start
7
+ # writing your domain specific language!
8
+ #
9
+ # See README.rdoc for a usage example.
10
+ #
11
+ class Caesars
12
+ VERSION = "0.3.2"
13
+ # A subclass of ::Hash that provides method names for hash parameters.
14
+ # It's like a lightweight OpenStruct.
15
+ # ch = Caesars::Hash[:tabasco => :lots!]
16
+ # puts ch.tabasco # => lots!
17
+ #
18
+ class Hash < ::Hash
19
+ def method_missing(meth)
20
+ (self.has_key?(meth)) ? self[meth] : nil
21
+ end
22
+ end
23
+ # An instance of Caesars::Hash which contains the data specified by your DSL
24
+ attr_accessor :caesars_properties
25
+ # Creates an instance of Caesars.
26
+ # +name+ is .
27
+ def initialize(name=nil)
28
+ @caesars_name = name if name
29
+ @caesars_properties = Caesars::Hash.new
30
+ @caesars_pointer = @caesars_properties
31
+ end
32
+ # This method handles all of the attributes that do not contain blocks.
33
+ def method_missing(name, *args, &b)
34
+ return @caesars_properties[name] if @caesars_properties.has_key?(name) && args.empty? && b.nil?
35
+ if @caesars_pointer[name]
36
+ @caesars_pointer[name] = [@caesars_pointer[name]] unless @caesars_pointer[name].is_a?(Array)
37
+ @caesars_pointer[name] += args
38
+ elsif !args.empty?
39
+ @caesars_pointer[name] = args.size == 1 ? args.first : args
40
+ end
41
+ end
42
+ # see bin/example for usage.
43
+ def self.virgin(meth)
44
+ self.bloody(meth, false)
45
+ end
46
+ # see bin/example for usage.
47
+ def self.bloody(meth, execute=true)
48
+ define_method(meth) do |*names,&b| # |*names,&b| syntax does not parse in Ruby 1.8
49
+ all = instance_variable_get("@" << meth.to_s) || []
50
+
51
+ names.each do |name|
52
+ instance_variable_set("@" << meth.to_s, all << name)
53
+
54
+ if execute
55
+ prev = @caesars_pointer
56
+ @caesars_pointer[name] ||= Caesars::Hash.new
57
+ @caesars_pointer = @caesars_pointer[name]
58
+ b.call if b
59
+ @caesars_pointer = prev
60
+ else
61
+ @caesars_pointer[name] = b
62
+ end
63
+
64
+ end
65
+ nil
66
+ end
67
+ define_method("#{meth}_values") do ||
68
+ instance_variable_get("@" << meth.to_s) || []
69
+ end
70
+ end
71
+ # Executes automatically when Caesars is subclassed. This creates the
72
+ # YourClass::DSL module which contains a single method: method_missing.
73
+ # This is used to catch the top level DSL method. That's why you can
74
+ # used any method name you like.
75
+ def self.inherited(modname)
76
+ meth = (modname.to_s.split(/::/))[-1].downcase # Some::ClassName => classname
77
+ module_eval %Q{
78
+ module #{modname}::DSL
79
+ def #{meth}(*args, &b)
80
+ name = !args.empty? ? args.first.to_s : nil
81
+ varname = "@#{meth.to_s}"
82
+ varname << "_\#{name}" if name
83
+ i = instance_variable_set(varname, #{modname.to_s}.new(name))
84
+ i.instance_eval(&b) if b
85
+ i
86
+ end
87
+ end
88
+ }, __FILE__, __LINE__
89
+ end
90
+ end
91
+
92
+
93
+
94
+
95
+
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: caesars
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: ruby
6
+ authors:
7
+ - Delano Mandelbaum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-04 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A simple class for rapid DSL prototyping in Ruby.
17
+ email: delano@solutious.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - LICENSE.txt
25
+ - CHANGES.txt
26
+ files:
27
+ - CHANGES.txt
28
+ - LICENSE.txt
29
+ - README.rdoc
30
+ - Rakefile
31
+ - bin/example
32
+ - bin/example.bat
33
+ - caesars.gemspec
34
+ - lib/caesars.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/delano/caesar
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --line-numbers
40
+ - --inline-source
41
+ - --title
42
+ - "Caesars: A simple class for rapid DSL prototyping in Ruby."
43
+ - --main
44
+ - README.rdoc
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project: caesars
62
+ rubygems_version: 1.2.0
63
+ signing_key:
64
+ specification_version: 1
65
+ summary: "Caesars: A simple class for rapid DSL prototyping in Ruby."
66
+ test_files: []
67
+