#------------------------------------------------------------------------ # (The MIT License) # # Copyright (c) 2008-2011 Rhomobile, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # http://rhomobile.com #------------------------------------------------------------------------ module Rhom def self.any_model_changed? ::Rho::RHO.get_db_partitions.each_value do |db| result = db.execute_sql("SELECT object FROM changed_values LIMIT 1 OFFSET 0" ) return true if result && result.length > 0 end false end class RhomObjectFactory def initialize #unless not defined? Rho::RhoConfig.sources # init_objects #end end # Initialize new object with dynamic attributes def self.init_object(classname) #Rho::RhoConfig.sources.each do |classname,source| unless Object.const_defined?(classname.intern) Object.const_set(classname.intern, Class.new do include ::Rhom::RhomObject extend ::Rhom::RhomObject # This holds the attributes for an instance of # the rhom object attr_accessor :vars def generate_id Rho::RhoConfig.generate_id() end def initialize(obj=nil) @vars = {} self.rhom_init(@vars) self.vars[:object] = "#{generate_id()}" unless obj && obj[:object] self.vars[:source_id] = self.get_inst_source_id.to_i if obj obj.each do |key,value| self.vars[key.to_sym()] = value if key && key.length > 0 end end end def object @vars[:object] end def source_id @vars[:source_id] end def to_s @vars.to_s if @vars end def to_json(*args) @vars.to_json(*args) if @vars end def method_missing(name, *args) unless name == Fixnum if name[name.length()-1] == '=' @vars[name.to_s.chop.to_sym()] = args[0] else @vars[name] end end end class << self def metadata src_name = get_source_name() return Rho::RhoController.cached_metadata[src_name] if Rho::RhoController.cached_metadata.has_key?(src_name) db = ::Rho::RHO.get_src_db(src_name) result = db.select_from_table('sources', 'metadata', {"name"=>src_name} ) if result && result.length > 0 && result[0]['metadata'] Rho::RhoController.cached_metadata[src_name] = Rho::JSON.parse(result[0]['metadata']) else Rho::RhoController.cached_metadata[src_name] = nil end Rho::RhoController.cached_metadata[src_name] end def count db = ::Rho::RHO.get_src_db(get_source_name) if is_schema_source() #res = db.select_from_table(get_schema_table_name(),'object').length db_res = db.execute_sql("SELECT COUNT(*) FROM " + get_schema_table_name()) #puts "db_res : #{db_res}" if db_res && db_res.length() > 0 res = db_res[0].values[0] else res = 0 end else res = db.select_from_table('object_values','object', {"source_id"=>get_source_id}, {"distinct"=>true}).length end res.to_i() end def backend_refresh_time db = ::Rho::RHO.get_src_db(get_source_name) result = db.select_from_table('sources', 'backend_refresh_time', {"source_id"=>get_source_id} ) nTime = 0 if result && result.length > 0 && result[0]['backend_refresh_time'] nTime = result[0]['backend_refresh_time'].to_i end Time.at(nTime) end def get_source_name self.name.to_s end def is_sync_source Rho::RhoConfig.sources[get_source_name]['sync_type'] != 'none' end def is_schema_source !Rho::RhoConfig.sources[get_source_name]['schema'].nil? end def get_schema_table_name get_source_name() end def get_values_table_name is_schema_source() ? get_schema_table_name() : 'object_values' end def get_source_id Rho::RhoConfig.sources[get_source_name]['source_id'].to_i end def convertOpToStr(val_op, value) res = "" if val_op.upcase == 'IN' if value.is_a?(String) value = value.split(",") value.each do |item| item.strip! if (item.start_with?("\"") && item.end_with?("\"")) || (item.start_with?("\'") && item.end_with?("\'")) item.slice!(0) item.chop! end end end if value.is_a?(Array) svalue = "" value.each do |item| svalue += ',' if svalue.length() > 0 svalue += "\'#{item}\'" end res += val_op + ' ( ' + svalue + ' )' else raise ArgumentError, 'IN parameters should be String or Array' end else res += val_op + ' ' + ::Rhom::RhomDbAdapter.get_value_for_sql_stmt(value, false) end res end def convertSqlConditionToStr(cond, op) if cond.is_a?(String) return cond end if cond.is_a?(Array) && !op condition_str = cond[0].split(/\?/).each_with_index { |param,i| param << cond[i+1].to_s }.join(' ').to_s puts "condition_str : #{condition_str}" return condition_str end nil end def convertConditionToStatement(cond, op, sql, vals) if cond.is_a?(Array) return unless op sqlRes = "" valsRes = [] cond.each do |item| sqlRes += ' ' + op + ' ' if sqlRes.length() > 0 sqlCond = "" valsCond = [] convertConditionToStatement(item[:conditions], item[:op], sqlCond, valsCond ) sqlRes << "(" + sqlCond + ")" valsRes.concat(valsCond) end sql << sqlRes vals.concat(valsRes) return end return if !cond.is_a?(Hash) bSimpleHash = true op = "AND" unless op cond.each do |key, value| if key.is_a?(Hash) sqlCond, valsCond = makeCondWhereEx(key, value, nil) sql << ' ' + op + ' ' if !bSimpleHash sql << sqlCond vals.concat(valsCond) bSimpleHash = false end end if bSimpleHash sqlCond, valsCond = ::Rhom::RhomDbAdapter.make_where_params(cond,'AND') sql << sqlCond vals.concat(valsCond) end end =begin def convertConditionToStr(cond, op, condition_hash) if cond.is_a?(String) return cond end if cond.is_a?(Array) if ( op ) condition_str = "" cond.each do |item| condition_str += ' ' + op + ' ' if condition_str.length() > 0 condition_str += '(' + convertConditionToStr(item[:conditions], item[:op], condition_hash) + ')' end else condition_str = cond[0].split(/\?/).each_with_index { |param,i| param << cond[i+1].to_s }.join(' ').to_s end puts "condition_str : #{condition_str}" return condition_str end bSimpleHash = true condition_str = "" cond.each do |key, value| if key.is_a?(Hash) bSimpleHash = false val_op = "=" val_op = key[:op] if key[:op] val_func = key[:func] if key[:func] attrib_name = key[:name] if key[:name] op = "AND" unless op condition_str += ' ' + op + ' ' if condition_str.length() > 0 if ( val_func && val_func.length() > 0 ) condition_str += val_func + "(\"" + attrib_name + "\")" else condition_str += "\"" + attrib_name + "\"" end condition_str += ' ' + convertOpToStr(val_op, value) end end if bSimpleHash condition_hash = cond condition_str = ::Rhom::RhomDbAdapter.where_str(condition_hash) if condition_hash end condition_str end =end def makeCondWhere(key,value,srcid_value) sql = "" val_op = '=' val_func = '' attrib_name = '' if key.is_a?(Hash) val_op = key[:op] if key[:op] val_func = key[:func] if key[:func] attrib_name = key[:name] if key[:name] else attrib_name = key end sql << "attrib=" + ::Rhom::RhomDbAdapter.get_value_for_sql_stmt( val_func.upcase == 'CAST' ? attrib_name.split(' ')[0] : attrib_name) sql << " AND source_id=" + srcid_value if val_func.upcase == 'CAST' sql << " AND " + val_func + "(value " + attrib_name.split(' ')[1] + ' ' + attrib_name.split(' ')[2] + " ) " else sql << " AND " + (val_func.length > 0 ? val_func + "(value)" : "value") + ' ' end sql << convertOpToStr(val_op, value) sql end def getCondAttribName(key) if key.is_a?(Hash) attrib_name = key[:name] if key[:name] else attrib_name = key end attrib_name end def makeCondWhereEx(key,value,srcid_value) sql = "" vals = [] val_op = '=' val_func = '' attrib_name = '' if key.is_a?(Hash) val_op = key[:op] if key[:op] val_func = key[:func] if key[:func] attrib_name = key[:name] if key[:name] else attrib_name = key end if srcid_value.nil? sql << (val_func.length > 0 ? val_func + "(#{attrib_name})" : "#{attrib_name}") + ' ' else unless attrib_name.is_a?(String) && attrib_name == 'object' sql << "attrib=?" vals << (val_func.upcase == 'CAST' ? attrib_name.split(' ')[0] : attrib_name) sql << " AND source_id=?" vals << srcid_value if val_func.upcase == 'CAST' sql << " AND " + val_func + "(value " + attrib_name.split(' ')[1] + ' ' + attrib_name.split(' ')[2] + " ) " else sql << " AND " + (val_func.length > 0 ? val_func + "(value)" : "value") + ' ' end else sql << " source_id=?" vals << srcid_value sql << " AND object " end end if val_op == 'IN' or val_op == 'in' if value.is_a?(String) value = value.split(",") value.each do |item| item.strip! if item.start_with?("\"") && item.end_with?("\"") item.slice!(0) item.chop! end end end if value.is_a?(Array) sql << val_op + " ( #{Array.new( value.length(), '?').join(',')} )" vals.concat( value ) else raise ArgumentError, 'IN parameters should be String or Array' end else sql << val_op + " ?" vals << value #.to_s end return sql, vals end def find_objects(condition_hash, op, limit, offset, order_attr, &block) nulls_cond = {} if op == 'AND' condition_hash.each do |key,value| if value.nil? nulls_cond[key] = value condition_hash.delete(key) end end end strLimit = nil if !(block_given? || order_attr) strLimit = " LIMIT " + limit.to_s + " OFFSET " + offset.to_s if limit && offset && condition_hash.length <= 1 && nulls_cond.length == 0 end #puts "non-null select start" db = ::Rho::RHO.get_src_db(get_source_name) listObjs = [] if op == 'OR' && condition_hash.length > 1 srcid_value = ::Rhom::RhomDbAdapter.get_value_for_sql_stmt(get_source_id) mapObjs = {} bStop = false condition_hash.each do |key,value| sql = "" sql << "SELECT object,attrib,value FROM object_values WHERE \n" sql << makeCondWhere(key,value,srcid_value) resObjs = db.execute_sql(sql) resObjs.each do |rec| next if mapObjs[ rec['object'] ] mapObjs[ rec['object'] ] = 1 listObjs << rec bStop = !(block_given? || order_attr) && limit && offset && nulls_cond.length == 0 && listObjs.length >= offset + limit break if bStop end break if bStop end else if condition_hash.length > 0 sql = "" vals = [] condition_hash.each do |key,value| sql << "\nINTERSECT\n" if sql.length > 0 attrib_name = getCondAttribName(key) unless attrib_name && attrib_name.is_a?(String) && attrib_name == 'object' sql << "SELECT object FROM object_values WHERE \n" else sql << "SELECT DISTINCT(object) FROM object_values WHERE \n" end sqlCond, valCond = makeCondWhereEx(key,value,get_source_id) sql << sqlCond vals = vals + valCond sql << strLimit if strLimit end listObjs = db.execute_sql(sql, vals) else sql = "" vals = [] sql << "SELECT distinct( object ) FROM object_values WHERE \n" sql << "source_id=?" vals << get_source_id sql << strLimit if strLimit listObjs = db.execute_sql(sql, vals) end end #puts "non-null select end : #{listObjs.length}" nIndex = -1 res = [] listObjs.each do |obj| nIndex += 1 next if !(block_given? || order_attr) && offset && nIndex < offset && !strLimit bSkip = false #obj_value = ::Rhom::RhomDbAdapter.get_value_for_sql_stmt(obj['object']) nulls_cond.each do |key,value| sql = "" sql << "SELECT value FROM object_values WHERE \n" sql << "object=?" # + obj_value sql << " AND attrib=?" # + ::Rhom::RhomDbAdapter.get_value_for_sql_stmt(key) sql << " AND source_id=?" # + srcid_value attrVal = db.execute_sql(sql, obj['object'], key, get_source_id) #puts 'attrVal: ' + attrVal.inspect if attrVal if attrVal && attrVal.length>0 && attrVal[0]['value'] bSkip = true break end end next if bSkip res << obj end res end def find_objects_ex(condition_ar, op, limit, offset, order_attr, &block) mapObjs = {} listObjs = [] condition_ar.each do |cond| res = find_objects(cond[:conditions], cond[:op], limit, offset, order_attr, &block) if listObjs.length() == 0 if condition_ar.length() > 1 res.each do |hash_attrs| mapObjs[ hash_attrs['object'] ] = 1 end end listObjs = res else if op == 'OR' res.each do |hash_attrs| obj = hash_attrs['object'] if !mapObjs.has_key?(obj) listObjs << hash_attrs mapObjs[ obj ] = 1 end end else andRes = [] res.each do |hash_attrs| obj = hash_attrs['object'] if mapObjs.has_key?(obj) andRes << hash_attrs end end listObjs = andRes end end end listObjs end def find_bycondhash(args, &block) #puts 'find_bycondhash start' + (block_given? ? 'with block' : "") condition_hash = {} select_arr = nil limit = nil offset = 0 order_dir="" ret_list = [] op = 'AND' nSrcID = get_source_id.to_i if args[1] condition_hash = args[1][:conditions] if args[1][:conditions] if args[1][:per_page] #and args[1][:offset] limit = args[1][:per_page].to_i offset = args[1][:offset] ? args[1][:offset].to_i : 0 end select_arr = args[1][:select] if args[1][:select] order_dir = args[1][:orderdir] order_attr = args[1][:order] op = args[1][:op].upcase if args[1][:op] end if args.first == :first limit = 1 offset = 0 unless offset end attribs = nil if select_arr attribs = select_arr if order_attr order_attr_arr = [] if order_attr.is_a?(Array) order_attr_arr = order_attr else order_attr_arr.push(order_attr) end attribs = attribs | order_attr_arr end else #attribs = SyncEngine.get_src_attrs(nSrcID) end #srcid_value = ::Rhom::RhomDbAdapter.get_value_for_sql_stmt(get_source_id) db = ::Rho::RHO.get_src_db(get_source_name) db.lock_db begin listObjs = [] if condition_hash.is_a?(Hash) listObjs = find_objects(condition_hash, op, limit, offset, order_attr, &block) else listObjs = find_objects_ex(condition_hash, op, limit, offset, order_attr, &block) end nCount = 0; if args.first == :count nCount = listObjs.length else listObjs.each do |obj| sql = "" values = [] #if attribs # nonExistAttrs = attribs.dup # nonExistAttrs.delete(obj['attrib']) if obj['attrib'] # if nonExistAttrs.length > 0 # sql << "SELECT attrib,value FROM object_values WHERE object=? AND source_id=?" # values << obj['object'] # values << get_source_id # # quests = "" # nonExistAttrs.each do |attr| # quests << ',' if quests.length() > 0 # quests << '?' # values << attr # end # # sql << " AND attrib IN (#{quests})" # end #else sql << "SELECT attrib,value FROM object_values WHERE \n" sql << "object=? AND source_id=?" values << obj['object'] values << get_source_id #end #puts "get attribs: #{sql}" listAttrs = sql.length > 0 ? db.execute_sql(sql,values) : [] new_obj = self.new({:object=>"#{obj['object']}"}) #new_obj.vars.merge!({:object=>"#{obj['object']}"}) #if attribs && obj['attrib'] #new_obj.vars.merge!( {obj['attrib'].to_sym()=>obj['value'] }) if obj['value'] # new_obj.vars[obj['attrib'].to_sym()] = obj['value'] if obj['value'] #end listAttrs.each do |attrValHash| attrName = attrValHash['attrib'] attrVal = attrValHash['value'] #new_obj.vars.merge!( { attrName.to_sym()=>attrVal } ) if attrVal if attribs && attribs != '*' next unless attribs.include? attrName end new_obj.vars[attrName.to_sym()] = attrVal if attrVal #nonExistAttrs.delete(attrName) if nonExistAttrs end #if nonExistAttrs # nonExistAttrs.each do |attrName| # new_obj.vars.merge!( { attrName.to_sym()=>nil } ) # end #end ret_list << new_obj break if !(block_given? || order_attr) && limit && ret_list.length >= limit end end ensure db.unlock_db end order_array(ret_list, order_attr,order_dir, &block) if (block_given? || order_attr) && limit ret_list = ret_list.slice(offset,limit) end #puts "find_bycondhash end: #{ret_list.length} objects" return nCount if args.first == :count if args.first == :first return ret_list.length > 0 ? ret_list[0] : nil end ret_list end def order_array(ret_list, order_attr, order_dir, &block) if order_attr ret_list.sort! { |x,y| res = 0 if order_attr.is_a?(Array) order_attr.each_index { |i| vx = x.vars[order_attr[i].to_sym()] vy = y.vars[order_attr[i].to_sym()] res = vx && vy ? (block_given? ? yield(vx,vy): vx <=> vy) : 0 dir = 'ASC' if order_dir && order_dir.is_a?(Array) && i < order_dir.length() dir = order_dir[i].upcase() else dir = order_dir.upcase() if order_dir && order_dir.is_a?(String) end res *= -1 if dir && dir == 'DESC' break if res != 0 } else vx = x.vars[order_attr.to_sym()] vy = y.vars[order_attr.to_sym()] res = vx && vy ? (block_given? ? yield(vx,vy): vx <=> vy) : 0 res *= -1 if order_dir && order_dir.upcase() == 'DESC' end res } elsif block_given? ret_list.sort! { |x,y| res = yield(x,y) res *= -1 if order_dir && order_dir.upcase() == 'DESC' res } end end def make_sql_order(params) order_attr = params[:order] order_dir = params[:orderdir] res = "" if order_attr && order_attr.is_a?(Array) order_attr.each_index do |i| res += "," if i > 0 res += "\"#{order_attr[i]}\" " if order_dir && order_dir.is_a?(Array) && i < order_dir.length() res += order_dir[i].upcase() else res += order_dir && order_dir.is_a?(String) ? order_dir.upcase() : "ASC" end end else res = "\"#{order_attr}\" " + (order_dir ? order_dir.upcase() : "") end res end # retrieve a single record if object id provided, otherwise return # full list corresponding to factory's source id def find(*args, &block) raise ::Rhom::RecordNotFound if args[0].nil? or args.length == 0 #puts "Inside find: args - #{args.inspect}" ret_list = [] conditions = {} where_cond = nil limit = nil offset = nil # first find all query objects if args.first == :all || args.first == :first || args.first == :count if !is_schema_source() #!args[1] || !args[1][:conditions] || if args[1] && args[1][:conditions] && args[1][:conditions].is_a?(Hash) return find_bycondhash(args,&block) end if args[1] && args[1][:op] && args[1][:conditions] && args[1][:conditions].is_a?(Array) return find_bycondhash(args,&block) end where_cond = {"source_id"=>get_source_id} end elsif args.first.is_a?(String) if !is_schema_source() where_cond = {"object"=>strip_braces(args.first.to_s),"source_id"=>get_source_id} else where_cond = {"object"=>strip_braces(args.first.to_s)} end limit = 1 offset = 0 end if args.first == :count && !args[1] return count() end # do we have conditions? # if so, add them to the query #condition_hash = nil select_arr = nil condition_str = nil order_dir="" nSrcID = get_source_id.to_i if args[1] condition_str = convertSqlConditionToStr(args[1][:conditions], args[1][:op]) if args[1][:conditions] if args[1][:per_page] #and args[1][:offset] limit = args[1][:per_page].to_i offset = args[1][:offset] ? args[1][:offset].to_i : 0 end select_arr = args[1][:select] if args[1][:select] order_dir = args[1][:orderdir] order_attr = args[1][:order] end if args.first == :first limit = 1 offset = 0 unless offset end strLimit = nil if !block_given? strLimit = " LIMIT " + limit.to_s + " OFFSET " + offset.to_s if limit && offset end # return horizontal resultset from database # for example, an object that has attributes name,industry: # | object | name | industry | # --------------------------------------------------------------------------- # | 3560c0a0-ef58-2f40-68a5-48f39f63741b |A.G. Parr PLC 37862 |Entertainment| if select_arr attribs = select_arr #attribs = attribs | condition_hash.keys if condition_hash else attribs = '*' end order_manually = false if attribs and attribs.length > 0 sql = "" list = [] db = ::Rho::RHO.get_src_db(get_source_name) if !is_schema_source() objects = nil if !condition_str #srcid_value = ::Rhom::RhomDbAdapter.get_value_for_sql_stmt(get_source_id) values = [] if args.first.is_a?(String) objects = [ { 'object' => strip_braces(args.first.to_s) } ] else if !block_given? && order_attr && order_attr.is_a?(String) if !args[1][:dont_ignore_missed_attribs] sql << "SELECT object FROM object_values WHERE source_id=? " sql << " AND attrib=? ORDER BY \"value\" " + ( order_dir ? order_dir : "") values << nSrcID values << order_attr end else #it is more effective to use old find here if attribs && attribs != '*' && attribs.length() != 0 && !args[1][:dont_ignore_missed_attribs] sql << "SELECT object FROM object_values WHERE attrib=? AND source_id=?" values << nSrcID values << attribs[0] else if limit == 1 && offset == 0 sql = "SELECT object FROM object_values WHERE source_id=?" values << nSrcID elsif strLimit sql = "SELECT distinct(object) FROM object_values WHERE source_id=?" values << nSrcID end #sql << "SELECT distinct(object) FROM object_values WHERE source_id=" << srcid_value end order_manually = !order_attr.nil? end if sql.length() > 0 sql << strLimit if strLimit #puts "Database query start" # : #{sql}; #{values}" objects = db.execute_sql(sql, values) #puts "Database query end" #: #{objects}" end end if objects objects.each do |object| object_id = object['object'] sql2 = "SELECT attrib,value FROM object_values WHERE object=? AND source_id=?" values2 = [] values2 << object_id values2 << nSrcID #if attribs && attribs != '*' # quests = "" # attribs.each do |attr| # quests << ',' if quests.length() > 0 # quests << '?' # values2 << attr # end # # sql2 << " AND attrib IN (#{quests})" #end item_attribs = db.execute_sql(sql2, values2) if item_attribs && item_attribs.length() > 0 new_item = {'object'=>object_id } item_attribs.each do |item| if attribs && attribs != '*' next unless attribs.include? item['attrib'] end new_item[item['attrib']] = item['value'] end list << new_item end end end #puts "Processing rhom objects end1, #{list.length} objects" end if objects.nil? if !condition_str && !(args[1] && args[1][:dont_ignore_missed_attribs]) sql = "SELECT object, attrib, value FROM object_values WHERE source_id=? order by object" values = [nSrcID] objects = db.execute_sql(sql, values) new_item = nil objects.each do |item| if ( !new_item || new_item['object'] != item['object']) list << new_item if new_item new_item = {'object'=>item['object'] } end new_item[item['attrib']] = item['value'] if item['value'] end list << new_item if new_item order_manually = !order_attr.nil? else if attribs == "*" raise ArgumentError, "Use Rhom advanced find syntax or specify :select parameter when use sql queries!" #attribs = SyncEngine.get_src_attrs(Rho::RhoConfig.sources[get_source_name]['partition'].to_s, nSrcID) end if attribs and attribs.length > 0 sql << "SELECT * FROM (\n" if condition_str sql << "SELECT object, \n" #attribs.reject! {|attrib| select_arr.index(attrib).nil?} if select_arr attribs.each do |attrib| unless attrib.nil? or attrib.length == 0 or ::Rhom::RhomObject.method_name_reserved?(attrib) sql << "MAX(CASE WHEN attrib = '#{attrib}' THEN value ELSE NULL END) AS \'#{attrib}\',\n" end end sql.chomp! sql.chop! sql << " FROM object_values ov \n" sql << "where " + ::Rhom::RhomDbAdapter.where_str(where_cond) + "\n" if where_cond and where_cond.length > 0 sql << "group by object\n" sql << "order by " + make_sql_order(args[1]) if !block_given? && order_attr #sql << ") WHERE " + ::Rhom::RhomDbAdapter.where_str(condition_hash) if condition_hash sql << ") WHERE " + condition_str if condition_str sql << strLimit if strLimit #puts "Database query start" list = db.execute_sql(sql) #puts "Database query end" end end end else #schema source attribs = attribs.join(',') if attribs.is_a?(Array) sql << "SELECT #{attribs} FROM #{get_schema_table_name}" vals = [] if where_cond and where_cond.length > 0 sql << " WHERE " + ::Rhom::RhomDbAdapter.where_str(where_cond) elsif condition_str sql << " WHERE " + condition_str elsif args[1] && args[1][:conditions] sqlCond = "" convertConditionToStatement(args[1][:conditions], args[1][:op], sqlCond, vals) #condHash = {} #sqlCond = convertConditionToStr(args[1][:conditions], args[1][:op], condHash) sql << " WHERE " + sqlCond if sqlCond.length() > 0 end sql << " order by " + make_sql_order(args[1]) if !block_given? && order_attr sql << strLimit if strLimit #puts "Database query start" #: #{sql}" list = db.execute_sql(sql, vals) #puts "Database query end" end if args.first != :count list.each do |rowhash| # always return object field with surrounding '{}' #rowhash[:object] = "#{rowhash['object']}" #rowhash[:source_id] = nSrcID new_obj = self.new({:object=>"#{rowhash['object']}"}) #new_obj.vars.merge!(rowhash) rowhash.each do |attrName, attrVal| new_obj.vars.merge!( { attrName.to_sym()=>attrVal } ) if attrVal end ret_list << new_obj end #puts "Processing rhom objects end, #{ret_list.length} objects" end else puts "Processing rhom objects end, no attributes found." end if block_given? || order_manually order_array(ret_list, order_attr, order_dir, &block) ret_list = ret_list.slice(offset,limit) if limit end return list.length if args.first == :count if args.first == :first || args.first.is_a?(String) return ret_list.length > 0 ? ret_list[0] : nil end ret_list end def find_by_sql(sql_query) raise ArgumentError, 'find_by_sql only works with FixedSchema models' if !is_schema_source db = ::Rho::RHO.get_src_db(get_source_name) list = db.execute_sql(sql_query) ret_list = [] list.each do |rowhash| new_obj = self.new({:object=>"#{rowhash['object']}"}) rowhash.each do |attrName, attrVal| new_obj.vars.merge!( { attrName.to_sym()=>attrVal } ) if attrVal end ret_list << new_obj end ret_list end def search(args) args[:source_names] = [self.name.to_s] SyncEngine.search(args) end def sync(callback=nil, callback_data="", show_status_popup=nil) src_id = get_source_id.to_i() SyncEngine.set_notification(src_id, callback, callback_data) if callback if !show_status_popup.nil? SyncEngine.dosync_source(src_id, show_status_popup) else SyncEngine.dosync_source(src_id) end end # Alias for find def find_all(args=nil) find(:all, args) end # Returns a set of rhom objects, limiting the set to length :per_page # If no :per_page is specified, the default size is 10 def paginate(args=nil) # Default to 10 items per page args[:page] ||= 0 args[:per_page] ||= 10 args[:offset] = args[:page] * args[:per_page] find(:all, args) end def set_notification(url,params) SyncEngine.set_notification(get_source_id.to_i,url,params) end def clear_notification SyncEngine.clear_notification(get_source_id.to_i) end def on_sync_delete_error( objects, action ) raise ArgumentError, 'on_sync_delete_error action should be :retry' unless action == :retry return unless is_sync_source() nSrcID = get_source_id() db_partition = Rho::RhoConfig.sources[get_source_name]['partition'].to_s db = ::Rho::RHO.get_src_db(get_source_name) db.start_transaction begin objects.each do |obj, values| values['attributes'].each do |attrib, value| resUpdateType = db.select_from_table('changed_values', 'update_type', {"object"=>obj, "source_id"=>nSrcID, "attrib"=>attrib, 'sent'=>0}) next if resUpdateType && resUpdateType.length > 0 attrib_type = SyncEngine.is_blob_attr(db_partition, nSrcID,attrib) ? "blob.file" : "" db.insert_into_table('changed_values', {"source_id"=>nSrcID, "object"=>obj, "attrib"=>attrib, "value"=>value, "update_type"=>'delete', "attrib_type"=>attrib_type }) end end db.commit rescue Exception => e puts 'on_sync_delete_error Exception: ' + e.inspect db.rollback raise end end def on_sync_update_error( objects, action, rollback_data = nil ) raise ArgumentError, 'on_sync_update_error action should be :retry or :rollback' unless action == :retry || action == :rollback return unless is_sync_source() nSrcID = get_source_id() db_partition = Rho::RhoConfig.sources[get_source_name]['partition'].to_s tableName = is_schema_source() ? get_schema_table_name : 'object_values' db = ::Rho::RHO.get_src_db(get_source_name) db.start_transaction begin if action == :rollback raise ArgumentError, 'on_sync_update_error with :rollback action should provide update-rollback parameter' unless rollback_data rollback_data.each do |obj, values| values['attributes'].each do |attrib, value| _insert_or_update_attr(db, is_schema_source(), tableName, nSrcID, obj, attrib, value) end end else objects.each do |obj, values| values['attributes'].each do |attrib, value| resUpdateType = db.select_from_table('changed_values', 'update_type', {"object"=>obj, "source_id"=>nSrcID, "attrib"=>attrib, 'sent'=>0}) next if resUpdateType && resUpdateType.length > 0 attrib_type = SyncEngine.is_blob_attr(db_partition, nSrcID,attrib) ? "blob.file" : "" db.insert_into_table('changed_values', {"source_id"=>nSrcID, "object"=>obj, "attrib"=>attrib, "value"=>value, "update_type"=>'update', "attrib_type"=>attrib_type }) end end end db.commit rescue Exception => e puts 'on_sync_update_error Exception: ' + e.inspect db.rollback raise end end def on_sync_create_error( objects, action ) raise ArgumentError, 'on_sync_create_error action should be :delete or :recreate' unless action == :delete || action == :recreate return unless is_sync_source() ar_objs = objects if objects.is_a?(Hash) ar_objs = objects.keys() elsif !objects.is_a?(Array) ar_objs = [objects] end puts "ar_objs : #{ar_objs}" tableName = is_schema_source() ? get_schema_table_name : 'object_values' nSrcID = get_source_id() db = ::Rho::RHO.get_src_db(get_source_name) db.start_transaction begin ar_objs.each do |obj| if action == :recreate deletes = db.select_from_table('changed_values', 'object', {"update_type"=>'delete', "object"=>obj, "source_id"=>nSrcID }) unless ( deletes && deletes.length() > 0 ) db.delete_from_table('changed_values', {'object'=>obj, "source_id"=>nSrcID}) db.insert_into_table('changed_values', {'update_type'=>'create', 'attrib'=>'object', "source_id"=>nSrcID, "object"=>obj} ) next end end db.delete_from_table('changed_values', {'object'=>obj, "source_id"=>nSrcID}) if is_schema_source() db.delete_from_table(tableName, {'object'=>obj } ) else db.delete_from_table(tableName, {'object'=>obj, "source_id"=>nSrcID} ) end end db.commit rescue Exception => e puts 'on_create_error Exception: ' + e.inspect db.rollback raise end end # deletes all records matching conditions (optionally nil) def delete_all(*args) db = ::Rho::RHO.get_src_db(get_source_name) tableName = is_schema_source() ? get_schema_table_name : 'object_values' op = 'AND' op = args[0][:op].upcase if args && args[0] && args[0][:op] conditions = args[0][:conditions] if args && args[0] && args[0][:conditions] begin db.start_transaction if is_schema_source() vals = [] sqlCond = "" convertConditionToStatement(conditions, op, sqlCond, vals) if is_sync_source() sql = "SELECT object FROM #{tableName}" #sql << " WHERE " + ::Rhom::RhomDbAdapter.where_str(condition_hash) if condition_hash #sql << " WHERE " + condition_str if condition_str sql << " WHERE " + sqlCond if sqlCond.length() > 0 listObjs = db.execute_sql(sql, vals) listObjs.each do |item| db.delete_from_table('changed_values', {'object'=>item['object'], "source_id"=>get_source_id(), "sent"=>0}) attrsList = db.select_from_table(tableName, '*', {"object"=>item['object']}) attrsList[0].each do |attrName, attrValue| next if attrName == 'object' db.insert_into_table('changed_values', {"source_id"=>get_source_id(), "object"=>item['object'], "attrib"=> attrName, "value"=>attrValue, "update_type"=>'delete'}) end sql = "DELETE FROM #{tableName} WHERE object=?" values = [ item['object'] ] db.execute_sql(sql,values) end else #just delete objects sql = "DELETE FROM #{tableName}" #sql << " WHERE " + ::Rhom::RhomDbAdapter.where_str(condition_hash) if condition_hash #sql << " WHERE " + condition_str if condition_str sql << " WHERE " + sqlCond if sqlCond.length() > 0 db.execute_sql(sql, vals) end else listObjs = [] if !conditions if is_sync_source() listObjs = db.execute_sql("SELECT DISTINCT(object) FROM #{tableName} WHERE source_id=?", get_source_id() ) else db.delete_from_table(tableName, {"source_id"=>get_source_id()} ) end elsif conditions.is_a?(Hash) listObjs = find_objects(conditions, op, nil, 0, nil) else listObjs = find_objects_ex(conditions, op, nil, 0, nil) end listObjs.each do |item| if is_sync_source() db.delete_from_table('changed_values', {'object'=>item['object'], "source_id"=>get_source_id(), "sent"=>0}) attrsList = db.select_from_table(tableName, '*', {"object"=>item['object'], "source_id"=>get_source_id()}) attrsList.each do |attrName| db.insert_into_table('changed_values', {"source_id"=>get_source_id(), "object"=>item['object'], "attrib"=> attrName['attrib'], "value"=>attrName['value'], "update_type"=>'delete'}) end end sql = "DELETE FROM #{tableName} WHERE \n" sql << "object=? AND source_id=?" values = [] values << item['object'] values << get_source_id db.execute_sql(sql,values) end end db.commit rescue Exception => e puts 'delete_all Exception: ' + e.inspect db.rollback raise end end # saves the current object to the database as a create type def create(obj) new_obj = self.new(obj) new_obj.create new_obj end end #class methods # if app server does not support oo in inserts. # app client should check this method before update or delete # overwise all modifications of unconfirmed created item will be lost def can_modify db = ::Rho::RHO.get_src_db(get_inst_source_name) obj = self.object #self.inst_strip_braces(self.object) result = db.execute_sql("SELECT object FROM changed_values WHERE source_id=? and object=? and sent>1 LIMIT 1 OFFSET 0", get_inst_source_id().to_i(), obj ) return !(result && result.length > 0) end def self.changed? db = ::Rho::RHO.get_src_db(get_source_name) result = db.execute_sql("SELECT object FROM changed_values WHERE source_id=? LIMIT 1 OFFSET 0", get_source_id().to_i ) return result && result.length > 0 end def changed? db = ::Rho::RHO.get_src_db(get_inst_source_name) obj = self.object #self.inst_strip_braces(self.object) result = db.execute_sql("SELECT object FROM changed_values WHERE source_id=? and object=? LIMIT 1 OFFSET 0", get_inst_source_id().to_i(), obj ) return result && result.length > 0 end # deletes the record from the viewable list as well as # adding a delete record to the list of sync operations def destroy obj = self.object #self.inst_strip_braces(self.object) update_type='delete' if obj db = ::Rho::RHO.get_src_db(get_inst_source_name) begin tableName = is_inst_schema_source() ? get_inst_schema_table_name() : 'object_values' db.start_transaction #save list of attrs attrsList = nil if is_inst_schema_source() attrsList = db.select_from_table(tableName, '*', {"object"=>obj}) db.delete_from_table(tableName, {"object"=>obj}) else attrsList = db.select_from_table(tableName, '*', {"object"=>obj, "source_id"=>self.get_inst_source_id()}) db.delete_from_table(tableName, {"object"=>obj, "source_id"=>self.get_inst_source_id()}) end resUpdateType = is_inst_sync_source() ? db.select_from_table('changed_values', 'update_type', {"object"=>obj, "update_type"=>'create', "sent"=>0}) : nil if resUpdateType && resUpdateType.length > 0 update_type = nil end db.delete_from_table('changed_values', {"object"=>obj, "source_id"=>self.get_inst_source_id(), "sent"=>0}) if is_inst_sync_source() if is_inst_sync_source() && update_type and attrsList and attrsList.length() > 0 # now add delete operation if is_inst_schema_source() attrsList[0].each do |attrName, attrValue| next if attrName == 'object' db.insert_into_table('changed_values', {"source_id"=>self.get_inst_source_id, "object"=>obj, "attrib"=> attrName, "value"=>attrValue, "update_type"=>update_type}) end else attrsList.each do |attrName| db.insert_into_table('changed_values', {"source_id"=>self.get_inst_source_id, "object"=>obj, "attrib"=> attrName['attrib'], "value"=>attrName['value'], "update_type"=>update_type}) end end end db.commit rescue Exception => e puts 'destroy Exception: ' + e.inspect db.rollback raise end end true end def is_blob_attrib(db_partition, nSrcID,attrib_name) SyncEngine.is_blob_attr(db_partition, nSrcID.to_i,attrib_name) #return attrib_name == "image_uri" end def create nSrcID = self.get_inst_source_id obj = self.object src_name = get_inst_source_name tableName = is_inst_schema_source() ? get_inst_schema_table_name() : 'object_values' isSchemaSrc = is_inst_schema_source() db = ::Rho::RHO.get_src_db(src_name) begin db.start_transaction db.insert_into_table('changed_values', {'update_type'=>'create', 'attrib'=>'object', "source_id"=>nSrcID,"object"=>obj} ) if is_inst_sync_source() if isSchemaSrc db.insert_into_table(tableName, self.vars, {:source_id=>true}) else self.vars.each do |key_a,value| key = key_a.to_s next if ::Rhom::RhomObject.method_name_reserved?(key) val = value.to_s #self.inst_strip_braces(value.to_s) db.insert_into_table(tableName, {"source_id"=>nSrcID, "object"=>obj, "attrib"=>key, "value"=>val }) end end db.commit rescue Exception => e puts 'create Exception: ' + e.inspect db.rollback raise end end # saves the current object to the database as a create type def save # iterate over each instance variable and insert create row to table obj = self.object #self.inst_strip_braces(self.object) nSrcID = self.get_inst_source_id db = ::Rho::RHO.get_src_db(get_inst_source_name) db_partition = Rho::RhoConfig.sources[get_inst_source_name]['partition'].to_s tableName = is_inst_schema_source() ? get_inst_schema_table_name() : 'object_values' isSchemaSrc = is_inst_schema_source() #call create if item does not exists is_new_item = false begin db.lock_db() if isSchemaSrc existing_attribs = db.execute_sql("SELECT object FROM #{tableName} WHERE object=? LIMIT 1 OFFSET 0",obj) else existing_attribs = db.execute_sql("SELECT object FROM #{tableName} WHERE object=? AND source_id=? LIMIT 1 OFFSET 0",obj,nSrcID) end unless existing_attribs && existing_attribs.length > 0 is_new_item = true create(); end db.unlock_db rescue Exception => e puts 'save Exception: ' + e.inspect db.unlock_db raise end return if is_new_item begin db.start_transaction update_type = 'update' ignore_changed_values = true resUpdateType = nil if is_inst_sync_source() resUpdateType = db.select_from_table('changed_values', 'update_type', {"object"=>obj, "source_id"=>nSrcID, 'sent'=>0}) update_type = resUpdateType[0]['update_type'] if resUpdateType && resUpdateType.length > 0 ignore_changed_values = update_type=='create' if is_inst_full_update unless resUpdateType && resUpdateType.length > 0 db.insert_into_table('changed_values', {"source_id"=>nSrcID, "object"=>obj, "attrib"=>'object', "value"=>"", "update_type"=>update_type}) end ignore_changed_values = update_type=='update' end end self.vars.each do |key_a,value| key = key_a.to_s next if ::Rhom::RhomObject.method_name_reserved?(key) val = value.to_s #self.inst_strip_braces(value.to_s) # add rows excluding object, source_id and update_type fields = {"source_id"=>nSrcID, "object"=>obj, "attrib"=>key, "value"=>val, "update_type"=>update_type} fields = is_blob_attrib(db_partition, nSrcID, key) ? fields.merge!({"attrib_type" => "blob.file"}) : fields resValue = nil if isSchemaSrc resValue = db.select_from_table(tableName, key, {"object"=>obj}) else resValue = db.select_from_table(tableName, 'value', {"object"=>obj, "attrib"=>key, "source_id"=>nSrcID}) end if resValue && resValue.length > 0 isModified = false if is_inst_full_update isModified = true else oldValue = isSchemaSrc ? resValue[0][key] : resValue[0]['value'] isModified = oldValue != val if isModified && val && oldValue.nil? && val.to_s().length == 0 isModified = false end if isModified && oldValue && val.nil? && oldValue.to_s().length == 0 isModified = false end end if isModified unless ignore_changed_values resUpdateType = db.select_from_table('changed_values', 'update_type', {"object"=>obj, "attrib"=>key, "source_id"=>nSrcID, 'sent'=>0}) if resUpdateType && resUpdateType.length > 0 db.update_into_table('changed_values', {"value"=>val}, {"object"=>obj, "attrib"=>key, "source_id"=>nSrcID}) else db.insert_into_table('changed_values', fields) end end if isSchemaSrc db.update_into_table(tableName, {key=>val}, {"object"=>obj}) else db.update_into_table(tableName, {"value"=>val}, {"object"=>obj, "attrib"=>key, "source_id"=>nSrcID}) end end else db.insert_into_table('changed_values', fields) unless ignore_changed_values fields.delete("update_type") fields.delete("attrib_type") if isSchemaSrc db.insert_into_table(tableName, {key=>val, "object"=>obj}) else db.insert_into_table(tableName, fields) end end end db.commit rescue Exception => e puts 'save Exception: ' + e.inspect db.rollback raise end true end def self._insert_or_update_attr(db, is_schema, tableName, nSrcID, obj, attrib, new_val) if is_schema result = db.select_from_table(tableName, 'object', {"object"=>obj}) if result && result.length > 0 db.update_into_table(tableName, {attrib=>new_val}, {"object"=>obj}) else db.insert_into_table(tableName, {"object"=>obj, attrib=>new_val}) end else result = db.select_from_table(tableName, 'source_id', {"object"=>obj, "attrib"=>attrib, "source_id"=>nSrcID}) if result && result.length > 0 db.update_into_table(tableName, {"value"=>new_val}, {"object"=>obj, "attrib"=>attrib, "source_id"=>nSrcID}) else db.insert_into_table(tableName, {"source_id"=>nSrcID, "object"=>obj, "attrib"=>attrib, "value"=>new_val}) end end end # updates the current record in the viewable list and adds # a sync operation to update def update_attributes(attrs) obj = self.object #self.inst_strip_braces(self.object) update_type='update' nSrcID = self.get_inst_source_id db = ::Rho::RHO.get_src_db(get_inst_source_name) db_partition = Rho::RhoConfig.sources[get_inst_source_name]['partition'].to_s tableName = is_inst_schema_source() ? get_inst_schema_table_name() : 'object_values' begin db.start_transaction ignore_changed_values = true if is_inst_sync_source() resUpdateType = db.select_from_table('changed_values', 'update_type', {"object"=>obj, "source_id"=>nSrcID, 'sent'=>0}) update_type = resUpdateType[0]['update_type'] if resUpdateType && resUpdateType.length > 0 ignore_changed_values = update_type=='create' if is_inst_full_update unless resUpdateType && resUpdateType.length > 0 db.insert_into_table('changed_values', {"source_id"=>nSrcID, "object"=>obj, "attrib"=>'object', "value"=>"", "update_type"=>update_type}) end ignore_changed_values = update_type=='update' end end attrs.each do |attrib,val| attrib = attrib.to_s.gsub(/@/,"") next if ::Rhom::RhomObject.method_name_reserved?(attrib) # Don't save objects with braces to database new_val = val.to_s #self.inst_strip_braces(val.to_s) isModified = false old_val = self.send attrib.to_sym unless ::Rhom::RhomObject.method_name_reserved?(attrib) isModified = old_val != new_val if isModified && new_val && old_val.nil? && new_val.to_s().length == 0 isModified = false end if isModified && old_val && new_val.nil? && old_val.to_s().length == 0 isModified = false end # if the object's value doesn't match the database record # then we procede with update if isModified self.class._insert_or_update_attr(db, is_inst_schema_source(), tableName, nSrcID, obj, attrib, new_val) unless ignore_changed_values if resUpdateType && resUpdateType.length > 0 db.delete_from_table('changed_values', {"object"=>obj, "attrib"=>attrib, "source_id"=>nSrcID, "sent"=>0}) end attrib_type = is_blob_attrib(db_partition, nSrcID, attrib) ? "blob.file" : "" db.insert_into_table('changed_values', {"source_id"=>nSrcID, "object"=>obj, "attrib"=>attrib, "value"=>new_val, "update_type"=>update_type, "attrib_type"=>attrib_type }) end # update in-memory object self.vars[attrib.to_sym()] = new_val #unless is_inst_full_update end end db.commit rescue Exception => e puts 'update_attributes Exception: ' + e.inspect db.rollback raise end true end def get_inst_source_name self.class.name.to_s end def get_inst_source_id Rho::RhoConfig.sources[get_inst_source_name]['source_id'].to_i end def is_inst_sync_source Rho::RhoConfig.sources[get_inst_source_name]['sync_type'] != 'none' end def is_inst_schema_source !Rho::RhoConfig.sources[get_inst_source_name]['schema'].nil? end def get_inst_schema_table_name get_inst_source_name() end def is_inst_full_update Rho::RhoConfig.sources[get_inst_source_name]['full_update'] end def inst_strip_braces(str=nil) str ? str.gsub(/\{/,"").gsub(/\}/,"") : nil end end) end #unless #modelname = classname.split(/(?=[A-Z])/).map{|w| w.downcase}.join("_") #puts "classname: #{classname}; modelname: #{modelname}" #require "#{classname}/#{modelname}" if File.exists? File.join(Rho::RhoFSConnector.get_base_app_path,'app',classname,"#{modelname}" + RHO_RB_EXT) #end end #init_object end # RhomObjectFactory end # Rhom