Module: Inform::Prototypical
- Included in:
- Object, System::Object
- Defined in:
- lib/runtime/prototype.rb
Overview
The Inform::Prototypical module
Constant Summary collapse
- MethodWriterTemplate =
'%<method_name>s='.freeze
- VariableTemplate =
'@%<property>s'.freeze
- MethodBooleanOrBangPattern =
%r{([\?!]+)$}.freeze
- MethodWriterPattern =
%r{=$}.freeze
- LinkPattern =
%r{.+_to$}.freeze
- AccessorMethodTemplate =
%( def %<accessor>s(*args) args.empty? ? self._get(__method__) : self._set(__method__, *args) end ).freeze
- WriterMethodTemplate =
%( def %<writer>s(value) self._assign(__method__.to_s.chop.to_sym, value) end ).freeze
- MISSING_PROPERTIES_MESSAGE =
rubocop: enable Metrics/AbcSize rubocop: enable Metrics/CyclomaticComplexity rubocop: enable Metrics/MethodLength
'Missing properties attribute for: ' \ '%<identity>s'.freeze
Instance Method Summary collapse
-
#&(property, *args) ⇒ Object
This method tries to access or write values given by *args to the property using an accessor or writer method, and failing that, will invoke the prototype method, which dynamically defines an accessor or writer method on the receiver for future ease of access.
-
#_assign(key, value) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength rubocop: disable Metrics/PerceivedComplexity.
- #_get(key) ⇒ Object
-
#_set(key, *values) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength rubocop: disable Metrics/PerceivedComplexity.
-
#accessor_and_writer(key) ⇒ Object
def respond_to_missing?(method, include_all=false) m = method.to_s return false if m =~ /?$/ mname = m =~ /=$/ ? m.chop : m property = mname.intern Inform::Runtime.invocation_properties.include? property end.
- #context_event ⇒ Object
- #ensure_properties_attribute! ⇒ Object
- #instance_variables_map ⇒ Object
- #key_refers_to_link?(key) ⇒ Boolean
-
#method_missing(method, *args, &block) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength rubocop: disable Metrics/PerceivedComplexity rubocop: disable Style/MissingRespondToMissing.
-
#prototype(key, *args, &block) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength.
- #string?(key) ⇒ Boolean
- #values ⇒ Object
-
#|(property, *args) ⇒ Object
This method attempts to get a value if it exists.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength rubocop: disable Metrics/PerceivedComplexity rubocop: disable Style/MissingRespondToMissing
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/runtime/prototype.rb', line 133 def method_missing(method, *args, &block) return super if method == :to_ary m = method.to_s return super if MethodBooleanOrBangPattern.match?(m) mname = MethodWriterPattern.match?(m) ? m.chop : m property = mname.to_sym variable = format(VariableTemplate, property: property).to_sym event = context_event if MethodWriterPattern.match?(m) || (LinkPattern.match?(m) && !args.empty?) if !event.nil? && Inform::Runtime.invocation_properties.include?(property) event.send(method, *args, &block) elsif inflib&.library_method?(method) inflib.send(method, *args, &block) else self.prototype(method, *args, &block) end elsif !event.nil? && Inform::Runtime.invocation_properties.include?(property) result = event.send(method, *args, &block) log.debug("Value for event #{event}.#{method} is: #{result || 'nil'}") if defined? DEBUG result elsif inflib&.library_method?(method) inflib.send(method, *args, &block) elsif inflib.respond_to?(method) && Inform::Runtime.invocation_properties.include?(property) inflib.send(method, *args, &block) elsif inflib&.instance_variable_get(variable) || inflib&.instance_variables&.include?(variable) inflib.instance_variable_get(variable) elsif (properties.respond_to?(:key?) && properties.key?(property)) || (respond_to?(:_key?) && _key?(property)) || !args.empty? self.prototype(method, *args, &block) else super end end |
Instance Method Details
#&(property, *args) ⇒ Object
This method tries to access or write values given by *args to the property using an accessor or writer method, and failing that, will invoke the prototype method, which dynamically defines an accessor or writer method on the receiver for future ease of access. rubocop: disable Metrics/AbcSize rubocop: disable Metrics/BlockNesting rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength rubocop: disable Metrics/PerceivedComplexity
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/runtime/prototype.rb', line 60 def &(property, *args) return nil if property.nil? return super unless property.is_a?(Symbol) || property.is_a?(String) return nil if property.respond_to?(:empty?) && property.empty? property, sub_property = property.to_s.split('.').map(&:to_sym) if !args.empty? setter = format(MethodWriterTemplate, method_name: property) if !sub_property.nil? value = self.send(property) if value.object? value.&(sub_property, *args) elsif value.respond_to?(:key?) value[sub_property] = (args.length > 1 ? args : args.first) self.send(setter, value) else self.send(setter, *args) end elsif self.respond_to?(property) && args.length == self.method(property).arity self.send(property, *args) elsif self.respond_to?(setter) && args.length == self.method(setter).arity self.send(setter, *args) elsif !Kernel.methods.include?(property) self.send(property, *args) else self.prototype(property, *args) end elsif !sub_property.nil? value = self.send(property) if value.object? value.&(sub_property, *args) elsif value.respond_to?(property) value.send(property) elsif value.respond_to?(:key?) value[sub_property] else value end elsif self.respond_to?(property) self.send(property) elsif (self.properties.respond_to?(:key?) && self.properties.key?(property)) || (self.respond_to?(:_key?) && self._key?(property)) self.prototype(property) else return nil end end |
#_assign(key, value) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength rubocop: disable Metrics/PerceivedComplexity
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/runtime/prototype.rb', line 282 def _assign(key, value) raise "The key parameter must be a symbol" unless key.is_a? Symbol result = nil if value.object? result = self._set_object(key, value) if self.respond_to?(:_set_object) elsif value.nil? && self.respond_to?(:_key?) && self._key?(key) result = self._unset_object(key) if self.respond_to?(:_unset_object) elsif value.nil? && self.properties.key?(key) self.will_change_column(:properties) if self.respond_to?(:will_change_column) self.properties.delete(key) self.save_changes if self.respond_to?(:save_changes) else self.will_change_column(:properties) if self.respond_to?(:will_change_column) self.properties[key] = value self.save_changes if self.respond_to?(:save_changes) end result || self.properties.dup[key] end |
#_get(key) ⇒ Object
238 239 240 241 242 |
# File 'lib/runtime/prototype.rb', line 238 def _get(key) result = nil result = self._get_object(key) if self.respond_to?(:_key?) && self._key?(key) result || self.properties.dup[key] end |
#_set(key, *values) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength rubocop: disable Metrics/PerceivedComplexity
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/runtime/prototype.rb', line 248 def _set(key, *values) raise "The key parameter must be a symbol" unless key.is_a? Symbol raise "The values parameter may not be empty" if values.empty? result = nil if values.first.object? result = self._set_object(key, values.first) if respond_to? :_set_object elsif values.first.nil? && self.respond_to?(:_key?) && self._key?(key) result = self._unset_object(key) if self.respond_to?(:_unset_object) elsif values.first.nil? self.will_change_column(:properties) if self.respond_to?(:will_change_column) self.properties.delete(key) self.save_changes if self.respond_to?(:save_changes) elsif values.length > 1 self.will_change_column(:properties) if self.respond_to?(:will_change_column) self.properties[key] = [] self.properties[key].concat(values) values.clear self.save_changes if self.respond_to?(:save_changes) else self.will_change_column(:properties) if self.respond_to?(:will_change_column) self.properties[key] = values.first self.save_changes if self.respond_to?(:save_changes) end result || self.properties.dup[key] end |
#accessor_and_writer(key) ⇒ Object
def respond_to_missing?(method, include_all=false)
m = method.to_s
return false if m =~ /\?$/
mname = m =~ /\=$/ ? m.chop : m
property = mname.intern
Inform::Runtime.invocation_properties.include? property
end
182 183 184 185 |
# File 'lib/runtime/prototype.rb', line 182 def accessor_and_writer(key) return [key.to_s.chop, key] if MethodWriterPattern.match?(key) [key, format(MethodWriterTemplate, method_name: key)] end |
#context_event ⇒ Object
25 26 27 |
# File 'lib/runtime/prototype.rb', line 25 def context_event Thread.current[:event] end |
#ensure_properties_attribute! ⇒ Object
229 230 231 232 |
# File 'lib/runtime/prototype.rb', line 229 def ensure_properties_attribute! return unless self.properties.nil? raise StandardError, format(MISSING_PROPERTIES_MESSAGE, identity: self.identity) end |
#instance_variables_map ⇒ Object
29 30 31 32 33 |
# File 'lib/runtime/prototype.rb', line 29 def instance_variables_map instance_variables.each_with_object({}) do |key, memo| memo[key] = instance_variable_get(key) end end |
#key_refers_to_link?(key) ⇒ Boolean
234 235 236 |
# File 'lib/runtime/prototype.rb', line 234 def key_refers_to_link?(key) Inform::Library::DeclaredProperties.memo.key?(key) && LinkPattern.match?(key.to_s) end |
#prototype(key, *args, &block) ⇒ Object
rubocop: disable Metrics/AbcSize rubocop: disable Metrics/CyclomaticComplexity rubocop: disable Metrics/MethodLength
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/runtime/prototype.rb', line 202 def prototype(key, *args, &block) ensure_properties_attribute! accessor, writer = accessor_and_writer(key) if key_refers_to_link?(key) self.will_change_column(:properties) if self.respond_to?(:will_change_column) if args.empty? && self.properties.key?(key) return if (to_obj = get_constant(self.properties[key])).nil? || !to_obj.object? self.link(key, to_obj) self.properties.delete(key) self.save_changes if self.respond_to?(:save_changes) return to_obj end self.properties[key] = args.first end accessor = format(AccessorMethodTemplate, accessor: accessor) self.instance_eval(accessor, __FILE__, __LINE__) writer = format(WriterMethodTemplate, writer: writer) self.instance_eval(writer, __FILE__, __LINE__) self.send(key, *args, &block) end |
#string?(key) ⇒ Boolean
42 43 44 45 46 |
# File 'lib/runtime/prototype.rb', line 42 def string?(key) v = self.&key return false if v.nil? v.respond_to?(:to_s) && !v.to_s.empty? end |
#values ⇒ Object
35 36 37 38 39 40 |
# File 'lib/runtime/prototype.rb', line 35 def values super || instance_variables_map rescue StandardError => e log.warn "Unexpected error invoking super(Inform::Prototypical#values): #{e.}" instance_variables_map end |
#|(property, *args) ⇒ Object
This method attempts to get a value if it exists. If no value is already set on the receiver, any given arguments will be set on the receiver for future access.
115 116 117 118 119 120 121 |
# File 'lib/runtime/prototype.rb', line 115 def |(property, *args) return nil if property.nil? return super unless property.respond_to?(:to_s) return nil if property.to_s.empty? v = self.&property v || send(property, *args) end |