om 1.8.0 → 1.8.1

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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/.rubocop.yml +1 -0
  4. data/.rubocop_todo.yml +382 -0
  5. data/.travis.yml +10 -0
  6. data/Rakefile +1 -1
  7. data/container_spec.rb +14 -14
  8. data/gemfiles/gemfile.rails3 +11 -0
  9. data/gemfiles/gemfile.rails4 +10 -0
  10. data/lib/om.rb +9 -12
  11. data/lib/om/samples/mods_article.rb +9 -9
  12. data/lib/om/tree_node.rb +6 -6
  13. data/lib/om/version.rb +1 -1
  14. data/lib/om/xml.rb +18 -20
  15. data/lib/om/xml/container.rb +12 -12
  16. data/lib/om/xml/document.rb +3 -7
  17. data/lib/om/xml/dynamic_node.rb +45 -50
  18. data/lib/om/xml/named_term_proxy.rb +13 -13
  19. data/lib/om/xml/node_generator.rb +3 -3
  20. data/lib/om/xml/template_registry.rb +18 -26
  21. data/lib/om/xml/term.rb +30 -46
  22. data/lib/om/xml/term_value_operators.rb +52 -56
  23. data/lib/om/xml/term_xpath_generator.rb +51 -57
  24. data/lib/om/xml/terminology.rb +8 -10
  25. data/lib/om/xml/validation.rb +19 -19
  26. data/lib/om/xml/vocabulary.rb +4 -4
  27. data/lib/tasks/om.rake +4 -6
  28. data/om.gemspec +1 -2
  29. data/spec/integration/differentiated_elements_spec.rb +2 -2
  30. data/spec/integration/element_value_spec.rb +13 -13
  31. data/spec/integration/proxies_and_ref_spec.rb +15 -15
  32. data/spec/integration/querying_documents_spec.rb +24 -18
  33. data/spec/integration/rights_metadata_integration_example_spec.rb +18 -18
  34. data/spec/integration/selective_querying_spec.rb +1 -1
  35. data/spec/integration/serialization_spec.rb +13 -13
  36. data/spec/integration/set_reentrant_terminology_spec.rb +7 -7
  37. data/spec/integration/xpathy_stuff_spec.rb +16 -16
  38. data/spec/spec_helper.rb +2 -3
  39. data/spec/unit/container_spec.rb +28 -29
  40. data/spec/unit/document_spec.rb +49 -50
  41. data/spec/unit/dynamic_node_spec.rb +55 -47
  42. data/spec/unit/named_term_proxy_spec.rb +16 -16
  43. data/spec/unit/node_generator_spec.rb +7 -7
  44. data/spec/unit/nokogiri_sanity_spec.rb +30 -30
  45. data/spec/unit/om_spec.rb +5 -5
  46. data/spec/unit/template_registry_spec.rb +69 -69
  47. data/spec/unit/term_builder_spec.rb +77 -77
  48. data/spec/unit/term_spec.rb +78 -72
  49. data/spec/unit/term_value_operators_spec.rb +186 -191
  50. data/spec/unit/term_xpath_generator_spec.rb +37 -43
  51. data/spec/unit/terminology_builder_spec.rb +85 -85
  52. data/spec/unit/terminology_spec.rb +98 -98
  53. data/spec/unit/validation_spec.rb +22 -22
  54. data/spec/unit/xml_serialization_spec.rb +21 -22
  55. data/spec/unit/xml_spec.rb +7 -7
  56. metadata +143 -147
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec :path=>"../"
4
+
5
+ gem 'activemodel', '~> 3.2.13'
6
+
7
+ group :development, :test do
8
+ gem 'rcov', :platform => :mri_18
9
+ gem 'simplecov', :platform => :mri_19
10
+ gem 'simplecov-rcov', :platform => :mri_19
11
+ end
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec :path=>"../"
4
+
5
+ gem 'activemodel', '4.0.0.rc1'
6
+
7
+ group :development, :test do
8
+ gem 'simplecov', :platform => :mri_19
9
+ gem 'simplecov-rcov', :platform => :mri_19
10
+ end
data/lib/om.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
- require 'active_support/concern'
3
- require 'active_model/dirty'
2
+ require 'active_model'
4
3
  require 'deprecation'
