<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>The Napkin ~ A Blog By Highgroove Studios comments on Metaprogramming and Ruby on Rails</title>
    <link>http://napkin.highgroove.com/</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>The Napkin ~ A Blog By Highgroove Studios comments</description>
    <item>
      <title>"Metaprogramming and Ruby on Rails": comment by Derek</title>
      <description>&lt;p&gt;Wing,&lt;/p&gt;


	&lt;p&gt;This is only a piece of the application&amp;#8230;there are several more actions other than #list and several models that require the same treatments.&lt;/p&gt;


	&lt;p&gt;The actions and models have been changed several times as the application as evolved &amp;#8211; replicating this functionality would have been a major pain.&lt;/p&gt;</description>
      <pubDate>Wed,  9 Aug 2006 00:21:39 EST</pubDate>
      <guid>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-38</guid>
      <link>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-38</link>
    </item>
    <item>
      <title>"Metaprogramming and Ruby on Rails": comment by wing</title>
      <description>&lt;p&gt;Great article for guiding through the concept, but I&amp;#8217;m not very sure if it&amp;#8217;s worth the effort for the application.&lt;/p&gt;


	&lt;p&gt;The code:&lt;/p&gt;


	&lt;p&gt;def list
    @school = School.find(params[:school])
    @file_records = @school.file_records
    paginate_file_records
    render :action =&amp;gt; &amp;#8216;list&amp;#8217;
  end
alias index list&lt;/p&gt;


	&lt;p&gt;Doesn&amp;#8217;t seem that much of a hassle to replicate. That and it is probably not used too many times in the application anyway.&lt;/p&gt;


	&lt;p&gt;It is, however, nonetheless an very nicely done piece of code.&lt;/p&gt;</description>
      <pubDate>Tue,  8 Aug 2006 18:40:21 EST</pubDate>
      <guid>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-36</guid>
      <link>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-36</link>
    </item>
    <item>
      <title>"Metaprogramming and Ruby on Rails": comment by Peter</title>
      <description>&lt;p&gt;Great article! I implemented this idea as a plugin so I can easily share it between apps.&lt;/p&gt;


	&lt;p&gt;I had to change&lt;/p&gt;


	&lt;p&gt;model.constantize&lt;/p&gt;


	&lt;p&gt;to&lt;/p&gt;


	&lt;p&gt;model.to_s.constantize&lt;/p&gt;


	&lt;p&gt;because I was sending model as a symbol. I don&amp;#8217;t know why it looks like it works for you but not for me.&lt;/p&gt;</description>
      <pubDate>Sat, 22 Jul 2006 20:42:23 EST</pubDate>
      <guid>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-30</guid>
      <link>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-30</link>
    </item>
    <item>
      <title>"Metaprogramming and Ruby on Rails": comment by Derek</title>
      <description>&lt;p&gt;&lt;em&gt;Tom:&lt;/em&gt; You are correct&amp;#8230;I&amp;#8217;ve updated this in the article.&lt;/p&gt;


	&lt;p&gt;&lt;em&gt;Tanner:&lt;/em&gt; Thanks! More Ruby is a good thing!&lt;/p&gt;


	&lt;p&gt;&lt;em&gt;Joey&lt;/em&gt;: Nice&amp;#8230;I updated that as well.&lt;/p&gt;</description>
      <pubDate>Wed, 28 Jun 2006 21:18:31 EST</pubDate>
      <guid>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-22</guid>
      <link>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-22</link>
    </item>
    <item>
      <title>"Metaprogramming and Ruby on Rails": comment by Joey</title>
      <description>&lt;p&gt;klass = Object.const_get(model.to_s.camelize) 
can be:
klass = model.constantize&lt;/p&gt;</description>
      <pubDate>Tue, 27 Jun 2006 17:44:17 EST</pubDate>
      <guid>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-21</guid>
      <link>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-21</link>
    </item>
    <item>
      <title>"Metaprogramming and Ruby on Rails": comment by TannerShot</title>
      <description>&lt;p&gt;Very nice walkthrough. Props for a clean writing style that is a pleasure to read. Makes me want to use Ruby more, and perl less.&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;m subscribing to your feed so I actch your next post!&lt;/p&gt;</description>
      <pubDate>Tue, 27 Jun 2006 13:07:19 EST</pubDate>
      <guid>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-20</guid>
      <link>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-20</link>
    </item>
    <item>
      <title>"Metaprogramming and Ruby on Rails": comment by Tom Brice</title>
      <description>&lt;p&gt;I haven&amp;#8217;t tried it but will step 2 and 3 work?&lt;/p&gt;


	&lt;p&gt;Your file in/lib is called “school_item_scaffold.rb”&lt;/p&gt;


	&lt;p&gt;You add this to ”/config/environment.rb”:
