Our Rumble App: What Does this Error Mean? 3

Posted by james
on Sunday, October 19

Francois, Daniel, and I (and Mat, in spirit) spent the weekend rumbling. It was a great time coding with these two superstars, but you don't care about that.

Our app is called what does this error mean?. We all see error messages, and until now, the best way to look for solutions to those error messages was doing a google search. The problem with google searches, though, is that the results are ordered by the quality of the site, not by the quality of the solution. What does this error mean solves that problem, and a few more.

But, really, reading sucks. So, watch our screencast to learn all about wdtem.

Rails & Merb Integration

As part of our rumble project, we built plugins for both rails and merb that override their default development mode error messages. With our plugin installed, you'll see our logo below the error message. Simply click on the logo to automatically jump to a what does this error mean search!

For rails:

$ script/plugin install git://github.com/giraffesoft/what_does_this_error_mean-rails.git

For merb:

sudo gem install what_does_this_error_mean-merb

Please Vote for Us!

If you think our app is cool, please consider voting for us once rails rumble voting starts! We don't know the url yet, but have been told to point people to our team page.

Watch François Beausoleil Train for Rails Rumble 2

Posted by james
on Wednesday, October 15

My Rails Rumble team is all set to win, because we trained the hardest. Check out the video François made of some of his hard work preparing for the competition.


RailsRumble 2008 Training from François Beausoleil on Vimeo.

Blank: A Starter App for r_c and shoulda Users 17

Posted by james
on Thursday, October 09

Bort was released recently. Peter Cooper speculated that "...it could well catch on as the de facto bare bones / generic Rails application". But, what about us non-RSpec users? There are dozens of us, I tell you. Dozens!

We build a lot of apps at GiraffeSoft — we love to experiment with whatever ideas excite us on any given day. We're all sick of editing restful_auth code, and moving tests over to Shoulda and the one assertion per test pattern. Bort doesn't suit our needs. So, blank was born.

Right now, it's pretty simple. It has authentication, and forgot password. That's about it. But, it's no biggie. Since blank creates your new app as a git repo that shares history with blank's repo, you can pull in changes we make at any time. So, when we finally get around to implementing openid support, you'll get it for free, if you start with blank.

Vendored

All of our standard tools (and rails) are vendored:

  • active_presenter
  • andand
  • attribute_fu
  • hoptoad
  • mocha
  • rake
  • restful_authentication
  • ruby-openid
  • will_paginate

Installation

Installing blank is as easy as running a rake task. Except that blank uses thor instead, because it’s the new hotness, and it supports remote tasks.

Just install thor:

$ sudo gem install thor

…then install blank’s thor tasks:

$ thor install http://github.com/giraffesoft/blank/tree/master%2Fthorfiles%2Fblank.thor?raw=true

…then you’re ready to create a new app with blank:

$ thor blank:new_app the_name_of_my_app the@git.repo.it.will.live.in

That’s it! The thorfile will display a couple of rake notes where you should replace blank app with your app's name. Also, you'll want to fill in your hoptoad API key in config/initializers/hoptoad.rb.

If we improve the thor file, all you have to do is run:

$ thor update blank

before creating your next app, and you’ll get the changes automagically.

Development

All development will be done at the github repo. Fork away :)

Credits

Blank was created by me, with contributions from Daniel Haran.

Introducing ActivePresenter: The presenter library you already know. 22

Posted by james
on Sunday, July 27

Presenters were a hot topic in the rails community last year. A lot of prominent bloggers wrote about using them, and the implementations they had come up with. Oddly, though, when I needed one a couple of weeks ago, I was unable to find a suitable implementation. Lots of articles — no code.

Let's answer the question on everybody's mind before we move on. Feel free to skip ahead if you already know the answer.

WTF is a presenter?!

In its simplest form, a presenter is an object that wraps up several other objects to display, and manipulate them on the front end. For example, if you have a form that needs to manipulate several models, you'd probably want to wrap them in a presenter.

Indeed, attribute_fu solves this problem for some cases. However, when you're dealing with unrelated models, or, really, any situation other than a parent saving its children, you're probably better off using a presenter.

Presenters take the multi model saving code out of your controller, and put it in to a nice object. Because presenter logic is encapsulated, it's reusable, and easy to test.

Want one?

ActivePresenter

