Class: Jinx::MatchVisitor
- Inherits:
-
ReferenceVisitor
- Object
- Visitor
- ReferenceVisitor
- Jinx::MatchVisitor
- Defined in:
- lib/jinx/resource/match_visitor.rb
Overview
A MatchVisitor visits two domain objects’ visitable attributes transitive closure in lock-step.
Direct Known Subclasses
Defined Under Namespace
Classes: DefaultMatcher
Constant Summary collapse
- DEF_MATCHER =
DefaultMatcher.new
Instance Attribute Summary collapse
-
#matches ⇒ {Resource => Resource}
readonly
The domain object matches.
Attributes inherited from Visitor
#cycles, #lineage, #options, #visited
Instance Method Summary collapse
- #add_match(source, target) ⇒ Object private
-
#copy_unmatched(source) ⇒ Resource?
private
A copy of the given source if this ReferenceVisitor has a copier, nil otherwise.
-
#identifier_match(source) ⇒ Object
private
The target matching the given source on the identifier, if any.
-
#initialize(opts = nil) {|obj| ... } ⇒ MatchVisitor
constructor
Creates a new visitor which matches source and target domain object references.
-
#match_for(source) ⇒ Object
private
The target matching the given source.
-
#match_for_visited(source) ⇒ <Resource>
private
The source match.
-
#match_reference(source, target, attribute) ⇒ {Resource => Resource}
private
Matches the given source and target attribute references.
-
#match_references(source, target, attributes) ⇒ {Resource => Resource}
private
The referenced attribute matches.
-
#visit(source, target) {|target, source| ... } ⇒ Object
Visits the source and target.
-
#visit_matched(source) {|target, source| ... } ⇒ Object
private
Visits the given source domain object.
Methods inherited from ReferenceVisitor
Methods inherited from Visitor
#clear, #current, #depth_first?, #filter, #from, #node_children, #prune_cycle_nodes, #root, #sync, #to_enum, #visit_children, #visit_node_and_children, #visit_recursive, #visit_root, #visited?
Constructor Details
#initialize(opts = nil) {|obj| ... } ⇒ MatchVisitor
Creates a new visitor which matches source and target domain object references. The domain attributes to visit are determined by calling the selector block given to this initializer. The selector arguments consist of the match source and target.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/jinx/resource/match_visitor.rb', line 22 def initialize(opts=nil) raise ArgumentError.new("Reference visitor missing domain reference selector") unless block_given? opts = Options.to_hash(opts) @matcher = opts.delete(:matcher) || DEF_MATCHER @matchable = opts.delete(:matchable) @copier = opts.delete(:copier) # the source => target matches @matches = {} # Apply a filter to the selected references so that only a matched reference is visited. opts[:filter] = Proc.new { |src| @matches[src] } # the class => {id => target} hash @id_mtchs = LazyHash.new { Hash.new } # Match the source references before navigating from the source to its references, since # only a matched reference is visited. super do |src| tgt = @matches[src] # the attributes to match on mas = yield(src) # match the attribute references match_references(src, tgt, mas) mas end end |
Instance Attribute Details
Instance Method Details
#add_match(source, target) ⇒ Object (private)
161 162 163 164 165 |
# File 'lib/jinx/resource/match_visitor.rb', line 161 def add_match(source, target) @matches[source] = target @id_mtchs[source.class][source.identifier] = target if source.identifier target end |
#copy_unmatched(source) ⇒ Resource? (private)
175 176 177 178 179 180 |
# File 'lib/jinx/resource/match_visitor.rb', line 175 def copy_unmatched(source) return unless @copier copy = @copier.call(source) logger.debug { "#{qp} copied unmatched #{source} to #{copy}." } if @verbose add_match(source, copy) end |
#identifier_match(source) ⇒ Object (private)
168 169 170 171 |
# File 'lib/jinx/resource/match_visitor.rb', line 168 def identifier_match(source) tgt = @id_mtchs[source.class][source.identifier] if source.identifier @matches[source] = tgt if tgt end |
#match_for(source) ⇒ Object (private)
157 158 159 |
# File 'lib/jinx/resource/match_visitor.rb', line 157 def match_for(source) @matches[source] or identifier_match(source) end |
#match_for_visited(source) ⇒ <Resource> (private)
Returns the source match.
101 102 103 104 105 |
# File 'lib/jinx/resource/match_visitor.rb', line 101 def match_for_visited(source) target = @matches[source] if target.nil? then raise ValidationError.new("Match visitor target not found for #{source}") end target end |
#match_reference(source, target, attribute) ⇒ {Resource => Resource} (private)
Matches the given source and target attribute references. The match is performed by this visitor’s matcher.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/jinx/resource/match_visitor.rb', line 127 def match_reference(source, target, attribute) srcs = source.send(attribute).to_enum tgts = target.send(attribute).to_enum # the match targets mtchd_tgts = Set.new # capture the matched targets and the the unmatched sources unmtchd_srcs = srcs.reject do |src| # the prior match, if any tgt = match_for(src) mtchd_tgts << tgt if tgt end # the unmatched targets unmtchd_tgts = tgts.difference(mtchd_tgts) logger.debug { "#{qp} matching #{unmtchd_tgts.qp}..." } if @verbose and not unmtchd_tgts.empty? # match the residual targets and sources rsd_mtchs = @matcher.match(unmtchd_srcs, unmtchd_tgts, source, attribute) # add residual matches rsd_mtchs.each { |src, tgt| add_match(src, tgt) } logger.debug { "#{qp} matched #{rsd_mtchs.qp}..." } if @verbose and not rsd_mtchs.empty? # The source => target match hash. # If there is a copier, then copy each unmatched source. matches = srcs.to_compact_hash { |src| match_for(src) or copy_unmatched(src) } matches end |
#match_references(source, target, attributes) ⇒ {Resource => Resource} (private)
111 112 113 114 115 116 117 118 |
# File 'lib/jinx/resource/match_visitor.rb', line 111 def match_references(source, target, attributes) # collect the references to visit matches = {} attributes.each do |ma| matches.merge!(match_reference(source, target, ma)) end matches end |
#visit(source, target) {|target, source| ... } ⇒ Object
Visits the source and target.
If a block is given to this method, then this method returns the evaluation of the block on the visited source reference and its matching copy, if any. The default return value is the target which matches source.
59 60 61 62 63 64 65 66 67 |
# File 'lib/jinx/resource/match_visitor.rb', line 59 def visit(source, target, &block) # clear the match hashes @matches.clear @id_mtchs.clear # seed the matches with the top-level source => target add_match(source, target) # Visit the source reference. super(source) { |src| visit_matched(src, &block) } end |
#visit_matched(source) {|target, source| ... } ⇒ Object (private)
Visits the given source domain object.
88 89 90 91 92 93 94 95 96 |
# File 'lib/jinx/resource/match_visitor.rb', line 88 def visit_matched(source) tgt = @matches[source] || return # Match the unvisited matchable references, if any. if @matchable then mas = @matchable.call(source) - attributes_to_visit(source) mas.each { |ma| match_reference(source, tgt, ma) } end block_given? ? yield(source, tgt) : tgt end |