dsl_block 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6c00534daad8cd4a27d4f3855c4d9a03fad82595
4
+ data.tar.gz: 4dfb593b99f4653aecd81b661e4a04533068a87e
5
+ SHA512:
6
+ metadata.gz: bb9421a382aca361db5ff31e801d5f064a5789077d68b994aee65a92d87e4385c26631da68466203c5030f6271371db9444a59c9da991e46fcd41f111da462bf
7
+ data.tar.gz: 227ebf931eca9fdb584fc51dea8a422a74bfa9fad5823af7127937ef69f0c277efc3c152ebd781f19f4a75cd76c5e0e802d65cfbd5e3783f0215868a965a4376
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dsl_block.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Frank Hall
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # DslBlock
2
+
3
+ DslBlock allows you to use classes to define blocks with commands for a Domain Specific Language. The commands are automatically relayed to your instance method.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'dsl_block'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install dsl_block
18
+
19
+ ## Usage
20
+
21
+ class Foo < DslBlock
22
+ commands :show_foo
23
+ def show_foo(x)
24
+ puts "Mr. T says you are a foo times #{x.to_i}"
25
+ end
26
+ end
27
+
28
+ class Bar < DslBlock
29
+ commands :show_bar
30
+ def show_bar(x)
31
+ puts "Ordering #{x.to_i} Shirley Temples from the bar"
32
+ end
33
+ end
34
+
35
+ class Baz < DslBlock
36
+ commands :show_baz
37
+ def show_baz(x)
38
+ puts "Baz spaz #{x.inspect}"
39
+ end
40
+ end
41
+
42
+ Baz.add_command_to(Bar)
43
+ Bar.add_command_to(Foo, true)
44
+ Foo.add_command_to(self)
45
+
46
+
47
+ foo do
48
+ puts self.inspect # => #<Foo:0x007f98f187e240 @block=#<Proc:0x...>, @parent=nil>
49
+ x = 10/10
50
+ show_foo x # => Mr. T says you are a foo times 1
51
+
52
+ bar do
53
+ x *= 2
54
+ show_bar x # => Ordering 2 Shirley Temples from the bar
55
+ x += 1
56
+ show_foo x # => Mr. T says you are a foo times 3
57
+
58
+ baz do
59
+ x *= 4
60
+ x /= 3
61
+ show_baz x # => Baz spaz 4
62
+ begin
63
+ x += 1
64
+ show_bar 5 # This will throw a NameError
65
+ rescue NameError
66
+ puts 'No bar for us'
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+
76
+ ## Contributing
77
+
78
+ 1. Fork it
79
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
80
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
81
+ 4. Push to the branch (`git push origin my-new-feature`)
82
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rdoc/task'
3
+ require 'rspec/core/rake_task'
4
+ require 'rdoc/task'
5
+
6
+ RDoc::Task.new do |rdoc|
7
+ rdoc.rdoc_dir = 'doc'
8
+ rdoc.main = 'DslBlock.html'
9
+ rdoc.rdoc_files.include 'lib'
10
+ end
11
+
12
+
13
+ RSpec::Core::RakeTask.new
14
+
15
+ task :default => :spec
16
+ task :test => :spec
data/dsl_block.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dsl_block/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'dsl_block'
8
+ spec.version = DslBlock::VERSION
9
+ spec.authors = ['Frank Hall']
10
+ spec.email = ['ChapterHouse.Dune@gmail.com']
11
+ spec.summary = %q{Quick and simple DSL creator.}
12
+ spec.description = %q{DslBlock is a quick and simple DSL creator.}
13
+ spec.homepage = 'http://chapterhouse.github.io/dsl_block'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'activesupport', '~> 4.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'rspec', '~> 2.14.1'
26
+ spec.add_development_dependency 'simplecov', '~> 0.7.1'
27
+ spec.add_development_dependency 'rdoc', '~> 4.0.0'
28
+
29
+ end
@@ -0,0 +1,37 @@
1
+ class DslBlock
2
+
3
+ # The Executor class is designed to run a block of code in isolation
4
+ # for the DslBlock class. By running it in a 'sandbox', the block
5
+ # of code cannot inadvertently access protected and private methods
6
+ # within the DslBlock without explicate declaration by the DslBlock.
7
+ # To the user, it will appear that the block runs directly in the
8
+ # DslBlock but in a partly restricted manner if they care to investigate.
9
+ # In this fashion, executor is effectively a transparent proxy.
10
+ class Executor < BasicObject
11
+
12
+ def initialize(dsl_block)
13
+ @dsl_block = dsl_block
14
+ end
15
+
16
+ def method_missing(method, *args, &block)
17
+ # If the dsl block lists the method as a callable command
18
+ if @dsl_block._commands.include?(method)
19
+ # Attempt to call it
20
+ begin
21
+ @dsl_block.send(method, *args, &block)
22
+ rescue => e
23
+ # If there is any type of error, remove ourselves from the callstack to reduce confusion.
24
+ e.set_backtrace(::Kernel.caller.select { |x| !x.include?(__FILE__)})
25
+ ::Kernel.raise e
26
+ end
27
+ else
28
+ # Otherwise raise a no method error as if the method does not really exist, regardless of reality.
29
+ name_error = ::NameError.new("undefined local variable or method `#{method}' for #{self.inspect}")
30
+ name_error.set_backtrace(::Kernel.caller.select { |x| !x.include?(__FILE__)})
31
+ ::Kernel::raise name_error
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,10 @@
1
+ class DslBlock
2
+ # 1.0.0
3
+ VERSION = '1.0.0'
4
+
5
+ module Version # :nodoc: all
6
+ MAJOR, MINOR, PATCH, *OTHER = VERSION.split('.')
7
+ NUMBERS = [MAJOR, MINOR, PATCH, *OTHER]
8
+ end
9
+
10
+ end
data/lib/dsl_block.rb ADDED
@@ -0,0 +1,159 @@
1
+ require 'dsl_block/version'
2
+ require 'dsl_block/executor'
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ # DslBlock is a base class for defining a Domain Specific Language. Subclasses of DslBlock define the desired dsl.
6
+ # These methods become available to ruby code running in the context of the subclass.
7
+ # The block execution is automatically isolated to prevent the called block from accessing instance methods unless
8
+ # specifically designated as callable. DslBlocks can be nested and parent blocks can allow their methods to be exposed to child block.
9
+ #
10
+ # ==== Example
11
+ # # Define three DslBlocks each with at least one command in each block
12
+ # class Foo < DslBlock
13
+ # commands :show_foo
14
+ # def show_foo(x)
15
+ # "Mr. T says you are a foo times #{x.to_i}"
16
+ # end
17
+ # end
18
+ #
19
+ # class Bar < DslBlock
20
+ # commands :show_bar
21
+ # def show_bar(x)
22
+ # "Ordering #{x.to_i} Shirley Temples from the bar"
23
+ # end
24
+ # end
25
+ #
26
+ # class Baz < DslBlock
27
+ # commands :show_baz
28
+ # def show_baz(x)
29
+ # "Baz spaz #{x.inspect}"
30
+ # end
31
+ # end
32
+ #
33
+ # # Connect the blocks to each other so they can be easily nested
34
+ # Baz.add_command_to(Bar)
35
+ # Bar.add_command_to(Foo, true) # Let Bar blocks also respond to foo methods
36
+ # Foo.add_command_to(self)
37
+ #
38
+ # # Use the new DSL
39
+ # foo do
40
+ # self.inspect # => #<Foo:0x007fdbd52b54e0 @block=#<Proc:0x007fdbd52b5530@/home/fhall/wonderland/alice.rb:29>, @parent=nil>
41
+ # x = 10/10
42
+ # show_foo x # => Mr. T says you are a foo times 1
43
+ #
44
+ # bar do
45
+ # x *= 2
46
+ # show_bar x # => Ordering 2 Shirley Temples from the bar
47
+ #
48
+ # x += 1
49
+ # show_foo x # => Mr. T says you are a foo times 3
50
+ #
51
+ # baz do
52
+ #
53
+ # x *= 4
54
+ # x /= 3
55
+ # show_baz x # => Baz spaz 4
56
+ #
57
+ # begin
58
+ # x += 1
59
+ # show_bar x # => NameError
60
+ # rescue NameError
61
+ # 'No bar for us'
62
+ # end
63
+ # end
64
+ #
65
+ # end
66
+ #
67
+ # end
68
+ #
69
+ class DslBlock
70
+ # Parent object providing additional commands to the block.
71
+ attr_accessor :parent
72
+ # Block of code that will be executed upon yield.
73
+ attr_accessor :block
74
+
75
+ # With no arguments, returns an array of command names that this DslBlock makes available to blocks either directly or indirectly.
76
+ # With arguments, adds new names to the array of command names, then returns the new array.
77
+ def self.commands(*args)
78
+ @commands ||= []
79
+ @commands = (@commands + args.map(&:to_sym)).uniq
80
+ @commands
81
+ end
82
+
83
+ # This is a convenience command that allows this DslBlock to inject itself as a method into another DslBlock or Object.
84
+ # If the parent is also a DslBlock, the new method will automatically be added to the available commands.
85
+ #
86
+ # Params:
87
+ # +destination+:: The object to receive the new method
88
+ # +propigate_local_commands+:: Allow methods in the destination to be called by the block. (default: false)
89
+ # +command_name+:: The name of the method to be created or nil to use the default which is based off of the class name. (default: nil)
90
+ def self.add_command_to(destination, propigate_local_commands=false, command_name=nil)
91
+ # Determine the name of the method to create
92
+ command_name = (command_name || name).to_s.underscore.to_sym
93
+ # Save a reference to our self so we will have something to call in a bit when self will refer to someone else.
94
+ this_class = self
95
+ # Define the command in the destination.
96
+ destination.send(:define_method, command_name) do |&block|
97
+ # Create a new instance of our self with the callers 'self' passed in as an optional parent.
98
+ # Immediately after initialization, yield the block.
99
+ this_class.new(propigate_local_commands ? self : nil, &block).yield
100
+ end
101
+ # Add the new command to the parent if it is a DslBlock.
102
+ destination.commands << command_name if destination.is_a?(Class) && destination < DslBlock
103
+ end
104
+
105
+ # Create a new DslBlock instance.
106
+ # +parent+:: Optional parent DslBlock or Object that is providing additional commands to the block. (default: nil)
107
+ # +block+:: Required block of code that will be executed when yield is called on the new DslBlock instance.
108
+ def initialize(parent = nil, &block)
109
+ raise ArgumentError, 'block must be provided' unless block_given?
110
+ @block = block
111
+ @parent = parent
112
+ end
113
+
114
+ # This is the entire list of commands that this instance makes available to the block of code to be run.
115
+ # It is a combination of three distinct sources.
116
+ # 1. The class's declared commands
117
+ # 2. If there is a parent of this DslBock instance...
118
+ # * The parents declared commands if it is a DslBlock
119
+ # * The parents public_methods if it is any other type of object
120
+ # 3. Kernel.methods
121
+ #
122
+ # This method is prefixed with an underscore in an attempt to avoid collisions with commands in the given block.
123
+ def _commands
124
+ cmds = self.class.commands.dup
125
+ if @parent
126
+ if @parent.is_a?(DslBlock)
127
+ cmds += @parent._commands
128
+ else
129
+ cmds += @parent.public_methods
130
+ end
131
+ end
132
+ (cmds + Kernel.methods).uniq
133
+ end
134
+
135
+ # Yield the block given.
136
+ def yield
137
+ begin
138
+ # Evaluate the block in an executor to provide isolation
139
+ # and prevent accidental interference with ourselves.
140
+ Executor.new(self).instance_eval(&@block)
141
+ rescue Exception => e
142
+ e.set_backtrace(caller.select { |x| !x.include?(__FILE__)})
143
+ raise e
144
+ end
145
+ end
146
+
147
+ # :nodoc:
148
+ def respond_to_missing?(method, include_all)
149
+ @parent && @parent.respond_to?(method, include_all) || super
150
+ end
151
+
152
+ def method_missing(name, *args, &block)
153
+ if @parent && @parent.respond_to?(name)
154
+ @parent.send(name, *args, &block)
155
+ else
156
+ super
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe DslBlock::Executor do
4
+
5
+ context '.new' do
6
+
7
+ it 'inherits from BasicObject to constrain the block' do
8
+ expect(DslBlock::Executor.superclass).to equal(BasicObject)
9
+ end
10
+
11
+ end
12
+
13
+ context '#method_missing' do
14
+
15
+ it 'calls the method on the dsl_block if it contains the method in its #_commands' do
16
+ dsl_block = Object.new
17
+ dsl_block.stub(:foo).and_return('bar')
18
+ dsl_block.stub(:_commands).and_return([:foo, :inspect])
19
+ executor = DslBlock::Executor.new(dsl_block)
20
+ expect(executor.foo).to eql('bar')
21
+ end
22
+
23
+ it 'raises NameError if the dsl_block does not contain the method in its #_commands' do
24
+ dsl_block = Object.new
25
+ dsl_block.stub(:foo).and_return('bar')
26
+ dsl_block.stub(:_commands).and_return([:inspect])
27
+ executor = DslBlock::Executor.new(dsl_block)
28
+ expect { executor.bar }.to raise_error(NameError)
29
+ end
30
+
31
+ end
32
+
33
+ context 'any exception' do
34
+
35
+ it 'removes itself from the backtrace to make it easier to understand' do
36
+ begin
37
+ dsl_block = Object.new
38
+ dsl_block.stub(:_commands).and_return([:inspect])
39
+ executor = DslBlock::Executor.new(dsl_block)
40
+ executor.bar
41
+ rescue => e
42
+ expect(e.backtrace.any? { |x| x.include?('dsl_block/lib/dsl_block/executor.rb')} ).to be_false
43
+ end
44
+
45
+ begin
46
+ dsl_block = Object.new
47
+ dsl_block.stub(:foo) { raise 'Kaboom' }
48
+ dsl_block.stub(:_commands).and_return([:inspect, :foo])
49
+ executor = DslBlock::Executor.new(dsl_block)
50
+ executor.foo
51
+ rescue => e
52
+ expect(e.message).to eql('Kaboom')
53
+ expect(e.backtrace.any? { |x| x.include?('dsl_block/lib/dsl_block/executor.rb')} ).to be_false
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+
61
+ end
@@ -0,0 +1,241 @@
1
+ require 'spec_helper'
2
+
3
+ describe DslBlock do
4
+
5
+ before(:each) do
6
+ dsl_reset
7
+ end
8
+
9
+ context '.commands' do
10
+
11
+ it 'starts as an empty array' do
12
+ expect(dsl_class1.commands).to eql([])
13
+ end
14
+
15
+ it 'accepts multiple method names' do
16
+ dsl_class1.commands :a, :b, :c
17
+ expect(dsl_class1.commands).to eql([:a, :b, :c])
18
+ end
19
+
20
+ it 'appends the method names to the existing list' do
21
+ dsl_class1.commands :a, :b, :c
22
+ dsl_class1.commands :d, :e, :f
23
+ expect(dsl_class1.commands).to eql([:a, :b, :c, :d, :e, :f])
24
+ end
25
+
26
+ it 'returns the current method names when setting' do
27
+ expect(dsl_class1.commands(*[:a, :b, :c])).to eql([:a, :b, :c])
28
+ expect(dsl_class1.commands(*[:d, :e, :f])).to eql([:a, :b, :c, :d, :e, :f])
29
+ end
30
+
31
+ it 'removes duplicates' do
32
+ dsl_class1.commands :a, :b, :a
33
+ dsl_class1.commands :c, :b, :d
34
+ expect(dsl_class1.commands).to eql([:a, :b, :c, :d])
35
+ end
36
+
37
+ end
38
+
39
+ context '.add_command_to' do
40
+
41
+ it 'defines a method in the destination' do
42
+ dsl_class2.add_command_to(dsl_class1)
43
+ expect(dsl_class1.instance_methods.include?(dsl_class2_command)).to be_true
44
+ end
45
+
46
+ it 'allows for the name of the method to be chosen' do
47
+ dsl_class2.add_command_to(dsl_class1, nil, :foo)
48
+ expect(dsl_class1.instance_methods.include?(:foo)).to be_true
49
+ end
50
+
51
+ it 'adds to the commands in the destination if it is a DslBlock' do
52
+ dsl_class2.add_command_to(dsl_class1)
53
+ expect(dsl_class1.commands.include?(dsl_class2_command)).to be_true
54
+ end
55
+
56
+ it 'allows a non DslBlock destination' do
57
+ generic_class = Class.new
58
+ dsl_class2.add_command_to(generic_class)
59
+ expect(generic_class.instance_methods.include?(dsl_class2_command)).to be_true
60
+ end
61
+
62
+ context 'the method created' do
63
+
64
+ it 'creates a new instance of the target' do
65
+ dsl_class2.should_receive(:new).and_call_original
66
+ dsl12 {}
67
+ end
68
+
69
+ it 'calls the block given' do
70
+ expect(dsl12 { 1 }).to equal(1)
71
+ end
72
+
73
+ it 'executes the block in the context of the target' do
74
+ expect(dsl12 { self }).to be_instance_of(dsl_class2)
75
+ end
76
+
77
+ end
78
+
79
+ context 'propagate_commands' do
80
+
81
+ it 'by default is false and does not propagate parent block commands' do
82
+ dsl_class1.send(:define_method, :foo) { |x| 'foo' * x }
83
+ dsl_class1.commands :foo
84
+
85
+ dsl = dsl12(false) { foo(2) }
86
+
87
+ expect { dsl.yield }.to raise_error(NameError)
88
+ # Prove we can call it normally
89
+ expect( dsl.foo(1)).to eql('foo')
90
+ end
91
+
92
+ it 'can be true to propagate parent block commands' do
93
+ target = nil
94
+
95
+ dsl_class1.send(:define_method, :foo) { |x| 'foo' * x }
96
+ dsl_class1.commands :foo
97
+
98
+ dsl = dsl12(false, true) { foo(2) }
99
+
100
+ expect(dsl.yield).to eql('foofoo')
101
+ # Prove we can call it normally
102
+ expect(dsl.foo(1)).to eql('foo')
103
+ end
104
+
105
+ it 'will not propagate parent block commands that aren\'t marked as commands' do
106
+ dsl_class1.send(:define_method, :foo) { |x| 'foo' * x }
107
+ # Unlike above, :foo will not added to the list of commands at this point.
108
+
109
+ dsl = dsl12(false) { foo(2) }
110
+
111
+ expect { dsl.yield }.to raise_error(NameError)
112
+ # Prove we can call it normally
113
+ expect(dsl.foo(1)).to eql('foo')
114
+ end
115
+
116
+ end
117
+
118
+ end
119
+
120
+
121
+ context '.new' do
122
+
123
+ it 'requires a block' do
124
+ expect{dsl_class1.new}.to raise_error(ArgumentError, 'block must be provided')
125
+ end
126
+
127
+ it 'stores the block for later execution' do
128
+ block = Proc.new {}
129
+ dsl = dsl_class1.new(&block)
130
+ expect(dsl.instance_variable_get(:@block)).to equal(block)
131
+ end
132
+
133
+ it 'can also take a parent object' do
134
+ object = Object.new
135
+ dsl = dsl_class1.new(object) {}
136
+ expect(dsl.instance_variable_get(:@parent)).to equal(object)
137
+ end
138
+
139
+ end
140
+
141
+ context '#_commands' do
142
+
143
+ context 'without a parent object' do
144
+
145
+ it 'shows only the dsl class commands and the Kernel.methods available to the block passed' do
146
+ dsl_class1.commands :foo, :bar
147
+ dsl = dsl1(false) {}
148
+ expect(dsl._commands.sort).to eql((dsl_class1.commands + Kernel.methods).uniq.sort)
149
+ end
150
+
151
+ end
152
+
153
+ context 'with a DslBlock parent' do
154
+
155
+ it 'shows the dls class commands, the parent._commands, and the Kernel.methods available to the block passed' do
156
+ dsl_class2.send(:define_method, :true_self) { self }
157
+ dsl_class2.commands :true_self
158
+ dsl_class1.commands :foo, :bar
159
+ dsl2_instance = dsl12(true, true) { true_self }
160
+
161
+ expect(dsl2_instance._commands.sort).to eql((dsl_class1.commands + dsl_class2.commands + Kernel.methods).sort)
162
+ end
163
+
164
+ end
165
+
166
+ context 'with a generic Object parent' do
167
+
168
+ it 'shows the dls class commands, the object.public_methods, and the Kernel.methods available to the block passed' do
169
+ array = Array.new
170
+ dsl1_instance = dsl_class1.new(array) {}
171
+ expect(dsl1_instance._commands.sort).to eql((Kernel.methods + dsl_class1.commands + array.public_methods).sort.uniq)
172
+ end
173
+
174
+ end
175
+
176
+ end
177
+
178
+ context '#yield' do
179
+
180
+ it 'yields the block given at instantiation' do
181
+ dsl = dsl_class1.new { 3 }
182
+ expect(dsl.yield).to equal(3)
183
+ end
184
+
185
+ it 'creates an executor to evaluate the block' do
186
+ dsl = dsl_class1.new {}
187
+ DslBlock::Executor.should_receive(:new).with(dsl).and_call_original
188
+ dsl.yield
189
+ end
190
+
191
+ it 'isolates the block by evaluating it in the context of the executor' do
192
+ block = Proc.new {}
193
+ dsl = dsl_class1.new(&block)
194
+ executor = DslBlock::Executor.new(dsl)
195
+ executor.should_receive(:instance_eval).with(&block)
196
+ DslBlock::Executor.stub(:new).and_return(executor)
197
+ dsl.yield
198
+ end
199
+
200
+ it 'cleans up any backtraces by removing itself from the call stack' do
201
+ begin
202
+ dsl123 { raise 'Kaboom' }
203
+ rescue => e
204
+ expect(e.message).to eql('Kaboom')
205
+ expect(e.backtrace.any? { |x| x.include?('dsl_block/lib/dsl_block.rb')} ).to be_false
206
+ end
207
+ end
208
+ end
209
+
210
+ context '#respond_to_missing?' do
211
+
212
+ it 'behaves as normal if no parent is set' do
213
+ dsl = dsl_class1.new {}
214
+ expect(dsl.respond_to?(:each)).to equal(false)
215
+ expect(dsl.respond_to?(:to_s)).to equal(true)
216
+ end
217
+
218
+ it 'also checks with the parent if it is set' do
219
+ dsl = dsl_class1.new(Array.new) {}
220
+ expect(dsl.respond_to?(:each)).to equal(true)
221
+ expect(dsl.respond_to?(:to_s)).to equal(true)
222
+ end
223
+
224
+ end
225
+
226
+ context '#method_missing?' do
227
+
228
+ it 'behaves as normal if no parent is set' do
229
+ dsl = dsl_class1.new {}
230
+ expect { dsl.each }.to raise_error(NoMethodError)
231
+ end
232
+
233
+ it 'relays the call to the parent if it is set' do
234
+ dsl = dsl_class1.new(Array.new) {}
235
+ expect { dsl.each }.not_to raise_error
236
+ end
237
+
238
+ end
239
+
240
+ end
241
+
@@ -0,0 +1,114 @@
1
+ require 'simplecov'
2
+ SimpleCov.start('test_frameworks')
3
+ require 'rspec/autorun'
4
+ require 'dsl_block'
5
+
6
+ RSpec.configure do |config|
7
+ config.order = 'random'
8
+ end
9
+
10
+ # Due to the nature of these tests, the number of discreete classes involved, and scoping issues
11
+ # the memoizaton will occur here instead of the standard rspec 'let' statements. Feel free to
12
+ # move these back to standard let statements if the specs can be kept as clean or cleaner
13
+
14
+
15
+ # Generic dsl class. Used in the specs as the outermost block
16
+ def dsl_class1
17
+ @dsl_class1||= Class.new(DslBlock).tap do |klass|
18
+ def klass.name
19
+ 'Dsl1'
20
+ end
21
+ end
22
+ end
23
+
24
+ # Generic dsl class. Used in the specs as the middle block
25
+ def dsl_class2
26
+ @dsl_class2 ||= Class.new(DslBlock).tap do |klass|
27
+ def klass.name
28
+ 'Dsl2'
29
+ end
30
+ end
31
+ end
32
+
33
+ # Generic dsl class. Used in the specs as the innermst block
34
+ def dsl_class3
35
+ @dsl_class3 ||= Class.new(DslBlock).tap do |klass|
36
+ def klass.name
37
+ 'Dsl3'
38
+ end
39
+ end
40
+ end
41
+
42
+ # The command the outermost dsl block is usually called by
43
+ def dsl_class1_command
44
+ dsl_class1.name.underscore.to_sym
45
+ end
46
+
47
+ # The command the middle dsl block is usually called by
48
+ def dsl_class2_command
49
+ dsl_class2.name.underscore.to_sym
50
+ end
51
+
52
+ # The command the innermost dsl block is usually called by
53
+ def dsl_class3_command
54
+ dsl_class3.name.underscore.to_sym
55
+ end
56
+
57
+ # Reset all of the classes for a new test
58
+ def dsl_reset
59
+ @dsl_class1 = nil
60
+ @dsl_class2 = nil
61
+ @dsl_class3 = nil
62
+ end
63
+
64
+
65
+
66
+
67
+ # The following are helpers to DRY up the tests.
68
+ # The numbers after 'dsl' are references to the order the dsl blocks are nested.
69
+
70
+
71
+ # No nested dsl. Block given is run at the ellipsis
72
+ # dsl_class1 do
73
+ # ...
74
+ # end
75
+ def dsl1(auto_yield=true, &block)
76
+ dsl = dsl_class1.new(&block)
77
+ auto_yield ? dsl.yield : dsl
78
+ end
79
+
80
+ # Nested dsl. Block given is run at the ellipsis
81
+ # dsl_class1 do
82
+ # dsl_class2 do
83
+ # ...
84
+ # end
85
+ # end
86
+ def dsl12(auto_yield=true, propigate12=false,&block)
87
+ dsl_class2.add_command_to(dsl_class1, propigate12)
88
+ command = dsl_class2_command
89
+ dsl = dsl_class1.new do
90
+ self.send(command, &block)
91
+ end
92
+ auto_yield ? dsl.yield : dsl
93
+ end
94
+
95
+ # Double nested dsl. Block given is run at the ellipsis
96
+ # dsl_class1 do
97
+ # dsl_class2 do
98
+ # dsl_class3 do
99
+ # ...
100
+ # end
101
+ # end
102
+ # end
103
+ def dsl123(auto_yield=true, propigate12=false, propigate23=false, &block)
104
+ dsl_class2.add_command_to(dsl_class1, propigate12)
105
+ dsl_class3.add_command_to(dsl_class2, propigate23)
106
+ command2 = dsl_class2_command
107
+ command3 = dsl_class3_command
108
+ dsl = dsl_class1.new do
109
+ self.send(command2) do
110
+ self.send(command3, &block)
111
+ end
112
+ end
113
+ auto_yield ? dsl.yield : dsl
114
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dsl_block
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Frank Hall
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.14.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 2.14.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.7.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.7.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: rdoc
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 4.0.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 4.0.0
97
+ description: DslBlock is a quick and simple DSL creator.
98
+ email:
99
+ - ChapterHouse.Dune@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - dsl_block.gemspec
111
+ - lib/dsl_block.rb
112
+ - lib/dsl_block/executor.rb
113
+ - lib/dsl_block/version.rb
114
+ - spec/lib/dsl_block/dsl_executor_spec.rb
115
+ - spec/lib/dsl_block_spec.rb
116
+ - spec/spec_helper.rb
117
+ homepage: http://chapterhouse.github.io/dsl_block
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.0.3
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Quick and simple DSL creator.
141
+ test_files:
142
+ - spec/lib/dsl_block/dsl_executor_spec.rb
143
+ - spec/lib/dsl_block_spec.rb
144
+ - spec/spec_helper.rb
145
+ has_rdoc: