Cory O'Daniel – These are just words Software development, thoughts, and randomness

7Oct/110

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

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

12Apr/111

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.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

23Mar/110

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.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

22Feb/110

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.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

16Feb/113

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.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

2Nov/100

methods_like helper for finding ruby methods

Here is a little gem I have in my ~/.irbrc I use it a lot when debugging code. I tend to use call #methods a lot on objects to figure out how they quack.

Sometimes I can remember kinda what a method's name is I want to use, but doing something like the following can generate a huge amount of output especially when working with ActiveRecord

1
my_object.methods.sort

So I threw together this little method that adds to ruby's base Object class

1
2
3
4
5
6
7
8
class Object
  def methods_like(pat)
    pattern =  pat.is_a?(String) ? /#{pat}/ : pat
    self.methods.sort.select{|m|
      m =~ pattern
    }
  end
end

Now from IRB, Rails console, or Padrino console you can do things like:

my_collection.methods_like(/^update/) #=> [ array of method names that start with update ]
user.methods_like("name") #=> [ :first_name, :last_name, :etc ]
Array.new.methods_like "sort" #=> ["sort", "sort!", "sort_by"]

Its a handy little snippet when you can't remember an exact method name, and generally for me works faster than googling for it. Toss it in your ~/.irbrc and tell me what you think.

UPDATE
I added some more functionality for getting additional methods (private, protected, singleton):

