Class: ActiveDocument::Base

Inherits:
Finder
  • Object
show all
Includes:
ClassLevelInheritableAttributes
Defined in:
lib/ActiveDocument/active_document.rb

Overview

Developers should extend this class to create their own domain classes


= Usage

Dynamic Finders

ActiveDocument::Base provides extensive methods for finding matching documents based on a variety of criteria.

Find By Element

Accessed via find_by_ELEMENT method where Element = the name of your element. Executes a search for all documents with an element ELEMENT that contains the value passed in to the method call. The signature of this dynamic finder is: find_by_ELEMENT(value, root [optional], element_namespace [optional], root_namespace [optional])

Parameters details are as follows: Value: the text to be found within the given element. This is a mandatory parameter Namespace: The namespace in which the element being searched occurs. This is an optional element. If provided, it will be used in the search and will override any default values. If no namespace if provided then the code will attempt to dynamically determine the namespace. First, if the element name is contained in the namespaces hash then that namespace is used. If the element name is not found then the default_namespace is used. If there is no default namespace, then no namespace is used.


== Dynamic Accessors

In addition to the ability to access the underlying XML document (as a Nokogiri XML Document) you have the ability to access the XML as attributes of your domain object via dynamic attribute accessors (eg. domain_object.element_name). Attribute accessors always return instances of ActiveDocument::ActiveDocument::PartialResult. This class works just like a regular ActiveDocument::ActiveDocument::Base object in that you access its members like regular properties.

NOTE: Ruby does NOT support hyphens in method names. Because of this if you have an element called, for example, version-number, you CAN’T do x.version-number to access the version-number element. To work around this problem substitute the word HYPHEN (all caps) for any - in your elements names. In the previous exmaple using x.versionHYPHENnumber will correctly resolve to the version-number element.

More complex dynamic accessors are also supported. Instead of just looking for an element anywhere in the document, you can be more specific. For example, domain_object.chapter.paragraph will find all paragraph elements that are children of chapter elements.


Direct Known Subclasses

PartialResult

Defined Under Namespace

Classes: PartialResult

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ClassLevelInheritableAttributes

included

Methods inherited from Finder

co_occurrence, config, execute_attribute_finder, execute_finder, search

Constructor Details

#initialize(xml_string = "", uri = "nil") ⇒ Base

create a new instance with an optional xml string to use for constructing the model



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ActiveDocument/active_document.rb', line 75

def initialize(xml_string = "", uri = "nil")
  @document = Nokogiri::XML(xml_string) do |config|
    config.noblanks
  end
  if !xml_string.empty? and self.class.my_root.nil? then
    @root = @document.root.name
  else
    @root = self.class.my_root
  end
  @uri = uri
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_id, *arguments, &block) ⇒ Object

enables the dynamic property accessors



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/ActiveDocument/active_document.rb', line 128

def method_missing(method_id, * arguments, & block)
  @@log.debug("ActiveDocument::Base at line #{__LINE__}: method called is #{method_id} with arguments #{arguments}")
  method = method_id.to_s
  method = method.sub("HYPHEN", "-")
  if method =~ /^(\w*-?\w*)$/ # methods with no '.' in them and not ending in '='
    if arguments.length > 0
      super
    end
    access_element $1
  elsif method =~ /^(\w*)=$/ && arguments.length == 1 # methods with no '.' in them and ending in '='
    set_element($1, arguments[0])
  else
    super
  end
end

Class Attribute Details

.default_namespace(prefix) ⇒ the default element namespace prefix (readonly)

defines the default namespace prefix to be used for all otherwise unspecified elements. The prefix should have already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime

Parameters:

  • prefix (the registered namespace prefix for all otherwise unspecified elements)

Returns:

  • (the default element namespace prefix)


230
231
232
# File 'lib/ActiveDocument/active_document.rb', line 230

def default_namespace
  @default_namespace
end

.namespaces(namespace_hash) ⇒ the resultant hash (readonly)

