diff --git a/lib/deja/neo_parse.rb b/lib/deja/neo_parse.rb index 63cd214..34f2df7 100644 --- a/lib/deja/neo_parse.rb +++ b/lib/deja/neo_parse.rb @@ -1,6 +1,7 @@ module Deja module NeoParse extend ActiveSupport::Concern + include TypeCaster module ClassMethods def normalize(hash, type = :eager) @@ -26,7 +27,7 @@ def sane_hash(hash) attr_hash[:start_node] = record['start'].split('/').last.to_i if record['start'] attr_hash[:end_node] = record['end'].split('/').last.to_i if record['end'] record['data'].each do |key, value| - attr_hash[key.to_sym] = value + attr_hash[key.to_sym] = record['data']['type'].nil? ? value : TypeCaster.reversecast(key.to_sym, value, record['data']['type']) end attr_hash end diff --git a/lib/deja/node.rb b/lib/deja/node.rb index c2e4e9c..0a265fd 100644 --- a/lib/deja/node.rb +++ b/lib/deja/node.rb @@ -215,7 +215,7 @@ def persisted_attributes inst_vars = instance_variables.map { |i| i.to_s[1..-1].to_sym } attrs = (self.class.attributes + self.class.composed_attributes) & inst_vars attrs.inject({}) do |memo, (k, v)| - memo[k] = send(k) + memo[k] = TypeCaster.typecast(k, send(k), self.class.name) memo end end diff --git a/lib/deja/relationship.rb b/lib/deja/relationship.rb index 66f70d7..ab23c96 100644 --- a/lib/deja/relationship.rb +++ b/lib/deja/relationship.rb @@ -109,7 +109,7 @@ def persisted_attributes inst_vars = instance_variables.map { |i| i.to_s[1..-1].to_sym } attrs = self.class.attributes & inst_vars attrs.inject({}) do |memo, (k, v)| - memo[k] = send(k) + memo[k] = TypeCaster.typecast(k, send(k), self.class.name) memo end end diff --git a/lib/deja/schema_generator.rb b/lib/deja/schema_generator.rb index cc583eb..d7e4cbb 100644 --- a/lib/deja/schema_generator.rb +++ b/lib/deja/schema_generator.rb @@ -29,14 +29,8 @@ def attribute(name, type, opts = {}) def attr_accessorize(name, opts) send(:attr_accessor, name) define_attribute_methods name - define_method("#{name}") do - #reversecast(name, instance_variable_get("@#{name}")) - instance_variable_get("@#{name}") - end define_method("#{name}=") do |new_value| - casted_value = typecast(name, new_value) - send("#{name}_will_change!") if (casted_value != instance_variable_get("@#{name}") && !instance_variable_get("@#{name}").nil?) - #instance_variable_set("@#{name}", casted_value) + send("#{name}_will_change!") if (new_value != instance_variable_get("@#{name}") && !instance_variable_get("@#{name}").nil?) instance_variable_set("@#{name}", new_value) end end diff --git a/lib/deja/type_caster.rb b/lib/deja/type_caster.rb index db979fd..3614dbd 100644 --- a/lib/deja/type_caster.rb +++ b/lib/deja/type_caster.rb @@ -2,46 +2,52 @@ module Deja module TypeCaster extend ActiveSupport::Concern - included do - # cast back to Ruby objects where representation in the graph is different - def reversecast(attr_name, value) - return nil if value.nil? + # cast back to Ruby objects where representation in the graph is different + def self.reversecast(attr_name, value, klass) + return nil if value.nil? - data_type = self.class.schema[:attributes][attr_name][:type].to_s + data_type = klass.constantize.schema[:attributes][attr_name][:type].to_s - case data_type - when 'Date' - Date.parse(value.to_s) - when 'Time' - Time.at(value) - else - value - end + case data_type + when 'Integer' + value.to_i + when 'Float' + value.to_f + when 'String' + value.to_s + when 'Deja::Boolean' + Boolean.true?(value) + when 'Date' + Date.parse(value.to_s) + when 'Time' + Time.at(value) + else + value end + end - # cast to neo4j basic types and raise errors when invalid/unrecognized data type - def typecast(attr_name, value) - return nil if value.nil? + # cast to neo4j basic types and raise errors when invalid/unrecognized data type + def self.typecast(attr_name, value, klass) + return nil if value.nil? - data_type = self.class.schema[:attributes][attr_name][:type].to_s + data_type = klass.constantize.schema[:attributes][attr_name][:type].to_s - case data_type - when 'Integer' - Integer(value) - when 'Float' - Float(value) - when 'Deja::Boolean' - raise TypeError, "invalid boolean value passed in: '#{value}'" unless Boolean.boolean?(value) - Boolean.true?(value) - when 'String' - String(value) - when 'Date' - Date.parse(value.to_s).strftime("%Y%m%d").to_i - when 'Time' - value.to_i - else - raise TypeError, "undefined data type #{data_type} for attribute #{name}" - end + case data_type + when 'Integer' + Integer(value) + when 'Float' + Float(value) + when 'Deja::Boolean' + raise TypeError, "invalid boolean value passed in: '#{value}'" unless Boolean.boolean?(value) + Boolean.true?(value) + when 'String' + String(value) + when 'Date' + Date.parse(value.to_s).strftime("%Y%m%d").to_i + when 'Time' + value.to_i + else + raise TypeError, "undefined data type #{data_type} for attribute #{name}" end end end diff --git a/spec/type_caster_spec.rb b/spec/type_caster_spec.rb index 342a446..1807dc0 100644 --- a/spec/type_caster_spec.rb +++ b/spec/type_caster_spec.rb @@ -8,71 +8,71 @@ context 'typecast' do it 'should typecast a valid string' do person.name = 'james bond' - expect(person.name).to eq('james bond') + expect(TypeCaster.typecast(:name, person.name, 'Person')).to eq('james bond') end it 'should typecast a valid integer' do person.age = '-5' - expect(person.age).to eq(-5) + expect(TypeCaster.typecast(:age, person.age, 'Person')).to eq(-5) person.age = 10 - expect(person.age).to eq(10) + expect(TypeCaster.typecast(:age, person.age, 'Person')).to eq(10) end it 'should fail to cast an invalid integer' do - expect { person.age = '5a' }.to raise_error + expect(TypeCaster.typecast(:age, person.age, 'Person')).to raise_error end it 'should cast a valid float' do person.bank_balance = 50000.51 - expect(person.bank_balance).to eq(50000.51) + expect(TypeCaster.typecast(:bank_balance, person.bank_balance, 'Person')).to eq(50000.51) person.bank_balance = '10.25' - expect(person.bank_balance).to eq(10.25) + expect(TypeCaster.typecast(:bank_balance, person.bank_balance, 'Person')).to eq(10.25) end it 'should fail to cast an invalid floating point value' do - expect { person.bank_balance = '5a' }.to raise_error + expect{TypeCaster.typecast(:bank_balance, '5a', 'Person')}.to raise_error end it 'should cast a valid boolean value' do person.vip = 'true' - expect(person.vip).to eq(true) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(true) person.vip = 't' - expect(person.vip).to eq(true) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(true) person.vip = true - expect(person.vip).to eq(true) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(true) person.vip = 1 - expect(person.vip).to eq(true) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(true) person.vip = 'false' - expect(person.vip).to eq(false) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(false) person.vip = 'f' - expect(person.vip).to eq(false) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(false) person.vip = false - expect(person.vip).to eq(false) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(false) person.vip = 0 - expect(person.vip).to eq(false) + expect(TypeCaster.typecast(:vip, person.vip, 'Person')).to eq(false) end it 'should fail to cast an invalid boolean value' do - expect { person.vip = 'tru1' }.to raise_error TypeError + expect { TypeCaster.typecast(:vip, 'tru1', 'Person') }.to raise_error TypeError end it 'should fail on unrecognized data type' do - expect { person.tags = ['engineer', 'designer', 'entrepreneur'] }.to raise_error TypeError + expect { TypeCaster.typecast(:tags, ['engineer', 'designer', 'entrepreneur'], 'Person') }.to raise_error TypeError end it 'should validate date values' do Timecop.freeze do person.born_on = Date.today - expect(person.instance_variable_get("@born_on")).to eq(Date.today.strftime("%Y%m%d").to_i) + expect(person.instance_variable_get("@born_on")).to eq(Date.today) expect(person.born_on).to eq(Date.today) end end @@ -80,7 +80,7 @@ it 'should validate time values' do Timecop.freeze do person.knighted_at = Time.now - expect(person.instance_variable_get("@knighted_at")).to eq(Time.now.to_i) + expect(person.instance_variable_get("@knighted_at")).to eq(Time.now) expect(person.knighted_at.to_i).to eq(Time.now.to_i) end end