Daniel and I wrote most of this on the train ride over to RubyFringe. It is an ultra-simple presenter base class that is designed to wrap ActiveRecord models (and, potentially, others that act like them).

ActivePresenter works just like an ActiveRecord model, except that it works with multiple models at the same time. Let me show you.

Imagine we've got a signup form that needs to create a new User, and a new Account. We'd create a presenter that looks like this.

class SignupPresenter < ActivePresenter::Base
  presents :user, :account
end

Then, we'd write a new action like this one:

def new
  @signup_presenter = SignupPresenter.new
end

And a form:

<%= error_messages_for :signup_presenter %>

<%- form_for @signup_presenter, :url => signup_url do |f| -%>
  <%= f.label :account_subdomain, "Subdomain" %>
  <%= f.text_field :account_subdomain %>
  <%= f.label :user_login, "Login" %>
  <%= f.text_field :user_login %>
<%- end -%>

A create action:

def create
  @signup_presenter = SignupPresenter.new(params[:signup_presenter])
  
  if @signup_presenter.save
    redirect_to dashboard_url
  else
    render :action => "new"
  end
end

Lastly, an update action:

def update
  @signup_presenter = SignupPresenter.new(:user => current_user, :account => current_account)
  
  if @signup_presenter.update_attributes(params[:signup_presenter])
    redirect_to dashboard_url
  else
    render :action => "edit"
  end
end

Seem familiar?

If you're using r_c, most of this comes for free with:

class SignupsController < ResourceController::Base
  model_name :signup_presenter
end

For more on complex forms in rails, and the presenter pattern, see my upcoming PeepCode screencast!

Organization

I have been sticking my presenters in app/presenters. If you want to do the same, you'll need to add a line like this to your environment.rb:

