Skip to content
accelerando

Category Archives: Rails

Rails 2.1: send_file :x_sendfile => true

05-Jun-08

The “x_sendfile” argument on the send_file method in Rails 2.1 is not well thought off as it has an impact in development mode also. I guess most Rails coders won’t have Apache proxying their mongrels in dev mode and so they don’t get to see any images or files but the plain path information.

I’ll guess i stay with the x_send_file solution as described here.

PDF::Writer and Ruby on Rails 2.1

04-Jun-08

Some days ago, Ruby On Rails 2.1 saw the light of day and as usual, i eagerly updated my Daily Fratze project.

I had some minor problems due to an old version of will_paginate and some major ones with my use of PDF::Writer.

The PDF::Writer library still works very well but the the instructions here (under PDF::Writer (Austin Ziegler) on how the register an “rpdf” template handler don’t apply anymore due to changes in the Rails template system but can easily be fixed:

In environment.rb change

ActionView::Base.register_template_handler 'rpdf', ActionView::PDFRender

to

ActionView::Template.register_template_handler 'rpdf', ActionView::PDFRender

And also change your ActionView::PDFRender class to:

module ActionView # :nodoc:
  require 'pdf/writer'
  class PDFRender
    PAPER = 'A4'
    include ApplicationHelper
    include ActionView::Helpers::AssetTagHelper
    include ActionView::Helpers::TextHelper      
    include ActionView::Helpers::TagHelper
    include ActionView::Helpers::UrlHelper
 
    def initialize(action_view)
      @action_view = action_view
    end
 
    # Render the PDF
    def render(template, local_assigns = {})
      @action_view.controller.headers["Content-Type"] ||= 'application/pdf'
 
      # Retrieve controller variables
      @action_view.controller.instance_variables.each do |v|
        instance_variable_set(v, @action_view.controller.instance_variable_get(v))
      end
 
      pdf = ::PDF::Writer.new( :paper => PAPER )
      pdf.compressed = true if RAILS_ENV != 'development'
      eval template.source, nil, "#{@action_view.base_path}/#{@action_view.first_render}.#{@action_view.finder.pick_template_extension(@action_view.first_render)}" 
 
      pdf.render
    end
 
    def self.compilable?
      false
    end
 
    def compilable?
      self.class.compilable?
    end
  end
end

And enjoy happy PDF generation ;)

Turning off x_send_file in development mode

23-Jan-08

I just discovered the great x_send_file plugin and technique and use it extensivly for daily fratze. I replaced the majority of send_file calls with x_send_file but not all (the in memory thingies i serve cannot be send through apache). This works great in production mode but in development mode, it fails as there is no apache sitting in front the mongrels. Therefore i added the following to my $app_home/config/environments/development.rb:

class ActionController::Base
  def x_send_file(path, options = {})
    send_file(path, options)
  end
end

So all calls to x_send_file in dev mode are delegated to the original send_file.

If anyone can present me a cleaner solution, i.e. with method aliasing, feel free to drop a comment.

Using rubyzip to create zip files on the fly

21-Jan-08

In my Daily Fratze project the users should be able to download their faces as a zip file backup.

Until now they have been able to upload zip files. For that, i used rubyzip which worked quite well.

As a starting point i found a nice article on the joy of rubyzip, but this has a major flaw for me. It uses the Zip::ZipFile interface to create its archives. This interfaces takes a filename as parameter and either creates this file if it doesn’t exists or tries to open it as a zip archive.

I doesn’t want my directories polluted by some random zip files so i tried to use TempFile. Creating a new TempFile leads to an existing file which Zip::ZipFile cannot open.

My solution uses the more basic interface Zip::ZipOutputStream. Further requirements were adding binary files with arbitrary names and not like in the examples of rubyzip, creating new files with some textual content. Here we go:

require 'zip/zip'
require 'zip/zipfilesystem'
 
