om 1.0.2 → 1.1.0

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