bullet_train-roles 0.1.0 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c87ff4b45f3eed3dc5e3b2d7e6239160d4f83c3fee07483647b960f10e22ff1
4
- data.tar.gz: 9302b17d61bbf57210ba56edacb75a5b4e4b00e4568ac67fe157fe9872cfcff5
3
+ metadata.gz: f03a796a2c17954ad4c25e313caea68ba0b60b7e8901e7c1acf83d869e100592
4
+ data.tar.gz: 131e6518cefb00c6ea280d4ac4bf492961cb6f45b6d0ef3d695b66de9366d906
5
5
  SHA512:
6
- metadata.gz: f766e94d3a385fa76603f50d462e1606d9fc7038b17855fa5300f227a81160c79fc489ad201e457c8e35ee1a364d27617e8d8b839fd94071bb581dd0b7d91dce
7
- data.tar.gz: cd8e636d008364f3e6169dd67e0839fbb362167ce4cf19fce135d35020a81143573072848f5062683c94fb696a4c69020c83e72f4339733d5e4d212c6cc1e184
6
+ metadata.gz: 7df53d71fb2d393e84a30a5efb1fa9f351781653b40e1cc31e5cf565d4432228b3a78b6924d021908c5d463695dfdeb0dff834a0f1c63a26438774e8159f4438
7
+ data.tar.gz: 56a0c8b76f5105959d828db3f619c863cc917fd3caf1e8dc838151268da68da1bce455ebd3378f68dfa7bf78d804d673df311f6351cfd0a47a2b4f43a6c68c07
data/Gemfile.lock CHANGED
@@ -9,7 +9,7 @@ GIT
9
9
  PATH
10
10
  remote: .
11
11
  specs:
12
- bullet_train-roles (0.1.0)
12
+ bullet_train-roles (0.1.4)
13
13
  active_hash
14
14
  activesupport
15
15
  cancancan
@@ -173,6 +173,7 @@ GEM
173
173
 
174
174
  PLATFORMS
175
175
  arm64-darwin-20
176
+ arm64-darwin-21
176
177
 
177
178
  DEPENDENCIES
178
179
  active_hash!
@@ -187,4 +188,4 @@ DEPENDENCIES
187
188
  standard (~> 1.5.0)
188
189
 
189
190
  BUNDLED WITH
190
- 2.2.15
191
+ 2.3.4
data/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Bullet Train Roles
2
2
 
