om 1.4.4 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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