merb-auth-slice-password-reset 1.1.0

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 (35) hide show
  1. data/.gitignore +15 -0
  2. data/LICENSE +20 -0
  3. data/README.textile +219 -0
  4. data/Rakefile +41 -0
  5. data/TODO +2 -0
  6. data/app/controllers/application.rb +5 -0
  7. data/app/controllers/passwords.rb +49 -0
  8. data/app/helpers/application_helper.rb +64 -0
  9. data/app/helpers/mailer_helper.rb +28 -0
  10. data/app/mailers/password_reset_mailer.rb +13 -0
  11. data/app/mailers/views/password_reset_mailer/password_reset.text.erb +3 -0
  12. data/app/views/layout/merb_auth_slice_password_reset.html.erb +16 -0
  13. data/app/views/passwords/forgot_password.html.erb +7 -0
  14. data/app/views/passwords/reset.html.erb +15 -0
  15. data/config/init.rb +80 -0
  16. data/lib/merb-auth-slice-password-reset.rb +82 -0
  17. data/lib/merb-auth-slice-password-reset/merbtasks.rb +112 -0
  18. data/lib/merb-auth-slice-password-reset/mixins/senile_user.rb +81 -0
  19. data/lib/merb-auth-slice-password-reset/mixins/senile_user/ar_senile_user.rb +19 -0
  20. data/lib/merb-auth-slice-password-reset/mixins/senile_user/dm_senile_user.rb +22 -0
  21. data/lib/merb-auth-slice-password-reset/mixins/senile_user/mm_senile_user.rb +22 -0
  22. data/lib/merb-auth-slice-password-reset/mixins/senile_user/sq_senile_user.rb +20 -0
  23. data/lib/merb-auth-slice-password-reset/slicetasks.rb +18 -0
  24. data/lib/merb-auth-slice-password-reset/spectasks.rb +75 -0
  25. data/public/javascripts/master.js +0 -0
  26. data/public/stylesheets/master.css +2 -0
  27. data/spec/mailers/password_reset_mailer_spec.rb +50 -0
  28. data/spec/mixins/senile_user_spec.rb +111 -0
  29. data/spec/requests/passwords_spec.rb +85 -0
  30. data/spec/spec_helper.rb +61 -0
  31. data/stubs/app/controllers/passwords.rb +13 -0
  32. data/stubs/app/mailers/views/password_reset_mailer/new_password.html.erb +3 -0
  33. data/stubs/app/mailers/views/password_reset_mailer/password_reset.text.erb +5 -0
  34. data/stubs/app/views/passwords/forgot_password.html.erb +7 -0
  35. metadata +169 -0
