Whoa! Rhino on Rails and Google's Corporate Culture
Monit for Your Uptime
One of the shocking things about going into production is to go on a little break or even get away from the house only to gasp in horror as you stare at a blank 404 where your earth shaking rails app should be when you get back online.
Integrating stuff like memcached into your rails app is mostly trivial thanks to the work of some wonderful rails hackers but keeping this moving parts moving is a task in itself. What’s the use of having a killer app that only works on your dev box right?
Naturally, the good thing about development is that everyone else has been there and done that. The other day I discovered monit thanks to igvita.com.
Sometimes a variety of bugs in plugins or your code can bring your web app down to its knees. This doesn’t even include those moments when your hosting service goes down for whatever reason whether it be maintenance or trouble. Smart developers (that’s you) will already have crucial parts of their service automatically restart along with any hardware restarting either by setting their chkconfig or whatever their system uses. Most of the libraries that we depend on are mostly reliable most of the time like the MySQL server that we only need to worry about start up initialization (although even this depends on how hard you push it).
Unfortunately, for most beginners, rails still continues to be a dream to develop in and a nightmare to deploy unless you know what you’re doing. Also random application crashes will increase the more moving parts you have such as memcached, mongrel, and acts_as_ferret.
Enter Monit
Monit is a wonderful little tool to ensure that your little web app keeps chugging along happily. It’ll monitor whatever you tell it to and ensure that they continue to run under the conditions you specify.
Using it with mongrel_cluster will ensure that users of your web app will be served a proper web page even as one of the other mongrels get culled for misbehaving.
In order to fully take advantage of mongrel_cluster and monit, you need to grab the mongrel_cluster prerelease. You can of course, use monit to serve as your cluster management but as Ezra notes, mongrel_cluster does a better job of keeping memory consumption in check as well as dealing with orphaned pids (only prerelease) that has given anyone that deploys rails a headache or two.
I can also vouch for the fact that monit has a slew of cool features to help you really keep the system humming along nicely. Remember memcached? Well, as much as it provides performace boosts to your app, it also introduces a potential headache. You see, when used in conjunction with mongrel you need to be careful to ensure that memcached is running before your mongrel starts. Otherwise you get weird behavior such as your flash[:notices] not showing up.
So, suppose your memcached crashes but mongrel stays up. Even if memcached got restarted, you’re only half way there since you need to restart mongrel immediately after that. Fortunately, this is trivial with monit because you can simply set a hierarchy by stipulating a depends on condition. This will ensure that whatever is being depended on will be restarted by monit first before the other processes get started.
set daemon 60
set init
set mailserver localhost
set mail-format { from: monit@monit.com }
set alert who@gmail.com
set httpd port 2812 and
use address localhost
allow localhost
check process mongrel_8000
with pidfile /var/run/mongrel_cluster/mongrel.pid
start program = "/usr/local/bin/mongrel_rails cluster::start -C /root/inbited.com/current/config/mongrel_cluster.yml --clean --only 8000"
stop program = "/usr/local/bin/mongrel_rails cluster::stop -C /root/current/config/mongrel_cluster.yml --clean --only 8000"
if totalmem is greater than 60.0 MB for 5 cycles then restart
if cpu is greater than 50% for 2 cycles then alert
if cpu is greater than 80% for 3 cycles then restart
if loadavg(5min) greater than 10 for 8 cycles then restart
if 3 restarts within 5 cycles then timeout
depends on memcached
if failed port 8000 protocol http
with timeout 10 seconds
then restart
group mongrel
check process mongrel_8001
with pidfile /var/run/mongrel_cluster/mongrel.pid
start program = "/usr/local/bin/mongrel_rails cluster::start -C /root/inbited.com/current/config/mongrel_cluster.yml --clean --only 8001"
stop program = "/usr/local/bin/mongrel_rails cluster::stop -C /root/current/config/mongrel_cluster.yml --clean --only 8001"
if totalmem is greater than 60.0 MB for 5 cycles then restart
if cpu is greater than 50% for 2 cycles then alert
if cpu is greater than 80% for 3 cycles then restart
if loadavg(5min) greater than 10 for 8 cycles then restart
if 3 restarts within 5 cycles then timeout
depends on memcached
if failed port 8001 protocol http
with timeout 10 seconds
then restart
group mongrel
check process mongrel_8002
with pidfile /var/run/mongrel_cluster/mongrel.pid
start program = "/usr/local/bin/mongrel_rails cluster::start -C /root/inbited.com/current/config/mongrel_cluster.yml --clean --only 8002"
stop program = "/usr/local/bin/mongrel_rails cluster::stop -C /root/current/config/mongrel_cluster.yml --clean --only 8002"
if totalmem is greater than 60.0 MB for 5 cycles then restart
if cpu is greater than 50% for 2 cycles then alert
if cpu is greater than 80% for 3 cycles then restart
if loadavg(5min) greater than 10 for 8 cycles then restart
if 3 restarts within 5 cycles then timeout
depends on memcached
if failed port 8002 protocol http
with timeout 10 seconds
then restart
group mongrel
check process memcached
with pidfile /tmp/memcached.pid
start program = "/etc/init.d/memcached start"
stop program = "/etc/init.d/memcached stop"
if failed host 127.0.0.1 port 11211 then restart
This sample config is based on igvita and Ezra’s. As you can see there’s a “depends on memcached” call. What this does is set a hierarchy so if memcached gets restarted, mongrels will get restarted afterwards. On initialization, memcached will get started first so your web app can function properly.
I plan on tweaking this so that I can be extra sure to have essential services like mysql and nginx humming along properly as well (although I’ve yet to see these go out, the load on my service is currently close to nil).
I’ve only touched on the tip of the iceberg really and I just think I might even check out how to use this to keep rogue apps from eating up too many resources on my mac.
Now, apps should never ever be crashing to start but things happen and rails will always have its kinks until well it’s… enterprise ready™. But hey, it’s more important that your app stays humming especially if you have moving parts like memcached or ferret that form an integral part of your service.
Monit will also dutifully email you of any incidences that trigger it. This combined with exception notification can provide you with valuable debugging info.
Monit makes Mongrel play nice! – igvita.com
Mongrel Mongrel Cluster 1.0.1.1 Prerelease: Healing power of the pack!
Rails Time Zone Guide for Beginners
private
def set_timezone
if logged_in? && !current_user.time_zone.nil?
TzTime.zone = current_user.tz
else
TzTime.zone = TZInfo::Timezone.new(ENV['TZ'])
end
yield
TzTime.reset!
end
This takes advantage of the fact that I have this in environment.rb:
config.active_record.default_timezone = :utc ENV['TZ'] = 'America/Los_Angeles'You can of course, replace "ENV['TZ']" in the filter above with a valid time zone string such as 'America/Los_Angeles'. You always need to have a valid TZInfo::Timezone object when setting the time zone which is why we convert the string to a time zone object by calling TZInfo::Timezone.new("the_time_zone_string") otherwise you get funky errors like "TZInfo::Timezone constructed directly" and a mess of others. For my views I always format the date time into something more user friendly using strftime so I have this in one of my modules to convert the time in views.
def tz_convert
TzTime.zone.nil? ? self : TzTime.zone.utc_to_local(self.utc)
end
def full_date
self.tz_convert.strftime("%m/%d/%Y %H:%M")
end
def short_date
self.tz_convert.strftime("%m/%d %H:%M")
end
The tz_convert method is written so it wont throw an error if the time zone isn't set.
Like I said, this is an incomplete guide so you definitely want to read up on the other blogs out there:
caboose adding timezone to your rails app
Peter Marklund's Home : Rails Recipe: A Timezone Aware Datetime Picker
View Helpers Make Strings
def nested_comments(comments) ul(comments) do |item| member_thumb(User.find(item["user_id"])) + link_to(item["title"], :controller => 'communities', :action => 'show_topic', :id => item["id"]) + "The bottom version is more readable in a larger sense but top is way more concise. They use one of the available ruby concatenation methods." + item["content"] + "
" end end def nested_comments(comments) ul(comments) do |item| string = "" string << member_thumb(User.find(item["user_id"])) string << link_to(item["title"], :controller => 'communities', :action => 'show_topic', :id => item["id"]) string << "" string << item["content"] string << "
" string end end
Has_many_polymorphs for Real Polymorphism
UPDATE: Evan has updated has_many_polypmorphs so that child and parent can access each other both ways. For the average user, this means just use it like you normally would.
One of the things about being a rails beginner is that you don’t know whether you can’t do something efficiently because you don’t know how to do it or because Rails just doesn’t support it.
I think polymorphic associations as they currently stand are pretty limited or at least you could say they impose limitations. Just read Josh Susser’s blog for various examples.
That’s how I ended up arriving on the has\_many\_polymorphs doorstep. If polymorphic associations are the flexible joints of associations has\_many\_polymorphs takes it to a new level with joints that can flex 360 degrees in every direction.
It was hard to find friendly documentation suitable for a beginner but it really is simply once you have it. My example is probably one of the most basic uses but it’ll have to do. Check the documentation on Evan Weaver’s blog or fish through the plugin directory for examples within the tests(it’s pretty well-documented).
In my application I have Pictures and I have a bunch of other models that will use pictures like Profiles or Communities. This is easy to understand. One way to do it is documented in the wiki How to Use Polymorphic Associations. This document is a bit old so I’m not sure if it’s still the most efficient way of doing it but you end up with two models solely for tying together two different models. You need a Folder model and Linkings model. Not only that you have to set up all the association statements correctly and specify callbacks and other stuff to make them useful.
With has\_many\_polymorphs you’ll get all this for free.
It’s really easy with examples so here we go:
class Picture < ActiveRecord::Base
has_many_polymorphs :consumers, :from => [:profiles, :communities], :through => :picture_consumers
class PictureConsumer < ActiveRecord::Base
belongs_to :picture
belongs_to :consumer, :polymorphic => true
class Community < ActiveRecord::Base
This is what the PictureConsumer scheme looks like:
create_table "picture_consumers", :force => true do |t|
t.column "picture_id", :integer
t.column "consumer_id", :integer
t.column "consumer_type", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
As you can see, the Community class (and neither does the profile class) require any association to be declared. This is because has\_many\_polymorphs takes care of it for us. Really, it’s that simple. With those you can now do:
@some_community.pictures
or
@some_picture.communities
You can associate pictures with as many models that you would like. Has\_many\_polymorphs also allows you to create joins that are polymorphic on both sides. Less configuration and more flexibility is certainly a plus.
For me these include the below. For application.rb:
require 'app/models/picture'
class ApplicationController < ActionController::Base
(update: no longer needed since update, see link below or note at the very top)
This ensures the associations get loaded properly even when used in the console under development or for rendering in views.
UPDATE: Don’t do this either. It may unload some model methods after the second request. If you don’t know what that means you’re safe not putting the above in your environment file. Everything works fine out of the box thanks to Evan’s hard work.
The entirety of the information here was ripped out of Evan’s blog so please check it out and send him your regards. It’s marvellous work that I’m surprised is not already part of Rails.
has_many_polymorphs activerecord plugin :: evan weaver
has_many :through: The other side of polymorphic :through associations
Back from the Abyss: A Helper to Create Nested Lists
Sorry for not writing in a while. I've been busy doing other stuff and on top of that DreamHost has been real brutal to this Mephisto blog. My blog was inaccessible for a long long time and there really wasn't anything I could do. Support requests simply came back as "can't do anything for third party software". Oh well, I'm not complaining but it's a shame. I'm using WordPress for another blog and I've got to say it's a pleasure to use a mature blog with lots of plugins to meet every need. I'm going to be tool agnostic from now on. That's not to say I want to do PHP. Mephisto is great for seeing a top class rails coder in action.
So anyways, as lot as DreamHost lets me run this blog, I'll try to write something up.
Today, I came across a great snippet you can add in your helpers to iterate over html lists, you know the typical ol ul elements.
Which lets you write: <%= ul @pages.map { |x| link_to_page(x) } %>
Well, this is a great time saver and way better than hand-coding it yourself but I wanted something that could produce a nested list from an array of arrays. Now my code isn't the most elegant but it works. It uses recursion to produce a nested list (although the spacing and newlines aren't included so the source looks garbled in real life).
So this in your views: <%= ol ["first", "first", ["third", ["second", "second", "second"], "third", "third", ["second", "second", "second"]]] %>
Becomes:
- first
- first
- third
- second
- second
- second
- third
- third
- second
- second
- second
Here's the snippet:
def html_list(type, elements, options = {})
items = elements.map do |element|
if element.is_a?(Array)
element = html_list(type, element, options = {})
else
content_tag("li", element)
end
end
content_tag(type, items, options)
end
Haven't tried it for something nested deeper than two elements so mileage may vary. What can you use it for? I was thinking nested comments (will have to figure that one out).
