From the category archives:

Ruby on Rails

Using a Rails collection partial with a counter

October 30, 2009

No Gravatar

I was reworking an application today and noticed there was a portion of the code that was rendering a counter.

# what I saw
<% @wheels.each_with_index do |wheel,count| %>
  <%= render :partial => "wheel", :locals => { :wheel => wheel, :count => count} %>
<% end %>
 
# you might also be doing
<% count =0 %>
<% @wheels.each do |wheel| %>
  <%= render :partial => "wheel", :locals => { :wheel => wheel, :count => count} %>
  <%= count += 1 %>
<% end %>

Instead of using a block and rendering the partial each time, let’s try something different. We’ll use:

<%= render :partial => "wheel", :collection => @wheels %>

This will allow us to iterate through each item in our @wheels collection. Rails will make each element available to the wheel partial using the “wheel” variable. If our partial was “book” our variable would be “book” as well. If you want to change the name of the variable, you can do this:

<%= render :partial => "wheel", :collection => @wheels, :as => "dinosaur" %>

Now that we’re rendering a collection, we have a helper available to us inside of the partial that returns the index. For example:

# in our view	
<%= render :partial => "wheel", :collection => @wheels %>
 
# in our wheel partial
# returns index
<%= wheel_count %>
 
# another example:
# in our view	
<%= render :partial => "page", :collection => @pages %>
 
# our page partial
# returns index
<%= page_count %>

{ View Comments }

Multiple database connections with Rails

October 11, 2009

No Gravatar

Recently a client requsted that I add a new admnistration panel to an existing Rails application. I thought to myself, “That sounds simple enough?”; welll, things are never as simple. In addition to this control panel, they wanted to use an existing MySQL database and its contents which, of course, did not follow the Rails naming conventions. Anyhow, after some research here is what I came up with.

Our first stop is in the database.yml file; the file should look something like this:

# database.yml 
development:
  adapter: mysql
  encoding: utf8
  username: root
  password: 
  database: example_development

This shouldn’t be a suprise to you. The database specified above is the default database that all of our Active Record models will use; if we want to add another connection we will need to start by speficing the connection. This connection can be another MySQL, PostgreSQL, or even an Oracle connection.

# database.yml 
development:
  adapter: mysql
  encoding: utf8
  username: root
  password: 
  database: rails_development
 
# here we specify new connection
pixel_development:
  adapter: mysql
  encoding: utf8
  username: legacy_username
  password: legacy_password
  database: legacy_database

Now that we have the connections specified, we need to move to our model; it is in here where we redirect the request to a non-standard connection. I have created a new model called “Pixel” which will interface with a database used for tracking pixels.

# app/models/pixel.rb
class Pixel < ActiveRecord::Base
end

Unless we specificy another database connection, Rails will use the default enviroments database; in this case “development”. Let’s change this by adding the following line:

# this will use our "pixel_development" database
class Pixel < ActiveRecord::Base
  establish_connection :pixel_development
end
 
 
# but let's make this more dynamic so we can use 
# develpoment, production, and test databases
class Pixel < ActiveRecord::Base
  establish_connection :pixel_#{RAILS_ENV}
end

Now we should be good to go! Any requests to the Pixel model will connect to the second database. Say the table name is set in stone and cannot be modified? No problem.

# this will use our "pixel_development" database
class Pixel < ActiveRecord::Base
  establish_connection :pixel_#{RAILS_ENV}
  set_table_name "your_table_name_here"
end

{ View Comments }

Watermarking images paperclip’s post-processor

April 8, 2009

No Gravatar

For those of you who wanted to use thoughtbot’s paperclip plugin to manage image uploads but were having trouble watermarking said images, behold!

http://github.com/ng/paperclip-watermarking-app/tree/master

{ View Comments }

Dynamically set a domain for a Rails asset host

March 14, 2009

No Gravatar

I’ve been wanting to implement an asset host for my Rails app, looking over the API I saw that Rails’ asset_host only supported a single domain. I’m running multiple domains off one Rails app, so this would have been a problem. After a little research, I came up with a solution that would work for me.

I run this as a before_filter in my Application Controller.

