Announcing the Badger Rails Plugin

Badger is a simple Rails plugin that creates photo badges. A site often allows its users to upload a profile image. A profile image is just that, an image resized to fit in a predefined space to show up in the user’s profile.

With Badger, you can have something prettier – a badge that shows the user- uploaded image on top of another image that identifies the user as a part of the community. We have company badges, security badges, so why not web badges to have your users show off his/her affection for your site?

Badger works by accepting cropping parameters of the overlay image in a hash (x1, y1, width, height), which is used to crop the overlay image. It then resizes the cropped image to the size specified by composite_width and composite_height in badger.yml. Finally, it places the resized image on top of the background image at location specified by composite_x and composite_y in badger.yml. The resulting image is saved back to either the filesystem or Amazon S3, using attachment_fu.

Badger requires the attachment_fu plugin, ImageMagick, and MiniMagick. Also, the JavaScript Image Cropper UI can be used to obtain the cropping parameters from the users.

Configuration

When this plugin is installed , the badger.yml will be copied to the config directory. You need to specify the following:

  1. background : filename of the background image, searching from public/images
  2. composite_x : top left corner of the overlay image location in x
  3. composite_y : top left corner of the overlay image location in y
  4. composite_width : width of the overlay image in pixels
  5. composite_height : height of the overlay image in pixels


For example, I want to overlay an image on top of a background image (badge.jpg). The box for the overlay image should be 30 pixels in width and 20 pixels in height, and it should appear at (x, y) = (60, 80) of the background image. My badger.yml then looks like:

  development:
    background: badge.jpg
    composite_x: 60
    composite_y: 80
    composite_width: 30
    composite_height: 20

Example

In the model that you use to store attachments:

  class Photo < ActiveRecord::Base
    has_attachment :content_type => :image,
                   :storage => :s3,
                   :max_size => 1.megabytes,
                   :resize_to => '320x200>',
                   :thumbnails => { :thumb => '100x100>' },
                   :processor => :MiniMagick
    validates_as_attachment
    has_badge :storage => :s3
  end

In the controller:

  def create_my_awesome_badge
    @photo = Photo.find(params[:id])
    # params[:crop_coord] is a hash with indexes x1, y1, width, height
    @photo.create_badge(params[:crop_coord])
  end

Improvements Needed

Please feel free to submit patches for bug fixes and improvements. Specifically, I would like to:

1. Use something nicer than system(“convert blah…”), but couldn’t get it to work. I don’t think Minimagick supports compositing images, so RMagick may have to be used, but is it worth the heavy memory consumption?

2. Make it more flexible (i.e. accept background image and composite params dynamically instead of in badger.yml). Maybe pass them in the call to create_badge?

Share:

Comment on this post (2 comments)


Deploying your Rails applications with Phusion Passenger

There have been several methods to deploy an Ruby on Rails application. Until recently, the most popular is to run Apache and proxy balance to multiple Mongrel instances that are running simultaneously.

Passenger, developed by Phusion, is the new kid entering the Rails deployment market. Everyone has been using the Apache PHP module for years and deploying a PHP applications is a snap. This has not been possible with Rails until Passenger. It is extremely easy, and you can still use Capistrano to automate deployment. I will show you how I get it to work on Ubuntu.

sudo gem install passenger
passenger-install-apache2-module

Update: Phusion just released Passenger 2.0 RC 1. You can download this version and do gem install passenger-1.9.0.gem instead. But I had an error compiling it on Mac OS X Leopard. hongli pointed me to use the version from GitHub that has the fix and it works like a charm. Thanks Phusion guys.

To get it from GitHub:

git clone git://github.com/FooBarWidget/passenger.git

I created a separate /etc/apache2/mods-available/passenger.load and it contains the following:

For 1.0.5:

LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/ext/apache2/mod_passenger.so
RailsSpawnServer /usr/local/lib/ruby/gems/1.8/gems/passenger-1.0.5/bin/passenger-spawn-server
RailsRuby /usr/local/bin/ruby

For the GitHub version (Of course the path will look different depending on where your git clone is):