3
- This Ruby Gem provides a Yaml-based configuration layer on top of CanCanCan's ability file. You can use this configuration file to simplify the definition of the most common types of permissions, while still implementing more complicated permissions in CanCanCan's traditional `app/model/ability.rb`.
3
+ Bullet Train Roles provides a Yaml-based configuration layer on top of [CanCanCan](https://github.com/CanCanCommunity/cancancan). You can use this configuration file to simplify the definition of many common permissions, while still implementing more complicated permissions in CanCanCan's traditional `app/model/ability.rb`.
4
4
 
5
- ## Example Domain Model
5
+ Additionally, Bullet Train Roles makes it trivial to assign the same roles and associated permissions at different levels in your application. For example, you can assign someone administrative privileges at a team level, or only at a project level.
6
+
7
+ Bullet Train Roles was created by [Andrew Culver](http://twitter.com/andrewculver) and [Adam Pallozzi](https://twitter.com/adampallozzi).
8
+
9
+ ## Example Domain Model
6
10
 
7
11
  For the sake of this document, we're going to assume the following example modeling around users and teams:
8
12
 
@@ -11,18 +15,21 @@ For the sake of this document, we're going to assume the following example model
11
15
  - A `Membership` can have zero, one, or many `Role`s assigned.
12
16
  - A `Membership` without a `Role` is just a default team member.
13
17
 
14
- You don't have to name your models the same thing in order to use this Ruby Gem, but it does depend on having a similar structure.
18
+ You don't have to name your models the same thing in order to use this Ruby Gem, but it does depend on having a similar structure.
15
19
 
16
20
  > If you're interested in reading more about how and why Bullet Train implements this structure, you can [read about it on our blog](https://blog.bullettrain.co/teams-should-be-an-mvp-feature/).
17
21
 
18
22
  ## Installation
19
23
 
20
- Add this line to your application's Gemfile:
24
+ Add these lines to your application's Gemfile:
21
25
 
22
26
  ```ruby
27
+ gem "active_hash", github: "bullet-train-co/active_hash"
23
28
  gem "bullet_train-roles"
24
29
  ```
25
30
 
31
+ > We have to link to a specific downstream version of ActiveHash temporarily while working to merge certain fixes and updates upstream.
32
+
26
33
  And then execute the following in your shell:
27
34
 
28
35
  ```
@@ -32,25 +39,24 @@ bundle install
32
39
  Finally, run the installation generator:
33
40
 
34
41
  ```
35
- rails generate bullet_train:roles:install User Membership Team
42
+ rails generate bullet_train:roles:install
36
43
  ```
37
44
 
38
- This will:
45
+ The installer defaults to installing a configuration for `Membership` and `Team`, but it will prompt you so you can specify different models if they differ in your application.
46
+
47
+ The installer will:
39
48
 
40
49
  - stub out a configuration file in `config/models/roles.yml`.
41
50
  - create a database migration to add `role_ids:jsonb` to `Membership`.
42
51
  - add `include Role::Support` to `app/models/membership.rb`.
43
52
  - add a basic `permit` call in `app/models/ability.rb`.
44
53
 
45
- ### Limitations
46
-
47
- The generators currently assume you're using PostgreSQL and `jsonb` will be available when generating a `role_ids` column. If you're using MySQL, you can edit these migrations and use `json` instead, although you won't be able to set a default value and you'll need to take care of this in the model.
48
54
 
49
55
  ## Usage
50
56
 
51
- The provided `Role` model is backed (via [ActiveHash](https://github.com/active-hash/active_hash)) by a Yaml configuration in `config/models/roles.yml`.
57
+ The provided `Role` model is backed by a Yaml configuration in `config/models/roles.yml`.
52
58
 
53
- To help explain this configuration and it's options, we'll provide the following hypothetical example:
59
+ To help explain this configuration and its options, we'll provide the following hypothetical example:
54
60
 
55
61
  ```
56
62
  default:
data/bin/setup CHANGED
@@ -5,4 +5,10 @@ set -vx
5
5
 
6
6
  bundle install
7
7
 
8
+ # We need to cd into the dummy app because it uses a different Rakefile
9
+ cd test/dummy
10
+ bundle install
11
+ rake db:reset
12
+ cd ../..
13
+
8
14
  # Do any other automated setup that you need to do here
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Roles
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.4"
5
5
  end
@@ -5,6 +5,7 @@ require_relative "roles/version"
5
5
  require_relative "../models/role"
6
6
  require_relative "../roles/permit"
7
7
  require_relative "../roles/support"
8
+ require_relative "../roles/user"
8
9
 
9
10
  module Roles
10
11
  class Error < StandardError; end
@@ -31,6 +31,7 @@ module BulletTrain
31
31
  # TODO: follow back Andrew on question about this in Slack
32
32
 
33
33
  add_permit_to_ability_model(top_level_model, associated_model)
34
+ add_concern_to_user
34
35
  end
35
36
 
36
37
  private
@@ -46,7 +47,7 @@ module BulletTrain
46
47
  end
47
48
 
48
49
  def db_adapter
49
- allowed_adapter_types = %w[mysql sqlite postgresql]
50
+ allowed_adapter_types = %w[mysql mysql2 sqlite postgresql]
50
51
 
51
52
  adapter_name = ActiveRecord::Base.connection.adapter_name.downcase
52
53
 
@@ -89,14 +90,6 @@ module BulletTrain
89
90
  File.write(file_location, update_file_content.join)
90
91
  end
91
92
 
92
- def add_default_value_to_migration(file_name, table_name)
93
- file_location = Dir["db/migrate/*_#{file_name}.rb"].last
94
- line_to_match = "add_column :#{table_name.downcase}, :role_ids"
95
- content_to_add = ", default: []\n"
96
-
97
- add_in_file(file_location, line_to_match, content_to_add)
98
- end
99
-
100
93
  def migration_file_exists?(file_name)
101
94
  file_location = Dir["db/migrate/*_#{file_name}.rb"].last
102
95
 
@@ -118,8 +111,6 @@ module BulletTrain
118
111
 
119
112
  generate "migration", "#{migration_file_name} role_ids:#{json_data_type_identifier}"
120
113
 
121
- add_default_value_to_migration(migration_file_name, top_level_model_table_name)
122
-
123
114
  puts("Success 🎉🎉\n\n")
124
115
  end
125
116
 
@@ -160,22 +151,44 @@ module BulletTrain
160
151
  end
161
152
 
162
153
  def add_permit_to_ability_model(top_level_model, associated_model)
163
- # TODO: need to make this more smart e.g.
164
- # 1. should know what parameter is used for user e.g. def initialize(user)
165
- # 2. what if code doesn't include "if user.present?", maybe we need to handle that, consult with Andrew
166
154
  file_location = "app/models/ability.rb"
167
- line_to_match = "if user.present?"
168
- content_to_add = "\n permit user, through: :#{top_level_model.downcase.pluralize}, parent: :#{associated_model.downcase}\n"
169
-
170
- puts("Adding 'permit user, through: :#{top_level_model.downcase}, parent: :#{associated_model.downcase}' to #{associated_model}\n\n")
155
+ line_to_match = "include CanCan::Ability"
156
+ content_to_add = "\n include Roles::Permit\n"
157
+ puts("Adding 'include Roles::Permit' to #{associated_model}\n\n")
171
158
 
172
159
  if line_exists_in_file?(file_location, content_to_add)
173
160
  message = "#{remove_new_lines_and_spaces(content_to_add)} already exists in #{associated_model}!!\n\n"
161
+ line_already_exists(message)
162
+ else
163
+ add_in_file(file_location, line_to_match, content_to_add)
164
+ end
174
165
 
175
- return line_already_exists(message)
166
+ line_to_match = "def initialize(user)"
167
+ content_to_add = "\n permit user, through: :#{top_level_model.downcase.pluralize}, parent: :#{associated_model.downcase} if user.present?\n"
168
+ puts("Adding 'permit' to #{associated_model}\n\n")
169
+
170
+ if line_exists_in_file?(file_location, content_to_add)
171
+ message = "#{remove_new_lines_and_spaces(content_to_add)} already exists in #{associated_model}!!\n\n"
172
+ line_already_exists(message)
173
+ else
174
+ add_in_file(file_location, line_to_match, content_to_add)
176
175
  end
177
176
 
178
- add_in_file(file_location, line_to_match, content_to_add)
177
+ puts("Success 🎉🎉\n\n")
178
+ end
179
+
180
+ def add_concern_to_user
181
+ file_location = "app/models/user.rb"
182
+ line_to_match = "class User < ApplicationRecord"
183
+ content_to_add = "\n include Roles::User\n"
184
+ puts("Adding 'include Roles::User' to User\n\n")
185
+
186
+ if line_exists_in_file?(file_location, content_to_add)
187
+ message = "#{remove_new_lines_and_spaces(content_to_add)} already exists in User!!\n\n"
188
+ line_already_exists(message)
189
+ else
190
+ add_in_file(file_location, line_to_match, content_to_add)
191
+ end
179
192
 
180
193
  puts("Success 🎉🎉\n\n")
181
194
  end
@@ -1,10 +1,8 @@
1
1
  default:
2
2
  models:
3
- -
4
3
 
5
4
  editor:
6
5
  models:
7
- -
8
6
  manageable_roles:
9
7
  - editor
10
8
 
data/lib/models/role.rb CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "active_hash"
4
4
 
5
- class RemovingLastTeamAdminException < RuntimeError; end
6
-
7
5
  class Role < ActiveYaml::Base
8
6
  include ActiveYaml::Aliases
9
7
  set_root_path "config/models"
@@ -111,23 +109,21 @@ class Role < ActiveYaml::Base
111
109
  class Collection < Array
112
110
  def initialize(model, ary)
113
111
  @model = model
114
-
115
112
  super(ary)
116
113
  end
117
114
 
118
115
  def <<(role)
119
116
  return true if include?(role)
120
-
121
- role_ids = @model.role_ids
122
-
117
+ role_ids = @model.role_ids || []
123
118
  role_ids << role.id
124
-
125
119
  @model.update(role_ids: role_ids)
126
120
  end
127
121
 
128
122
  def delete(role)
129
- @model.role_ids -= [role.key]
130
-
123
+ return @model.save unless include?(role)
124
+ current_role_ids = @model.role_ids || []
125
+ new_role_ids = current_role_ids - [role.key]
126
+ @model.role_ids = new_role_ids
131
127
  @model.save
132
128
  end
133
129
  end
data/lib/roles/support.rb CHANGED
@@ -44,7 +44,7 @@ module Roles
44
44
  after_destroy :invalidate_cache
45
45
 
46
46
  def validate_roles
47
- self.role_ids = role_ids.select(&:present?)
47
+ self.role_ids = role_ids&.select(&:present?) || []
48
48
 
49
49
  return if @allowed_roles.nil?
50
50
 
@@ -66,7 +66,7 @@ module Roles
66
66
  end
67
67
 
68
68
  def roles_without_defaults
69
- role_ids.map { |role_id| Role.find(role_id) }
69
+ role_ids&.map { |role_id| Role.find(role_id) } || []
70
70
  end
71
71
 
72
72
  def manageable_roles
data/lib/roles/user.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+
5
+ module Roles
6
+ module User
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ def parent_ids_for(role, through, parent)
11
+ parent_id_column = "#{parent}_id"
12
+ key = "#{role.key}_#{through}_#{parent_id_column}s"
13
+ # TODO Maybe we should make ability caching a default feature of the gem?
14
+ # If we do that, we would just make it check whether `ability_cache` exists.
15
+ # return ability_cache[key] if ability_cache && ability_cache[key]
16
+ role = nil if role.default?
17
+ value = send(through).with_role(role).distinct.pluck(parent_id_column)
18
+ # TODO Maybe we should make ability caching a default feature of the gem?
19
+ # current_cache = ability_cache || {}
20
+ # current_cache[key] = value
21
+ # update_column :ability_cache, current_cache
22
+ value
23
+ end
24
+ end
25
+ end
26
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train-roles
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Prabin Poudel
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-01-06 00:00:00.000000000 Z
12
+ date: 2022-02-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: byebug
@@ -192,6 +192,7 @@ files:
192
192
  - lib/models/role.rb
193
193
  - lib/roles/permit.rb
194
194
  - lib/roles/support.rb
195
+ - lib/roles/user.rb
195
196
  homepage: https://github.com/bullet-train-co/bullet_train-roles
196
197
  licenses:
197
198
  - MIT