Gift

NOTE: My thanks go out to the guys at RailsConf who attended my "Birds of a Feather" talk about DRYing up views using wrapper helper classes. They gave me the idea to abstract my ideas one level further into its own class and now we have GiftWrap -- I hope you guys can make good use of it!

Just get GiftWrap now!

UPDATED: GiftWrap has been now been updated to allow for using wrapper partials with the use_partial method rather than the before_yield and after_yield methods. Read the README file to see examples of how to use this new functionality.

Blocks and Procs

I have always been very impressed by the way the Rails form_for helper method works. I loved that you never had to remember the </form> closing tag or use the silly (and very deprecated) end_form_tag helper method. Perhaps what I was most drawn to about the method was how the block acted as a "closure" -- not just in the programming sense, but also in a semantic sense. It struck me as very beautiful that this method could create a mini-environment of rules and abilities, allow anything to happen within this environment and then close that context until the next time.

And that, of course, is the beauty of using blocks and procs in Ruby.

So why do these blocks in the view stand out to me when their counterparts in models and controllers simply aren't that big of a deal? I think part of the reason is that you don't see blocks in the view very often. The content_tag helper takes a block, but the method itself isn't really that helpful. Looping methods like each take a block, of course, but again that's nothing to blog about. Form_for seems to be a fairly unique method within the Rails framework.

I have found, however, that using blocks within the view to wrap and contextualize markup can be extremely helpful in DRYing up views and making markup more modular.

Nice and Easy Blocks with GiftWrap

Creating a method that works like form_for is a little tricky. First you have to create a helper class with a series of methods for generating the wrapper markup and anything else you need. Then you need to create the helper method itself, which initializes your class, uses the confusing concat method to write your wrapper markup to the ERB buffer, and then yields the class instance to your block.

Thankfully, I was able to abstract all the common elements into the GiftWrap class. By subclassing GiftWrap and following a couple simple conventions, you will easily be able to create wrapper classes that can bring clarity and DRYness to your views.

Example: The Help Popup

The site that I am currently building requires a large number of "question mark" buttons -- if the user is unsure of what a certain section of the site does, a quick press of help button gives them a simple popup box with an explanation. The markup for these buttons/popups looked like this:

<p class="help_popup_link" id="feature_help_popup_link">
  <a href="#" onclick="$(this).up('.help_popup_link').next().toggle(); return false;">Help</a>
</p>
<div class="help_popup" id="feature_help_popup" style="display: none;">
  <p class="close_button">
    <a href="#" onclick="$(this).up('.help_popup').hide(); return false;">Close</a>
 </p>
 <!-- any amount of markup should be insertable here -->
</div>

These popups will all share similar markup, but their content can vary wildly. I quickly ruled out using partials to DRY my view -- I need too much flexibility in what goes inside this wrapper markup.

I chose instead to use a wrapper class that inherits from GiftWrap.

class HelpPopup < GiftWrap

  before_yield :help_button, :open_block, :close_button
  after_yield :close_block

  def initialize(id)
    @id = id
  end

  private

  def help_button
    %Q{
      <p class="help_popup_link" id="#{@id}_help_popup_link">
        #{link_to_function 'Help', "$(this).up('.help_popup_link').next().toggle()"}
      </p>
    }
  end

  def close_button
    %Q{
      <p class="close_button">
        #{link_to_function 'Close', "$(this).up('.help_popup').hide()"}
      </p>
    }
  end

  # Called before yielding a block to this instance
  def open_block
    %Q{<div class="help_popup" id="#{@id}_help_popup" style="display: none;">}
  end

  # Called after yielding a block to this instance
  def close_block
    "</div>"
  end

end

As you probably have guessed, the methods passed into the before_yield method will be called in order before the block is run and the returned values will be added to the markup. The after_yield method acts the same.

When GiftWrap is subclassed, a helper method is dynamically generated as the underscored name of the new class. In this example the helper method will be named help_popup because the class is named HelpPopup.

