From 969ee1f6efc5c433dc7fdb104a1a05cf1accd7b6 Mon Sep 17 00:00:00 2001 From: Daniel Luna Date: Mon, 19 Mar 2018 17:19:20 -0300 Subject: [PATCH 1/7] Extracting invites' email sending into an InviteEmail operation object Following the service/operation object pattern (for instance, see http://trailblazer.to/gems/operation/2.0/), we should be able to send e-mail invites decoupled from a particular controller action. This also allows one to use a different mailer class from the original. Useful when you want to configure the e-mail for a particular invitable. --- .../invitation/invites_controller.rb | 46 +-------------- app/operations/invitation/invite_emails.rb | 56 +++++++++++++++++++ .../invitation/invite_emails_spec.rb | 18 ++++++ 3 files changed, 76 insertions(+), 44 deletions(-) create mode 100644 app/operations/invitation/invite_emails.rb create mode 100644 spec/operations/invitation/invite_emails_spec.rb diff --git a/app/controllers/invitation/invites_controller.rb b/app/controllers/invitation/invites_controller.rb index 32f6049..723f2b8 100644 --- a/app/controllers/invitation/invites_controller.rb +++ b/app/controllers/invitation/invites_controller.rb @@ -13,7 +13,7 @@ module Invitation # * override after_invite_existing_user or after_invite_new_user # class InvitesController < ApplicationController - + def new @invite = InviteForm.new(invite_params) render template: 'invites/new' @@ -24,12 +24,8 @@ def new # 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 - + failures = Invitation::InviteEmails.new(invites).send_invites logger.info "!!!!!!!!!!!!!!!!!!!!! INSIDE CREATE: current_user: #{current_user.inspect}" respond_to do |format| format.html do @@ -55,21 +51,6 @@ def create end end - private - - # 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) @@ -79,28 +60,5 @@ def url_after_invite(invite) 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) - 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 - end - end end end diff --git a/app/operations/invitation/invite_emails.rb b/app/operations/invitation/invite_emails.rb new file mode 100644 index 0000000..5030046 --- /dev/null +++ b/app/operations/invitation/invite_emails.rb @@ -0,0 +1,56 @@ +module Invitation + class InviteEmails + attr_reader :invites, :failures, :mailer + + def initialize(invites, opts = {}) + @invites = invites + @mailer = opts[:mailer] || InviteMailer + end + + def send_invites + @failures = [] + ActiveRecord::Base.transaction do + invites.each { |invite| invite.save ? do_invite(invite) : @failures << invite.email } + end + @failures + 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(mailer.existing_user(invite)) + after_invite_existing_user(invite) + invite.save + else + deliver_email(mailer.new_user(invite)) + after_invite_new_user(invite) + 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 + end + end + + private + + # 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 + end +end diff --git a/spec/operations/invitation/invite_emails_spec.rb b/spec/operations/invitation/invite_emails_spec.rb new file mode 100644 index 0000000..2281929 --- /dev/null +++ b/spec/operations/invitation/invite_emails_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +RSpec.describe Invitation::InviteEmails do + describe 'custom mailer' do + let(:mail) { double('Mail') } + let(:test_mailer) { double('InviteMailer') } + + subject { described_class.new(invites, mailer: test_mailer).send_invites } + + let(:invites) { [create(:invite, :recipient_is_existing_user)] } + + it 'sends email through the given mailer' do + allow(mail).to receive(:deliver).once + expect(test_mailer).to receive(:existing_user).once.and_return(mail) + subject + end + end +end From e8ffc98990b12357ed612d9ae279c1d18787386d Mon Sep 17 00:00:00 2001 From: Nasir Ibrahim Date: Fri, 10 Apr 2020 15:32:59 -0400 Subject: [PATCH 2/7] [NO STORY] Update Rails version --- invitation.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invitation.gemspec b/invitation.gemspec index 4780d14..4ac4227 100644 --- a/invitation.gemspec +++ b/invitation.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.rdoc'] s.test_files = Dir['test/**/*'] - s.add_dependency 'rails', '>= 4.0', '< 5.2' + s.add_dependency 'rails', '~> 5.2', '>= 5.2.4.2' s.add_development_dependency 'factory_girl', '~> 4.8' s.add_development_dependency 'rspec-rails', '~> 3.5' From 725db1a0fdaea38ec9af2721fe246153302b6aec Mon Sep 17 00:00:00 2001 From: Nasir Ibrahim Date: Fri, 10 Apr 2020 15:32:59 -0400 Subject: [PATCH 3/7] [NO STORY] Update Rails version --- invitation.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invitation.gemspec b/invitation.gemspec index 4780d14..4ac4227 100644 --- a/invitation.gemspec +++ b/invitation.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.rdoc'] s.test_files = Dir['test/**/*'] - s.add_dependency 'rails', '>= 4.0', '< 5.2' + s.add_dependency 'rails', '~> 5.2', '>= 5.2.4.2' s.add_development_dependency 'factory_girl', '~> 4.8' s.add_development_dependency 'rspec-rails', '~> 3.5' From b6476836843b7f04c677cbeabcf888711add5691 Mon Sep 17 00:00:00 2001 From: mfitzhenry Date: Fri, 28 Apr 2023 00:11:47 -0400 Subject: [PATCH 4/7] update ruby and rails, fix specs --- .byebug_history | 16 ++++++++++++++++ .tool-versions | 1 + app/assets/config/manifest.js | 3 +++ app/models/invite.rb | 2 +- invitation.gemspec | 7 ++++--- spec/dummy/app/assets/config/manifest.js | 3 +++ spec/dummy/config/environments/development.rb | 2 +- spec/dummy/config/environments/production.rb | 2 ++ spec/dummy/config/environments/test.rb | 3 ++- spec/spec_helper.rb | 4 +++- 10 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 .byebug_history create mode 100644 .tool-versions create mode 100644 app/assets/config/manifest.js create mode 100644 spec/dummy/app/assets/config/manifest.js diff --git a/.byebug_history b/.byebug_history new file mode 100644 index 0000000..57a5daa --- /dev/null +++ b/.byebug_history @@ -0,0 +1,16 @@ +exit +post path, **{:params=>{:invite=>{:invitable_id=>1, :invitable_type=>"Company", :emails=>["gug@gug.com", "gug2@gug.com"]}}, :headers=>{"ACCEPT"=>"application/json"}} +post path, {:params=>{:invite=>{:invitable_id=>1, :invitable_type=>"Company", :emails=>["gug@gug.com", "gug2@gug.com"]}}, :headers=>{"ACCEPT"=>"application/json"}} +post path +args +path +exit +post path, *args +exit +continue +:q +*args +args +path +post path, **args +post path, *args diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..2c0c270 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.0.0 diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000..e694c75 --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,3 @@ + //= link_tree ../images + //= link_directory ../javascripts .js + //= link_directory ../stylesheets .css \ No newline at end of file diff --git a/app/models/invite.rb b/app/models/invite.rb index 73ef069..28e3657 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -14,7 +14,7 @@ class Invite < ActiveRecord::Base end before_create :generate_token - before_save :set_email_case, on: :create + before_save :set_email_case, if: :new_record? before_save :check_recipient_existence validates :email, presence: true diff --git a/invitation.gemspec b/invitation.gemspec index 4ac4227..09316dd 100644 --- a/invitation.gemspec +++ b/invitation.gemspec @@ -17,11 +17,11 @@ Gem::Specification.new do |s| s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.rdoc'] s.test_files = Dir['test/**/*'] - s.add_dependency 'rails', '~> 5.2', '>= 5.2.4.2' + s.add_dependency 'rails', '~> 6.0.2', '>= 6.0.2' s.add_development_dependency 'factory_girl', '~> 4.8' - s.add_development_dependency 'rspec-rails', '~> 3.5' - s.add_development_dependency 'rspec-mocks', '~> 3.5' + s.add_development_dependency 'rspec-rails' + s.add_development_dependency 'rspec-mocks' s.add_development_dependency 'pry', '~> 0.10' s.add_development_dependency 'sqlite3' s.add_development_dependency 'shoulda-matchers' @@ -31,6 +31,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'appraisal' s.add_development_dependency 'bundler' s.add_development_dependency 'rake' + s.add_development_dependency 'byebug' s.required_ruby_version = Gem::Requirement.new('>= 2.0') end diff --git a/spec/dummy/app/assets/config/manifest.js b/spec/dummy/app/assets/config/manifest.js new file mode 100644 index 0000000..e694c75 --- /dev/null +++ b/spec/dummy/app/assets/config/manifest.js @@ -0,0 +1,3 @@ + //= link_tree ../images + //= link_directory ../javascripts .js + //= link_directory ../stylesheets .css \ No newline at end of file diff --git a/spec/dummy/config/environments/development.rb b/spec/dummy/config/environments/development.rb index b55e214..f309044 100644 --- a/spec/dummy/config/environments/development.rb +++ b/spec/dummy/config/environments/development.rb @@ -35,7 +35,7 @@ # Checks for improperly declared sprockets dependencies. # Raises helpful error messages. config.assets.raise_runtime_errors = true - + config.assets.precompile = ["manifest.js"] # Raises error for missing translations # config.action_view.raise_on_missing_translations = true end diff --git a/spec/dummy/config/environments/production.rb b/spec/dummy/config/environments/production.rb index 5c1b32e..d2e97e6 100644 --- a/spec/dummy/config/environments/production.rb +++ b/spec/dummy/config/environments/production.rb @@ -34,6 +34,8 @@ # Asset digests allow you to set far-future HTTP expiration dates on all assets, # yet still be able to expire them through the digest params. config.assets.digest = true + + config.assets.precompile = ["manifest.js"] # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb diff --git a/spec/dummy/config/environments/test.rb b/spec/dummy/config/environments/test.rb index 1e09eca..cd69adf 100644 --- a/spec/dummy/config/environments/test.rb +++ b/spec/dummy/config/environments/test.rb @@ -6,7 +6,8 @@ # your test database is "scratch space" for the test suite and is wiped # and recreated between test runs. Don't rely on the data there! config.cache_classes = true - + config.assets.precompile = ["manifest.js"] + # Do not eager load code on boot. This avoids loading your whole application # just for the purpose of running a single test. If you are using a tool that # preloads Rails for running tests, you may have to set it to true. diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2c1336a..743daa7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,6 +14,7 @@ require 'capybara/rspec' require 'factory_girl' require 'timecop' +require 'byebug' Rails.backtrace_cleaner.remove_silencers! @@ -64,7 +65,8 @@ def mock_request(params = {}) # def do_post(path, *args) if Rails::VERSION::MAJOR >= 5 - post path, *args + # byebug + post path, **args.first else post path, *(args.collect{|i| i.values}.flatten) end From 67bef419cf5e0e2fc1d6686cfba753843c2743d4 Mon Sep 17 00:00:00 2001 From: Matt Fitz-Henry <1731764+mfitzhenry@users.noreply.github.com> Date: Fri, 28 Apr 2023 12:20:55 -0400 Subject: [PATCH 5/7] Add circleci (#7) * Add circleci and remove old ruby-version file * trigger for circle * update bundler version * update circle ci config * remove junit formatter from circle ci config --------- Co-authored-by: mfitzhenry --- .circleci/config.yml | 40 ++++++++++ .ruby-version | 1 - README.md | 164 ++++++++++++++++++++--------------------- invitation.gemspec | 2 +- test_results/rspec.xml | 9 +++ 5 files changed, 129 insertions(+), 87 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .ruby-version create mode 100644 test_results/rspec.xml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..88aa06a --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,40 @@ +version: 2.0 + +jobs: + "ruby-3.0.0": + docker: + - image: circleci/ruby:3.0.0 + environment: + BUNDLE_JOBS: 3 + BUNDLE_RETRY: 3 + BUNDLE_PATH: vendor/bundle + steps: + - checkout + - restore_cache: + keys: + - v1-bundle-{{ .Branch }}-{{ checksum "invitation.gemspec" }} + - v1-bundle-{{ .Branch }} + - run: + name: Upgrade Bundler + command: gem install --no-doc bundler -v "$(grep bundler *.gemspec | awk '{print $NF}' | tr -d \' )" + - run: + name: Bundle Install + command: bundle install + - save_cache: + key: v1-bundle-{{ .Branch }}-{{ checksum "invitation.gemspec" }} + paths: + - vendor/bundle + - run: + name: Run rspec in parallel + command: | + bundle exec rspec --profile 10 \ + --out test_results/rspec.xml \ + --format progress + - store_test_results: + path: test_results + +workflows: + version: 2 + test: + jobs: + - "ruby-3.0.0" diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 0bee604..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.3.3 diff --git a/README.md b/README.md index ed0b5e8..5a182f4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,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) - ## Overview Allow users to invite others to join an organization or resource. Plenty of gems can issue a 'system-wide' invitation, @@ -18,25 +17,23 @@ but few offer 'scoped' invitations, giving an invited user access to a particula Invitations are issued via email. You can invite users new to join the system while giving them permissions to a resource, or invite existing users by giving them access to a new resource. -* a user can invite someone to join an invitable by providing an email address to invite -* if the user already exists, that user is granted access to the invitable, and a notification email is sent -* if the user does not exist, sends an email with a link to sign up. When the new user signs up, -they are added to the invitable resource/organization. -* the invite grants the invited user access to ONLY the invitable organization they were invited to. - +- a user can invite someone to join an invitable by providing an email address to invite +- if the user already exists, that user is granted access to the invitable, and a notification email is sent +- if the user does not exist, sends an email with a link to sign up. When the new user signs up, + they are added to the invitable resource/organization. +- the invite grants the invited user access to ONLY the invitable organization they were invited to. ## Prerequisites -* An authentication system with a User model and current_user helper, e.g. https://github.com/tomichj/authenticate -* Your user model must include an :email attribute. -* Additional model classes that are resources or organizations you wish to invite users to join, usually with a -many-to-many relationship to your user model. -* You probably also want an authorization system to restrict who can issue invitations to particular resources +- An authentication system with a User model and current_user helper, e.g. https://github.com/tomichj/authenticate +- Your user model must include an :email attribute. +- Additional model classes that are resources or organizations you wish to invite users to join, usually with a + many-to-many relationship to your user model. +- You probably also want an authorization system to restrict who can issue invitations to particular resources -A example user-to-organization system you might be familiar with: Basecamp's concepts of accounts and +A example user-to-organization system you might be familiar with: Basecamp's concepts of accounts and projects (invitables) and users. - ## Install To get started, add Invitation to your `Gemfile` and run `bundle install` to install it: @@ -51,14 +48,13 @@ Then run the invitation install generator: rails generate invitation:install ``` -If your user model is not User, you can optionally specify one: `rails generate invitation:install --model Profile`. +If your user model is not User, you can optionally specify one: `rails generate invitation:install --model Profile`. The install generator does the following: -* Add an initializer at `config/initializers/invitation.rb`, see [Configure](#configure) below. -* Insert `include Invitation::User` into your `User` model. -* Create a migration for the Invite class. - +- Add an initializer at `config/initializers/invitation.rb`, see [Configure](#configure) below. +- Insert `include Invitation::User` into your `User` model. +- Create a migration for the Invite class. Then run the migration that Invitation just generated. @@ -66,7 +62,6 @@ Then run the migration that Invitation just generated. rake db:migrate ``` - ## Configure Override any of these defaults in your application `config/initializers/invitation.rb`. @@ -74,7 +69,7 @@ Override any of these defaults in your application `config/initializers/invitati ```ruby Invitation.configure do |config| config.user_model = '::User' - config.user_registration_url = ->(params) { Rails.application.routes.url_helpers.sign_up_url(params) } + config.user_registration_url = ->(params) { Rails.application.routes.url_helpers.sign_up_url(params) } config.mailer_sender = 'reply@example.com' config.routes = true config.case_sensitive_email = true @@ -83,16 +78,16 @@ end Configuration parameters are described in detail here: [configuration] - ### Invitable You'll need to configure one or more model classes as `invitables`. Invitables are resources or organizations that can be joined with an invite. -An `invitable` must have some sort of name for Invitation to use in views and mailers. An invitable needs to +An `invitable` must have some sort of name for Invitation to use in views and mailers. An invitable needs to call a class method, `invitable`, with one of the following options: -* `named: "String"` -* `named_by: :some_method_name`. + +- `named: "String"` +- `named_by: :some_method_name`. Example: a Company model that users can be invited to join. The companies are identified in invitation emails by their `name` attribute: @@ -103,20 +98,21 @@ class Company < ActiveRecord::Base end ``` - ### User Registration Controller Your user registration controller must `include Invitation::UserRegistration`. You'll want to invoke `set_invite_token` before you execute your `new` action, and `process_invite_token` after your `create` action. If you're using [Authenticate](https://github.com/tomichj/authenticate), for example: + ```ruby class UsersController < Authenticate::UsersController include Invitation::UserRegistration before_action :set_invite_token, only: [:new] after_action :process_invite_token, only: [:create] end -``` +``` + To pass the invite token on signup, add `invite_token` as a hidden field in your signup form. ## Usage @@ -125,26 +121,26 @@ Invitation adds routes to create invitations (GET new_invite and POST invites). Invitation and set up an invitable, add a link to new_invite, specifying the the invitable id and type in the link: ```erb - <%= link_to 'invite a friend', + <%= link_to 'invite a friend', new_invite_path(invite: { invitable_id: account.id, invitable_type: 'Account' } ) %> ``` -Invitation includes a simple `invitations#new` view which accepts an email address for a user to invite. +Invitation includes a simple `invitations#new` view which accepts an email address for a user to invite. When the form is submitted, [invites#create](app/controllers/invitation/invites_controller.rb) will create an [invite](app/models/invite.rb) to track the invitation. An email is then sent: -* a new user is emailed a link to your user registration page as set in [configuration], with a secure -invitation link that will be used to 'claim' the invitation when the new user registers - -* an existing user is emailed a notification to tell them that they've been added to the resource +- a new user is emailed a link to your user registration page as set in [configuration], with a secure + invitation link that will be used to 'claim' the invitation when the new user registers +- an existing user is emailed a notification to tell them that they've been added to the resource ### JSON Invitation You can send a JSON request to [invites#create](app/controllers/invitation/invites_controller.rb). -* request: +- request: + ```javascript invite: { @@ -165,37 +161,40 @@ invite: } ``` -* response: +- response: + ```javascript { - "id": Number, - "email": String, - "sender_id": Number, - "recipient_id": Number (optional), - "invitable_id": Number, - "invitable_type": String, + "id": Number, + "email": String, + "sender_id": Number, + "recipient_id": Number (optional), + "invitable_id": Number, + "invitable_type": String, } ``` or, with multiple emails requested, an array of responses: ```javascript -[{ - "id": Number, - "email": String, - "sender_id": Number, - "recipient_id": Number (optional), - "invitable_id": Number, - "invitable_type": String, -}, -{ - "id": Number, - "email": String, - "sender_id": Number, - "recipient_id": Number (optional), - "invitable_id": Number, - "invitable_type": String, -}] +[ + { + id: Number, + email: String, + sender_id: Number, + recipient_id: Number(optional), + invitable_id: Number, + invitable_type: String, + }, + { + id: Number, + email: String, + sender_id: Number, + recipient_id: Number(optional), + invitable_id: Number, + invitable_type: String, + }, +]; ``` ## Security @@ -207,8 +206,9 @@ Most implementations will require extending the `InvitationsController`. [See be about extending InvitationController. A common use case: -* every invitable resource has authorization requirements exposed via a method, we'll call it `can_invite?(user)` -* the current user must be authorized before issuing invitations + +- every invitable resource has authorization requirements exposed via a method, we'll call it `can_invite?(user)` +- the current user must be authorized before issuing invitations To implement: extend `InvitesController` and add a before_action to authorize access to the resource or resources. A real implementation would probably do something more than just `raise 'unauthorized'`. @@ -231,10 +231,8 @@ class InvitesController < Invitation::InvitesController end ``` - ## Overriding Invitation - ### Views You can quickly get started with a rails application using the built-in views. See [app/views](/app/views) for @@ -247,7 +245,6 @@ You can use the Invitation view generator to copy the default views and translat $ rails generate invitation:views ``` - ### Routes Invitation adds routes to your application. See [config/routes.rb](/config/routes.rb) for the default routes. @@ -271,7 +268,6 @@ will also switch off the routes by setting `config.routes = false` in your [conf $ rails generate invitation:routes ``` - ### Controllers You can customize the `invites_controller.rb` and the `invites_mailer.rb`. See [app/controllers](/app/controllers) @@ -279,7 +275,7 @@ for the controller, and [app/mailers](/app/mailers) for the mailer. To override `invites_controller.rb`, subclass the controller and update your routes to point to your implementation. -* subclass the controller: +- subclass the controller: ```ruby # app/controllers/invites_controller.rb @@ -292,12 +288,12 @@ class InvitesController < Invitation::InvitesController end ``` -* update your routes to use your new controller. +- update your routes to use your new controller. Start by dumping a copy of Invitation's routes to your `config/routes.rb`: ```sh -$ rails generate invitation:routes +$ rails generate invitation:routes ``` Now update `config/routes.rb`, changing the controller entry so it now points to your `invites` controller instead @@ -307,21 +303,20 @@ of `invitation/invites`: resources :invites, controller: 'invites', only: [:new, :create] ``` -You can also use the Invitation controller generator to copy the default controller and mailer into +You can also use the Invitation controller generator to copy the default controller and mailer into your application if you would prefer to more heavily modify the controller. ```sh $ rails generate invitation:controllers ``` - ### Layout Invitation uses your application's default layout. If you would like to change the layout Invitation uses when rendering views, you can either deploy copies of the controllers and customize them, or you can specify the layout in an initializer. This should be done in a to_prepare callback in `config/application.rb` because it's executed once in production and before each request in development. - + You can specify the layout per-controller: ```ruby @@ -330,7 +325,6 @@ config.to_prepare do end ``` - ### Translations All flash messages and email subject lines are stored in [i18n translations](http://guides.rubyonrails.org/i18n.html). @@ -338,39 +332,39 @@ Override them like any other i18n translation. See [config/locales/invitation.en.yml](/config/locales/invitation.en.yml) for the default messages. - ## Thanks This gem was inspired by and draws heavily from: -* https://gist.github.com/jlegosama/9026919 + +- https://gist.github.com/jlegosama/9026919 With additional inspiration from: -* https://github.com/scambra/devise_invitable + +- https://github.com/scambra/devise_invitable ## Contributors Many thanks to: -* [augustocbx](https://github.com/augustocbx) added pt-BR locale file and fixed an init bug -* [vincentwoo](https://github.com/vincentwoo/) raising the security bar, & bumping Invitation to rails 5.1 -* [conarro](https://github.com/conarro) added case_sensitive_email configuration option -* [itkin](https://github.com/itkin) bugfix, stringified configuration.user_model -* [thesubr00t](https://github.com/thesubr00t) made recipient association optional for rails 5+ +- [augustocbx](https://github.com/augustocbx) added pt-BR locale file and fixed an init bug +- [vincentwoo](https://github.com/vincentwoo/) raising the security bar, & bumping Invitation to rails 5.1 +- [conarro](https://github.com/conarro) added case_sensitive_email configuration option +- [itkin](https://github.com/itkin) bugfix, stringified configuration.user_model +- [thesubr00t](https://github.com/thesubr00t) made recipient association optional for rails 5+ ## Future changes -* accepted flag, so we can scope invites by accepted vs not yet accepted -* expiration date - invites expire, scope expired by not expired -* move all view text to locale -* issue many invitations at once? -* dynamic user name lookup? requires JS, CSS -* add JS support to invites#create +- accepted flag, so we can scope invites by accepted vs not yet accepted +- expiration date - invites expire, scope expired by not expired +- move all view text to locale +- issue many invitations at once? +- dynamic user name lookup? requires JS, CSS +- add JS support to invites#create ## License This project rocks and uses MIT-LICENSE. - [configuration]: lib/invitation/configuration.rb [CHANGELOG]: CHANGELOG.md [GitHub Issues]: https://github.com/tomichj/invitation/issues diff --git a/invitation.gemspec b/invitation.gemspec index eadc5a3..b29eccb 100644 --- a/invitation.gemspec +++ b/invitation.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'database_cleaner' s.add_development_dependency 'timecop' s.add_development_dependency 'appraisal' - s.add_development_dependency 'bundler' + s.add_development_dependency 'bundler', '~> 2.2.32' s.add_development_dependency 'rake' s.add_development_dependency 'byebug' diff --git a/test_results/rspec.xml b/test_results/rspec.xml new file mode 100644 index 0000000..9fc3c3b --- /dev/null +++ b/test_results/rspec.xml @@ -0,0 +1,9 @@ + +Randomized with seed 6387 +................................................. + +Finished in 0.95816 seconds (files took 4.23 seconds to load) +49 examples, 0 failures + +Randomized with seed 6387 + From b5da384ee54870fc4a12f27f9db33040beb21c9b Mon Sep 17 00:00:00 2001 From: Matt Fitz-Henry <1731764+mfitzhenry@users.noreply.github.com> Date: Fri, 28 Apr 2023 12:32:38 -0400 Subject: [PATCH 6/7] remove byebug history and add to git ignore (#9) Co-authored-by: mfitzhenry --- .byebug_history | 16 ---------------- .gitignore | 2 ++ 2 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 .byebug_history diff --git a/.byebug_history b/.byebug_history deleted file mode 100644 index 57a5daa..0000000 --- a/.byebug_history +++ /dev/null @@ -1,16 +0,0 @@ -exit -post path, **{:params=>{:invite=>{:invitable_id=>1, :invitable_type=>"Company", :emails=>["gug@gug.com", "gug2@gug.com"]}}, :headers=>{"ACCEPT"=>"application/json"}} -post path, {:params=>{:invite=>{:invitable_id=>1, :invitable_type=>"Company", :emails=>["gug@gug.com", "gug2@gug.com"]}}, :headers=>{"ACCEPT"=>"application/json"}} -post path -args -path -exit -post path, *args -exit -continue -:q -*args -args -path -post path, **args -post path, *args diff --git a/.gitignore b/.gitignore index 8726991..e12ab61 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ spec/dummy/tmp/ spec/dummy/.sass-cache /.idea/ *emfile.lock +.byebug_history +``` From b638fcbf584f197c1d4faeac4e898b87a5db6c18 Mon Sep 17 00:00:00 2001 From: Matt Fitz-Henry <1731764+mfitzhenry@users.noreply.github.com> Date: Thu, 4 May 2023 14:26:35 -0400 Subject: [PATCH 7/7] Update Flaky Specs & Update Rails Version to 7.0.4.3 (#10) * updated flaky specs and update rails version to 7.0.4.3 * update bundler version --------- Co-authored-by: mfitzhenry --- invitation.gemspec | 24 +++++++++---------- spec/dummy/config/application.rb | 1 + spec/dummy/config/environments/development.rb | 2 +- spec/dummy/config/environments/production.rb | 2 +- spec/dummy/config/environments/test.rb | 2 +- spec/dummy/config/initializers/assets.rb | 11 --------- spec/requests/invite_one_user_spec.rb | 16 ++++++------- spec/requests/invite_two_users_spec.rb | 10 ++++---- spec/spec_helper.rb | 8 +++---- 9 files changed, 31 insertions(+), 45 deletions(-) delete mode 100644 spec/dummy/config/initializers/assets.rb diff --git a/invitation.gemspec b/invitation.gemspec index b29eccb..959201a 100644 --- a/invitation.gemspec +++ b/invitation.gemspec @@ -1,5 +1,5 @@ require 'English' -$LOAD_PATH.push File.expand_path('../lib', __FILE__) +$LOAD_PATH.push File.expand_path('lib', __dir__) require 'invitation/version' require 'date' @@ -17,21 +17,21 @@ Gem::Specification.new do |s| s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.rdoc'] s.test_files = Dir['test/**/*'] - s.add_dependency 'rails', '~> 6.0.2', '>= 6.0.2' - + s.add_dependency 'rails', '~> 7.0.4.3', '< 8.0' + + s.add_development_dependency 'appraisal' + s.add_development_dependency 'bundler', '~> 2.4', '>= 2.4.12' + s.add_development_dependency 'byebug' + s.add_development_dependency 'capybara' + s.add_development_dependency 'database_cleaner' s.add_development_dependency 'factory_girl', '~> 4.8' - s.add_development_dependency 'rspec-rails' - s.add_development_dependency 'rspec-mocks' s.add_development_dependency 'pry', '~> 0.10' - s.add_development_dependency 'sqlite3' + s.add_development_dependency 'rake' + s.add_development_dependency 'rspec-mocks' + s.add_development_dependency 'rspec-rails' s.add_development_dependency 'shoulda-matchers' - s.add_development_dependency 'capybara' - s.add_development_dependency 'database_cleaner' + s.add_development_dependency 'sqlite3' s.add_development_dependency 'timecop' - s.add_development_dependency 'appraisal' - s.add_development_dependency 'bundler', '~> 2.2.32' - s.add_development_dependency 'rake' - s.add_development_dependency 'byebug' s.required_ruby_version = Gem::Requirement.new('>= 2.0') end diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb index 04af407..3f0d39b 100644 --- a/spec/dummy/config/application.rb +++ b/spec/dummy/config/application.rb @@ -10,6 +10,7 @@ class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. + config.load_defaults 7.0 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. diff --git a/spec/dummy/config/environments/development.rb b/spec/dummy/config/environments/development.rb index f309044..5f0e0f8 100644 --- a/spec/dummy/config/environments/development.rb +++ b/spec/dummy/config/environments/development.rb @@ -35,7 +35,7 @@ # Checks for improperly declared sprockets dependencies. # Raises helpful error messages. config.assets.raise_runtime_errors = true - config.assets.precompile = ["manifest.js"] + # config.assets.precompile = ["manifest.js"] # Raises error for missing translations # config.action_view.raise_on_missing_translations = true end diff --git a/spec/dummy/config/environments/production.rb b/spec/dummy/config/environments/production.rb index d2e97e6..a7b9d1f 100644 --- a/spec/dummy/config/environments/production.rb +++ b/spec/dummy/config/environments/production.rb @@ -35,7 +35,7 @@ # yet still be able to expire them through the digest params. config.assets.digest = true - config.assets.precompile = ["manifest.js"] + # config.assets.precompile = ["manifest.js"] # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb diff --git a/spec/dummy/config/environments/test.rb b/spec/dummy/config/environments/test.rb index cd69adf..2ed8783 100644 --- a/spec/dummy/config/environments/test.rb +++ b/spec/dummy/config/environments/test.rb @@ -6,7 +6,7 @@ # your test database is "scratch space" for the test suite and is wiped # and recreated between test runs. Don't rely on the data there! config.cache_classes = true - config.assets.precompile = ["manifest.js"] + # config.assets.precompile = ["manifest.js"] # Do not eager load code on boot. This avoids loading your whole application # just for the purpose of running a single test. If you are using a tool that diff --git a/spec/dummy/config/initializers/assets.rb b/spec/dummy/config/initializers/assets.rb deleted file mode 100644 index 01ef3e6..0000000 --- a/spec/dummy/config/initializers/assets.rb +++ /dev/null @@ -1,11 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path -# Rails.application.config.assets.paths << Emoji.images_path - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -# Rails.application.config.assets.precompile += %w( search.js ) diff --git a/spec/requests/invite_one_user_spec.rb b/spec/requests/invite_one_user_spec.rb index dc50466..0b6a7ca 100644 --- a/spec/requests/invite_one_user_spec.rb +++ b/spec/requests/invite_one_user_spec.rb @@ -9,18 +9,18 @@ context 'invite a new user' do let(:email) { 'gug@gug.com' } - subject { + subject do do_post invites_path, - params: { invite: { invitable_id: @company.id, - invitable_type: @company.class.name, - email: email } }, - **json_headers() - } + params: { invite: { invitable_id: @company.id, + invitable_type: @company.class.name, + email: email } }, + **json_headers + end it 'returns json' do sign_in_with @user subject - expect(response.content_type).to eq('application/json') + expect(response.content_type).to eq('application/json; charset=utf-8') end it 'returns success' do @@ -45,7 +45,5 @@ expect(invite['invitable_id']).to eq @company.id expect(invite['invitable_type']).to eq @company.class.name end - end end - diff --git a/spec/requests/invite_two_users_spec.rb b/spec/requests/invite_two_users_spec.rb index b721e38..a2d1b38 100644 --- a/spec/requests/invite_two_users_spec.rb +++ b/spec/requests/invite_two_users_spec.rb @@ -12,16 +12,16 @@ let(:email2) { 'gug2@gug.com' } subject do do_post invites_path, - params: { invite: { invitable_id: @company.id, - invitable_type: @company.class.name, - emails: [email1, email2] } }, - **json_headers() + params: { invite: { invitable_id: @company.id, + invitable_type: @company.class.name, + emails: [email1, email2] } }, + **json_headers end it 'returns json' do sign_in_with @user subject - expect(response.content_type).to eq('application/json') + expect(response.content_type).to eq('application/json; charset=utf-8') end it 'responds with success' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 743daa7..fda0f1b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,12 +1,10 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)) ENV['RAILS_ENV'] ||= 'test' -require File.expand_path('../dummy/config/environment.rb', __FILE__) +require File.expand_path('dummy/config/environment.rb', __dir__) # nasty hacky catch of environment data wiped out by tests run in rails 4 via appraisal -if ActiveRecord::VERSION::STRING >= '5.0' - system('bin/rails dummy:db:environment:set RAILS_ENV=test') -end +system('bin/rails dummy:db:environment:set RAILS_ENV=test') if ActiveRecord::VERSION::STRING >= '5.0' require 'rspec/rails' require 'shoulda-matchers' @@ -68,6 +66,6 @@ def do_post(path, *args) # byebug post path, **args.first else - post path, *(args.collect{|i| i.values}.flatten) + post path, *args.collect { |i| i.values }.flatten end end