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 |
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 :/
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.
Biting myself on the ass with Cucumber
So I spent a few hours today biting my own ass with cucumber.
If you are ever trying to "see" stuff in your page and notice that it has strangely disappeared, like oh say, your flash messages or validation errors. You may want to pay close attention if you are testing the page you are on just before hand.
I found out a sore lesson today, there is a big difference between:
1 2 | Then I am on the sign up page And I should see "Email can't be blank" |
And
1 2 | Then I should be on the sign up page And I should see "Email can't be blank" |
The difference here? "The I am on" reloads the page. I didn't know that. So I was trying to make sure I was on the right page, then checking for errors, etc. The page would reload, all my errors would be gone, and my feature was failing.
"Then I should be on" tests that the url matches the one you are on.
The more you know.
Matching Unicode characters and strings with Regexp in Ruby 1.9.2
So I've been working on a revamp of a project (still secret, ohhhh ahhh) and I needed Unicode support for multiple languages. I whipped up a pretty simple regexp and found that it didn't work for my test case.
So I've got something equivalent to this running inside some rails rspec test. And my test is failing because I'm expecting some russian words.
1 2 3 | greeting = "Добрый день." greeting.scan( /\w/u ) #=> [] |
WTF.
I double check:
1 2 3 4 5 6 7 8 | greeting = "Добрый день." pattern = /\w/u puts greeting.encoding # => UTF-8 puts pattern.encoding # => UTF-8 |
Hell, I even have "# encoding: UTF-8" at the top of the file.
Fuuuuuuuuuuuuuuuuuuuuuuu.
After some google and some irc'ing I find this answer, which solves the problem and allows for matches of unicode characters in ruby 1.9.2, but I still can't figure out what the 'p' stands for. I'm guessing "please". The problem is apparently there was an issue with \w matching unicode characters, so \w specifically only works with ASCII.
1 2 3 | greeting = "Добрый день." greeting.scan( /\p{Word}/u ) #=> ["Добрый", "день"] |
Wööt.
HamlburgerHelper sets the Table – Easily create and display standard tables in Rails
I create tables a lot in my back end for display information.
I posted a table display/create helper method on dzone that easy to use but has a ton of options.
This is the helper I currently use on my admin backend pages to create the simplest table to a pretty complex table.
Here are all of the available options and their defaults:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | :table_class => 'display_grid', # CSS class name of the table :table_id => "display_grid_#{objects.first.class.to_s.underscore.pluralize}", # CSS ID of the table :heading_class => 'display_grid_heading', # CSS class name of the first TR (one containing TH) :heading_id => "display_grid_heading_#{objects.first.class.to_s.underscore.pluralize}", # CSS ID of the first TR :th_class => 'display_grid_th', # CSS class for all TH elements :tr_class => 'display_grid_tr', # CSS class for all TR elements :td_class => 'display_grid_td', # CSS class for all TD elements :even_odd => true, # Should even/odd classes be added :format_date => nil, #nil | lambda{|datetime|} # Time formatter (receives date object, expects string) :numeric_td_class => 'numeric', # CSS class for any TDs containing a number :date_td_class => 'date', # CSS class for any TDs containing something date-like :string_td_class => 'string', # CSS class for any TDs containing a string # Any of this will create an 'Actions' heading, the lambdas receive the object, and expect a string :show_action => nil, # lambda{|object| link_to '', my_path(object)} :edit_action => nil, # lambda{|object|} :destroy_action => nil, # lambda{|object|} |
Using it is pretty easy, just drop it in your app/helpers folder and...
1 2 3 4 | display_grid(User.all, { :show_action => lambda{|user| link_to user.display_name, public_user_path(user) } :table_id => 'cool_users' }) |
Its pretty simple. It'll dump out a table, and then you can go and style it using the CSS classes/ids above.
Woot, back end DRY.