Monday, November 30, 2009

Clearing out content in content_for

In rails, multiple calls to content_for append their body's together and yield the concatenation of all the calls. Conceptually, this looks something like:

<% content_for :title do %>
Hello
<% end %>
<% content_for :title do %>
World
<% end %>


producing "Hello World" when you call
<% yield :title %>


In my current project, I was using redbox to create popup windows (see next post for details). In some cases, I wanted to have multiple popup windows on the same page. However, when I created those popups, each partial had a content_for :title and I was ending up with later popups having titles that contained all the words from previous titles.

After a little digging, I decided I needed a version of content_for that reset the value rather than appending to it. I found some old patches for rails that changed how content_for worked, but I thought it would be cleaner to have a new method that set the content_for variable rather than patching the version in rails.

#config/initializers/set_content_for.rb
module ActionView
module Helpers
module CaptureHelper
def set_content_for(name, content = nil, &block)
ivar = "@content_for_#{name}"
instance_variable_set(ivar, nil)
content_for(name, content, &block)
end
end
end
end


by putting this in /config/initializers it's automatically loaded by rails at startup. You use it just like you'd use content_for
<% set_content_for :title do %>
...whatever...
<end %>


but it clears out the current value of the section before setting it to the content of your new block.

Using a modal layout with redbox for popup windows

In my current project, I've been using redbox for popup windows. By default, these come up as a plain white box, so I wanted to style them with a colored area for a title, a region at the bottom for buttons, etc. So I created a layout that has all the styles

#app/views/layouts/_modal.erb
<div class="modal_box">
<div id="title">
<h3>
<%= yield :title %>
<span><%= link_to_close_redbox 'close' %></span>
</h3>
</div>
<div id="modal_flashes">
<%= render :partial => '/layouts/flashes' %>
</div>
<%= yield %>
</div>


then to create a modal window, I just create a partial with the content I want to appear in the modal window

#app/views/wizard/_advice.html.erb
<% content_for :title do %>
How to...
<% end %>
<div class="modal_content content_section">
<p>As the ...</p>
</div>
<div class="actions">
<%= link_to_close_redbox 'Close', :class => "modal_action_link" %>
</div>


and render that partial in a hidden div on my main view

#app/views/wizard/page1.html.erb
<div id="advice" style="display:none;">
<%= render :partial => 'advice', :layout => '/layouts/modal' %>
</div>


that is pulled by by a link_to_redbox call
<%= link_to_redbox 'advice?', 'advice' %>


In this case, I could have gone farther and pushed more of the styling & divs into the modal template. Then I would have had additional content_for's for :body and :actions. I didn't do that because in other places I needed to have the the popups display forms which included content in the body and submit buttons down in the actions. By making everything other than the title the be the content that was yielded, that was easy to do.

Friday, September 25, 2009

Watching the International Space Station

Allison, Jessie and I just got back from sitting on Skyline Boulevard watching the International Space Station shoot across the sky. It was super bright and it's very cool to watch this "star" streak across the sky and know that there are people living and working on it.

If you want to check it out, go to
http://www.heavens-above.com/

and put in your latitude and longitude to see when the ISS will be visible from where you live.

To find your latitude and longitude, pull up your home address on google maps and then paste

javascript:void(prompt('',gApplication.getMap().getCenter()));

into the place you normally enter URL's.

Thursday, July 16, 2009

DRYing Up Views Between Browser and Mobile Versions (across MIME types)

I was working on an app and decided that in order for it to be truly usable on mobile devices (iPhone, Blackberry, etc), it needed to have views that were tailored for small screens.

I started by registering a new MIME type 'mobile' so I could detect it, and respond_to mobile requests appropriately.
#config/initializers/mime_types.rb


Mime::Type.register_alias "text/html", :mobile


However, I quickly ran into a problem. I was duplicating a lot of code between my abc.html.erb views and my new abc.mobile.erb views. The obvious solution was to use partials. However, the *.mobile.erb views were failing to load the partials I already had.

What I realized after a little playing around was that if you have a view with a declared MIME type (new.mobile.erb) and you try to render a partial


<%= render :partial => 'posts' %>

Rails will look for partials with the same MIME type (_posts.mobile.erb), but it will not find partials with other MIME types (_posts.html.erb). However, the solution is to take the MIME type out of the partial (_posts.erb), then the partial will be found when called from either an HTML or a Mobile view.

Tuesday, June 09, 2009

RSpec route_for & params_from with Nested Routes & Custom Methods

I was just writing some route specs for nested routes that used PUT methods and it took some searching and experimentation to get it to work. I didn't find a lot of relevant documentation, so hopefully this will help others.

#routes.rb

map.resources :accounts do |account|
account.resources :accounts_users,
:member => {:upgrade => :put, :downgrade => :put}
end



Testing the generator (route_for)

route_for(:controller => "accounts_users",
:action => "upgrade",
:account_id => "3",
:id => "5").should ==
{:path => "/accounts/3/accounts_users/5/upgrade",
:method => 'put'}


In this case rather than matching the output of route_for against a simple string, you match it against a hash with :path and :method

Testing the recognition (params_from)

params_from(:put,
"/accounts/3/accounts_users/1/upgrade").should ==
{:controller => "accounts_users", :action => "upgrade",
:id => "1", :account_id => "3"}

Labels: , ,

Wednesday, January 16, 2008

Cool way to test controllers that are protected by an authentication system

Whenever you've got a Rails app that has a login system, you tend to run into problems testing your controllers because the before_filter :login_required (or whatever you've called it) fires before you get to any of the controller logic you want to test.

So you typically end up adding stuff to your tests to fake out the login tests ... either by manually creating a session that will pass the before_filter or by actually including code to do a valid login in the setup (before) for the test.

I just came across a blog post:
Using Rspec On Controllers that points out that you can very simply avoid this problem by stubbing out the method being called by the before_filter:


controller.stub!(:myfilter).and_return(true)


Sweet!

-Steve

Tuesday, December 25, 2007

Nice Post on Controllers that extend an API

I just read Nathan's post on RESTful controllers:

REST: From theory to practice

I found this article helpful. One of the things about RESTful applications that always felt awkward was how to go beyond controllers that fronted a model object. I thought the examples in this post made sense as concrete examples of where you would want to create "subcontrollers" that provide an extended API and additionally functionality for a single model.

Labels: ,

Monday, August 13, 2007

Talk on RDebug from the SF Ruby Meetup

I had a chance to speak at the August 13th Ruby Meetup in San Francisco. Here are the slides about RDebug and using it with Rails.

The slides


Labels: , , ,