Episode 35: Custom REST Actions

Posted over 7 years back at Railscasts

REST adds many constraints. It restricts your controllers to seven actions. Normally this is okay, but sometimes you need to add your own custom actions. Learn how in this episode.

DRYing Up Polymorphic Controllers

Posted over 7 years back at Revolution On Rails

Polymorphic routes allow drying up the controller implementation when functionality is identical, regardless of entry point. A good example is comments for articles and blogs. There is a challenge to balance the implementation of the comments controller reflecting the multiple incoming routes. Let's look at the way it could be written.

Routing is straightforward with blogs and article models acting as commentable and both the comment model and comment controllers being polymorphic:

ActionController::Routing::Routes.draw do |map|
map.resources :articles, :has_many => [ :comments ]
map.resources :blogs, :has_many => [ :comments ]
end


This means that a comment can be created via post to either /articles/1/comments/new or /blogs/1/comments/new. The comments controller can be implemented to handle both:

class CommentsController < ApplicationController

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to parent_url(@parent)
else
render :action => 'new'
end

end

private

def parent_object
case
when params[:article_id] then Article.find_by_id(params[:article_id])
when params[:news_id] then News.find_by_id(params[:news_id])
end
end

def parent_url(parent)
case
when params[:article_id] then article_url(parent)
when params[:news_id] then news_url(parent)
end
end

end


This method works fine and there is not much drive to start refactoring it right away. This changes, though, if there is a need to add another commentable or allow some other polymorphic route. Instead of adding more 'when' clauses the whole functionality can be extracted and abstracted based on the idea of having fixed naming conventions for resources that allow movement from a controller name to a model. The refactored example has the parent functionality extracted to the application controller to share it as-is with other polymorphic routes:

class ApplicationController < ActionController::Base

protected

class << self

attr_reader :parents

def parent_resources(*parents)
@parents = parents
end

end

def parent_id(parent)
request.path_parameters["#{ parent }_id"]
end

def parent_type
self.class.parents.detect { |parent| parent_id(parent) }
end

def parent_class
parent_type && parent_type.to_s.classify.constantize
end

def parent_object
parent_class && parent_class.find_by_id(parent_id(parent_type))
end

end

class CommentsController < ApplicationController

parent_resources :article, :blogs

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to send("#{ parent_type }_url", @parent)
else
render :action => 'new'
end

end

end

The parent_resources call declares resources that are parent for a current controller. An alternative approach is to guess such parent resources from the request URI and routes. Aaron is currently working on a patch on Edge implementing it. We'll update this post later.

If you currently use multiple polymorphic resources and have if clauses in the controller code, you might want to rethink how it could be DRYed up using this approach. In some cases views are very parent type specific. Then it might be better to have different templates and partials rendered via render :template => "/controller/#{ parent_type }_action".

DRYing Up Polymorphic Controllers

Posted over 7 years back at Revolution On Rails

Polymorphic routes allow drying up the controller implementation when functionality is identical, regardless of entry point. A good example is comments for articles and blogs. There is a challenge to balance the implementation of the comments controller reflecting the multiple incoming routes. Let's look at the way it could be written.

Routing is straightforward with blogs and article models acting as commentable and both the comment model and comment controllers being polymorphic:

ActionController::Routing::Routes.draw do |map|
map.resources :articles, :has_many => [ :comments ]
map.resources :blogs, :has_many => [ :comments ]
end


This means that a comment can be created via post to either /articles/1/comments/new or /blogs/1/comments/new. The comments controller can be implemented to handle both:

class CommentsController < ApplicationController

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to parent_url(@parent)
else
render :action => 'new'
end

end

private

def parent_object
case
when params[:article_id] then Article.find_by_id(params[:article_id])
when params[:news_id] then News.find_by_id(params[:news_id])
end
end

def parent_url(parent)
case
when params[:article_id] then article_url(parent)
when params[:news_id] then news_url(parent)
end
end

end


This method works fine and there is not much drive to start refactoring it right away. This changes, though, if there is a need to add another commentable or allow some other polymorphic route. Instead of adding more 'when' clauses the whole functionality can be extracted and abstracted based on the idea of having fixed naming conventions for resources that allow movement from a controller name to a model. The refactored example has the parent functionality extracted to the application controller to share it as-is with other polymorphic routes:

class ApplicationController < ActionController::Base

protected

class << self

attr_reader :parents

def parent_resources(*parents)
@parents = parents
end

end

def parent_id(parent)
request.path_parameters["#{ parent }_id"]
end

def parent_type
self.class.parents.detect { |parent| parent_id(parent) }
end

def parent_class
parent_type && parent_type.to_s.classify.constantize
end

def parent_object
parent_class && parent_class.find_by_id(parent_id(parent_type))
end

