-
Notifications
You must be signed in to change notification settings - Fork 0
/
004_authlogic.rails
1418 lines (1202 loc) · 40.1 KB
/
004_authlogic.rails
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Authlogic template
#
# Builds on top of:
# - 003_haml.rails
#
# Inherited setup:
# - Rails application directory structure initiliazed with git as the SCM system
# - Standard "ignored files" are added to '.gitignore' (.log, database.yml, etc)
# - Saved standard "empty" directories you want in your app
# - A base database.yml file set up to talk to Sqlite3
# - Removes unnecessary files...except index.html (README, et cetera)
# - A single "Initial commit" commit in your repository history
# - RSpec, rspec-rails, webrat, and cucumber gems
# - Builds the gems
# - Removes the test directory
# - App initialized for both rspec and cucumber
# - ie: './script/generate rspec' (& cucumber) have been run
#
# Added functionality/features:
# - Authlogic authentication plugin
# - generates a 'user_session' model (for authlogic session management)
# - generates a user model & migration with the default attributes
# for authlogic
# - includes 'magic column' attributes
# - runs the migration created
# - added a Session controller for logging in
# - added _very_ basic login form which redirects
# back to the homepage upon successful sign in
# - Secure 'Forgot your email' implementation with specs
# - an 'exterior' controller with an 'index' action & view
# - intended solely for 'external' pages
# - a UsersController with #new & #create implemented
# - user is redirected to root path after creation
# - added a "map.root" definition pointing to ExteriorController#index
# - removes the default 'index.html' from public
#
# Helper methods
# After the first run of ./script/server, the init file is missing
# the "require 'rubygems'" line...we don't want to have to commit
# that in or leave the repository in a dirty state after the first
# "quick run"...
def replace_default_haml_init
filename = "vendor/plugins/haml/init.rb"
run "rm -f #{filename}"
file filename,
%q{begin
require File.join(File.dirname(__FILE__), 'lib', 'haml') # From here
rescue LoadError
require 'haml' # From gem
end
# Load Haml and Sass
Haml.init_rails(binding)
}
end
def replace_default_test_environment_config
filename = "config/environments/test.rb"
run "rm -f #{filename}"
file filename,
%q{# Settings specified here will take precedence over those in config/environment.rb
config.gem 'webrat', :lib => false
config.gem 'rspec-rails', :lib => false, :version => '>= 1.2.2'
config.gem 'rspec', :lib => false, :version => '>= 1.2.2'
config.gem 'cucumber', :version => ">= 0.2.0"
config.gem 'ZenTest', :lib => 'zentest'
config.gem 'builder'
config.gem 'diff-lcs', :lib => 'diff/lcs'
config.gem 'nokogiri'
config.gem 'treetop'
config.gem 'term-ansicolor', :lib => 'term/ansicolor'
config.gem 'thoughtbot-factory_girl', :lib => 'factory_girl', :source => 'http://gems.github.com'
config.gem 'bmabey-email_spec', :lib => 'email_spec', :source => 'http://gems.github.com', :version => '>= 0.1.3'
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.action_controller.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.action_view.cache_template_loading = true
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
config.action_mailer.default_url_options = {:host => 'localhost'}
}
end
def create_user_database_migration
file "db/migrate/20090327020652_create_users.rb",
%q{class CreateUsers < ActiveRecord::Migration
def self.up
create_table "users", :force => true do |t|
t.string :login, :null => false # optional, you can use email instead, or both
t.string :email, :null => false, :default => ""
t.string :perishable_token, :null => false, :default => ""
t.string :crypted_password, :null => false
t.string :password_salt, :null => false # optional, but highly recommended
t.string :single_access_token, :null => false # optional, see Authlogic::Session::Params
t.string :persistence_token, :null => false # optional, see Authlogic::Session::Perishability
t.integer :login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
t.integer :failed_login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
t.datetime :last_request_at # optional, see Authlogic::Session::MagicColumns
t.datetime :current_login_at # optional, see Authlogic::Session::MagicColumns
t.datetime :last_login_at # optional, see Authlogic::Session::MagicColumns
t.string :current_login_ip # optional, see Authlogic::Session::MagicColumns
t.string :last_login_ip # optional, see Authlogic::Session::MagicColumns
t.timestamps
end
add_index :users, :login
add_index :users, :last_request_at
add_index :users, :perishable_token
add_index :users, :email
end
def self.down
drop_table :users
end
end
}
end
def add_authlogic_to_user_model
file "app/models/user.rb",
%q{class User < ActiveRecord::Base
acts_as_authentic
def deliver_password_reset_instructions!
reset_perishable_token!
Notifier.deliver_password_reset_instructions(self)
end
end
}
end
def replace_default_user_spec
file_name = "spec/models/user_spec.rb"
run "rm #{file_name}"
file file_name,
%q{require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe User do
context "deliver_password_reset_instructions!" do
before(:each) do
@typical_user = Factory(:typical_user)
end
it "resets the user's perishable token" do
@typical_user.should_receive(:reset_perishable_token!)
@typical_user.deliver_password_reset_instructions!
end
it "sends an email to the user with instructions on how to reset their password" do
Notifier.should_receive(:deliver_password_reset_instructions).with(@typical_user)
@typical_user.deliver_password_reset_instructions!
end
end
end
}
end
def generate_session_controller_and_spec
generate "rspec_controller", "session"
file_name = "app/controllers/session_controller.rb"
run "rm #{file_name}"
file file_name,
%q{class SessionController < ApplicationController
before_filter :require_no_user, :only => [:new, :create]
before_filter :require_user, :only => :destroy
def new
@user_session = UserSession.new
end
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
redirect_to root_path
else
flash[:error] = "Login failed. Please try again."
render :action => :new
end
end
def destroy
current_user_session.destroy
redirect_to login_path
end
def confirm_logout
end
end
}
file "spec/controllers/session_controller.rb",
%q{require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe SessionController do
context "a GET to new" do
it "loads a user session for the view template" do
get :new
assigns(:user_session).should_not be_nil
end
end
context "a GET to confirm_logout" do
it "renders the confirm_logout view template" do
get :confirm_logout
response.should render_template('session/confirm_logout')
end
end
context "a DELETE to destroy" do
before(:each) do
@user_session = mock_model(UserSession, :destroy => true).as_null_object
controller.stub!(:current_user_session).and_return(@user_session)
end
def perform_request
delete :destroy
end
it "destroys the user's session" do
@user_session.should_receive(:destroy)
perform_request
end
it "redirects to the login path" do
perform_request
response.should redirect_to(login_path)
end
end
context "a POST to create" do
before(:each) do
controller.stub!(:current_user_session).and_return(nil)
@user_session = mock("New UserSession").as_null_object
@request_params = mock("Params for new UserSession")
UserSession.stub!(:new).with(@request_params).and_return(@user_session)
end
def perform_request
post :create, :user_session => @request_params
end
it "should save a new user session" do
@user_session.should_receive(:save).and_return(true)
perform_request
assigns(:user_session).should == @user_session
end
context "with valid credentials" do
before(:each) do
@user_session.stub!(:save).and_return(true)
end
it "redirects to the home page" do
perform_request
response.should redirect_to(root_path)
end
end
context "with invalid credentials" do
before(:each) do
@user_session.stub!(:save).and_return(false)
end
it "assigns an error for the view template" do
perform_request
flash[:error].should_not be_nil
end
it "renders the login form" do
perform_request
response.should render_template('session/new')
end
end
end
end
}
end
def generate_logout_confirmation_page
file "app/views/session/confirm_logout.html.haml",
%q{%h1 Logout confirmation
%p
You must be logged out to view the page requested.
%p
Are you sure you want to log out?
%ul
%li= link_to "Yes, log me out", logout_path, :id => :logout_confirmation
%li= link_to "No, take me back to the home page", root_path, :id => :cancel_and_go_home
}
end
def generate_login_view
file "app/views/session/new.html.haml",
%q{- form_for @user_session, :url => session_path, :method => :post, :html => {:id => :login_form} do |f|
= f.error_messages
%div
= f.label :login
%div
= f.text_field :login
%div
= f.label :password
%div
= f.password_field :password
%div
= f.submit "Login", :id => :attempt_login
= link_to "Forget your password?", new_support_password_reset_request_path
}
end
def generate_home_page
run "rm -f app/views/exterior/index.html.erb"
file "app/views/exterior/index.html.haml",
%q{%div
%p
- if logged_in?
Yes...it was really that easy. Now go write your app...
= link_to "[Logout]", logout_path, :id => "logout"
- else
Hello. Welcome to the future.
= link_to "Sign in", login_path, :id => "login_link"
or
= link_to "Sign up", signup_path, :id => "signup_link"
}
end
def replace_default_application_controller
file_name = "app/controllers/application_controller.rb"
run "rm -f #{file_name}"
file file_name,
%q{class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
helper_method :current_user_session, :current_user, :logged_in?
# Scrub sensitive parameters from your log
filter_parameter_logging :password, :password_confirmation
private
def current_user_session
return @current_user_session if defined?(@current_user_session)
@current_user_session = UserSession.find
end
def current_user
return @current_user if defined?(@current_user)
@current_user = current_user_session && current_user_session.record
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_session_path
return false
end
end
def require_no_user
if current_user
store_location
flash[:notice] = "You must be logged out to access this page"
redirect_to confirm_logout_path
return false
end
end
def store_location
session[:return_to] = request.request_uri
end
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
def logged_in?
!current_user.nil?
end
end
}
end
def generate_users_controller_and_spec
generate("rspec_controller", "users")
controller_file_name = "app/controllers/users_controller.rb"
controller_spec_file_name = "spec/controllers/users_controller_spec.rb"
run "rm -f #{controller_file_name}"
file controller_file_name,
%q{class UsersController < ApplicationController
before_filter :require_no_user, :only => [:new, :create]
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save
flash[:notice] = "Thanks for signing up!"
redirect_to root_path
else
flash[:error] = "There was an error setting up your account."
flash[:error] += "Please verify the supplied information and try again."
render :action => :new
end
end
end
}
run "rm -f #{controller_spec_file_name}"
file controller_spec_file_name,
%q{require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe UsersController do
before(:each) do
@user = mock_model(User)
end
context "a GET to new" do
it "assigns a user object for the view template" do
User.stub!(:new).and_return(@user)
get :new
assigns(:user).should == @user
end
end
context "a POST to create" do
before(:each) do
@user_params = mock("Params for new user")
User.stub!(:new).with(@user_params).and_return(@user)
@user.stub!(:save)
end
def perform_request
post :create, :user => @user_params
end
it "initializes a new user object with the supplied parameters" do
perform_request
assigns(:user).should == @user
end
it "saves the user object" do
@user.should_receive(:save)
perform_request
end
context "with valid attributes" do
before(:each) do
@user.stub!(:save).and_return(true)
end
it "redirects to the home page" do
perform_request
response.should redirect_to(root_path)
end
it "sets a notification for the view template" do
perform_request
flash[:notice].should_not be_nil
end
end
context "with invalid attributes" do
before(:each) do
@user.stub!(:save).and_return(false)
end
it "renders the signup form again" do
perform_request
response.should render_template('users/new')
end
it "sets an error message for the view template" do
perform_request
flash[:error].should_not be_nil
end
end
end
end
}
end
def generate_new_user_view
file "app/views/users/new.html.haml",
%q{%h1 Setting up a new account
- form_for @user do |f|
= error_messages_for(:user)
%div
= f.label :email
= f.text_field :email
%div
= f.label :login
= f.text_field :login
%div
= f.label :password
= f.password_field :password
%div
= f.label :password_confirmation, "Confirm Password"
= f.password_field :password_confirmation
%div
= submit_tag "Sign up", :id => :create_user
}
end
# Used this instead of 'routes' commands because I
# could not figure out a way to use namespaces with
# templates.
def replace_routes_file
file_name = "config/routes.rb"
run "rm #{file_name}"
file file_name,
%q{ActionController::Routing::Routes.draw do |map|
map.signup '/signup', :controller => 'users', :action => 'new', :method => :get
map.confirm_logout '/logout/confirm', :controller => 'session', :action => 'confirm_logout', :method => :get
map.logout '/logout', :controller => 'session', :action => 'destroy', :method => :delete
map.login '/login', :controller => 'session', :action => 'new', :method => :get
map.resources :users
map.resource :session, :controller => 'session'
map.root :controller => 'exterior'
map.namespace :support do |support|
support.resource :password_reset_request, :controller => "PasswordResetRequest"
end
# For some reason these seem to be required for RSpec to recognize namespaced
# routes correctly
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
}
end
def update_spec_helper_and_add_factories
run "rm spec/spec_helper.rb"
file "spec/spec_helper.rb",
%q{# This file is copied to ~/spec when you run 'ruby script/generate rspec'
# from the project root directory.
ENV["RAILS_ENV"] ||= 'test'
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
require 'spec/autorun'
require 'spec/rails'
require File.expand_path(File.dirname(__FILE__) + '/factories')
# This simplifies testing mailers with RSpec
# Read more here: http://github.com/bmabey/email-spec/tree/master
require "email_spec/helpers"
require "email_spec/matchers"
Spec::Runner.configure do |config|
# If you're not using ActiveRecord you should remove these
# lines, delete config/database.yml and disable :active_record
# in your config/boot.rb
config.use_transactional_fixtures = true
config.use_instantiated_fixtures = false
config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
# == Fixtures
#
# You can declare fixtures for each example_group like this:
# describe "...." do
# fixtures :table_a, :table_b
#
# Alternatively, if you prefer to declare them only once, you can
# do so right here. Just uncomment the next line and replace the fixture
# names with your fixtures.
#
# config.global_fixtures = :table_a, :table_b
#
# If you declare global fixtures, be aware that they will be declared
# for all of your examples, even those that don't use them.
#
# You can also declare which fixtures to use (for example fixtures for test/fixtures):
#
# config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
#
# == Mock Framework
#
# RSpec uses it's own mocking framework by default. If you prefer to
# use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
#
# == Notes
#
# For more information take a look at Spec::Runner::Configuration and Spec::Runner
# This simplifies testing mailers with RSpec
# Read more here: http://github.com/bmabey/email-spec/tree/master
config.include(EmailSpec::Helpers)
config.include(EmailSpec::Matchers)
end
}
run "rm spec/factories.rb"
file "spec/factories.rb",
%q[Factory.sequence :email do |n|
"standard_user_#{n}@example.com"
end
Factory.sequence :login do |n|
"standard_user_#{n}"
end
Factory.define :typical_user, :class => User do |f|
f.email { Factory.next(:email) }
f.login { Factory.next(:login) }
f.password 'password'
f.password_confirmation 'password'
end
]
end
def add_notifier_spec
file "spec/models/notifier_spec.rb",
%q[require File.dirname(__FILE__) + '/../spec_helper'
describe Notifier do
include ActionController::UrlWriter
before(:each) do
@host = "example.com"
end
before(:all) do
@user = User.new(:login => "user_1", :password => "tester", :password_confirmation => "password", :email => "jdoe@example.com")
end
context "emailing password reset instructions" do
before(:each) do
@notification = Notifier.create_password_reset_instructions(@user)
end
subject {@notification}
it "delivers the message to the supplied user's email" do
should deliver_to(@user.email)
end
it "should contain a link to the confirmation page" do
should have_body_text(/#{edit_support_password_reset_request_url(:host => @host)}/)
end
end
end
]
end
def add_notifier_model
file "app/models/notifier.rb",
%q{class Notifier < ActionMailer::Base
default_url_options[:host] = "example.com"
def password_reset_instructions(user)
subject "How to reset your password..."
from "Admin <noreply@#{default_url_options[:host]}>"
recipients user.email
sent_on Time.now
body :edit_support_password_reset_request_url => edit_support_password_reset_request_url(:id => user.perishable_token)
end
end
}
end
def add_view_for_password_reset_instructions
file "app/views/notifier/password_reset_instructions.html.haml",
%q{A request to reset your password has been made.
If you did not make this request, simply ignore this email.
If you did make this request just click the link below:
= @edit_support_password_reset_request_url
If the above URL does not work try copying and pasting it into your browser.
If you continue to have problem please feel free to contact us.
}
end
def add_password_reset_request_views
file "app/views/support/password_reset_request/new.html.haml",
%q{%h1 Send me a link to reset my password
- form_for User.new, :url => support_password_reset_request_path, :method => :post, :html => {:id => :new_reset_password_request, :class => nil} do |f|
= f.label :email
= f.text_field :email
= submit_tag "Submit"
}
file "app/views/support/password_reset_request/edit.html.haml",
%q{%h1 Change My Password
- form_for @user, :url => support_password_reset_request_path(:id => params[:id]), :method => :put do |f|
- if f.error_messages
.error
= f.error_messages
%div
= f.label :password
= f.password_field :password
%div
= f.label :password_confirmation
= f.password_field :password_confirmation
%div
= f.submit "Update my password and log me in"
}
end
def add_password_reset_request_controller_spec
file "spec/controllers/support/password_reset_request_controller_spec.rb",
%q{require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
describe Support::PasswordResetRequestController do
before(:each) do
@user = mock_model(User, :email => "jdoe@example.com")
end
shared_examples_for "Any perishable token request" do
it "looks up the user using the supplied perishable token" do
User.should_receive(:find_using_perishable_token).with(@token)
perform_request
end
end
shared_examples_for "Any invalid perishable token request" do
it "redirects to the home page" do
perform_request
response.should redirect_to(root_path)
end
it "notifies the user that their request failed" do
perform_request
flash[:error].should_not be_nil
end
end
context "a GET to new" do
it "renders the 'new' template" do
get :new
response.should render_template('support/password_reset_request/new')
end
end
context "a POST to create" do
def make_request
post :create, :user => {:email => @user.email}
end
context "with an email that exists in the system" do
before(:each) do
User.stub!(:find_by_email).with(@user.email).and_return(@user)
@user.stub!(:deliver_password_reset_instructions!)
end
it "delivers the password reset instructions to the user" do
@user.should_receive(:deliver_password_reset_instructions!)
make_request
end
it "redirects to the home page" do
make_request
response.should redirect_to(root_path)
end
it "notifies the user to check their email" do
make_request
flash[:notice].should_not be_nil
end
end
context "with an email that does not exist in the system" do
before(:each) do
User.stub!(:find_by_email).and_return(nil)
end
it "notifies the user that the email does not exist" do
make_request
flash[:error].should_not be_nil
end
it "renders the password reset request form again" do
make_request
response.should render_template('support/password_reset_request/new')
end
end
end
context "a GET to edit" do
before(:each) do
@token = mock("User's perishable token")
end
def perform_request
get :edit, :id => @token
end
it_should_behave_like "Any perishable token request"
context "with a valid token" do
before(:each) do
User.stub!(:find_using_perishable_token).and_return(@user)
end
it "renders the reset password form" do
perform_request
response.should render_template('support/password_reset_request/edit')
end
end
context "with an invalid token" do
before(:each) do
User.stub!(:find_using_perishable_token).and_return(nil)
end
it_should_behave_like "Any invalid perishable token request"
end
end
context "a PUT to update" do
def perform_request
put :update, :id => @token, :user => {:password => @password, :password_confirmation => @password}
end
context "with a valid token" do
before(:each) do
@token = mock("User's perishable token")
@password = mock("The password the user supplied")
User.stub!(:find_using_perishable_token).with(@token).and_return(@user)
@user.stub!(:save)
@user.stub!(:password=).with(@password)
@user.stub!(:password_confirmation=).with(@password)
end
it "updates the user's password with the supplied information" do
@user.should_receive(:password=).with(@password)
@user.should_receive(:password_confirmation=).with(@password)
perform_request
end
it "saves the user" do
@user.should_receive(:save)
perform_request
end
context "and password" do
before(:each) do
@user.stub!(:save).and_return(true)
end
it "notifies them that their password was updated" do
perform_request
flash[:notice].should_not be_nil
end
it "redirects back to the home page" do
perform_request
response.should redirect_to(root_path)
end
end
context "but an invalid password" do
before(:each) do
@user.stub!(:save).and_return(false)
end
it "notifies them that their password was updated" do
perform_request
flash[:error].should_not be_nil
end
it "renders the reset password form" do
perform_request
response.should render_template('support/password_reset_request/edit')
end
end
end
context "with an invalid token" do
before(:each) do
User.stub!(:find_using_perishable_token).and_return(nil)
end
it_should_behave_like "Any invalid perishable token request"
it "does not update the password or save the user" do
@user.should_not_receive(:password)
@user.should_not_receive(:password_confirmation)
@user.should_not_receive(:save)
perform_request
end
end
end
end
}
end
def add_password_reset_request_controller
file "app/controllers/support/password_reset_request_controller.rb",
%q{class Support::PasswordResetRequestController < ApplicationController
before_filter :require_no_user
def new
render
end
def create
@user = User.find_by_email(supplied_email)
if @user
@user.deliver_password_reset_instructions!
flash[:notice] = "We have emailed you a link to reset your password which expires in 10 minutes. "
flash[:notice] += "Don't worry if you can't get to it right away...you can restart this process as many times as you need to."
redirect_to root_url
else
flash[:error] = "No user was found with that email address"
render :action => :new
end
end
def edit
unless user_found_using_perishable_token
deny_request_and_send_to_home_page
end
end
def update
if user_not_found_using_perishable_token
deny_request_and_send_to_home_page && return
end
@user.password = params[:user][:password]
@user.password_confirmation = params[:user][:password_confirmation]
if @user.save
flash[:notice] = "Password successfully updated"
redirect_to root_path
else
flash[:error] = "Your password was not updated."
render :action => :edit
end
end
private
def supplied_email
params[:user] && params[:user][:email]
end
def user_found_using_perishable_token
@user ||= User.find_using_perishable_token(params[:id])
end
def user_not_found_using_perishable_token
user_found_using_perishable_token.nil?
end
def deny_request_and_send_to_home_page
flash[:error] = "We're sorry, but we could not locate your account. " +
"If you are having issues try copying and pasting the URL " +
"from your email into your browser or restarting the " +
"reset password process."
redirect_to root_url
end
end
}
end
def add_gem_for_testing_email
file "features/support/env.rb",
%q{# Sets up the Rails environment for Cucumber
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
require 'cucumber/rails/world'
require 'cucumber/formatters/unicode' # Comment out this line if you don't want Cucumber Unicode support
# This adds support for email matchers in cucumber
# Read more here: http://github.com/bmabey/email-spec/tree/master
require 'email_spec/cucumber'
Cucumber::Rails.use_transactional_fixtures