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.
-
#mold ⇒ #call?
Forge mold.
-
#sequences ⇒ Hash{Symbol => Sequence}
readonly
Used sequences.
-
#traits ⇒ Hash{Symbol => Hash{Symbol => Proc}}
readonly
Trait definitions.
Instance Method Summary collapse
-
#attribute(name, &definition) ⇒ Symbol
(also: #[])
Define an attribute, possibly transient.
-
#freeze ⇒ self
Freezes the instance, including
attributes,sequencesandtraits. -
#initialize {|f| ... } ⇒ ForgeDSL
constructor
Define forge’s parameters through DSL.
-
#inspect ⇒ String
Return a string containing a human-readable representation of the definition.
-
#sequence(name, initial = 1) {|value| ... } ⇒ Symbol
Define an attribute, using a sequence.
-
#trait(name) {|f| ... } ⇒ Symbol
Define a trait — a group of attributes with non-default values.
Methods inherited from UnBasicObject
#class, #eql?, #frozen?, #hash, #is_a?, #pretty_print, #pretty_print_cycle, #respond_to?, #to_s
Constructor Details
#initialize {|f| ... } ⇒ 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.
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/object_forge/forge_dsl.rb', line 56 def initialize(&dsl) super @attributes = {} @sequences = {} @traits = {} dsl.arity.zero? ? instance_exec(&dsl) : yield(self) freeze end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name) ⇒ Symbol (private)
Define an attribute using a shorthand.
Can not be used to define attributes with reserved names. Trying to use a conflicting name will lead to usual issues with calling random methods. When in doubt, use #attribute or #[] instead.
Reserved names are:
-
all names ending in ?, ! or =
-
all names starting with a non-word ASCII character (operators, ‘,
[],[]=) -
rand
276 277 278 279 280 281 |
# File 'lib/object_forge/forge_dsl.rb', line 276 def method_missing(name, **nil, &) return super if frozen? return attribute(name, &) if respond_to_missing?(name, false) raise DSLError, "#{name.inspect} is a reserved name (in #{name.inspect})" end |
Instance Attribute Details
#attributes ⇒ Hash{Symbol => Proc} (readonly)
Returns attribute definitions.
22 23 24 |
# File 'lib/object_forge/forge_dsl.rb', line 22 def attributes @attributes end |
#mold ⇒ #call?
Returns forge mold.
31 32 33 |
# File 'lib/object_forge/forge_dsl.rb', line 31 def mold @mold end |
#sequences ⇒ Hash{Symbol => Sequence} (readonly)
Returns used sequences.
25 26 27 |
# File 'lib/object_forge/forge_dsl.rb', line 25 def sequences @sequences end |
#traits ⇒ Hash{Symbol => Hash{Symbol => Proc}} (readonly)
Returns trait definitions.
28 29 30 |
# File 'lib/object_forge/forge_dsl.rb', line 28 def traits @traits end |
Instance Method Details
#attribute(name, &definition) ⇒ Symbol Also known as: []
Define an attribute, possibly transient.
DSL does not know or care what attributes the forged 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.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/object_forge/forge_dsl.rb', line 133 def attribute(name, &definition) unless ::Symbol === name raise ::ArgumentError, "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 name end |
#freeze ⇒ self
Called automatically in #initialize.
Freezes the instance, including attributes, sequences and traits. Prevents further responses through #method_missing.
73 74 75 76 77 78 79 80 |
# File 'lib/object_forge/forge_dsl.rb', line 73 def freeze ::Object.instance_method(:freeze).bind_call(self) @attributes.freeze @sequences.freeze @traits.freeze @mold.freeze self end |
#inspect ⇒ String
Return a string containing a human-readable representation of the definition.
249 250 251 252 253 254 |
# File 'lib/object_forge/forge_dsl.rb', line 249 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 |
#sequence(name, initial = 1) {|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.
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/object_forge/forge_dsl.rb', line 180 def sequence(name, initial = 1, **nil, &) unless ::Symbol === name raise ::ArgumentError, "sequence name must be a Symbol, #{name.class} given (in #{name.inspect})" end seq = @sequences[name] ||= Sequence.new(initial) if block_given? attribute(name) { instance_exec(seq.next, &) } else attribute(name) { 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.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/object_forge/forge_dsl.rb', line 227 def trait(name, **nil) unless ::Symbol === name raise ::ArgumentError, "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 |