nom-xml 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/nom/xml.rb +2 -0
- data/lib/nom/xml/decorators.rb +1 -0
- data/lib/nom/xml/decorators/template_registry.rb +7 -0
- data/lib/nom/xml/decorators/terminology.rb +65 -20
- data/lib/nom/xml/nokogiri_extension.rb +15 -40
- data/lib/nom/xml/template_registry.rb +163 -0
- data/lib/nom/xml/term.rb +74 -0
- data/lib/nom/xml/terminology.rb +0 -91
- data/lib/nom/xml/version.rb +1 -1
- data/spec/examples/template_registry_example_spec.rb +43 -0
- data/spec/lib/nokogiri_extension_spec.rb +7 -4
- data/spec/lib/terminology_decorator_spec.rb +159 -2
- metadata +9 -4
data/lib/nom/xml.rb
CHANGED
data/lib/nom/xml/decorators.rb
CHANGED
@@ -4,44 +4,89 @@ module Nom::XML::Decorators::Terminology
|
|
4
4
|
klass.add_terminology_methods!
|
5
5
|
end
|
6
6
|
|
7
|
+
##
|
8
|
+
# Add terminology accessors for querying child terms
|
7
9
|
def add_terminology_methods!
|
8
|
-
|
10
|
+
self.term_accessors.each do |k, t|
|
11
|
+
(class << self; self; end).send(:define_method, k.to_sym) do |*args|
|
12
|
+
options = args.extract_options!
|
13
|
+
|
14
|
+
args += options.map { |key, value| %{#{key}="#{value.gsub(/"/, '\\\"') }"} }
|
15
|
+
|
16
|
+
xpath = t.local_xpath
|
17
|
+
|
18
|
+
xpath += "[#{args.join('][')}]" unless args.empty?
|
19
|
+
|
20
|
+
result = case self
|
21
|
+
when Nokogiri::XML::Document
|
22
|
+
self.root.xpath(xpath, self.document.terminology_namespaces)
|
23
|
+
else
|
24
|
+
self.xpath(xpath, self.document.terminology_namespaces)
|
25
|
+
end
|
26
|
+
|
27
|
+
m = t.options[:accessor]
|
28
|
+
case
|
29
|
+
when m.nil?
|
30
|
+
result
|
31
|
+
when m.is_a?(Symbol)
|
32
|
+
result.collect { |r| r.send(m) }
|
33
|
+
when m.is_a?(Proc)
|
34
|
+
result.collect { |r| m.call(r) }
|
35
|
+
else
|
36
|
+
raise "Unknown accessor class: #{m.class}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
self
|
9
42
|
end
|
10
43
|
|
44
|
+
##
|
45
|
+
# Get the terminology terms associated with this node
|
11
46
|
def terms
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
47
|
+
return {} unless self.respond_to? :parent and not self.parent.term_accessors.empty?
|
48
|
+
|
49
|
+
self.parent.term_accessors.select do |k,term|
|
50
|
+
self.parent.xpath(term.local_xpath, self.document.terminology_namespaces).include? self
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
##
|
56
|
+
# Collection of salient terminology accessors for this node
|
57
|
+
def term_accessors
|
58
|
+
case
|
59
|
+
when (self == self.document.root or self.is_a? Nokogiri::XML::Document)
|
60
|
+
root_terms
|
61
|
+
else
|
62
|
+
child_terms
|
18
63
|
end
|
19
64
|
end
|
20
65
|
|
21
66
|
private
|
22
67
|
|
23
|
-
|
68
|
+
##
|
69
|
+
# Root terms for the document
|
70
|
+
def root_terms
|
24
71
|
self.document.terminology.terms
|
25
72
|
end
|
26
73
|
|
27
|
-
|
74
|
+
##
|
75
|
+
# Terms that are immediate children of this node, or are globally applicable
|
76
|
+
def child_terms
|
28
77
|
h = {}
|
29
78
|
|
30
|
-
|
31
|
-
|
79
|
+
terms.each do |k,term|
|
80
|
+
|
81
|
+
term.terms.each do |k1, v1|
|
32
82
|
h[k1] = v1
|
33
83
|
end
|
34
84
|
end
|
35
85
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
##
|
40
|
-
#find this self in the terminology
|
41
|
-
def collect_terms_for_node
|
42
|
-
self.parent.terms.select do |k,term|
|
43
|
-
self.parent.xpath(term.local_xpath, self.document.terminology_namespaces).include? self
|
86
|
+
self.ancestors.each do |a|
|
87
|
+
a.term_accessors.each { |k,t| h[k] ||= t if t.options[:global] }
|
44
88
|
end
|
45
|
-
end
|
46
89
|
|
90
|
+
h
|
91
|
+
end
|
47
92
|
end
|
@@ -6,14 +6,17 @@ module Nom::XML
|
|
6
6
|
def nom!
|
7
7
|
unless decorators(Nokogiri::XML::Node).include? Nom::XML::Decorators::Terminology
|
8
8
|
decorators(Nokogiri::XML::Node) << Nom::XML::Decorators::Terminology
|
9
|
-
decorate!
|
10
9
|
end
|
11
10
|
|
12
11
|
unless decorators(Nokogiri::XML::NodeSet).include? Nom::XML::Decorators::NodeSet
|
13
12
|
decorators(Nokogiri::XML::NodeSet) << Nom::XML::Decorators::NodeSet
|
14
|
-
decorate!
|
15
13
|
end
|
16
14
|
|
15
|
+
unless decorators(Nokogiri::XML::Document).include? Nom::XML::Decorators::TemplateRegistry
|
16
|
+
decorators(Nokogiri::XML::Document) << Nom::XML::Decorators::TemplateRegistry
|
17
|
+
end
|
18
|
+
|
19
|
+
decorate!
|
17
20
|
self
|
18
21
|
end
|
19
22
|
|
@@ -30,46 +33,18 @@ module Nom::XML
|
|
30
33
|
@terminology ||= Nom::XML::Terminology.new
|
31
34
|
end
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def add_terminology_methods node
|
37
|
-
terms_to_add = node.terms
|
38
|
-
node.ancestors.each do |a|
|
39
|
-
a.terms.each { |k,t| terms_to_add[k] ||= t if t.options[:global] }
|
40
|
-
end
|
41
|
-
|
42
|
-
terms_to_add.each do |k, t|
|
43
|
-
(class << node; self; end).send(:define_method, k.to_sym) do |*args|
|
44
|
-
options = args.extract_options!
|
45
|
-
|
46
|
-
args += options.map { |key, value| %{#{key}="#{value.gsub(/"/, '\\\"') }"} }
|
47
|
-
|
48
|
-
xpath = t.local_xpath
|
49
|
-
|
36
|
+
def template_registry
|
37
|
+
@template_registry ||= Nom::XML::TemplateRegistry.new
|
38
|
+
end
|
50
39
|
|
51
|
-
|
40
|
+
# Define a new node template with the Nom::XML::TemplateRegistry.
|
41
|
+
# * +name+ is a Symbol indicating the name of the new template.
|
42
|
+
# * The +block+ does the work of creating the new node, and will receive
|
43
|
+
# a Nokogiri::XML::Builder and any other args passed to one of the node instantiation methods.
|
44
|
+
def define_template name, &block
|
45
|
+
self.template_registry.define name, &block
|
46
|
+
end
|
52
47
|
|
53
|
-
result = case self
|
54
|
-
when Nokogiri::XML::Document
|
55
|
-
self.root.xpath(xpath, node.document.terminology_namespaces)
|
56
|
-
else
|
57
|
-
self.xpath(xpath, node.document.terminology_namespaces)
|
58
|
-
end
|
59
48
|
|
60
|
-
m = t.options[:accessor]
|
61
|
-
case
|
62
|
-
when m.nil?
|
63
|
-
result
|
64
|
-
when m.is_a?(Symbol)
|
65
|
-
result.collect { |r| r.send(m) }
|
66
|
-
when m.is_a?(Proc)
|
67
|
-
result.collect { |r| m.call(r) }
|
68
|
-
else
|
69
|
-
raise "Unknown accessor class: #{m.class}"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
49
|
end
|
75
50
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
class Nom::XML::TemplateRegistry
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@templates = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
# Define an XML template
|
8
|
+
# @param [Symbol] a node_type key to associate with this template
|
9
|
+
# @param [Block] a block that will receive a [Nokogiri::XML::Builder] object and any arbitrary parameters passed to +instantiate+
|
10
|
+
# @return the +node_type+ Symbol
|
11
|
+
def define(node_type, &block)
|
12
|
+
unless node_type.is_a?(Symbol)
|
13
|
+
raise TypeError, "Registered node type must be a Symbol (e.g., :person)"
|
14
|
+
end
|
15
|
+
|
16
|
+
@templates[node_type] = block
|
17
|
+
node_type
|
18
|
+
end
|
19
|
+
|
20
|
+
# Undefine an XML template
|
21
|
+
# @param [Symbol] the node_type key of the template to undefine
|
22
|
+
# @return the +node_type+ Symbol
|
23
|
+
def undefine(node_type)
|
24
|
+
@templates.delete(node_type)
|
25
|
+
node_type
|
26
|
+
end
|
27
|
+
|
28
|
+
# Check whether a particular node_type is defined
|
29
|
+
# @param [Symbol] the node_type key to check
|
30
|
+
# @return [True] or [False]
|
31
|
+
def has_node_type?(node_type)
|
32
|
+
@templates.has_key?(node_type)
|
33
|
+
end
|
34
|
+
|
35
|
+
# List defined node_types
|
36
|
+
# @return [Array] of node_type symbols.
|
37
|
+
def node_types
|
38
|
+
@templates.keys
|
39
|
+
end
|
40
|
+
|
41
|
+
# Instantiate a detached, standalone node
|
42
|
+
# @param [Symbol] the node_type to instantiate
|
43
|
+
# @param additional arguments to pass to the template
|
44
|
+
def instantiate(node_type, *args)
|
45
|
+
result = create_detached_node(nil, node_type, *args)
|
46
|
+
# Strip namespaces from text and CDATA nodes. Stupid Nokogiri.
|
47
|
+
result.traverse { |node|
|
48
|
+
if node.is_a?(Nokogiri::XML::CharacterData)
|
49
|
+
node.namespace = nil
|
50
|
+
end
|
51
|
+
}
|
52
|
+
return result
|
53
|
+
end
|
54
|
+
|
55
|
+
# +instantiate+ a node and add it as a child of the [Nokogiri::XML::Node] specified by +target_node+
|
56
|
+
# @return the new [Nokogiri::XML::Node]
|
57
|
+
def add_child(target_node, node_type, *args, &block)
|
58
|
+
attach_node(:add_child, target_node, :self, node_type, *args, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
# +instantiate+ a node and add it as a following sibling of the [Nokogiri::XML::Node] specified by +target_node+
|
62
|
+
# @return the new [Nokogiri::XML::Node]
|
63
|
+
def add_next_sibling(target_node, node_type, *args, &block)
|
64
|
+
attach_node(:add_next_sibling, target_node, :parent, node_type, *args, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
# +instantiate+ a node and add it as a preceding sibling of the [Nokogiri::XML::Node] specified by +target_node+
|
68
|
+
# @return the new [Nokogiri::XML::Node]
|
69
|
+
def add_previous_sibling(target_node, node_type, *args, &block)
|
70
|
+
attach_node(:add_previous_sibling, target_node, :parent, node_type, *args, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
# +instantiate+ a node and add it as a following sibling of the [Nokogiri::XML::Node] specified by +target_node+
|
74
|
+
# @return +target_node+
|
75
|
+
def after(target_node, node_type, *args, &block)
|
76
|
+
attach_node(:after, target_node, :parent, node_type, *args, &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
# +instantiate+ a node and add it as a preceding sibling of the [Nokogiri::XML::Node] specified by +target_node+
|
80
|
+
# @return +target_node+
|
81
|
+
def before(target_node, node_type, *args, &block)
|
82
|
+
attach_node(:before, target_node, :parent, node_type, *args, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# +instantiate+ a node replace the [Nokogiri::XML::Node] specified by +target_node+ with it
|
86
|
+
# @return the new [Nokogiri::XML::Node]
|
87
|
+
def replace(target_node, node_type, *args, &block)
|
88
|
+
attach_node(:replace, target_node, :parent, node_type, *args, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
# +instantiate+ a node replace the [Nokogiri::XML::Node] specified by +target_node+ with it
|
92
|
+
# @return +target_node+
|
93
|
+
def swap(target_node, node_type, *args, &block)
|
94
|
+
attach_node(:swap, target_node, :parent, node_type, *args, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
def methods
|
98
|
+
super + @templates.keys.collect { |k| k.to_s }
|
99
|
+
end
|
100
|
+
|
101
|
+
def method_missing(sym,*args)
|
102
|
+
sym = sym.to_s.sub(/_$/,'').to_sym
|
103
|
+
if @templates.has_key?(sym)
|
104
|
+
instantiate(sym,*args)
|
105
|
+
else
|
106
|
+
super(sym,*args)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# Create a new Nokogiri::XML::Node based on the template for +node_type+
|
113
|
+
#
|
114
|
+
# @param [Nokogiri::XML::Node] builder_node The node to use as starting point for building the node using Nokogiri::XML::Builder.with(builder_node). This provides namespace info, etc for constructing the new Node object. If nil, defaults to {Nom::XML::TemplateRegistry#empty_root_node}. This is just used to create the new node and will not be included in the response.
|
115
|
+
# @param node_type a pointer to the template to use when creating the node
|
116
|
+
# @param [Array] args any additional args
|
117
|
+
def create_detached_node(builder_node, node_type, *args)
|
118
|
+
proc = @templates[node_type]
|
119
|
+
if proc.nil?
|
120
|
+
raise NameError, "Unknown node type: #{node_type.to_s}"
|
121
|
+
end
|
122
|
+
if builder_node.nil?
|
123
|
+
builder_node = empty_root_node
|
124
|
+
end
|
125
|
+
|
126
|
+
builder = Nokogiri::XML::Builder.with(builder_node) do |xml|
|
127
|
+
proc.call(xml,*args)
|
128
|
+
end
|
129
|
+
builder_node.elements.last.remove
|
130
|
+
end
|
131
|
+
|
132
|
+
# Create a new XML node of type +node_type+ and attach it to +target_node+ using the specified +method+
|
133
|
+
#
|
134
|
+
# @param [Symbol] method name that should be called on +target_node+, usually a Nokogiri::XML::Node instance method
|
135
|
+
# @param [Nokogiri::XML::Node or Nokogiri::XML::NodeSet with only one Node in it] target_node
|
136
|
+
# @param [Symbol] builder_node_offset Indicates node to use as the starting point for _constructing_ the new node using {Nom::XML::TemplateRegistry#create_detached_node}. If this is set to :parent, target_node.parent will be used. Otherwise, target_node will be used.
|
137
|
+
# @param node_type
|
138
|
+
# @param [Array] args any additional arguments for creating the node
|
139
|
+
def attach_node(method, target_node, builder_node_offset, node_type, *args, &block)
|
140
|
+
if target_node.is_a?(Nokogiri::XML::NodeSet) and target_node.length == 1
|
141
|
+
target_node = target_node.first
|
142
|
+
end
|
143
|
+
builder_node = builder_node_offset == :parent ? target_node.parent : target_node
|
144
|
+
new_node = create_detached_node(builder_node, node_type, *args)
|
145
|
+
result = target_node.send(method, new_node)
|
146
|
+
# Strip namespaces from text and CDATA nodes. Stupid Nokogiri.
|
147
|
+
new_node.traverse { |node|
|
148
|
+
if node.is_a?(Nokogiri::XML::CharacterData)
|
149
|
+
node.namespace = nil
|
150
|
+
end
|
151
|
+
}
|
152
|
+
if block_given?
|
153
|
+
yield result
|
154
|
+
else
|
155
|
+
return result
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def empty_root_node
|
160
|
+
Nokogiri::XML('<root/>').root
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
data/lib/nom/xml/term.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Nom::XML
|
2
|
+
class Term
|
3
|
+
attr_reader :name
|
4
|
+
attr_reader :terms
|
5
|
+
attr_reader :parent
|
6
|
+
attr_writer :parent
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize parent, name, options = {}, *args, &block
|
10
|
+
@name = name
|
11
|
+
@terms = {}
|
12
|
+
@parent = parent
|
13
|
+
@options = options || {}
|
14
|
+
|
15
|
+
in_edit_context do
|
16
|
+
yield(self) if block_given?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def in_edit_context &block
|
21
|
+
@edit_context = true
|
22
|
+
yield
|
23
|
+
@edit_context = false
|
24
|
+
end
|
25
|
+
|
26
|
+
def in_edit_context?
|
27
|
+
@edit_context
|
28
|
+
end
|
29
|
+
|
30
|
+
def parent_xpath
|
31
|
+
@parent_xpath ||= self.parent.xpath
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear_parent_cache!
|
35
|
+
@parent_xpath = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def xpath
|
39
|
+
[parent_xpath, local_xpath].flatten.compact.join("/")
|
40
|
+
end
|
41
|
+
|
42
|
+
def local_xpath
|
43
|
+
("#{xmlns}:" unless xmlns.blank? ).to_s + (options[:path] || name).to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def xmlns
|
47
|
+
(options[:xmlns] if options) || (self.parent.xmlns if self.parent)
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing method, *args, &block
|
51
|
+
if in_edit_context?
|
52
|
+
add_term(method, *args, &block)
|
53
|
+
elsif key?(method)
|
54
|
+
term(method)
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def key? term
|
61
|
+
terms.key? term
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
def add_term method, options = {}, *args, &block
|
66
|
+
terms[method] = Term.new(self, method, options, *args, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def term method, *args, &block
|
70
|
+
terms[method]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/nom/xml/terminology.rb
CHANGED
@@ -1,94 +1,4 @@
|
|
1
1
|
module Nom::XML
|
2
|
-
class Term
|
3
|
-
attr_reader :name
|
4
|
-
attr_reader :terms
|
5
|
-
attr_reader :parent
|
6
|
-
attr_writer :parent
|
7
|
-
attr_reader :options
|
8
|
-
|
9
|
-
def initialize parent, name, options = {}, *args, &block
|
10
|
-
@name = name
|
11
|
-
@terms = {}
|
12
|
-
@parent = parent
|
13
|
-
@options = options || {}
|
14
|
-
|
15
|
-
in_edit_context do
|
16
|
-
yield(self) if block_given?
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def in_edit_context &block
|
21
|
-
@edit_context = true
|
22
|
-
yield
|
23
|
-
@edit_context = false
|
24
|
-
end
|
25
|
-
|
26
|
-
def terms_from_node node
|
27
|
-
terms.select do |k, term|
|
28
|
-
node.parent.xpath(term.xpath).include? node
|
29
|
-
end.flatten
|
30
|
-
end
|
31
|
-
|
32
|
-
def in_edit_context?
|
33
|
-
@edit_context
|
34
|
-
end
|
35
|
-
|
36
|
-
def parent_xpath
|
37
|
-
@parent_xpath ||= self.parent.xpath
|
38
|
-
end
|
39
|
-
|
40
|
-
def clear_parent_cache
|
41
|
-
@parent_xpath = nil
|
42
|
-
end
|
43
|
-
|
44
|
-
def xpath
|
45
|
-
[parent_xpath, local_xpath].flatten.compact.join("/")
|
46
|
-
end
|
47
|
-
|
48
|
-
def local_xpath
|
49
|
-
("#{xmlns}:" unless xmlns.blank? ).to_s + (options[:path] || name).to_s
|
50
|
-
end
|
51
|
-
|
52
|
-
def xmlns
|
53
|
-
(options[:xmlns] if options) || (self.parent.xmlns if self.parent)
|
54
|
-
end
|
55
|
-
|
56
|
-
def method_missing method, *args, &block
|
57
|
-
if in_edit_context?
|
58
|
-
add_term(method, *args, &block)
|
59
|
-
elsif key?(method)
|
60
|
-
term(method)
|
61
|
-
# else if options[:ref]
|
62
|
-
# resolve_ref_and_return_term(method, *args, &block)
|
63
|
-
else
|
64
|
-
super
|
65
|
-
end
|
66
|
-
#terms[method]
|
67
|
-
end
|
68
|
-
|
69
|
-
def key? term
|
70
|
-
terms.key? term
|
71
|
-
end
|
72
|
-
|
73
|
-
def substitute_parent p
|
74
|
-
obj = self.dup
|
75
|
-
|
76
|
-
obj.parent = p
|
77
|
-
obj.clear_parent_cache
|
78
|
-
|
79
|
-
obj
|
80
|
-
end
|
81
|
-
|
82
|
-
protected
|
83
|
-
def add_term method, options = {}, *args, &block
|
84
|
-
terms[method] = Term.new(self, method, options, *args, &block)
|
85
|
-
end
|
86
|
-
|
87
|
-
def term method, *args, &block
|
88
|
-
terms[method]
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
2
|
class Terminology < Term
|
93
3
|
def initialize *args, &block
|
94
4
|
@terms = {}
|
@@ -97,7 +7,6 @@ module Nom::XML
|
|
97
7
|
end
|
98
8
|
end
|
99
9
|
|
100
|
-
|
101
10
|
def xpath
|
102
11
|
nil
|
103
12
|
end
|
data/lib/nom/xml/version.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Template Registry example" do
|
4
|
+
let(:file) {
|
5
|
+
<<-eoxml
|
6
|
+
<root>
|
7
|
+
<a>1234</a>
|
8
|
+
|
9
|
+
<b>asdf</b>
|
10
|
+
|
11
|
+
<c>
|
12
|
+
<nested>poiuyt</nested>
|
13
|
+
</c>
|
14
|
+
</root>
|
15
|
+
eoxml
|
16
|
+
}
|
17
|
+
let(:xml) { Nokogiri::XML(file) }
|
18
|
+
|
19
|
+
subject {
|
20
|
+
xml.define_template :a do |xml, value|
|
21
|
+
xml.a { xml.text value }
|
22
|
+
end
|
23
|
+
|
24
|
+
xml.set_terminology do |t|
|
25
|
+
t.a
|
26
|
+
t.b
|
27
|
+
t.b_ref :path => 'b'
|
28
|
+
|
29
|
+
t.c do |c|
|
30
|
+
c.nested
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
xml.nom!
|
35
|
+
|
36
|
+
xml
|
37
|
+
}
|
38
|
+
|
39
|
+
it "should work" do
|
40
|
+
subject.root.add_child(subject.template(:a, 'asdf'))
|
41
|
+
subject.a.should have(2).items
|
42
|
+
end
|
43
|
+
end
|
@@ -16,11 +16,14 @@ describe Nom::XML::NokogiriExtension do
|
|
16
16
|
</item>
|
17
17
|
eoxml
|
18
18
|
}
|
19
|
-
describe "#add_terminology_methods" do
|
20
|
-
context "root node" do
|
21
|
-
it "should add terminology methods to the root node" do
|
22
19
|
|
23
|
-
|
20
|
+
describe "#nom!" do
|
21
|
+
it "should decorate Nodes and Nodesets with our decorators" do
|
22
|
+
doc.nom!
|
23
|
+
|
24
|
+
doc.root.should be_a_kind_of(Nom::XML::Decorators::Terminology)
|
25
|
+
|
26
|
+
doc.xpath('//*').should be_a_kind_of(Nom::XML::Decorators::NodeSet)
|
24
27
|
end
|
25
28
|
end
|
26
29
|
end
|
@@ -1,6 +1,163 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Nutrition" do
|
4
|
+
let(:file) {
|
5
|
+
<<-eoxml
|
6
|
+
<root>
|
7
|
+
<a>1234</a>
|
8
|
+
|
9
|
+
<b>asdf</b>
|
10
|
+
|
11
|
+
<c>
|
12
|
+
<nested>poiuyt</nested>
|
13
|
+
</c>
|
14
|
+
</root>
|
15
|
+
eoxml
|
16
|
+
}
|
17
|
+
let(:xml) { Nokogiri::XML(file) }
|
18
|
+
|
19
|
+
let(:document) {
|
20
|
+
xml.set_terminology do |t|
|
21
|
+
t.a
|
22
|
+
t.b
|
23
|
+
t.b_ref :path => 'b'
|
24
|
+
|
25
|
+
t.c do |c|
|
26
|
+
c.nested
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
xml.nom!
|
31
|
+
|
32
|
+
xml
|
33
|
+
}
|
34
|
+
|
35
|
+
describe "#add_terminology_methods!" do
|
36
|
+
subject do
|
37
|
+
m = mock()
|
38
|
+
m.stub(:document).and_return(mock(:terminology_namespaces => {}))
|
39
|
+
m.stub(:term_accessors).and_return(@term_accessors)
|
40
|
+
m.extend Nom::XML::Decorators::Terminology
|
41
|
+
m
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should define terminology accessors" do
|
45
|
+
mock_term = mock
|
46
|
+
@term_accessors = { :asdf => mock_term }
|
47
|
+
|
48
|
+
subject.should respond_to(:asdf)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should perform basic xpath queries" do
|
52
|
+
mock_term = mock(:local_xpath => '//asdf', :options => {})
|
53
|
+
@term_accessors = { :asdf => mock_term }
|
54
|
+
|
55
|
+
subject.should_receive(:xpath).with('//asdf', anything)
|
56
|
+
|
57
|
+
subject.asdf
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should perform xpath queries with constraints" do
|
61
|
+
mock_term = mock(:local_xpath => '//asdf', :options => {})
|
62
|
+
@term_accessors = { :asdf => mock_term }
|
63
|
+
|
64
|
+
subject.should_receive(:xpath).with('//asdf[predicate="value"]', anything)
|
65
|
+
subject.should_receive(:xpath).with('//asdf[predicate="\"value\""]', anything)
|
66
|
+
subject.should_receive(:xpath).with('//asdf[custom-xpath-predicate()]', anything)
|
67
|
+
subject.should_receive(:xpath).with('//asdf[custom-xpath-predicate()][predicate="value"]', anything)
|
68
|
+
|
69
|
+
subject.asdf(:predicate => 'value')
|
70
|
+
subject.asdf(:predicate => '"value"')
|
71
|
+
subject.asdf('custom-xpath-predicate()')
|
72
|
+
subject.asdf('custom-xpath-predicate()', :predicate => 'value')
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should execute accessors" do
|
76
|
+
mock_term = mock(:local_xpath => '//asdf', :options => {:accessor => :text })
|
77
|
+
@term_accessors = { :asdf => mock_term }
|
78
|
+
|
79
|
+
m = mock()
|
80
|
+
subject.should_receive(:xpath).with('//asdf', anything).and_return([m])
|
81
|
+
m.should_receive(:text)
|
82
|
+
|
83
|
+
subject.asdf
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should execute proc-based accessors" do
|
87
|
+
mock_term = mock(:local_xpath => '//asdf', :options => {:accessor => lambda { |x| x.zxcvb } })
|
88
|
+
@term_accessors = { :asdf => mock_term }
|
89
|
+
|
90
|
+
m = mock()
|
91
|
+
subject.should_receive(:xpath).with('//asdf', anything).and_return([m])
|
92
|
+
m.should_receive(:zxcvb)
|
93
|
+
|
94
|
+
subject.asdf
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#terms" do
|
99
|
+
|
100
|
+
context "root element" do
|
101
|
+
subject { document.root }
|
102
|
+
|
103
|
+
it "should not have any associated terminology terms" do
|
104
|
+
subject.terms.should be_empty
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
context "node with a single term" do
|
110
|
+
subject { document.xpath('//a').first }
|
111
|
+
|
112
|
+
it "should have a single term" do
|
113
|
+
subject.terms.should have(1).item
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should find the right term" do
|
117
|
+
subject.terms.keys.should include(:a)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "node with multiple terms" do
|
122
|
+
subject { document.xpath('//b').first }
|
123
|
+
|
124
|
+
it "should have multiple terms" do
|
125
|
+
subject.terms.should have(2).items
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should find the right terms" do
|
129
|
+
subject.terms.keys.should include(:b, :b_ref)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#term_accessors" do
|
135
|
+
|
136
|
+
context "node" do
|
137
|
+
subject { document.xpath('//c').first }
|
138
|
+
|
139
|
+
it "should have a child accessor" do
|
140
|
+
subject.send(:term_accessors).keys.should include(:nested)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "document" do
|
145
|
+
subject { document }
|
146
|
+
|
147
|
+
it "should have all the root terms" do
|
148
|
+
subject.send(:term_accessors).keys.should include(:a, :b, :c)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "root node" do
|
153
|
+
subject { document.root }
|
154
|
+
|
155
|
+
it "should have all the root terms" do
|
156
|
+
subject.send(:term_accessors).keys.should include(:a, :b, :c)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
2
160
|
|
3
|
-
describe Nom::XML::Decorators::Terminology do
|
4
161
|
|
5
162
|
end
|
6
163
|
|
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.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -139,13 +139,17 @@ files:
|
|
139
139
|
- lib/nom/xml.rb
|
140
140
|
- lib/nom/xml/decorators.rb
|
141
141
|
- lib/nom/xml/decorators/nodeset.rb
|
142
|
+
- lib/nom/xml/decorators/template_registry.rb
|
142
143
|
- lib/nom/xml/decorators/terminology.rb
|
143
144
|
- lib/nom/xml/nokogiri_extension.rb
|
145
|
+
- lib/nom/xml/template_registry.rb
|
146
|
+
- lib/nom/xml/term.rb
|
144
147
|
- lib/nom/xml/terminology.rb
|
145
148
|
- lib/nom/xml/version.rb
|
146
149
|
- nom.gemspec
|
147
150
|
- spec/examples/namespaced_example_spec.rb
|
148
151
|
- spec/examples/nutrition_example_spec.rb
|
152
|
+
- spec/examples/template_registry_example_spec.rb
|
149
153
|
- spec/fixtures/nutrition.xml
|
150
154
|
- spec/fixtures/xml_namespaces.xml
|
151
155
|
- spec/lib/nodeset_decorator_spec.rb
|
@@ -168,7 +172,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
168
172
|
version: '0'
|
169
173
|
segments:
|
170
174
|
- 0
|
171
|
-
hash:
|
175
|
+
hash: 1606554633515477206
|
172
176
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
177
|
none: false
|
174
178
|
requirements:
|
@@ -177,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
177
181
|
version: '0'
|
178
182
|
segments:
|
179
183
|
- 0
|
180
|
-
hash:
|
184
|
+
hash: 1606554633515477206
|
181
185
|
requirements: []
|
182
186
|
rubyforge_project:
|
183
187
|
rubygems_version: 1.8.23
|
@@ -187,6 +191,7 @@ summary: asdf
|
|
187
191
|
test_files:
|
188
192
|
- spec/examples/namespaced_example_spec.rb
|
189
193
|
- spec/examples/nutrition_example_spec.rb
|
194
|
+
- spec/examples/template_registry_example_spec.rb
|
190
195
|
- spec/fixtures/nutrition.xml
|
191
196
|
- spec/fixtures/xml_namespaces.xml
|
192
197
|
- spec/lib/nodeset_decorator_spec.rb
|