oh hai.

Easy logging with Sinatra

Posted: March 28th, 2009 | Author: Nick | Filed under: sinatra | Tags: , | 3 Comments »

Sinatra doesn’t come with any logging utilities but Rack sure does. When I’m working on a Sinatra app it’s really handy to have logging available and Rack provides multiple levels of logging that you can use in your Sinatra application.

To get a logger set up it is pretty simple. First create a configure block unless you already have one then put this code in your configure block.

configure do 
  Log = Logger.new("sinatra.log") # or log/development.log, whichever you prefer
  Log.level  = Logger::INFO 
  # I'm assuming the other logging levels are debug & error, couldn't find documentation on the different levels though
end

Now whenever you need to debug something in your application or can’t figure out why something isn’t working just send the area in question to your log file.

Log.info "WTF, why isn't this working #{@users.inspect}"


Bare Sinatra App for deploying to Passenger

Posted: March 27th, 2009 | Author: Nick | Filed under: sinatra | Tags: , | No Comments »

I’ve been working on a handful of Sinatra applications recently and I’ve deployed them all using Rack on Passenger. While Sinatra only does require one file to run on your box with mongrel, when it’s using rack with passenger it requires a few more directories and files. It’s pretty much your basic rack directory structure but it’s nice to have a bare app to work. I went ahead and created a bare sinatra application and threw it up on github to make the process faster of creating a new sinatra application.

Here’s the details of the sinatra skeleton application
.gitignore - ignore .swp and other temp files

*~
*.swp
config/db.rb


Capfile
- this is needed for capistrano to deploy. This is put here by running capify . in the directory of the application

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
load 'config/deploy'


app.rb
- The core app file. The file just includes a require to rubygems and sinatra which is all that is needed and a route or two.

require 'rubygems'
require 'sinatra'

config.ru - this is an important file since this is what rack looks at to figure out how to run your application properly.

require 'rubygems'
require 'sinatra'
require 'app.rb'
path = '' # set the root path of your app here. e.g. /var/www/username/somesite
 
set :root, path
set :views, path + '/views'
set :public, path + '/public'
set :run, false # this line tells mongrel not to run and to let passenger handle the application
set :environment, :production
set :raise_errors, true 
 
# sinatra doesn't have anything built in for logging so you can use the stdout to log to a file
log = File.new("sinatra.log", "a")
STDOUT.reopen(log)
STDERR.reopen(log)
 
# always put this line last so that all of your settings are properly loaded before sinatra is booted up
run Sinatra::Application

config/deploy.rb - contains your deploy recipe. I usually put a database connection file in here as well such as db.rb. When I need activerecord then I just put a require activerecord in my app.rb file and then a load config/db.rb to get my models connected to my database.
config/db.rb

ActiveRecord::Base.establish_connection(
  :adapter => 'mysql',
  :username => 'root',
  :password => '',
  :host => 'localhost',
  :database => 'somedb'
)


models.rb
- I usually put all of my models in here just to organize the code better. Again, this isn’t necessary for sinatra, it’s all up to however you would like to do it.

class Book < < ActiveRecord::Base
  validates_presence_of :title
end

public/images
public/stylesheets
public/javascripts

every application is going to have those three directories, might as well create them by default.

tmp/
in order to restart your sinatra application with passenger you need the tmp directory so that you can run touch tmp/restart.txt to restart your application.

views/
views contains all of my sinatra layout and action files. Sinatra uses layout.haml/erb as your default layout similar to how rails uses the application layout file. Put your sass files in here as well and create a path in your app.rb similar to the following to get your sass to compile to css.

get '/stylesheets/application.sass' do 
  sass :application
end


Ruby server monitoring script

Posted: June 17th, 2008 | Author: Nick | Filed under: ruby | Tags: , | No Comments »

I wrote this script as a basic fix to monitor if certain sites are up. The script needs to be on a different server than where the sites exist for the best results and obviously with a cron job running every 5-10 minutes or more often if needed.

