Class: Petra::Proxies::AbstractProxy

Inherits:
Object
  • Object
show all
Defined in:
lib/petra/proxies/abstract_proxy.rb

Direct Known Subclasses

ObjectProxy

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.available_class_proxiesHash

Determines the available object proxy classes and the ruby classes they can be used for. All classes in the Petra::Proxies namespace are automatically recognized as long as they define a CLASS_NAMES constant.

If multiple proxies specify the same class name, the last one by sorting wins.

Returns:

  • (Hash)

    The available proxy classes in the format (“ClassName” => “ProxyClassName”)



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/petra/proxies/abstract_proxy.rb', line 48

def available_class_proxies
  @available_class_proxies ||= Petra::Proxies.constants.each_with_object({}) do |c, h|
    klass = Petra::Proxies.const_get(c)
    # Skip non-class constants (this includes modules)
    next unless klass.is_a?(Class)
    # Skip every class which is not an ObjectProxy. There shouldn't be any
    # in this namespace, but you never know...
    next unless klass <= Petra::Proxies::ObjectProxy
    # Skip proxy classes which do not specify which classes
    # they were built for
    next unless klass.const_defined?(:CLASS_NAMES)

    klass.const_get(:CLASS_NAMES).each { |n| h[n] = "Petra::Proxies::#{c}" }
  end
end

.available_module_proxiesObject

Returns only module proxies

See Also:

  • #available_class_proxies


69
70
71
72
73
74
75
76
77
78
# File 'lib/petra/proxies/abstract_proxy.rb', line 69

def available_module_proxies
  @available_module_proxies ||= Petra::Proxies.constants.each_with_object({}) do |c, h|
    klass = Petra::Proxies.const_get(c)
    next unless klass.is_a?(Module)
    next unless klass.included_modules.include?(Petra::Proxies::ModuleProxy)
    next unless klass.const_defined?(:MODULE_NAMES)

    klass.const_get(:MODULE_NAMES).each { |n| h[n] = "Petra::Proxies::#{c}" }
  end
end

.for(object, inherited: false, **options) ⇒ Object

Builds an ObjectProxy for the given object. If a more specific proxy class exists for the given object, it will be used instead of the generic Petra::Proxies::ObjectProxy.

If there is no proxy for the exact class of the given object, its superclasses are automatically tested.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/petra/proxies/abstract_proxy.rb', line 16

def for(object, inherited: false, **options)
  # If the given object is configured not to use a possibly existing
  # specialized proxy (e.g. the ActiveRecord::Base proxy), we simply
  # build a default ObjectProxy for it, but we'll still try to extend it using
  # available ModuleProxies
  default_proxy = ObjectProxy.new(object, inherited, **options)
  default_proxy.send :mixin_module_proxies!
  return default_proxy unless inherited_config_for(object, :use_specialized_proxy)

  # Otherwise, we search for a specialized proxy for the object's class
  # and its superclasses until we either find one or reach the
  # default ObjectProxy
  klass = object.is_a?(Class) ? object : object.class
  klass = klass.superclass until available_class_proxies.key?(klass.to_s)
  proxy = available_class_proxies[klass.to_s].constantize.new(object, inherited, **options)

  # If we reached Object, we might still find one or more ModuleProxy module we might
  # mix into the resulting ObjectProxy. Otherwise, the specialized proxy will most likely
  # have included the necessary ModuleProxies itself.
  proxy.send(:mixin_module_proxies!) if proxy.instance_of?(Petra::Proxies::ObjectProxy)
  proxy
end

.inherited_config_for(object, name, *args) ⇒ Object

Retrieves a configuration value with the given name respecting custom configurations made for its class (or class family)



84
85
86
87
88
89
90
# File 'lib/petra/proxies/abstract_proxy.rb', line 84

def inherited_config_for(object, name, *args)
  # If the proxied object already is a class, we don't use its class (Class)
  # as there is a high chance nobody will ever use this object proxy on
  # this level of meta programming
  klass = object.is_a?(Class) ? object : object.class
  Petra.configuration.class_configurator(klass).__inherited_value(name, *args)
end

Instance Method Details

#mixin_module_proxies!Object

As it might happen that a custom proxy has to be defined for behaviour introduced to different classes as an included module (an example would be Enumerable), it has to be possible to define an equivalent to object proxies for them. This function inspects all modules which were previously included into the proxied object’s singleton class and automatically adds matching module proxies.

Please take a look at Petra::Proxies::EnumerableProxy for an example module proxy



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/petra/proxies/abstract_proxy.rb', line 102

def mixin_module_proxies!
  # Neither symbols nor fixnums may have singleton classes, see the corresponding Kernel method
  return if proxied_object.is_a?(Integer) || proxied_object.is_a?(Symbol)

  # Do not load ModuleProxies if the object's configuration denies it
  return unless object_config(:mixin_module_proxies)

  proxied_object.singleton_class.included_modules.each do |mod|
    proxy_module = Petra::Proxies::ObjectProxy.available_module_proxies[mod.to_s].try(:constantize)
    # Skip all included modules without ModuleProxies
    next unless proxy_module

    singleton_class.class_eval do
      # Extend the proxy with the module proxy's class methods
      extend proxy_module.const_get(:ClassMethods) if proxy_module.const_defined?(:ClassMethods)

      # Include the module proxy's instance methods
      include proxy_module.const_get(:InstanceMethods) if proxy_module.const_defined?(:InstanceMethods)

      proxy_module.const_get(:INCLUDES).each { |m| include m } if proxy_module.const_defined?(:INCLUDES)
    end
  end
end

#object_config(name, *args) ⇒ Object

See Also:

  • the proxied object is automatically passed in as first parameter


137
138
139
# File 'lib/petra/proxies/abstract_proxy.rb', line 137

def object_config(name, *args)
  self.class.inherited_config_for(proxied_object, name, *args)
end

#transactionPetra::Components::Transaction

Returns the currently active transaction.

Returns:



129
130
131
# File 'lib/petra/proxies/abstract_proxy.rb', line 129

def transaction
  Petra.transaction_manager.current_transaction
end