5
4
  require 'nokogiri'
6
5
 
@@ -10,7 +9,7 @@ module OM
10
9
  # Converts [{":person"=>"0"}, ":last_name"] to [{:person=>0}, :last_name]
11
10
  def self.destringify(params)
12
11
  case params
13
- when String
12
+ when String
14
13
  if params == "0" || params.to_i != 0
15
14
  result = params.to_i
16
15
  elsif params[0,1] == ":"
@@ -19,13 +18,13 @@ module OM
19
18
  result = params.to_sym
20
19
  end
21
20
  return result
22
- when Hash
21
+ when Hash
23
22
  result = {}
24
23
  params.each_pair do |k,v|
25
24
  result[ destringify(k) ] = destringify(v)
26
25
  end
27
26
  return result
28
- when Array
27
+ when Array
29
28
  result = []
30
29
  params.each do |x|
31
30
  result << destringify(x)
@@ -35,31 +34,29 @@ module OM
35
34
  return params
36
35
  end
37
36
  end
38
-
37
+
39
38
  # Convert a Term pointer into a flat array without Hashes.
40
39
  # If include_indices is set to false, node indices will be removed.
41
40
  #
42
41
  # @param [Array] pointers array that you would pass into other Accessor methods
43
42
  # @param [Boolean] include_indices (default: true) if set to false, parent indices will be excluded from the array
44
43
  # @example Turn a pointer into a flat array with node indices preserved
45
- # OM.pointers_to_flat_array( [{:conference=>0}, {:role=>1}, :text] )
44
+ # OM.pointers_to_flat_array( [{:conference=>0}, {:role=>1}, :text] )
46
45
  # => [:conference, 0, :role, 1, :text]
47
46
  # @example Remove node indices by setting include_indices to false
48
- # OM.pointers_to_flat_array( [{:conference=>0}, {:role=>1}, :text], false )
47
+ # OM.pointers_to_flat_array( [{:conference=>0}, {:role=>1}, :text], false )
49
48
  # => [:conference, :role, :text]
50
49
  def self.pointers_to_flat_array(pointers, include_indices=true)
51
50
  flat_array = []
52
51
  pointers.each do |pointer|
53
52
  if pointer.kind_of?(Hash)
54
53
  flat_array << pointer.keys.first
55
- if include_indices
56
- flat_array << pointer.values.first
57
- end
54
+ flat_array << pointer.values.first if include_indices
58
55
  else
59
56
  flat_array << pointer
60
57
  end
61
58
  end
62
- return flat_array
59
+ flat_array
63
60
  end
64
61
 
65
62
  def self.version
@@ -1,7 +1,7 @@
1
1
  class OM::Samples::ModsArticle
2
-
2
+
3
3
  include OM::XML::Document
4
-
4
+
5
5
  set_terminology do |t|
6
6
  t.root(:path=>"mods", :xmlns=>"http://www.loc.gov/mods/v3", :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd", "xmlns:foo"=>"http://my.custom.namespace")
7
7
 
@@ -11,17 +11,17 @@ class OM::Samples::ModsArticle
11
11
  t.main_title_lang(:path=>{:attribute=> "xml:lang"})
12
12
  }
13
13
  t.french_title(:ref=>[:title_info,:main_title], :attributes=>{"xml:lang"=>"fre"})
14
-
14
+
15
15
  t.language(:index_as=>[:facetable],:path=>{:attribute=>"lang"})
16
- }
16
+ }
17
17
  t.language{
18
18
  t.lang_code(:index_as=>[:facetable], :path=>"languageTerm", :attributes=>{:type=>"code"})
19
19
  }
