Rails 3 Easy Search forms using SimpleForm and ActiveModel
For the sites I'm currently working on I have search pages all over the place for different resources, especially on the backend. In the past I would forego using form builders and just do some HTML to make a form GET the data. I hate HTML.
So I did some poking around to see what I could do to DRY up my time on creating these search forms. My first thought was to create a Search class and use active model, but it sucked because I everytime I had to create a new form I had to come into the search class and add a bunch of attr_accessors for all of the search fields. That sucks.
So I decided to mix it up with OpenStruct and ActiveModel
require 'ostruct' class Search < OpenStruct include ActiveModel::Validations include ActiveModel::Conversion extend ActiveModel::Naming def initialize(*args) super end def persisted? ; false ; end; end
Now I use this one model for searching of all of my resources.
I create the form super easily with SimpleForm:
= simple_form_for(@search, url: my_cool_path(), html:{method: :get}) do |f| = f.input :text = f.select :category, Product::CATEGORIES = f.input :minimum_price = f.input :maximum_price = f.submit :search
In your controller you are going to get a params[:search] hash with :text, :category, :minimum_price, :maximum_price.
Want to persist that search across page reloads?
class ApplicationController < ActionController::Base before_filter :persist_search protected def persist_search @search = Search.new params[:search] end end
Now you'll have a Search object in all of your requests with your user's search params. Use it as you will to return them the resources they are looking for.
*Side note*
I use the above logic for all of my resources, until one of my resources has a requirement like a validation. Then I break it into something like FlightSearch:
require 'ostruct' # ostruct for that lazy goodness class FlightSearch < OpenStruct # Get activemodel in there for all its cool functionality like model name, pluralization, validation, yada yada yada include ActiveModel::Validations include ActiveModel::Conversion extend ActiveModel::Naming validates_presence_of :origin_airport, :destination_airport # you can do cool things here, just make sure you call super so ostruct # takes all those hash params and makes them methods def initialize(*args) super end # ActiveModel needs to know that this wasn't persisted. def persisted? ; false ; end; end
Airbrake’s Javascript Error Reporting and Google
So today I turned on the experimental JavaScript Error Reporting in Airbrake today. Within about 5 minutes my inbox was bombarded.
Looks like Google the masters of writing software for everything are very diligent in testing their code.
I have a feeling I'm going to have to add a lot of exclusions to my "Global Error Classes" list.

