Skip to content

Instantly share code, notes, and snippets.

@mgmarlow
Last active December 2, 2020 00:36
Show Gist options
  • Save mgmarlow/f52ad2dc27aa9942e0ab96b9348a10de to your computer and use it in GitHub Desktop.
Save mgmarlow/f52ad2dc27aa9942e0ab96b9348a10de to your computer and use it in GitHub Desktop.
Model Translation Layer
require 'ostruct'
class String
def underscore
self.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end
class ApiService
def self.call
{
"Id" => 1234,
"ServiceName" => "foo-bar",
"Description" => "foo bar baz",
"IgnoredAttribute" => 5678
}
end
end
# ModelTranslator has two jobs:
#
# 1. Translate CamelCase string keys to underscore symbols.
# 2. Set up hooks for model translators to have more control over
# their underlying data structure.
#
module ModelTranslator
def self.included(base)
base.class_eval do
extend ClassMethods
attr_accessor :attributes
end
end
module ClassMethods
def rename(attr, options={})
renamed_attributes[attr] = options[:to]
end
def ignore(*attr)
ignored_attributes.concat(attr)
end
def call(raw_data)
new(raw_data).attributes
end
def renamed_attributes
@renamed_attributes ||= {}
end
def ignored_attributes
@ignored_attributes ||= []
end
end
def initialize(raw_data)
@attributes = raw_data.each_with_object({}) do |(k, v), h|
key = k.underscore.to_sym
next if self.class.ignored_attributes.include?(key)
attribute_name = self.class.renamed_attributes[key] || key
h[attribute_name] = v
end
end
end
# Can be defined for each model, or models can just use the default ModelTranslator
class ServiceTranslator
include ModelTranslator
rename :service_name, to: :service
ignore :ignored_attribute
end
# Pretend this is an ActiveRecord class
class ServiceModel < OpenStruct
attr_accessor :id, :name, :service
end
raw_data = ApiService.call
# When instantiating models, translation must be opted into by using the
# corresponding translator class.
model = ServiceModel.new(ServiceTranslator.call(raw_data))
puts model
# => #<ServiceModel id=1234, service="foo-bar", description="foo bar baz">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment