Class: Gamera::Builder
- Inherits:
-
Object
- Object
- Gamera::Builder
- Extended by:
- Forwardable, Dsl
- Defined in:
- lib/gamera/builder.rb
Overview
Builders provide a generic, standard interface for creating/setting data in an app.
For easy creation of a builder use the with_options() class builder method. For example:
class UserBuilder < Builder.(:name, :bday, :nickname)
def build
User.create!(name: name, bday: bday, nickname: nickname)
end
end
The created builder will automatically have methods to set each of the options. In the example above the UserBuilder#with_nickname method will return a new UserBuilder
that has the specified nickname.
UserBuilder.new
.with_nickname("shorty")
.result
#=> User(name: nil, bday: nil, nickname: "shorty")
Sometimes the way you refer to values inside the builder may be different than how clients do. In that case, builders can define setter methods that use terminology that matches the client’s point of view.
class UserBuilder < Builder.(:name, :bday)
def born_on(a_date)
refine_with bday: a_date
end
end
Default values can be specified using the default_for
class method.
class UserBuilder < Builder.with_options(:name, :bday)
default_for :name, "Jane"
default_for :bday { Time.now }
end
You can handle type conversions using coercion methods.
class UserBuilder < Builder.(:name, :bday)
def name_coercion(new_name)
new_name ? new_name.to_s : "Bob"
end
end
To use this builder:
UserBuilder.new
.born_on(25.years.ago)
.result
#=> User(name: "Bob", bday: #<Date: Sat, 09 Dec 1989>)
or
UserBuilder.new
.with_bday(25.years.ago)
.result
#=> User(name: "Bob", bday: #<Date: Sat, 09 Dec 1989>)
or
UserBuilder.new(bday: 25.years.ago)
.result
#=> User(name: "Bob", bday: #<Date: Sat, 09 Dec 1989>)
Defined Under Namespace
Modules: Dsl
Class Method Summary collapse
-
.create_with(spec, &block) ⇒ Object
One way to create builders.
-
.with_options(*option_names) ⇒ Object
Another way to create builders.
Instance Method Summary collapse
-
#build ⇒ Object
Executes the builder.
-
#refine_with(alterations) ⇒ Object
Returns a clone of this object but with options listed in
alterations
updated to match. -
#result ⇒ Object
The object built by this builder.
Methods included from Dsl
Class Method Details
.create_with(spec, &block) ⇒ Object
One way to create builders.
b = Builder.create_with(name: "Bob", bday: 25.years.ago) do
User.create!(name: name, bday: bday)
end
b.build
#=> User(name: "Bob", bday: #<Date: Sat, 09 Dec 1989>)
109 110 111 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 |
# File 'lib/gamera/builder.rb', line 109 def self.create_with(spec, &block) struct = Struct.new(*spec.keys) do def initialize( = {}) super .each { |opt, value| self[opt] = value } end def with(, &block) new_struct = dup .each { |opt, value| new_struct[opt] = value } if block_given? new_struct.class.class_eval do define_method :build, &block end end new_struct end members.each do |opt| define_method "with_#{opt}" do |value, &inner_block| with(opt => value, &inner_block) end end define_method :build, &block end struct.new spec end |
.with_options(*option_names) ⇒ Object
Another way to create builders.
For easy creation of a builder use the with_options() class builder method. For example:
class UserBuilder < Builder.(:name, :bday, :nickname)
def build
User.create!(name: name, bday: bday, nickname: nickname)
end
end
The created builder will automatically have methods to set each of the options. In the example above the UserBuilder#with_nickname method will return a new UserBuilder
that has the specified nickname.
UserBuilder.new
.with_nickname("shorty")
.result
#=> User(name: nil, bday: nil, nickname: "shorty")
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 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 |
# File 'lib/gamera/builder.rb', line 182 def self.(*option_names) init_arg_list = option_names.map { |o| "#{o}: nil" }.join(', ') args_to_ivars = option_names.map { |o| "@#{o} = #{o}_coercion(#{o})" }.join('; ') Class.new(self) do module_eval <<-METH def initialize(#{init_arg_list}) # def initialize(tags: nil, name: nil) #{args_to_ivars} # @tags = tags_coercion(tags); @name = name_coercion(name) super() # super() end # end METH # +with_...+ methods option_names.each do |name| define_method(:"with_#{name}") do |new_val, *extra| val = if extra.any? # called with multiple params, eg (p1,p2,p3,...), so # package those as an array and pass them in [new_val] + extra else # called with single param new_val end refine_with(name => val) end end protected attr_reader(*option_names) define_method(:options) do Hash[option_names.map { |o| [o, send(o)] }] end option_names.each do |o_name| define_method(:"#{o_name}_coercion") { |new_val| new_val } end end end |
Instance Method Details
#build ⇒ Object
Don’t call this method directly, use #result
instead.
Executes the builder
232 233 234 |
# File 'lib/gamera/builder.rb', line 232 def build raise NotImplementedError end |
#refine_with(alterations) ⇒ Object
Returns a clone of this object but with options listed in alterations
updated to match.
240 241 242 |
# File 'lib/gamera/builder.rb', line 240 def refine_with(alterations) self.class.new .merge(alterations) end |
#result ⇒ Object
The object built by this builder
225 226 227 |
# File 'lib/gamera/builder.rb', line 225 def result @result ||= build end |