Skip to main content

Mobomo webinars-now on demand! | learn more.

One of my favorite techniques to DRY up a Rails application is to pull out common functionality into a simple “vocabularized method,” by which I mean a simple descriptive method call that can be made from within your model or controller definition. For instance, in some applications I might have multiple models that have an “avatar.”

These avatars all behave the same and should do the same things so I don’t want to just repeat myself in the code. Instead I set up a file called has_avatar.rb inside my config/initializers folder. We use Paperclip for attachment handling at the moment, so I am going to create a wrapper for the specific Paperclip functionality I need for the avatars. Here’s the code:

module HasAvatar   STYLES = { :large  => ["200x200#", :png],              :medium => ["100x100#", :png],               :small  => ["70x70#", :png],              :little => ["50x50#", :png],              :tiny   => ["24x24#", :png] }                 def self.included(base)     base.extend ClassMethods   end      module ClassMethods     def has_avatar       has_attached_file :avatar,                         PAPERCLIP_DEFAULTS.merge(                           :styles => HasAvatar::STYLES,                           :default_style => :medium,                           :default_url => "https://assets.presentlyapp.com/images/avatars/missing_:style.png",                           :path => ":account/avatars/:class/:login/:style.:extension"                         )     end   end end  ActiveRecord::Base.send :include, HasAvatar

We define a module, HasAvatar that will add a new class method called has_avatar into whatever class it is included. I defined a constant STYLES that lets me access the style hash outside of the attachment definition. A final piece of DRYness in the code is the PAPERCLIP_DEFAULTS constant which is just the default setup for all attachments (S3 Bucket, etc.) and I override the options I need for the avatars by merge-ing them in.

class User < ActiveRecord::Base   has_avatar end  class Group < ActiveRecord::Base   has_avatar end

Now both users and groups will have all of the expected Paperclip functionality without having to repeat ourselves. This is a simple example but it shows the general practices behind building your application vocabulary, which is just my made-up term for the abstract reusable components that are specific only to this application. Of course, if it’s useful outside of the application, you might want to just go ahead and pluginize it!

The usefulness of these abstracted methods also comes in through the inherently polymorphic nature of Ruby. Throughout my code I can write helpers, views and more to support avatars without caring whether the object in question is a User or a Group. Basically, the DRYer you start the DRYer you stay.

Categories
Author

After developing a number of applications, there were some actions I found myself performing over and over again with each new controller in each new application. With my DRY-sense tingling, I decided to do something about it and create a plugin to clean up some of those repetitive tasks that were vital but annoying to set up. Introducing Needy Controllers.

Needy Controllers takes care of your controllers’…well, needs. You can include stylesheets, javascripts, and memoized record helpers in your controllers and views with a single command, and even use filter-chain-esque options to specify and tailor to your needs. Basically, it simplifies the process of:

  1. Including stylesheets in your app on a per-controller/action basis
  2. Including javascripts on a per-controller/action basis
  3. Having helper functions to fetch records in common RESTful resource mappings

Installation

git clone git://github.com/mbleigh/needy-controllers.git vendor/plugins/needy_controllers

Usage

Styles and Scripts

To use Needy Controllers for styles and scripts, you simply call a “needs” option inside your controller like so:

class MyController < ApplicationController   needs :styles => :standard   needs :styles => :show, :only => :show   needs :scripts => :behave, :except => :show    def index     # this action will have access to the 'standard.css' stylesheet   end      def show     # this action will have access to the 'show.css' stylesheet     # in addition to 'standard.css'     # but will not have access to 'behave.js'   end end

Now that you have created your behavior and style chains, you need to include them in the view. Luckily, this is exceedingly easy! Just include :needs in your include and link tags like so:

<%= stylesheet_link_tag 'something', :needs %> <%= javascript_include_tag 'prototype', :needs, 'effects' %>

An additional benefit of the style and behavior chains is inheritance: namely, any controller that inherits from a controller with a stylesheet or javascript included using the above method will automatically have the same stylesheet and javascript included in itself. This allows you to easily set up includes that are scoped to your exact needs with as little work as possible.

Model Fetching

To use Needy Controllers for fetching records, you use it similarly, and it creates a helper:

class MyController < ApplicationController   # here's a standard problem   needs :record => :user, :from => :id, :as => :user end

The :from and :as options in this example are the defaults (:id for :from and :as defaults to the name of the record). This will create a method (“user” in the example)
that will be accessible both from the controller and from the view as a helper. It will find the record with a matching ID to the URL parameter associated with the :from option. Therefore if you had nested resources you could call it as such:

needs :record => :user, :from => :user_id

That’s pretty much it, just a few simple ways to make your coding life easier. As always, there is a Trac available for any issues and I would love to hear any feedback you might have.

Categories
Author

