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)
|
|
54
|
-
#
|
|
55
|
-
#
|
|
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.
|
|
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 =
|
|
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
|
|
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
|
|
175
|
-
if method =~ /
|
|
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
|
|
199
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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))
|
|
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 =
|
|
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
|
|
57
|
-
xquery =
|
|
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 <<
|
|
70
|
-
<constraint name="word">
|
|
71
|
-
<word>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
</
|
|
75
|
-
|
|
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
|
-
-
|
|
8
|
-
|
|
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-
|
|
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
|