Skip to content

Commit

Permalink
Merge pull request #43 from brianstien/remove_active_attr_typecasting
Browse files Browse the repository at this point in the history
Remove active attr typecasting
  • Loading branch information
liveh2o authored Jun 17, 2016
2 parents c4fe798 + dc63d44 commit 948a813
Show file tree
Hide file tree
Showing 16 changed files with 247 additions and 21 deletions.
8 changes: 8 additions & 0 deletions lib/active_remote/attributes.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
module ActiveRemote
module Attributes
def attributes
@attributes ||= begin
attribute_names = self.class.attribute_names
Hash[attribute_names.map { |key| [key, send(key)] }]
end
@attributes.dup
end

# Read attribute from the attributes hash
#
def read_attribute(name)
Expand Down
16 changes: 14 additions & 2 deletions lib/active_remote/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@
require 'active_remote/scope_keys'
require 'active_remote/search'
require 'active_remote/serialization'
require 'active_remote/typecasting'
require 'active_remote/validations'

module ActiveRemote
class Base
extend ActiveModel::Callbacks

include ActiveAttr::Model
include ActiveAttr::BasicModel
include ActiveAttr::BlockInitialization
include ActiveAttr::Logger
include ActiveAttr::MassAssignment
include ActiveAttr::AttributeDefaults
include ActiveAttr::QueryAttributes
include ActiveAttr::Serialization

include Association
include Attributes
Expand All @@ -35,6 +42,7 @@ class Base
include ScopeKeys
include Search
include Serialization
include Typecasting

# Overrides some methods, providing support for dirty tracking,
# so it needs to be included last.
Expand All @@ -49,7 +57,11 @@ class Base
define_model_callbacks :initialize, :only => :after

def initialize(*)
@attributes ||= {}
@attributes ||= begin
attribute_names = self.class.attribute_names
Hash[attribute_names.map { |key| [key, send(key)] }]
end

@new_record = true

skip_dirty_tracking do
Expand Down
37 changes: 18 additions & 19 deletions lib/active_remote/serializers/protobuf.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
require "active_remote/typecasting"

module ActiveRemote
module Serializers
module Protobuf
extend ActiveSupport::Concern

TYPECASTER_MAP = {
::Protobuf::Field::BoolField => ActiveAttr::Typecasting::BooleanTypecaster,
::Protobuf::Field::BytesField => ActiveAttr::Typecasting::StringTypecaster,
::Protobuf::Field::DoubleField => ActiveAttr::Typecasting::FloatTypecaster,
::Protobuf::Field::Fixed32Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::Fixed64Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::FloatField => ActiveAttr::Typecasting::FloatTypecaster,
::Protobuf::Field::Int32Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::Int64Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sfixed32Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sfixed64Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sint32Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sint64Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::StringField => ActiveAttr::Typecasting::StringTypecaster,
::Protobuf::Field::Uint32Field => ActiveAttr::Typecasting::IntegerTypecaster,
::Protobuf::Field::Uint64Field => ActiveAttr::Typecasting::IntegerTypecaster
::Protobuf::Field::BoolField => ActiveRemote::Typecasting::BooleanTypecaster,
::Protobuf::Field::BytesField => ActiveRemote::Typecasting::StringTypecaster,
::Protobuf::Field::DoubleField => ActiveRemote::Typecasting::FloatTypecaster,
::Protobuf::Field::Fixed32Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::Fixed64Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::FloatField => ActiveRemote::Typecasting::FloatTypecaster,
::Protobuf::Field::Int32Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::Int64Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sfixed32Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sfixed64Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sint32Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::Sint64Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::StringField => ActiveRemote::Typecasting::StringTypecaster,
::Protobuf::Field::Uint32Field => ActiveRemote::Typecasting::IntegerTypecaster,
::Protobuf::Field::Uint64Field => ActiveRemote::Typecasting::IntegerTypecaster
}

module ClassMethods
Expand Down Expand Up @@ -127,10 +129,7 @@ def typecasted_value
end

def typecaster
@typecaster ||= begin
typecaster = TYPECASTER_MAP[field.type_class]
typecaster.new if typecaster
end
@typecaster ||= TYPECASTER_MAP[field.type_class]
end

def typecaster?
Expand Down
60 changes: 60 additions & 0 deletions lib/active_remote/typecasting.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require "active_remote/typecasting/big_decimal_typecaster"
require "active_remote/typecasting/boolean"
require "active_remote/typecasting/boolean_typecaster"
require "active_remote/typecasting/date_time_typecaster"
require "active_remote/typecasting/date_typecaster"
require "active_remote/typecasting/float_typecaster"
require "active_remote/typecasting/integer_typecaster"
require "active_remote/typecasting/object_typecaster"
require "active_remote/typecasting/string_typecaster"

module ActiveRemote
module Typecasting
extend ActiveSupport::Concern

TYPECASTER_MAP = {
BigDecimal => ::ActiveRemote::Typecasting::BigDecimalTypecaster,
Boolean => ::ActiveRemote::Typecasting::BooleanTypecaster,
Date => ::ActiveRemote::Typecasting::DateTypecaster,
DateTime => ::ActiveRemote::Typecasting::DateTimeTypecaster,
Float => ::ActiveRemote::Typecasting::FloatTypecaster,
Integer => ::ActiveRemote::Typecasting::IntegerTypecaster,
Object => ::ActiveRemote::Typecasting::ObjectTypecaster,
String => ::ActiveRemote::Typecasting::StringTypecaster
}.freeze

