om 1.6.1 → 1.7.0.rc1

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/Gemfile.lock CHANGED
@@ -1,7 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- om (1.6.0)
4
+ om (1.6.2)
5
+ activemodel
6
+ activesupport
5
7
  mediashelf-loggable
6
8
  nokogiri (>= 1.4.2)
7
9
 
@@ -9,36 +11,45 @@ GEM
9
11
  remote: http://rubygems.org/
10
12
  specs:
11
13
  RedCloth (4.2.9)
12
- columnize (0.3.5)
13
- debugger (1.1.3)
14
+ activemodel (3.2.8)
15
+ activesupport (= 3.2.8)
16
+ builder (~> 3.0.0)
17
+ activesupport (3.2.8)
18
+ i18n (~> 0.6)
19
+ multi_json (~> 1.0)
20
+ awesome_print (1.0.2)
21
+ builder (3.0.0)
22
+ columnize (0.3.6)
23
+ debugger (1.2.0)
14
24
  columnize (>= 0.3.1)
15
25
  debugger-linecache (~> 1.1.1)
16
- debugger-ruby_core_source (~> 1.1.2)
17
- debugger-linecache (1.1.1)
26
+ debugger-ruby_core_source (~> 1.1.3)
27
+ debugger-linecache (1.1.2)
18
28
  debugger-ruby_core_source (>= 1.1.1)
19
29
  debugger-ruby_core_source (1.1.3)
20
30
  diff-lcs (1.1.3)
21
- equivalent-xml (0.2.8)
31
+ equivalent-xml (0.2.9)
22
32
  nokogiri (>= 1.4.3)
33
+ i18n (0.6.0)
23
34
  linecache (0.46)
24
35
  rbx-require-relative (> 0.0.4)
25
36
  mediashelf-loggable (0.4.9)
26
37
  metaclass (0.0.1)
27
- mocha (0.10.0)
38
+ mocha (0.12.3)
28
39
  metaclass (~> 0.0.1)
29
40
  multi_json (1.3.6)
30
- nokogiri (1.5.0)
41
+ nokogiri (1.5.5)
31
42
  rake (0.9.2.2)
32
- rbx-require-relative (0.0.5)
33
- rcov (0.9.11)
34
- rspec (2.7.0)
35
- rspec-core (~> 2.7.0)
36
- rspec-expectations (~> 2.7.0)
37
- rspec-mocks (~> 2.7.0)
38
- rspec-core (2.7.1)
39
- rspec-expectations (2.7.0)
40
- diff-lcs (~> 1.1.2)
41
- rspec-mocks (2.7.0)
43
+ rbx-require-relative (0.0.9)
44
+ rcov (1.0.0)
45
+ rspec (2.11.0)
46
+ rspec-core (~> 2.11.0)
47
+ rspec-expectations (~> 2.11.0)
48
+ rspec-mocks (~> 2.11.0)
49
+ rspec-core (2.11.1)
50
+ rspec-expectations (2.11.2)
51
+ diff-lcs (~> 1.1.3)
52
+ rspec-mocks (2.11.2)
42
53
  ruby-debug (0.10.4)
43
54
  columnize (>= 0.1)
44
55
  ruby-debug-base (~> 0.10.4.0)
@@ -50,13 +61,14 @@ GEM
50
61
  simplecov-html (0.5.3)
51
62
  simplecov-rcov (0.2.3)
52
63
  simplecov (>= 0.4.1)
53
- yard (0.7.4)
64
+ yard (0.8.2.1)
54
65
 
55
66
  PLATFORMS
56
67
  ruby
57
68
 
58
69
  DEPENDENCIES
59
70
  RedCloth (~> 4.2.9)
71
+ awesome_print
60
72
  debugger
61
73
  equivalent-xml (>= 0.2.4)
62
74
  mocha (>= 0.9.8)
data/History.textile CHANGED
@@ -1,3 +1,6 @@
1
+ h3. 1.7.0
2
+ Add casting to dates and integers when you specify the :type attribute on a terminology node
3
+
1
4
  h3. 1.6.1
2
5
  Integration spec to illustrate selective querying.
3
6
  Add #use_terminology and #extend_terminology methods to extend existing OM terminologies
data/devel/notes.txt ADDED
@@ -0,0 +1,12 @@
1
+ Purposes of OM (based on notes from conversation with Matt Zumwalt):
2
+
3
+ 1. Provide convenient names to access information in an XML document --
4
+ essentially a declarative mechanism to define methods that will issue
5
+ xpath queries against an XML document.
6
+
7
+ 2. Provides a convient way to create new, empty XML documents (or fragments),
8
+ with implied parent nodes (if any) and default attribute values (if any).
9
+ This is done by overriding the class method xml_template() in your XML
10
+ document class.
11
+
12
+ 3. Provides a way to map your OM terminology into SOLR.
data/lib/om.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'rubygems'
2
+ require 'active_support/concern'
3
+ require 'active_model/dirty'
2
4
 