20
- t.abstract
20
+ t.abstract
21
21
  t.subject {
22
22
  t.topic(:index_as=>[:facetable])
23
- }
24
- t.topic_tag(:proxy=>[:subject, :topic])
23
+ }
24
+ t.topic_tag(:proxy=>[:subject, :topic])
25
25
  # t.topic_tag(:index_as=>[:facetable],:path=>"subject", :default_content_path=>"topic")
26
26
  # This is a mods:name. The underscore is purely to avoid namespace conflicts.
27
27
  t.name_ {
@@ -40,7 +40,7 @@ class OM::Samples::ModsArticle
40
40
  t.computing_id
41
41
  t.name_content(:path=>"text()")
42
42
  }
43
- # lookup :person, :first_name
43
+ # lookup :person, :first_name
44
44
  t.person(:ref=>:name, :attributes=>{:type=>"personal"}, :index_as=>[:facetable])
45
45
  t.department(:proxy=>[:person,:description],:index_as=>[:facetable])
46
46
  t.organization(:ref=>:name, :attributes=>{:type=>"corporate"}, :index_as=>[:facetable])
@@ -78,7 +78,7 @@ class OM::Samples::ModsArticle
78
78
  t.title(:proxy=>[:title_info, :main_title])
79
79
  t.journal_title(:proxy=>[:journal, :title_info, :main_title])
80
80
  end
81
-
81
+
82
82
  # Changes from OM::Properties implementation
83
83
  # renamed family_name => last_name
84
84
  # start_page & end_page now accessible as [:journal, :issue, :pages, :start] (etc.)
data/lib/om/tree_node.rb CHANGED
@@ -1,23 +1,23 @@
1
1
  module OM::TreeNode
2
-
2
+
3
3
  attr_accessor :ancestors
4
-
4
+
5
5
  # insert the mapper into the given parent
6
6
  def set_parent(parent_mapper)
7
7
  parent_mapper.children[@name] = self
8
8
  @ancestors << parent_mapper
9
9
  end
10
-
10
+
11
11
  # insert the given mapper into the current mappers children
12
12
  def add_child(child_mapper)
13
13
  child_mapper.ancestors << self
14
- @children[child_mapper.name.to_sym] = child_mapper
14
+ @children[child_mapper.name.to_sym] = child_mapper
15
15
  end
16
-
16
+
17
17
  def retrieve_child(child_name)
18
18
  child = @children.fetch(child_name, nil)
19
19
  end
20
-
20
+
21
21
  def parent
22
22
  ancestors.last
23
23
  end
data/lib/om/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Om
2
- VERSION = "1.8.0"
2
+ VERSION = '1.8.1'
3
3
  end
data/lib/om/xml.rb CHANGED
@@ -13,46 +13,44 @@ require "om/xml/dynamic_node"
13
13
  require "om/xml/template_registry"
14
14
 
15
15
  module OM::XML
16
-
16
+
17
17
  # Raised when the XML document or XML template can't be found during an operation
18
18
  class TemplateMissingException < StandardError; end
19
-
19
+
20
20
  attr_accessor :ng_xml
21
-
21
+
22
22
  # Module Methods -- These methods can be called directly on the Module itself
23
-
23
+
24
24
  # Transforms an array of values into a string delimited by +delimiter+
25
25
  def self.delimited_list( values_array, delimiter=", ")
26
26
  values_array.join(delimiter)
27
27
  end
28
-
29
- # Class Methods -- These methods will be available on classes that include this Module
30
-
28
+
29
+ # Class Methods -- These methods will be available on classes that include this Module
30
+
31
31
  module ClassMethods
32
-
32
+
33
33
  # @pointer accessor or property info pointer
34
- #
34
+ #
35
35
  # ex. [[:person,1],:role] will be converted to [{:person=>1},:role]
36
- def sanitize_pointer(pointer)
37
- if pointer.kind_of?(Array)
36
+ def sanitize_pointer(pointer)
37
+ if pointer.kind_of?(Array)
38
38
  pointer.each do |x|
39
- if x.kind_of?(Array)
40
- pointer[pointer.index(x)] = Hash[x[0],x[1]]
41
- end
39
+ pointer[pointer.index(x)] = Hash[x[0],x[1]] if x.kind_of?(Array)
42
40
  end
43
41
  end
44
- return pointer
42
+ pointer
45
43
  end
46
-
44
+
47
45
  end
48
-
46
+
49
47
  # Instance Methods -- These methods will be available on instances of classes that include this module
50
-
48
+
51
49
  def self.included(klass)
52
50
  klass.extend(ClassMethods)
53
51
  klass.send(:include, OM::XML::Container)
54
52
  klass.send(:include, OM::XML::Validation)
55
53
  end
56
-
57
-
54
+
55
+
58
56
  end
@@ -1,12 +1,12 @@
1
1
  module OM::XML::Container
2
2
  extend ActiveSupport::Concern
3
-
3
+
4
4
  attr_accessor :ng_xml
5
-
6
- # Class Methods -- These methods will be available on classes that include this Module
7
-
5
+
6
+ # Class Methods -- These methods will be available on classes that include this Module
7
+
8
8
  module ClassMethods
9
-
9
+
10
10
  # @xml String, File or Nokogiri::XML::Node
11
11
  # @tmpl ActiveFedora::MetadataDatastream
12
12
  # Careful! If you call this from a constructor, be sure to provide something 'ie. self' as the @tmpl. Otherwise, you will get an infinite loop!
@@ -18,25 +18,25 @@ module OM::XML::Container
18
18
  else
19
19
  tmpl.ng_xml = Nokogiri::XML::Document.parse(xml)
20
20
  end
21
- return tmpl
21
+ tmpl
22
22
  end
23
-
23
+
24
24
  # By default, new OM Document instances will create an empty xml document, but if you override self.xml_template to return a different object (e.g. Nokogiri::XML::Document), that will be created instead.
25
25
  # You can make this method create the documents however you want as long as it returns a Nokogiri::XML::Document.
26
- # In the tutorials, we use Nokogiri::XML::Builder in this mehtod and call its .doc method at the end of xml_template in order to return the Nokogiri::XML::Document object. Instead of using Nokogiri::XML::Builder, you could put your template into an actual xml file and have xml_template use Nokogiri::XML::Document.parse to load it. That’s up to you.
26
+ # In the tutorials, we use Nokogiri::XML::Builder in this mehtod and call its .doc method at the end of xml_template in order to return the Nokogiri::XML::Document object. Instead of using Nokogiri::XML::Builder, you could put your template into an actual xml file and have xml_template use Nokogiri::XML::Document.parse to load it. That’s up to you.
27
27
  # @return Nokogiri::XML::Document
28
28
  def xml_template
29
29
  Nokogiri::XML::Document.parse("")
30
30
  end
31
-
31
+
32
32
  end
33
-
33
+
34
34
  def ng_xml
35
35
  @ng_xml ||= self.class.xml_template
36
36
  end
37
37
 
38
38
  # Instance Methods -- These methods will be available on instances of classes that include this module
39
-
39
+
40
40
  def to_xml(xml = ng_xml)
41
41
  if xml == ng_xml
42
42
  return xml.to_xml
@@ -52,5 +52,5 @@ module OM::XML::Container
52
52
  raise "You can only pass instances of Nokogiri::XML::Node into this method. You passed in #{xml}"
53
53
  end
54
54
  end
55
-
55
+
56
56
  end
@@ -35,7 +35,7 @@ module OM::XML::Document
35
35
  self.terminology_builder.extend_terminology(&block)
36
36
  rebuild_terminology!
37
37
  end
38
-
38
+
39
39
  # (Explicitly) inherit terminology from upstream classes
40
40
  def use_terminology klass
41
41
  self.terminology_builder = klass.terminology_builder.dup
@@ -69,9 +69,7 @@ module OM::XML::Document
69
69
  end
70
70
 
71
71
  def ng_xml_will_change!
72
- if self.respond_to?(:dirty=)
73
- self.dirty = true
74
- end
72
+ self.dirty = true if self.respond_to?(:dirty=)
75
73
 
76
74
  # throw away older version.
77
75
  changed_attributes['ng_xml'] = nil
@@ -196,9 +194,7 @@ module OM::XML::Document
196
194
 
197
195
  private
198
196
  def manipulate_node(method, target, *args, &block)
199
- if target.is_a?(Array)
200
- target = self.find_by_terms(*target)
201
- end
197
+ target = self.find_by_terms(*target) if target.is_a?(Array)
202
198
  raise "You must call define_template before calling #{method}" if template_registry.nil?
203
199
  ng_xml_will_change!
204
200
  template_registry.send(method, target, *args, &block)
@@ -3,8 +3,8 @@ module OM
3
3
  #
4
4
  # Provides a natural syntax for using OM Terminologies to access values from xml Documents
5
5
  #
6
- # *Note*: All of these examples assume that @article is an instance of OM::Samples::ModsArticle. Look at that file to see the Terminology.
7
- #
6
+ # *Note*: All of these examples assume that @article is an instance of OM::Samples::ModsArticle. Look at that file to see the Terminology.
7
+ #
8
8
  # @example Return an array of the value(s) "start page" node(s) from the second issue node within the first journal node
9
9
  # # Using DynamicNode syntax:
10
10
  # @article.journal(0).issue(1).pages.start
@@ -30,7 +30,7 @@ module OM
30
30
  instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }
31
31
 
32
32
  attr_accessor :key, :index, :parent, :addressed_node, :term
33
- def initialize(key, index, document, term, parent=nil) ##TODO a real term object in here would make it easier to lookup
33
+ def initialize(key, index, document, term, parent=nil) # #TODO a real term object in here would make it easier to lookup
34
34
  self.key = key
35
35
  self.index = index
36
36
  @document = document
@@ -38,33 +38,23 @@ module OM
38
38
  self.parent = parent
39
39
  end
40
40
 
41
- def method_missing (name, *args, &block)
42
- if /=$/.match(name.to_s)
43
- new_update_node(name, args)
44
- elsif args.length > 1
45
- new_update_node_with_index(name, args)
46
- else
47
- child = term_child_by_name(term.nil? ? parent.term : term, name)
48
- if child
49
- OM::XML::DynamicNode.new(name, args.first, @document, child, self)
50
- else
51
- val.send(name, *args, &block)
52
- end
53
- end
54
- end
55
-
56
- def new_update_node(name, args)
57
- modified_name = name.to_s.chop.to_sym
58
- child = term.retrieve_term(modified_name)
59
- node = OM::XML::DynamicNode.new(modified_name, nil, @document, child, self)
60
- node.val=args
41
+ # In practice, method_missing will respond 4 different ways:
42
+ # (1) ALL assignment operations are accepted/attempted as new nodes,
43
+ # (2) ANY operation with multiple arguments is accepted/attempted as a new node (w/ index),
44
+ # (3) With an auto-constructed sub DynamicNode object,
45
+ # (4) By handing off to val. This is the only route that will return NoMethodError.
46
+ #
47
+ # Here we don't have args, so we cannot handle cases 2 and 3. But we can at least do 1 and 4.
48
+ def respond_to_missing?(name, include_private = false)
49
+ /=$/.match(name.to_s) || val.respond_to?(name, include_private) || super
61
50
  end
62
51
 
63
- def new_update_node_with_index(name, args)
64
- index = args.shift
65
- child = term.retrieve_term(name)
66
- node = OM::XML::DynamicNode.new(name, index, @document, child, self)
67
- node.val=args
52
+ def method_missing(name, *args, &block)
53
+ return new_update_node(name.to_s.chop.to_sym, nil, args) if /=$/.match(name.to_s)
54
+ return new_update_node(name, args.shift, args) if args.length > 1
55
+ child = term_child_by_name(term.nil? ? parent.term : term, name)
56
+ return OM::XML::DynamicNode.new(name, args.first, @document, child, self) if child
57
+ val.send(name, *args, &block)
68
58
  end
