om 1.4.4 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- om (1.4.3)
4
+ om (1.5.0)
5
5
  mediashelf-loggable
6
6
  nokogiri (>= 1.4.2)
7
7
 
data/History.textile CHANGED
@@ -1,3 +1,7 @@
1
+ h3. 1.5.0
2
+
3
+ HYDRA-358 Added support for namespaceless terminologies
4
+
1
5
  h3. 1.4.4
2
6
 
3
7
  HYDRA-415 https://jira.duraspace.org/browse/HYDRA-415 Fixed insert of attribute nodes
data/lib/om/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Om
2
- VERSION = "1.4.4"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -1,27 +1,27 @@
1
1
  module OM::XML::Document
2
-
3
-
4
- # Class Methods -- These methods will be available on classes that include this Module
5
-
2
+
3
+
4
+ # Class Methods -- These methods will be available on classes that include this Module
5
+
6
6
  module ClassMethods
7
-
7
+
8
8
  attr_accessor :terminology, :template_registry
9
-
9
+
10
10
  # Sets the OM::XML::Terminology for the Document
11
11
  # Expects +&block+ that will be passed into OM::XML::Terminology::Builder.new
12
12
  def set_terminology &block
13
13
  @terminology = OM::XML::Terminology::Builder.new( &block ).build
14
14
  end
15
-
15
+
16
16
  # Define a new node template with the OM::XML::TemplateRegistry.
17
17
  # * +name+ is a Symbol indicating the name of the new template.
18
- # * The +block+ does the work of creating the new node, and will receive
18
+ # * The +block+ does the work of creating the new node, and will receive
19
19
  # a Nokogiri::XML::Builder and any other args passed to one of the node instantiation methods.
20
20
  def define_template name, &block
21
21
  @template_registry ||= OM::XML::TemplateRegistry.new
22
22
  @template_registry.define name, &block
23
23
  end
24
-
24
+
25
25
  # Returns any namespaces defined by the Class' Terminology
26
26
  def ox_namespaces
27
27
  if @terminology.nil?
@@ -30,16 +30,16 @@ module OM::XML::Document
30
30
  return @terminology.namespaces
31
31
  end
32
32
  end
33
-
33
+
34
34
  end
35
-
35
+
36
36
  # Instance Methods -- These methods will be available on instances of classes that include this module
37
-
37
+
38
38
  attr_accessor :ox_namespaces
39
-
39
+
40
40
  def self.included(klass)
41
41
  klass.extend(ClassMethods)
42
-
42
+
43
43
  klass.send(:include, OM::XML::Container)
44
44
  klass.send(:include, OM::XML::TermValueOperators)
45
45
  klass.send(:include, OM::XML::Validation)
@@ -54,7 +54,7 @@ module OM::XML::Document
54
54
  node.val=args
55
55
  else
56
56
  super
57
- end
57
+ end
58
58
  else
59
59
  term = self.class.terminology.retrieve_term(name)
60
60
  if (term)
@@ -63,25 +63,29 @@ module OM::XML::Document
63
63
  super
64
64
  end
65
65
  end
66
-
66
+
67
67
 
68
68
  end
69
69
 
70
70
 
71
71
  def find_by_xpath(xpath)
72
- ng_xml.xpath(xpath, ox_namespaces)
72
+ if ox_namespaces.values.compact.empty?
73
+ ng_xml.xpath(xpath)
74
+ else
75
+ ng_xml.xpath(xpath, ox_namespaces)
76
+ end
73
77
  end
74
-
75
-
78
+
79
+
76
80
  # Applies the property's corresponding xpath query, returning the result Nokogiri::XML::NodeSet
77
81
  def find_by_terms_and_value(*term_pointer)
78
- xpath = self.class.terminology.xpath_for(*term_pointer)
82
+ xpath = self.class.terminology.xpath_for(*term_pointer)
79
83
  find_by_xpath(xpath) unless xpath.nil?
80
84
  end
81
-
85
+
82
86
 
