Attach non image files in Rails with paperclip

19 05 2009

Paperclip, by Thoughtbot, has pretty much become the standard when it comes to attaching files to models in Rails. It has a very easy to use API, allows the user to create her own post-processing code (such as OCR or others), and provides callbacks for before and after post-processing.

This post does not cover getting started with paperclip. There are plenty of other posts that cover that. The intent is to get you up and running on using paperclip to attach non-image files.

Out of the box, the default post processor invoked upon upload is the Paperclip::Thumbnail processor. This processor creates thumbnails of an image based on the styles hash passed to the has_attached_file method.

If you want to upload word documents, excel files, and other non-image data, the Thumbnail processor will fail, and the attachment will not succeed. This processor does not check if the file itself is an image, and tries to call ImageMagick’s identify command on the file anyways.

Paperclip makes solving this extremely simple: prevent the post processing from happening when the file is not an image. Just like on ActiveRecord callbacks, you can return false on paperclip’s before_post_process callback to avoid the processing from happening in the first place. One possible solution is to put the following in whatever model you’re using paperclip on:

  before_post_process :image?
  def image?
    !(data_content_type =~ /^image.*/).nil?
  end


In this case, we’ve created an image? method which returns true if the content type starts with the string image (as in ‘image/png’, ‘image/jpeg’, etc). What’s neat about this is that this same method can be used in your views to either render an image_tag, or something else such as an icon or the file’s name, depending on whether the file is an image.





Compiling Ruby 1.9 with GCC 4.4

28 04 2009

Got the 1.9 pickaxe, so it’s definitely time to get Ruby 1.9 installed alongside 1.8. It turns out that if you try to build Ruby 1.9 with GCC 4.4 by means of the typical ./configure; make; sudo make install, you will hit a wall on the make step. I’m running into this while evaluating Fedora11 snap1, still in beta. The issue is that GCC 4.4 introduces a few changes that will impede ruby 1.9 from compiling without some minor adjustments.

These are the steps to get going:
1. Download, verify and unpack Ruby 1.9.
$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p0.tar.gz
$ md5sum ruby-1.9.1-p0.tar.gz
50e4f381ce68c6de72bace6d75f0135b ruby-1.9.1-p0.tar.gz #verify that hash matches
$ tar xvf ruby-1.9.1-p0.tar.gz
$ cd ruby-1.9.1-p0

At this point, we have verified and extracted the ruby 1.9 source code. Go ahead and create your platform’s compile configuration. There are many flags you can pass. To see them all, type ./configure –help. I’ve chosen to keep the libraries in their default locations, and suffix the binaries with ‘19′ (leaving your 1.8 ruby installation intact). Some folks prefer to install it on a completely different path to avoid this very issue, by using the –prefix-path flag instead. That’s up to you.
./configure --program-suffix=19 --disable-pthread
At this point, you may be tempted to type “make”. If you do, you’ll get the following error:

--snip--
cont.c:90:6: error: #elif with no expression
cont.c:270:6: error: #elif with no expression
cont.c:317:6: error: #elif with no expression
make: *** [cont.o] Error 1


The resolution is to edit the cont.c file to go on. Open it up, and change the lines 90, 270 and 317 from #elif to #else.

Now, carry on as usual:
$ make
$ make test #make sure everything's sane
$ sudo make install #or su -, or whatever it is you kids use to gain admin privileges these days

You’re done. irb will open up the console on ruby 1.8, and irb19 will open it in ruby 1.9. Similarly, point your scripts to the ruby19 binary instead of ruby.

You can now try out some of the new ruby 1.9 features in irb19:
$irb19
irb(main):001:0> [1,2].inject(:+) #new injections
=> 3
irb(main):002:0> {a: 'a_val', b: 'b_val'} #new hash synthax
=> {:a=>"a_val", :b=>"b_val"}
irb(main):003:0> p = -> a,b,c {a+b+c} #lambda shorthand
=> #
irb(main):004:0> Time.now.tuesday?
=> true
irb(main):005:0> beta_string = 'ß' #utf-8 strings
=> "ß"
irb(main):006:0> π = 3.14 # and variables
=> 3.14
irb(main):007:0> π * 5
=> 15.7

