Class: ObjectForge::ForgeDSL
- Inherits:
-
UnBasicObject
- Object
- UnBasicObject
- ObjectForge::ForgeDSL
- Defined in:
- lib/object_forge/forge_dsl.rb
Overview
This class is not intended to be used directly, but it’s not a private API.
DSL for defining a forge.
Instance Attribute Summary collapse
-
#attributes ⇒ Hash{Symbol => Proc}
readonly
Attribute definitions.
-
#options ⇒ Hash{Symbol => Any}
readonly
Options for forge, such as mold.
-
#sequences ⇒ Hash{Symbol => Sequence}
readonly
Used sequences.
-
#traits ⇒ Hash{Symbol => Hash{Symbol => Proc}}
readonly
Trait definitions.
Instance Method Summary collapse
-
#attribute(name, transient: false, &definition) ⇒ Symbol
(also: #[])
Define an attribute, possibly transient.
-
#freeze ⇒ self
Freezes the instance, including
options,attributes,sequencesandtraits. -
#initialize {|dsl| ... } ⇒ ForgeDSL
constructor
Define forge’s parameters through DSL.
-
#inspect ⇒ String
Return a string containing a human-readable representation of the definition.
-
#option(name, value) ⇒ Symbol
Set a value for a forge’s option.
-
#sequence(name, initial = 1, transient: false) {|value| ... } ⇒ Symbol
Define an attribute, using a sequence.
-
#trait(name) {|f| ... } ⇒ Symbol
Define a trait — a group of attributes with non-default values.
-
#transient(name) ⇒ Object
Define a transient attribute.
Methods inherited from UnBasicObject
#class, #eql?, #frozen?, #hash, #is_a?, #pretty_print, #pretty_print_cycle, #respond_to?, #to_s
Constructor Details
#initialize {|dsl| ... } ⇒ ForgeDSL
Define forge’s parameters through DSL.
If the block has a parameter, an object will be yielded, and self context will be preserved. Otherwise, DSL will change self context inside the block, without ability to call methods available outside.
57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/object_forge/forge_dsl.rb', line 57 def initialize(&dsl) super @attributes = {} @sequences = {} @traits = {} @options = {} @transient_attributes = [] dsl.arity.zero? ? instance_exec(&dsl) : yield(self) shape_attribute_list! freeze end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, value = nil) ⇒ Symbol (private)
Define an attribute (like name) or set a option (like name=) using a shorthand.
Can not be used with reserved names. Trying to use a conflicting name will lead to usual issues with calling random methods. When in doubt, use #attribute or #option instead.
Reserved names are:
-
all names ending in ?, !
-
all names starting with a non-word ASCII character (operators, ‘,
[],[]=) -
randandyard
310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/object_forge/forge_dsl.rb', line 310 def method_missing(name, value = nil, **nil, &) return super(name) if frozen? if valid_option_method?(name) # Intentionally passing block to `option` to trigger DSLError if it is present. return option(name[...-1].to_sym, value, &) # steep:ignore NoMethod, UnexpectedBlockGiven end # Block can be missing, but `attribute` will raise if it is. return attribute(name, &) if respond_to_missing?(name, false) # steep:ignore BlockTypeMismatch raise DSLError, "#{name.inspect} is a reserved name (in #{name.inspect})" end |
Instance Attribute Details
#attributes ⇒ Hash{Symbol => Proc} (readonly)
Returns attribute definitions.
21 22 23 |
# File 'lib/object_forge/forge_dsl.rb', line 21 def attributes @attributes end |
#options ⇒ Hash{Symbol => Any} (readonly)
Returns options for forge, such as mold.
30 31 32 |
# File 'lib/object_forge/forge_dsl.rb', line 30 def @options end |
#sequences ⇒ Hash{Symbol => Sequence} (readonly)
Returns used sequences.
24 25 26 |
# File 'lib/object_forge/forge_dsl.rb', line 24 def sequences @sequences end |
#traits ⇒ Hash{Symbol => Hash{Symbol => Proc}} (readonly)
Returns trait definitions.
27 28 29 |
# File 'lib/object_forge/forge_dsl.rb', line 27 def traits @traits end |
Instance Method Details
#attribute(name, transient: false, &definition) ⇒ Symbol Also known as: []
Define an attribute, possibly transient.
DSL does not know or care what attributes the target class has, so the only difference between “real” and “transient” attributes is how the class itself treats them.
It is also possible to define attributes using method_missing shortcut, except for conflicting or reserved names.
You can refer to any other attribute inside the attribute definition block. self[:name] can be used to refer to an attribute with a conflicting or reserved name.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/object_forge/forge_dsl.rb', line 152 def attribute(name, transient: false, &definition) unless ::Symbol === name raise ::TypeError, "attribute name must be a Symbol, #{name.class} given (in #{name.inspect})" end unless block_given? raise DSLError, "attribute definition requires a block (in #{name.inspect})" end if @current_trait @traits[@current_trait][name] = definition else @attributes[name] = definition end @transient_attributes << name if transient name end |
#freeze ⇒ self
Called automatically in #initialize.
Freezes the instance, including options, attributes, sequences and traits. Prevents further responses through #method_missing.
77 78 79 80 81 82 83 84 85 |
# File 'lib/object_forge/forge_dsl.rb', line 77 def freeze ::Kernel.instance_method(:freeze).bind_call(self) @attributes.freeze @sequences.freeze @traits.freeze @options.freeze @options[:attribute_list].freeze self end |
#inspect ⇒ String
Return a string containing a human-readable representation of the definition.
282 283 284 285 286 287 |
# File 'lib/object_forge/forge_dsl.rb', line 282 def inspect "#<#{self.class.name}:#{__id__} " \ "attributes=#{@attributes.keys.inspect} " \ "sequences=#{@sequences.keys.inspect} " \ "traits={#{@traits.map { |k, v| "#{k.inspect}=#{v.keys.inspect}" }.join(", ")}}>" end |
#option(name, value) ⇒ Symbol
Set a value for a forge’s option.
Possible options depend on used forge, but for default ObjectForge::Forge a :mold is expected. Check its documentation for full list of available options.
It is also possible to set options through method_missing, using name with a = suffix.
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/object_forge/forge_dsl.rb', line 106 def option(name, value) unless ::Symbol === name raise ::TypeError, "option name must be a Symbol, #{name.class} given (in #{name.inspect})" end raise DSLError, "option definition does not take a block (in #{name.inspect})" if block_given? @options[name] = value name end |
#sequence(name, initial = 1, transient: false) {|value| ... } ⇒ Symbol
Define an attribute, using a sequence.
name is used for both attribute and sequence, for the whole forge. If the name was used for a sequence previously, the sequence will not be redefined on subsequent calls.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/object_forge/forge_dsl.rb', line 211 def sequence(name, initial = 1, transient: false, &) unless ::Symbol === name raise ::TypeError, "sequence name must be a Symbol, #{name.class} given (in #{name.inspect})" end seq = @sequences[name] ||= Sequence.new(initial) if block_given? attribute(name, transient: transient) do instance_exec(seq.next, &) # steep:ignore BlockTypeMismatch end else attribute(name, transient: transient) { seq.next } end name end |
#trait(name) {|f| ... } ⇒ Symbol
Traits can not be defined inside of traits.
Define a trait — a group of attributes with non-default values.
DSL yields itself to the block, in case you need to refer to it. This can be used to define traits using a block coming from outside of DSL.
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/object_forge/forge_dsl.rb', line 261 def trait(name, **nil) unless ::Symbol === name raise ::TypeError, "trait name must be a Symbol, #{name.class} given (in #{name.inspect})" end if @current_trait raise DSLError, "can not define trait inside of another trait (in #{name.inspect})" end raise DSLError, "trait definition requires a block (in #{name.inspect})" unless block_given? @current_trait = name @traits[name] = {} yield self @traits[name].freeze @current_trait = nil name end |
#transient(name) ⇒ Object
Define a transient attribute.
176 177 178 |
# File 'lib/object_forge/forge_dsl.rb', line 176 def transient(name, &) attribute(name, transient: true, &) end |