<?xml version='1.0' encoding='utf-8' ?>
<rss version='2.0' xmlns:dc='http://purl.org/dc/elements/1.1/'>
  <channel>
    <title>Wolfmans Howlings: Bit Vector Preferences</title>
    <link>http://blog.wolfman.com/articles/2007/8/7/bit-vector-preferences</link>
    <description>A programmers Blog about Ruby, Rails and a few other issue</description>
    <language>en-us</language>
    <ttl>40</ttl>
    <item>
      <title>Bit Vector Preferences</title>
      <description>
        &lt;p&gt;In my latest web project I potentially have a lot of boolean
        preferences, which I use for enabling or disabling various email
        notifications to users.&lt;/p&gt;
        
        &lt;p&gt;Rather than having to add a migration everytime I want to add a new
        preference, I thought I would use the &lt;code&gt;composed_of&lt;/code&gt; feature in my model
        and compose the boolean preferences from a bitvector. That way I can
        simply modify my model to add new preferences rather than add new
        columns to the database.&lt;/p&gt;
        
        &lt;p&gt;I also wanted this to be easy to add new boolean preferences, so I use
        some Macros (I guess you could also call it Meta-Programming) to do
        all the repetitive code.&lt;/p&gt;
        
        &lt;p&gt;The result is a little class in my Person model called Preferences,
        one integer field in my persons database called preferences, and a
        composed_of :preferences in the Person model, and of course the
        following class in the person.rb model.&lt;/p&gt;
        
        &lt;pre&gt;&lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Base&lt;/span&gt;
        
          &lt;span class=&quot;comment&quot;&gt;#...&lt;/span&gt;
        
          &lt;span class=&quot;comment&quot;&gt;# place to store bit vector preferences&lt;/span&gt;
          &lt;span class=&quot;comment&quot;&gt;# to add a new preference:-&lt;/span&gt;
          &lt;span class=&quot;comment&quot;&gt;#   add symbol of preference to @@bits with bit allocation&lt;/span&gt;
          &lt;span class=&quot;comment&quot;&gt;#   update initialize defaults if initial default is true&lt;/span&gt;
          &lt;span class=&quot;keyword&quot;&gt;class &lt;/span&gt;&lt;span class=&quot;class&quot;&gt;Preferences&lt;/span&gt;
            &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;symbol&quot;&gt;:comment_notifications&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;symbol&quot;&gt;:friendship_notifications&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;symbol&quot;&gt;:event_notifications&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;symbol&quot;&gt;:misc_notifications&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;}&lt;/span&gt;
        
            &lt;span class=&quot;comment&quot;&gt;# create a reader for each preference&lt;/span&gt;
            &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each_key&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
              &lt;span class=&quot;ident&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
        
            &lt;span class=&quot;comment&quot;&gt;# Initialize from integer or Hash&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;prefs&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;prefs&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;nil?&lt;/span&gt;
                &lt;span class=&quot;comment&quot;&gt;# set the defaults to false if not been set before&lt;/span&gt;
                &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
                  &lt;span class=&quot;ident&quot;&gt;instance_variable_set&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;@&lt;span class=&quot;expr&quot;&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;  
                &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
                &lt;span class=&quot;comment&quot;&gt;# override default here        &lt;/span&gt;
                &lt;span class=&quot;attribute&quot;&gt;@comment_notifications&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt;
                &lt;span class=&quot;attribute&quot;&gt;@event_notifications&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt;
              &lt;span class=&quot;keyword&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;prefs&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;comment&quot;&gt;# initialize from parameter Hash, and default to false if absent from hash&lt;/span&gt;
                &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
                  &lt;span class=&quot;ident&quot;&gt;instance_variable_set&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;@&lt;span class=&quot;expr&quot;&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;  
                &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
        
                &lt;span class=&quot;ident&quot;&gt;prefs&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
                  &lt;span class=&quot;keyword&quot;&gt;raise&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;Unknown preference &lt;span class=&quot;expr&quot;&gt;#{k}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;has_key?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;ident&quot;&gt;instance_variable_set&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;@&lt;span class=&quot;expr&quot;&gt;#{k}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;
                &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
              &lt;span class=&quot;keyword&quot;&gt;else&lt;/span&gt;
                &lt;span class=&quot;comment&quot;&gt;# create from integer bit vector&lt;/span&gt;
                &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
                  &lt;span class=&quot;ident&quot;&gt;instance_variable_set&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;@&lt;span class=&quot;expr&quot;&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;,&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;prefs&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;  
                &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
              &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
        
            &lt;span class=&quot;comment&quot;&gt;# returns bit vector of preferences&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;method&quot;&gt;preferences&lt;/span&gt;
              &lt;span class=&quot;ident&quot;&gt;bv&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;
              &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
                &lt;span class=&quot;ident&quot;&gt;bv&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|=&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;instance_variable_get&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;(&amp;quot;&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;@&lt;span class=&quot;expr&quot;&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;&amp;quot;)&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;number&quot;&gt;0&lt;/span&gt;  
              &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
              &lt;span class=&quot;keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;bv&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
        
            &lt;span class=&quot;comment&quot;&gt;# create a predicate for each preference&lt;/span&gt;
            &lt;span class=&quot;attribute&quot;&gt;@@bits&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;each_key&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;|&lt;/span&gt;
              &lt;span class=&quot;ident&quot;&gt;alias_method&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;punct&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;').&lt;/span&gt;&lt;span class=&quot;ident&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ident&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;punct&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
          &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
        
          &lt;span class=&quot;comment&quot;&gt;# access preferences as bit vector&lt;/span&gt;
          &lt;span class=&quot;ident&quot;&gt;composed_of&lt;/span&gt; &lt;span class=&quot;symbol&quot;&gt;:preferences&lt;/span&gt;
        
          &lt;span class=&quot;comment&quot;&gt;#...&lt;/span&gt;
        
        &lt;span class=&quot;keyword&quot;&gt;end&lt;/span&gt;
        
        &lt;/pre&gt;
        
        &lt;p&gt;All I need to do to add new preferences is add it to the &lt;code&gt;@@bits&lt;/code&gt;
        class variable, which is a Hash of the preference name as a symbol and
        the bit it sets in the integer (actually the value of the bit, bit0 is
        1, bit1 is 2 etc). The rest of the code is derived from the class
        variable.&lt;/p&gt;
        
        &lt;p&gt;To make things easier I also add a predicate for each preference, so I
        can access &lt;code&gt;@person.preferences.comment_notifications?&lt;/code&gt; to see if any
        comment notifications are required for instance.&lt;/p&gt;
        
        &lt;p&gt;One other thing I do in the initialize method is set up defaults for
        the preferences. This is only really needed if it is being added as an
        after thought, and the column &lt;code&gt;preferences&lt;/code&gt; is NULL in the database.&lt;/p&gt;
        
        &lt;p&gt;Because &lt;code&gt;composed_of&lt;/code&gt; classes are immutable you must always create a
        whole new one to update them, so I also allow initialize to be called
        with a Hash, which can come straight from the controller. The last
        case of initialize is being passed the integer from the database,
        expanding it into the various boolean instance variables. The
        &lt;code&gt;preferences&lt;/code&gt; method does the reverse and converts the boolean
        instance variables into the bit vector. Calls to these are all taken
        care of by ActiveRecord.&lt;/p&gt;
        
        &lt;p&gt;An example of it being called from the controller is...&lt;/p&gt;
        
        &lt;pre&gt;&lt;code&gt;@person.preferences= Person::Preferences.new(params[:preferences])
        &lt;/code&gt;&lt;/pre&gt;
        
        &lt;p&gt;presuming you have a bunch of check boxes in your view which are
        passed in as part of the preferences hash.&lt;/p&gt;
        
        &lt;p&gt;Because I added this later I had one migration to initially add the
        new column...&lt;/p&gt;
        
        &lt;pre&gt;&lt;code&gt;add_column :people, :preferences, :integer
        &lt;/code&gt;&lt;/pre&gt;
        
        &lt;p&gt;&lt;a href=&quot;http://technorati.com/tag/composed_of&quot; rel=&quot;tag&quot;&gt;&lt;/a&gt;&lt;/p&gt;
      </description>
      <author>Jim Morris</author>
      <pubDate>Tue, 07 Aug 2007 23:53:09 -0700</pubDate>
      <link>http://blog.wolfman.com/articles/2007/8/7/bit-vector-preferences</link>
      <guid isPermaLink='false'>urn:uuid:4fb543c1-0bf5-4881-9fc5-aabe2d0789c2</guid>
    </item>
    <item>
      <title>"Bit Vector Preferences" by Uri Lewin</title>
      <description>Thanks, I was looking exactly for this functionality and it required very little effort to put it in use.</description>
      <pubDate>Thu, 05 Jun 2008 13:04:21 -0700</pubDate>
      <link>http://blog.wolfman.com/posts/34#comment-173</link>
      <guid isPermaLink='false'>urn:uuid:6918a7dd-4a68-45d0-9b4e-62e5af8e9ae3</guid>
    </item>
    <item>
      <title>"Bit Vector Preferences" by Alex</title>
      <description>Cool. You should mention the max amount of prefs that can be stored by this feature due to the length of an integer.</description>
      <pubDate>Fri, 05 Sep 2008 00:52:19 -0700</pubDate>
      <link>http://blog.wolfman.com/posts/34#comment-174</link>
      <guid isPermaLink='false'>urn:uuid:155e836d-3f09-4866-ac43-98d679e5c61a</guid>
    </item>
  </channel>
</rss>