3
5
  require 'nokogiri'
4
6
 
data/lib/om/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Om
2
- VERSION = "1.6.1"
2
+ VERSION = "1.7.0.rc1"
3
3
  end
@@ -1,4 +1,5 @@
1
1
  module OM::XML::Container
2
+ extend ActiveSupport::Concern
2
3
 
3
4
  attr_accessor :ng_xml
4
5
 
@@ -11,7 +12,7 @@ module OM::XML::Container
11
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!
12
13
  def from_xml(xml=nil, tmpl=self.new) # :nodoc:
13
14
  if xml.nil?
14
- tmpl.ng_xml = self.xml_template
15
+ # noop: handled in #ng_xml accessor.. tmpl.ng_xml = self.xml_template
15
16
  elsif xml.kind_of? Nokogiri::XML::Node
16
17
  tmpl.ng_xml = xml
17
18
  else
@@ -30,12 +31,12 @@ module OM::XML::Container
30
31
 
31
32
  end
32
33
 
34
+ def ng_xml
35
+ @ng_xml ||= self.class.xml_template
36
+ end
37
+
33
38
  # Instance Methods -- These methods will be available on instances of classes that include this module
34
39
 
35
- def self.included(klass)
36
- klass.extend(ClassMethods)
37
- end
38
-
39
40
  def to_xml(xml = ng_xml)
40
41
  if xml == ng_xml
41
42
  return xml.to_xml
@@ -52,4 +53,4 @@ module OM::XML::Container
52
53
  end
53
54
  end
54
55
 
55
- end
56
+ end
@@ -1,5 +1,5 @@
1
1
  module OM::XML::Document
2
-
2
+ extend ActiveSupport::Concern
3
3
 
4
4
  # Class Methods -- These methods will be available on classes that include this Module
5
5
 
@@ -7,24 +7,39 @@ module OM::XML::Document
7
7
 
8
8
  attr_accessor :terminology, :terminology_builder, :template_registry
9
9
 
10
+ def terminology
11
+ @terminology ||= terminology_builder.build
12
+ end
13
+
14
+ def terminology_builder
15
+ @terminology_builder ||= OM::XML::Terminology::Builder.new
16
+ end
17
+
18
+ def template_registry
19
+ @template_registry ||= OM::XML::TemplateRegistry.new
20
+ end
21
+
22
+ def rebuild_terminology!
23
+ @terminology = @terminology_builder.build
24
+ end
25
+
10
26
  # Sets the OM::XML::Terminology for the Document
11
27
  # Expects +&block+ that will be passed into OM::XML::Terminology::Builder.new
12
28
  def set_terminology &block
13
- @terminology_builder = OM::XML::Terminology::Builder.new( &block )
14
-
15
- @terminology = @terminology_builder.build
29
+ @terminology_builder = OM::XML::Terminology::Builder.new(&block)
30
+ rebuild_terminology!
16
31
  end
17
32
 
18
33
  # Update the OM::XML::Terminology with additional terms
19
34
  def extend_terminology &block
20
- @terminology_builder.extend_terminology(&block)
21
- @terminology = @terminology_builder.build
35
+ self.terminology_builder.extend_terminology(&block)
36
+ rebuild_terminology!
22
37
  end
23
-
38
+
24
39
  # (Explicitly) inherit terminology from upstream classes
25
40
  def use_terminology klass
26
- @terminology_builder = klass.terminology_builder.dup
27
- @terminology = @terminology_builder.build
41
+ self.terminology_builder = klass.terminology_builder.dup
42
+ rebuild_terminology!
28
43
  end
29
44
 
30
45
  # Define a new node template with the OM::XML::TemplateRegistry.
@@ -32,31 +47,38 @@ module OM::XML::Document
32
47
  # * The +block+ does the work of creating the new node, and will receive
33
48
  # a Nokogiri::XML::Builder and any other args passed to one of the node instantiation methods.
34
49
  def define_template name, &block
35
- @template_registry ||= OM::XML::TemplateRegistry.new
36
- @template_registry.define name, &block
50
+ self.template_registry.define name, &block
37
51
  end
38
52
 
39
53
  # Returns any namespaces defined by the Class' Terminology
40
54
  def ox_namespaces
41
- if @terminology.nil?
42
- return {}
43
- else
44
- return @terminology.namespaces
45
- end
55
+ self.terminology.namespaces
46
56
  end
47
-
48
57
  end
49
58
 