@@ -0,0 +1,22 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module SenileUser
5
+ module MMClassMethods
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ key :password_reset_code, String
9
+ end # base.class_eval
10
+ def find_with_password_reset_code(code)
11
+ find(:first, :conditions => {:password_reset_code => code})
12
+ end
13
+
14
+ def find_with_login_param(param_name, value)
15
+ find(:first, :conditions => {param_name => value})
16
+ end
17
+ end # self.extended
18
+ end # MMClassMethods
19
+ end # SenileUser
20
+ end # Mixins
21
+ end # Authentication
22
+ end #Merb
@@ -0,0 +1,20 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module SenileUser
5
+ module SQClassMethods
6
+ def self.extended(base)
7
+ def find_with_password_reset_code(code)
8
+ self[:password_reset_code => code]
9
+ end
10
+ def find_with_login_param(param_name, value)
11
+ self[param_name => value]
12
+ end
13
+ end # self.extended
14
+ end # SQClassMethods
15
+ module SQInstanceMethods
16
+ end # SQInstanceMethods
17
+ end # SenileUser
18
+ end # Mixins
19
+ end # Authentication
20
+ end # Merb
@@ -0,0 +1,18 @@
1
+ namespace :slices do
2
+ namespace :"merb-auth-slice-password-reset" do
3
+
4
+ # add your own merb-auth-slice-password-reset tasks here
5
+
6
+ # implement this to test for structural/code dependencies
7
+ # like certain directories or availability of other files
8
+ desc "Test for any dependencies"
9
+ task :preflight do
10
+ end
11
+
12
+ # implement this to perform any database related setup steps
13
+ desc "Migrate the database"
14
+ task :migrate do
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,75 @@
1
+ namespace :slices do
2
+ namespace :"merb-auth-slice-password-reset" do
3
+
4
+ desc "Run slice specs within the host application context"
5
+ task :spec => [ "spec:explain", "spec:default" ]
6
+
7
+ namespace :spec do
8
+
9
+ slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
10
+
11
+ task :explain do
12
+ puts "\nNote: By running MerbAuthSlicePasswordReset specs inside the application context any\n" +
13
+ "overrides could break existing specs. This isn't always a problem,\n" +
14
+ "especially in the case of views. Use these spec tasks to check how\n" +
15
+ "well your application conforms to the original slice implementation."
16
+ end
17
+
18
+ Spec::Rake::SpecTask.new('default') do |t|
19
+ t.spec_opts = ["--format", "specdoc", "--colour"]
20
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
21
+ end
22
+
23
+ desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
24
+ Spec::Rake::SpecTask.new('model') do |t|
25
+ t.spec_opts = ["--format", "specdoc", "--colour"]
26
+ if(ENV['MODEL'])
27
+ t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
28
+ else
29
+ t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
30
+ end
31
+ end
32
+
33
+ desc "Run all controller specs, run a spec for a specific Controller with CONTROLLER=MyController"
34
+ Spec::Rake::SpecTask.new('controller') do |t|
35
+ t.spec_opts = ["--format", "specdoc", "--colour"]
36
+ if(ENV['CONTROLLER'])
37
+ t.spec_files = Dir["#{slice_root}/spec/controllers/**/#{ENV['CONTROLLER']}_spec.rb"].sort
38
+ else
39
+ t.spec_files = Dir["#{slice_root}/spec/controllers/**/*_spec.rb"].sort
40
+ end
41
+ end
42
+
43
+ desc "Run all view specs, run specs for a specific controller (and view) with CONTROLLER=MyController (VIEW=MyView)"
44
+ Spec::Rake::SpecTask.new('view') do |t|
45
+ t.spec_opts = ["--format", "specdoc", "--colour"]
46
+ if(ENV['CONTROLLER'] and ENV['VIEW'])
47
+ t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/#{ENV['VIEW']}*_spec.rb"].sort
48
+ elsif(ENV['CONTROLLER'])
49
+ t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/*_spec.rb"].sort
50
+ else
51
+ t.spec_files = Dir["#{slice_root}/spec/views/**/*_spec.rb"].sort
52
+ end
53
+ end
54
+
55
+ desc "Run all mailer specs, run a spec for a specific Mailer with MAILER=MyMailer"
56
+ Spec::Rake::SpecTask.new('mailer') do |t|
57
+ t.spec_opts = ["--format", "specdoc", "--colour"]
58
+ if(ENV['MAILER'])
59
+ t.spec_files = Dir["#{slice_root}/spec/mailer/**/#{ENV['MAILER']}_spec.rb"].sort
60
+ else
61
+ t.spec_files = Dir["#{slice_root}/spec/mailers/**/*_spec.rb"].sort
62
+ end
63
+ end
64
+
65
+ desc "Run all specs and output the result in html"
66
+ Spec::Rake::SpecTask.new('html') do |t|
67
+ t.spec_opts = ["--format", "html"]
68
+ t.libs = ['lib', 'server/lib' ]
69
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ html, body { margin: 0; padding: 0; }
2
+ #container { width: 800px; margin: 4em auto; padding: 4em 4em 6em 4em; background: #DDDDDD; }
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "PasswordResetMailer" do
4
+
5
+ before(:all) do
6
+ Merb::Router.prepare { add_slice(:merb_auth_slice_password_reset)}
7
+ User.auto_migrate!
8
+ end
9
+
10
+ after(:all) do
11
+ Merb::Router.reset!
12
+ end
13
+
14
+ describe MerbAuthSlicePasswordReset::PasswordResetMailer do
15
+
16
+ def deliver(action, mail_opts= {},opts = {})
17
+ MerbAuthSlicePasswordReset::PasswordResetMailer.dispatch_and_deliver action, mail_opts, opts
18
+ @delivery = Merb::Mailer.deliveries.last
19
+ end
20
+
21
+ before(:each) do
22
+ @u = User.new(:email => "homer@simpsons.com", :login => "homer")
23
+ @u.send(:password_reset_code=, "12345")
24
+ @mailer_params = { :from => "info@mysite.com", :to => @u.email, :subject => "Welcome to MySite.com" }
25
+ end
26
+
27
+ after(:each) do
28
+ Merb::Mailer.deliveries.clear
29
+ end
30
+
31
+ it "should send mail to homer@simpsons.com for the password reset email" do
32
+ deliver(:password_reset, @mailer_params, :user => @u)
33
+ @delivery.assigns(:headers).should include("to: homer@simpsons.com")
34
+ end
35
+
36
+ it "should send the mail from 'info@mysite.com' for the the password reset email" do
37
+ deliver(:password_reset, @mailer_params, :user => @u)
38
+ @delivery.assigns(:headers).should include("from: info@mysite.com")
39
+ end
40
+
41
+ it "should mention the password reset link in the the password reset emails" do
42
+ deliver(:password_reset, @mailer_params, :user => @u)
43
+ the_url = MerbAuthSlicePasswordReset::PasswordResetMailer.new.slice_url(:reset_password, :password_reset_code => @u.password_reset_code)
44
+ the_url.should_not be_nil
45
+ @delivery.text.should include(the_url)
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,111 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "Senile User" do
4
+
5
+ before(:all) do
6
+ Merb::Router.prepare { add_slice(:merb_auth_slice_password_reset) }
7
+ end
8
+
9
+ after(:all) do
10
+ Merb::Router.reset!
11
+ end
12
+
13
+ describe "SenileUser Mixin" do
14
+
15
+ include SenileUserSpecHelper
16
+
17
+ before(:all) do
18
+ User.auto_migrate!
19
+ end
20
+
21
+ before(:each) do
22
+ @user = User.new(user_attributes)
23
+ end
24
+
25
+ after(:each) do
26
+ User.all.destroy!
27
+ end
28
+
29
+ it "should add the 'password_reset_code' property to the user model" do
30
+ @user.should respond_to(:password_reset_code)
31
+ @user.should respond_to(:password_reset_code=)
32
+ end
33
+
34
+ end
35
+
36
+
37
+ # describe "SenileUser Mixin Activation Code" do
38
+ #
39
+ # include SenileUserSpecHelper
40
+ #
41
+ # before(:all) do
42
+ # User.auto_migrate!
43
+ # end
44
+ #
45
+ # after(:each) do
46
+ # User.all.destroy!
47
+ # end
48
+ #
49
+ # before(:each) do
50
+ # @user = User.new(user_attributes)
51
+ # end
52
+ #
53
+ # it "should set the activation_code" do
54
+ # @user.activation_code.should be_nil
55
+ # @user.save
56
+ # @user.activation_code.should_not be_nil
57
+ # end
58
+ # end
59
+ #
60
+ #
61
+ # describe "Activation" do
62
+ #
63
+ # include SenileUserSpecHelper
64
+ #
65
+ # before(:all) do
66
+ # User.auto_migrate!
67
+ # end
68
+ #
69
+ # before(:each) do
70
+ # User.all.destroy!
71
+ # @user = User.create(user_attributes)
72
+ # end
73
+ #
74
+ # after(:each) do
75
+ # User.all.destroy!
76
+ # end
77
+ #
78
+ # it "should mark users as active" do
79
+ # @user.should_not be_active
80
+ # @user.activate
81
+ # @user.should be_active
82
+ # @user.reload
83
+ # @user.should be_active
84
+ # end
85
+ #
86
+ # it "should mark user as (just) activated" do
87
+ # @user.activate
88
+ # @user.should be_recently_activated
89
+ # end
90
+ #
91
+ # it "should set the activated_at property to the current date and time" do
92
+ # now = DateTime.now
93
+ # DateTime.should_receive(:now).and_return(now)
94
+ # @user.activate
95
+ # @user.activated_at.should == now
96
+ # end
97
+ #
98
+ # it "should clear out the activation code" do
99
+ # @user.activation_code.should_not be_nil
100
+ # @user.activate
101
+ # @user.activation_code.should be_nil
102
+ # end
103
+ #
104
+ # it "should send out the activation notification" do
105
+ # @user.should_receive(:send_activation_notification)
106
+ # @user.activate
107
+ # end
108
+ #
109
+ # end
110
+
111
+ end
@@ -0,0 +1,85 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+
3
+ describe "passwords" do
4
+ # It seems I can't spec requests unless I add an application.html.erb file.
5
+ # So the following two lines and the associated code in the before and after
6
+ # blocks create and delete that file.
7
+ LAYOUT_FILE = Merb.root / "app/views/layout/application.html.erb"
8
+ @layout_file_exits = false
9
+
10
+ before(:all) do
11
+ Merb::Router.reset!
12
+ Merb::Router.prepare { slice(:merb_auth_slice_password_reset) }
13
+ User.auto_migrate!
14
+
15
+ @u = User.create(:email => "homer@simpsons.com", :login => "homer")
16
+ @u.generate_password_reset_code
17
+ @code = @u.password_reset_code
18
+
19
+ if File.exists?(LAYOUT_FILE)
20
+ @layout_file_exits = true
21
+ else
22
+ File.open(LAYOUT_FILE, 'w') {|f| f.write("<%= catch_content :for_layout %>") }
23
+ end
24
+ end
25
+
26
+ after(:all) do
27
+ Merb::Router.reset!
28
+
29
+ File.delete(LAYOUT_FILE) unless @layout_file_exits
30
+ end
31
+
32
+ describe "reset" do
33
+
34
+ before(:each) do
35
+ @response = request("/reset_password/#{@code}")
36
+ end
37
+
38
+ it "should respond successfully" do
39
+ @response.should be_successful
40
+ end
41
+
42
+ it "should return a form to the user" do
43
+ @response.should have_xpath("//form")
44
+ end
45
+
46
+ it "should send the completed form params to /reset_check" do
47
+ @response.should have_xpath("//form[@action='/reset_check/#{@code}']")
48
+ end
49
+
50
+ it "should have password and password_confirmation attributes wrapped in a user object" do
51
+ @response.should have_xpath("//input[@name='user[password]']")
52
+ @response.should have_xpath("//input[@name='user[password_confirmation]']")
53
+ end
54
+
55
+ it "should raise a 404 if the code doesn't match a user" do
56
+ request("/reset_password/nonsense").status.should == 404
57
+ end
58
+
59
+ end
60
+
61
+ describe "reset_check" do
62
+
63
+ before(:each) do
64
+ @response = request("/reset_check/#{@code}", :method => "POST",
65
+ :params => { :user => { :password => :blah, :password_confirmation => :blah } } )
66
+ end
67
+
68
+ it "should raise a 404 if the code doesn't match a user" do
69
+ request("/reset_check/nonsense").status.should == 404
70
+ end
71
+
72
+ it "should redirect to root if the user is saved" do
73
+ @response.should redirect_to("/", :message => {:notice => "Your password has been changed"})
74
+ end
75
+
76
+ it "should render the reset form if the user can't be saved" do
77
+ class User; def update(val); return false; end; end
78
+ response = request("/reset_check/#{@code}", :method => "POST",
79
+ :params => { :user => { :password => :blah, :password_confirmation => :blah } } )
80
+ response.should have_xpath("//form[@action='/reset_check/#{@code}']")
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,61 @@
1
+ require 'rubygems'
2
+ require 'merb-core'
3
+ require 'merb-slices'
4
+ require 'spec'
5
+ require 'dm-core'
6
+ require 'dm-validations'
7
+
8
+ # Add merb-auth-slice-password-reset.rb to the search path
9
+ Merb::Plugins.config[:merb_slices][:auto_register] = true
10
+ Merb::Plugins.config[:merb_slices][:search_path] = File.join(File.dirname(__FILE__), '..', 'lib', 'merb-auth-slice-password-reset.rb')
11
+
12
+ # Using Merb.root below makes sure that the correct root is set for
13
+ # - testing standalone, without being installed as a gem and no host application
14
+ # - testing from within the host application; its root will be used
15
+ Merb.start_environment(
16
+ :testing => true,
17
+ :adapter => 'runner',
18
+ :environment => ENV['MERB_ENV'] || 'test',
19
+ :merb_root => Merb.root,
20
+ :session_store => :memory,
21
+ :exception_details => true
22
+ )
23
+
24
+ module Merb
25
+ module Test
26
+ module SliceHelper
27
+
28
+ # The absolute path to the current slice
29
+ def current_slice_root
30
+ @current_slice_root ||= File.expand_path(File.join(File.dirname(__FILE__), '..'))
31
+ end
32
+
33
+ # Whether the specs are being run from a host application or standalone
34
+ def standalone?
35
+ Merb.root == ::MerbAuthSlicePasswordReset.root
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+
42
+ module SenileUserSpecHelper
43
+ def user_attributes(options = {})
44
+ { :login => 'fred',
45
+ :email => 'fred@example.com'
46
+ }.merge(options)
47
+ end
48
+ end
49
+
50
+ class Merb::Mailer
51
+ self.delivery_method = :test_send
52
+ end
53
+
54
+ Spec::Runner.configure do |config|
55
+ config.include(Merb::Test::ViewHelper)
56
+ config.include(Merb::Test::RouteHelper)
57
+ config.include(Merb::Test::ControllerHelper)
58
+ config.include(Merb::Test::SliceHelper)
59
+ config.include(SenileUserSpecHelper)
60
+ config.before(:all){ User.auto_migrate! }
61
+ end