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.
-
#settings ⇒ Hash{Symbol => Any}
readonly
Settings for forge, such as mold.
-
#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
settings,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.
-
#setting(name, value) ⇒ Symbol
Set a value for a forge’s setting.
-
#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.
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/object_forge/forge_dsl.rb', line 58 def initialize(&dsl) super @attributes = {} @sequences = {} @traits = {} @settings = {} 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, value = nil) ⇒ Symbol (private)
Define an attribute (like name) or set a setting (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 #setting instead.
Reserved names are:
-
all names ending in ?, !
-
all names starting with a non-word ASCII character (operators, ‘,
[],[]=) -
rand
283 284 285 286 287 288 289 290 291 |
# File 'lib/object_forge/forge_dsl.rb', line 283 def method_missing(name, value = nil, **nil, &) return super(name) if frozen? if valid_setting_method?(name) return setting(name[...-1].to_sym, value) # steep:ignore NoMethod end 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 |
#settings ⇒ Hash{Symbol => Any} (readonly)
Returns settings for forge, such as mold.
31 32 33 |
# File 'lib/object_forge/forge_dsl.rb', line 31 def settings @settings 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.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/object_forge/forge_dsl.rb', line 139 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 settings, attributes, sequences and traits. Prevents further responses through #method_missing.
76 77 78 79 80 81 82 83 |
# File 'lib/object_forge/forge_dsl.rb', line 76 def freeze ::Kernel.instance_method(:freeze).bind_call(self) @attributes.freeze @sequences.freeze @traits.freeze @settings.freeze self end |
#inspect ⇒ String
Return a string containing a human-readable representation of the definition.
255 256 257 258 259 260 |
# File 'lib/object_forge/forge_dsl.rb', line 255 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.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/object_forge/forge_dsl.rb', line 186 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 |
#setting(name, value) ⇒ Symbol
Set a value for a forge’s setting.
Possible settings depend on used forge, but for default ObjectForge::Forge a mold is expected.
It is also possible to set settings through method_missing, using name with a = suffix.
102 103 104 105 106 107 108 109 110 |
# File 'lib/object_forge/forge_dsl.rb', line 102 def setting(name, value) unless ::Symbol === name raise ::ArgumentError, "setting name must be a Symbol, #{name.class} given" end @settings[name] = value 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.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/object_forge/forge_dsl.rb', line 233 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 |