end

class CommentsController < ApplicationController

parent_resources :article, :blogs

def new
@parent = parent_object
@comment = Comment.new
end

def create

@parent = parent_object
@comment = @parent.comments.build(params[:comment])

if @comment.valid? and @comment.save
redirect_to send("#{ parent_type }_url", @parent)
else
render :action => 'new'
end

end

end

The parent_resources call declares resources that are parent for a current controller. An alternative approach is to guess such parent resources from the request URI and routes. Aaron is currently working on a patch on Edge implementing it. We'll update this post later.

If you currently use multiple polymorphic resources and have if clauses in the controller code, you might want to rethink how it could be DRYed up using this approach. In some cases views are very parent type specific. Then it might be better to have different templates and partials rendered via render :template => "/controller/#{ parent_type }_action".

Possibilities become real.

Posted over 7 years back at work.rowanhick.com

A while back, at a previous company, our clients inundated us with feature requests. At one point at the infancy of this software's life, the codeword for "thank you for your request, we'll try to get it into the software" was called Possibilities. It always left the door open, however I'm sure the rate at things went through the door and never came back (well not until years later!) possibilities became a dreaded word... Now however (my personal belief) is between Rails and Flex turning the seemingly impossible to implement round in no time at all. After a rocky road start to Flex, I'm now having the opportunity to develop a full blown B2B application in it. Replacing a PHP/HTML solution. Both exciting and nerve racking - last night a good 2 hours was spent tracking down how exactly to get something shown in the correct order. I am well impressed, and looking forward to the future of possibilities with Flex 2, Apollo (and what Flex 3 may bring). To share my enthusiasm a good portion of this blog is going to be dedicated to documenting the rebuild of the previously mentioned B2B application. Currently I have client buy in, after ~ 6 hours of knocking up simple interface showing off a very 'windows like' application within a web browser he's sold on it, so it's now the big re-write. Here's the starting point for it: - A rails app with database migrations to take the v1 app's database and migrate it into a rails friendly naming scheme. - Basic objects replicating core functionality within Rails - Two flex front ends, one for general order management, and the other a tailored interface specifically for manufacturing. So far, here's my pro's and con's list Pro + It's going to work once, anywhere. Flash bugs cross browser are fee and far between. (compare that with XHTML/CSS/JS) + More native application functionality (drag/drop, grids etc) + Less network traffic per session + More responsive application Cons - Write, compile, write, compile, write, compile (although Flex Builder/Flex compiler shell reduces the pain) - A lot more code in building the interface - Guess work figureing out how to do complex stuff - Loosing some of Rails niceties (or duplication of things like validations) Next up some screenshots....

RailsConf 2007 - Ruby on Rails Podcast

Posted over 7 years back at Ruby on Rails Podcast

Interviews from RailsConf 2007 in Portland.

Episode 34: Named Routes

Posted over 7 years back at Railscasts

When you add a custom route, make it a named route so you have url helper methods to easily link to that new route. See episode for details.

Railsconf 2007 FTW

Posted over 7 years back at zerosum dirt(nap) - Home

Had a great time at Railsconf 2007! Just got back. A little jetlagged, and in need of (another) nap. In summary: lots of good presentations, superb microbrews, a hilarious zefrank performance, and a number of lurking lolcats.

Highlights for me included DHH’s keynote, the Rails way ‘live’ performance by Jamis and Koz (although I have to politely disagree with them on the need for private ActiveRecord attributes), Ezra’s deployment and scaling session, and the homesteading talk by Matthew Bass. Thanks to everyone who helped make it happen.

Also got to meet some fellow IRC peeps in person. Now ur tru idinty is nown to me, bewar!

Episode 33: Making a Plugin

Posted over 7 years back at Railscasts

You can sometimes remove a lot of duplication by generating methods dynamic. In this episode I will show you how to create a plugin which does exactly that.

Only In Kenya

Posted over 7 years back at Sporkmonger

As a few of you are probably aware by now, I will be taking a break from Ruby, and computing in general for a couple of months. I am getting on a plane headed for Nairobi on May 20th. I won’t be back until August 26th. I’m not taking a computer with me, though I will have limited access through internet cafes, so email should work, but don’t expect quick replies.

For those who are curious, the intent of the trip is humanitarian in nature. There will be trips to Uganda and Tanzania, and some volcano climbing. I’ll be sure to take lots of photos, which will no doubt end up on Flickr.

Update:

In London. About to fall asleep.

Update:

In Kenya. Amazingly still awake.

Episode 32: Time in Text Field

Posted over 7 years back at Railscasts

Although Rails does allow you to edit time attributes with text fields, it's not very flexible. In this episode you will learn how to use a virtual attribute to format the time to your liking.

RailsConf '07 and SF geekSessions

Posted over 7 years back at PJ Hyett

The Err Free team will be accepting beers at RailsConf for anyone interested, come find us.

Chris will be talking about his acts_as_cached plugin on Saturday. He promised me it’s gonna be good and he’s not one to disappoint.

Also, if you’re going to be in the Bay area after RailsConf, there’s a three hour session on the 22nd concerning whether Rails can scale.

It’s $15, has smart people talking, and there’s an open bar afterwards…need I say more? Check it out.

RailsConf '07 and SF geekSessions

Posted over 7 years back at PJ Hyett

The Err Free team will be accepting beers at RailsConf for anyone interested, come find us.

Chris will be talking about his acts_as_cached plugin on Saturday. He promised me it’s gonna be good and he’s not one to disappoint.

Also, if you’re going to be in the Bay area after RailsConf, there’s a three hour session on the 22nd concerning whether Rails can scale.

It’s $15, has smart people talking, and there’s an open bar afterwards…need I say more? Check it out.

Oregon

Posted over 7 years back at zerosum dirt(nap) - Home

So here I am in sunny Oregon. Around 1PM I hop on a train headed to Portland for RailsConf 07. Came out a few days early to catch up with my good buddy Ty in Eugene and work on the startup idea we’ve been banging around. And do some hiking, drinking, and dissecting of Heroes too, of course.

I kind of love being out here and yet still having my internal clock set to east coast time. I wake up “early” for a change, and bang out a nasty chunk of work before anyone else here is even awake yet. Otoh, I feel like an old man when I begin to falter around 11PM. Heh.

Anyway, I’m looking forward to the conference. I’ll be attending the tutorials day tomorrow, specifically the scaling session with Joyent’s Jason Hoffman as well as David Black’s ‘Routing Roundup’. If you see me, say hi.

Backup of Bloglines keep new items

Posted over 7 years back at Spejman On Rails

If you are a Bloglines user, you probably save the interesting posts using "keep new" feature.

If you have use this reader for a time, the quantity of post saved could be large... Some day I thought that won't be funny to loose all this data, then I build a ruby script to backup bloglines "keep new" items into a xml file.

With this script I learned better Mechanize and Hpricot Ruby libraries.
Este script me ha servido para probar dos librerías muy útiles de Ruby: mechanize y hpricot.

You need these libraries in order to use the script:


gem install json
gem install activesupport
gem install hpricot
gem install mechanize


And here you have the script. I think is very understandable. I hope it helps you to backup your bloglines account (remember to change EMAIL and PASSWORD values with yours) or to understand better how mechanize and hpricot works.

require "rubygems"
require "hpricot"
require "json"
require "mechanize"
require "active_support"

# Reads a bloglines javascript tree structure that has all
# feeds data.
def read_tree( tree_base, label = "" )
tree_base.each do |tree|
if tree["kids"]
read_tree tree["kids"], label + "/" + tree["n"]
else
@feeds << [tree["n"], label, tree["kn"], "http://www.bloglines.com/myblogs_display?sub=#{tree["id"]}&site=#{tree["sid"]}"]
end
end
end

# Add more memory to hpricot otherwise couldn't load some webs.
Hpricot.buffer_size = 262144

agent = WWW::Mechanize.new
page = agent.get 'http://www.bloglines.com/login'

form = page.forms[1]
form.email = 'EMAIL'
form.password = 'PASSWORD'

page = agent.submit form

# Get the bloglines sindicated feeds
menu_page = agent.get "http://www.bloglines.com/myblogs_subs"
start_text = "var initTreeData = "
end_text = "\n;\n"
js_feeds_tree_str = menu_page.content[menu_page.content.index(start_text)+start_text.size..menu_page.content.index(end_text)]
feeds_tree = JSON.parse js_feeds_tree_str.gsub("\\","")
@feeds = []
read_tree(feeds_tree["kids"])

puts "<bloglines_saves>"
@feeds.each do |feed|

page = agent.get feed[3]
doc= Hpricot(page.content)

# get the content of all saved feed posts
content = ((doc/"body")/"td.article")
next if content.empty?
puts "<feed name=\"#{feed[0].strip}\" folder=\"#{feed[1].strip}\">"

# Iterate each saved feed post
((doc/"body")/"a.bl_itemtitle").each_with_index do |title, index|
puts "<feed_save title=\"#{title.inner_html.strip}\" href=\"#{title.attributes["href"]}\">"
puts content[index].inner_html.to_xs
puts "</feed_save>"
end
puts "</feed>"

end
puts "</bloglines_saves>"

Más información:

Hampton Catlin - Ruby on Rails Podcast

Posted over 7 years back at Ruby on Rails Podcast

The author of HAML rants on markup, Macs, diversity, and tech writing.
Also mentioned