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
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, #cyclic_nodes, #depth_first?, #filter, #from, #node_children, #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 45 46 47 48 |
# 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 visited reference so that only a matched reference is visited. # the reference filter flt = opts[:filter] opts[:filter] = Proc.new do |src| (flt.nil? or flt.call(src)) and !!@matches[src] end # 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)
164 165 166 167 168 |
# File 'lib/jinx/resource/match_visitor.rb', line 164 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)
Returns a copy of the given source if this ReferenceVisitor has a copier, nil otherwise.
178 179 180 181 182 183 |
# File 'lib/jinx/resource/match_visitor.rb', line 178 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)
Returns the target matching the given source on the identifier, if any.
171 172 173 174 |
# File 'lib/jinx/resource/match_visitor.rb', line 171 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)
Returns the target matching the given source.
160 161 162 |
# File 'lib/jinx/resource/match_visitor.rb', line 160 def match_for(source) @matches[source] or identifier_match(source) end |
#match_for_visited(source) ⇒ <Resource> (private)
Returns the source match.
105 106 107 108 109 |
# File 'lib/jinx/resource/match_visitor.rb', line 105 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.
131 132 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 |
# File 'lib/jinx/resource/match_visitor.rb', line 131 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)
Returns the referenced attribute matches.
115 116 117 118 119 120 121 122 |
# File 'lib/jinx/resource/match_visitor.rb', line 115 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.
63 64 65 66 67 68 69 70 71 |
# File 'lib/jinx/resource/match_visitor.rb', line 63 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.
92 93 94 95 96 97 98 99 100 |
# File 'lib/jinx/resource/match_visitor.rb', line 92 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 |