activedocument 0.4.2 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -50,25 +50,20 @@ module ActiveDocument
50
50
  # -------------------
51
51
  # == Dynamic Accessors
52
52
  # In addition to the ability to access the underlying XML document (as a Nokogiri XML Document) you have the ability
53
- # to access the XML as attributes of your domain object via dynamic attribute accessors (eg. domain_object.element_name) The rules for using accessors
54
- # are as follows:
55
- # 1. If the element_name is a simple type (i.e. text node with no children <tt><example>text</exmample></tt>)
56
- # 1. If there is only one occurence of the element, return its text value
57
- # 2. If there are multiple occurences of the element, return a list of each text value
58
- # 2. If the element_name is a complex type (e.g. <tt><example><text>hi</text></example></tt>)
59
- # 1. If there is only one ocurence of the element then return it as a Nokoguri Element
60
- # 2. If there are multiple occurences of the element, return a list of Nokoguri Elements
53
+ # to access the XML as attributes of your domain object via dynamic attribute accessors (eg. domain_object.element_name).
54
+ # Attribute accessors always return instances of ActiveDocument::ActiveDocument::PartialResult. This class works just
55
+ # like a regular ActiveDocument::ActiveDocument::Base object in that you access its members like regular properties.
61
56
  #
62
- # More complex dynamic accessors are also supported. They still adhere to the rules above, but instead of just looking
57
+ # More complex dynamic accessors are also supported. Instead of just looking
63
58
  # for an element anywhere in the document, you can be more specific. For example, domain_object.chapter.paragraph
64
59
  # will find all paragraph elements that are children of chapter elements.
65
60
  # -------------------
66
61
  class Base < Finder
67
62
  include ClassLevelInheritableAttributes
68
- inheritable_attributes_list :my_namespaces, :my_default_namespace, :root
63
+ inheritable_attributes_list :my_namespaces, :my_default_namespace, :root, :my_attribute_namespaces, :my_default_attribute_namespaces
69
64
  @my_namespaces = Hash.new
70
- @my_default_namespace = String.new
71
- attr_reader :document, :uri, :my_namespaces, :my_default_namespace, :root
65
+ @my_default_namespace = nil
66
+ attr_reader :document, :uri, :my_namespaces, :my_default_namespace, :root, :my_attribute_namespaces, :my_default_attribute_namespaces
72
67
 
73
68
 
74
69
  # create a new instance with an optional xml string to use for constructing the model
@@ -106,7 +101,7 @@ module ActiveDocument
106
101
 
107
102
  def [](key)
108
103
  namespace = namespace_for_element(key)
109
- if namespace.empty?
104
+ if namespace.nil? || namespace.empty?
110
105
  @document.root.xpath("@#{key}").to_s
111
106
  else
112
107
  @document.root.xpath("@ns:#{key}", {'ns' => namespace}).to_s
@@ -117,7 +112,7 @@ module ActiveDocument
117
112
  set_attribute(key, value)
118
113
  end
119
114
 
120
- # enables the dynamic finders
115
+ # enables the dynamic property accessors
121
116
  def method_missing(method_id, * arguments, & block)
122
117
  @@log.debug("ActiveDocument::Base at line #{__LINE__}: method called is #{method_id} with arguments #{arguments}")
123
118
  method = method_id.to_s
@@ -147,22 +142,52 @@ module ActiveDocument
147
142
  namespace
148
143
  end
149
144
 
145
+ def namespace_for_attribute(attribute)
146
+ namespace = nil
147
+ if !@my_attribute_namespaces.nil? && @my_attribute_namespaces[attribute]
148
+ namespace = @my_attribute_namespaces[attribute]
149
+ else
150
+ namespace = @my_default_attribute_namespace unless @my_default_attribute_namespace.nil?
151
+ end
152
+ namespace
153
+ end
154
+
150
155
  def namespaces(namespace_hash)
151
156
  @my_namespaces = namespace_hash
152
157
  end
153
158
 
159
+ def attribute_namespaces(namespace_hash)
160
+ @my_attribute_namespaces = namespace_hash
161
+ end
162
+
154
163
  def add_namespace(element, uri)
155
164
  @my_namespaces[element.to_s] == uri