83
87
  # +term_pointer+ Variable length array of values in format [:accessor_name, :accessor_name ...] or [{:accessor_name=>index}, :accessor_name ...]
84
- # @example:
88
+ # @example:
85
89
  # find_by_terms( {:person => 1}, :first_name )
86
90
  # @example
87
91
  # find_by_terms( [:person, 1, :first_name] )
@@ -89,10 +93,10 @@ module OM::XML::Document
89
93
  # @example Pass in your own xpath query if you don't want to bother with Term pointers but do want OM to handle namespaces for you.
90
94
  # find_by_terms('//oxns:name[@type="personal"][contains(oxns:role, "donor")]')
91
95
  def find_by_terms(*term_pointer)
92
- xpath = self.class.terminology.xpath_with_indexes(*term_pointer)
96
+ xpath = self.class.terminology.xpath_with_indexes(*term_pointer)
93
97
  find_by_xpath(xpath) unless xpath.nil?
94
98
  end
95
-
99
+
96
100
  # Test whether the document has a node corresponding to the given term_pointer
97
101
  # @param [Array] term_pointer to test
98
102
  def node_exists?(*term_pointer)
@@ -103,11 +107,11 @@ module OM::XML::Document
103
107
  def template_registry
104
108
  self.class.template_registry
105
109
  end
106
-
110
+
107
111
  def template(node_type, *args)
108
112
  template_registry.instantiate(node_type, *args)
109
113
  end
110
-
114
+
111
115
  # Instantiate a +node_type+ template and add it as a child of +target_node+, where +target_node+ is one of:
112
116
  # * a Nokogiri::XML::Node
113
117
  # * a single-element Nokogiri::XML::NodeSet
@@ -118,8 +122,8 @@ module OM::XML::Document
118
122
  def add_child_node(target_node, node_type, *args, &block)
119
123
  manipulate_node(:add_child, target_node, node_type, *args, &block)
120
124
  end
121
-
122
- # Instantiate a +node_type+ template and insert it as the following sibling of +target_node+.
125
+
126
+ # Instantiate a +node_type+ template and insert it as the following sibling of +target_node+.
123
127
  # Returns the new Nokogiri::XML::Node.
124
128
  def add_next_sibling_node(target_node, node_type, *args, &block)
125
129
  manipulate_node(:add_next_sibling, target_node, node_type, *args, &block)
@@ -154,7 +158,7 @@ module OM::XML::Document
154
158
  def swap_node(target_node, node_type, *args, &block)
155
159
  manipulate_node(:swap, target_node, node_type, *args, &block)
156
160
  end
157
-
161
+
158
162
  # Returns a hash combining the current documents namespaces (provided by nokogiri) and any namespaces that have been set up by your Terminology.
159
163
  # Most importantly, this matches the 'oxns' namespace to the namespace you provided in your Terminology's root term config
160
164
  def ox_namespaces
data/lib/om/xml/term.rb CHANGED
@@ -1,37 +1,37 @@
1
- # Special options:
2
- # data_type, index_as, attributes,
1
+ # Special options:
2
+ # data_type, index_as, attributes,
3
3
  # is_root_term, required
4
4
  #
5
5
  class OM::XML::Term
6
-
6
+
7
7
  # Term::Builder Class Definition
8
8
  #
9
9
  # @example
10
- # tb2 = OM::XML::Term::Builder.new("my_term_name").path("fooPath").attributes({:lang=>"foo"}).index_as([:searchable, :facetable]).required(true).data_type(:text)
10
+ # tb2 = OM::XML::Term::Builder.new("my_term_name").path("fooPath").attributes({:lang=>"foo"}).index_as([:searchable, :facetable]).required(true).data_type(:text)
11
+ #
11
12
  #
12
- #
13
13
  #
14
- # When coding against Builders, remember that they rely on MethodMissing,
15
- # so any time you call a method on the Builder that it doesn't explicitly recognize,
14
+ # When coding against Builders, remember that they rely on MethodMissing,
15
+ # so any time you call a method on the Builder that it doesn't explicitly recognize,
16
16
  # the Builder will add your method & arguments to the it's settings and return itself.
