Feed Fetcher: A plugin for finding RSS feeds from URLs
Posted by Craig Ambrose on March 27, 2007 at 08:52 AM
I’ve got a site with a few blog aggregation features, and when users type in their blog URL to add to the site, they don’t seem to know the difference between the blog html page URL and the blog RSS feed URL.
So, I wanted the site to work regardless of which one they used, by finding RSS resource links from the HTML page (if that’s what they enter). This is what Google Reader and all the other nice looking blog readers do.
It’s not earth shatteringly exciting code, but it’s good enough to re-use, and it’s fully tested, so I’ve made into a plugin. Now there’s no excuse for offering a sloppy user experience for subscribing to feeds.
Installation
script/plugin install svn://rubyforge.org/var/svn/ambroseplugins/feed_fetcher
Usage
The main function that you’ll be using is the class method get_feed_source on the FeedFetcher class.
If successful, it’ll return a FeedSource object, that you can interrogate for the feed details or
the items in the current feed. If it fails, it’ll throw an exception that you can use to determine
the problem and return a message to the user.
Here’s the code that I use to call the feed fetcher from within my Blog model. If it works, I’m
setting a couple of instance members on self.
begin
result = FeedFetcher::FeedFetcher.get_feed_source(site_url)
if result
self.feed_url = result.url
self.title = result.title
end
rescue FeedFetcher::NoFeedForPageError
@feed_error = "Sorry, we couldn't find a feed for this URL. Your blog needs to have a RSS feed facility for us to use it on Playful Bent."
rescue FeedFetcher::PageFeedError
@feed_error = "You blog has a RSS feed, which is great. However, it doesn't work for us right now, which is less great. Sorry, this wont work."
rescue FeedFetcher::PageDoesntExistError
@feed_error = "Are you sure you typed that right? We just tried to fetch that URL and we couldn't find anything there at all."
end
More Info
I keep a static page on this plugin here. You can check back there for the current information at any point.
In Support of Kathy Sierra
Posted by Craig Ambrose on March 26, 2007 at 10:05 PM
This is the first time I’ve written a post here that doesn’t have a chunk of code or a programming technique explained. However, it’s important enough to break my stride for a moment.
If you’re into rails, or part of the web2.0 crowd, then you are most certainly indebted to the work that Kathy Sierra has done.
Death Threats Against Bloggers
This shit is not ok.
Go have a read and offer support and encouragement.
Redbox Has Moved
Posted by Craig Ambrose on March 25, 2007 at 11:53 PM
I’ve been having svn troubles over the last few days, and people haven’t been able to download Redbox, or my Associated List plugin. I’ve moved them both to rubyforge, which should be much more reliable. New locations at:
Redbox
svn://rubyforge.org/var/svn/ambroseplugins/redbox
Associated List
svn://rubyforge.org/var/svn/ambroseplugins/associated_list
I’ll try and filter these through to all the appropriate places. In the meantime, you can update your plugin sources for a given project using:
script/plugin source svn://rubyforge.org/var/svn/ambroseplugins/
(from within a rails application base directory)
The Dangers of Removing Duplication
Posted by Craig Ambrose on March 16, 2007 at 08:32 PM
Now there is a controversial title.
I’m not trying to say that it’s dangerous to remove duplication in your code, merely that there are dangers that you might face when doing so. Duplication in code is a bad smell. It’s a reminder to us that we need to refactor it in some way in order to improve the design and make the duplication go away.
Sometimes, we get so caught up in the idea the removing the duplication is the only goal that we forget about the fact that this process is actually supposed to drive good object oriented design. Looking back at rails code that I’ve written over the last year, the place where I’m seeing this more than any other is in helpers.
Rails helper methods are places to extract logic from the view. They are tremendously handy, and a nice easy place to stick logic that appears in several different view templates, or contains so much conditional logic that it would make our rhtml templates unreadable. However, they are also just procedures. I find myself slipping into the trap of using procedural programming techniques to remove duplication here, rather than good design practices. Sometimes you can spot this because you find yourself starting to add a lot of arguments to you helper methods.
I’m going to share with you an example of some of my bad code. No programmer likes to do this, but I don’t feel too bad about it because I’ve just removed this method from a project, so I can at least say I did write this bad code, but now I’ve improved it.
Some Code in Need of Refactoring
def editable_section_if(condition, name, resource_name = nil, form_url = nil)
rollover_link_name = "rollover_link_#{name}"
display_div = "display_#{name}"
edit_div = "edit_#{name}"
partial_prefix = resource_name.nil? ? '' : "#{resource_name}/" result = "" if condition
result << "<div class=\"editable_text\" onmouseover=\"Element.show('#{rollover_link_name}')\" onmouseout=\"Element.hide('#{rollover_link_name}')\" id=\"#{display_div}\">"
result << '<div class="rollover_link_bar">'
result << link_to_function("Click to Edit", "Element.hide('#{display_div}'); Element.show('#{edit_div}')", {:class => "rollover_link", :id => rollover_link_name, :style => "display: none;"})
result << ' </div>'
result << render(:partial => "#{partial_prefix}display_#{name}")
result << '</div>' result << "<div id=\"#{edit_div}\" style=\"display: none\">" form_url = {:action => "update_#{name}", :id => params[:id]} if form_url.nil?
form_url.merge!(:controller => resource_name) unless resource_name.nil?
result << form_remote_tag(:url => form_url, :method => :put, :update => name, :loading => "Element.show('spinner_#{name}')", :loaded => "Element.hide('spinner_#{name}')", :html => {:class => 'inline'}) result << render(:partial => "#{partial_prefix}edit_#{name}")
result << "<div class=\"editable_form_buttons\">"
result << submit_tag("Save")
result << " or "
result << link_to_function("Cancel", "Element.hide('#{edit_div}'); Element.show('#{display_div}')")
result << "</div>" result << '</form>'
result << '</div>'
else
result << render(:partial => "#{partial_prefix}display_#{name}")
end result
endAbout the Code
This is a helper method to generate an inline form, which remains hidden in the page. The div displaying the data is loaded from a partial, as is the contents of the form, and edit and cancel buttons are used to toggle between them with javascript. The submit button uses ajax to submit the form. Also, it uses a condition to determine if the data is even editable, and if not, just displays the data, not the form.
This approach is a fairly standard thing when you realise that you want to do some inline editing that’s more than just one field, and so is a bit more complex that the built in rails inline editor helpers.
So what are the bad smells here?
First up is the really heavy string manipulation. I find myself doing this in helpers a fair bit. I probably started writing this in a rhtml template and then moved it into a helper when I needed to re-use it. The ERb of a rhtml template is more suitable for something which is so html heavy. All this string append operations, and bits of html everywhere make this method really hard to read. If I was missing a closing html tag, would I be able to easily tell? Do you find yourself writing helpers like this, or is it just me? Perhaps it should be a partial.
Secondly, the last two parameters were actually added latter. They sound important, but they have default values of nil. They are actually an attempt to try and make this system compatible with REST. They don’t entirely succeed, and this is an ugly function to try and make work in this situation.
Thirdly, this method is too long. Some programmers believe that methods of this length are just fine. If you are one such, please don’t ever bother to send me your resume.
I have already refactored this code, and I like the way it’s coming along, so keep an eye out for another blog post soon where I’ll suggest some better ways of doing this.
Pagination Using AJAX
Posted by Craig Ambrose on March 12, 2007 at 08:35 AM
The rails pagination helpers automatically give us a set of links to load different pages of a result set. Occasionally, you might not want to load these other pages of results as full page loads. There’s no reason why you can’t call an index action using AJAX, and have it respond with some javascript to update the list.
If you’re going to do this, you’ll need a different pagination helper. Since I’m such a nice guy, I’ve already written it for you.
The Code
def ajax_pagination_links(paginator, options={}, html_options={})
name = options[:name] || ActionView::Helpers::PaginationHelper::DEFAULT_OPTIONS[:name]
url_params = (options[:params] || ActionView::Helpers::PaginationHelper::DEFAULT_OPTIONS[:params]).clone links = pagination_links_each(paginator, options) do |n|
url_params[name] = n
link_to_remote(n.to_s, {:url => url_params, :method => :get, :loading => "Element.update('#{name}_loading_number', #{n}); Element.hide('#{name}_links'); Element.show('#{name}_loading')", :complete => "Element.show('#{name}_links'); Element.hide('#{name}_loading')"}, html_options)
end
loading_number = content_tag('span', '', :id => "#{name}_loading_number")
loading_spinner = image_tag 'spinner.gif'
loading = content_tag('div', "...loading page " + loading_number + ' ' + loading_spinner, :id => "#{name}_loading", :style => "display:none", :class => 'loading_text')
links_tag = content_tag('div', links, :id => "#{name}_links")
links_tag + loading
end
Usage
Simply wack that method in a relevant helper module, and use as in the following example:
<%= ajax_pagination_links @message_pages, :name => 'message_pages' %>
You only really need to specify the name option if there is going to be more that one of these in the current DOM and thus you’re going to hit problems with multiples of the same ID.
Also, I’ve referenced a spinner image. You may want to provide that, or pull the image tag bit out of the code.
Finally, I’ve added classes to things if you want to style it. I think it looks good with:
div.loading_text
{
color: #666;
}
Loading Parts of a Page in the Background
Posted by Craig Ambrose on March 05, 2007 at 10:24 PM
Occasionally I find that one of my apps ends up with “the page from hell”. The design seems to require that a certain page pull in all sorts of information from different places, and perhaps some of it isn’t even viewed straight away, it’s just being loaded into the background so that the user can access it instantly.
I had such a page, which had a growing number of tab pages that I wanted to load without any delay at all. I was starting to get worried about the page load time, but perhaps even worse was the fact that the controller loading this page was becoming quite messy, since some of the tabs clearly involved other resources.
The following helper module might be of some use to you. It defines a helper method called call_remote_queue, which executes a sequence of queued ajax requests.
The Code
module RemoteQueueHelper def call_remote_queue(urls = [], spinner_id = nil)
javascript_tag remote_queue_js(urls.reverse, spinner_id)
endprotected def remote_queue_js(urls, spinner_id)
this_url = urls.pop
this_options = remote_url_options(this_url) return nil unless this_url if urls.empty?
this_options[:complete] = "Element.hide('#{spinner_id}')"
else
this_options[:complete] = remote_queue_js(urls, spinner_id) unless urls.empty?
end
remote_function(this_options)
end def remote_url_options(url)
if url.is_a? Hash
result = url
else
result = {}
result[:url] = url
result[:method] = :get
end
result
endend
Usage
<%= call_remote_queue [user_stories_path(@user), user_dares_path(@user)], 'area_tabbar_spinner' %>
In my case, the urls in question used rjs templates to add the tab page to the list of tab pages. The second, optional parameter is the id of a spinner element to disable when the chain of loading is complete.
Example
This was used on the user profile page of an adult social networking site over at Playful Bent. This is an adult site, so use your own judgement as to whether to view it at work.
Here is a link to a profile which demonstrates this code, and doesn’t display any particularly naughty pictures to annoy your boss (but no guarantees about five minutes from now I’m afraid, it’s user content).
A Rake Task for Database Backups
Posted by Craig Ambrose on March 01, 2007 at 12:54 AM
I wrote this little rake task for a client yesterday, and it seems handy enough that it’s worth disseminating. It doesn’t, however, seem to justify a plugin as yet.
Before doing this, I did search through the lists of rails plugins to see if there was a solution for backup up databases. I was fairly surprised to find that there wasn’t. Yes, to some extent this is our hosting provider’s responsibility. However, they often charge to retrieve backups, and might not give us the fine grained control that we are after.
To use this task, add the following code to a rakefile somewhere. I created a file called backup.rake, and put it inside /lib/tasks.
The Code
require 'find'namespace :db do desc "Backup the database to a file. Options: DIR=base_dir RAILS_ENV=production MAX=20"
task :backup => [:environment] do
datestamp = Time.now.strftime("%Y-%m-%d_%H-%M-%S") base_path = ENV["DIR"] || "db"
backup_base = File.join(base_path, 'backup')
backup_folder = File.join(backup_base, datestamp)
backup_file = File.join(backup_folder, "#{RAILS_ENV}_dump.sql.gz") File.makedirs(backup_folder) db_config = ActiveRecord::Base.configurations[RAILS_ENV] sh "mysqldump -u #{db_config['username']} -p#{db_config['password']} -Q —add-drop-table -O add-locks=FALSE -O lock-tables=FALSE #{db_config['database']} | gzip -c > #{backup_file}" dir = Dir.new(backup_base)
all_backups = dir.entries[2..-1].sort.reverse
puts "Created backup: #{backup_file}" max_backups = ENV["MAX"].to_i || 20
unwanted_backups = all_backups[max_backups..-1] || []
for unwanted_backup in unwanted_backups
FileUtils.rm_rf(File.join(backup_base, unwanted_backup))
puts "deleted #{unwanted_backup}"
end
puts "Deleted #{unwanted_backups.length} backups, #{all_backups.length - unwanted_backups.length} backups available"
endend
Usage
Here’s how the new backup rake task works. You call it with:
rake db:backupOptions
It has a few possible options, I’ll give an example first, then explain:
rake db:backup DIR=/home/sugarstats RAILS_ENV=production MAX=10The DIR option specifies the base directory for the backups. I create a “backup” directory inside that, so you don’t need to include that on your path. This actually defaults to “db” inside the application, but you will want to overwrite it with the option above.
RAILS_ENV should be set to production. It is probably development by default.
MAX is the number of backups to keep. It defaults to 20, which I think is a nice safe number. Once this number of backups is exceeded, older backups are deleted. There’s no way to turn this off, other than removing that bit of code.
In Production
You’ll want to call this from cron. Like all rake tasks, it’s expecting to be run from the application directory, so the cron task needs to change the directory first. I’ve found that "cd /app_dir && rake db:migrate" works well, but you should test your cron task is working. I’m always getting cron tasks wrong.
Credits
This code was paid for by Marston Alfred, over at Sugarstats and is used in production for that site. Sugarstats is a website to help diabetics manage their sugar levels, activities, and medication, and is well worth a look (even if I do say so myself).
