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)
|
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
|