require &amp;#8216;acts_as_school_item&amp;#8217;
ActionController::Base.send(:include,ActionController::Acts::SchoolItem )&lt;/p&gt;


	&lt;p&gt;for that require to work wouldn&amp;#8217;t you have to name your file &amp;#8220;acts_as_school_item.rb&amp;#8221;?&lt;/p&gt;</description>
      <pubDate>Tue, 27 Jun 2006 12:02:23 EST</pubDate>
      <guid>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-19</guid>
      <link>http://napkin.highgroove.com/articles/2006/06/27/metaprogramming-and-ruby-on-rails#comment-19</link>
    </item>
    <item>
      <title>"Metaprogramming and Ruby on Rails" by derek</title>
      <description>&lt;p&gt;Metaprogramming is your secret identical twin that likes doing all of the things you don&amp;#8217;t. Need to take out the trash? Just tell your twin. Need to program in Java? Send your twin an email.&lt;/p&gt;


	&lt;p&gt;Metaprogramming, defined as &lt;a href="http://poignantguide.net/ruby/chapter-6.html"&gt;writing code that writes code&lt;/a&gt;  by Why The Lucky Stiff, makes scaffolding, associations, validations, and the many magical parts of Rails possible. Implementing metaprogramming techniques can drastically eliminate duplicate code, making your applications far easier to maintain and build. It also lets your code do the work &amp;#8211; not you.&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://www.campussync.com"&gt;CampusSync.com&lt;/a&gt;, a client project of ours, is a collaboration site for college students. It has several administration areas that are almost identical, but not a good fit for Rail&amp;#8217;s standard scaffolding. The solution to eliminating duplicate code: roll our own metaprogramming solution.&lt;/p&gt;

&lt;h3&gt;The problem&lt;/h3&gt;


	&lt;p&gt;Every school in CampusSync has on-campus events, organizations, shared files, comments, and more. The CampusSync staff needs to review this content. We need to be able to search and view items, sort, edit, etc. So how can we go about this without creating separate views and controller actions for each type of reviewable class?&lt;/p&gt;


	&lt;h3&gt;Our End Goal&lt;/h3&gt;


&lt;pre&gt;
class CommentsController &amp;lt; ActionController
     school_item_scaffold :comment
end
&lt;/pre&gt;

	&lt;p&gt;Isn&amp;#8217;t the above code pretty? One line of code adds all the functionality we need.&lt;/p&gt;


	&lt;h3&gt;How to Get There&lt;/h3&gt;


	&lt;p&gt;&lt;strong&gt;1. Write Your Tests&lt;/strong&gt;&lt;/p&gt;


	&lt;p&gt;Let&amp;#8217;s write a simple test to check our &lt;code&gt;FilesController#list&lt;/code&gt; action. The FilesController lets administrator review uploaded files.&lt;/p&gt;


&lt;pre&gt;
  def test_list
    get :list, :school =&amp;gt; @school.id
    assert_response :success
    assert assigns(:file_records).any?
  end
&lt;/pre&gt;

	&lt;p&gt;&lt;strong&gt;2. Create a module with the custom scaffolding behavior.&lt;/strong&gt;&lt;/p&gt;


	&lt;p&gt;I created a file called &amp;#8220;acts_as_school_item.rb&amp;#8221; and placed it in the &amp;#8221;/lib&amp;#8221; folder of the CampusSync application.&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;3. Load the file when starting the Rails application by placing the lines below in &amp;#8221;/config/environment.rb&amp;#8221;.&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;
require 'acts_as_school_item'
ActionController::Base.send(:include,ActionController::Acts::SchoolItem )
&lt;/pre&gt;

	&lt;p&gt;&lt;strong&gt;3. Setup the basic module structure.&lt;/strong&gt;&lt;/p&gt;


	&lt;p&gt;Below is the typical structure of a metaprogramming module.&lt;/p&gt;


&lt;pre&gt;
module ActionController
  module  Acts

    module SchoolItem
      def self.included(base)
        base.extend(ClassMethods)  
      end

      module  ClassMethods
        # The method that attaches this behavior to the controller.
        def school_item_scaffold(model,options = {})

          module_eval &amp;lt;&amp;lt;-CODE
            # we'll define our scaffolded actions in here
          CODE

        end # school_item_scaffold
      end # Class Methods
    end # SchoolItem
  end # Acts