Using devise’s scoped url helpers in Cucumber; new_registration_path, new_session_path
You can use devise's scoped url helpers from cucumber, but you have to use them by their real Url Helper method names:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | module NavigationHelpers def path_to(page_name) case page_name when /the sign up page/ # dont use the helper's helper # new_registration_path(:user) # use the underlying url helper new_user_registration_path when /the sign in page/ # dont use the helper's helper # new_session_path(:user) # use the underlying url helper new_user_session_path else #...yada, yada |
Screen shots of Steve Jobs Homage Pages, and Smarmy comments.
This is just a few screen shots of some big software companies with homages on their home pages to Steve Jobs. These are listed from Classy to Least Classy.
Apple
Well of course they have one.
Google
Despite the phone war, classy Google - Very classy.
Adobe
Bastard wouldn't let your crappy software on his iOS, but a little reminiscent photo. *Respect*
Microsoft
"Steve Jobs is dead, btw do you have an XBox 360 yet? Apple never made a console - Just sayin'"
Dell
"Who the fuck is Steve Jobs?" Michael Dell! *Tsk, tsk* That's just rude.
Application stuck installing on iPhone
I was trying to install an app last night while buzzed in a shady dive bar with no signal.
It took forever and eventually my phone gave up. I figured when I was on a decent network it'd continue installing. Nopecity.
Today the app has about half the meter full.
How do you fix it?
I tried clicking. Nope. Deleting? Nope - it was disabled.
Reboot the phone? Yea! It prompted me for my password and all is good.
cucumber.yml was found, but could not be parsed. Please refer to cucumber’s documentation on correct profile usage.
Yeah, I got that message today and wasted about 30 minutes of my life.
I hadn't changed my cuke yaml file since I started work on the app.
I tried:
* upgrading cucumber (FAIL)
* using another yaml file (FAIL)
* upgrading gherkin (FAIL)
* hard reseting the branch I was on (FAIL)
Turned out to just be a bum rerun.txt file. So I deleted it.
I need a refund on that 30 minutes.
How to stop ruby’s builder gem from escaping too much stuff (like the links in your atom feeds) :(
So, I recently started working on Dealbase and its a rails 2 site. I don't have the luxury of called #html_safe on a string to stop it from being escaped.
While working on our syndication system I came across an interesting issue where the links in our Atom feeds were getting escaped:
1 2 3 4 5 6 7 | <!-- instead of --> <link type="text/html" rel="alternate" href="http://dealbase.dev/Scottsdale-hotel-deals-discounts-457?deal=266679&foo=bar"/> <!-- I was getting --> <link type="text/html" rel="alternate" href="http://dealbase.dev/Scottsdale-hotel-deals-discounts-457?deal=266679&foo=bar"/> <!-- WOMP --> |
So I breakpointed and started poking around:
1 2 3 4 5 6 7 8 9 10 11 | atom_feed do |feed| feed.title("My cool feed") feed.updated(@items.first.try(:created_at)) for item in @items # here was the problem... cool_item_url(item) is getting escaped... right here though, its FINE?! feed.entry(deal, :url => cool_item_url(item)) do |entry| #... yada, yada end end end |
Ok, so something is being called on it... right?
I inspect the methods on the string that gets returned from cool_item_url(item) and found an interesting one called "to_xs".
So I throw an instance_eval in there to see whats up with this method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | atom_feed do |feed| feed.title("My cool feed") feed.updated(@items.first.try(:created_at)) link_hack = cool_item_url(item) link_hack.instance_eval do def to_xs raise Exception, "STOP CALLING THIS" end end for item in @items feed.entry(deal, :url => link_hack) do |entry| #... yada, yada end end end |
Hit the atom feed, and boom. Exception. Yep, somethings calling it. I looked at the backtrace and its builder. After poking around I found this from the builder docs:
"String attribute values are now escaped by default by Builder (NOTE: this is new behavior as of version 2.0).
However, occasionally you need to use entities in attribute values. Using a symbols (rather than a string) for an attribute value will cause Builder to not run its quoting/escaping algorithm on that particular value."
So the fix?
To call to_sym on the link in the builder.
1 2 3 4 5 6 7 8 9 10 | atom_feed do |feed| feed.title("My cool feed") feed.updated(@items.first.try(:created_at)) for item in @items feed.entry(deal, :url => cool_item_url(item).to_sym) do |entry| #... yada, yada end end end |
Its ugly, but it works. Thanks builder :/
Soft launch of Rebody (rebodyhq.com)
Ian Serlin and I have been working on kolaboratory's first official project for the past few months called Rebody.
Rebody is a fitness and health analytics and tracking platform. The goal is to help people lose weight like I did through setting short goals and focusing on your body's metrics so you can see what really works for you.
Beyond that Rebody will soon have a network of trusted and qualified personal trainers to look into your metrics and questions to help you determine how to "rebuild yourself", as well as a community of people sharing their results that you can browse for inspiration or to apply their methods to yourself.
We are in our soft launch right now accepting Alpha sign ups. For pre-registering you'll get the first three months for free and lock in at $9 a month for life (our competitors software starts around $25/mo).
I started my experiment today. 10% body fat here I come. Can I do it? You'll need to follow me on rebody to find out.
Hulu’s April Fool’s Prank
If you haven't seen Hulu's April fool's prank, you are missing out.

I have to say, they had me at first, but if they really wanted to trick me they would have pretended to add some good content.
PWNED.
Backdateable mongoid models – a simple little module I use a lot
I have a lot of models in one of the projects I am currently working on that need to be backdate-able. This is just a simple module I add to any class that needs to support it. It could be easily tweaked for ActiveRecord.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | module Backdateable module ClassMethods;end; def self.included(base) base.extend(ClassMethods) #allows this to be an accessor on the front end, although, # if the date is left blank, it gets set properly in before base.send(:field, :backdated, type: Boolean, default: false) base.send(:field, :recorded_at, type: Time) if base.respond_to?(:default_scope) base.send(:default_scope, :asc, :recorded_at) end # Changing this to before_create only allows backdating on creation... base.send(:before_save, :set_recorded_at) end # defaulting recorded at to created_at lets this be sortable by recorded at # we override backdated here in case the date was originally left blank def set_recorded_at self[:recorded_at] ||= self[:created_at] self[:backdated] = (self[:recorded_at] != self[:created_at]) true end end |
Now you can do:
1 2 3 4 5 6 7 8 9 10 11 | class Measurement include Mongoid::Document include Mongoid::Timestamps include Backdateable end Measurement.create # => created_at & recorded_at are set to the same, backdated is set to false Measurement.create(:recorded_at => Time.now.advance(:days => -7)) # => recorded_at is set to the past, backdated is set to true |
Why do I default recorded at to created at? So I can sort by recorded_at.
Done, son.