# for my use, @site.domain returns the user's current
# domain. you should change this to whatever method
# you use to get the domain
 
class ApplicationController < ActionController::Base
before_filter :find_asset_host
 
private
 
  def find_asset_host
    ActionController::Base.asset_host = Proc.new { |source|
        asset_hosts = %w{icarus zeus aphrodite etc etc}
 
        # disable asset host for development 
        if is_development?
          ""
        else
          "http://#{asset_hosts[rand(4)]}.#{@site.domain}"
        end
      }    
  end
 
end

{ View Comments }

Simple non-model check box properties

March 6, 2009

No Gravatar

Need to pass a property that isn’t associated with a model? I did. It took me a few minutes, but the solution is pretty easy.

Instead of passing the instance variable your form is using, try this.

<% form_for [:admin, @post]} do |f| %>
  <%= f.error_messages %>
 
	<p>
		<%= f.label :title %>
		<%= f.text_field :title %>
	</p>
 
	<p>
		<%= label(:skip_queue, "Skip queue") %>	
		<%= check_box("overrides", "skip_queue") %>	
	</p>
 
	<p>
		<%= f.submit "Submit" %>
	</p>
<% end %>

You’d access the property of the check box like so:

# log
Processing Admin::PostsController#create (for 127.0.0.1 at 2009-03-06 03:03:01) [POST]
  Parameters: {"commit"=>"Update", "post"=>{"title"=>"Title", "overrides"=>{"skip_queue"=>"1"}}
 
# access the property
params[:overrides][:skip_queue]

{ View Comments }

Paperclip deletes images with empty file fields update with rails 2.3

March 5, 2009

No Gravatar

Bahhh! When you update a model that contains an empty file field in Rails 2.3 the attachment is deleted. It looks like empty file fields get passed as nil and then Paperclip gets angry.

Processing Admin::PostsController#update (for 127.0.0.1 at 2009-03-05 01:21:08) [PUT]
  Parameters: {"commit"=>"Update", "post"=>{"title"=>"Post Title", "image"=>nil}, "id"=>"124"}

There are two quick monkey patches that I’ve been using, the first, was splitting off attachments to their own method (edit_attachment)…but this quickly became a nuisance. A quicker work around is to remove the attachment field, in this case “image”, from the parameters.

 
# PUT /posts/1
# PUT /posts/1.xml
def update
  @post = Post.find(params[:id])
 
  # delete the offending parameter
  params[:post].delete(:image) if params[:post][:image].nil?
 
  respond_to do |format|
    if @post.update_attributes(params[:post])
      flash[:notice] = 'Post was successfully updated.'
      format.html { redirect_to(admin_post_url(@post)) }
    else
      format.html { render :action => "edit" }
    end
  end
end

{ View Comments }

Watermark your images in Rails

March 3, 2009

No Gravatar

I’ve been working on a project that requires me to watermark all my images. Here’s a script I put together using rmagick. I plan on releasing a skeleton application that will accept uploaded files and watermark them.

Ideally, I’d like to use paperclip to handle uploading, and write a processor subclass to handle watermarking…unfortunately I’m running into some issues. If anybody wants to lend a hand, I have the public repository up at: http://github.com/ng/paperclip-watermarking-app/tree

require 'RMagick'
 
# load source and watermark images
src = Magick::ImageList.new("source-image.jpg").first
watermark = Magick::Image.read("watermark-image.png").first
 
# create a new, blank image with a height that is the sum of
# the source and watermark
dst = Image.new(src.columns, src.rows+watermark.rows) { self.background_color = 'white' }
 
# place the source image on the blank image
result = dst.composite(src, Magick::NorthGravity, Magick::OverCompositeOp)
 
# place the watermark over the previous transformation
result = result.composite(watermark, Magick::SouthEastGravity, Magick::OverCompositeOp)
 
# write it out
result.write('watermarked-image.jpg')

{ View Comments }

Paperclip not saving your images?

March 2, 2009

No Gravatar

I’ve had a few guys email me asking if I had any idea why Paperclip wasn’t saving their uploaded images. Here’s a form one of them sent me:

<% form_for(@post) do |f| %>
  <%= f.error_messages %>
 
 
    <%= f.label :title %>
    <%= f.text_field :title %>
 
 
 
    <%= f.label :description %>
    <%= f.text_area :description %>
 
 
  <%= f.file_field :image %>
 
 
    <%= f.submit "Update" %>
 
 
<% end %>

Notice anything different on this form?

<% form_for(@post, :html => { :multipart => true }) do |f| %>
  <%= f.error_messages %>
 
 
    <%= f.label :title %>
    <%= f.text_field :title %>
 
 
 
    <%= f.label :description %>
    <%= f.text_area :description %>
 
 
  <%= f.file_field :image %>
 
 
    <%= f.submit "Update" %>
 
 
<% end %>

Simple mistake, but I’ve done it a few times in the early AM and wondered WTF was going on!

:html => { :multipart => true

{ View Comments }

Check if a file exists in Rails

February 21, 2009

No Gravatar

Recently I was porting a legacy PHP application to Rails. One of the primary tasks was to resize a bunch of images. Below you can see what one of the database rows looks like.

mysql> SELECT id, filename FROM old_posts
+----+---------------------------------------+
| id | filename                              |
+----+---------------------------------------+
|  1 | unxplained-photo-1225258423-85706.jpg |
+----+---------------------------------------+
1 row in set (0.00 sec)                                                                   
 
mysql>

You’ll see that the name of the filename is stored in the database, the problem I encountered was that often times the actual file didn’t exist even though the record remained in the database.

Rails would get angry at me when I tried to resize an non-existent file. To account for this, I did the following:

if FileTest.exists?(filename)
 # some code here to
 # resize the image
end

For more information on this handy ruby module pop into the docs.

{ View Comments }

Rails’ symbols: don’t create them dynamically!

February 20, 2009

No Gravatar

Symbols are great, we use them in hashes all the time…but did you know, symbols aren’t part of Rails’ garbage collection? When you create them, the memory the use has been allocated permanently! Why is this bad? So if we create a handful of symobls…no biggie, but what if we’re dynamically creating lots and lots of them? Major faux pas!

This was brought to my attention last night when I was trying to create a hash with some dynamic symbols. See below:

 
## here is some data
 
>> hardware = Hardware.find(:all, :limit => 2)
=> [#<Hardware id: 1, name: "Bolts", description: "Lorem ipsum dolor sit amet, consectetur adipisicing...", photo_front_file_name: "144_replica_wheels_Closeout_30_percent_off_prices_l...", photo_front_content_type: "image/jpeg", photo_front_file_size: 80842, photo_angle_file_name: nil, photo_angle_content_type: nil, photo_angle_file_size: nil, created_at: "2009-02-19 11:04:48", updated_at: "2009-02-20 20:29:46">, #<Hardware id: 2, name: "Nuts", description: "Lorem ipsum dolor sit amet, consectetur adipisicing...", photo_front_file_name: nil, photo_front_content_type: nil, photo_front_file_size: nil, photo_angle_file_name: nil, photo_angle_content_type: nil, photo_angle_file_size: nil, created_at: "2009-02-19 11:04:48", updated_at: "2009-02-20 07:18:32">]
>> y hardware.first
--- !ruby/object:Hardware
attributes:
  name: Bolts
  updated_at: 2009-02-20 20:29:46
  photo_angle_file_size:
  photo_front_file_name: 144_replica_wheels_Closeout_30_percent_off_prices_listed_.jpg
  photo_front_file_size: "80842"
  id: "1"
  description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  photo_angle_content_type:
  photo_front_content_type: image/jpeg
  created_at: 2009-02-19 11:04:48
  photo_angle_file_name:
attributes_cache: {}
 
=> nil
 
## don't do this
for hardware_item in hardware
 formatted[:#{hardware_item.name.downcase}] = hardware_item.description
end
 
>> formatted[:bolts]
=> "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
 
## this is better
for hardware_item in hardware
 formatted[hardware_item.name.downcase] = hardware_item.description
end
 
>> formatted['bolts']
=> "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

{ View Comments }