Blog Archives

Adding additional processing support to CarrierWave

Posted in Code, Featured, Inside TFG, Ruby on Rails, Tips and Tricks, Websites or Tools

CarrierWave is a great gem for adding image uploading and basic processing abilities to your Rails applications. By default there is no way to set the quality of the resized images, which could be a very useful feature.

One of the applications that we’re currently building has a requirement to resize and compress images to produce smaller file sizes, as well as stripping out any personal information that may be stored in the uploaded images.

Out of the box CarrierWave provides a consistent interface to process images using RMagick, MiniMagick or ImageScience. Resizing and cropping is supported for all three image processing engines but setting the quality or removing personal data is not supported. Thankfully, CarrierWave provides an easy way to extend the default functionality so we can do more.

For the examples I’ll be adding extra functionality to RMagick processing. If you’re using MiniMagick or ImageScience the methods will need to be altered to work correctly. We’ll start by adding a new initializer into our application.

Can’t see this Gist? View it on Github!

You may be wondering about the fix_exif_rotation method. Well, some modern cameras always take photos upright, in portrait. When you take a photo in landscape mode the photo is actually saved as a portrait, but the photo will contain some extra orientation metadata. When we remove the embedded data in the photo this value gets removed, so as a result we need to manually rotate the photo, which is what this method does.

The application server will need to be restarted before the new initializer can be used. Once restarted the new filters can be used in an uploader like so:

Can’t see this Gist? View it on Github!

That’s it! That’s all that’s needed to add extra quality and processing functionality to carrierwave.

Accessing ActiveRecord and ActiveModel Callback Chains in Rails 3

Posted in Code, Ruby on Rails, Tips and Tricks

It seems that access to the ActiveRecord callback chain has changed recently, however the documentation still contains the following :

To list the methods and procs registered with a particular callback, append _callback_chain to the callback name that you wish to list and send that to your class from the Rails console.

This used to work in Rails 2.3.x and I’d used it a few times before, however trying to use it in Rails 3 gave me problems :


