<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>README.markdown</filename>
    </added>
    <added>
      <filename>lib/bitmask-attribute.rb</filename>
    </added>
    <added>
      <filename>lib/bitmask_attribute/value_proxy.rb</filename>
    </added>
    <added>
      <filename>rails/init.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,4 +1,4 @@
-README.rdoc
+README.markdown
 lib/**/*.rb
 bin/*
 features/**/*.feature</diff>
      <filename>.document</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-Copyright (c) 2009 Bruce Williams
+Copyright (c) 2007-2009 Bruce Williams
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the</diff>
      <filename>LICENSE</filename>
    </modified>
    <modified>
      <diff>@@ -0,0 +1,100 @@
+require 'bitmask_attribute/value_proxy'
+
+module BitmaskAttribute
+  
+  class Definition
+    
+    attr_reader :attribute, :values, :extension
+    def initialize(attribute, values=[], &amp;extension)
+      @attribute = attribute
+      @values = values
+      @extension = extension
+    end
+    
+    def install_on(model)
+      validate_for model
+      generate_bitmasks_on model
+      override model
+      create_convenience_method_on model
+    end
+    
+    #######
+    private
+    #######
+
+    def validate_for(model)
+      unless model.columns.detect { |col| col.name == attribute.to_s &amp;&amp; col.type == :integer }
+        raise ArgumentError, &quot;`#{attribute}' is not an integer column of `#{model}'&quot;
+      end
+    end
+    
+    def generate_bitmasks_on(model)
+      model.bitmasks[attribute] = returning HashWithIndifferentAccess.new do |mapping|
+        values.each_with_index do |value, index|
+          mapping[value] = 0b1 &lt;&lt; index
+        end
+      end
+    end
+    
+    def override(model)
+      override_getter_on(model)
+      override_setter_on(model)
+    end
+    
+    def override_getter_on(model)
+      model.class_eval(&lt;&lt;-EVAL)
+        def #{attribute}
+          @#{attribute} ||= BitmaskAttribute::ValueProxy.new(self, :#{attribute}, &amp;self.class.bitmask_definitions[:#{attribute}].extension)
+        end
+      EVAL
+    end
+    
+    def override_setter_on(model)
+      model.class_eval(&lt;&lt;-EVAL)
+        def #{attribute}=(raw_value)
+          values = raw_value.kind_of?(Array) ? raw_value : [raw_value]
+          #{attribute}.replace(values)
+        end
+      EVAL
+    end
+    
+    def create_convenience_method_on(model)
+      model.class_eval(&lt;&lt;-EVAL)
+        def self.bitmask_for_#{attribute}(*values)
+          values.inject(0) do |bitmask, value|
+            unless (bit = bitmasks[:#{attribute}][value])
+              raise ArgumentError, &quot;Unsupported value for #{attribute}: \#{value.inspect}&quot;
+            end
+            bitmask | bit
+          end
+        end
+      EVAL
+    end
+    
+  end
+  
+  def self.included(model)
+    model.extend ClassMethods
+  end
+    
+  module ClassMethods
+    
+    def bitmask(attribute, options={}, &amp;extension)
+      unless options[:as] &amp;&amp; options[:as].kind_of?(Array)
+        raise ArgumentError, &quot;Must provide an Array :as option&quot;
+      end
+      bitmask_definitions[attribute] = BitmaskAttribute::Definition.new(attribute, options[:as].to_a, &amp;extension)
+      bitmask_definitions[attribute].install_on(self)
+    end
+    
+    def bitmask_definitions
+      @bitmask_definitions ||= {}
+    end
+    
+    def bitmasks
+      @bitmasks ||= {}
+    end
+      
+  end
+  
+end
\ No newline at end of file</diff>
      <filename>lib/bitmask_attribute.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,104 @@
 require 'test_helper'
 
 class BitmaskAttributeTest &lt; Test::Unit::TestCase
-  should &quot;probably rename this file and start testing for real&quot; do
-    flunk &quot;hey buddy, you should probably rename this file and start testing for real&quot;
+  
+  context &quot;Campaign&quot; do
+
+    should &quot;can assign single value to bitmask&quot; do
+      assert_stored Campaign.new(:medium =&gt; :web), :web
+    end
+
+    should &quot;can assign multiple values to bitmask&quot; do
+      assert_stored Campaign.new(:medium =&gt; [:web, :print]), :web, :print
+    end
+
+    should &quot;can add single value to bitmask&quot; do
+      campaign = Campaign.new(:medium =&gt; [:web, :print])
+      assert_stored campaign, :web, :print
+      campaign.medium &lt;&lt; :phone
+      assert_stored campaign, :web, :print, :phone
+    end
+
+    should &quot;ignores duplicate values added to bitmask&quot; do
+      campaign = Campaign.new(:medium =&gt; [:web, :print])
+      assert_stored campaign, :web, :print
+      campaign.medium &lt;&lt; :phone
+      assert_stored campaign, :web, :print, :phone
+      campaign.medium &lt;&lt; :phone
+      assert_stored campaign, :web, :print, :phone
+      assert_equal 1, campaign.medium.select { |value| value == :phone }.size
+    end
+
+    should &quot;can assign new values at once to bitmask&quot; do
+      campaign = Campaign.new(:medium =&gt; [:web, :print])
+      assert_stored campaign, :web, :print
+      campaign.medium = [:phone, :email]
+      assert_stored campaign, :phone, :email
+    end
+
+    should &quot;can save bitmask to db and retrieve values transparently&quot; do
+      campaign = Campaign.new(:medium =&gt; [:web, :print])
+      assert_stored campaign, :web, :print
+      assert campaign.save
+      assert_stored Campaign.find(campaign.id), :web, :print
+    end
+
+    should &quot;can add custom behavor to value proxies during bitmask definition&quot; do
+      campaign = Campaign.new(:medium =&gt; [:web, :print])
+      assert_raises NoMethodError do
+        campaign.medium.worked?
+      end
+      assert_nothing_raised do
+        campaign.misc.worked?
+      end
+      assert campaign.misc.worked?
+    end
+
+    should &quot;cannot use unsupported values&quot; do
+      assert_unsupported { Campaign.new(:medium =&gt; [:web, :print, :this_will_fail]) }
+      campaign = Campaign.new(:medium =&gt; :web)
+      assert_unsupported { campaign.medium &lt;&lt; :this_will_fail_also }
+      assert_unsupported { campaign.medium = [:so_will_this] }
+    end
+
+    should &quot;can determine bitmasks using convenience method&quot; do
+      assert Campaign.bitmask_for_medium(:web, :print)
+      assert_equal(
+        Campaign.bitmasks[:medium][:web] | Campaign.bitmasks[:medium][:print],
+        Campaign.bitmask_for_medium(:web, :print)
+      )
+    end
+
+    should &quot;assert use of unknown value in convenience method will result in exception&quot; do
+      assert_unsupported { Campaign.bitmask_for_medium(:web, :and_this_isnt_valid)  }
+    end
+
+    should &quot;hash of values is with indifferent access&quot; do
+      string_bit = nil
+      assert_nothing_raised do
+        assert (string_bit = Campaign.bitmask_for_medium('web', 'print'))
+      end
+      assert_equal Campaign.bitmask_for_medium(:web, :print), string_bit
+    end
+
+    #######
+    private
+    #######
+
+    def assert_unsupported(&amp;block)
+      assert_raises(ArgumentError, &amp;block)
+    end
+
+    def assert_stored(record, *values)
+      values.each do |value|
+        assert record.medium.any? { |v| v.to_s == value.to_s }, &quot;Values #{record.medium.inspect} does not include #{value.inspect}&quot;
+      end
+      full_mask = values.inject(0) do |mask, value|
+        mask | Campaign.bitmasks[:medium][value]
+      end
+      assert_equal full_mask, record.medium.to_i
+    end
+
   end
+
 end</diff>
      <filename>test/bitmask_attribute_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,55 @@
 require 'rubygems'
 require 'test/unit'
 require 'shoulda'
+begin
+  require 'redgreen'
+rescue LoadError
+end
+
+require 'active_support'
+require 'active_record'
 
 $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
 $LOAD_PATH.unshift(File.dirname(__FILE__))
-require 'bitmask_attribute'
+require 'bitmask-attribute'
+require File.dirname(__FILE__) + '/../rails/init'
+
+ActiveRecord::Base.establish_connection(
+  :adapter  =&gt; 'sqlite3',
+  :database =&gt; ':memory:'
+)
+
+ActiveRecord::Schema.define do 
+  create_table :campaigns do |table|
+    table.column :medium, :integer
+    table.column :misc, :integer
+  end
+end
+
+# Pseudo model for testing purposes
+class Campaign &lt; ActiveRecord::Base
+  bitmask :medium, :as =&gt; [:web, :print, :email, :phone]
+  bitmask :misc, :as =&gt; %w(some useless values) do
+    def worked?
+      true
+    end
+  end
+end
 
 class Test::Unit::TestCase
+  
+  def assert_unsupported(&amp;block)
+    assert_raises(ArgumentError, &amp;block)
+  end
+  
+  def assert_stored(record, *values)
+    values.each do |value|
+      assert record.medium.any? { |v| v.to_s == value.to_s }, &quot;Values #{record.medium.inspect} does not include #{value.inspect}&quot;
+    end
+    full_mask = values.inject(0) do |mask, value|
+      mask | Campaign.bitmasks[:medium][value]
+    end
+    assert_equal full_mask, record.medium.to_i
+  end
+  
 end</diff>
      <filename>test/test_helper.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>README.rdoc</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>63c415689c5e91ca4179b32a543413d180888971</id>
    </parent>
  </parents>
  <author>
    <name>Bruce Williams</name>
    <email>bruce@codefluency.com</email>
  </author>
  <url>http://github.com/bruce/bitmask-attribute/commit/ded0bdaf3ca904c0487fa306bdc99d09b1cd8ebc</url>
  <id>ded0bdaf3ca904c0487fa306bdc99d09b1cd8ebc</id>
  <committed-date>2009-05-18T18:56:31-07:00</committed-date>
  <authored-date>2009-05-18T18:56:31-07:00</authored-date>
  <message>Update documentation from old `bitmask_field` plugin, move things around.</message>
  <tree>d9b1cd0a5c773bb7af17f56ecee75dfc270ee70f</tree>
  <committer>
    <name>Bruce Williams</name>
    <email>bruce@codefluency.com</email>
  </committer>
</commit>
