nom-xml 0.3.0 → 0.5.0

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