yourdsl 0.7.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.
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in lispy.gemspec
4
+ gemspec
@@ -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.
@@ -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).
@@ -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
@@ -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
+ #
@@ -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
@@ -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