As much as we all strive to write perfect, semantic XHTML that can be styled entirely independently, the reality of browser compatibility and even our own creative choices often leads to design refactoring. Obviously in Rails one big help for this is the use of partials, making sure that you can change your markup in one place and it will be reflected for often-repeated content. But what about for lower-level, underlying layout structure? Nested partials quickly become a mess to deal with, and traditional helpers don't always offer the flexibility and intelligence that is needed to get the job done.

Enter block-accepting helpers. These allow you to wrap markup around content without the ugliness of a my_thing_start and my_thing_end. With a little more work, they can also provide an intelligent layout structure that requires minimal (and beautiful) code in the view. Let's start with a simple example. Lets say that most, but not all, of the pages in my app have a similar container and title. Creating multiple layouts using partials for everything just to add or not add a couple of lines of code is way too heavy. On the other hand, my designers want the flexibility to change how the container is represented in markup, so I can't just throw the markup into each action. Here's the markup I want (for now):

<div class='main_container'>      <h3><!-- Some Title --></h3>      <!-- Some Contents-->  </div>

This is obviously pretty basic, and wouldn't be a big deal to change if the design changed, but this is just an example. If you're dealing with some kind of nested div rounded corner solution, or any number of other necessary CSS/markup hacks to get the look you desire, it could become quite a chore. So how do we create our DRY helper? Primarily through the use of the concat method in the ActionView::Helpers::TextHelper module. Here's my helper (just put this in either the relevant helper or ApplicationHelper:

def main_container(options = {}, &block)    concat("<div class='main_container'>",block.binding)    concat("<h3>#{options[:title]}</h3>",block.binding) unless options[:title].blank?    yield    concat("</div>",block.binding)  end

So that wasn't so bad, was it? Now in our views all we have to do to get our main_container is:

<% main_container :title => 'My Page Title' do %>      <p>We can put whatever we want here, and it will appear inside the container.</p>  <% end %>

Cool, huh? There are other resources that dig much deeper into blocks, procs, and the ruby wonderfulness going on behind the scenes, but this is purely a functional demonstration. Suffice to say that placing a &block (or &proc or whatever you want to call it) as the last parameter of a method allows that method to accept a block and later yield to it. The yield statement works much the way it does in layouts, passing code execution for a time and then getting it back when the yielded code is finished.

Do make note that, like form_for, you do not use the <%= but just <% when declaring your block. This is because you aren't returning anything, but rather directly concatenating code onto the binding passed when you make the block call.

Now that was certainly useful, but doesn't really have that 'wow' factor that really makes you appreciate a new way of doing things. Let's take a stab at another common problem in app design: menu systems. Using block-accepting helpers and introducing the concept of a builder class, we can build menus intelligently and easily.

The implementation you are probably already familiar with of this type of class is the FormBuilder used in form_for. We will take that same concept and apply it to menus on a web application, taking care of a number of common headaches associated with menus. Here's the whole enchilada, and we'll go through it piece by piece. This code should be dropped into a helper:

def menu(options = {}, &block)    id = options[:id] || 'menu'    concat("<div id='#{id}'><ul>",block.binding)    # we yield a MenuBuilder so that specialized methods     # may be called inside the block    yield MenuBuilder.new(self)    concat("</ul></div>",block.binding)  end    class MenuBuilder    def initialize(template)      # by passing in the ActionView instance, we gain       # access to all of its helpers through @template      @template = template      @action_count = 0    end      def action(name, options = {}, html_options = {})      # create a local array for the classes we will be tacking on to the list item      classes = Array.new      # add the 'current' class if the current page matches the link options      classes << 'current' if @template.current_page?(options)      # add the 'first' class if this is the first time we've added an action      classes << 'first' if (@action_count == 0)      @action_count += 1      "<li class='#{classes.join(' ')}'>#{@template.link_to(name, options, html_options)}</li>"    end  end

So that should look at least somewhat familiar, right? Here's a sample implementation so that we can talk about what's going on:

<% menu do |m| %>    <%= m.action 'Home', home_path %>    <%= m.action 'Blog', blog_path %>    <%= m.action 'Account', edit_user_path(current_user) %>  <% end %>

So in just a few readable lines of code, we've done all we need for a pretty complex menu. However, because of our helper, all of the complexities are going on behind the scenes! So let's talk about our helpers. The actual helper that we call is very similar to the one we made earlier with one difference: rather than yielding with no parameters, we yielded a MenuBuilder object, which was then called in the yielded code to create menu actions. A builder can be used when there needs to be state information or more complex logic than simple helpers can provide.

In this case, we wanted to make sure that an action was marked as the first action if it was indeed the first to be called. By implementing an instance variable to count the number of actions that had been called, we are able to do this with ease. Additionally, if we ever wanted to change how the menu items were rendered, all of the changes are completely contained in the helper and builder, leaving the views unchanged.

I've found it a useful practice to stub out block-accepting helpers for any markup that I think I'll be using frequently at the start of an application. This gives the developers a chance to keep going while the design can evolve on a completely separate track. Additionally, it creates some fantastically readable code in the views.

Categories
Author
1
Subscribe to Dry