1.18.2008

Fixing attachment_fu on Windows

Like many others, I've encountered issue when developing Rails applications using attachment_fu on Windows. After doing some research, I've come up with the following solution to the problem.

The problem has two parts :
  1. Size is not included in the list error message,
  2. Timeout error when uploading to S3.

Fixing "Size is not included in the list" error message

Some people have reported that there is a timing issue, when trying to get the file size, with Tempfile on Windows. It seems that the size of the file is not properly reported by Windows after writing data to it. Proposed solutions for this problem include :
  1. Sleeping in a loop as long as the file size is 0,
  2. Reading back the entire file in memory.

I think I found a better and less patchy solution for this issue: forcing the OS to flush the file to disk before reading it's size.
Here is the code to do it :
require 'tempfile'
class Tempfile
def size
if @tmpfile
@tmpfile.fsync # added this line
@tmpfile.flush
@tmpfile.stat.size
else
0
end
end
end

Doing a flush is not enough... flush will flush the Ruby buffer but the file may not be immediately written to the disk by the OS. Doing the fsync ensure that the file is written to disk by the OS before continuing. After that, Windows will properly report the actual file size.

Fixing the Timeout error when uploading to S3

This issue is related to opening files for reading on Windows. On Windows, you have to open the file in binary mode. So patching attachment_fu is simple :
require 'technoweenie/attachment_fu/backends/s3_backend'
module Technoweenie
module AttachmentFu
module Backends
module S3Backend
protected
def save_to_storage
if save_attachment?
S3Object.store(
full_filename,
(temp_path ? File.open(temp_path, "rb") : temp_data), # added , "rb"
bucket_name,
:content_type => content_type,
:access => attachment_options[:s3_access]
)
end

@old_filename = nil
true
end
end
end
end
end

I've also included a fix from someone else (which was not enough in itself to solve my S3 upload problem):
module Technoweenie
module AttachmentFu
# Gets the data from the latest temp file. This will read the file into memory.
def temp_data
if save_attachment?
f = File.new( temp_path )
f.binmode
return f.read
else
return nil
end
end
end
end

Wrapping it up

So I put all this code in lib/attachment_fu_patch.rb and required it in environment.rb.

Problem fixed!

Note, I did not test it on other OSes, but these fixes should not have any adverse effects.

11.15.2007

Loading ActionMailer SMTP settings from the database


Here is a simple way to load your SMTP settings from the database instead of having to call ActionMailer::Base.smtp_settings= in environment.rb, development.rb or production.rb. Using the database to store these setting, while a bit slower, allow one to easily allow an administration to change the configuration without having to restart the Rails application.

The first approach I used was to just override the self.smtp_settings method in my ActionMailer::Base subclass. But that didn't work due to cattr_accessor not working as expected. So I had to also add class_inheritable_accessor :smtp_settings.

So I did the following :
class StuffNotifier < ActionMailer::Base
class_inheritable_accessor :smtp_settings

def self.smtp_settings
Hash.new {|h, k|
Preference["smtp.#{k.to_s}"]
}
end
end
I use Hash.new with a Proc because the stuff the Preference[] method access the database, if I construct an Hash with all the value loaded from the database, then the Hash will be constructed many times as ActionMailer::Base access self.smtp_settings many times when sending an email. Using an empty hash with a default Proc solved my problem. I also prepend "smtp." so it retrieves the "smtp.host" value from my system preference table.

If somebody knows of a better way to do it, feel free to add a comment or a link.




Storing System Preferences in the Database

Here is a simple way to store system preferences in the database for a Ruby on Rails application. This simple solution offers no caching, so accessing the same preference many times will result in many database request. Suggestions for per request caching are welcomed.

Using the code bellow, one can access preferences like this :
admin_email= Preference["admin_email"]
First, let's start with a migration that will create a table in which we can store many types of preferences :
class CreatePreferences < ActiveRecord::Migration
def self.up
create_table :preferences do |t|
t.column :setting, :string
t.column :type, :string
t.column :value, :string
end
end

