May 30th, 2008 cschneid
To turn on sessions normally, you just have to run:
enable :sessions
# OR
set :sessions, true
Under the covers, that sets up Rack::Session::Cookie as a rack middleware component. If you were to do this manually, it would look like:
use Rack::Session::Cookie
The problem with relying on this is that the Cookie session store limits you to storing only 4k of data. It’s normally not a great idea to store a ton of data in the session, but it’s sometimes necessary. The other problem with using the cookie session stores is that the data is stored unencrypteted (but packed as base64). So you open your application up to a potential attack from a malicious user manipulated cookie.
So what we need to do in order to “use” the alternate session implementation is just to run:
use Rack::Session::Pool
# Or another session store that matches the rack API
# DON'T enable :session
# DON'T set :session, true
I haven’t seen many session implementations in Rack, but the edge Rack (and the next release presumably) will make it simple to create new implementations. Just check out Rack::Session::Abstract::Id for some helpers. Also, edge Rack does support a Memcached session adapter, which looks pretty promising as a usable session container.
Posted in Programming, Ruby, Sinatra | 1 Comment »
May 28th, 2008 cschneid
After my half-assed post about RSpec testing the Sinatra framework got several google hits, I figured I’d look into it a little further. Here’s what I found.
First, a very simple Sinatra application.
$:.unshift("../sinatra/lib")
require 'sinatra'
get '/' do
"Hello"
end
get '/:thingy' do
"Hello #{params[:thingy]}"
end
Now a test script.
$:.unshift("../sinatra/lib")
require 'sinatra'
require 'spec/interop/test'
require 'sinatra/test/unit'
describe 'Hello World' do
require 'hello'
specify "should render hello at /" do
get_it '/'
@response.should be_ok
@response.body.should == "Hello"
end
specify "should render argument at /anything" do
get_it '/foo'
@response.should be_ok
@response.body.should == "Hello foo"
get_it '/bar'
@response.should be_ok
@response.body.should == "Hello bar"
end
specify "should not respond to nested paths" do
get_it '/foo/bar'
@response.should_not be_ok
end
end
What I did - basically, all that needed to be done was to bypass the ‘sinatra/test/spec’ file, which in turn requires the test/spec library, which we need to avoid.
Instead, we require the RSpec interop library, and then get the ‘sinatra/test/unit’ file, which sets up a reasonable environment to start testing, including deferring to ‘sinatra/test/methods’ to define the helpers like ‘get_it’, ‘post_it’ and so on. The bonus is that you get to skip the require of test/spec, and get to stick to pure RSpec.
Where to go from here - I would like some input on what other helpers would be cool/useful. I haven’t worked with RSpec all that much (well.. at all), so I don’t know what’s helpful, and what’s not. Leave comments, or hit me up on IRC.
Posted in Programming, Ruby, Sinatra, Testing | 3 Comments »
May 28th, 2008 cschneid
Last night, two of my patches to Sinatra got merged into the main branch. I’ve already written about the erb_locals feature. The other one was a slightly older one to fix development reloading in situations where Sinatra was not called directly.
The situation which triggered the issue:
- $0 (the file actually run by ruby) was not the file where the Sinatra routing and application was defined
- The file that actually was $0 called ‘require’ on the Sinatra application.
- A Sinatra development environment reload happened.
Ruby prevents duplicate loading of libraries by only loading each require once per lifetime of the VM. If you really do want to reload the file, you have to use Kernel.load. Which of course is how Sinatra handles the development environment’s reloading.
Original code:
New code:
# app_file defaults to $0
Kernel.load Sinatra.options.app_file
Now that app_file is an option, it’s easily overridden in the file with your Sinatra application.
Now development reloading works easier, and Sinatra is a better embeddable framework. No changes are needed to existing applications since app_file defaults to $0, the default behavior is exactly the same as the old behavior.
Posted in Programming, Ruby, Sinatra | No Comments »
May 27th, 2008 cschneid
Today in #sinatra, a question came from jshsu about using a the helper defined in the wiki, with ERB, using locals like you would in Rails.
It looked something like:
<%= partial(:_post, :locals => {:post => post} %>;
In Rails, that passes a local variable called “post” into the template, with the value of whatever post is now. The problem is that ERB doesn’t support this nativlely. It’s sort of a hack to make it work.
The whole chunk of code:
def render_erb(content, options = {})
locals_opt = options.delete(:locals) || {}
locals_code = ""
locals_hash = {}
locals_opt.each do |key, value|
locals_code << "#{key} = locals_hash[:#{key}]\n"
locals_hash[:"#{key}"] = value
end
body = ::ERB.new(content).src
eval("#{locals_code}#{body}", binding)
end
The initialization part - the locals variable contains key/value mappings containing all the values to pass into the template. locals_code and locals_hash are place holders for the code generation that happens in the next step. They need to be here because if we defined them in the each block below, they’d be block scoped and disappear at the end of that block. We don’t want that, so we need to pre-define them up here.
locals_opt = options.delete(:locals) || {}
locals_code = ""
locals_hash = {}
Code generation and storage of values - For each key/value pair in the locals hash, store it off in a locals_hash variable indexed by a symbol, and define the code to pull that value out a little later. The locals_code variable will be included in the rendered code in the next step.
locals_opt.each do |key, value|
locals_code << "#{key} = locals_hash[:#{key}]\n"
locals_hash[:"#{key}"] = value
end
ERB rendering and eval() - The first statement here renders the erb template down to ruby code. Calling the src attribute on the rendered template gives back a string of ruby code matching that rendered template. Once we have that, we paste the locals_code to the top of the rendered template, and then calls eval with the current binding. The binding of course allows the locals_code’s references to locals_hash to resolve correctly.
body = ::ERB.new(content).src
eval("#{locals_code}#{body}", binding)
Posted in Programming, Sinatra | 2 Comments »
May 27th, 2008 cschneid
UPDATE: I made another post about how to do RSpec testing without having to patch sinatra.
I haven’t looked into this myself, but thanks the the enterprising jodo on #sinatra, and the mad coding skillz of avdi on github, there is a patch to enable interop between test-spec (default) and rspec.
So give it a try, and see how it goes. Report back on #sinatra, or leave a comment here.
Posted in Programming, Ruby, Sinatra | No Comments »
May 25th, 2008 cschneid
The Sinatra framework has a fairly unmapped deployment landscape. The standard answer is to use Thin or Mongrel, and have a reverse proxy (lighttpd, or nginx, or even Apache) point to your bundle of servers.
But of course that isn’t always possible. Cheap shared hosting (like Dreamhost) won’t let you run Thin or Mongrel, or setup reverse proxies (at least on the shared plan).
Luckily Rack supports various connectors, including CGI and FastCGI. Unluckily for us, FastCGI doesn’t quite work with the current Sinatra git HEAD.
To get a dumb “hello world” Sinatra application up and running on dreamhost involves pulling down the current Sinatra code, and hacking at it a bit. Don’t worry, it’s mostly just commenting out a few lines, and tweaking another line.
Files needed:
- .htaccess
- dispatch.fcgi
- Tweaked sinatra.rb
.htaccess
RewriteEngine on
AddHandler fastcgi-script .fcgi
Options +FollowSymLinks +ExecCGI
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
dispatch.fcgi
#!/usr/bin/ruby
require 'sinatra/lib/sinatra.rb'
require 'rubygems'
fastcgi_log = File.open("fastcgi.log", "a")
STDOUT.reopen fastcgi_log
STDERR.reopen fastcgi_log
STDOUT.sync = true
set :logging, false
set :server, "FastCGI"
module Rack
class Request
def path_info
@env["SCRIPT_URL"].to_s
end
def path_info=(s)
@env["SCRIPT_URL"] = s.to_s
end
end
end
load 'test.rb'
sinatra.rb - Replace this function with the new version here.
def run
begin
#puts "== Sinatra has taken the stage on port #{port} for #{env} with backup by #{server.name}"
require 'pp'
server.run(application) do |server|
trap(:INT) do
server.stop
#puts "\n== Sinatra has ended his set (crowd applauds)"
end
end
rescue Errno::EADDRINUSE => e
#puts "== Someone is already performing on port #{port}!"
end
end
Hopefully that helps somebody get up and running, check out #sinatra on freenode.org to ask questions, but be patient, we’re friendly, but work day jobs.
Posted in Programming, Ruby, Sinatra | No Comments »
May 24th, 2008 cschneid
Instead of writing a pastebin for semantic data like I had planned (still a good idea I’ll get to eventually), I’ve started working on a blog engine initially written by Ryan Tomayko called Wink. If you visit his blog, you can see the only live instance of Wink. He recently open sourced it with a little cajoling from the rest of the guys in #sinatra.
To get a feel for what it can do, check out Ryan’s post “Administrative Debris”. You can get a very good feel for what Wink is all about: simplicity, consistency, and ease.
Currently it’s not quite in a usable state. If you grab the 0.1 tag off of github, it should run for you, maybe with some tweaking or fighting with it. If you want to give it a shot, please do so, and report back on freenode.org either in #sinatra or #wink on how it went.
Beyond that, we’re going to solidify some of the wibbly-wobbly parts, add a few features, and document how to install and deploy.
I see Wink as being the shining example of what a great sinatra application can be, and I’m glad I got in on the ground floor of it’s open source life.
To grab the source code, or just follow development head over to github at Ryan’s Wink repository, or mine.
Posted in Programming, Rails, Sinatra | 1 Comment »
May 20th, 2008 cschneid
Sinatra has several configurable options you can set at the top of your application.
Default options, straight out of the code:
@default_options = {
:run => true,
:port => 4567,
:env => :development,
:root => root,
:views => root + '/views',
:public => root + '/public',
:sessions => false,
:logging => true,
:raise_errors => false
}
Key options, and what they do:
- run - Should Sinatra auto-call run on itself with an at_exit handler. The idea is that you don’t have to put in a Sinatra.run anywhere in your application, it just happens automatically. I can’t think of a good reason to turn this off.
- env - Most of the time this is overridden by either the command line or the rackup file. A set in an application’s code will override any other previous setting, but this default isn’t used very often.
- root - Setting this has no effect, since it isn’t ever used anywhere in the code (currently…)
- views - Default views dir
- public - Default public dir (public means static files like css and js)
- sessions - Turn on sessions, uses a cookie store by default. I don’t know how to configure the store.
- logging - enables the Rack::CommonLogger middleware.
One at a time:
# Two arguments, key, then value
set :sessions, true
set :logging, false
All at once:
# One argument, a hash mapping key -> value
set :sessions => true,
:logging => false,
:views => 'path/to/views'
### For binary options, you can use enable:
enable :sessions, :logging
Posted in Programming, Sinatra | 1 Comment »
May 20th, 2008 cschneid
Turns out splat routes are much easier than I previously posted. I don’t know when this came into the code, I think you can only get this if you go and check out the newest code.
Straight outta the README:
get '/say/*/to/*' do
# matches /say/hello/to/world
params["splat"] # => ["hello", "world"]
end
get '/download/*.*' do
# matches /download/path/to/file.xml
params["splat"] # => ["path/to/file", "xml"]
end
At this point, you have a nice and easy way of determining what’s in the splat.
Posted in Programming, Sinatra | 1 Comment »
May 17th, 2008 cschneid
As a developer who moved from a testing position, I have the background to really understand how hard testing really is. Since so many organizations force the developers to do double duty as testers, hopefully this article can help streamline that process by focusing attention down to what really matters. Since testing is such a large topic, I’ll dive into each one later in more detail.
Why Test?
Because humans aren’t perfect, not even close. There are so many places in the software development lifecycle where things can go wrong. From bad or underspecified requirements, to misunderstandings by the developer of the business domain, to off-by-one and null pointer bugs in the code to poor performance.
There is no way to ever catch all of these potential issues, since the number of possible testing permutations grows so quickly. What you can do as a developer turned tester is to use your analytic frame of mind to hunt down and kill bugs as quickly as possible.
What to Test?
Everything! Well, you don’t have time? How about the most critical stuff?
My general approach to planning a project’s testing is:
- Run through everything once. Run through the “happy path” of the code, and verify it at least does a close approximation to what it should. This step should probably happen during the initial building of the code.
- Rank your components or areas by criticality and perceived errors. You will get an idea in step 1 that certain areas are buggier than others. By combining the impact of failure for an area with the likelihood of failure, you’ll get the risk of each area. (impact * likelihood = risk)
- Test as much as you can in the time you have.
- Be as honest as possible with your boss on what’s good and what’s not. You are there to find bugs, they are there to decide what risk is acceptable.
Pessimism and Optimism
- Positive testing
- Test the ideal conditions, the user doesn’t do anything weird, and the input is correct. Verify it returns correct values. Use your knowledge of what the code is doing to narrow down unique cases. Does the code handle Japan queries differently than US queries? Test both. US and Europe follow the same code path? Test one, and leave the other as a TODO item.
- Negative testing
- Now use your imagination. What happens if the user clicks stuff out of order? Enters in a 0 length string? a 1000 length? An email with two “@” signs? Cancel a process halfway through, does the program recover? Try unplugging your network cable halfway through an operation, does the program hang? Lots of weird cases can happen, try to reproduce the most likely (ram, disk, network, bad input).
Automated or Manual
Both! Use each where it makes sense. The cost-benefit curves over time for each type of test look entirely different. Automated tests are expensive up front, but almost free to run over and over. Manual tests take about the same amount of work every time they are run, no matter how many times before they were run.
Remember that many types of tests are simply impossible to run without automation. Things like load testing, stress testing, memory leak testing, and so on are so tedious or large that there isn’t any way to manually execute it.
Manual tests are great since humans will never follow a script perfectly, by veering off course just slightly during the execution of the test, they will find errors that rigid automated tests will never find. Automated tests suffer from running over the same path over, and over again. In addition, manual testing will find issues that the automated tests weren’t even designed to look for. Things like “clicking that button causes the one over there to change text… that’s not right”.
Glossary:
- Black Box
- Testing the software from the outside. Using only the user interfaces and external API of the program to test.
- White Box
- Testing the program from the inside. You see the code, the branches and loops, giving you a better chance of forcing out bugs. Tools like code coverage comes in at this point.
- Grey Box
- The guy is making up terms… I guess if I was forced to define it, it would be using whitebox code analysis to determine useful black box tests.
- Unit Testing
- Testing one method call, or on function at a time. Very low level testing, typically done by the developer themselves. This is very good at catching typo, off by one, and code errors. It is almost worthless at catching assumption or understanding errors.
- Component Testing
- I made up this term, since I haven’t heard it put nicely elsewhere. This is just testing a single standalone module or component of the larger software system.
- Integration Testing
- Combine all the components together, and you have the whole system. Test that integration between components, look for flaws at the junctions between them.
- User Acceptance Testing
- Show the technically clean application to your final users, have them go through the original set of requirements, and verify they are complete. This type of testing may happen several times as major chunks of the application get completed.
Posted in Programming, Testing | No Comments »