Class: CouchrestEnv::GlueEnv

Inherits:
Object
  • Object
show all
Includes:
CouchRest::Mixins::Views::ClassMethods, CouchrestViews
Defined in:
lib/glue_envs/couchrest_glue_env.rb

Overview

EnvName = :couchrest_env #name for couchrest environments

Constant Summary collapse

VersionKey =

used to identify metadata for models (should be consistent across models) ModelKey = :_id

:_rev
PersistLayerKey =

NamespaceKey = :bufs_namespace

:_id
CouchMetadataKeys =

required by CouchDb to be unique in DB

[:_pos, :_deleted_conflicts, :couchrest, :all_this_class]
QueryAllStr =
"by_all_bufs".to_sym
AttachClassBaseName =
"MoabAttachmentHandler"
DesignDocBaseName =

used to be module name

"CouchRestEnv"
@@log =

Set Logger

TinkitLog.set(self.name, :warn)

Constants included from CouchrestViews

CouchrestViews::DefineViews

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from CouchrestViews

#call_view, #set_view_value_match, #view_map

Constructor Details

#initialize(persist_env, data_model_bindings) ⇒ GlueEnv

Returns a new instance of GlueEnv.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/glue_envs/couchrest_glue_env.rb', line 194

def initialize(persist_env, data_model_bindings)
  couchrest_env = persist_env[:env]
  couch_db_host = couchrest_env[:host]
  db_name_path = couchrest_env[:path]
  @user_id = couchrest_env[:user_id]
  @model_name = persist_env[:name]
  
  #data_model_bindings from NodeElementOperations
  key_fields = data_model_bindings[:key_fields] 
  initial_views_data = data_model_bindings[:views] || []
  
  #FIXME: Major BUG!! when setting multiple environments in that this may cross-contaminate across users
  #if those users share the same db.  Testing up to date has been users on different dbs, so not an issue to date
  #also, one solution might be to force users to their own db? (what about sharing though?)
  #The problem is that there is one "query_all" per database, and it gets set to the last user class
  #that sets it.  [Is this still a bug? 12/16/10]
  #@user_id = db_user_id
  #user_attach_class_name = "UserAttach#{db_user_id}"
  #the rescue is so that testing works
  #begin
  #  attachClass = UserNode.const_get(user_attach_class_name)
  #rescue NameError
  #  puts "Warning:: Multiuser support for attachments not enabled. Using generic Attachment Class"
  #  attachClass = CouchrestAttachment
  #end
  couch_db_location = set_db_location(couch_db_host, db_name_path)
  @db = CouchRest.database!(couch_db_location)
  @model_save_params = {:db => @db}
  @persist_layer_key = PersistLayerKey
  
  #@collection_namespace = CouchrestEnv.set_collection_namespace(db_name_path, @user_id)
  #@user_datastore_location = CouchRestEnv.set_user_datastore_location(@db, @user_id)
  @user_datastore_location = set_namespace(db_name_path, @user_id)
  @design_doc = set_couch_design(@db, @user_id)#, @collection_namespace)
  @node_key = key_fields[:primary_key]     
  #
  @define_query_all = QueryAllStr #CouchRestEnv.query_for_all_collection_records
  
  @required_instance_keys = key_fields[:required_keys] #DataStructureModels::RequiredInstanceKeys
  @required_save_keys = key_fields[:required_keys] #DataStructureModels::Tinkit::RequiredSaveKeys
  @model_key = @node_key #ModelKey #CouchRestEnv::ModelKey
  @version_key = VersionKey #CouchRestEnv::VersionKey
  @namespace_key = @model_name #NamespaceKey #CouchRestEnv::NamespaceKey
  #TODO: Need to investigate whether to keep model_key = node_key in metadata
  @metadata_keys = [@persist_layer_key, @version_key, @namespace_key] + CouchMetadataKeys #CouchRestEnv.set_db_metadata_keys #(@collection_namespace)
 
  @views = CouchRestViews
  @views.set_view_all(@db, @design_doc, @model_name, @user_datastore_location)
  
  @views.set_my_cat_view(@db, @design_doc, @user_datastore_location)
  
  
  
  #set new view
  initial_views_data.each do |view_name, view_data|
    set_view_value_match(@db, @design_doc, @namespace_key, @user_datastore_location, view_data[:field])
  end
  
  #@views.set_new_views(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
  
  attach_class_name = "#{AttachClassBaseName}#{@user_id}"
  @attachClass = set_attach_class(@db.root, attach_class_name) 
  @moab_data = {:db => @db, 
                          :design_doc => @design_doc, 
                          :attachClass => @attachClass}
  #TODO: Have to do the above, but want to do the below
  #@attachClass = set_attach_class(@db.root, attach_class_name)
  @_files_mgr_class = CouchrestInterface::FilesMgr
end

Instance Attribute Details

