September 10th, 2008 cschneid
Sinatra provides a helpful pair of special handlers for 404 and 500 errors. By default, Sinatra has an ugly “Not Found” and “Error” message, these allow you to customize them, and render your own pages.
It’s pretty simple:
# Render views/404.haml
not_found do
haml :'404'
end
# Render views/500.haml
# @e holds whatever was thrown, in this example, a string,
# but it could have an Error class of some sort.
error do
@e = request.env['sinatra_error']
haml :'500'
end
get '/' do
raise "Error happened!"
end
Notice that I had to surround the templates in single quotes. This is because ruby syntax doesn’t let symbol’s first character be a number. By quoting it, it gets around that issue.
Posted in Programming | 2 Comments »
September 9th, 2008 cschneid
Ryan Tomayko provided a great changelist for the new gem release of Sinatra, which I hope to expand on a bit here, by pointing you to more details on the changes.
Here’s a quick summary of changes since the 0.2.2 release:
-
Add sinatra.gemspec w/ support for github gem builds. Forks can now
enable the build gem option in github to get free username-sinatra.gem
builds: gem install username-sinatra.gem — source=http://gems.github.com/
-
Require Rack 0.4; removes frozen rack dir.
-
Basic RSpec support; require ‘sinatra/test/rspec’ instead of
‘sinatra/test/spec’ to use. [avdi]
-
before filters can modify request environment vars used for
routing (e.g., PATH_INFO, REQUEST_METHOD, etc.) for URL rewriting
type functionality.
-
In-file templates now uses @@ instead of ## as template separator.
-
Top-level environment test predicates: development?, test?, production?
-
Top-level “set”, “enable”, and “disable” methods for tweaking
app options. [rtomayko] — This allows the syntax in my post about options
-
Top-level “use” method for building Rack middleware pipelines
leading to app. see readme for usage. [rtomayko] — this is the use syntax in my post about sessions
-
New “reload” option - set false to disable reloading in development.
-
New “host” option - host/ip to bind to [cschneid]
-
New “app_file” option - override the file to reload in development
mode [cschneid] — This makes embedding Sinatra inside other apps easier to do during development. More info in my app_file post.
-
Development error/not_found page cleanup [sr, adamwiggins]
-
Remove a bunch of core extensions (String#to_param, String#from_param,
Hash#from_params, Hash#to_params, Hash#symbolize_keys, Hash#pass)
-
Various grammar and formatting fixes to README; additions on
community and contributing [cypher]
-
Build RDoc using Hanna template: http://sinatra.rubyforge.org/api/
-
Specs, documentation and fixes for splat’n routes [vic] — See how to use splat routes in my post on it.
-
Fix whitespace errors across all source files. [rtomayko]
-
Fix streaming issues with Mongrel (body not closed). [bmizerany]
-
Fix various issues with environment not being set properly (configure
blocks not running, error pages not registering, etc.) [cypher]
-
Fix to allow locals to be passed to ERB templates [cschneid] — More Details
-
Fix locking issues causing random errors during reload in development.
-
Fix for escaped paths not resolving static files [Matthew Walker]
Posted in Programming, Ruby, Sinatra | No Comments »
September 8th, 2008 cschneid
Well, Ryan Tomayko has taken up the reins for a gem release from the every busy Blake, and rolled out a new Sinatra gem.
You need to upgrade to this one. All sorts of bug fixes, new features, and just general polish.
I’ll come back in a day or two to write a nice long post will a rundown of the features. The nice thing is that most of what I’ve been writing about in the past has been against edge Sinatra. Since gem equals edge right now, everything I’ve written should apply to everybody (without all the work of running the github version). So go upgrade!
Posted in Programming | No Comments »
August 28th, 2008 cschneid
I just read a post over at proggit titled “Can Unit Testing be a Waste?”. The basic argument was that unit testing should go at the problem from a high level of abstraction, and you shouldn’t worry about going after every low level method in your DAO classes, or your User model.
One of the ways the author used to justify his argument was an example in Java that testing the high level got an overall coverage amount in the 95+% range. This is a misleading number.
The goal of unit testing is not to run your code, that only shows that you compile, and don’t blow up. At best it’s a form of smoke testing. The goal of unit testing is to ensure that your unit works as expected, and because you’re so close to the unit, you’ll be able to setup the environment (and mocks) to simulate issues and problems.
Then you verify using the various asserts.
My argument against using the highest level of abstraction is that you give up some of the core benefits of unit testing, namely the ability to really dig into your implementation and identify edge cases, and error situations.
Before I get yelled at, I know that going too low into the nitty-gritty ties your unit tests too closely to your code, which negates many of the “safety net” features that make refactoring easy. Honestly, what I think was being described in the “Can Unit Testing be a Waste?” post was integration (or feature, or validation, or whatever term you like) tests, not unit tests.
Stop getting sidetracked by coverage, focus on what bugs are being exposed by your test code, and focus on the benefits you get from writing the test case.
Posted in Programming, Rant | 1 Comment »
August 16th, 2008 cschneid
I setup Passenger (mod_rails) on my Macbook Pro last night, with the Passenger Preference Pane. I ran into a few small issues while doing it, and hopefully I can share them, and their solutions.
The gem install - First I did the standard gem install.
sudo gem install passenger
Apache setup - Then do the passenger apache installation, which compiles the module, and has details about the exact content you need to insert into httpd.conf
sudo passenger-install-apache2-module
httpd.conf setup - The passenger-install-apache2-module dumps a few lines to the console that need to be included in httpd.conf. Edit /etc/apache2/httpd.conf (sudo vim /etc/apache2/httpd.conf). I put the inserted lines near line #117 in my httpd.conf, after the rest of the modules are defined.
LoadModule passenger_module /Library/Ruby/Gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so
PassengerRoot /Library/Ruby/Gems/1.8/gems/passenger-2.0.3
PassengerRuby /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
Use the preference pane to setup an app - For testing purposes, I just used a rails app. I wanted to be sure Passenger was working correctly before going after Rack apps. Very quickly, I ran into an issue. This line was in /var/log/apache2/error_log
[Fri Aug 15 21:23:11 2008] [notice] child pid 48688 exit signal Abort trap (6)
terminate called after throwing an instance of 'Passenger::FileSystemException'
what(): Cannot stat '/Users/cschneid/Documents/Programming/Rails/BashFight/public/../config/environment.rb': Permission denied (13)
[Fri Aug 15 21:38:17 2008] [error] [client 127.0.0.1] (13)Permission denied: access to / denied
The solution to the permissions - It turns out that there is another step that is only mentioned in a single mailing list post. Apache needs another entry allowing itself access to the files. This is different and distinct from the filesystem permissions, which confused me for a while while trying to diagnose this.
<Directory "/Users/cschneid">
order allow,deny
Allow from all
</Directory>
Not quite - filesystem permissions matter too! After finding mention of the apache user directory details, I thought I had it fixed. BUT… not quite. It turns out that Apache needs file system permissions all the way up and down the file system tree. As you can see above, I keep all my code in /Users/cschneid/Documents/Programming/…. What turned out to be the final problem I faced was that /Users/cschneid had a permissions of 700. Apache was giving up because of that. A quick chmod 755 /Users/cschneid fixed that problem. Just know that you need to check permissions on every directory in the path to your app.
The Sinatra App - Works exactly as you’d expect. Go see my various posts on deploying to a Passenger install (Another try w/ logging), (Deploying to Dreamhost) to see how to setup your config.ru file.
Posted in Programming | No Comments »
July 22nd, 2008 cschneid
I’ve finally gotten off my ass, and setup http://sinatra-book.gittr.com. I’ve setup a cron job to build the book, and it should have a current version always there. Check it out, and as always, we love contributions, so hit up github.com/cschneid/sinatra-book to help out.
Posted in Programming | 1 Comment »
July 17th, 2008 cschneid
Most web applications have some sort of admin area where users shouldn’t be. Passwording it is the obvious solution, but it isn’t obvious how to implement passwords in Sinatra. I stole this code from Ryan Tomayko’s Wink blog, and extracted it into a more generic module for reuse. Two parts are involved, the module, which I put into a separate file, and include. And then the code itself to pull it into the helpers and use it in the event handlers.
The module:
module Sinatra
module Authorization
def auth
@auth ||= Rack::Auth::Basic::Request.new(request.env)
end
def unauthorized!(realm="myApp.com")
header 'WWW-Authenticate' => %(Basic realm="#{realm}")
throw :halt, [ 401, 'Authorization Required' ]
end
def bad_request!
throw :halt, [ 400, 'Bad Request' ]
end
def authorized?
request.env['REMOTE_USER']
end
def authorize(username, password)
# Insert your logic here to determine if username/password is good
false
end
def require_administrative_privileges
return if authorized?
unauthorized! unless auth.provided?
bad_request! unless auth.basic?
unauthorized! unless authorize(*auth.credentials)
request.env['REMOTE_USER'] = auth.username
end
def admin?
authorized?
end
end
end
To use the module:
require 'authorization'
helpers do
include Sinatra::Authorization
end
get '/admin' do
require_administrative_privileges
# Do private stuff
end
EDIT: Thanks to foca on #sinatra for the nicer setup of authorize
License: Because this was extracted out of Wink, follow the Wink license (found here).
Posted in Programming, Ruby, Sinatra | 7 Comments »
July 17th, 2008 cschneid
Once of the common things needed in webapps is to escape html.
Most frameworks acknowledge that need, and include helpers to do it for you. Sinatra isn’t that kind of framework. If you want it, it’s easy to wire in, but you need to ask.
Luckily, Sinatra is easily convinced, and will point you to it’s close friend, Rack.
helpers do
include Rack::Utils
alias_method :h, :escape_html
end
These few lines will include all of Rack::Utils (a few methods), and then rename one of them to match what Rails and Merb (and others) provide.
Now in your views, you can easily call escape html like:
Posted in Programming, Sinatra | No Comments »
July 16th, 2008 cschneid
Over the course of about 4 hours I setup a website to do logging of #sinatra, and put it on my brand new url http://www.irclogger.com. Go check it out for back history of the #sinatra room. Hopefully it’s all google searchable too.
Future features include, but are not limited to:
- a proper header
- a sidebar with all of the external links mentioned
- multi channel logging (not limited to #sinatra)
Many thanks to sr and foca for pushing me to get features done. Nothing like peer pressure to make code fly out.
Posted in Programming | No Comments »
July 16th, 2008 cschneid
After some talk in the #sinatra IRC channel, I decided to start a proper book project to collect and collate all of the brain dumping I’ve been doing here.
Check out the git repository at http://github.com/cschneid/sinatra-book
So far the project has been progressing fairly slowly, but there is already some great content in there. I will continue to convert blog posts into chapters, and look forward to any other contributions from others.
I will be setting up a nightly build of the book, pointing at the currently unsetup sinatra-book.gittr.com.
To build it yourself in the mean time, you will need a copy of “thor”, a reasonably cool rake/sake replacement. You’ll also need maruku, the markdown library we are using to write everything.
Posted in Sinatra | No Comments »