caesars 0.3.2

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