Now, let’s see what the fuzz is all about. I tried running the the full ruby benchmark suite, but I got a nasty kernel panic:Apr 27 23:03:45 hgnux kernel: Critical temperature reached (95 C), shutting down.Guess I need a laptop that sucks less to run it. This isn’t over though: I’ll stick my laptop in the freezer if I have to, just to run the full benchmark suite.

Instead, let’s run and benchmark the most inefficient piece of crap on both versions of ruby. Recursion will do the trick. Let’s calculate factorials and fibonaccis left and right. Save this script somewhere:

#recursion.rb
require 'benchmark'
include Benchmark
def fact(n)
  n == 1 ? 1 : n*fact(n-1)
end

def fib(n)
  if n == 0 || n == 1
    n
  else
    fib(n-1) + fib(n-2)
  end
end

puts "Ruby #{RUBY_VERSION} patch #{RUBY_PATCHLEVEL}"

bm do |r|
  r.report do
    (1..35).each do |n|
      fact(n)
      fib(n)
    end
  end
end

Now, invoke it with both your ruby and your ruby19 binaries:

Ruby 1.8 vs. Ruby 1.9 running recursion.rb

Ruby 1.8 vs. Ruby 1.9 running recursion.rb


$ ruby recursion.rb
Ruby 1.8.6 patch 287
      user     system      total        real
155.320000  45.950000 201.270000 (229.181959)


$ ruby19 recursion.rb
Ruby 1.9.1 patch 0
      user     system      total        real
 16.030000   0.100000  16.130000 ( 16.852891)

Sweet! I’ve also included the results of vmstat + vmplot of these runs. Most impressive is the minimized number of system interrupts on the ruby 1.9 version.

References:
Ruby downloads: http://www.ruby-lang.org/en/downloads/
GCC 4.4 porting notes: http://gcc.gnu.org/gcc-4.4/porting_to.html
Extended vmplot: http://ronaldbradford.com/blog/extending-vmplot-2009-03-31/
Ruby benchmark suite: http://github.com/acangiano/ruby-benchmark-suite/tree/master





Really silly percentage calculation in Ruby

22 03 2009

Here’s a silly way to calculate percentages of numbers in a Ruby project or Rails app.

The end result of this is having created a few methods for Ruby numeric classes that allow you to calculate percentages:
>> 23.456.five_percent
=> 1.1728
>> 100.46.seven_percent
=> 7.0322
>> 95.fifty_percent
=> 47.5

To accomplish this, we’ll use use meta programming to add the methods to the Fixnum and Float classes.
First, create a way to translate numbers in english to numeric values. This was taken from http://www.ruby-forum.com/topic/132735#591799.
Then, we’ll make use of Ruby’s method_missing to find out if this is a “*_percent” method and act accordingly. The percent_method? method returns true if it is, false if it ain’t.

module NumberPercentageExtension
  module InstanceMethods
    ENGLISH_VALUE = {}
      %w| zero one two three four five six seven eight nine ten eleven
          twelve thirteen fourteen fifteen sixteen seventeen eighteen
          nineteen |.each_with_index{ |word,i| ENGLISH_VALUE[word] = i }
      %w| zero ten twenty thirty forty fifty sixty seventy eighty
          ninety|.each_with_index{ |word,i| ENGLISH_VALUE[word] = i*10 }
    ENGLISH_VALUE['hundred'] = 100
    def percent_method?(method)
      tokens = method.to_s.split('_')
      return false if tokens.size < 2
      is_percent = tokens[-1] == 'percent'
      tokens[0..-2].collect { |word| is_percent &&= ENGLISH_VALUE.has_key?(word) }
      return is_percent
    end
    def method_missing(method, *args)
      if percent_method?(method)
        method.to_s.split('_')[0..-2].map { |word| ENGLISH_VALUE[word] }.sum * self / 100.to_f
      else
        raise "NoMethodError: undefined method #{method} for class #{self.class.name}"
      end
    end
  end
