Skip to content

Instantly share code, notes, and snippets.

@TomTriple
Created Feb 28, 2011
Embed
What would you like to do?
# See Mike Perham's proposal on ticket: https://rails.lighthouseapp.com/projects/8994/tickets/6477-activemodel-transformations
# Chainable Transformations API
require "active_support/core_ext/string/inflections"
require "active_support/concern"
module ActiveModel
module Transformers
extend ActiveSupport::Concern
module ClassMethods
def transform(*args, &block)
name = args[0]
define_method :"#{name}=" do |attr_value|
@_transform_chain ||= {}
@_transform_attribute = name
@_transform_chain[@_transform_attribute] = Transformers::TransformerChain.new(attr_value)
if(args.length == 1)
instance_eval(&block)
else
args.delete_at(0)
instance_eval do
args.each do |arg|
with(arg)
end
end
end
write_attribute(name, @_transform_chain[@_transform_attribute].transform)
end
end
end
module InstanceMethods
def with(sym_or_block, options = {})
if sym_or_block.is_a?(Symbol)
transformer = Transformers.const_get(:"#{sym_or_block.to_s.camelize}Transformer").new(options)
else
transformer = AnonTransformer.new(sym_or_block)
end
@_transform_chain[@_transform_attribute] << transformer
end
end
class StripTransformer
def initialize(options)
@options = {
:r => true,
:l => true
}.merge(options)
end
def transform(value)
case @options
when {:r => true, :l => true} then value.strip
when {:r => false, :l => true} then value.lstrip
when {:r => true, :l => false} then value.rstrip
end
end
end
class DigitTransformer
def initialize(options)
end
def transform(value)
value.gsub(/\D/, '')
end
end
class AnonTransformer
def initialize(block)
@block = block
end
def transform(value)
@block.call(value)
end
end
class TransformerChain
def initialize(value)
@ops = []
@value = value
end
def <<(transformer)
@ops << transformer
end
def transform
@ops.inject(@value) {|acc, t| t.transform(acc)}
end
end
end
end
class Asset < ActiveRecord::Base
include ActiveModel::Transformers
# Usage examples for chainable transformer API. UseCases:
# - builtin transformer without customized options
# - builtin transformer with customized options
# - custom transformer (as a lambda)
# API for builtin transformers relying on default-options
transform :age, :digit, :strip
# API for builtin transformer with customized options and a custom transformer
transform :name do
with :strip, :l => false
with lambda {|value| "custom transformer: ...#{value}..."}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment