class Moneta::Transformer
Transforms keys and values (Marshal, YAML, JSON, Base64, MD5, …). You can bypass the transformer (e.g. serialization) by using the `:raw` option.
@example Add `Moneta::Transformer` to proxy stack
Moneta.build do transformer :key => [:marshal, :escape], :value => [:marshal] adapter :File, :dir => 'data' end
@example Bypass serialization
store.store('key', 'value', :raw => true) store['key'] # raises an Exception store.load('key', :raw => true) # returns 'value' store['key'] = 'value' store.load('key', :raw => true) # returns "\x04\bI\"\nvalue\x06:\x06ET"
@api public
Constants
- KEY_TRANSFORMER
Allowed key transformers (Read it like a regular expression!)
- TRANSFORMER
Available key/value transformers
- VALUE_TRANSFORMER
Allowed value transformers (Read it like a regular expression!)
Public Class Methods
@param [Moneta store] adapter The underlying store @param [Hash] options @return [Transformer] new Moneta transformer @option options [Array] :key List of key transformers in the order in which they should be applied @option options [Array] :value List of value transformers in the order in which they should be applied @option options [String] :prefix Prefix string for key namespacing (Used by the :prefix key transformer) @option options [String] :secret HMAC secret to verify values (Used by the :hmac value transformer) @option options [Integer] :maxlen Maximum key length (Used by the :truncate key transformer)
# File lib/moneta/transformer.rb, line 32 def new(adapter, options = {}) keys = [options[:key]].flatten.compact values = [options[:value]].flatten.compact raise ArgumentError, 'Option :key or :value is required' if keys.empty? && values.empty? options[:prefix] ||= '' if keys.include?(:prefix) name = class_name(keys, values) const_set(name, compile(keys, values)) unless const_defined?(name) const_get(name).original_new(adapter, options) end
Private Class Methods
# File lib/moneta/transformer.rb, line 183 def class_name(keys, values) (keys.empty? ? '' : keys.map(&:to_s).map(&:capitalize).join + 'Key') + (values.empty? ? '' : values.map(&:to_s).map(&:capitalize).join + 'Value') end
# File lib/moneta/transformer.rb, line 44 def compile(keys, values) @key_validator ||= compile_validator(KEY_TRANSFORMER) @value_validator ||= compile_validator(VALUE_TRANSFORMER) raise ArgumentError, 'Invalid key transformer chain' if @key_validator !~ keys.map(&:inspect).join raise ArgumentError, 'Invalid value transformer chain' if @value_validator !~ values.map(&:inspect).join klass = Class.new(self) klass.class_eval " def initialize(adapter, options = {}) super #{compile_initializer('key', keys)} #{compile_initializer('value', values)} end ", __FILE__, __LINE__ key, key_opts = compile_transformer(keys, 'key') dump, dump_opts = compile_transformer(values, 'value') load, load_opts = compile_transformer(values.reverse, 'value', 1) if values.empty? compile_key_transformer(klass, key, key_opts) elsif keys.empty? compile_value_transformer(klass, load, load_opts, dump, dump_opts) else compile_key_value_transformer(klass, key, key_opts, load, load_opts, dump, dump_opts) end klass end
Compile option initializer
# File lib/moneta/transformer.rb, line 150 def compile_initializer(type, transformers) transformers.map do |name| t = TRANSFORMER[name] (t[1].to_s + t[2].to_s).scan(/@\w+/).uniq.map do |opt| "raise ArgumentError, \"Option #{opt[1..-1]} is required for #{name} #{type} transformer\" unless #{opt} = options[:#{opt[1..-1]}]\n" end end.join("\n") end
# File lib/moneta/transformer.rb, line 80 def compile_key_transformer(klass, key, key_opts) klass.class_eval " def key?(key, options = {}) @adapter.key?(#{key}, #{without key_opts}) end def increment(key, amount = 1, options = {}) @adapter.increment(#{key}, amount, #{without key_opts}) end def load(key, options = {}) @adapter.load(#{key}, #{without :raw, key_opts}) end def store(key, value, options = {}) @adapter.store(#{key}, value, #{without :raw, key_opts}) end def delete(key, options = {}) @adapter.delete(#{key}, #{without :raw, key_opts}) end def create(key, value, options = {}) @adapter.create(#{key}, value, #{without :raw, key_opts}) end ", __FILE__, __LINE__ end
# File lib/moneta/transformer.rb, line 123 def compile_key_value_transformer(klass, key, key_opts, load, load_opts, dump, dump_opts) klass.class_eval " def key?(key, options = {}) @adapter.key?(#{key}, #{without key_opts}) end def increment(key, amount = 1, options = {}) @adapter.increment(#{key}, amount, #{without key_opts}) end def load(key, options = {}) value = @adapter.load(#{key}, #{without :raw, key_opts, load_opts}) value && !options[:raw] ? #{load} : value end def store(key, value, options = {}) @adapter.store(#{key}, options[:raw] ? value : #{dump}, #{without :raw, key_opts, dump_opts}) value end def delete(key, options = {}) value = @adapter.delete(#{key}, #{without :raw, key_opts, load_opts}) value && !options[:raw] ? #{load} : value end def create(key, value, options = {}) @adapter.create(#{key}, options[:raw] ? value : #{dump}, #{without :raw, key_opts, dump_opts}) end ", __FILE__, __LINE__ end
Returned compiled transformer code string
# File lib/moneta/transformer.rb, line 166 def compile_transformer(transformer, var, i = 2) value, options = var, [] transformer.each do |name| raise ArgumentError, "Unknown transformer #{name}" unless t = TRANSFORMER[name] require t[3] if t[3] code = t[i] options += code.scan(/options\[:(\w+)\]/).flatten value = if t[0] == :serialize && var == 'key' "(tmp = #{value}; String === tmp ? tmp : #{code % 'tmp'})" else code % value end end return value, options end
# File lib/moneta/transformer.rb, line 159 def compile_validator(s) Regexp.new('\A' + s.gsub(/\w+/) do '(' + TRANSFORMER.select {|k,v| v.first.to_s == $& }.map {|v| ":#{v.first}" }.join('|') + ')' end.gsub(/\s+/, '') + '\Z') end
# File lib/moneta/transformer.rb, line 103 def compile_value_transformer(klass, load, load_opts, dump, dump_opts) klass.class_eval " def load(key, options = {}) value = @adapter.load(key, #{without :raw, load_opts}) value && !options[:raw] ? #{load} : value end def store(key, value, options = {}) @adapter.store(key, options[:raw] ? value : #{dump}, #{without :raw, dump_opts}) value end def delete(key, options = {}) value = @adapter.delete(key, #{without :raw, load_opts}) value && !options[:raw] ? #{load} : value end def create(key, value, options = {}) @adapter.create(key, options[:raw] ? value : #{dump}, #{without :raw, dump_opts}) end ", __FILE__, __LINE__ end
# File lib/moneta/transformer.rb, line 75 def without(*options) options = options.flatten.uniq options.empty? ? 'options' : "Utils.without(options, #{options.map(&:to_sym).map(&:inspect).join(', ')})" end