end


We also want respond_to? to be aware of the new *_percent methods, so we include that as well.

    def respond_to?(method)
      return true if percent_method?(method)
      super(method)
    end


Finally, include the module in both Fixnum and Float:

  Fixnum.class_eval do
    include InstanceMethods
  end
  Float.class_eval do
    include InstanceMethods
  end

Till next one.





Upgrade to Rails 2.2 on Windows

17 01 2009

A glimpse at the bottom of the Rails 2.2 release notes show that Rails 2.2 require RubyGems version 1.3.1.

After failing to simply gem update –system, try the following:


> gem install rubygems-update
> cd c:\ruby\lib\ruby\gems\1.8\gems\rubygems-update-1.3.1
> ruby install.rb

This will successfully update RubyGems, and you should finally be at:


> gem -v
1.3.1

Now, we can simply do


> gem install rails
Successfully installed activesupport-2.2.2
Successfully installed activerecord-2.2.2
Successfully installed actionpack-2.2.2
Successfully installed actionmailer-2.2.2
Successfully installed activeresource-2.2.2
Successfully installed rails-2.2.2
6 gems installed
Installing ri documentation for activesupport-2.2.2...
Installing ri documentation for activerecord-2.2.2...
Installing ri documentation for actionpack-2.2.2...
Installing ri documentation for actionmailer-2.2.2...
Installing ri documentation for activeresource-2.2.2...
Installing RDoc documentation for activesupport-2.2.2...
Installing RDoc documentation for activerecord-2.2.2...
Installing RDoc documentation for actionpack-2.2.2...
Installing RDoc documentation for actionmailer-2.2.2...
Installing RDoc documentation for activeresource-2.2.2...

So far so good. Now, if you froze an earlier rails version in your app, setting it free is as simple as:


rake rails:unfreeze

At this point, update your config/environment.rb to rails version 2.2.2, and then run:


>rake rails:freeze:gems
(in C:/code/app)
Freezing to the gems for Rails 2.2.2
rm -rf vendor/rails
mkdir -p vendor/rails
cd vendor/rails
Unpacked gem: 'C:/code/app/vendor/rails/activesupport-2.2.2'
mv activesupport-2.2.2 activesupport
Unpacked gem: 'C:/code/app/vendor/rails/activerecord-2.2.2'
mv activerecord-2.2.2 activerecord
Unpacked gem: 'C:/code/app/vendor/rails/actionpack-2.2.2'
mv actionpack-2.2.2 actionpack
Unpacked gem: 'C:/code/app/vendor/rails/actionmailer-2.2.2'
mv actionmailer-2.2.2 actionmailer
Unpacked gem: 'C:/code/app/vendor/rails/activeresource-2.2.2'
mv activeresource-2.2.2 activeresource
Unpacked gem: 'C:/code/app/vendor/rails/rails-2.2.2'
cd -

Now run your tests suite. You do have tests. You do.

Bonus: If you are stuck on windows, do yourself a favor and use console2 for a half decent command prompt. Easier to select/paiste text, resize the window (no full screen (!!!)), tabs, and other goodies.





Vim for Rails development

23 12 2008

UPDATE: This configuration, while valid, is pretty obsolete. I recommend you just grab my config from github at http://github.com/hgimenez/vimfiles/tree/master which I update more often.

I’ve been using Vim for a while for all ruby and rails development. Vim is a fantastic cross platform text editor, and the more you use it, the more you realize how efficient it is.

vim screenshot on windows

vim screenshot on windows

