devise_invitable 2.0.14 → 3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -0
- data/README.md +166 -97
- data/app/controllers/devise/invitations_controller.rb +8 -2
- data/app/controllers/devise_invitable/registrations_controller.rb +15 -10
- data/lib/devise_invitable/invitation.rb +89 -0
- data/lib/devise_invitable/inviter.rb +23 -1
- data/lib/devise_invitable/migrator.rb +113 -0
- data/lib/devise_invitable/models.rb +353 -86
- data/lib/devise_invitable/rails.rb +10 -0
- data/lib/devise_invitable/version.rb +1 -1
- data/lib/devise_invitable.rb +40 -4
- data/lib/generators/active_record/devise_invitable_install_generator.rb +22 -0
- data/lib/generators/active_record/devise_invitable_upgrade_generator.rb +28 -0
- data/lib/generators/active_record/templates/create_devise_invitations.rb +23 -0
- data/lib/generators/active_record/templates/upgrade_devise_invitations.rb +72 -0
- data/lib/generators/devise_invitable/devise_invitable_generator.rb +2 -4
- data/lib/generators/devise_invitable/install_generator.rb +8 -10
- data/lib/generators/devise_invitable/upgrade_generator.rb +16 -0
- data/lib/generators/mongoid/devise_invitable_install_generator.rb +7 -0
- data/lib/generators/mongoid/devise_invitable_upgrade_generator.rb +11 -0
- data/lib/tasks/devise_invitable_tasks.rake +10 -0
- data/test/functional/registrations_controller_test.rb +24 -3
- data/test/generators_test.rb +16 -9
- data/test/integration/invitation_test.rb +24 -3
- data/test/mailers/invitation_mail_test.rb +1 -0
- data/test/models/invitable_test.rb +40 -2
- data/test/models/separate_invitations_test.rb +181 -0
- data/test/models_test.rb +34 -0
- data/test/rails_app/app/controllers/boolean_inviter_invitations_controller.rb +11 -0
- data/test/rails_app/app/controllers/restricted_invitations_controller.rb +9 -0
- data/test/rails_app/app/models/user.rb +8 -2
- data/test/rails_app/config/initializers/devise.rb +0 -9
- data/test/rails_app/config/routes.rb +2 -0
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +22 -0
- data/test/routes_test.rb +6 -0
- metadata +26 -13
- data/lib/generators/active_record/devise_invitable_generator.rb +0 -19
- data/lib/generators/active_record/templates/migration.rb +0 -22
- data/lib/generators/mongoid/devise_invitable_generator.rb +0 -8
data/lib/devise_invitable.rb
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
module DeviseInvitable
|
|
2
|
+
class AmbiguousInviteKeyError < StandardError; end
|
|
3
|
+
class ResourceAttributesRequirePlaceholderError < ArgumentError; end
|
|
4
|
+
|
|
2
5
|
autoload :Inviter, 'devise_invitable/inviter'
|
|
6
|
+
autoload :Invitation, 'devise_invitable/invitation'
|
|
3
7
|
autoload :Mailer, 'devise_invitable/mailer'
|
|
8
|
+
autoload :Migrator, 'devise_invitable/migrator'
|
|
4
9
|
autoload :Mapping, 'devise_invitable/mapping'
|
|
5
10
|
autoload :ParameterSanitizer, 'devise_invitable/parameter_sanitizer'
|
|
6
11
|
module Controllers
|
|
@@ -10,6 +15,7 @@ module DeviseInvitable
|
|
|
10
15
|
end
|
|
11
16
|
|
|
12
17
|
require 'devise'
|
|
18
|
+
require 'active_support/deprecation'
|
|
13
19
|
require 'devise_invitable/routes'
|
|
14
20
|
require 'devise_invitable/rails'
|
|
15
21
|
|
|
@@ -58,16 +64,26 @@ module Devise
|
|
|
58
64
|
mattr_accessor :resend_invitation
|
|
59
65
|
@@resend_invitation = true
|
|
60
66
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
67
|
+
# Deprecated compatibility setting. Invitations now always use a polymorphic
|
|
68
|
+
# invited_by association.
|
|
63
69
|
mattr_accessor :invited_by_class_name
|
|
64
70
|
@@invited_by_class_name = nil
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
def self.invited_by_class_name=(value)
|
|
73
|
+
DeviseInvitable.warn_deprecated_invited_by_setting(:invited_by_class_name)
|
|
74
|
+
@@invited_by_class_name = value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Deprecated compatibility setting. The shared invitations table always uses
|
|
78
|
+
# invited_by_id and invited_by_type.
|
|
68
79
|
mattr_accessor :invited_by_foreign_key
|
|
69
80
|
@@invited_by_foreign_key = nil
|
|
70
81
|
|
|
82
|
+
def self.invited_by_foreign_key=(value)
|
|
83
|
+
DeviseInvitable.warn_deprecated_invited_by_setting(:invited_by_foreign_key)
|
|
84
|
+
@@invited_by_foreign_key = value
|
|
85
|
+
end
|
|
86
|
+
|
|
71
87
|
# Public: The column name used for counter_cache column. If this is nil,
|
|
72
88
|
# the #invited_by association is declared without counter_cache. (default: nil)
|
|
73
89
|
mattr_accessor :invited_by_counter_cache
|
|
@@ -83,6 +99,26 @@ module Devise
|
|
|
83
99
|
# set when user is invited or it will be set later (default: true).
|
|
84
100
|
mattr_accessor :require_password_on_accepting
|
|
85
101
|
@@require_password_on_accepting = true
|
|
102
|
+
|
|
103
|
+
# Public: Create the invited resource as soon as an invitation is sent.
|
|
104
|
+
# When false, only the invitation and its invite keys are persisted and the
|
|
105
|
+
# resource is created when the invitation is accepted.
|
|
106
|
+
mattr_accessor :create_resource_on_invitation
|
|
107
|
+
@@create_resource_on_invitation = false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
module DeviseInvitable
|
|
111
|
+
def self.deprecator
|
|
112
|
+
@deprecator ||= ActiveSupport::Deprecation.new('3.0', 'DeviseInvitable')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.warn_deprecated_invited_by_setting(setting)
|
|
116
|
+
deprecator.warn(
|
|
117
|
+
"The #{setting} setting is deprecated and has no effect. " \
|
|
118
|
+
"Invitations now always use a polymorphic invited_by association " \
|
|
119
|
+
"with invited_by_id and invited_by_type."
|
|
120
|
+
)
|
|
121
|
+
end
|
|
86
122
|
end
|
|
87
123
|
|
|
88
124
|
Devise.add_module :invitable, controller: :invitations, model: 'devise_invitable/models', route: { invitation: [nil, :new, :accept] }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rails/generators/active_record'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Generators
|
|
5
|
+
class DeviseInvitableInstallGenerator < ActiveRecord::Generators::Base
|
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
7
|
+
argument :name, type: :string, default: 'devise_invitation'
|
|
8
|
+
|
|
9
|
+
def copy_devise_invitations_migration
|
|
10
|
+
migration_template 'create_devise_invitations.rb',
|
|
11
|
+
'db/migrate/create_devise_invitations.rb',
|
|
12
|
+
migration_version: migration_version
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def migration_version
|
|
16
|
+
if Rails::VERSION::MAJOR >= 5
|
|
17
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'rails/generators/active_record'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Generators
|
|
5
|
+
class DeviseInvitableUpgradeGenerator < ActiveRecord::Generators::Base
|
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
7
|
+
|
|
8
|
+
class_option :invite_keys, type: :array, default: ['email']
|
|
9
|
+
|
|
10
|
+
def copy_upgrade_migration
|
|
11
|
+
migration_template 'upgrade_devise_invitations.rb',
|
|
12
|
+
"db/migrate/upgrade_#{table_name}_devise_invitations.rb",
|
|
13
|
+
migration_version: migration_version,
|
|
14
|
+
invite_keys: options[:invite_keys]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def migration_version
|
|
18
|
+
if Rails::VERSION::MAJOR >= 5
|
|
19
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def invite_keys
|
|
24
|
+
options[:invite_keys]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class CreateDeviseInvitations < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
+
def change
|
|
3
|
+
create_table :devise_invitations do |t|
|
|
4
|
+
t.string :invitation_token
|
|
5
|
+
t.datetime :invitation_created_at
|
|
6
|
+
t.datetime :invitation_sent_at
|
|
7
|
+
t.datetime :invitation_accepted_at
|
|
8
|
+
t.datetime :superseded_at
|
|
9
|
+
t.string :superseded_reason
|
|
10
|
+
t.datetime :revoked_at
|
|
11
|
+
t.datetime :resource_created_at
|
|
12
|
+
t.json :invite_key, null: false, default: {}
|
|
13
|
+
t.string :invite_key_digest, null: false
|
|
14
|
+
t.references :invitable, polymorphic: true
|
|
15
|
+
t.references :invited_by, polymorphic: true
|
|
16
|
+
t.timestamps
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
add_index :devise_invitations, :invitation_token, unique: true
|
|
20
|
+
add_index :devise_invitations, [:invitable_type, :invite_key_digest],
|
|
21
|
+
name: 'index_devise_invitations_on_type_and_invite_key'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
|
|
3
|
+
class Upgrade<%= table_name.camelize %>DeviseInvitations < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
OLD_INVITATION_COLUMNS = %i[
|
|
5
|
+
invitation_token invitation_created_at invitation_sent_at
|
|
6
|
+
invitation_accepted_at invited_by_id invited_by_type
|
|
7
|
+
].freeze
|
|
8
|
+
|
|
9
|
+
def up
|
|
10
|
+
create_devise_invitations_table unless table_exists?(:devise_invitations)
|
|
11
|
+
|
|
12
|
+
resource_class = Class.new(ActiveRecord::Base) do
|
|
13
|
+
self.table_name = '<%= table_name %>'
|
|
14
|
+
self.inheritance_column = :_type_disabled
|
|
15
|
+
end
|
|
16
|
+
invitation_class = Class.new(ActiveRecord::Base) do
|
|
17
|
+
self.table_name = 'devise_invitations'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
resource_class.where.not(invitation_created_at: nil).find_each do |resource|
|
|
21
|
+
invite_key = resource.attributes.slice(<%= invite_keys.map(&:to_s).map(&:inspect).join(', ') %>)
|
|
22
|
+
digest_components = invite_key.map { |key, value| [key.to_s, value.to_s] }.sort.flatten
|
|
23
|
+
invitation_class.create!(
|
|
24
|
+
invitation_token: resource.invitation_token,
|
|
25
|
+
invitation_created_at: resource.invitation_created_at,
|
|
26
|
+
invitation_sent_at: resource.invitation_sent_at,
|
|
27
|
+
invitation_accepted_at: resource.invitation_accepted_at,
|
|
28
|
+
resource_created_at: resource.invitation_created_at,
|
|
29
|
+
invite_key: invite_key,
|
|
30
|
+
invite_key_digest: Digest::SHA256.hexdigest((['<%= class_name %>'] + digest_components).join("\0")),
|
|
31
|
+
invitable_type: '<%= class_name %>',
|
|
32
|
+
invitable_id: resource.id,
|
|
33
|
+
invited_by_type: resource.invited_by_type,
|
|
34
|
+
invited_by_id: resource.invited_by_id,
|
|
35
|
+
created_at: resource.invitation_created_at,
|
|
36
|
+
updated_at: resource.invitation_accepted_at || resource.invitation_sent_at || resource.invitation_created_at
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
OLD_INVITATION_COLUMNS.each do |column|
|
|
41
|
+
remove_column :<%= table_name %>, column if column_exists?(:<%= table_name %>, column)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def down
|
|
46
|
+
raise ActiveRecord::IrreversibleMigration,
|
|
47
|
+
'Multiple invitation records cannot be losslessly moved back onto <%= table_name %>'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def create_devise_invitations_table
|
|
53
|
+
create_table :devise_invitations do |t|
|
|
54
|
+
t.string :invitation_token
|
|
55
|
+
t.datetime :invitation_created_at
|
|
56
|
+
t.datetime :invitation_sent_at
|
|
57
|
+
t.datetime :invitation_accepted_at
|
|
58
|
+
t.datetime :superseded_at
|
|
59
|
+
t.string :superseded_reason
|
|
60
|
+
t.datetime :revoked_at
|
|
61
|
+
t.datetime :resource_created_at
|
|
62
|
+
t.json :invite_key, null: false, default: {}
|
|
63
|
+
t.string :invite_key_digest, null: false
|
|
64
|
+
t.references :invitable, polymorphic: true
|
|
65
|
+
t.references :invited_by, polymorphic: true
|
|
66
|
+
t.timestamps
|
|
67
|
+
end
|
|
68
|
+
add_index :devise_invitations, :invitation_token, unique: true
|
|
69
|
+
add_index :devise_invitations, [:invitable_type, :invite_key_digest],
|
|
70
|
+
name: 'index_devise_invitations_on_type_and_invite_key'
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -3,14 +3,12 @@ module DeviseInvitable
|
|
|
3
3
|
class DeviseInvitableGenerator < Rails::Generators::NamedBase
|
|
4
4
|
namespace 'devise_invitable'
|
|
5
5
|
|
|
6
|
-
desc 'Add :invitable directive
|
|
6
|
+
desc 'Add the :invitable directive to the given model.'
|
|
7
7
|
|
|
8
8
|
def inject_devise_invitable_content
|
|
9
9
|
path = File.join('app', 'models', "#{file_path}.rb")
|
|
10
10
|
inject_into_file(path, 'invitable, :', after: 'devise :') if File.exist?(path)
|
|
11
11
|
end
|
|
12
|
-
|
|
13
|
-
hook_for :orm
|
|
14
12
|
end
|
|
15
13
|
end
|
|
16
|
-
end
|
|
14
|
+
end
|
|
@@ -2,14 +2,16 @@ module DeviseInvitable
|
|
|
2
2
|
module Generators
|
|
3
3
|
class InstallGenerator < Rails::Generators::Base
|
|
4
4
|
source_root File.expand_path('../../templates', __FILE__)
|
|
5
|
-
desc '
|
|
5
|
+
desc 'Install the invitations store, add configuration, and copy the English locale.'
|
|
6
|
+
|
|
7
|
+
hook_for :orm, as: :devise_invitable_install
|
|
6
8
|
|
|
7
9
|
def add_config_options_to_initializer
|
|
8
10
|
devise_initializer_path = 'config/initializers/devise.rb'
|
|
9
11
|
if File.exist?(devise_initializer_path)
|
|
10
12
|
old_content = File.read(devise_initializer_path)
|
|
11
13
|
|
|
12
|
-
if old_content.match(
|
|
14
|
+
if old_content.match(/^\s*# ==> Configuration for :invitable\n/)
|
|
13
15
|
false
|
|
14
16
|
else
|
|
15
17
|
inject_into_file(devise_initializer_path, before: " # ==> Configuration for :confirmable\n") do
|
|
@@ -44,14 +46,10 @@ module DeviseInvitable
|
|
|
44
46
|
# Default: true
|
|
45
47
|
# config.resend_invitation = false
|
|
46
48
|
|
|
47
|
-
#
|
|
48
|
-
# the
|
|
49
|
-
# Default:
|
|
50
|
-
# config.
|
|
51
|
-
|
|
52
|
-
# The foreign key to the inviting model (if invited_by_class_name is set)
|
|
53
|
-
# Default: :invited_by_id
|
|
54
|
-
# config.invited_by_foreign_key = :invited_by_id
|
|
49
|
+
# Create a placeholder resource as soon as an invitation is sent. When false,
|
|
50
|
+
# the resource is created only after the invitation is accepted.
|
|
51
|
+
# Default: false
|
|
52
|
+
# config.create_resource_on_invitation = true
|
|
55
53
|
|
|
56
54
|
# The column name used for counter_cache column. If this is nil,
|
|
57
55
|
# the #invited_by association is declared without counter_cache.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module DeviseInvitable
|
|
2
|
+
module Generators
|
|
3
|
+
class UpgradeGenerator < Rails::Generators::NamedBase
|
|
4
|
+
namespace 'devise_invitable:upgrade'
|
|
5
|
+
|
|
6
|
+
class_option :invite_keys,
|
|
7
|
+
type: :array,
|
|
8
|
+
default: ['email'],
|
|
9
|
+
desc: 'Columns used as invite keys in the old resource table'
|
|
10
|
+
|
|
11
|
+
desc 'Generate a migration from resource-backed invitations to the invitations table.'
|
|
12
|
+
|
|
13
|
+
hook_for :orm, as: :devise_invitable_upgrade
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Mongoid
|
|
2
|
+
module Generators
|
|
3
|
+
class DeviseInvitableUpgradeGenerator < Rails::Generators::NamedBase
|
|
4
|
+
desc 'Print the Mongoid invitation data migration command.'
|
|
5
|
+
|
|
6
|
+
def show_migration_task
|
|
7
|
+
say "Run: bin/rails devise_invitable:migrate_invitations[#{class_name}]"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
namespace :devise_invitable do
|
|
2
|
+
desc 'Move legacy invitation fields into devise_invitations (MODEL=User also supported)'
|
|
3
|
+
task :migrate_invitations, [:model] => :environment do |_task, args|
|
|
4
|
+
model_name = args[:model] || ENV['MODEL']
|
|
5
|
+
raise ArgumentError, 'Pass a model name: rake devise_invitable:migrate_invitations[User]' if model_name.blank?
|
|
6
|
+
|
|
7
|
+
count = DeviseInvitable::Migrator.call(model_name.constantize)
|
|
8
|
+
puts "Migrated #{count} invitation#{'s' unless count == 1} for #{model_name}"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -25,6 +25,7 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
|
|
|
25
25
|
sign_out @issuer
|
|
26
26
|
|
|
27
27
|
@invitee = User.where(email: invitee_email).first
|
|
28
|
+
invitation = @invitee.current_invitation
|
|
28
29
|
assert_nil @invitee.invitation_accepted_at
|
|
29
30
|
assert_not_nil @invitee.invitation_token
|
|
30
31
|
assert !@invitee.confirmed?
|
|
@@ -45,10 +46,11 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
|
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
assert @invitee.encrypted_password.present?
|
|
48
|
-
|
|
49
|
+
assert_nil @invitee.invitation_accepted_at
|
|
49
50
|
assert_nil @invitee.invitation_token
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
assert_equal 'registered', invitation.reload.superseded_reason
|
|
52
|
+
assert_equal @issuer, invitation.invited_by
|
|
53
|
+
refute_predicate @invitee, :invitation_pending?
|
|
52
54
|
assert @invitee.confirmation_token.present?
|
|
53
55
|
assert !@invitee.confirmed?
|
|
54
56
|
end
|
|
@@ -79,6 +81,25 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
|
|
|
79
81
|
assert !@user.confirmed?
|
|
80
82
|
end
|
|
81
83
|
|
|
84
|
+
test "registration supersedes invitations when placeholders are disabled" do
|
|
85
|
+
User.create_resource_on_invitation = false
|
|
86
|
+
invitee_email = 'deferred@example.org'
|
|
87
|
+
User.invite!(email: invitee_email)
|
|
88
|
+
invitation = DeviseInvitable::Invitation.first
|
|
89
|
+
|
|
90
|
+
assert_nil User.where(email: invitee_email).first
|
|
91
|
+
|
|
92
|
+
post :create, params: { user: { email: invitee_email, password: '1password', bio: '.' } }
|
|
93
|
+
|
|
94
|
+
invitee = User.find_by!(email: invitee_email)
|
|
95
|
+
assert_equal invitee, invitation.reload.invitable
|
|
96
|
+
assert_equal 'registered', invitation.superseded_reason
|
|
97
|
+
assert_nil invitation.invitation_accepted_at
|
|
98
|
+
refute_predicate invitee, :invitation_pending?
|
|
99
|
+
ensure
|
|
100
|
+
User.create_resource_on_invitation = true
|
|
101
|
+
end
|
|
102
|
+
|
|
82
103
|
test "not invitable resources can register" do
|
|
83
104
|
@request.env['devise.mapping'] = Devise.mappings[:admin]
|
|
84
105
|
invitee_email = 'invitee@example.org'
|
data/test/generators_test.rb
CHANGED
|
@@ -6,30 +6,37 @@ require 'generators/devise_invitable/devise_invitable_generator'
|
|
|
6
6
|
class GeneratorsTest < ActiveSupport::TestCase
|
|
7
7
|
RAILS_APP_PATH = File.expand_path("../rails_app", __FILE__)
|
|
8
8
|
|
|
9
|
-
test "rails g should include the
|
|
9
|
+
test "rails g should include the generators" do
|
|
10
10
|
@output = `cd #{RAILS_APP_PATH} && rails g`
|
|
11
|
-
generators = %w/devise_invitable devise_invitable:form_for devise_invitable:install devise_invitable:invitation_views devise_invitable:simple_form_for devise_invitable:views/
|
|
11
|
+
generators = %w/devise_invitable devise_invitable:form_for devise_invitable:install devise_invitable:invitation_views devise_invitable:simple_form_for devise_invitable:upgrade devise_invitable:views/
|
|
12
12
|
|
|
13
13
|
generators.each do |generator|
|
|
14
|
-
@output
|
|
14
|
+
assert_includes @output, generator
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
test "rails g devise_invitable:install" do
|
|
19
19
|
@output = `cd #{RAILS_APP_PATH} && rails g devise_invitable:install -p`
|
|
20
|
-
puts @output
|
|
21
|
-
assert @output.match(%r{(inject|insert|File unchanged! The supplied flag value not found!).* config/initializers/devise\.rb\n})
|
|
22
20
|
assert @output.match(%r|create.* config/locales/devise_invitable\.en\.yml\n|)
|
|
21
|
+
if DEVISE_ORM == :active_record
|
|
22
|
+
assert_match(%r|create.*db/migrate/\d{14}_create_devise_invitations\.rb|, @output)
|
|
23
|
+
end
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
test "rails g devise_invitable Octopussy" do
|
|
26
27
|
@output = `cd #{RAILS_APP_PATH} && rails g devise_invitable Octopussy -p`
|
|
27
28
|
assert @output.match(%r{(inject|insert|File unchanged! The supplied flag value not found!).* app/models/octopussy\.rb\n})
|
|
28
|
-
|
|
29
|
+
assert_no_match(%r|invoke.* #{DEVISE_ORM}\n|, @output)
|
|
30
|
+
assert_no_match(%r|db/migrate/\d{14}_devise_invitable_add_to_octopussies\.rb|, @output)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
test "rails g devise_invitable:upgrade User" do
|
|
35
|
+
@output = `cd #{RAILS_APP_PATH} && rails g devise_invitable:upgrade User -p`
|
|
29
36
|
if DEVISE_ORM == :active_record
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
assert_match(%r|create.*db/migrate/\d{14}_upgrade_users_devise_invitations\.rb|, @output)
|
|
38
|
+
else
|
|
39
|
+
assert_match(/devise_invitable:migrate_invitations\[User\]/, @output)
|
|
33
40
|
end
|
|
34
41
|
end
|
|
35
42
|
end
|
|
@@ -152,7 +152,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
152
152
|
|
|
153
153
|
visit new_user_password_path
|
|
154
154
|
fill_in 'user_email', with: 'valid@email.com'
|
|
155
|
-
|
|
155
|
+
find('input[type="submit"]').click
|
|
156
156
|
|
|
157
157
|
visit edit_user_password_path(reset_password_token: Thread.current[:token])
|
|
158
158
|
set_password visit: false, button: 'Change my password'
|
|
@@ -209,7 +209,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
209
209
|
assert_equal 2, user.invitation_limit
|
|
210
210
|
end
|
|
211
211
|
|
|
212
|
-
test 'should
|
|
212
|
+
test 'should decrement invitation limit for each invitation record' do
|
|
213
213
|
User.stubs(:invitation_limit).returns(3)
|
|
214
214
|
|
|
215
215
|
user = create_full_user
|
|
@@ -227,7 +227,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
227
227
|
assert_equal root_path, current_path
|
|
228
228
|
assert page.has_css?('p#notice', text: 'An invitation email has been sent to user@test.com.')
|
|
229
229
|
user = User.find(user.id)
|
|
230
|
-
assert_equal
|
|
230
|
+
assert_equal 1, user.invitation_limit
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
test 'invited_by should be set when user invites someone' do
|
|
@@ -247,6 +247,27 @@ class InvitationTest < ActionDispatch::IntegrationTest
|
|
|
247
247
|
assert_redirected_to new_admin_session_path
|
|
248
248
|
end
|
|
249
249
|
|
|
250
|
+
test 'custom inviter authentication can redirect without rendering twice' do
|
|
251
|
+
sign_in_as_user
|
|
252
|
+
|
|
253
|
+
get new_restricted_invitation_path
|
|
254
|
+
|
|
255
|
+
assert_redirected_to root_path
|
|
256
|
+
assert_equal 'You are not authorized to access this page', flash[:alert]
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
test 'boolean inviter authentication uses the signed in resource as inviter' do
|
|
260
|
+
inviter = User.create!(
|
|
261
|
+
email: 'boolean-inviter@example.com',
|
|
262
|
+
password: '123456',
|
|
263
|
+
password_confirmation: '123456'
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
post boolean_inviter_invitation_path, params: { user: { email: 'boolean-invitee@example.com' } }
|
|
267
|
+
|
|
268
|
+
assert_equal inviter, User.find_by!(email: 'boolean-invitee@example.com').invited_by
|
|
269
|
+
end
|
|
270
|
+
|
|
250
271
|
test 'authenticated admin should be able to send an admin invitation' do
|
|
251
272
|
admin = Admin.create(email: 'admin@test.com', password: '123456', password_confirmation: '123456')
|
|
252
273
|
sign_in_as_user admin
|
|
@@ -15,6 +15,17 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
15
15
|
assert_nil new_user.invitation_token
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
test 'accepting_invitation? does not warn before invitation acceptance' do
|
|
19
|
+
original_verbose = $VERBOSE
|
|
20
|
+
$VERBOSE = true
|
|
21
|
+
|
|
22
|
+
_, warnings = capture_io { refute_predicate new_user, :accepting_invitation? }
|
|
23
|
+
|
|
24
|
+
refute_includes warnings, 'instance variable @accepting_invitation not initialized'
|
|
25
|
+
ensure
|
|
26
|
+
$VERBOSE = original_verbose
|
|
27
|
+
end
|
|
28
|
+
|
|
18
29
|
test 'should update the invitations count counter cache' do
|
|
19
30
|
if defined?(ActiveRecord)
|
|
20
31
|
current_user = new_user
|
|
@@ -430,6 +441,30 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
430
441
|
assert_equal [{error: :taken}], same_user.errors.details[:email]
|
|
431
442
|
end
|
|
432
443
|
|
|
444
|
+
test 'should not modify associations when an existing user cannot be invited' do
|
|
445
|
+
existing_user = User.create!(email: 'existing@email.com', password: '123456789')
|
|
446
|
+
User.invite!({ email: 'invitee@email.com' }, existing_user)
|
|
447
|
+
sent_invitation = existing_user.sent_invitations.first
|
|
448
|
+
|
|
449
|
+
invited_user = User.invite!(
|
|
450
|
+
email: existing_user.email,
|
|
451
|
+
sent_invitation_ids: []
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
assert_equal [{ error: :taken }], invited_user.errors.details[:email]
|
|
455
|
+
assert_equal existing_user, sent_invitation.reload.invited_by
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
test 'should reject an invite key that matches multiple records' do
|
|
459
|
+
2.times do
|
|
460
|
+
User.new(email: 'duplicate@email.com').save(validate: false)
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
assert_raises DeviseInvitable::AmbiguousInviteKeyError do
|
|
464
|
+
User.invite!(email: 'duplicate@email.com')
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
433
468
|
test 'should return a record with errors if user with pending invitation was found by e-mail' do
|
|
434
469
|
existing_user = User.invite!(email: 'valid@email.com')
|
|
435
470
|
user = User.invite!(email: 'valid@email.com')
|
|
@@ -447,7 +482,7 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
447
482
|
end
|
|
448
483
|
end
|
|
449
484
|
|
|
450
|
-
test 'should
|
|
485
|
+
test 'should not assign or validate other attributes if user was found by e-mail' do
|
|
451
486
|
begin
|
|
452
487
|
validate_on_invite = User.validate_on_invite
|
|
453
488
|
User.validate_on_invite = true
|
|
@@ -456,7 +491,8 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
456
491
|
user = User.invite!(email: 'valid@email.com', username: 'a' * 50)
|
|
457
492
|
assert_equal user, existing_user
|
|
458
493
|
assert_equal [{error: :taken}], user.errors.details[:email]
|
|
459
|
-
|
|
494
|
+
assert_empty user.errors[:username]
|
|
495
|
+
assert_nil user.username
|
|
460
496
|
ensure
|
|
461
497
|
User.validate_on_invite = validate_on_invite
|
|
462
498
|
end
|
|
@@ -741,8 +777,10 @@ class InvitableTest < ActiveSupport::TestCase
|
|
|
741
777
|
|
|
742
778
|
test "user.invite! should trigger callbacks" do
|
|
743
779
|
user = User.new(email: 'valid@email.com')
|
|
780
|
+
assert_callbacks_not_fired :before_invitation_created, user
|
|
744
781
|
assert_callbacks_not_fired :after_invitation_created, user
|
|
745
782
|
user.invite!
|
|
783
|
+
assert_callbacks_fired :before_invitation_created, user
|
|
746
784
|
assert_callbacks_fired :after_invitation_created, user
|
|
747
785
|
end
|
|
748
786
|
|