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 »
May 17th, 2008 cschneid
Sinatra has a great way of defining routing. You define a route, and then define exactly how it responds right in that same spot. Just the right amount of magic! The routes have only a few basic forms, but it’s more than enough to handle most any situation. In my examples I use get, but there’s no reason you can’t use the same routing with post, put, or delete
No option route
get '/about' do
haml :about
end
Single option
get '/thing/:id' do
@thing = Thing.find(params[:id])
haml :thing_show
end
Multiple options
get '/thing/:id/subthing/:subid' do
@thing = Thing.find(params[:id])
@subthing = SubThing.find(params[:subid])
haml :subthing_show
end
Handling file formats
get '/thing.:format' do
case params[:format]
when 'xml'
render_xml
when 'json'
render_json
end
end
Handling full subfolders
This one is ugly. There’s apparently a feature request + possible patch out there (as mentioned on the mailing list, but it hasn’t been implemented yet.
get '/archive/*' do
star_depth = 1 # /archive comes before the *
splat = request.path_info.split('/')[(star_depth + 1)..-1].join('/')
splat
end
Posted in Programming, Ruby, Sinatra | 3 Comments »
May 11th, 2008 cschneid
I’ve found a very cool new Ruby web framework in the vein of Camping. It’s called Sinatra, and it’s a very minimalist approach to writing RESTful applications.
A quick snippet of code to give you a feel for just how minimal things can get:
require 'rubygems'
require 'sinatra'
get '/' do
"Hello world"
end
Or for something more exciting with sessions:
require 'rubygems'
require 'sinatra'
set_option :sessions, true
get '/' do
session["foo"] ||= 0
session["foo"] += 1
"Hello world, you've asked for foo: #{session["foo"].to_s} number of times"
end
As you can see, it’s dead simple to write a trivial app. Over the next few blog posts, I’m going to get a “semantic pastebin” up and running, complete with database access, backend jobs, form handling, and probably some other cool stuff.
Posted in Programming, Ruby, Sinatra | No Comments »
May 10th, 2008 cschneid
As much fun as git tutorials, and comparisons between mercurial and bazzar are, they don’t really help if you don’t understand some of the basics. Hopefully this can quickly clear up some of the easy stuff.
Source control only really makes sense if you can see what it does, and how that maps into common and useful tasks.
What’s the problem?
Source code is easily misplaced. Shared code being emailed back and forth, or being shared on a windows share drive is easily misplaced. Even worse, you can have two copies of the same file, and not know which one is newer.
What can source control do?
- Provide the authoritative source of the code
- Match changes to their author
- Commit messages with details of how and why the code changed
- Merge changes from two different authors on the same file
- Tag releases, important milestones, builds
- Branch to have multiple streams of development
- Get all versions of the code going backwards based on date, tag, or version
- Easily combined with a backup solution to keep code safe.
How can two people work side-by-side on the same project?
Merging or locking. Visual source safe locks files when you check them out, and unlocks when you check in. Since only one person has the lock, there’s no risk of overlap. This can get annoying when you need a file that has it’s lock being held by a coworker.
Merging means that it tries to reconcile changes to the same file by two different people only when it has to. There’s no lock on the file, and concurrent changes can happen easily. There are algorithms that try to “merge” the two changed copies together. Most of the time, it works great, and no human interaction is needed. If the two people changed the files enough, or overlapped their changes, then the second person to check in is told about the overlap, and is forced to reconcile it manually in a text editor before checking in the final version.
Most source implementations take this merging approach, including Subversion, CVS, Borlands’s StarTeam (can also do locking), and others.
What is merging?
If I change line 5, and you change line 30, the software is smart enough to realize they don’t overlap, and shoves them together into a single file. If the changes do overlap, then it alerts you, and marks the locations in the file for you to fix manually. This sounds scary, but it happens far less often than you think, especially if you keep commits small.
What is tagging?
Label a certain point in time with a name. This can be anywhere from “NightlyBuild_2008_05_09” or “Release_3.56” or “UAT_1”. It just marks a set of file versions with a name so you can refer back to them later.
Why would I want the code from last Tuesday or from a tag?
Several reasons:
- It gives confidence later that when you build the final release version, it’s exactly the same code that was signed off on by the users.
- You can go back in time to a previous version when the users accuse you of having introduced bugs into a new version
- You don’t have to refer to things by date
- You don’t have to remember that version 3 was built from the code on the 10th of January.
- Any other time you want a human readable label on your code
What is branching?
This is where we get to the tricky part of source control. Let me first start with the problem that this solves.
We’ve released AwesomeThing version 1.0, and are ramping up to move the whole thing to version 2.0. Doing so will require lots of code changes, and take a fair amount of time.
So, we’re halfway through the new 2.0 version, and a bug report comes in for 1.0. What do we do? We labeled the code, so we can get the code as it was back in the release, but after we make the change, how do we keep track of it? We can’t insert it back in time, and it doesn’t make any sense to insert it at the head of the chain either, since that’s well on it’s way to version 1.0.
Branching is the solution. When you release a version of the code, you “branch” it into a support branch.
Looking at the timeline:
-------------- 1.0 Work
turns into
------------ 1.0 Release ---------- 2.0
\
\------ 1.0 Bug Fix Branch
Now you can get the newest version of the deployed code off of the maintenance branch, or the newest version of the next version off of the main (“head”) line.
At the point you release A.05, you just forget that the A.03 branch exists and make another branch for A.05 maintenance, so it would look like:
----- 1.0 Release ------------ 2.0 Release ----- New Features
\ \
\------ 1.0 Branch \---- 2.0 Bug Fix Branch
What else can you use branches for?
Branches are useful for all sorts of things other than just release management.
One of those uses is doing feature branches. Say you are working on the fancy new A.06 release, and need to rip out some of the guts of the code in order to get the code how you need it. If this takes two weeks, and you are following the best practice of checking in code early and often, you’ll break the code for all of the other developers during that time, who are relying on a working component to continue testing their new work against.
What you do is a feature branch. This looks like:
------- Work --------- Other work ------- Merge Point
\ /
\-------- Disruptive feature ----/
This shows another feature of branches. You can merge between them. At the end of your feature branch, when you have a nice, tested chunk of code you merge it back into the mainline development and integrate it with the rest of the code. Chances are higher of conflicts here than in the stepping on toes case described in merging up above, but it is exactly the same process.
Bug fixes can also be merged across branches in the Bug Fix branch scenario as well, to avoid losing a fix after you’ve done it. Basically, you fix and deploy off of the branch, then merge that fix back into the mainline development. This keeps both branches in sync, without having to duplicate work.
Best Practices
- Check in early, and often. Keep the code in the repository for everybody to see. Each check in should reflect only one thing, one bug fix, one CR, one very small feature.
- Check out your code often. This just gets a fresh copy of what everybody else is working on.
- Give good commit messages. This lets other developers know what you changed without digging into the code
- Tag everything that makes sense, too many is much better than too few
- Branch on releases to keep a bug fix branch handy
- Don’t duplicate what the repository does for you. It manages history very well, there is no reason to leave “commented out code, since I might need it later” in the repository, when you can pull it up at any time in the future. Embrace the delete key. Don’t mark the code with comments with the CR number (it should be in the commit message) for your change.
Posted in Programming, Source Control | No Comments »