config.load_paths += %W( #{RAILS_ROOT}/app/presenters )

Get It!

As a gem:

$ sudo gem install active_presenter

As a rails gem dependency:

config.gem 'active_presenter'
Or get the source from github:
$ git clone git://github.com/giraffesoft/active_presenter.git

(or fork it at http://github.com/giraffesoft/active_presenter)

Also, check out the RDoc.

Business Logic Bleeding in to Views and Controllers 7

Posted by james
on Wednesday, July 16

I've been doing a fair bit of training recently — both while bringing Mat up to speed on the latest and greatest best practices, and going and speaking to clients' teams. A few concepts keep coming up, so I'm going to try to blog about them as they do. Here's the first one.

I see this all over client projects, and admittedly, some of my older ones:

<%- if @post.creator == current_user -%>
  <%= link_to "Edit", edit_post_path(@post) %>
<%- end -%>

Seems innocuous — the post can only be be edited by its creator. But, that is a business logic rule, so it belongs in your model (and your tests).

context "Posts" do
  setup do
    @creator      = create_user
    @post         = @creator.posts.create!(hash_for_post)
    @another_user = create_user
  end

  should "be editable by their creator" do
    assert @post.can_be_edited_by?(@user)
  end
  
  should "not be editable by another random user" do
    assert !@post.can_be_edited_by?(@another_user)
  end
end
class Post < ActiveRecord::Base
  def can_be_edited_by?(user)
    user == creator
  end
end

Why? A few reasons. First, because your model classes should represent a complete picture of your data's structure, and business logic rules. Second, a rule like that deserves testing, even if it's as simple as the one in this example. Finally, because later on, you might want admins to be able to edit posts, too.

context "Posts" do
  setup do
    @creator      = create_user
    @post         = @creator.posts.create!(hash_for_post)
    @another_user = create_user
    @admin        = create_user :admin => true
  end

  should "be editable by their creator" do
    assert @post.can_be_edited_by?(@user)
  end
  
  should "be editable by an admin" do
    assert @post.can_be_edited_by?(@admin)
  end
  
  should "not be editable by another random user" do
    assert !@post.can_be_edited_by?(@another_user)
  end
end
class Post < ActiveRecord::Base
  def can_be_edited_by?(user)
    user == creator || user.admin?
  end
end

If we're using the can_be_edited_by? method all over our controllers and views, a change to the access policy shouldn't entail anything other than editing a couple of lines of code in our models and unit tests.

Subtler

Here's another one I see all the time (especially in my code). This one tends to be harder to spot.

class MessagesController
  before_filter :login_required
  
  protected
    def project
      @project ||= current_user.projects.find(params[:project_id])
    end
  
    def messages
      project.messages.find(:all)
    end
end

The idea here is that we're using the association as an implicit access control mechanism. If the user row is associated with the project row, the user has access to that project. I know that there are several r_c users who have used this pattern, since it's so easy to implement with r_c. I've even recommended it. Ouch.

The problem with this pattern is lack of encapsulation. There are a good handful of situation in which you might want the access rule to change such that you'd have to go and change every call to current_user.projects. Which is ugly.

What if you decided to make ProjectMembership a join model, for example? Rather than actually deleting membership rows, you decide you'd like, for record keeping purposes, to have a revoked_at field which denotes a former membership made invalid. You might think — no problem, I can just change the conditions of the association.

class User
  has_many :projects, :through => :project_memberships, :conditions => "project_memberships.revoked_at IS NULL"
end

Aside from ambiguous naming, this remains an incomplete solution. At some point, you're going to decide that admins have access to all projects. You could add that condition to your SQL, too, but that approach is problematic for the same reason we're talking about, here: the definition of an admin may change. So, we need a different strategy.

class Project
  has_many :project_memberships
  has_many :users, :through => :project_memberships
  
  named_scope :with_active_membership_for, lambda { |user| { :include => :project_memberships, :conditions => ['project_memberships.user_id = ? AND project_memberships.revoked_at IS NULL', user.id] } }
  
  def self.for(user)
    user.admin? ? self : self.with_active_membership_for(user)
  end
end

This isn't a perfect solution, since the definition of a revoked membership is living in the SQL; ideally that would be defined in ProjectMembership. But, with a join model, I'm not sure of any other way. So, at least in this instance, we can use the Project.for method in our controllers, and views, and not have to worry about a change in the project access rules causing a need for major refactoring later on.

class MessagesController
  before_filter :login_required
  
  protected
    def project
      @project ||= Project.for(current_user).find(params[:project_id])
    end
  
    def messages
      project.messages.find(:all)
    end
end

But, Not Always

One could look at these examples and decide to engage in reductio ad absurdum. Why not encapsulate the messages collection, they'd ask?

The rule I tend to stick to is that if I know that there's any kind of access control policy being enforced on an object or collection, it gets encapsulated, and tested in the model.

In the fictional examples above, the assumption was that provided access to the project, a user would be allowed to access all of messages within. In that case, I wouldn't have encapsulated, because there would have been no rule to encapsulate.

As soon as there is a policy, though, it belongs in tested methods in your models.

The controller's responsibility in all of this is to control access to a resource in the sense that it actually performs the check, and redirects the user to a login screen, or shows an access denied message if they are not entitled to perform said action on said resource. The controller is not, however, responsible for deciding who it is that gets access; that's the model's job.

Like a bouncer at the door of a nightclub, your controller shouldn't make the rules, it should only enforce them. Luckily for you, cute 17 year old girls won't have the same effect on your controller that they might on a bouncer.

r_c, meet RSpec 2

Posted by james
on Friday, June 20

Jonathan Barket just sent me a patch that gets resource_controller's scaffold_resource generator to spit out RSpec specs! I quickly merged it in to master, and wanted to announce it to the world, since it's something I've been hoping to get added to the plugin for a while.

r_c's scaffold_resource generator now supports vanilla test/unit, Shoulda, and RSpec for tests, and erb, and haml for templating. It will sense which of those plugins or gems you have installed in your app, and generate accordingly — it's absolutely 0 configuration.

So, if you're an RSpec user, grab the latest master from github, and get generating! :)

Introducing has_browser: Parameterized browse interfaces for your AR models. 8

Posted by james
on Monday, May 19

Pretty soon after starting to use has_finder, I needed to write a browse interface. That is, given a set of parameters, return all the models that match. With has_finder, it is possible to compartmentalize those parameters in to small chunks, each with names that make sense in your model's domain. You can then chain them together to get their combined scope. The only problem iswas calling them when you aren't aware, in advance, of which finders need to be called.

Having had this problem a few times, and being sick of solving it over and over again, I decided to extract a general solution from a project I'm working on.

has_browser

It's a simple plugin, with a simple syntax. Using the canonical blog example, let's imagine we want to create a browse interface to posts. We'd want the user to be able to browse by category, author, or tags, but not to be able to access any of the other finders on the Post model for obvious security reasons. To set up has_browser, we'd do something like this:

has_browser :category, :tags, :author

Then, assuming the has_finders are already written, the posts can be browsed as follows:

Post.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james')

In that example, each of the finders requires an argument; has_browser also supports finders that don't. As long as the argumentless finder is present in the browse hash, it will be called:

has_browser :category, :tags, :author, :without_args => [:order_by_date, :order_by_number_of_comments]

Post.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james', :order_by_number_of_comments => 'true')

Browse can also be called from association_proxies. For a multi-blog platform, we could easily restrict browsing of posts to the current blog:

@blog.posts.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james', :order_by_number_of_comments => 'true')

Since has_browser returns the same proxy as has_finder, it is possible to further restrict the results of a browse by chaining finders after the browse call. With our blog, for example, we'd probably want to restrict browsing to published posts.

@blog.posts.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james', :order_by_number_of_comments => 'true').published

Note: It is not possible to chain finders before the browse call.

Finally, like has_finder, has_browser is compatible with will_paginate out of the box.

Get It

Releases of has_browser will be available as a gem, which you can freeze in to your rails app (init.rb included):

$ sudo gem install has_browser

Development, of course, is at github.

Experimental resource_controller Feature: Custom Action Discovery 4

Posted by james
on Wednesday, May 07

A few weeks ago, Nate Wiger emailed me to ask whether I was interested in a patch for r_c. Evidently, it is possible to determine which controller actions need to be created by examining the routes that are pointing to it. Nate had also noticed that most controller actions follow a pattern:

  1. Load or build an object or a collection (i.e. Post.find(1) or Post.build(params[:post])).
  2. Optionally do something with the object(s) (i.e. @post.save).
  3. Optionally set the flash.
  4. Render a response.

Put those pieces together, and it's possible to create a controller abstraction that is aware of what's routed to it, and sets up a set of sensible defaults, even for custom actions. As you might have guessed, Nate and I have done exactly that.

For the 7 default RESTful actions, things haven't changed much. The only real difference is that you can now change step 2 (refer to list above), by passing a block to action.action.

create.action do
  @post.my_custom_save_method
end

The action block's return value determines whether the success or failure callbacks (i.e. success.flash vs failure.wants) are triggered.

For custom actions, you'll now be able to use the same DSL you're used to for customizing default actions. For example, if you had a custom action called update_collection, you might put something like this in your routes:

map.connect '/posts', :controller => 'posts', :action => 'update_collection', :conditions => {:method => :put}

Based on that route, r_c will automatically be aware of your action, and create a basic skeleton for it, following the pattern of the list above. For a collection update method, you might want to customize your action like this:

update_collection do
  before { @posts.each { |p| p.update_attributes params[:posts] } }
  update_collection.action { @posts.save }
end

That's all you'd have to do. r_c would automatically load the objects (including any parent, respecting polymorphism), and render using its internal helpers.

Note: It is also possible to modify loading by setting a block for the build accessor, which corresponds to step 1.

Highly Experimental

This is highly experimental software. I'm not sure how much I love it, since it's a bit on the magical side for my tastes. However, it is kinda neat to have your controller be aware of which actions to create, based on what is routed to it. I have been having a bit of fun playing around with this.

So, please give it a shot, and let us know how you like it on the mailing list.

Get it from github (checkout the automatic_route_discovery branch). If you aren't using git, you can get a tarball here.

Rails Training w/ James Golick & Other Rails Ninjas 0

Posted by james
on Tuesday, April 29

This past December, a friend of mine, Peter, wanted to improve his rails skills. I had been asked by a few people about teaching some rails training sessions, but wanted to give it a beta test first. When I was in Toronto over the holidays, Peter and I gave it a trial run, which went great. Since then, I've been dying to offer ruby / rails training to a wider audience.

How It'll Work

The sessions will be based on the format that I tried out in my beta session with Peter. We'll start by covering some advanced rails fundamentals, with time for questions, and plenty of time to go off on long tangents about whatever you might want to learn (like, how to write a plugin, or how some of the rails internals work). The remainder of the session will be spent coding.

During the coding portion of the sessions, the instructor(s) will pair program with each of the participants in a rotation. Think of it as an opportunity to work extremely closely with a rails expert. We'll be able to apply what we've learned in the earlier portion of the session. We'll work on your real problems; that way, we'll be teaching a custom course that's custom tailored to you, and you'll walk away from the experience with some code, to boot!

Participants will be asked to bring two projects: one in progress (or finished), and one idea.

  • The project in progress serves as a tool for analysis of where you're at, and what kind of coding practices you have in general. We'll work out some possible refactorings for that project, and talk about how you'd make use of advanced rails techniques to improve its readability, maintainability, etc.
  • Next, we'll work on your project idea. The rest of the session will be spent architecting, and actually coding this project. We'll help you pick the right plugin set, model your data, and anything else.

Logistics

  • I'd like to offer the first session some time in early July, most likely over a weekend.
  • The sessions will be held in Montreal, unless there's a compelling reason to offer them somewhere else, like, a group of participants who all live in the same city.
  • Price is still TBD; I've got some potential sponsors (which would offset the price for participants), but nothing confirmed yet. There would certainly be a discount for groups or companies.

Finally, I intend to keep the instructor to participant ratio extremely low to support plenty of one-on-one time. So, if there is sufficient interest, I'll get some of my fellow Montreal rails ninjas in as additional instructors. I've got some awesome people in mind. Who knows — you might even get scouted by one of the local rails shops!

If you're interested, send me an email (top right) for more info.

Introducing Action Messager: Dead simple IM notifications for your app! 6

Posted by james
on Sunday, April 06

Sometimes email is the wrong choice for webapp notifications. Our inboxes are becoming increasingly cluttered, and especially for those of us who carry PDAs, it can be a pain to scroll through twelve facebook notifications just to get to the mail that we actually need to read. What's more, email just isn't that great of a tool for receiving short messages, since you have to open them each individually. At the very least, it's nice to have another choice.

Particularly with shorter notifications, instant messaging can be a great alternative to email. The messages don't clutter up your inbox, or need to be opened individually. Most people already use it on a daily basis. The only problem is implementation.

ActionMessager

ActionMessager is a simple framework for creating IM-based notifiers. It is structured like ActionMailer, so it's got virtually zero learning curve for most rails developers. What's more, because the syntax is the same, it's pretty easy to create a delegate class that acts like your mailer, but sends IMs as well. That means drop-in replacement!

All you have to do start sending IM notifications to your users is subclass ActionMessager::Base. Then, create a method that sets an array of recipients, and returns the message you'd like to send:

class JabberNotifier < ActionMessager::Base
  def friendship_request(friendship_request)
    @recipients = friendship_request.receiver.jabber_contact
    
    "You have received a friendship request from #{friendship_request.sender.name}! Click here to accept or decline: #{friendship_request.url}"
  end
end

Then, wherever you'd like to send the notification:

JabberNotifier.deliver_friendship_request(friendship_request)

That's all there is to it.

Caveats

Currently, only jabber is supported. It is possible to access other IM services over jabber, but I'm not 100% clear how it works, and I don't yet have need for it, so that may or may not come later.

The bigger caveat, though, is speed. Sending a notification takes anywhere from 1-2s (whether you send 1 or 5). It may be possible to improve performance by using a different jabber library, and I'll probably investigate that in the near future. Even with a performance boost, though, you should still take your notifications out of the request cycle, by using something like workling to process them asynchronously.

Get It!

Get action_messager in gem form:

$ sudo gem install action_messager

Or get the source, from github:

$ git clone git://github.com/giraffesoft/action_messager.git

attribute_fu and jQuery shake hands 4

Posted by james
on Wednesday, April 02

This blog has been quiet of late, because I'm working on a couple of exciting, but still top-secret projects. Anyway, I recently moved one of those projects over to jQuery, because of its speed, syntax, and general awesomeness. Tonight, when I went to create a multi-model form with attribute_fu, I was stopped dead in my tracks by its heavy dependency on prototype. A few minutes of hacking later, a_f and jQuery are playing rather nicely together.

Get it from the jquery branch of a_f's git repo. If you aren't using git yet, this might be just the excuse you need to check it out! Or, download a tarball from github. After you install the plugin, you'll have to copy its one javascript dependency (jQuery templates) from the javascripts directory over in to your public/javascripts and require it in your layout.

Plugins I've Known and Loved #3: Ultrasphinx 27

Posted by james
on Monday, March 03

If you've ever implemented searching in your rails app, you probably noticed that it's a major hassle (unless, of course, you already know about Ultrasphinx). Ferret is probably the most commonly used indexing solution, but, it isn't anywhere near production ready. acts_as_solr, another option, is so full of show stopper bugs that it really isn't even worth bothering with. The worst part about both of those solutions is that they usually work great in your development environment. Try to put them in to production, though, and you're in for big problems.

Enter Ultrasphinx, by Evan Weaver. Sphinx is a super high performance search daemon, designed to suck information out of a mysql or pgsql database (though, it isn't limited to that). Normally, one would configure Sphinx with SQL queries that it uses to fetch the data. Apparently, the configuration file can be a major hassle to work with — I wouldn't know. Ultrasphinx provides you with a declarative API that it ultimately uses to generate the Sphinx configuration file for you, making the process quick and painless. A few lines of code, a couple of rake commands, and you're searching.

The Sphinx approach has several advantages over ferret, and acts_as_solr. First of all, Sphinx is extremely stable. They're nearing a 1.0 release, and its stability certainly merits that. You don't even really need to worry about monitoring the search daemon, because it just isn't going to crash. Also, if you're running any rails apps in production, you know that long running ActiveRecord callbacks can lead to your app performing very poorly. The ferret and solr solutions both rely on active record callbacks to inform the indexer of new data. Moreover, if the search daemon goes down for any period of time, or, say, the index becomes corrupted (ferret, I'm looking at you), your entire app is going to be down for the count. You set the Sphinx indexer to run every half hour on a cron job, and since it works with the db directly, its performance and stability characteristics have absolutely zero impact on your app.

Trying it Out

So, let's implement a search for our blog. First, we'll want to edit the paths to the index and logs in the default config file. Note that Ultrasphinx uses two types of config files: one that the programmer edits, and one that it generates; both are necessary on all machines that are accessing the index (seriously, not having the generated config on a slave machine caused me some trouble). Then, we'll want to declare our post model as indexed (note that you must declare the fields as strings, not symbols):

class Post < ActiveRecord::Base
  is_indexed :fields => ['title', 'body']
end

Then, we'll need to ask Ultrasphinx to generate a configuration file. You've got to re-run this rake task any time you make changes to the definition of your models' is_indexed declaration. Make sure to .gitignore (svn:ignore for people still stuck in svn land) the generated file in development. Evan recommends checking the production file in to version control, but I have it set to generate automatically on deploy with a cap recipe. That way, if I make changes to the indexing, I won't forget to re-generate the config file. All you have to do is run:

$ rake ultrasphinx:configure

Then, since it's our first time, we've got to run the indexer:

$ rake ultrasphinx:index

Finally, we'll need to start the search daemon:

$ rake ultrasphinx:daemon:start

Now, we can start searching.

@search = Ultrasphinx::Search.new(:query => params[:query])
@search.run
@search.results

So, it's really easy to build a basic search engine using Ultrasphinx. There are some gotchas, though.

Gotchas & Notes

  • More complex indexing with Ultrasphinx can be slightly more verbose, and SQL-focused than it would be with a solution that relies on AR callbacks to do its indexing.
  • Transforming data with Ruby is impossible; the data you index must be in your database (unless you use a stored procedure, which can actually be written in ruby if you're using pgsql, but I digress).
  • Ultrasphinx preloads your indexed models when it is initialized. So, if your models depend on any monkey patches that live in your app's lib directory, they must be loaded before the Ultrasphinx (in my experience, this has meant pluginizing my monkey patches). Because of the way that exceptions are caught in the preloading routine, you won't see the actual error that is stopping your model from loading. Instead, you'll just get a constant loading error, or a name error, or something. If you see something like that, and your models load fine without Ultrasphinx installed, look for dependency issues.
  • Ultrasphinx attempts to preload your indexed models using a regex that doesn't respect commenting (at the time of writing). If you're struggling with issues mentioned in the last gotcha, you'll probably try commenting out the is_indexed call to see whether that's what's causing the problem. That won't work. You can either delete the is_indexed call entirely when debugging, or pull from my git repo, where I've modified the regex to respect #-style commenting (but not the =begin/=end style).
  • If you see DivideByZeroErrors in production, it's probably because you're missing the generated configuration file on one or more of your app server machines.

Check it Out

You'll need to grab Sphinx. Then, the plugin...

Get it from svn:

$ svn export svn://rubyforge.org/var/svn/fauna/ultrasphinx/trunk vendor/plugins/ultrasphinx

Or pull from my git repo (for the change described in the gotchas section):

git clone git://github.com/giraffesoft/ultrasphinx.git

To Sum Up

Ultrasphinx is by far the most effective rails searching solution I've come across. Unlike most of the other options, the search daemon is incredibly stable, and the index never seems to become corrupted (I'm running it in a relatively high load production environment with absolutely zero trouble so far). Also, since Ultrasphinx doesn't rely on AR callbacks for indexing, your application isn't quite as coupled to your search daemon; if it dies, search functionality will break, but the rest of your app will still function. It's not without problems, and complex indexing can be trickier, but Ultrasphinx's stability and performance make the choice a no-brainer.

Plugins I've Known and Loved #2: has_finder 10

Posted by james
on Monday, February 25

Plugins I've Known and Loved started out as a would-be series of presentations at Montreal on Rails. I've been bad about getting to the second presentation, so I thought I'd continue PLIKaL here. No, I'm not going to talk about any of my plugins. Instead, over the next week or two, I'd like to tell you about some wonderful plugins that I only recently discovered (though, none of them are particularly new). Today — a plugin that belongs in everybody's toolbox: has_finder.

The basic idea of this school of plugins is creating reusable scopes for your models. For instance, if you had a blogging application with a post model, you might want to query for published posts. To that end, you might write something like this in your controller:

Post.find(:all, :conditions => {:published => true})

Hopefully, though, you're familiar with the skinny controller, fat model best practice, so you'd write it like this:

class Post < ActiveRecord::Base
  def self.published
    find(:all, :conditions => {:published => true})
  end
end

That's a lot better, but far from perfect. If you were to write several such methods, for example, you would not be able to combine them easily. So, looking for all published posts written by James would require writing a second method; not very DRY. Of course, ideally, you'd use an association proxy to accomplish that goal anyhow. That would work with our hand-written finder, but we'd lose all the benefits of the association_proxy like nested finds (...published.find(:all, :order => 'created_at DESC')), and we certainly wouldn't be able to chain two of them together (...published.order_by_recent). has_finder solves those problems, and more.

I Can Has Finder?

In order to reproduce the finder we wrote earlier, you'd write the following:

has_finder :published, :conditions => {:published => true}

Should you want to find all published blog posts by James, you could then make use of association proxy:

User.find_by_name('James').published.find(:all, :order => 'created_at DESC')

I have come across a good number of associations used for similar purposes, but defined on the associated model. For instance (omitting irrelevant details):

has_many :published_posts, :conditions => {:published => true}

Having discovered the wonders of has_finder, I now consider this to be an anti-pattern. The has_finder method is definitely DRYer. Far more importantly, however, the definition of what makes a post published (in our case, :published => true) belongs in the post model. The same rule applies to ordering. Following this best practice ensures there's only one point of change for refactoring, and that your models and their tests tell a more complete story about the data they represent.

Finally, has_finder supports parameterization of finders. You can wrap your conditions hash in a lambda that accepts an arbitrary number of arguments. Continuing with our blog post example, you might wish to query your posts table for all posts this week, this month, or this year, depending on the situation. To keep DRY, you could define your finder as follows:

has_finder :since, lambda { |date| {:conditions => ['created_at > ?', date]} }

Using it like this:

Post.since(1.week.ago)

To put it all together, let's query for all James's published posts in the last week:

User.find_by_name('James').posts.published.since(1.week.ago)

It reads just like a sentence!

Oh yeah, and all of this is compatible out of the box with will_paginate. I heard a rumor that it's going to be sucked down in to rails, too. Really, how could you not check it out (get it here)?

resource_controller 0.2: maintenance release - no more edge_compatible branch 2

Posted by james
on Friday, February 15

This is mainly a maintenance release.

  • The helpers have been broken out in to four separate files internally, to help with managing the deep nesting branch.
  • There have also been a few little refactorings in preparation for some new features to come shortly.
  • The biggest thing to note for users is that there is no longer an edge_compatible branch. Since rails 1.2.6 generates the same style of named routes as 2.0.2 (edit_tag_photo_path instead of tag_edit_photo_path), there is no longer a need to continue two separate streams of development (yay)!.
  • The generator has been updated to spit out the right filenames for templates (rhtml/haml vs html.erb/html.haml), and old-style migrations (t.column instead of t.string) for any users still stuck on 1.2.6, so the transition shouldn't be a problem.

Get It!

You can get the new version by exporting from svn:

svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller

Or, if you're using piston, you may need to switch to the new url if you were previously on edge compatible (this is untested, so it may be slightly wrong).

  piston switch http://svn.jamesgolick.com/resource_controller/tags/stable
  piston update

As always, everybody is encouraged to come join the discussion on the increasingly lively mailing list.

A Response to Dreamhost 1

Posted by james
on Saturday, January 12

Dreamhost sure stirred things up with their article rant about rails deployment and performance issues. Their complaint is roughly that they are having to work too hard to support rails; it needs performance and deployment improvements. Dreamhost voiced all of these complaints on the company's blog.

As they put it...

What I do have personal knowledge of is how difficult it can be to get a Rails application up and running and to keep it running. DreamHost has over 10 years of experience running applications in most of the most popular web programming frameworks and Rails has and continues to be one of the most frustrating.
...the solution from the Rails community itself was quite honestly, stupid.
That suggestion shows either a complete lack of understanding of how web hosting works, or an utter disregard for the real world.
Ruby on Rails needs to be a helluva lot faster.
Ruby on Rails needs to more or less work in ANY environment.

Forgive me for chopping up their arguments. If you have the time, please read the article. These quotes don't quite do it justice; they serve merely to provide the tone of the piece.

Frankly, I think DHH responded well: rails core team is there to serve their own purposes. Ruby on Rails doesn't need to do anything, despite what a Dreamhost blogger might suggest. The people hacking on rails core don't target platforms like Dreamhost's. So, in fact, rails is not designed to run on shared hosting.

It's not just shared hosting, either. Dreamhost is trying to support rails on massively oversold servers. Configuring rails in a shared environment (particularly under apache) is difficult; configuring, and maintaining their servers to support rails probably costs Dreamhost a lot of money. Rails doesn't perform well in that environment, which probably costs them even more money in support calls from frustrated customers. So, their profit margins on rails service are likely narrowing.

Why is that the rails community's problem to solve? Dreamhost has a business challenge. Rails deployment isn't perfect — it has more than its fair share of problems; but, there are plenty of rails apps running just fine in production. This blog, a rails app, runs on a 20$ per month plan from slicehost. The identical app barely ran at Dreamhost. It's anecdotal evidence, sure, but the point is that rails deployment (at least on this scale) is far from impossible. Dreamhost just can't seem to squeeze it in to their oversold offerings. And, they want somebody else to fix the problem for them.

There are plenty of shared hosting offerings available that support rails nicely. Those companies don't seem to be having any trouble creating an environment that works. None of them have published angry articles pointing the finger at "the rails community". But, I digress.

If it were an individual complaining — a noobie who was having a tough time deploying his rails app on a server he could afford — then perhaps there would be a reason to look at this differently. But, Dreamhost is a corporation; they are simply "...looking to capitalize on a framework that's driving a lot of demand...", to borrow some words from DHH.

If this had been a PC manufacturer, complaining loudly (and rudely) that linux doesn't run on cheap enough hardware for them to sell PCs for 50$/each... If Linksys had posted a rant on their company blog, complaining that linux needed to be faster, because their routers weren't performing well enough... If IBM had posted a rant on their company blog, complaining that the linux community showed "...either a complete lack of understanding of how web hosting works, or an utter disregard for the real world."... Everybody would have said the same thing: so, fix it.

Plenty of businesses are capitalizing on open source technologies. But, part of the deal in the real world is that when something doesn't work right, you might have to fix it yourself; but you can, and that's part of what's so great about open source. Engine Yard just hired the entire rubinius team (and some people to hack merb, so I hear). Linux kernel developments are facilitated in large part by corporate sponsorship, bounties, etc. The system works, because companies can solve their own problems, while leveraging the work of countless others. That's how it works in the real world.


Clicky Web Analytics