Monday, June 14, 2010

Create A Rails 3 beta With Authlogic, Declarative Authorization a

Create A Rails 3 beta With Authlogic, Declarative Authorization and Cucumber

I have updated my auth_with_roles app to use rails 3.0.7. You can find the code at https://github.com/johnivanoff/auth_with_roles/tree/rails_3_0_7

This was done on a windows wachine so forgive the C:\
And it's a crappy first draft.

My test aren't the best and need refactoring but this should get you started.
I plan to update this when rails 3 is released. I would also like to intergate Factory Girl.



This project uses:

Let's get going.
C:\web> rails auth_with_roles
C:\web> cd auth_with_roles
C:\web\auth_with_roles> git init
C:\web\auth_with_roles> git add .
C:\web\auth_with_roles> git commit -m "auth_with_roles Scaffold"

Let's add the gems for cucumber.
open /Gemfile add . . .
# Use Authlogic for security
gem "authlogic", :git => "git://github.com/odorcicd/authlogic.git", :branch => "rails3"
# using for role assignments
gem "declarative_authorization", :git => "git://github.com/stffn/declarative_authorization.git"

group :test do
gem 'capybara'
gem 'database_cleaner'
gem 'cucumber-rails'
gem 'cucumber', '0.7.2'
gem 'rspec-rails', '2.0.0.beta.8'
gem 'spork'
gem 'launchy' # So you can do Then show me the page
end


run
C:\web\auth_with_roles> bundle install

bootstrap your Rails app, for rspec:
C:\web\auth_with_roles> rails g rspec:install

Finally, bootstrap your Rails app, for cucumber:
C:\web\auth_with_roles> rails generate cucumber:skeleton --rspec --capybara

create the authlogic base.
C:\web\auth_with_roles> rails g cucumber:feature user username:string email:string password:string
C:\web\auth_with_roles> rails g scaffold user username:string email:string password:string

create_user migration
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :username
t.string :email
t.string :crypted_password
t.string :password_salt
t.string :persistence_token
t.timestamps
end
add_index :users, :username
add_index :users, :persistence_token
end
def self.down
drop_table :users
end
end

run
C:\web\auth_with_roles>rake db:migrate

Create a UserSessionsController
C:\web\auth_with_roles> rails g authlogic:session user_session
C:\web\auth_with_roles> rails g controller user_sessions new create destroy

fix up the sign up form /app/views/users/_form.html.erb
<%= form_for(@user) do |f| %>
<% if @user.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>


fix the user index fix /app/views/users/index.html.erb
<h1>Listing users</h1>
<table>
<tr>
<th>Username</th>
<th>Email</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @users.each do |user| %>
<tr>
<td><%= user.username %></td>
<td><%= user.email %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New User', new_user_path %>


the user show /app/views/users/show.html.erb
<p class="notice"><%= notice %></p>
<p>
<b>Username:</b>
<%= @user.username %>
</p>
<p>
<b>Email:</b>
<%= @user.email %>
</p>
<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>



fix up the login form /app/views/user_sessions/new.html.erb
<%= form_for @user_session, :url => login_path do |f| %>
<% if @user_session.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(@user_session.errors.count, "error") %> prohibited this asset_type from being saved:</h2>
<ul>
<% @user_session.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :username %><br />
<%= f.text_field :username %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p><%= f.submit "Log in" %></p>
<% end %>


add the routes /config/routes.rb
AuthWithRoles::Application.routes.draw do |map|
resources :users
controller :user_sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end

match 'register', :to => 'users#new', :as => "register"
end



Modify the application controller /apps/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
layout 'application'
helper :all
helper_method :current_user_session, :current_user
#filter_parameter_logging :password, :password_confirmation
before_filter :set_current_user
protected
def set_current_user
Authorization.current_user = current_user
end
private
def current_user_session
return @current_user_session if defined?(@current_user_session)
@current_user_session = UserSession.find
end
def current_user
return @current_user if defined?(@current_user)
@current_user = current_user_session && current_user_session.record
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to login_url
return false
end
end
def require_no_user
if current_user
store_location
flash[:notice] = "You must be logged out to access this page"
redirect_to account_url
return false
end
end
def store_location
session[:return_to] = request.fullpath
end
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
end



user_sessions_controller.rb /apps/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
def new
@user_session = UserSession.new
end
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
flash[:notice] = "Successfully logged in."
redirect_to users_path
else
render :action => 'new'
end
end
def destroy
current_user_session.destroy
flash[:notice] = "Successfully logged out."
redirect_back_or_default login_url
end
end


Modify the user controller /apps/controllers/users_controller.rb
class UsersController < ApplicationController
#before_filter :require_no_user, :only => [:new, :create]
#before_filter :require_user, :only => [:show, :edit, :update]
# GET /users
# GET /users.xml
def index
@users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @users }
end
end
# GET /users/1
# GET /users/1.xml
def show
@user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @user }
end
end
# GET /users/new
# GET /users/new.xml
def new
@user = User.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @user }
end
end
# GET /users/1/edit
def edit
@user = User.find(params[:id])
end
# POST /users
# POST /users.xml
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
format.html { redirect_to(@user, :notice => 'Registration successful.') }
format.xml { render :xml => @user, :status => :created, :location => @user }
else
format.html { render :action => "new" }
format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
@user = User.find(params[:id])
respond_to do |format|
if @user.update_attributes(params[:user])
format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.xml
def destroy
@user = User.find(params[:id])
@user.destroy
respond_to do |format|
format.html { redirect_to(users_url) }
format.xml { head :ok }
end
end
end



