Skip to content

Instantly share code, notes, and snippets.

@brianjlandau
Created January 21, 2010 18:24
Show Gist options
  • Save brianjlandau/283033 to your computer and use it in GitHub Desktop.
Save brianjlandau/283033 to your computer and use it in GitHub Desktop.
acts_as_flagable
module ActiveRecord
module Acts
module AsFlagable
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def acts_as_flagable(options, flagable_column = 'flags')
unless flagable?
cattr_accessor :flag_column_name
self.flag_column_name = flagable_column
cattr_accessor :flag_values
self.flag_values = options
cattr_accessor :flag_names
self.flag_names = options.keys.map(&:to_s)
before_validation :set_flag_mask
attr_accessor *options.keys
options.each_key do |flag|
define_method "#{flag}?" do
@attributes[flag_column_name.to_s] ||= 0 # Hack because shoulda in some cases sets this to nil causing tests to fail.
(self.send(flag_column_name.to_sym) & flag_values[flag]) == flag_values[flag]
end
end
include InstanceMethods
end
end
def flagable?
self.included_modules.include?(InstanceMethods)
end
end
module InstanceMethods
def after_initialize
# Make sure we correctly set the flags when we retrieve a record.
unless new_record?
flag_values.each_key do |flag|
if self.send("#{flag}?".to_sym)
self.send("#{flag}=".to_sym, true)
else
self.send("#{flag}=".to_sym, false)
end
end
end
end
private
def set_flag_mask
# correctly transfer all flags to the bitmask
self.send("#{flag_column_name}=".to_sym, 0)
flag_values.each do |flag, value|
if ConnectionAdapters::Column.value_to_boolean(self.send(flag))
self.send("#{flag_column_name}=".to_sym, (self.send(flag_column_name.to_sym) | value))
end
end
end
end
end
end
end
ActiveRecord::Base.send(:include, ActiveRecord::Acts::AsFlagable)
class ActiveSupport::TestCase
def self.should_be_flagable(flags, flag_column = "flags")
should_have_instance_methods flag_column, "#{flag_column}="
should 'have a flag column' do
assert self.class.described_type.column_names.include?(flag_column.to_s)
end
should 'have a flag_values class method' do
assert_equal flags, self.class.described_type.flag_values
end
should 'have a flag_names class method with all flag names' do
assert_same_elements flags.keys.map(&:to_s), self.class.described_type.flag_names
end
should 'be flagable?' do
assert self.class.described_type.flagable?
end
flags.each do|flag, value|
should_have_instance_methods "#{flag}", "#{flag}=", "#{flag}?"
should "correctly set the #{flag} instance variable to true on initiation" do
subject.send("#{flag}=", "1")
subject.save!
found_record = self.class.described_type.find(subject.id)
assert found_record.send(flag.to_sym)
end
should "correctly set the #{flag} instance variable to false on initiation" do
subject.send("#{flag}=", "0")
subject.save!
found_record = self.class.described_type.find(subject.id)
assert !found_record.send(flag.to_sym)
end
should "correctly return true for #{flag}?" do
subject.send("#{flag}=", "1")
subject.save!
found_record = self.class.described_type.find(subject.id)
assert found_record.send("#{flag}?".to_sym)
end
should "correctly return false for #{flag}?" do
subject.send("#{flag}=", "0")
subject.save!
found_record = self.class.described_type.find(subject.id)
assert !found_record.send("#{flag}?".to_sym)
end
should "correctly return true for #{flag}? after save using update attributes" do
subject.update_attributes(flag => "1")
subject.save!
assert subject.send("#{flag}?".to_sym)
end
should "correctly return false for #{flag}? after save using update attributes" do
subject.update_attributes(flag => "0")
subject.save!
assert !subject.send("#{flag}?".to_sym)
end
should "correctly return true for #{flag}? after failed save on new record" do
new_record = self.class.described_type.new(flag => "1")
new_record.save
assert new_record.send("#{flag}?".to_sym)
end
should "correctly return false for #{flag}? after failed save on new record" do
new_record = self.class.described_type.new(flag => "0")
new_record.save
assert !new_record.send("#{flag}?".to_sym)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment