nom-xml 0.3.0 → 0.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.
data/README.md CHANGED
@@ -1,17 +1,17 @@
1
- # NOM
1
+ # nom-xml
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/cbeer/nom.png)](http://travis-ci.org/cbeer/nom)
4
4
 
5
5
  A library to help you tame sprawling XML schemas.
6
6
 
7
- NOM allows you to define a “terminology” to ease translation between XML and ruby objects – you can query the xml for Nodes or node values without ever writing a line of XPath. NOM is built on top of [nokogiri](http://nokogiri.org) decorators, which means you can mix-and-match NOM accessors with nokogiri xpaths, xml manipulation, and traversing and it will just work.
7
+ nom-xml allows you to define a “terminology” to ease translation between XML and ruby objects – you can query the xml for Nodes or node values without ever writing a line of XPath. nom-xml is built on top of [nokogiri](http://nokogiri.org) decorators, which means you can mix-and-match NOM accessors with nokogiri xpaths, xml manipulation, and traversing and it will just work.
8
8
 
9
9
 
10
10
  Some Handy Links
11
11
  ----------------
12
12
  Here are some resources to help you learn more about nom-xml:
13
13
 
14
- - [API](http://rubydoc.info/github/cbeer/nom) - A reference to NOM's classes
14
+ - [API](http://rubydoc.info/github/cbeer/nom-xml) - A reference to nom-xml's classes
15
15
  - [#projecthydra](http://webchat.freenode.net/?channels=#projecthydra) on irc.freenode.net
16
16
  - [Project Hydra Google Group](http://groups.google.com/group/hydra-tech) - community mailing list and forum
17
17
 
@@ -1,5 +1,29 @@
1
1
  module Nom::XML::Decorators::NodeSet
2
2
 
3
+ def values_for_term term
4
+ result = self
5
+ result = result.select &(term.options[:if]) if term.options[:if].is_a? Proc
6
+ result = result.reject &(term.options[:unless]) if term.options[:unless].is_a? Proc
7
+
8
+ m = term.options[:accessor]
9
+ return_value = case
10
+ when m.nil?
11
+ result
12
+ when m.is_a?(Symbol)
13
+ result.collect { |r| r.send(m) }.compact
14
+ when m.is_a?(Proc)
15
+ result.collect { |r| m.call(r) }.compact
16
+ else
17
+ raise "Unknown accessor class: #{m.class}"
18
+ end
19
+
20
+ if return_value and (term.options[:single] or (result.length == 1 and result.first.is_a? Nokogiri::XML::Attr))
21
+ return return_value.first
22
+ end
23
+
24
+ return return_value
25
+ end
26
+
3
27
  ##
4
28
  # Add a #method_missing handler to NodeSets. If all of the elements in the Nodeset
5
29
  # respond to a method (e.g. if it is a term accessor), call that method on all the
@@ -9,7 +33,11 @@ module Nom::XML::Decorators::NodeSet
9
33
  result = self.collect { |node| node.send(sym, *args, &block) }.flatten
10
34
  self.class.new(self.document, result) rescue result
11
35
  else
12
- super
36
+ begin
37
+ self.document.template_registry.send(sym, self, *args, &block)
38
+ rescue NameError
39
+ super
40
+ end
13
41
  end
14
42
  end
15
43
 
@@ -20,7 +20,11 @@ module Nom::XML::Decorators::Terminology
20
20
 
21
21
  self.send(method, *args, &block)
22
22
  else
23
- super
23
+ begin
24
+ self.document.template_registry.send(method, self, *args, &block)
25
+ rescue NameError
26
+ super
27
+ end
24
28
  end
25
29
  end
26
30
 
@@ -35,7 +39,7 @@ module Nom::XML::Decorators::Terminology
35
39
  def terms
36
40
  @terms ||= self.ancestors.map { |p| p.term_accessors(self).map { |keys, values| values } }.flatten.compact.uniq
37
41
  end
38
-
42
+
39
43
  protected
40
44
  ##
41
45
  # Collection of salient terminology accessors for this node
@@ -94,9 +98,6 @@ module Nom::XML::Decorators::Terminology
94
98
 
95
99
  xpath = term.local_xpath
96
100
 
97
- xpath += "[#{term.options[:if]}]" if term.options[:if] and term.options[:if].is_a? String
98
- xpath += "[not(#{term.options[:unless]})]" if term.options[:unless] and term.options[:unless].is_a? String
99
-
100
101
  xpath += "[#{args.join('][')}]" unless args.empty?
101
102
 
102
103
  result = case self
@@ -106,26 +107,6 @@ module Nom::XML::Decorators::Terminology
106
107
  self.xpath(xpath, self.document.terminology_namespaces)
107
108
  end
108
109
 
109
- result = result.select &(term.options[:if]) if term.options[:if].is_a? Proc
110
- result = result.reject &(term.options[:unless]) if term.options[:unless].is_a? Proc
111
-
112
- m = term.options[:accessor]
113
-
114
- return_value = case
115
- when m.nil?
116
- result
117
- when m.is_a?(Symbol)
118
- result.collect { |r| r.send(m) }.compact
119
- when m.is_a?(Proc)
120
- result.collect { |r| m.call(r) }.compact
121
- else
122
- raise "Unknown accessor class: #{m.class}"
123
- end
124
-
125
- if return_value and (term.options[:single] or (result.length == 1 and result.first.is_a? Nokogiri::XML::Attr))
126
- return return_value.first
127
- end
128
-
129
- return return_value
110
+ result.values_for_term(term)
130
111
  end
131
112
  end
@@ -9,6 +9,9 @@ module Nom::XML
9
9
  unless decorators(Nokogiri::XML::Node).include? Nom::XML::Decorators::Terminology
10
10
  decorators(Nokogiri::XML::Node) << Nom::XML::Decorators::Terminology
11
11
  end
12
+ unless decorators(Nokogiri::XML::Attr).include? Nom::XML::Decorators::Terminology
13
+ decorators(Nokogiri::XML::Attr) << Nom::XML::Decorators::Terminology
14
+ end
12
15
 
13
16
  unless decorators(Nokogiri::XML::NodeSet).include? Nom::XML::Decorators::NodeSet
14
17
  decorators(Nokogiri::XML::NodeSet) << Nom::XML::Decorators::NodeSet
@@ -44,71 +44,46 @@ class Nom::XML::TemplateRegistry
44
44
  def instantiate(node_type, *args)
45
45
  result = create_detached_node(nil, node_type, *args)
46
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
47
+ unless jruby?
48
+ result.traverse { |node|
49
+ if node.is_a?(Nokogiri::XML::CharacterData)
50
+ node.namespace = nil
51
+ end
52
+ }
50
53
  end
51
- }
52
54
  return result
53
55
  end
54
56
 
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)
57
+ ACTIONS = {
58
+ :add_child => :self, :add_next_sibling => :parent, :add_previous_sibling => :parent,
59
+ :after => :parent, :before => :parent, :replace => :parent, :swap => :parent
60
+ }
61
+ def method_missing(sym,*args,&block)
62
+ method = sym.to_s.sub(/_+$/,'').to_sym
63
+ if @templates.has_key?(method)
104
64
  instantiate(sym,*args)
65
+ elsif ACTIONS.has_key?(method)
66
+ target_node = args.shift
67
+ attach_node(method, target_node, ACTIONS[method], *args, &block)
68
+ elsif method.to_s =~ /^(#{ACTIONS.keys.join('|')})_(.+)$/
69
+ method = $1.to_sym
70
+ template = $2.to_sym
71
+ if ACTIONS.has_key?(method) and @templates.has_key?(template)
72
+ target_node = args.shift
73
+ attach_node(method, target_node, ACTIONS[method], template, *args, &block)
74
+ end
105
75
  else
106
76
  super(sym,*args)
107
77
  end
78
+
108
79
  end
109
80
 
110
81
  private
111
82
 
83
+ def jruby?
84
+ defined?(RUBY_ENGINE) and (RUBY_ENGINE == 'jruby')
85
+ end
86
+
112
87
  # Create a new Nokogiri::XML::Node based on the template for +node_type+
113
88
  #
114
89
  # @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.
@@ -144,11 +119,13 @@ class Nom::XML::TemplateRegistry
144
119
  new_node = create_detached_node(builder_node, node_type, *args)
145
120
  result = target_node.send(method, new_node)
146
121
  # 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
- }
122
+ unless jruby?
123
+ new_node.traverse { |node|
124
+ if node.is_a?(Nokogiri::XML::CharacterData)
125
+ node.namespace = nil
126
+ end
127
+ }
128
+ end
152
129
  if block_given?
153
130
  yield result
154
131
  else
data/lib/nom/xml/term.rb CHANGED
@@ -46,7 +46,12 @@ module Nom::XML
46
46
  # Get the relative xpath to this node from its immediate parent's term
47
47
  # @return [String]
48
48
  def local_xpath
49
- ("#{xmlns}:" unless xmlns.blank? ).to_s + (options[:path] || name).to_s
49
+ xpath = ("#{xmlns}:" unless xmlns.blank? ).to_s + (options[:path] || name).to_s
50
+
51
+ xpath += "[#{options[:if]}]" if options[:if] and options[:if].is_a? String
52
+ xpath += "[not(#{options[:unless]})]" if options[:unless] and options[:unless].is_a? String
53
+
54
+ xpath
50
55
  end
51
56
 
52
57
  ##
@@ -56,6 +61,12 @@ module Nom::XML
56
61
  terminology.document.root.xpath(xpath, terminology.namespaces)
57
62
  end
58
63
 
64
+ ##
65
+ # Get the document values associated with the term (after e.g. accessors)
66
+ def values
67
+ terminology.document.root.xpath(xpath, terminology.namespaces).value_for_term(self)
68
+ end
69
+
59
70
  ##
60
71
  # Does this term have a sub-term called term_name
61
72
  # @attr [String] term_key
@@ -1,5 +1,5 @@
1
1
  module Nom
2
2
  module XML
3
- VERSION = '0.3.0'
3
+ VERSION = '0.5.0'
4
4
  end
5
5
  end
@@ -6,11 +6,11 @@ Gem::Specification.new do |s|
6
6
  s.name = "nom-xml"
7
7
  s.version = Nom::XML::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Chris Beer"]
10
- s.email = %q{cabeer@stanford.edu}
11
- s.homepage = %q{http://github.com/cbeer/nom}
12
- s.summary = %q{ asdf }
13
- s.description = %q{ asdfgh }
9
+ s.authors = ["Chris Beer", "Michael B. Klein"]
10
+ s.email = %q{cabeer@stanford.edu mbklein@gmail.com}
11
+ s.homepage = %q{http://github.com/cbeer/nom-xml}
12
+ s.summary = %q{ A library to help you tame sprawling XML schemas. }
13
+ s.description = %q{ NOM allows you to define a “terminology” to ease translation between XML and ruby objects }
14
14
 
15
15
  s.add_dependency 'activesupport'
16
16
  s.add_dependency 'i18n'
@@ -19,6 +19,7 @@ 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 "equivalent-xml"
22
23
  s.files = `git ls-files`.split("\n")
23
24
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
25
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nom::XML::Decorators::NodeSet do
4
+ subject {
5
+ doc = Nokogiri::XML '<root><a n="1"/><b /><c /></root>'
6
+ doc.nom!
7
+ doc
8
+ }
9
+ describe "#values_for_term" do
10
+
11
+
12
+ it "should do if" do
13
+ t = mock(:options => { :if => lambda { |x| false }})
14
+
15
+ t1 = mock(:options => { :if => lambda { |x| true }})
16
+
17
+ subject.xpath('//*').values_for_term(t).should be_empty
18
+ subject.xpath('//*').values_for_term(t1).should_not be_empty
19
+ end
20
+
21
+ it "should do unless" do
22
+ t = mock(:options => { :unless => lambda { |x| false }})
23
+
24
+ t1 = mock(:options => { :unless => lambda { |x| true }})
25
+
26
+ subject.xpath('//*').values_for_term(t).should_not be_empty
27
+ subject.xpath('//*').values_for_term(t1).should be_empty
28
+ end
29
+
30
+ it "should do a nil accessor" do
31
+ t = mock(:options => { :accessor => nil})
32
+
33
+ subject.xpath('//*').values_for_term(t).should == subject.xpath('//*')
34
+ end
35
+
36
+ it "should do a Proc accessor" do
37
+ t = mock(:options => { :accessor => lambda { |x| 1 }})
38
+ subject.xpath('//a').values_for_term(t).should == [1]
39
+ end
40
+
41
+ it "should do a symbol accessor" do
42
+ t = mock(:options => { :accessor => :z})
43
+ subject.xpath('//a').first.should_receive(:z).and_return(1)
44
+ subject.xpath('//a').values_for_term(t).should == [1]
45
+ end
46
+
47
+ it "should do single" do
48
+ t = mock(:options => { :single => true})
49
+ subject.xpath('//*').values_for_term(t).should == subject.xpath('//*').first
50
+
51
+ end
52
+
53
+ it "should treat an attribute as a single" do
54
+ t = mock(:options => { })
55
+ subject.xpath('//@n').values_for_term(t).should be_a_kind_of Nokogiri::XML::Attr
56
+
57
+ end
58
+ end
59
+
60
+ describe "method missing and respond to" do
61
+ it "should respond to methods on nodes if all nodes in the nodeset respond to the method" do
62
+ subject.xpath('//*').should respond_to :text
63
+ end
64
+
65
+ it "should respond to methods on nodes if all nodes in the nodeset respond to the method" do
66
+ subject.xpath('//*').should_not respond_to :text_123
67
+ end
68
+
69
+ it "should work" do
70
+ subject.xpath('//*').name.should include("a", "b", "c")
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,215 @@
1
+ require 'spec_helper'
2
+
3
+ describe "NOM::XML::TemplateRegistry" do
4
+
5
+ let(:file) { '<people xmlns="urn:registry-test"><person title="Actor">Alice</person></people>' }
6
+ let(:xml) { Nokogiri::XML(file) }
7
+ let(:expectations) {
8
+ {
9
+ :before => %{<people xmlns="urn:registry-test"><person title="Builder">Bob</person><person title="Actor">Alice</person></people>},
10
+ :after => %{<people xmlns="urn:registry-test"><person title="Actor">Alice</person><person title="Builder">Bob</person></people>},
11
+ :instead => %{<people xmlns="urn:registry-test"><person title="Builder">Bob</person></people>}
12
+ }
13
+ }
14
+ subject {
15
+ xml.set_terminology(:namespaces => { 'default' => 'urn:registry-test' }) do |t|
16
+ t.person(:xmlns => 'default') {
17
+ t.title(:path => "@title")
18
+ }
19
+ end
20
+
21
+ xml.define_template :person do |xml,name,title|
22
+ xml.person(:title => title) do
23
+ xml.text(name)
24
+ end
25
+ end
26
+
27
+ xml.nom!
28
+
29
+ xml
30
+ }
31
+
32
+ describe "template definitions" do
33
+ it "should contain predefined templates" do
34
+ subject.template_registry.node_types.should include(:person)
35
+ subject.template_registry.node_types.should_not include(:zombie)
36
+ end
37
+
38
+ describe "ZOMG ZOMBIES!!11!!!1" do
39
+ before(:each) do
40
+ subject.define_template :zombie do |xml,name|
41
+ xml.monster(:wants => 'braaaaainz') do
42
+ xml.text(name)
43
+ end
44
+ end
45
+ end
46
+ it "should define new templates" do
47
+ subject.template_registry.node_types.should include(:zombie)
48
+ end
49
+
50
+ it "should instantiate a detached node from a template" do
51
+ node = subject.template_registry.instantiate(:zombie, 'Zeke')
52
+ expectation = Nokogiri::XML('<monster wants="braaaaainz">Zeke</monster>').root
53
+ node.should be_equivalent_to(expectation)
54
+ end
55
+
56
+ it "should raise an error when trying to instantiate an unknown node_type" do
57
+ lambda { subject.template_registry.instantiate(:demigod, 'Hercules') }.should raise_error(NameError)
58
+ end
59
+
60
+ it "should raise an exception if a missing method name doesn't match a node_type" do
61
+ lambda { subject.template_registry.demigod('Hercules') }.should raise_error(NameError)
62
+ end
63
+
64
+ it "should undefine existing templates" do
65
+ subject.template_registry.node_types.should include(:zombie)
66
+ subject.template_registry.undefine :zombie
67
+ subject.template_registry.node_types.should_not include(:zombie)
68
+ end
69
+
70
+ it "should complain if the template name isn't a symbol" do
71
+ lambda { subject.template_registry.define("die!") { |xml| subject.this_never_happened } }.should raise_error(TypeError)
72
+ end
73
+
74
+ it "should report on whether a given template is defined" do
75
+ subject.template_registry.has_node_type?(:zombie).should == true
76
+ subject.template_registry.has_node_type?(:demigod).should == false
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ describe "template-based document manipulations" do
83
+ it "should accept a Nokogiri::XML::Node as target" do
84
+ subject.template_registry.after(subject.root.elements.first, :person, 'Bob', 'Builder')
85
+ subject.root.elements.length.should == 2
86
+ end
87
+
88
+ it "should accept a Nokogiri::XML::NodeSet as target" do
89
+ subject.template_registry.after(subject.root.elements, :person, 'Bob', 'Builder')
90
+ subject.root.elements.length.should == 2
91
+ end
92
+
93
+ it "should instantiate a detached node from a template using the template name as a method" do
94
+ node = subject.template_registry.person('Odin', 'All-Father')
95
+ expectation = Nokogiri::XML('<person title="All-Father">Odin</person>').root
96
+ node.should be_equivalent_to(expectation)
97
+ end
98
+
99
+ it "should add_child" do
100
+ return_value = subject.template_registry.add_child(subject.root, :person, 'Bob', 'Builder')
101
+ return_value.should == subject.person[1]
102
+ subject.should be_equivalent_to(expectations[:after]).respecting_element_order
103
+ end
104
+
105
+ it "should add_next_sibling" do
106
+ return_value = subject.template_registry.add_next_sibling(subject.person.first, :person, 'Bob', 'Builder')
107
+ return_value.should == subject.person[1]
108
+ subject.should be_equivalent_to(expectations[:after]).respecting_element_order
109
+ end
110
+
111
+ it "should add_previous_sibling" do
112
+ return_value = subject.template_registry.add_previous_sibling(subject.person.first, :person, 'Bob', 'Builder')
113
+ return_value.should == subject.person.first
114
+ subject.should be_equivalent_to(expectations[:before]).respecting_element_order
115
+ end
116
+
117
+ it "should after" do
118
+ return_value = subject.template_registry.after(subject.person.first, :person, 'Bob', 'Builder')
119
+ return_value.should == subject.person.first
120
+ subject.should be_equivalent_to(expectations[:after]).respecting_element_order
121
+ end
122
+
123
+ it "should before" do
124
+ return_value = subject.template_registry.before(subject.person.first, :person, 'Bob', 'Builder')
125
+ return_value.should == subject.person[1]
126
+ subject.should be_equivalent_to(expectations[:before]).respecting_element_order
127
+ end
128
+
129
+ it "should replace" do
130
+ target_node = subject.person.first
131
+ return_value = subject.template_registry.replace(target_node, :person, 'Bob', 'Builder')
132
+ return_value.should == subject.person.first
133
+ subject.should be_equivalent_to(expectations[:instead]).respecting_element_order
134
+ end
135
+
136
+ it "should swap" do
137
+ target_node = subject.person.first
138
+ return_value = subject.template_registry.swap(target_node, :person, 'Bob', 'Builder')
139
+ return_value.should == target_node
140
+ subject.should be_equivalent_to(expectations[:instead]).respecting_element_order
141
+ end
142
+
143
+ it "should yield the result if a block is given" do
144
+ target_node = subject.person.first
145
+ expectation = Nokogiri::XML('<person xmlns="urn:registry-test" title="Actor">Alice</person>').root
146
+ subject.template_registry.swap(target_node, :person, 'Bob', 'Builder') { |old_node|
147
+ old_node.should be_equivalent_to(expectation)
148
+ old_node
149
+ }.should be_equivalent_to(expectation)
150
+ end
151
+ end
152
+
153
+ describe "document-based document manipulations" do
154
+ it "should accept a Nokogiri::XML::Node as target" do
155
+ subject.root.elements.first.after_person('Bob', 'Builder')
156
+ subject.root.elements.length.should == 2
157
+ end
158
+
159
+ it "should accept a Nokogiri::XML::NodeSet as target" do
160
+ subject.person.after_person(:person, 'Bob', 'Builder')
161
+ subject.root.elements.length.should == 2
162
+ end
163
+
164
+ it "should instantiate a detached node from a template" do
165
+ node = subject.template_registry.instantiate(:person, 'Odin', 'All-Father')
166
+ expectation = Nokogiri::XML('<person title="All-Father">Odin</person>').root
167
+ node.should be_equivalent_to(expectation)
168
+ end
169
+
170
+ it "should add_child_node" do
171
+ return_value = subject.root.add_child_person('Bob', 'Builder')
172
+ return_value.should == subject.person[1]
173
+ subject.should be_equivalent_to(expectations[:after]).respecting_element_order
174
+ end
175
+
176
+ it "should add_next_sibling_node" do
177
+ return_value = subject.person[0].add_next_sibling_person('Bob', 'Builder')
178
+ return_value.should == subject.person[1]
179
+ subject.should be_equivalent_to(expectations[:after]).respecting_element_order
180
+ end
181
+
182
+ it "should add_previous_sibling_node" do
183
+ return_value = subject.person[0].add_previous_sibling_person('Bob', 'Builder')
184
+ return_value.should == subject.person.first
185
+ subject.should be_equivalent_to(expectations[:before]).respecting_element_order
186
+ end
187
+
188
+ it "should after_node" do
189
+ return_value = subject.person[0].after_person('Bob', 'Builder')
190
+ return_value.should == subject.person.first
191
+ subject.should be_equivalent_to(expectations[:after]).respecting_element_order
192
+ end
193
+
194
+ it "should before_node" do
195
+ return_value = subject.person[0].before_person('Bob', 'Builder')
196
+ return_value.should == subject.person[1]
197
+ subject.should be_equivalent_to(expectations[:before]).respecting_element_order
198
+ end
199
+
200
+ it "should replace_node" do
201
+ target_node = subject.person.first
202
+ return_value = target_node.replace_person('Bob', 'Builder')
203
+ return_value.should == subject.person.first
204
+ subject.should be_equivalent_to(expectations[:instead]).respecting_element_order
205
+ end
206
+
207
+ it "should swap_node" do
208
+ target_node = subject.person.first
209
+ return_value = target_node.swap_person('Bob', 'Builder')
210
+ return_value.should == target_node
211
+ subject.should be_equivalent_to(expectations[:instead]).respecting_element_order
212
+ end
213
+ end
214
+
215
+ end
@@ -26,7 +26,6 @@ describe "Nutrition" do
26
26
  c.nested
27
27
  end
28
28
 
29
- t.d :if => 'my-custom-function()'
30
29
  end
31
30
 
32
31
  xml.nom!
@@ -34,102 +33,19 @@ describe "Nutrition" do
34
33
  xml
35
34
  }
36
35
 
37
- describe "#add_terminology_methods!" do
38
- subject do
39
- m = mock()
40
- m.stub(:document).and_return(mock(:terminology_namespaces => {}))
41
- m.stub(:term_accessors).and_return(@term_accessors)
42
- m.extend Nom::XML::Decorators::Terminology
43
- m
44
- end
45
-
46
- it "should define terminology accessors" do
47
- mock_term = mock(:options => {})
48
- @term_accessors = { :asdf => mock_term }
49
-
50
- subject.should respond_to(:asdf)
51
- end
52
-
53
- it "should perform basic xpath queries" do
54
- mock_term = mock(:local_xpath => '//asdf', :options => {})
55
- @term_accessors = { :asdf => mock_term }
56
-
57
- subject.should_receive(:xpath).with('//asdf', anything)
58
-
59
- subject.asdf
60
- end
61
-
62
- it "should perform xpath queries with constraints" do
63
- mock_term = mock(:local_xpath => '//asdf', :options => {})
64
- @term_accessors = { :asdf => mock_term }
65
-
66
- subject.should_receive(:xpath).with('//asdf[predicate="value"]', anything)
67
- subject.should_receive(:xpath).with('//asdf[predicate="\"value\""]', anything)
68
- subject.should_receive(:xpath).with('//asdf[custom-xpath-predicate()]', anything)
69
- subject.should_receive(:xpath).with('//asdf[custom-xpath-predicate()][predicate="value"]', anything)
70
-
71
- subject.asdf(:predicate => 'value')
72
- subject.asdf(:predicate => '"value"')
73
- subject.asdf('custom-xpath-predicate()')
74
- subject.asdf('custom-xpath-predicate()', :predicate => 'value')
75
- end
76
-
77
- it "should execute accessors" do
78
- mock_term = mock(:local_xpath => '//asdf', :options => {:accessor => :text })
79
- @term_accessors = { :asdf => mock_term }
80
-
81
- m = mock()
82
- subject.should_receive(:xpath).with('//asdf', anything).and_return([m])
83
- m.should_receive(:text)
84
-
85
- subject.asdf
86
- end
87
-
88
- it "should execute proc-based accessors" do
89
- mock_term = mock(:local_xpath => '//asdf', :options => {:accessor => lambda { |x| x.zxcvb } })
90
- @term_accessors = { :asdf => mock_term }
36
+ describe "#add_terminology_method_overrides!" do
91
37
 
92
- m = mock()
93
- subject.should_receive(:xpath).with('//asdf', anything).and_return([m])
94
- m.should_receive(:zxcvb)
95
-
96
- subject.asdf
97
- end
98
-
99
- it "should raise an error on unknown accessors" do
100
- mock_term = mock(:local_xpath => '//asdf', :options => {:accessor => 123 })
101
- @term_accessors = { :asdf => mock_term }
102
-
103
- subject.should_receive(:xpath).with('//asdf', anything)
104
-
105
- expect { subject.asdf }.to raise_error
106
- end
107
-
108
- it "should convert single-valued objects to single values" do
109
- mock_term = mock(:local_xpath => '//asdf', :options => {:single => true })
110
- @term_accessors = { :asdf => mock_term }
111
-
112
- subject.should_receive(:xpath).with('//asdf', anything).and_return([1])
113
-
114
- subject.asdf.should == 1
115
- end
116
-
117
- it "should evaluate if" do
118
- mock_term = mock(:local_xpath => '//asdf', :options => {:if => 'my-custom-function()' })
119
- @term_accessors = { :asdf => mock_term }
120
-
121
- subject.should_receive(:xpath).with('//asdf[my-custom-function()]', anything).and_return([1])
122
-
123
- subject.asdf.should == [1]
38
+ it "should warn you if you try to override already existing methods" do
39
+ pending if defined? JRUBY_VERSION
40
+ mock_term = {:text => mock(:options => {})}
41
+ document.a.first.stub(:term_accessors).and_return mock_term
42
+ expect { document.a.first.add_terminology_method_overrides! }.to raise_error /Trying to redefine/
124
43
  end
125
-
126
- it "should evaluate unless" do
127
- mock_term = mock(:local_xpath => '//asdf', :options => {:unless => 'my-custom-function()' })
128
- @term_accessors = { :asdf => mock_term }
129
-
130
- subject.should_receive(:xpath).with('//asdf[not(my-custom-function())]', anything).and_return([1])
131
-
132
- subject.asdf.should == [1]
44
+
45
+ it "should let you override the warning" do
46
+ mock_term = {:text => mock(:options => { :override => true } )}
47
+ document.a.first.stub(:term_accessors).and_return mock_term
48
+ expect { document.a.first.add_terminology_method_overrides! }.to_not raise_error /Trying to redefine/
133
49
  end
134
50
  end
135
51
 
data/spec/spec_helper.rb CHANGED
@@ -5,6 +5,7 @@ end
5
5
 
6
6
  require 'rspec/autorun'
7
7
  require 'nom'
8
+ require 'equivalent-xml/rspec_matchers'
8
9
 
9
10
  RSpec.configure do |config|
10
11
 
data/spec/test_spec.rb CHANGED
@@ -2,6 +2,21 @@ require 'spec_helper'
2
2
 
3
3
  describe Nom::XML do
4
4
 
5
+ it "shouldn't break under jruby" do
6
+ doc = Nokogiri::XML <<-eoxml
7
+ <root>
8
+ <a b="1">123</a>
9
+ </root>
10
+ eoxml
11
+
12
+ doc.set_terminology do |t|
13
+ t.b :path => '//@b', :accessor => lambda { |x| x.text }
14
+ end
15
+
16
+ doc.nom!
17
+
18
+ doc.b.should == "1"
19
+ end
5
20
  it "should do stuff with terminologies" do
6
21
  doc = Nokogiri::XML <<-eos
7
22
  <root>
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nom-xml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Chris Beer
9
+ - Michael B. Klein
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2012-11-07 00:00:00.000000000 Z
13
+ date: 2012-12-03 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: activesupport
@@ -107,8 +108,25 @@ dependencies:
107
108
  - - ! '>='
108
109
  - !ruby/object:Gem::Version
109
110
  version: '0'
110
- description: ! ' asdfgh '
111
- email: cabeer@stanford.edu
111
+ - !ruby/object:Gem::Dependency
112
+ name: equivalent-xml
113
+ requirement: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ description: ! ' NOM allows you to define a “terminology” to ease translation between
128
+ XML and ruby objects '
129
+ email: cabeer@stanford.edu mbklein@gmail.com
112
130
  executables: []
113
131
  extensions: []
114
132
  extra_rdoc_files:
@@ -131,7 +149,7 @@ files:
131
149
  - lib/nom/xml/term.rb
132
150
  - lib/nom/xml/terminology.rb
133
151
  - lib/nom/xml/version.rb
134
- - nom.gemspec
152
+ - nom-xml.gemspec
135
153
  - spec/examples/mods_example_spec.rb
136
154
  - spec/examples/namespaced_example_spec.rb
137
155
  - spec/examples/nutrition_example_spec.rb
@@ -141,11 +159,12 @@ files:
141
159
  - spec/fixtures/xml_namespaces.xml
142
160
  - spec/lib/nodeset_decorator_spec.rb
143
161
  - spec/lib/nokogiri_extension_spec.rb
162
+ - spec/lib/template_registry_spec.rb
144
163
  - spec/lib/terminology_decorator_spec.rb
145
164
  - spec/lib/terminology_spec.rb
146
165
  - spec/spec_helper.rb
147
166
  - spec/test_spec.rb
148
- homepage: http://github.com/cbeer/nom
167
+ homepage: http://github.com/cbeer/nom-xml
149
168
  licenses: []
150
169
  post_install_message:
151
170
  rdoc_options: []
@@ -159,7 +178,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
178
  version: '0'
160
179
  segments:
161
180
  - 0
162
- hash: -2552568584722504681
181
+ hash: -2880685956354160513
163
182
  required_rubygems_version: !ruby/object:Gem::Requirement
164
183
  none: false
165
184
  requirements:
@@ -168,13 +187,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
187
  version: '0'
169
188
  segments:
170
189
  - 0
171
- hash: -2552568584722504681
190
+ hash: -2880685956354160513
172
191
  requirements: []
173
192
  rubyforge_project:
174
193
  rubygems_version: 1.8.23
175
194
  signing_key:
176
195
  specification_version: 3
177
- summary: asdf
196
+ summary: A library to help you tame sprawling XML schemas.
178
197
  test_files:
179
198
  - spec/examples/mods_example_spec.rb
180
199
  - spec/examples/namespaced_example_spec.rb
@@ -185,6 +204,7 @@ test_files:
185
204
  - spec/fixtures/xml_namespaces.xml
186
205
  - spec/lib/nodeset_decorator_spec.rb
187
206
  - spec/lib/nokogiri_extension_spec.rb
207
+ - spec/lib/template_registry_spec.rb
188
208
  - spec/lib/terminology_decorator_spec.rb
189
209
  - spec/lib/terminology_spec.rb
190
210
  - spec/spec_helper.rb