devise_invitable 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/Gemfile +3 -0
  2. data/Gemfile.lock +108 -0
  3. data/README.rdoc +13 -1
  4. data/Rakefile +4 -3
  5. data/VERSION +1 -1
  6. data/app/controllers/{invitations_controller.rb → devise/invitations_controller.rb} +1 -1
  7. data/app/views/devise/invitations/edit.html.erb +14 -0
  8. data/app/views/devise/invitations/new.html.erb +12 -0
  9. data/app/views/{devise_mailer → devise/mailer}/invitation.html.erb +0 -0
  10. data/{lib/devise_invitable → config}/locales/en.yml +3 -0
  11. data/devise_invitable.gemspec +48 -35
  12. data/lib/devise_invitable.rb +4 -1
  13. data/lib/devise_invitable/controllers/helpers.rb +3 -2
  14. data/lib/devise_invitable/controllers/url_helpers.rb +21 -17
  15. data/lib/devise_invitable/mailer.rb +5 -7
  16. data/lib/devise_invitable/model.rb +5 -4
  17. data/lib/devise_invitable/rails.rb +13 -2
  18. data/lib/devise_invitable/routes.rb +6 -25
  19. data/lib/devise_invitable/schema.rb +2 -2
  20. data/test/integration/invitable_test.rb +31 -44
  21. data/test/integration_tests_helper.rb +22 -3
  22. data/test/mailers/invitation_test.rb +4 -4
  23. data/test/model_tests_helper.rb +1 -1
  24. data/test/models/invitable_test.rb +6 -6
  25. data/test/models_test.rb +1 -5
  26. data/test/rails_app/app/controllers/application_controller.rb +1 -8
  27. data/test/rails_app/app/helpers/application_helper.rb +0 -1
  28. data/test/rails_app/app/models/user.rb +1 -1
  29. data/test/rails_app/app/views/layouts/application.html.erb +15 -0
  30. data/test/rails_app/config.ru +4 -0
  31. data/test/rails_app/config/application.rb +45 -0
  32. data/test/rails_app/config/boot.rb +13 -110
  33. data/test/rails_app/config/environment.rb +4 -43
  34. data/test/rails_app/config/environments/development.rb +22 -13
  35. data/test/rails_app/config/environments/production.rb +41 -20
  36. data/test/rails_app/config/environments/test.rb +35 -28
  37. data/test/rails_app/config/initializers/backtrace_silencers.rb +1 -1
  38. data/test/rails_app/config/initializers/devise.rb +82 -45
  39. data/test/rails_app/config/initializers/inflections.rb +10 -2
  40. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  41. data/test/rails_app/config/initializers/secret_token.rb +7 -0
  42. data/test/rails_app/config/initializers/session_store.rb +2 -9
  43. data/test/rails_app/config/locales/en.yml +5 -0
  44. data/test/rails_app/config/routes.rb +3 -2
  45. data/test/routes_test.rb +4 -4
  46. data/test/test_helper.rb +7 -36
  47. metadata +85 -33
  48. data/app/views/invitations/edit.html.erb +0 -14
  49. data/app/views/invitations/new.html.erb +0 -12
  50. data/test/rails_app/config/initializers/new_rails_defaults.rb +0 -21
  51. data/test/rails_app/vendor/plugins/devise_invitable/init.rb +0 -1
@@ -1,6 +1,5 @@
1
1
  module Devise
2
2
  module Models
3
-
4
3
  # Invitable is responsible to send emails with invitations.
5
4
  # When an invitation is sent to an email, an account is created for it.
6
5
  # An invitation has a link to set the password, as reset password from recoverable.
@@ -19,7 +18,6 @@ module Devise
19
18
  # User.find(1).accept_invitation! # accept invitation
20
19
  # User.find(1).resend_invitation! # reset invitation status and send invitation again
21
20
  module Invitable
22
-
23
21
  def self.included(base)
24
22
  base.class_eval do
25
23
  extend ClassMethods