modify the user model /app/model/user.rb
class User < ActiveRecord::Base
acts_as_authentic
end


we'll need to modify the cucumber test /features/manage_users.feature
Feature: Manage users
In order to [goal]
[stakeholder]
wants [behaviour]
Scenario: Register new user
Given the following roles:
|name|
|admin|
And I am on the new user page
When I fill in "Username" with "username 1"
And I fill in "Email" with "example@example.com"
And I fill in "Password" with "secret"
And I fill in "Password confirmation" with "secret"
And I check "admin"
And I press "Create"
Then I should see "username 1"
And I should see "example@example.com"
And I should see "admin"
Scenario: Delete user
Given the following users:
|username |email|password|password_confirmation|role_ids|
|username 1|email1@example.com|secret|secret|1|
|username 2|email2@example.com|secret|secret|2|
|username 3|email3@example.com|secret|secret|2|
|username 4|email4@example.com|secret|secret|2|
When I delete the 3rd user
Then I should see the following users:
|Username |Email|
|username 1|email1@example.com|
|username 2|email2@example.com|
|username 4|email4@example.com|



run
C:\web\auth_with_roles> rake cucumber
...........

2 scenarios (2 passed)
11 steps (11 passed)

run
C:\web\auth_with_roles> rake spec

Well we have some test to correct.

let modify the user session spec in /spec/controllers/user_sessions_controller_spec.rb
require 'spec_helper'
describe UserSessionsController do
def mock_usersession(stubs={})
@mock_usersession ||= mock_model(UserSession, stubs).as_null_object
end
describe "GET 'new'" do
it "should be successful" do
get 'new'
response.should be_success
end
end
describe "GET 'create'" do
it "should be successful" do
get 'create'
response.should be_success
end
end
describe "DELETE destroy" do
it "destroys the current session" do
UserSession.should_receive(:find).with("37") { mock_usersession }
mock_usersession.should_receive(:destroy)
delete :destroy, :id => "37"
end
it "redirects to the login page" do
UserSession.stub(:find) { mock_usersession(:destroy => true) }
delete :destroy
response.should redirect_to(login_url)
end
end
end

Now modify the user's index spec /spec/views/users/index.html.erb_spec.rb
require 'spec_helper'
describe "users/index.html.erb" do
before(:each) do
assign(:users, [
stub_model(User,
:username => "MyUsername",
:email => "MyEmail",
:password => "MyPassword"
),
stub_model(User,
:username => "MyUsername",
:email => "MyEmail",
:password => "MyPassword"
)
])
end
it "renders a list of users" do
render
response.should have_selector("tr>td", :content => "MyUsername".to_s, :count => 2)
response.should have_selector("tr>td", :content => "MyEmail".to_s, :count => 2)
end
end


run
C:\web\auth_with_roles> rake spec

...................*.....*


Finished in 2.34 seconds
26 examples, 0 failures, 2 pending

Pending:
User add some examples to (or delete) ./spec/models/user_spec.rb (Not Yet Implemented)
# ./spec/models/user_spec.rb:4
user_sessions/create.html.erb add some examples to (or delete) ./spec/views/user_sessions/create.html.erb_spec.rb (Not Yet Implemented)
# ./spec/views/user_sessions/create.html.erb_spec.rb:4

All tests are passing, although they are not complete.

Let's get the login spec working /spec/views/new.html.erb_spec
require 'spec_helper'
describe "user_sessions/new.html.erb" do
before(:each) do
assign(:user_session, stub_model(User,
:new_record? => true,
:username => "MyString",
:password => "MyString"
))
end
it "renders login form" do
render
response.should have_selector("form", :action => login_path, :method => "post") do |form|
form.should have_selector("input#user_username", :name => "user[username]")
form.should have_selector("input#user_password", :name => "user[password]")
end
end
end

Let's get the User model working /spec/models/user_spec.rb
require 'spec_helper'
describe User do
before(:each) do
@user = User.new(:username => "Jimmy",
:email => "jimmy@example.com",
:password => "secret",
:password_confirmation => "secret")
end
it "is valid with valid attributes" do
@user.should be_valid
end
it "is not valid without a username" do
@user.username = nil
@user.should_not be_valid
end
it "is not valid without a unique username" do
@user2 = User.create( :username => "Jimmy",
:email => "johnny@example.com",
:password => "secret",
:password_confirmation => "secret")
@user.should_not be_valid
end
it "is not valid without an email" do
@user.email = nil
@user.should_not be_valid
end
it "is not valid without a unique email" do
@user2 = User.create( :username => "Johnny",
:email => "jimmy@example.com",
:password => "secret",
:password_confirmation => "secret")
@user.should_not be_valid
end
it "is not valid without a password"
it "is not valid without matching password and password confirmation" do
@user.password = "wrong"
@user.should_not be_valid
end
it "is not valid without a password confirmation" do
@user.password_confirmation = nil
@user.should_not be_valid
end
end



