om 1.4.4 → 1.5.0

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