156
165
  end
157
166
 
167
+ def add_attribute_namespace(attribute, uri)
168
+ @my_attribute_namespaces[attribute.to_s] == uri
169
+ end
170
+
158
171
  def remove_namespace(element)
159
172
  @my_namespaces.delete element
160
173
  end
161
174
 
175
+ def remove_attribute_namespace(attribute)
176
+ @my_attribute_namespaces.delete attribute
177
+ end
178
+
162
179
  def default_namespace(namespace)
163
180
  @my_default_namespace = namespace # todo should this just be an entry in namespaces?
164
181
  end
165
182
 
183
+ def root(root)
184
+ @root = root
185
+ end
186
+
187
+ def default_attribute_namespace(namespace)
188
+ @my_default_attribute_namespace = namespace # todo should this just be an entry in namespaces?
189
+ end
190
+
166
191
  def delete(uri)
167
192
  @@ml_http.send_xquery(@@xquery_builder.delete(uri))
168
193
  end
@@ -171,8 +196,33 @@ module ActiveDocument
171
196
  def method_missing(method_id, * arguments, & block)
172
197
  @@log.debug("ActiveDocument::Base at line #{__LINE__}: method called is #{method_id} with arguments #{arguments}")
173
198
  method = method_id.to_s
174
- # identify element search methods
175
- if method =~ /find_by_(.*)$/ and arguments.length > 0
199
+ # identify attribute search methods
200
+ if method =~ /find_by_attribute_(.*)$/ and arguments.length >= 2
201
+ attribute = $1.to_sym
202
+ element = arguments[0]
203
+ value = arguments[1]
204
+ if arguments[2]
205
+ root = arguments[2]
206
+ else
207
+ root = @root || self.class.name
208
+ end
209
+ if arguments[3]
210
+ element_namespace = arguments[3]
211
+ else
212
+ element_namespace = namespace_for_element(element)
213
+ end
214
+ if arguments[4]
215
+ attribute_namespace = arguments[4]
216
+ else
217
+ attribute_namespace = namespace_for_attribute(attribute)
218
+ end
219
+ if arguments[5]
220
+ root_namespace = arguments[5]
221
+ else
222
+ root_namespace = namespace_for_element(root)
223
+ end
224
+ execute_attribute_finder(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace)
225
+ elsif method =~ /find_by_(.*)$/ and arguments.length > 0 # identify element search methods
176
226
  value = arguments[0]
177
227
  element = $1.to_sym
178
228
  if arguments[1]
@@ -195,21 +245,25 @@ module ActiveDocument
195
245
 
196
246
  end
197
247
 
198
- # Returns an ActiveXML object representing the requested information. If no document exists at that uri then an
199
- # empty domain object is created and returned
248
+ # Returns an ActiveXML object representing the requested information. If no document exists at that uri then
249
+ # a LoadException is thrown
200
250
  def load(uri)
201
- self.new(@@ml_http.send_xquery(@@xquery_builder.load(uri)), uri)
251
+ document = @@ml_http.send_xquery(@@xquery_builder.load(uri))
252
+ if document.empty?
253
+ raise LoadException, "File #{uri} not found", caller
254
+ end
255
+ self.new(document, uri)
202
256
  end
203
257
 
204
258
  # Finds all documents of this type that contain the word anywhere in their structure
205
259
  def find_by_word(word, root=@root, namespace=@my_default_namespace)
206
- SearchResults.new(@@ml_http.send_xquery(@@xquery_builder.find_by_word(word, root, namespace)))
260
+ xquery = @@xquery_builder.find_by_word(word, root, namespace)
261
+ @@log.info("ActiveDocument.execute_find_by_word at line #{__LINE__}: #{xquery}")
262
+ SearchResults.new(@@ml_http.send_xquery(xquery))
207
263
  end
208
264
 
209
-
210
265
  end # end inner class
211
266
 
212
-
213
267
  class PartialResult < self
214
268
  include Enumerable
215
269
  # todo should this contain a reference to its parent?
@@ -244,8 +298,8 @@ module ActiveDocument
244
298
  @document.text
245
299
  end
246
300
 
