lperichon-devise_invitable 0.3.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.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +92 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/app/controllers/devise/invitations_controller.rb +52 -0
- data/app/views/devise/invitations/edit.html.erb +14 -0
- data/app/views/devise/invitations/new.html.erb +12 -0
- data/app/views/devise/mailer/invitation.html.erb +8 -0
- data/devise_invitable.gemspec +123 -0
- data/init.rb +1 -0
- data/lib/devise_invitable.rb +21 -0
- data/lib/devise_invitable/controllers/helpers.rb +6 -0
- data/lib/devise_invitable/controllers/url_helpers.rb +24 -0
- data/lib/devise_invitable/locales/en.yml +5 -0
- data/lib/devise_invitable/mailer.rb +8 -0
- data/lib/devise_invitable/model.rb +139 -0
- data/lib/devise_invitable/rails.rb +15 -0
- data/lib/devise_invitable/routes.rb +11 -0
- data/lib/devise_invitable/schema.rb +11 -0
- data/test/integration/invitable_test.rb +122 -0
- data/test/integration_tests_helper.rb +39 -0
- data/test/mailers/invitation_test.rb +62 -0
- data/test/model_tests_helper.rb +41 -0
- data/test/models/invitable_test.rb +172 -0
- data/test/models_test.rb +35 -0
- data/test/rails_app/app/controllers/admins_controller.rb +6 -0
- data/test/rails_app/app/controllers/application_controller.rb +10 -0
- data/test/rails_app/app/controllers/home_controller.rb +4 -0
- data/test/rails_app/app/controllers/users_controller.rb +12 -0
- data/test/rails_app/app/helpers/application_helper.rb +3 -0
- data/test/rails_app/app/models/user.rb +4 -0
- data/test/rails_app/app/views/home/index.html.erb +0 -0
- data/test/rails_app/config/boot.rb +110 -0
- data/test/rails_app/config/database.yml +22 -0
- data/test/rails_app/config/environment.rb +44 -0
- data/test/rails_app/config/environments/development.rb +17 -0
- data/test/rails_app/config/environments/production.rb +28 -0
- data/test/rails_app/config/environments/test.rb +28 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_app/config/initializers/devise.rb +105 -0
- data/test/rails_app/config/initializers/inflections.rb +2 -0
- data/test/rails_app/config/initializers/new_rails_defaults.rb +21 -0
- data/test/rails_app/config/initializers/session_store.rb +15 -0
- data/test/rails_app/config/routes.rb +3 -0
- data/test/rails_app/vendor/plugins/devise_invitable/init.rb +1 -0
- data/test/routes_test.rb +20 -0
- data/test/test_helper.rb +58 -0
- metadata +173 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Sergio Cambra
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
= devise_invitable
|
2
|
+
|
3
|
+
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
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
All gems are on gemcutter, so you need to add gemcutter to your sources if you haven’t yet:
|
8
|
+
|
9
|
+
sudo gem sources -a http://gemcutter.org/
|
10
|
+
|
11
|
+
Install devise_invitable gem, it should install dependencies (such as devise and warden):
|
12
|
+
|
13
|
+
sudo gem install devise_invitable
|
14
|
+
|
15
|
+
Configure devise_invitable inside your app (and warden and devise if you weren't using them):
|
16
|
+
|
17
|
+
config.gem 'warden'
|
18
|
+
config.gem 'devise'
|
19
|
+
config.gem 'devise_invitable'
|
20
|
+
|
21
|
+
== Basic Usage
|
22
|
+
|
23
|
+
Follow the walkthrough for devise with the following modifications.
|
24
|
+
|
25
|
+
Add t.invitable to the migration:
|
26
|
+
|
27
|
+
create_table :users do
|
28
|
+
...
|
29
|
+
t.invitable
|
30
|
+
...
|
31
|
+
end
|
32
|
+
add_index :users, :invitation_token # for invitable
|
33
|
+
|
34
|
+
Add :invitable to the devise line in your model:
|
35
|
+
|
36
|
+
class User < ActiveRecord::Base
|
37
|
+
devise ..., :invitable
|
38
|
+
end
|
39
|
+
|
40
|
+
If you are using devise :all, you can add :invitable to config.all in devise initializer:
|
41
|
+
Devise.setup do |config|
|
42
|
+
...
|
43
|
+
config.all = [..., :invitable]
|
44
|
+
...
|
45
|
+
end
|
46
|
+
|
47
|
+
== Model configuration
|
48
|
+
|
49
|
+
DeviseInvitable adds a new configuration option, :invite_for. It's the time a invitation is valid for. Default value is nil, which means invitation doesn't expire.
|
50
|
+
|
51
|
+
== Controller filters
|
52
|
+
|
53
|
+
It adds authenticate_resource! filter to restrict who can send invitations. You can override this method in your ApplicationController. Default behavior is require authentication of the same resource_name, so if you only have a model with devise it will allow to all authenticated users to send invitations.
|
54
|
+
|
55
|
+
You have to configure mailer as it's required for confirmable and recoverable.
|
56
|
+
|
57
|
+
== I18n
|
58
|
+
|
59
|
+
It uses two flash messages, :send_invitation and :updated, which are translated as other flash messages from devise.
|
60
|
+
|
61
|
+
== Adding Invitable to a running application
|
62
|
+
|
63
|
+
Define a migration to add invitable to your model:
|
64
|
+
|
65
|
+
change_table :your_table do |t|
|
66
|
+
t.string :invitation_token, :limit => 20
|
67
|
+
t.datetime :invitation_sent_at
|
68
|
+
t.index :invitation_token
|
69
|
+
end
|
70
|
+
|
71
|
+
# Allow null encrypted_password and password_salt
|
72
|
+
change_column :your_table, :encrypted_password, :string, :null => true
|
73
|
+
change_column :your_table, :password_salt, :string, :null => true
|
74
|
+
|
75
|
+
Add :invitable to the devise line of your model, or to config.all in devise initializer if your model uses devise :all.
|
76
|
+
|
77
|
+
Override authenticate_resource! filter if you need to customize who can send invitations.
|
78
|
+
|
79
|
+
== Note on Patches/Pull Requests
|
80
|
+
|
81
|
+
* Fork the project.
|
82
|
+
* Make your feature addition or bug fix.
|
83
|
+
* Add tests for it. This is important so I don't break it in a
|
84
|
+
future version unintentionally.
|
85
|
+
* Commit, do not mess with rakefile, version, or history.
|
86
|
+
(if you want to have your own version, that is fine but
|
87
|
+
bump version in a commit by itself I can ignore when I pull)
|
88
|
+
* Send me a pull request. Bonus points for topic branches.
|
89
|
+
|
90
|
+
== Copyright
|
91
|
+
|
92
|
+
Copyright (c) 2009 Sergio Cambra. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "lperichon-devise_invitable"
|
8
|
+
gem.summary = %Q{An invitation strategy for devise}
|
9
|
+
gem.description = %Q{It adds support for send invitations by email (it requires to be authenticated) and accept the invitation setting the password}
|
10
|
+
gem.email = "sergio@entrecables.com"
|
11
|
+
gem.homepage = "http://github.com/scambra/devise_invitable"
|
12
|
+
gem.authors = ["Sergio Cambra"]
|
13
|
+
gem.add_development_dependency 'mocha'
|
14
|
+
gem.add_development_dependency 'webrat'
|
15
|
+
gem.add_dependency 'devise', '~> 1.1.0'
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/*_test.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "devise_invitable #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Devise::InvitationsController < ApplicationController
|
2
|
+
include Devise::Controllers::InternalHelpers
|
3
|
+
include Devise::Controllers::Helpers
|
4
|
+
|
5
|
+
before_filter :authenticate_resource!, :only => [:new, :create]
|
6
|
+
before_filter :require_no_authentication, :only => [:edit, :update]
|
7
|
+
helper_method :after_sign_in_path_for
|
8
|
+
|
9
|
+
# GET /resource/invitation/new
|
10
|
+
def new
|
11
|
+
build_resource
|
12
|
+
render_with_scope :new
|
13
|
+
end
|
14
|
+
|
15
|
+
# POST /resource/invitation
|
16
|
+
def create
|
17
|
+
self.resource = resource_class.send_invitation(params[resource_name])
|
18
|
+
|
19
|
+
if resource.errors.empty?
|
20
|
+
set_flash_message :notice, :send_instructions
|
21
|
+
redirect_to after_sign_in_path_for(resource_name)
|
22
|
+
else
|
23
|
+
render_with_scope :new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# GET /resource/invitation/edit?invitation_token=abcdef
|
28
|
+
def edit
|
29
|
+
self.resource = resource_class.new
|
30
|
+
resource.invitation_token = params[:invitation_token]
|
31
|
+
render_with_scope :edit
|
32
|
+
end
|
33
|
+
|
34
|
+
# PUT /resource/invitation
|
35
|
+
def update
|
36
|
+
self.resource = resource_class.accept_invitation!(params[resource_name])
|
37
|
+
|
38
|
+
if resource.errors.empty?
|
39
|
+
set_flash_message :notice, :updated
|
40
|
+
sign_in_and_redirect(resource_name, resource)
|
41
|
+
else
|
42
|
+
render_with_scope :edit
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def authenticate_resource!
|
49
|
+
send :"authenticate_#{resource_name}!"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<h2>Set your password</h2>
|
2
|
+
|
3
|
+
<% form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :put } do |f| %>
|
4
|
+
<%= f.error_messages %>
|
5
|
+
<%= f.hidden_field :invitation_token %>
|
6
|
+
|
7
|
+
<p><%= f.label :password %></p>
|
8
|
+
<p><%= f.password_field :password %></p>
|
9
|
+
|
10
|
+
<p><%= f.label :password_confirmation %></p>
|
11
|
+
<p><%= f.password_field :password_confirmation %></p>
|
12
|
+
|
13
|
+
<p><%= f.submit "Set my password" %></p>
|
14
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h2>Send invitation</h2>
|
2
|
+
|
3
|
+
<% form_for resource, :as => resource_name, :url => invitation_path(resource_name) do |f| %>
|
4
|
+
<%= f.error_messages %>
|
5
|
+
|
6
|
+
<p><%= f.label :email %></p>
|
7
|
+
<p><%= f.text_field :email %></p>
|
8
|
+
|
9
|
+
<p><%= f.submit "Send an invitation" %></p>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<%= link_to "Home", after_sign_in_path_for(resource_name) %><br />
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Hello <%= @resource.email %>!
|
2
|
+
|
3
|
+
Someone has invited you to <%= root_url %>, you can accept it through the link below.
|
4
|
+
|
5
|
+
<%= link_to 'Accept invitation', edit_invitation_url(@resource, :invitation_token => @resource.invitation_token) %>
|
6
|
+
|
7
|
+
If you don't want to accept the invitation, please ignore this email.
|
8
|
+
Your account won't be created until you access the link above and set your password.
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{devise_invitable}
|
8
|
+
s.version = "0.3.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sergio Cambra"]
|
12
|
+
s.date = %q{2010-05-17}
|
13
|
+
s.description = %q{It adds support for send invitations by email (it requires to be authenticated) and accept the invitation setting the password}
|
14
|
+
s.email = %q{sergio@entrecables.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"app/controllers/devise/invitations_controller.rb",
|
27
|
+
"app/views/devise/mailer/invitation.html.erb",
|
28
|
+
"app/views/devise/invitations/edit.html.erb",
|
29
|
+
"app/views/devise/invitations/new.html.erb",
|
30
|
+
"devise_invitable.gemspec",
|
31
|
+
"init.rb",
|
32
|
+
"lib/devise_invitable.rb",
|
33
|
+
"lib/devise_invitable/controllers/helpers.rb",
|
34
|
+
"lib/devise_invitable/controllers/url_helpers.rb",
|
35
|
+
"lib/devise_invitable/locales/en.yml",
|
36
|
+
"lib/devise_invitable/mailer.rb",
|
37
|
+
"lib/devise_invitable/model.rb",
|
38
|
+
"lib/devise_invitable/rails.rb",
|
39
|
+
"lib/devise_invitable/routes.rb",
|
40
|
+
"lib/devise_invitable/schema.rb",
|
41
|
+
"test/integration/invitable_test.rb",
|
42
|
+
"test/integration_tests_helper.rb",
|
43
|
+
"test/mailers/invitation_test.rb",
|
44
|
+
"test/model_tests_helper.rb",
|
45
|
+
"test/models/invitable_test.rb",
|
46
|
+
"test/models_test.rb",
|
47
|
+
"test/rails_app/app/controllers/admins_controller.rb",
|
48
|
+
"test/rails_app/app/controllers/application_controller.rb",
|
49
|
+
"test/rails_app/app/controllers/home_controller.rb",
|
50
|
+
"test/rails_app/app/controllers/users_controller.rb",
|
51
|
+
"test/rails_app/app/helpers/application_helper.rb",
|
52
|
+
"test/rails_app/app/models/user.rb",
|
53
|
+
"test/rails_app/app/views/home/index.html.erb",
|
54
|
+
"test/rails_app/config/boot.rb",
|
55
|
+
"test/rails_app/config/database.yml",
|
56
|
+
"test/rails_app/config/environment.rb",
|
57
|
+
"test/rails_app/config/environments/development.rb",
|
58
|
+
"test/rails_app/config/environments/production.rb",
|
59
|
+
"test/rails_app/config/environments/test.rb",
|
60
|
+
"test/rails_app/config/initializers/backtrace_silencers.rb",
|
61
|
+
"test/rails_app/config/initializers/devise.rb",
|
62
|
+
"test/rails_app/config/initializers/inflections.rb",
|
63
|
+
"test/rails_app/config/initializers/new_rails_defaults.rb",
|
64
|
+
"test/rails_app/config/initializers/session_store.rb",
|
65
|
+
"test/rails_app/config/routes.rb",
|
66
|
+
"test/rails_app/vendor/plugins/devise_invitable/init.rb",
|
67
|
+
"test/routes_test.rb",
|
68
|
+
"test/test_helper.rb"
|
69
|
+
]
|
70
|
+
s.homepage = %q{http://github.com/scambra/devise_invitable}
|
71
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
72
|
+
s.require_paths = ["lib"]
|
73
|
+
s.rubygems_version = %q{1.3.6}
|
74
|
+
s.summary = %q{An invitation strategy for devise}
|
75
|
+
s.test_files = [
|
76
|
+
"test/integration/invitable_test.rb",
|
77
|
+
"test/integration_tests_helper.rb",
|
78
|
+
"test/mailers/invitation_test.rb",
|
79
|
+
"test/model_tests_helper.rb",
|
80
|
+
"test/models/invitable_test.rb",
|
81
|
+
"test/models_test.rb",
|
82
|
+
"test/rails_app/app/controllers/admins_controller.rb",
|
83
|
+
"test/rails_app/app/controllers/application_controller.rb",
|
84
|
+
"test/rails_app/app/controllers/home_controller.rb",
|
85
|
+
"test/rails_app/app/controllers/users_controller.rb",
|
86
|
+
"test/rails_app/app/helpers/application_helper.rb",
|
87
|
+
"test/rails_app/app/models/user.rb",
|
88
|
+
"test/rails_app/config/boot.rb",
|
89
|
+
"test/rails_app/config/environment.rb",
|
90
|
+
"test/rails_app/config/environments/development.rb",
|
91
|
+
"test/rails_app/config/environments/production.rb",
|
92
|
+
"test/rails_app/config/environments/test.rb",
|
93
|
+
"test/rails_app/config/initializers/backtrace_silencers.rb",
|
94
|
+
"test/rails_app/config/initializers/inflections.rb",
|
95
|
+
"test/rails_app/config/initializers/new_rails_defaults.rb",
|
96
|
+
"test/rails_app/config/initializers/session_store.rb",
|
97
|
+
"test/rails_app/config/initializers/devise.rb",
|
98
|
+
"test/rails_app/config/routes.rb",
|
99
|
+
"test/rails_app/vendor/plugins/devise_invitable/init.rb",
|
100
|
+
"test/routes_test.rb",
|
101
|
+
"test/test_helper.rb"
|
102
|
+
]
|
103
|
+
|
104
|
+
if s.respond_to? :specification_version then
|
105
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
106
|
+
s.specification_version = 3
|
107
|
+
|
108
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
109
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
110
|
+
s.add_development_dependency(%q<webrat>, [">= 0"])
|
111
|
+
s.add_runtime_dependency(%q<devise>, ["~> 1.1.0"])
|
112
|
+
else
|
113
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
114
|
+
s.add_dependency(%q<webrat>, [">= 0"])
|
115
|
+
s.add_dependency(%q<devise>, ["~> 1.1.0"])
|
116
|
+
end
|
117
|
+
else
|
118
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
119
|
+
s.add_dependency(%q<webrat>, [">= 0"])
|
120
|
+
s.add_dependency(%q<devise>, ["~> 1.1.0"])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'devise_invitable'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
unless defined?(Devise)
|
2
|
+
require 'devise'
|
3
|
+
end
|
4
|
+
|
5
|
+
Devise.module_eval do
|
6
|
+
# Time interval where the invitation token is valid.
|
7
|
+
mattr_accessor :invite_for
|
8
|
+
@@invite_for = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
Devise.add_module :invitable, :controller => :invitations, :model => 'devise_invitable/model', :route => :invitation
|
12
|
+
|
13
|
+
module DeviseInvitable; end
|
14
|
+
|
15
|
+
# Had to comment those two lines out and add them to the application initialized
|
16
|
+
|
17
|
+
require 'devise_invitable/mailer'
|
18
|
+
require 'devise_invitable/routes'
|
19
|
+
require 'devise_invitable/schema'
|
20
|
+
require 'devise_invitable/controllers/url_helpers'
|
21
|
+
require 'devise_invitable/rails'
|