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.
@@ -1,5 +1,5 @@
1
1
  # When you're defining a Terminology, you will usually use a "Terminology Builder":OM/XML/Terminology/Builder.html to create it
2
- # Each line you put into a "Terminology Builder":OM/XML/Terminology/Builder.html is passed to the constructor for a "Term Builder":OM/XML/Term/Builder.html.
2
+ # Each line you put into a "Terminology Builder":OM/XML/Terminology/Builder.html is passed to the constructor for a "Term Builder":OM/XML/Term/Builder.html.
3
3
  # See the "OM::XML::Term::Builder":OM/XML/Term/Builder.html API docs for complete description of your options for defining each Term.
4
4
  #
5
5
  # The most important thing to define in a Terminology is the root term. This is the place where you set namespaces and schemas for the Terminology
@@ -8,23 +8,24 @@
8
8
  # t.root(:path=>"mods", :xmlns=>"http://www.loc.gov/mods/v3", :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd")
9
9
  # end
10
10
  # terminology = terminology_builder.build
11
+ # If you do not set a namespace, the terminology will assume there is no namespace on the xml document.
11
12
  class OM::XML::Terminology
12
-
13
+
13
14
  class BadPointerError < StandardError; end
14
15
  class CircularReferenceError < StandardError; end
15
-
16
+
16
17
  # Terminology::Builder Class Definition
17
18
  #
18
- # When coding against Builders, remember that they rely on MethodMissing,
19
- # so any time you call a method on the Builder that it doesn't explicitly recognize,
19
+ # When coding against Builders, remember that they rely on MethodMissing,
20
+ # so any time you call a method on the Builder that it doesn't explicitly recognize,
20
21
  # the Builder will add your method & arguments to the it's settings and return itself.
21
22
  class Builder
22
-
23
+
23
24
  attr_accessor :schema, :namespaces
24
25
  attr_reader :term_builders
25
26
  ###
26
27
  # Create a new Terminology Builder object. +options+ are sent to the top level
27
- # Document that is being built.
28
+ # Document that is being built.
28
29
  # (not yet supported:) +root+ can be a point in an existing Terminology that you want to add Mappers into
29
30
  #
30
31
  # Building a document with a particular encoding for example:
@@ -37,10 +38,10 @@ class OM::XML::Terminology
37
38
  @namespaces = options.fetch(:namespaces,{})
38
39
  @term_builders = {}
39
40
  @cur_term_builder = nil
40
-
41
+
41
42
  yield self if block_given?
42
43
  end
43
-
44
+
44
45
  # Set the root of the Terminology, along with namespace & schema info
45
46
  def root opts, &block
46
47
  @schema = opts.fetch(:schema,nil)
@@ -55,36 +56,36 @@ class OM::XML::Terminology
55
56
  term_opts.delete(:schema)
56
57
  root_term_builder.settings.merge!(term_opts)
57
58
  @term_builders[root_term_builder.name] = root_term_builder
58
-
59
+
59
60
  return root_term_builder
60
61
  end
61
-
62
+
62
63
  # Returns an array of Terms that have been marked as "root" terms
63
64
  def root_term_builders
64
65
  @term_builders.values.select {|term_builder| term_builder.settings[:is_root_term] == true }
65
66
  end
66
-
67
+
67
68
  def method_missing method, *args, &block # :nodoc:
68
69
  parent_builder = @cur_term_builder
69
70
  @cur_term_builder = OM::XML::Term::Builder.new(method.to_s.sub(/[_!]$/, ''), self)
70
-
71
+
71
72
  # Attach to parent
72
73
  if parent_builder
73
74
  parent_builder.add_child @cur_term_builder
74
75
  else
75
76
  @term_builders [@cur_term_builder.name] = @cur_term_builder
76
77
  end
77
-
78
+
78
79
  # Apply options
79
80
  opts = args.shift
80
81
  @cur_term_builder.settings.merge!(opts) if opts
81
-
82
+
82
83
  # Parse children
83
84
  yield if block
84
-
85
+
85
86
  @cur_term_builder = parent_builder
86
87
  end