Now create the Roles scaffold

C:\web\auth_with_roles> rails g cucumber:feature role name:string
C:\web\auth_with_roles> rails g scaffold role name:string

create the join table
C:\web\auth_with_roles> rails g model assignment user_id:integer role_id:integer
C:\web\auth_with_roles> rake db:migrate

update the models to

in /app/models/assignment.rb
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
end

in /app/models/role.rb
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
end

in /app/models/user.rb
class User < ActiveRecord::Base
acts_as_authentic
has_many :assignments
has_many :roles, :through => :assignments
def role_symbols
roles.map do |role|
role.name.underscore.to_sym
end
end
end


run some tests
run
C:\web\auth_with_roles> rake cucumber
..................

4 scenarios (4 passed)
18 steps (18 passed)
0m6.688s

run
C:\web\auth_with_roles> rake spec
..................................**.....*.............


Finished in 6.66 seconds
55 examples, 0 failures, 3 pending

Pending:
Assignment add some examples to (or delete) ./spec/models/assignment_spec.rb (Not Yet Implemented)
# ./spec/models/assignment_spec.rb:4
Role add some examples to (or delete) ./spec/models/role_spec.rb (Not Yet Implemented)
# ./spec/models/role_spec.rb:4
User is not valid without a password (Not Yet Implemented)
# ./spec/models/user_spec.rb:37

Let's add some tests for the Role model. /spec/models/role_spec.rb
require 'spec_helper'
describe User do
before(:each) do
@role = Role.new(:name => "Jimmy")
end
it "is valid with valid attributes" do
@role.should be_valid
end
it "is not valid without a name" do
@role.name = nil
@role.should_not be_valid
end
it "is not valid without a unique name"
end

run
C:\web\auth_with_roles> rake spec
..................................*.F*.....*.............

1) Role is not valid without a name
Failure/Error: @role.should_not be_valid
expected valid? to return false, got true
# ./spec/models/role_spec.rb:14
# ./spec/controllers/roles_controller_spec.rb:3


Finished in 4.59 seconds
57 examples, 1 failures, 3 pending

Pending:
Assignment add some examples to (or delete) ./spec/models/assignment_spec.rb (Not Yet Implemented)
# ./spec/models/assignment_spec.rb:4
Role is not valid without a unique name (Not Yet Implemented)
# ./spec/models/role_spec.rb:17
User is not valid without a password (Not Yet Implemented)
# ./spec/models/user_spec.rb:37


We need to add vilidation to the Role model /app/models/role.rb
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
validates :name, :presence => true
end


run
C:\web\auth_with_roles> rake spec
..................................*..*.....*.............


Finished in 4.75 seconds
57 examples, 0 failures, 3 pending

Pending:
Assignment add some examples to (or delete) ./spec/models/assignment_spec.rb (Not Yet Implemented)
# ./spec/models/assignment_spec.rb:4
Role is not valid without a unique name (Not Yet Implemented)
# ./spec/models/role_spec.rb:17
User is not valid without a password (Not Yet Implemented)
# ./spec/models/user_spec.rb:37

Passed.
Let's add finish the tests for the Role model. /spec/models/role_spec.rb
require 'spec_helper'
describe User do
before(:each) do
@role = Role.new(:name => "Jimmy")
end
it "is valid with valid attributes" do
@role.should be_valid
end
it "is not valid without a name" do
@role.name = nil
@role.should_not be_valid
end
it "is not valid without a unique name" do
@role2 = ROle.create( :username => "Jimmy") @role.should_not be_valid
end
end

run
C:\web\auth_with_roles> rake spec
..................................*..F.....*.............

1) Role is not valid without a unique name
Failure/Error: @role.should_not be_valid
expected valid? to return false, got true
# ./spec/models/role_spec.rb:19
# ./spec/controllers/roles_controller_spec.rb:3


Finished in 4.63 seconds
57 examples, 1 failures, 2 pending

Pending:
Assignment add some examples to (or delete) ./spec/models/assignment_spec.rb (Not Yet Implemented)
# ./spec/models/assignment_spec.rb:4
User is not valid without a password (Not Yet Implemented)
# ./spec/models/user_spec.rb:37


We need to add vilidation to the Role model /app/models/role.rb
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
validates :name, :presence => true
validates :name, :uniqueness => true
end


run
C:\web\auth_with_roles> rake spec
..................................*........*.............


Finished in 6.27 seconds
57 examples, 0 failures, 2 pending

Pending:
Assignment add some examples to (or delete) ./spec/models/assignment_spec.rb (Not Yet Implemented)
# ./spec/models/assignment_spec.rb:4
User is not valid without a password (Not Yet Implemented)
# ./spec/models/user_spec.rb:37

We need to lock down the roles controller to admin only

In the application controller /app/controllers/application_controller.rb add
class ApplicationController < ActionController::Base
protect_from_forgery
layout 'application'
helper :all
helper_method :current_user_session, :current_user
#filter_parameter_logging :password, :password_confirmation
before_filter :set_current_user
protected
def set_current_user
Authorization.current_user = current_user
end
def permission_denied
flash[:error] = "Sorry, you are not allowed to access that page."
redirect_to '/'
end
private
def current_user_session
return @current_user_session if defined?(@current_user_session)
@current_user_session = UserSession.find
end
def current_user
return @current_user if defined?(@current_user)
@current_user = current_user_session && current_user_session.record
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to login_url
return false
end
end
def require_no_user
if current_user
store_location
flash[:notice] = "You must be logged out to access this page"
redirect_to account_url
return false
end
end
def store_location
session[:return_to] = request.fullpath
end
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
end

