devise_invitations 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +116 -0
- data/LICENSE +9 -0
- data/README.md +46 -0
- data/Rakefile +12 -0
- data/app/views/devise_invitations/mailer/instructions.html.erb +5 -0
- data/circle.yml +11 -0
- data/config/locales/devise_invitations.en.yml +7 -0
- data/devise_invitations.gemspec +22 -0
- data/lib/devise_invitations.rb +14 -0
- data/lib/devise_invitations/controller.rb +26 -0
- data/lib/devise_invitations/engine.rb +4 -0
- data/lib/devise_invitations/mailer.rb +24 -0
- data/lib/devise_invitations/model.rb +18 -0
- data/lib/devise_invitations/version.rb +3 -0
- data/lib/generators/devise_invitations/install_generator.rb +61 -0
- data/lib/generators/devise_invitations/migrations/templates/migration.rb +19 -0
- data/lib/generators/devise_invitations/models/templates/associations.rb +3 -0
- data/lib/generators/devise_invitations/routes/templates/routes.rb +6 -0
- data/lib/generators/devise_invitations/specs/templates/association.rb +2 -0
- data/lib/generators/devise_invitations/specs/templates/controller.rb +71 -0
- data/lib/generators/devise_invitations/specs/templates/factory.rb +7 -0
- data/lib/generators/devise_invitations/specs/templates/model.rb +21 -0
- data/lib/generators/devise_invitations/specs_generator.rb +30 -0
- data/magic.sh +83 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7aac54a0d1a5d67f8a773fc598199289e6365b32
|
4
|
+
data.tar.gz: 6c54a34bd81348aa616628c776a05cfd545d88d7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3c83fcc55964355ae244cd81c401baa549a9a43a6dfb63a6609152fd7cf378ad318baecf058b6712a20e14bc17452d31e4dae83054f106393454610b61eafb9c
|
7
|
+
data.tar.gz: 484c2973f4247f5335773e28c370d1a61db1197e047a4524a8c0473c9bf52593e517a39d3f6f6dac37d81c4e2009424f205779c338a6e4ec4c7894e392533f4a
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
devise_invitations (0.1)
|
5
|
+
devise_invitable (~> 1.5)
|
6
|
+
faker (~> 1.6)
|
7
|
+
has_secure_token (~> 1.0)
|
8
|
+
shoulda-matchers (~> 2.0)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
actionmailer (4.2.5)
|
14
|
+
actionpack (= 4.2.5)
|
15
|
+
actionview (= 4.2.5)
|
16
|
+
activejob (= 4.2.5)
|
17
|
+
mail (~> 2.5, >= 2.5.4)
|
18
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
19
|
+
actionpack (4.2.5)
|
20
|
+
actionview (= 4.2.5)
|
21
|
+
activesupport (= 4.2.5)
|
22
|
+
rack (~> 1.6)
|
23
|
+
rack-test (~> 0.6.2)
|
24
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
25
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
26
|
+
actionview (4.2.5)
|
27
|
+
activesupport (= 4.2.5)
|
28
|
+
builder (~> 3.1)
|
29
|
+
erubis (~> 2.7.0)
|
30
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
31
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
32
|
+
activejob (4.2.5)
|
33
|
+
activesupport (= 4.2.5)
|
34
|
+
globalid (>= 0.3.0)
|
35
|
+
activemodel (4.2.5)
|
36
|
+
activesupport (= 4.2.5)
|
37
|
+
builder (~> 3.1)
|
38
|
+
activerecord (4.2.5)
|
39
|
+
activemodel (= 4.2.5)
|
40
|
+
activesupport (= 4.2.5)
|
41
|
+
arel (~> 6.0)
|
42
|
+
activesupport (4.2.5)
|
43
|
+
i18n (~> 0.7)
|
44
|
+
json (~> 1.7, >= 1.7.7)
|
45
|
+
minitest (~> 5.1)
|
46
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
47
|
+
tzinfo (~> 1.1)
|
48
|
+
arel (6.0.3)
|
49
|
+
bcrypt (3.1.10)
|
50
|
+
builder (3.2.2)
|
51
|
+
devise (3.5.4)
|
52
|
+
bcrypt (~> 3.0)
|
53
|
+
orm_adapter (~> 0.1)
|
54
|
+
railties (>= 3.2.6, < 5)
|
55
|
+
responders
|
56
|
+
thread_safe (~> 0.1)
|
57
|
+
warden (~> 1.2.3)
|
58
|
+
devise_invitable (1.5.5)
|
59
|
+
actionmailer (>= 3.2.6, < 5)
|
60
|
+
devise (>= 3.2.0)
|
61
|
+
erubis (2.7.0)
|
62
|
+
faker (1.6.1)
|
63
|
+
i18n (~> 0.5)
|
64
|
+
globalid (0.3.6)
|
65
|
+
activesupport (>= 4.1.0)
|
66
|
+
has_secure_token (1.0.0)
|
67
|
+
activerecord (>= 3.0)
|
68
|
+
i18n (0.7.0)
|
69
|
+
json (1.8.3)
|
70
|
+
loofah (2.0.3)
|
71
|
+
nokogiri (>= 1.5.9)
|
72
|
+
mail (2.6.3)
|
73
|
+
mime-types (>= 1.16, < 3)
|
74
|
+
mime-types (2.99)
|
75
|
+
mini_portile2 (2.0.0)
|
76
|
+
minitest (5.8.3)
|
77
|
+
nokogiri (1.6.7.1)
|
78
|
+
mini_portile2 (~> 2.0.0.rc2)
|
79
|
+
orm_adapter (0.5.0)
|
80
|
+
rack (1.6.4)
|
81
|
+
rack-test (0.6.3)
|
82
|
+
rack (>= 1.0)
|
83
|
+
rails-deprecated_sanitizer (1.0.3)
|
84
|
+
activesupport (>= 4.2.0.alpha)
|
85
|
+
rails-dom-testing (1.0.7)
|
86
|
+
activesupport (>= 4.2.0.beta, < 5.0)
|
87
|
+
nokogiri (~> 1.6.0)
|
88
|
+
rails-deprecated_sanitizer (>= 1.0.1)
|
89
|
+
rails-html-sanitizer (1.0.2)
|
90
|
+
loofah (~> 2.0)
|
91
|
+
railties (4.2.5)
|
92
|
+
actionpack (= 4.2.5)
|
93
|
+
activesupport (= 4.2.5)
|
94
|
+
rake (>= 0.8.7)
|
95
|
+
thor (>= 0.18.1, < 2.0)
|
96
|
+
rake (10.5.0)
|
97
|
+
responders (2.1.1)
|
98
|
+
railties (>= 4.2.0, < 5.1)
|
99
|
+
shoulda-matchers (2.8.0)
|
100
|
+
activesupport (>= 3.0.0)
|
101
|
+
thor (0.19.1)
|
102
|
+
thread_safe (0.3.5)
|
103
|
+
tzinfo (1.2.2)
|
104
|
+
thread_safe (~> 0.1)
|
105
|
+
warden (1.2.4)
|
106
|
+
rack (>= 1.0)
|
107
|
+
|
108
|
+
PLATFORMS
|
109
|
+
ruby
|
110
|
+
|
111
|
+
DEPENDENCIES
|
112
|
+
devise_invitations!
|
113
|
+
rake (~> 10.4)
|
114
|
+
|
115
|
+
BUNDLED WITH
|
116
|
+
1.11.2
|
data/LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Copyright (c) 2016 Roxas Shadow <webmaster@giovannicapuano.net> All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
4
|
+
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
6
|
+
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
|
9
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
[![Circle CI](https://circleci.com/gh/RoxasShadow/devise_invitations.svg?style=svg)](https://circleci.com/gh/RoxasShadow/devise_invitations)
|
2
|
+
|
3
|
+
DeviseInvitable
|
4
|
+
===============
|
5
|
+
Allow multiple invitations on top of [devise_invitable](https://github.com/scambra/devise_invitable).
|
6
|
+
|
7
|
+
Why?
|
8
|
+
----
|
9
|
+
Basically devise_invitable, by design, can't handle multiple invitations, since it doesn't use any support table but creates a new hidden `User` for every invitation, storing there both the inviter (through the polymorphic column `sent_by`) and the token.
|
10
|
+
|
11
|
+
This means that one user is invitable once and only once.
|
12
|
+
This means that you'll find many hidden `User`s that probably will never sign up your website.
|
13
|
+
|
14
|
+
So what?
|
15
|
+
--------
|
16
|
+
So let's create a new table that allows an email address to be invited multiple times by different people, allowing the user to ignore them and sign up normally or, instead, choosing which invitation mail s?he wants to accept. Invitations are not deleted after the related user signs up the website, and they'll be flagged too (`pending` are all the invitations sent, `accepted` is an invitation that has been accepted by an user and `ignored` are the invitations ignored by the user since s?he accepted the one marked as `accepted`).
|
17
|
+
|
18
|
+
But *devise_invitable* is useful!
|
19
|
+
---------------------------------
|
20
|
+
I know, in fact I'm creating nothing but a layer on top of it. You still need and use *devise_invitable* (I mean, views, controller and the attributes it sets on the invited users).
|
21
|
+
|
22
|
+
Setup
|
23
|
+
-----
|
24
|
+
Create a model called `User`, then run both Devise and *devise_invitable* on it, following their instructions.
|
25
|
+
|
26
|
+
Then, add `gem 'devise_invitations'` to your Gemfile and run `$ bundle install`.
|
27
|
+
|
28
|
+
You can install *devise_invitations* just by running `$ bin/rails g devise_invitations:install` and `bin/rake db:migrate`.
|
29
|
+
|
30
|
+
This is what's new in your project:
|
31
|
+
|
32
|
+
- A migration for a new table called `invitations` is created
|
33
|
+
- A `has_many` association for `User` (useful to fetch the sent invitations) is injected
|
34
|
+
- A new route for said action is injected as well
|
35
|
+
|
36
|
+
If you need to customize something feel free to hard-copy it into your project (i.e. the mailer and the related view).
|
37
|
+
|
38
|
+
If you use *RSpec* and *FactoryGirl* you can run `$ bin/rails g devise_invitations:specs` to copy the specs for the new associations, controllers and models to your codebase. You can extend and customize them as you wish.
|
39
|
+
|
40
|
+
Now, you need an action to let your users invite other ones. Basically it's just
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
DeviseInvitations::Invitation.create!(email: params[:email], sent_by: current_user)
|
44
|
+
```
|
45
|
+
|
46
|
+
You can add new columns to the table for new attributes too, like `user_type`, `message` or whatever you want.
|
data/Rakefile
ADDED
data/circle.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'lib/devise_invitations/version.rb'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'devise_invitations'
|
5
|
+
s.version = DeviseInvitations::VERSION
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.author = 'Roxas Shadow'
|
8
|
+
s.email = 'webmaster@giovannicapuano.net'
|
9
|
+
s.homepage = 'https://github.com/RoxasShadow/devise_invitations'
|
10
|
+
s.summary = 'Allow multiple invitations on top of devise_invitable'
|
11
|
+
s.description = 'Extend devise and devise_invitable allowing multiple invitations.'
|
12
|
+
s.license = 'BSD-2-Clause'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files specs`.split("\n")
|
16
|
+
s.require_path = 'lib'
|
17
|
+
|
18
|
+
s.add_dependency 'devise_invitable', '~> 1.5'
|
19
|
+
s.add_dependency 'has_secure_token', '~> 1.0'
|
20
|
+
s.add_dependency 'shoulda-matchers', '~> 2.0'
|
21
|
+
s.add_dependency 'faker', '~> 1.6'
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'has_secure_token' unless Rails.version.start_with?('5')
|
2
|
+
|
3
|
+
if Rails.env.test?
|
4
|
+
require 'shoulda-matchers'
|
5
|
+
require 'faker'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'devise_invitations/engine'
|
9
|
+
|
10
|
+
module DeviseInvitations
|
11
|
+
autoload :Mailer, 'devise_invitations/mailer'
|
12
|
+
autoload :InvitationsController, 'devise_invitations/controller'
|
13
|
+
autoload :Invitation, 'devise_invitations/model'
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class DeviseInvitations::InvitationsController < Devise::InvitationsController
|
2
|
+
def accept
|
3
|
+
invitation = DeviseInvitations::Invitation.pending.find_by(token: params[:token])
|
4
|
+
|
5
|
+
if invitation.present? && invitation.valid?
|
6
|
+
invitation_params = { email: invitation.email }
|
7
|
+
|
8
|
+
user = User.invite!(invitation_params, invitation.sent_by) do |u|
|
9
|
+
u.skip_invitation = true
|
10
|
+
end
|
11
|
+
|
12
|
+
user.update(invitation_sent_at: Time.now.utc)
|
13
|
+
|
14
|
+
statuses = DeviseInvitations::Invitation.statuses
|
15
|
+
invitation.update(status: statuses[:accepted])
|
16
|
+
DeviseInvitations::Invitation.pending
|
17
|
+
.where(email: invitation.email)
|
18
|
+
.update_all(status: statuses[:ignored])
|
19
|
+
|
20
|
+
redirect_to accept_invitation_url(user, invitation_token: user.raw_invitation_token)
|
21
|
+
else
|
22
|
+
flash[:error] = t('en.invitations.accept.not_valid')
|
23
|
+
redirect_to root_path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class DeviseInvitations::Mailer < ApplicationMailer
|
2
|
+
def instructions(invitation)
|
3
|
+
@token = invitation.token
|
4
|
+
@sender = invitation.sent_by
|
5
|
+
@app_name = app_name
|
6
|
+
|
7
|
+
mail(
|
8
|
+
subject: t('.subject'),
|
9
|
+
from: @sender.email,
|
10
|
+
to: invitation.email,
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def app_name
|
17
|
+
Rails.application.class.parent_name
|
18
|
+
.underscore
|
19
|
+
.humanize
|
20
|
+
.split
|
21
|
+
.map(&:capitalize)
|
22
|
+
.join(' ')
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class DeviseInvitations::Invitation < ActiveRecord::Base
|
2
|
+
belongs_to :sent_by, class_name: 'User', foreign_key: 'sent_by_id'
|
3
|
+
has_secure_token :token
|
4
|
+
|
5
|
+
validates :email, presence: true, format: { with: Devise.email_regexp }
|
6
|
+
validates :email, :sent_by, uniqueness: { scope: [:email, :sent_by] }
|
7
|
+
validate :presence_as_user, on: :create
|
8
|
+
|
9
|
+
enum status: [:pending, :accepted, :ignored]
|
10
|
+
|
11
|
+
after_create { DeviseInvitations::Mailer.instructions(self).deliver_later }
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def presence_as_user
|
16
|
+
errors.add(:email, :user_already_exists) if User.exists?(email: email)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module DeviseInvitations
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
def install
|
5
|
+
%w(migrations models routes).each do |m|
|
6
|
+
invoke "devise_invitations:#{m}"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MigrationsGenerator < Rails::Generators::Base
|
12
|
+
include Rails::Generators::Migration
|
13
|
+
|
14
|
+
source_root File.expand_path('../migrations/templates', __FILE__)
|
15
|
+
|
16
|
+
def copy_migration
|
17
|
+
migration_template('migration.rb', 'db/migrate/create_invitations.rb')
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.next_migration_number(path)
|
21
|
+
if @prev_migration
|
22
|
+
@prev_migration += 1
|
23
|
+
else
|
24
|
+
@prev_migration = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
@prev_migration.to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class ModelsGenerator < Rails::Generators::Base
|
32
|
+
source_root File.expand_path('../models/templates', __FILE__)
|
33
|
+
|
34
|
+
def inject_associations
|
35
|
+
inject_into_file('app/models/user.rb', after: 'ActiveRecord::Base') do
|
36
|
+
File.read(
|
37
|
+
File.join(
|
38
|
+
File.expand_path('../models/templates', __FILE__),
|
39
|
+
'associations.rb'
|
40
|
+
)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class RoutesGenerator < Rails::Generators::Base
|
47
|
+
source_root File.expand_path('../routes/templates', __FILE__)
|
48
|
+
|
49
|
+
def inject_routes
|
50
|
+
inject_into_file('config/routes.rb', after: 'devise_for :users') do
|
51
|
+
File.read(
|
52
|
+
File.join(
|
53
|
+
File.expand_path('../routes/templates', __FILE__),
|
54
|
+
'routes.rb'
|
55
|
+
)
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateInvitations < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :invitations do |t|
|
4
|
+
t.integer :sent_by_id, null: false
|
5
|
+
t.string :email, null: false
|
6
|
+
t.integer :status, null: false, default: 0
|
7
|
+
t.string :token, null: false
|
8
|
+
|
9
|
+
t.timestamps null: false
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :invitations, :sent_by_id
|
13
|
+
add_index :invitations, :email
|
14
|
+
add_index :invitations, :status
|
15
|
+
add_index :invitations, :token
|
16
|
+
|
17
|
+
add_foreign_key :invitations, :users, column: :sent_by_id
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe DeviseInvitations::InvitationsController do
|
4
|
+
describe 'GET #accept' do
|
5
|
+
before { @request.env['devise.mapping'] = Devise.mappings[:user] }
|
6
|
+
|
7
|
+
let(:email) { Faker::Internet.safe_email }
|
8
|
+
let(:user) { User.find_by(email: email) }
|
9
|
+
|
10
|
+
let(:joe) { create(:user) }
|
11
|
+
let(:sender) { create(:user) }
|
12
|
+
let(:jack) { create(:user) }
|
13
|
+
|
14
|
+
let!(:joe_invitation) { create(:invitation, email: email, sent_by: joe) }
|
15
|
+
let!(:sender_invitation) { create(:invitation, email: email, sent_by: sender) }
|
16
|
+
let!(:jack_invitation) { create(:invitation, email: email, sent_by: jack) }
|
17
|
+
|
18
|
+
context 'token is valid' do
|
19
|
+
subject { get :accept, token: sender_invitation.token }
|
20
|
+
|
21
|
+
it { is_expected.to have_http_status(302) }
|
22
|
+
|
23
|
+
it 'creates a new user' do
|
24
|
+
expect { subject }.to change { User.unscoped.find_by(email: email).class }
|
25
|
+
.from(NilClass)
|
26
|
+
.to(User)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'sets a invited_by attribute to the new user' do
|
30
|
+
subject
|
31
|
+
expect(user.invited_by).to eq sender
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'flags as accepted the current invitation' do
|
35
|
+
expect { subject }.to change { sender_invitation.reload.status }
|
36
|
+
.from('pending')
|
37
|
+
.to('accepted')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'flags as ignored the remanining invitations' do
|
41
|
+
expect { subject }.to change { joe_invitation.reload.status }
|
42
|
+
.from('pending')
|
43
|
+
.to('ignored')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'does not accept additional requests' do
|
47
|
+
subject
|
48
|
+
|
49
|
+
expect { get :accept, token: sender_invitation.token }
|
50
|
+
.to change { flash[:error] }
|
51
|
+
.from(nil)
|
52
|
+
.to(I18n.t('en.invitations.accept.not_valid'))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'token is valid' do
|
57
|
+
subject { get :accept, token: 'lolwut' }
|
58
|
+
|
59
|
+
it { is_expected.to have_http_status(302) }
|
60
|
+
|
61
|
+
it 'generates a flash error' do
|
62
|
+
subject
|
63
|
+
expect(flash[:error]).to eq I18n.t('en.invitations.accept.not_valid')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'does not create any new user' do
|
67
|
+
expect { subject }.to_not change { User.unscoped.count }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe DeviseInvitations::Invitation do
|
4
|
+
it { is_expected.to belong_to :sent_by }
|
5
|
+
|
6
|
+
let(:valid) { build(:invitation) }
|
7
|
+
it { expect(valid.pending?).to be_truthy }
|
8
|
+
it { expect(valid.valid?).to be_truthy }
|
9
|
+
|
10
|
+
let(:invalid) { build(:invitation, email: 'lolwut') }
|
11
|
+
it { expect(invalid.valid?).to be_falsy }
|
12
|
+
|
13
|
+
context 'use an email that is already registered inside the platform' do
|
14
|
+
before { create(:user, email: 'joe@example.com') }
|
15
|
+
subject { create(:invitation, email: 'joe@example.com') }
|
16
|
+
|
17
|
+
it 'raises an error' do
|
18
|
+
expect { subject }.to raise_error(ActiveRecord::RecordInvalid)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module DeviseInvitations
|
2
|
+
module Generators
|
3
|
+
class SpecsGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../specs/templates', __FILE__)
|
5
|
+
|
6
|
+
def copy_factory
|
7
|
+
copy_file('factory.rb', 'spec/factories/invitations.rb')
|
8
|
+
end
|
9
|
+
|
10
|
+
def copy_controller
|
11
|
+
copy_file('controller.rb', 'spec/controllers/invitations_controller_spec.rb')
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_model
|
15
|
+
copy_file('model.rb', 'spec/models/invitation_spec.rb')
|
16
|
+
end
|
17
|
+
|
18
|
+
def inject_association
|
19
|
+
inject_into_file('spec/models/user_spec.rb', after: 'RSpec.describe User, type: :model do') do
|
20
|
+
File.read(
|
21
|
+
File.join(
|
22
|
+
File.expand_path('../specs/templates', __FILE__),
|
23
|
+
'association.rb'
|
24
|
+
)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/magic.sh
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
##
|
2
|
+
# Hello, I'm a magical little girl in aid of CI-kun.
|
3
|
+
# I'll create and setup a new Rails application from scratch so CI-kun
|
4
|
+
# can test it. Bye-chi!
|
5
|
+
##
|
6
|
+
|
7
|
+
gem install rails
|
8
|
+
|
9
|
+
rails new test_app
|
10
|
+
|
11
|
+
cd test_app
|
12
|
+
|
13
|
+
echo "gem 'rspec-rails'
|
14
|
+
gem 'factory_girl_rails'
|
15
|
+
gem 'letter_opener'
|
16
|
+
gem 'faker'
|
17
|
+
|
18
|
+
gem 'devise'
|
19
|
+
gem 'devise_invitable'
|
20
|
+
gem 'devise_invitations', path: '../'
|
21
|
+
" >> Gemfile
|
22
|
+
|
23
|
+
echo "`sed '45d' Gemfile`" > Gemfile # spring a shit
|
24
|
+
|
25
|
+
bundle install
|
26
|
+
|
27
|
+
rails g rspec:install
|
28
|
+
|
29
|
+
# we need a root_path
|
30
|
+
rails g controller welcome index
|
31
|
+
contents="root 'welcome#index'"
|
32
|
+
echo "`sed \"8s/^/$contents/\" config/routes.rb`" > config/routes.rb
|
33
|
+
|
34
|
+
rails g model user
|
35
|
+
rails g mailer UserMailer # we need an ApplicationMailer too
|
36
|
+
|
37
|
+
rails g devise:install
|
38
|
+
rails g devise user
|
39
|
+
|
40
|
+
rails g devise_invitable:install
|
41
|
+
rails g devise_invitable user
|
42
|
+
|
43
|
+
## Uncomment these to check the diff later
|
44
|
+
# git init
|
45
|
+
# git add .
|
46
|
+
# git commit -m "Initial commit"
|
47
|
+
|
48
|
+
rails g devise_invitations:install
|
49
|
+
rails g devise_invitations:specs
|
50
|
+
|
51
|
+
rake db:migrate
|
52
|
+
|
53
|
+
# Enable FactoryGirl and Shoulda
|
54
|
+
echo "
|
55
|
+
RSpec.configure do |config|
|
56
|
+
config.include FactoryGirl::Syntax::Methods
|
57
|
+
end
|
58
|
+
|
59
|
+
Shoulda::Matchers.configure do |config|
|
60
|
+
config.integrate do |with|
|
61
|
+
with.test_framework :rspec
|
62
|
+
with.library :rails
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
" >> spec/rails_helper.rb
|
67
|
+
|
68
|
+
echo "FactoryGirl.define do
|
69
|
+
factory :user do
|
70
|
+
email { Faker::Internet.safe_email }
|
71
|
+
password { Faker::Internet.password(8) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
" > spec/factories/users.rb
|
75
|
+
|
76
|
+
contents="config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }"
|
77
|
+
echo "`sed \"39s/^/$contents/\" config/environments/test.rb`" > config/environments/test.rb
|
78
|
+
|
79
|
+
contents="config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }"
|
80
|
+
echo "`sed \"38s/^/$contents/\" config/environments/development.rb`" > config/environments/development.rb
|
81
|
+
|
82
|
+
contents="config.action_mailer.delivery_method = :letter_opener"
|
83
|
+
echo "`sed \"33s/^/$contents/\" config/environments/development.rb`" > config/environments/development.rb
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devise_invitations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roxas Shadow
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: devise_invitable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: has_secure_token
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: shoulda-matchers
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faker
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
description: Extend devise and devise_invitable allowing multiple invitations.
|
70
|
+
email: webmaster@giovannicapuano.net
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- ".gitignore"
|
76
|
+
- Gemfile
|
77
|
+
- Gemfile.lock
|
78
|
+
- LICENSE
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- app/views/devise_invitations/mailer/instructions.html.erb
|
82
|
+
- circle.yml
|
83
|
+
- config/locales/devise_invitations.en.yml
|
84
|
+
- devise_invitations.gemspec
|
85
|
+
- lib/devise_invitations.rb
|
86
|
+
- lib/devise_invitations/controller.rb
|
87
|
+
- lib/devise_invitations/engine.rb
|
88
|
+
- lib/devise_invitations/mailer.rb
|
89
|
+
- lib/devise_invitations/model.rb
|
90
|
+
- lib/devise_invitations/version.rb
|
91
|
+
- lib/generators/devise_invitations/.DS_Store
|
92
|
+
- lib/generators/devise_invitations/install_generator.rb
|
93
|
+
- lib/generators/devise_invitations/migrations/.DS_Store
|
94
|
+
- lib/generators/devise_invitations/migrations/templates/migration.rb
|
95
|
+
- lib/generators/devise_invitations/models/.DS_Store
|
96
|
+
- lib/generators/devise_invitations/models/templates/associations.rb
|
97
|
+
- lib/generators/devise_invitations/routes/templates/routes.rb
|
98
|
+
- lib/generators/devise_invitations/specs/.DS_Store
|
99
|
+
- lib/generators/devise_invitations/specs/templates/association.rb
|
100
|
+
- lib/generators/devise_invitations/specs/templates/controller.rb
|
101
|
+
- lib/generators/devise_invitations/specs/templates/factory.rb
|
102
|
+
- lib/generators/devise_invitations/specs/templates/model.rb
|
103
|
+
- lib/generators/devise_invitations/specs_generator.rb
|
104
|
+
- magic.sh
|
105
|
+
homepage: https://github.com/RoxasShadow/devise_invitations
|
106
|
+
licenses:
|
107
|
+
- BSD-2-Clause
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.5.1
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Allow multiple invitations on top of devise_invitable
|
129
|
+
test_files: []
|