50
59
  # Instance Methods -- These methods will be available on instances of classes that include this module
51
60
 
52
61
  attr_accessor :ox_namespaces
53
62
 
54
- def self.included(klass)
55
- klass.extend(ClassMethods)
63
+ included do
64
+ include OM::XML::Container
65
+ include OM::XML::TermValueOperators
66
+ include OM::XML::Validation
67
+ include ActiveModel::Dirty
68
+
69
+ end
70
+
71
+ def ng_xml_will_change!
72
+ if self.respond_to?(:dirty=)
73
+ self.dirty = true
74
+ end
56
75
 
57
- klass.send(:include, OM::XML::Container)
58
- klass.send(:include, OM::XML::TermValueOperators)
59
- klass.send(:include, OM::XML::Validation)
76
+ # throw away older version.
77
+ changed_attributes['ng_xml'] = nil
78
+ end
79
+
80
+ def ng_xml_changed?
81
+ changed.include?('ng_xml')
60
82
  end
61
83
 
62
84
  def method_missing(name, *args)
@@ -77,20 +99,13 @@ module OM::XML::Document
77
99
  super
78
100
  end
79
101
  end
80
-
81
-
82
102
  end
83
103
 
84
104
 
85
105
  def find_by_xpath(xpath)
86
- if ox_namespaces.values.compact.empty?
87
- ng_xml.xpath(xpath)
88
- else
89
- ng_xml.xpath(xpath, ox_namespaces)
90
- end
106
+ ng_xml.xpath(xpath, ox_namespaces)
91
107
  end
92
108
 
93
-
94
109
  # Applies the property's corresponding xpath query, returning the result Nokogiri::XML::NodeSet
95
110
  def find_by_terms_and_value(*term_pointer)
96
111
  xpath = self.class.terminology.xpath_for(*term_pointer)
@@ -176,7 +191,7 @@ module OM::XML::Document
176
191
  # Returns a hash combining the current documents namespaces (provided by nokogiri) and any namespaces that have been set up by your Terminology.
177
192
  # Most importantly, this matches the 'oxns' namespace to the namespace you provided in your Terminology's root term config
178
193
  def ox_namespaces
179
- @ox_namespaces ||= ng_xml.namespaces.merge(self.class.ox_namespaces)
194
+ @ox_namespaces ||= ng_xml.namespaces.merge(self.class.ox_namespaces).reject { |k,v| v.nil? || v.empty? }
180
195
  end
181
196
 
182
197
  private
@@ -185,6 +200,7 @@ module OM::XML::Document
185
200
  target = self.find_by_terms(*target)
186
201
  end
187
202
  raise "You must call define_template before calling #{method}" if template_registry.nil?
203
+ ng_xml_will_change!
188
204
  template_registry.send(method, target, *args, &block)
189
205
  end
190
206
  end
@@ -68,6 +68,7 @@ module OM
68
68
  end
69
69
 
70
70
  def val=(args)
71
+ @document.ng_xml_will_change!
71
72
  new_values = sanitize_new_values(args.first)
72
73
  new_values.keys.sort { |a,b| a.to_i <=> b.to_i }.each do |y|
73
74
  z = new_values[y]
@@ -79,21 +80,19 @@ module OM
79
80
  @document.term_value_update(xpath, y.to_i, z)
80
81
  end
81
82
  end
82
- if @document.respond_to?(:dirty=)
83
- @document.dirty = true
84
- end
85
83
  end
86
84
 
87
85
  def sanitize_new_values(new_values)
88
86
  # Sanitize new_values to always be a hash with indexes
89
87
  case new_values
90
88
  when Hash
89
+ new_values.each {|k, v| v = serialize(v) }
91
90
  when Array
92
91
  nv = new_values.dup
93
92
  new_values = {}
94
- nv.each {|v| new_values[nv.index(v).to_s] = v}
93
+ nv.each {|v| new_values[nv.index(v).to_s] = serialize(v)}
95
94
  else
96
- new_values = {"0"=>new_values}
95
+ new_values = {"0"=>serialize(new_values)}
97
96
  end
98
97
  new_values
99
98
  end
@@ -109,7 +108,34 @@ module OM
109
108
  def val
110
109
  query = xpath
111
110
  trim_text = !query.index("text()").nil?
112
- return @document.find_by_xpath(query).collect {|node| (trim_text ? node.text.strip : node.text) }
111
+ val = @document.find_by_xpath(query).collect {|node| (trim_text ? node.text.strip : node.text) }
112
+ deserialize(val)
113
+ end
114
+
115
+ # @param string
116
+ # @return [String,Date,Integer]
117
+ def deserialize(val)
118
+ case term.type
119
+ when :date
120
+ val.map { |v| Date.parse(v)}
121
+ when :integer
122
+ val.map { |v| v.to_i}
123
+ else
124
+ val
125
+ end
126
+ end
127
+
128
+ # @param val [String,Date,Integer]
129
+ def serialize (val)
130
+ case term.type
131
+ when :date
132
+ val.to_s
133
+ when :integer
134
+ val.to_s
135
+ else
136
+ val
137
+ end
138
+
113
139
  end
