Class: TinkitBaseNode
- Inherits:
-
Object
- Object
- TinkitBaseNode
- Defined in:
- lib/tinkit_base_node.rb
Constant Summary collapse
Class Attribute Summary collapse
-
.data_struc ⇒ Object
uppercased to highlight its supporting the class.
-
.metadata_keys ⇒ Object
uppercased to highlight its supporting the class.
-
.myGlueEnv ⇒ Object
uppercased to highlight its supporting the class.
Instance Attribute Summary collapse
-
#_files_mgr ⇒ Object
Instance Accessors.
-
#_model_metadata ⇒ Object
Instance Accessors.
-
#_user_data ⇒ Object
Instance Accessors.
-
#attached_files ⇒ Object
Instance Accessors.
-
#my_GlueEnv ⇒ Object
Instance Accessors.
Class Method Summary collapse
-
.__create_from_other_node(other_node) ⇒ Object
Create the document in the BUFS node format from an existing node.
-
.all(data_structure_changes = {}) ⇒ Object
TODO: Add the very cool feature to spec (creating new fields on the fly) TODO: Document the feature too!!.
-
.all_native_records ⇒ Object
Collection Methods This returns all records, but does not create an instance of this class for each record.
-
.attachment_base_id ⇒ Object
Returns the id that will be appended to the document ID to uniquely identify attachment documents associated with the main document TODO: NOT COMPLETELY ABSTRACTED YET.
-
.call_new_view(view_name, match_key) ⇒ Object
Not implemented on all persistence layers yet (just couchrest and filesystem).
-
.call_view(param, match_keys, data_structure_changes = {}) ⇒ Object
Not implemented on all persistence layers yet (just couchrest and filesystem) may be deprecated.
-
.destroy_all ⇒ Object
This destroys all nodes in the model this is more efficient than calling destroy on instances of this class as it avoids instantiating only to destroy it.
- .find_nodes_where(key, relation, this_value) ⇒ Object
- .get(id) ⇒ Object
- .modify_data_structures(base_data, changes) ⇒ Object
-
.set_environment(persist_env, data_model_bindings) ⇒ Object
Class Methods Setting up the Class Environment - The class environment holds all model-specific implementation details (not used when created by factory?).
Instance Method Summary collapse
-
#__destroy_node ⇒ Object
Deletes the object.
- #__export_attachment(attachment_name) ⇒ Object
- #__get_attachment_metadata(attachment_name) ⇒ Object
- #__get_attachments_metadata ⇒ Object
- #__import_attachment(attach_name, att_xfer_format) ⇒ Object
-
#__method_wrapper(param, unbound_op) ⇒ Object
TODO: Method Wrapper is not sufficiently tested The method operations are completely decoupled from the object that they are bound to.
-
#__save ⇒ Object
Save the object to the CouchDB database.
-
#__set_userdata_key(attr_var, attr_value) ⇒ Object
This will take a key-value pair and create an instance variable (actually it’s a method)using key as the method name, and sets the return value to the value associated with that key changes to the key’s value are reflected in subsequent method calls, and the value can be updated by using method_name = some value.
- #__unset_userdata_key(param) ⇒ Object
-
#add_parent_categories(new_cats) ⇒ Object
Deprecated Methods———————— Adds parent categories, it can accept a single category or an array of categories aliased for backwards compatibility, this method is dynamically defined and generated.
-
#add_raw_data(attach_name, content_type, raw_data, file_modified_at = nil) ⇒ Object
Get attachment content.
- #files_add(file_datas) ⇒ Object
- #files_remove_all ⇒ Object
- #files_subtract(file_basenames) ⇒ Object
-
#get_file_data(attachment_name) ⇒ Object
def attachment_url(attachment_name) current_node_doc = self.class.get(self) att_doc_id = current_node_doc current_node_attachment_doc = self.class.user_attachClass.get(att_doc_id) current_node_attachment_doc.attachment_url(attachment_name) end.
- #get_raw_data(attachment_name) ⇒ Object
-
#initialize(init_params = {}) ⇒ TinkitBaseNode
constructor
Normal instantiation can take two forms that differ only in the source for the initial parameters.
- #method_missing(meth_sym, *args, &block) ⇒ Object
- #raise_method_missing(meth_sym, *args) ⇒ Object
-
#remove_parent_categories(cats_to_remove) ⇒ Object
Can accept a single category or an array of categories aliased for backwards compatiblity the method is dynamically defined and generated.
Constructor Details
#initialize(init_params = {}) ⇒ TinkitBaseNode
Normal instantiation can take two forms that differ only in the source for the initial parameters. The constructor could be called by the user and passed only user data, or the constructor could be called by a class collection method and the initial parameters would come from a datastore. In the latter case, some of the parameters will include information about the datastore (model metadata).
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/tinkit_base_node.rb', line 345 def initialize(init_params = {}) #setting the class accessor to also be an instance accessor #for convenience and hopefully doesn't create confusion @my_GlueEnv = self.class.myGlueEnv @@log.debug {"initializing with: #{init_params.inspect}"} if @@log.debug? raise "init_params cannot be nil" unless init_params @saved_to_model = nil #TODO rename to sychronized_to_model #make sure keys are symbols init_params = HashKeys.str_to_sym(init_params) @_user_data, @_model_metadata = filter_user_from_model_data(init_params) @@log.debug {"data filtered into user data: #{@_user_data}"} if @@log.debug? @@log.debug {"data filtered into model metadata: #{@_model_metadata}"} if @@log.debug? instance_data_validations(@_user_data) node_key = get__user_data_id(@_user_data) moab_file_mgr = @my_GlueEnv._files_mgr_class.new(@my_GlueEnv, node_key) @_files_mgr = FilesMgr.new(moab_file_mgr) @_model_metadata = (@_model_metadata, node_key) @@log.debug {"Updated model metadata: #{@_model_metadata.inspect}"} if @@log.debug? init_params.each do |attr_name, attr_value| __set_userdata_key(attr_name.to_sym, attr_value) end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth_sym, *args, &block) ⇒ Object
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 |
# File 'lib/tinkit_base_node.rb', line 610 def method_missing(meth_sym, *args, &block) meth_str = meth_sym.to_s raise_method_missing(meth_sym, args) unless @_user_data @@log.debug { "User Data (methods generated from these keys): #{@_user_data.inspect}"} if @@log.debug? return_value = "method_not_found_here" @_user_data.keys.each do |existing_methods_base| meth_regex_str ="^#{existing_methods_base}_" meth_regex = Regexp.new(meth_regex_str) if meth_str.match(meth_regex) return_value = @_user_data[existing_methods_base] break end end if return_value == "method_not_found_here" raise_method_missing(meth_sym, *args) else puts "Warning: Method #{meth_sym.inspect} not defined for all fields\ returning value of the field in those cases" return return_value end end |
Class Attribute Details
.data_struc ⇒ Object
uppercased to highlight its supporting the class
163 164 165 |
# File 'lib/tinkit_base_node.rb', line 163 def data_struc @data_struc end |
.metadata_keys ⇒ Object
uppercased to highlight its supporting the class
163 164 165 |
# File 'lib/tinkit_base_node.rb', line 163 def @metadata_keys end |
.myGlueEnv ⇒ Object
uppercased to highlight its supporting the class
163 164 165 |
# File 'lib/tinkit_base_node.rb', line 163 def myGlueEnv @myGlueEnv end |
Instance Attribute Details
#_files_mgr ⇒ Object
Instance Accessors
169 170 171 |
# File 'lib/tinkit_base_node.rb', line 169 def _files_mgr @_files_mgr end |
#_model_metadata ⇒ Object
Instance Accessors
169 170 171 |
# File 'lib/tinkit_base_node.rb', line 169 def @_model_metadata end |
#_user_data ⇒ Object
Instance Accessors
169 170 171 |
# File 'lib/tinkit_base_node.rb', line 169 def _user_data @_user_data end |
#attached_files ⇒ Object
Instance Accessors
169 170 171 |
# File 'lib/tinkit_base_node.rb', line 169 def attached_files @attached_files end |
#my_GlueEnv ⇒ Object
Instance Accessors
169 170 171 |
# File 'lib/tinkit_base_node.rb', line 169 def my_GlueEnv @my_GlueEnv end |
Class Method Details
.__create_from_other_node(other_node) ⇒ Object
Create the document in the BUFS node format from an existing node.
318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/tinkit_base_node.rb', line 318 def self.__create_from_other_node(other_node) #TODO:Figure out data structure imports #Idea, for duplicates, this node takes precedence #for new data structures, other node operations (if they exist) are used #Not implemented yet, though #TODO: add to spec #TODO: what about node id collisions? currently ignoring it #and letting the persistence model work it out this_node = self.new(other_node._user_data) this_node.__save this_node.(other_node.) if other_node.attached_files end |
.all(data_structure_changes = {}) ⇒ Object
TODO: Add the very cool feature to spec (creating new fields on the fly) TODO: Document the feature too!!
228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/tinkit_base_node.rb', line 228 def self.all(data_structure_changes = {}) #add_keys = data_structure_changes[:add] #remove_keys = data_structure_changes[:remove] #TODO: test for proper format raw_nodes = @myGlueEnv.raw_all raw_nodes.map! do |base_data| combined_data = self.modify_data_structures(base_data, data_structure_changes) self.new(combined_data) end raw_nodes end |
.all_native_records ⇒ Object
Collection Methods This returns all records, but does not create an instance of this class for each record. Each record is provided in its native form.
222 223 224 |
# File 'lib/tinkit_base_node.rb', line 222 def self.all_native_records @myGlueEnv.query_all end |
.attachment_base_id ⇒ Object
Returns the id that will be appended to the document ID to uniquely identify attachment documents associated with the main document TODO: NOT COMPLETELY ABSTRACTED YET
334 335 336 |
# File 'lib/tinkit_base_node.rb', line 334 def self. @myGlueEnv. end |
.call_new_view(view_name, match_key) ⇒ Object
Not implemented on all persistence layers yet (just couchrest and filesystem)
251 252 253 254 255 256 257 258 259 260 |
# File 'lib/tinkit_base_node.rb', line 251 def self.call_new_view(view_name, match_key) results = if @myGlueEnv.respond_to? :call_view @myGlueEnv.call_view(view_name, @myGlueEnv.moab_data, @myGlueEnv.namespace_key, @myGlueEnv.user_datastore_location, match_key) end results end |
.call_view(param, match_keys, data_structure_changes = {}) ⇒ Object
Not implemented on all persistence layers yet (just couchrest and filesystem) may be deprecated
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/tinkit_base_node.rb', line 277 def self.call_view(param, match_keys, data_structure_changes = {}) view_method_name = "by_#{param}".to_sym #using CouchDB style for now records = if @myGlueEnv.views.respond_to? view_method_name @myGlueEnv.views.__send__(view_method_name, @myGlueEnv.moab_data, @myGlueEnv.user_datastore_location, match_keys) else #TODO: Think of a more elegant way to handle an unknown view raise "Unknown design view #{view_method_name} called for: #{param}" end nodes = [] records.map do |base_data| if base_data combined_data = self.modify_data_structures(base_data, data_structure_changes) nodes << self.new(combined_data) end end return nodes end |
.destroy_all ⇒ Object
This destroys all nodes in the model this is more efficient than calling destroy on instances of this class as it avoids instantiating only to destroy it
312 313 314 315 |
# File 'lib/tinkit_base_node.rb', line 312 def self.destroy_all all_records = self.all_native_records @myGlueEnv.destroy_bulk(all_records) end |
.find_nodes_where(key, relation, this_value) ⇒ Object
262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/tinkit_base_node.rb', line 262 def self.find_nodes_where(key, relation, this_value) records = @myGlueEnv.find_nodes_where(key, relation, this_value) nodes = [] records.map do |base_data| if base_data #combined_data = self.modify_data_structures(base_data, data_structure_changes) #nodes << self.new(combined_data) nodes << self.new(base_data) end end return nodes end |
.get(id) ⇒ Object
299 300 301 302 303 304 305 306 |
# File 'lib/tinkit_base_node.rb', line 299 def self.get(id) data = @myGlueEnv.get(id) rtn = if data self.new(data) else nil end end |
.modify_data_structures(base_data, changes) ⇒ Object
243 244 245 246 247 248 |
# File 'lib/tinkit_base_node.rb', line 243 def self.modify_data_structures(base_data, changes) add_keys_values = changes[:add]||{} remove_keys = changes[:remove]||[] #note its an array removed_data = base_data.delete_if {|k,v| remove_keys.include?(k)} added_data = add_keys_values.merge(removed_data) #so that add doesn't overwrite existing keys end |
.set_environment(persist_env, data_model_bindings) ⇒ Object
Class Methods Setting up the Class Environment - The class environment holds all model-specific implementation details (not used when created by factory?)
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/tinkit_base_node.rb', line 188 def self.set_environment(persist_env, data_model_bindings) class_name = persist_env[:name] model_name = class_name model_env = persist_env[:env] #key_fields = data_model_bindings[:key_fields] #initial_views_data = data_model_bindings[:data_ops_set] #dynamically determine what's needed glue_file_name = "#{model_name}_glue_env" #moab_file_name = "moab_#{model_name}_env" #dynamic require (maybe just keep this static?) require Tinkit.glue glue_file_name #require Tinkit.moabs moab_file_name glue_lc_name = "#{model_name}_env" glue_const_name = Camel.ize(glue_lc_name) glueModule = Object.const_get(glue_const_name) glueClass = glueModule::GlueEnv #orig #@myGlueEnv = glueClass.new(persist_env, data_model_bindings) #/orig #new persistent_model_glue_obj = glueClass.new(persist_env, data_model_bindings) @myGlueEnv = persistent_model_glue_obj #GlueEnv.new(persistent_model_glue_obj) @metadata_keys = @myGlueEnv. end |
Instance Method Details
#__destroy_node ⇒ Object
Deletes the object
481 482 483 |
# File 'lib/tinkit_base_node.rb', line 481 def __destroy_node @my_GlueEnv.destroy_node(self.) end |
#__export_attachment(attachment_name) ⇒ Object
465 466 467 468 469 |
# File 'lib/tinkit_base_node.rb', line 465 def () md = () data = get_raw_data() export = {:metadata => md, :raw_data => data} end |
#__get_attachment_metadata(attachment_name) ⇒ Object
513 514 515 516 517 |
# File 'lib/tinkit_base_node.rb', line 513 def () all_md = index_name = TkEscape.escape() all_md[index_name.to_sym] end |
#__get_attachments_metadata ⇒ Object
504 505 506 507 508 509 510 511 |
# File 'lib/tinkit_base_node.rb', line 504 def md = @_files_mgr.(self) md = HashKeys.str_to_sym(md) md.each do |fbn, fmd| md[fbn] = HashKeys.str_to_sym(fmd) end md end |
#__import_attachment(attach_name, att_xfer_format) ⇒ Object
471 472 473 474 475 476 477 478 |
# File 'lib/tinkit_base_node.rb', line 471 def (attach_name, att_xfer_format) #transfer format is the format of the export method content_type = att_xfer_format[:metadata][:content_type] file_modified_at = att_xfer_format[:metadata][:file_modified] raw_data = att_xfer_format[:raw_data] #raise "Attachment provided no data to import" unless raw_data add_raw_data(attach_name, content_type, raw_data, file_modified_at) end |
#__method_wrapper(param, unbound_op) ⇒ Object
TODO: Method Wrapper is not sufficiently tested The method operations are completely decoupled from the object that they are bound to. This creates a problem when operations act on themselves (for example adding x to the current value requires the adder to determine the current value of x). To get around this self-referential problem while maintaining the decoupling this wrapper is used. Essentially it takes the unbound two parameter (this, other) and binds the current value to (this). This allows a more natural form of calling these operations. In other words description_add(new_string) can be used, rather than description_add(current_string, new_string).
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/tinkit_base_node.rb', line 411 def __method_wrapper(param, unbound_op) @@log.debug {"__method_wrapper with #{param.inspect}, #{unbound_op.inspect}"} if @@log.debug? #What I want is to call obj.param_op(other) example: obj.links_add(new_link) #which would then add new_link to obj.links #however, the predefined operation (add in the example) has no way of knowing #about links, so the predefined operation takes two parameters (this, other) #and this method wraps the obj.links so that the links_add method doesn't have to #include itself as a paramter to the predefined operation #lambda {|other| @node_data_hash[param] = unbound_op.call(@node_data_hash[param], other)} lambda {|other| old_this = self.__send__("#{param}".to_sym) #original value #we're going to compare the new value to the old later if old_this this = old_this.dup else this = old_this end rtn_data = unbound_op.call(this, other) new_this = rtn_data[:update_this] self.__send__("#{param}=".to_sym, new_this) it_changed = true it_changed = false if (old_this == new_this) || !(rtn_data.has_key?(:update_this)) not_in_model = !@saved_to_model self.__save if (not_in_model || it_changed)#unless (@saved_to_model && save) #don't save if the value hasn't changed rtn = rtn_data[:return_value] || rtn_data[:update_this] rtn } end |
#__save ⇒ Object
Save the object to the CouchDB database
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/tinkit_base_node.rb', line 448 def __save save_data_validations(self._user_data) node_key = @my_GlueEnv.node_key node_id = self.[node_key] @@log.debug {"User Data to save: #{self._user_data}"} if @@log.debug? model_data = #raise model_data.inspect @@log.debug { "saving (including injected model data): #{model_data.inspect}"} if @@log.debug? res = @my_GlueEnv.save(model_data) version_key = @my_GlueEnv.version_key #TODO: Make consistent with rev keys rev_data = {version_key => res['rev']} update_self(rev_data) return self end |
#__set_userdata_key(attr_var, attr_value) ⇒ Object
This will take a key-value pair and create an instance variable (actually it’s a method)using key as the method name, and sets the return value to the value associated with that key changes to the key’s value are reflected in subsequent method calls, and the value can be updated by using method_name = some value. Additionally, any custom operations that have been defined for that key name will be loaded in and assigned methods in the form methodname_operation
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'lib/tinkit_base_node.rb', line 380 def __set_userdata_key(attr_var, attr_value) ops = self.class.data_struc.field_op_defs #data_ops #|| NodeElementOperations.ops #@@log.debug {"Ops Def: #{ops.inspect}"} if @@log.debug? #ops = NodeElementOperations::Ops #incorporates predefined methods #@@log.debug {"Setting method #{attr_var.inspect}, #{ops[attr_var].inspect}"} if @@log.debug? add_op_method(attr_var, ops[attr_var]) if (ops && ops[attr_var]) unless self.class..include? attr_var.to_sym @_user_data[attr_var] = attr_value else raise "Metadata Keys: #{self.class..inspect} Key match: #{attr_var.to_sym.inspect} UserData: #{@_user_data.inspect}" end #manually setting instance variable (rather than using instance_variable_set), # so @node_data_hash can be updated #dynamic method acting like an instance variable getter self.class.__send__(:define_method, "#{attr_var}".to_sym, lambda {@_user_data[attr_var]} ) #dynamic method acting like an instance variable setter self.class.__send__(:define_method, "#{attr_var}=".to_sym, lambda {|new_val| @_user_data[attr_var] = new_val} ) end |
#__unset_userdata_key(param) ⇒ Object
439 440 441 442 |
# File 'lib/tinkit_base_node.rb', line 439 def __unset_userdata_key(param) self.class.__send__(:remove_method, param.to_sym) @_user_data.delete(param) end |
#add_parent_categories(new_cats) ⇒ Object
Deprecated Methods———————— Adds parent categories, it can accept a single category or an array of categories aliased for backwards compatibility, this method is dynamically defined and generated
524 525 526 527 |
# File 'lib/tinkit_base_node.rb', line 524 def add_parent_categories(new_cats) raise "Warning:: add_parent_categories is being deprecated, use <param_name>_add instead ex: parent_categories_add(cats_to_add) " parent_categories_add(new_cats) end |
#add_raw_data(attach_name, content_type, raw_data, file_modified_at = nil) ⇒ Object
Get attachment content. Note that the data is read in as a complete block, this may be something that needs optimized. TODO: add_raw_data parameters to a hash?
541 542 543 544 545 546 547 548 549 550 551 |
# File 'lib/tinkit_base_node.rb', line 541 def add_raw_data(attach_name, content_type, raw_data, file_modified_at = nil) attached_basenames = @_files_mgr.add_raw_data(self, attach_name, content_type, raw_data, file_modified_at = nil) if self.attached_files self.attached_files += attached_basenames self.attached_files.uniq! #removing duplicates is ok because these names are keys to the underlying attached file data (dupes would point to the same data) else self.__set_userdata_key(:attached_files, attached_basenames) end self.__save end |
#files_add(file_datas) ⇒ Object
553 554 555 556 557 558 559 560 561 562 563 564 565 |
# File 'lib/tinkit_base_node.rb', line 553 def files_add(file_datas) file_datas = [file_datas].flatten #TODO keep original names, and have model abstract character issues #TODO escaping is spread all over, do it in one place attached_basenames = @_files_mgr.add_files(self, file_datas) if self.attached_files self.attached_files += attached_basenames self.attached_files.uniq! #removing duplicates is ok because these names are keys to the underlying attached file data (dupes would point to the same data) else self.__set_userdata_key(:attached_files, attached_basenames) end self.__save end |
#files_remove_all ⇒ Object
574 575 576 577 578 |
# File 'lib/tinkit_base_node.rb', line 574 def files_remove_all @_files_mgr.subtract_files(self, :all) self.attached_files = nil self.__save end |
#files_subtract(file_basenames) ⇒ Object
567 568 569 570 571 572 |
# File 'lib/tinkit_base_node.rb', line 567 def files_subtract(file_basenames) file_basenames = [file_basenames].flatten @_files_mgr.subtract_files(self, file_basenames) self.attached_files -= file_basenames self.__save end |
#get_file_data(attachment_name) ⇒ Object
def attachment_url(attachment_name)
current_node_doc = self.class.get(self['_id'])
att_doc_id = current_node_doc['attachment_doc_id']
= self.class.user_attachClass.get(att_doc_id)
.()
end
593 594 595 596 597 598 599 |
# File 'lib/tinkit_base_node.rb', line 593 def get_file_data() @_files_mgr.get_file_data(self, ) #current_node_doc = self.class.get(self['_id']) #att_doc_id = current_node_doc['attachment_doc_id'] #current_node_attachment_doc = self.class.user_attachClass.get(att_doc_id) #current_node_attachment_doc.read_attachment(attachment_name) end |
#get_raw_data(attachment_name) ⇒ Object
580 581 582 |
# File 'lib/tinkit_base_node.rb', line 580 def get_raw_data() @_files_mgr.get_raw_data(self, ) end |
#raise_method_missing(meth_sym, *args) ⇒ Object
601 602 603 604 605 606 607 608 |
# File 'lib/tinkit_base_node.rb', line 601 def raise_method_missing(meth_sym, *args) raise NoMethodError, <<-ERRORINFO base class: TinkitBaseNode actual class: #{self.class} method: #{meth_sym.inspect} args: #{args.inspect} ERRORINFO end |
#remove_parent_categories(cats_to_remove) ⇒ Object
Can accept a single category or an array of categories aliased for backwards compatiblity the method is dynamically defined and generated
531 532 533 534 |
# File 'lib/tinkit_base_node.rb', line 531 def remove_parent_categories(cats_to_remove) raise "Warning:: remove_parent_categories is being deprecated, use <param_name>_subtract instead ex: parent_categories_subtract(cats_to_remove)" parent_categories_subtract(cats_to_remove) end |