Class: Petra::Proxies::ObjectProxy
- Inherits:
-
AbstractProxy
- Object
- AbstractProxy
- Petra::Proxies::ObjectProxy
- Includes:
- Comparable
- Defined in:
- lib/petra/proxies/object_proxy.rb
Overview
To avoid messing with the methods defined by ActiveRecord or similar, the programmer should use these proxy objects (object.petra.*) which handle actions on a different level.
This class is the base proxy class which can be extended to cover certain behaviours that would be too complex to be put inside the configuration.
Constant Summary collapse
- CLASS_NAMES =
%w[Object].freeze
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Very simple spaceship operator based on the object key TODO: See if this causes problems when ID-ordering is expected.
-
#__attribute_key(attribute) ⇒ String
Generates a unique attribute key based on the proxied object’s class, id and a given attribute.
-
#__created? ⇒ Boolean
true
if the proxied object was created (= initialized + persisted) during the current transaction. -
#__destroyed? ⇒ Boolean
true
if the proxied object was destroyed during the transaction. -
#__existing? ⇒ Boolean
true
if the proxied object existed before the transaction started. -
#__new? ⇒ Boolean
true
if the proxied object did not exist before the transaction started. -
#__object_id ⇒ Object
Generates an ID for the proxied object based on the class configuration.
-
#__object_key ⇒ String
Generates a unique object key based on the proxied object’s class and id.
- #__original_attribute?(attribute_name) ⇒ Boolean deprecated Deprecated.
-
#method_missing(meth, *args, &block) ⇒ Object
Catch all methods which are not defined on this proxy object as they are most likely meant to go to the proxied object.
-
#new(*args) ⇒ Object
Creepy!.
-
#petra ⇒ Object
Do not create new proxies for already proxied objects.
-
#respond_to_missing?(meth) ⇒ Boolean
It is necessary to forward #respond_to? queries to the proxied object as otherwise certain calls, especially from the Rails framework itself will fail.
-
#unproxied ⇒ Object
Access the proxied object publicly from each petra proxy TODO: This should not leave the proxy!.
Methods inherited from AbstractProxy
available_class_proxies, available_module_proxies, for, inherited_config_for, #mixin_module_proxies!, #object_config, #transaction
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, *args, &block) ⇒ Object
Catch all methods which are not defined on this proxy object as they are most likely meant to go to the proxied object
Also checks a few special cases like attribute reads/changes. Please note that a method may be e.g. a persistence method AND an attribute writer (for normal objects, every attribute write would be persisted to memory), so we have to execute all matching handlers in a queue.
rubocop:disable Style/MethodMissing
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 |
# File 'lib/petra/proxies/object_proxy.rb', line 72 def method_missing(meth, *args, &block) # If no transaction is currently running, we proxy everything # to the original object. unless Petra.transaction_running? Petra.logger.info "No transaction running, proxying #{meth} to original object." return unproxied.public_send(meth, *args, &block) end # As calling a superclass method in ruby does not cause method calls within this method # to be called within the superclass context, the correct (= the child class') attribute # detectors are run. result = __handlers.execute_missing_queue(meth, *args, block: block) do |queue| queue << :handle_attribute_change if __attribute_writer?(meth) queue << :handle_attribute_read if __attribute_reader?(meth) queue << :handle_dynamic_attribute_read if __dynamic_attribute_reader?(meth) queue << :handle_object_persistence if __persistence_method?(meth) end Petra.logger.debug "#{object_class_or_self}##{meth}(#{args.map(&:inspect).join(', ')}) => #{result.inspect}" result rescue SystemStackError => e exception = ArgumentError.new("Method '#{meth}' lead to a SystemStackError due to `method_missing`") exception.set_backtrace(e.backtrace.uniq) raise exception end |
Instance Method Details
#<=>(other) ⇒ Object
Very simple spaceship operator based on the object key TODO: See if this causes problems when ID-ordering is expected.
For existing objects that shouldn't be the case in most situations as
a collection mostly contains only objects of one kind
178 179 180 |
# File 'lib/petra/proxies/object_proxy.rb', line 178 def <=>(other) __object_key <=> other.__object_key end |
#__attribute_key(attribute) ⇒ String
Generates a unique attribute key based on the proxied object’s class, id and a given attribute
139 140 141 |
# File 'lib/petra/proxies/object_proxy.rb', line 139 def __attribute_key(attribute) [proxied_object.class, __object_id, attribute].map(&:to_s).join('/') end |
#__created? ⇒ Boolean
Returns true
if the proxied object was created (= initialized + persisted) during the current transaction.
161 162 163 |
# File 'lib/petra/proxies/object_proxy.rb', line 161 def __created? transaction.objects.created?(self) end |
#__destroyed? ⇒ Boolean
Returns true
if the proxied object was destroyed during the transaction.
168 169 170 |
# File 'lib/petra/proxies/object_proxy.rb', line 168 def __destroyed? transaction.objects.destroyed?(self) end |
#__existing? ⇒ Boolean
Returns true
if the proxied object existed before the transaction started.
153 154 155 |
# File 'lib/petra/proxies/object_proxy.rb', line 153 def __existing? transaction.objects.existing?(self) end |
#__new? ⇒ Boolean
Returns true
if the proxied object did not exist before the transaction started.
146 147 148 |
# File 'lib/petra/proxies/object_proxy.rb', line 146 def __new? transaction.objects.new?(self) end |
#__object_id ⇒ Object
Generates an ID for the proxied object based on the class configuration. New objects (= objects which were generated within this transaction) receive an artificial ID
115 116 117 118 119 120 121 |
# File 'lib/petra/proxies/object_proxy.rb', line 115 def __object_id @__object_id ||= if __new? transaction.objects.next_id else object_config(:id_method, proc_expected: true, base: proxied_object) end end |
#__object_key ⇒ String
Generates a unique object key based on the proxied object’s class and id
128 129 130 |
# File 'lib/petra/proxies/object_proxy.rb', line 128 def __object_key [proxied_object.class, __object_id].map(&:to_s).join('/') end |
#__original_attribute?(attribute_name) ⇒ Boolean
Checks whether the given attribute was altered during the current transaction. Note that an attribute counts as ‘altered` even if it was reset to its original value in a later transaction step.
TODO: Check for dynamic attribute readers?
58 59 60 |
# File 'lib/petra/proxies/object_proxy.rb', line 58 def __original_attribute?(attribute_name) !transaction.attribute_value?(self, attribute: attribute_name.to_s) end |
#new(*args) ⇒ Object
Creepy!
32 33 34 35 |
# File 'lib/petra/proxies/object_proxy.rb', line 32 def new(*args) class_method! proxied_object.new(*args).petra end |
#petra ⇒ Object
Do not create new proxies for already proxied objects. Instead, return the current proxy object
27 28 29 |
# File 'lib/petra/proxies/object_proxy.rb', line 27 def petra(*) self end |
#respond_to_missing?(meth) ⇒ Boolean
It is necessary to forward #respond_to? queries to the proxied object as otherwise certain calls, especially from the Rails framework itself will fail. Hidden methods are ignored.
106 107 108 |
# File 'lib/petra/proxies/object_proxy.rb', line 106 def respond_to_missing?(meth, *) proxied_object.respond_to?(meth) end |
#unproxied ⇒ Object
Access the proxied object publicly from each petra proxy TODO: This should not leave the proxy!
45 46 47 |
# File 'lib/petra/proxies/object_proxy.rb', line 45 def unproxied proxied_object end |