Saturday, November 3, 2012

Rainbows! + client_max_body_size

Rainbows! kills requests that exceed it's client_max_body_size silently, like a ninja. You won't even know it happened.

Turns out Rainbows had a configuration option called client_max_body_size that defaulted to 1 MB The option is documented here

If this options is on, Rainbows will kill large requests silently. You might not know it's breaking unless you run something in front of it.

Rainbows! do
  # let nginx handle max body size
  client_max_body_size nil 
end

Wednesday, May 30, 2012

Redis Memory Usage Benchmark

So it seems that in Redis (2.4.17):

  1. Shortening keys an overkill in most cases
  2. Shortening hash keys should be done (whenever memory usage is a concern)

Here are the tests I ran:

Key -> Value

SET 8 mil 17 character keys to '1':

(1000000...9000000).each {|i| r.set "1234567890:#{i}", 1} used_memory_human:614.40M

SET 8 mil 6 character keys to '1':

(1000000...9000000).each {|i| r.set "#{i}", 1} used_memory_human:492.02M

For normal keys: reducing the key length by 65% resulted in 20% reduction of memory usage

Key -> Hash -> Value

hash_max_zipmap_entries:512

8,000 hashes with 500 15-character keys with value '1' each (4 mil values in total):

(1000...9000).each { |i| (1000...1500).each { |j| r.hset("h:#{i}", "0123456789:#{j}", 1) } } used_memory_human:74.38M

8,000 hashes with 500 4-character keys with value '1' each (4 mil values in total):

(1000...9000).each { |i| (1000...1500).each { |j| r.hset("h:#{i}", "#{j}", 1) } } used_memory_human:29.73M
75% hash key length reduction lead to 57% memory usage reduction

Thursday, May 24, 2012

CarrierWave image dimensions

Unfortunately, CarrierWave does not support storing image data (such as width and height) out of the box
There are various solutions online, such as this (StackOverflow), that seem to work at first, but they break on form re-submit (e.g. when validations fail)
Another solution is posted in CarrierWave's Google group that gets much closer to a working solution:

before :cache, :capture_size_before_cache 
before :retrieve_from_cache, :capture_size_after_retrieve_from_cache  
def capture_size_before_cache(new_file) 
  model.header_width, model.header_height = `identify -format "%wx%h" #{new_file.path}`.split(/x/) 
end 
def capture_size_after_retrieve_from_cache(cache_name) 
  model.header_width, model.header_height = `identify -format "%wx%h" #...@file.path}`.split(/x/) 
end 

Fixing the bugs (before -> after) and code smell in the solution (using identify directly), we get a working version:

# Somewhere in your uploader:
  attr_reader :geometry
  after :cache, :capture_size
  after :retrieve_from_cache, :capture_size
  def capture_size(*args)    
    img = ::MiniMagick::Image::read(File.binread(@file.file))
    @geometry = [img[:width], img[:height]]
  end

Wednesday, February 1, 2012

rails_email_preview

I've written a gem rails_email_preview that allows you to visually preview your emails in Rails 3.
Head over to GitHub repo for details.

Some screenshots:

S25BKS9X655H

Wednesday, January 25, 2012

Mac OS X + Ruby 1.9.x with RVM + Passenger

Installing Passenger + Ruby 1.9.2 with RVM on Mac is a huge pain. Luckily, here are the commands you need to run to make it happen. It even gives you Passenger Pane to manage your apps. You'll need XCode installed for this to work. This does work for Lion too.
cd ~

# Install brew and a few useful packages
/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)" 
brew update
brew install git mysql libiconv
brew link libiconv

# Install RVM
bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )
echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile
source ~/.bash_profile

# Install Ruby (this is for 1.9.2. As of the time of writing 1.9.3 has issues with ruby-debug19 gem)
rvm pkg install readline
rvm pkg install iconv
rvm install 1.9.2 --with-readline-dir=$rvm_path/usr --with-iconv-dir=$rvm_path/usr
rvm use ruby-1.9.2 --default