#_files_mgr_classObject

Returns the value of attribute _files_mgr_class.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def _files_mgr_class
  @_files_mgr_class
end

#attachClassObject

Returns the value of attribute attachClass.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def attachClass
  @attachClass
end

#attachment_base_idObject

Returns the value of attribute attachment_base_id.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def attachment_base_id
  @attachment_base_id
end

#dbObject

Returns the value of attribute db.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def db
  @db
end

#design_docObject

Returns the value of attribute design_doc.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def design_doc
  @design_doc
end

#metadata_keysObject

Returns the value of attribute metadata_keys.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def 
  @metadata_keys
end

#moab_dataObject

Returns the value of attribute moab_data.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def moab_data
  @moab_data
end

#model_keyObject

Returns the value of attribute model_key.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def model_key
  @model_key
end

#model_save_paramsObject

Returns the value of attribute model_save_params.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def model_save_params
  @model_save_params
end

#namespace_keyObject

Returns the value of attribute namespace_key.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def namespace_key
  @namespace_key
end

#node_keyObject

Returns the value of attribute node_key.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def node_key
  @node_key
end

#persist_layer_keyObject

Returns the value of attribute persist_layer_key.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def persist_layer_key
  @persist_layer_key
end

#query_allObject

TODO move to ViewsMgr and change the confusing accessor/method clash



327
328
329
# File 'lib/glue_envs/couchrest_glue_env.rb', line 327

def query_all
  @query_all
end

#required_instance_keysObject

Returns the value of attribute required_instance_keys.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def required_instance_keys
  @required_instance_keys
end

#required_save_keysObject

Returns the value of attribute required_save_keys.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def required_save_keys
  @required_save_keys
end

#user_datastore_locationObject

Returns the value of attribute user_datastore_location.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def user_datastore_location
  @user_datastore_location
end

#user_idObject

Returns the value of attribute user_id.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def user_id
  @user_id
end

#version_keyObject

Returns the value of attribute version_key.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def version_key
  @version_key
end

#viewsObject

Returns the value of attribute views.



173
174
175
# File 'lib/glue_envs/couchrest_glue_env.rb', line 173

def views
  @views
end

Instance Method Details

#db_destroy(model_metadata) ⇒ Object

TODO: update glue models so that it’s id and rev that are explicitly needed which begs the question whether rev is supported or not



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/glue_envs/couchrest_glue_env.rb', line 462

def db_destroy()
  id_to_delete = [@persist_layer_key] ||  generate_pk_data([@node_key])
  @@log.info { "Destroy in DB, Key: #{id_to_delete.inspect} in #{.inspect}" } if @@log.info?
  
  rev_to_delete = [@version_key] || @db.get(id_to_delete)['_rev']  
  doc_to_delete = {'_id' =>  id_to_delete, '_rev' => rev_to_delete }
 
  @@log.debug { "Attempting to Delete: #{doc_to_delete.inspect}" } if @@log.debug?
  begin
    @db.delete_doc(doc_to_delete)
  rescue ArgumentError => e
    puts "Rescued Error: #{e} while trying to destroy #{[@model_key]} node"
    #Code here was deleting the latest version, but rather than wait for an error, it was proactively checked
    #so this block may not be needed any more
    #node = node.class.get(model_metadata[@model_key]) #(model_metadata['_id'])
    #db_destroy(model_metadata)
  end
end

#destroy_bulk(user_records_to_delete) ⇒ Object

TODO: Investigate if Couchrest bulk actions or design views will assist here fixed to delete orphaned attachments, but this negates much of the advantage of using this method in the first place or perhaps using a close to the metal design view based on the class name?? (this may be better)



511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/glue_envs/couchrest_glue_env.rb', line 511

def destroy_bulk(user_records_to_delete)
  #TODO: Investigate why mutiple ids may be returned for the same record
  #Answer Database Corruption
  user_records_to_delete.uniq!
  #puts "List of all records: #{user_records_to_delete.map{|r| r['_id']}.inspect}"

  user_records_to_delete.each do |user_rec|
    begin
      db_key = user_rec[@persist_layer_key] || generate_pk_data(user_rec[@model_key])
      att_doc_id = attachClass.uniq_att_doc_id(db_key)
      r = @db.get(db_key )
      @db.delete_doc(r)
      begin
        att_doc = @db.get(att_doc_id)
      rescue
        att_doc = nil
      end
      @db.delete_doc(att_doc) if att_doc
    rescue RestClient::RequestFailed
      @@log.warn{ "Warning:: Failed to delete document?" } if @@log.warn?
    end
  end
  nil #TODO ok to return nil if all docs destroyed? also, not verifying
end

#destroy_node(model_metadata) ⇒ Object

Not tested in factory tests (but is in couchrest tests)



450
451
452
453
454
455
456
457
458
# File 'lib/glue_envs/couchrest_glue_env.rb', line 450