LoadModule passenger_module /home/rlaw/downloads/passenger/ext/apache2/mod_passenger.so
PassengerRoot /home/rlaw/downloads/passenger
PassengerRuby /usr/local/bin/ruby

I then tell Apache to load the Passenger module:

a2enmod passenger

Now, I create a virtual host configuration for one of my Rails app in /etc/apache2/sites-available/myapp:

<VirtualHost *:80>
  ServerAdmin webmaster@myapp.com
  ServerName myapp.com

  DocumentRoot /home/deploy/apps/myapp/current/public

  <Directory /home/deploy/apps/myapp/current/public>
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  </Directory>

  LogLevel warn
  ErrorLog /var/log/apache2/myapp/error.log
  CustomLog /var/log/apache2/myapp/access.log combined
</VirtualHost>

I then restart Apache:

sudo /etc/init.d/apache2 reload

When you need to restart your application because you have changed some code that Rails does not reload in production, just do:

touch /home/deploy/apps/myapp/current/tmp/restart.txt

I have not tried their Ruby Enterprise Edition yet. They claim substantial memory and speed improvement at RailsConf 2008, so it will be interesting to see how that develops.

Share:

Comment on this post (1 comment)


Announcing Fu-fu: The Profanity Filter for Rails

Posted by on June 6th, 2008.

That’s the most foul, cruel, and bad-tempered word you ever set eyes on!
Look, that word’s got a vicious streak a mile wide! It’s a killer!

There will be no killer words in this application: Behold the mighty Fu-fu! And there was much rejoicing… But first, a little history on Fu-fu: The Profanity Filter for Rails.

A Little History

Recently, I needed a simple (profanity/cuss/swear/bad word) filter for a Rails app, so I hit up Google for the answer as this seemed like a problem that should have been solved by an expert. Sadly, this was not the case.

Over the next day or so I was able to get a simple prototype up and running in our application and that’s the way it stayed for the next couple weeks. Then I realized that this was reason I wasn’t able to find a profanity filter plugin on Google.

Upon closer inspection it seems that people are building their own filters and leaving it at that. Almost being guilty of this, I decided that it was time to give back to the community and get a profanity filter plugin out in the wild.

I was able to publish the first version of the Profanity Filter during the Community Code Drive at RailsConf 2008. Hacking in the same room as DHH, David Chelimsky, Chad Fowler, Rich Kilmer, Marcel Molina Jr., and the entire Intridea team is a great motivator.

During RailsConf we decided that the plugin needed a real name; “Profanity Filter” wasn’t cutting it. Someone suggested fu-fu pronounced ‘eff-you-foo’. That was promptly shortened to ‘foo-foo’. How can you not love something named Fu-fu that deals with profanity and abuses plugin idioms at the same time?

Continue Rejoicing (Examples!)

The interface for Fu-fu is clean and straight forward. For example. lets say that ‘frak’ is a common curse word.

class Post < ActiveRecord::Base
  profanity_filter :title, :body
end

post = Post.create(:title => 'Fraking title', :body => 'What a bunch of frak')

post.title          #=> '@#$% title'
post.title_original #=> 'Fraking title'

post.body           #=> 'What a bunch of @#$%'
post.body_original  #=> 'What a bunch of frak'

By default the filter will replace common curses with the standard curse notation of ’@#$%’. Fu-fu is also has the ability to do dictionary replacements:

class Post < ActiveRecord::Base
  profanity_filter :title, :body, :method => 'dictionary'
end

post = Post.create(:title => 'Fraking title', :body => 'What a bunch of frak')

post.title          #=> 'fr*k*ng title'
post.body           #=> 'What a bunch of fr*k'

Fu-fu comes with a default dictionary file that replaces all vowels with asterisks (*).

You can also add an exclamation point to the end of the filter call (profanity_filter!) and have the method save the filtered text to the database (although this is not recommended for most applications).

You can also call Fu-fu directly:

ProfanityFilter::Base.clean('frak')               #=> '@#$%'
ProfanityFilter::Base.clean('frak', 'dictionary') #=> 'fr*k

Todos and Fixes