t = Tempfile.new("some-weird-temp-file-basename-#{request.remote_ip}")
# Give the path of the temp file to the zip outputstream, it won't try to open it as an archive.
Zip::ZipOutputStream.open(t.path) do |zos|
  some_file_list.each do |file|
    # Create a new entry with some arbitrary name
    zos.put_next_entry("some-funny-name.jpg")
    # Add the contents of the file, don't read the stuff linewise if its binary, instead use direct IO
    zos.print IO.read(file.path)
  end
end
# End of the block  automatically closes the file.
# Send it using the right mime type, with a download window and some nice file name.
send_file t.path, :type => 'application/zip', :disposition => 'attachment', :filename => "some-brilliant-file-name.zip"
# The temp file will be deleted some time...
t.close

Edit: The basename given to Tempfile.new is what the name says: A basename. It doesn’t need to denote a full path. Tempfile creates an arbitrary path for you in the default temporary directory.

Why opinionated software sucks… Not!

20-Dec-07

The best software has a vision. The best software takes sides.

Make Opinionated Software

There’s been a lot of discussion around David Heinemeier Hanssons principle of “opinionated software”. Some people say that “to David, a piece of opinionated software is written in such a way that makes it easy to do things one way and ugly and difficult to do things another way”.

Maybe this approach “with a carrot and a stick” seams odd and old-fashioned, but i think its actually working and because of that, many people don’t like it.

Imagine, you’re using a product you like, you value its creator and especially his opinion and then, something you wanna do, doesn’t work because of obvious arbitrary restriction, you will be pissed for sure.

Certainly not all decisions made with a strong opinion are good, but i think, many of them made boldly had a reason to be done in a definite way and not another, often arising from the same reason you valued the creator of them before.

I’m writing this because i was really pissed when Rails 2.0 came out. It may be sound picky, but in Agile Web Development with Rails” DHH stated: “This fom of attribute accessor looks at the coluumn’s value. It is interpreted as false only if it is the number zero; one of the strings “0″, “f”, “false” or “” (the empty string); a nil; or the constant false. Otherwise it is interpreted as true.”, which means: A convenient method to add a question mark to attribute accessors to query them if they are true or false.

This due to the fact that there is an SQL standard that defines a datatype BOOLEAN but most of the RDBMS do implement them in their own way, most of the time as tinyint constrained to 0 and 1 (Oracle favours “y” and “n” for whatever reason) and so on.

I myself went with “t” or “f” as i can easily memorize it (true and false, obviously, sure, we can argue if i shouldn’t know about 0 being false and all other being true, i do know but the less i need to think about the obvious the more i can concentrate on actually doing something).

So with Rails 2.0 stuff breaks: DHH and the Rails team went with the MySQL approach: 0 and 1 only, 0 is false, 1 is true. This is fine as for example MySQL defines constants like TRUE and FALSE, that although being 1 and 0, conforms to the standard.

As i saw pieces of app failing with the new release (important parts like things being public or not, blocked users and so on), i was angry… “The bad guys destroy my toy” boohoo and i filed this ticket.

After thinking about (and actually changing my enums(‘t’,'f’) to the pseudotype boolean), i think it’s the right decision. As the model existed before i started working with Rails, i didn’t use migrations to generate tables, which is pity as with this decision, i would get the right datatypes in all supported databases. But even though, i like things being conform to a standard most vendors seem to listen to.

Without the Railsteam willingly breaking compatibility and being opinionated, i would have sweared less on the one side but i would have missed the opportunity to do some justified rewriting.

For me it’s ok to be opinionated and a little bit rockstar like, if you got the right reasons. I myself tend to be opinionated enough on my own work but i can defend my decisions most of the time.

It’s not always nice to be corrected and the corrector maybe doesn’t seem like a teamplayer, but i can life with the concept of the “Benevolent Dictator” when the product does the things its expected to do good and isn’t a “creative” mess.

Just in case you haven’t seen the great movie Borat or are totally unaware to irony, i don’t think that software with an opinion and attitude suck.

Close
E-mail It