17
17
  class Builder
18
18
  attr_accessor :name, :settings, :children, :terminology_builder
19
-
19
+
20
20
  def initialize(name, terminology_builder=nil)
21
21
  @name = name.to_sym
22
22
  @terminology_builder = terminology_builder
23
23
  @settings = {:required=>false, :data_type=>:string}
24
24
  @children = {}
25
25
  end
26
-
26
+
27
27
  def add_child(child)
28
28
  @children[child.name] = child
29
29
  end
30
-
30
+
31
31
  def retrieve_child(child_name)
32
32
  child = @children.fetch(child_name, nil)
33
33
  end
34
-
34
+
35
35
  def lookup_refs(nodes_visited=[])
36
36
  result = []
37
37
  if @settings[:ref]
@@ -40,7 +40,7 @@ class OM::XML::Term
40
40
  raise "Cannot perform lookup_ref for the #{self.name} builder. It doesn't have a reference to any terminology builder"
41
41
  end
42
42
  target = self.terminology_builder.retrieve_term_builder(*@settings[:ref])
43
-
43
+
44
44
  # Fail on circular references and return an intelligible error message
45
45
  if nodes_visited.include?(target)
46
46
  nodes_visited << self
@@ -59,12 +59,12 @@ class OM::XML::Term
59
59
  end
60
60
  return result
61
61
  end
62
-
62
+
63
63
  # 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
64
64
  # operates recursively, so it is possible to apply refs that in turn refer to other nodes.
65
65
  def resolve_refs!
66
66
  name_of_last_ref = nil
67
- lookup_refs.each_with_index do |ref,z|
67
+ lookup_refs.each_with_index do |ref,z|
68
68
  @settings = two_layer_merge(@settings, ref.settings)
69
69
  @children.merge!(ref.children)
70
70
  name_of_last_ref = ref.name
@@ -75,22 +75,22 @@ class OM::XML::Term
75
75
  @settings.delete :ref
76
76
  return self
77
77
  end
78
-
78
+
79
79
  # Returns a new Hash that merges +downstream_hash+ with +upstream_hash+
80
- # similar to calling +upstream_hash+.merge(+downstream_hash+) only it also merges
80
+ # similar to calling +upstream_hash+.merge(+downstream_hash+) only it also merges
81
81
  # any internal values that are themselves Hashes.
82
82
  def two_layer_merge(downstream_hash, upstream_hash)
83
83
  up = upstream_hash.dup
84
84
  dn = downstream_hash.dup
85
85
  up.each_pair do |setting_name, value|
86
- if value.kind_of?(Hash) && downstream_hash.has_key?(setting_name)
86
+ if value.kind_of?(Hash) && downstream_hash.has_key?(setting_name)
87
87
  dn[setting_name] = value.merge(downstream_hash[setting_name])
88
88
  up.delete(setting_name)
89
89
  end
90
90
  end
91
91
  return up.merge(dn)
92
92
  end
93
-
93
+
94
94
  # Builds a new OM::XML::Term based on the Builder object's current settings
95
95
  # If no path has been provided, uses the Builder object's name as the term's path
96
96
  # Recursively builds any children, appending the results as children of the Term that's being built.
@@ -100,9 +100,9 @@ class OM::XML::Term
100
100
  if term.self.settings.has_key?(:proxy)
101
101
  term = OM::XML::NamedTermProxy.new(self.name, self.settings[:proxy], terminology, self.settings)
102
102
  else
103
- term = OM::XML::Term.new(self.name)
104
-
105
- self.settings.each do |name, values|
103
+ term = OM::XML::Term.new(self.name, {}, terminology)
104
+
105
+ self.settings.each do |name, values|
106
106
  if term.respond_to?(name.to_s+"=")
107
107
  term.instance_variable_set("@#{name}", values)
108
108
  end
