I know, big surprise, something stops working in facebox. In this case, it’s datepicker, which is actually a pretty nice little date selection plugin. Unfortunately, it uses HTML element ID attributes in order to function, and that means it all falls apart when facebox clones the HTML. After fiddling with it for a while, I figured out the following:

First, you have to deal with that pesky ID problem. Thankfully, you can use jQuery selectors to find the datepicker fields, and then the plugin will determine the IDs dynamically. Which means you don’t have to know the IDs before hand. So, you just bind a method to the facebox.reveal event to change the IDs, like so:

jQuery(document).bind('reveal.facebox', function() {
  jQuery('#facebox .datepicker').each( function(intIndex){
    jQuery(this).attr('id',jQuery(this).attr('id') + '-2')
  });
});

Of course, it is possible that those field IDs are important to you, In which case, when you need to refer to them you should remember that you appended that ‘-2′ to the end of the ID.

The next problem is that datepicker initiates after the page loads, which means that when facebox clones the HTML to display, the new date field won’t have initialized. This is simply solved by adding some more code to the binding the initialize method to the facebox.reveal event, instead of the page load. Like so:

jQuery(document).bind('reveal.facebox', function() {
  jQuery('#facebox .datepicker').each( function(intIndex){
    jQuery(this).attr('id',jQuery(this).attr('id') + '-2')
  });
  jQuery('#facebox .datepicker').datepick({
    alignment: 'bottomLeft',
    buttonImageOnly: true,
    buttonImage: '/images/calendar_date_select/calendar.gif',
    showOn: 'both',
    showStatus: true
  });
  jQuery('#facebox [name=field_name]').datepick('option', 'onSelect', function(value, date) {
    jQuery('#facebox [name=field_name]').val(value);
  });
});

There you go, now it initializes when the facebox loads. Problem solved.

Or is it?

It just happens that my particular implementation involved more than one facebox, each with it’s own datepicker. Suddenly, all of my well thought out hacks fall apart, and nothing works again.

There’s probably a prettier way to fix this, but I needed the solution to be modular. A given facebox might appear on one page by itself, and on another page with several others. I needed to solve the conflict without breaking the independence, so I ended up wrapping the whole script in an if statement that checks for an element on a given form.

jQuery(document).bind('reveal.facebox', function() {
  if(jQuery('#facebox [name=model[field]]').length>0){
    jQuery('#facebox .datepicker').each( function(intIndex){
      jQuery(this).attr('id',jQuery(this).attr('id') + '-2')
    });
    jQuery('#facebox .datepicker').datepick({
      alignment: 'bottomLeft',
      buttonImageOnly: true,
      buttonImage: '/images/calendar_date_select/calendar.gif',
      showOn: 'both',
      showStatus: true
    });
    jQuery('#facebox [name=field_name]').datepick('option', 'onSelect', function(value, date) {
      jQuery('#facebox [name=field_name]').val(value);
    });
  }
});

Not pretty, I know. Especially, since the above code is replicated everywhere a facebox with a datepicker field appears on the page, sometimes multiple times on one page. It works, though. That’s the important thing, right?

none

A while back, I tweeted about how cool it was to have a software patch I submitted be accepted.

You know what’s cooler than that?

Realizing that someone is actually using a plugin I wrote.

I mean, working with open source, when you put something like that out there, it’s with the idea that people will use it. But having the idea is a lot different from the realization that someone you’ve never met half way around the world found something you did to be useful.

Edited to add: I just realized that it might not be obvious from the link, but I’m talking about the db_free_solr plugin.

one

I know, I know. No one uses Solr anymore. Except me. And the guy that maintains acts_as_solr. And all those people who talk about it on twitter. Okay, so there are still a few people using it. This is for them.

Just a quick note on how you can improve your user’s browsing experience in an app that uses acts_as_solr. Our app has been using acts_as_solr for a year or more now, and we’ve generally been happy with it. Re-indexing a big table can be painful, but we don’t really end up doing that very often.

The one area that was still causing us some pain was the inline indexing of objects. I think the standard solution for this issue has been to defer indexing when creating/updating/deleting and have a cron job scheduled to take care of it at regular intervals. And that both works and is relatively painless. But that solution negates one of the things I like about acts_as_solr, as compared to other search solutions, which is the fact that indexing can be real time.

Anyway, I’m getting a little long winded, all I wanted to do here was point out something that, in retrospect, might seem obvious to others.

Recently, for reasons unrelated to Solr, we implemented workling and starling so that we could background some tasks that were delaying (and in some cases, timing out) our users’ sessions. While I was working on it, I realized that we could use workling to background the inline solr indexing, allowing us to speed up save operations while still basically having real time indexing. Here’s how we did it (all of this assumes that you have workling installed and functioning):