# Now it's passenger's turn
gem install passenger
passenger-install-apache2-module # takes a while, grab a coffee
echo "Listen 9010" | sudo tee --append /etc/apache2/httpd.conf
echo "LoadModule passenger_module $(passenger-config  --root)/ext/apache2/mod_passenger.so" | sudo tee -a /etc/apache2/httpd.conf
echo "PassengerRoot $(passenger-config  --root)" | sudo tee -a /etc/apache2/httpd.conf
echo "PassengerRuby $(which ruby)" | sudo tee -a/etc/apache2/httpd.conf
gem install ppane
echo "Include /etc/apache2/passenger_pane_vhosts/*.conf" | sudo tee -a /etc/apache2/httpd.conf
sudo apachectl restart

# Let's also install node.js. Needed for coffee-script
git clone git://github.com/ry/node.git
cd node
./configure
make
sudo make install
Here is how you add a webapp to passenger (let's say it's in ~/Gitspace/my-rails-app) First run this code, which tells Passenger to run your webapp in the context of its own gemset
cd ~/Gitspace/my-rails-app
rvm --gemset --create "1.9.2@my-rails-app"
echo "config/setup_load_paths.rb" >> .git/info/exclude
curl "https://gist.github.com/1676966" > config/setup_load_paths.rb # Read more on http://beginrescueend.com/integration/passenger/
Then go to Passenger Pane in System Preferences, and drag your app onto the list. Then click restart. Done!

Saturday, June 11, 2011

Easy DOM building with DOMBrew

I wrote a nifty library to simplify building DOM in JavaScript. It goes something like this:

# DOMBrew builds DOM from a css like selector and a hash of attributes.
# Normally you would alias it to a short variable
$b = DOMBrew
 
# Construct an element like this:
$b('span#the-span.classy', 'Hello World').asDOM()
# => <span id="the-span" class="classy">Hello World</span>
 
# Or like this:
$b('ul#container', html: 'My <b>awesome</b> list')
  .append(
    $b 'li#first', 'one'
    $b 'li', 'two'
    $b 'li', text: "three", data: { stuff: 'abc', more: 'def' }
    $b 'text', 'That is all'                                    
  ).asDOM()
 
# <ul id=​"container">​
#   My <b>​awesome​</b>​ list
#   <li id="first">​one​</li>​
#   <li>​two​</li>​
#   <li data-stuff=​"abc" data-more=​"def">
#     ​three
#   ​</li>​
#   That is all
# </ul>​
 
 
# To get html as a string:
$b('#container').html() 
# => "<div id="container"></div>"
 
# .append(...)           # => append children
# .dom()  or .asDOM()    # => result as DOM
# .html() or .asHTML()   # => result as html in a string

You can get DOMBrew at https://github.com/glebm/DOMBrew
DOMBrew is written in CoffeeScript - a language that compiles to JavaScript (learn more)

Sunday, May 29, 2011

to_spreadsheet: gem for integrated Rails xls rendering

to_spreadsheet is a gem that lets you render xls from your existing
haml/erb views from Rails (>= 3.0).
related: see rendering xlsx from rails 3

Installation

Add it to your Gemfile:

gem 'to_spreadsheet'

Usage

In your controller:

class MyThingiesController < ApplicationController
    respond_to :xls, :html
 
    def index
        @my_thingies = MyThingie.all
        respond_with @my_thingies
    end
end
In your view partial:

# _my_thingie.haml
%table
  %caption My thingies
  %thead
    %tr
      %td ID
      %td Name
  %tbody
    - my_thingies.each do |thingie|
      %tr
        %td.number= thingie.id
        %td= thingie.name
  %tfoot
    %tr
      %td(colspan="2") #{my_thingies.length}
In your index.xls.haml:

= render 'my_thingies', :my_thingies => @my_thingies
In your index.html.haml:

= link_to 'Download XLS', my_thingies_url(:format => :xls)
= render 'my_thingies', :my_thingies => @my_thingies