Class: OM::XML::Term::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/om/xml/term.rb

Overview

Term::Builder Class Definition

When coding against Builders, remember that they rely on MethodMissing, so any time you call a method on the Builder that it doesn’t explicitly recognize, the Builder will add your method & arguments to the it’s settings and return itself.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, terminology_builder = nil) ⇒ Builder

Returns a new instance of Builder.



11
12
13
14
15
16
# File 'lib/om/xml/term.rb', line 11

def initialize(name, terminology_builder=nil)
  @name = name.to_sym
  @terminology_builder = terminology_builder
  @settings = {:required=>false, :data_type=>:string}
  @children = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Any unknown method calls will add an entry to the settings hash and return the current object



116
117
118
119
120
121
122
# File 'lib/om/xml/term.rb', line 116

def method_missing method, *args, &block 
  if args.length == 1
    args = args.first
  end
  @settings[method] = args
  return self
end

Instance Attribute Details

#childrenObject

Returns the value of attribute children.



9
10
11
# File 'lib/om/xml/term.rb', line 9

def children
  @children
end

#nameObject

Returns the value of attribute name.



9
10
11
# File 'lib/om/xml/term.rb', line 9

def name
  @name
end

#settingsObject

Returns the value of attribute settings.



9
10
11
# File 'lib/om/xml/term.rb', line 9

def settings
  @settings
end

#terminology_builderObject

Returns the value of attribute terminology_builder.



9
10
11
# File 'lib/om/xml/term.rb', line 9

def terminology_builder
  @terminology_builder
end

Instance Method Details

#add_child(child) ⇒ Object



18
19
20
# File 'lib/om/xml/term.rb', line 18

def add_child(child)
  @children[child.name] = child
end

#build(terminology = nil) ⇒ Object

Builds a new OM::XML::Term based on the Builder object’s current settings If no path has been provided, uses the Builder object’s name as the term’s path Recursively builds any children, appending the results as children of the Term that’s being built.

Parameters:



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/om/xml/term.rb', line 94

def build(terminology=nil)
  self.resolve_refs!
  if term.self.settings.has_key?(:proxy)
    term = OM::XML::NamedTermProxy.new(self.name, self.settings[:proxy], terminology, self.settings)
  else
    term = OM::XML::Term.new(self.name)
  
    self.settings.each do |name, values|  
      if term.respond_to?(name.to_s+"=")
        term.instance_variable_set("@#{name}", values)
      end
    end
    @children.each_value do |child|
      term.add_child child.build(terminology)
    end
    term.generate_xpath_queries!
  end
  
  return term
end

#lookup_refs(nodes_visited = []) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/om/xml/term.rb', line 26

def lookup_refs(nodes_visited=[])
  result = []
  if @settings[:ref]
    # Fail if we do not have terminology builder
    if self.terminology_builder.nil?
      raise "Cannot perform lookup_ref for the #{self.name} builder.  It doesn't have a reference to any terminology builder"
    end
    begin
      target = self.terminology_builder.retrieve_term_builder(*@settings[:ref])
    rescue OM::XML::Terminology::BadPointerError
      # Clarify message on BadPointerErrors
      raise OM::XML::Terminology::BadPointerError, "#{self.name} refers to a Term Builder that doesn't exist.  The bad pointer is #{@settings[:ref].inspect}"
    end
    
    # Fail on circular references and return an intelligible error message
    if nodes_visited.include?(target)
      nodes_visited << self
      nodes_visited << target
      trail = ""
      nodes_visited.each_with_index do |node, z|
        trail << node.name.inspect
        unless z == nodes_visited.length-1
          trail << " => "
        end
      end
      raise OM::XML::Terminology::CircularReferenceError, "Circular reference in Terminology: #{trail}"
    end
    result << target
    result.concat( target.lookup_refs(nodes_visited << self) )
  end
  return result
end

#resolve_refs!Object

If a :ref value has been set, looks up the target of that ref and merges the target’s settings & children with the current builder’s settings & children operates recursively, so it is possible to apply refs that in turn refer to other nodes.



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/om/xml/term.rb', line 61

def resolve_refs!
  name_of_last_ref = nil
  lookup_refs.each_with_index do |ref,z|        
    @settings = two_layer_merge(@settings, ref.settings)
    @children.merge!(ref.children)
    name_of_last_ref = ref.name
  end
  if @settings[:path].nil? && !name_of_last_ref.nil?
    @settings[:path] = name_of_last_ref.to_s
  end
  @settings.delete :ref
  return self
end

#retrieve_child(child_name) ⇒ Object



22
23
24
# File 'lib/om/xml/term.rb', line 22

def retrieve_child(child_name)
  child = @children.fetch(child_name, nil)
end

#two_layer_merge(downstream_hash, upstream_hash) ⇒ Object

Returns a new Hash that merges downstream_hash with upstream_hash similar to calling upstream_hash.merge(downstream_hash) only it also merges any internal values that are themselves Hashes.



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/om/xml/term.rb', line 78

def two_layer_merge(downstream_hash, upstream_hash)
  up = upstream_hash.dup
  dn = downstream_hash.dup
  up.each_pair do |setting_name, value|
    if value.kind_of?(Hash) && downstream_hash.has_key?(setting_name)  
      dn[setting_name] = value.merge(downstream_hash[setting_name])
      up.delete(setting_name)
    end
  end
  return up.merge(dn)
end