private

def attribute=(name, value)
return super if value.nil?

typecaster = _attribute_typecaster(name)
return super unless typecaster

super(name, typecaster.call(value))
end

def _attribute_typecaster(attribute_name)
self.class.attributes[attribute_name][:typecaster] || _typecaster_for(attribute_name)
end

def _typecaster_for(attribute_name)
type = self.class.attributes[attribute_name][:type]
return nil unless type

TYPECASTER_MAP[type]
end

module ClassMethods
def inspect
inspected_attributes = attribute_names.sort.map { |name| "#{name}: #{_attribute_type(name)}" }
attributes_list = "(#{inspected_attributes.join(", ")})" unless inspected_attributes.empty?
"#{name}#{attributes_list}"
end

def _attribute_type(attribute_name)
attributes[attribute_name][:type] || Object
end
end
end
end
21 changes: 21 additions & 0 deletions lib/active_remote/typecasting/big_decimal_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require "bigdecimal"
require "bigdecimal/util"
require "active_support/core_ext/big_decimal/conversions"

module ActiveRemote
module Typecasting
class BigDecimalTypecaster
def self.call(value)
if value.is_a?(BigDecimal)
value
elsif value.is_a?(Rational)
value.to_f.to_d
elsif value.respond_to?(:to_d)
value.to_d
else
BigDecimal.new(value.to_s)
end
end
end
end
end
7 changes: 7 additions & 0 deletions lib/active_remote/typecasting/boolean.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module ActiveRemote
module Typecasting
# For use in :type dsl option
class Boolean
end
end
end
15 changes: 15 additions & 0 deletions lib/active_remote/typecasting/boolean_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module ActiveRemote
module Typecasting
class BooleanTypecaster
FALSE_VALUES = ["n", "N", "no", "No", "NO", "false", "False", "FALSE", "off", "Off", "OFF", "f", "F"]

def self.call(value)
case value
when *FALSE_VALUES then false
when Numeric, /^\-?[0-9]/ then !value.to_f.zero?
else value.present?
end
end
end
end
end
9 changes: 9 additions & 0 deletions lib/active_remote/typecasting/date_time_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module ActiveRemote
module Typecasting
class DateTimeTypecaster
def self.call(value)
value.to_datetime if value.respond_to?(:to_datetime)
end
end
end
end
10 changes: 10 additions & 0 deletions lib/active_remote/typecasting/date_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module ActiveRemote
module Typecasting
class DateTypecaster
def self.call(value)
value.to_date if value.respond_to?(:to_date)
rescue NoMethodError, ArgumentError
end
end
end
end
9 changes: 9 additions & 0 deletions lib/active_remote/typecasting/float_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module ActiveRemote
module Typecasting
class FloatTypecaster
def self.call(value)
value.to_f if value.respond_to?(:to_f)
end
end
end
end
10 changes: 10 additions & 0 deletions lib/active_remote/typecasting/integer_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module ActiveRemote
module Typecasting
class IntegerTypecaster
def self.call(value)
value.to_i if value.respond_to?(:to_i)
rescue FloatDomainError
end
end
end
end
9 changes: 9 additions & 0 deletions lib/active_remote/typecasting/object_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module ActiveRemote
module Typecasting
class ObjectTypecaster
def self.call(value)
value
end
end
end
end
9 changes: 9 additions & 0 deletions lib/active_remote/typecasting/string_typecaster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module ActiveRemote
module Typecasting
class StringTypecaster
def self.call(value)
value.to_s if value.respond_to?(:to_s)
end
end
end
end
39 changes: 39 additions & 0 deletions spec/lib/active_remote/typecasting_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'spec_helper'
describe ::ActiveRemote::Typecasting do
let(:test_class) { ::TypecastedAuthor }

describe "boolean" do
it "casts to boolean" do
record = test_class.new(:writes_fiction => "no")
expect(record.writes_fiction).to eq(false)
end
end

describe "datetime" do
it "casts to datetime" do
record = test_class.new(:birthday => "2016-01-01")
expect(record.birthday).to eq(DateTime.parse("2016-01-01"))
end
end

describe "float" do
it "casts to float" do
record = test_class.new(:net_sales => "2000.20")
expect(record.net_sales).to eq(2000.2)
end
end

describe "integer" do
it "casts to integer" do
record = test_class.new(:age => "40")
expect(record.age).to eq(40)
end
end

describe "string" do
it "casts to string" do
record = test_class.new(:guid => 1000)
expect(record.guid).to eq("1000")
end
end
end
1 change: 1 addition & 0 deletions spec/support/models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
require 'support/models/category'
require 'support/models/post'
require 'support/models/tag'
require 'support/models/typecasted_author'
8 changes: 8 additions & 0 deletions spec/support/models/typecasted_author.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class TypecastedAuthor < ::ActiveRemote::Base
attribute :guid, :type => String
attribute :name, :typecaster => StringTypecaster
attribute :age, :type => Integer
attribute :birthday, :type => DateTime
attribute :writes_fiction, :type => Boolean
attribute :net_sales, :type => Float
end

0 comments on commit 948a813

Please sign in to comment.