In the role controller /app/controllers/roles_controller.rb add
class RolesController < ApplicationController
filter_resource_access
.....................................
end

In the user controller /app/controllers/users_controller.rb add
class RolesController < ApplicationController
filter_resource_access
.....................................
end


run
C:\web\auth_with_roles> rake cucumber
.F--.F-...........

(::) failed steps (::)

cannot fill in, no text field, text area or password field with id, name, or label 'Name' found (Capybara::ElementNotFound)
./features/step_definitions/web_steps.rb:41
./features/step_definitions/web_steps.rb:14:in `with_scope'
./features/step_definitions/web_steps.rb:40:in `/^(?:|I )fill in "([^\"]*)" with "([^\"]*)"(?: within "([^\"]*)")?$/'
features\manage_roles.feature:8:in `When I fill in "Name" with "name 1"'

scope '//table//*[position() = 4 and self::tr]' not found on page (Capybara::ElementNotFound)
./features/step_definitions/role_steps.rb:7:in `/^I delete the (\d+)(?:st|nd|rd|th) role$/'
features\manage_roles.feature:42:in `When I delete the 3rd role'

Failing Scenarios:
cucumber features\manage_roles.feature:6 # Scenario: Register new role
cucumber features\manage_roles.feature:35 # Scenario: Delete role

4 scenarios (2 failed, 2 passed)
18 steps (2 failed, 3 skipped, 13 passed)
0m7.813s
rake aborted!
Command failed with status (1): [C:/Ruby/bin/ruby.exe -I "C:/Documents and ...]

Well we got redirected to the home page. We'll have to log in.
in /featues/manage_roles.feature add
Feature: Manage roles
In order to manage roles
as an Admin
I want to create and edit roles.
Scenario: Register new role
Given the following roles:
|name|
|admin|
And the following users:
|username |email|password|password_confirmation|role_ids|
|don |email1@example.com|secret|secret|1|
And I am logged in as "don" with password "secret"
Given I am on the new role page
When I fill in "Name" with "name 1"
And I press "Create"
Then I should see "name 1"
Scenario: Delete role
Given the following roles:
|name |
|admin |
|name 2|
|name 3|
|name 4|
And the following users:
|username |email |password|password_confirmation|role_ids|
|don |email1@example.com|secret |secret |1 |
And I am logged in as "don" with password "secret"
When I delete the 3rd role
Then I should see the following roles:
|Name |
|admin |
|name 2|
|name 4|

run
C:\web\auth_with_roles> rake cucumber
..U----..U--...........

4 scenarios (2 undefined, 2 passed)
23 steps (6 skipped, 2 undefined, 15 passed)
0m5.781s

You can implement step definitions for undefined steps with these snippets:

Given /^I am logged in as "([^\"]*)" with password "([^\"]*)"$/ do |arg1, arg2|
pending # express the regexp above with the code you wish you had
end

in /featues/step_definitions/user_steps.rb add
Given /^I am logged in as "([^\"]*)" with password "([^\"]*)"$/ do |username, password|
unless username.blank?
visit login_url
fill_in "Username", :with => username
fill_in "Password", :with => password
click_button "Log in"
end
end
run
C:\web\auth_with_roles> rake cucumber
.......................

4 scenarios (4 passed)
23 steps (23 passed)
0m10.063s

Let's update the users feature so we can select a role for a new users and admin can show edit or destroy. everyone can see a list of the users
In /features/manage_users.feature
Feature: Manage users
In order to manage Users
As an Admin
I want to manage users
@focus
Scenario: Register new user
Given the following roles:
|name|
|admin|
And I am on the new user page
When I fill in "Username" with "username 1"
And I fill in "Email" with "example@example.com"
And I fill in "Password" with "secret"
And I fill in "Password confirmation" with "secret"
And I check "admin"
And I press "Create"
Then I should see "username 1"
And I should see "example@example.com"
And I should see "admin"
Scenario: Delete user
Given the following users:
|username |email|password|password_confirmation|
|username 1|email1@example.com|secret|secret|
|username 2|email2@example.com|secret|secret|
|username 3|email3@example.com|secret|secret|
|username 4|email4@example.com|secret|secret|
When I delete the 3rd user
Then I should see the following users:
|Username |Email|
|username 1|email1@example.com|
|username 2|email2@example.com|
|username 4|email4@example.com|
Scenario: Admin sees all user's edit links
Given the following roles:
|name|
|admin|
|guest|
And the following users:
|username |email|password|password_confirmation|role_ids|
|don |email1@example.com|secret|secret|1|
|username 2|email2@example.com|secret|secret|2|
|username 3|email3@example.com|secret|secret|2|
|username 4|email4@example.com|secret|secret|2|
And I am logged in as "don" with password "secret"
Then I should see the following users:
|Username |Email|Show|Edit|Destroy|
|don |email1@example.com|Show|Edit|Destroy|
|username 2|email2@example.com|Show|Edit|Destroy|
|username 3|email3@example.com|Show|Edit|Destroy|
|username 4|email4@example.com|Show|Edit|Destroy|

you might have noticed I added an @focus tag. We'll be running one scenario at a time now. we'll be able to focus on it.
run
C:\web\auth_with_roles> cucumber --tags @focus
......F----

(::) failed steps (::)

cannot check field, no checkbox with id, name, or label 'admin' found (Capybara::ElementNotFound)
./features/step_definitions/web_steps.rb:78
./features/step_definitions/web_steps.rb:14:in `with_scope'
./features/step_definitions/web_steps.rb:77:in `/^(?:|I )check "([^\"]*)"(?: within "([^\"]*)")?$/'
features\manage_users.feature:16:in `And I check "admin"'