@@ -29,7 +27,10 @@ module Devise
29
27
  # Accept an invitation by clearing invitation token and confirming it if model
30
28
  # is confirmable
31
29
  def accept_invitation!
32
- self.update_attribute :invitation_token, nil if self.invited?
30
+ if self.invited?
31
+ self.invitation_token = nil
32
+ self.save(:validate => false)
33
+ end
33
34
  end
34
35
 
35
36
  # Verifies whether a user has been invited or not
@@ -39,7 +40,7 @@ module Devise
39
40
 
40
41
  # Send invitation by email
41
42
  def send_invitation
42
- ::Devise::Mailer.invitation(self).deliver
43
+ ::Devise.mailer.invitation(self).deliver
43
44
  end
44
45
 
45
46
  # Reset invitation token and send invitation again
@@ -1,3 +1,14 @@
1
- Rails.configuration.after_initialize do
2
- I18n.load_path.unshift File.expand_path(File.join(File.dirname(__FILE__), 'locales', 'en.yml'))
1
+ module DeviseInvitable
2
+ class Engine < ::Rails::Engine
3
+
4
+ initializer "devise_invitable.add_url_helpers" do |app|
5
+ ActionController::Base.send :include, DeviseInvitable::Controllers::UrlHelpers
6
+ ActionView::Base.send :include, DeviseInvitable::Controllers::UrlHelpers
7
+ end
8
+
9
+ config.after_initialize do
10
+ Devise::Mailer.send :include, DeviseInvitable::Mailer
11
+ end
12
+
13
+ end
3
14
  end
@@ -1,29 +1,10 @@
1
- module DeviseInvitable
2
- module Routes #:doc:
3
- # Adds route generation for invitable. This method is responsible to
4
- # generate all needed routes for devise, based on what modules you have
5
- # defined in your model.
6
- # Examples: Let's say you have an User model configured to use
7
- # invitable module. After creating this inside your routes:
8
- #
9
- # map.devise_for :users
10
- #
11
- # this method is going to look inside your User model and create the
12
- # needed routes:
13
- #
14
- # # Invitation routes for Invitable, if User model has :invitable configured
15
- # new_user_invitation GET /users/invitation/new(.:format) {:controller=>"invitations", :action=>"new"}
16
- # user_invitation PUT /users/invitation(.:format) {:controller=>"invitations", :action=>"update"}
17
- # POST /users/invitation(.:format) {:controller=>"invitations", :action=>"create"}
18
- # accept_user_invitation GET /users/invitation/accept(.:format) {:controller=>"invitations", :action=>"edit"}
19
- #
20
-
1
+ module ActionDispatch::Routing
2
+ class Mapper
21
3
  protected
22
- def invitable(routes, mapping)
23
- routes.resource :invitation, :only => [:new, :create, :update], :as => mapping.path_names[:invitation]
24
- routes.send(:"accept_#{mapping.name}_invitation", mapping.path_names[:accept] || 'accept', :controller => 'invitations', :action => 'edit', :name_prefix => nil, :path_prefix => "#{mapping.as}/invitation", :conditions => { :method => :get })
4
+ def devise_invitation(mapping, controllers)
5
+ resource :invitation, :only => [:new, :create, :update], :path => mapping.path_names[:invitation], :controller => controllers[:invitations] do
6
+ get :edit, :path => mapping.path_names[:accept], :as => :accept
7
+ end
25
8
  end
26
9
  end
27
10
  end
28
-
29
- ActionDispatch::Routing::Mapper.send :include, DeviseInvitable::Routes
@@ -2,8 +2,8 @@ module DeviseInvitable
2
2
  module Schema
3
3
  # Creates invitation_token and invitation_sent_at.
4
4
  def invitable
5
- apply_schema :invitation_token, String, :limit => 20
6
- apply_schema :invitation_sent_at, DateTime
5
+ apply_devise_schema :invitation_token, String, :limit => 20
6
+ apply_devise_schema :invitation_sent_at, DateTime
7
7
  end
