building_blocks 0.0.1

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: a8b00f6d31a66e4ee00c4617a1ea5542407a060d
4
+ data.tar.gz: d68f14603537f03ce13da0d30ccc642e5354153e
5
+ SHA512:
6
+ metadata.gz: 96a5948aa3fe772601b35ecbea6bcdbfcda56ebe8d0895a21a486077c04d808c2918fdc92a8199ec95854d2685e47cfe526198967c7a68a37c6e08e8bcf9415f
7
+ data.tar.gz: d7f7ec68e9dc53d0b60c913f54489326db92ac8eb16660aa3285839207523429f2158acce609c2d4ce27d5af928ddf9bfa13e52735d53da03dddb6581bef8a14
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/.travis.yml ADDED
@@ -0,0 +1,22 @@
1
+ branches:
2
+ only:
3
+ - master
4
+
5
+ before_install:
6
+ - env
7
+
8
+ before_script:
9
+ - bundle exec rake
10
+
11
+ bundler_args: --without doc
12
+
13
+ cache:
14
+ - apt
15
+ - bundler
16
+
17
+ language: ruby
18
+
19
+ rvm:
20
+ - 1.9.3
21
+ - 2.0.0
22
+ - 2.1.1
data/.yardopts ADDED
@@ -0,0 +1,7 @@
1
+ --output-dir doc/
2
+ --readme README.md
3
+ --title BuildingBlocks
4
+ --markup=markdown
5
+ --protected
6
+ --private
7
+ lib/**/*.rb
data/Gemfile ADDED
@@ -0,0 +1,24 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :doc do
6
+ gem 'yard', require: false
7
+ end
8
+
9
+ group :test do
10
+ gem 'coveralls', :require => false
11
+ gem 'faker'
12
+ gem 'minitest'
13
+ gem 'mocha', :require => false
14
+ gem 'shoulda-context'
15
+ end
16
+
17
+ # Dev and test gems not needed for CI
18
+ unless ENV['CI']
19
+ group :development, :test do
20
+ gem 'guard'
21
+ gem 'guard-minitest'
22
+ gem 'pry-debugger', :platforms => :mri
23
+ end
24
+ end
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ guard :minitest do
2
+ ignore(%r{.*sw[pon]$})
3
+
4
+ watch(%r{^lib/building_blocks.rb}) { 'test' }
5
+ watch(%r{^lib/building_blocks/(.+)\.rb}) { |m| "test/unit/#{m[1]}_test.rb" }
6
+ watch(%r{^test/(?:.+/).+_test\.rb$})
7
+ watch('test/test_helper.rb') { 'test' }
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Interval Braining
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,107 @@
1
+ # BuildingBlocks
2
+ [![Build Status](https://travis-ci.org/interval-braining/building_blocks.png)](https://travis-ci.org/interval-braining/building_blocks)
3
+ [![Coverage Status](https://coveralls.io/repos/interval-braining/building_blocks/badge.png)](https://coveralls.io/r/interval-braining/building_blocks)
4
+ [![Code Climate](https://codeclimate.com/github/interval-braining/building_blocks.png)](https://codeclimate.com/github/interval-braining/building_blocks)
5
+ [![Dependency Status](https://gemnasium.com/interval-braining/building_blocks.png)](https://gemnasium.com/interval-braining/building_blocks)
6
+
7
+ Simple DSL for defining Builder classes with customized DSLs.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'building_blocks'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install building_blocks
22
+
23
+ ## Usage
24
+
25
+ BuildingBlocks strives to make it easy to create customizable block-based DSLs
26
+ for building all variety of objects.
27
+
28
+ This is achieved through the use of Builder objects. At their core, Builders are
29
+ any object that defines a `build` method that takes in a block and returns an
30
+ Object. How that block is used and what Object is returned can depend greatly on
31
+ the Builder being used. In some cases the block could be ignored entirely. In
32
+ other cases the Object returned could be a JSON string. It all depends on the
33
+ Builder.
34
+
35
+ Because Builders are just Plain Old Ruby Objects, there are no restrictions on
36
+ how Builders can be defined or what they can accomplish during their build
37
+ process. However, since there are a few common patterns employed by most
38
+ Builders, BuildingBlocks offers a Buillder Builder that allows for defining
39
+ Builder objects using a simple DSL.
40
+
41
+ ### BuilderBuilder DSL
42
+ Though BuildingBlocks is not itself a Builder, it does implement a `build`
43
+ method that delegates all calls to the configured
44
+ `BuildingBlocks.default_builder`. By default the `default_builder` is configured
45
+ to use `BuildingBlocks::Builders::BuilderBuilder`, a sort of meta-Builder in that
46
+ it's a Builder that also happens to build other valid Builders.
47
+
48
+ ```ruby
49
+ # Creating a new builder class using the default builder DSL
50
+ Point = Struct.new(:x, :y)
51
+ PointBuilder = BuildingBlocks.build do
52
+ resource_class Point
53
+ attribute :x
54
+ attribute :y
55
+ end
56
+
57
+ # Creating a new Point instance using PointBuilder
58
+ # Both the block arg, builder, and self are the PointBuilder instance
59
+ point = PointBuilder.build do |builder|
60
+ x 1
61
+ y 1
62
+ end
63
+ # => #<struct Point x=1, y=1>
64
+
65
+
66
+ # A more complex example of the default builder DSL
67
+ class Circle
68
+ attr_accessor :radius
69
+ attr_reader :center
70
+
71
+ def center=(point)
72
+ raise ArgumentError, 'Bad point' unless point.is_a?(Point)
73
+ @center = point
74
+ end
75
+ end
76
+
77
+ CircleBuilder = BuildingBlocks.build do
78
+ resource_class Circle
79
+ attribute :radius
80
+ builder :center, :center=, PointBuilder
81
+ delegate :x, :x=, :center
82
+ delegate :y, :y=, :center
83
+ end
84
+
85
+ circle = CircleBuilder.build do |c|
86
+ radius 5
87
+ center do |m|
88
+ x 5
89
+ y 5
90
+ end
91
+
92
+ # Override the previously set value for y
93
+ y 1
94
+ end
95
+ # => #<Circle @center=#<struct Point x=5, y=1>, @radius=5>
96
+ ```
97
+
98
+ More information on the default builder definition DSL can be found in the
99
+ [docs](http://rubydoc.info/gems/building_blocks/0.0.1/frames)
100
+
101
+ ## Contributing
102
+
103
+ 1. Fork it ( https://github.com/interval-braining/building_blocks/fork )
104
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
105
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
106
+ 4. Push to the branch (`git push origin my-new-feature`)
107
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake.application.options.suppress_backtrace_pattern = /.*/
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.pattern = 'test/**/*_test.rb'
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'building_blocks/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'building_blocks'
8
+ spec.version = BuildingBlocks::VERSION
9
+ spec.authors = ['Danny Guinther']
10
+ spec.email = ['dannyguinther@gmail.com']
11
+ spec.summary = %q{Block-based Object Builders}
12
+ spec.description = %q{Simple DSL for defining block-based Object builders}
13
+ spec.homepage = 'https://github.com/interval-braining/building_blocks'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.test_files = spec.files.grep(%r{^test/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler', '~> 1.5'
21
+ spec.add_development_dependency 'rake', '~> 0'
22
+ end
@@ -0,0 +1,59 @@
1
+ require 'forwardable'
2
+ require 'building_blocks/version'
3
+ require 'building_blocks/builders/builder_builder'
4
+
5
+ # Primary BuildingBlocks object providing an interface to build/define other
6
+ # builders using builders that build new builder objects.
7
+ module BuildingBlocks
8
+
9
+ class << self
10
+ extend Forwardable
11
+ def_delegator :default_builder, :build
12
+ end
13
+
14
+ # The default builder used for defining new builders when {build build}
15
+ # is invoked.
16
+ # @see build
17
+ # @see BuildingBlocks::Builders::BuilderBuilder
18
+ DEFAULT_DEFINITION_BUILDER = BuildingBlocks::Builders::BuilderBuilder
19
+
20
+ # Returns the object currently configured for use when defining new builders
21
+ # using {build build}. If a custom value has not been configured the
22
+ # {DEFAULT_DEFINITION_BUILDER default builder} is returned.
23
+ # @return [#build] The builder to be used for defining new builders when
24
+ # {build build} is invoked.
25
+ # @see build
26
+ # @see DEFAULT_DEFINITION_BUILDER
27
+ def self.default_builder
28
+ return @default_builder || DEFAULT_DEFINITION_BUILDER
29
+ end
30
+
31
+ # Sets the class that is used when defining new builders when using {build
32
+ # build} to the provided **builder**. If a value of `nil` or `false` is
33
+ # provided the {DEFAULT_DEFINITION_BUILDER default builder} will be
34
+ # used.
35
+ # @param [#build] builder The object to be used when defining new builders
36
+ # when using {build build}.
37
+ # @raise [ArgumentError] Raised if the object provided does not implement
38
+ # a #build method and is neither nil nor false.
39
+ # @return [#build] The builder argument provided is returned.
40
+ # @see DEFAULT_DEFINITION_BUILDER
41
+ # @see build
42
+ def self.default_builder=(builder)
43
+ if !!builder && !builder.respond_to?(:build)
44
+ raise ArgumentError, 'Expected object that responds to #build'
45
+ end
46
+ @default_builder = builder
47
+ end
48
+
49
+ # Namespace for builder classes
50
+ module Builders; end
51
+
52
+ # @!method self.build(*args, &block)
53
+ # Builds a new object using the the {default_builder default builder}. Any
54
+ # **args** or **block** provided are delegated directly to the `build` method
55
+ # of the {default_builder default builder}.
56
+ # @return [Object] The result of invoking `build` on the
57
+ # {#default_builder default builder}.
58
+ # @see default_builder
59
+ end
@@ -0,0 +1,341 @@
1
+ module BuildingBlocks
2
+ module Builders
3
+
4
+ # A builder that builds other builders using a minimal definition DSL.
5
+ # In brief, classes generated by this builder offer a number of built-in DSL
6
+ # macros that are used to describe how methods called against instances of
7
+ # the generated builder class are translated into actions to be taken on the
8
+ # {InstanceMethods#instance object instance} being built by the generated
9
+ # builder instance.
10
+ class BuilderBuilder
11
+
12
+ # Generates a new builder class with a number of built-in DSL methods used
13
+ # to describe how actions taken against instances of the builder class are
14
+ # translated into actions to be taken on the {InstanceMethods#instance
15
+ # object instance} being built. Further information on the available DSL
16
+ # methods can be found in {ClassInstanceMethods}.
17
+ # @return [Class] The generated builder class
18
+ # @see ClassInstanceMethods
19
+ def self.build
20
+ klass = Class.new
21
+ klass.extend(ClassInstanceMethods)
22
+ klass.send(:include, InstanceMethods)
23
+ klass.module_eval(&Proc.new) if block_given?
24
+ klass
25
+ end
26
+
27
+ # Methods available to classes generated by {BuilderBuilder
28
+ # BuilderBuilder}. Provides a generic, declarative DSL for describing
29
+ # custom builder classes.
30
+ module ClassInstanceMethods
31
+ include Forwardable
32
+
33
+ # The default means by which a new instance of the
34
+ # {ClassInstanceMethods#resource_class resource class} is initialized.
35
+ # @see {ClassInstanceMethods#initialize_with)
36
+ DEFAULT_INITIALIZE_WITH = lambda { |instance| resource_class.new }
37
+
38
+ # Declares a delegate method that will be available to instances of the
39
+ # generated builder class that describes a simple attribute that can be
40
+ # delegated directly to an existing method affiliated with the object
41
+ # instance being built.
42
+ #
43
+ # Though the underlying use of {delegate delegate} gives this method an
44
+ # enormous amount of potential power, the {attribute attribute} method
45
+ # is intended for use in fairly simple situations where delegation is
46
+ # required and the `attribute` descriptor provides some semantic value.
47
+ # More complex interactions should consider using the
48
+ # {delegate delegate} method directly to give library users some insight
49
+ # into the intention for more complex behavior.
50
+ # @param [String,Symbol] attr_name The name of the method that will be
51
+ # available to instances of the generated builder class that when
52
+ # invoked will trigger the **receiver_method** on the **receiver**.
53
+ # @param [String,Symbol] receiver_method The name of the method that
54
+ # should be invoked on the **receiver** object when the **attr_name**
55
+ # method is invoked. Defaults to a setter form of **attr_name**.
56
+ # @param [String,Symbol] receiver The object or method of the
57
+ # generated builder instance that should receive invocations of
58
+ # **receiver_method** when the **attr_name** method is called.
59
+ # Defaults to the {InstanceMethods#instance object instance} being
60
+ # built.
61
+ #
62
+ # If a String or Symbol value is provided for **receiver**, the value
63
+ # provided is resolved against the {InstanceMethods#instance object
64
+ # instance} being built at runtime to determine the appropriate
65
+ # receiver.
66
+ #
67
+ # For example if a value of :foo were provided for the receiver
68
+ # argument, at runtime, the receiver would be resolved to the object
69
+ # returned by invoking :foo on the {InstanceMethods#instance object
70
+ # instance} being built. Once resolved, the **receiver_method** would
71
+ # then be invoked on the receiver with any approprate arguments.
72
+ # @return [Symbol] The Symbol identifier form of **attr_name**
73
+ # identifying the method that will be available to instances of the
74
+ # generated builder class.
75
+ # @see #delegate
76
+ def attribute(attr_name, receiver_method = :"#{attr_name}=", receiver = nil)
77
+ delegate(attr_name, receiver_method, receiver)
78
+ return attr_name.to_sym
79
+ end
80
+
81
+ # By default, builds a new instance of the configured {#resource_class
82
+ # resource class}. This is achieved by evaluating the configured
83
+ # {#initialize_with} proc in the context of an instance of the generated
84
+ # builder class to generate a new object that will be used by the
85
+ # builder instance. If a block is given, the block is evaluated in the
86
+ # context of the builder instance after initialization.
87
+ # @param [Proc] block Optional block evaluated by the generated builder
88
+ # class instance after the {InstanceMethods#instance object instance}
89
+ # being built has been initialized.
90
+ # @return [Object] Returns the generated {InstanceMethods#instance
91
+ # object instance}.
92
+ # @see #initialize_with
93
+ def build
94
+ instance = instance_eval(&self.initialize_with)
95
+ builder = new(instance)
96
+ builder.instance_eval(&Proc.new) if block_given?
97
+ instance
98
+ end
99
+
100
+ # Declares a method, **attr_name**, available to instances of the
101
+ # generated builder class, that when called delegates to a
102
+ # **builder_class** to generate an object that is then passed to the
103
+ # **receiver_method** of the **receiver**.
104
+ #
105
+ # If a **builder_class** is given, the provided **builder_class** is
106
+ # used to generate the object for the receiver. If no **builder_class**
107
+ # is given and instead a **block** is provided, the given block is
108
+ # treated as an anonymous builder definition that is passed to
109
+ # {BuildingBlocks.build} to generate a new builder class that will be
110
+ # used later when **attr_name** is invoked on an instance of the
111
+ # generated builder class.
112
+ #
113
+ # See {InstanceMethods#example_builder_attr} for details of the method
114
+ # invoked when **attr_name** is called on an instance of the generated
115
+ # builder class.
116
+ #
117
+ # @param [String,Symbol] attr_name The name that will be used to define
118
+ # the method that will be available to instances of the generated
119
+ # builder class that when invoked will trigger the **builder_class**.
120
+ # @param [String,Symbol] receiver_method The name of the method that
121
+ # should be invoked upon the **receiver** object when the
122
+ # **attr_name** method is invoked. Defaults to a setter form of
123
+ # **attr_name**.
124
+ # @param [Class] builder_class The builder class that should be used
125
+ # to generate new objects when the **attr_name** method is invoked.
126
+ # Cannot be used in conjunction with a given **block**.
127
+ # @param [Proc,String,Symbol] receiver The object or method of the
128
+ # generated builder instance that should receive invocations of
129
+ # **receiver_method** when the **attr_name** method is called.
130
+ # Defaults to the {InstanceMethods#instance object instance} being
131
+ # built.
132
+ #
133
+ # If a String or Symbol value is provided for **receiver**, the value
134
+ # provided is resolved against the {InstanceMethods#instance object
135
+ # instance} being built at runtime to determine the appropriate
136
+ # receiver.
137
+ #
138
+ # For example if a value of :foo were provided for the **receiver**
139
+ # argument, at runtime, the **receiver** would be resolved to the
140
+ # object returned by invoking :foo on the {InstanceMethods#instance
141
+ # object instance} being built. Once resolved, the **receiver_method**
142
+ # would then be invoked on the **receiver** with any approprate
143
+ # arguments.
144
+ #
145
+ # If a Proc is provided for the **receiver** argument, the Proc will
146
+ # be evaluated at runtime in the context of the
147
+ # {InstanceMethods#instance object instance} being built and should
148
+ # return the intended **receiver** object.
149
+ # @param [Proc] block A block may be provided in place of a predefined
150
+ # **builder_class**. If such a block is provided, the given block is
151
+ # evaluated by {BuildingBlocks.build} to generate a new anonymous
152
+ # builder class. Cannot be used in conjunction with a predefined
153
+ # **builder_class**.
154
+ # @raise [ArgumentError] Raised if both a **builder_class** and a
155
+ # **block** are given or neither a **builder_class** nor **block** are
156
+ # given.
157
+ # @return [Symbol] The Symbol identifier form of **attr_name**
158
+ # identifying the method that will be available to instances of the
159
+ # generated builder class.
160
+ # @see BuildingBlocks.build
161
+ # @see #define_builder_method
162
+ # @see InstanceMethods#example_builder_attr
163
+ def builder(attr_name, receiver_method = "#{attr_name}=", builder_class = nil, receiver = nil)
164
+ if block_given? ^ builder_class.nil?
165
+ raise ArgumentError, 'Either a builder class or a builder definition is required'
166
+ elsif block_given?
167
+ builder_class = BuildingBlocks.build(&Proc.new)
168
+ end
169
+
170
+ if receiver.is_a?(Proc)
171
+ receiver_proc = receiver
172
+ elsif receiver.is_a?(Symbol) || receiver.is_a?(String)
173
+ receiver_proc = instance_eval("lambda { |i| @instance.#{receiver} }")
174
+ else
175
+ receiver_proc = lambda { |i| @instance }
176
+ end
177
+
178
+ define_builder_method(attr_name, receiver_method, builder_class, receiver_proc)
179
+ attr_name.to_sym
180
+ end
181
+
182
+ # Declares a delegate method that will be available to instances of the
183
+ # generated builder class that describes a behavior that should be
184
+ # delegated from the builder instance to the **receiver_method** of the
185
+ # derived **receiver**.
186
+ #
187
+ # @param [String,Symbol] attr_name An identifer that will be used to
188
+ # name the delegate method defined on the builder instance.
189
+ # @param [String,Symbol] receiver_method The method that should be
190
+ # invoked on the **receiver** when the delegate method is invoked.
191
+ # Defaults to the same value provided for **attr_name**.
192
+ # @param [String,Symbol] receiver The object or method of the
193
+ # generated builder instance that should receive invocations of
194
+ # **receiver_method** when the **attr_name** method is called.
195
+ # Defaults to the {InstanceMethods#instance object instance} being
196
+ # built.
197
+ #
198
+ # If a String or Symbol value is provided for **receiver**, the value
199
+ # provided is resolved against the {InstanceMethods#instance object
200
+ # instance} being built at runtime to determine the appropriate
201
+ # **receiver**.
202
+ #
203
+ # For example if a value of :foo were provided for the **receiver**
204
+ # argument, at runtime, the **receiver** would be resolved to the
205
+ # object returned by invoking :foo on the {InstanceMethods#instance
206
+ # object instance} being built. Once resolved, the **receiver_method**
207
+ # would then be invoked on the **receiver** with any approprate
208
+ # arguments.
209
+ # @return [Symbol] The Symbol identifier form of **attr_name**
210
+ # identifying the method that will be available to instances of the
211
+ # generated builder class.
212
+ def delegate(attr_name, receiver_method = attr_name, receiver = nil)
213
+ receiver = receiver.nil? || receiver.empty? ? :@instance : :"@instance.#{receiver}"
214
+ def_delegator(receiver, receiver_method, attr_name)
215
+ attr_name.to_sym
216
+ end
217
+
218
+ # When no block is given, returns the proc currently configured to be
219
+ # invoked when a new instance of {#resource_class resource class} is
220
+ # needed. If a custom proc has not been configured, returns the
221
+ # {DEFAULT_INITIALIZE_WITH default initialization proc}.
222
+ #
223
+ # When a block is given, the method acts as a setter and the provided
224
+ # block is preserved and later used to initialize new instances of
225
+ # {#resource_class resource class} as they are required.
226
+ # @return [Proc] The proc that will be used to initialize new instances
227
+ # of {#resouce_class resource class}.
228
+ def initialize_with
229
+ @initialize_with = Proc.new if block_given?
230
+ @initialize_with || DEFAULT_INITIALIZE_WITH
231
+ end
232
+
233
+ # Sets a custom proc to be used when initializing new instances of
234
+ # {#resource_class resource class}.
235
+ # @param [Proc] initializer The proc to be used to initialize new
236
+ # instances or {resource_class resource class}.
237
+ # @raise [ArgumentError] Raised if the provided **initializer** is
238
+ # missing or is not a proc.
239
+ # @return [Proc] The **initializer** argument provided is returned.
240
+ def initialize_with=(initializer)
241
+ unless initializer.is_a?(Proc)
242
+ raise ArgumentError, "Expected proc initializer, got #{initializer.class.name}"
243
+ end
244
+ @initialize_with = initializer
245
+ end
246
+
247
+ # The class that the generated builder class should generate instances
248
+ # of. When no value is passed for the **klass** argument, returns the
249
+ # currently configured resource class. If a value is provided for
250
+ # **klass**, the method acts as a setter and the provided **klass**
251
+ # argument is preserved and later used for the resource class of the
252
+ # generated builder class. For further information on how instances of
253
+ # the {#resource_class resource class} are built, see {#initialize_with}.
254
+ # @return [Object] The class that the generated builder class will
255
+ # generate instances of.
256
+ # @see #initialize_with
257
+ def resource_class(klass = nil)
258
+ @resource_class = klass unless klass.nil?
259
+ @resource_class
260
+ end
261
+
262
+ # Sets the class that the generated builder class will generate
263
+ # instances of. For further information on how instances of the
264
+ # {resource_class resource class} are built, see {#initialize_with}.
265
+ # @return [Object] The value given for the **klass** argument is
266
+ # returned.
267
+ # @see #initialize_with
268
+ def resource_class=(klass)
269
+ @resource_class = klass
270
+ end
271
+
272
+ private
273
+
274
+ # Handles the wiring required to invoke a builder when **attr_name** is
275
+ # invoked.
276
+ def define_builder_method(attr_name, receiver_method, builder_class, receiver_proc)
277
+ define_method(attr_name) do |existing_object = nil, &block|
278
+ begin
279
+ if block.nil? ^ !!existing_object
280
+ raise ArgumentError, 'Either a static instance or a builder proc is required'
281
+ end
282
+ receiver = instance_eval(&receiver_proc)
283
+ instance = block.nil? ? existing_object : builder_class.build(&block)
284
+ receiver.send(receiver_method, instance)
285
+ instance
286
+ rescue Exception
287
+ $@.delete_if{|s| %r"#{Regexp.quote(__FILE__)}"o =~ s}
288
+ ::Kernel::raise
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ # Methods available to instances of a generated builder class.
295
+ module InstanceMethods
296
+ # Initializes a new instance of the generated builder class. Requires
297
+ # the {ClassInstanceMethods#resource_class resource class} **instance**
298
+ # that the builder will populate to be provided.
299
+ # @param [Object] instance The object that should be populated by the
300
+ # builder instance.
301
+ def initialize(instance)
302
+ @instance = instance
303
+ end
304
+
305
+ # Returns the {InstanceMethods#instance object instance} being built by
306
+ # the builder instance.
307
+ # @return [Object] The {InstanceMethods#instance object instance} being
308
+ # built by the builder.
309
+ def instance
310
+ return @instance
311
+ end
312
+
313
+ # @!method example_builder_attr(existing_object = nil, &block)
314
+ # Reference for instance methods defined via {ClassInstanceMethods#builder}.
315
+ #
316
+ # This method is not actually defined, but serves as a reference for
317
+ # the instance method defined when an attribute is defined on the
318
+ # parent builder class using {ClassInstanceMethods#builder}.
319
+ #
320
+ # Invokes the `receiver_method` of `receiver` with the provided
321
+ # **existing_object** or a new object generated by invoking the
322
+ # *build* method of the derived `builder_class` with the given
323
+ # **block**.
324
+ # @param [Object] existing_object An existing object to use as the
325
+ # argument to `receiver_method`. Cannot be used in conjunction
326
+ # with a block.
327
+ # @param [Proc] block A block to be given to the *build* method of
328
+ # the derived `builder_class` to generate a new object. Cannot be
329
+ # used in conjunction with a provided **existing_object**.
330
+ # @raise [ArgumentError] Raised if both an **existing_object** and
331
+ # **block** are given or neither an **existing_object** nor
332
+ # **block** are provided.
333
+ # @return [Object] The provided **existing_object** or the object
334
+ # generated by invoking the *build* method of the `builder_class`
335
+ # with the given **block**.
336
+ # @see ClassInstanceMethods#builder
337
+ end
338
+
339
+ end
340
+ end
341
+ end
@@ -0,0 +1,4 @@
1
+ module BuildingBlocks
2
+ # The version of BuildingBlocks in use.
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,10 @@
1
+ if ENV['CI']
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+ end
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest/unit'
8
+ require 'mocha/setup'
9
+ require 'shoulda/context'
10
+ require 'building_blocks'
@@ -0,0 +1,406 @@
1
+ require 'test_helper'
2
+
3
+ module BuildingBlocks
4
+ module Builders
5
+ class BuilderBuilderTest < MiniTest::Test
6
+
7
+ context 'BuilderBuilder class methods' do
8
+
9
+ should 'generate a new Builder class without block given' do
10
+ builder = BuilderBuilder.build
11
+ assert_kind_of Class, builder
12
+ assert builder.ancestors.include?(BuildingBlocks::Builders::BuilderBuilder::InstanceMethods)
13
+ end
14
+
15
+
16
+ should 'generate a new Builder class with block given' do
17
+ new_block_arg = new_self = nil
18
+ # Validates block form
19
+ builder = BuilderBuilder.build do |subclass|
20
+ # Leave marker of instance evaluation
21
+ define_singleton_method(:builder_evaluated) { true }
22
+ # Test usage of block argument
23
+ subclass.resource_class = Hash
24
+ # Set up variables for evaluation back in the test
25
+ new_self = self
26
+ new_block_arg = subclass
27
+ end
28
+
29
+ # Validate DSL context variables
30
+ assert_equal builder, new_block_arg
31
+ assert_equal builder, new_self
32
+
33
+ # Validate assignments and markers
34
+ assert_equal Hash, builder.resource_class
35
+ assert_equal true, builder.builder_evaluated
36
+ end
37
+
38
+ end
39
+
40
+
41
+ context 'BuilderBuilder generated Builder class methods' do
42
+
43
+ setup do
44
+ @struct = struct = Struct.new(:key, :value)
45
+ @dict_builder = BuildingBlocks.build do
46
+ resource_class struct
47
+ attribute(:key)
48
+ attribute(:value)
49
+ end
50
+ @builder = BuildingBlocks.build
51
+ end
52
+
53
+
54
+ context '::attribute' do
55
+
56
+ should 'raise ArgumentError unless attribute name provided' do
57
+ assert_raises(ArgumentError) { BuildingBlocks.build { attribute } }
58
+ end
59
+
60
+
61
+ should 'delegate calls to attr_name to receiver via receiver_method' do
62
+ @builder.expects(:def_delegator).with(:'@instance.c', :b, :a,)
63
+ @builder.attribute(:a, :b, :c)
64
+ end
65
+
66
+
67
+ should 'delegate to attr_name= on receiver by default' do
68
+ @builder.expects(:def_delegator).with(:@instance, :a=, :a,)
69
+ @builder.attribute(:a)
70
+ end
71
+
72
+
73
+ should 'delegate directly to the @instance by default' do
74
+ @builder.expects(:def_delegator).with(:@instance, :a=, :a)
75
+ @builder.attribute(:a)
76
+ end
77
+
78
+
79
+ should 'return the Symbol form of the provided attr_name' do
80
+ result = @builder.attribute(:a)
81
+ assert_equal :a, result
82
+ end
83
+
84
+ end
85
+
86
+
87
+ context '::builder' do
88
+
89
+ should 'raise an error unless a Builder class xor proc is provided' do
90
+ assert_raises(ArgumentError) do
91
+ BuildingBlocks.build { builder(:foo) }
92
+ end
93
+
94
+ dict_builder = @dict_builder
95
+ assert_raises(ArgumentError) do
96
+ BuildingBlocks.build { builder(:foo, :foo=, dict_builder) { |i| } }
97
+ end
98
+ end
99
+
100
+
101
+ should 'raise an error unless an attr_name is provided' do
102
+ assert_raises(ArgumentError) { BuildingBlocks.build { builder } }
103
+ end
104
+
105
+
106
+ should 'return the Symbol form of the provided attr_name' do
107
+ dict_builder = @dict_builder
108
+ result = @builder.builder(:foo, :value=, dict_builder)
109
+ assert_equal :foo, result
110
+ end
111
+
112
+
113
+ should 'take a custom receiver method name' do
114
+ dict_builder, struct = @dict_builder, @struct
115
+ builder = BuildingBlocks.build do
116
+ resource_class struct
117
+ builder(:foo, :value=, dict_builder)
118
+ end
119
+ instance = builder.build { foo { key(:a); value(:b) } }
120
+ assert_equal :a, instance.value.key
121
+ assert_equal :b, instance.value.value
122
+ end
123
+
124
+
125
+ should 'take a custom receiver proc' do
126
+ dict_builder, struct = @dict_builder, @struct
127
+ builder = BuildingBlocks.build do
128
+ resource_class struct
129
+ builder(:foo, :value=, dict_builder, lambda { |i| String })
130
+ end
131
+ String.expects(:value=)
132
+ builder.build { foo { key(:a); value(:b) } }
133
+ end
134
+
135
+
136
+ should 'should accept a proc that is a Builder definition' do
137
+ struct = @struct
138
+ builder = BuildingBlocks.build do
139
+ resource_class struct
140
+ builder(:foo, :value=) do |i|
141
+ resource_class struct
142
+ attribute :bar, :key=
143
+ end
144
+ end
145
+ instance = builder.build { foo { bar :a } }
146
+ assert_equal :a, instance.value.key
147
+ end
148
+
149
+
150
+ should 'accept a custom builder class' do
151
+ dict_builder, struct = @dict_builder, @struct
152
+ db = dict_builder.build
153
+ dict_builder.expects(:build).returns(db)
154
+ builder = BuildingBlocks.build do
155
+ resource_class struct
156
+ builder(:foo, :value=, dict_builder)
157
+ end
158
+ instance = builder.build { foo { |i| } }
159
+ assert_equal db, instance.value
160
+ end
161
+
162
+
163
+ should 'accept a custom receiver identifier' do
164
+ dict_builder, struct = @dict_builder, @struct
165
+ builder = BuildingBlocks.build do
166
+ resource_class struct
167
+ builder(:value, :value=, dict_builder)
168
+ builder(:foo, :value=, dict_builder, :value)
169
+ end
170
+ instance = builder.build do
171
+ value { key(:a) }
172
+ foo { |i| value(:b) }
173
+ end
174
+ assert_equal :b, instance.value.value.value
175
+ end
176
+
177
+ end
178
+
179
+
180
+ context '::delegate' do
181
+
182
+ should 'raise ArgumentError unless method_name provided' do
183
+ assert_raises(ArgumentError) { @builder.delegate }
184
+ end
185
+
186
+
187
+ should 'create a delegator to @instance.receiver.receiver_method aliased to method_name' do
188
+ @builder.expects(:def_delegator).with(:'@instance.receiver', :receiver_method, :method_name)
189
+ @builder.delegate(:method_name, :receiver_method, :receiver)
190
+ end
191
+
192
+
193
+ should 'create a delegator to @instance.receiver_method aliased to method_name by default' do
194
+ @builder.expects(:def_delegator).with(:@instance, :receiver_method, :method_name)
195
+ @builder.delegate(:method_name, :receiver_method)
196
+ end
197
+
198
+
199
+ should 'create a delegator to @instance.method_name aliased to method_name by default' do
200
+ @builder.expects(:def_delegator).with(:@instance, :method_name, :method_name)
201
+ @builder.delegate(:method_name)
202
+ end
203
+
204
+
205
+ should 'return the symbol identifier form of attr_name' do
206
+ assert_equal :method_name, @builder.delegate('method_name')
207
+ end
208
+
209
+ end
210
+
211
+
212
+ context '::new' do
213
+
214
+ should 'accept a block and evaluate the block on the builder instance' do
215
+ struct = @dict_builder.build do
216
+ key :a
217
+ value 'a'
218
+ end
219
+ assert_equal :a, struct.key
220
+ assert_equal 'a', struct.value
221
+ end
222
+
223
+
224
+ should 'return the generated instance when no block given' do
225
+ the_struct = @struct.new
226
+ the_struct.key = :a
227
+ the_struct.value = 'a'
228
+ @struct.expects(:new).returns(the_struct)
229
+
230
+ struct = @dict_builder.build
231
+ assert_equal :a, struct.key
232
+ assert_equal 'a', struct.value
233
+ end
234
+
235
+
236
+ should 'utilize a custom initialize_with proc if available' do
237
+ @dict_builder.initialize_with do |builder|
238
+ instance = resource_class.new
239
+ instance.key = :b
240
+ instance.value = 'b'
241
+ instance
242
+ end
243
+
244
+ struct = @dict_builder.build
245
+ assert_equal :b, struct.key
246
+ assert_equal 'b', struct.value
247
+ end
248
+
249
+ end
250
+
251
+
252
+ context '::initialize_with' do
253
+
254
+ setup do
255
+ @initializer = lambda { |i| Hash.new }
256
+ @dict_builder.initialize_with(&@initializer)
257
+ end
258
+
259
+
260
+ should 'set @initialize_with if given a block' do
261
+ assert_equal @dict_builder.initialize_with, @initializer
262
+ assert_equal @dict_builder.instance_variable_get(:@initialize_with), @initializer
263
+ end
264
+
265
+
266
+ should 'return @initialize_with if no block given' do
267
+ assert_equal @dict_builder.instance_variable_get(:@initialize_with), @dict_builder.initialize_with
268
+ end
269
+
270
+ end
271
+
272
+
273
+ context '::initialize_with=' do
274
+
275
+ setup do
276
+ @initializer = lambda { |i| Hash.new }
277
+ end
278
+
279
+
280
+ should 'raise an error if a non-proc is provided' do
281
+ assert_raises(ArgumentError) do
282
+ @dict_builder.initialize_with = :foo
283
+ end
284
+ end
285
+
286
+
287
+ should 'set @initialize_with if given a proc' do
288
+ @dict_builder.initialize_with = @initializer
289
+ assert_equal @dict_builder.initialize_with, @initializer
290
+ assert_equal @dict_builder.instance_variable_get(:@initialize_with), @initializer
291
+ end
292
+
293
+ end
294
+
295
+
296
+ context '::resource_class' do
297
+
298
+ should 'return the current resource_class when no arguments are given' do
299
+ assert_equal nil, @builder.resource_class
300
+ @builder.resource_class = @struct
301
+ assert_equal @struct, @builder.resource_class
302
+ end
303
+
304
+
305
+ should 'set the current resource_class when an argument is given' do
306
+ @builder.resource_class(@struct)
307
+ assert_equal @struct, @builder.resource_class
308
+ end
309
+
310
+
311
+ should 'set and return the argument given when given an argument' do
312
+ assert_equal @struct, @builder.resource_class(@struct)
313
+ end
314
+
315
+ end
316
+
317
+
318
+ context '::resource_class=' do
319
+
320
+ should 'set the current resource_class' do
321
+ @builder.resource_class = @struct
322
+ assert_equal @struct, @builder.resource_class
323
+ end
324
+
325
+
326
+ should 'return the argument given' do
327
+ assert_equal @struct, @builder.resource_class = @struct
328
+ end
329
+
330
+ end
331
+
332
+ end
333
+
334
+
335
+ context 'BuilderBuilder generated Builder class instance methods' do
336
+
337
+ setup do
338
+ @struct = struct = Struct.new(:key, :value)
339
+ @dict_builder = BuildingBlocks.build do
340
+ resource_class struct
341
+ attribute(:key)
342
+ attribute(:value)
343
+ end
344
+ end
345
+
346
+
347
+ context '::builder generated instance method' do
348
+
349
+ setup do
350
+ dict_builder, struct = @dict_builder, @struct
351
+ @builder_instance = BuildingBlocks.build do
352
+ resource_class struct
353
+ builder(:foo, :value=, dict_builder)
354
+ end
355
+ end
356
+
357
+
358
+ should 'raise ArgumentError if given static instance and block or neither' do
359
+ assert_raises(ArgumentError) { @builder_instance.build { foo } }
360
+ assert_raises(ArgumentError) do
361
+ @builder_instance.build { foo(true) {|i| } }
362
+ end
363
+ end
364
+
365
+
366
+ should 'pass a static instance to the receiver method of the receiver' do
367
+ instance = @builder_instance.build { foo(true) }
368
+ assert_equal true, instance.value
369
+ end
370
+
371
+
372
+ should 'evaluate builder definition and send instance to the receiver method of receiver' do
373
+ instance = @builder_instance.build { foo { value(true) } }
374
+ assert_equal true, instance.value.value
375
+ end
376
+
377
+ end
378
+
379
+
380
+ context '#initialize' do
381
+
382
+ should 'require an instance' do
383
+ assert_raises(ArgumentError) { @dict_builder.new }
384
+ struct = @struct.new(:k, :v)
385
+ builder = @dict_builder.new(struct)
386
+ assert_equal struct, builder.instance
387
+ end
388
+
389
+ end
390
+
391
+
392
+ context '#instance' do
393
+
394
+ should 'return the object instance being built' do
395
+ struct = @struct.new(:k, :v)
396
+ builder = @dict_builder.new(struct)
397
+ assert_equal struct, builder.instance
398
+ end
399
+
400
+ end
401
+
402
+ end
403
+
404
+ end
405
+ end
406
+ end
@@ -0,0 +1,136 @@
1
+ require 'test_helper'
2
+
3
+ class BuildingBlocksTest < MiniTest::Test
4
+
5
+ DEFAULT_BUILDER = BuildingBlocks::DEFAULT_DEFINITION_BUILDER
6
+
7
+ context 'BuildingBlocks' do
8
+
9
+ should 'be defined' do
10
+ assert BuildingBlocks
11
+ end
12
+
13
+
14
+ context 'Factory methods' do
15
+
16
+ context '::build' do
17
+
18
+ setup do
19
+ @builder = BuildingBlocks.build
20
+ end
21
+
22
+
23
+ should 'return a default Builder instance when no block given' do
24
+ assert_kind_of Class, @builder
25
+ assert @builder.ancestors.include?(BuildingBlocks::Builders::BuilderBuilder::InstanceMethods)
26
+ end
27
+
28
+
29
+ should 'return an evaluated Builder instance when block is given' do
30
+ struct = Struct.new(:key, :value)
31
+
32
+ dict_builder = BuildingBlocks.build do |b|
33
+ resource_class struct
34
+ attribute :key
35
+ attribute :value
36
+ end
37
+
38
+ struct_dict_builder = BuildingBlocks.build do |i|
39
+ resource_class struct
40
+ attribute(:key)
41
+ builder(:value, :value=, dict_builder)
42
+ delegate(:inner_value, :value=, 'value')
43
+ end
44
+
45
+ dict = dict_builder.build do |b|
46
+ key(:a)
47
+ value(:b)
48
+ end
49
+ assert_equal :a, dict.key
50
+ assert_equal :b, dict.value
51
+
52
+ struct_dict = struct_dict_builder.build do |b|
53
+ key(:c)
54
+ value do
55
+ key(:d)
56
+ end
57
+ inner_value(:e)
58
+ end
59
+ assert_equal :c, struct_dict.key
60
+ assert_equal :d, struct_dict.value.key
61
+ assert_equal :e, struct_dict.value.value
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+
69
+ context 'configuration' do
70
+
71
+ setup do
72
+ @original_config = BuildingBlocks.default_builder
73
+ @simple_builder = Class.new do
74
+ attr_accessor :args, :block
75
+ def self.build(*args)
76
+ instance = new
77
+ instance.block = Proc.new if block_given?
78
+ instance.args = args
79
+ instance
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ teardown do
86
+ BuildingBlocks.default_builder = @original_config
87
+ end
88
+
89
+
90
+ context '::default_builder' do
91
+
92
+ should 'return @default_defintion_builder or DEFAULT_DEFINITION_BUILDER' do
93
+ BuildingBlocks.default_builder = nil
94
+ assert_equal BuildingBlocks.default_builder, DEFAULT_BUILDER
95
+ BuildingBlocks.default_builder = @simple_builder
96
+ assert_equal BuildingBlocks.default_builder, @simple_builder
97
+ end
98
+
99
+ end
100
+
101
+
102
+ context '::default_builder=' do
103
+
104
+ [nil, false].each do |value|
105
+ should "accept #{value.inspect} to reset the default_builder" do
106
+ BuildingBlocks.default_builder = @simple_builder
107
+ assert_equal BuildingBlocks.default_builder, @simple_builder
108
+ BuildingBlocks.default_builder = value
109
+ assert_equal BuildingBlocks.default_builder, DEFAULT_BUILDER
110
+ end
111
+ end
112
+
113
+
114
+ should 'raise an error if the proivded object does not respond to #build' do
115
+ BuildingBlocks.default_builder = nil
116
+ assert_equal BuildingBlocks.default_builder, DEFAULT_BUILDER
117
+ assert_raises(ArgumentError) do
118
+ BuildingBlocks.default_builder = Class.new
119
+ end
120
+ end
121
+
122
+
123
+ should 'set the default_builder' do
124
+ BuildingBlocks.default_builder = nil
125
+ assert_equal BuildingBlocks.default_builder, DEFAULT_BUILDER
126
+ BuildingBlocks.default_builder = @simple_builder
127
+ assert_equal BuildingBlocks.default_builder, @simple_builder
128
+ end
129
+
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+
136
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: building_blocks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Danny Guinther
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Simple DSL for defining block-based Object builders
42
+ email:
43
+ - dannyguinther@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".travis.yml"
50
+ - ".yardopts"
51
+ - Gemfile
52
+ - Guardfile
53
+ - LICENSE
54
+ - README.md
55
+ - Rakefile
56
+ - building_blocks.gemspec
57
+ - lib/building_blocks.rb
58
+ - lib/building_blocks/builders/builder_builder.rb
59
+ - lib/building_blocks/version.rb
60
+ - test/test_helper.rb
61
+ - test/unit/builders/builder_builder_test.rb
62
+ - test/unit/building_blocks_test.rb
63
+ homepage: https://github.com/interval-braining/building_blocks
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.2.2
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Block-based Object Builders
87
+ test_files:
88
+ - test/test_helper.rb
89
+ - test/unit/builders/builder_builder_test.rb
90
+ - test/unit/building_blocks_test.rb
91
+ has_rdoc: