dsl_block 1.0.0 → 2.0.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.
- checksums.yaml +4 -4
- data/.gitignore +17 -17
- data/.rspec +2 -2
- data/Gemfile +4 -4
- data/LICENSE.txt +22 -22
- data/README.md +82 -82
- data/Rakefile +15 -15
- data/dsl_block.gemspec +29 -29
- data/lib/dsl_block.rb +168 -159
- data/lib/dsl_block/executor.rb +37 -37
- data/lib/dsl_block/version.rb +10 -10
- data/spec/lib/dsl_block/dsl_executor_spec.rb +60 -60
- data/spec/lib/dsl_block_spec.rb +251 -241
- data/spec/spec_helper.rb +114 -114
- metadata +3 -4
data/lib/dsl_block/executor.rb
CHANGED
@@ -1,37 +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
|
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
|
data/lib/dsl_block/version.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
class DslBlock
|
2
|
-
#
|
3
|
-
VERSION = '
|
4
|
-
|
5
|
-
module Version # :nodoc: all
|
6
|
-
MAJOR, MINOR, PATCH, *OTHER = VERSION.split('.')
|
7
|
-
NUMBERS = [MAJOR, MINOR, PATCH, *OTHER]
|
8
|
-
end
|
9
|
-
|
10
|
-
end
|
1
|
+
class DslBlock
|
2
|
+
# 2.0.0
|
3
|
+
VERSION = '2.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
|
@@ -1,61 +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
|
-
|
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
61
|
end
|
data/spec/lib/dsl_block_spec.rb
CHANGED
@@ -1,241 +1,251 @@
|
|
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,
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
dsl
|
99
|
-
|
100
|
-
expect(dsl.
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
expect
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
context '
|
154
|
-
|
155
|
-
it 'shows the
|
156
|
-
|
157
|
-
|
158
|
-
dsl_class1.commands
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
expect(
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
dsl.
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
it '
|
229
|
-
dsl = dsl_class1.new {}
|
230
|
-
expect
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
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, :command_name => :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
|
+
dsl_class1.send(:define_method, :foo) { |x| 'foo' * x }
|
94
|
+
dsl_class1.commands :foo
|
95
|
+
|
96
|
+
dsl = dsl12(false, true) { foo(2) }
|
97
|
+
|
98
|
+
expect(dsl.yield).to eql('foofoo')
|
99
|
+
# Prove we can call it normally
|
100
|
+
expect(dsl.foo(1)).to eql('foo')
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'will not propagate parent block commands that aren\'t marked as commands' do
|
104
|
+
dsl_class1.send(:define_method, :foo) { |x| 'foo' * x }
|
105
|
+
# Unlike above, :foo will not added to the list of commands at this point.
|
106
|
+
|
107
|
+
dsl = dsl12(false) { foo(2) }
|
108
|
+
|
109
|
+
expect { dsl.yield }.to raise_error(NameError)
|
110
|
+
# Prove we can call it normally
|
111
|
+
expect(dsl.foo(1)).to eql('foo')
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
context '.new' do
|
120
|
+
|
121
|
+
it 'requires a block' do
|
122
|
+
expect{dsl_class1.new}.to raise_error(ArgumentError, 'block must be provided')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'accepts the block in the options hash' do
|
126
|
+
block = Proc.new {}
|
127
|
+
expect{dsl_class1.new(:block => block) }.to_not raise_error
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'stores the block for later execution' do
|
131
|
+
block = Proc.new {}
|
132
|
+
dsl = dsl_class1.new(&block)
|
133
|
+
expect(dsl.instance_variable_get(:@block)).to equal(block)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'uses regular block over options block' do
|
137
|
+
block1 = Proc.new {}
|
138
|
+
block2 = Proc.new {}
|
139
|
+
dsl = dsl_class1.new(:block => block1, &block2)
|
140
|
+
expect(dsl.instance_variable_get(:@block)).to equal(block2)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'can also take a parent object' do
|
144
|
+
object = Object.new
|
145
|
+
dsl = dsl_class1.new(:parent => object) {}
|
146
|
+
expect(dsl.instance_variable_get(:@parent)).to equal(object)
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
context '#_commands' do
|
152
|
+
|
153
|
+
context 'without a parent object' do
|
154
|
+
|
155
|
+
it 'shows only the dsl class commands and the Kernel.methods available to the block passed' do
|
156
|
+
dsl_class1.commands :foo, :bar
|
157
|
+
dsl = dsl1(false) {}
|
158
|
+
expect(dsl._commands.sort).to eql((dsl_class1.commands + Kernel.methods).uniq.sort)
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'with a DslBlock parent' do
|
164
|
+
|
165
|
+
it 'shows the dls class commands, the parent._commands, and the Kernel.methods available to the block passed' do
|
166
|
+
dsl_class2.send(:define_method, :true_self) { self }
|
167
|
+
dsl_class2.commands :true_self
|
168
|
+
dsl_class1.commands :foo, :bar
|
169
|
+
dsl2_instance = dsl12(true, true) { true_self }
|
170
|
+
|
171
|
+
expect(dsl2_instance._commands.sort).to eql((dsl_class1.commands + dsl_class2.commands + Kernel.methods).sort)
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'with a generic Object parent' do
|
177
|
+
|
178
|
+
it 'shows the dls class commands, the object.public_methods, and the Kernel.methods available to the block passed' do
|
179
|
+
array = Array.new
|
180
|
+
dsl1_instance = dsl_class1.new(:parent => array) {}
|
181
|
+
expect(dsl1_instance._commands.sort).to eql((Kernel.methods + dsl_class1.commands + array.public_methods).sort.uniq)
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
context '#yield' do
|
189
|
+
|
190
|
+
it 'yields the block given at instantiation' do
|
191
|
+
dsl = dsl_class1.new { 3 }
|
192
|
+
expect(dsl.yield).to equal(3)
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'creates an executor to evaluate the block' do
|
196
|
+
dsl = dsl_class1.new {}
|
197
|
+
DslBlock::Executor.should_receive(:new).with(dsl).and_call_original
|
198
|
+
dsl.yield
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'isolates the block by evaluating it in the context of the executor' do
|
202
|
+
block = Proc.new {}
|
203
|
+
dsl = dsl_class1.new(&block)
|
204
|
+
executor = DslBlock::Executor.new(dsl)
|
205
|
+
executor.should_receive(:instance_eval).with(&block)
|
206
|
+
DslBlock::Executor.stub(:new).and_return(executor)
|
207
|
+
dsl.yield
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'cleans up any backtraces by removing itself from the call stack' do
|
211
|
+
begin
|
212
|
+
dsl123 { raise 'Kaboom' }
|
213
|
+
rescue => e
|
214
|
+
expect(e.message).to eql('Kaboom')
|
215
|
+
expect(e.backtrace.any? { |x| x.include?('dsl_block/lib/dsl_block.rb')} ).to be_false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context '#respond_to_missing?' do
|
221
|
+
|
222
|
+
it 'behaves as normal if no parent is set' do
|
223
|
+
dsl = dsl_class1.new {}
|
224
|
+
expect(dsl.respond_to?(:each)).to equal(false)
|
225
|
+
expect(dsl.respond_to?(:to_s)).to equal(true)
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'also checks with the parent if it is set' do
|
229
|
+
dsl = dsl_class1.new(:parent => Array.new) {}
|
230
|
+
expect(dsl.respond_to?(:each)).to equal(true)
|
231
|
+
expect(dsl.respond_to?(:to_s)).to equal(true)
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
context '#method_missing?' do
|
237
|
+
|
238
|
+
it 'behaves as normal if no parent is set' do
|
239
|
+
dsl = dsl_class1.new {}
|
240
|
+
expect { dsl.each }.to raise_error(NoMethodError)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'relays the call to the parent if it is set' do
|
244
|
+
dsl = dsl_class1.new(:parent => Array.new) {}
|
245
|
+
expect { dsl.each }.not_to raise_error
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|