Acts as array lib
Why?
A while ago i needed to store data in a rails model as an array. This is simple enough to do using active record serialize however I wanted a little more. I wanted some getters and setters to set and get as an array or a csv list as well as remove duplicates and blanks. So I wrote a lib, nothing fancy just something quick to avoid repetition in my models.
########################################################################
# This lib adds acts_as_array_on class method to activerecord base
# The purpose is to serialize the attributes passed in as an array
# as well as declare a few getters and setters for easy use
########################################################################
module ActsAsArrayOn
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
def acts_as_array_on(*args)
args.each do |attr|
class_eval do
serialize attr.to_sym, Array
one = attr.to_s.singularize
many = attr.to_s.pluralize
# Setter as array
setter = "#{many}=".to_sym
define_method setter do |value|
write_attribute(attr, value.reject{|i| i.blank? }.uniq)
end
# Setter as string
setter = "#{one}_list=".to_sym
define_method setter do |value|
write_attribute(attr, value.split(',').reject{ |i|
i.blank?
}.map(&:strip).uniq)
end
# Getter as array
getter = many.to_sym
define_method getter do
read_attribute(attr) || []
end
# Getter as string
getter = "#{one}_list".to_sym
define_method getter do
read_attribute(attr).andand.join(", ") || ""
end
end
end
end
end
end
ActiveRecord::Base.send :include, ActsAsArrayOn
Usage
# In your model class User < ActiveRecord::Base acts_as_array_on :education, :spoken_language end # In your views/controllers # Get educations as array @user.educations # Get educations as list @user.education_list
Cache Class
Why?
This next example was used more as a proof of concept then anything else and was never used in any sort of production environment and so was never tested for performance, but I kind of like it so here it is. The idea is simple, in the case where you had to perform an expensive computation repeatedly it may be beneficial to cache the function and the result for use later. So here is my solution to this problem.
class Cache
def initialize
@cache = Hash.new
end
def cache_function(function)
return Proc.new do |*args|
#Not found: Add to cache
if !@cache.key? [function, args]
puts "Caching function #{function.name} with args: #{args.join(',')}"
cache_item = CacheItem.new(function, args, function.call(*args))
@cache[ [function, args] ] = cache_item
#Found: get cache item
else
cache_item = @cache[ [function, args] ]
#Expire cache if necesary
if cache_item.expire?
cache_item.result = function.call(*args)
puts "Exipiring Cache for #{function.name} with args: #{args.join(',')}"
else
puts "Cache hit for #{function.name} with args: #{args.join(',')}"
end
end
@cache[ [function, args] ].result
end
end
end
class CacheItem
attr_accessor :method, :args, :created_at, :use_count
MAX_CACHE_HITS=10
MAX_CACHE_TIME=300 #300 seconds == 5 minutes
def expire?
@use_count >= MAX_CACHE_HITS || (Time.now-@created_at) > MAX_CACHE_TIME
end
def initialize(method, args, result)
@method = method
@args = args
@result = result
@use_count = 0
@created_at = Time.now
end
def result
@use_count+=1
return @result
end
def result=(value)
@use_count = 0
@created_at = Time.now
@result = value
end
end
Usage
#!/usr/bin/ruby require 'cache_item' require 'cache' def test_function(a,b,c) return a+b+c end cache = Cache.new #Create a cached function for test_function cached_function = cache.cache_function( method(:test_function) ) #Should hit the cache 10 times then expire 15.times do |i| dummy_var = cached_function2.call(1,2,3) end