boilerplate_generator 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. data/History.txt +3 -0
  2. data/Manifest.txt +32 -0
  3. data/README.txt +152 -0
  4. data/USAGE +30 -0
  5. data/boilerplate_generator.rb +69 -0
  6. data/lib/boilerplate_generator/version.rb +9 -0
  7. data/templates/app/controllers/account_controller.rb +19 -0
  8. data/templates/app/controllers/application.rb +6 -0
  9. data/templates/app/controllers/user_controller.rb +34 -0
  10. data/templates/app/helpers/account_helper.rb +2 -0
  11. data/templates/app/helpers/user_helper.rb +2 -0
  12. data/templates/app/models/user.rb +71 -0
  13. data/templates/app/views/account/login.haml +16 -0
  14. data/templates/app/views/account/login.html.erb +17 -0
  15. data/templates/app/views/layouts/application.haml +11 -0
  16. data/templates/app/views/layouts/application.html.erb +17 -0
  17. data/templates/app/views/user/_form.haml +17 -0
  18. data/templates/app/views/user/_form.html.erb +13 -0
  19. data/templates/app/views/user/edit.haml +7 -0
  20. data/templates/app/views/user/edit.html.erb +8 -0
  21. data/templates/app/views/user/new.haml +11 -0
  22. data/templates/app/views/user/new.html.erb +11 -0
  23. data/templates/app/views/user/show.haml +4 -0
  24. data/templates/app/views/user/show.html.erb +4 -0
  25. data/templates/config/routes.rb +12 -0
  26. data/templates/db/migrate/create_users.rb +18 -0
  27. data/templates/lib/authentication_system.rb +167 -0
  28. data/templates/test/fixtures/users.yml +15 -0
  29. data/templates/test/functional/account_controller_test.rb +77 -0
  30. data/templates/test/functional/user_controller_test.rb +119 -0
  31. data/templates/test/test_helper.rb +37 -0
  32. data/templates/test/unit/user_test.rb +116 -0
  33. metadata +77 -0
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ +++ 0.0.1 2007-05-07
2
+
3
+ + Initial gem release
data/Manifest.txt ADDED
@@ -0,0 +1,32 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ USAGE
5
+ boilerplate_generator.rb
6
+ lib/boilerplate_generator/version.rb
7
+ templates/app/controllers/account_controller.rb
8
+ templates/app/controllers/application.rb
9
+ templates/app/controllers/user_controller.rb
10
+ templates/app/helpers/account_helper.rb
11
+ templates/app/helpers/user_helper.rb
12
+ templates/app/models/user.rb
13
+ templates/app/views/account/login.haml
14
+ templates/app/views/account/login.html.erb
15
+ templates/app/views/layouts/application.haml
16
+ templates/app/views/layouts/application.html.erb
17
+ templates/app/views/user/_form.haml
18
+ templates/app/views/user/_form.html.erb
19
+ templates/app/views/user/edit.haml
20
+ templates/app/views/user/edit.html.erb
21
+ templates/app/views/user/new.haml
22
+ templates/app/views/user/new.html.erb
23
+ templates/app/views/user/show.haml
24
+ templates/app/views/user/show.html.erb
25
+ templates/config/routes.rb
26
+ templates/db/migrate/create_users.rb
27
+ templates/lib/authentication_system.rb
28
+ templates/test/fixtures/users.yml
29
+ templates/test/functional/account_controller_test.rb
30
+ templates/test/functional/user_controller_test.rb
31
+ templates/test/test_helper.rb
32
+ templates/test/unit/user_test.rb
data/README.txt ADDED
@@ -0,0 +1,152 @@
1
+ = Boilerplate Generator
2
+
3
+ Boilerplate is a generator for new Rails projects. It automatically creates a
4
+ working account system for user login and registration, a basic layout, some
5
+ named routes, and (of course) a comprehensive suite of tests.
6
+
7
+ If you have any questions, comments, criticisms, or unmelted chocolate
8
+ sundaes, please contact me via electronic mail. My address is
9
+ packagetheif@gmail.com
10
+
11
+ == Why?
12
+
13
+ Because I get tired of creating the same basic things each time I start a new
14
+ project. Steeped in frustration, I extracted all the preliminary code from last
15
+ project I created and packaged it up for posterity.
16
+
17
+ == What's included?
18
+
19
+ Nothing fancy, really. That's the whole idea: no smoke, no mirrors. Just simple
20
+ code that's easy to modify and/or extend.
21
+
22
+ models
23
+ A User model with support for hashed (and salted) passwords, all the necessary
24
+ validations, and an authenticate class method. Take a look at
25
+ db/migrate/###_create_users.rb if you want to see the schema definition
26
+
27
+ views
28
+ Views for login and signup, plus a simple xhtml layout that includes
29
+ the default javascript libraries
30
+
31
+ controllers
32
+ An account controller with index, login, signup, and logout actions; a
33
+ modified application_controller that mixes in the AuthenticationSystem module.
34
+ The current_user method returns a User object provided a User with an id equal
35
+ to session[:user] can be found; the authenticate method redirects to the
36
+ login_url if current_user returns false
37
+
38
+ routes
39
+ Named routes for signup_url, login_url, and logout_url and a resource route
40
+ for users (map.resource :user)
41
+
42
+ migrations
43
+ A migration to create the Users table
44
+
45
+ tests
46
+ A comprehensive suite of tests covering the User model, and the all the
47
+ Account and User actions
48
+
49
+ == Test Specs
50
+
51
+ Account controller should:
52
+ * login user and redirect
53
+ * not login invalid user
54
+ * logout and redirect
55
+ * remember login if remember me is set
56
+ * not remember login if remember me is not set
57
+ * delete token on logout
58
+ * login automatically from cookie
59
+ * fail cookie login with expired cookie
60
+ * fail cookie login with invalid cookie
61
+
62
+ User controller should:
63
+ * create user
64
+ * require login on create
65
+ * require unique login on create
66
+ * require password on create
67
+ * require password confirmation on create
68
+ * require email on create
69
+ * require valid email on create
70
+ * require authentication for show
71
+ * require authentication for edit
72
+ * require authentication for update
73
+ * accept http basic authentication
74
+
75
+ User should:
76
+ * create user
77
+ * update user
78
+ * destroy user
79
+ * accept valid emails
80
+ * not allow invalid emails
81
+ * not allow spaces or dots in login
82
+ * reset password
83
+ * not rehash password when unchanged
84
+ * authenticate user
85
+ * set remember token
86
+ * unset remember token
87
+ * remember login for one week
88
+ * remember login until one week from now
89
+ * remember login for default two weeks
90
+
91
+ == Usage
92
+
93
+ Boilerplate is intended to be used on a fresh Rails application. You're free to
94
+ use it on an app with code in it already; it will prompt you before it tries
95
+ to overwrite any existing files.
96
+
97
+ To generate the boilerplate code for your application, use the generate script:
98
+
99
+ $ ./script/generate boilerplate
100
+
101
+ Assuming you have the databases set up for your application, run the migration
102
+ script which will create the User table:
103
+
104
+ $ rake db:migrate
105
+
106
+ Run the test suite to make sure everything is kosher:
107
+
108
+ $ rake test
109
+
110
+ == Generated files
111
+
112
+ The following files are generated:
113
+
114
+ +-- app
115
+ | |-- controllers
116
+ | | |-- account_controller.rb
117
+ | | |-- user_controller.rb
118
+ | | `-- application.rb
119
+ | |-- models
120
+ | | `-- user.rb
121
+ | `-- views
122
+ | |-- account
123
+ | | |-- index.rhtml
124
+ | | |-- login.rhtml
125
+ | | `-- signup.rhtml
126
+ | |-- user
127
+ | | |-- login.rhtml
128
+ | | |-- show.rhtml
129
+ | | `-- signup.rhtml
130
+ | `-- layouts
131
+ | `-- application.rhtml
132
+ |-- config
133
+ | `-- routes.rb
134
+ |-- db
135
+ | `-- migrate
136
+ | |-- ###_create_sessions.rb
137
+ | `-- ###_create_users.rb
138
+ |-- lib
139
+ | `-- authentication_system.rb
140
+ `-- test
141
+ |-- fixtures
142
+ | `-- users.yml
143
+ |-- functional
144
+ | |-- user_controller_test.rb
145
+ | `-- account_controller_test.rb
146
+ |-- test_helper.rb
147
+ `-- unit
148
+ `-- user_test.rb
149
+
150
+
151
+ ---
152
+ Copyright (c) 2007 Jeffrey Allan Hardy, released under the MIT license
data/USAGE ADDED
@@ -0,0 +1,30 @@
1
+ NAME
2
+ boilerplate - creates a working account system for user login and
3
+ registration
4
+
5
+ SYNOPSIS
6
+ boilerplate
7
+
8
+ DESCRIPTION
9
+ Automatically creates a working account system for user login and
10
+ registration, a basic layout, some named routes, and (of course) a
11
+ comprehensive suite of tests.
12
+
13
+ Boilerplate is intended to be used on a fresh Rails application.
14
+ You're free to use it on an app with code in it already; it will
15
+ prompt you before it tries to overwrite any existing files.
16
+
17
+ EXAMPLE
18
+ To generate the boilerplate code for your application, use the
19
+ generate script:
20
+
21
+ ./script/generate boilerplate
22
+
23
+ Assuming you have the databases set up for your application, run
24
+ the migration script which will create the User table:
25
+
26
+ rake db:migrate
27
+
28
+ Run the test suite to make sure everything is kosher:
29
+
30
+ rake test
@@ -0,0 +1,69 @@
1
+ class BoilerplateGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ # Set the file extension to use for templates
5
+ ext = (File.exist?('vendor/plugins/haml')) ? 'haml' : 'html.erb'
6
+
7
+ # Models
8
+ dir = 'app/models'
9
+ m.template File.join(dir, file = 'user.rb'), File.join(dir, file)
10
+
11
+ # Views
12
+ dir = 'app/views/account'
13
+ m.directory dir
14
+ m.template File.join(dir, file = "login.#{ext}"), File.join(dir, file)
15
+
16
+ dir = 'app/views/user'
17
+ m.directory dir
18
+ m.template File.join(dir, file = "_form.#{ext}"), File.join(dir, file)
19
+ m.template File.join(dir, file = "edit.#{ext}"), File.join(dir, file)
20
+ m.template File.join(dir, file = "show.#{ext}"), File.join(dir, file)
21
+ m.template File.join(dir, file = "new.#{ext}"), File.join(dir, file)
22
+
23
+ # Layouts
24
+ dir = 'app/views/layouts'
25
+ m.template File.join(dir, file = "application.#{ext}"), File.join(dir, file)
26
+
27
+ # Helpers
28
+ dir = 'app/helpers'
29
+ m.template File.join(dir, file = 'account_helper.rb'), File.join(dir, file)
30
+ m.template File.join(dir, file = 'user_helper.rb'), File.join(dir, file)
31
+
32
+ # Controllers
33
+ dir = 'app/controllers'
34
+ m.template File.join(dir, file = 'account_controller.rb'), File.join(dir, file)
35
+ m.template File.join(dir, file = 'user_controller.rb'), File.join(dir, file)
36
+ m.template File.join(dir, file = 'application.rb'), File.join(dir, file)
37
+
38
+ # Libraries
39
+ dir = 'lib'
40
+ m.template File.join(dir, file = 'authentication_system.rb'), File.join(dir, file)
41
+
42
+ # Routes
43
+ dir = 'config'
44
+ m.template File.join(dir, file = 'routes.rb'), File.join(dir, file)
45
+
46
+ # Migrations
47
+ dir = 'db/migrate'
48
+ m.directory dir
49
+ m.migration_template File.join(dir, file = 'create_users.rb'), dir, :migration_file_name => file.sub(/\.rb$/, '')
50
+
51
+ # Test Helpers
52
+ dir = 'test'
53
+ m.template File.join(dir, file = 'test_helper.rb'), File.join(dir, file)
54
+
55
+ # Fixtures
56
+ dir = 'test/fixtures'
57
+ m.template File.join(dir, file = 'users.yml'), File.join(dir, file)
58
+
59
+ # Functional Tests
60
+ dir = 'test/functional'
61
+ m.template File.join(dir, file = 'account_controller_test.rb'), File.join(dir, file)
62
+ m.template File.join(dir, file = 'user_controller_test.rb'), File.join(dir, file)
63
+
64
+ # Unit Tests
65
+ dir = 'test/unit'
66
+ m.template File.join(dir, file = 'user_test.rb'), File.join(dir, file)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,9 @@
1
+ module BoilerplateGenerator #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ class AccountController < ApplicationController
2
+ def login
3
+ if request.post?
4
+ if self.current_user = User.authenticate(params[:login], params[:password])
5
+ remember_current_user if params[:remember_me] == '1'
6
+ redirect_back_or_to user_url
7
+ else
8
+ flash[:error] = 'Invalid user/password combination'
9
+ redirect_to login_path
10
+ end
11
+ end
12
+ end
13
+
14
+ def logout
15
+ logout_current_user!
16
+ flash[:notice] = 'You have been logged out'
17
+ redirect_to login_path
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ class ApplicationController < ActionController::Base
2
+ include AuthenticationSystem
3
+ before_filter :authenticate_with_cookies
4
+
5
+ helper :all
6
+ end
@@ -0,0 +1,34 @@
1
+ class UserController < ApplicationController
2
+ before_filter :authenticate, :except => [:new, :create]
3
+
4
+ def show
5
+ @user = current_user
6
+ end
7
+
8
+ def new
9
+ @user = User.new
10
+ end
11
+
12
+ def create
13
+ @user = User.new(params[:user])
14
+ if @user.save
15
+ self.current_user = @user
16
+ flash[:notice] = 'Thanks for signing up!'
17
+ redirect_to root_url
18
+ else
19
+ render :action => 'new'
20
+ end
21
+ end
22
+
23
+ def edit
24
+ @user = current_user
25
+ end
26
+
27
+ def update
28
+ @user = current_user
29
+ if @user.update_attributes(params[:user])
30
+ flash[:notice] = 'Account information updated'
31
+ redirect_to edit_user_url
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,2 @@
1
+ module AccountHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module UserHelper
2
+ end
@@ -0,0 +1,71 @@
1
+ require 'digest/sha1'
2
+
3
+ class User < ActiveRecord::Base
4
+ attr_accessor :password
5
+
6
+ validates_presence_of :login
7
+ validates_length_of :login, :within => 3..40
8
+ validates_uniqueness_of :login, :case_sensitive => false
9
+ validates_format_of :login, :with => /^[\w-]+$/i, :message => 'cannot contain spaces or special characters'
10
+
11
+ validates_presence_of :email
12
+ validates_format_of :email, :with => /^[^@][\w.-]*@[\w.-]+[.][a-z]{2,4}$/i
13
+
14
+ validates_presence_of :password, :if => :password_required?
15
+ validates_length_of :password, :within => 4..40, :if => :password_required?
16
+ validates_presence_of :password_confirmation, :if => :password_required?
17
+ validates_confirmation_of :password, :if => :password_required?
18
+
19
+ validates_acceptance_of :terms_of_service
20
+
21
+ before_save :hash_password
22
+
23
+ attr_protected :hashed_password, :remember_token, :remember_token_expires_at
24
+
25
+ def self.authenticate(login, password)
26
+ user = find_by_login(login)
27
+ return user if user && user.authenticated?(password)
28
+ end
29
+
30
+ def authenticated?(password)
31
+ hashed_password == encrypt(password)
32
+ end
33
+
34
+ def remember
35
+ remember_for 2.weeks
36
+ end
37
+
38
+ def remember_for(time)
39
+ remember_until time.from_now.utc
40
+ end
41
+
42
+ def remember_until(time)
43
+ self.remember_token_expires_at = time
44
+ self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
45
+ save
46
+ end
47
+
48
+ def forget
49
+ self.remember_token_expires_at = nil
50
+ self.remember_token = nil
51
+ save
52
+ end
53
+
54
+ def to_s
55
+ login
56
+ end
57
+
58
+ protected
59
+ def hash_password
60
+ return if password.blank?
61
+ self.hashed_password = encrypt(password)
62
+ end
63
+
64
+ def password_required?
65
+ hashed_password.blank? || !password.blank?
66
+ end
67
+
68
+ def encrypt(string)
69
+ Digest::SHA1.hexdigest(string)
70
+ end
71
+ end
@@ -0,0 +1,16 @@
1
+ %h1 Login
2
+
3
+ - form_tag do
4
+ %fieldset
5
+ %p
6
+ Login:<br />
7
+ = text_field_tag :login
8
+ %p
9
+ Password:<br />
10
+ = password_field_tag :password
11
+ %p
12
+ = "#{check_box_tag :remember_me} Remember me for 2 weeks"
13
+ %p
14
+ = submit_tag 'Login'
15
+
16
+ %h2= "Don't have an account? You should #{link_to 'Sign up!', new_user_url}"
@@ -0,0 +1,17 @@
1
+ <h1>Login</h1>
2
+
3
+ <%% form_tag do -%>
4
+ <fieldset>
5
+ <p><label for="login">Login:</label><br />
6
+ <%%= text_field_tag :login %></p>
7
+
8
+ <p><label for="password">Password:</label><br />
9
+ <%%= password_field_tag :password %></p>
10
+
11
+ <p><label><%%= check_box_tag :remember_me %> Remember me for 2 weeks</label></p>
12
+
13
+ <p><%%= submit_tag 'Login' %></p>
14
+ </fieldset>
15
+ <%% end -%>
16
+
17
+ <h2>Don't have an account? You should <%%= link_to 'Sign up!', new_user_url %></h2>
@@ -0,0 +1,11 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title= controller.controller_name.capitalize
5
+ %meta{'http-equiv' => 'Content-Type', 'content' => 'text/html; charset=utf-8'}/
6
+ = javascript_include_tag :defaults
7
+ %body
8
+ #container
9
+ #content
10
+ = [:notice, :error].collect {|k| content_tag(:div, flash[k], :class => "flash #{k}") if flash[k] }.join
11
+ = yield
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html>
3
+ <head>
4
+ <title><%%= controller.controller_name.capitalize %></title>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <%%= javascript_include_tag :defaults %>
7
+ </head>
8
+
9
+ <body>
10
+ <div id="container">
11
+ <div id="content">
12
+ <%%= [:notice, :error].collect {|k| content_tag(:div, flash[k], :class => "flash #{k}") if flash[k] }.join %>
13
+ <%%= yield %>
14
+ </div>
15
+ </div>
16
+ </body>
17
+ </html>
@@ -0,0 +1,17 @@
1
+ = error_messages_for 'user'
2
+
3
+ %p
4
+ Login:<br />
5
+ = text_field :user, :login
6
+
7
+ %p
8
+ Email:<br />
9
+ = text_field :user, :email
10
+
11
+ %p
12
+ Password:<br />
13
+ = password_field :user, :password
14
+
15
+ %p
16
+ Password confirmation:<br />
17
+ = password_field :user, :password_confirmation
@@ -0,0 +1,13 @@
1
+ <%%= error_messages_for 'user' %>
2
+
3
+ <p><label>Login:</label><br />
4
+ <%%= text_field :user, :login %></p>
5
+
6
+ <p><label>Email:</label><br />
7
+ <%%= text_field :user, :email %></p>
8
+
9
+ <p><label>Password:</label><br />
10
+ <%%= password_field :user, :password %></p>
11
+
12
+ <p><label>Password confirmation:</label><br />
13
+ <%%= password_field :user, :password_confirmation %></p>
@@ -0,0 +1,7 @@
1
+ %h1 Edit your account details
2
+
3
+ - form_tag user_path, :method => :put do
4
+ %fieldset
5
+ = render :partial => 'form'
6
+ %p
7
+ = submit_tag 'Save Changes'
@@ -0,0 +1,8 @@
1
+ <h1>Edit your account details</h1>
2
+
3
+ <%% form_tag user_path, :method => :put do -%>
4
+ <fieldset>
5
+ <%%= render :partial => 'form' %>
6
+ <p><%%= submit_tag 'Save Changes' %></p>
7
+ </fieldset>
8
+ <%% end -%>
@@ -0,0 +1,11 @@
1
+ %h1 Signup for an account
2
+
3
+ - form_tag user_path do
4
+ %fieldset
5
+ = render :partial => 'form'
6
+ %p
7
+ = "#{check_box :user, :terms_of_service} Yes, I have read and accept your #{link_to 'terms of service'}"
8
+ %p
9
+ = submit_tag 'Signup'
10
+
11
+ %h2= "Already have an account? #{link_to 'Log in', login_url}"
@@ -0,0 +1,11 @@
1
+ <h1>Signup for an account</h1>
2
+
3
+ <%% form_tag user_path do -%>
4
+ <fieldset>
5
+ <%%= render :partial => 'form' %>
6
+ <p><label><%%= check_box :user, :terms_of_service %> Yes, I have read and accept your</label> <%%= link_to 'terms of service', '#' %></p>
7
+ <p><%%= submit_tag 'Signup' %></p>
8
+ </fieldset>
9
+ <%% end -%>
10
+
11
+ <h2>Already have an account? <%%= link_to 'Log in', login_url %></h2>
@@ -0,0 +1,4 @@
1
+ %h1 You are logged in
2
+
3
+ = link_to 'logout', logout_url
4
+ = link_to 'edit your account information', edit_user_path
@@ -0,0 +1,4 @@
1
+ <h1>You are logged in</h1>
2
+
3
+ <%%= link_to 'logout', logout_url %> |
4
+ <%%= link_to 'edit your account information', edit_user_path %>
@@ -0,0 +1,12 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.root :controller => 'account', :action => 'login'
3
+
4
+ map.login 'login', :controller => 'account', :action => 'login'
5
+ map.logout 'logout', :controller => 'account', :action => 'logout'
6
+
7
+ map.resource :user
8
+
9
+ # Install the default routes as the lowest priority.
10
+ map.connect ':controller/:action/:id'
11
+ map.connect ':controller/:action/:id.:format'
12
+ end
@@ -0,0 +1,18 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.column :login, :string
5
+ t.column :email, :string
6
+ t.column :hashed_password, :string
7
+ t.column :remember_token, :string
8
+ t.column :remember_token_expires_at, :datetime
9
+ t.column :created_at, :datetime
10
+ t.column :updated_at, :datetime
11
+ end
12
+ add_index :users, [:login, :hashed_password]
13
+ end
14
+
15
+ def self.down
16
+ drop_table :users
17
+ end
18
+ end
@@ -0,0 +1,167 @@
1
+ # Heavily influenced by xal's original login_generator (circa 2004) and
2
+ # technoweenie's acts_as_authenticated.
3
+ #
4
+ # Provides a simple authentication system with support for HTTP Basic
5
+ # Authentication and remember-me goodness via cookies. To use simply
6
+ # mix in to ApplicationController.
7
+ #
8
+ # Assumes a named login route (login_url), a User model with the class
9
+ # method `authenticate(login, password)` and the following schema:
10
+ #
11
+ # create_table :users do |t|
12
+ # t.column :login, :string
13
+ # t.column :email, :string
14
+ # t.column :hashed_password, :string
15
+ # t.column :remember_token, :string
16
+ # t.column :remember_token_expires_at, :datetime
17
+ # end
18
+ #
19
+ # ---
20
+ # Copyright (c) 2006 Jeffrey Hardy (packagethief)
21
+ #
22
+ # Permission is hereby granted, free of charge, to any person obtaining
23
+ # a copy of this software and associated documentation files (the
24
+ # "Software"), to deal in the Software without restriction, including
25
+ # without limitation the rights to use, copy, modify, merge, publish,
26
+ # distribute, sublicense, and/or sell copies of the Software, and to
27
+ # permit persons to whom the Software is furnished to do so, subject to
28
+ # the following conditions:
29
+ #
30
+ # The above copyright notice and this permission notice shall be
31
+ # included in all copies or substantial portions of the Software.
32
+ #
33
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
36
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
37
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
38
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
39
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40
+ module AuthenticationSystem
41
+ protected
42
+
43
+ def self.included(controller)
44
+ controller.helper_method :current_user, :logged_in?
45
+ controller.filter_parameter_logging :password
46
+ end
47
+
48
+ def find_current_user
49
+ if session[:user_id]
50
+ User.find_by_id(session[:user_id])
51
+ else
52
+ authenticate_with_http_basic {|u, p| User.authenticate(u, p) }
53
+ end
54
+ end
55
+
56
+ def current_user
57
+ @current_user ||= find_current_user
58
+ end
59
+
60
+ def current_user=(user)
61
+ @current_user = user.is_a?(User) ? user : nil
62
+ session[:user_id] = @current_user ? @current_user.id : nil
63
+ end
64
+
65
+ def logged_in?
66
+ current_user.is_a?(User)
67
+ end
68
+
69
+ # Filter method to enforce a login requirement
70
+ # Apply as a before_filter on any controller you want to protect
71
+ def authenticate
72
+ (logged_in? && authorized?) ? true : access_denied
73
+ end
74
+
75
+ # Filter method to login a user using a cookie
76
+ # Apply as a before_filter on the application controller
77
+ def authenticate_with_cookies
78
+ return if logged_in? or cookies[:auth_token].nil?
79
+ self.current_user = User.find_by_remember_token(cookies[:auth_token])
80
+ end
81
+
82
+ # Overwrite in idividual controllers to control authorization
83
+ def authorized?
84
+ true
85
+ end
86
+
87
+ # Overwrite in idividual controllers to customize the flash message shown when authorization fails
88
+ def unauthorized_message
89
+ "Please login first and we'll send you right along"
90
+ end
91
+
92
+ # Sets the appropriate cookies to remember the current user
93
+ def remember_current_user
94
+ if logged_in?
95
+ current_user.remember
96
+ cookies[:auth_token] = { :value => current_user.remember_token, :expires => current_user.remember_token_expires_at }
97
+ end
98
+ end
99
+
100
+ # Logs out the current user by removing the session, cookie, and any HTTP auth data
101
+ def logout_current_user!
102
+ current_user.forget rescue nil
103
+ cookies.delete :auth_token
104
+ self.current_user = nil
105
+ request.env['HTTP_AUTHORIZATION'] = nil
106
+ end
107
+
108
+ def access_denied
109
+ respond_to do |format|
110
+ format.html do
111
+ store_location
112
+ flash[:notice] = unauthorized_message
113
+ redirect_to login_url
114
+ end
115
+ format.xml do
116
+ headers['Status'] = 'Unauthorized'
117
+ headers['WWW-Authenticate'] = 'Basic realm="Application"'
118
+ render :text => "401 Unauthorized\n", :status => :unauthorized
119
+ end
120
+ end
121
+ false
122
+ end
123
+
124
+ def store_location
125
+ session[:return_to] = request.request_uri
126
+ end
127
+
128
+ def redirect_back_or_to(options)
129
+ session[:return_to] ? redirect_to(session[:return_to]) : redirect_to(options)
130
+ session[:return_to] = nil
131
+ end
132
+
133
+ # Inspired by DHH's http_authentication plugin
134
+ # See http://dev.rubyonrails.org/svn/rails/plugins/http_authentication
135
+ def authenticate_with_http_basic(&login_procedure)
136
+ if authorization = request.env['HTTP_AUTHORIZATION'] || request.env['X-HTTP_AUTHORIZATION']
137
+ login_procedure.call(*Base64.decode64(authorization.split.last).split(/:/, 2))
138
+ end
139
+ end
140
+
141
+ # Helpers to make testing the functionality provided by this library
142
+ # easier. Include AuthenticationSystem::TestHelpers in RAILS_ROOT/test/test_helper.rb
143
+ module TestHelpers
144
+ def login_as(user)
145
+ @request.session[:user_id] = users(user.to_sym).id
146
+ end
147
+
148
+ def authenticate_as(login, password)
149
+ @request.env['HTTP_ACCEPT'] = @request.env['Content-Type'] = 'text/xml'
150
+ @request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("#{login}:#{password}")}"
151
+ end
152
+
153
+ def logout!
154
+ @request.session[:user_id] = nil
155
+ @controller.instance_variable_set('@current_user', nil)
156
+ @request.env['HTTP_AUTHORIZATION'] = nil
157
+ end
158
+
159
+ def auth_token(token)
160
+ CGI::Cookie.new('name' => 'auth_token', 'value' => token)
161
+ end
162
+
163
+ def cookie_for(user)
164
+ auth_token(users(user).remember_token)
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,15 @@
1
+ eugene:
2
+ id: 1
3
+ login: 'eugene'
4
+ email: 'eugene@example.com'
5
+ hashed_password: 'e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4' # => secret
6
+ created_at: <%%= Time.now.to_s :db %>
7
+ updated_at: <%%= Time.now.to_s :db %>
8
+
9
+ arthur:
10
+ id: 2
11
+ login: 'arthur'
12
+ email: 'arthur@example.com'
13
+ hashed_password: 'e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4' # => secret
14
+ created_at: <%%= Time.now.to_s :db %>
15
+ updated_at: <%%= Time.now.to_s :db %>
@@ -0,0 +1,77 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'account_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class AccountController; def rescue_action(e) raise e end; end
6
+
7
+ class AccountControllerTest < Test::Unit::TestCase
8
+ fixtures :users
9
+
10
+ def setup
11
+ @controller = AccountController.new
12
+ @request = ActionController::TestRequest.new
13
+ @response = ActionController::TestResponse.new
14
+ end
15
+
16
+ def test_should_login_user_and_redirect
17
+ post :login, :login => 'eugene', :password => 'secret'
18
+ assert @controller.send(:logged_in?)
19
+ assert session[:user_id]
20
+ assert_response :redirect
21
+ end
22
+
23
+ def test_should_not_login_invalid_user
24
+ post :login, :login => 'eugene', :password => 'bad password'
25
+ assert !@controller.send(:logged_in?)
26
+ assert_nil session[:user_id]
27
+ assert flash[:error]
28
+ assert_response :redirect
29
+ end
30
+
31
+ def test_should_logout_and_redirect
32
+ login_as :eugene
33
+ get :logout
34
+ assert_nil session[:user_id]
35
+ assert flash[:notice]
36
+ assert_response :redirect
37
+ end
38
+
39
+ def test_should_remember_login_if_remember_me_is_set
40
+ post :login, :login => 'eugene', :password => 'secret', :remember_me => "1"
41
+ assert @controller.send(:logged_in?)
42
+ assert_not_nil @response.cookies["auth_token"]
43
+ end
44
+
45
+ def test_should_not_remember_login_if_remember_me_is_not_set
46
+ post :login, :login => 'eugene', :password => 'secret', :remember_me => "0"
47
+ assert_nil @response.cookies["auth_token"]
48
+ end
49
+
50
+ def test_should_delete_token_on_logout
51
+ login_as :eugene
52
+ get :logout
53
+ assert_equal @response.cookies['auth_token'], []
54
+ end
55
+
56
+ def test_should_login_automatically_from_cookie
57
+ users(:eugene).remember
58
+ @request.cookies['auth_token'] = cookie_for(:eugene)
59
+ get :login
60
+ assert @controller.send(:logged_in?)
61
+ end
62
+
63
+ def test_should_fail_cookie_login_with_expired_cookie
64
+ users(:eugene).remember
65
+ users(:eugene).update_attribute :remember_token_expires_at, 5.minutes.ago.utc
66
+ @request.cookies['auth_token'] = cookie_for(:eugene)
67
+ get :login
68
+ assert_equal false, !@controller.send(:logged_in?)
69
+ end
70
+
71
+ def test_should_fail_cookie_login_with_invalid_cookie
72
+ users(:eugene).remember
73
+ @request.cookies['auth_token'] = auth_token('invalid_auth_token')
74
+ get :login
75
+ assert !@controller.send(:logged_in?)
76
+ end
77
+ end
@@ -0,0 +1,119 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'user_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class UserController; def rescue_action(e) raise e end; end
6
+
7
+ class UserControllerTest < Test::Unit::TestCase
8
+ fixtures :users
9
+
10
+ def setup
11
+ @controller = UserController.new
12
+ @request = ActionController::TestRequest.new
13
+ @response = ActionController::TestResponse.new
14
+ end
15
+
16
+ def test_should_create_user
17
+ assert_difference 'User.count' do
18
+ post :create, :user => valid_user
19
+ assert assigns(:user)
20
+ assert flash[:notice]
21
+ assert_response :redirect
22
+ end
23
+ end
24
+
25
+ def test_should_require_login_on_create
26
+ assert_no_difference 'User.count' do
27
+ post :create, :user => valid_user(:login => nil)
28
+ assert assigns(:user).errors.on(:login)
29
+ assert_response :success
30
+ end
31
+ end
32
+
33
+ def test_should_require_unique_login_on_create
34
+ assert_no_difference 'User.count' do
35
+ post :create, :user => valid_user(:login => 'eugene')
36
+ assert assigns(:user).errors.on(:login)
37
+ assert_response :success
38
+ end
39
+ end
40
+
41
+ def test_should_require_password_on_create
42
+ assert_no_difference 'User.count' do
43
+ post :create, :user => valid_user(:password => nil)
44
+ assert assigns(:user).errors.on(:password)
45
+ assert_response :success
46
+ end
47
+ end
48
+
49
+ def test_should_require_password_confirmation_on_create
50
+ assert_no_difference 'User.count' do
51
+ post :create, :user => valid_user(:password_confirmation => nil)
52
+ assert_response :success
53
+ end
54
+ end
55
+
56
+ def test_should_require_email_on_create
57
+ assert_no_difference 'User.count' do
58
+ post :create, :user => valid_user(:email => nil)
59
+ assert assigns(:user).errors.on(:email)
60
+ assert_response :success
61
+ end
62
+ end
63
+
64
+ def test_should_require_valid_email_on_create
65
+ assert_no_difference 'User.count' do
66
+ post :create, :user => valid_user(:email => 'invalid@')
67
+ assert assigns(:user).errors.on(:email)
68
+ assert_response :success
69
+ end
70
+ end
71
+
72
+ def test_should_require_authentication_for_show
73
+ get :show
74
+ assert_redirected_to login_url
75
+
76
+ @request.env['HTTP_ACCEPT'] = @request.env['Content-Type'] = 'text/xml'
77
+ get :show
78
+ assert_response :unauthorized
79
+ end
80
+
81
+ def test_should_require_authentication_for_edit
82
+ get :edit
83
+ assert_redirected_to login_url
84
+
85
+ @request.env['HTTP_ACCEPT'] = @request.env['Content-Type'] = 'text/xml'
86
+ get :edit
87
+ assert_response :unauthorized
88
+ end
89
+
90
+ def test_should_require_authentication_for_update
91
+ post :update
92
+ assert_redirected_to login_url
93
+
94
+ @request.env['HTTP_ACCEPT'] = @request.env['Content-Type'] = 'text/xml'
95
+ post :update
96
+ assert_response :unauthorized
97
+ end
98
+
99
+ def test_should_accept_http_basic_authentication
100
+ login_as('eugene')
101
+ get :edit
102
+ assert_response :success
103
+
104
+ logout!
105
+
106
+ # HTTP_AUTH
107
+ authenticate_as('eugene', 'secret')
108
+ get :edit
109
+ assert_response :success
110
+ end
111
+
112
+ protected
113
+ def valid_user(overrides = {})
114
+ { :login => 'rusty',
115
+ :email => 'rusty@example.com',
116
+ :password => 'secret',
117
+ :password_confirmation => 'secret' }.merge(overrides)
118
+ end
119
+ end
@@ -0,0 +1,37 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
3
+ require 'test_help'
4
+
5
+ class Test::Unit::TestCase
6
+ # Transactional fixtures accelerate your tests by wrapping each test method
7
+ # in a transaction that's rolled back on completion. This ensures that the
8
+ # test database remains unchanged so your fixtures don't have to be reloaded
9
+ # between every test method. Fewer database queries means faster tests.
10
+ #
11
+ # Read Mike Clark's excellent walkthrough at
12
+ # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
13
+ #
14
+ # Every Active Record database supports transactions except MyISAM tables
15
+ # in MySQL. Turn off transactional fixtures in this case; however, if you
16
+ # don't care one way or the other, switching from MyISAM to InnoDB tables
17
+ # is recommended.
18
+ self.use_transactional_fixtures = true
19
+
20
+ # Instantiated fixtures are slow, but give you @david where otherwise you
21
+ # would need people(:david). If you don't want to migrate your existing
22
+ # test cases which use the @david style and don't mind the speed hit (each
23
+ # instantiated fixtures translates to a database query per test method),
24
+ # then set this back to true.
25
+ self.use_instantiated_fixtures = false
26
+
27
+ # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
28
+ # If you need to control the loading order (due to foreign key constraints etc), you'll
29
+ # need to change this line to explicitly name the order you desire.
30
+ #
31
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
32
+ # -- they do not yet inherent this setting
33
+ #fixtures :all
34
+
35
+ # Mixin the helpers provided by the authentication library
36
+ include AuthenticationSystem::TestHelpers
37
+ end
@@ -0,0 +1,116 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class UserTest < Test::Unit::TestCase
4
+ fixtures :users
5
+
6
+ def test_should_create_user
7
+ assert_difference 'User.count' do
8
+ user = User.create(valid_user)
9
+ assert !user.new_record?, "#{user.errors.full_messages.to_sentence}"
10
+ end
11
+ end
12
+
13
+ def test_should_update_user
14
+ u = users(:eugene)
15
+ attributes = {
16
+ :login => 'eugene_smith',
17
+ :email => 'eugene.smith@example.com',
18
+ :password => 'terces',
19
+ :password_confirmation => 'terces'
20
+ }
21
+ assert u.update_attributes(attributes), u.errors.full_messages
22
+ end
23
+
24
+ def test_should_destroy_user
25
+ assert_difference 'User.count', -1 do
26
+ User.find(:first).destroy
27
+ end
28
+ end
29
+
30
+ def test_should_accept_valid_emails
31
+ %w(user@example.com user@example.co.uk ab@cd.ef 1@example.ca john.q@example.com john_q-pub.lic@ex-ample.co.uk 8375309@90210.org).each do |email|
32
+ assert_valid User.new(valid_user(:email => email))
33
+ end
34
+ end
35
+
36
+ def test_should_not_allow_invalid_emails
37
+ user = User.new(valid_user)
38
+
39
+ %w(@foo.bar foo@ foo@bar @).each do |email|
40
+ user.email = email
41
+ assert !user.valid?
42
+ assert user.errors.on(:email)
43
+ end
44
+ end
45
+
46
+ def test_should_not_allow_spaces_or_dots_in_login
47
+ user = User.new(valid_user)
48
+
49
+ ['foo.bar', 'foo bar'].each do |login|
50
+ user.login = login
51
+ assert !user.valid?
52
+ assert user.errors.on(:login)
53
+ end
54
+ end
55
+
56
+ def test_should_reset_password
57
+ users(:eugene).update_attributes(:password => 'new password', :password_confirmation => 'new password')
58
+ assert_equal users(:eugene), User.authenticate('eugene', 'new password')
59
+ end
60
+
61
+ def test_should_not_rehash_password_when_unchanged
62
+ users(:eugene).update_attributes(:login => 'enegue')
63
+ assert_equal users(:eugene), User.authenticate('enegue', 'secret')
64
+ end
65
+
66
+ def test_should_authenticate_user
67
+ assert_equal users(:eugene), User.authenticate('eugene', 'secret')
68
+ end
69
+
70
+ def test_should_set_remember_token
71
+ users(:eugene).remember
72
+ assert_not_nil users(:eugene).remember_token
73
+ assert_not_nil users(:eugene).remember_token_expires_at
74
+ end
75
+
76
+ def test_should_unset_remember_token
77
+ users(:eugene).remember
78
+ assert_not_nil users(:eugene).remember_token
79
+ users(:eugene).forget
80
+ assert_nil users(:eugene).remember_token
81
+ end
82
+
83
+ def test_should_remember_login_for_one_week
84
+ before = 1.week.from_now.utc
85
+ users(:eugene).remember_for 1.week
86
+ after = 1.week.from_now.utc
87
+ assert_not_nil users(:eugene).remember_token
88
+ assert_not_nil users(:eugene).remember_token_expires_at
89
+ assert users(:eugene).remember_token_expires_at.between?(before, after)
90
+ end
91
+
92
+ def test_should_remember_login_until_one_week_from_now
93
+ time = 1.week.from_now.utc
94
+ users(:eugene).remember_until time
95
+ assert_not_nil users(:eugene).remember_token
96
+ assert_not_nil users(:eugene).remember_token_expires_at
97
+ assert_equal users(:eugene).remember_token_expires_at, time
98
+ end
99
+
100
+ def test_should_remember_login_for_default_two_weeks
101
+ before = 2.weeks.from_now.utc
102
+ users(:eugene).remember
103
+ after = 2.weeks.from_now.utc
104
+ assert_not_nil users(:eugene).remember_token
105
+ assert_not_nil users(:eugene).remember_token_expires_at
106
+ assert users(:eugene).remember_token_expires_at.between?(before, after)
107
+ end
108
+
109
+ protected
110
+ def valid_user(overrides={})
111
+ { :login => 'rusty',
112
+ :email => 'rusty@example.com',
113
+ :password => 'secret',
114
+ :password_confirmation => 'secret' }.merge(overrides)
115
+ end
116
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: boilerplate_generator
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2007-05-08 00:00:00 -04:00
8
+ summary: Boilerplate is a generator for new Rails projects
9
+ require_paths:
10
+ - lib
11
+ email: packagethief@gmail.com
12
+ homepage: http://boilerplate.rubyforge.org
13
+ rubyforge_project: boilerplate
14
+ description: Boilerplate is a generator for new Rails projects
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Jeffrey Hardy (packagethief)
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - USAGE
36
+ - boilerplate_generator.rb
37
+ - lib/boilerplate_generator/version.rb
38
+ - templates/app/controllers/account_controller.rb
39
+ - templates/app/controllers/application.rb
40
+ - templates/app/controllers/user_controller.rb
41
+ - templates/app/helpers/account_helper.rb
42
+ - templates/app/helpers/user_helper.rb
43
+ - templates/app/models/user.rb
44
+ - templates/app/views/account/login.haml
45
+ - templates/app/views/account/login.html.erb
46
+ - templates/app/views/layouts/application.haml
47
+ - templates/app/views/layouts/application.html.erb
48
+ - templates/app/views/user/_form.haml
49
+ - templates/app/views/user/_form.html.erb
50
+ - templates/app/views/user/edit.haml
51
+ - templates/app/views/user/edit.html.erb
52
+ - templates/app/views/user/new.haml
53
+ - templates/app/views/user/new.html.erb
54
+ - templates/app/views/user/show.haml
55
+ - templates/app/views/user/show.html.erb
56
+ - templates/config/routes.rb
57
+ - templates/db/migrate/create_users.rb
58
+ - templates/lib/authentication_system.rb
59
+ - templates/test/fixtures/users.yml
60
+ - templates/test/functional/account_controller_test.rb
61
+ - templates/test/functional/user_controller_test.rb
62
+ - templates/test/test_helper.rb
63
+ - templates/test/unit/user_test.rb
64
+ test_files: []
65
+
66
+ rdoc_options: []
67
+
68
+ extra_rdoc_files: []
69
+
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ requirements: []
75
+
76
+ dependencies: []
77
+