Sets the hash of elements to namespace prefixes. All prefixes should have already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime prefixes as strings]

Parameters:

  • namespace_hash (A hash where they keys are element names as strings and the values are namespace)

    amespace_hash [A hash where they keys are element names as strings and the values are namespace

Returns:

  • (the resultant hash)


174
175
176
# File 'lib/ActiveDocument/active_document.rb', line 174

def namespaces
  @namespaces
end

.root(root) ⇒ Object (readonly)

sets the root element of the document. If not set it will default to the classname



246
247
248
# File 'lib/ActiveDocument/active_document.rb', line 246

def root
  @root
end

Instance Attribute Details

#documentObject (readonly)

Returns the value of attribute document.



71
72
73
# File 'lib/ActiveDocument/active_document.rb', line 71

def document
  @document
end

#my_attribute_namespacesObject (readonly)

Returns the value of attribute my_attribute_namespaces.



71
72
73
# File 'lib/ActiveDocument/active_document.rb', line 71

def my_attribute_namespaces
  @my_attribute_namespaces
end

#my_default_attribute_namespacesObject (readonly)

Returns the value of attribute my_default_attribute_namespaces.



71
72
73
# File 'lib/ActiveDocument/active_document.rb', line 71

def my_default_attribute_namespaces
  @my_default_attribute_namespaces
end

#my_default_namespaceObject (readonly)

Returns the value of attribute my_default_namespace.



71
72
73
# File 'lib/ActiveDocument/active_document.rb', line 71

def my_default_namespace
  @my_default_namespace
end

#my_namespacesObject (readonly)

Returns the value of attribute my_namespaces.



71
72
73
# File 'lib/ActiveDocument/active_document.rb', line 71

def my_namespaces
  @my_namespaces
end

#rootObject (readonly)

Returns the root element for this object



110
111
112
# File 'lib/ActiveDocument/active_document.rb', line 110

def root
  @root
end

#uriObject (readonly)

Returns the value of attribute uri.



71
72
73
# File 'lib/ActiveDocument/active_document.rb', line 71

def uri
  @uri
end

Class Method Details

.add_attribute_namespace(attribute, prefix) ⇒ the resultant updated hash of attributes to namespace prefixes

Adds an attribute / namespace prefix pair to the existing hash. All prefixes should have already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime

Parameters:

  • attribute (The attribute)
  • prefix (the namespace prefix to be associated with the attribute)

Returns:

  • (the resultant updated hash of attributes to namespace prefixes)


204
205
206
207
# File 'lib/ActiveDocument/active_document.rb', line 204

def add_attribute_namespace(attribute, prefix)
  @my_attribute_namespaces[attribute.to_s] = prefix
  @my_attribute_namespaces
end

.add_namespace(element, prefix) ⇒ the resultant updated hash of elements to namespace prefixes

Adds an element / namespace prefix pair to the existing hash. All prefixes should have already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime

Parameters:

  • element (The element)
  • prefix (the namespace prefix to be associated with the element)

Returns:

  • (the resultant updated hash of elements to namespace prefixes)


194
195
196
197
# File 'lib/ActiveDocument/active_document.rb', line 194

def add_namespace(element, prefix)
  @my_namespaces[element.to_s] = prefix
  @my_namespaces
end

.attribute_namespaces(namespace_hash) ⇒ the resultant hash

Sets the hash of attributes to namespace prefixes. All prefixes should have already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime prefixes as strings]

