nom-xml 0.1.2 → 0.2.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.
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -1
- data/lib/nom/xml/decorators/nodeset.rb +13 -0
- data/lib/nom/xml/decorators/terminology.rb +78 -66
- data/lib/nom/xml/nokogiri_extension.rb +11 -2
- data/lib/nom/xml/version.rb +1 -1
- data/nom.gemspec +0 -2
- data/spec/examples/mods_example_spec.rb +3 -4
- data/spec/examples/namespaced_example_spec.rb +2 -2
- data/spec/examples/template_registry_example_spec.rb +1 -0
- data/spec/fixtures/mods_document.xml +0 -1
- metadata +4 -19
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
module Nom::XML::Decorators::NodeSet
|
2
|
+
|
3
|
+
##
|
4
|
+
# Add a #method_missing handler to NodeSets. If all of the elements in the Nodeset
|
5
|
+
# respond to a method (e.g. if it is a term accessor), call that method on all the
|
6
|
+
# elements in the node
|
2
7
|
def method_missing sym, *args, &block
|
3
8
|
if self.all? { |node| node.respond_to? sym }
|
4
9
|
result = self.collect { |node| node.send(sym, *args, &block) }.flatten
|
@@ -7,4 +12,12 @@ module Nom::XML::Decorators::NodeSet
|
|
7
12
|
super
|
8
13
|
end
|
9
14
|
end
|
15
|
+
|
16
|
+
def respond_to? sym
|
17
|
+
if self.all? { |node| node.respond_to? sym }
|
18
|
+
true
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
10
23
|
end
|
@@ -1,91 +1,60 @@
|
|
1
1
|
module Nom::XML::Decorators::Terminology
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
options = args.extract_options!
|
13
|
-
|
14
|
-
args += options.map { |key, value| %{#{key}="#{value.gsub(/"/, '\\\"') }"} }
|
15
|
-
|
16
|
-
xpath = t.local_xpath
|
17
|
-
|
18
|
-
xpath += "[#{t.options[:if]}]" if t.options[:if] and t.options[:if].is_a? String
|
19
|
-
xpath += "[not(#{t.options[:unless]})]" if t.options[:unless] and t.options[:unless].is_a? String
|
20
|
-
|
21
|
-
xpath += "[#{args.join('][')}]" unless args.empty?
|
22
|
-
|
23
|
-
result = case self
|
24
|
-
when Nokogiri::XML::Document
|
25
|
-
self.root.xpath(xpath, self.document.terminology_namespaces)
|
26
|
-
else
|
27
|
-
self.xpath(xpath, self.document.terminology_namespaces)
|
28
|
-
end
|
29
|
-
|
30
|
-
result = result.select &t.options[:if] if t.options[:if].is_a? Proc
|
31
|
-
result = result.reject &t.options[:unless] if t.options[:unless].is_a? Proc
|
32
|
-
|
33
|
-
m = t.options[:accessor]
|
34
|
-
return_value = case
|
35
|
-
when m.nil?
|
36
|
-
result
|
37
|
-
when m.is_a?(Symbol)
|
38
|
-
result.collect { |r| r.send(m) }.compact
|
39
|
-
when m.is_a?(Proc)
|
40
|
-
result.collect { |r| m.call(r) }.compact
|
41
|
-
else
|
42
|
-
raise "Unknown accessor class: #{m.class}"
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
if return_value and (t.options[:single] or (return_value.length == 1 and return_value.first.is_a? Nokogiri::XML::Attr))
|
47
|
-
return return_value.first
|
2
|
+
|
3
|
+
METHODS_WE_NEED_TO_BE_CAREFUL_OF = [:id, :type]
|
4
|
+
|
5
|
+
METHODS_WE_NEED_TO_BE_CAREFUL_OF.each do |t|
|
6
|
+
class_eval <<-eos
|
7
|
+
def #{t.to_s} *args, &block
|
8
|
+
if self.term_accessors[:#{t}]
|
9
|
+
lookup_term(self.term_accessors[:#{t}], *args, &block)
|
10
|
+
else
|
11
|
+
super
|
48
12
|
end
|
13
|
+
end
|
14
|
+
eos
|
15
|
+
end
|
49
16
|
|
50
|
-
|
17
|
+
def method_missing method, *args, &block
|
18
|
+
if self.term_accessors[method.to_sym]
|
19
|
+
(class << self; self; end).send(:define_method, method.to_sym) do |*local_args|
|
20
|
+
lookup_term(self.term_accessors[method.to_sym], *local_args)
|
51
21
|
end
|
22
|
+
|
23
|
+
self.send(method, *args, &block)
|
24
|
+
else
|
25
|
+
super
|
52
26
|
end
|
27
|
+
end
|
53
28
|
|
54
|
-
|
29
|
+
def respond_to? method
|
30
|
+
super || self.term_accessors[method.to_sym]
|
55
31
|
end
|
56
32
|
|
57
33
|
##
|
58
|
-
# Get the
|
34
|
+
# Get the terms associated with this node
|
59
35
|
def terms
|
60
|
-
|
61
|
-
p = self.parent
|
62
|
-
|
63
|
-
t = []
|
64
|
-
|
65
|
-
until p.is_a? Nokogiri::XML::Document
|
66
|
-
p.term_accessors.each do |k,term|
|
67
|
-
t << term if term.nodes.include? self
|
68
|
-
end
|
69
|
-
p = p.parent
|
70
|
-
end
|
71
|
-
|
72
|
-
@terms ||= t
|
36
|
+
@terms ||= self.ancestors.map { |p| p.term_accessors(self).map { |keys, values| values } }.flatten.compact.uniq
|
73
37
|
end
|
74
38
|
|
75
39
|
protected
|
76
40
|
##
|
77
41
|
# Collection of salient terminology accessors for this node
|
78
|
-
|
79
|
-
|
42
|
+
#
|
43
|
+
# The root note or document node should have all the root terms
|
44
|
+
def term_accessors matching_node = nil
|
45
|
+
terms = case
|
80
46
|
when (self == self.document.root or self.is_a? Nokogiri::XML::Document)
|
81
47
|
root_terms
|
82
48
|
else
|
83
49
|
child_terms
|
84
50
|
end
|
51
|
+
|
52
|
+
terms &&= terms.select { |key, term| term.nodes.include? matching_node } if matching_node
|
53
|
+
|
54
|
+
terms
|
85
55
|
end
|
86
56
|
|
87
57
|
private
|
88
|
-
|
89
58
|
##
|
90
59
|
# Root terms for the document
|
91
60
|
def root_terms
|
@@ -97,17 +66,60 @@ module Nom::XML::Decorators::Terminology
|
|
97
66
|
def child_terms
|
98
67
|
h = {}
|
99
68
|
|
69
|
+
# collect the sub-terms of the terms applicable to this node
|
100
70
|
terms.each do |term|
|
101
|
-
|
102
71
|
term.terms.each do |k1, v1|
|
103
72
|
h[k1] = v1
|
104
73
|
end
|
105
74
|
end
|
106
75
|
|
76
|
+
# and mix in any global terms of this node's ancestors
|
107
77
|
self.ancestors.each do |a|
|
108
78
|
a.term_accessors.each { |k,t| h[k] ||= t if t.options[:global] }
|
109
79
|
end
|
110
80
|
|
111
81
|
h
|
112
82
|
end
|
83
|
+
|
84
|
+
def lookup_term term, *args
|
85
|
+
options = args.extract_options!
|
86
|
+
|
87
|
+
args += options.map { |key, value| %{#{key}="#{value.gsub(/"/, '\\\"') }"} }
|
88
|
+
|
89
|
+
xpath = term.local_xpath
|
90
|
+
|
91
|
+
xpath += "[#{term.options[:if]}]" if term.options[:if] and term.options[:if].is_a? String
|
92
|
+
xpath += "[not(#{term.options[:unless]})]" if term.options[:unless] and term.options[:unless].is_a? String
|
93
|
+
|
94
|
+
xpath += "[#{args.join('][')}]" unless args.empty?
|
95
|
+
|
96
|
+
result = case self
|
97
|
+
when Nokogiri::XML::Document
|
98
|
+
self.document.root.xpath(xpath, self.document.terminology_namespaces)
|
99
|
+
else
|
100
|
+
self.xpath(xpath, self.document.terminology_namespaces)
|
101
|
+
end
|
102
|
+
|
103
|
+
result = result.select &(term.options[:if]) if term.options[:if].is_a? Proc
|
104
|
+
result = result.reject &(term.options[:unless]) if term.options[:unless].is_a? Proc
|
105
|
+
|
106
|
+
m = term.options[:accessor]
|
107
|
+
|
108
|
+
return_value = case
|
109
|
+
when m.nil?
|
110
|
+
result
|
111
|
+
when m.is_a?(Symbol)
|
112
|
+
result.collect { |r| r.send(m) }.compact
|
113
|
+
when m.is_a?(Proc)
|
114
|
+
result.collect { |r| m.call(r) }.compact
|
115
|
+
else
|
116
|
+
raise "Unknown accessor class: #{m.class}"
|
117
|
+
end
|
118
|
+
|
119
|
+
if return_value and (term.options[:single] or (return_value.length == 1 and return_value.first.is_a? Nokogiri::XML::Attr))
|
120
|
+
return return_value.first
|
121
|
+
end
|
122
|
+
|
123
|
+
return return_value
|
124
|
+
end
|
113
125
|
end
|
@@ -3,6 +3,8 @@ require 'active_support/core_ext/array'
|
|
3
3
|
module Nom::XML
|
4
4
|
module NokogiriExtension
|
5
5
|
|
6
|
+
##
|
7
|
+
# Apply NOM decorators to Nokogiri classes
|
6
8
|
def nom!
|
7
9
|
unless decorators(Nokogiri::XML::Node).include? Nom::XML::Decorators::Terminology
|
8
10
|
decorators(Nokogiri::XML::Node) << Nom::XML::Decorators::Terminology
|
@@ -20,19 +22,26 @@ module Nom::XML
|
|
20
22
|
self
|
21
23
|
end
|
22
24
|
|
25
|
+
##
|
26
|
+
# Set the terminology accessors for this document
|
23
27
|
def set_terminology options = {}, &block
|
24
|
-
@terminology_namespaces = options[:namespaces]
|
25
28
|
@terminology = Nom::XML::Terminology.new(self, options, &block)
|
29
|
+
|
30
|
+
self
|
26
31
|
end
|
27
32
|
|
28
33
|
def terminology_namespaces
|
29
|
-
@
|
34
|
+
@terminology.namespaces
|
30
35
|
end
|
31
36
|
|
32
37
|
def terminology
|
33
38
|
@terminology ||= Nom::XML::Terminology.new self
|
34
39
|
end
|
35
40
|
|
41
|
+
def terminology= terminology
|
42
|
+
@terminology = terminology
|
43
|
+
end
|
44
|
+
|
36
45
|
def template_registry
|
37
46
|
@template_registry ||= Nom::XML::TemplateRegistry.new
|
38
47
|
end
|
data/lib/nom/xml/version.rb
CHANGED
data/nom.gemspec
CHANGED
@@ -19,8 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_development_dependency "rspec"
|
20
20
|
s.add_development_dependency "rake"
|
21
21
|
s.add_development_dependency "yard"
|
22
|
-
s.add_development_dependency "redcarpet"
|
23
|
-
|
24
22
|
s.files = `git ls-files`.split("\n")
|
25
23
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
26
24
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Namespaces example" do
|
4
4
|
|
5
|
-
let(:file) { File.
|
5
|
+
let(:file) { File.read(File.join(File.dirname(__FILE__), '..', 'fixtures', 'mods_document.xml')) }
|
6
6
|
let(:xml) { Nokogiri::XML(file) }
|
7
7
|
|
8
8
|
subject {
|
@@ -17,7 +17,7 @@ describe "Namespaces example" do
|
|
17
17
|
t.corporate_authors :path => '//mods:name[@type="corporate"]'
|
18
18
|
t.personal_authors :path => 'mods:name[@type="personal"]' do |n|
|
19
19
|
n.roleTerm :path => 'mods:role/mods:roleTerm', :index_as => [:type_1] do |r|
|
20
|
-
r.
|
20
|
+
r._type :path => '@type'
|
21
21
|
end
|
22
22
|
|
23
23
|
n.name_role_pair :path => '.', :accessor => lambda { |node| node.roleTerm.text + ": " + node.namePart.text }
|
@@ -32,7 +32,6 @@ describe "Namespaces example" do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
xml.nom!
|
35
|
-
|
36
35
|
xml
|
37
36
|
}
|
38
37
|
|
@@ -67,7 +66,7 @@ describe "Namespaces example" do
|
|
67
66
|
eric.namePart.text.should == "Alterman, Eric"
|
68
67
|
eric.roleTerm.text.should == "creator"
|
69
68
|
|
70
|
-
eric.roleTerm.
|
69
|
+
eric.roleTerm._type.text.should == "text"
|
71
70
|
end
|
72
71
|
|
73
72
|
it "should let you mix and match xpaths and nom accessors" do
|
@@ -6,8 +6,8 @@ describe "Namespaces example" do
|
|
6
6
|
|
7
7
|
subject {
|
8
8
|
xml.set_terminology(:namespaces => { 'html4' => 'http://www.w3.org/TR/html4/', 'furniture' => "http://www.w3schools.com/furniture"}) do |t|
|
9
|
-
t.table :xmlns => 'html4' do |
|
10
|
-
|
9
|
+
t.table :xmlns => 'html4' do |tb|
|
10
|
+
tb.tr do |tr|
|
11
11
|
tr.td
|
12
12
|
end
|
13
13
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nom-xml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -107,22 +107,6 @@ dependencies:
|
|
107
107
|
- - ! '>='
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
|
-
- !ruby/object:Gem::Dependency
|
111
|
-
name: redcarpet
|
112
|
-
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
|
-
requirements:
|
115
|
-
- - ! '>='
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
|
-
requirements:
|
123
|
-
- - ! '>='
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '0'
|
126
110
|
description: ! ' asdfgh '
|
127
111
|
email: cabeer@stanford.edu
|
128
112
|
executables: []
|
@@ -131,6 +115,7 @@ extra_rdoc_files:
|
|
131
115
|
- LICENSE
|
132
116
|
- README.md
|
133
117
|
files:
|
118
|
+
- .travis.yml
|
134
119
|
- Gemfile
|
135
120
|
- LICENSE
|
136
121
|
- README.md
|
@@ -174,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
174
159
|
version: '0'
|
175
160
|
segments:
|
176
161
|
- 0
|
177
|
-
hash: -
|
162
|
+
hash: -597609388430309558
|
178
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
164
|
none: false
|
180
165
|
requirements:
|
@@ -183,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
168
|
version: '0'
|
184
169
|
segments:
|
185
170
|
- 0
|
186
|
-
hash: -
|
171
|
+
hash: -597609388430309558
|
187
172
|
requirements: []
|
188
173
|
rubyforge_project:
|
189
174
|
rubygems_version: 1.8.23
|