captured sparks

Archive for February, 2008

Creating monthly archives in Rails

Whilst creating my own blog software has made me realise how little I needed compared to the vast functionality of readily-available software, one of the things I did want was a monthly archive view.

The first thing I did was create a method in the Entry model that would assign each entry an “archive link” consisting of the month and year in which the entry was created:

class Entry < ActiveRecord::Base

after_create :create_archive_link
...
def create_archive_link
  month = self.created_at.strftime("%B %Y")
  month = month.downcase.sub(/[ ]/, "")
  self.month_year = month
  self.save
end

I have to confess at this point that I broke from the RESTful conventions that I try to employ and created a monthly_index method in the entries controller:

def monthly_index
  @entries = Entry.find_all_by_month_year(params[:month])
  @entry = @entries[1]
  @all_entries = Entry.find(:all)
end

For the blog index, I then list the monthly archives using the following code:

<ul>
  <% @all_entries.group_by(&:month_year).each do |month, entries| %>
    <li><%= link_to "#{month} (#{entries.size})", monthly_archive_url(month.downcase.sub(/[ ]/, "")) %></li>
  <% end %>
</ul>

This code does several things in a short space of time:

1) Groups each entry by a defined month_year method resulting in a hash keyed on the month_year method

2) Prepares to iterate through the hash based on the month_year key

3) Lists the month name and the number of posts in that month with a link to a monthly_archive_url route that is defined in routes.rb

In routes.rb, the following route is defined:

ActionController::Routing::Routes.draw do |map|
...
  map.monthly_archive 'entries/archive/:month', :controller => 'entries', :action => 'monthly_index'
...
end

I think the code could be cleaned up a little bit by defining more methods in the model. Just writing this has made me think that I should define a method to convert the archive link of february2008 back into February 2008.

What I have found is that writing a lot of the code needed to run an effective blogging platform is not particularly complicated when the function needed is broken down into a series of steps.

About the sidebar

I’ve add a sidebar to the individual blog entry pages so that I can let visitors know that we do some web design.

Just to confirm that I haven’t left off my ruby output tags. The idea of using variable names was inspired by Wil Wheaton and the auto-responder he used to have.

Dynamically generating a sitemap for Google

One of the issues I encountered with my previous Rails application was adding a sitemap to the root directory so that I could use Google’s Webmaster Tools. As I was not under a deadline for sparky, I was able to dedicate some time to the issue.

I figured the easiest way to create the sitemap was to use Rails’ built-in XML builder to dynamically generate the sitemap when requested by Google. This has the advantage of picking up every new blog entry.

First was the controller. In order to keep everything segregated and RESTful, I created a sitemaps controller with one action:

class SitemapController < ApplicationController

  def sitemap
    @entries = Entry.find(:all)
    @jobs = Job.find(:all)

    respond_to do |format|
      format.xml { render :layout => false }
    end
  end

end

I retrieve all the dynamic information in the controller, which is then passed to the view file. Creating the view was a lot of trial and error, particularly in terms of getting it to match the sitemap specification. Here though is the final view file split into chunks for commentary purposes.

base_url = "http://www.capturedsparks.com"
xml.instruct! :x ml, :version=>"1.0"
xml.tag! 'urlset', "xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9" do

This sets the header information in the resulting xml file as required by the sitemap specification.

for entry in @entries do
  xml.tag! 'url' do
    xml.tag! 'loc', "#{base_url}#{generate_url(entry)}"
    xml.tag! 'lastmod', entry.updated_at.strftime("%Y-%m-%d")
    xml.tag! 'changefreq', 'monthly'
    xml.tag! 'priority', '0.8'
  end
end

This iterates through the blog entries, which are contained in the @entries variable set in the controller. This is then followed with identical code for the @jobs variable.

For each of the static pages (including the blog and job indexes), I use the following code assigning them higher priorities than the individual blog and job entries.

xml.tag! 'url' do
    xml.tag! 'loc', "http://www.capturedsparks.com/about/"
    xml.tag! 'changefreq', 'monthly'
    xml.tag! 'priority', '1'
end

The resulting xml file can be seen here.

The need to know Ruby

As part of the development of my app, I had a need to display various different categories of to-do items – complete, overdue and “normal”.

I was using a method in the Todo class similar to the following to create these categories:

def self.find_overdue
  overdue = Array.new
  items = find(:all)
  for item in items
    if item.overdue?
      overdue << item
    else
    end
  end
  overdue
end

I was reading through the online Ruby documentation on another issue and came across a nice method in the Hash class: reject. This acts as a “delete if” method and can substantially reduce the code above:

def self.find_overdue
  items = find(:all)
  items.reject { |i| i.not_overdue? }
end

The “not_overdue” method is defined separately in the Todo class. I think this definitely shows the importance of knowing the Ruby language and equally demonstrates its power.

The cool stuff has to wait

As I sit here, all sorts of ideas are popping into my head that would be awesome to implement for my upcoming app. Unfortunately, they need to go on the back-burner.

I started work on the app yesterday afternoon and am still in the process of creating my models and controllers to give a basic functionality. Whilst I do enjoy working in Rails, this is the boring bit. I can’t wait until I can really get into creating my own methods and putting some of these ideas into practice.

Everyone should have a web app

I’ve come up with a little idea for a web app.

It’s going to be for a niche market where the current solutions to the issue are enterprise level with a huge server licence and slightly-less-huge user licences. There is a lot of competition for the traditional solution but none for a simple, hosted solution.

The market I am targeting is historically very traditional and this may present a small barrier to adoption but this is for the sector in which I currently work and I know that I would want something like this.

It’s not going to come as a shock to say that I admire 37 Signals (who doesn’t?). Part of their ethos though is to build what you know and what you use. That way, there is less chance of featuritis. And I know exactly what this app should do.

Custom spam prevention

Inspired by Jonathan, I spent some time last night working on spam prevention for the comments here at captured sparks. I had seen a good article on integrating Akismet with Rails but as this is a home-baked CMS, and having read Jonathan’s approach to the issue decided to have a go myself.

I adopted a similar approach to Jonathan of assigning a score to a comment and then either automatically posting the comment or holding it back for approval . The validation is handled by a before_create callback in the comment model. Here’s an extract of the validation method:

body = self.body
...
score -= body.scan(/viagra/).size
...
case score
when 1..999
 write_attribute(:approved, true)
...

At the moment, the validation is very basic and leans towards a cautious approach to automatic posting. Having said that, all comments are saved to the database for review. I’m not content enough to start automatically rejecting the comments yet.

Thank to Jonathan for the inspiration.

UK to ban filesharers from the net?

A report in the Times this morning is getting a lot of coverage. It states that the UK government intends to legislate to require ISPs to take action against individuals using their networks for file-sharing activities.

Quite how the government expects ISPs to monitor every bit of traffic that passes through their network is anyone’s guess. It is again indicative of the music industry’s insistence on spending their money on lobbying for legislation to protect a failing business model, rather than adapting to the changing face of music.

Becky Hogge of the Open Rights Group (an organisation for which I volunteer) is appearing opposite the BPI on Radio 4’s World at One to discuss the story. ORG will no doubt be responding to any future consultation.

For further reading on the story, I recommend Techcrunch UK, Techcrunch, the always excellent Techdirt and BBC News’ Darren Waters.

UPDATE: ORG have a post about this story.

Using self in Ruby

One of the things that had me confused early on when programming in Ruby was when to use “self” for a method and when not to.

The easiest way to summarise, I think, is to say that self.method should be used when referring to a method that operates on the class itself. method should be used when referring to a method that operates on an instance of the class.

The most common place this is seen in Rails application is in authentication systems. The method for logging a user into an application is usually written as:

Class User < ActiveRecord::Base

  def self.authenticate(user, password)
    ...
  end

end

This is then called in the controller as:

Class SessionsController < ActionController::Base

  def new
    @user = User.authenticate(params[:session])
  end

end

In contrast, methods can then be called on the @user object once the user has logged in. For example, to check if the user is an administrator, the following method could be used in our User class:

def is_admin?
  ...
end

This could then be called within the view, perhaps to display certain restricted information:

<%= if @user.is_admin? %>

As a complete aside, I love how the above code demonstrates the readability of Ruby. There is no doubt as to what that statement means.

Of We and I

Having worked in a multi-national corporate for the last 6 years, it is common practice for me to refer to a company as “we”. You may have noticed that I use “we” when talking about captured sparks on this site. The headline on the homepage is “We can make your website sparkle”.

We, in the case of captured sparks, is me. It is I. One person.

Should it ever get to the stage where other people come on board, I’ll let you know. Until then, it’s just me. Even when I say “we”.

Next Page »