247
- def each(&block)
248
- @document.each(&block)
301
+ def each(& block)
302
+ @document.each(& block)
249
303
  end
250
304
 
251
305
  end
@@ -296,7 +350,7 @@ module ActiveDocument
296
350
 
297
351
  def set_attribute(attribute, value)
298
352
  namespace = namespace_for_element(attribute)
299
- node = if namespace.empty?
353
+ node = if namespace.nil? || namespace.empty?
300
354
  @document.xpath("@#{attribute}")
301
355
  else
302
356
  @document.xpath("@ns:#{attribute}", {'ns' => namespace})
@@ -330,5 +384,9 @@ module ActiveDocument
330
384
 
331
385
  end
332
386
 
387
+ class LoadException < PersistenceException
388
+
389
+ end
390
+
333
391
 
334
392
  end # end module
@@ -37,7 +37,11 @@ module ActiveDocument
37
37
  @@log.debug("ActiveDocument::Finder at line #{__LINE__}: method called is #{method_id} with arguments #{arguments}")
38
38
  method = method_id.to_s
39
39
  # identify finder methods
40
- if method =~ /find_by_(.*)$/ and arguments.length > 0
40
+ # todo verify these parameters
41
+ if method =~ /find_by_attribute_(.*)$/ and arguments.length > 0
42
+ namespace = arguments[1] if arguments.length == 2
43
+ execute_attribute_finder($1.to_sym, arguments[0], nil, namespace)
44
+ elsif method =~ /find_by_(.*)$/ and arguments.length > 0
41
45
  namespace = arguments[1] if arguments.length == 2
42
46
  execute_finder($1.to_sym, arguments[0], nil, namespace)
43
47
  else
@@ -51,11 +55,17 @@ module ActiveDocument
51
55
  SearchResults.new(@@ml_http.send_xquery(xquery))
52
56
  end
53
57
 
58
+ def self.execute_attribute_finder(element, attribute, value, root = nil, element_namespace = nil, attribute_namespace = nil, root_namespace = nil)
59
+ xquery = @@xquery_builder.find_by_attribute(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace)
60
+ @@log.info("Finder.execute_attribute_finder at line #{__LINE__}: #{xquery}")
61
+ SearchResults.new(@@ml_http.send_xquery(xquery))
62
+ end
63
+
54
64
  def self.search(search_string, start = 1, page_length = 10, options = nil)
55
65
  start ||= 1
56
66
  page_length ||= 10
57
67
  search_text = @@xquery_builder.search(search_string, start, page_length, options)
58
- SearchResults.new(@@ml_http.send_xquery(search_text)) # todo support options node
68
+ SearchResults.new(@@ml_http.send_xquery(search_text))
59
69
  end
60
70
 
61
71
  # returns a hash where the key is the terms of the co-occurrence separated by a | and the value is the frequency count
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  module ActiveDocument
16
-
16
+ # todo create new unit tests for this class - the old ones were no good
17
17
  class MarkLogicQueryBuilder
18
18
 
19
19
  def load(uri)
@@ -32,16 +32,16 @@ module ActiveDocument
32
32
  xdmp:default-permissions(),
33
33
  xdmp:default-collections())
34
34
  GENERATED
35
-
35
+
36
36
  end
37
37
 
38
38
  # This method does a full text search
39
39
  def find_by_word(word, root, namespace)
