Skip to main content

Mobomo webinars-now on demand! | learn more.

Six years after Ajax was widely popularized with the introduction of Gmail, we are still stuck with synchronous file uploading. Certain techniques have done their best to fill this niche by using Flash apps, hidden iframes and the like, but all have felt like hacks at best.

Enter jquery-binaryUpload, a jQuery plugin I wrote to allow asynchronous file uploading in Firefox 3.5+, Chrome 5+ and Safari 4+. It works in Firefox 3.5 through the use of the File object, while in Firefox 4+ and the other browsers it takes advantage of the FormData object. The plugin papers over these different implementations with a clean simple interface.

The included sample code demonstrates typical usage with a Sinatra or Rails app though the plugin itself is completely framework agnostic. Callbacks are descriptive while additional options are outlined in the documentation. Please report any problems you run into or suggestions you have. Enjoy!

The IE Question
So far as I have been able to glean from my research, asynchronous uploading in Internet Explorer, short of an ActiveX control or one of the aforementioned techniques, is not currently feasible. IE does not natively support the FormData or File objects and the IE9 dev team makes no mention of it on their site. My efforts to contact their dev team have been to no avail.

Categories
Author

Hot on the heels of my post on why XMPP will be huge, here’s a ruby library to pre-initialize BOSH sessions in your Ruby web applications. This feature allows you to by-pass exposing your user’s XMPP credentials in your HTML views.

The process follows as such:

There are many XMPP servers and BOSH connection managers out there, but as of now this library has only been tested with eJabberd 1.2+. Please feel free to fork and submit a pull request if you’d like to contribute.

The plugins and documentation can be found at: http://www.github.com/skyfallsin/ruby_bosh

Jack Moffit’s written a Django/Python example here.

Categories
Author

Today came the announcement that all of the major search engines are going to support a ‘canonical’ URL hint that will allow site owners to specify a single URL for content that may be replicated across many URLs (such as for categories etc.).

To make use of this in Rails applications, I’ve written a plugin that allows you to easily specify canonical URLs for your content. To install it as a gem, just add this to your environment.rb:

config.gem 'mbleigh-canonical-url', :source => 'http://gems.github.com', :lib => 'canonical_url'

Using it is extremely simple; just add this to the <head> section of your layout:

<%= canonical_link_tag %>

And in your controllers, any time you want to specify a canonical URL you can do so like this:

class BlogController < ApplicationController   def show     @post = find_post # assume this is a standard blog post type record     canonical_url blog_post_path(post.year, post.month, post.day, post.slug)   end end

Now any time the show action is run, no matter how the routing came to be there, a single canonical URL will be shown in the header. If no canonical URL is specified in the controller (or through the canonical_link_tag helper directly) the helper will not output anything, making it completely harmless to add to any application.

The source for the plugin is available on GitHub. So go forth and canonize your applications!

Categories
Author

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?

Categories
Author

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.

Categories
Author

First the database migration used in all examples (using Rails 2.0 sexy migrations):

class CreateClassifications < ActiveRecord::Migration    def self.up      create_table :classifications do |t|        t.integer :parent_id        t.integer :lft        t.integer :rgt        t.string  :name        t.text    :description        t.timestamps      end    end      def self.down      drop_table :classifications    end  end

Please note that the left and right columns are named in such a way that they will not conflict with database reserved words commonly used in join statements.

Then the code:

class NestedSet < ActiveRecord::Base    acts_as_nested_set      def self.all_parents      self.roots.map do |root|         (root.full_set.collect {|child| child if child.children.size > 0}).compact.flatten      end    end      def self.all_leaves      self.roots.map do |root|         (root.full_set.collect {|child| child if child.children.size == 0}).compact.flatten      end    end    end

This code works - but works slowly and yields horrific SQL. The reason it's slow and horrific is because you need to hit each node in the tree to test for it's number of children. This is less than ideal especially if you need to do this regularly (or on a tree with hundreds of nodes) - which leads us to the next iteration:

class NestedSet < ActiveRecord::Base    acts_as_nested_set      def self.all_parents      self.find(:all, :conditions => "rgt <> lft + 1")    end      def self.all_leaves      self.find(:all, :conditions => "rgt = lft + 1")    end    end

This works much better and is far more efficient. Each call to the class method makes a single query to the database. The reason the we can do this is because of the way nested sets work. A good explaination of this can be found in the BetterNestedSet README:

