module Rhoconnect module Model class Exception < RuntimeError; end # raise this to cause client to be logged out during a sync class LoginException < Rhoconnect::Model::Exception; end class LogoffException < Rhoconnect::Model::Exception; end # raise these to trigger rhoconnect sending an error to the client class ServerTimeoutException < Rhoconnect::Model::Exception; end class ServerErrorException < Rhoconnect::Model::Exception; end class ObjectConflictErrorException < Rhoconnect::Model::Exception; end class Base attr_accessor :session, :source def initialize(source) @source = source end # public API Method for loading an instance of the Model def self.load(username) source = Source.load(self.name, {:user_id => username,:app_id => APP_NAME}) self.create(source) end # public API method to load the Model's data def get_data(docname = :md) source.get_data(docname) end def self.partition_name(user_id) user_id end def self.load_source_model(source) model_klass=nil if source begin if source.name source.name = source.name.dup if source.name.frozen? source.name.strip! end # certain models are pre-defined and reserved # load them and instantiate if Rhoconnect.predefined_sources.has_key?(source.name) model_klass = Object.const_get(source.name) elsif Object.const_defined?(source.name) && Object.const_get(source.name).to_s.split("::").first != 'Rhoconnect' # fix until source adpaters are phased out, checking for Rhoconnect namespace # so that backend models with same name as Rhoconnect models are instantiated correctly model_klass = Object.const_get(source.name) unless model_klass require under_score(source.name) model_klass = Object.const_get(source.name) end else model_klass=Rhoconnect::Model::DynamicAdapterModel end rescue Exception => e log "Failure to find class for model #{source.name}: #{e.inspect.to_s}" raise e end end model_klass end def self.create(source) model=nil model_klass = load_source_model(source) if model_klass begin model = model_klass.new(source) rescue ArgumentError => e # Backward compatibility with code generated by gems < 2.2.0 log "ERROR: credential parameter in `initialize` method is deprecated and removed in version >= 2.2.0." raise e rescue Exception => e log "Failure to create model from class #{source.name}: #{e.inspect.to_s}" raise e end end model end def login; end def query(params=nil); end def search(params=nil); end def sync if @result and @result.empty? @source.lock(:md) do |s| s.flush_data(:md) s.put_value(:md_size,0) end else if @result @source.put_tmp_data(@tmp_docname,@result) @stash_size += @result.size end @source.lock(:md) do |s| s.flush_data(:md) s.rename_tmp_data(@tmp_docname,:md) s.put_value(:md_size,@stash_size) end end end def before_query @tmp_docname = :md.to_s + get_random_uuid @stash_size = 0 end def after_query res = nil if @source.is_pass_through? res = @result else self.sync res = true end res end def do_query(params=nil) before_query params ? self.query(params) : self.query after_query end def stash_result return if @result.nil? @source.put_tmp_data(@tmp_docname,@result,true) @stash_size += @result.size @result = nil end def expire_bulk_data(partition = :user) Rhoconnect.expire_bulk_data(current_user.login,partition) end # do pre-processing before CUD operation def validate(operation,operations_hashes,client_ids) {} end def create(create_hash); end def update(update_hash); end def delete(delete_hash); end def ask(params=nil); end def logoff; end # re-define in subc;ass to provide your own filename def store_blob(obj,field_name,blob) blob[:tempfile].path if blob[:tempfile] end # plugin callbacks implementation def push_objects(params) timeout = params[:timeout] || 10 raise_on_expire = params[:raise_on_expire] || false rebuild_md = params[:rebuild_md].nil? ? true : params[:rebuild_md] objects = params[:objects] @source.lock(:md,timeout,raise_on_expire) do |s| diff_count = 0 # in case of rebuild_md # we clean-up and rebuild the whole :md doc # on every request if(rebuild_md) doc = @source.get_data(:md) orig_doc_size = doc.size objects.each do |id,obj| doc[id] ||= {} doc[id].merge!(obj) end if objects diff_count = doc.size - orig_doc_size @source.put_data(:md,doc) else # if rebuild_md == false # we only operate on specific set values # which brings a big optimization # in case of small transactions diff_count = @source.update_objects(:md, objects) end @source.update_count(:md_size,diff_count) end @source.announce_changes end def push_deletes(params) timeout = params[:timeout] || 10 raise_on_expire = params[:raise_on_expire] || false rebuild_md = params[:rebuild_md].nil? ? true : params[:rebuild_md] objects = params[:objects] @source.lock(:md,timeout,raise_on_expire) do |s| diff_count = 0 if(rebuild_md) # in case of rebuild_md # we clean-up and rebuild the whole :md doc # on every request doc = @source.get_data(:md) orig_doc_size = doc.size objects.each do |id| doc.delete(id) end if objects diff_count = doc.size - orig_doc_size @source.put_data(:md,doc) else # if rebuild_md == false # we only operate on specific set values # which brings a big optimization # in case of small transactions diff_count = -@source.remove_objects(:md, objects) end @source.update_count(:md_size,diff_count) end @source.announce_changes end def current_user @source.user end end end # to preserve backward compatibility # TODO: Remove in 4.1 SourceAdapter = Rhoconnect::Model::Base SourceAdapterException = Rhoconnect::Model::Exception SourceAdapterLoginException = Rhoconnect::Model::LoginException SourceAdapterLogoffException = Rhoconnect::Model::LogoffException SourceAdapterServerTimeoutException = Rhoconnect::Model::ServerTimeoutException SourceAdapterServerErrorException = Rhoconnect::Model::ServerErrorException SourceAdapterObjectConflictErrorException = Rhoconnect::Model::ObjectConflictErrorException end