yourdsl 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +19 -0
- data/README.md +106 -0
- data/Rakefile +14 -0
- data/example/feature.rb +82 -0
- data/lib/yourdsl.rb +74 -0
- data/test/yourdsl_test.rb +154 -0
- data/yourdsl.gemspec +21 -0
- metadata +57 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Ryan Allen, Envato Pty Ltd
|
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.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# YourDSL
|
2
|
+
|
3
|
+
## Assumptions.
|
4
|
+
|
5
|
+
* People love libraries with DSLs like Sinatra and ActiveRecord and Babushka and AASM and (name your favourite Ruby library).
|
6
|
+
* What they love about these is the small amount of Ruby syntax required to get a whole lot done.
|
7
|
+
* They love that they can contextualise their definitions (that make the libraries do things) with natural Ruby syntax such as blocks.
|
8
|
+
* To enable these kinds of libraries the use of so-called 'meta-programming' constructs are required (instance_eval, instance_exec).
|
9
|
+
* Clever but inexperienced Ruby programmers often make very useful libraries with wonderful APIs but horrible internals
|
10
|
+
|
11
|
+
## Goals.
|
12
|
+
|
13
|
+
* Decouple the language definition of an internal DSL from its implementation
|
14
|
+
* Allow library authors to dream up APIs and implement them without entering Ruby metaprogramming hell (it is a real place).
|
15
|
+
|
16
|
+
## Example
|
17
|
+
class Feature < Test::Unit::TestCase
|
18
|
+
# Configuring lispy to geenrate an AST for our language
|
19
|
+
|
20
|
+
PROC_KEYWORDS = [:Given, :When, :Then, :And]
|
21
|
+
KEYWORDS = [:Scenario, :Tag] + PROC_KEYWORDS
|
22
|
+
|
23
|
+
extend YourDSL
|
24
|
+
record_your_dsl :only => KEYWORDS, :retain_blocks_for => PROC_KEYWORDS
|
25
|
+
|
26
|
+
# Using our language
|
27
|
+
Scenario "this gets lispyified" do
|
28
|
+
Given "something" do
|
29
|
+
end
|
30
|
+
|
31
|
+
Then "test something exists" do
|
32
|
+
fail "ohai"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Barfing out the AST for the above
|
38
|
+
require 'rubygems'
|
39
|
+
require 'awesome_print'
|
40
|
+
ap Feature.output
|
41
|
+
|
42
|
+
# Example 'interpreter' for the AST generated above
|
43
|
+
# Executes the block on the Given and Then from above sequentially
|
44
|
+
# Spike on how an acceptance testing DSL could work
|
45
|
+
Feature.class_eval do
|
46
|
+
def test_something
|
47
|
+
scenario = Feature.output.expressions.first
|
48
|
+
steps = scenario.scope.expressions
|
49
|
+
instance_eval &steps[0].proc
|
50
|
+
instance_eval &steps[1].proc
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
OUTPUTS:
|
55
|
+
➜ example git:(no_more_last_last_last) ✗ ruby feature.rb
|
56
|
+
{
|
57
|
+
:file => "feature.rb",
|
58
|
+
:expressions => [
|
59
|
+
[0] {
|
60
|
+
:symbol => :Scenario,
|
61
|
+
:args => "this gets lispyified",
|
62
|
+
:lineno => "14",
|
63
|
+
:proc => nil,
|
64
|
+
:scope => {
|
65
|
+
:expressions => [
|
66
|
+
[0] {
|
67
|
+
:symbol => :Given,
|
68
|
+
:args => "something",
|
69
|
+
:lineno => "15",
|
70
|
+
:proc => #<Proc:0x000001010e9140@feature.rb:15>,
|
71
|
+
:scope => nil
|
72
|
+
},
|
73
|
+
[1] {
|
74
|
+
:symbol => :Then,
|
75
|
+
:args => "test something exists",
|
76
|
+
:lineno => "18",
|
77
|
+
:proc => #<Proc:0x000001010e8ec0@feature.rb:18>,
|
78
|
+
:scope => nil
|
79
|
+
}
|
80
|
+
]
|
81
|
+
}
|
82
|
+
}
|
83
|
+
]
|
84
|
+
}
|
85
|
+
Loaded suite feature
|
86
|
+
Started
|
87
|
+
E
|
88
|
+
Finished in 0.006305 seconds.
|
89
|
+
|
90
|
+
1) Error:
|
91
|
+
test_something(Feature):
|
92
|
+
RuntimeError: ohai
|
93
|
+
feature.rb:19:in `block (2 levels) in <class:Feature>'
|
94
|
+
feature.rb:34:in `instance_eval'
|
95
|
+
feature.rb:34:in `test_something'
|
96
|
+
|
97
|
+
1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
|
98
|
+
|
99
|
+
## Credits
|
100
|
+
|
101
|
+
* Lispy concept and original impl by Ryan Allen (http://github.com/ryan-allen/lispy)
|
102
|
+
* Rewritten and extended by Evan Light (http://github.com/elight/yourdsl)
|
103
|
+
|
104
|
+
## License.
|
105
|
+
|
106
|
+
Released under the MIT license (see MIT-LICENSE).
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
task :default do
|
5
|
+
Rake::Task['run_tests'].invoke
|
6
|
+
end
|
7
|
+
|
8
|
+
task :run_tests do
|
9
|
+
if RUBY_VERSION =~ /^1.9/
|
10
|
+
require_relative 'test/yourdsl_test'
|
11
|
+
else
|
12
|
+
require 'test/yourdsl_test'
|
13
|
+
end
|
14
|
+
end
|
data/example/feature.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
load '../lib/yourdsl.rb'
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class Feature < Test::Unit::TestCase
|
6
|
+
PROC_KEYWORDS = [:Given, :When, :Then, :And]
|
7
|
+
KEYWORDS = [:Scenario, :Tag] + PROC_KEYWORDS
|
8
|
+
|
9
|
+
extend YourDSL
|
10
|
+
record_your_dsl :only => KEYWORDS, :retain_blocks_for => PROC_KEYWORDS
|
11
|
+
|
12
|
+
# Everything after this gets lispyified
|
13
|
+
|
14
|
+
Scenario "this gets lispyified" do
|
15
|
+
Given "something" do
|
16
|
+
end
|
17
|
+
|
18
|
+
Then "test something exists" do
|
19
|
+
fail "ohai"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'awesome_print'
|
26
|
+
ap Feature.output
|
27
|
+
|
28
|
+
Feature.class_eval do
|
29
|
+
# manually execute "Given" and "Then"
|
30
|
+
def test_something
|
31
|
+
scenario = Feature.output.expressions.first
|
32
|
+
steps = scenario.scope.expressions
|
33
|
+
instance_eval &steps[0].proc
|
34
|
+
instance_eval &steps[1].proc
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# OUTPUTS:
|
39
|
+
# ➜ example git:(no_more_last_last_last) ✗ ruby feature.rb
|
40
|
+
# {
|
41
|
+
# :file => "feature.rb",
|
42
|
+
# :expressions => [
|
43
|
+
# [0] {
|
44
|
+
# :symbol => :Scenario,
|
45
|
+
# :args => "this gets lispyified",
|
46
|
+
# :lineno => "14",
|
47
|
+
# :proc => nil,
|
48
|
+
# :scope => {
|
49
|
+
# :expressions => [
|
50
|
+
# [0] {
|
51
|
+
# :symbol => :Given,
|
52
|
+
# :args => "something",
|
53
|
+
# :lineno => "15",
|
54
|
+
# :proc => #<Proc:0x000001010e9140@feature.rb:15>,
|
55
|
+
# :scope => nil
|
56
|
+
# },
|
57
|
+
# [1] {
|
58
|
+
# :symbol => :Then,
|
59
|
+
# :args => "test something exists",
|
60
|
+
# :lineno => "18",
|
61
|
+
# :proc => #<Proc:0x000001010e8ec0@feature.rb:18>,
|
62
|
+
# :scope => nil
|
63
|
+
# }
|
64
|
+
# ]
|
65
|
+
# }
|
66
|
+
# }
|
67
|
+
# ]
|
68
|
+
# }
|
69
|
+
# Loaded suite feature
|
70
|
+
# Started
|
71
|
+
# E
|
72
|
+
# Finished in 0.006305 seconds.
|
73
|
+
#
|
74
|
+
# 1) Error:
|
75
|
+
# test_something(Feature):
|
76
|
+
# RuntimeError: ohai
|
77
|
+
# feature.rb:19:in `block (2 levels) in <class:Feature>'
|
78
|
+
# feature.rb:34:in `instance_eval'
|
79
|
+
# feature.rb:34:in `test_something'
|
80
|
+
#
|
81
|
+
# 1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
|
82
|
+
#
|
data/lib/yourdsl.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
module YourDSL
|
2
|
+
class Scope < Struct.new(:expressions); end
|
3
|
+
class Expression < Struct.new(:symbol, :args, :lineno, :proc, :scope); end
|
4
|
+
class Output < Struct.new(:file, :expressions); end
|
5
|
+
|
6
|
+
VERSION = '0.7.0'
|
7
|
+
|
8
|
+
METHODS_TO_KEEP = /^__/, /class/, /instance_/, /method_missing/, /object_id/
|
9
|
+
|
10
|
+
instance_methods.each do |m|
|
11
|
+
undef_method m unless METHODS_TO_KEEP.find { |r| r.match m }
|
12
|
+
end
|
13
|
+
|
14
|
+
def record_your_dsl(opts = {})
|
15
|
+
@@remember_blocks_starting_with = Array(opts[:retain_blocks_for])
|
16
|
+
@@only = Array(opts[:only])
|
17
|
+
@@exclude = Array(opts[:except])
|
18
|
+
@@output = Output.new
|
19
|
+
@@file = nil
|
20
|
+
@stack = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def output
|
24
|
+
if @current_scope
|
25
|
+
@@output.expressions = @current_scope.expressions
|
26
|
+
end
|
27
|
+
@@output
|
28
|
+
end
|
29
|
+
|
30
|
+
def file=(file)
|
31
|
+
unless @@file
|
32
|
+
@@file = file
|
33
|
+
@@output.file = @@file
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_missing(sym, *args, &block)
|
38
|
+
caller[0] =~ (/(.*):(.*):in?/)
|
39
|
+
file, lineno = $1, $2
|
40
|
+
self.file = file
|
41
|
+
|
42
|
+
if !@@only.empty? && !@@only.include?(sym)
|
43
|
+
fail(NoMethodError, sym.to_s)
|
44
|
+
end
|
45
|
+
if !@@exclude.empty? && @@exclude.include?(sym)
|
46
|
+
fail(NoMethodError, sym.to_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
args = (args.length == 1 ? args.first : args)
|
50
|
+
@current_scope ||= Scope.new([])
|
51
|
+
@current_scope.expressions << Expression.new(sym, args, lineno)
|
52
|
+
if block
|
53
|
+
# there is some simpler recursive way of doing this, will fix it shortly
|
54
|
+
if @@remember_blocks_starting_with.include? sym
|
55
|
+
@current_scope.expressions.last.proc = block
|
56
|
+
else
|
57
|
+
nest(&block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def nest(&block)
|
64
|
+
@stack.push @current_scope
|
65
|
+
|
66
|
+
new_scope = Scope.new([])
|
67
|
+
@current_scope.expressions.last.scope = new_scope
|
68
|
+
@current_scope = new_scope
|
69
|
+
|
70
|
+
instance_exec(&block)
|
71
|
+
|
72
|
+
@current_scope = @stack.pop
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'yourdsl'
|
3
|
+
|
4
|
+
class YourDSLTest < Test::Unit::TestCase
|
5
|
+
class Whatever
|
6
|
+
extend YourDSL
|
7
|
+
record_your_dsl
|
8
|
+
|
9
|
+
setup :workers => 30, :connections => 1024
|
10
|
+
http :access_log => :off do
|
11
|
+
server :listen => 80 do
|
12
|
+
location '/' do
|
13
|
+
doc_root '/var/www/website'
|
14
|
+
end
|
15
|
+
location '~ .php$' do
|
16
|
+
fcgi :port => 8877
|
17
|
+
script_root '/var/www/website'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
ohai
|
22
|
+
end
|
23
|
+
@@whatever = Whatever.output
|
24
|
+
|
25
|
+
class MoarYourDSL
|
26
|
+
extend YourDSL
|
27
|
+
record_your_dsl
|
28
|
+
|
29
|
+
setup
|
30
|
+
setup do
|
31
|
+
lol
|
32
|
+
lol do
|
33
|
+
hi
|
34
|
+
hi
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@@moar_yourdsl = MoarYourDSL.output
|
39
|
+
|
40
|
+
class RetainBlocks
|
41
|
+
extend YourDSL
|
42
|
+
record_your_dsl :retain_blocks_for => [:Given, :When, :Then]
|
43
|
+
|
44
|
+
Scenario "My first awesome scenario" do
|
45
|
+
Given "teh shiznit" do
|
46
|
+
#shiznit
|
47
|
+
end
|
48
|
+
|
49
|
+
When "I do something" do
|
50
|
+
#komg.do_something
|
51
|
+
end
|
52
|
+
|
53
|
+
Then "this is pending"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
@@retain_blocks = RetainBlocks.output
|
57
|
+
|
58
|
+
begin
|
59
|
+
class OptInParsing
|
60
|
+
extend YourDSL
|
61
|
+
record_your_dsl :only => [:Foo]
|
62
|
+
|
63
|
+
Foo "bar" do
|
64
|
+
blech
|
65
|
+
end
|
66
|
+
end
|
67
|
+
rescue
|
68
|
+
@@opt_in_parsing_error = $!
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
class OptOutParsing
|
73
|
+
extend YourDSL
|
74
|
+
record_your_dsl :except => [:blech]
|
75
|
+
|
76
|
+
Foo "bar" do
|
77
|
+
blech
|
78
|
+
end
|
79
|
+
end
|
80
|
+
rescue
|
81
|
+
@@opt_out_parsing_error = $!
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_yourdsl
|
85
|
+
expected = YourDSL::Output.new(__FILE__, [
|
86
|
+
YourDSL::Expression.new(:setup, {:workers=>30, :connections=>1024}, "9"),
|
87
|
+
YourDSL::Expression.new(:http, {:access_log =>:off}, "10", nil,
|
88
|
+
YourDSL::Scope.new.tap { |s1| s1.expressions = [
|
89
|
+
YourDSL::Expression.new(:server, {:listen=>80}, "11", nil,
|
90
|
+
YourDSL::Scope.new.tap { |s2| s2.expressions = [
|
91
|
+
YourDSL::Expression.new(:location, '/', "12", nil,
|
92
|
+
YourDSL::Scope.new.tap { |s3| s3.expressions = [
|
93
|
+
YourDSL::Expression.new(:doc_root, '/var/www/website', "13")
|
94
|
+
]}
|
95
|
+
),
|
96
|
+
YourDSL::Expression.new(:location, '~ .php$', "15", nil,
|
97
|
+
YourDSL::Scope.new.tap { |s4| s4.expressions = [
|
98
|
+
YourDSL::Expression.new(:fcgi, {:port => 8877}, "16"),
|
99
|
+
YourDSL::Expression.new(:script_root, '/var/www/website', "17")
|
100
|
+
]}
|
101
|
+
)
|
102
|
+
]}
|
103
|
+
)
|
104
|
+
]}
|
105
|
+
),
|
106
|
+
YourDSL::Expression.new(:ohai, [], "21")
|
107
|
+
])
|
108
|
+
assert_equal expected, @@whatever
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_moar_yourdsl
|
112
|
+
expected = YourDSL::Output.new(__FILE__, [
|
113
|
+
YourDSL::Expression.new(:setup, [], "29"),
|
114
|
+
YourDSL::Expression.new(:setup, [], "30", nil,
|
115
|
+
YourDSL::Scope.new.tap { |s1| s1.expressions = [
|
116
|
+
YourDSL::Expression.new(:lol, [], "31"),
|
117
|
+
YourDSL::Expression.new(:lol, [], "32", nil,
|
118
|
+
YourDSL::Scope.new.tap { |s2| s2.expressions = [
|
119
|
+
YourDSL::Expression.new(:hi, [], "33"),
|
120
|
+
YourDSL::Expression.new(:hi, [], "34")
|
121
|
+
]}
|
122
|
+
)
|
123
|
+
]}
|
124
|
+
)
|
125
|
+
])
|
126
|
+
assert_equal expected, @@moar_yourdsl
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_conditionally_preserving_procs
|
130
|
+
ast = @@retain_blocks
|
131
|
+
scenario = ast.expressions.first
|
132
|
+
assert_equal :Scenario, scenario.symbol
|
133
|
+
assert_equal "My first awesome scenario", scenario.args
|
134
|
+
given = scenario.scope.expressions.first
|
135
|
+
assert_equal :Given, given.symbol
|
136
|
+
assert_equal "teh shiznit", given.args
|
137
|
+
assert_instance_of Proc, given.proc
|
138
|
+
assert_nil given.scope
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_opt_in_parsing
|
142
|
+
assert_instance_of NoMethodError, @@opt_in_parsing_error
|
143
|
+
assert_match "blech", @@opt_in_parsing_error.message
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_opt_out_parsing
|
147
|
+
begin
|
148
|
+
flunk if @@opt_out_parsing_fail
|
149
|
+
rescue
|
150
|
+
assert_instance_of NoMethodError, @@opt_out_parsing_error
|
151
|
+
assert_match "blech", @@opt_out_parsing_error.message
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/yourdsl.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "yourdsl"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "yourdsl"
|
7
|
+
s.version = YourDSL::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Evan Light", "Ryan Allen"]
|
10
|
+
s.email = ["evan@tripledogdare.net", "ryan@ryanface.com"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/yourdsl"
|
12
|
+
s.summary = %q{~1/2 of a compiler: a partial lexer and nested DSL language implementation}
|
13
|
+
s.description = %q{Records an internal DSL for playback as an AST within Ruby}
|
14
|
+
|
15
|
+
s.rubyforge_project = "yourdsl"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yourdsl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Evan Light
|
9
|
+
- Ryan Allen
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2011-08-08 00:00:00.000000000Z
|
14
|
+
dependencies: []
|
15
|
+
description: Records an internal DSL for playback as an AST within Ruby
|
16
|
+
email:
|
17
|
+
- evan@tripledogdare.net
|
18
|
+
- ryan@ryanface.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- .gitignore
|
24
|
+
- Gemfile
|
25
|
+
- MIT-LICENSE
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- example/feature.rb
|
29
|
+
- lib/yourdsl.rb
|
30
|
+
- test/yourdsl_test.rb
|
31
|
+
- yourdsl.gemspec
|
32
|
+
homepage: http://rubygems.org/gems/yourdsl
|
33
|
+
licenses: []
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project: yourdsl
|
52
|
+
rubygems_version: 1.8.6
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: ! '~1/2 of a compiler: a partial lexer and nested DSL language implementation'
|
56
|
+
test_files:
|
57
|
+
- test/yourdsl_test.rb
|