Parameters:

  • namespace_hash (A hash where they keys are element names as strings and the values are namespace)

    amespace_hash [A hash where they keys are element names as strings and the values are namespace

Returns:

  • (the resultant hash)


184
185
186
187
# File 'lib/ActiveDocument/active_document.rb', line 184

def attribute_namespaces(namespace_hash)
  @my_attribute_namespaces = namespace_hash
  @my_attribute_namespaces
end

.default_attribute_namespace(prefix) ⇒ the default attribute namespace prefix

defines the default namespace prefix to be used for all otherwise unspecified attributes. The prefix should have already been registered with the framework via the ActiveDocument::DataBaseConfiguration class. Otherwise, errors will likely occur at runtime

Parameters:

  • prefix (the registered namespace prefix for all otherwise unspecified attributes)

Returns:

  • (the default attribute namespace prefix)


240
241
242
243
# File 'lib/ActiveDocument/active_document.rb', line 240

def default_attribute_namespace(prefix)
  @my_default_attribute_namespace = prefix
  @my_default_attribute_namespace
end

.delete(uri) ⇒ Object



255
256
257
258
259
260
261
262
263
264
# File 'lib/ActiveDocument/active_document.rb', line 255

def delete(uri)
  doc_uri = (uri || @uri)
  if doc_uri then
    response_array = ActiveDocument::CoronaInterface.delete(doc_uri)
    uri_array = response_array[:uri]
    @@ml_http.send_corona_request(uri_array[0], uri_array[1])
  else
    raise ArgumentError, "uri must not be nil", caller
  end
end

.find_by_word(word, root = @root, namespace = @my_default_namespace) ⇒ Object

Finds all documents of this type that contain the word anywhere in their structure



348
349
350
351
352
353
# File 'lib/ActiveDocument/active_document.rb', line 348

def find_by_word(word, root=@root, namespace=@my_default_namespace)
  response_array = ActiveDocument::CoronaInterface.find_by_word(word, root, namespace)
  uri_array = response_array[:uri]
  @@log.info("ActiveDocument.execute_find_by_word at line #{__LINE__}: #{response_array}")
  SearchResults.new(@@ml_http.send_corona_request(uri_array[0], uri_array[1], nil, response_array[:post_parameters]))
end

.load(uri) ⇒ Object

Returns an ActiveXML object representing the requested information. If no document exists at that uri then a LoadException is thrown



333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/ActiveDocument/active_document.rb', line 333

def load(uri)
  response_array = ActiveDocument::CoronaInterface.load(uri)
  uri_array = response_array[:uri]
  begin
    document = @@ml_http.send_corona_request(uri_array[0], uri_array[1])
  rescue Net::HTTPServerException => exception
    raise LoadException, "File #{uri} not found", caller
  end
  if document.empty?
    raise LoadException, "File #{uri} not found", caller
  end
  self.new(document, uri)
end

.method_missing(method_id, *arguments, &block) ⇒ Object

enables the dynamic finders



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/ActiveDocument/active_document.rb', line 267

def method_missing(method_id, * arguments, & block)
  @@log.debug("ActiveDocument::Base at line #{__LINE__}: method called is #{method_id} with arguments #{arguments}")
  method = method_id.to_s
  # identify attribute search methods
  if method =~ /find_by_attribute_(.*)$/ and arguments.length >= 2
    attribute = $1.to_sym
    element = arguments[0]
    value = arguments[1]
    if arguments[2]
      root = arguments[2]
    else
      root = @root || self.class.name
    end
    if arguments[3]
      element_namespace = arguments[3]
    else
      element_namespace = namespace_for_element(element)
    end
    if arguments[4]
      attribute_namespace = arguments[4]
    else
      attribute_namespace = namespace_for_attribute(attribute)
    end
    if arguments[5]
      root_namespace = arguments[5]
    else
      root_namespace = namespace_for_element(root)
    end
    if arguments[6]
      options = arguments[6]
    else
      options = nil
    end
    execute_attribute_finder(element, attribute, value, root, element_namespace, attribute_namespace, root_namespace, options)
  elsif method =~ /find_by_(.*)$/ and arguments.length > 0 # identify element search methods
    value = arguments[0]
    element = $1 # todo: this used to be converted to a symbol. Make sure that keeping it as a string doesn't break something
    if arguments[1]
      root = arguments[1]
    else
      root = @root
    end
    if arguments[2]
      element_namespace = arguments[2]
    else
      element_namespace = namespace_for_element(element)
    end
    if arguments[3]
      root_namespace = arguments[3]
    else
      root_namespace = namespace_for_element(root) unless root.nil?
    end
    if arguments[4]
      options = arguments[4]
    else
      options = nil
    end
    execute_finder(element, value, root, element_namespace, root_namespace, options)
  else
    super
  end

end

.my_rootObject



250
251
252
# File 'lib/ActiveDocument/active_document.rb', line 250

def my_root
  @root
end

.namespace_for_attribute(attribute) ⇒ Object



158
159
160
161
162
163
164
165
166
# File 'lib/ActiveDocument/active_document.rb', line 158

def namespace_for_attribute(attribute)
  namespace = nil
  if !@my_attribute_namespaces.nil? && @my_attribute_namespaces[attribute.to_sym]
    namespace = @my_attribute_namespaces[attribute.to_sym]
  else
    namespace = @my_default_attribute_namespace unless @my_default_attribute_namespace.nil?
  end
  namespace
end

.namespace_for_element(element) ⇒ Object



148
149
150
151
152
153
154
155
156
# File 'lib/ActiveDocument/active_document.rb', line 148

def namespace_for_element(element)
  namespace = nil
  if !@my_namespaces.nil? && @my_namespaces[element.to_sym]
    namespace = @my_namespaces[element.to_sym]
  else
    namespace = @my_default_namespace unless @my_default_namespace.nil?
  end
  namespace
end

.remove_attribute_namespace(attribute) ⇒ the resultant updated hash of attributes to namespace prefixes

Removes an attribute / namespace prefix pair from the existing hash. #todo what about the corona config?

Parameters:

  • attribute (the attribute to be removed)

    ]

