whisky tour - http://whiskygeeks.com/
Below you'll find a quick stream of consciousness that includes photos, quotes, code snippets, and possibly even a full post. enjoy!
whisky tour - http://whiskygeeks.com/
We’ve recently scoped several projects in Boulder. While scoping, several clients asked how we’re able to estimate a project’s cost with such confidence. I’ve thought about this a bit and I actually think this is pretty interesting and remarkably similar to something we’ve all been doing for a while: estimating user stories in Tracker.
Scoping a project is very similar to determining the number of points or weight you might assign to a user story or feature in an Agile backlog. Similar to measuring the relative size or complexity of a feature, we’re just measuring the relative size or complexity of the project against similar projects that we’ve completed. The important piece here is that we’re measuring against completed projects.
With stories or features you estimate size and then derive duration in order to achieve a running feature velocity. Feature velocity is essential and the key to predictability. Completing features and measuring their associated duration is the empirical data we use to determine when a future set of stories will be completed or delivered. When scoping a project we’re missing this empirical data, although we still need to actually provide a duration to calculate the project’s cost. So how do we estimate a project’s scope and cost?
Imagine estimating a feature that you just completed for another project. You’d probably be pretty close on the points assignment as well as confidence. Now apply this to a project. It’s becomes easier to scope a project when you’ve completed a few similar projects. The more projects the better the comparison and resultant estimate.
Completed projects are valuable because we know how long they took to complete. For example, client A’s CRM was roughly 1 pair for 2 months. Client B’s CRM required 2 pairs for 4 months. And client C’s CRM, the one we’re currently estimating, is slightly more complex than client A’s although much less complex than client B’s. We’ll likely estimate client C’s CRM at 2 pairs for 2 months.
It’s actually not all gravy. There are several risks that may increase a project’s scope. For example, if we’re up against an aggressive timeline, we may include an additional pair for a few weeks to help increase feature velocity. Similarly, if there are complex integrations, like a 3rd party billing system or external messaging subsystems, we may include an additional pair for a month.
During an initial scoping we help reveal or tease out high level risks and then prioritize theses risks against constraints like timeline and budget. The more similar the risks we’ve resolved on completed projects, the more confidence we have to estimate risks during a new project scoping. Again, pretty similar to how we estimate features in a backlog. We’re just estimating the size or complexity of risks against similar risks with known durations.
Wouldn’t it be nice to include the resque-web interface underneath your current application’s url? Wouldn’t it then be nice to use the same authentication mechanism that your web application uses? Here’s a solution that we used on a recent Rails 2.3 ey-cloud project that accomplished both goals. First, we created a simple resque_web.ru file within our Rails 2.3 project. In this example we used the same Warden SSO authentication mechanism and fired up the resque-web server from the new mapping.
# Set up Resque environment
resque_config = YAML.load_file(File.join(File.dirname(__FILE__), 'config', 'resque.yml'))
env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
Resque.redis = resque_config[env]
map '/resque-web' do
use Rack::Session::Cookie, :key => 'rack.session',
:path => '/',
:expire_after => 60 ** 2,
:secret => 'supersecretresquekey'
use CasrackTheAuthenticator::Simple, :cas_server => "http://sso.your_sso_server.com"
use CasrackTheAuthenticator::RequireCAS
use Rack::ShowExceptions
run Resque::Server.new
end
The important piece here is that we use ‘/resque-web’, we’ll use this same path in a bit within our Nginx configuration. We also used the rack session cookies with our SSO authenticator.
Another interesting piece here is that we’re using Castronaut and Casrack the Authenticator, which makes SSO pretty straight forward.
Next we created an upstream server in Nginx to resolve the path. We’re just using Thin here.
Here’s our Chef template for the upstream server.
upstream resque_web_upstream {
server unix:/var/run/engineyard//.thin.1.sock;
}
The important piece here is that the upstream server lives outside of our web application config area.
Here’s our Chef recipe for creating the upstream server file.
template "/etc/nginx/servers/resque_web.conf" do
owner "root"
group "root"
mode 0755
source "resque_web.conf.erb"
variables({
:app_name => app_name
})
end
And then finally, we created a new custom location within our Nginx server configuration.
Here’s our Chef template for the custom Nginx Location.
location /resque-web {
proxy_set_header Host $http_host;
include /etc/nginx/common/proxy.conf;
proxy_pass http://resque_web_upstream;
}
And here’s our Chef recipe for creating the custom location file.
template "/etc/nginx/servers/#{app_name}/custom.locations.conf" do
owner "root"
group "root"
mode 0755
source "custom.locations.conf.erb"
variables({
:app_name => app_name
})
end
That’s it!
Recently there has been a lot of discussion around project dependencies. As a result, it has been pretty hard to decide when and if to include a new gem into a project.
Overall, I think I tend to weigh in heavier on the side of exclude everything possible except what is absolutely necessary. That being said, here’s the mental check list I tend to work through when deciding whether or not to include a dependency.
Is it something that I really need? Is it something that I can write myself?
Rails or MySQL might be something I’d rather not write because it might take a while versus an authorization component that would take only a few hours or days. It might be just as quick to write an authorization component then attempt to understand and debug another developers intentions.
Am I ready to support the Gem if the maintainers won’t?
Is this something I want to continue to maintain and grok a year from now, or will the Gem be obsolete?
Will it help me achieve a deliverables drop dead date?
Will it help me ensure that a deadline will not slip?
Can I accelerate toward a deliverable by including a Gem and then revisit the dependency?
What happens when I need to modify the Gem to tailor it to my needs? How do I keep up with the fixes from the original branch?
I tend to error on the side of Gems lasting for 6-18 months, then they’re essentially dead or re-written by the same author.
Is DRYing up my code really that important?
I’ve found that it is easy for people to ramp up on verbose code, especially verbose test code versus ramping up on code that includes a hierarchy of dependencies that one would need to sort through.
Continuing this thought, is there a ramp up time? And how many people will need to ramp up on the dependent Gem?
Further more, do I need total control over the component that I’m thinking about including?
Am I worried about a rogue check in that might impact the end-user experience when I rebase?
Is there a performance implication that I would like better to manage?
In summary, I guess I tend to get a bit uncomfortable with a large number of dependencies and wind up continually try to reduce the number of project dependencies.
We’re using Devise’s token_authenticatable to improve our Cucumber test suite performance. Here’s an example feature
Scenario: I would like to edit a user account
Given I am logged in as an admin user
...
And here’s the token_authenticatable step
Given /^(?:I am logged in|the user is logged in|the user logs in|I log in)(?: as an? (\w+) user)?$/ do |role|
if ENV['use_token']
visit admin_dashboard_path(:token => @user.authentication_token)
else
Given("the user logs in with email: \"#{@user.email}\", password: \"#{@password}\"")
...
end
We’ll then run cucumber using a “faster” profile
$ cucumber -p fasterAnd finally, here’s our cucumber.yml
faster: features FASTER=trueOur cucumber tests run significantly faster after switching to the token_authenticatable approach.
Here’s a quick snippet that we’re using to reindex Solr from our Cucumber features. “And I reindex assets” helps to ensure that we didn’t leave any cruft around from previous tests and that we’ve properly indexed our Pickle model.
Background:
And I am logged in as an admin user
And an asset with snowboarding tags: "snowboarding" exists with title: "Snowboarding", alt_text: "Snowboarding", summary: "A summary about snowboards"
And I reindex assets
Scenario: An Editor searches for an asset by title
When I go to the url: "/assets?q=Snowboarding"
Then I should see "Snowboarding"
Here’s the actual step
When /^I reindex (.+)$/ do |model_name|
model_name.singularize.capitalize.constantize.solr_reindex
end
Ever wondered how to login as another user within Devise? Recently we had a feature request that would provide Admins with the ability to sign is as another user. You could imagine live demonstrations or even production support calls where you would like to be signed in as another user, yet not have to ask for or decrypt their current password. After stewing for a bit, we found a pretty nice solution with Devise. Here’s the Cucumber feature…
Feature: Sign in as another user
As an admin
I want to sign in as another user
Scenario: Admin can sign in as another user
Given I am logged in as an admin user
And a user: "bob" exists with email: "bob@example.com", password: "password", ...
When I go to the admin users page
And I follow the "Sign in as" link for user: "bob"
Then I should see "Welcome Bob"
The trick was to store the admin info within the rack session.
request.env[‘rack.session’][‘devise.remember_admin_user_id’]
We decided on using a Rails 3 concern to keep our actual strategy clean.
require 'devise/strategies/base'
module SignInAs
module Concerns
module RememberAdmin
extend ActiveSupport::Concern
private
def remember_admin_id
request.env['rack.session']['devise.remember_admin_user_id']
end
def remember_admin_id=(id)
request.env['rack.session']['devise.remember_admin_user_id'] = id
end
def remember_admin_id?
request.env['rack.session'] && request.env['rack.session']['devise.remember_admin_user_id'].present?
end
def clear_remembered_admin_id
request.env['rack.session']['devise.remember_admin_user_id'] = nil
end
end
end
end
The above really makes writing the Devise strategy fairly easy.
require 'devise/strategies/base'
module SignInAs
module Devise
module Strategies
class FromAdmin ::Devise::Strategies::Base
include SignInAs::Concerns::RememberAdmin
def valid?
remember_admin_id?
end
def authenticate!
resource = User.find(remember_admin_id)
if resource
clear_remembered_admin_id
success!(resource)
else
pass
end
end
end
end
end
end
Warden::Strategies.add(:sign_in_as, SignInAs::Devise::Strategies::FromAdmin)
Then we just wire in our new strategy. Last line above.
Lastly, here’s our sign-in-as controller which sets the Devise session variable.
class SignInAsController ApplicationController
include SignInAs::Concerns::RememberAdmin
layout 'admin/application'
def create
if can?(:manage, :users)
self.remember_admin_id = current_user.id
sign_in :user, User.find(params[:user_id])
end
redirect_to '/admin'
end
end
That’s it, pretty neat and might make for a nice Gem or Devise addition.
If you’ve recently forked the ey-cloud-recipes on GitHub and then had issues managing and deploying multiple projects with disparate dependencies using the single forked gem, then we have a solution that has worked well on a recent project. We’ve tucked the cookbooks directory underneath our Rails project. To apply Chef changes, we installed the ‘engineyard’ gem and us ‘ey recipes upload’ and ‘ey recipes apply’ from within our Rails project. Upside, everything you need to know about the project is local to the project.
. ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── cookbooks │ ├── main │ │ ├── attributes │ │ │ └── recipe.rb │ │ ├── definitions │ │ │ └── ey_cloud_report.rb │ │ ├── libraries │ │ │ ├── ruby_block.rb │ │ │ └── run_for_app.rb │ │ └── recipes │ │ └── default.rb │ ├── redis │ │ ├── README.rdoc │ │ ├── recipes │ │ │ └── default.rb │ │ └── templates │ │ └── default │ │ ├── redis.conf.erb │ │ └── redis.monitrc.erb │ └── sunspot │ ├── recipes │ │ └── default.rb │ └── templates │ └── default │ ├── solr.erb │ └── solr.monitrc.erb
Ian McFarland, Principal and VP of Technology for Pivotal Labs, reprises his popular RailsConf 2010 talk. Ian describes the technical and social aspects of how Pivotal practices agile software development.