Archive for the ‘Hosting’ Category

Smart Searching Using Anonymous Scopes

Sunday, June 21st, 2009

I really like how Lovetastic’s search works.  Instead of a plethora of select boxes, users can simply use expressions to create searches.  We did something similar with Tryst’s searching.  One text field and you enter stuff like age ranges (30-45), things like smoke-yes or smoke-no, cities, zipcodes, etc.

Tryst.com Search

To make this whole thing more Rails like, I did not want to just use Regex to parse out search terms and plug them into a complicated SQL query.  That would be soooooo PHP.

Named Scopes are awesome.  You can use them to built decently formatted and fairly complicated SQL queries in a nice, no bullshit, manner. You can do cool stuff like this:

@users = User.active.males.in_zipcode(params[:zip_code]).nonsmokers.top_ten

Just keep stacking scopes on.  Rails generates the query and it usually doesn’t suck.

Go read the API docs for the basics on Named Scopes. We’re gonna do some not basic stuff. Check out Ryan Bate’s Anonymous Scopes Railscast, which introduced me to this method of generating Scopes.

We wanted to add a Class Method called search and have it define the Scopes. We pass in the params hash, anything else we need (like the current user), and let it handle the Scoping and determining what we’re looking for from the search params.

To start, here is our search class method.

# app/models/user.rb
 def self.search(params, current_user)
    page = params[:page] || 1
    search_terms = params[:search]
end

Yeah, using Will Paginate and assigning my search terms to a local variable. I’ve also written another Class Method that does the parsing of the params.

# app/models/user.r.b
def self.search(params, current_user)
    page = params[:page] || 1
    search_terms = params[:search]
   #parse params
    max_age, min_age, zip_codes, gender, smoke, drink = self.get_parameters(search_terms.to_s, current_user)
  end

Now, we’re going to define some dynamic scopes. This is awesome. For it to work, we’ll add an initializer.

#config/initializers/scopes.rb

class ActiveRecord::Base
  named_scope :conditions, lambda { |*args| {:conditions => args} }
end

Now, lets make the scopes.

def self.search(params, current_user)
    page = params[:page] || 1
    search_terms = params[:search]

    max_age, min_age, zip_codes, gender, smoke, drink = self.get_parameters(search_terms.to_s, current_user)

    scope = User.scoped({})
    scope = scope.conditions "users.age >= ? and users.age <= ?", min_age, max_age unless min_age.blank?
    scope = scope.conditions "users.zip_code in (?)", zip_codes unless zip_codes.blank?
    scope = scope.conditions "users.smoker = ?", smoke unless smoke.blank?
    scope = scope.conditions "users.drink = ?", drink unless drink.blank?
    scope = scope.conditions "users.gender = ?", gender unless gender.blank?
    scope.paginate :page => page, :order => "created_at DESC"
  end

Pretty slick, huh? So now, in my controller I just need

# app/controllers/search.rb
def users
    @users = User.search(params, current_user)
end

Assuming your class method for parsing out scope parameters doesn’t suck, you should have very clean, cuddly, and concise searching. No more worrying about breaking some huge crappy SQL query.

You’ll notice I used

users.zip_codes in (?)

This is because I can pass an array of zipcodes and return all the users in those zipcodes. This is because I’ve implemented radial distance searching base on zipcodes. Users can search like ” 60606 25miles” and return all users who are within 25 miles of 60606. We’ll go into how this works in the next post.

Deployment Nightmare: Godaddy

Tuesday, November 4th, 2008

Against the advice of, well, the entire Rails community, I attempted to deploy a project to Godaddy.

Godaddy proudly displays the little Ruby logo next to Php when browsing through their hosting plans, and seeing as you can get Rails hosting for $7/month through them, it seems like a decent deal, even if you know it’s gonna get slow.

Baisically, what ends up happening when deploying to Godaddy, is you upload your application to a special directory you create through their control pannel, modify your dispatch.fcgi file, .htaccess files, turn off Java and pray.  Its all supposed to run as FCGI in the end, which is not the speediest way to deploy a Rails app, but is acceptable for smaller, non-demanding applications.

The application I was attempting to deploy was a Rails 2.1 app, and I found that Godaddy is rocking Rails 1.1.6 (Release Aug 2006), so I froze Rails (standard practice), change the configs to point to the manually created database (no rake support here folks) and tried to hit it.  Nothing.  First, every time you modify the .htaccess file (and you will need to – many times) it takes 10-30 minutes for Apache to notice.  So, you’re stuck making a change, waiting, testing, washing and repeating.  If you’re using FCGI, there is literally NO output into the ‘errorlog’ Godaddy gives you, so you have to deploy with regular CGI to get any idea why your application isn’t working and then switch back to FCGI and pray.

Finally, after a couple hours of searching the interwebs and poking and prodding, I was getting somewhere – the error log started to get populated, but FastCGI wasn’t starting correctly.  After some snooping, I saw that Rails 2.1 assumes that you’re not running whatever version of Gem was around in 2006.  Gem was failing with a method not found error when Rails tries to talk to Godaddy’s almost 3 year old version.

I putzed around a bit looking for ways to skip that step of Rails’ boot process, swaping out boot.rb files from older version of Rails, hacking, etc.. and to no avail.

This is where I stopped.  I figured that even if I got past this problem, the version of Rmagick was from mid 2006 and would likely not work with my app, and I wasn’t about to try and get Godaddy to update it.  If they haven’t updated any of the Rails stuff yet, I don’t think they are going to any time soon.

My message for Godaddy is this:  I want the entire day I spend on this back.  You should stop advertising that you support Ruby/Rails when you obviously don’t.  I’d bet 90% of Rails apps in development today would not be able to run on their hosting plans. Eh.

Uncrappifying your Qwest Modem (if it is a GT-701WG that is)

Tuesday, July 8th, 2008

I have DSL service from Qwest.  It is ok.  Beats the competition. The one thing I HATE is the modem I got from them, an Actiontec GT-701WG.

The problem?  It was darn near impossible to make it be JUST a modem, not act as a router/gateway, not act as a NAT, no frickin Actioncrap firewall, just give me the WAN Ip address!

Nearley, but not impossible.  Here is how.

The trick is to set the modem to transparent bridged mode (I think it was labeled as RFC 1483 Bridged). You should be able to find it somewhere in the advanced setup section.  The username and password needed below are also found on the same page.

Then you take your handy dandy router (mine is running DD WRT v.24, may not work with standard firmware) and change the WAN setup like this:

1. Connection type = PPPoE (Even though Qwest says they only support PPPoA
2. Username = your username… our was parts of our last name followed by some numbers.
3. Password = corresponding password
4. PPP Compression = On.  At least I have it on.

Everything else in that option group set to off.  While you’re at it, go to DNS and grab OpenDNS’s DNS server’s ips and throw them in there.

Now what the heck did this do?  Well, now your router’s WAN IP will be the Ip assigned by Qwest.  Before it was being assigned an IP by the Actiontec modem, probably 192.168.1.1XX, cause it was acting as a router, which sucks.  Now, the router is the gateway and, if you’re running DD WRT or some other thirdparty firmware, a much better gateway than the Actiontec was.

Its been going for a month like this and hasn’t been power cycled.  Not to mention all the much better features DD WRT offers.  Sweet.