First, we created app/workers/solr_worker.rb:

class SolrWorker < Workling::Base
 
  def index_object(options={})
    object = options[:object_type].constantize.find_by_id(options[:object_id])
    unless object.blank?
      object.solr_save
    end
  end
 
  def destroy_object_index(options={})
    object = options[:object_type].constantize.find_by_id(options[:object_id])
    unless object.blank?
      object.solr_destroy
    end
  end
 
end

Next, in vendor/plugins/acts_as_solr/lib/instance_methods.rb, we added two methods:

def async_solr_save
  SolrWorker.async_index_object(:object_type => self.class.name, :object_id => self.id)
end
 
def async_solr_destroy
  SolrWorker.async_destroy_object_index(:object_type => self.class.name, :object_id => self.id)
end

And, finally, in vendor/plugins/acts_as_solr/lib/acts_methods.rb, we changed two lines from this:

after_save    :solr_save
after_destroy :solr_destroy

To this:

after_save    :async_solr_save
after_destroy :async_solr_destroy

And that’s it. This solution has been in place for a couple of weeks now, and we’ve seen a real improvement.

none

Sooner or later in the life cycle of any application, there comes a time when it makes sense to explore areas where backgrounding processes might improve your Users’ experience. Right now, if you’re looking to do that, the place to turn is Workling.

The obvious candidates for this kind of backgrounding include sending emails and search engine indexing. In our app, though, the first performance killers to present themselves were several calls to third party APIs. Due to the limitations of one of these APIs, and some funky accounting requirements, one process can take as many as 24 separate calls (some lookups, some method calls). Now, the API response times are pretty good, but no matter how quick they may be, it’s obvious that this is going to add some inconvenient overhead to what should be a fast, responsive User session.

In working to background this particular process, I came up against some Workling limitations that may not be typical, or well documented. (Actually, they may be well documented, but since Google just kind of assumes that when you type “workling” you really mean “working”, it’s not always easy to find the pertinent posts.) Anyway, without further ado, some questions/problems I came across, and the answers that worked for me:

1. The standard how-to, startup guide has you calling the worker from the controller, but is it possible to call it from within a model instance?

This one was pretty easy to test, and the answer seems to be yes, you can. I understand it may not be the best practice, but should you find it preferable, calling the worker from within a model does not appear to present a problem. (For my purposes, it ended up making sense to call it from the controller, but I did have it working from a model at first.)

2. Can you pass a model as one of the options’ hash values?

No. Trying this approach, you will very quickly run up against errors like “A copy of ModelName has been removed from the module tree but is still active!”

3. Do ActiveRecord associations work in the worker process?

That would be a no. It’s not a question I thought to ask initially, but I came up against it pretty early in the process. I could be wrong here, but in my experience, it seems like if you want to deal with a given object, you have to load it from within the worker. For me, any model methods that directly interacted with the DB failed (including simple lookups). Which made for some creative, maybe-a-little-hacky coding on my part.

Update: It looks like the reason that ActiveRecord associations were breaking down for me wasn’t a limitation of Workling, but rather an issue with a default setting in the workling plugin. In workling/lib/workling/remote/runners/spawn_runner.rb you’ll find the following line of code:

@@options = { :method => (RAILS_ENV == "test" || RAILS_ENV == "development" ? :thread : :fork) }

Which is probably all well and good, so long as you’re on Rails 2.2, in which Rails is made thread safe. If, however, you’re running Rails 2.1 still, you’re going to want to change that line so it always uses :fork. Changing that solved the association related issues I had been having.

4. What about calling it from within a transaction?

This is potentially problematic, but not necessarily. For instance, if the worker needs to access a record that was created inside the same transaction. When it goes looking for it in the database, it may be out of luck. In retrospect, it made sense, but it didn’t occur to me until I hit the wall.

I think that covers all of the little gotchas I’ve come across so far. We’ve yet to deploy this latest code, or even to add Starling into the mix, so there’s always the possibility of more. If so, I’ll be sure to update this.

none

This one is pretty obvious, but I’m going to say it anyway: Do not, I repeat, do not, rely on model validations to protect your database integrity!

My example for this is a relatively minor one, but it could easily have been much worse:

During the early development of an app I worked on, we installed acts_as_taggable_on_steroids to handle tagging. The tag model, as defined in tag.rb has this line:

validates_uniqueness_of :name

Makes sense, right? Because to have duplicates of any given tag would negate the effectiveness of tagging on a whole. Well, depending on how you had things set up anyway.