An easy way to visualize how a nested set works is to think of a parent entity surrounding all  of its children, and its parent surrounding it, etc. So this tree:      root      |_ Child 1        |_ Child 1.1        |_ Child 1.2      |_ Child 2        |_ Child 2.1        |_ Child 2.2    Could be visualized like this:      ___________________________________________________________________     |  Root                                                             |     |    ____________________________    ____________________________   |     |   |  Child 1                  |   |  Child 2                  |   |     |   |   __________   _________  |   |   __________   _________  |   |     |   |  |  C 1.1  |  |  C 1.2 |  |   |  |  C 2.1  |  |  C 2.2 |  |   |     1   2  3_________4  5________6  7   8  9_________10 11_______12 13  14     |   |___________________________|   |___________________________|   |     |___________________________________________________________________|     The numbers represent the left and right boundaries.  The table then might  look like this:       id | parent_id | lft  | rgt  | data      1 |           |    1 |   14 | root      2 |         1 |    2 |    7 | Child 1      3 |         2 |    3 |    4 | Child 1.1      4 |         2 |    5 |    6 | Child 1.2      5 |         1 |    8 |   13 | Child 2      6 |         5 |    9 |   10 | Child 2.1      7 |         5 |   11 |   12 | Child 2.2

You can also check out additional nested set explanation in an article titled "A Nested Set Implementation..." (paying special attention to the illustrations in section #3).

Going back to our example, there's one more thing we can to do make this one step better - move our methods into the plugin implementation. This makes sense as our class methods are not directly related to our application logic and pertain mainly to nested set behavior.

You can add a version of our methods to the nested set plugin by adding to the ClassMethods module in better_nested_set.rb in the lib folder of the BetterNestedSet plugin with the following:

def parents    acts_as_nested_set_options[:class].find(:all, :conditions => "(#{acts_as_nested_set_options[:right_column]} <> #{acts_as_nested_set_options[:left_column]} + 1)", :order => "#{acts_as_nested_set_options[:left_column]}")  end    def leaves    acts_as_nested_set_options[:class].find(:all, :conditions => "(#{acts_as_nested_set_options[:right_column]} = #{acts_as_nested_set_options[:left_column]} + 1)", :order => "#{acts_as_nested_set_options[:left_column]}")  end

Or if you're savvy you can just apply the patch I created that includes these methods along with documentation and tests:

  • download the patch
  • move the patch to the root of your BetterNestedSet plugin
  • patch -p0 < leaves_and_parents_class_methods.patch

I've also added the patch as an enhancement to the BetterNestedSet trac.

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:

Categories
Author

At Intridea, we’ve been busy little beavers working on a couple of hush-hush facebook apps. Needless to say, you will be hearing about those quite soon. But first, a little discussion about Rails plugins for the Facebook F8 API is in order.

The most popular Rails plugin for the Facebook API is RFacebook. The core implementation of RFacebook relies a lot on Hpricot and method_missing (covered earlier in this post) to map the XML results from Facebook to Ruby objects.

There are a few goodies in RFacebook: SSH tunnelling, URL rewriting, and a nice debug panel that displays right on the facebook canvas. RFacebook is becoming more and more mature by the day, but unfortunately maturity sometimes does not equal usability or productivity (by Rails standards, of course). My main issue with RFacebook however, is the convoluted MVC design. For example, facebook session calls are made through a fbsession object. However, this object is accessible only within the controllers, and not the models. Moreover, fbsession objects use method_missing to transfer methods not defined in the ruby code over to Facebook, and so there is no concrete mapping between functions and their variables—it makes things like passing partials instead of strings as parameters for session calls in the controller more difficult, and lo and behold, we’re forced to use the ugliness that can only be mentioned as “html-code-in-the-controller”. Small things surely, but we use Rails and Ruby to “increase productivity and happiness”, and any libraries that have the possibility of being heavily used should be written with such things in mind.

A new contender is Facebooker written by Chad Fowler. He outlines the design points (most of which are vehement arguments against RFacebook’s way of doing things), in a very nice post. A quick look at the code (which is all I have done so far, more to come later) suggests that this is really a very concrete API, that maps functions to non-method_missing functions. However, it does have a bit of a learning curve right now and is well below RFacebook in terms of immediate user-friendliness, which is to be expected since it is still under development and has had no public announcement besides that blog post. More details as we move forward..

Running a quick search on ‘rails facebook plugins’ gives me more results today than it did a few weeks ago, so it seems apparent that there are a few more plugins left that I have missed. Along with that, the new release of Facebook’s Data Store will most definitely bring forth atleast a few more new ones.

Categories
Author
1
Subscribe to Plugin