But you won’t get too far with the default vim installation. Here’s a few settings and plugins that take the experience from great to superb.

Starting with the plugins:

  • matchit.vim: Press % to jump between parenthesis, brackets, html tags, you name it!
  • fuzzyfinder and fuzzyfinder_textmate: This duo make finding files in your project a snap, making plugins like Project and NERDTree a waist of time in my opinion (there’s always :E or :Sex if you really need to browse around).
  • surround.vim is all about dealing with surroundings in text strings. It helps to rapidly change single to double quotes, quotes to html, etc.
  • BlockComment.vim: select a block in visual mode, and use .c to comment it, and .C to uncomment it.
  • camelcasemotion.vim will create word boundaries at CamelCase or under_score word endings when using the motion keys w, b and e (see my vimrc for configuration).
  • rails.vim. If you’re doing Rails programming, you must install this. End of story.
  • snippetsEmu emulates a bunch of the snippets implemented in E/TextMate. For instance, key in def and a method definition will be expanded with the cursor right on the spot to write the method name. Other snippets for each, map, and others are also emulated.
  • vcscommand.vim: Why would you have to leave your editor to commit your changes or update your code? You don’t. VCSCommand handles Git, SVN and other SCMs
  • Exuberant CTags is just magic! Position the cursor over any method call and press C-[ to jump right to the method definition. C-T takes you back. A must have.

In terms of eye candy, there are three colorschemes that I'd like to mention: railscasts, oceandeep and vividchalk. I keep alternating between these, but have settled for railscasts for a while now.

The font I've settled for is Envy Code R.

Finally, my .vimrc file:


set nocompatible
" I usually go back and forth between these three colorschemes.
":colorscheme oceandeep
":colorscheme vividchalk
:colorscheme railscasts
" Some taken from http://items.sjbach.com/319/configuring-vim-right
set history=1000
" additional matches for %: do/end, if/else/end, the usual () [] {}…
runtime macros/matchit.vim
set wildmode=list:longest
” search case insensitive
set ignorecase
” search case sensitive when search-term has a capital letter
set smartcase
nnoremap 3
set ruler
” Intuitive backspacing in insert mode allow, backspace over everything
set backspace=indent,eol,start
” Highlight search terms…
set hlsearch
” search dynamically as keyword is typed.
set incsearch
set shortmess=atI
” get rid of the BEEP
set visualbell
” Provide two lines of context
set scrolloff=2
” set GUI font, again, I go back and forth between these
“set gfn=Bitstream_Vera_Sans_Mono:h9:cANSI
set gfn=Envy_Code_R:h10:cANSI
” when a line has wrapped, jk navigation moves one line on the screen, instead of one line in the file
nnoremap j gj
nnoremap k gk
vnoremap j gj
vnoremap k gk
nnoremap gj
nnoremap gk
vnoremap gj
vnoremap gk
inoremap gj
inoremap gk
” CamelCaseMotion plugin mappings
map w CamelCaseMotion_w
map b CamelCaseMotion_b
map e CamelCaseMotion_e
” Tell vim to remember certain things when we exit
” ‘10 : marks will be remembered for up to 10 previously edited files
” “100 : will save up to 100 lines for each register
” :20 : up to 20 lines of command-line history will be remembered
” % : saves and restores the buffer list
” n… : where to save the viminfo files
set viminfo=’10,\”100,:20,%,n~/.viminfo
” remember open buffers accross sessions
” set viminfo^=%
” Quick write (F2) and load (F3) session:
map :mksession! ~\vim_session
map :source ~\vim_session
” highlight line of cursor:
set cursorline
” Remove toolbar
set guioptions-=T
” and menus
set guioptions-=m
” For fuzzy finder/textmate
” Requires fuzzyfinder and fuzzyfinder_textmate plugins
let g:fuzzy_ignore = “*.log”
let g:fuzzy_ignore = “.svn” Read the rest of this entry »