HourEntry.before_save_callback_chain
NoMethodError: undefined method `before_save_callback_chain' for #

After some digging around ActiveRecord then ActiveModel and ActiveSupport I figured I could just access the _#{type}_callbacks method and then reject and select these arrays to get what was required. This is what I came up with to get the before_save callbacks that I was interested in.


HourEntry._save_callbacks.select { |callback| callback.kind.eql?(:before) }

This returns me the array of callback objects that are chained before I call save on the HourEntry model. If I want to check for a particular proc being in that chain then I can do :


HourEntry._save_callbacks.select { |callback| callback.kind.eql?(:after) }.collect(&:filter).include?(:archive_if_no_users)

It’s not optimal chaining a number of enumerable methods but I think it seems pretty readable.

I am hoping that the callback_chain methods return at some point (though I only used them sparingly) but more than that I’m hoping someone might be able to set me straight on how I’m going about getting around the callback chains on ActiveRecord and ActiveModel.

Broken Arrow Keys – Ubuntu 10.10 / VMWare Fusion / Macbook Pro

Posted in Inside TFG, Tips and Tricks

After installing Ubuntu 10.10 (Maverick Meerkat) in a VMWare Fusion virtual machine on a MacBook Pro, I noticed the arrow keys were broken in Ubuntu. It turned out that the Fusion install wizard had configured the keyboard to use evdev, which did not work correctly inside the VM.

The solution is to reconfigure the keyboard by running the following command:

sudo dpkg-reconfigure console-setup

To navigate the setup program without the arrow keys, I pressed the first letter of the desired option (eg. “a” for Apple Laptop), then pressed the right command key to cycle downwards through the list.

The configuration options that I chose were:

Apple Laptop for keyboard
USA as origin of keyboard
USA as keyboard layout
No AltGr
No Compose key
Enter for the rest of the defaults.

The arrow keys work fine now.

A brief introduction to the RVM Ruby API

Posted in Code, Ruby on Rails, Tips and Tricks

As part of the work done on RVM during the Ruby Summer of Code, One of the things I worked to add was a fairly comprehensive Ruby version of the bash API.

Since then, the Ruby API has been used to build a couple of small projects and tools but it still maintains relatively undocumented.

Today, I’m going to introduce the basics of working with the Ruby API, starting by introducing the fundamental concepts you need to know and then going on to cover how the most common example of it’s use (rvm gemset support in passenger) works under the hood.

Getting started

First off, you’re going to want to make sure you actually have the Ruby API installed. If you have a remotely recent (e.g. newer than 6 or so months) copy of rvm, you’ll already have a copy – Otherwise, you’re going to need to update your rvm install using rvm update.

To load the API, open up irb and type the following:

# First, find where rvm is installed
rvm_path = File.expand_path(ENV['rvm_path'] || '~/.rvm')
# Secondly, add the ruby library to the load path
$LOAD_PATH.unshift File.join(rvm_path, 'lib')
# Finally, actually load rvm
require 'rvm'

Once said code has been executed, you should have access to RVM constant in your irb session. The method we use to require it means that it is loaded from the currently installed version of rvm so you don’t have to deal with issues related to different versions between rvm itself and the Ruby API.

The bare basics

The ruby api works on the concept of a rvm ‘environment’ – Essentially, you can think of them as the equivalent of a shell instance – They keep track of instance variables, have their own ruby / gemset selection and are sandboxed (for the most part) from other environments. Creating a new environment is as simple as doing the following in your ruby code:

env = RVM.environment 'ree'

Or, alternatively, you can pass a block and it will be called with the environment as an argument. Along side this, you have RVM.environments, which accepts multiple ruby names and calls the block with each environment.

For convenience sake, RVM exposes RVM.current, an environment that defaults to the currently loaded gemset (e.g. if you open irb in rbx@rails3, it will be an environment using rbx@rails3).

As an added bonus, when you call an undefined method on RVM, we automatically use method missing to call it on RVM.current – Hence, If RVM::Environment#some_instance_method exists, you can use RVM.some_instance_method to call it in the current environment object.

Working with environments

So, now that you know how to get environment object, you need to know how to use it. In the simplest form, the Ruby API mirrors the command line program by translating:

rvm command action arg1 arg2

Into:

env.command_action arg1, arg2

With the library automatically taking care of converting arguments (e.g. a hash as the last value will be converted to arguments to the program).

As an example, to call rvm list strings, One would use env.list_strings. Likewise, rvm use becomes env.use and so on and so forth. In these cases, use switches the environments ruby – not the running ruby itself.

One of the added features in the Ruby API is that use and gemset_use have alternatives with a bang (use! and gemset_use!) that will switch the currently loaded ruby’s gemset (by changing GEM_PATH and GEM_HOME and then telling rubygems about the change) - raising an exception if it’s for a different ruby installation. This switch makes it possibly to dynamically switch your ruby applications gemset whilst running.

Of course, dealing with a direct port of the API isn’t always nice when you’re writing a lot of code env.alias_list feels clunky compared to most ruby code and some of the tools are rough around the edges. With this in mind, the ruby api offers wrappers for situations where this becomes noticeable, e.g env.aliases will return a more ruby-like wrapper for the alias command that lets you do things such as env.alias.all, env.aliases.create and so on. In some cases (e.g. env.list_*), the wrappers add features (such as expanding strings) that aren’t found in the bash API via the wrapper.

A real world example

With that said and done, it’s time to look at a real world example. In this case, the most commonly-used example of the rvm ruby API – A use of passengers support for a config/setup_load_paths.rb file which lets passenger automatically load .rvmrc files. When a compatible ruby is detected, the file will automatically switch the gemset on the fly – In essence, letting passenger work with the one-gemset-per-application philosophy we advocate when dealing with rvm.

For references sake, the rvm portion of the code is:

if ENV['MY_RUBY_HOME'] && ENV['MY_RUBY_HOME'].include?('rvm')
  begin
    rvm_path     = File.dirname(File.dirname(ENV['MY_RUBY_HOME']))
    rvm_lib_path = File.join(rvm_path, 'lib')
    $LOAD_PATH.unshift rvm_lib_path
    require 'rvm'
    RVM.use_from_path! File.dirname(File.dirname(__FILE__))
  rescue LoadError
    # RVM is unavailable at this point.
    raise "RVM ruby lib is currently unavailable."
  end
end

Line by line, this code essentially:

  1. Checks that we’re loading it in an RVM ruby – rvm always sets the MY_RUBY_HOME environment variable when using wrappers and we ensure that “rvm” is part of said variables value.
  2. It begins a begin-rescue-end section of code to load rvm – Giving us a nice error message if rvm isn’t available.
  3. Based on my ruby home, We find the location of your rvm install (as rvm_path may or may not be set at this point).
  4. We add the rvm ruby lib directory to the load path
  5. We require the API

Up until this point, it is almost exactly like our original example. Where it differs is the next line:

RVM.use_from_path! File.dirname(File.dirname(__FILE__))

Knowing what I wrote about earlier, we know this is the same as:

RVM.current.use_from_path! File.dirname(File.dirname(__FILE__))

Which, if we look under the hood, does:

RVM.current.use! RVM.current.tools.path_identifier(path)

Expanding this again using what we learnt earlier, we know that RVM.current.use! will take a given ruby string (e.g. ree@rails3) and attempt to switch out the gemset when the ruby versions match. Continuing on, we know RVM.current.tools.path_identifier is roughly the same as RVM.current.tools_path_identifier – a line which takes a path and, taking into account .rvmrc files, returns the identifier (e.g. ree@rails3) we’d get if we had changed into the given directory from the command line.

Putting it all together, the ruby api essentially just finds out which identifier the given path should use (by loading a projects .rvmrc in the given environment), checks they’re for the same ruby install (to avoid issues with binary gems) and finally switches out the GEM_HOME and GEM_PATH variables for the current process, letting your application use a different gemset.

In short, under the hood it simple finds out the gemset details and updates your application to switch them out similar to how rvm would from the command line.

More examples

We’ve only covered the basics of of the ruby api – For the most part, everything implemented in the bash API is supposed to be (except where it lags behind in terms of updates) exposed via the Ruby API in a simple, consistent manner. In practice, this has primarily been used for things like infinity_test and various CI-related work which make working with multiple rubies easier from ruby applications.

I’d ultimately love to see it used more for tools built around rvm – e.g. a web interface for managing a servers system wide install and exposing it over HTTP. Lastly, because it’s written in Ruby, if you’ve ever wanted to contribute to rvm but have felt apprehensive due to the fact it’s written in shell script, the rvm ruby API is a good place to start.

Twitter Panel

Posted in Industry Trends, Inside TFG, Tips and Tricks

Here at The Frontier Group, we have a team of designers and developers who are very good. Great, even. Fantastic. Superb. Phenomenal. Insert other synonyms for “awesome” here.

Even so, we recognize that we don’t (and can’t) know everything. Our web design and development focus is specific. In areas outside our expertise, we could always stand to learn and grow and we do every day.

This is where you come in. We’re going strong with connections here in Australia, and we’d like to reach out to our friends in the U.S. We’re looking for a few good web designers and developers to connect and share knowledge with. Okay, more than just a few – we’re looking for 50. One from each of the United States. We’d eventually like to connect with one (or several) designers and developers from every country, but we thought we’d start with one and work our way up from there.

Why are we doing this, you ask? Besides making international connections, we’d like to formulate a monthly “Ask The Experts” panel on Twitter. Once a month at a scheduled time, our panel will convene and discuss a given topic – everything from the latest Ruby gem to HTML5. Other Twitter users will be encouraged to come and hang out, learn, and contribute or “ask the experts” for advice. We’ll have our very own hashtag so the panel’s posts will be easy to locate.

We’re hoping it will be a great resource for both us and the web community.

So what do you say? Will you join us in building the Twitter panel to end all Twitter panels? A hashtag that means people actually increase their knowledge? And if you’re not a resident of the U.S., no worries. Remember, we’re aiming to connect to everywhere in the world eventually.

Express your interest in the comments, or on our Twitter.

Twitter

The latest @rubyfive podcast is up, our own @sj26 receiving a mention for Ruby 1.9.3 performance improvements. http://t.co/hfx3EPMz

@frontiergroup about 4 days ago #

Search Posts

Featured Posts

Categories

Archives

View more archives