The implementation now looks like this in the view...

<% help_popup :feature do %>
  <p>This is explanation text within the popup box.</p>
  <p>More text here.</p>
<% end %>

This generates this markup...

<p class="help_popup_link" id="feature_help_popup_link">
  <a href="#" onclick="$(this).up('.help_popup_link').next().toggle(); return false;">Help</a>
</p>
<div class="help_popup" id="feature_help_popup" style="display: none;">
  <p class="close_button">
    <a href="#" onclick="$(this).up('.help_popup').hide(); return false;">Close</a>
 </p>
 <p>This is explanation text within the popup box.</p>
 <p>More text here.</p>
</div>

Example: Boxes with Rounded Corners

Another quality of this site is that it uses boxes with rounded corners extensively. Using a GiftWrap subclass I can remove all the repetitive wrapper markup needed for a 'sliding doors' approach, but that's not all! I also want to use a standard set of markup and CSS classes for my subheaders and "more" links, so it would be great to encapsulate that within this block as well. Here's how we can do it...

class RoundCornerBox < GiftWrap
  before_yield :header, :open_divs
  after_yield :close_divs

  # A new instance of RoundCornerBox will be instantiated each time the
  # dynamic round_corner_box is called in the view. The arguments passed into
  # the helper method are passed on directly to the initialize method.
  def initialize(name, options={})
    @name = name
    @header = options[:header]
  end

  # # # These methods can be used within your wrapper block. # # #

  def subheader(text)
    "<h4 class='subheader'>#{text}</h4>"
  end

  def more_button(text, url)
    "<p class='more_button'>#{link_to text, url}</p>"
  end

  private

  # # # These methods will only be used internally for creating the wrapper markup. # # #

  def header
    @header ? "<h3 class='round_corner_box_header'>#{@header}</h3>" : ''
  end

  def open_divs
    %Q{
      <div class="round_corner_box_top">
        <div class="round_corner_box_bottom" id="#{@name}_round_corner_box">
    }
  end

  def close_divs
    "</div>\n</div>"
  end
end

Now here's how we can implement those public methods...


<% round_corner_box :news, :header => "Today's News" do |b| %>
  <%= b.subheader "Top Story" %>
  <p>Today was a very exciting day in the news...</p>
  <%= b.more_button "Full Story", top_story_path %>

  <%= b.subheader "Lesser Story" %>
  <p>This story is definitely less important than the top story...</p>
  <%= b.more_button "Full Story", bottom_story_path %>
<% end %>

With the GiftWrap class, you now can easily harness the power and flexibility of using wrapper classes in your view to DRY up your code. So go ahead, get GiftWrap now!

If you have ideas for how to improve or extend GiftWrap, please feel free to fork the Git repository or leave comments.



Jake1

Recently I was working with a unique relationship in which each child object was a specific instance of its parent while also inheriting multiple attributes from that parent. In this particular situation the traditional method of accessing the parents attribute though the child is both tedious and verbose. Rather then calling the attribute directly, I wanted to clean things up and call the child directly, leaving the child to fetch the attribute from the parent. Not only would this reduce the amount of code needed, but it would also help separate the concerns of the model from the views. I decided to concentrate on imitating the inheritance rather then the relationship since the inheritance evolved from normalizing the data.

At first I thought it would be easiest to override Child#method_missing, an obvious choice for dynamically monkey patching any object. Unfortunately I was unable to find a working pattern that did not rely on exceptions, the time consuming respond_to? method or a white list of method names to forward. The voices in my head kept insisting there had to be a better, more ruby-esque way to achieve the same result while automating the grunt work.

With a bit of help from Google and caffeine I stumbled over Rails Ticket 4133 incorporating the delegate method, which creates a method that delegates attribute calls onto related objects.

class Parent << ActiveRecord::Base
  has_many :children
end

class Child << ActiveRecord::Base
  belongs_to :parent
  delegate :name, :to => :parent
end

