Skip to content

Commit

Permalink
extracted InviteForm to app/forms/invitation/invite_form.rb
Browse files Browse the repository at this point in the history
  • Loading branch information
tomichj committed Mar 13, 2018
1 parent e9abbc8 commit 9cdde38
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 128 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Invitation Changelog

## [0.5.0] - March 12, 2018

### API change
- `InviteForm` extracted from invites_controller.rb, added at `app/forms/invitation/invite_form.rb`
- `controllers_generator.rb` now has three targets: `create_controllers`, `create_mailers`, and `create_forms`
- deleted empty `lib/tasks/invitation_tasks.rake`

[0.5.0]: https://github.com/tomichj/invitation/compare/0.4.5...0.5.0


## [0.4.5] - March 9, 2018

### Bugfix:
Expand Down
13 changes: 0 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,6 @@ Please use [GitHub Issues] to report bugs. You can contact me directly on twitte
[![Gem Version](https://badge.fury.io/rb/invitation.svg)](https://badge.fury.io/rb/invitation) ![Build status](https://travis-ci.org/tomichj/invitation.svg?branch=master) ![Code Climate](https://codeclimate.com/github/tomichj/invitation/badges/gpa.svg)


### Warning - 0.4.2 api change

Release 0.4.2 includes an API change in configuration, `config.user_model` is now set to a string. Example:

```ruby
Invitation.configuration do |config|
config.user_model = 'Profile'
end
```

Fix in 0.4.3: `config.user_model` now accepts the user class again, in addition to the preferred String.


## Overview

Allow users to invite others to join an organization or resource. Plenty of gems can issue a 'system-wide' invitation,
Expand Down
195 changes: 84 additions & 111 deletions app/controllers/invitation/invites_controller.rb
Original file line number Diff line number Diff line change
@@ -1,133 +1,106 @@
#
# Invitation::InvitesController - issue invitations to users via email address.
#
# Controller uses an (inner class) Form object that is not a persisted model. The
# form can accept many email addresses, and creates on Invite per email address.
#
# Subclass and modify or extend, or copy the controller into your app with `rails generate invitation:controller`.
#
# Common extensions include:
# * add authorization checks: subclass and add before_actions to :new and :create.
# * override after_invite_existing_user or after_invite_new_user
#
class Invitation::InvitesController < ApplicationController
def new
@invite = InviteForm.new(invite_params)
render template: 'invites/new'
end
module Invitation

# Create one or more Invite instances.
# invite: { invitable_id, invitable_type, email or emails:[] }
#
def create
failures = []
invites = InviteForm.new(invite_params).build_invites(current_user)
ActiveRecord::Base.transaction do
invites.each { |invite| invite.save ? do_invite(invite) : failures << invite.email }
# Invitation::InvitesController - issue invitations to users via email address.
#
# Controller uses an (inner class) Form object that is not a persisted model. The
# form can accept many email addresses, and creates on Invite per email address.
#
# Subclass and modify/extend, or copy the controller into your app with `rails generate invitation:controller`.
#
# Common extensions could include:
# * add authorization checks: subclass and add before_actions to :new and :create.
# * override after_invite_existing_user or after_invite_new_user
#
class InvitesController < ApplicationController

def new
@invite = InviteForm.new(invite_params)
render template: 'invites/new'
end
logger.info "!!!!!!!!!!!!!!!!!!!!! INSIDE CREATE: current_user: #{current_user.inspect}"
respond_to do |format|
format.html do
if failures.empty?
flash[:notice] = t('invitation.flash.invite_issued', count: invites.count)
else
flash[:error] = t('invitation.flash.invite_error', count: failures.count, email: failures.to_sentence)
end
redirect_to url_after_invite(invites.first) # FIXME: redirect to back

#
# Create one or more Invite instances.
# invite: { invitable_id, invitable_type, email or emails:[] }
#
def create
failures = []
invites = InviteForm.new(invite_params).build_invites(current_user)
ActiveRecord::Base.transaction do
invites.each { |invite| invite.save ? do_invite(invite) : failures << invite.email }
end
format.json do
if failures.empty?
# If we received a single email, json response should be a scalar, not an array.
invites = params[:invite].key?('email') ? invites.first : invites
render json: invites.as_json(except: [:token, :created_at, :updated_at]), status: 201
else
render json: {
message: t('invitation.flash.invite_error', count: failures.count, email: failures.to_sentence),
status: :unprocessable_entity
}

logger.info "!!!!!!!!!!!!!!!!!!!!! INSIDE CREATE: current_user: #{current_user.inspect}"
respond_to do |format|
format.html do
if failures.empty?
flash[:notice] = t('invitation.flash.invite_issued', count: invites.count)
else
flash[:error] = t('invitation.flash.invite_error', count: failures.count, email: failures.to_sentence)
end
redirect_to url_after_invite(invites.first) # FIXME: redirect to back
end
format.json do
if failures.empty?
# If we received a single email, json response should be a scalar, not an array.
invites = params[:invite].key?('email') ? invites.first : invites
render json: invites.as_json(except: [:token, :created_at, :updated_at]), status: 201
else
render json: {
message: t('invitation.flash.invite_error', count: failures.count, email: failures.to_sentence),
status: :unprocessable_entity
}
end
end
end
end
end

private

# A form object pretends to be 'invite', but accepts both 'email' and 'emails'.
# It knows how to build all of the invite instances.
class InviteForm
include ActiveModel::Model
attr_accessor :invitable_id, :invitable_type, :email, :emails
attr_reader :invitable
private

def self.model_name # form masquerades as 'invite'
ActiveModel::Name.new(self, nil, 'Invite')
# Override this if you want to do something more complicated for existing users.
# For example, if you have a more complex permissions scheme than just a simple
# has_many relationship, enable it here.
def after_invite_existing_user(invite)
# Add the user to the invitable resource/organization
invite.invitable.add_invited_user(invite.recipient)
end

def initialize(attributes = {})
@emails ||= []
super
# Override if you want to do something more complicated for new users.
# By default we don't do anything extra.
def after_invite_new_user(invite)
end

def invitable
@invitable ||= @invitable_type.classify.constantize.find(@invitable_id)
# After an invite is created, redirect the user here.
# Default implementation doesn't return a url, just the invitable.
def url_after_invite(invite)
invite.invitable
end

def build_invites(current_user)
all_emails.reject(&:blank?).collect do |e|
Invite.new(invitable_id: @invitable_id, invitable_type: @invitable_type, sender_id: current_user.id, email: e)
end
def invite_params
params[:invite] ? params.require(:invite).permit(:invitable_id, :invitable_type, :email, emails: []) : {}
end

private

def all_emails
@emails + [@email]
end
end

# Override this if you want to do something more complicated for existing users.
# For example, if you have a more complex permissions scheme than just a simple
# has_many relationship, enable it here.
def after_invite_existing_user(invite)
# Add the user to the invitable resource/organization
invite.invitable.add_invited_user(invite.recipient)
end

# Override if you want to do something more complicated for new users.
# By default we don't do anything extra.
def after_invite_new_user(invite)
end

# After an invite is created, redirect the user here.
# Default implementation doesn't return a url, just the invitable.
def url_after_invite(invite)
invite.invitable
end

def invite_params
params[:invite] ? params.require(:invite).permit(:invitable_id, :invitable_type, :email, emails: []) : {}
end

# Invite user by sending email.
# Existing users are granted permissions via #after_invite_existing_user.
# New users are granted permissions via #after_invite_new_user, currently a null op.
def do_invite(invite)
if invite.existing_user?
deliver_email(InviteMailer.existing_user(invite))
after_invite_existing_user(invite)
invite.save
else
deliver_email(InviteMailer.new_user(invite))
after_invite_new_user(invite)
# Invite user by sending email.
# Existing users are granted permissions via #after_invite_existing_user.
# New users are granted permissions via #after_invite_new_user, currently a null op.
def do_invite(invite)
if invite.existing_user?
deliver_email(InviteMailer.existing_user(invite))
after_invite_existing_user(invite)
invite.save
else
deliver_email(InviteMailer.new_user(invite))
after_invite_new_user(invite)
end
end
end

# Use deliver_later from rails 4.2+ if available.
def deliver_email(mail)
if mail.respond_to?(:deliver_later)
mail.deliver_later
else
mail.deliver
# Use deliver_later from rails 4.2+ if available.
def deliver_email(mail)
if mail.respond_to?(:deliver_later)
mail.deliver_later
else
mail.deliver
end
end
end
end
36 changes: 36 additions & 0 deletions app/forms/invitation/invite_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module Invitation

# A form object pretends to be 'invite', but accepts both 'email' and 'emails'.
# It knows how to build all of the invite instances.
class InviteForm
include ActiveModel::Model

attr_accessor :invitable_id, :invitable_type, :email, :emails
attr_reader :invitable

def self.model_name # form masquerades as 'invite'
ActiveModel::Name.new(self, nil, 'Invite')
end

def initialize(attributes = {})
@emails ||= []
super
end

def invitable
@invitable ||= @invitable_type.classify.constantize.find(@invitable_id)
end

def build_invites(current_user)
all_emails.reject(&:blank?).collect do |e|
Invite.new(invitable_id: @invitable_id, invitable_type: @invitable_type, sender_id: current_user.id, email: e)
end
end

private

def all_emails
@emails + [@email]
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ def create_controllers
def create_mailers
directory 'app/mailers'
end

def create_forms
directory 'app/forms'
end
end
end
end
4 changes: 0 additions & 4 deletions lib/tasks/invitation_tasks.rake

This file was deleted.

0 comments on commit 9cdde38

Please sign in to comment.