nom-xml 0.0.3 → 0.0.4
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/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
|