om 1.0.2 → 1.1.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.
@@ -36,11 +36,12 @@ describe "OM::XML::Terminology" do
36
36
  t.family_name(:path=>"namePart", :attributes=>{:type=>"family"})
37
37
  t.given_name(:path=>"namePart", :attributes=>{:type=>"given"}, :label=>"first name")
38
38
  t.terms_of_address(:path=>"namePart", :attributes=>{:type=>"termsOfAddress"})
39
+ t.person_id(:path=>"namePart", :attributes=>{:type=>:none})
39
40
  }
40
41
  # lookup :person, :first_name
41
42
  t.person(:ref=>:name, :attributes=>{:type=>"personal"})
42
43
  t.conference(:ref=>:name, :attributes=>{:type=>"conference"})
43
-
44
+
44
45
  t.role {
45
46
  t.text(:path=>"roleTerm",:attributes=>{:type=>"text"})
46
47
  t.code(:path=>"roleTerm",:attributes=>{:type=>"code"})
@@ -78,6 +79,7 @@ describe "OM::XML::Terminology" do
78
79
 
79
80
  @test_full_terminology.retrieve_term(:person).xpath.should == '//oxns:name[@type="personal"]'
80
81
  @test_full_terminology.retrieve_term(:person).xpath_relative.should == 'oxns:name[@type="personal"]'
82
+ @test_full_terminology.retrieve_term(:person, :person_id).xpath_relative.should == 'oxns:namePart[not(@type)]'
81
83
  end
82
84
 
83
85
  it "constructs templates for value-driven searches" do
@@ -221,6 +223,9 @@ describe "OM::XML::Terminology" do
221
223
  @test_full_terminology.xpath_for(:person, :date).should == '//oxns:name[@type="personal"]/oxns:namePart[@type="date"]'
222
224
 
223
225
  @test_full_terminology.xpath_for(:person, :date, "2010").should == '//oxns:name[@type="personal"]/oxns:namePart[@type="date" and contains(., "2010")]'
226
+
227
+ @test_full_terminology.xpath_for(:person, :person_id).should == '//oxns:name[@type="personal"]/oxns:namePart[not(@type)]'
228
+
224
229
  end
225
230
 
226
231
  it "should support including root terms in term pointer" do
@@ -12,10 +12,7 @@ describe "OM::XML::Container" do
12
12
 
13
13
  it "should automatically include the other modules" do
14
14
  XMLTest.included_modules.should include(OM::XML::Container)
15
- XMLTest.included_modules.should include(OM::XML::Accessors)
16
15
  XMLTest.included_modules.should include(OM::XML::Validation)
17
- XMLTest.included_modules.should include(OM::XML::Properties)
18
- XMLTest.included_modules.should include(OM::XML::PropertyValueOperators)
19
16
  end
20
17
 
21
18
  describe "#sanitize_pointer" do
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 2
10
- version: 1.0.2
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Zumwalt
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-15 00:00:00 -06:00
18
+ date: 2011-03-05 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -208,14 +208,10 @@ files:
208
208
  - lib/om/samples/mods_article.rb
209
209
  - lib/om/tree_node.rb
210
210
  - lib/om/xml.rb
211
- - lib/om/xml/accessors.rb
212
211
  - lib/om/xml/container.rb
213
212
  - lib/om/xml/document.rb
214
- - lib/om/xml/generator.rb
215
213
  - lib/om/xml/named_term_proxy.rb
216
214
  - lib/om/xml/node_generator.rb
217
- - lib/om/xml/properties.rb
218
- - lib/om/xml/property_value_operators.rb
219
215
  - lib/om/xml/term.rb
220
216
  - lib/om/xml/term_value_operators.rb
221
217
  - lib/om/xml/term_xpath_generator.rb
@@ -231,15 +227,11 @@ files:
231
227
  - spec/integration/rights_metadata_integration_example_spec.rb
232
228
  - spec/spec.opts
233
229
  - spec/spec_helper.rb
234
- - spec/unit/accessors_spec.rb
235
230
  - spec/unit/container_spec.rb
236
231
  - spec/unit/document_spec.rb
237
- - spec/unit/generator_spec.rb
238
232
  - spec/unit/named_term_proxy_spec.rb
239
233
  - spec/unit/node_generator_spec.rb
240
234
  - spec/unit/om_spec.rb
241
- - spec/unit/properties_spec.rb
242
- - spec/unit/property_value_operators_spec.rb
243
235
  - spec/unit/term_builder_spec.rb
244
236
  - spec/unit/term_spec.rb
245
237
  - spec/unit/term_value_operators_spec.rb
@@ -285,15 +277,11 @@ summary: "OM (Opinionated Metadata): A library to help you tame sprawling XML sc
285
277
  test_files:
286
278
  - spec/integration/rights_metadata_integration_example_spec.rb
287
279
  - spec/spec_helper.rb
288
- - spec/unit/accessors_spec.rb
289
280
  - spec/unit/container_spec.rb
290
281
  - spec/unit/document_spec.rb
291
- - spec/unit/generator_spec.rb
292
282
  - spec/unit/named_term_proxy_spec.rb
293
283
  - spec/unit/node_generator_spec.rb
294
284
  - spec/unit/om_spec.rb
295
- - spec/unit/properties_spec.rb
296
- - spec/unit/property_value_operators_spec.rb
297
285
  - spec/unit/term_builder_spec.rb
298
286
  - spec/unit/term_spec.rb
299
287
  - spec/unit/term_value_operators_spec.rb
@@ -1,217 +0,0 @@
1
- module OM::XML::Accessors
2
-
3
- module ClassMethods
4
- attr_accessor :accessors
5
-
6
- def accessor(accessor_name, opts={})
7
- @accessors ||= {}
8
- insert_accessor(accessor_name, opts)
9
- end
10
-
11
- def insert_accessor(accessor_name, accessor_opts, parent_names=[])
12
- unless accessor_opts.has_key?(:relative_xpath)
13
- accessor_opts[:relative_xpath] = "oxns:#{accessor_name}"
14
- end
15
-
16
- destination = @accessors
17
- parent_names.each do |parent_name|
18
- destination = destination[parent_name][:children]
19
- end
20
-
21
- destination[accessor_name] = accessor_opts
22
-
23
- # Recursively call insert_accessor for any children
24
- if accessor_opts.has_key?(:children)
25
- children_array = accessor_opts[:children].dup
26
- accessor_opts[:children] = {}
27
- children_array.each do |child|
28
- if child.kind_of?(Hash)
29
- child_name = child.keys.first
30
- child_opts = child.values.first
31
- else
32
- child_name = child
33
- child_opts = {}
34
- end
35
- insert_accessor(child_name, child_opts, parent_names+[accessor_name] )
36
- end
37
- end
38
-
39
- end
40
-
41
- # Generates accessors from the object's @properties hash.
42
- # If no properties have been declared, it doesn't do anything.
43
- def generate_accessors_from_properties
44
- if self.properties.nil? || self.properties.empty?
45
- return nil
46
- end
47
- @accessors ||= {}
48
- # Skip the :unresolved portion of the properties hash
49
- accessorizables = self.properties.dup
50
- accessorizables.delete(:unresolved)
51
- accessorizables.each_pair do |property_ref, property_info|
52
- insert_accessor_from_property(property_ref, property_info)
53
- end
54
- end
55
-
56
- # Recurses through a property's info and convenience methods, adding accessors as necessary
57
- def insert_accessor_from_property(property_ref, property_info, parent_names=[])
58
- insert_accessor(property_ref,{:relative_xpath => property_info[:xpath_relative], :children=>[]}, parent_names)
59
- property_info.fetch(:convenience_methods,{}).each_pair do |cm_name, cm_info|
60
- insert_accessor_from_property(cm_name, cm_info, parent_names+[property_ref] )
61
- end
62
- end
63
-
64
- # Returns the configuration info for the selected accessor.
65
- # Ingores any integers in the array (ie. nodeset indices intended for use in other accessor convenience methods)
66
- def accessor_info(*pointers)
67
- info = @accessors
68
-
69
- # flatten the pointers array, excluding node indices
70
- pointers = pointers_to_flat_array(pointers, false)
71
-
72
- pointers.each do |pointer|
73
-
74
- unless pointers.index(pointer) == 0
75
- info = info.fetch(:children, nil)
76
- if info.nil?
77
- return nil
78
- end
79
- end
80
-
81
- info = info.fetch(pointer, nil)
82
- if info.nil?
83
- return nil
84
- end
85
- end
86
- return info
87
- end
88
-
89
-
90
- def accessor_xpath(*pointers)
91
- if pointers.first.kind_of?(String)
92
- return pointers.first
93
- end
94
-
95
- keys = []
96
- xpath = "//"
97
- pointers.each do |pointer|
98
-
99
- if pointer.kind_of?(Hash)
100
- k = pointer.keys.first
101
- index = pointer[k]
102
- else
103
- k = pointer
104
- index = nil
105
- end
106
-
107
- keys << k
108
-
109
- # key_index = keys.index(k)
110
- pointer_index = pointers.index(pointer)
111
- # accessor_info = accessor_info(*keys[0..key_index])
112
- accessor_info = accessor_info(*keys)
113
-
114
- # Return nil if there is no accessor info to work with
115
- if accessor_info.nil? then return nil end
116
-
117
- relative_path = accessor_info[:relative_xpath]
118
-
119
- if relative_path.kind_of?(Hash)
120
- if relative_path.has_key?(:attribute)
121
- relative_path = "@"+relative_path[:attribute]
122
- end
123
- else
124
-
125
- unless index.nil?
126
- relative_path = add_node_index_predicate(relative_path, index)
127
- end
128
-
129
- if accessor_info.has_key?(:default_content_path)
130
- relative_path << "/"+accessor_info[:default_content_path]
131
- end
132
-
133
- end
134
- if pointer_index > 0
135
- relative_path = "/"+relative_path
136
- end
137
- xpath << relative_path
138
- end
139
-
140
- return xpath
141
- end
142
-
143
- def accessor_constrained_xpath(pointers, constraint)
144
- constraint_function = "contains(., \"#{constraint}\")"
145
- xpath = self.accessor_xpath(*pointers)
146
- xpath = self.add_predicate(xpath, constraint_function)
147
- end
148
-
149
- # Adds xpath xpath node index predicate to the end of your xpath query
150
- # Example:
151
- # add_node_index_predicate("//oxns:titleInfo",0)
152
- # => "//oxns:titleInfo[1]"
153
- #
154
- # add_node_index_predicate("//oxns:titleInfo[@lang=\"finnish\"]",0)
155
- # => "//oxns:titleInfo[@lang=\"finnish\"][1]"
156
- def add_node_index_predicate(xpath_query, array_index_value)
157
- modified_query = xpath_query.dup
158
- modified_query << "[#{array_index_value + 1}]"
159
- end
160
-
161
- # Adds xpath:position() method call to the end of your xpath query
162
- # Examples:
163
- #
164
- # add_position_predicate("//oxns:titleInfo",0)
165
- # => "//oxns:titleInfo[position()=1]"
166
- #
167
- # add_position_predicate("//oxns:titleInfo[@lang=\"finnish\"]",0)
168
- # => "//oxns:titleInfo[@lang=\"finnish\" and position()=1]"
169
- def add_position_predicate(xpath_query, array_index_value)
170
- position_function = "position()=#{array_index_value + 1}"
171
- self.add_predicate(xpath_query, position_function)
172
- end
173
-
174
- def add_predicate(xpath_query, predicate)
175
- modified_query = xpath_query.dup
176
- # if xpath_query.include?("]")
177
- if xpath_query[xpath_query.length-1..xpath_query.length] == "]"
178
- modified_query.insert(xpath_query.rindex("]"), " and #{predicate}")
179
- else
180
- modified_query << "[#{predicate}]"
181
- end
182
- return modified_query
183
- end
184
-
185
- def accessor_generic_name(*pointers)
186
- pointers_to_flat_array(pointers, false).join("_")
187
- end
188
-
189
- def accessor_hierarchical_name(*pointers)
190
- pointers_to_flat_array(pointers, true).join("_")
191
- end
192
-
193
- def pointers_to_flat_array(pointers, include_indices=true)
194
- OM.pointers_to_flat_array(pointers, include_indices)
195
- end
196
-
197
- end
198
-
199
- # Instance Methods -- These methods will be available on instances of OM classes (ie. the actual xml documents)
200
-
201
- def self.included(klass)
202
- klass.extend(ClassMethods)
203
- end
204
-
205
- # *pointers Variable length array of values in format [:accessor_name, :accessor_name ...] or [{:accessor_name=>index}, :accessor_name ...]
206
- # example: [:person, 1, :first_name]
207
- # Currently, indexes must be integers.
208
- def retrieve(*pointers)
209
- xpath = self.class.accessor_xpath(*pointers)
210
- if xpath.nil?
211
- return nil
212
- else
213
- return ng_xml.xpath(xpath, ox_namespaces)
214
- end
215
- end
216
-
217
- end
@@ -1,26 +0,0 @@
1
- module OM::XML::Generator
2
-
3
- attr_accessor :ng_xml
4
-
5
- # Class Methods -- These methods will be available on classes that include this Module
6
-
7
- module ClassMethods
8
-
9
- def generate(property_ref, builder_new_value, opts={})
10
- template = builder_template(property_ref, opts)
11
- builder_call_body = eval('"' + template + '"')
12
- builder = Nokogiri::XML::Builder.new do |xml|
13
- eval( builder_call_body )
14
- end
15
-
16
- return builder.doc
17
- end
18
-
19
- end
20
-
21
- # Instance Methods -- These methods will be available on instances of classes that include this module
22
-
23
- def self.included(klass)
24
- klass.extend(ClassMethods)
25
- end
26
- end
@@ -1,396 +0,0 @@
1
- module OM::XML::Properties
2
-
3
- attr_accessor :ng_xml
4
-
5
- # Class Methods -- These methods will be available on classes that include this Module
6
-
7
- module ClassMethods
8
- attr_accessor :root_property_ref, :root_config, :ox_namespaces
9
- attr_reader :properties
10
-
11
- def root_property( property_ref, path, namespace, opts={})
12
- property property_ref, opts.merge({:path=>path, :ref=>property_ref})
13
- @root_config = opts.merge({:namespace=>namespace, :path=>path, :ref=>property_ref})
14
- @root_property_ref = property_ref
15
- @ox_namespaces = {'oxns'=>root_config[:namespace]}
16
- @schema_url = opts[:schema]
17
- end
18
-
19
- def property( property_ref, opts={})
20
- @properties ||= {}
21
- @properties[property_ref] = opts.merge({:ref=>property_ref})
22
- configure_property @properties[property_ref]
23
- configure_paths @properties[property_ref]
24
- end
25
-
26
- def configure_property(prop_hash=root_config)
27
- if prop_hash.has_key?(:variant_of)
28
- properties[prop_hash[:ref]] = properties[prop_hash[:variant_of]].deep_copy.merge(prop_hash)
29
- end
30
- if !prop_hash.has_key?(:convenience_methods)
31
- prop_hash[:convenience_methods] = {}
32
- end
33
- end
34
-
35
- # Generates appropriate xpath queries for a property described by the given hash
36
- # putting the results into the hash under keys like :xpath and :xpath_constrained
37
- # This is also done recursively for all subelements and convenience methods described in the hash.
38
- def configure_paths(prop_hash=root_config)
39
- prop_hash[:path] ||= ""
40
- # prop_hash[:path] = Array( prop_hash[:path] )
41
- xpath_opts = {}
42
-
43
- if prop_hash.has_key?(:variant_of)
44
- xpath_opts[:variations] = prop_hash
45
- end
46
-
47
- xpath_constrained_opts = xpath_opts.merge({:constraints=>:default})
48
-
49
- relative_xpath = generate_xpath(prop_hash, xpath_opts.merge(:relative=>true))
50
- # prop_hash[:xpath] = generate_xpath(prop_hash, xpath_opts)
51
- prop_hash[:xpath] = "//#{relative_xpath}"
52
- prop_hash[:xpath_relative] = relative_xpath
53
- prop_hash[:xpath_constrained] = generate_xpath(prop_hash, xpath_constrained_opts).gsub('"', '\"')
54
-
55
- prop_hash[:convenience_methods].each_pair do |cm_name, cm_props|
56
- cm_xpath_relative_opts = cm_props.merge(:variations => cm_props, :relative=>true)
57
- cm_constrained_opts = xpath_opts.merge(:constraints => cm_props)
58
-
59
- cm_relative_xpath = generate_xpath(cm_props, cm_xpath_relative_opts)
60
- prop_hash[:convenience_methods][cm_name][:xpath_relative] = cm_relative_xpath
61
- # prop_hash[:convenience_methods][cm_name][:xpath] = generate_xpath(cm_xpath_hash, cm_xpath_opts)
62
- prop_hash[:convenience_methods][cm_name][:xpath] = prop_hash[:xpath] + "/" + cm_relative_xpath
63
- prop_hash[:convenience_methods][cm_name][:xpath_constrained] = generate_xpath(prop_hash, cm_constrained_opts).gsub('"', '\"')
64
- end
65
-
66
- if prop_hash.has_key?(:subelements)
67
- prop_hash[:subelements].each do |se|
68
- configure_subelement_paths(se, prop_hash, xpath_opts)
69
- end
70
- end
71
-
72
- if properties.fetch(:unresolved, {}).has_key?(prop_hash[:ref])
73
- ref = prop_hash[:ref]
74
- properties[:unresolved][ref].each do |parent_prop_hash|
75
- logger.debug "Resolving #{ref} subelement for #{parent_prop_hash[:ref]} property"
76
- configure_subelement_paths(ref, parent_prop_hash, xpath_opts)
77
- end
78
- properties[:unresolved].delete(ref)
79
- end
80
-
81
- end
82
-
83
- def configure_subelement_paths(se, parent_prop_hash, parent_xpath_opts)
84
- # se_xpath_opts = parent_xpath_opts.merge(:subelement_of => parent_prop_hash[:ref])
85
- se_xpath_opts = parent_xpath_opts
86
- new_se_props = {}
87
- if parent_prop_hash.has_key?(:variant_of)
88
- se_xpath_opts[:variations] = parent_prop_hash
89
- end
90
-
91
- if se.instance_of?(String)
92
-
93
- new_se_props[:path] = se
94
- new_se_props[:xpath_relative] = generate_xpath({:path=>se}, :relative=>true)
95
- new_se_props[:xpath] = parent_prop_hash[:xpath] + "/" + new_se_props[:xpath_relative]
96
- se_xpath_constrained_opts = se_xpath_opts.merge({:constraints=>{:path=>se}})
97
- new_se_props[:xpath_constrained] = generate_xpath(parent_prop_hash, se_xpath_constrained_opts)
98
-
99
- elsif se.instance_of?(Symbol)
100
-
101
- if properties.has_key?(se)
102
-
103
- se_props = properties[se]
104
-
105
- new_se_props = se_props.deep_copy
106
- # new_se_props[:path] = se_props[:path]
107
- # new_se_props[:xpath_relative] = se_props[:xpath_relative]
108
- new_se_props[:xpath] = parent_prop_hash[:xpath] + "/" + new_se_props[:xpath_relative]
109
- se_xpath_constrained_opts = parent_xpath_opts.merge(:constraints => se_props, :subelement_of => parent_prop_hash[:ref])
110
- new_se_props[:xpath_constrained] = generate_xpath(parent_prop_hash, se_xpath_constrained_opts)
111
-
112
- else
113
- properties[:unresolved] ||= {}
114
- properties[:unresolved][se] ||= []
115
- properties[:unresolved][se] << parent_prop_hash
116
- logger.debug("Added #{se.inspect} to unresolved properties with parent #{parent_prop_hash[:ref]}")
117
- end
118
- else
119
- logger.info("failed to generate path for #{se.inspect}")
120
- se_xpath = ""
121
- se_xpath_constrained = ""
122
- end
123
-
124
- if new_se_props.has_key?(:xpath_constrained)
125
- new_se_props[:xpath_constrained].gsub!('"', '\"')
126
- end
127
-
128
- properties[ parent_prop_hash[:ref] ][:convenience_methods][se.to_sym] = new_se_props
129
-
130
- end
131
-
132
- def generate_xpath( property_info, opts={})
133
- prefix = "oxns"
134
- property_info[:path] ||= ""
135
- path = property_info[:path]
136
- unless path.kind_of?(Hash)
137
- path = Array( path )
138
- end
139
- template = ""
140
- template << "//" unless opts[:relative]
141
- template << "#{prefix}:"
142
-
143
- if path.kind_of?(Hash)
144
- if path.has_key?(:attribute)
145
- template = "@"+path[:attribute]
146
- end
147
- else
148
- template << delimited_list(path, "/#{prefix}:")
149
- end
150
-
151
- predicates = []
152
- default_content_path = property_info.has_key?(:default_content_path) ? property_info[:default_content_path] : ""
153
- subelement_path_parts = []
154
-
155
- # Skip everything if a template was provided
156
- if opts.has_key?(:template)
157
- template = eval('"' + opts[:template] + '"')
158
- else
159
- # Apply variations
160
- if opts.has_key?(:variations)
161
- if opts[:variations].has_key?(:attributes)
162
- opts[:variations][:attributes].each_pair do |attr_name, attr_value|
163
- predicates << "@#{attr_name}=\"#{attr_value}\""
164
- end
165
- end
166
- if opts[:variations].has_key?(:subelement_path)
167
- if opts[:variations][:subelement_path].instance_of?(Array)
168
- opts[:variations][:subelement_path].each do |se_path|
169
- subelement_path_parts << "#{prefix}:#{se_path}"
170
- end
171
- else
172
- subelement_path_parts << "#{prefix}:#{opts[:variations][:subelement_path]}"
173
- end
174
- end
175
- end
176
-
177
- # Apply constraints
178
- if opts.has_key?(:constraints)
179
- arguments_for_contains_function = []
180
- if opts[:constraints] == :default
181
- if property_info.has_key?(:default_content_path)
182
- default_content_path = property_info[:default_content_path]
183
- arguments_for_contains_function << "#{prefix}:#{default_content_path}"
184
- end
185
- elsif opts[:constraints].has_key?(:path)
186
- constraint_predicates = []
187
-
188
- constraints_path_arg = opts[:constraints][:path]
189
- if constraints_path_arg.kind_of?(Hash)
190
- if constraints_path_arg.has_key?(:attribute)
191
- constraint_path = "@"+constraints_path_arg[:attribute]
192
- end
193
- else
194
- constraint_path = "#{prefix}:#{constraints_path_arg}"
195
- end
196
- if opts.has_key?(:subelement_of) && opts[:constraints].has_key?(:default_content_path)
197
- # constraint_path = "#{prefix}:#{opts[:constraints][:path]}"
198
- constraint_path << "/#{prefix}:#{opts[:constraints][:default_content_path]}"
199
- end
200
- arguments_for_contains_function << constraint_path
201
-
202
- if opts[:constraints].has_key?(:attributes) && opts[:constraints][:attributes].kind_of?(Hash)
203
- opts[:constraints][:attributes].each_pair do |attr_name, attr_value|
204
- constraint_predicates << "@#{attr_name}=\"#{attr_value}\""
205
- end
206
- end
207
-
208
- unless constraint_predicates.empty?
209
- arguments_for_contains_function.last << "[#{delimited_list(constraint_predicates)}]"
210
- end
211
-
212
- end
213
- arguments_for_contains_function << "\":::constraint_value:::\""
214
-
215
- predicates << "contains(#{delimited_list(arguments_for_contains_function)})"
216
-
217
- end
218
-
219
- unless predicates.empty?
220
- template << "["
221
- template << delimited_list(predicates, " and ")
222
- template << "]"
223
- end
224
-
225
- unless subelement_path_parts.empty?
226
- subelement_path_parts.each {|path_part| template << "/#{path_part}"}
227
- end
228
- end
229
-
230
- # result = eval( '"' + template + '"' )
231
- return template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }
232
- end
233
-
234
- #
235
- # Convenience Methods for Retrieving Generated Info
236
- #
237
-
238
- def xpath_query_for( property_ref, query_opts={}, opts={} )
239
-
240
- if property_ref.instance_of?(String)
241
- xpath_query = property_ref
242
- else
243
- property_info = property_info_for( property_ref )
244
-
245
- if !property_info.nil?
246
- if query_opts.kind_of?(String)
247
- constraint_value = query_opts
248
- xpath_template = property_info[:xpath_constrained]
249
- xpath_query = eval( '"' + xpath_template + '"' )
250
- elsif query_opts.kind_of?(Hash) && !query_opts.empty?
251
- key_value_pair = query_opts.first
252
- constraint_value = key_value_pair.last
253
- xpath_template = property_info[:convenience_methods][key_value_pair.first][:xpath_constrained]
254
- xpath_query = eval( '"' + xpath_template + '"' )
255
- else
256
- xpath_query = property_info[:xpath]
257
- end
258
- else
259
- xpath_query = nil
260
- end
261
- end
262
- return xpath_query
263
- end
264
-
265
- def property_info_for(property_ref)
266
- if property_ref.instance_of?(Array) && property_ref.length < 2
267
- property_ref = property_ref[0]
268
- end
269
- if property_ref.instance_of?(Symbol)
270
- property_info = properties[property_ref]
271
- elsif property_ref.kind_of?(Array)
272
- prop_ref = property_ref[property_ref.length-2]
273
- cm_name = property_ref[property_ref.length-1]
274
- if properties.has_key?(prop_ref)
275
- property_info = properties[prop_ref][:convenience_methods][cm_name]
276
- end
277
- else
278
- property_info = nil
279
- end
280
- return property_info
281
- end
282
-
283
-
284
- def delimited_list( values_array, delimiter=", ")
285
- result = values_array.collect{|a| a + delimiter}.to_s.chomp(delimiter)
286
- end
287
-
288
-
289
- #
290
- # Builder Support
291
- #
292
-
293
- # @property_ref reference to the property you want to generate a builder template for
294
- # @opts
295
- def builder_template(property_ref, opts={})
296
- property_info = property_info_for(property_ref)
297
-
298
- prop_info = property_info.merge(opts)
299
-
300
- if prop_info.nil?
301
- return nil
302
- else
303
- node_options = []
304
- node_child_template = ""
305
- if prop_info.has_key?(:default_content_path)
306
- node_child_options = ["\':::builder_new_value:::\'"]
307
- node_child_template = " { xml.#{property_info[:default_content_path]}( #{delimited_list(node_child_options)} ) }"
308
- else
309
- node_options = ["\':::builder_new_value:::\'"]
310
- end
311
- # if opts.has_key?(:attributes) ...
312
- # ...
313
- if prop_info.has_key?(:attributes)
314
- applicable_attributes( prop_info[:attributes] ).each_pair do |k,v|
315
- node_options << ":#{k}=>\'#{v}\'"
316
- end
317
- end
318
- template = "xml.#{prop_info[:path]}( #{delimited_list(node_options)} )" + node_child_template
319
- return template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }
320
- end
321
- end
322
-
323
- # @attributes_spec Array or Hash
324
- # Returns a Hash where all of the values are strings
325
- # {:type=>"date"} will return {:type=>"date"}
326
- # {["authority", {:type=>["text","code"]}] will return {:type=>"text"}
327
- def applicable_attributes(attributes)
328
-
329
- if attributes.kind_of?(Hash)
330
- attributes_hash = attributes
331
- elsif attributes.kind_of?(Array)
332
- attributes_hash = {}
333
- attributes.each do |bute|
334
- if bute.kind_of?(Hash)
335
- attributes_hash.merge!(bute)
336
- end
337
- end
338
- end
339
-
340
- applicable_attributes = {}
341
- attributes_hash.each_pair {|k,v| applicable_attributes[k] = condense_value(v) }
342
-
343
-
344
- return applicable_attributes
345
- end
346
-
347
- def condense_value(value)
348
- if value.kind_of?(Array)
349
- return value.first
350
- else
351
- return value
352
- end
353
- end
354
-
355
- def logger
356
- @logger ||= defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : Logger.new(STDOUT)
357
- end
358
-
359
- private :applicable_attributes
360
- end
361
-
362
- # Instance Methods -- These methods will be available on instances of classes that include this module
363
-
364
- def self.included(klass)
365
- klass.extend(ClassMethods)
366
- end
367
-
368
- # Applies the property's corresponding xpath query, returning the result Nokogiri::XML::NodeSet
369
- def lookup( property_ref, query_opts={}, opts={} )
370
- xpath_query = xpath_query_for( property_ref, query_opts, opts )
371
-
372
- if xpath_query.nil?
373
- result = []
374
- else
375
- result = ng_xml.xpath(xpath_query, ox_namespaces)
376
- end
377
-
378
- return result
379
- end
380
-
381
-
382
- # Returns a hash combining the current documents namespaces (provided by nokogiri) and any namespaces that have been set up by your class definiton.
383
- # Most importantly, this matches the 'oxns' namespace to the namespace you provided in your root property config
384
- def ox_namespaces
385
- @ox_namespaces ||= ng_xml.namespaces.merge(self.class.ox_namespaces)
386
- end
387
-
388
- def xpath_query_for( property_ref, query_opts={}, opts={} )
389
- self.class.xpath_query_for( property_ref, query_opts, opts )
390
- end
391
-
392
- def property_info_for(property_ref)
393
- self.class.property_info_for(property_ref)
394
- end
395
-
396
- end