end # ActionController
&lt;/pre&gt;

	&lt;p&gt;&lt;strong&gt;4. Add Our Scaffolding Functionality&lt;/strong&gt;&lt;/p&gt;


	&lt;p&gt;We&amp;#8217;ll create the shared views in &lt;code&gt;app/views/admin/school_items/&lt;/code&gt;.  For now, let&amp;#8217;s just add a non-metaized &lt;code&gt;#list&lt;/code&gt; action:&lt;/p&gt;


&lt;pre&gt;
def list
    @school = School.find(params[:school])
    @file_records = @school.file_records
    paginate_file_records
    render :action =&amp;gt; 'list'
  end
alias index list
&lt;/pre&gt;

	&lt;p&gt;And here it is after meta-izing the &lt;code&gt;#list&lt;/code&gt; action:&lt;/p&gt;


&lt;pre&gt;
module ActionController
  module  Acts
    module SchoolItem
      def self.included(base)
        base.extend(ClassMethods)  
      end

      module  ClassMethods
        # Adds custom scaffolding for viewing a school's associated +model+ objects. 
        # 
        # Options:
        #   - klass: The class of the model object
        #   - friendly_name: A human name to use when referring to the +model+ objects in the views. 
        #   - pluralized_name: Defines how we retrieve the records from the school and the instance variable
        #                      that contains the items in the +list+ action. 
        #   - per_page: Number of records to display per-page in the list view.
        def school_item_scaffold(model,options = {})
          klass = model.constantize
          write_inheritable_attribute(:school_item_options, {
            :klass =&amp;gt; klass,
            :friendly_name =&amp;gt; klass.to_s,
            :pluralized_name =&amp;gt; klass.to_s.tableize,
            :per_page =&amp;gt; 50
          }.merge(options))
          class_inheritable_reader :school_item_options

          module_eval &amp;lt;&amp;lt;-CODE
            def list
              @school = School.find(params[:school])
              @#{school_item_options[:pluralized_name]} = @school.#{school_item_options[:pluralized_name]}

              paginate_#{school_item_options[:pluralized_name]}

              render :template =&amp;gt; 'admin/school_items/list'
            end
            alias index list

            private

            def paginate_#{school_item_options[:pluralized_name]}
              @pages,@#{school_item_options[:pluralized_name]} = paginate_collection(@#{school_item_options[:pluralized_name]}, 
                                                      :per_page =&amp;gt; #{school_item_options[:per_page]},
                                                      :page =&amp;gt; params[:page])
            end
          CODE
        end # school_item_scaffold
      end # ClassMethods
    end # SchoolItem
  end # Acts
end # ActionController
&lt;/pre&gt;

	&lt;h3&gt;Code Explained&lt;/h3&gt;


	&lt;p&gt;When &lt;code&gt;#school_item_scaffold&lt;/code&gt; is called inside of a controller, we place all of the options in the &lt;code&gt;school_item_options&lt;/code&gt; inheritance attribute. We can access this attribute from inside the &lt;code&gt;ClassMethods&lt;/code&gt; module and from our views (&lt;code&gt;&amp;lt;%= controller.school_item_options %&amp;gt;&lt;/code&gt;).&lt;/p&gt;


	&lt;p&gt;After we set this inheritance attribute, we then build the actions. In this case, we are just creating a &lt;code&gt;#list&lt;/code&gt; action and a private method to paginate the results. And that&amp;#8217;s it!&lt;/p&gt;


	&lt;p&gt;We can now add our customized scaffolding to any controller:&lt;/p&gt;


&lt;pre&gt;
class Admin::EventsController &amp;lt; AbstractAdminController
  school_item_scaffold :event
end
&lt;/pre&gt;

	&lt;p&gt;When CampusSync adds more reviewable content, we won&amp;#8217;t have to create any duplicate code, and we have less code to maintain.&lt;/p&gt;</description>
      <pubDate>Tue, 27 Jun 2006 03:39:00 EST</pubDate>
      <guid>&lt;a href="/articles/2006/06/27/metaprogramming-and-ruby-on-rails"&gt;Metaprogramming and Ruby on Rails&lt;/a&gt;</guid>
      <link>&lt;a href="/articles/2006/06/27/metaprogramming-and-ruby-on-rails"&gt;Metaprogramming and Ruby on Rails&lt;/a&gt;</link>
    </item>
  </channel>
</rss>