114
140
 
115
141
  def nodeset
@@ -117,6 +143,10 @@ module OM
117
143
  trim_text = !query.index("text()").nil?
118
144
  return @document.find_by_xpath(query)
119
145
  end
146
+
147
+ def delete
148
+ nodeset.delete
149
+ end
120
150
 
121
151
  def inspect
122
152
  val.inspect
@@ -13,7 +13,7 @@ class OM::XML::NamedTermProxy
13
13
  # @param [Hash] opts additional Term options
14
14
  def initialize(name, proxy_pointer, terminology, opts={})
15
15
  opts = {:namespace_prefix=>"oxns", :ancestors=>[], :children=>{}}.merge(opts)
16
- [:children, :ancestors].each do |accessor_name|
16
+ [:children, :ancestors, :index_as].each do |accessor_name|
17
17
  instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
18
18
  end
19
19
  @terminology = terminology
@@ -45,7 +45,14 @@ class OM::XML::NamedTermProxy
45
45
  return false
46
46
  end
47
47
 
48
- def method_missing
48
+ ##
49
+ # Always co-erce :index_as attributes into an Array
50
+ def index_as
51
+ if @index_as
52
+ Array(@index_as)
53
+ else
54
+ self.proxied_term.index_as
55
+ end
49
56
  end
50
57
 
51
58
  # Any unknown method calls will be proxied to the proxied term
data/lib/om/xml/term.rb CHANGED
@@ -135,6 +135,9 @@ class OM::XML::Term
135
135
  attr_accessor :name, :xpath, :xpath_constrained, :xpath_relative, :path, :index_as, :required, :data_type, :variant_of, :path, :default_content_path, :is_root_term
136
136
  attr_accessor :children, :internal_xml, :terminology
137
137
 
138
+ # the data type for this node
139
+ attr_accessor :type
140
+
138
141
  # Any XML attributes that qualify the Term.
139
142
  #
140
143
  # @example Declare a Term that has a given attribute (ie. //title[@xml:lang='eng'])
@@ -170,11 +173,22 @@ class OM::XML::Term
170
173
  # If you want to specify which namespace a term is using, use:
171
174
  # namspace_prefix => "bar"
172
175
  # This value defaults to nil, in which case if a default namespace is set in the termnology, that namespace will be used.
176
+ #
177
+ # @param name [Symbol] the name to refer to this term by
178
+ # @param opts [Hash]
179
+ # @options opts [Array] :index_as a list of indexing hints provided to to_solr
180
+ # @options opts [String] :path partial xpath that points to the node.
181
+ # @options opts [Hash] :attributes xml attributes to match in the selector
182
+ # @options opts [String] :namespace_prefix xml namespace for this node
183
+ # @options opts [Symbol] :type one of :string, :date, :integer. Defaults to :string
173
184
  def initialize(name, opts={}, terminology=nil)
174
185
  opts = {:ancestors=>[], :children=>{}}.merge(opts)
175
- [:children, :ancestors,:path, :index_as, :required, :type, :variant_of, :path, :attributes, :default_content_path, :namespace_prefix].each do |accessor_name|
186
+ [:children, :ancestors,:path, :index_as, :required, :variant_of, :path, :attributes, :default_content_path, :namespace_prefix].each do |accessor_name|
176
187
  instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
177
188
  end
189
+
190
+ self.type = opts[:type] || :string
191
+
178
192
  unless terminology.nil?
179
193
  if opts[:namespace_prefix].nil?
180
194
  unless terminology.namespaces["xmlns"].nil?
@@ -188,6 +202,7 @@ class OM::XML::Term
188
202
  end
189
203
  end
190
204
 
205
+
191
206
  def self.from_node(mapper_xml)
192
207
  name = mapper_xml.attribute("name").text.to_sym
193
208
  attributes = {}
@@ -211,6 +226,12 @@ class OM::XML::Term
211
226
  return new_mapper
212
227
  end
213
228
 
229
+ ##
230
+ # Always co-erce :index_as attributes into an Array
231
+ def index_as
232
+ Array(@index_as)
233
+ end
234
+
214
235
  # crawl down into mapper's children hash to find the desired mapper
215
236
  # ie. @test_mapper.retrieve_mapper(:conference, :role, :text)
216
237
  def retrieve_term(*pointers)