If you're like me, you've found yourself with a cucumber step definition like this:
And you want to write a step definition like this:
Instead of doing all that extra work, I threw together a cucumber step that lets you add times to any existing step definition:
To make things even easier I created a gem called Timebomb. It lets you append time constraints to your cucumber step defintions by mixing and matching:
- seconds, minutes, hours, days, weeks, months, years
- ago, from now
So with the following step definition:
I can write any of the following:
- Given I received an invitation 1 day from now
- Given I received an invitation 2 weeks ago
- Given I received an invitation 3 months from now
- Given I received an invitation 15 years ago
Timebomb to the rescue, BOOM!
== Installing ==
First install Timebomb
- sudo gem install timebomb --source http://gemcutter.org
Now, just require it in your cucumber env.rb file. BOOM goes the dynamite.
Saturday, November 14, 2009
Tuesday, July 7, 2009
Vim Plugin for Ruby Testing
If you're using ruby and you're using vim, you've probably noticed some awesome tools out there (mostly created by Tim Pope). You've got rails.vim, ruby.vim, etc. However, I noticed that there was a lack of support for some features that Textmate users have been taking for granted for a long time. I'm talking about the support Textmate provides around running your tests.
The key features I was missing from Textmate were having text output in a new buffer, the ability to run a focused test (the test my cursor was currently on) and the ability to run all tests in a given context. With the help of some ThoughtWorks colleagues on a previous project, I've put together a vim plugin that I think meets these needs.
The plugin is ruby_focused_unit_test_vim. The plugin currently supports the following ruby testing tools:
- test/unit
- rspec
- shoulda
- dust
The current features the plugin supports are:
- run focused test
- run focused context
- run entire test file
- all commands stream output to a new buffer
The streaming feature is very cool (thanks to David Vollbracht). It gives you instant feedback as your tests pass/fail in a new buffer. It feels very similar to the test output in Textmate.
The installation process is simple. Just clone the git repo and copy the ruby_focused_unit_test.vim file into your .vim/plugin directory.
Once you've installed the plugin, three commands become available to you:
- RunRubyFocusedUnitTest
- RunRubyFocusedContext
- RunAllRubyTests
To make things simple, I map these commands in my .vimrc:
I hope you find the plugin useful. In the future I'll write about my overall vim setup for ruby/rails development.
The key features I was missing from Textmate were having text output in a new buffer, the ability to run a focused test (the test my cursor was currently on) and the ability to run all tests in a given context. With the help of some ThoughtWorks colleagues on a previous project, I've put together a vim plugin that I think meets these needs.
The plugin is ruby_focused_unit_test_vim. The plugin currently supports the following ruby testing tools:
- test/unit
- rspec
- shoulda
- dust
The current features the plugin supports are:
- run focused test
- run focused context
- run entire test file
- all commands stream output to a new buffer
The streaming feature is very cool (thanks to David Vollbracht). It gives you instant feedback as your tests pass/fail in a new buffer. It feels very similar to the test output in Textmate.
The installation process is simple. Just clone the git repo and copy the ruby_focused_unit_test.vim file into your .vim/plugin directory.
Once you've installed the plugin, three commands become available to you:
- RunRubyFocusedUnitTest
- RunRubyFocusedContext
- RunAllRubyTests
To make things simple, I map these commands in my .vimrc:
I hope you find the plugin useful. In the future I'll write about my overall vim setup for ruby/rails development.
Monday, December 29, 2008
Sinatra Dances on Passenger's Stage
I wrote briefly in my last post about how I've been working with sinatra quite a bit lately. After some great experiences at work, I decided to move my homepage over to sinatra from rails. It was totally inane to have the site written in rails in the first place, but I'd been learning the framework at the time and when you have a hammer...well, you know.
Anyway, I learned quite a bit about deploying a sinatra app on passenger as well as using passenger for development of sinatra apps and I figured I'd talk a bit about that. One great thing about passenger is its ability to serve any rack application. Rack is a tiny little framework that acts as a sort of standard for dealing with http requests and sinatra supports rack. This means deploying a sinatra app on passenger is pretty simple, but I had to poke around a bit to get the wheels running smoothly. Let's first look at my application layout.
We'll look at each of these files a bit closer. First and foremost, we've got the drewolson.rb file. This is my sinatra app, which we won't go into now, but remember that your sinatra app should sit at the root of your directory structure. You can also see that I have 3 subdirectories. The first is public, where you throw any static files (like images, stylesheets or favicons). The next is the views directory, which is pretty self-explanatory. Throw your view files in here.
Last but not least is the test subdirectory. This is where the tests for my sinatra app reside. Sinatra actually provides you with some pretty nice testing tools, and I'll just show you my test_helper.rb which all my tests require.
Ok, now onto the deployment fun stuff. There are two files that are key to deploying sinatra apps on passenger: config.ru and Capfile. Capfile is my capistrano deployment script and config.ru is my rackup file. Let's look at config.ru first.
I tried several variants of this file, but this is the only one that works for me. Notice that you have to explicitly state where you app_file is located as well as where your views directory sits.
Next up, let's look at my Capfile.
Again, this is super simple when compared to a standard rails Capfile. Notice that we overwrite deploy:restart. All we have to do with passenger is touch a file. Note that this also assumes you have ssh access to your server and that you're using git as your version control system.
The last piece of the puzzle is setting up a virtual host for apache. Now, you've probably already done this before but just to be complete I'll show you my virtual host for this application.
Yes, that's it. Seriously. Assuming you've enabled the site in apache, all you have to do on your first deploy is run:
cap deploy:setup
cap deploy
Bam, you're up and running with a sinatra app on passenger. But that's not all. One thing that I struggled with when getting this all working was constantly deploying, checking the app, fixing the config.ru, deploying, checking the app ... All this could have been avoided if I'd simply been developing on passenger locally and using it to run my rack based sinatra app locally before deploying. Well it turns out this is super simple. You can use the awesome passenger prefpane if you're on OS X. Simply install it, point it at your directory, and you now have a local copy of your sinatra app running on passenger + rack. How sweet is that?
Update:
It seems that newer versions of sinatra and rack break these instructions. They are verified working for rack 0.4.0 and sinatra 0.3.2. Updated code snippets to reflect this.
Anyway, I learned quite a bit about deploying a sinatra app on passenger as well as using passenger for development of sinatra apps and I figured I'd talk a bit about that. One great thing about passenger is its ability to serve any rack application. Rack is a tiny little framework that acts as a sort of standard for dealing with http requests and sinatra supports rack. This means deploying a sinatra app on passenger is pretty simple, but I had to poke around a bit to get the wheels running smoothly. Let's first look at my application layout.
We'll look at each of these files a bit closer. First and foremost, we've got the drewolson.rb file. This is my sinatra app, which we won't go into now, but remember that your sinatra app should sit at the root of your directory structure. You can also see that I have 3 subdirectories. The first is public, where you throw any static files (like images, stylesheets or favicons). The next is the views directory, which is pretty self-explanatory. Throw your view files in here.
Last but not least is the test subdirectory. This is where the tests for my sinatra app reside. Sinatra actually provides you with some pretty nice testing tools, and I'll just show you my test_helper.rb which all my tests require.
Ok, now onto the deployment fun stuff. There are two files that are key to deploying sinatra apps on passenger: config.ru and Capfile. Capfile is my capistrano deployment script and config.ru is my rackup file. Let's look at config.ru first.
I tried several variants of this file, but this is the only one that works for me. Notice that you have to explicitly state where you app_file is located as well as where your views directory sits.
Next up, let's look at my Capfile.
Again, this is super simple when compared to a standard rails Capfile. Notice that we overwrite deploy:restart. All we have to do with passenger is touch a file. Note that this also assumes you have ssh access to your server and that you're using git as your version control system.
The last piece of the puzzle is setting up a virtual host for apache. Now, you've probably already done this before but just to be complete I'll show you my virtual host for this application.
Yes, that's it. Seriously. Assuming you've enabled the site in apache, all you have to do on your first deploy is run:
cap deploy:setup
cap deploy
Bam, you're up and running with a sinatra app on passenger. But that's not all. One thing that I struggled with when getting this all working was constantly deploying, checking the app, fixing the config.ru, deploying, checking the app ... All this could have been avoided if I'd simply been developing on passenger locally and using it to run my rack based sinatra app locally before deploying. Well it turns out this is super simple. You can use the awesome passenger prefpane if you're on OS X. Simply install it, point it at your directory, and you now have a local copy of your sinatra app running on passenger + rack. How sweet is that?
Update:
It seems that newer versions of sinatra and rack break these instructions. They are verified working for rack 0.4.0 and sinatra 0.3.2. Updated code snippets to reflect this.
Tuesday, December 23, 2008
Mashup your RSS Feeds with ruby + sinatra + pipe
I've been doing some build work lately and I stumbled upon a situation last week where I needed to take a bunch of RSS feeds, aggregate them and filter only the specific items I wanted. This actually manifested itself with continuous integration boxes. We have a lot of CI boxes running a lot of builds but each team is only interested in several of these builds.
One option is using something like CCMenu, but I actually needed a consumable RSS feed for use by another service that displayed build status is a very big, readable way (for a monitor in the team area). This aggregation and filtering of RSS feeds is, in fact, exactly what Yahoo Pipes does. However, these are all internal feeds, so Yahoo Pipes wouldn't work for me here.
Lately, I have been toying with sinatra, a great little web framework in ruby. I decided to write a tool that could be used in conjunction with sinatra to get me my RSS feed aggregation and filtering. That tool is called (not surprisingly) pipe. Below is the code for creating a simple feed containing all the ruby related articles on ycombinator and the clojure related articles on the programming subreddit:
You can install pipes like so:
- gem sources -a http://gems.github.com
- sudo gem install drewolson-pipe
The documentation is here. Enjoy.
One option is using something like CCMenu, but I actually needed a consumable RSS feed for use by another service that displayed build status is a very big, readable way (for a monitor in the team area). This aggregation and filtering of RSS feeds is, in fact, exactly what Yahoo Pipes does. However, these are all internal feeds, so Yahoo Pipes wouldn't work for me here.
Lately, I have been toying with sinatra, a great little web framework in ruby. I decided to write a tool that could be used in conjunction with sinatra to get me my RSS feed aggregation and filtering. That tool is called (not surprisingly) pipe. Below is the code for creating a simple feed containing all the ruby related articles on ycombinator and the clojure related articles on the programming subreddit:
You can install pipes like so:
- gem sources -a http://gems.github.com
- sudo gem install drewolson-pipe
The documentation is here. Enjoy.
Monday, September 8, 2008
Diggr released
I finally got around to a small side project I have wanted to work on: a new ruby wrapper for the Digg API. There's one existing library out there that I know of, but I didn't like the syntax all that much. So, like any good developer, I've reinvented the wheel! My library is called diggr and it strives to be simple and consistent with the Digg API endpoints listed here. Rather than boring you with a detailed description, I'll just show some code snippets below. For more information on diggr check out:
- Docs
- Source
Here are a few simple examples:
- Docs
- Source
Here are a few simple examples:
require 'rubygems'
require 'diggr'
diggr = Diggr::API.new
# retrieve a single user by user name and print the number of profile views
user = diggr.user("johndoe").fetch
puts user.profileviews
# iterator over the most recent 10 stories (default return size) and print their titles
diggr.stories.each do |story|
puts story.title
end
# print the title of the 3 most recent hot stories
diggr.stories.hot.options(:count => 3).each do |story|
puts story.title
end
# build an array of stories whos title contains "foo"
diggr.stories.inject([]) do |array,story|
array << story if story.title =~ /foo/
array
end
# print the title of the 2nd and 3rd most recent stories
diggr.stories.options(:count => 2, :offset => 2).each do |story|
puts story.title
end
Wednesday, June 11, 2008
Ruby and Macros: An Experiment
Lately I've been doing a lot of reading about Lisp. This got me thinking about how I could implement something similar to Lisp macros in ruby. DISCLAIMER: I am not a Lisp guru. I'm sure I'm missing the finer points of Lisp macros. I'm sure I'm an idiot.
That said, what I wanted to come up with was a way to define methods such that you received both the arguments to the method and the block given to the method completely unevaluated. That is, you'll be given an array of strings representing the arguments and one large string representing the block passed to the marco. You are then be free to do whatever you'd like with the arguments and the body. This, to me, captures at least the essence of macros (yes, I'm probably wrong). To do so, I've whipped up a gem called def_macro.
Below I'll show you some samples using the gem to define two classic Lisp macros: with and loop. The with macro basically gives a block access to some local variables while executing. The loop macro just loops over some code n times (the loop macro I'm describing here is WAY simplified from the true Lisp loop macro). Anyway, I'll show the examples in two stages: defining the macro using def_macro and using the defined macro.
Notice in this case I'm only evaluating the first argument (there's only one). By doing so, I've set my local variables. I'm then free to evaluate the body string. Everything works as expected and we see 3 printed out. Notice that if I'm passing code as arguments, they must be inside of a proc or lambda. Ruby forces us to do this if we don't want the code evaluated immediately.
The simplified loop macro is even easier to implement. Take a look:
Notice this time that it's not necessary to wrap the argument in a proc as it doesn't matter when the number 2 is evaluated.
You can download the gem in two ways:
- rubyforge: sudo gem install defmacro
- github: sudo gem install drewolson-defmacro --source=http://gems.github.com
The source is on github at http://github.com/drewolson/def_macro/tree/master
Update: Fixed spelling of macro (thanks to Nwallins on reddit)
That said, what I wanted to come up with was a way to define methods such that you received both the arguments to the method and the block given to the method completely unevaluated. That is, you'll be given an array of strings representing the arguments and one large string representing the block passed to the marco. You are then be free to do whatever you'd like with the arguments and the body. This, to me, captures at least the essence of macros (yes, I'm probably wrong). To do so, I've whipped up a gem called def_macro.
Below I'll show you some samples using the gem to define two classic Lisp macros: with and loop. The with macro basically gives a block access to some local variables while executing. The loop macro just loops over some code n times (the loop macro I'm describing here is WAY simplified from the true Lisp loop macro). Anyway, I'll show the examples in two stages: defining the macro using def_macro and using the defined macro.
require 'rubygems'
require 'def_macro'The with macro:
def_macro :with do |args,body|
eval args.first
eval body
end
with proc { a = 1; b = 2 } do
puts a + b
end # => 3Notice in this case I'm only evaluating the first argument (there's only one). By doing so, I've set my local variables. I'm then free to evaluate the body string. Everything works as expected and we see 3 printed out. Notice that if I'm passing code as arguments, they must be inside of a proc or lambda. Ruby forces us to do this if we don't want the code evaluated immediately.
The simplified loop macro is even easier to implement. Take a look:
The loop macro:
def_macro :loop do |args,body|
(eval args.first).times do
eval body
end
end
loop 2 do
puts "hi"
end # => "hi" "hi"Notice this time that it's not necessary to wrap the argument in a proc as it doesn't matter when the number 2 is evaluated.
You can download the gem in two ways:
- rubyforge: sudo gem install defmacro
- github: sudo gem install drewolson-defmacro --source=http://gems.github.com
The source is on github at http://github.com/drewolson/def_macro/tree/master
Update: Fixed spelling of macro (thanks to Nwallins on reddit)
Thursday, May 29, 2008
Remote Git Repos on Ubuntu: The Right Way
Overview
Over the past week or so I've been setting up remote git repositories on my slice. I figured it would be helpful to put together a post regarding what I've found to be the right way to set up remote git repos. The info in this post is basically collected from several blogs and articles, but I've yet to see all of it in a single post.
Before we get started, I'm going to make a couple of assumptions:
- You're running ubuntu
- You have ssh access to your box
- You have access to a user on the sudoer list
- You have git installed on your server and your client machine
- You've generated a public/private key and it's in the standard location
Ok, enough with the chitchat, let's just get started.
Adding the git user
1. Create a git user on your server
On your server:
sudo adduser git2. Create your .ssh folder and authorized_keys file for the git user
On your server:
su git # switch to the git user
cd ~ # change to git's home dir
mkdir .ssh # make the .ssh dir
touch .ssh/authorized_keys # create an empty authorized_keys file3. Next, we need to copy the public key from our client system and add it to the authorized_keys for the git user on the server.
On your client:
ssh copy-id git@yourserver.com4. Because we're going to be giving anyone access to the git user who needs access to the git repos, we want to restrict the actions the git user can take. To do so, we'll set the git users shell to git-shell. First, log back into your server on a user with sudo access (not the git user) and do the following:
On your server:
which git-shell # remember this location for the next step. mine is /usr/bin/git-shell
sudo vim /etc/passwd # find the line related to the git user, change the section that says
# /bin/sh to /usr/bin/git-shell (or whatever you got from the first step)Creating a new git repo
So we've created our git user and now we'd like to create a new repository that we can use from the clients. It's actually really simple. We basically create a folder on the server in our git user's home directory named whatever we'd like the repo to be named and them we initialize it as a bare git repo. One thing to keep in is that all these commands will be performed from a user that has sudo access and is NOT the git user. Because we've set the git user's shell to git-shell, we can no longer switch to that user or ssh in as them.
On your server:
sudo mkdir /home/git/foo # create foo directory
cd /home/git/foo # change to that directory
sudo git --bare init # initialize a bare git repo
sudo chown -R git:git /home/git/foo # make the git user the owner of the repoFirst Commit to the Repo
After initializing the repo, we're going to do a first commit from our client machine. The first commit is slightly complicated, but after you'll be all set to go.
On our client:
mkdir foo # make a new folder
cd foo
git init # initialize the git repo and add a file
touch README
git add .
git commit -m "added README"
git remote add origin git@yourserver.com:foo # add the remote git repo
git push origin master # push your commit to itCloning your Repo
Our repo is now all set to clone! On our client, we could futz around with the .git/config to make our master branch track the origin, but if we clone the repo it does it for us! So, we'll just delete the directory we've just created and reclone the repo. That way, we don't have to remember how to set up the .git/config file (and I'm lazy).
On our client:
rm -r foo/
git clone git@yourserver.com:fooAnd that's really all there is to it. If you'd like to give someone else access to your git repo, simply add their public key to authorized_keys. Keep in mind that with this setup, all users will have access to all git repos you create in the git user's home directory. I'll talk more about giving different access in a later post. Hope this helps you all getting git remote repos up for all your projects.
Update: Now uses ssh copy-id to copy public key to the remote server.
Subscribe to:
Posts (Atom)