40
- xquery = <<GENERATED
41
- import module namespace search = "http://marklogic.com/appservices/search"at "/MarkLogic/appservices/search/search.xqy";
42
- search:search("#{word}",
43
- <options xmlns="http://marklogic.com/appservices/search">
44
- GENERATED
40
+ xquery = <<-GENERATED
41
+ import module namespace search = "http://marklogic.com/appservices/search"at "/MarkLogic/appservices/search/search.xqy";
42
+ search:search("#{word}",
43
+ <options xmlns="http://marklogic.com/appservices/search">
44
+ GENERATED
45
45
  unless root.nil?
46
46
  xquery << "<searchable-expression"
47
47
 
@@ -53,12 +53,35 @@ GENERATED
53
53
  xquery << "</options>)"
54
54
  end
55
55
 
56
- def find_by_element(element, value, root = nil, element_namespace = nil, root_namespace = nil)
57
- xquery = <<GENERATED
58
- import module namespace search = "http://marklogic.com/appservices/search"at "/MarkLogic/appservices/search/search.xqy";
59
- search:search("word:#{value}",
60
- <options xmlns="http://marklogic.com/appservices/search">
61
- GENERATED
56
+ def find_by_element(element, value, root, element_namespace, root_namespace)
57
+ xquery = <<-GENERATED
58
+ import module namespace search = "http://marklogic.com/appservices/search"at "/MarkLogic/appservices/search/search.xqy";
59
+ search:search("word:#{value}",
60
+ <options xmlns="http://marklogic.com/appservices/search">
61
+ GENERATED
62
+ unless root.nil?
63
+ xquery << "<searchable-expression"
64
+ xquery << " xmlns:a=\"#{root_namespace}\"" unless root_namespace.nil?
65
+ xquery << '>/'
66
+ xquery << "a:" unless root_namespace.nil?
67
+ xquery << "#{root}</searchable-expression>"
68
+ end
69
+ xquery << <<-CONSTRAINT
70
+ <constraint name="word">
71
+ <word>
72
+ <element ns="#{element_namespace unless element_namespace.nil?}" name="#{element}"/>
73
+ </word>
74
+ </constraint></options>)
75
+ CONSTRAINT
76
+ end
77
+
78
+ def find_by_attribute(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace)
79
+ # todo should the searchable expression portion be refactored?
80
+ xquery = <<-GENERATED
81
+ import module namespace search = "http://marklogic.com/appservices/search"at "/MarkLogic/appservices/search/search.xqy";
82
+ search:search("word:#{value}",
83
+ <options xmlns="http://marklogic.com/appservices/search">
84
+ GENERATED
62
85
  unless root.nil?
63
86
  xquery << "<searchable-expression"
64
87
  xquery << " xmlns:a=\"#{root_namespace}\"" unless root_namespace.nil?
@@ -66,13 +89,14 @@ GENERATED
66
89
  xquery << "a:" unless root_namespace.nil?
67
90
  xquery << "#{root}</searchable-expression>"
68
91
  end
69
- xquery << <<CONSTRAINT
70
- <constraint name="word">
71
- <word>
72
- <element ns="#{element_namespace unless element_namespace.nil?}" name="#{element}"/>
73
- </word>
74
- </constraint></options>)
75
- CONSTRAINT
92
+ xquery << <<-CONSTRAINT
93
+ <constraint name="word">
94
+ <word>
95
+ <attribute ns="#{attribute_namespace unless attribute_namespace.nil?}" name="#{attribute}"/>
96
+ <element ns="#{element_namespace unless element_namespace.nil?}" name="#{element}"/>
97
+ </word>
98
+ </constraint></options>)
99
+ CONSTRAINT
76
100
  end
77
101
 
78
102
  def search(search_text, start, page_length, options)
metadata CHANGED
@@ -4,9 +4,8 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 4
8
- - 2
9
- version: 0.4.2
7
+ - 5
8
+ version: "0.5"
10
9
  platform: ruby
11
10
  authors:
12
11
  - Clark D. Richey, Jr.
@@ -14,7 +13,7 @@ autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
15
 
17
- date: 2010-07-21 00:00:00 -07:00
16
+ date: 2010-09-22 00:00:00 -07:00
18
17
  default_executable:
19
18
  dependencies:
20
19
  - !ruby/object:Gem::Dependency
@@ -40,7 +39,6 @@ extensions: []
40
39
  extra_rdoc_files: []
41
40
 
42
41
  files:
43
- - lib/active_document.rb
44
42
  - lib/ActiveDocument/active_document.rb
45
43
  - lib/ActiveDocument/finder.rb
46
44
  - lib/ActiveDocument/inheritable.rb
@@ -50,6 +48,7 @@ files:
50
48
  - lib/ActiveDocument/search_match.rb
51
49
  - lib/ActiveDocument/search_result.rb
52
50
  - lib/ActiveDocument/search_results.rb
51
+ - lib/active_document.rb
53
52
  - lib/activedocument.rb
54
53
  has_rdoc: true
55
54
  homepage: http://github.com/crichey/ActiveDocument