69
59
 
70
60
  def val=(args)
@@ -82,16 +72,7 @@ module OM
82
72
  end
83
73
  end
84
74
 
85
-
86
- def term_child_by_name(term, name)
87
- if (term.kind_of? NamedTermProxy)
88
- @document.class.terminology.retrieve_node(*(term.proxy_pointer.dup << name))
89
- else
90
- term.retrieve_term(name)
91
- end
92
- end
93
-
94
- def val
75
+ def val
95
76
  query = xpath
96
77
  trim_text = !query.index("text()").nil?
97
78
  val = @document.find_by_xpath(query).collect {|node| (trim_text ? node.text.strip : node.text) }
@@ -101,13 +82,13 @@ module OM
101
82
  def nodeset
102
83
  query = xpath
103
84
  trim_text = !query.index("text()").nil?
104
- return @document.find_by_xpath(query)
85
+ @document.find_by_xpath(query)
105
86
  end
106
87
 
107
88
  def delete
108
89
  nodeset.delete
109
90
  end
110
-
91
+
111
92
  def inspect
112
93
  val.inspect
113
94
  end
@@ -126,7 +107,7 @@ module OM
126
107
  else ### A pointer
127
108
  parent.nil? ? [key] : parent.to_pointer << key
128
109
  end
129
- end
110
+ end
130
111
 
131
112
  def xpath
132
113
  if parent.nil?
@@ -135,10 +116,8 @@ module OM
135
116
  chain = retrieve_addressed_node( )
136
117
  '//' + chain.map { |n| n.xpath}.join('/')
137
118
  end
138
-
139
119
  end
140
120
 
141
-
142
121
  class AddressedNode
143
122
  attr_accessor :xpath, :key, :pointer
144
123
  def initialize (pointer, xpath, key)
@@ -147,15 +126,12 @@ module OM
147
126
  self.pointer = pointer
148
127
  end
149
128
  end
150
-
129
+
151
130
  ##
152
131
  # This is very similar to Terminology#retrieve_term, however it expands proxy paths out into their cannonical paths
153
132
  def retrieve_addressed_node()
154
133
  chain = []
155
-
156
- if parent
157
- chain += parent.retrieve_addressed_node()
158
- end
134
+ chain += parent.retrieve_addressed_node() if parent
159
135
  if (self.index)
160
136
  ### This is an index
161
137
  node = AddressedNode.new(key, term.xpath_relative, self)
@@ -166,17 +142,36 @@ module OM
166
142
  first = proxy.shift
167
143
  p = @document.class.terminology.retrieve_node(*first)
168
144
  chain << AddressedNode.new(p, p.xpath_relative, self)
169
- while !proxy.empty?
145
+ until proxy.empty?
170
146
  first = proxy.shift
171
147
  p = p.retrieve_term(first)
172
148
  chain << AddressedNode.new(p, p.xpath_relative, self)
173
149
  end
174
- else
150
+ else
175
151
  chain << AddressedNode.new(key, term.xpath_relative, self)
176
152
  end
177
153
  chain
178
154
  end
179
155
 
156
+ private
157
+
158
+ # Only to be called by method_missing, hence the NoMethodError.
159
+ # We know term.sanitize_new_values would fail in .val= if we pass a nil term.
160
+ def new_update_node(name, index, args)
161
+ child = term.retrieve_term(name)
162
+ raise NoMethodError, "undefined method `#{name}' in OM::XML::DynamicNode for #{self}:#{self.class}" if child.nil?
163
+ node = OM::XML::DynamicNode.new(name, index, @document, child, self)
164
+ node.val = args
165
+ end
166
+
167
+ # Only to be called by method_missing
168
+ def term_child_by_name(term, name)
169
+ if (term.kind_of? NamedTermProxy)
170
+ @document.class.terminology.retrieve_node(*(term.proxy_pointer.dup << name))
171
+ else
172
+ term.retrieve_term(name)
173
+ end
174
+ end
180
175
 
181
176
  end
182
177
  end