Rails

Create your own CarrierWave processors

If you need to manage images in a rails application, I can only recommand CarrierWave.

That library will allow you to easily manage files upload. But also post processing on images, such as changing their sizes.

I use CarrierWave on this blog even though you don't see it yet. Every category and every article has an image. That image has several different versions which have different sizes.

For one of those versions, I which to have rounded corners. You can see the result in the image at the left.

Let's see in details how I got that.

 

Post processing CarrierWave

Let's imagine the following uploader :

class CategoryUploader < CarrierWave::Uploader::Base
    include CarrierWave::RMagick
    version :thumb do
        process :resize_to_fill => [248, 163]
    end
end

We have a category uploader which will create a "thumb" version of the image. That image will have a size of 248 * 163 pixels.

You can find the processor "resize_to_fill" which will change the image's size at lib/carrierwave/processing/rmagick.rb.

In order to create our rounded corners, we'll create our own processor. I've placed it at lib/dmathieu/carrier_wave/round.rb.

unless defined? Magick 
    begin
        require 'rmagick'
    rescue LoadError
        require 'RMagick'
    rescue LoadError
        puts "WARNING: Failed to require rmagick, image processing may fail!"
    end
end

module Dmathieu
    module CarrierWave
        module Round

            module ClassMethods
                def rounded_corner(radius = 10)
                    process :rounded_corner => [radius]
                end
            end

            ##
            # Makes the image's corners round
            #
            #
            # === Parameters
            #
            # [radius (#to_s)] the corner radius
            #
            # === Yields
            #
            # [Magick::Image] additional manipulations to perform
            #
            def rounded_corner(radius = 10)
                #
                # See above for this method's code
                #
            end
        end
    end

Then, in your uploader, you can include that newly created processor.

class CategoryUploader < CarrierWave::Uploader::Base
    include CarrierWave::RMagick
    include Dmathieu::CarrierWave::Round

    version :thumb do
        process :resize_to_fill => [248, 163]
        process :rounded_corner
    end
end

You can see that, in our module, we create a method rounded_corner which will do the job of adding the rounded corners in our image. That action will be automatically called by CarrierWave when generating that version of the image.

Rounding up the corners

In order to manipulate our image, we use rmagick.

We're gonna do the following :

  • Create a new image of the same size than the previous which will contain a rectangle with rounded corners.
  • Put that image above the previous one.

Quite easy no ? ;-)

def rounded_corner(radius = 10)
    manipulate! do |img|
        masq = ::Magick::Image.new(img.columns, img.rows).matte_floodfill(1, 1)         
        ::Magick::Draw.new.
            fill('transparent').
            stroke('black').
            stroke_width(1).
            roundrectangle(0, 0, img.columns - 1, img.rows - 1, radius, radius).
            draw(masq)

        img.composite!(masq, 0, 0, Magick::LightenCompositeOp)
        img = yield(img) if block_given?
        img
    end
end

We've just defined the rounded_corner method, which is located in the module provided earlier.

The manipulate! method is located in the rmagick.rb processor and gives us the image, allowing us to update it and to transmit it back to the next processor.

masq = ::Magick::Image.new(img.columns, img.rows).matte_floodfill(1, 1)

We create here a new image of the same size than the previous. But which contains nothing.

::Magick::Draw.new.
        fill('transparent').
        stroke('black').
        stroke_width(1).
        roundrectangle(0, 0, img.columns - 1, img.rows - 1, radius, radius).
        draw(masq)

We draw in the image we've just created.

fill('transparent').

The rectangle we'll create will have a transparent background.

stroke('black').
stroke_width(1).

That rectangle will have a black border of 1 pixel.

roundrectangle(0, 0, img.columns - 1, img.rows - 1, radius, radius).
draw(masq)

We create here the rectangle and add it to the previously create image.

img.composite!(masq, 0, 0, Magick::LightenCompositeOp)

Finally we add that rectangle image on top of the one which already exists, giving the effet we want.

img = yield(img) if block_given?
img

CarrierWave's processors are nested. Each one of them is executed one after the other. With yield, we give the image to the next processor. It'll then just have to execute it's own modifications.

Finally we return the image which will be saved.

Conclusion

As you can see, CarrierWave allows us to create generic processors so you can get your images exactly as you want them.

I'm actually quite surprise to see there's not a lot of open source processors running wild.

Rails : number of test and code lines

I'm a bit obsessed by my applications statistics. So on many of them, I build tools allowing me to generate graphics with several evolution indicators of theirs datas over time. On RefStats for example, this graphic shows the evolution of the number of positions; the number of users; of websites and the average execution time for the crawler.

Moreover, you probably already know the rails commande rake stats which gives some statistics datas on your application code. Number of code lines, number of test lines, ... The class that builds it is located in the railties. Unfortunately this class isn't abstract at all. It shows directly the datas to the console. Not very cool to enter them to your database.

So I've taken this class to update it and allow you to get those datas in an abstract way and use them in an application :) So let's me introduct you to Rails Code Statistics (yeah my projects names are always very original)

The principle is pretty simple. You instantiate the class :

stats = CodeSearch.new

Then you can the datas as an hash :

p stats.to_h
As a json element :
p stats.to_json

Only the total statistics

p stats.total
Only the detailed statistics
p stats.statistics

And then do whatever you want with them :) For me for example, I have a rake task which is executed automatically once a day and puts the number of code and test lines to my database. Then I only have to manipulate this database to build a graph.

Note : the version above will only work if you've made your tests with RSpec. If you use Shoulda, I let you do it yourself :mrgreen: And if you use Test::Unit, I invite you to try RSpec. You will not want to do anything else after :mrgreen:

Rails : add a validation error

You probably already know (otherwise, I highly invite you to read the Ruby on Rails bases) how easy it is to validate datas with our favorite framework :)

if myDatas.save
    #The datas have been saved
else
    #There's an error. The myDatas.errors contains it.
end

After that, you only need to add some validation parameters for the datas in the model. But now, let's guess others validations cases, a bit particular that doesn't enter in the "normal" case.

You only need to create a new method which will come validate your datas (the example above is completely fake) :

def validates_roxitude(*attribute)
    reg = Regexp.new '/^ruby(.*?)rox$/'
    self.errors.add('rox', 'Hey, Ruby rox. You have to say it !') unless reg.match attribute
end

Here, we declare an error except if the field starts par "ruby" and ends by "rox".

We only need to force the validation with this method for our field and Ruby rox ! ;)

class myModel < ActiveRecord::Base
    validates_roxitude :myField
end