8
8
  end
9
9
  end
@@ -1,16 +1,15 @@
1
1
  require 'test/test_helper'
2
2
  require 'test/integration_tests_helper'
3
3
 
4
- class InvitationTest < ActionController::IntegrationTest
4
+ class InvitationTest < ActionDispatch::IntegrationTest
5
+ def teardown
6
+ Capybara.reset_sessions!
7
+ end
5
8
 
6
9
  def send_invitation(&block)
7
10
  visit new_user_invitation_path
8
11
 
9
- assert_response :success
10
- assert_template 'invitations/new'
11
- assert warden.authenticated?(:user)
12
-
13
- fill_in 'email', :with => 'user@test.com'
12
+ fill_in 'user_email', :with => 'user@test.com'
14
13
  yield if block_given?
15
14
  click_button 'Send an invitation'
16
15
  end
@@ -19,62 +18,53 @@ class InvitationTest < ActionController::IntegrationTest
19
18
  unless options[:visit] == false
20
19
  visit accept_user_invitation_path(:invitation_token => options[:invitation_token])
21
20
  end
22
- assert_response :success
23
- assert_template 'invitations/edit'
24
21
 
25
- fill_in 'Password', :with => '987654321'
26
- fill_in 'Password confirmation', :with => '987654321'
22
+ fill_in 'user_password', :with => '987654321'
23
+ fill_in 'user_password_confirmation', :with => '987654321'
27
24
  yield if block_given?
28
25
  click_button 'Set my password'
29
26
  end
30
27
 
31
28
  test 'not authenticated user should not be able to send an invitation' do
32
29
  get new_user_invitation_path
33
- assert_not warden.authenticated?(:user)
34
-
35
- assert_redirected_to new_user_session_path(:unauthenticated => true)
30
+ assert_redirected_to new_user_session_path
36
31
  end
37
32
 
38
33
  test 'authenticated user should be able to send an invitation' do
39
34
  sign_in_as_user
40
35
 
41
36
  send_invitation
42
- assert_template 'home/index'
43
- assert_equal 'An email with instructions about how to set the password has been sent.', flash[:notice]
37
+ assert_equal root_path, current_path
38
+ assert page.has_css?('p#notice', :text => 'An email with instructions about how to set the password has been sent.')
44
39
  end
45
40
 
46
41
  test 'authenticated user with invalid email should receive an error message' do
47
- user = create_user
48
- sign_in_as_user
42
+ user = create_full_user
43
+ sign_in_as_user(user)
49
44
  send_invitation do
50
- fill_in 'email', :with => user.email
45
+ fill_in 'user_email', :with => user.email
51
46
  end
52
47
 
53
- assert_response :success
54
- assert_template 'invitations/new'
55
- assert_have_selector "input[type=text][value='#{user.email}']"
56
- assert_contain 'Email has already been taken'
48
+ assert_equal user_invitation_path, current_path
49
+ assert page.has_css?("input[type=text][value='#{user.email}']")
50
+ assert page.has_css?('#error_explanation li', :text => 'Email has already been taken')
57
51
  end
58
52
 
59
53
  test 'authenticated user should not be able to visit edit invitation page' do
60
54
  sign_in_as_user
61
55
 
62
- get accept_user_invitation_path
56
+ visit accept_user_invitation_path
63
57
 
64
- assert_response :redirect
65
- assert_redirected_to root_path
66
- assert warden.authenticated?(:user)
58
+ assert_equal root_path, current_path
67
59
  end
68
60
 
69
61
  test 'not authenticated user with invalid invitation token should not be able to set his password' do
70
62
  user = create_user
71
63
  set_password :invitation_token => 'invalid_token'
72
64
 
73
- assert_response :success
74
- assert_template 'invitations/edit'
75
- assert_have_selector '#errorExplanation'
76
- assert_contain 'Invitation token is invalid'
77
- assert_not user.reload.valid_password?('987654321')
65
+ assert_equal user_invitation_path, current_path
66
+ assert page.has_css?('#error_explanation li', :text => 'Invitation token is invalid')
67
+ assert !user.reload.valid_password?('987654321')
78
68
  end
79
69
 
80
70
  test 'not authenticated user with valid invitation token but invalid password should not be able to set his password' do
@@ -83,19 +73,17 @@ class InvitationTest < ActionController::IntegrationTest
83
73
  fill_in 'Password confirmation', :with => 'other_password'
84
74
  end
85
75
 
86
- assert_response :success
87
- assert_template 'invitations/edit'
88
- assert_have_selector '#errorExplanation'
89
- assert_contain 'Password doesn\'t match confirmation'
90
- assert_not user.reload.valid_password?('987654321')
76
+ assert_equal user_invitation_path, current_path
77
+ assert page.has_css?('#error_explanation li', :text => 'Password doesn\'t match confirmation')
78
+ assert !user.reload.valid_password?('987654321')
91
79
  end
92
80
 
93
81
  test 'not authenticated user with valid data should be able to change his password' do
94
82
  user = create_user(false)
95
83
  set_password :invitation_token => user.invitation_token
96
84
 
97
- assert_template 'home/index'
98
- assert_equal 'Your password was set successfully. You are now signed in.', flash[:notice]
85
+ assert_equal root_path, current_path
86
+ assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
99
87
  assert user.reload.valid_password?('987654321')
100
88
  end
101
89
 
@@ -104,19 +92,18 @@ class InvitationTest < ActionController::IntegrationTest
104
92
  set_password :invitation_token => user.invitation_token do
105
93
  fill_in 'Password confirmation', :with => 'other_password'
106
94
  end
107
- assert_response :success
108
- assert_have_selector '#errorExplanation'
109
- assert_not user.reload.valid_password?('987654321')
95
+ assert_equal user_invitation_path, current_path
96
+ assert page.has_css?('#error_explanation')
97
+ assert !user.reload.valid_password?('987654321')
110
98
 
111
99
  set_password :invitation_token => user.invitation_token
112
- assert_equal 'Your password was set successfully. You are now signed in.', flash[:notice]
100
+ assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
113
101
  assert user.reload.valid_password?('987654321')
114
102
  end
115
103
 
116
104
  test 'sign in user automatically after setting it\'s password' do
117
105
  user = create_user(false)
118
106
  set_password :invitation_token => user.invitation_token
119
-
120
- assert warden.authenticated?(:user)
107
+ assert_equal root_path, current_path
121
108
  end
122
109
  end
@@ -4,8 +4,27 @@ class ActionController::IntegrationTest
4
4
  request.env['warden']
5
5
  end
6
6
 
7
- def sign_in_as_user
8
- Warden::Proxy.any_instance.stubs(:user).at_least_once.returns(User.new)
7
+ def create_full_user
8
+ @user ||= begin
9
+ user = User.create!(
10
+ :username => 'usertest',
11
+ :email => 'fulluser@test.com',
12
+ :password => '123456',
13
+ :password_confirmation => '123456',
14
+ :created_at => Time.now.utc
15
+ )
16
+ user.confirm!
17
+ user
18
+ end
19
+ end
20
+
21
+ def sign_in_as_user(user = nil)
22
+ user ||= create_full_user
23
+ visit new_user_session_path
24
+ fill_in 'user_email', :with => user.email
25
+ fill_in 'user_password', :with => '123456'
26
+ fill_in 'user_password', :with => user.password
27
+ click_button 'Sign in'
9
28
  end
10
29
 
11
30
  def create_user(accept_invitation = true)
@@ -13,7 +32,7 @@ class ActionController::IntegrationTest
13
32
  user.skip_confirmation!
14
33
  user.invitation_token = 'token'
15
34
  user.invitation_sent_at = Time.now.utc
16
- user.save(false)
35
+ user.save(:validate => false)
17
36
  user.accept_invitation! if accept_invitation
