├── lib └── tasks │ ├── .gitkeep │ └── cucumber.rake ├── public ├── favicon.ico ├── javascripts │ ├── .gitkeep │ └── application.js ├── stylesheets │ └── .gitkeep ├── images │ └── rails.png ├── robots.txt ├── 422.html ├── 404.html └── 500.html ├── .rspec ├── vendor └── plugins │ └── .gitkeep ├── .rvmrc ├── app ├── helpers │ ├── articles_helper.rb │ └── application_helper.rb ├── views │ ├── articles │ │ ├── new.html.erb │ │ ├── edit.html.erb │ │ ├── show.html.erb │ │ ├── index.html.erb │ │ └── _form.html.erb │ └── layouts │ │ └── application.html.erb ├── controllers │ ├── application_controller.rb │ └── articles_controller.rb └── models │ ├── article.rb │ └── user.rb ├── autotest └── discover.rb ├── .gitignore ├── spec ├── controllers │ └── articles_controller_spec.rb ├── models │ ├── user_spec.rb │ └── article_spec.rb ├── factories.rb └── spec_helper.rb ├── config.ru ├── config ├── environment.rb ├── initializers │ ├── mime_types.rb │ ├── inflections.rb │ ├── session_store.rb │ ├── backtrace_silencers.rb │ ├── secret_token.rb │ └── devise.rb ├── locales │ ├── en.yml │ └── devise.en.yml ├── boot.rb ├── cucumber.yml ├── database.yml ├── environments │ ├── development.rb │ ├── test.rb │ └── production.rb ├── routes.rb └── application.rb ├── doc └── README_FOR_APP ├── db ├── migrate │ ├── 20100917022609_add_user_id_to_articles.rb │ ├── 20100903014530_create_articles.rb │ └── 20100917004442_devise_create_users.rb ├── seeds.rb └── schema.rb ├── Rakefile ├── features ├── step_definitions │ ├── article_steps.rb │ ├── common_steps.rb │ ├── email_steps.rb │ └── web_steps.rb ├── editing_article.feature ├── homepage.feature ├── support │ ├── paths.rb │ └── env.rb ├── create_article.feature └── new_user_signup.feature ├── script ├── rails └── cucumber ├── Gemfile ├── Gemfile.lock └── README /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/javascripts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm use 1.8.7@bddclass 2 | -------------------------------------------------------------------------------- /public/javascripts/application.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/articles_helper.rb: -------------------------------------------------------------------------------- 1 | module ArticlesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /autotest/discover.rb: -------------------------------------------------------------------------------- 1 | Autotest.add_discovery { "rails" } 2 | Autotest.add_discovery { "rspec2" } 3 | -------------------------------------------------------------------------------- /public/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edebill/intro_bdd/master/public/images/rails.png -------------------------------------------------------------------------------- /app/views/articles/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Article

2 | 3 | <%= render 'form', :article => @article %> 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.sqlite3 3 | log/*.log 4 | tmp/**/* 5 | .#* 6 | *# 7 | *~ 8 | capybara-*.html 9 | 10 | -------------------------------------------------------------------------------- /spec/controllers/articles_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ArticlesController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /app/views/articles/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing "<%= @article.title %>"

2 | 3 | <%= render 'form', :article => @article %> 4 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe User do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /app/views/articles/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @article.title %> (<%= link_to "Edit", edit_article_path(@article) %>)

2 |

<%= @article.body %>

3 | -------------------------------------------------------------------------------- /app/models/article.rb: -------------------------------------------------------------------------------- 1 | class Article < ActiveRecord::Base 2 | belongs_to :user 3 | 4 | validates_presence_of :title, :body 5 | validates_uniqueness_of :title 6 | end 7 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Blog::Application 5 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Blog::Application.initialize! 6 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /db/migrate/20100917022609_add_user_id_to_articles.rb: -------------------------------------------------------------------------------- 1 | class AddUserIdToArticles < ActiveRecord::Migration 2 | def self.up 3 | add_column :articles, :user_id, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :articles, :user_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | require 'rake' 6 | 7 | Blog::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/views/articles/index.html.erb: -------------------------------------------------------------------------------- 1 |

My Blog

2 |
3 | <% @articles.each do |article| %> 4 |
5 |

<%= link_to article.title, article %>

6 |

<%= article.user.email %>

