Rails Active Record composed_of
# migration
class CreateCallEvents < ActiveRecord ::Migration [ 5.2 ]
def change
create_table :call_events do |t |
t . jsonb :raw_data
t . string :call_sid , null : false
t . string :caller_phone , null : false
t . string :caller_city
t . string :caller_state
t . string :caller_zipcode
t . string :called_phone , null : false
t . string :called_city
t . string :called_state
t . string :called_zipcode
t . timestamps
end
end
end
# model
# A call event from Twilio Webhook
class CallEvent < ApplicationRecord
validates :raw_data , :call_sid , :caller_phone , :called_phone , presence : true
validates :call_sid , uniqueness : { case_sensitive : false }
[ :caller , :called ] . each do |caller_or_called |
define_method caller_or_called do
# getter
CallEvent ::Phone . new (
phone : send ( "#{ caller_or_called } _phone" ) ,
city : send ( "#{ caller_or_called } _city" ) ,
state : send ( "#{ caller_or_called } _state" ) ,
zipcode : send ( "#{ caller_or_called } _zipcode" )
)
end
# setter
define_method "#{ caller_or_called } =" do |call_event_phone |
raise ArgumentError , "must respond to phone" unless call_event_phone . respond_to? ( :phone )
self [ "#{ caller_or_called } _phone" ] = call_event_phone . phone
self [ "#{ caller_or_called } _city" ] = call_event_phone . city
self [ "#{ caller_or_called } _state" ] = call_event_phone . state
self [ "#{ caller_or_called } _zipcode" ] = call_event_phone . zipcode
end
end
# sub-model for caller and called
class Phone
attr_reader :phone , :city , :state , :zipcode
def initialize ( phone :, city : nil , state : nil , zipcode : nil )
@phone = phone
@city = city
@state = state
@zipcode = zipcode
end
def to_s
phone
end
end
end
private def create_call_event
CallEvent . create! (
raw_data : raw_data ,
call_sid : raw_data [ "CallSid" ] ,
caller : caller_info ,
called : called_info
)
end
private def caller_info
CallEvent ::Phone . new (
phone : raw_data [ "From" ] || raw_data [ "Called" ] ,
city : raw_data [ "FromCity" ] || raw_data [ "CalledCity" ] ,
state : raw_data [ "FromState" ] || raw_data [ "CalledState" ] ,
zipcode : raw_data [ "FromZip" ] || raw_data [ "CalledZip" ]
)
end
private def called_info
CallEvent ::Phone . new (
phone : raw_data [ "To" ] || raw_data [ "Called" ] ,
city : raw_data [ "ToCity" ] || raw_data [ "CalledCity" ] ,
state : raw_data [ "ToState" ] || raw_data [ "CalledState" ] ,
zipcode : raw_data [ "ToZip" ] || raw_data [ "CalledZip" ]
)
end
private def raw_data
@raw_data ||= params . permit ( *PERMITTED_PARAM_KEYS ) . to_h . with_indifferent_access
end