However there are a few drawbacks to using the delegate method, such as still relying on a white-list of attributes, and the helper costing more keystrokes then the original code. Even more disturbing is that the delegate helper does not generate the attribute query method. I adore uniformity in software, and if a model method Child#name exists, so should the query accessor Child#name? (at least in Rails).

Fortunately we can inject a bit of voodoo into ActiveRecord itself to extend and customize how the delegate method operates. Although I have taken the low road and created a monkey patch, with a few slight modifications and a well rounded test suite a Rails plugin could be born.

module ActsAsDelegatableTo
    def acts_as_delegatable_to(table, *exceptions)          
          local_columns = self.columns.map { |attr| attr.name }
          table_columns = eval(%{#{table.to_s.capitalize}.columns}).map { |attr| attr.name }
          table_columns.reject! { |attr| attr == 'id' or local_columns.include?(attr) or exceptions.include?(attr) }

          table_columns.each { |attr|
                  class_eval(%{delegate :#{attr}, :to => :#{table}})
                  class_eval(%{delegate :#{attr}?, :to => :#{table}})
          }
    end
  end

  ActiveRecord::Base.send :include, ActsAsDelegatableTo
  

Rather then explicitly programming or including the delegate methods using the white-list approach, acts_as_delegatable_to creates delegate methods to each of the fields defined in the parent's data model, excluding any the child may have redefined. But wait, there's more! For only a few key strokes more we also gave ourselves the standard Rails attribute query methods, providing access to not only Parent#attribute but Parent#attribute? as well.

class Parent << ActiveRecord::Base
  has_many :children
end

class Child << ActiveRecord::Base
  belongs_to :parent
  acts_as_delegatable_to :parent
end

By calling acts_as_delegatable_to :parent, each Child object instance will have attribute accessors and query methods to each field belonging to its parent. Just remember that this sort of relationship is not common before blindly inheriting all of the attributes of related objects. This method could cause nasty headaches and sleepless nights if it is not used with caution. There are no protections for naming conflicts or duplicated class evaluations, and the inheritance is generated based on the data model, not the business logic. If you decide to take this approach, just proceed with caution!



Black-carriage

From the Browser to the Desktop

As websites graduate to version 2.0 with their dynamic user-generated data-driven content we are adjusting our vocabulary to talk about them. We are no longer just building sites, we are launching applications. Indeed many applications from the desktop realm are being replicated online. Google Documents, for example, could hypothetically replace the need for something like Microsoft Office, but with the added benefit of everything being a part of the cloud so that a document may be accessed and edited by multiple users from multiple locations, independent of each users' operating system.

This isn't a one way street, desktop applications do not all live in the 'desktop bubble'. At startup many applications check for updates and external information, like iTunes downloaded track information, album artwork and so on.

Lately the line between the internet and the hard drive has become a bit blurred. Several technologies are in development that allow web developers to create what appear to be full fledged desktop applications using HTML, CSS, Javascript, Actionscript — the Web oriented tools we already know. An overly simplified description of this is a browserless Web application that doesn't necessarily use the Web. Google Gears, Mozilla's Prism and Adobe AIR are a few of the efforts put forward recently to move the web onto the desktop to varying degrees.

I recently had the opportunity to try out one of these "horseless carriages". What follows is an overview of my experience with the Adobe Integrated Runtime (AIR), formerly known as Apollo.

The Pieces

There are two pieces to any AIR application. First there's the runtime environment which handles HTML rendering and all of the difficult behind-the-scenes stuff that us web developers would rather not worry about. The second piece is the actual application composed of your regular Web technologies (HTML, Javascript, PDF's, video, Flash, etc).

When a user installs an AIR application for the first time they must install the runtime environment as well. Later, when the user installs another AIR app, they will not have to download the already installed runtime. The whole download and installation process is designed to be "seamless", appearing as if the user is downloading just one item regardless of whether they need to download AIR or not. As I'll explain later, however, it is not always so smooth.

Benefits

So why build an AIR application?

Why put a Web application on your desktop? One of the advantages that all of the aforementioned tools cite is the ability to work offline. Granted, reliable internet access is becoming less and less of an issue, but what good is any Web application when you don't have a working internet connection? Working offline usually means storing information in a database and synching that information with a master database when a connection becomes available later. AIR comes packaged with SQLite for this purpose. Alternatively data can be stored as XML and can even be encrypted. Furthermore, if it is not necessary to store some information online, storing it locally can save some time because data does not need to be sent back and forth. This all makes for a smoother, persistent user experience.

Consistancy

AIR uses the WebKit HTML rendering engine which recently passed the Acid3 test for Web standards, so you can rest assured that your floats will properly float and your padding will properly pad. No need to hack together special code for you-know-who.

OS integration

AIR provides a windowing API that allows you to interact with the operating system's windows. You can set the size and position of windows, restrict them to a range of sizes, dictate whether they can be resized or minimized, display your app fullscreen, or run the app with no window at all. You can even create your own custom window GUI. One of the advantages of having this kind of control is that browser windows often have elements (back/forward and refresh buttons, search bar, etc) that are not necessarily relevant to every application. Sometimes less is more. AIR also allows for other kinds of integration with the desktop including access to the file system, clipboard data, drag and drop events and so on.

The Seams

While all of this is good and exciting and lets web developers pretend that we can build desktop applications, AIR's system is not perfect.

AIR detection

As I mentioned before, any AIR app is composed of two pieces; the runtime environment and implementation code. If the user already has AIR installed it doesn't make much sense to install it again. Adobe provides a Flash movie that they call a "badge" that functions as a button for downloading the application. More importantly, it also performs a bit of magic to determine whether the user has AIR installed already or not. If they don't, it downloads both AIR and your app and installs them together all in one smooth and easy action.

This is all fine and well except for the fact that the badge requires the latest version of Flash in order to do this. A bit of plugin version detection can render an alternative page from which the user can download first AIR and then your app. This isn't so bad except for:

  1. If they don't install AIR first followed by your app second, the installation of your app will fail.
  2. Users often don't know what they have installed on their computer and may try to install AIR unnecessarily.
  3. Explaining to your users why they need to download and install item 'A' first in order to install item 'B' may lead to a bit of required reading and confusion. Such messages are in danger of being ignored as a bunch of legal mumbo jumbo or simply as a waste of time.

Granted, the users who will be tripped up by such things are probably a small minority of users lacking in computer savviness and patience, but you should still be aware of who your audience is. If they are likely to have trouble with the two-piece download then chances are good that their version of Flash Player is not even current enough to do the AIR detection, thus all but defeating the purpose of the "seamless" download feature.

Security Certificates

Because AIR apps can be given access to the user's system and transfer data over the internet there is an obvious security concern. Adobe requires that AIR apps be packaged with a security certificate (hopefully) verifying the authenticity of the application. This is designed to prevent someone else from maliciously altering your app and redistributing it, getting you into a heap of trouble.

You certainly don't want to get into trouble because someone posing as you has done something nasty to your users, but preventing this isn't ideal either. The solution is to purchase a certificate from a certificate authority such as Thawte or Verisign for around $300 a year, but some developers or clients may consider this to be an unnecessary expense. This doesn't actually prevent anyone from creating a fraudulent version of your app, it just proves that you are the one who packaged your app.

The alternative is to create a self-signed certificate, and Adobe provides some tools for easily doing this. The only problem with self-signing your certificate is that when the user installs your app they are presented with this window:

The language here is intentionally ominous. You are most likely creating something innocent, but your users may be scared off by the idea of giving someone of unknown identity unrestricted access to their computer. I don't know that there is a better solution to the security issue but it is something to consider.

Perhaps I'm griping about things that are inevitable and/or insignificant, but there's my two cents. I do think AIR is worth a try, at least to impress your friends with your mad desktop app building skills.




RSS Feed


CATEGORIES


ARCHIVES


BOOKMARKED


Add to Technorati Favorites