@@ -112,12 +112,12 @@ class OM::XML::Term
112
112
  end
113
113
  term.generate_xpath_queries!
114
114
  end
115
-
115
+
116
116
  return term
117
117
  end
118
-
118
+
119
119
  # Any unknown method calls will add an entry to the settings hash and return the current object
120
- def method_missing method, *args, &block
120
+ def method_missing method, *args, &block
121
121
  if args.length == 1
122
122
  args = args.first
123
123
  end
@@ -125,16 +125,16 @@ class OM::XML::Term
125
125
  return self
126
126
  end
127
127
  end
128
-
128
+
129
129
  #
130
- # Class Definition for Term
130
+ # Class Definition for Term
131
131
  #
132
132
 
133
133
  include OM::TreeNode
134
134
 
135
135
  attr_accessor :name, :xpath, :xpath_constrained, :xpath_relative, :path, :index_as, :required, :data_type, :variant_of, :path, :default_content_path, :is_root_term
136
136
  attr_accessor :children, :internal_xml, :terminology
137
-
137
+
138
138
  # Any XML attributes that qualify the Term.
139
139
  #
140
140
  # @example Declare a Term that has a given attribute (ie. //title[@xml:lang='eng'])
@@ -142,19 +142,19 @@ class OM::XML::Term
142
142
  # @example Use nil to point to nodes that do not have a given attribute (ie. //title[not(@xml:lang)])
143
143
  # t.title_without_lang_attribute(:path=>"title", :attributes=>{"xml:lang"=>nil})
144
144
  attr_accessor :attributes
145
-
145
+
146
146
  # Namespace Prefix (xmlns) for the Term.
147
147
  #
148
148
  # By default, OM assumes that all terms in a Terminology have the namespace set in the root of the document. If you want to set a different namespace for a Term, pass :namespace_prefix into its initializer (or call .namespace_prefix= on its builder)
149
149
  # If a node has _no_ namespace, you must explicitly set namespace_prefix to nil. Currently you have to do this on _each_ term, you can't set namespace_prefix to nil for an entire Terminology.
150
- #
150
+ #
151
151
  # @example
152
152
  # # For xml like this
153
153
  # <foo xmlns="http://foo.com/schemas/fooschema" xmlns:bar="http://bar.com/schemas/barschema">
154
154
  # <address>1400 Pennsylvania Avenue</address>
155
155
  # <bar:latitude>56</bar:latitude>
156
156
  # </foo>
157
- #
157
+ #
158
158
  # # The Terminology would look like this
159
159
  # OM::XML::Terminology::Builder.new do |t|
160
160
  # t.root(:name=>:foo, :path=>"foo", :xmlns=>"http://foo.com/schemas/fooschema", "xmlns:bar"=>"http://bar.com/schemas/barschema")
@@ -163,23 +163,32 @@ class OM::XML::Term
163
163
  # end
164
164
  #
165
165
  attr_accessor :namespace_prefix
166
-
167
-
166
+
167
+
168
168
  # h2. Namespaces
169
- # By default, OM assumes that all terms in a Terminology have the namespace set in the root of the document. If you want to set a different namespace for a Term, pass :namespasce_prefix into its initializer (or call .namespace_prefix= on its builder)
170
- # If a node has _no_ namespace, you must explicitly set namespace_prefix to nil.
171
- def initialize(name, opts={})
172
- opts = {:namespace_prefix=>"oxns", :ancestors=>[], :children=>{}}.merge(opts)
169
+ # By default, OM assumes you have no namespace defined unless it is explicitly defined at the root of your document.
170
+ # If you want to specify which namespace a term is using, use:
171
+ # namspace_prefix => "bar"
172
+ # This value defaults to nil, in which case if a default namespace is set in the termnology, that namespace will be used.
173
+ def initialize(name, opts={}, terminology=nil)
174
+ opts = {:ancestors=>[], :children=>{}}.merge(opts)
173
175
  [:children, :ancestors,:path, :index_as, :required, :type, :variant_of, :path, :attributes, :default_content_path, :namespace_prefix].each do |accessor_name|
