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