87
-
88
+
88
89
  # Returns the TermBuilder corresponding to the given _pointer_.
89
90
  def retrieve_term_builder(*args)
90
91
  args_cp = args.dup
@@ -100,35 +101,35 @@ class OM::XML::Terminology
100
101
  end
101
102
  return current_term
102
103
  end
103
-
104
+
104
105
  def build
105
106
  terminology = OM::XML::Terminology.new(:schema=>@schema, :namespaces=>@namespaces)
106
- root_term_builders.each do |root_term_builder|
107
- root_term_builder.children = self.term_builders.dup
107
+ root_term_builders.each do |root_term_builder|
108
+ root_term_builder.children = self.term_builders.dup
108
109
  root_term_builder.children.delete(root_term_builder.name)
109
- end
110
+ end
110
111
  @term_builders.each_value do |root_builder|
111
112
  terminology.add_term root_builder.build(terminology)
112
113
  end
113
114
  terminology
114
115
  end
115
116
  end
116
-
117
+
117
118
  # Terminology Class Definition
118
-
119
+
119
120
  attr_accessor :terms, :schema, :namespaces
120
-
121
+
121
122
  def initialize(options={})
122
123
  @schema = options.fetch(:schema,nil)
123
124
  @namespaces = options.fetch(:namespaces,{})
124
125
  @terms = {}
125
126
  end
126
-
127
+
127
128
  # Add a term to the root of the terminology
128
129
  def add_term(term)
129
130
  @terms[term.name.to_sym] = term
130
131
  end
131
-
132
+
132
133
  # Returns true if the current terminology has a term defined at the location indicated by +pointers+ array
133
134
  def has_term?(*pointers)
134
135
  begin
@@ -138,7 +139,7 @@ class OM::XML::Terminology
138
139
  return false
139
140
  end
140
141
  end
141
-
142
+
142
143
  # Returns the Term corresponding to the given _pointer_.
143
144
  # Proxies are not expanded
144
145
  def retrieve_term(*args)
@@ -162,11 +163,11 @@ class OM::XML::Terminology
162
163
  if current_term.kind_of? OM::XML::NamedTermProxy
163
164
  args = (current_term.proxy_pointer + args).flatten
164
165
  current_term = context.children[args.shift]
165
- end
166
+ end
166
167
  args.empty? ? current_term : retrieve_node_subsequent(args, current_term)
167
168
  end
168
169
 
169
-
170
+
170
171
  ##
171
172
  # This is very similar to retrieve_term, however it expands proxy paths out into their cannonical paths
172
173
  def retrieve_node(*args)
@@ -174,7 +175,7 @@ class OM::XML::Terminology
174
175
  if current_term.kind_of? OM::XML::NamedTermProxy
175
176
  args = (current_term.proxy_pointer + args).flatten
176
177
  current_term = terms[args.shift]
177
- end
178
+ end
178
179
  args.empty? ? current_term : retrieve_node_subsequent(args, current_term)
179
180
  end
180
181
 
@@ -187,7 +188,7 @@ class OM::XML::Terminology
187
188
  return pointers.first
188
189
  end
189
190
  query_constraints = nil
190
-
191
+
191
192
  if pointers.length > 1 && !pointers.last.kind_of?(Symbol)
192
193
  query_constraints = pointers.pop
193
194
  end
@@ -199,12 +200,12 @@ class OM::XML::Terminology
199
200
  constraint_value = query_constraints
200
201
  xpath_template = term.xpath_constrained
201
202
  xpath_query = eval( '"' + xpath_template + '"' )
202
- elsif query_constraints.kind_of?(Hash) && !query_constraints.empty?
203
- key_value_pair = query_constraints.first
203
+ elsif query_constraints.kind_of?(Hash) && !query_constraints.empty?
204
+ key_value_pair = query_constraints.first
204
205
  constraint_value = key_value_pair.last
205
206
  xpath_template = term.children[key_value_pair.first].xpath_constrained
206
- xpath_query = eval( '"' + xpath_template + '"' )
207
- else
207
+ xpath_query = eval( '"' + xpath_template + '"' )
208
+ else
208
209
  xpath_query = term.xpath
209
210
  end
210
211
  else
@@ -212,36 +213,36 @@ class OM::XML::Terminology
212
213
  end
213
214
  xpath_query
214
215
  end
215
-
216
+
216
217
  # Use the current terminology to generate an xpath with (optional) node indexes for each of the term pointers.
217
- # Ex. terminology.xpath_with_indexes({:conference=>0}, {:role=>1}, :text )
218
+ # Ex. terminology.xpath_with_indexes({:conference=>0}, {:role=>1}, :text )
218
219
  # will yield an xpath like this: '//oxns:name[@type="conference"][1]/oxns:role[2]/oxns:roleTerm[@type="text"]'
219
220
  def xpath_with_indexes(*pointers)
220
221
  OM::XML::TermXpathGenerator.generate_xpath_with_indexes(self, *pointers)
221
222
  end
222
-
223
+
223
224
  # Retrieves a Term corresponding to +term_pointers+ and return the corresponding xml_builder_template for that term.
224
225
  # The resulting xml_builder_template can be passed as a block into Nokogiri::XML::Builder.new
225
226
  #
226
227
  # +term_pointers+ point to the Term you want to generate a builder template for
227
228
  # If the last term_pointer is a String or a Hash, it will be passed into the Term's xml_builder_template method as extra_opts
228
229
  # see also: Term.xml_builder_template
229
- def xml_builder_template(*term_pointers)
230
+ def xml_builder_template(*term_pointers)
230
231
  extra_opts = {}
231
-
232
+
232
233
  if term_pointers.length > 1 && !term_pointers.last.kind_of?(Symbol)
233
234
  extra_opts = term_pointers.pop
234
235
  end
235
-
236
+
236
237
  term = retrieve_term(*term_pointers)
237
238
  return term.xml_builder_template(extra_opts)
238
239
  end
239
-
240
+
240
241
  # Returns an array of Terms that have been marked as "root" terms
241
242
  def root_terms
242
243
  terms.values.select {|term| term.is_root_term? }
243
244
  end
244
-
245
+
245
246
  # Return an XML representation of the Terminology and its terms
246
247
  # @param [Hash] options, the term will be added to it. If :children=>false, skips rendering child Terms
247
248
  # @param [Nokogiri::XML::Document] (optional) document to insert the term xml into
@@ -263,24 +264,24 @@ class OM::XML::Terminology
263
264
  }
264
265
  end
265
266
  }
266
- xml.terms
267
+ xml.terms
267
268
  }
268
269
  end
269
270
  document = builder.doc
270
271
  terms.values.each {|term| term.to_xml(options,document.xpath("//terms").first)}
271
272
  return document
272
273
  end
273
-
274
+
274
275
  def self.term_generic_name(*pointers)
275
276
  pointers_to_flat_array(pointers, false).join("_")
276
277
  end
277
-
278
+
278
279
  def self.term_hierarchical_name(*pointers)
279
280
  pointers_to_flat_array(pointers, true).join("_")
280
281
  end
281
-
282
+
282
283
  def self.pointers_to_flat_array(pointers, include_indices=true)
283
284
  OM.pointers_to_flat_array(pointers, include_indices)
284
285
  end
285
-
286
+
286
287
  end
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0"?>
2
+ <note>
3
+ <to>Tove</to>
4
+ <from>Jani</from>
5
+ <heading>Reminder</heading>
6
+ <body>Don't forget me this weekend!</body>
7
+ </note>
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
  require "om"
3
3
 
4
4
  describe "OM::XML::Term" do
5
-
5
+
6
6
  before(:each) do
7
7
  @test_name_part = OM::XML::Term.new(:namePart, {}).generate_xpath_queries!
8
8
  @test_volume = OM::XML::Term.new(:volume, :path=>"detail", :attributes=>{:type=>"volume"}, :default_content_path=>"number")
@@ -10,11 +10,8 @@ describe "OM::XML::Term" do
10
10
  @test_affiliation = OM::XML::Term.new(:affiliation)
11
11
  @test_role_code = OM::XML::Term.new(:roleTerm, :attributes=>{:type=>"code"})
12
12
  end
13
-
13
+
14
14
  describe '#new' do
15
- it "should set default values" do
16
- @test_name_part.namespace_prefix.should == "oxns"
17
- end
18
15
  it "should set path from mapper name if no path is provided" do
19
16
  @test_name_part.path.should == "namePart"
20
17
  end
@@ -25,14 +22,14 @@ describe "OM::XML::Term" do
25
22
  local_mapping.xpath_constrained.should be_nil
26
23
  end
27
24
  end
28
-
25
+
29
26
  describe 'inner_xml' do
30
27
  it "should be a kind of Nokogiri::XML::Node" do
31
28
  pending
32
29
  @test_mapping.inner_xml.should be_kind_of(Nokogiri::XML::Node)
33
30
  end
34
31
  end
35
-
32
+
36
33
  describe '#from_node' do
37
34
  it "should create a mapper from a nokogiri node" do
38
35
  pending "probably should do this in the Builder"
@@ -52,7 +49,7 @@ describe "OM::XML::Term" do
52
49
  mapper.path.should == "name"
53
50
  mapper.attributes.should == {:type=>"personal"}
54
51
  mapper.internal_xml.should == node
55
-
52
+
56
53
  child = mapper.children[:first_name]
57
54
 
58
55
  child.name.should == :first_name
@@ -61,35 +58,35 @@ describe "OM::XML::Term" do
61
58
  child.internal_xml.should == node.xpath("./mapper").first
62
59
  end
63
60
  end
64
-
61
+
65
62
  describe ".label" do
66
63
  it "should default to the mapper name with underscores converted to spaces"
67
64
  end
68
-
65
+
69
66
  describe ".retrieve_term" do
70
67
  it "should crawl down into mapper children to find the desired term" do
71
68
  mock_role = mock("mapper", :children =>{:text=>"the target"})
72
- mock_conference = mock("mapper", :children =>{:role=>mock_role})
73
- @test_name_part.expects(:children).returns({:conference=>mock_conference})
69
+ mock_conference = mock("mapper", :children =>{:role=>mock_role})
70
+ @test_name_part.expects(:children).returns({:conference=>mock_conference})
74
71
  @test_name_part.retrieve_term(:conference, :role, :text).should == "the target"
75
72
  end
76
73
  it "should return an empty hash if no term can be found" do
77
74
  @test_name_part.retrieve_term(:journal, :issue, :end_page).should == nil
78
75
  end
79
76
  end
80
-
77
+
81
78
  describe 'inner_xml' do
82
79
  it "should be a kind of Nokogiri::XML::Node" do
83
80
  pending
84
81
  @test_name_part.inner_xml.should be_kind_of(Nokogiri::XML::Node)
85
82
  end
86
83
  end
87
-
84
+
88
85
  describe "getters/setters" do
89
86
  it "should set the corresponding .settings value and return the current value" do
90
87
  [:path, :index_as, :required, :data_type, :variant_of, :path, :attributes, :default_content_path, :namespace_prefix].each do |method_name|
91
88
  @test_name_part.send(method_name.to_s+"=", "#{method_name.to_s}foo").should == "#{method_name.to_s}foo"
92
- @test_name_part.send(method_name).should == "#{method_name.to_s}foo"
89
+ @test_name_part.send(method_name).should == "#{method_name.to_s}foo"
93
90
  end
94
91
  end
95
92
  end
@@ -136,21 +133,21 @@ describe "OM::XML::Term" do
136
133
  @test_name_part.ancestors.should include(@test_volume)
137
134
  end
138
135
  end
139
-
136
+
140
137
  describe "generate_xpath_queries!" do
141
138
  it "should return the current object" do
142
139
  @test_name_part.generate_xpath_queries!.should == @test_name_part
143
140
  end
144
- it "should regenerate the xpath values" do
141
+ it "should regenerate the xpath values" do
145
142
  @test_volume.xpath_relative.should be_nil
146
143
  @test_volume.xpath.should be_nil
147
144
  @test_volume.xpath_constrained.should be_nil