class Object
  # pattern - string or pattern to match
  # access  - :public, :private, :protected, :singleton, :all
  def methods_like(pattern, access = :public, details = false)    
    master_method_list = case access
    when :public then self.methods
    when :private then self.private_methods
    when :protected then self.protected_methods 
    when :singleton then self.singleton_methods
    when :all
      (
        self.methods            +
        self.private_methods    +
        self.protected_methods  +
        self.singleton_methods
      ).uniq
    end
 
    matched_method_list = master_method_list.sort.select{ |m| m =~ ( pattern.is_a?(String) ? /#{pattern}/ : pattern ) }
 
    if !details
      matched_method_list
    else
      details_list = {}
 
      matched_method_list.each do |current_method|
        details_list[current_method] = method_details(current_method)
      end
 
      details_list
    end
  end
 
  # Returns details about method
  def method_details(current_method)
    current_method = current_method.to_s
    access_level = if self.methods.include?(current_method)
      :public
    elsif self.private_methods.include?(current_method)
      :private
    elsif self.protected_methods.include?(current_method)
      :protected
    elsif self.singleton_methods.include?(current_method)
      :singleton
    end
 
    {
      :owner    => self.method(current_method).owner,
      :arity    => self.method(current_method).arity,
      :receiver => self.method(current_method).receiver,
      :access   => access_level
    }
  end
end

Now you can do:

variable.methods_like pattern, :private #=> Array of private methods with pattern
variable.methods_like pattern, :all, true #=> Hash of methods with additional details

If you pass 'true' for details you will get a hash that looks like:

Array.new.methods_like "sort", :all, true #=>
{
  "sort_by"=>{:owner=>Enumerable, :access=>:public, :receiver=>[], :arity=>0}, 
  "sort!"    =>{:owner=>Array, :access=>:public, :receiver=>[], :arity=>0}, 
  "sort"     =>{:owner=>Array, :access=>:public, :receiver=>[], :arity=>0}
}

Access options available are :public, :private, :singleton, :protected, :all

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

14Jun/100

Testing 404/500, Error Pages with Webrat visit method

This is a super simple snippet, and maybe there is something built into webrat, but I didn't find it. I needed to test for 404 pages in some of my tests (I was writing tests that made sure a page didnt show for disabled content). Purposely getting 404's can be annoying though since webrat opens up the 404 page in your default browser. There is a variable available in Webrat's configuration that you can use to temporarily disable this functionality. I wrapped it in the following method:

1
2
3
4
5
  def visit_but_dont_open(url)
    Webrat.configuration.instance_variable_set("@open_error_files", false)
    visit url
    Webrat.configuration.instance_variable_set("@open_error_files", true)
  end

Throw that in your test_helper and you can start doing:

1
2
visit_but_dont_open "Http://example.com/page/that/doesnt/exist"
assert !response.ok?

Yay, 404.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

Tagged as: , , No Comments
7May/101

Ruby 1.9 Date#strftime adds a space on %b

So, I had a spec failing in my integration suite and I couldn't figure out what the hell it was - EVERYTHING LOOKED LEGIT. I even logged into the site, and visually verified it. Whats the dilly?

I upgraded to ruby 1.9 from ruby 1.8 and apparently, when doing strftime on date, you get a free whitespace character with "%b"

1
2
3
# Ruby 1.8
irb(main):006:0> Date.today.strftime("%b %e, %Y")
=> "May 7, 2010"
1
2
irb(main):007:0> Date.today.strftime("%b %e, %Y")
=> "May  7, 2010" # TWO SPACES

Super dumb - wasted a good 10 minutes of my day because I could see the diff between:

1
2
"May  7, 2010"
"May 7, 2010"

In the middle of a web page full of other content.

Why the freebee space character on the %b? Dunno. I just smashed my stftime statement together and my specs are rolling.

1
Date.today.strftime("%b%e, %Y")

Uh, yeah - this is my bug report.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

17Apr/100

Padrino, Compass, and Sass – Working happily via Ian Serlin

My cohort, Ian Serlin, discovered this. In a project we are working on we could get Compass to play well with PadrinoRb. It seems like the #sass method doesn't care about the options being passed to it, and we kept getting stack traces rendered into our CSS files. The stack trace was ruby looking for - and failing to find compass/reset.css.

This is the code that DOESN'T work (padrino 0.9.10, compass 0.8.17, sinatra 1.0).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  configure do
    register SassInitializer #The Rack Sass reloader...
 
    Compass.configuration do |config|
      config.project_path = File.dirname(__FILE__)
      config.sass_dir = "stylesheets"
      config.project_type = :stand_alone
      config.http_path = "/"
      config.css_dir = "stylesheets"
      config.images_dir = "images"
      config.output_style = :compressed
    end
  end   
  get '/stylesheets/:file.css' do
    content_type 'text/css', :charset => 'utf-8'
    # This is the doc on how to give Sass some more load paths to find the compass  files. 
    #   it doesnt work :P and for that matter, neither does:
    # sass :file, Compass.sass_engine_options
    # or the set :sass, whatever_hash_here
    sass :file, :sass => Compass.sass_engine_options
  end

Our solution was to force Sass options and Compass options to merge...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  configure do
    register SassInitializer
 
    Compass.configuration do |config|
      config.project_path = File.dirname(__FILE__)
      config.sass_dir = "stylesheets"
      config.project_type = :stand_alone
      config.http_path = "/"
      config.css_dir = "stylesheets"
      config.images_dir = "images"
      config.output_style = :compressed
    end
 
    Sass::Plugin.options.merge!(Compass.sass_engine_options)
  end
 
  get '/stylesheets/:file.css' do
    content_type 'text/css', :charset => 'utf-8'
    sass :file
  end

Yay, magic spells - it works. You can check out more ian magic spells at ianserlin.com.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

5Mar/106

Attaching local or remote files to Paperclip and Milton Models in Rails (Mocking content_type and original_filename in a Tempfile)

I was working on a project today where I needed to import some data from MySpace accounts (yeah, MySpace), which included importing the users profile image. In the controller that did the importing I was using OpenURI to retrieve the image and then turn it into a Tempfile to be attached the the model, like so:

1
2
3
4
5
6
7
def import
#...snip
  tempfile = Tempfile.new( my_filename)
  tempfile.write open( image_url ).read
  @imported_user.images.create(:file => tempfile)
#...snip
end

This doesn't work. It blows up missing one of two methods:

  1. #original_filename
  2. #content_type

If you inspect a normal file upload in Rails which has these methods, you'll find that is just a regular old Tempfile. But it has the methods! If you create a Tempfile manually, it won't have the methods. That's because Rails magics them on. I am not sure why they don't create a subclass like Rails::Tempfile that contains these methods and just use that. I guess its because OOP is retarded (sarcasm).

So, I wrote a little subclass that will take a file path, and quack like the magic'd rails Tempfiles you get from an upload so you can attach local files to models or even remote files.

This works pretty straightforward and I'm currently using it in production. It won't work on windows systems because the dependency on the 'file' binary. Also, some linux systems are missing this library by default, so make sure you yum|dpkg|apt|port or whatever to get it installed.

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
27
28
29
30
31
32
require 'open-uri'
require 'digest/sha1'
 
class RemoteFile < ::Tempfile
 
  def initialize(path, tmpdir = Dir::tmpdir)
    @original_filename  = File.basename(path)
    @remote_path        = path
 
    super Digest::SHA1.hexdigest(path), tmpdir
    fetch
  end
 
  def fetch
    string_io = OpenURI.send(:open, @remote_path)
    self.write string_io.read
    self.rewind
    self
  end
 
  def original_filename
    @original_filename
  end
 
  def content_type
    mime = `file --mime -br #{self.path}`.strip
    mime = mime.gsub(/^.*: */,"")
    mime = mime.gsub(/;.*$/,"")
    mime = mime.gsub(/,.*$/,"")
    mime
  end
end

Usage is pretty simple:

1
2
3
remote_file = RemoteFile.new("http://www.google.com/intl/en_ALL/images/logo.gif")
remote_file.original_filename #=> logo.gif
remote_file.content_type #= image/gif

Using it in your controller:

1
2
3
4
5
def import
  #...snip
  @imported_user.images.create(:file => RemoteFile.new( url_to_image ))
  #...snip
end

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon