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.
-
#mold ⇒ #call?
Forge mold.
-
#mold=(mold) ⇒ #call?
Set the forge mold.
-
#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.
53 54 55 56 57 58 59 60 61 62 |
# File 'lib/object_forge/forge_dsl.rb', line 53 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
280 281 282 283 284 285 |
# File 'lib/object_forge/forge_dsl.rb', line 280 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 |
#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.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/object_forge/forge_dsl.rb', line 137 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.
70 71 72 73 74 75 76 77 |
# File 'lib/object_forge/forge_dsl.rb', line 70 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.
253 254 255 256 257 258 |
# File 'lib/object_forge/forge_dsl.rb', line 253 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 |
#mold ⇒ #call?
Returns forge mold.
81 82 83 84 |
# File 'lib/object_forge/forge_dsl.rb', line 81 def mold # rubocop:disable Style/TrivialAccessors # Not using attr_reader because YARD eats `#mold=` then. @mold end |
#mold=(mold) ⇒ #call?
Set the forge mold.
Mold is an object that knows how to take a hash of attributes and create an object from them. It can also be a class with #call, in which case a new mold will be instantiated automatically for each build through Molds::WrappedMold. If a single instance is enough, please call .new yourself once.
100 101 102 103 104 105 106 107 108 |
# File 'lib/object_forge/forge_dsl.rb', line 100 def mold=(mold) if nil == mold || mold.respond_to?(:call) # rubocop:disable Style/YodaCondition @mold = mold elsif ::Class === mold && mold.public_method_defined?(:call) @mold = Molds::WrappedMold.new(mold) else raise DSLError, "mold must respond to or implement #call" end 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.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/object_forge/forge_dsl.rb', line 184 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.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/object_forge/forge_dsl.rb', line 231 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 |