But alas, there is still danger in Caerbannog. As with all things, there is room for improvement.

* better filter regex, doesn't currently catch things like 'f-r-a-k'
* needs support for multiple dictionaries and configuration
* needs support for different levels of filtering (prude, normal, weak, etc)

Installation

To install the Fu-fu: The Profanity Filter on Edge Rails or Rails 2.1 and greater:

script/plugin install git://github.com/adambair/fu-fu.git

On earlier versions of Rails:

git clone git://github.com/adambair/fu-fu.git vendor/plugins/fu-fu

Resources

Bug tracking is available through the Fu-fu Lighthouse project. Also, feel free to contribute. I’ll be happy to accept patches and push requests for reasonable fixes and additions as long as they come with test coverage.

The source code is available on GitHub.

For general discussion about the plugin, please use the forums and wall of Fu-Fu’s Acts As Community Profile

Thanks to the Intridea team for their time, contributions, and suggestions. I’ve had a great time building Fu-fu and I hope someone may find it useful.

Share:

Comment on this post (7 comments)


Mash - Mocking Hash for total poser objects

Posted by on April 12th, 2008.

There are a number of times when I need something like an OpenStruct with a little more power. Often times this is for API-esque calls that don’t merit a full on ActiveResource. I wrote a small class for use with my ruby-github library and wanted to make it a separate gem because I think it’s pretty useful to have around.

Usage

Basically a Mash is a Hash that acts a little more like a full-fledged object when it comes to the keyed values. Using Ruby’s method punctuation idioms, you can easily create pseudo-objects that store information in a clean, easy way. At a basic level this just means writing and reading arbitrary attributes, like so:

author = Mash.new
author.name # => nil
author.name = "Michael Bleigh" 
author.name # => "Michael Bleigh" 
author.email = "michael@intridea.com" 
author.inspect # => <Mock name="Michael Bleigh" email="michael@intridea.com">

So far that’s pretty much how an OpenStruct behaves. And, like an OpenStruct, you can pass in a hash and it will convert it. Unlike an OpenStruct, however, Mash will recursively descend, converting Hashes into Mashes so you can assign multiple levels from a single source hash. Take this as an example:

hash = { :author => {:name => "Michael Bleigh", :email => "michael@intridea.com"},
       :gems => [{:name => "ruby-github", :id => 1}, {:name => "mash", :id => 2}]}

mash = Mash.new(hash)
mash.author.name # => "Michael Bleigh" 
mash.gems.first.name # => "ruby-github"

This can be really useful if you have just parsed out XML or JSON into a hash and just want to dump it into a richer format. It’s just that easy! You can use the ? operator at the end to check for whether or not an attribute has already been assigned:

mash = Mash.new
mash.name? # => false
mash.name = "Michael Bleigh" 
mash.name? # => true

A final, and a little more difficult to understand, method modifier is a bang (!) at the end of the method. This essentially forces the Mash to initialize that value as a Mash if it isn’t already initialized (it will return the existing value if one does exist). Using this method, you can set ‘deep’ values without the hassle of going through many lines of code. Example:

mash = Mash.new
mash.author!.name = "Michael Bleigh" 
mash.author.info!.url = "http://www.mbleigh.com/" 
mash.inspect # => <Mash author=<Mash name="Michael Bleigh" info=<Mash url="http://www.mbleigh.com/">>>
mash.author.info.url # => "http://www.mbleigh.com/"

One final useful way to use the Mash library is by extending it! Subclassing Mash can give you some nice easy ways to create simple record-like objects:

class Person < Mash
  def full_name
    "#{first_name}#{" " if first_name? && last_name?}#{last_name}" 
  end
end

bob = Person.new(:first_name => "Bob", :last_name => "Bobson")
bob.full_name # => "Michael Bleigh"

