From 88165e9a01c243d5d673e27c51cb966ff3c4708f Mon Sep 17 00:00:00 2001 From: Dustin Zeisler Date: Sat, 8 Feb 2014 16:07:39 -0800 Subject: [PATCH 1/2] Feature for field method, passing a type option will accepts any active record sql types and type casts the value when it is set. --- lib/active_hash/base.rb | 7 ++++--- spec/active_hash/base_spec.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/active_hash/base.rb b/lib/active_hash/base.rb index dfac2188..7a12e09c 100644 --- a/lib/active_hash/base.rb +++ b/lib/active_hash/base.rb @@ -200,7 +200,7 @@ def field(field_name, options = {}) field_names << field_name define_getter_method(field_name, options[:default]) - define_setter_method(field_name) + define_setter_method(field_name, options[:type]) define_interrogator_method(field_name) define_custom_find_method(field_name) define_custom_find_all_method(field_name) @@ -265,11 +265,12 @@ def define_getter_method(field, default_value) private :define_getter_method - def define_setter_method(field) + def define_setter_method(field, sql_type) method_name = "#{field}=" unless has_instance_method?(method_name) define_method(method_name) do |new_val| - attributes[field] = new_val + column = ActiveRecord::ConnectionAdapters::Column.new(field, nil, sql_type, false) + attributes[field] = column.type_cast(new_val) end end end diff --git a/spec/active_hash/base_spec.rb b/spec/active_hash/base_spec.rb index 2b4c6067..339fcc77 100644 --- a/spec/active_hash/base_spec.rb +++ b/spec/active_hash/base_spec.rb @@ -634,6 +634,41 @@ class Region < ActiveHash::Base country.name.should == "foobar" end end + + context "for fields with type values" do + + before do + Country.field :population, :type => 'integer', :default => false + end + + context "entered values will be converted to set type" do + + it 'when pass to new' do + country = Country.new(:population => "500000") + country.population.should == 500000 + end + + it 'when pass to setter' do + country = Country.new + country.population = '500000' + country.population.should == 500000 + end + + end + + context 'valid types, accept ActiveRecord sql types' do + + before do + Country.field :independence_day, :type => 'date', :default => false + end + + it 'date' do + country = Country.new(:independence_day => "01/12/1934") + country.independence_day.class.should == Date + end + end + + end end describe "interrogator methods" do From aa3cc2e1ab3d9a856890c34867d8c5d0fcc3c056 Mon Sep 17 00:00:00 2001 From: Dustin Zeisler Date: Sun, 9 Feb 2014 11:40:37 -0800 Subject: [PATCH 2/2] Implement Type casting using the Virtus gem. --- Gemfile | 1 + gemfiles/rails_2.3.gemfile | 1 + gemfiles/rails_3.0.gemfile | 1 + gemfiles/rails_3.1.gemfile | 1 + gemfiles/rails_3.2.gemfile | 1 + gemfiles/rails_4.0.gemfile | 1 + gemfiles/rails_edge.gemfile | 1 + lib/active_hash.rb | 1 + lib/active_hash/base.rb | 24 +++++++++++++++++++----- spec/active_hash/base_spec.rb | 6 +++--- 10 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 7b47c2bd..be253c9d 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ gem 'rspec', '~> 2.2.0' gem 'wwtd' gem 'rake' gem 'json' +gem 'virtus' platforms :jruby do gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6' diff --git a/gemfiles/rails_2.3.gemfile b/gemfiles/rails_2.3.gemfile index 8d6d541e..bbf4fe09 100644 --- a/gemfiles/rails_2.3.gemfile +++ b/gemfiles/rails_2.3.gemfile @@ -5,6 +5,7 @@ gem 'rspec', '~> 2.2.0' gem 'wwtd' gem 'rake' gem 'json' +gem 'virtus' platform :jruby do gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6' diff --git a/gemfiles/rails_3.0.gemfile b/gemfiles/rails_3.0.gemfile index 22341787..e3d6437a 100644 --- a/gemfiles/rails_3.0.gemfile +++ b/gemfiles/rails_3.0.gemfile @@ -5,6 +5,7 @@ gem 'rspec', '~> 2.2.0' gem 'wwtd' gem 'rake' gem 'json' +gem 'virtus' platform :jruby do gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6' diff --git a/gemfiles/rails_3.1.gemfile b/gemfiles/rails_3.1.gemfile index f8b0a6fa..c1485d15 100644 --- a/gemfiles/rails_3.1.gemfile +++ b/gemfiles/rails_3.1.gemfile @@ -5,6 +5,7 @@ gem 'rspec', '~> 2.2.0' gem 'wwtd' gem 'rake' gem 'json' +gem 'virtus' platform :jruby do gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6' diff --git a/gemfiles/rails_3.2.gemfile b/gemfiles/rails_3.2.gemfile index 8d2185c5..7fca5be1 100644 --- a/gemfiles/rails_3.2.gemfile +++ b/gemfiles/rails_3.2.gemfile @@ -5,6 +5,7 @@ gem 'rspec', '~> 2.2.0' gem 'wwtd' gem 'rake' gem 'json' +gem 'virtus' platform :jruby do gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6' diff --git a/gemfiles/rails_4.0.gemfile b/gemfiles/rails_4.0.gemfile index 203cde08..2f8c1cda 100644 --- a/gemfiles/rails_4.0.gemfile +++ b/gemfiles/rails_4.0.gemfile @@ -5,6 +5,7 @@ gem 'rspec', '~> 2.2.0' gem 'wwtd' gem 'rake' gem 'json' +gem 'virtus' platform :jruby do gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6' diff --git a/gemfiles/rails_edge.gemfile b/gemfiles/rails_edge.gemfile index eb17871b..9e0add0f 100644 --- a/gemfiles/rails_edge.gemfile +++ b/gemfiles/rails_edge.gemfile @@ -6,6 +6,7 @@ gem 'rspec', '~> 2.2.0' gem 'wwtd' gem 'rake' gem 'json' +gem 'virtus' platform :jruby do gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6' diff --git a/lib/active_hash.rb b/lib/active_hash.rb index bedd6194..abc68c95 100644 --- a/lib/active_hash.rb +++ b/lib/active_hash.rb @@ -20,3 +20,4 @@ require 'active_json/base' require 'associations/associations' require 'enum/enum' +require 'virtus' diff --git a/lib/active_hash/base.rb b/lib/active_hash/base.rb index 7a12e09c..268a8b85 100644 --- a/lib/active_hash/base.rb +++ b/lib/active_hash/base.rb @@ -39,6 +39,14 @@ def field_names @field_names ||= [] end + def types + @types ||= {} + end + + def set_type(field_name, type) + types[field_name] = Virtus::Attribute.build(type) + end + def the_meta_class class << self self @@ -199,8 +207,9 @@ def field(field_name, options = {}) validate_field(field_name) field_names << field_name + set_type(field_name, options[:type]) define_getter_method(field_name, options[:default]) - define_setter_method(field_name, options[:type]) + define_setter_method(field_name) define_interrogator_method(field_name) define_custom_find_method(field_name) define_custom_find_all_method(field_name) @@ -265,12 +274,11 @@ def define_getter_method(field, default_value) private :define_getter_method - def define_setter_method(field, sql_type) + def define_setter_method(field) method_name = "#{field}=" unless has_instance_method?(method_name) define_method(method_name) do |new_val| - column = ActiveRecord::ConnectionAdapters::Column.new(field, nil, sql_type, false) - attributes[field] = column.type_cast(new_val) + attributes[field] = coerce(field, new_val) end end end @@ -375,7 +383,7 @@ def has_singleton_method?(name) end - attr_reader :attributes + attr_reader :attributes, :types def initialize(attributes = {}) attributes.symbolize_keys! @@ -471,5 +479,11 @@ def marked_for_destruction? false end + def coerce(field, new_val) + type = self.class.types[field] + return attributes[field] = type.coerce(new_val) unless type.nil? + return new_value + end + end end diff --git a/spec/active_hash/base_spec.rb b/spec/active_hash/base_spec.rb index 339fcc77..c51461ed 100644 --- a/spec/active_hash/base_spec.rb +++ b/spec/active_hash/base_spec.rb @@ -638,7 +638,7 @@ class Region < ActiveHash::Base context "for fields with type values" do before do - Country.field :population, :type => 'integer', :default => false + Country.field :population, :type => 'Integer', :default => false end context "entered values will be converted to set type" do @@ -656,10 +656,10 @@ class Region < ActiveHash::Base end - context 'valid types, accept ActiveRecord sql types' do + context 'valid types, accepts Ruby types' do before do - Country.field :independence_day, :type => 'date', :default => false + Country.field :independence_day, :type => 'Date', :default => false end it 'date' do