Enter problem:

One feature of the app was the ability to do a bulk import of a CSV file, including tags. Any of you who have tried to do bulk imports in Rails will probably know that the first thing you do after writing a working prototype, is to factor out any and all usage of ActiveRecord, because, as beautiful and powerful as ActiveRecord is, it’s dog slow when you’re trying to import and save a few thousand records.

For those of you who haven’t realized where this is going, circumventing ActiveRecord means that all of those neat little model validations never fire. Which means duplicate tag records just kept on piling up, until we finally noticed it and I had to waste time writing a migration to consolidate the tags and their associations.

What’s the solution?

Database constraints!

Are you using validates_uniqueness_of? Add a unique index to the table!**

* No, it’s not my first mistake, but the first in what I’m sure will be many posts featuring stupid mistakes I’ve made that you should learn from.
** This message brought to you by the DBA in me. Rails is fantastic, but I miss me some SQL.

none

The principle of fat models and skinny controllers is one that I first learned when I was initiated into the Rails community about a year and a half ago. Every single one of my fellow developers repeated it, almost like a mantra. The only problem was that no one seemed to be able to articulate the why of it. Don’t get me wrong, I think they knew; I just think that they had lived with it so long that the reasons had become an afterthought.

Being the curious sort, I needed to know the why of it. Not understanding the reasons behind the thing made it difficult for me to adopt it. I would fill the controller and view with logic, telling myself the whole time that once I had it working I could clean it up and move it into the model. I’m sure a lot of you are shaking your heads as you read this, because you know what I’m going to say next. That ever elusive “clean up” session never happened. The obvious result being that my code was functional, but ugly. I was fast up front, but when it came time to make changes, things would slow down quite a bit. Especially if I wasn’t the developer assigned to make the change.

There are a lot of good articles out there about maintainability and the like. Ultimately, though, it’s about a lot more than that. Skinny controllers and fat models, as a principle, is the glue that holds pretty much all of the good coding practices together.

  • TDD: Controllers are a pain in the ass to test. Ideally, all you want to have to test in controllers is that the right template is being rendered, the right action is being taken. The minute you have to start testing assignation of values, etc… That’s the minute you stop wanting to test at all.
  • DRY: It’s a lot easier to spot duplicate code if it’s all in the same place, and with fat models that’s much more likely to be the case. Also, if a function you need is sitting at the bottom of some controller, it starts to get a lot easier to just copy and paste it, rather than trying to share it.
  • Maintainability: The previous 2 points kind of play into this one. If you have good test coverage, and don’t have to worry so much about changes having unforeseen (and untraceable) effects in other parts of your app, you’ll be a lot less hesitant about refactoring your code. If your code is DRY, you won’t have to worry so much that the change you make here, might not show up there.
  • Readability: Let’s face it, the code that’s easiest to read, isn’t DRY. It’s linear, it’s all in one place, it doesn’t make function calls that you have to hunt down (”Is that function in the helper? No, I think it’s in a lib file. No, wait, maybe it’s in this included module…”). The problem is, that this code also isn’t maintainable at all. With the kind of setup I just described, one little text change is likely to require a dozen different files to be altered. So, short of that kind of readability, you want to have everything be easily locatable. Sure, an IDE can help with that, but for those of us using TextMate and vim, we have to resort to keeping things as concise as possible. And DRY does help with that. As does keeping as much of the logic in the model (i.e. in one place) as possible.
  • Design: Skinny controllers and fat models… You know what’s left out of that equation? The views. One of the nice side effects of keeping as much logic as possible in the model is that it also stays out of the view. Once you start filling the controller with unnecessary code, it’s really easy to start doing a bunch of if and case statements in the view. (”Oh, and we need this record, too, but only in this one little spot that may not even be rendered… I’ll just put the Class.find here at the top of the partial.” And so it goes.) The more you can keep code out of your view, the easier it will be for your designer to go in and integrate his or her newly chopped comp.

All of it is made easier by sticking to the skinny controller/fat model ideal. And, conversely, all of it is a lot easier to allow to fall by the wayside if you don’t.

Of course, I realize I’m not really saying anything new here, or maybe I’m just not saying anything that most rails developers don’t already know. But sometimes it helps to say it out loud, even if you’re repeating yourself.

none

So, this was a pain: I spent about 2 hours this weekend trying to find an easy way to execute a SQL select statement while circumventing the ActiveRecord model. After scouring the web and some old code, I found this:

Model.connection.select_rows(sql)

It returns an array of arrays, each inner array representing a record of data. Easy to do, and the results are easy to deal with.

none

Categories

Links

Most commented