Failing Scenarios:
cucumber features\manage_users.feature:6 # Scenario: Register new user

1 scenario (1 failed)
11 steps (1 failed, 4 skipped, 6 passed)
0m7.453s


We Update the _form view so we can add roles to users /app/views/users/_form.html.erb
<%= form_for(@user) do |f| %>
<% if @user.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<p>
<%= f.label :roles %><br />
<% for role in Role.all %>
<%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %>
<label for="user_role_ids_"><%= role.name %></label>
<% end %>
</p>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
run
C:\web\auth_with_roles> cucumber --tags @focus
..........F

(::) failed steps (::)

<false> is not true. (Test::Unit::AssertionFailedError)
C:/Ruby/lib/ruby/1.8/test/unit/assertions.rb:48:in `assert_block'
C:/Ruby/lib/ruby/1.8/test/unit/assertions.rb:500:in `_wrap_assertion'
C:/Ruby/lib/ruby/1.8/test/unit/assertions.rb:46:in `assert_block'
C:/Ruby/lib/ruby/1.8/test/unit/assertions.rb:63:in `assert'
C:/Ruby/lib/ruby/1.8/test/unit/assertions.rb:495:in `_wrap_assertion'
C:/Ruby/lib/ruby/1.8/test/unit/assertions.rb:61:in `assert'
./features/step_definitions/web_steps.rb:112
./features/step_definitions/web_steps.rb:14:in `with_scope'
./features/step_definitions/web_steps.rb:108:in `/^(?:|I )should see "([^\"]*)"(?: within "([^\"]*)")?$/'
features\manage_users.feature:20:in `And I should see "admin"'

Failing Scenarios:
cucumber features\manage_users.feature:6 # Scenario: Register new user

1 scenario (1 failed)
11 steps (1 failed, 10 passed)
0m7.531s


We'll also update the show page so we can see the user's roles /app/views/users/show.html.erb
<p class="notice"><%= notice %></p>
<p>
<b>Username:</b>
<%= @user.username %>
</p>
<p>
<b>Email:</b>
<%= @user.email %>
</p>
<p><%= pluralize(@user.roles.size, 'Role') %></p>
<ul>
<% @user.roles.each do |role| %>
<li><%= role.name %></li>
<% end %>
</ul>
<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>

run
C:\web\auth_with_roles> cucumber --tags @focus
...........

1 scenario (1 passed)
11 steps (11 passed)
0m7.828s

It passes.
Now we want only the admin to 'Show", "Edit', and 'Destroy' users.
in /features/manage_users.feature add
@focus
Scenario: Admin sees all user's edit links
Given the following roles:
|name|
|admin|
|guest|
And the following users:
|username |email|password|password_confirmation|role_ids|
|don |email1@example.com|secret|secret|1|
|username 2|email2@example.com|secret|secret|2|
|username 3|email3@example.com|secret|secret|2|
|username 4|email4@example.com|secret|secret|2|
And I am logged in as "don" with password "secret"
Then I should see the following users:
|Username |Email|Show|Edit|Destroy|
|don |email1@example.com|Show|Edit|Destroy|
|username 2|email2@example.com|Show|Edit|Destroy|
|username 3|email3@example.com|Show|Edit|Destroy|
|username 4|email4@example.com|Show|Edit|Destroy|
And I should see "New user"

Remove the previous @focus from feature
run
C:\web\auth_with_roles> cucumber --tags @focus
......F

(::) failed steps (::)

Tables were not identical (Cucumber::Ast::Table::Different)
./features/step_definitions/user_steps.rb:13:in `/^I should see the following users:$/'
features\manage_users.feature:73:in `Then I should see the following users:'

Failing Scenarios:
cucumber features\manage_users.feature:61 # Scenario: Admin sees all user's edit links

2 scenarios (1 failed, 1 passed)
7 steps (1 failed, 6 passed)

we need to modify the index view /app/views/users/index.html.erb
<h1>Listing users</h1>
<table>
<tr>
<th>Username</th>
<th>Email</th>
<th>Show</th>
<th>Edit</th>
<th>Destroy</th>
</tr>
<% @users.each do |user| %>
<tr>
<td><%= user.username %></td>
<td><%= user.email %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />

<%= link_to 'New User', new_user_path %>

run
C:\web\auth_with_roles> cucumber --tags @focus
.....

1 scenario (1 passed)
5 steps (5 passed)
0m7.828s

We will need to fix the delete user scenario. remember to move the @focus tag to it.
Scenario: Delete user
Given the following roles:
|name|
|admin|
|guest|
And the following users:
|username |email|password|password_confirmation|role_ids|
|don |email1@example.com|secret|secret|1|
|username 2|email2@example.com|secret|secret|2|
|username 3|email3@example.com|secret|secret|2|
|username 4|email4@example.com|secret|secret|2|
And I am logged in as "don" with password "secret"
When I delete the 3rd user
Then I should see the following users:
|Username |Email|
|don |email1@example.com|
|username 2|email2@example.com|
|username 4|email4@example.com|


run
C:\web\auth_with_roles> cucumber --tags @focus
.....

1 scenario (1 passed)
5 steps (5 passed)
0m7.828s

Let's add a scenario so that users can see everyone but do not see the show edit or destroy and the New user link.
@focus
Scenario: guest sees all user's and Show links, but no edit or destroy links
Given the following roles:
|name|
|admin|
|guest|
And the following users:
|username |email|password|password_confirmation|role_ids|
|don |email1@example.com|secret|secret|1|
|jimmy |email2@example.com|secret|secret|2|
|username 3|email3@example.com|secret|secret|2|
|username 4|email4@example.com|secret|secret|2|
And I am logged in as "jimmy" with password "secret"
Then I should see the following users:
|Username |Email |Show|Edit|Destroy|
|don |email1@example.com|Show| | |
|jimmy |email2@example.com|Show| | |
|username 3|email3@example.com|Show| | |
|username 4|email4@example.com|Show| | |
And I should not see "New User"



run
C:\web\auth_with_roles> cucumber --tags @focus
...F-

(::) failed steps (::)

Tables were not identical (Cucumber::Ast::Table::Different)
./features/step_definitions/user_steps.rb:13:in `/^I should see the following users:$/'
features\manage_users.feature:97:in `Then I should see the following users:'

Failing Scenarios:
cucumber features\manage_users.feature:85 # Scenario: guest sees all user's and Show links, but no edit or destroy links

1 scenario (1 failed)
5 steps (1 failed, 1 skipped, 3 passed)
0m14.359s

Let's updae the index page /app/views/users/index.html.erb
<h1>Listing users</h1>
<table>
<tr>
<th>Username</th>
<th>Email</th>
<th>Show</th>
<th>Edit</th>
<th>Destroy</th>
</tr>
<% @users.each do |user| %>
<tr>
<td><%= user.username %></td>
<td><%= user.email %></td>
<td>
<% if permitted_to? :show, @user %>
<%= link_to 'Show', user %>
<% end %>
</td>
<td>
<% if permitted_to? :edit, @user %>
<%= link_to 'Edit', edit_user_path(user) %>
<% end %>
</td>
<td>
<% if permitted_to? :destroy, @user %>
<%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %>
<% end %>
</td>
</tr>
<% end %>
</table>
<br />
<% if permitted_to? :create, @user %>
<%= link_to 'New User', new_user_path %>
<% end %>

run
C:\web\auth_with_roles> cucumber --tags @focus
.....

1 scenario (1 passed)
5 steps (5 passed)
0m7.828s

Let's test all the cucumber tests
C:\web\auth_with_roles> rake cucumber
......................................

6 scenarios (6 passed)
38 steps (38 passed)
0m18.016s

Now only an admin can create, edit or delete roles.
You'll need to lock down the Users so only an admin can create, edit or destroy users, or what ever you decide.

Cheers,
John

Source code on git hub.

Thanks to
railscasts Authlogic
and the documentation for the gems I used.

Tuesday, May 11, 2010

Sam's Club MS 150 Frisco to Fort Wort...

Sam's Club MS 150 Frisco to Fort Worth 2010

day 1

It's been over a week since we crossed the finish line. I think I have recovered. Back on that Saturday when I loaded my bike into the car, as I shut the trunk I thought "Did I just hear thunder?" Shortly after that it stared to rain. At the house it was pouring. I thought, "Not again." As I drove up to the start in Frisco the rain let up, thanks Connie. The parking lot was dry at the start. Finished getting dressed and took my bags over to the truck to be hauled to TMS. I decided to keep my rain jacket In hind sight I could have left it at home, who knew? Around 7:20 am the team I was riding with "Team Bike Mart" rolled out and started our journey. It's tough not to get caught up in the adrenaline and start ridding with the big boys and girls. At the third rest stop I overheard some people talking about keeping the pace around 17 mph. I asked if I could ride with them. We clipped along at a good pace. We had a good lunch. It's hard not to over eat. 

Why there are those wicked little hills after lunch? that's just cruel. we rode along and stopped at a few stops but not all of them. I didn't get my yearly picture of me and Chester the Cheetah. As we were on the last leg of day one my legs reminded me that I had not been drinking enough. I was able to fight off the cramps. I drank heavily after the ride. I drank water not beer. we finished around 2 pm. here's the data from my ride.


Day 2

It was a little chilly in the morning. I decided not to wear a jacket hoping that it would get warm soon. as I was ridding out after stuffing myself full of pancakes and bacon, I noticed a group of riders from vipmotorcars.net and they looked like they would ride my pace. They started to do a rolling paceline and I was able to get in. We kept that going until the first rest stop. I the rest stop we introduced ourselves and I asked if I could tag along. "Sure" was what I heard. after that we picked up the pace a little and kepth the paceline rolling. We would pick up a few people here and there. We hit ever other rest stop. At one point we had a double paceline going since there were six of us. We made it through the hills and everything else. They were great to ride with. we crossed the finish line around 1 pm.

data from day 2*


*my "computer" didn't pick up my heart rate after lunch, but I finally got it back.


I'm ready to do it again.

You can find some more reading about this on twitter

Thursday, March 25, 2010

New Ruby on Rails Setup on Windows XP

Ruby on Rails(2.3.5) Setup on Windows XP

I followed the directions on rubyonrails.org

I installed rubyinstaller-1.8.7-p249-rc2.exe

Then I updated the gems.
-------------------------------------------------
update ruby gem
   C:\>gem update --system                 
install rails
   C:\>gem install rails                   
-------------------------------------------------
Let's see what's installed.

C:\>gem list --local                       
                                                                     
*** LOCAL GEMS ***                         
                                                                     
actionmailer (2.3.5)                       
actionpack (2.3.5)                         
activerecord (2.3.5)                       
activeresource (2.3.5)                     
activesupport (2.3.5)                      
rack (1.0.1)                               
rails (2.3.5)                              
rake (0.8.7)                               
rubygems-update (1.3.6)                    
-------------------------------------------------

Install the mongrel gem
    C:\>gem install mongrel 
-------------------------------------------------
Let's see what's installed.

C:\>gem list --local                       
                                                                     
*** LOCAL GEMS ***                         
                                                                     
actionmailer (2.3.5)                       
actionpack (2.3.5)                         
activerecord (2.3.5)                       
activeresource (2.3.5)                     
activesupport (2.3.5)                      
cgi_multipart_eof_fix (2.5.0)              
gem_plugin (0.2.3)                         
mongrel (1.1.5)                            
rack (1.0.1)                               
rails (2.3.5)                              
rake (0.8.7)                               
rubygems-update (1.3.6)                    
win32-service (0.5.2)                      


-------------------------------------------------

Install mongrel_service for mswin32
    C:\>gem install mongrel_service --platform=mswin32 
-------------------------------------------------
Let's see what's installed.

C:\>gem list --local                       
                                                                     
*** LOCAL GEMS ***                         
                                                                     
actionmailer (2.3.5)                       
actionpack (2.3.5)                         
activerecord (2.3.5)                       
activeresource (2.3.5)                     
activesupport (2.3.5)                      
cgi_multipart_eof_fix (2.5.0)              
gem_plugin (0.2.3)                         
mongrel (1.1.5)                            
mongrel_service (0.3.4)                    
rack (1.0.1)                               
rails (2.3.5)                              
rake (0.8.7)                               
rubygems-update (1.3.6)                    
win32-service (0.5.2)                      
-------------------------------------------------
install gem for mySql
    C:\>gem install mysql 
-------------------------------------------------
Let's see what's installed.

C:\>gem list --local                       
                                                                     
*** LOCAL GEMS ***                         
                                                                     
actionmailer (2.3.5)                       
actionpack (2.3.5)                         
activerecord (2.3.5)                       
activeresource (2.3.5)                     
activesupport (2.3.5)                      
cgi_multipart_eof_fix (2.5.0)              
gem_plugin (0.2.3)                         
mongrel (1.1.5)                            
mongrel_service (0.3.4)                    
mysql (2.8.1)                              
rack (1.0.1)                               
rails (2.3.5)                              
rake (0.8.7)                               
rubygems-update (1.3.6)                    
win32-service (0.5.2)                      
-------------------------------------------------
install sqlite gem
    C:\>gem install sqlite3-ruby 
-------------------------------------------------
Let's see what's installed.

C:\>gem list --local                       
                                                                     
*** LOCAL GEMS ***                         
                                                                     
actionmailer (2.3.5)                       
actionpack (2.3.5)                         
activerecord (2.3.5)                       
activeresource (2.3.5)                     
activesupport (2.3.5)                      
cgi_multipart_eof_fix (2.5.0)              
gem_plugin (0.2.3)                         
mongrel (1.1.5)                            
mongrel_service (0.3.4)                    
mysql (2.8.1)                              
rack (1.0.1)                               
rails (2.3.5)                              
rake (0.8.7)                               
rubygems-update (1.3.6)                    
sqlite3-ruby (1.2.5)                       
win32-service (0.5.2)                      
-------------------------------------------------

It now works with slqite3 and mysql.

Lets add MS SQL server

install gem activerecord sqlserver adapter (http://github.com/rails-sqlserver/2000-2005-adapter)
    C:\> gem install activerecord-sqlserver-adapter 
I ran into a problem that it could install the gem so I have to dowdload devkit so it would "make" it has instruction on how to install.

installed devkit: unzipped rubyforge.org/frs/download.php/66888/devkit-3.4.5r3-20091110.7z 
into c:\ruby to get ruby-odbc to build
    C:\>gem install ruby-odbc  
        -- which built and installed v. 0.999



-------------------------------------------------
Let's see what's installed.

C:\>gem list --local                       
                                                                     
*** LOCAL GEMS ***                         
                                                                     
actionmailer (2.3.5)                       
actionpack (2.3.5)                         
activerecord (2.3.5)                       
activerecord-sqlserver-adapter (2.3.4)     
activeresource (2.3.5)                     
activesupport (2.3.5)                      
cgi_multipart_eof_fix (2.5.0)              
gem_plugin (0.2.3)                         
mongrel (1.1.5)                            
mongrel_service (0.3.4)                    
mysql (2.8.1)                              
rack (1.0.1)                               
rails (2.3.5)                              
rake (0.8.7)                               
ruby-odbc (0.9999)                         
rubygems-update (1.3.6)                    
sqlite3-ruby (1.2.5)                       
win32-service (0.5.2)                      
-------------------------------------------------

create a database on your SQL Server

Open your SQL Server client tool and create a database called sqlserverapp

Add a user with SQL Server authentication called user with secret as password to the database you just created.

Create an ODBC link name sqlserverapp to connect to this database.

Then you have to edit your database configuration file to tell rails to use SQL Server and your new database.

Create an ODBC link name sqlserverapp to connect to this database.

Edit the database.yml file.

Change the section under development with the following :

    development:
    adapter: sqlserver
    mode: odbc
    dsn: sqlserverapp 
    username: user
    password: secret
    host: mycomputer


Note : replace mycomputer with the name of your instance of SQL Server (in most cases, locahost should do the job).


You should be god to go.

Please see Rails 2 and SQL Server 2008 on Windows made simple for more information on connecting to the MS Sequel Server.

Friday, March 19, 2010

84

I rode a whopping 84 miles this week. I should be ready for the MS150 in may. I'm still looking for donations.

Wednesday, February 24, 2010

I rode with the Parker College guys today. We had a blast, but they hurt me today.
conditions: 40°F Partly Cloudy Wind: N at 10 mph Humidity: 43%

Tuesday, February 23, 2010

ride for 2010-02-23

I had a nice ride today. I felt like I had a lot of energy today. Always nice to ride with someone, although I think I might have hurt him.
38°F Cloudy Wind: N at 15 mph Humidity: 55% Windchill: 32°F

Data from my ride.

Wednesday, January 13, 2010

Friday, September 04, 2009

I Fell Off My Bicycle.

On Monday (2009-08-31) I was riding down the hill/bridge when I turned the corner to get off of NW Highway I hit a spot of water, that I didn't see, and the next thing I knew I was sliding across the road. Luckily there was a guy in a pickup truck that was at the stop sign and made sure I was OK and gave me a ride back to the office. Mike Owens got a bunch of First Aid stuff to patch me up. I didn't hit my head.

My bike doesn’t seem too bad. The brakes a rubbing and I need to adjust my rear derailleur. It’s clicking when I pedal. I’m going to take it to the shop to have them look at it. Also get it tuned up since it’s been a few years since I’ve done that.

Here are some pictures.
Shortly after it happened

The next day.

Wednesday, August 26, 2009

creating PDF and fonts





ColdFusion - Creating PDFs and CSS


original code.

<cfoutput>

<cfdocument format="PDF" bookmark="yes">

<style>

<!---
td{border-bottom: 1px solid ##cccccc; font-size: 10pt;}

-->
</style>

<cfloop query="GetRemotes">

 <cfdocumentsection name="#remoteLocationsMailingAddressRet.name#">

        <table width="100%" border="0" align="center" cellpadding="1" cellspacing="1">
            <tr valign="bottom">
                <td>#remoteLocationsMailingAddressRet.name#</td>
            </tr>
        </table>
 </cfdocumentsection>

</cfloop>

</cfdocument>

</cfoutput>


I noticed that when I commented out the <cfdocumentsection> tag the
CSS would be there. 


So I moved my <style> inside the <cfdocumentsection> tag and it uses
the CSS. :-)

go figure. 


so I now have 

------------------------------------
<cfoutput>

<cfdocument format="PDF" bookmark="yes">

<cfloop query="GetRemotes">

 <cfdocumentsection name="#remoteLocationsMailingAddressRet.name#">

<style>


<!---
td{border-bottom: 1px solid ##cccccc; font-size: 10pt;}


-->

</style>

 <cfdocumentsection name="#remoteLocationsMailingAddressRet.name#">

        <table width="100%" border="0" align="center" cellpadding="1" cellspacing="1">
            <tr valign="bottom">
                <td>#remoteLocationsMailingAddressRet.name#</td>
            </tr>
        </table>
 </cfdocumentsection>

</cfloop>

</cfdocument>

</cfoutput>



Smashing!

Wednesday, July 08, 2009

Wednesday, February 18, 2009

The Official AMBER Alert for iPhone

AMBER Alert is the official iPhone AMBER Alert notification tool for AMBERalert.com. AMBER Alerts are issued whenever a missing child case is granted AMBER status and has been used to aide in the recovery of missing children. The iPhone application provides a real-time feed of recent alerts including victim photos and information, suspect photos and descriptions, vehicle photos and descriptions, and a reporting mechanism allowing you to report a sighting.

Tuesday, February 17, 2009

Secure your ColdFusion application against SQL injection attacks

Adobe - Developer Center : Secure your ColdFusion application against SQL injection attacks

I'm rewriting a CF app and it's scary how vulnerable this app was. I wish tutorials would start off with security. I know it's not fun or sexy but it's needed.