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.
-
#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.
52 53 54 55 56 57 58 59 60 61 |
# File 'lib/object_forge/forge_dsl.rb', line 52 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
247 248 249 250 251 252 |
# File 'lib/object_forge/forge_dsl.rb', line 247 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.
21 22 23 |
# File 'lib/object_forge/forge_dsl.rb', line 21 def attributes @attributes 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, &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.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/object_forge/forge_dsl.rb', line 104 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.
69 70 71 72 73 74 75 |
# File 'lib/object_forge/forge_dsl.rb', line 69 def freeze ::Object.instance_method(:freeze).bind_call(self) @attributes.freeze @sequences.freeze @traits.freeze self end |
#inspect ⇒ String
Return a string containing a human-readable representation of the definition.
220 221 222 223 224 225 |
# File 'lib/object_forge/forge_dsl.rb', line 220 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.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/object_forge/forge_dsl.rb', line 151 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.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/object_forge/forge_dsl.rb', line 198 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 |