18
37
  user
19
38
  end
@@ -27,7 +27,7 @@ class InvitationMailTest < ActionMailer::TestCase
27
27
  end
28
28
 
29
29
  test 'content type should be set to html' do
30
- assert_equal 'text/html', mail.content_type
30
+ assert_equal 'text/html; charset=UTF-8', mail.content_type
31
31
  end
32
32
 
33
33
  test 'send invitation to the user email' do
@@ -39,13 +39,13 @@ class InvitationMailTest < ActionMailer::TestCase
39
39
  end
40
40
 
41
41
  test 'setup subject from I18n' do
42
- store_translations :en, :devise => { :mailer => { :invitation => 'Invitation' } } do
43
- assert_equal 'Invitation', mail.subject
42
+ store_translations :en, :devise => { :mailer => { :invitation => { :subject => 'Localized Invitation' } } } do
43
+ assert_equal 'Localized Invitation', mail.subject
44
44
  end
45
45
  end
46
46
 
47
47
  test 'subject namespaced by model' do
48
- store_translations :en, :devise => { :mailer => { :user => { :invitation => 'User Invitation' } } } do
48
+ store_translations :en, :devise => { :mailer => { :invitation => { :user_subject => 'User Invitation' } } } do
49
49
  assert_equal 'User Invitation', mail.subject
50
50
  end
51
51
  end
@@ -35,7 +35,7 @@ class ActiveSupport::TestCase
35
35
  user.skip_confirmation!
36
36
  user.invitation_token = invitation_token
37
37
  user.invitation_sent_at = Time.now.utc
38
- user.save(false)
38
+ user.save(:validate => false)
39
39
  user
40
40
  end
41
41
  end
@@ -45,7 +45,7 @@ class InvitableTest < ActiveSupport::TestCase
45
45
 
46
46
  User.stubs(:invite_for).returns(1.day)
47
47
  user.invitation_sent_at = 1.day.ago
48
- assert_not user.valid_invitation?
48
+ assert !user.valid_invitation?
49
49
  end
50
50
 
51
51
  test 'should never generate the same invitation token for different users' do
@@ -112,7 +112,7 @@ class InvitableTest < ActiveSupport::TestCase
112
112
  user.update_attribute(:invitation_token, nil)
113
113
  invited_user = User.send_invitation(:email => user.email)
114
114
  assert_equal invited_user, user
115
- assert_equal 'has already been taken', invited_user.errors[:email]
115
+ assert_equal ['has already been taken'], invited_user.errors[:email]
116
116
  end
117
117
 
118
118
  test 'should return a new record with errors if e-mail is blank' do
@@ -124,7 +124,7 @@ class InvitableTest < ActiveSupport::TestCase
124
124
  test 'should return a new record with errors if e-mail is invalid' do
125
125
  invited_user = User.send_invitation(:email => 'invalid_email')
126
126
  assert invited_user.new_record?
127
- assert_equal "is invalid", invited_user.errors[:email]
127
+ assert_equal ["is invalid"], invited_user.errors[:email]
128
128
  end
129
129
 
130
130
  test 'should find a user to set his password based on invitation_token' do
@@ -138,13 +138,13 @@ class InvitableTest < ActiveSupport::TestCase
138
138
  test 'should return a new record with errors if no invitation_token is found' do
139
139
  invited_user = User.accept_invitation!(:invitation_token => 'invalid_token')
140
140
  assert invited_user.new_record?
141
- assert_equal 'is invalid', invited_user.errors[:invitation_token]
141
+ assert_equal ['is invalid'], invited_user.errors[:invitation_token]
142
142
  end
143
143
 
144
144
  test 'should return a new record with errors if invitation_token is blank' do
145
145
  invited_user = User.accept_invitation!(:invitation_token => '')
146
146
  assert invited_user.new_record?
147
- assert_equal "can't be blank", invited_user.errors[:invitation_token]
147
+ assert_equal ["can't be blank"], invited_user.errors[:invitation_token]
148
148
  end