def destroy_node()
  #att_doc = node.my_GlueEnv.attachClass.get(node.attachment_doc_id) if node.respond_to?(:attachment_doc_id)
  attachClass = @moab_data[:attachClass]
  att_doc_id = attachClass.uniq_att_doc_id([@persist_layer_key])
  att_doc = attachClass.get(att_doc_id) if att_doc_id
  #raise "Destroying Attachment #{att_doc.inspect} derived from #{@model_metadata.inspect} "
  att_doc.destroy if att_doc
  db_destroy()
end

#find_contains(key, this_value) ⇒ Object



363
364
365
366
367
368
369
370
371
372
# File 'lib/glue_envs/couchrest_glue_env.rb', line 363

def find_contains(key, this_value)
  #TODO: Make a view for this rather than doing it in ruby
  results =[]
  query_all.each do |record|
    test_val = record[key]
    results << record  if find_contains_type_helper(test_val, this_value)
  end
  @@log.debug {"Found contains results: #{results.inspect}"} if @@log.debug?
  results 
end

#find_contains_type_helper(stored_data, this_value) ⇒ Object



374
375
376
377
378
379
380
381
382
383
# File 'lib/glue_envs/couchrest_glue_env.rb', line 374

def find_contains_type_helper(stored_data, this_value)
  resp = nil
  #stored_data = jparse(stored_dataj)
  if stored_data.respond_to?(:"include?")
    resp = (stored_data.include?(this_value))
  else
    resp = (stored_data == this_value)
  end
  return resp
end

#find_equals(key, this_value) ⇒ Object



353
354
355
356
357
358
359
360
361
# File 'lib/glue_envs/couchrest_glue_env.rb', line 353

def find_equals(key, this_value)
  results =[]
  query_all.each do |record|
    test_val = record[key]
    results << record  if test_val == this_value
  end
  @@log.debug {"Found equals results: #{results.inspect}"} if @@log.debug?
  results 
end

#find_nodes_where(key, relation, this_value) ⇒ Object