174
- instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
176
+ instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
177
+ end
178
+ unless terminology.nil?
179
+ if opts[:namespace_prefix].nil?
180
+ unless terminology.namespaces["xmlns"].nil?
181
+ @namespace_prefix = "oxns"
182
+ end
183
+ end
175
184
  end
176
185
  @name = name
177
186
  if @path.nil? || @path.empty?
178
187
  @path = name.to_s
179
188
  end
180
189
  end
181
-
182
- def self.from_node(mapper_xml)
190
+
191
+ def self.from_node(mapper_xml)
183
192
  name = mapper_xml.attribute("name").text.to_sym
184
193
  attributes = {}
185
194
  mapper_xml.xpath("./attribute").each do |a|
@@ -189,19 +198,19 @@ class OM::XML::Term
189
198
  [:index_as, :required, :type, :variant_of, :path, :default_content_path, :namespace_prefix].each do |accessor_name|
190
199
  attribute = mapper_xml.attribute(accessor_name.to_s)
191
200
  unless attribute.nil?
192
- new_mapper.instance_variable_set("@#{accessor_name}", attribute.text )
193
- end
201
+ new_mapper.instance_variable_set("@#{accessor_name}", attribute.text )
202
+ end
194
203
  end
195
204
  new_mapper.internal_xml = mapper_xml
196
-
205
+
197
206
  mapper_xml.xpath("./mapper").each do |child_node|
198
207
  child = self.from_node(child_node)
199
208
  new_mapper.add_child(child)
200
209
  end
201
-
210
+
202
211
  return new_mapper
203
212
  end
204
-
213
+
205
214
  # crawl down into mapper's children hash to find the desired mapper
206
215
  # ie. @test_mapper.retrieve_mapper(:conference, :role, :text)
207
216
  def retrieve_term(*pointers)
@@ -220,19 +229,19 @@ class OM::XML::Term
220
229
  end
221
230
  return target
222
231
  end
223
-
232
+
224
233
  def is_root_term?
225
234
  @is_root_term == true
226
235
  end
227
-
236
+
228
237
  def xpath_absolute
229
238
  @xpath
230
239
  end
231
-
240
+
232
241
  # +term_pointers+ reference to the property you want to generate a builder template for
233
242
  # @opts
234
243
  def xml_builder_template(extra_opts = {})
235
- extra_attributes = extra_opts.fetch(:attributes, {})
244
+ extra_attributes = extra_opts.fetch(:attributes, {})
236
245
 
237
246
  node_options = []
238
247
  node_child_template = ""
@@ -260,7 +269,7 @@ class OM::XML::Term
260
269
  end
261
270
  return template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }
262
271
  end
263
-
272
+
264
273
  # Generates absolute, relative, and constrained xpaths for the term, setting xpath, xpath_relative, and xpath_constrained accordingly.
265
274
  # Also triggers update_xpath_values! on all child nodes, as their absolute paths rely on those of their parent nodes.
266
275
  def generate_xpath_queries!
@@ -270,7 +279,7 @@ class OM::XML::Term
270
279
  self.children.each_value {|child| child.generate_xpath_queries! }
271
280
  return self
272
281
  end
273
-
282
+
274
283
  # Return an XML representation of the Term
275
284
  # @param [Hash] options, the term will be added to it. If :children=>false, skips rendering child Terms
276
285
  # @param [Nokogiri::XML::Document] (optional) document to insert the term xml into
@@ -314,7 +323,7 @@ class OM::XML::Term
314
323
  xml.constrained xpath_constrained
315
324
  }
316
325
  if options.fetch(:children, true)
317
- xml.children
326
+ xml.children
318
327
  end
319
328
  }
320
329
  end
@@ -324,7 +333,7 @@ class OM::XML::Term
324
333
  end
325
334
  return doc
326
335
  end
327
-
336
+
328
337
  # private :update_xpath_values
329
-
338
+
330
339
  end