activedocument 0.4.2 → 0.5

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.
@@ -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