om 1.6.1 → 1.7.0.rc1

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