For advanced usage that I’m not quite ready to tackle in a blog post, you can override assignment methods (such as name= and this behavior will be picked up even when the Mash is being initialized by cloning a Hash.

Installation

It’s available as a gem on Rubyforge, so your easiest method will be:

sudo gem install mash

If you prefer to clone the GitHub source directly:

git clone git://github.com/mbleigh/mash.git

This is all very simple but also very powerful. I have a number of projects that will be getting some Mashes now that I’ve written the library, and maybe you’ll find a use for it as well.

Share:

Comment on this post (9 comments)


Beboist -- Updates and Attention

Posted by on April 3rd, 2008.

Our friends at Bebo have selected our Beboist plugin to be one of their featured Bebo Social API libraries.

This joyous occasion can only be properly acknowledged by the announcement that Beboist has now been moved to Github, a Git repository host where the cool kids play nowadays. We feel that Github’s convenient fork-edit-push code publishing mechanism will only help Beboist grow even quicker to become a prominent solution for working with the Bebo API.

The old SVN repository will still remain up, but all future development will take place on Github.

So, without further ado—here’s the Beboist Github repository:

http://github.com/skyfallsin/beboist/tree/master

You will install this plugin from your RAILS_ROOT directory as such:

git clone git://github.com/skyfallsin/beboist.git vendor/plugins/beboist

Git has a slight learning curve—here are a few resources to help you get started if you have never used it before: SVN to Git Crash Course, Git Tutorial

Intridea’s Public Trac is still up for bug reports.

Share:

Comment on this post (7 comments)


acts_as_community private beta

Posted by on February 28th, 2008.

Today Intridea launched the private beta of acts_as_community, a community site for Ruby and Rails developers.

acts_as_community is a place for Rubyists and Rails developers to gather and interact. Our hope is to bring the community closer together in collaboration and communication so that everyone can benefit from others' experiences.

If you would like to join email us at aac@intridea.com for the beta key.

Share:

Comment on this post (0 comments)


Beboist - A Rails Plugin for the Bebo Social API

Posted by on January 10th, 2008.

UPDATE

Click here for the latest on Beboist


The Beboist plugin provides a Rails interface to the Bebo Social Networking API.

The plugin was designed from the ground-up to be flexible enough to accommodate any changes to the API, while at the same time providing a clean interface that will be familiar to most Rails developers.

Setup

Ensure that the json gem is installed on your system and the Beboist plugin is installed in your vendor/plugins folder:

gem install json
script/plugin install http://svn.intridea.com/svn/public/beboist

Generate your config/bebo.yml file using

script/generate beboist_settings

Fill in your appropriate app settings in config/bebo.yml. Ensure that your app name is right.

Generate the first migration for your users table using:

script/generate beboist_user_migration

Migrate your database using

rake db:migrate

In your application.rb, insert the following filters:

before_filter :reject_unadded_users
before_filter :find_bebo_user

Write your app, and keep an eye on your logs to catch any possible error messages.

API Reference

The methods listed in the Bebo API Documentation are mapped to Ruby classes in the following manner:

users.get_info(uids => "1,2,3", fields => "first_name, last_name")
  # BECOMES
BeboUsers.get_info :uids => [1,2,3], :fields => ["first_name", "last_name"]

Notes

The Beboist plugin uses Bebo’s JSON API, and the ‘json’ gem to directly convert JSON objects to Ruby. It works with Rails 2.0+, but has not been tested on Rails 1.2. Check the README for more details, and file tickets at Intridea’s Public Trac

Share:

Comment on this post (7 comments)


OpenSocial, Buzz and the Tao of Releasing an API

Posted by on November 3rd, 2007.

Michael Arrington announced OpenSocial on TechCrunch two days before its official release. Prior to that, there were whispers everywhere about Google’s new social platform, but not many seemed to know what exactly was about to go down. Needless to say, this is good buzz. Two days before ‘launch’ the overwhelming mood among web developers, especially us who dwell in the realms of social networking, was one of intense (even feverish at some points) anticipation. What unfolded over the next few days, combined with what we observed of Facebook’s API venture, provides us a set of best practices that we can apply to an API release.


Continue reading this post

Share:

Comment on this post (0 comments)


Improved BetterNestedSet Plugin

Posted by on October 27th, 2007.

On a recent project when I was using the BetterNestedSet plugin to manage a large hierarchal set of data, I encountered a problem that required me to find all of the items in a nested set that had children and those that didn't. In nested set terms I wanted: all 'parent' nodes and all 'leaf' nodes that exist within the 'tree'.

If you want to do this through the current BetterNestedSet interface you might be tempted to do something like this:


Continue reading this post

Share:

Comment on this post (1 comment)


Campfire SVN and email notification

Posted by on October 5th, 2007.

Here's a quick way to add Subversion notification for Campfire using Tinder.

Create svn-campfire.rb with the correct username and password:

Add this to your SVN post-commit hook:

/usr/local/bin/ruby /path/to/svn-campfire.rb "$1" "$2"

Here's a quick way to send emails to campfire. Create an email address to use for sending messages to campfire and then create mailer-campfire.rb with your domain, usernames and passwords:

Then create a cron job to fire this off every minute:

* * * * * /usr/local/bin/ruby /home/path/to/mailer-campfire.rb

We also use it to remind us of daily standups:

Our cron job fires it off every weekday at 2pm.:

0 14 * * 1-5 /usr/local/bin/ruby /home/path/to/status-campfire.rb

Next step, create a Jabber gateway for Campfire using Tinder.

Share:

Comment on this post (1 comment)


Bending Ruby (Part I) - An User-friendly Hash using method_missing

Posted by on August 7th, 2007.

There’s been a lot of talk about method_missing lately. Let’s do a little example that leverages this freaky but neat little method in our quest to bend Rails to our will. The following code is a handy trick that lets you access key values in a Hash as regular class methods.

class Hash
  def method_missing(method, *params)
    method = method.to_sym
    return self[method] if self.keys.collect(&:to_sym).include?(method)
    super
  end
end

If you have a key called :name, then my_hash[:name] and my_hash.name will now give the same result. If you call some key that doesn’t exist, then the super call tells Object to throw a generic NoMethodError. Neat, no?

P.S. the collect(&:to_sym) shortcut used above actually evaluates to collect{|x| x.to_sym} in Rails. This functionality is not available in Ruby, but it can easily be replicated by overriding the Symbol class, like so:

class Symbol
  def to_proc(*args)
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end
end

Bending Ruby will be a series of posts that will build on one another.

Share:

Comment on this post (1 comment)


Customized page.* methods for Rails' RJS Templates

Posted by on August 6th, 2007.

So you want to clear your live search fields the moment someone clicks on a result. Here’s a little bit of code that will let you do so in your RJS template:

However, suppose you want to use this same live search clearing scheme over multiple controllers. You can either do re-use the above code for each respective controller method, or you can write the following in your RJS views, thereby DRY-ing up your code:

How, oh how ever do I implement this for my app? Well, this works because in Ruby, it’s quite easy to add or override methods to any class or module. So let’s hack into ActionView, which is the part of Rails that gives you all those neat-o functions in your views (for example: the much-loved link_to).

The trick to overriding Rails code is to always have a copy of Edge Rails checked out to refer to, so you can get the namespace right. For this example, we have established that we want to extend the page.* methods that are prevalent through RJS templates and “render :update” calls. First we need to find out where the code lives. Let’s run a text search on our edge rails copy for ‘insert_html’, which is a commonly used RJS method. We then find out that it’s at action_pack/lib/action_view/helpers/prototype_helper.rb. Well, this is a start!

Next, we look at the namespace of the code in prototype_helper.rb, and try to find insert_html. We then see that it’s nested like so:

Now, in lib/actionview_hacks.rb, copy this namespace and replace the insert_html method with a method of your choice:

Next, update your environment.rb file by adding this somewhere:

That’s it! You can now call page.clear_my_live_search from any RJS template or render :update method. You can also use update_page_tag to insert the page. methods we just generated into your views as javascript.

Also, this example is quite basic. Some might call it overkill to override Rails helpers to get this running. Another good way of doing the same is to define a new method in one of your helpers that uses update_page, like so:

You can then use this helper all over the place, too.

This article barely touches the surface of what’s possible when extending Rails—most existing Rails plugins probably got their start by practically the same process as what we followed here.

Share:

Comment on this post (2 comments)