148
-
145
+
149
146
  @test_volume.generate_xpath_queries!.should == @test_volume
150
-
151
- @test_volume.xpath_relative.should == 'oxns:detail[@type="volume"]'
152
- @test_volume.xpath.should == '//oxns:detail[@type="volume"]'
153
- @test_volume.xpath_constrained.should == '//oxns:detail[@type="volume" and contains(oxns:number, "#{constraint_value}")]'.gsub('"', '\"')
147
+
148
+ @test_volume.xpath_relative.should == 'detail[@type="volume"]'
149
+ @test_volume.xpath.should == '//detail[@type="volume"]'
150
+ @test_volume.xpath_constrained.should == '//detail[@type="volume" and contains(number, "#{constraint_value}")]'.gsub('"', '\"')
154
151
  end
155
152
  it "should trigger update on any child objects" do
156
153
  mock_child = mock("child term")
@@ -159,44 +156,44 @@ describe "OM::XML::Term" do
159
156
  @test_name_part.generate_xpath_queries!
160
157
  end
161
158
  end
162
-
159
+
163
160
  describe "#xml_builder_template" do
164
-
161
+
165
162
  it "should generate a template call for passing into the builder block (assumes 'xml' as the argument for the block)" do
166
163
  @test_date.xml_builder_template.should == 'xml.namePart( \'#{builder_new_value}\', \'type\'=>\'date\' )'
167
164
  @test_affiliation.xml_builder_template.should == 'xml.affiliation( \'#{builder_new_value}\' )'
168
165
  end
169
166
  it "should accept extra options" do
170
- marcrelator_role_xml_builder_template = 'xml.roleTerm( \'#{builder_new_value}\', \'type\'=>\'code\', \'authority\'=>\'marcrelator\' )'
167
+ marcrelator_role_xml_builder_template = 'xml.roleTerm( \'#{builder_new_value}\', \'type\'=>\'code\', \'authority\'=>\'marcrelator\' )'
171
168
  @test_role_code.xml_builder_template(:attributes=>{"authority"=>"marcrelator"}).should == marcrelator_role_xml_builder_template
172
169
  end
173
-
170
+
174
171
  it "should work for namespaced nodes" do
175
172
  @ical_date = OM::XML::Term.new(:ical_date, :path=>"ical:date")
176
173
  @ical_date.xml_builder_template.should == "xml[\'ical\'].date( '\#{builder_new_value}' )"
177
174
  @ical_date = OM::XML::Term.new(:ical_date, :path=>"date", :namespace_prefix=>"ical")
178
175
  @ical_date.xml_builder_template.should == "xml[\'ical\'].date( '\#{builder_new_value}' )"
179
176
  end
180
-
181
- it "should work for nodes with default_content_path" do
177
+
178
+ it "should work for nodes with default_content_path" do
182
179
  @test_volume.xml_builder_template.should == "xml.detail( \'type\'=>'volume' ) { xml.number( '\#{builder_new_value}' ) }"
183
180
  end
184
-
181
+
185
182
  it "should support terms that are attributes" do
186
183
  @type_attribute_term = OM::XML::Term.new(:type_attribute, :path=>{:attribute=>:type})
187
184
  @type_attribute_term.xml_builder_template.should == "xml.@type( '\#{builder_new_value}' )"
188
185
  end
189
-
186
+
190
187
  it "should support terms with namespaced attributes" do
191
188
  @french_title = OM::XML::Term.new(:french_title, :path=>"title", :attributes=>{"xml:lang"=>"fre"})
192
189
  @french_title.xml_builder_template.should == "xml.title( '\#{builder_new_value}', 'xml:lang'=>'fre' )"
193
190
  end
194
-
191
+
195
192
  it "should support terms that are namespaced attributes" do
196
193
  @xml_lang_attribute_term = OM::XML::Term.new(:xml_lang_attribute, :path=>{:attribute=>"xml:lang"})
197
194
  @xml_lang_attribute_term.xml_builder_template.should == "xml.@xml:lang( '\#{builder_new_value}' )"
198
195
  end
199
-
196
+
200
197
  end
201
-
198
+
202
199
  end