Last active
August 29, 2015 14:21
-
-
Save jeremyf/7aaa6d9858dd743a6719 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Sipity | |
module Decorators | |
# Provides a convenience wrapper of an object to assist in equality testing. | |
# This is key as it relates to PowerConverter and how it is used. | |
class ComparableSimpleDelegator < SimpleDelegator | |
class_attribute :base_class, instance_writer: false | |
def initialize(object, localization_assistant: default_localization_assistant) | |
super(object) | |
self.localization_assistant = localization_assistant | |
end | |
# Yup, I'm delegating the #class method to the localization assistant | |
# Because Rails uses `object.class.human_attribute_name` or | |
# `object.class.model_name` with great fervor. | |
delegate :model_name, :class, to: :localization_assistant | |
delegate :base_class, to: :singleton_class | |
class << self | |
def ===(other) | |
super || base_class === other | |
end | |
end | |
def is_a?(classification) | |
# REVIEW: Is base_class == classification a reasonable assumption? | |
# Thinking in terms of Liskov's Substitution this may be necessary. | |
super || __getobj__.is_a?(classification) || base_class == classification | |
end | |
alias_method :kind_of?, :is_a? | |
private | |
attr_accessor :localization_assistant | |
def default_localization_assistant | |
Decorators::LocalizationAssistant.new(base_class: base_class, decorating_class: singleton_class) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Sipity | |
module Decorators | |
# The Localization Assistant must double as the object's class. | |
# | |
# One reason for these antics is that Rails translations make heavy use | |
# of the class method `ActiveModel::Base.human_attribute_name`. This is | |
# painful because in order to circumvent column name translations, I must | |
# override the object's `#class` instance method. | |
# | |
# This object is here to help with that. It provides a pattern for stepping | |
# in and helping with translations. | |
# | |
# @example | |
# | |
# class Book < ActiveRecord::Base | |
# def initialize(*args, &block) | |
# super | |
# @localization_assistant = Sipity::Decorators::LocalizationAssistant.new( | |
# decorating_class: singleton_class, base_class: CreativeWork | |
# ) | |
# end | |
# delegate :class, :model_name, to: :@localization_assistant | |
# end | |
# | |
class LocalizationAssistant | |
# I want a clean room that delegates most of its responsibilities down to | |
# the associated singleton_class. | |
# | |
# A BasicObject doesn't work because :respond_to? gets obliterated in that | |
# implementation. | |
METHOD_NAMES_TO_KEEP = [:respond_to?, :__id__, :__send__, :send, :public_send].freeze | |
instance_methods.each do |method_name| | |
undef_method(method_name) unless METHOD_NAMES_TO_KEEP.include?(method_name) | |
end | |
# Given how this is to be used, I believe the :decorating_class will need | |
# to be the initializing object's #singleton_class; See the above example. | |
def initialize(decorating_class:, base_class:) | |
self.decorating_class = decorating_class | |
self.base_class = base_class | |
end | |
# REVIEW: do I want the given model name to be a wrapper for the base_class's | |
# model_name? The reason I'm curious is if we want a custom param_key for the | |
# given object? | |
delegate :human_attribute_name, :model_name, to: :base_class | |
# Included because developers should know what the heck this object is. | |
def inspect | |
format( | |
'#<%s:%#0x @decorating_class=%s @base_class=%s>', | |
"Sipity::Decorators::LocalizationAssistant", | |
__id__, | |
decorating_class.inspect, | |
base_class.inspect | |
) | |
end | |
# As per the above example, I want to be able to delegate an object's | |
# #class instance_method to an instance of this object. | |
def class | |
self | |
end | |
attr_reader :base_class, :decorating_class | |
private | |
attr_writer :base_class, :decorating_class | |
def method_missing(method_name, *args, &block) | |
decorating_class.send(method_name, *args, &block) | |
end | |
def respond_to_missing?(*args) | |
decorating_class.respond_to?(*args) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment