Extend Paperclip 2.3.1.1 to retrieve attachment’s dimensions in Rails 2.3.8

In my previous post, I mentioned a way of validating the size of the attachment and I did warn readers that it is a monkey-patch and it needs serious refactoring.

Big refactoring is a result of several small ones and that’s exactly what I have done here. I have extracted the code out and it looks much neater now. As a result, I have reduced my technical debt and in the process gained a better understanding of Paperclip.

Objective:
1. Make code reusable, probably extract it out and then use Ruby’s magic to add it straight to Paperclip. So, that next time I can just use it without doing all the extra work.
2. Make the existing code neat.
3. To retrieve the width and height of the attachment.

This is where we left the code last time:

class Dummy < ActiveRecord::Base
   has_attached_file :photo
   def validate
     temp_file = photo.queued_for_write[:original] #get the file that is being uploaded
     dimensions = Paperclip::Geometry.from_file(temp_file)
     if (dimensions.width > desired_width) || (dimensions.height > desired_height)
        errors.add("photo_size", "must be image size #{desired_width}x#{desired_height}.")
     end
   end
   def desired_height
      # retrieve it from a different model
   end
   def desired_width
     # retrieve it from a different model
   end

Above, I have used @queued_for_write to retrieve the attachment object but if this code changes in Paperclip and I am using it in, let us say 5 different places, I will have to fix it in all those places. I just don’t want to come back and change the code every time Paperclip changes anything. Therefore, this is plain wrong. Similarly, if the Paperclip::Geometry’s API changes, I will have to endure the same painful process yet again. Therefore, it becomes essential to extract this code out into its own module and then it’s all in one place. Right, so I created a module, as below:

module Paperclip
  module Dimension

    # calculates width using processor
    def width_of name
      return 0 unless path(name)
      Geometry.from_file(path(name)).width.to_i
    end

    # calculates height using processor
    def height_of name
      return 0 unless path(name)
      Geometry.from_file(path(name)).height.to_i
    end

    #path to the attachment
    def path name
      attachment_for(name).queued_for_write[:original]
    end

  end
end

I am instead using a method attachment_for within Paperclip. For example, if you have code like:

has_attached_file :photo

then, you can do something like:

attachment_for :photo

to get hold of the attachment object. But bear in mind, has_attached_file is a class method where as attachment_for is an instance method (I can’t really explain the difference here but the fact is they can’t be used in a similar way). This allows me to change the code in my model to something like this:

class Dummy < ActiveRecord::Base
   has_attached_file :photo
   def validate
     if width_of(:photo) > desired_width || height_of(:photo) > desired_height
        errors.add("photo_size", "must be image size #{desired_width}x#{desired_height}.")
     end
   end
   def desired_height
      # retrieve it from a different model
   end
   def desired_width
     # retrieve it from a different model
   end

Wow! That is clean and has the same syntax as has_attached_file. I don’t need to indulge in Paperclip’s nitty gritty. It’s in a different module. But you can’t run this code just yet. Because, it’s not hooked into my application yet. If you do run it, you should see NoMethodError on width_of. In order to hook this code up into our application, add the module to a file, let us name it, paperclip.rb and put it inside app_root/config/initializers/.

So, when the app starts up it will load this file but this module is nothing without the support of actual Paperclip. Therefore, to hook it into Paperclip, we will add the following line at the bottom and the module in it’s final state would look something like this:

module Paperclip
  module Dimension

    # calculates width using processor
    def width_of name
      return 0 unless path(name)
      Geometry.from_file(path(name)).width.to_i
    end

    # calculates height using processor
    def height_of name
      return 0 unless path(name)
      Geometry.from_file(path(name)).height.to_i
    end

    #path to the attachment
    def path name
      attachment_for(name).queued_for_write[:original]
    end

  end
end

Paperclip::InstanceMethods.send(:include, Paperclip::Dimension)

I am including our custom module inside Paperclip::InstanceMethods, which gets called when has_attached_file is invoked from within the model class and ensures all the instance methods are available to our model class.

Now, if you run it. It will work like a charm. By just adding this code to your initializers you can simply retrieve the width and height of the attachment. Here’s the gist of the file, if you need it.

I hope it helps and for anyone interested, please go ahead and create a validator for dimensions just like we have one for size.

Posted in Programming | Tagged , , | Leave a comment

Paperclip 2.3.1.1 and validate attachment’s dimension

Paperclip is an easy-to-use file upload gem released by Thoughtbot. It provides several validations out of the box like validation on size (as in file size) of the attachment, if the attachment is present or not and so on. However, it does not have any validations on the dimensions of the file because sometimes you have to ensure the file is the correct dimension to avoid any design disasters.

To validate dimensions, one needs two things, namely:

1) Dimension of the file that is being uploaded and
2) Dimensions to validate against or the desired dimensions

One tutorial, I found particular useful in my quest is by Roberto Soares. It doesn’t exactly touch on the topic I am talking about here but it does point me in the right direction. He gives code to calculate the dimension of the file being uploaded, in case you would like to save the height and width of the file in the model.

How is my problem different from Roberto’s?
1) I do not want to save the height and width of the file in the database.
2) My desired dimensions are already stored in a different model, i.e. I do not need to define it explicitly like other validations, such as validates_length_of :name, :within => 2..10

So, it is a particular sort of situation. Without any more discussions, here’s the code:

class Dummy < ActiveRecord::Base

   has_attached_file :photo

   def validate
     temp_file = photo.queued_for_write[:original] #get the file that is being uploaded
     dimensions = Paperclip::Geometry.from_file(temp_file)
     if (dimensions.width > desired_width) || (dimensions.height > desired_height)
        errors.add("photo_size", "must be image size #{desired_width}x#{desired_height}.")
     end
   end

   def desired_height
      # retrieve it from a different model
   end

   def desired_width
     # retrieve it from a different model
   end

Key thing to note, is the @queued_for_write attribute that is defined on Attachment class within Paperclip. This keeps hold of the original file that is being uploaded, which I then retrieve to get the dimensions. I simply compare the height and width in the validate method that gets called before save. This does work but it’s a dirty hack and is impossible to reuse. Therefore, I am going to try and extract it out and make it more reusable. Stay tuned for it.

Please Note: This is just a monkey-patch and will increase your technical debt. Use it carefully, with an intention to refactor it as soon as possible.

Posted in Programming | Tagged , , | Leave a comment

Bundler 0.9.25, Rails 2.3.8 and vendor/gems has no specification file error

Majority of us have seen this dreadful error after running rake:gems:unpack or a similar command to register gems with the application.

config.gem: Unpacked gem clearance-0.8.8 in vendor/gems has no specification file. Run 'rake gems:refresh_specs' to fix this.

I was under the impression that this happens due to some bug in rails gem dependency management and never expected the same to happen with Bundler. Unfortunately, I was wrong. Just yesterday, I had this error and it’s one of those things that doesn’t really stop your app from working but is very annoying.

There is a fix on Stack Overflow but it might not work if you are using bundler. The command to use remains the same but it needs to be slightly modified. Here are the steps:

This assumes that you get this error with clearance.
Step 1

cd vendor/gems/clearance-0.8.8

Step 2

gem specification ../../cache/clearance-0.8.8.gem > .specification

With bundler the gem files are actually stored in the vendor/cache directory. specification is a gem command that extracts a .specification file from gem file. Here’s full explanation of it.

That’s it. Wasn’t that easy?

Posted in Programming | Tagged , , , | 1 Comment

When Safari 5′s Reader feature fails

Safari 5 is awesome and one thing that sticks out is the Reader feature. It basically identifies some pages, for example: news pages on BBC, and allows user to produce a better, enhanced page with same text but much readable than before and devoid of any ads or pictures. But it fails badly here. If you are on Safari 5 and click the link and then click the READER button in the address bar, you’ll agree with me.

Well, apart from one odd example, I think it works brilliant. I just thought I will point it out that it should identify the code blocks with pre or code tags and format them accordingly.

Posted in Programming | Tagged , | Leave a comment

Jquery, JSSpec and an elegant timepicker

Time selection via web has always been those grey areas where one does not really find good solutions. With the advent of Jquery, there are plethora of date selection javascripts out there. However, time selection has not received similar treatment. After much searching I found two solutions. The first one was very fancy but depended heavily on the Jquery Widget and with custom ui stuff rewritten in recent versions, it simply did not work with newest custom-ui scripts.

The second solution was simple and straight-forward but laden with bugs. However, a bit of hacking yielded a good working solution but it was still lacking something important and essential i.e. Tests. After a bit of googling, I zeroed on JSSpec as the testing framework. QUnit is at par with JSSpec but I quite like the RSpec way of testing and JSSpec emulates that for javascript.

