Class: TinkitNodeFactory

Inherits:
Object
  • Object
show all
Defined in:
lib/tinkit_node_factory.rb

Overview

The factory for building Tinkit Classes. Each Tinkit class is roughly equivalent to other ORM classes (Rails, Couchrest, etc).

It has class methods for operating across the entire collection, and each instance of that class is an individual node. So:
   SomeTinkitClass.all  #=> returns all records maintained by SomeTinkitClass as Tinkit nodes
   some_tinkit_node = SomeTinkitClass.all.first
   yet_another_tinkit_node = SomeTinkitClass.find_node_where(:my_data, :contains, "foo").first
   another_tinkit_node = SomeTinkitClass.get(node_primary_key_value)

Note on record/node terminology.  Records and Nodes are essentially a collection of data that corresponds to an instance of
the Tinkit class.  The difference is that I try to use the term "record" when referring to the persisted layer data object, and
node when referring to the Tinkit wrapped data object.

Constant Summary collapse

@@log =

Set Logger

TinkitLog.set(self.name, :debug)

Class Method Summary collapse

Class Method Details

.env_formatter(model_name, node_class_id, user_id, path, host = nil) ⇒ Object

Some sugar to help contruct a node environment. It will put properly format supplied data. Note that the model name must already be defined as a model in the glue_env! TODO: Add to specs



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/tinkit_node_factory.rb', line 26

def self.env_formatter(model_name, node_class_id, user_id, path, host = nil)
          #binding data (note this occurs in two different places in the env)

  key_fields = {:required_keys => [:id],
                       :primary_key => :id }


  #data model
  field_op_set =nil
  #op_set_mod => <Using default definitions>

  data_model = {:field_op_set => field_op_set, :key_fields => key_fields, :views => nil}

  #persistence layer model
  pmodel_env = { :host => host,
                        :path => path,
                        :user_id => user_id}
  persist_model = {:name => model_name, :env => pmodel_env}

  #final env model
  env = { :node_class_id => node_class_id,
              :data_model => data_model,
              :persist_model => persist_model }
end

.make(node_env) ⇒ Object

Builds the class from the provided environment the environment takes the following form

:node_class_id  -  The Name of the class to create
:persist_model  -  Information to model the user data in the persistence layer
:env                 -  Information to set up the persistence layer

:persist_model fields
    :name  -  The name of the persistence layer to use.
                 This name must match one of the persistent layers "glue" environments
                  Out of the box, tinkit currently supports
                      :filesystem - basic filesystem interface (linux only right now)
                      :couchrest  - interface to CouchDB, requires a CouchDB instance (will work with CouchOne as well)
                      :mysql        - interface to a MySQL instance (I've only tested local mysql instances)
                      :sdb_s3      - interface to Amazon Web Services databases, requires AWS account (SDB for node data, S3 for files)
                      other persistent layers can be supported and added by creating the appropriate plugin in the glue_envs directory  

    :key_fields  -  Identifies the primary key and any required keys. There can only be a single primary key, but multiple required keys
        :primary_key     -  The data that identifies the node. This key must be unique for each node within the tinkit node class
        :required_keys  -   List of keys required, should contain primary key.

:env fields
    :user_id  -  This id is propagated to the persistence layer to segregate content into different contexts if necessary. A classic
                      example would be to give multiple users there own segregated persistence layer, before segregating by tinkit class.
                      Important note: persistent layer context segregation is not enforced in the tinkit runtime environment.  This means that if I
                      used the same node_class_id for two different user_ids in the same runtime instance of tinkit, name collisions
                      would occur. In other words, if user_ids :batman and :robin both had class ids of :BatCloset, then the contents 
                      and location of :BatCloset will be unpredictable (roughly similar to multi-thread behavior in a unsafe thread environment)
                      Having :batman with :BatmanCloset, and :robin with :RobinCloset would work fine, and if a shared closet was needed, you 
                      could create a class called :SharedCloset (user_id doesn't matter) and progamattically update it as dictated by application needs.
                      tl;dr:  Don't duck punch yourself.  

  :path  -  Persistence layer location.  The particulars vary by persistence layer, but in general it says "this is where the persistence layer is gonna live"
                     filesystem  -  The path to the directory for all tinkit persisted data (created if it doesn't exist)
                      couchrest  -  The URL of the couchrest database to use (error if there is no CouchDB at that location)
                      mysql        -   A name prepended to mysql tables to keep tables unique (created if it doesn't exist)
                      sdb_s3      -  Simple DB domain (created if it doesn't exist)

Example:

env = {:node_class_id => "HelloWorldClass",
              :persist_model => {
                    :name => "filesystem",
                    :key_fields => {
                            :required_keys => [:id],
                            :primary_key => :id,
                     },
              :env => {
                    :user_id => "me",
                    :path => "/tmp/tinkit_hello_world_test/"
              }
        }
}


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/tinkit_node_factory.rb', line 105

def self.make(node_env)
  TinkitLog.log_raise "No Node Environment provided" unless node_env
  TinkitLog.log_raise "Empty Node Environment provided" if node_env.empty?
  TinkitLog.log_raise "Malformed Node Environment" unless node_env.respond_to?(:keys)
  TinkitLog.log_raise "Malformed Node Environment" unless node_env.keys.include? :persist_model

  #TODO: Make setting the environment thread safe
  #__user_doc_class_name = node_env[:node_class_id]
  user_doc_class_name = "Tinkit::#{node_env[:node_class_id]}"

  #Security TODO: remove spaces and other pototential injection issues
  #---- Dynamic Class Definitions ----
  dyn_user_class_def = "class #{user_doc_class_name} < TinkitBaseNode
    
    class << self; attr_accessor :user_attachClass, end

    end"

  #Create the Tinkit Class
  TinkitNodeFactory.class_eval(dyn_user_class_def)
  
  docClass = Tinkit.const_get(node_env[:node_class_id])
      
  #TODO: Streamline the paramaters (maybe one env paramater?)
  class_environment = node_env[:persist_model]
  
  neo_env = node_env[:data_model] || {}
  
  neo = NodeElementOperations.new(neo_env)
  docClass.data_struc = neo  #TODO: Why is this parameter passed magically, vs the others passed normally?

  data_model_bindings = {:key_fields => neo.key_fields,
                                    #:data_ops_set => neo.field_op_set_sym,
                                    :views => neo.views}

  docClass.set_environment(class_environment, data_model_bindings)
  docClass
end