Class: Octiron::Transmogrifiers::Registry
- Inherits:
-
Object
- Object
- Octiron::Transmogrifiers::Registry
- Includes:
- Support::Identifiers
- Defined in:
- lib/octiron/transmogrifiers/registry.rb
Overview
Registers transmogrifiers between one (event) class and another.
A transmogrifier is an object with a call method or a block that accepts an instance of one (event) class and produces an instance of another (event) class.
The registry also exposes a #transmogrify function which uses any registered transmogrifier, or raises an error if there is no transmogrification possible.
One piece of magic makes this particularly powerful: the registry creates a graph of how to transmogrify an object to another by chaining transmogrifiers. That is, if there is a transmogrifier that turns A into B, and one that turns B into C, then by chaining both the registry can also turn A into C directly. (This is done via the ‘rgl’ gem).
Instance Attribute Summary collapse
-
#default_namespace ⇒ String
readonly
The default namespace to search for transmogrifiers.
Instance Method Summary collapse
-
#clear ⇒ Object
Clears the registry of all transmogrifiers.
-
#deregister(from, to) ⇒ Object
(also: #unregister)
Deregister transmogrifier.
-
#initialize(default_namespace = ::Octiron::Transmogrifiers) ⇒ Registry
constructor
A new instance of Registry.
-
#register(from, to, overwrite = false, transmogrifier_object = nil, &transmogrifier_proc) ⇒ Object
Register transmogrifier.
-
#transmogrify(from, to, verify_results = true) ⇒ Object
Transmogrify an object of one class into another class.
Methods included from Support::Identifiers
Methods included from Support::Constantize
Methods included from Support::CamelCase
Constructor Details
#initialize(default_namespace = ::Octiron::Transmogrifiers) ⇒ Registry
Returns a new instance of Registry.
43 44 45 46 |
# File 'lib/octiron/transmogrifiers/registry.rb', line 43 def initialize(default_namespace = ::Octiron::Transmogrifiers) @default_namespace = default_namespace.to_s clear end |
Instance Attribute Details
#default_namespace ⇒ String (readonly)
Returns the default namespace to search for transmogrifiers.
38 39 40 |
# File 'lib/octiron/transmogrifiers/registry.rb', line 38 def default_namespace @default_namespace end |
Instance Method Details
#clear ⇒ Object
Clears the registry of all transmogrifiers
50 51 52 53 54 55 56 |
# File 'lib/octiron/transmogrifiers/registry.rb', line 50 def clear @graph = RGL::DirectedAdjacencyGraph.new @visitor = RGL::DijkstraVisitor.new(@graph) @map_data = {} @map = nil @transmogrifiers = {} end |
#deregister(from, to) ⇒ Object Also known as: unregister
Deregister transmogrifier
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/octiron/transmogrifiers/registry.rb', line 113 def deregister(from, to) # Convert to canonical names from_name = identify(from) to_name = identify(to) key = [from_name, to_name] # Graph, map data and transmogrifiers need to be modified @graph.remove_edge(from_name, to_name) @map_data.delete(key) @map = RGL::EdgePropertiesMap.new(@map_data, true) @transmogrifiers.delete(key) end |
#register(from, to, overwrite = false, transmogrifier_object = nil, &transmogrifier_proc) ⇒ Object
Register transmogrifier
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 |
# File 'lib/octiron/transmogrifiers/registry.rb', line 75 def register(from, to, overwrite = false, transmogrifier_object = nil, &transmogrifier_proc) transmogrifier = transmogrifier_proc || transmogrifier_object if not transmogrifier raise ArgumentError, "Please pass either an object or a transmogrifier "\ "block" end # Convert to canonical names from_name = identify(from) to_name = identify(to) key = [from_name, to_name] # We treat the graph as authoritative for what transmogrifiers exist. if @graph.has_edge?(from_name, to_name) if not overwrite raise ArgumentError, "Registry already knows a transmogrifier for "\ "#{key}, aborting!" end end # Add edges and map data for the shortest path search. We treat all paths # as equally weighted. @graph.add_edge(from_name, to_name) @map_data[key] = 1 @map = RGL::EdgePropertiesMap.new(@map_data, true) # Finally, register transmogrifier @transmogrifiers[key] = transmogrifier end |
#transmogrify(from, to, verify_results = true) ⇒ Object
Transmogrify an object of one class into another class. If ‘verify_results` is true, the transmogrification result is checked to match the target class or hash, and an error is raised if there is no match.
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 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/octiron/transmogrifiers/registry.rb', line 134 def transmogrify(from, to, verify_results = true) # Get lookup keys from_name = from.class.to_s if from.is_a?(Hash) # Finding the correct from_name is tricky, because from is not a # prototype, but the graph and all intermediate from_name = best_matching_hash_prototype(from) end to_name = identify(to) # We'll ask the graph for the shortest path. If there is none, we can't # transmogrify. (Note: the @map changes with each registration/ # deregistration, so we instanciate the algorithm here). algo = RGL::DijkstraAlgorithm.new(@graph, @map, @visitor) path = algo.shortest_path(from_name, to_name) if path.nil? raise ArgumentError, "No transmogrifiers for #{[from_name, to_name]} "\ "found, aborting!" end # Transmogrify for each part of the path input = from result = nil path.inject do |step_from, step_to| # Call transmogrifier key = [step_from, step_to] result = @transmogrifiers[key].call(input) # Verify result if verify_results if result.nil? raise "Transmogrifier returned nil result!" end if step_to.is_a?(Hash) result.extend(::Collapsium::PrototypeMatch) if not result.prototype_match(step_to) raise "Transmogrifier returned Hash that did not match prototype "\ "#{step_to}, aborting!" end elsif result.class.to_s != step_to raise "Transmogrifier returned result of invalid class "\ "#{result.class}, aborting!" end end # Result is input for the next transmogrifier in the chain input = result # Make step_to the next step_from next step_to end return result end |