Testing is hard. No doubts about it. But testing javascript is a completely different ball game. It is easy and quite fun, once you get the hang of it but initially it is just confusing. I had problems in setting up the test cases, ensuring the requisites are there. For example: if we are testing weather an input box slides out on a link click then we need to make sure that an input box is there. I know it sounds easy but when it comes down to writing specs, mind goes completely blank. Anyways, here’s the link to jquery-timepicker. Go and check it out.

Posted in Javascript, Programming | Tagged , , | Leave a comment

Bundler and Rails 2.3.8

Life as a “Rails Developer” is a pretty smooth sailing and Bundler just makes it ever better. Rails 2.3.8 is the newest version from the Rails team. Don’t get me wrong, I love to hack my way through Rails3 but time is a big constraint for the project I am working on at the moment. With Rails3, I am bound to hit some road blocks but this is just a quick project I would like to get done and over with.

I am not going to talk about how to initialise bunder and create a Gemfile. You can follow that easily from the github page. So, for the sake of this post let us assume that this is what your Gemfile looks like:

source :gemcutter

gem "rails",          "2.3.8"
gem "i18n"
gem "formtastic",     "0.9.1"

group :test do
  gem "rspec",        "1.3.0"
  gem "rspec-rails",  "1.3.2"
end

Now, when you run the following command:

bundler install vendor

you will get an error related to ruby_version_check.rb not found or something similar complaining about the absence of ruby_version_check ruby script. To fix it, just create a file named ruby_version_check.rb in your project’s lib folder and copy the contents of this gist in it. I copied this file from the Rails master branch and changed the min_release variable to 1.8.6 since 1.8.7 gave me some nasty segmentation errors on my mac. But if you have 1.8.7 installed and working correctly, change the min_release back to 1.8.7.

Next, step is to add the following code to your environment.rb file:

require "bundler"
Bundler.setup

and you should be good to go.

However, there are two things that I have not yet figured out a solution to:

  1. Using Rails engines like clearance since the generators have been completely revamped in Rails3 and might need a through investigation. So, for now I have added it in the old way using config.gem syntax in the environment.rb file.
  2. I got some strange error on time zone which went away by commenting out config.time_zone = ‘UTC’ line of code. This still needs investigation.

I hope this helps anyone trying to get their head around using Bundler with Rails 2.3.8. This set up might work with pervious versions and if you will try it out for yourself.

Update: Just tried using rspec_controller generator and it can’t be invoked as well. So, using Bundler does not hook up the generators. Although. I would think that should not be the case since Bundler adds all the required libs to the $LOAD_PATH so that they are found when Rails goes looking for them. Needs more investigation.

Posted in Programming | Tagged , | 7 Comments

Quote of the day

“If Rails is a smart organised businessman then PHP is a homeless bum.” – an individual who spent hours looking at old skool PHP code

Posted in Quote | Tagged | Leave a comment

Pecking order

From Bob Sutton’s blog

.

Posted in General | Tagged | Leave a comment

Quote of the day

Taken from StackOverflow podcast #85, and it is beautiful.

“Even very smart people have to be in the right place at the right time to be successful. The best success strategy is probably dogged, bullheaded persistence, because there are so many variables that you can’t control or even predict.”

Very nice.

Posted in Quote | Tagged | Leave a comment

Virtues of a Bad Programmer

In contrast to my earlier post.

I am no expert to judge people based on their programming skills. I am a n00b myself. However, I do try and move away from the n00b junction all the time. Anyways, these are the virtues of a bad programmer:

  • Laziness – OMG, who the hell is going to refactor this? Let us just duplicate code all over the shop. I can’t be asked to go over the old code and ensure it can accommodate new business requirements. I will just leave it like this and let someone else worry about it.
  • Impatience – I am not going to google for 2 hours to find a solution to the problem. Neither will I spend time looking through this open source code to fix the issue. Instead, I will cut corners and try and make a business case out of it. I have no patience to upgrade to the latest version since that breaks my existing code and I am not bothered to look through the issues and fix them.
  • Hubris – Clean code? It works man! I do not care if it’s a nightmare for the next guy, my job is to get it to work. It works and that’s what matters.
Posted in Programming | Tagged , | Leave a comment