The ruby server monitoring script could also be expanded to ssh into another box and run the command needed to boot the server back up.

First let’s go over what the db looks like…
Sites

  • id
  • domain (url to curl)
  • active (turn monitoring on/off for sites)

Reports

  • id
  • sent_at (time when report was sent)
  • down (count of how many sites were down)
  • fixed (report has been marked as fixed)

Ruby monitoring script

#!/usr/bin/ruby
#change line above to reference your ruby location
 
require 'rubygems'
require 'net/smtp'
require 'mysql'
require 'time'
require 'curb'
 
def check
     @time = nil
 
    # Connect to db that stores all of our sites to monitor
     db = Mysql::new("localhost","user","pass","database")
 
	# Make sure we are only looking at sites that are active
    sites = db.query("SELECT * FROM sites WHERE active = 1")
	# Getting the most recent report since we only want to run the script to send a report every 2 hours
    last_report = db.query("SELECT * FROM reports WHERE fixed = 0 ORDER BY sent_at DESC Limit 0,1")
    last_report.each {|r| @time = r[1]}
		# If there isn't a report in the database yet it sets the time to greater than 2 hours so the script will run and report if needed.
        if @time.nil?
         @diff = 7201
        else
		# Calculating the difference in time
         @report = Time.parse(@time)
         @now = Time.now
         @diff = @now - @report
        end
			# Change this number to whatever you woudl prefer in seconds. Currently it is 2 hours
        unless @diff.to_i < 7200
            body = ""
            sites_down = 0
 
            sites.each do |s|
				# Prints the site name to the user
                print s[1]
				# need a begin/rescue in case the domain can't be curled
                begin
				# Uses curb to curl the domain
                 c = Curl::Easy.perform(s[1])
                puts " - #{c.response_code}"
 
			   # check http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for examples of response codes
                unless c.response_code < 399
					# reports to the screen when manually ran that the site is down
                      puts "--DOWN--"
					  #increments the number of sites down and writes to the body of the email
                      sites_down = sites_down + 1
                      body = body + "DOWN - #{s[1]}\n"
               end
 
                rescue
				# if the script is unable to curl the site we still mark it as down since we want to make sure we are in good shape
                    sites_down = sites_down + 1
                    body = body + s[1] + " unable to curl, check record in db and ensure that it is being pointed\n"
                        puts "--DOWN--"
                end
 
                #only send if sites are down
 
            end
 
            if sites_down &gt; 0
                #send text and email
                record_report = db.query("INSERT INTO reports(down) VALUES(#{sites_down})")
				# I use this one for a text message.
                send_email("A server needs help! #{sites_down} down!","destination_address")
				# this is for an inbox since it provides more details to aid in troubleshooting later
                send_email(body,"myemail@blah.com")
            end
         else
			# Since there is still a report that is in the db that was recorded less than 2 hours ago we just want to notify the user if running it from shell.
            puts "Please run 'up.rb' first and then 'mon.rb' again to curl all sites."
            puts "You are seeing this message because there is a report that hasn't been marked as fixed within the past 2 hours."
         end
end
 
def send_email(body,to)
    msg = <
To: Admin 
Subject: Some sites are down
#{body}
END_OF_MESSAGE
 
    Net::SMTP.start('localhost') do |smtp|
        smtp.send_message msg, "from_address", to
    end
end
#need to run the function
check

Script to update reports table so regular monitoring can continue (up.rb)

#!/usr/bin/ruby
 
require 'mysql'
 
db = Mysql::new("localhost","user","pass","database")
up = db.query("UPDATE reports SET fixed = 1 WHERE fixed = 0")
 
puts "Reports marked as fixed, resuming normal monitoring."

You should also set up a crontab such as….

This one runs every 5 minutes.

MAILTO=”"
*/5 * * * * sh -c $’/usr/bin/ruby /home/username/mon.rb’

An alternative to a home made script is to use a 3rd party service such as Rails based server monitoring site Scout


