Archive for the ‘ruby on rails’ Category

Ruby Hash: Convert String keys to Symbols

Wednesday, August 19th, 2009

Ever had a hash which contained strings as keys and needed symbols instead? I do: From a REXML::Document I created a hash. Unfortunately, the keys were all strings and I needed them to be symbols at some other point in the app. Thank god, Ruby is capable of extending any object “on the fly”. So, I wrote a simple extension to Hash, which you might find useful as well:

class Hash
  #take keys of hash and transform those to a symbols
  def self.transform_keys_to_symbols(value)
    return value if not value.is_a?(Hash)
    hash = value.inject({}){|memo,(k,v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
    return hash
  end
end

Usage is:

a = { "key" => 123, "inner_hash" => { "another_key" => "blabla" }}
Hash.transform_keys_to_symbols(a)
#returns
a = { :key => 123, :inner_hash => { :another_key => "blabla" }}

Passenger and OpenID

Thursday, June 25th, 2009

I recently updated Passenger 2.0.6 to 2.2.4 and experienced that openID logins were not working anymore. Seems like it tries to log output to some strange log location.

However: If anyone comes across the same problem: Simply add

OpenID::Util.logger = RAILS_DEFAULT_LOGGER

to your environment.rb file and everything will work like a treat again.

Rails’ to_xml w/ multiple associations.

Thursday, May 14th, 2009

This is a pain and took me about an hour to figure out today.

Rails ActiveRecord instances offer a nice function to render xml:

my_model.to_xml()

This method can be fed with a parameter

:include => []

to have all associations being integrated in the XML tree, just like when calling

my_model = MyModel.find( :include => [association])

But when I tried to use it, it always failed when dealing with nested associations. Until I found out the trick.
This is how it works: Basically you need to build a list of nested hashes.

class Book < ActiveRecord::Base
  has_many :pages
  has_one :o wner
end
class Owner < ActiveRecord::Base
  has_many :books
end
class Page < ActiveRecord::Base
  has_many :spots_of_coffees
  belongs_to :book
end
class SpotOfCoffee < ActiveRecord::Base
  belongs_to :page
end

Now, you want to create a nice XML output containing a book, with its owner, pages and all spots of coffee?
Pretty easy:

  @book = Book.first
  includes = {} # let's build a hash; it's easier to read. At least for me...
  includes[:owner] = {} # owner has no association. So, let's take an empty hash as target
  includes[:pages] = { :include => :spots_of_coffee } #load pages and include its spots of coffee

  respond_to do |format|
    format.xml  { render :text => @book.to_xml(:include => includes) }
  end

Rails: Custom image sizes using has_attachment

Wednesday, April 15th, 2009

Ever wanted to offer users the possibility to define their very own image sizes for uploaded pictures?

Here’s how you can quickly do that.

You will have a class acting as a an attachment such as:

class AssetImage < ActiveRecord::Base

has_attachment :content_type => :image,
:storage => :file_system,
:max_size => 1.megabytes,
:path_prefix => "public/system/images/#{ActiveRecord::Base.configurations[RAILS_ENV]['domain_name']}/assets/images",
:thumbnails => { :large => '450>', :normal => '300', :medium => '200', :thumbnail => [100,75] },
:processor => :rmagick

validates_as_attachment

...

First add a cattr_accessor to store your custom size in the class:

cattr_accessor :custom_size

Now, add a before_save callback which takes the assigned value and adds it to the list of requested image sizes. It is using the value as a minimum size while keeping aspect ratio.

#store custom size
def before_save
  return if AssetImage.custom_size.nil?
  attachment_options[:thumbnails][:custom] = AssetImage.custom_size.to_s + ">"
end

In your controller do something like this: Take a value from your parameters and store it in your model to have your image customized.

def add_image
  AssetImage.custom_size = params[:custom_size].to_i.to_s rescue "100"
  a = AssetImage.new(params[:asset_image])
  a.save
  redirect_to :action => :index
end

Use ar_mailer for future mail delivery

Wednesday, December 10th, 2008

This little howto describes, how to use ar_mailer to schedule emails for future delivery.

Sorry for the bad formatting. Might change it someday…
1) Migrate emails table and add needed fields

  add_column :emails, :various, :string #holds classname and id of any object you want to
  add_column :emails, :type, :string #type of email
  add_column :emails, :date_to_send, :date #date to send that mail

2) Override “def find” in email.rb

  def self.find(*args)
    with_scope(:find=>{ :conditions=>["(date_to_send IS NULL OR date_to_send <= ?)", Date.today] }) do
      super(*args)
    end
  end

3) Create DelayedEmail class, inherting Email

  class DelayedEmail < Email
    before_save :set_due_date
    before_save :set_various_field
    @@days_before_sending = 21 #default is 3 weeks after creation of various object
    @@various_class = nil

    def set_due_date
      self.date_to_send = Date.today + @@days_before_sending
    end

    def set_various_field
      name = @@various_class.class.to_s + "_" rescue "NOCLASS_"
      id = @@various_class.id.to_s rescue "0"
      self.various = name + id
    end

    #tell email to which object it belongs. might be important for future deletion of unsent mails
    def self.set_various_class(c)
      @@various_class = c
    end

    #tell email how many days shall pass by before sending email
    def self.set_days(days)
      @@days_before_sending = days
    end
  end

4) Modify ar_mailer standard emailer class in controller before delivery and set it back afterwards

  DelayedEmail.set_days(10)
  DelayedEmail.set_various_class(SOMEOBJECT)
  ActionMailer::ARMailer.email_class=DelayedEmail
  #Let any mailer send an email through this class
  #eg. Mailer.deliver_mymail(...)
  ActionMailer::ARMailer.email_class=Email