devise_invitable 1.3.2 → 1.4.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 49059f27df2618f9c0491fd1b7578b33619f2ef5
4
+ data.tar.gz: da0b1ff3962cba2784b8c082c013fb483ee0517e
5
+ SHA512:
6
+ metadata.gz: 56e5cc31bd011a67887ea3338ee66682dc398008f4a18f094fec9084b0635efc8684d5251aca72a9d7daecf228ea06aeec84bca49a8dd24bd3750c063f7b2e56
7
+ data.tar.gz: 162bc6c70f81f0b65ca73233a5ebb5ac53895b5e6c57f4085d522807592feccb29ce93c113e1d67d974fa95ebf026054d137a6a5c01c1af3913bf84df14d2f7a
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ = 1.4.0
2
+ Override active_for_authentication? and inactive_message instead of valid_password?
3
+ To use counter_cache, invited_by_counter_cache must be set, no more checking of invitations_count to enable counter cache
4
+
1
5
  = 1.3.0
2
6
  Now devise 3.1 compatible, @token must be used instead of @resource.invitation_token in mail views
3
7
 
data/README.rdoc CHANGED
@@ -1,7 +1,7 @@
1
1
  = DeviseInvitable
2
2
  {<img src="https://travis-ci.org/scambra/devise_invitable.png"/>}[http://travis-ci.org/scambra/devise_invitable]
3
3
 
4
- It adds support to devise[http://github.com/plataformatec/devise] for send invitations by email (it requires to be authenticated) and accept the invitation setting the password.
4
+ It adds support to devise[http://github.com/plataformatec/devise] for sending invitations by email (it requires to be authenticated) and accept the invitation setting the password.
5
5
 
6
6
  DeviseInvitable currently supports Rails 3 and 4, if you want to use it with Rails 2.3 you must install version {0.2.3}[http://rubygems.org/gems/devise_invitable/versions/0.2.3]
7
7
 
@@ -16,7 +16,7 @@ Install DeviseInvitable gem, it will also install dependencies (such as devise a
16
16
  Add DeviseInvitable to your Gemfile (and Devise if you weren't using them):
17
17
 
18
18
  gem 'devise', '>= 2.0.0'
19
- gem 'devise_invitable', '~> 1.1.0'
19
+ gem 'devise_invitable', '~> 1.3.4'
20
20
 
21
21
  === Automatic installation
22
22
 
@@ -172,6 +172,30 @@ be sure that you generate the views and put them into the controller that you ge
172
172
 
173
173
  rails generate devise_invitable:views users/invitations
174
174
 
175
+ To change behaviour of inviting or accepting users, you can simply override two methods:
176
+
177
+ class Users::InvitationsController < Devise::InvitationsController
178
+ private
179
+
180
+ # this is called when creating invitation
181
+ # should return an instance of resource class
182
+ def invite_resource
183
+ ## skip sending emails on invite
184
+ resource_class.invite!(invite_params, current_inviter) do |u|
185
+ u.skip_invitation = true
186
+ end
187
+ end
188
+
189
+ # this is called when accepting invitation
190
+ # should return an instance of resource class
191
+ def accept_resource
192
+ resource = resource_class.accept_invitation!(update_resource_params)
193
+ ## Report accepting invitation to analytics
194
+ Analytics.report('invite.accept', resource.id)
195
+ resource
196
+ end
197
+ end
198
+
175
199
  == Strong Parameters
176
200
 
177
201
  When you customize your own views, you may end up adding new attributes to forms. Rails 4 moved the parameter sanitization from the model to the controller, causing DeviseInvitable to handle this concern at the controller as well. Read about it in {devise README}[http://github.com/plataformatec/devise#strong-parameters]
@@ -209,11 +233,16 @@ To send an invitation to a user, use the <tt>invite!</tt> class method. <tt>:ema
209
233
 
210
234
  If you want to create the invitation but not send it, you can set <tt>skip_invitation</tt> to true.
211
235
 
212
- User.invite!(:email => "new_user@example.com", :name => "John Doe") do |u|
236
+ user = User.invite!(:email => "new_user@example.com", :name => "John Doe") do |u|
213
237
  u.skip_invitation = true
214
238
  end
215
239
  # => the record will be created, but the invitation email will not be sent
216
240
 
241
+ When generating the <tt>accept_user_invitation_url</tt> yourself, you must use the <tt>raw_invitation_token</tt>
242
+ the value is temporarily available when you invite a user and will be decrypted when recieved.
243
+
244
+ accept_user_invitation_url(:invitation_token => user.raw_invitation_token)
245
+
217
246
  When skip_invitation is used, you must also then set the invitation_sent_at field when the user is sent their
218
247
  token. Failure to do so will yield "Invalid invitation token" errors when the user attempts to accept the invite.
219
248
  You can set it like so:
@@ -228,6 +257,11 @@ You can add :skip_invitation to attributes hash if skip_invitation is added to a
228
257
  Skip_invitation skips sending the email, but sets invitation_token, so invited_to_sign_up? on the
229
258
  resulting user returns true.
230
259
 
260
+ **Warning**
261
+
262
+ When using skip_invitation you must send the email with the user object instance that generated the tokens, as
263
+ user.raw_invitation_token is available only to the instance and is not persisted in the database.
264
+
231
265
  You can send an invitation to an existing user if your workflow creates them separately:
232
266
 
233
267
  user = User.find(42)
@@ -237,6 +271,8 @@ You can also set <tt>invited_by</tt> when using the <tt>invite!</tt> class metho
237
271
 
238
272
  User.invite!({:email => "new_user@example.com"}, current_user) # current_user will be set as invited_by
239
273
 
274
+
275
+
240
276
  === Accept an invitation
241
277
 
242
278
  To accept an invitation with a token use the <tt>accept_invitation!</tt> class method. <tt>:invitation_token</tt> must be present in the parameters hash. You can also include other attributes in the hash.
@@ -345,6 +381,12 @@ Take a look at the generated locale file (in <tt>config/locales/devise_invitable
345
381
 
346
382
  DeviseInvitable supports ActiveRecord and Mongoid, like Devise.
347
383
 
384
+ == Wiki
385
+
386
+ It's possible to find additional information about DeviseInvitable on the Wiki:
387
+
388
+ https://github.com/scambra/devise_invitable/wiki
389
+
348
390
  == Testing
349
391
 
350
392
  To test DeviseInvitable for the ActiveRecord ORM with RVM, Ruby 1.9.2, and Rubygems 1.8.17:
@@ -17,7 +17,10 @@ class Devise::InvitationsController < DeviseController
17
17
  self.resource = invite_resource
18
18
 
19
19
  if resource.errors.empty?
20
- set_flash_message :notice, :send_instructions, :email => self.resource.email if self.resource.invitation_sent_at
20
+ yield resource if block_given?
21
+ if is_flashing_format? && self.resource.invitation_sent_at
22
+ set_flash_message :notice, :send_instructions, :email => self.resource.email
23
+ end
21
24
  respond_with resource, :location => after_invite_path_for(resource)
22
25
  else
23
26
  respond_with_navigational(resource) { render :new }
@@ -32,46 +35,51 @@ class Devise::InvitationsController < DeviseController
32
35
 
33
36
  # PUT /resource/invitation
34
37
  def update
35
- self.resource = resource_class.accept_invitation!(update_resource_params)
38
+ self.resource = accept_resource
36
39
 
37
40
  if resource.errors.empty?
38
- flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
39
- set_flash_message :notice, flash_message
41
+ yield resource if block_given?
42
+ flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
43
+ set_flash_message :notice, flash_message if is_flashing_format?
40
44
  sign_in(resource_name, resource)
41
45
  respond_with resource, :location => after_accept_path_for(resource)
42
46
  else
43
47
  respond_with_navigational(resource){ render :edit }
44
48
  end
45
49
  end
46
-
50
+
47
51
  # GET /resource/invitation/remove?invitation_token=abcdef
48
52
  def destroy
49
53
  resource.destroy
50
- set_flash_message :notice, :invitation_removed
54
+ set_flash_message :notice, :invitation_removed if is_flashing_format?
51
55
  redirect_to after_sign_out_path_for(resource_name)
52
56
  end
53
57
 
54
58
  protected
55
59
 
56
- def invite_resource
57
- resource_class.invite!(invite_params, current_inviter)
60
+ def invite_resource(&block)
61
+ resource_class.invite!(invite_params, current_inviter, &block)
62
+ end
63
+
64
+ def accept_resource
65
+ resource_class.accept_invitation!(update_resource_params)
58
66
  end
59
67
 
60
68
  def current_inviter
61
- @current_inviter ||= authenticate_inviter!
69
+ authenticate_inviter!
62
70
  end
63
71
 
64
72
  def has_invitations_left?
65
73
  unless current_inviter.nil? || current_inviter.has_invitations_left?
66
74
  self.resource = resource_class.new
67
- set_flash_message :alert, :no_invitations_remaining
75
+ set_flash_message :alert, :no_invitations_remaining if is_flashing_format?
68
76
  respond_with_navigational(resource) { render :new }
69
77
  end
70
78
  end
71
-
79
+
72
80
  def resource_from_invitation_token
73
81
  unless params[:invitation_token] && self.resource = resource_class.find_by_invitation_token(params[:invitation_token], true)
74
- set_flash_message(:alert, :invitation_token_invalid)
82
+ set_flash_message(:alert, :invitation_token_invalid) if is_flashing_format?
75
83
  redirect_to after_sign_out_path_for(resource_name)
76
84
  end
77
85
  end
@@ -83,6 +91,6 @@ class Devise::InvitationsController < DeviseController
83
91
  def update_resource_params
84
92
  devise_parameter_sanitizer.sanitize(:accept_invitation)
85
93
  end
86
-
94
+
87
95
  end
88
96
 
@@ -1,8 +1,7 @@
1
- <p>Hello <%= @resource.email %>!</p>
1
+ <p><%= t("devise.mailer.invitation_instructions.hello", email: @resource.email) %></p>
2
2
 
3
- <p>Someone has invited you to <%= root_url %>, you can accept it through the link below.</p>
3
+ <p><%= t("devise.mailer.invitation_instructions.someone_invited_you", url: root_url) %></p>
4
4
 
5
- <p><%= link_to 'Accept invitation', accept_invitation_url(@resource, :invitation_token => @token) %></p>
5
+ <p><%= link_to t("devise.mailer.invitation_instructions.accept"), accept_invitation_url(@resource, :invitation_token => @token) %></p>
6
6
 
7
- <p>If you don't want to accept the invitation, please ignore this email.<br />
8
- Your account won't be created until you access the link above and set your password.</p>
7
+ <p><%= t("devise.mailer.invitation_instructions.ignore").html_safe %></p>
@@ -1,5 +1,7 @@
1
1
  en:
2
2
  devise:
3
+ failure:
4
+ invited: 'You have a pending invitation, accept it to finish creating your account.'
3
5
  invitations:
4
6
  send_instructions: 'An invitation email has been sent to %{email}.'
5
7
  invitation_token_invalid: 'The invitation token provided is not valid!'
@@ -15,3 +17,7 @@ en:
15
17
  mailer:
16
18
  invitation_instructions:
17
19
  subject: 'Invitation instructions'
20
+ hello: 'Hello %{email}'
21
+ someone_invited_you: 'Someone has invited you to %{url}, you can accept it through the link below.'
22
+ accept: 'Accept invitation'
23
+ ignore: "If you don't want to accept the invitation, please ignore this email.<br />Your account won't be created until you access the link above and set your password."
@@ -29,18 +29,23 @@ module Devise
29
29
 
30
30
  included do
31
31
  include ::DeviseInvitable::Inviter
32
- if Devise.invited_by_class_name
33
- belongs_to :invited_by, :class_name => Devise.invited_by_class_name
32
+ belongs_to_options = if Devise.invited_by_class_name
33
+ {:class_name => Devise.invited_by_class_name}
34
34
  else
35
- belongs_to :invited_by, :polymorphic => true
35
+ {:polymorphic => true}
36
36
  end
37
+ if defined?(ActiveRecord) && defined?(ActiveRecord::Base) && self < ActiveRecord::Base
38
+ counter_cache = Devise.invited_by_counter_cache
39
+ belongs_to_options.merge! :counter_cache => counter_cache if counter_cache
40
+ end
41
+ belongs_to :invited_by, belongs_to_options
37
42
 
38
43
  include ActiveSupport::Callbacks
39
44
  define_callbacks :invitation_accepted
40
45
 
41
46
  attr_writer :skip_password
42
47
 
43
- scope :active, lambda { where(:invitation_token => nil) }
48
+ scope :no_active_invitation, lambda { where(:invitation_token => nil) }
44
49
  if defined?(Mongoid) && defined?(Mongoid::Document) && self < Mongoid::Document
45
50
  scope :invitation_not_accepted, lambda { where(:invitation_accepted_at => nil, :invitation_token.ne => nil) }
46
51
  scope :invitation_accepted, lambda { where(:invitation_accepted_at.ne => nil) }
@@ -57,9 +62,8 @@ module Devise
57
62
  def self.required_fields(klass)
58
63
  fields = [:invitation_token, :invitation_created_at, :invitation_sent_at, :invitation_accepted_at,
59
64
  :invitation_limit, :invited_by_id, :invited_by_type]
60
- if Devise.invited_by_class_name
61
- fields -= [:invited_by_type]
62
- end
65
+ fields << :invitations_count if defined?(ActiveRecord) && self < ActiveRecord::Base
66
+ fields -= [:invited_by_type] if Devise.invited_by_class_name
63
67
  fields
64
68
  end
65
69
 
@@ -81,12 +85,6 @@ module Devise
81
85
  end
82
86
  end
83
87
 
84
- # Verifies whether a user has accepted an invitation (or is accepting it), or was never invited
85
- def accepting_or_not_invited?
86
- ActiveSupport::Deprecation.warn "accepting_or_not_invited? is deprecated and will be removed from DeviseInvitable 1.1.0 (use accepted_or_not_invited? instead)"
87
- accepted_or_not_invited?
88
- end
89
-
90
88
  # Verifies whether a user has been invited or not
91
89
  def invited_to_sign_up?
92
90
  persisted? && invitation_token.present?
@@ -112,7 +110,8 @@ module Devise
112
110
  def self.confirmation_required?; false; end
113
111
  end
114
112
 
115
- generate_invitation_token if self.invitation_token.nil? || (!@skip_invitation && @raw_invitation_token.nil?)
113
+ yield self if block_given?
114
+ generate_invitation_token if self.invitation_token.nil? || (!@skip_invitation || @raw_invitation_token.nil?)
116
115
  self.invitation_created_at = Time.now.utc
117
116
  self.invitation_sent_at = self.invitation_created_at unless @skip_invitation
118
117
  self.invited_by = invited_by if invited_by
@@ -135,10 +134,14 @@ module Devise
135
134
  end
136
135
 
137
136
  # Only verify password when is not invited
138
- def valid_password?(password)
137
+ def active_for_authentication?
139
138
  super unless invited_to_sign_up?
140
139
  end
141
140
 
141
+ def inactive_message
142
+ invited_to_sign_up? ? :invited : super
143
+ end
144
+
142
145
  def after_password_reset
143
146
  super
144
147
  accept_invitation! if invited_to_sign_up?
@@ -172,7 +175,7 @@ module Devise
172
175
  !@skip_password && super
173
176
  end
174
177
 
175
-
178
+
176
179
  # Checks if the invitation for the user is within the limit time.
177
180
  # We do this by calculating if the difference between today and the
178
181
  # invitation sent date does not exceed the invite for time configured.
@@ -279,19 +282,11 @@ module Devise
279
282
  invitation_token = Devise.token_generator.digest(self, :invitation_token, original_token)
280
283
 
281
284
  invitable = find_or_initialize_with_error_by(:invitation_token, invitation_token)
282
- if !invitable.persisted? && Devise.allow_insecure_token_lookup
283
- invitable = find_or_initialize_with_error_by(:invitation_token, original_token)
284
- end
285
285
  invitable.errors.add(:invitation_token, :invalid) if invitable.invitation_token && invitable.persisted? && !invitable.valid_invitation?
286
286
  invitable.invitation_token = original_token
287
287
  invitable unless only_valid && invitable.errors.present?
288
288
  end
289
289
 
290
- # Generate a token checking if one does not already exist in the database.
291
- def invitation_token
292
- generate_token(:invitation_token)
293
- end
294
-
295
290
  # Callback convenience methods
296
291
  def before_invitation_accepted(*args, &blk)
297
292
  set_callback(:invitation_accepted, :before, *args, &blk)
@@ -7,12 +7,14 @@ module DeviseInvitable
7
7
 
8
8
  # We use to_prepare instead of after_initialize here because Devise is a Rails engine; its
9
9
  # mailer is reloaded like the rest of the user's app. Got to make sure that our mailer methods
10
- # are included each time Devise::Mailer is (re)loaded.
10
+ # are included each time Devise.mailer is (re)loaded.
11
11
  config.to_prepare do
12
- require 'devise/mailer'
13
- Devise::Mailer.send :include, DeviseInvitable::Mailer
12
+ Devise.mailer.send :include, DeviseInvitable::Mailer
13
+ unless Devise.mailer.ancestors.include?(Devise::Mailers::Helpers)
14
+ Devise.mailer.send :include, Devise::Mailers::Helpers
15
+ end
14
16
  end
15
- # extend mapping with after_initialize becuase is not reloaded
17
+ # extend mapping with after_initialize because it's not reloaded
16
18
  config.after_initialize do
17
19
  Devise::Mapping.send :include, DeviseInvitable::Mapping
18
20
  Devise::ParameterSanitizer.send :include, DeviseInvitable::ParameterSanitizer
@@ -1,3 +1,3 @@
1
1
  module DeviseInvitable
2
- VERSION = '1.3.2'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -61,6 +61,11 @@ module Devise
61
61
  # the #invited_by association is declared to be polymorphic. (default: nil)
62
62
  mattr_accessor :invited_by_class_name
63
63
  @@invited_by_class_name = nil
64
+
65
+ # Public: The column name used for counter_cache column. If this is nil,
66
+ # the #invited_by association is declared without counter_cache. (default: nil)
67
+ mattr_accessor :invited_by_counter_cache
68
+ @@invited_by_counter_cache = nil
64
69
  end
65
70
 
66
71
  Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => {:invitation => [nil, :new, :accept]}
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  source_root File.expand_path("../templates", __FILE__)
7
7
 
8
8
  def copy_devise_migration
9
- migration_template "migration.rb", "db/migrate/devise_invitable_add_to_#{table_name}"
9
+ migration_template "migration.rb", "db/migrate/devise_invitable_add_to_#{table_name}.rb"
10
10
  end
11
11
  end
12
12
  end
@@ -7,6 +7,8 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
7
7
  t.datetime :invitation_accepted_at
8
8
  t.integer :invitation_limit
9
9
  t.references :invited_by, :polymorphic => true
10
+ t.integer :invitations_count, default: 0
11
+ t.index :invitations_count
10
12
  t.index :invitation_token, :unique => true # for invitable
11
13
  t.index :invited_by_id
12
14
  end
@@ -21,7 +23,11 @@ class DeviseInvitableAddTo<%= table_name.camelize %> < ActiveRecord::Migration
21
23
  def down
22
24
  change_table :<%= table_name %> do |t|
23
25
  t.remove_references :invited_by, :polymorphic => true
24
- t.remove :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
26
+ t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
25
27
  end
28
+ change_column_null :<%= table_name %>, :encrypted_password, false
29
+ <% if class_name.constantize.columns_hash['password_salt'] -%>
30
+ change_column_null :<%= table_name %>, :password_salt,false
31
+ <% end -%>
26
32
  end
27
33
  end
@@ -43,6 +43,20 @@ module DeviseInvitable
43
43
  # Default: false
44
44
  # config.validate_on_invite = true
45
45
 
46
+ # Resend invitation if user with invited status is invited again
47
+ # Default: true
48
+ # config.resend_invitation = false
49
+
50
+ # The class name of the inviting model. If this is nil,
51
+ # the #invited_by association is declared to be polymorphic.
52
+ # Default: nil
53
+ # config.invited_by_class_name = 'User'
54
+
55
+ # The column name used for counter_cache column. If this is nil,
56
+ # the #invited_by association is declared without counter_cache.
57
+ # Default: nil
58
+ # config.invited_by_counter_cache = :invitations_count
59
+
46
60
  CONTENT
47
61
  end
48
62
  end
@@ -7,5 +7,5 @@
7
7
  <%= f.input :password %>
8
8
  <%= f.input :password_confirmation %>
9
9
 
10
- <%= f.submit t("devise.invitations.edit.submit_button") %>
10
+ <%= f.button :submit, t("devise.invitations.edit.submit_button") %>
11
11
  <% end %>
@@ -7,5 +7,5 @@
7
7
  <%= f.input field %>
8
8
  <% end -%>
9
9
 
10
- <%= f.submit t("devise.invitations.new.submit_button") %>
10
+ <%= f.button :submit, t("devise.invitations.new.submit_button") %>
11
11
  <% end %>
@@ -25,7 +25,7 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
25
25
  sign_out @issuer
26
26
 
27
27
  @invitee = User.where(:email => invitee_email).first
28
- assert_blank @invitee.encrypted_password, "the password should be unset"
28
+ assert @invitee.encrypted_password.blank?, "the password should be unset"
29
29
 
30
30
  # sign_up the invitee
31
31
  assert_difference('ActionMailer::Base.deliveries.size') do
@@ -42,13 +42,13 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
42
42
  @invitee.save!
43
43
  end
44
44
 
45
- assert_present @invitee.encrypted_password
45
+ assert @invitee.encrypted_password.present?
46
46
  assert_not_nil @invitee.invitation_accepted_at
47
47
  assert_nil @invitee.invitation_token
48
- assert_present @invitee.invited_by_id
49
- assert_present @invitee.invited_by_type
48
+ assert @invitee.invited_by_id.present?
49
+ assert @invitee.invited_by_type.present?
50
50
  assert !@invitee.confirmed?
51
- assert_present @invitee.confirmation_token
51
+ assert @invitee.confirmation_token.present?
52
52
  end
53
53
 
54
54
  test "not invitable resources can register" do
@@ -58,7 +58,7 @@ class DeviseInvitable::RegistrationsControllerTest < ActionController::TestCase
58
58
  post :create, :admin => {:email => invitee_email, :password => "1password"}
59
59
 
60
60
  @invitee = Admin.where(:email => invitee_email).first
61
- assert_present @invitee.encrypted_password
61
+ assert @invitee.encrypted_password.present?
62
62
  end
63
63
 
64
64
  test "missing params on a create should not cause an error" do
@@ -17,18 +17,18 @@ class GeneratorsTest < ActiveSupport::TestCase
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
- assert @output.match(%r{(inject|insert).+ config/initializers/devise\.rb\n})
21
- assert @output.match(%r|create.+ config/locales/devise_invitable\.en\.yml\n|)
20
+ assert @output.match(%r{(inject|insert).* config/initializers/devise\.rb\n})
21
+ assert @output.match(%r|create.* config/locales/devise_invitable\.en\.yml\n|)
22
22
  end
23
23
 
24
24
  test "rails g devise_invitable Octopussy" do
25
25
  @output = `cd #{RAILS_APP_PATH} && rails g devise_invitable Octopussy -p`
26
- assert @output.match(%r{(inject|insert).+ app/models/octopussy\.rb\n})
27
- assert @output.match(%r|invoke.+ #{DEVISE_ORM}\n|)
26
+ assert @output.match(%r{(inject|insert).* app/models/octopussy\.rb\n})
27
+ assert @output.match(%r|invoke.* #{DEVISE_ORM}\n|)
28
28
  if DEVISE_ORM == :active_record
29
- assert @output.match(%r|create.+ db/migrate/\d{14}_devise_invitable_add_to_octopussies\.rb\n|)
29
+ assert @output.match(%r|create.* db/migrate/\d{14}_devise_invitable_add_to_octopussies\.rb\n|)
30
30
  elsif DEVISE_ORM == :mongoid
31
- assert !@output.match(%r|create.+ db/migrate/\d{14}_devise_invitable_add_to_octopussies\.rb\n|)
31
+ assert !@output.match(%r|create.* db/migrate/\d{14}_devise_invitable_add_to_octopussies\.rb\n|)
32
32
  end
33
33
  end
34
34
  end
@@ -80,7 +80,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
80
80
  end
81
81
  assert_equal user_invitation_path, current_path
82
82
  assert page.has_css?('#error_explanation li', :text => /Password .*doesn\'t match/)
83
- assert_blank user.encrypted_password
83
+ assert user.encrypted_password.blank?
84
84
  end
85
85
 
86
86
  test 'not authenticated user with valid data should be able to change his password' do
@@ -99,7 +99,7 @@ class InvitationTest < ActionDispatch::IntegrationTest
99
99
  end
100
100
  assert_equal user_invitation_path, current_path
101
101
  assert page.has_css?('#error_explanation')
102
- assert_blank user.encrypted_password
102
+ assert user.encrypted_password.blank?
103
103
 
104
104
  set_password :visit => false
105
105
  assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
@@ -1,9 +1,9 @@
1
- class ActionController::IntegrationTest
1
+ class ActionDispatch::IntegrationTest
2
2
 
3
3
  def warden
4
4
  request.env['warden']
5
5
  end
6
-
6
+
7
7
  def create_full_user
8
8
  @user ||= begin
9
9
  user = User.create!(
@@ -24,7 +24,7 @@ class ActionController::IntegrationTest
24
24
  visit send("new_#{resource_name}_session_path")
25
25
  fill_in "#{resource_name}_email", :with => user.email
26
26
  fill_in "#{resource_name}_password", :with => user.password
27
- click_button 'Sign in'
27
+ click_button 'Log in'
28
28
  end
29
29
 
30
30
  # Fix assert_redirect_to in integration sessions because they don't take into
@@ -11,6 +11,16 @@ class InvitableTest < ActiveSupport::TestCase
11
11
  assert_nil new_user.invitation_token
12
12
  end
13
13
 
14
+ test 'should update the invitations count counter cache' do
15
+ if defined?(ActiveRecord)
16
+ current_user = new_user
17
+ 2.times do |index|
18
+ User.invite!({:email => "valid#{index}@email.com"}, current_user)
19
+ end
20
+ assert_equal current_user.reload.invitations_count, 2
21
+ end
22
+ end
23
+
14
24
  test 'should not generate the raw invitation token after creating a record' do
15
25
  assert_nil new_user.raw_invitation_token
16
26
  end
@@ -18,14 +28,40 @@ class InvitableTest < ActiveSupport::TestCase
18
28
  test 'should regenerate invitation token each time' do
19
29
  user = new_user
20
30
  user.invite!
21
- token = user.invitation_token
31
+
22
32
  assert_not_nil user.invitation_token
33
+ assert_not_nil user.raw_invitation_token
23
34
  assert_not_nil user.invitation_created_at
35
+
24
36
  3.times do
25
37
  user = User.find(user.id)
26
- user.invite!
27
- assert_not_equal token, user.invitation_token
28
- token = user.invitation_token
38
+
39
+ assert_not_same user.invitation_token, lambda {
40
+ user.invite!
41
+ user.invitation_token
42
+ }.call
43
+ assert_not_nil user.raw_invitation_token
44
+ end
45
+ end
46
+
47
+ test 'should regenerate invitation token each time even if "skip_invitation" was true' do
48
+ user = new_user
49
+ user.skip_invitation = true
50
+ user.invite!
51
+
52
+ assert_not_nil user.invitation_token
53
+ assert_not_nil user.invitation_created_at
54
+
55
+ 3.times do
56
+ user = User.find(user.id)
57
+ user.skip_invitation = true
58
+
59
+ assert_not_same user.invitation_token, lambda {
60
+ user.invite!
61
+ user.invitation_token
62
+ }.call
63
+ assert_not_nil user.invitation_token
64
+ assert_not_nil user.raw_invitation_token
29
65
  end
30
66
  end
31
67
 
@@ -147,21 +183,21 @@ class InvitableTest < ActiveSupport::TestCase
147
183
 
148
184
  test 'should clear invitation token and set invitation_accepted_at while accepting the password' do
149
185
  user = User.invite!(:email => "valid@email.com")
150
- assert_present user.invitation_token
186
+ assert user.invitation_token.present?
151
187
  assert_nil user.invitation_accepted_at
152
188
  user.accept_invitation!
153
189
  user.reload
154
190
  assert_nil user.invitation_token
155
- assert_present user.invitation_accepted_at
191
+ assert user.invitation_accepted_at.present?
156
192
  end
157
193
 
158
194
  test 'should not clear invitation token or set accepted_at if record is invalid' do
159
195
  user = User.invite!(:email => "valid@email.com")
160
- assert_present user.invitation_token
196
+ assert user.invitation_token.present?
161
197
  assert_nil user.invitation_accepted_at
162
198
  User.accept_invitation!(:invitation_token => user.invitation_token, :password => '123456789', :password_confirmation => '987654321')
163
199
  user.reload
164
- assert_present user.invitation_token
200
+ assert user.invitation_token.present?
165
201
  assert_nil user.invitation_accepted_at
166
202
  end
167
203
 
@@ -172,8 +208,8 @@ class InvitableTest < ActiveSupport::TestCase
172
208
  user.reset_password_sent_at = Time.now.utc
173
209
  user.save
174
210
 
175
- assert_present user.reset_password_token
176
- assert_present user.invitation_token
211
+ assert user.reset_password_token.present?
212
+ assert user.invitation_token.present?
177
213
  User.reset_password_by_token(:reset_password_token => token, :password => '123456789', :password_confirmation => '123456789')
178
214
  assert_nil user.reload.reset_password_token
179
215
  assert_nil user.reload.invitation_token
@@ -187,11 +223,11 @@ class InvitableTest < ActiveSupport::TestCase
187
223
  user.reset_password_sent_at = Time.now.utc
188
224
  user.save
189
225
 
190
- assert_present user.reset_password_token
191
- assert_present user.invitation_token
226
+ assert user.reset_password_token.present?
227
+ assert user.invitation_token.present?
192
228
  User.reset_password_by_token(:reset_password_token => token, :password => '123456789', :password_confirmation => '12345678')
193
- assert_present user.reload.reset_password_token
194
- assert_present user.reload.invitation_token
229
+ assert user.reload.reset_password_token.present?
230
+ assert user.reload.invitation_token.present?
195
231
  assert user.invited_to_sign_up?
196
232
  end
197
233
 
@@ -202,7 +238,7 @@ class InvitableTest < ActiveSupport::TestCase
202
238
  user.reset_password_sent_at = Time.now.utc
203
239
  user.save
204
240
 
205
- assert_present user.reset_password_token
241
+ assert user.reset_password_token.present?
206
242
  assert_nil user.invitation_token
207
243
  User.reset_password_by_token(:reset_password_token => token, :password => '123456789', :password_confirmation => '123456789')
208
244
  assert_nil user.reload.invitation_token
@@ -221,7 +257,7 @@ class InvitableTest < ActiveSupport::TestCase
221
257
  test 'should return a record with invitation token and no errors to send invitation by email' do
222
258
  invited_user = User.invite!(:email => "valid@email.com")
223
259
  assert invited_user.errors.blank?
224
- assert_present invited_user.invitation_token
260
+ assert invited_user.invitation_token.present?
225
261
  assert_equal 'valid@email.com', invited_user.email
226
262
  assert invited_user.persisted?
227
263
  end
@@ -462,7 +498,18 @@ class InvitableTest < ActiveSupport::TestCase
462
498
  assert_no_difference('ActionMailer::Base.deliveries.size') do
463
499
  user.invite!
464
500
  end
465
- assert_present user.invitation_created_at
501
+ assert user.invitation_created_at.present?
502
+ assert_nil user.invitation_sent_at
503
+ end
504
+
505
+ test 'user.invite! should not send an invitation if we want to skip the invitation with block' do
506
+ user = new_user
507
+ assert_no_difference('ActionMailer::Base.deliveries.size') do
508
+ user.invite! do |u|
509
+ u.skip_invitation = true
510
+ end
511
+ end
512
+ assert user.invitation_created_at.present?
466
513
  assert_nil user.invitation_sent_at
467
514
  end
468
515
 
@@ -538,13 +585,13 @@ class InvitableTest < ActiveSupport::TestCase
538
585
 
539
586
  test 'should pass validation before accept if field is required in post-invited instance' do
540
587
  user = User.invite!(:email => "valid@email.com")
541
- user.testing_accepting_or_not_invited = true
588
+ user.testing_accepted_or_not_invited = true
542
589
  assert_equal true, user.valid?
543
590
  end
544
591
 
545
592
  test 'should fail validation after accept if field is required in post-invited instance' do
546
593
  user = User.invite!(:email => "valid@email.com")
547
- user.testing_accepting_or_not_invited = true
594
+ user.testing_accepted_or_not_invited = true
548
595
  user.accept_invitation!
549
596
  assert_equal false, user.valid?
550
597
  end
@@ -552,7 +599,7 @@ class InvitableTest < ActiveSupport::TestCase
552
599
  test 'should pass validation after accept if field is required in post-invited instance' do
553
600
  user = User.invite!(:email => "valid@email.com")
554
601
  user.username = 'test'
555
- user.testing_accepting_or_not_invited = true
602
+ user.testing_accepted_or_not_invited = true
556
603
  user.bio = "Test"
557
604
  user.accept_invitation!
558
605
  assert_equal true, user.valid?
@@ -39,12 +39,12 @@ class User < PARENT_MODEL_CLASS
39
39
  attr_accessor :callback_works, :bio, :token
40
40
  validates :username, :length => { :maximum => 20 }
41
41
 
42
- attr_accessor :testing_accepting_or_not_invited
43
- validates :username, :presence => true, :if => :testing_accepting_or_not_invited_validator?
42
+ attr_accessor :testing_accepted_or_not_invited
43
+ validates :username, :presence => true, :if => :testing_accepted_or_not_invited_validator?
44
44
  validates :bio, :presence => true, :if => :invitation_accepted?
45
45
 
46
- def testing_accepting_or_not_invited_validator?
47
- testing_accepting_or_not_invited && accepting_or_not_invited?
46
+ def testing_accepted_or_not_invited_validator?
47
+ testing_accepted_or_not_invited && accepted_or_not_invited?
48
48
  end
49
49
 
50
50
  after_invitation_accepted do |object|
@@ -90,6 +90,45 @@ Devise.setup do |config|
90
90
  # Setup a pepper to generate the encrypted password.
91
91
  # config.pepper = "e31589192aeea8807cb7d8686b0f8484d6cbfaaa65443d45144519ed1d4ffbc6ccb73b21a69ece276d94f2cac95d83990d824f36f301d6f585ededd1bf90d67d"
92
92
 
93
+ # ==> Configuration for :invitable
94
+ # The period the generated invitation token is valid, after
95
+ # this period, the invited resource won't be able to accept the invitation.
96
+ # When invite_for is 0 (the default), the invitation won't expire.
97
+ # config.invite_for = 2.weeks
98
+
99
+ # Number of invitations users can send.
100
+ # - If invitation_limit is nil, there is no limit for invitations, users can
101
+ # send unlimited invitations, invitation_limit column is not used.
102
+ # - If invitation_limit is 0, users can't send invitations by default.
103
+ # - If invitation_limit n > 0, users can send n invitations.
104
+ # You can change invitation_limit column for some users so they can send more
105
+ # or less invitations, even with global invitation_limit = 0
106
+ # Default: nil
107
+ # config.invitation_limit = 5
108
+
109
+ # The key to be used to check existing users when sending an invitation
110
+ # and the regexp used to test it when validate_on_invite is not set.
111
+ # config.invite_key = {:email => /\\A[^@]+@[^@]+\\z/}
112
+ # config.invite_key = {:email => /\\A[^@]+@[^@]+\\z/, :username => nil}
113
+
114
+ # Flag that force a record to be valid before being actually invited
115
+ # Default: false
116
+ # config.validate_on_invite = true
117
+
118
+ # Resend invitation if user with invited status is invited again
119
+ # Default: true
120
+ # config.resend_invitation = false
121
+
122
+ # The class name of the inviting model. If this is nil,
123
+ # the #invited_by association is declared to be polymorphic.
124
+ # Default: nil
125
+ # config.invited_by_class_name = 'User'
126
+
127
+ # The column name used for counter_cache column. If this is nil,
128
+ # the #invited_by association is declared without counter_cache.
129
+ # Default: nil
130
+ config.invited_by_counter_cache = :invitations_count
131
+
93
132
  # ==> Configuration for :confirmable
94
133
  # A period that the user is allowed to access the website even without
95
134
  # confirming his account. For instance, if set to 2.days, the user will be
@@ -27,15 +27,20 @@ class CreateTables < ActiveRecord::Migration
27
27
  t.integer :invitation_limit
28
28
  t.integer :invited_by_id
29
29
  t.string :invited_by_type
30
+ t.integer :invitations_count, :default => 0
30
31
 
31
32
  t.timestamps
32
33
  end
33
34
  add_index :users, :invitation_token, :unique => true
35
+ add_index :users, :invitations_count
34
36
 
35
37
  create_table :admins do |t|
36
38
  ## Database authenticatable
37
39
  t.string :email, :null => true, :default => ""
38
40
  t.string :encrypted_password, :null => true, :default => ""
41
+
42
+ t.integer :invitations_count, :default => 0
39
43
  end
44
+ add_index :admins, :invitations_count
40
45
  end
41
46
  end
metadata CHANGED
@@ -1,38 +1,34 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_invitable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2
5
- prerelease:
4
+ version: 1.4.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Sergio Cambra
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-01-04 00:00:00.000000000 Z
11
+ date: 2014-10-21 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: 1.1.0
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: 1.1.0
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: actionmailer
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: 3.2.6
38
34
  - - <
@@ -41,9 +37,8 @@ dependencies:
41
37
  type: :runtime
42
38
  prerelease: false
43
39
  version_requirements: !ruby/object:Gem::Requirement
44
- none: false
45
40
  requirements:
46
- - - ! '>='
41
+ - - '>='
47
42
  - !ruby/object:Gem::Version
48
43
  version: 3.2.6
49
44
  - - <
@@ -52,19 +47,17 @@ dependencies:
52
47
  - !ruby/object:Gem::Dependency
53
48
  name: devise
54
49
  requirement: !ruby/object:Gem::Requirement
55
- none: false
56
50
  requirements:
57
- - - ! '>='
51
+ - - '>='
58
52
  - !ruby/object:Gem::Version
59
- version: 3.1.0
53
+ version: 3.2.0
60
54
  type: :runtime
61
55
  prerelease: false
62
56
  version_requirements: !ruby/object:Gem::Requirement
63
- none: false
64
57
  requirements:
65
- - - ! '>='
58
+ - - '>='
66
59
  - !ruby/object:Gem::Version
67
- version: 3.1.0
60
+ version: 3.2.0
68
61
  description: It adds support for send invitations by email (it requires to be authenticated)
69
62
  and accept the invitation by setting a password.
70
63
  email:
@@ -154,6 +147,7 @@ files:
154
147
  homepage: https://github.com/scambra/devise_invitable
155
148
  licenses:
156
149
  - MIT
150
+ metadata: {}
157
151
  post_install_message:
158
152
  rdoc_options:
159
153
  - --main
@@ -162,22 +156,20 @@ rdoc_options:
162
156
  require_paths:
163
157
  - lib
164
158
  required_ruby_version: !ruby/object:Gem::Requirement
165
- none: false
166
159
  requirements:
167
- - - ! '>='
160
+ - - '>='
168
161
  - !ruby/object:Gem::Version
169
162
  version: 1.8.6
170
163
  required_rubygems_version: !ruby/object:Gem::Requirement
171
- none: false
172
164
  requirements:
173
- - - ! '>='
165
+ - - '>='
174
166
  - !ruby/object:Gem::Version
175
167
  version: 1.3.6
176
168
  requirements: []
177
169
  rubyforge_project:
178
- rubygems_version: 1.8.23
170
+ rubygems_version: 2.1.11
179
171
  signing_key:
180
- specification_version: 3
172
+ specification_version: 4
181
173
  summary: An invitation strategy for Devise
182
174
  test_files:
183
175
  - test/functional/controller_helpers_test.rb