lib/ooor/type_casting.rb
# OOOR: OpenObject On Ruby
# Copyright (C) 2009-2014 Akretion LTDA (<http://www.akretion.com>).
# Author: Raphaƫl Valyi
# Licensed under the MIT license, see MIT-LICENSE file
module Ooor
module TypeCasting
extend ActiveSupport::Concern
OPERATORS = ["=", "!=", "<=", "<", ">", ">=", "=?", "=like", "=ilike", "like", "not like", "ilike", "not ilike", "in", "not in", "child_of"]
BLACKLIST = %w[id write_date create_date write_ui create_ui]
module ClassMethods
def openerp_string_domain_to_ruby(string_domain) #FIXME: used? broken?
eval(string_domain.gsub('(', '[').gsub(')',']'))
end
def to_openerp_domain(domain)
if domain.is_a?(Hash)
return domain.map{|k,v| [k.to_s, '=', v]}
elsif domain == []
return []
elsif domain.is_a?(Array) && !domain.last.is_a?(Array)
return [domain]
else
return domain
end
end
def to_rails_type(type)
case type.to_sym
when :char
:string
when :binary
:file
when :many2one
:belongs_to
when :one2many
:has_many
when :many2many
:has_and_belongs_to_many
else
type.to_sym
end
end
def value_to_openerp(v)
if v == nil || v == ""
return false
elsif !v.is_a?(Integer) && !v.is_a?(Float) && v.is_a?(Numeric) && v.respond_to?(:to_f)
return v.to_f
elsif !v.is_a?(Numeric) && !v.is_a?(Integer) && v.respond_to?(:sec) && v.respond_to?(:year)#really ensure that's a datetime type
return "%d-%02d-%02d %02d:%02d:%02d" % [v.year, v.month, v.day, v.hour, v.min, v.sec]
elsif !v.is_a?(Numeric) && !v.is_a?(Integer) && v.respond_to?(:day) && v.respond_to?(:year)#really ensure that's a date type
return "%d-%02d-%02d" % [v.year, v.month, v.day]
elsif v == "false" #may happen with OOORBIT
return false
elsif v.respond_to?(:read)
return Base64.encode64(v.read())
else
v
end
end
def cast_request_to_openerp(request)
if request.is_a?(Array)
request.map { |item| cast_request_to_openerp(item) }
elsif request.is_a?(Hash)
request2 = {}
request.each do |k, v|
request2[k] = cast_request_to_openerp(v)
end
else
value_to_openerp(request)
end
end
def cast_answer_to_ruby!(answer)
def cast_map_to_ruby!(map)
map.each do |k, v|
if self.t.fields[k] && v.is_a?(String) && !v.empty?
case self.t.fields[k]['type']
when 'datetime'
map[k] = DateTime.parse(v)
when 'date'
map[k] = Date.parse(v)
end
end
end
end
if answer.is_a?(Array)
answer.each {|item| self.cast_map_to_ruby!(item) if item.is_a? Hash}
elsif answer.is_a?(Hash)
self.cast_map_to_ruby!(answer)
else
answer
end
end
end
def sanitize_attribute(skey, value)
type = self.class.fields[skey]['type']
if type == 'boolean' && (value == 1 || value == "1")
true
elsif type == 'boolean'&& (value == 0 || value == "0")
false
elsif value == false && type != 'boolean'
nil
elsif (type == 'char' || type == 'text') && value == "" && @attributes[skey] == nil
nil
else
value
end
end
def sanitize_association(skey, value)
if value.is_a?(Ooor::Base) || value.is_a?(Array) && value.all? {|i| i.is_a?(Ooor::Base)}
value
elsif value.is_a?(Array) && !self.class.many2one_associations.keys.index(skey)
value.reject {|i| i == ''}.map {|i| i.is_a?(String) ? i.to_i : i}
elsif value.is_a?(String)
sanitize_association_as_string(skey, value)
else
value
end
end
def sanitize_association_as_string(skey, value)
if self.class.polymorphic_m2o_associations.has_key?(skey)
value
elsif self.class.many2one_associations.has_key?(skey)
sanitize_m2o_association(value)
else
value.split(",").map {|i| i.to_i}
end
end
def sanitize_m2o_association(value)
if value.blank? || value == "0"
false
else
value.to_i
end
end
def to_openerp_hash
attribute_keys, association_keys = get_changed_values
associations = {}
association_keys.each { |k| associations[k] = self.cast_association(k) }
@attributes.slice(*attribute_keys).merge(associations)
end
def get_changed_values
attribute_keys = changed.select {|k| self.class.fields.has_key?(k)} - BLACKLIST
association_keys = changed.select {|k| self.class.associations_keys.index(k)}
return attribute_keys, association_keys
end
# talk OpenERP cryptic associations API
def cast_association(k)
if self.class.one2many_associations[k]
if @loaded_associations[k]
v = @loaded_associations[k].select {|i| i.changed?}
v = @associations[k] if v.empty?
else
v = @associations[k]
end
cast_o2m_association(v)
elsif self.class.many2many_associations[k]
v = @associations[k]
[[6, false, (v || []).map {|i| i.is_a?(Base) ? i.id : i}]]
elsif self.class.many2one_associations[k]
v = @associations[k] # TODO support for nested
cast_m2o_association(v)
end
end
def cast_o2m_association(v)
v.collect do |value|
if value.is_a?(Base)
if value.new_record?
[0, 0, value.to_openerp_hash]
elsif value.marked_for_destruction?
[2, value.id]
elsif value.id
[1, value.id , value.to_openerp_hash]
end
elsif value.is_a?(Hash)
[0, 0, value]
else #use case?
[1, value, {}]
end
end
end
def cast_o2m_nested_attributes(v)
v.keys.collect do |key|
val = v[key]
if !val["_destroy"].blank?
[2, val[:id].to_i || val['id']]
elsif val[:id] || val['id']
[1, val[:id].to_i || val['id'], val]
else
[0, 0, val]
end
end
end
def cast_m2o_association(v)
if v.is_a?(Array)
v[0]
elsif v.is_a?(Base)
v.id
else
v
end
end
end
end