149
149
 
150
150
  test 'should return record with errors if invitation_token has expired' do
@@ -153,7 +153,7 @@ class InvitableTest < ActiveSupport::TestCase
153
153
  User.stubs(:invite_for).returns(10.hours)
154
154
  invited_user = User.accept_invitation!(:invitation_token => 'valid_token')
155
155
  assert_equal user, invited_user
156
- assert_equal "is invalid", invited_user.errors[:invitation_token]
156
+ assert_equal ["is invalid"], invited_user.errors[:invitation_token]
157
157
  end
158
158
 
159
159
  test 'should set successfully user password given the new password and confirmation' do
data/test/models_test.rb CHANGED
@@ -14,14 +14,10 @@ class ActiveRecordTest < ActiveSupport::TestCase
14
14
  modules.each do |mod|
15
15
  assert include_module?(klass, mod), "#{klass} not include #{mod}"
16
16
  end
17
-
18
- (Devise::ALL - modules).each do |mod|
19
- assert_not include_module?(klass, mod), "#{klass} include #{mod}"
20
- end
21
17
  end
22
18
 
23
19
  test 'add invitable module only' do
24
- assert_include_modules Invitable, :database_authenticatable, :invitable
20
+ assert_include_modules Invitable, :invitable
25
21
  end
26
22
 
27
23
  test 'set a default value for invite_for' do
@@ -1,10 +1,3 @@
1
- # Filters added to this controller apply to all controllers in the application.
2
- # Likewise, all the methods added will be available for all controllers.
3
-
4
1
  class ApplicationController < ActionController::Base
5
- helper :all # include all helpers, all the time
6
- protect_from_forgery # See ActionController::RequestForgeryProtection for details
7
-
8
- # Scrub sensitive parameters from your log
9
- filter_parameter_logging :password
2
+ protect_from_forgery
10
3
  end
@@ -1,3 +1,2 @@
1
- # Methods added to this helper will be available to all templates in the application.
2
1
  module ApplicationHelper
3
2
  end
@@ -1,4 +1,4 @@
1
1
  class User < ActiveRecord::Base
2
- devise :database_authenticatable, :confirmable, :recoverable, :rememberable, :validatable
2
+ devise :database_authenticatable, :confirmable, :invitable, :validatable
3
3
  attr_accessible :username, :email, :password, :password_confirmation
4
4
  end
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>RailsApp</title>
5
+ <%= stylesheet_link_tag :all %>
6
+ <%= javascript_include_tag :defaults %>
7
+ <%= csrf_meta_tag %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= content_tag :p, flash[:notice], :id => 'notice' unless flash[:notice].blank? %>
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run TestApp::Application
@@ -0,0 +1,45 @@
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
+ require 'devise'
10
+ require 'devise_invitable'
11
+
12
+ module RailsApp
13
+ class Application < Rails::Application
14
+ # Settings in config/environments/* take precedence over those specified here.
15
+ # Application configuration should go into files in config/initializers
16
+ # -- all .rb files in that directory are automatically loaded.
17
+
18
+ # Custom directories with classes and modules you want to be autoloadable.
19
+ # config.autoload_paths += %W(#{config.root}/extras)
20
+
21
+ # Only load the plugins named here, in the order given (default is alphabetical).
22
+ # :all can be used as a placeholder for all plugins not explicitly named.
23
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24
+
25
+ # Activate observers that should always be running.
26
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27
+
28
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30
+ # config.time_zone = 'Central Time (US & Canada)'
31
+
32
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34
+ # config.i18n.default_locale = :de
35
+
36
+ # JavaScript files you want as :defaults (application.js is always included).
37
+ # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
38
+
39
+ # Configure the default encoding used in templates for Ruby 1.9.
40
+ config.encoding = "utf-8"
41
+
42
+ # Configure sensitive parameters which will be filtered from the log file.
43
+ config.filter_parameters += [:password]
44
+ end
45
+ end