Looking for my family blog?

If you are looking for JeremyandDarcy.com, please click here: http://jeremyanddarcy.com.

May 07 11:52

Alpha Pagination in Rails

One the first cool tricks probably everyone learns when learning Ruby are the range tricks.

(0..5).to_a #=> [0, 1, 2, 3, 4, 5]
(0...5).to_a #=> [0, 1, 2, 3, 4]

So I was looking to "alpha paginate" my user list in a Rails app. It was already paginated (by another team member of mine) with the pagination helper, but that's useless if I wanted to find a user with the last name beginning with L.

I've seen an alpha paginator out there, but I just couldn't help thinking that it was excruciatingly simple to implement my own.

I took a look at the Ruby, Range class, I figured if the Ruby creators were slick enough to give us cool things like (0..5), why not something like ('A'..'Z')? Well, sure enough, it's in there.

http://www.ruby-doc.org/core/classes/Range.html

Have I mentioned how much I love Ruby?

The first thing I did was write a helper method in application_helper.rb so that I could use this anywhere in my Rails app:

def alpha_links(action)
 return_text = '[ '
  ('A'..'Z').to_a.each do |letter|
   return_text = return_text + link_to(
    "#{letter} ", 
    :action => action, 
    :id => letter)
   end    
 return return_text + ' ]'
end

I figured it would be nice to have a single method call that generates all the links, and allows me to set the controller method that's called when a user clicks one of the links.

Then in the view, I added this line:

<%= alpha_links('list') %>

Now on the view I have a nifty block that looks like this:
[ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ]
where each letter is a link to:
/user/list/A or /user/list/B etc etc.

Finally, I modified the "list" controller method as follows:

def list
 if params[:id].nil?
  alpha = 'A%'
 else
  alpha = params[:id] + '%'
 end
 
 @users = User.find(
  :all,
  :order => 'last_name',  
  :conditions => ["last_name like ?", alpha])
 
end

Oops! As DHH used to say. My alpha pagination is done.

Note that ActiveRecord will sanitize the conditions hash in the example above, to avoid SQL inject attacks. It's worth mentioning that if I wrote that line like so:

 :conditions => "last_name like '#{alpha}%'")

ActiveRecord would NOT sanitize it and SQL could very easily be injected right from the URL!

Apr 23 09:32

Lambdas and named_scope

I'm been trying to wrap my head around lambdas. Like with most concepts, I try to educate myself on the subject, then as I'm writing code I try to find ways to employ the concepts to further sink them into my brain. I just haven't been able to do this with lambdas for some reason.

Then, one of my dev team members passed this link to the team to tell us about named_scopes: http://ryandaigle.com/articles/2008/3/24/what-s-new-in-edge-rails-has-fi...

Named_scopes are really cool, as a matter of fact, I was already doing something similar, but I was defining a method on the model and putting my custom finder there. Named_scopes just makes for less typing when doing this.

When I saw lambda in the named_scope example, I realized that I'm already using lambdas in my code, I just didn't make the connection between the concept and the application. Probably because in the context I'm employing lambdas, I don't need the lambda keyword.

I love the collect method on the enumerable class, and it turns out, it employs a lambda. Let's say I need an array of the catalog numbers for the line items on an order.

order = Order.find(:first)
catalog_numbers = order.line_items.find(:all).collect 
 { |item| item.cat_no }

I'm passing in the code block:
{ |item| item.cat_no }

to the collect method, and this is the concept of a lambda. You pass a code block into a method like a value.

Another example is the find method on enumerable. Let's say I have an array of hash objects (like what you get with an activerecord collection), and you want to find one of the hash objects (without going to the DB again).

my_item = order.line_items.target.find 
 { |item| item.cat_no == '231-5543' }

Note: if order is an activerecord object, calling the find method will call activerecord's find method. In this case I want enumerable's find method, hence the target method inserted in there. If order was just an array of hash objects, you can omit the 'target' method.

Also note, even with eager loading, calling order.line_items.find would generate another SELECT query. So in order to take advantage of eager loading, I'm using this pattern to pick out a single child.

So, looks like I'm using lambda's already, and didn't realize it.

Jan 30 18:53

acts_as_conference

Wow, it's been since July since I last posted on my blog. That's a cryin' shame. Verizon Business has been keeping me extremely busy, which is good. No time to blog though.

So, the point of this post, I'm attending acts_as_conference in Orlando next weekend. I'm very excited as this will be my first Rails conference!
http://www.actsasconference.com/

As for non-professional projects, HairForecast.com is rolling along, no major news to report on it but I'm pleased that it's visitor rate has been steady, and I'm still getting great fan mail on it a few times per week.

Jul 25 15:14

Shuffling my collection of girls

Yes, this is a dev post, no, it is not a post about my personal life. I'm happily married to only one woman. ;-)

Now that I've clarified that. If you've ever looked at HairForecast.com, you'll note that I have some graphics that depict the weather, and there's a different girl in each graphic. The girls wear coats when it's cold, they have umbrellas when it rains, their hair is blown around when it's windy, etc. You'll also note that I have 3 different girls that I rotate. I wanted to have the girls appear in random order, but never have the same girl appear twice (on the same forecast day).

Jul 10 13:42

Simplifying your test code

Robby, one of the developers I worked along side at BlueSpire, used a pattern in our test suite to simplify the setup of objects under test. I thought it very useful and it made the code so much more readable.

The idea is, when setting up an object n different ways, it's nice to set only the parameters you need on the same line as the instantiation.

Jul 08 22:18

Using method_missing in Ruby

I first learned about method_missing when Christopher Bennage asked me about it. Even after looking it up and explaining it to Christopher, I didn't find any practical use for it in the things I was doing.

At least until tonight, that is.

I've been working on expanding Hair Forecast into Canada. I have the Canadian weather data described in active record like so:

    create_table :canadaforecasts do |t|
      t.column :fcstdate, :date
      t.column :lat, :float
      t.column :lon, :float
    end

Jun 11 21:47

Hello Drupal!

I became frustrated with blogger lastnight. It refused to publish to my server, and didn't throw any errors. I was watching my log files on the server and blogger wasn't even attempting to connect and publish.

Jun 10 23:00

Discovering a compromised server

I took a freelance job today helping someone with his compromised server. He wasn't sure what the problem was, but he knew he was getting emails from his ISP about abuse reports naming his IP address.

Jun 09 16:14

Simple templates in RHTML

I'm creating a very simple site (4 static pages) for some friends and didn't need to do anything fancy (yet), so I elected to use plain HTML and Javascript and not use a framework (like Rails). I like Rails, but I just can't see myself deploying the whole Rails tree just for a 4 page static site.

May 23 11:23

Simple Webrick server for testing plain HTML

Sometimes I find myself in need of a quick and dirty HTTP server when I'm working in plain HTML with a little Javascript. For example: when I'm building banner ads that use scriptaculous. Rather than mess with lighttpd and get the files where it can see them, I found myself wanting to the do the old "ruby script/server" as we do in Rails.

So I looked at the Webrick home page, and ended up with this:

require 'webrick'
include WEBrick
s = HTTPServer.new(
 :Port => 3000,
 :DocumentRoot => Dir::pwd
)
trap("INT"){ s.shutdown }
s.start