Returns:

  • (the resultant updated hash of attributes to namespace prefixes)


220
221
222
223
# File 'lib/ActiveDocument/active_document.rb', line 220

def remove_attribute_namespace(attribute)
  @my_attribute_namespaces.delete attribute.to_s
  @my_attribute_namespaces
end

.remove_namespace(element) ⇒ the resultant updated hash of elements to namespace prefixes

Removes an element / namespace prefix pair from the existing hash. #todo what about the corona config?

Parameters:

  • element (the element to be removed)

    ]

Returns:

  • (the resultant updated hash of elements to namespace prefixes)


212
213
214
215
# File 'lib/ActiveDocument/active_document.rb', line 212

def remove_namespace(element)
  @my_namespaces.delete element.to_s
  @my_namespaces
end

Instance Method Details

#[](key) ⇒ Object



114
115
116
117
118
119
120
121
# File 'lib/ActiveDocument/active_document.rb', line 114

def [](key)
  namespace = namespace_for_element(key)
  if namespace.nil? || namespace.empty?
    @document.root.xpath("@#{key}").to_s
  else
    @document.root.xpath("@ns:#{key}", {'ns' => namespace}).to_s
  end
end

#[]=(key, value) ⇒ Object



123
124
125
# File 'lib/ActiveDocument/active_document.rb', line 123

def []=(key, value)
  set_attribute(key, value)
end

#save(uri = nil) ⇒ Object

saves this document to the repository. If uri is provided then that will be the value used for the uri. If no uri was passed in then the existing value or the uri is used, unless uri is nil in which case an exception will be thrown



98
99
100
101
102
103
104
105
106
107
# File 'lib/ActiveDocument/active_document.rb', line 98

def save(uri = nil)
  doc_uri = (uri || @uri)
  if doc_uri then
    response_array = ActiveDocument::CoronaInterface.save(doc_uri)
    uri_array = response_array[:uri]
    @@ml_http.send_corona_request(uri_array[0], uri_array[1], self.document.to_s)
  else
    raise ArgumentError, "uri must not be nil", caller
  end
end

#to_sObject



87
88
89
90
91
92
93
# File 'lib/ActiveDocument/active_document.rb', line 87

def to_s
  if @document
    @document.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
  else
    super.to_s
  end
end