def self.down
drop_table :preferences
end
end
Now, we create the model. Because we want to be able to get many types of preferences (and eventually validate their value in different ways), we create a base Preference class and many subclasses for all the types of preferences we want to have. Some subclass will override the value and value= methods to convert the value to the proper type :
class Preference < ActiveRecord::Base
validates_presence_of :setting

def self.[](setting)
setting= self.find_by_setting(setting)
setting.nil? ? nil : setting.value
end
end

class UrlPreference < Preference
end

class EmailPreference < Preference
end

class StringPreference < Preference
end

class IntegerPreference < Preference
def value()
self[:value].nil? ? nil : self[:value].to_i
end

def value=(v)
self[:value]= v.nil? ? nil : v.to_s
end
end
Note that we could add validation for the value attribute in UrlPreference and EmailPreference to validate the format of the value.

9.27.2007

More on the continuous tax

Cedric post on the continuous tax triggered what it seems as another debate between dynamically types language versus statically typed language. This funny thing that even if Java and Ruby are not always mentioned, it seems as this debate is more about who prefers to code in Java or who prefer to code in Ruby.

Eric Burke took my comment (calling it bullshit) on Cedric post to talk a bit on how he did pay this so-called tax once or twice.

While I do not really appreciate it when someone compare what I say with shit, the debate has a bit too much flames for me to add oil to it.

I find this debate to be much like flame wars that the young Java community endured with the C/C++ community a not so long time ago. Much of the debate revolve around one particular aspect of the newcomer without any substantial facts. In the early days of Java, it was performance (which was partly true, but even when we got Hotspot, naysayer were still saying that Java was interpreted). Now with the rise of dynamic language (yes I'm thinking Ruby but also Javascript), we have the Java dinosaurs arguing that you cannot do re-factoring or code completion with these language because they are dynamically typed. But these things can now be done with the latest IDEs. Maybe not to the extent that we can achieve with Java IDE but it is evolving rapidly.

Today dynamic language naysayer are no better that yesterday Java naysayers, they hold onto the same argument over and over without clearly substantiating them (I'm sorry when you loose 5 minutes, because a method does not really return the type you are expecting while acknowledging that you still have some miles to put behind you using the language, does not count as paying a continuous tax).

I use both Java and Ruby on a daily basis. While sometimes I may miss the righteousness of a statically typed language, I cannot say that I pay a higher tax when I use Ruby. Nor I can say that I pay a lower tax when programming in Java and having to write useless catch statements, having to write a freaking Comparator to sort object based on multiple attributes, having to check for nulls all the time (yes I know about the Null object idiom) or waiting for my application server to restart.

Both type of language have distinct advantages and inconvenients. I try to keep an open mind about it and use the best language/platform for the task at hand.

6.11.2007

No dev kit for Apple iPhone

It looks like Apple choose the only way they know to squash software developer complains about the iPhone... Marketing spin!

When the iPhone was first announced, Steve Jobs told everyone that the platform was closed to software development because he does not want 3rd party to crash your (or soon to be) shiny iPhone. Rightfully, software developer complained about this. Today, you can install new applications (J2ME) on most cell phones. It made no sense to have a phone as advanced as the iPhone not allowing one to install new applications.

So I guess, that Apple felt a bit of steam and needed to fix the situation. So we will be able to develop application for the iPhone... But Apple will still not gives any dev kit to do so. No, you will have to... write web applications! Yeah! The iPhone comes with Safari, so you can write web application (putting in a bit of Ajax would be better in Steve Jobs perspective) that will "run" on the iPhone. As we did not already knew that! I guess the greatest feature in the iPhone would be for the user to add a shortcut to your application so he can have a quick access to it. How innovative!

If only they were announcing that Google Gears was bundled so you can run you apps offline (or cache data on the iPhone to stay responsive), they it would be a nice announcement. I know that Apple can make any product look great, thanks to their marketing department, but this is really underwhelming! I'm sure they can do better than that!


AdSense Links