dsl_block 1.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 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: