Posts

  • SCCS 2006 - Round 3 - Plean Country Park

    The Plean race organised by Stirling Bike Club was always going to be one of the best of the series. Set on a weekday evening instead of the weekend, it was going to involve lights!

    The course was a new one for Plean, and really enjoyable. A few tight spots, but mostly flat out on long stretches around the bings. Lap times were around six minutes, which was pretty fast considering the course was just under two miles in length.

    The start was fast, and racing en-mass with so many other racers made it more exciting than usual. The dark, lit up only by our lights, and the high speeds, made it pretty exhilarating!

    By the 4th lap is was pretty well up the field, sitting on Iain Nimmo’s (SBC/Wheels) wheel. Going into the 5th lap my contact lens fell out, so that was me finished for the night.

    James Fraser-Moodie (Pedal Power RT) won. I’ll link to results when they’re available.

    I was a bit disappointed as it was one of the best races I’ve done this year, and I was feeling on particularly good form. Always next time!

    Update

    Results are now available.

  • SCCS 2006 - Round 2 - Mugdock Park

    It was a bit colder for the second round of the series at Mugdock Park. The course as always was cracking, still one of my favorites. After negotiating the chicane, then the off camber grassy bank, it’s up a wall then up a steep bank and back down. The bank was ridable for most of the race, though running made it easier to pass other riders. Then it was onto a long fast section, before a series of switchbacks on a climb. Round the ponds before some slidy technical trails. It was then back into the arena.

    Unlike at Townhill, I felt I got a good start, avoiding the mass pileup on the second corner. I settled down fairly quickly, and went round with Keiran Hay and Dave Lines for he first half of the race. Later I got a bit of pace on and started to catch people. I had one funny crash right after I’d passed Ruth Magavigan, I’m still trying to remove all the thorns from my arms!

    Provisional results can be found on Balamory’s website, and some photos can be found on my Flickr. I’ll upload more when I get them off the other card.

  • Good Week - Bad Week

    It’s been a good week. I’ve finally taken on some work after a month or so spent learning new stuff, plus I feel motivated about doing it.

    Today it became a bad week. My Macbook wouldn’t start up this morning, the only life from the hard drive being some nasty ticking sounds. Oh dear I thought. A quick tinker with Disk Utility and System Profiler led me to the conclusion that the drive was in fact dead!

    I dropped it off at my local Apple service centre and waved OS X goodbye for 1-2 weeks. Just as well I do regular backups and keep my working code in version control, I had only lost about 2 hours work at the most through the failure.

    So what am I going to use in the meantime? I borrowed a laptop, and quickly set about replacing it’s Windows install with linux. I decided to go to my favourite distribution of late for my server, Ubuntu. A stroke of luck here, the new version came out today; 6.10 “Edgy Eft”. So now the AMD Athlon powered HP laptop in front of me is singing along updating itself with all the latest patches.

    No doubt I’ll have more to say about my experiences in later posts, but for now I am left with the dilema; What IDE or editor for Ruby on linux?

  • The Secret to Winning Ebay Auctions

    For those of you that are lazy and don’t want to read lots because you have to rush of to get some early Ebay bids in.

    Do

    • Wait until the final 5 minutes to bid.
    • Bid the most you want to pay.

    Don’ts

    • Bid hours or days in advance. All you do is give other people the chance to bid over you. This will cause the price to inflate, costing you more. Sellers love this sort of thing, I know I do.
    • Breach your spending limit. If you don’t stick to your maximum, you’ll end up spending more than you wanted.
    • Bid less than your maximum. This is just silly, as you’ll probably have to bid later with your maximum.

    Hopefully this will help out some of the idiots who seem to think they’ll win an auction with a bid 3 days in advance of auction end. Sites like Bidnapper exist for a reason. Rant over.

  • Reducing Comment Spam - Akismet or CAPTCHA?

    Back when I had my blog running on PHP, I had a lot of trouble with spam. At the time I installed Akismet, but it didn’t seem to be doing as good a job as I had hoped. I eventually stopped accepting comments completely.

    Commenting was something that was on the ‘A’ list when it came to creating my new blogging app, it was something that I really needed to get feedback from those who read my blog. Without interaction, a website is pretty much a one-way affair.

    CAPTCHA

    I reintroduced comments in my new app, but this time using CAPTCHA (Those funny skewed words like the one to the left, that you have to type into a text box). It was fairly easy to setup once I had RMagick up and running with the validates_captcha plugin. It worked well, I received zero spam in the week or so I used it.

    There is a massive downside to using CAPTCHA though, it provides a barrier in terms of usability to people wanting to leave comments. Commenting should be as easy as possible, so people feel inclined to do so. Having to copy some text from one place into another is a hassle, and is likely to put a lot of people off. I know it puts me off. Also, anyone running their browser with images switched off will be unable to see the CAPTCHA image, and thus unable to post comments.

    Akismet

    From a user’s perspective, Akismet is a far friendlier proposition. There is nothing for the user to do other than to submit their comment. All the spam analysis is not done on the host server, but by Akismet, who compare the comment against their database of other spam, and tell you whether or not it is spam.

    It’s easy to implement in Wordpress and many other publishing apps. Installing in Rails is also easy, thanks to the turorial by Dieter Komendera and the Ruby Akismet class by David Czarnecki. I used a heavily modified version of the method in Dieter’s tutorial, and added methods to flag mistakes, using the methods provided in the Akismet API.

    It’s very accurate, and I’ve yet to get a false-positive where it incorrectly marks a comment as spam. Even though this hasn’t happened, I still like to keep even the spam comments, but not display them, until I have had time to review them. During moderation, I can correct mistakes made by Akismet, and automatically notify them of their mistake. In this way new spam is always being added to their database.

    This approach does mean a little extra work for the site owner, but surely for quality interaction with their readers they should be doing this anyway?

    Feel free to test Akismet out in my comments, and tell me what you think of various spam detection methods. If you want to generate a positive spam result, then use the name ‘viagra-test-123’, works every time! Have fun.

  • Cyclo-Cross Photo Essay - Taking on Water

    Thanks to my dad and Babs for doing my bottle at the race yesterday. Emma had some amusing pics of my him standing with his arm outstretched in a rather dynamic pose, but reaching for nothing. I created this mini-photo essay to explain it. Click for the full size version.

  • SCCS 2006 - Round 1 - Townhill Park, Dunfermline

    Steven Couper and his crew provided a well organised event for the first round of the Scottish Cyclo-Cross Series, held at Townhill Country Park, Dunfermline. The course itself with was fast and flowy, with plenty of short technical climbs and swoopy descents. Not being too muddy, lap times were pretty fast around the 5 minute mark.

    I got off to a fairly bad start, as I couldn’t get my foot into the pedal, so ended up down in around 15th place. Some close-overtaking on the first lap got me up to about 11th. On the third lap I caught two others including Al Dow, and preceded to hunt down Keiran Hay and Dave Lines. When I caught them they both seemed to have sat up, so I quickly passed on one of the rooty climbs. At this point Kieran’s saddle slipped, leaving the nose pointing upwards, resulting in Dave letting out a loud laugh.

    On the closing laps I caught Gary McRae, but was hesitant to pass him and lost out in the sprint for 5th place.

    Up front it seemed to be a three-way battle between Iain Nimmo, James Fraser-Moodie and Andy Wardman. James Melville had lead for the early laps, but fell off the pace mid way through. A final sprint resulted in Iain coming around the inside of Moodie on the final bend to take second, with Andy taking the win.

    I’ll update these results when I see a proper set and can remember/spell everyones name and team.

    Results

    1. Andy Wardman (Glentress / Hub in the Forest)
    2. Iain Nimmo (Stirling BC / Wheels)
    3. James Fraser Moodie (Pedal Power RT)
    4. James Melville (Glasgow United CC)
    5. Gary McRae (Pedal Power RT)
    6. Douglas Shearer (Stirling BC / Wheels)
    7. Dave Lines (Pedal Power RT)
    8. Kieran Hay (Velo Eccosse)

    For dates of events in the weeks to come, have a look at the Stirling Bike Club Forum

  • Modify Acts_As_Taggable to Return Tags By Model

    I’m sure many people are using the brilliant Rails plugin Acts As Taggable, and probably just as many using Tom Fake’s tag cloud method to generate nice tag clouds on their sites.

    Prerequisites

    I made a bit of a boo-boo with the original version of this article. The code I have provided is built upon a modification to the acts_as_taggable plugin I made a few weeks ago. In order to create clouds you have to add a new method as documented at the top of the TechKnow Zenze – Acts as Taggable Tag Cloud turorial. It is this method I modify in this article. Thanks to James King for pointing this out to me.

    To Clarify, you need:

    1. The acts_as_taggable plugin.
    2. To modify it as shown here.

    Onto the Good Stuff

    Recently I started to add a second model to my app, which also required tagging, and discovered that there was no built in way to get all the tags for the post model (To display as a blog tag cloud) or the image model (To display as a gallery tag cloud). There is a few hacks on the Rails wiki, but they are just that, fairly ugly hacks. I hope to do better here. I will present two similar methods here, one of which follows rails convention, the other which is slightly more user friendly.

    Following Rails Convention

    Anyone who has used either find or count with more complex queries will be familiar with using the :conditions argument in the method calls. The following modification to the method self.tags, in the file lib/tag.rb which is in the acts_as_taggable folder, allows :conditions to be used to retrieve only a prarticular taggable_type via it’s model name.

    By adding the following line between lines 4 and 5:

    
    query << " AND #{options[:conditions]}" if options[:conditions] != nil
    

    Our method will now look like this:
    
      def self.tags(options = {})
        query = "select tags.id, name, count(*) as count"
        query << " from taggings, tags"
        query << " where tags.id = tag_id"
        query << " AND #{options[:conditions]}" if options[:conditions] != nil
        query << " group by tag_id"
        query << " order by #{options[:order]}" if options[:order] != nil
        query << " limit #{options[:limit]}" if options[:limit] != nil
        tags = Tag.find_by_sql(query)
      end
    

    We can then call this as so:
    
    @tags = Tag.tags(:order => "name", :conditions => "taggings.taggable_type = 'post'")
    

    Despite the long winded method call, our hack does follow convention, and would allow other conditions to be passed if necessary. This hack is my personal choice between the two presented here, purely because it keeps convention.

    A Bit More User Friendly

    The above method is great, but if you want a short and quick way to set the taggable_type without knowing how the tables are related, this method is slightly more user friendly. We are modifying the same file and method as above.

    By adding the following line between lines 4 and 5:

    
    query << " AND taggings.taggable_type = #{options[:taggable_type]}" if options[:taggable_type] != nil
    

    Our method will now look like this:
    
      def self.tags(options = {})
        query = "select tags.id, name, count(*) as count"
        query << " from taggings, tags"
        query << " where tags.id = tag_id"
        query << " AND taggings.taggable_type = #{options[:taggable_type]}" if options[:taggable_type] != nil
        query << " group by tag_id"
        query << " order by #{options[:order]}" if options[:order] != nil
        query << " limit #{options[:limit]}" if options[:limit] != nil
        tags = Tag.find_by_sql(query)
      end
    
    We can call this as so:
    @tags = Tag.tags(:order => "name", :taggable_type => "post")

    Thats it! Our second method would probably benefit from a more friendly argument such as :model rather than the not entirely easy to remember :taggable_type, but I leave this up to you.

    Update – The Easiest Method Possible

    Thanks to Labratz comment, I have now eliminated a small error in my code. He also suggested an auxiliary method to add to acts_as_taggable (Which makes use of my second method above) to allow tags to be found for a specific model using:

    def tags(options = {})
       options.merge!(:taggable_type => self.to_s)
       Tag.tags(options)
    end

    Now we can call this with:

    tags_for_post_model = Post.tags

    Tada! Now I feel happy about the whole thing, much less of a hack. Check out Labratz blog post for the full lowdown.

    Did you like my Ruby on Rails related article? Then why not recommend me on Working with Rails?

  • XML-RPC Ping in Rails

    XML-RPC ping is one of those great things that allows you to tell web-based services about a change to your content, with very little input from you. In this post I show you how to add RPC Ping for Technorati, but this can be modified to allow you to ping a multitude of services using the specifications laid out on their API Pages.

    Ok, here we go..

    In the controller where you have your create and update methods (In this case for adding blog posts), add a private method (Remembering that anything after private will be only be available to this class) called rpc_ping.

    
    private
    

    def rpc_ping
    server = XMLRPC::Client.new2(“http://rpc.technorati.com/rpc/ping”)
    server.call(“weblogUpdates.ping”, “Douglas F Shearer”, “http://douglasfshearer.dyndns.org”)
    end


    Remember to replace my blog name and url with your own.

    Now in each of the methods in which you content changes add a call to our new method.

    
    def update
    	post = Post.new(params[:post])
    	if post.save
    		rpc_ping
    	end
    end
    

    One final thing. It’s nice to give a bit of feedback, so we’ll pass the result of the RPC call back to the interface using flash. We could format this nicely, but as it’s not going to be seen publicly, it’s enough to pass this back as a plain string for the moment.

    
    def update
    	post = Post.new(params[:post])
    	if post.save
    		result = rpc_ping
    		flash[:notice] = 'Ping result: ' + result.to_s
    	end
    end
    

    That’s it. In the future I’ll write-up how to ping multiple services, once I find a nice clean way to do that. My method above was based on olleolleolle’s Bigbold code snippet. Comments as always are welcome.

    Did you like my Ruby on Rails related article? Then why not recommend me on Working with Rails?

  • Custom counter_cache with Conditions

    The counter cache ability of a model relationship is amongst the most useful in Rails’ Active Record repertoire. In it’s standard form it looks like this:

    belongs_to :parent_model, :counter_cache => true

    Together with the matching has_many assertion (Or another rule) in the parent model, along with an appropriately named column in the parent table (In the case of our example that would be comments_count.

    The Problem

    Sometimes it would be great to not count every child object, but only those satisfying specific conditions.. How about having a blog with comments, and marking those comments as spam or not spam. We don’t want to tell our user that there is 3 comments, only for them to see 2, since 1 is spam and not available for public viewing until it has been moderated. In this case we would a custom counter cache, which at a guess could look something like:

    belongs_to :post, :counter_cache => true, :conditions => "self.is_spam = false"

    Unfortunately that doesn’t work, as it would violate Rails’ rule of convention over configuration. However, there is a solution.

    The Solution

    The counter cache works by simply updating a parent model’s count of related objects after each save or destroy operation on the child model. If we delete a comment, we expect the counter cache of the appropriate parent object to be decremented by one.

    We can use exactly the same methodology when it comes to keeping a custom counter cache. First up, create a new column in your parent table using migrations (You are using migrations right?). Name the column something appropriate, I’ll be using public_comments_count in my example.

    Next, we want to add some methods to child model (comment in this case) to update our custom counter cache on each save or destroy. Using after_save and after_destroy methods, we can do this every time and object is manipulated in any way that might affect our counter cache. The code looks as follows:

    def after_save
      self.update_counter_cache
    end
    
    def after_destroy
      self.update_counter_cache
    end
    
    def update_counter_cache
      self.post.public_comments_count = Comment.count( :conditions => ["is_spam = false AND post_id = ?",self.post.id])
      self.post.save
    end

    Thats it! Now everytime a comment is added, removed or modified, my custom counter cache will be updated.

    Hope this helps some people who like me were perplexed by the lack of support for this in Rails.

    Did you like my Ruby on Rails related article? Then why not recommend me on Working with Rails?