7 |
8 | <% end %> 9 |
10 | -------------------------------------------------------------------------------- /features/step_definitions/article_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^the article "([^"]*)" was written by "([^"]*)"$/ do |title, email| 2 | article = Article.find_by_title(title) || Factory(:article, :title => title) 3 | article.user = User.find_by_email(email) || Factory(:confirmed_user, :email => email) 4 | article.save 5 | end 6 | 7 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /db/migrate/20100903014530_create_articles.rb: -------------------------------------------------------------------------------- 1 | class CreateArticles < ActiveRecord::Migration 2 | def self.up 3 | create_table :articles do |t| 4 | t.string :title 5 | t.text :body 6 | 7 | t.timestamps 8 | end 9 | end 10 | 11 | def self.down 12 | drop_table :articles 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/views/articles/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for article do |f| %> 2 | 12 | 13 | <%= f.submit "Submit" %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /script/cucumber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 4 | if vendored_cucumber_bin 5 | load File.expand_path(vendored_cucumber_bin) 6 | else 7 | require 'rubygems' unless ENV['NO_RUBYGEMS'] 8 | require 'cucumber' 9 | load Cucumber::BINARY 10 | end 11 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) 7 | # Mayor.create(:name => 'Daley', :city => cities.first) 8 | -------------------------------------------------------------------------------- /spec/models/article_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Article do 4 | 5 | context "validations" do 6 | before do 7 | @article = Factory(:article) 8 | end 9 | it {@article.should validate_presence_of(:title)} 10 | it {@article.should validate_presence_of(:body)} 11 | it {@article.should validate_uniqueness_of(:title)} 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | gemfile = File.expand_path('../../Gemfile', __FILE__) 5 | begin 6 | ENV['BUNDLE_GEMFILE'] = gemfile 7 | require 'bundler' 8 | Bundler.setup 9 | rescue Bundler::GemNotFound => e 10 | STDERR.puts e.message 11 | STDERR.puts "Try running `bundle install`." 12 | exit! 13 | end if File.exist?(gemfile) 14 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Blog::Application.config.session_store :cookie_store, :key => '_blog_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rake db:sessions:create") 8 | # Blog::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/cucumber.yml: -------------------------------------------------------------------------------- 1 | <% 2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" 3 | rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" 4 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --tags ~@wip" 5 | %> 6 | default: --format=pretty 7 | wip: --tags @wip:3 --wip features 8 | rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip 9 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | # Include default devise modules. Others available are: 3 | # :token_authenticatable, :confirmable, :lockable and :timeoutable 4 | devise :database_authenticatable, :registerable, 5 | :recoverable, :rememberable, :trackable, :validatable, 6 | :confirmable 7 | 8 | # Setup accessible (or protected) attributes for your model 9 | attr_accessible :email, :password, :password_confirmation, :remember_me 10 | 11 | has_many :articles 12 | 13 | end 14 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Blog::Application.config.secret_token = '620b9363678ab266ec466bc167b0d237176a784445ff5058833612641e983789801bdc4ed79a7a0573f19950c6c8d402857979041f604aac3b2ae097557f73d8' 8 | -------------------------------------------------------------------------------- /features/step_definitions/common_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^I am logged in as "([^\"]*)"$/ do |email| 2 | Given %|I am on the homepage| 3 | When %|I follow "Sign in"| 4 | When %|I fill in "Email" with "#{email}"| 5 | When %|I fill in "Password" with "password"| 6 | When %|I press "Sign in"| 7 | end 8 | 9 | Then /^I should see awesome rounded corners$/ do 10 | page.should have_css(".rounded") 11 | end 12 | 13 | Then /^I should see the list of (.*)$/ do |model| 14 | page.should have_css("##{model}") 15 | end 16 | 17 | Then "I debug" do 18 | debugger 19 | true 20 | end 21 | 22 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3-ruby (not necessary on OS X Leopard) 3 | development: 4 | adapter: sqlite3 5 | database: db/development.sqlite3 6 | pool: 5 7 | timeout: 5000 8 | 9 | # Warning: The database defined as "test" will be erased and 10 | # re-generated from your development database when you run "rake". 11 | # Do not set this db to the same as development or production. 12 | test: &test 13 | adapter: sqlite3 14 | database: db/test.sqlite3 15 | pool: 5 16 | timeout: 5000 17 | 18 | production: 19 | adapter: sqlite3 20 | database: db/production.sqlite3 21 | pool: 5 22 | timeout: 5000 23 | 24 | cucumber: 25 | <<: *test -------------------------------------------------------------------------------- /db/migrate/20100917004442_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration 2 | def self.up 3 | create_table(:users) do |t| 4 | t.database_authenticatable :null => false 5 | t.recoverable 6 | t.rememberable 7 | t.trackable 8 | 9 | t.confirmable 10 | # t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both 11 | # t.token_authenticatable 12 | 13 | 14 | t.timestamps 15 | end 16 | 17 | add_index :users, :email, :unique => true 18 | add_index :users, :reset_password_token, :unique => true 19 | add_index :users, :confirmation_token, :unique => true 20 | # add_index :users, :unlock_token, :unique => true 21 | end 22 | 23 | def self.down 24 | drop_table :users 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /features/editing_article.feature: -------------------------------------------------------------------------------- 1 | Feature: Editing an existing article 2 | In order to update existing content and avoid irrelevance 3 | As a blog owner 4 | I want to edit an article 5 | 6 | Scenario: Editing existing article 7 | Given the following confirmed user exists: 8 | | Email | 9 | | user@example.com | 10 | 11 | Given the following article exists: 12 | | Title | 13 | | Existing article | 14 | 15 | When I am logged in as "user@example.com" 16 | And I am on the home page 17 | And I follow "Existing article" 18 | And I follow "Edit" 19 | And I fill in "Title" with "Better title" 20 | And I press "Submit" 21 | 22 | Then I should be on the home page 23 | And I should see "Article updated" 24 | And I should see "Better title" 25 | # Then show me the page 26 | 27 | -------------------------------------------------------------------------------- /features/homepage.feature: -------------------------------------------------------------------------------- 1 | Feature: Hello World 2 | In order to make sure cucumber is running 3 | As a web browser 4 | I want my test to check for Hello World 5 | 6 | Background: 7 | Given the following article exists: 8 | | title | body | 9 | | Some title | somebody | 10 | And the article "Some title" was written by "user@example.com" 11 | 12 | Scenario: Viewing the page 13 | 14 | Given I am on the home page 15 | Then I should see "Some title" 16 | And I should not see "somebody" 17 | And I should not see "Hello World" 18 | And I should see awesome rounded corners 19 | And I should see the list of articles 20 | 21 | When I follow "Some title" 22 | Then I should see "somebody" 23 | 24 | Scenario: Articles with authors 25 | Given I am on the home page 26 | Then I should see "user@example.com" within "article .author" 27 | 28 | -------------------------------------------------------------------------------- /spec/factories.rb: -------------------------------------------------------------------------------- 1 | Factory.sequence(:some_seq) 2 | Factory.define(:article) do |f| 3 | f.body "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 4 | f.sequence(:title) {|i| "Title #{i}"} 5 | f.association(:user, :factory => :confirmed_user) 6 | end 7 | 8 | Factory.define(:user) do |f| 9 | f.sequence(:email) { |i| "user#{i}@example.com" } 10 | f.password "password" 11 | f.password_confirmation "password" 12 | end 13 | 14 | Factory.define(:confirmed_user, :parent => :user) do |f| 15 | f.confirmed_at Time.zone.now 16 | end 17 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= 'test' 2 | require File.expand_path("../../config/environment", __FILE__) 3 | require 'rspec/rails' 4 | 5 | # Requires supporting ruby files with custom matchers and macros, etc, 6 | # in spec/support/ and its subdirectories. 7 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 8 | 9 | RSpec.configure do |config| 10 | # == Mock Framework 11 | # 12 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 13 | # 14 | # config.mock_with :mocha 15 | # config.mock_with :flexmock 16 | # config.mock_with :rr 17 | config.mock_with :rspec 18 | 19 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 20 | 21 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 22 | # examples within a transaction, comment the following line or assign false 23 | # instead of true. 24 | config.use_transactional_fixtures = true 25 | end 26 | -------------------------------------------------------------------------------- /features/support/paths.rb: -------------------------------------------------------------------------------- 1 | module NavigationHelpers 2 | # Maps a name to a path. Used by the 3 | # 4 | # When /^I go to (.+)$/ do |page_name| 5 | # 6 | # step definition in web_steps.rb 7 | # 8 | def path_to(page_name) 9 | case page_name 10 | 11 | when /the home\s?page/ 12 | '/' 13 | 14 | # Add more mappings here. 15 | # Here is an example that pulls values out of the Regexp: 16 | # 17 | # when /^(.*)'s profile page$/i 18 | # user_profile_path(User.find_by_login($1)) 19 | 20 | else 21 | begin 22 | page_name =~ /the (.*) page/ 23 | path_components = $1.split(/\s+/) 24 | self.send(path_components.push('path').join('_').to_sym) 25 | rescue Object => e 26 | raise "Can't find mapping from \"#{page_name}\" to a path.\n" + 27 | "Now, go and add a mapping in #{__FILE__}" 28 | end 29 | end 30 | end 31 | end 32 | 33 | World(NavigationHelpers) 34 | -------------------------------------------------------------------------------- /features/create_article.feature: -------------------------------------------------------------------------------- 1 | Feature: Posting a new article 2 | In order to put new advertising-generating content on the web 3 | As a blog owner 4 | I want to post new articles 5 | 6 | Scenario: Posting an article 7 | Given the following confirmed user exists: 8 | | Email | 9 | | user@example.com | 10 | When I am logged in as "user@example.com" 11 | When I follow "New post" 12 | Then I should be on the new article page 13 | And I should see "New Article" 14 | 15 | When I fill in "Title" with "Some title" 16 | And I fill in "Body" with "lorem ipsum solarem dolor meso" 17 | And I press "Submit" 18 | 19 | Then I should be on the home page 20 | And I should see "New article created" 21 | And I should see "Some title" 22 | 23 | Scenario: Maliciously posting an article 24 | When I go to the new article page 25 | Then I should see "Sign in" 26 | And I should be on the new user session page 27 | 28 | -------------------------------------------------------------------------------- /app/controllers/articles_controller.rb: -------------------------------------------------------------------------------- 1 | class ArticlesController < ApplicationController 2 | before_filter :authenticate_user!, :except => [:index, :show] 3 | 4 | def index 5 | @articles = Article.all 6 | end 7 | 8 | def show 9 | @article = Article.find params[:id] 10 | end 11 | 12 | def new 13 | @article = Article.new 14 | end 15 | 16 | def edit 17 | @article = Article.find(params[:id]) 18 | end 19 | 20 | def create 21 | @article = Article.new params[:article] 22 | @article.user_id = current_user.id 23 | if @article.valid? 24 | @article.save 25 | redirect_to root_url, :notice => "New article created" 26 | else 27 | render :new 28 | end 29 | end 30 | 31 | def update 32 | @article = Article.new params[:article] 33 | @article.user_id = current_user.id 34 | if @article.valid? 35 | @article.save 36 | redirect_to root_url, :notice => "Article updated." 37 | else 38 | render :new 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blog 5 | <%= stylesheet_link_tag :all %> 6 | <%= javascript_include_tag :defaults %> 7 | <%= csrf_meta_tag %> 8 | 9 | 10 |
11 | <% if user_signed_in? %> 12 |

Welcome <%= current_user.email %>

13 | 19 | <% else %> 20 | 26 | 27 | <% end %> 28 | 29 |
30 | <% [:notice, :error].each do |flashtype| %> 31 |
32 | <%= flash[flashtype] %> 33 |
34 | <% end %> 35 | 36 |
37 | <%= yield %> 38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Blog::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the webserver when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_view.debug_rjs = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Don't care if the mailer can't send 18 | config.action_mailer.raise_delivery_errors = false 19 | 20 | # Print deprecation notices to the Rails logger 21 | config.active_support.deprecation = :log 22 | 23 | # Only use best-standards-support built into browsers 24 | config.action_dispatch.best_standards_support = :builtin 25 | 26 | config.action_mailer.default_url_options = { :host => 'localhost:3000' } 27 | end 28 | 29 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'rails', '3.0.0' 4 | gem 'devise', "~> 1.1.2" 5 | 6 | # Bundle edge Rails instead: 7 | # gem 'rails', :git => 'git://github.com/rails/rails.git' 8 | 9 | gem 'sqlite3-ruby', :require => 'sqlite3' 10 | 11 | # Use unicorn as the web server 12 | # gem 'unicorn' 13 | 14 | # Deploy with Capistrano 15 | # gem 'capistrano' 16 | 17 | # To use debugger 18 | # gem 'ruby-debug' 19 | 20 | # Bundle the extra gems: 21 | # gem 'bj' 22 | # gem 'nokogiri' 23 | # gem 'sqlite3-ruby', :require => 'sqlite3' 24 | # gem 'aws-s3', :require => 'aws/s3' 25 | 26 | 27 | 28 | # Bundle gems for the local environment. Make sure to 29 | # put test-only gems in this group so their generators 30 | # and rake tasks are available in development mode: 31 | # group :development, :test do 32 | # gem 'webrat' 33 | # end 34 | group :test, :development do 35 | gem "rspec-rails", "= 2.0.0.beta.20" 36 | gem "shoulda" 37 | gem "factory_girl_rails" 38 | gem "email_spec" 39 | end 40 | 41 | group :cucumber do 42 | gem 'capybara' 43 | gem 'database_cleaner' 44 | gem 'cucumber-rails' 45 | gem 'cucumber' 46 | gem 'rspec-rails', "= 2.0.0.beta.20" 47 | gem 'spork' 48 | gem 'launchy' # So you can do Then show me the page 49 | gem "factory_girl_rails" 50 | gem "ruby-debug" 51 | gem "email_spec" 52 | end 53 | -------------------------------------------------------------------------------- /features/new_user_signup.feature: -------------------------------------------------------------------------------- 1 | Feature: New user signing up 2 | In order to keep spammers/malcontents from posting to my blog 3 | As an unregistered user 4 | I want to sign up for an account 5 | 6 | Scenario: Signing up as an unregistered user 7 | When I go to the home page 8 | And I follow "Sign up" 9 | 10 | Then I should be on the new user registration page 11 | When I fill in "Email" with "user@example.com" 12 | And I fill in "Password" with "password" 13 | And I fill in "Password confirmation" with "password" 14 | And I press "Sign up" 15 | 16 | Then I should be on the new user session page 17 | And I should see "a confirmation was sent to your e-mail" 18 | 19 | And "user@example.com" should receive an email with subject "Confirmation instructions" 20 | When "user@example.com" opens the email with subject "Confirmation instructions" 21 | And I click the first link in the email 22 | 23 | Then I should be on the home page 24 | And I should see "Welcome to your blog" 25 | 26 | 27 | Scenario: Signing in as a registered user 28 | Given the following confirmed user exists: 29 | |Email | 30 | |user@example.com | 31 | 32 | When I go to the home page 33 | And I follow "Sign in" 34 | And I fill in "Email" with "user@example.com" 35 | And I fill in "Password" with "password" 36 | And I press "Sign in" 37 | 38 | Then I should be on the home page 39 | And I should see "Signed in successfully" 40 | And I should see "Sign out" 41 | And I should not see "Sign up" 42 | And I should not see "Sign in" 43 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Blog::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Log error messages when you accidentally call methods on nil. 11 | config.whiny_nils = true 12 | 13 | # Show full error reports and disable caching 14 | config.consider_all_requests_local = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Raise exceptions instead of rendering exception templates 18 | config.action_dispatch.show_exceptions = false 19 | 20 | # Disable request forgery protection in test environment 21 | config.action_controller.allow_forgery_protection = false 22 | 23 | # Tell Action Mailer not to deliver emails to the real world. 24 | # The :test delivery method accumulates sent emails in the 25 | # ActionMailer::Base.deliveries array. 26 | config.action_mailer.delivery_method = :test 27 | 28 | # Use SQL instead of Active Record's schema dumper when creating the test database. 29 | # This is necessary if your schema can't be completely dumped by the schema dumper, 30 | # like if you have constraints or database-specific column types 31 | # config.active_record.schema_format = :sql 32 | 33 | # Print deprecation notices to the stderr 34 | config.active_support.deprecation = :stderr 35 | 36 | 37 | 38 | config.action_mailer.default_url_options = { :host => 'localhost:3000' } 39 | end 40 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Blog::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The production environment is meant for finished, "live" apps. 5 | # Code is not reloaded between requests 6 | config.cache_classes = true 7 | 8 | # Full error reports are disabled and caching is turned on 9 | config.consider_all_requests_local = false 10 | config.action_controller.perform_caching = true 11 | 12 | # Specifies the header that your server uses for sending files 13 | config.action_dispatch.x_sendfile_header = "X-Sendfile" 14 | 15 | # For nginx: 16 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' 17 | 18 | # If you have no front-end server that supports something like X-Sendfile, 19 | # just comment this out and Rails will serve the files 20 | 21 | # See everything in the log (default is :info) 22 | # config.log_level = :debug 23 | 24 | # Use a different logger for distributed setups 25 | # config.logger = SyslogLogger.new 26 | 27 | # Use a different cache store in production 28 | # config.cache_store = :mem_cache_store 29 | 30 | # Disable Rails's static asset server 31 | # In production, Apache or nginx will already do this 32 | config.serve_static_assets = false 33 | 34 | # Enable serving of images, stylesheets, and javascripts from an asset server 35 | # config.action_controller.asset_host = "http://assets.example.com" 36 | 37 | # Disable delivery errors, bad email addresses will be ignored 38 | # config.action_mailer.raise_delivery_errors = false 39 | 40 | # Enable threaded mode 41 | # config.threadsafe! 42 | 43 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 44 | # the I18n.default_locale when a translation can not be found) 45 | config.i18n.fallbacks = true 46 | 47 | # Send deprecation notices to registered listeners 48 | config.active_support.deprecation = :notify 49 | end 50 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | errors: 3 | messages: 4 | not_found: "not found" 5 | already_confirmed: "was already confirmed" 6 | not_locked: "was not locked" 7 | 8 | devise: 9 | failure: 10 | unauthenticated: 'You need to sign in or sign up before continuing.' 11 | unconfirmed: 'You have to confirm your account before continuing.' 12 | locked: 'Your account is locked.' 13 | invalid: 'Invalid email or password.' 14 | invalid_token: 'Invalid authentication token.' 15 | timeout: 'Your session expired, please sign in again to continue.' 16 | inactive: 'Your account was not activated yet.' 17 | sessions: 18 | signed_in: 'Signed in successfully.' 19 | signed_out: 'Signed out successfully.' 20 | passwords: 21 | send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.' 22 | updated: 'Your password was changed successfully. You are now signed in.' 23 | confirmations: 24 | send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.' 25 | confirmed: 'Your account was successfully confirmed. You are now signed in. Welcome to your blog.' 26 | registrations: 27 | signed_up: 'You have signed up successfully. If enabled, a confirmation was sent to your e-mail.' 28 | updated: 'You updated your account successfully.' 29 | destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.' 30 | unlocks: 31 | send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.' 32 | unlocked: 'Your account was successfully unlocked. You are now signed in.' 33 | mailer: 34 | confirmation_instructions: 35 | subject: 'Confirmation instructions' 36 | reset_password_instructions: 37 | subject: 'Reset password instructions' 38 | unlock_instructions: 39 | subject: 'Unlock Instructions' 40 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Blog::Application.routes.draw do 2 | devise_for :users 3 | 4 | # The priority is based upon order of creation: 5 | # first created -> highest priority. 6 | 7 | # Sample of regular route: 8 | # match 'products/:id' => 'catalog#view' 9 | # Keep in mind you can assign values other than :controller and :action 10 | 11 | # Sample of named route: 12 | # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase 13 | # This route can be invoked with purchase_url(:id => product.id) 14 | 15 | # Sample resource route (maps HTTP verbs to controller actions automatically): 16 | resources :articles 17 | 18 | # Sample resource route with options: 19 | # resources :products do 20 | # member do 21 | # get 'short' 22 | # post 'toggle' 23 | # end 24 | # 25 | # collection do 26 | # get 'sold' 27 | # end 28 | # end 29 | 30 | # Sample resource route with sub-resources: 31 | # resources :products do 32 | # resources :comments, :sales 33 | # resource :seller 34 | # end 35 | 36 | # Sample resource route with more complex sub-resources 37 | # resources :products do 38 | # resources :comments 39 | # resources :sales do 40 | # get 'recent', :on => :collection 41 | # end 42 | # end 43 | 44 | # Sample resource route within a namespace: 45 | # namespace :admin do 46 | # # Directs /admin/products/* to Admin::ProductsController 47 | # # (app/controllers/admin/products_controller.rb) 48 | # resources :products 49 | # end 50 | 51 | # You can have the root of your site routed with "root" 52 | # just remember to delete public/index.html. 53 | root :to => "articles#index" 54 | 55 | # See how all your routes lay out with "rake routes" 56 | 57 | # This is a legacy wild controller route that's not recommended for RESTful applications. 58 | # Note: This route will make all actions in every controller accessible via GET requests. 59 | # match ':controller(/:action(/:id(.:format)))' 60 | end 61 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # If you have a Gemfile, require the gems listed there, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(:default, Rails.env) if defined?(Bundler) 8 | 9 | module Blog 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Custom directories with classes and modules you want to be autoloadable. 16 | # config.autoload_paths += %W(#{config.root}/extras) 17 | 18 | # Only load the plugins named here, in the order given (default is alphabetical). 19 | # :all can be used as a placeholder for all plugins not explicitly named. 20 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 21 | 22 | # Activate observers that should always be running. 23 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 24 | 25 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 26 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 27 | # config.time_zone = 'Central Time (US & Canada)' 28 | 29 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 30 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 31 | # config.i18n.default_locale = :de 32 | 33 | # JavaScript files you want as :defaults (application.js is always included). 34 | config.action_view.javascript_expansions[:defaults] = %w() 35 | 36 | # Configure the default encoding used in templates for Ruby 1.9. 37 | config.encoding = "utf-8" 38 | 39 | # Configure sensitive parameters which will be filtered from the log file. 40 | config.filter_parameters += [:password] 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/tasks/cucumber.rake: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | 8 | unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks 9 | 10 | vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 11 | $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? 12 | 13 | begin 14 | require 'cucumber/rake/task' 15 | 16 | namespace :cucumber do 17 | Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| 18 | t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. 19 | t.fork = true # You may get faster startup if you set this to false 20 | t.profile = 'default' 21 | end 22 | 23 | Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| 24 | t.binary = vendored_cucumber_bin 25 | t.fork = true # You may get faster startup if you set this to false 26 | t.profile = 'wip' 27 | end 28 | 29 | Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t| 30 | t.binary = vendored_cucumber_bin 31 | t.fork = true # You may get faster startup if you set this to false 32 | t.profile = 'rerun' 33 | end 34 | 35 | desc 'Run all features' 36 | task :all => [:ok, :wip] 37 | end 38 | desc 'Alias for cucumber:ok' 39 | task :cucumber => 'cucumber:ok' 40 | 41 | task :default => :cucumber 42 | 43 | task :features => :cucumber do 44 | STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" 45 | end 46 | rescue LoadError 47 | desc 'cucumber rake task not available (cucumber not installed)' 48 | task :cucumber do 49 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' 50 | end 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended to check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(:version => 20100917022609) do 14 | 15 | create_table "articles", :force => true do |t| 16 | t.string "title" 17 | t.text "body" 18 | t.datetime "created_at" 19 | t.datetime "updated_at" 20 | t.integer "user_id" 21 | end 22 | 23 | create_table "users", :force => true do |t| 24 | t.string "email", :default => "", :null => false 25 | t.string "encrypted_password", :limit => 128, :default => "", :null => false 26 | t.string "password_salt", :default => "", :null => false 27 | t.string "reset_password_token" 28 | t.string "remember_token" 29 | t.datetime "remember_created_at" 30 | t.integer "sign_in_count", :default => 0 31 | t.datetime "current_sign_in_at" 32 | t.datetime "last_sign_in_at" 33 | t.string "current_sign_in_ip" 34 | t.string "last_sign_in_ip" 35 | t.string "confirmation_token" 36 | t.datetime "confirmed_at" 37 | t.datetime "confirmation_sent_at" 38 | t.datetime "created_at" 39 | t.datetime "updated_at" 40 | end 41 | 42 | add_index "users", ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true 43 | add_index "users", ["email"], :name => "index_users_on_email", :unique => true 44 | add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true 45 | 46 | end 47 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | ENV["RAILS_ENV"] ||= "test" 8 | require File.expand_path(File.dirname(__FILE__) + '/../../config/environment') 9 | 10 | require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support 11 | require 'cucumber/rails/rspec' 12 | require 'cucumber/rails/world' 13 | require 'cucumber/rails/active_record' 14 | require 'cucumber/web/tableish' 15 | 16 | require 'capybara/rails' 17 | require 'capybara/cucumber' 18 | require 'capybara/session' 19 | require 'cucumber/rails/capybara_javascript_emulation' # Lets you click links with onclick javascript handlers without using @culerity or @javascript 20 | require 'email_spec/cucumber' 21 | require 'factory_girl/step_definitions' 22 | 23 | # Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In 24 | # order to ease the transition to Capybara we set the default here. If you'd 25 | # prefer to use XPath just remove this line and adjust any selectors in your 26 | # steps to use the XPath syntax. 27 | Capybara.default_selector = :css 28 | 29 | # If you set this to false, any error raised from within your app will bubble 30 | # up to your step definition and out to cucumber unless you catch it somewhere 31 | # on the way. You can make Rails rescue errors and render error pages on a 32 | # per-scenario basis by tagging a scenario or feature with the @allow-rescue tag. 33 | # 34 | # If you set this to true, Rails will rescue all errors and render error 35 | # pages, more or less in the same way your application would behave in the 36 | # default production environment. It's not recommended to do this for all 37 | # of your scenarios, as this makes it hard to discover errors in your application. 38 | ActionController::Base.allow_rescue = false 39 | 40 | # If you set this to true, each scenario will run in a database transaction. 41 | # You can still turn off transactions on a per-scenario basis, simply tagging 42 | # a feature or scenario with the @no-txn tag. If you are using Capybara, 43 | # tagging with @culerity or @javascript will also turn transactions off. 44 | # 45 | # If you set this to false, transactions will be off for all scenarios, 46 | # regardless of whether you use @no-txn or not. 47 | # 48 | # Beware that turning transactions off will leave data in your database 49 | # after each scenario, which can lead to hard-to-debug failures in 50 | # subsequent scenarios. If you do this, we recommend you create a Before 51 | # block that will explicitly put your database in a known state. 52 | Cucumber::Rails::World.use_transactional_fixtures = true 53 | # How to clean your database when transactions are turned off. See 54 | # http://github.com/bmabey/database_cleaner for more info. 55 | if defined?(ActiveRecord::Base) 56 | begin 57 | require 'database_cleaner' 58 | DatabaseCleaner.strategy = :truncation 59 | rescue LoadError => ignore_if_database_cleaner_not_present 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | abstract (1.0.0) 5 | actionmailer (3.0.0) 6 | actionpack (= 3.0.0) 7 | mail (~> 2.2.5) 8 | actionpack (3.0.0) 9 | activemodel (= 3.0.0) 10 | activesupport (= 3.0.0) 11 | builder (~> 2.1.2) 12 | erubis (~> 2.6.6) 13 | i18n (~> 0.4.1) 14 | rack (~> 1.2.1) 15 | rack-mount (~> 0.6.12) 16 | rack-test (~> 0.5.4) 17 | tzinfo (~> 0.3.23) 18 | activemodel (3.0.0) 19 | activesupport (= 3.0.0) 20 | builder (~> 2.1.2) 21 | i18n (~> 0.4.1) 22 | activerecord (3.0.0) 23 | activemodel (= 3.0.0) 24 | activesupport (= 3.0.0) 25 | arel (~> 1.0.0) 26 | tzinfo (~> 0.3.23) 27 | activeresource (3.0.0) 28 | activemodel (= 3.0.0) 29 | activesupport (= 3.0.0) 30 | activesupport (3.0.0) 31 | arel (1.0.1) 32 | activesupport (~> 3.0.0) 33 | bcrypt-ruby (2.1.2) 34 | builder (2.1.2) 35 | capybara (0.3.9) 36 | culerity (>= 0.2.4) 37 | mime-types (>= 1.16) 38 | nokogiri (>= 1.3.3) 39 | rack (>= 1.0.0) 40 | rack-test (>= 0.5.4) 41 | selenium-webdriver (>= 0.0.3) 42 | columnize (0.3.1) 43 | configuration (1.1.0) 44 | cucumber (0.8.5) 45 | builder (~> 2.1.2) 46 | diff-lcs (~> 1.1.2) 47 | gherkin (~> 2.1.4) 48 | json_pure (~> 1.4.3) 49 | term-ansicolor (~> 1.0.4) 50 | cucumber-rails (0.3.2) 51 | cucumber (>= 0.8.0) 52 | culerity (0.2.12) 53 | database_cleaner (0.5.2) 54 | devise (1.1.2) 55 | bcrypt-ruby (~> 2.1.2) 56 | warden (~> 0.10.7) 57 | diff-lcs (1.1.2) 58 | email_spec (1.0.0) 59 | erubis (2.6.6) 60 | abstract (>= 1.0.0) 61 | factory_girl (1.3.2) 62 | factory_girl_rails (1.0) 63 | factory_girl (~> 1.3) 64 | rails (>= 3.0.0.beta4) 65 | ffi (0.6.3) 66 | rake (>= 0.8.7) 67 | gherkin (2.1.5) 68 | trollop (~> 1.16.2) 69 | i18n (0.4.1) 70 | json_pure (1.4.6) 71 | launchy (0.3.7) 72 | configuration (>= 0.0.5) 73 | rake (>= 0.8.1) 74 | linecache (0.43) 75 | mail (2.2.5) 76 | activesupport (>= 2.3.6) 77 | mime-types 78 | treetop (>= 1.4.5) 79 | mime-types (1.16) 80 | nokogiri (1.4.3.1) 81 | polyglot (0.3.1) 82 | rack (1.2.1) 83 | rack-mount (0.6.13) 84 | rack (>= 1.0.0) 85 | rack-test (0.5.4) 86 | rack (>= 1.0) 87 | rails (3.0.0) 88 | actionmailer (= 3.0.0) 89 | actionpack (= 3.0.0) 90 | activerecord (= 3.0.0) 91 | activeresource (= 3.0.0) 92 | activesupport (= 3.0.0) 93 | bundler (~> 1.0.0) 94 | railties (= 3.0.0) 95 | railties (3.0.0) 96 | actionpack (= 3.0.0) 97 | activesupport (= 3.0.0) 98 | rake (>= 0.8.4) 99 | thor (~> 0.14.0) 100 | rake (0.8.7) 101 | rspec (2.0.0.beta.20) 102 | rspec-core (= 2.0.0.beta.20) 103 | rspec-expectations (= 2.0.0.beta.20) 104 | rspec-mocks (= 2.0.0.beta.20) 105 | rspec-core (2.0.0.beta.20) 106 | rspec-expectations (2.0.0.beta.20) 107 | diff-lcs (>= 1.1.2) 108 | rspec-mocks (2.0.0.beta.20) 109 | rspec-rails (2.0.0.beta.20) 110 | rspec (= 2.0.0.beta.20) 111 | ruby-debug (0.10.3) 112 | columnize (>= 0.1) 113 | ruby-debug-base (~> 0.10.3.0) 114 | ruby-debug-base (0.10.3) 115 | linecache (>= 0.3) 116 | rubyzip (0.9.4) 117 | selenium-webdriver (0.0.28) 118 | ffi (>= 0.6.1) 119 | json_pure 120 | rubyzip 121 | shoulda (2.11.3) 122 | spork (0.8.4) 123 | sqlite3-ruby (1.3.1) 124 | term-ansicolor (1.0.5) 125 | thor (0.14.0) 126 | treetop (1.4.8) 127 | polyglot (>= 0.3.1) 128 | trollop (1.16.2) 129 | tzinfo (0.3.23) 130 | warden (0.10.7) 131 | rack (>= 1.0.0) 132 | 133 | PLATFORMS 134 | ruby 135 | 136 | DEPENDENCIES 137 | capybara 138 | cucumber 139 | cucumber-rails 140 | database_cleaner 141 | devise (~> 1.1.2) 142 | email_spec 143 | factory_girl_rails 144 | launchy 145 | rails (= 3.0.0) 146 | rspec-rails (= 2.0.0.beta.20) 147 | ruby-debug 148 | shoulda 149 | spork 150 | sqlite3-ruby 151 | -------------------------------------------------------------------------------- /features/step_definitions/email_steps.rb: -------------------------------------------------------------------------------- 1 | # Commonly used email steps 2 | # 3 | # To add your own steps make a custom_email_steps.rb 4 | # The provided methods are: 5 | # 6 | # last_email_address 7 | # reset_mailer 8 | # open_last_email 9 | # visit_in_email 10 | # unread_emails_for 11 | # mailbox_for 12 | # current_email 13 | # open_email 14 | # read_emails_for 15 | # find_email 16 | # 17 | # General form for email scenarios are: 18 | # - clear the email queue (done automatically by email_spec) 19 | # - execute steps that sends an email 20 | # - check the user received an/no/[0-9] emails 21 | # - open the email 22 | # - inspect the email contents 23 | # - interact with the email (e.g. click links) 24 | # 25 | # The Cucumber steps below are setup in this order. 26 | 27 | module EmailHelpers 28 | def current_email_address 29 | # Replace with your a way to find your current email. e.g @current_user.email 30 | # last_email_address will return the last email address used by email spec to find an email. 31 | # Note that last_email_address will be reset after each Scenario. 32 | last_email_address || "example@example.com" 33 | end 34 | end 35 | 36 | World(EmailHelpers) 37 | 38 | # 39 | # Reset the e-mail queue within a scenario. 40 | # This is done automatically before each scenario. 41 | # 42 | 43 | Given /^(?:a clear email queue|no emails have been sent)$/ do 44 | reset_mailer 45 | end 46 | 47 | # 48 | # Check how many emails have been sent/received 49 | # 50 | 51 | Then /^(?:I|they|"([^"]*?)") should receive (an|no|\d+) emails?$/ do |address, amount| 52 | unread_emails_for(address).size.should == parse_email_count(amount) 53 | end 54 | 55 | Then /^(?:I|they|"([^"]*?)") should have (an|no|\d+) emails?$/ do |address, amount| 56 | mailbox_for(address).size.should == parse_email_count(amount) 57 | end 58 | 59 | Then /^(?:I|they|"([^"]*?)") should receive (an|no|\d+) emails? with subject "([^"]*?)"$/ do |address, amount, subject| 60 | unread_emails_for(address).select { |m| m.subject =~ Regexp.new(subject) }.size.should == parse_email_count(amount) 61 | end 62 | 63 | Then /^(?:I|they|"([^"]*?)") should receive an email with the following body:$/ do |address, expected_body| 64 | open_email(address, :with_text => expected_body) 65 | end 66 | 67 | # 68 | # Accessing emails 69 | # 70 | 71 | # Opens the most recently received email 72 | When /^(?:I|they|"([^"]*?)") opens? the email$/ do |address| 73 | open_email(address) 74 | end 75 | 76 | When /^(?:I|they|"([^"]*?)") opens? the email with subject "([^"]*?)"$/ do |address, subject| 77 | open_email(address, :with_subject => subject) 78 | end 79 | 80 | When /^(?:I|they|"([^"]*?)") opens? the email with text "([^"]*?)"$/ do |address, text| 81 | open_email(address, :with_text => text) 82 | end 83 | 84 | # 85 | # Inspect the Email Contents 86 | # 87 | 88 | Then /^(?:I|they) should see "([^"]*?)" in the email subject$/ do |text| 89 | current_email.should have_subject(text) 90 | end 91 | 92 | Then /^(?:I|they) should see \/([^"]*?)\/ in the email subject$/ do |text| 93 | current_email.should have_subject(Regexp.new(text)) 94 | end 95 | 96 | Then /^(?:I|they) should see "([^"]*?)" in the email body$/ do |text| 97 | current_email.default_part_body.to_s.should include(text) 98 | end 99 | 100 | Then /^(?:I|they) should see \/([^"]*?)\/ in the email body$/ do |text| 101 | current_email.default_part_body.to_s.should =~ Regexp.new(text) 102 | end 103 | 104 | Then /^(?:I|they) should see the email delivered from "([^"]*?)"$/ do |text| 105 | current_email.should be_delivered_from(text) 106 | end 107 | 108 | Then /^(?:I|they) should see "([^\"]*)" in the email "([^"]*?)" header$/ do |text, name| 109 | current_email.should have_header(name, text) 110 | end 111 | 112 | Then /^(?:I|they) should see \/([^\"]*)\/ in the email "([^"]*?)" header$/ do |text, name| 113 | current_email.should have_header(name, Regexp.new(text)) 114 | end 115 | 116 | Then /^I should see it is a multi\-part email$/ do 117 | current_email.should be_multipart 118 | end 119 | 120 | Then /^(?:I|they) should see "([^"]*?)" in the email html part body$/ do |text| 121 | current_email.html_part.body.to_s.should include(text) 122 | end 123 | 124 | Then /^(?:I|they) should see "([^"]*?)" in the email text part body$/ do |text| 125 | current_email.text_part.body.to_s.should include(text) 126 | end 127 | 128 | # 129 | # Inspect the Email Attachments 130 | # 131 | 132 | Then /^(?:I|they) should see (an|no|\d+) attachments? with the email$/ do |amount| 133 | current_email_attachments.size.should == parse_email_count(amount) 134 | end 135 | 136 | Then /^there should be (an|no|\d+) attachments? named "([^"]*?)"$/ do |amount, filename| 137 | current_email_attachments.select { |a| a.filename == filename }.size.should == parse_email_count(amount) 138 | end 139 | 140 | Then /^attachment (\d+) should be named "([^"]*?)"$/ do |index, filename| 141 | current_email_attachments[(index.to_i - 1)].filename.should == filename 142 | end 143 | 144 | Then /^there should be (an|no|\d+) attachments? of type "([^"]*?)"$/ do |amount, content_type| 145 | current_email_attachments.select { |a| a.content_type.include?(content_type) }.size.should == parse_email_count(amount) 146 | end 147 | 148 | Then /^attachment (\d+) should be of type "([^"]*?)"$/ do |index, content_type| 149 | current_email_attachments[(index.to_i - 1)].content_type.should include(content_type) 150 | end 151 | 152 | Then /^all attachments should not be blank$/ do 153 | current_email_attachments.each do |attachment| 154 | attachment.read.size.should_not == 0 155 | end 156 | end 157 | 158 | Then /^show me a list of email attachments$/ do 159 | EmailSpec::EmailViewer::save_and_open_email_attachments_list(current_email) 160 | end 161 | 162 | # 163 | # Interact with Email Contents 164 | # 165 | 166 | When /^(?:I|they) follow "([^"]*?)" in the email$/ do |link| 167 | visit_in_email(link) 168 | end 169 | 170 | When /^(?:I|they) click the first link in the email$/ do 171 | click_first_link_in_email 172 | end 173 | 174 | # 175 | # Debugging 176 | # These only work with Rails and OSx ATM since EmailViewer uses RAILS_ROOT and OSx's 'open' command. 177 | # Patches accepted. ;) 178 | # 179 | 180 | Then /^save and open current email$/ do 181 | EmailSpec::EmailViewer::save_and_open_email(current_email) 182 | end 183 | 184 | Then /^save and open all text emails$/ do 185 | EmailSpec::EmailViewer::save_and_open_all_text_emails 186 | end 187 | 188 | Then /^save and open all html emails$/ do 189 | EmailSpec::EmailViewer::save_and_open_all_html_emails 190 | end 191 | 192 | Then /^save and open all raw emails$/ do 193 | EmailSpec::EmailViewer::save_and_open_all_raw_emails 194 | end 195 | -------------------------------------------------------------------------------- /config/initializers/devise.rb: -------------------------------------------------------------------------------- 1 | # Use this hook to configure devise mailer, warden hooks and so forth. The first 2 | # four configuration values can also be set straight in your models. 3 | Devise.setup do |config| 4 | # ==> Mailer Configuration 5 | # Configure the e-mail address which will be shown in DeviseMailer. 6 | config.mailer_sender = "please-change-me@config-initializers-devise.com" 7 | 8 | # Configure the class responsible to send e-mails. 9 | # config.mailer = "Devise::Mailer" 10 | 11 | # ==> ORM configuration 12 | # Load and configure the ORM. Supports :active_record (default) and 13 | # :mongoid (bson_ext recommended) by default. Other ORMs may be 14 | # available as additional gems. 15 | require 'devise/orm/active_record' 16 | 17 | # ==> Configuration for any authentication mechanism 18 | # Configure which keys are used when authenticating an user. By default is 19 | # just :email. You can configure it to use [:username, :subdomain], so for 20 | # authenticating an user, both parameters are required. Remember that those 21 | # parameters are used only when authenticating and not when retrieving from 22 | # session. If you need permissions, you should implement that in a before filter. 23 | # config.authentication_keys = [ :email ] 24 | 25 | # Tell if authentication through request.params is enabled. True by default. 26 | # config.params_authenticatable = true 27 | 28 | # Tell if authentication through HTTP Basic Auth is enabled. True by default. 29 | # config.http_authenticatable = true 30 | 31 | # Set this to true to use Basic Auth for AJAX requests. True by default. 32 | # config.http_authenticatable_on_xhr = true 33 | 34 | # The realm used in Http Basic Authentication 35 | # config.http_authentication_realm = "Application" 36 | 37 | # ==> Configuration for :database_authenticatable 38 | # For bcrypt, this is the cost for hashing the password and defaults to 10. If 39 | # using other encryptors, it sets how many times you want the password re-encrypted. 40 | config.stretches = 10 41 | 42 | # Define which will be the encryption algorithm. Devise also supports encryptors 43 | # from others authentication tools as :clearance_sha1, :authlogic_sha512 (then 44 | # you should set stretches above to 20 for default behavior) and :restful_authentication_sha1 45 | # (then you should set stretches to 10, and copy REST_AUTH_SITE_KEY to pepper) 46 | config.encryptor = :bcrypt 47 | 48 | # Setup a pepper to generate the encrypted password. 49 | config.pepper = "a1f3abf4f9a8ca0d34d67686253b1c6589b77e3826f9d00fc088f45e47b5c7e7f6b14db5cd2bb0e402c75d312a85e049c93538bd6a3e20694b88b30a415f5c47" 50 | 51 | # ==> Configuration for :confirmable 52 | # The time you want to give your user to confirm his account. During this time 53 | # he will be able to access your application without confirming. Default is nil. 54 | # When confirm_within is zero, the user won't be able to sign in without confirming. 55 | # You can use this to let your user access some features of your application 56 | # without confirming the account, but blocking it after a certain period 57 | # (ie 2 days). 58 | # config.confirm_within = 2.days 59 | 60 | # ==> Configuration for :rememberable 61 | # The time the user will be remembered without asking for credentials again. 62 | # config.remember_for = 2.weeks 63 | 64 | # If true, a valid remember token can be re-used between multiple browsers. 65 | # config.remember_across_browsers = true 66 | 67 | # If true, extends the user's remember period when remembered via cookie. 68 | # config.extend_remember_period = false 69 | 70 | # ==> Configuration for :validatable 71 | # Range for password length 72 | # config.password_length = 6..20 73 | 74 | # Regex to use to validate the email address 75 | # config.email_regexp = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i 76 | 77 | # ==> Configuration for :timeoutable 78 | # The time you want to timeout the user session without activity. After this 79 | # time the user will be asked for credentials again. 80 | # config.timeout_in = 10.minutes 81 | 82 | # ==> Configuration for :lockable 83 | # Defines which strategy will be used to lock an account. 84 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. 85 | # :none = No lock strategy. You should handle locking by yourself. 86 | # config.lock_strategy = :failed_attempts 87 | 88 | # Defines which strategy will be used to unlock an account. 89 | # :email = Sends an unlock link to the user email 90 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) 91 | # :both = Enables both strategies 92 | # :none = No unlock strategy. You should handle unlocking by yourself. 93 | # config.unlock_strategy = :both 94 | 95 | # Number of authentication tries before locking an account if lock_strategy 96 | # is failed attempts. 97 | # config.maximum_attempts = 20 98 | 99 | # Time interval to unlock the account if :time is enabled as unlock_strategy. 100 | # config.unlock_in = 1.hour 101 | 102 | # ==> Configuration for :token_authenticatable 103 | # Defines name of the authentication token params key 104 | # config.token_authentication_key = :auth_token 105 | 106 | # ==> Scopes configuration 107 | # Turn scoped views on. Before rendering "sessions/new", it will first check for 108 | # "users/sessions/new". It's turned off by default because it's slower if you 109 | # are using only default views. 110 | # config.scoped_views = true 111 | 112 | # Configure the default scope given to Warden. By default it's the first 113 | # devise role declared in your routes. 114 | # config.default_scope = :user 115 | 116 | # Configure sign_out behavior. 117 | # By default sign_out is scoped (i.e. /users/sign_out affects only :user scope). 118 | # In case of sign_out_all_scopes set to true any logout action will sign out all active scopes. 119 | # config.sign_out_all_scopes = false 120 | 121 | # ==> Navigation configuration 122 | # Lists the formats that should be treated as navigational. Formats like 123 | # :html, should redirect to the sign in page when the user does not have 124 | # access, but formats like :xml or :json, should return 401. 125 | # If you have any extra navigational formats, like :iphone or :mobile, you 126 | # should add them to the navigational formats lists. Default is [:html] 127 | # config.navigational_formats = [:html, :iphone] 128 | 129 | # ==> Warden configuration 130 | # If you want to use other strategies, that are not (yet) supported by Devise, 131 | # you can configure them inside the config.warden block. The example below 132 | # allows you to setup OAuth, using http://github.com/roman/warden_oauth 133 | # 134 | # config.warden do |manager| 135 | # manager.oauth(:twitter) do |twitter| 136 | # twitter.consumer_secret = 137 | # twitter.consumer_key = 138 | # twitter.options :site => 'http://twitter.com' 139 | # end 140 | # manager.default_strategies(:scope => :user).unshift :twitter_oauth 141 | # end 142 | end 143 | -------------------------------------------------------------------------------- /features/step_definitions/web_steps.rb: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | 8 | require 'uri' 9 | require 'cgi' 10 | require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths")) 11 | 12 | module WithinHelpers 13 | def with_scope(locator) 14 | locator ? within(locator) { yield } : yield 15 | end 16 | end 17 | World(WithinHelpers) 18 | 19 | Given /^(?:|I )am on (.+)$/ do |page_name| 20 | visit path_to(page_name) 21 | end 22 | 23 | When /^(?:|I )go to (.+)$/ do |page_name| 24 | visit path_to(page_name) 25 | end 26 | 27 | When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| 28 | with_scope(selector) do 29 | click_button(button) 30 | end 31 | end 32 | 33 | When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| 34 | with_scope(selector) do 35 | click_link(link) 36 | end 37 | end 38 | 39 | When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field, value, selector| 40 | with_scope(selector) do 41 | fill_in(field, :with => value) 42 | end 43 | end 44 | 45 | When /^(?:|I )fill in "([^"]*)" for "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector| 46 | with_scope(selector) do 47 | fill_in(field, :with => value) 48 | end 49 | end 50 | 51 | # Use this to fill in an entire form with data from a table. Example: 52 | # 53 | # When I fill in the following: 54 | # | Account Number | 5002 | 55 | # | Expiry date | 2009-11-01 | 56 | # | Note | Nice guy | 57 | # | Wants Email? | | 58 | # 59 | # TODO: Add support for checkbox, select og option 60 | # based on naming conventions. 61 | # 62 | When /^(?:|I )fill in the following(?: within "([^"]*)")?:$/ do |selector, fields| 63 | with_scope(selector) do 64 | fields.rows_hash.each do |name, value| 65 | When %{I fill in "#{name}" with "#{value}"} 66 | end 67 | end 68 | end 69 | 70 | When /^(?:|I )select "([^"]*)" from "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector| 71 | with_scope(selector) do 72 | select(value, :from => field) 73 | end 74 | end 75 | 76 | When /^(?:|I )check "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| 77 | with_scope(selector) do 78 | check(field) 79 | end 80 | end 81 | 82 | When /^(?:|I )uncheck "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| 83 | with_scope(selector) do 84 | uncheck(field) 85 | end 86 | end 87 | 88 | When /^(?:|I )choose "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| 89 | with_scope(selector) do 90 | choose(field) 91 | end 92 | end 93 | 94 | When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"(?: within "([^"]*)")?$/ do |path, field, selector| 95 | with_scope(selector) do 96 | attach_file(field, path) 97 | end 98 | end 99 | 100 | Then /^(?:|I )should see JSON:$/ do |expected_json| 101 | require 'json' 102 | expected = JSON.pretty_generate(JSON.parse(expected_json)) 103 | actual = JSON.pretty_generate(JSON.parse(response.body)) 104 | expected.should == actual 105 | end 106 | 107 | Then /^(?:|I )should see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector| 108 | with_scope(selector) do 109 | if page.respond_to? :should 110 | page.should have_content(text) 111 | else 112 | assert page.has_content?(text) 113 | end 114 | end 115 | end 116 | 117 | Then /^(?:|I )should see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector| 118 | regexp = Regexp.new(regexp) 119 | with_scope(selector) do 120 | if page.respond_to? :should 121 | page.should have_xpath('//*', :text => regexp) 122 | else 123 | assert page.has_xpath?('//*', :text => regexp) 124 | end 125 | end 126 | end 127 | 128 | Then /^(?:|I )should not see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector| 129 | with_scope(selector) do 130 | if page.respond_to? :should 131 | page.should have_no_content(text) 132 | else 133 | assert page.has_no_content?(text) 134 | end 135 | end 136 | end 137 | 138 | Then /^(?:|I )should not see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector| 139 | regexp = Regexp.new(regexp) 140 | with_scope(selector) do 141 | if page.respond_to? :should 142 | page.should have_no_xpath('//*', :text => regexp) 143 | else 144 | assert page.has_no_xpath?('//*', :text => regexp) 145 | end 146 | end 147 | end 148 | 149 | Then /^the "([^"]*)" field(?: within "([^"]*)")? should contain "([^"]*)"$/ do |field, selector, value| 150 | with_scope(selector) do 151 | field = find_field(field) 152 | field_value = (field.tag_name == 'textarea') ? field.text : field.value 153 | if field_value.respond_to? :should 154 | field_value.should =~ /#{value}/ 155 | else 156 | assert_match(/#{value}/, field_value) 157 | end 158 | end 159 | end 160 | 161 | Then /^the "([^"]*)" field(?: within "([^"]*)")? should not contain "([^"]*)"$/ do |field, selector, value| 162 | with_scope(selector) do 163 | field = find_field(field) 164 | field_value = (field.tag_name == 'textarea') ? field.text : field.value 165 | if field_value.respond_to? :should_not 166 | field_value.should_not =~ /#{value}/ 167 | else 168 | assert_no_match(/#{value}/, field_value) 169 | end 170 | end 171 | end 172 | 173 | Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should be checked$/ do |label, selector| 174 | with_scope(selector) do 175 | field_checked = find_field(label)['checked'] 176 | if field_checked.respond_to? :should 177 | field_checked.should be_true 178 | else 179 | assert field_checked 180 | end 181 | end 182 | end 183 | 184 | Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should not be checked$/ do |label, selector| 185 | with_scope(selector) do 186 | field_checked = find_field(label)['checked'] 187 | if field_checked.respond_to? :should 188 | field_checked.should be_false 189 | else 190 | assert !field_checked 191 | end 192 | end 193 | end 194 | 195 | Then /^(?:|I )should be on (.+)$/ do |page_name| 196 | current_path = URI.parse(current_url).path 197 | if current_path.respond_to? :should 198 | current_path.should == path_to(page_name) 199 | else 200 | assert_equal path_to(page_name), current_path 201 | end 202 | end 203 | 204 | Then /^(?:|I )should have the following query string:$/ do |expected_pairs| 205 | query = URI.parse(current_url).query 206 | actual_params = query ? CGI.parse(query) : {} 207 | expected_params = {} 208 | expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')} 209 | 210 | if actual_params.respond_to? :should 211 | actual_params.should == expected_params 212 | else 213 | assert_equal expected_params, actual_params 214 | end 215 | end 216 | 217 | Then /^show me the page$/ do 218 | save_and_open_page 219 | end 220 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | == Welcome to Rails 2 | 3 | Rails is a web-application framework that includes everything needed to create 4 | database-backed web applications according to the Model-View-Control pattern. 5 | 6 | This pattern splits the view (also called the presentation) into "dumb" 7 | templates that are primarily responsible for inserting pre-built data in between 8 | HTML tags. The model contains the "smart" domain objects (such as Account, 9 | Product, Person, Post) that holds all the business logic and knows how to 10 | persist themselves to a database. The controller handles the incoming requests 11 | (such as Save New Account, Update Product, Show Post) by manipulating the model 12 | and directing data to the view. 13 | 14 | In Rails, the model is handled by what's called an object-relational mapping 15 | layer entitled Active Record. This layer allows you to present the data from 16 | database rows as objects and embellish these data objects with business logic 17 | methods. You can read more about Active Record in 18 | link:files/vendor/rails/activerecord/README.html. 19 | 20 | The controller and view are handled by the Action Pack, which handles both 21 | layers by its two parts: Action View and Action Controller. These two layers 22 | are bundled in a single package due to their heavy interdependence. This is 23 | unlike the relationship between the Active Record and Action Pack that is much 24 | more separate. Each of these packages can be used independently outside of 25 | Rails. You can read more about Action Pack in 26 | link:files/vendor/rails/actionpack/README.html. 27 | 28 | 29 | == Getting Started 30 | 31 | 1. At the command prompt, create a new Rails application: 32 | rails new myapp (where myapp is the application name) 33 | 34 | 2. Change directory to myapp and start the web server: 35 | cd myapp; rails server (run with --help for options) 36 | 37 | 3. Go to http://localhost:3000/ and you'll see: 38 | "Welcome aboard: You're riding Ruby on Rails!" 39 | 40 | 4. Follow the guidelines to start developing your application. You can find 41 | the following resources handy: 42 | 43 | * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html 44 | * Ruby on Rails Tutorial Book: http://www.railstutorial.org/ 45 | 46 | 47 | == Debugging Rails 48 | 49 | Sometimes your application goes wrong. Fortunately there are a lot of tools that 50 | will help you debug it and get it back on the rails. 51 | 52 | First area to check is the application log files. Have "tail -f" commands 53 | running on the server.log and development.log. Rails will automatically display 54 | debugging and runtime information to these files. Debugging info will also be 55 | shown in the browser on requests from 127.0.0.1. 56 | 57 | You can also log your own messages directly into the log file from your code 58 | using the Ruby logger class from inside your controllers. Example: 59 | 60 | class WeblogController < ActionController::Base 61 | def destroy 62 | @weblog = Weblog.find(params[:id]) 63 | @weblog.destroy 64 | logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") 65 | end 66 | end 67 | 68 | The result will be a message in your log file along the lines of: 69 | 70 | Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! 71 | 72 | More information on how to use the logger is at http://www.ruby-doc.org/core/ 73 | 74 | Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are 75 | several books available online as well: 76 | 77 | * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) 78 | * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) 79 | 80 | These two books will bring you up to speed on the Ruby language and also on 81 | programming in general. 82 | 83 | 84 | == Debugger 85 | 86 | Debugger support is available through the debugger command when you start your 87 | Mongrel or WEBrick server with --debugger. This means that you can break out of 88 | execution at any point in the code, investigate and change the model, and then, 89 | resume execution! You need to install ruby-debug to run the server in debugging 90 | mode. With gems, use sudo gem install ruby-debug. Example: 91 | 92 | class WeblogController < ActionController::Base 93 | def index 94 | @posts = Post.find(:all) 95 | debugger 96 | end 97 | end 98 | 99 | So the controller will accept the action, run the first line, then present you 100 | with a IRB prompt in the server window. Here you can do things like: 101 | 102 | >> @posts.inspect 103 | => "[#nil, "body"=>nil, "id"=>"1"}>, 105 | #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" 107 | >> @posts.first.title = "hello from a debugger" 108 | => "hello from a debugger" 109 | 110 | ...and even better, you can examine how your runtime objects actually work: 111 | 112 | >> f = @posts.first 113 | => #nil, "body"=>nil, "id"=>"1"}> 114 | >> f. 115 | Display all 152 possibilities? (y or n) 116 | 117 | Finally, when you're ready to resume execution, you can enter "cont". 118 | 119 | 120 | == Console 121 | 122 | The console is a Ruby shell, which allows you to interact with your 123 | application's domain model. Here you'll have all parts of the application 124 | configured, just like it is when the application is running. You can inspect 125 | domain models, change values, and save to the database. Starting the script 126 | without arguments will launch it in the development environment. 127 | 128 | To start the console, run rails console from the application 129 | directory. 130 | 131 | Options: 132 | 133 | * Passing the -s, --sandbox argument will rollback any modifications 134 | made to the database. 135 | * Passing an environment name as an argument will load the corresponding 136 | environment. Example: rails console production. 137 | 138 | To reload your controllers and models after launching the console run 139 | reload! 140 | 141 | More information about irb can be found at: 142 | link:http://www.rubycentral.com/pickaxe/irb.html 143 | 144 | 145 | == dbconsole 146 | 147 | You can go to the command line of your database directly through rails 148 | dbconsole. You would be connected to the database with the credentials 149 | defined in database.yml. Starting the script without arguments will connect you 150 | to the development database. Passing an argument will connect you to a different 151 | database, like rails dbconsole production. Currently works for MySQL, 152 | PostgreSQL and SQLite 3. 153 | 154 | == Description of Contents 155 | 156 | The default directory structure of a generated Ruby on Rails application: 157 | 158 | |-- app 159 | | |-- controllers 160 | | |-- helpers 161 | | |-- models 162 | | `-- views 163 | | `-- layouts 164 | |-- config 165 | | |-- environments 166 | | |-- initializers 167 | | `-- locales 168 | |-- db 169 | |-- doc 170 | |-- lib 171 | | `-- tasks 172 | |-- log 173 | |-- public 174 | | |-- images 175 | | |-- javascripts 176 | | `-- stylesheets 177 | |-- script 178 | | `-- performance 179 | |-- test 180 | | |-- fixtures 181 | | |-- functional 182 | | |-- integration 183 | | |-- performance 184 | | `-- unit 185 | |-- tmp 186 | | |-- cache 187 | | |-- pids 188 | | |-- sessions 189 | | `-- sockets 190 | `-- vendor 191 | `-- plugins 192 | 193 | app 194 | Holds all the code that's specific to this particular application. 195 | 196 | app/controllers 197 | Holds controllers that should be named like weblogs_controller.rb for 198 | automated URL mapping. All controllers should descend from 199 | ApplicationController which itself descends from ActionController::Base. 200 | 201 | app/models 202 | Holds models that should be named like post.rb. Models descend from 203 | ActiveRecord::Base by default. 204 | 205 | app/views 206 | Holds the template files for the view that should be named like 207 | weblogs/index.html.erb for the WeblogsController#index action. All views use 208 | eRuby syntax by default. 209 | 210 | app/views/layouts 211 | Holds the template files for layouts to be used with views. This models the 212 | common header/footer method of wrapping views. In your views, define a layout 213 | using the layout :default and create a file named default.html.erb. 214 | Inside default.html.erb, call <% yield %> to render the view using this 215 | layout. 216 | 217 | app/helpers 218 | Holds view helpers that should be named like weblogs_helper.rb. These are 219 | generated for you automatically when using generators for controllers. 220 | Helpers can be used to wrap functionality for your views into methods. 221 | 222 | config 223 | Configuration files for the Rails environment, the routing map, the database, 224 | and other dependencies. 225 | 226 | db 227 | Contains the database schema in schema.rb. db/migrate contains all the 228 | sequence of Migrations for your schema. 229 | 230 | doc 231 | This directory is where your application documentation will be stored when 232 | generated using rake doc:app 233 | 234 | lib 235 | Application specific libraries. Basically, any kind of custom code that 236 | doesn't belong under controllers, models, or helpers. This directory is in 237 | the load path. 238 | 239 | public 240 | The directory available for the web server. Contains subdirectories for 241 | images, stylesheets, and javascripts. Also contains the dispatchers and the 242 | default HTML files. This should be set as the DOCUMENT_ROOT of your web 243 | server. 244 | 245 | script 246 | Helper scripts for automation and generation. 247 | 248 | test 249 | Unit and functional tests along with fixtures. When using the rails generate 250 | command, template test files will be generated for you and placed in this 251 | directory. 252 | 253 | vendor 254 | External libraries that the application depends on. Also includes the plugins 255 | subdirectory. If the app has frozen rails, those gems also go here, under 256 | vendor/rails/. This directory is in the load path. 257 | --------------------------------------------------------------------------------