current relations supported:

  • :equals (data in the key field matches this_value)

  • :contains (this_value is contained in the key field data (same as equals for non-enumerable types )



339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/glue_envs/couchrest_glue_env.rb', line 339

def find_nodes_where(key, relation, this_value)
  
  res = case relation
    when :equals
      find_equals(key, this_value)
    when :contains
      find_contains(key, this_value)
    else
      raise "Couldn't determine relationship between stored data and lookup data"
  end #case
  @@log.info {"Found #{res.size} nodes where #{key} #{relation} #{this_value}"} if @@log.info?
  return res    
end

#generate_model_key(namespace, node_key_value) ⇒ Object

I hope this can be replaced by the generate_pk_data, but need to make sure FIXME: Actually it has to be



489
490
491
492
493
494
# File 'lib/glue_envs/couchrest_glue_env.rb', line 489

def generate_model_key(namespace, node_key_value)
  #TODO: Make sure namespace is portable across model migrations (must be diff database)
  #"#{namespace}::#{node_key}"   # <== original if the below ends up breaking stuff
  #"#{node_key}"
  generate_pk_data(node_key_value)
end

#generate_pk_data(record_id) ⇒ Object



496
497
498
499
# File 'lib/glue_envs/couchrest_glue_env.rb', line 496

def generate_pk_data(record_id)
  #url_friendly_class_name = self.class.name.gsub('::','-')
  "#{user_id}:#{self.class.name}:#{record_id}"
end

#get(id) ⇒ Object



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/glue_envs/couchrest_glue_env.rb', line 423

def get(id)
  #id can be the model id or the persist layer id
  pk_data = if id.include? self.class.name
    id
  else
    generate_pk_data(id)
  end
  #maybe put in some validations to ensure its from the proper collection namespace?
  #pk_data = id #generate_pk_data(id)
  #Major TODO: Deconflict module CouchrestView and CouchRestViews
  namespace_key = CouchRestViews::ClassNamespaceKey
  #options, use native couchdb _id or buidl a view for the model key
  #currently opting for using _id, but this requires rebuilding the primary key data
  #which is not an ideal solution
  rtn = begin
    node = @db.get(pk_data)
    node = HashKeys.str_to_sym(node)
    node.delete(PersistLayerKey)
    node.delete(namespace_key)
    node
  rescue RestClient::ResourceNotFound => e
    nil
  end
  rtn
end

#raw_allObject

some models have additional processing required, but not this one



502
503
504
# File 'lib/glue_envs/couchrest_glue_env.rb', line 502

def raw_all
  query_all
end

#save(user_data) ⇒ Object



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/glue_envs/couchrest_glue_env.rb', line 386

def save(user_data)
  #I thinkthis was why I originally created the namespace concept but reconciliation is now required
  pk_data = generate_pk_data(user_data[@model_key])
  record_namespace = "#{@user_datastore_location}_#{@model_name}"
  #Major TODO: Deconflict module CouchrestView and CouchRestViews
  namespace_key = CouchRestViews::ClassNamespaceKey
   = {PersistLayerKey => pk_data,  namespace_key => record_namespace}
  new_data = user_data.dup.merge()
  db = @model_save_params[:db]
  
  raise "No database found to save data" unless db
  raise "No CouchDB Key (#{PersistLayerKey}) found in data: #{new_data.inspect}" unless new_data[PersistLayerKey]
  raise "No [#{@model_key}] key found in model data: #{new_data.inspect}" unless new_data[@model_key]
  
  model_data = HashKeys.sym_to_str(new_data) 
  #db.save_doc(model_data)
  begin
    #TODO: Genericize this
    res = db.save_doc(model_data)
  rescue RestClient::RequestFailed => e
    #TODO Update specs to test for this
    if e.http_code == 409
      doc_str = "Document Conflict in the Database." 
      @@log.warn { doc_str } if @@log.warn?
      existing_doc = db.get(model_data['_id'])
      rev = existing_doc['_rev']
      data_with_rev = model_data.merge({'_rev' => rev})
      res = db.save_doc(data_with_rev)
    else
     raise "Request Failed -- Response: #{res.inspect} Error:#{e}"\
     "\nAdditonal Data: model params: #{model_save_params.inspect}"\
     "\n                model data: #{model_data.inspect}"\
     "\n                all data: #{new_data.inspect}"
    end
  end
end

#set_attach_class(db_root_location, attach_class_name) ⇒ Object



314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/glue_envs/couchrest_glue_env.rb', line 314

def set_attach_class(db_root_location, attach_class_name)
  dyn_attach_class_def = "class #{attach_class_name} < CouchrestAttachment
    use_database CouchRest.database!(\"#{db_root_location}/\")
 
    def self.namespace
      CouchRest.database!(\"http://#{db_root_location}/\")
    end
  end"
  
  self.class.class_eval(dyn_attach_class_def)
  self.class.const_get(attach_class_name)
end

#set_couch_design(db, user_id) ⇒ Object

, view_name)



299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/glue_envs/couchrest_glue_env.rb', line 299

def set_couch_design(db, user_id) #, view_name)
  design_doc = CouchRest::Design.new
  design_doc.name = "#{DesignDocBaseName}_#{user_id}_Design"
  #example of a map function that can be passed as a parameter if desired (currently not needed)
  #map_function = "function(doc) {\n  if(doc['#{@@collection_namespace}']) {\n   emit(doc['_id'], 1);\n  }\n}"
  #design_doc.view_by collection_namespace.to_sym #, {:map => map_function }
  design_doc.database = db
  begin
    design_doc = db.get(design_doc['_id'])
  rescue RestClient::ResourceNotFound
    design_doc.save
  end
  design_doc
end

#set_db_location(couch_db_host, db_name_path) ⇒ Object

TODO Need to fix some naming issues before bringing this method over into the glue environment def set_attach_class(db_root_location, attach_class_name)

dyn_attach_class_def = "class #{attach_class_name} < CouchrestAttachment
  use_database CouchRest.database!(\"http://#{db_root_location}/\")

  def self.namespace
    CouchRest.database!(\"http://#{db_root_location}/\")
  end
end"

self.class.class_eval(dyn_attach_class_def)
self.class.const_get(attach_class_name)

end



278
279
280
281
282
# File 'lib/glue_envs/couchrest_glue_env.rb', line 278

def set_db_location(couch_db_host, db_name_path)
    couch_db_host.chop if couch_db_host =~ /\/$/ #removes any trailing slash
    db_name_path = "/#{db_name_path}" unless db_name_path =~ /^\// #check for le
    couch_db_location = "#{couch_db_host}#{db_name_path}"
end

#set_namespace(db_name_path, db_user_id) ⇒ Object

TODO: MAJOR Refactoring may have broken compatibility with already persisted data, need to figure out tool to migrate persisted data when changes occur TODO: MAJOR Namespace should not be bound to the underlying model it should be bound to user data only



287
288
289
290
291
292
293
# File 'lib/glue_envs/couchrest_glue_env.rb', line 287

def set_namespace(db_name_path, db_user_id)
    lose_leading_slash = db_name_path.split("/")
    lose_leading_slash.shift
    db_name = lose_leading_slash.join("")
    #namespace = "#{db_name}_#{db_user_id}"
    namespace = "#{db_user_id}"
end

#set_user_datastore_location(db, db_user_id) ⇒ Object



295
296
297
# File 'lib/glue_envs/couchrest_glue_env.rb', line 295

def set_user_datastore_location(db, db_user_id)
    "#{db.to_s}::#{db_user_id}"
end