PHP to Ruby on Rails Translations

Posted: April 20th, 2008 | Author: Nick | Filed under: php, ruby | Tags: , , , | No Comments »

I used to develop primarily in PHP and dabbled into ASP but then I learned about Ruby and the Rails framework and now I cringe when I need to develop something new that isn’t in Ruby. There are a ton of tutorials on Rails so if you aren’t familiar or want to learn just do some googling. My intention with this post is to compare the differences in chunks of common code between php and ruby. Items such as for loops, array manipulation in ruby, various string functions in ruby and php, etc. I obviously won’t write everything so if you think of something that would be handy to know then leave a comment with it in there. Yes I realize PHP has frameworks that add some of the handy features that Rails does.

Create Array:
Ruby:

myArray = ["dog","cat","mouse"]

PHP:

$myArray = array("dog","cat","mouse")

*With Ruby there’s no need to specify that it’s an array, it knows from the brackets. Also notice that PHP uses parentheses and Ruby uses brackets.

Using element of array by position

Ruby:

myArray[1] = "cat"

PHP:

$myArray(1) = "cat"

First Elements:

Ruby:

myArray.first = "dog"

PHP:

first($myArray) = "dog"

Ruby:

myArray.first(2) = ["dog","cat"]

PHP:

for ($i=0; $i < 1; $i++ ){$first2elements .= myArray($i);}

* If there is a better way to do this in PHP let me know if it doesn’t involved a loop.

Print out elements of Array

Ruby:

 myArray.inspect

PHP:

printr($myArray)

Comments

Ruby:

# COMMENT

*Ruby doesn’t have multiline comments at the moment, just get a good editor that has a shortcut for it such as Textmate or vim with the vim NERDCommenter plugin.

PHP:

// SINGLE LINE COMMENT
/* MULTILINE COMMENT */

View embed Tags
Ruby:

< %=%>

PHP: (Short version), (Long Version)

< ?php= ?>
< ?= ?>

*Both languages support putting an equal sign after the opening to print out what is in the tag. In Ruby if something isn’t meant to be printed out such as the opening loop tag then it will get mad at you since there’s nothing to print in that portion.

A quick note on Ruby before I go into the string section. You can call quite a few methods on arrays and strings that permanately change the current object you are working on. The Exclamation point is what tells Ruby to keep the changes you made to it or if you leave it off it remains the same when you call it again. For instance you can do…

String Manipulation

Ruby:

s = "hello world"

PHP:

$s = "hello world"
s.capitalize => "Hello World"
ucwords($s) = "Hello World"
s.capitalize! = "Hello World" *This keeps the string Capitalized
s.capitalize = "Hello World"

*The string is capitalized but only for this call on the capitalize method, if you want you can store it in a new variable to keep the capitalization.

Other good things to know about Ruby

Form Validation
Ruby:

validates_presence_of :animal_name #This goes in the definition of the model such as 'animals', going into detail about how models work is beyond the scope of this tutorial. Other handy ones you can use in rails are...
validates_acceptance_of #Good to use this for checkboxes.
validates_format_of #Can use regular expressions to validate fields such as email address.
validates_length_of :animal_name, :maximum = 20 #A max of 20 characters is aloud
validates_length_of :animal_name, :within= 5..20 #Needs to be within 5 to 20 characters
validates_numericality_of :feet #Makes sure that the user specifies a number for amount of feet
validates_presence_of :animal_name #Make sure animal name isn't blank
validates_uniqueness_of :animal_name #Make sure you only have one entry for 'dog' as an animal name

PHP: There are several PHP frameworks that have different ways of doing similar validation techniques try, CakePHP is a very popular one.

Link Creation

< %= link_to "link name" "/direction/for/link" %>
< %= javascript_include_tag "myJSFile" %>
< %= stylesheet_link_tag "myCSSFile" %>

A great resource for PHP to rails/ruby translations is http://railsforphp.com