teamable 0.0.1.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 92e14701652541974d41ad1b2f37bb99ae1ba17e2932da58ff40a60164e40be7
4
+ data.tar.gz: cac90b085b07a4b5b616168a6d4a33a60cf904f357a563c96fe8fbd699dfda1f
5
+ SHA512:
6
+ metadata.gz: a640b4d0df029a0cba8044a7ff47fea7bc56ab9538ae19ffd93a853b56a6b7bdd2001ccca4ca0c0028bd21199c71484ad3b7623b8105f1a3dcd92b2d85cee84e
7
+ data.tar.gz: 6ab50670c95b7451d6c35394302c812307f52f0678638eeced97527c6f8ae175c4e407a53fae297dee8135b7f0091c78cf5c3d55004811504b3f01399c8a3bf5
data/LICENSE.md ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2021 The Teamable developers.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ Teamable
2
+ ========
3
+ [![RuboCop Github Action](https://github.com/kiqr/teamable/actions/workflows/rubocop.yml/badge.svg)](https://github.com/kiqr/teamable/actions/workflows/rubocop.yml)
4
+ [![RSpec](https://github.com/kiqr/teamable/actions/workflows/rspec.yml/badge.svg)](https://github.com/kiqr/teamable/actions/workflows/rspec.yml)
5
+ [![codecov](https://codecov.io/gh/kiqr/teamable/branch/main/graph/badge.svg?token=UZMGXQKJRL)](https://codecov.io/gh/kiqr/teamable)
6
+ [![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE.md)
7
+
8
+ Teamable enables role based multi-user accounts (teams/organizations). Teams are represented by the `Account` model. An account can have multiple users associated with it and a user can have multiple accounts. You can think of accounts as a common name for **teams** or **organizations**.
9
+
10
+ Every user can have one personal account they're admin of, which allows our code to work the same way whether we're adding resources privately for a user or for a team/organization. When a user signs in for the first time, they will be redirected to `new_account_path` and prompted to create their first personal or team account.
11
+
12
+ ### Team resources
13
+
14
+ **We recommend** you to scope all user resources under accounts instead of users. For example, in an application containing projects, the `Project` model could be declared like this:
15
+
16
+ ```ruby
17
+ class Project < ApplicationRecord
18
+ # belongs_to :user <- don't do this.
19
+ belongs_to :account # personal or team account.
20
+ end
21
+ ```
22
+
23
+ or if you're combining teamable with [acts_as_tenant](https://github.com/ErwinM/acts_as_tenant):
24
+
25
+ ```ruby
26
+ class Project < ApplicationRecord
27
+ acts_as_tenant(:account)
28
+ end
29
+ ```
30
+
31
+ ### Switching between accounts
32
+
33
+ When building your customized account switcher, redirect your user to `switch_account_path`.
34
+
35
+ ```ruby
36
+ current_user.accounts.each do |account|
37
+ button_to account.name, switch_account_path(account), method: :patch
38
+ end
39
+ ```
40
+
41
+ Installation
42
+ ------------
43
+
44
+ Add the following line to Gemfile:
45
+
46
+ ```ruby
47
+ gem "teamable", "~> 1.0"
48
+ ```
49
+
50
+ and run `bundle install` from your terminal to install it.
51
+
52
+ Getting started
53
+ ---------------
54
+
55
+ **Teamable** expects a `User` model to be present and authenticatable before being installed. It also expects your application to respond properly to `current_user` and `user_signed_in?`. An authentication gem like [Authenticatable](https://github.com/kiqr/authenticatable) is recommended, but this gem should also work great with [Devise](https://github.com/heartcombo/devise), other gems or a custom solution.
56
+
57
+ #### Run the install generator
58
+
59
+ Our next step is to run the install generator. To run the installer run the following command:
60
+
61
+ ```console
62
+ $ rails g teamable:install
63
+ ```
64
+
65
+ This will generate two models, `Member` & `Account` with configuration for teamable, migration files and `teamable` routes. It will also inject the `Teamable::Models::User` concern into your `User`-model. The output should be something similar to:
66
+
67
+ ```console
68
+ foo@bar:~$ rails g teamable:install
69
+ insert app/models/user.rb
70
+ create app/models/account.rb
71
+ create app/models/member.rb
72
+ create db/migrate/20211010111722_teamable_create_accounts.rb
73
+ create db/migrate/20211010111723_teamable_create_members.rb
74
+ route teamable "organizations"
75
+ ```
76
+
77
+ Contributing
78
+ ------------
79
+ If you are interested in reporting/fixing issues and contributing directly to the code base, please see [CONTRIBUTING.md](CONTRIBUTING.md) for more information on what we're looking for and how to get started.
80
+
81
+ Versioning
82
+ ----------
83
+ This library aims to adhere to [Semantic Versioning 2.0.0](http://semver.org/). Violations
84
+ of this scheme should be reported as bugs. Specifically, if a minor or patch
85
+ version is released that breaks backward compatibility, that version should be
86
+ immediately yanked and/or a new version should be immediately released that
87
+ restores compatibility. Breaking changes to the public API will only be
88
+ introduced with new major versions. As a result of this policy, you can (and
89
+ should) specify a dependency on this gem using the [Pessimistic Version
90
+ Constraint](http://guides.rubygems.org/patterns/#pessimistic-version-constraint) with two digits of precision. For example:
91
+
92
+ ```ruby
93
+ gem "teamable", "~> 1.0"
94
+ ```
95
+
96
+ License
97
+ -------
98
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ class AccountsController < TeamableController
5
+ skip_before_action :authenticate_account!, only: %i[new create switch]
6
+
7
+ # GET /account/new
8
+ def new
9
+ @account = current_user.accounts.new
10
+ end
11
+
12
+ # POST /account
13
+ def create
14
+ @account = current_user.accounts.build(account_params)
15
+ @account.members.build(user: current_user)
16
+
17
+ if @account.save
18
+ update_teamable_session_id!(@account.id)
19
+ redirect_to root_path, notice: "success"
20
+ else
21
+ render :new, status: :unprocessable_entity
22
+ end
23
+ end
24
+
25
+ def switch
26
+ account = current_user.accounts.find(params[:id])
27
+ update_teamable_session_id!(account.id)
28
+ redirect_to after_account_switched_path
29
+ end
30
+
31
+ private
32
+
33
+ # Only allow a list of trusted parameters through.
34
+ def account_params
35
+ params.require(:account).permit(:name, :billing_email)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ class SetupController < TeamableController
5
+ # GET /account/setup
6
+ def new
7
+ @account = current_user.accounts.new
8
+ end
9
+
10
+ # POST /account/setup
11
+ def create
12
+ @account = current_user.accounts.build(account_params)
13
+ @account.members.build(user: current_user)
14
+
15
+ if @account.save
16
+ update_teamable_session_id!(@account.id)
17
+ redirect_to root_path, notice: "success"
18
+ else
19
+ render :new, status: :unprocessable_entity
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # Only allow a list of trusted parameters through.
26
+ def account_params
27
+ params.require(:account).permit(:name, :billing_email)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TeamableController < Teamable.config.parent_controller.constantize
4
+ before_action :authenticate_user!
5
+ before_action :authenticate_account!
6
+ end
@@ -0,0 +1,7 @@
1
+ <%= form_for @account, url: account_path, method: :post do |f| %>
2
+ <%= render 'teamable/shared/errors', resource: @account %>
3
+
4
+ <%= f.text_field :name, placeholder: "Account name" %><br>
5
+ <%= f.text_field :billing_email, placeholder: "Billing email" %><br>
6
+ <%= f.submit "Create account" %>
7
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <h2>New account</h2>
2
+ <p>Create a new personal or team account</p>
3
+
4
+ <%= render "teamable/shared/flash_messages" %>
5
+
6
+ <%= render "form" %>
@@ -0,0 +1,7 @@
1
+ <%= form_for @account, url: setup_account_path, method: :post do |f| %>
2
+ <%= render 'teamable/shared/errors', resource: @account %>
3
+
4
+ <%= f.text_field :name, placeholder: "Account name" %><br>
5
+ <%= f.text_field :billing_email, placeholder: "Billing email" %><br>
6
+ <%= f.submit "Create account" %>
7
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <h2>Setup your account</h2>
2
+ <p>Finish the registration by setting up your account</p>
3
+
4
+ <%= render "teamable/shared/flash_messages" %>
5
+
6
+ <%= render "form" %>
@@ -0,0 +1,7 @@
1
+ <% if resource.errors.any? %>
2
+ <ul>
3
+ <% resource.errors.full_messages.each do |message| %>
4
+ <li><%= message %></li>
5
+ <% end %>
6
+ </ul>
7
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% flash.each do |key, value| %>
2
+ <%= content_tag :div, value, class: "flash #{key}" %>
3
+ <% end %>
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+ require "rails/generators/active_model"
5
+ require "rails/generators/active_record/migration"
6
+
7
+ module Teamable
8
+ class InvalidUserModel < StandardError; end
9
+
10
+ class InstallGenerator < Rails::Generators::Base
11
+ include ActiveRecord::Generators::Migration
12
+ source_root File.expand_path("templates", __dir__)
13
+
14
+ def check_install_requirements
15
+ return if user_model_exists?
16
+
17
+ say "Teamable requires a User model to be present before you continue with the install", :red
18
+ say "Learn more at: https://github.com/kiqr/teamable#getting-started", :red
19
+ raise Teamable::InvalidUserModel, "Missing file: #{user_model_path}"
20
+ end
21
+
22
+ def inject_into_user_model
23
+ inject_into_class user_model_path, "User" do
24
+ " include Teamable::Models::User\n"
25
+ end
26
+ end
27
+
28
+ def generate_models
29
+ template "models/account.tt", "app/models/account.rb"
30
+ template "models/member.tt", "app/models/member.rb"
31
+ end
32
+
33
+ def generate_migrations
34
+ migration_template "migrations/account.tt", "#{db_migrate_path}/teamable_create_accounts.rb"
35
+ migration_template "migrations/member.tt", "#{db_migrate_path}/teamable_create_members.rb"
36
+ end
37
+
38
+ def add_route
39
+ route 'teamable "account"'
40
+ end
41
+
42
+ private
43
+
44
+ def user_model_exists?
45
+ File.exist?(user_model_path)
46
+ end
47
+
48
+ def user_model_path
49
+ File.join(destination_root, "app/models/user.rb")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TeamableCreateAccounts < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :accounts do |t|
6
+ t.string :name, null: false
7
+ t.string :billing_email, null: false
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TeamableCreateMembers < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :members do |t|
6
+ t.references :user, foreign_key: { to_table: :users }
7
+ t.references :account, foreign_key: { to_table: :accounts }
8
+ t.string :invitee_email
9
+ t.string :role, nullable: false
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Account < ApplicationRecord
4
+ include Teamable::Models::Account
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Member < ApplicationRecord
4
+ include Teamable::Models::Member
5
+
6
+ AVAILABLE_ROLES = %w[member admin].freeze
7
+ FIRST_USER_ROLE = "admin" # Default role for the user who created the account.
8
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Teamable
6
+ module Controllers
7
+ module CurrentAccountHelper
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ before_action :load_current_account
12
+
13
+ if respond_to?(:helper_method)
14
+ helpers = %w[current_account account_selected?]
15
+ helper_method(*helpers)
16
+ end
17
+
18
+ rescue_from Teamable::MissingAccountError do
19
+ redirect_to setup_account_path
20
+ end
21
+ end
22
+
23
+ # Raise Teamable::MissingAccountError unless account_selected? is true.
24
+ def authenticate_account!
25
+ return unless user_signed_in?
26
+ return if teamable_controller?
27
+
28
+ raise Teamable::MissingAccountError unless account_selected?
29
+ end
30
+
31
+ # Access current team.
32
+ def current_account
33
+ @current_account ||= Teamable::Current.account
34
+ end
35
+
36
+ # Check if a team has been selected
37
+ def account_selected?
38
+ !!current_account
39
+ end
40
+
41
+ # Store an account id in a session variable.
42
+ # Warning: this does not validate that the current_user
43
+ # actually is a member of the specified account.
44
+ def update_teamable_session_id!(account_id)
45
+ session[:teamable_account_id] = account_id
46
+ end
47
+
48
+ private
49
+
50
+ # Load current account from Account model and store it
51
+ # in the Teamable::Current singleton.
52
+ def load_current_account
53
+ return unless user_signed_in?
54
+
55
+ Teamable::Current.account ||= account_from_session || fallback_account || nil
56
+ end
57
+
58
+ # Try to load current account using session[:teamable_account_id]
59
+ def account_from_session
60
+ return if session[:teamable_account_id].blank?
61
+
62
+ current_user.accounts.find_by(id: session[:teamable_account_id])
63
+ end
64
+
65
+ # Finds last joined account if the user have any associated accounts.
66
+ def fallback_account
67
+ memberships = current_user.members
68
+ return nil if memberships.length.zero?
69
+
70
+ memberships.last.account # Return last joined account.
71
+ end
72
+
73
+ # Check if the current controller is a TeamableController
74
+ def teamable_controller?
75
+ is_a?(::TeamableController)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Teamable
6
+ module Controllers
7
+ module UrlHelpers
8
+ extend ActiveSupport::Concern
9
+
10
+ # Method references in Teamable::AccountsController#switch to redirect a user
11
+ # after they've switched to another account. You can override it in your
12
+ # ApplicationController to provide a custom url.
13
+ def after_account_switched_path
14
+ root_path
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ # Global class that holds the current user's selected team.
5
+ class Current < ActiveSupport::CurrentAttributes
6
+ attribute :account
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ # Makes Teamable available to Rails as an Engine.
5
+ class Engine < ::Rails::Engine
6
+ initializer "teamable.setup" do
7
+ # Make teamable helpers available in controllers.
8
+ ActionController::Base.include Teamable::Controllers::CurrentAccountHelper
9
+ ActionController::Base.include Teamable::Controllers::UrlHelpers
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ class MissingAccountError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ module Models
5
+ module Account
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ has_many :members
10
+ has_many :users, through: :members
11
+
12
+ validates :name, presence: true
13
+ validates :billing_email, presence: true, uniqueness: true
14
+ validates_format_of :billing_email, with: Teamable.config.email_regexp
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ module Models
5
+ module Member
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ belongs_to :user
10
+ belongs_to :account
11
+
12
+ before_validation :set_owner_default_role, on: :create
13
+ validates :role, presence: true
14
+ validates_format_of :invitee_email, with: Teamable.config.email_regexp, allow_blank: true
15
+ validates_inclusion_of :role, in: proc { ::Member::AVAILABLE_ROLES }
16
+ end
17
+
18
+ # Temporary store an invitee's email for validation.
19
+ attr_accessor :invitee_email
20
+
21
+ def role_name
22
+ role.capitalize
23
+ end
24
+
25
+ def role?(input)
26
+ role.include? input.to_s
27
+ end
28
+
29
+ private
30
+
31
+ def set_owner_default_role
32
+ self.role = ::Member::FIRST_USER_ROLE if role.nil?
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ module Models
5
+ module User
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ has_many :members
10
+ has_many :accounts, through: :members
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Routing
5
+ class Mapper
6
+ def teamable(team_label, options = {}, &block)
7
+ options[:path] ||= team_label.to_s
8
+
9
+ teamable_scope options do
10
+ teamable_accounts(options)
11
+ yield block if block_given?
12
+ end
13
+ end
14
+
15
+ protected
16
+
17
+ def teamable_scope(options, &block)
18
+ scope options[:path], module: "teamable", &block
19
+ end
20
+
21
+ def teamable_accounts(_options)
22
+ resource :account, only: %i[new create], path: "" do
23
+ get :setup, to: "setup#new"
24
+ post :setup, to: "setup#create"
25
+ patch ":id/switch", to: "accounts#switch", as: :switch
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Teamable
4
+ VERSION = "0.0.1.alpha"
5
+ end
data/lib/teamable.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "teamable/current"
4
+ require "teamable/routes"
5
+ require "teamable/version"
6
+
7
+ require "teamable/engine"
8
+
9
+ require "dry-configurable"
10
+
11
+ # Enable role based multi-user accounts (teams/organizations) in Rails.
12
+ module Teamable
13
+ extend Dry::Configurable
14
+
15
+ module Controllers
16
+ autoload :CurrentAccountHelper, "teamable/controllers/current_account_helper"
17
+ autoload :UrlHelpers, "teamable/controllers/url_helpers"
18
+ end
19
+
20
+ module Models
21
+ autoload :Account, "teamable/models/account"
22
+ autoload :Member, "teamable/models/member"
23
+ autoload :User, "teamable/models/user"
24
+ end
25
+
26
+ autoload :MissingAccountError, "teamable/errors/missing_account_error"
27
+
28
+ # The controller class that all Teamable controllers will inherit from.
29
+ # Defaults to `ApplicationController`.
30
+ setting :parent_controller, "ApplicationController"
31
+
32
+ # Email regex used to validate email formats. It simply asserts that
33
+ # one (and only one) @ exists in the given string. This is mainly
34
+ # to give user feedback and not to assert the e-mail validity.
35
+ setting :email_regexp, /\A[^@\s]+@[^@\s]+\z/
36
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: teamable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha
5
+ platform: ruby
6
+ authors:
7
+ - Rasmus Kjellberg
8
+ - KIQR
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2021-10-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dry-configurable
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.11.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.11.0
28
+ description:
29
+ email: hello@kiqr.dev
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE.md
35
+ - README.md
36
+ - app/controllers/teamable/accounts_controller.rb
37
+ - app/controllers/teamable/setup_controller.rb
38
+ - app/controllers/teamable_controller.rb
39
+ - app/views/teamable/accounts/_form.html.erb
40
+ - app/views/teamable/accounts/new.html.erb
41
+ - app/views/teamable/setup/_form.html.erb
42
+ - app/views/teamable/setup/new.html.erb
43
+ - app/views/teamable/shared/_errors.html.erb
44
+ - app/views/teamable/shared/_flash_messages.html.erb
45
+ - lib/generators/teamable/install_generator.rb
46
+ - lib/generators/teamable/templates/migrations/account.tt
47
+ - lib/generators/teamable/templates/migrations/member.tt
48
+ - lib/generators/teamable/templates/models/account.tt
49
+ - lib/generators/teamable/templates/models/member.tt
50
+ - lib/teamable.rb
51
+ - lib/teamable/controllers/current_account_helper.rb
52
+ - lib/teamable/controllers/url_helpers.rb
53
+ - lib/teamable/current.rb
54
+ - lib/teamable/engine.rb
55
+ - lib/teamable/errors/missing_account_error.rb
56
+ - lib/teamable/models/account.rb
57
+ - lib/teamable/models/member.rb
58
+ - lib/teamable/models/user.rb
59
+ - lib/teamable/routes.rb
60
+ - lib/teamable/version.rb
61
+ homepage: https://github.com/kiqr/teamable
62
+ licenses:
63
+ - MIT
64
+ metadata:
65
+ bug_tracker_uri: https://github.com/kiqr/teamable/issues
66
+ documentation_uri: https://github.com/kiqr/teamable/issues
67
+ source_code_uri: https://github.com/kiqr/teamable
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '2.6'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">"
80
+ - !ruby/object:Gem::Version
81
+ version: 1.3.1
82
+ requirements: []
83
+ rubygems_version: 3.2.26
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Extension to enable teams for Authenticatable
87
+ test_files: []