effective_organizations 0.0.2 → 0.0.6

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: bcbee46f5235da4b9d6fa04d84daab02383cfe373532138dc6ec601fc3110ced
4
- data.tar.gz: a400d6e2ecec13c3a98b6e58beda724471628e9262100dbeb5f9b414940fd417
3
+ metadata.gz: a2ee1cb9ffc7df883ea343966168245ab3d77ba59ef9753a20d825702ba46aaa
4
+ data.tar.gz: fcd8f65d9dbc66c5073077e6bd7fe9f48774c4444d965c7c4ce95d09c3d97935
5
5
  SHA512:
6
- metadata.gz: c7a6df31f4b94032905653c0ba44489ba4ac87b47f7e48e488726e4e9c9d8a26b41a02211f94f277ce91c871765ef0bce77a33ebf4aa0dbaef9a5f71ddf83520
7
- data.tar.gz: 84dfedd0a963e6080faac3e7e8553e7739d9179d9e13d4aabb251516848fae840668d02c627884d6acb78d12588d5a221a3d287ee0d63d9b75f7f4b0389dce30
6
+ metadata.gz: a34485e30e3d66f85cb4dff1ed2ccd6656134ac5171c2ceae64e8fc30336144c1ac6c9889dab98b018dbdad0c2b3bda96e583f70583a9907f09eaf1ed4ebe1a0
7
+ data.tar.gz: fb7e677000619da597b0b43f5ae827211d2d55fca0d51326559b27bb7fc7fbd7b3155260afa37f7768360a0f695d2d80667dabc680414f2479582c645c438dac
data/README.md CHANGED
@@ -71,16 +71,85 @@ Add a link to the admin menu:
71
71
 
72
72
  All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
73
73
 
74
+ ## Effective Roles
75
+
76
+ This gem works with effective roles for the representative roles.
77
+
78
+ Configure your `config/initializers/effective_roles.rb` something like this:
79
+
80
+ ```
81
+ EffectiveRoles.setup(:caaa) do |config|
82
+ config.roles = [:admin, :reserved, :owner, :billing] # Only add to the end of this array. Never prepend roles.
83
+
84
+ # config.role_descriptions
85
+ # ========================
86
+ # This setting configures the text that is displayed by form helpers (see README.md)
87
+
88
+ config.role_descriptions = {
89
+ 'User' => {
90
+ # User roles
91
+ admin: 'can log in to the /admin section of the website. full access to everything.',
92
+ },
93
+ 'Effective::Representative' => {
94
+ owner: 'the owner. full access to everything.',
95
+ billing: 'the billing contact. full access to everything.'
96
+ }
97
+ }
98
+
99
+ # config.assignable_roles
100
+ # Which roles can be assigned by whom
101
+ # =======================
102
+ # When current_user is passed into a form helper function (see README.md)
103
+ # this setting determines which roles that current_user may assign
104
+ config.assignable_roles = {
105
+ 'User' => { admin: [:admin] },
106
+
107
+ 'Effective::Representative' => {
108
+ admin: [:owner, :billing],
109
+ owner: [:owner, :billing],
110
+ billing: [:billing]
111
+ }
112
+ }
113
+ end
114
+ ```
115
+
116
+
74
117
  ## Permissions
75
118
 
76
119
  The permissions you actually want to define are as follows (using CanCan):
77
120
 
78
121
  ```ruby
122
+ if user.persisted?
123
+ can :index, EffectiveOrganizations.Organization
124
+ can(:show, EffectiveOrganizations.Organization) { |organization| user.organizations.include?(organization) }
125
+
126
+ can([:edit, :update], EffectiveOrganizations.Organization) do |organization|
127
+ rep = user.representative(organization: organization)
128
+ rep && (rep.is?(:owner) || rep.is?(:billing))
129
+ end
130
+
131
+ can :index, Effective::Representative
132
+ can(:new, Effective::Representative)
133
+
134
+ can([:create, :edit, :update], Effective::Representative) do |representative|
135
+ rep = user.representative(organization: representative.organization)
136
+ rep && (rep.is?(:owner) || rep.is?(:billing))
137
+ end
138
+
139
+ can(:destroy, Effective::Representative) do |representative|
140
+ allowed = !(representative.is?(:owner) || representative.is?(:billing))
141
+ rep = user.representative(organization: representative.organization)
142
+
143
+ allowed && rep && (rep.is?(:owner) || rep.is?(:billing))
144
+ end
145
+ end
79
146
 
80
147
  if user.admin?
81
148
  can :admin, :effective_organizations
82
- can :manage, Effective::Organization
83
- can :manage, Effective::Representative
149
+ can(crud, EffectiveOrganizations.Organization)
150
+
151
+ can(crud - [:destroy], Effective::Representative)
152
+ can(:destroy, Effective::Representative) { |rep| !rep.is?(:owner) }
84
153
  end
85
154
  ```
86
155
 
@@ -11,11 +11,24 @@ class EffectiveRepresentativesDatatable < Effective::Datatable
11
11
 
12
12
  col :roles, search: roles_collection
13
13
 
14
- actions_col
14
+ unless attributes[:actions] == false
15
+ actions_col
16
+ end
17
+
15
18
  end
16
19
 
17
20
  collection do
18
- Effective::Representative.deep.all.where(organization: current_user.organizations)
21
+ scope = Effective::Representative.deep.all.where(organization: current_user.organizations)
22
+
23
+ if attributes[:organization_id]
24
+ scope = scope.where(organization_id: attributes[:organization_id])
25
+ end
26
+
27
+ if attributes[:user_id]
28
+ scope = scope.where(user_id: attributes[:user_id])
29
+ end
30
+
31
+ scope
19
32
  end
20
33
 
21
34
  def roles_collection
@@ -6,7 +6,11 @@ module EffectiveOrganizationsOrganization
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  module Base
9
- def effective_organizations_organization
9
+ def effective_organizations_organization(users_source_type: nil)
10
+ @effective_organizations_organization_opts = {
11
+ users_source_type: users_source_type
12
+ }
13
+
10
14
  include ::EffectiveOrganizationsOrganization
11
15
  end
12
16
  end
@@ -20,7 +24,7 @@ module EffectiveOrganizationsOrganization
20
24
  end
21
25
 
22
26
  included do
23
- log_changes(except: :representatives) if respond_to?(:log_changes)
27
+ log_changes(except: [:representatives, :users]) if respond_to?(:log_changes)
24
28
 
25
29
  # rich_text_body
26
30
  # has_many_rich_texts
@@ -28,7 +32,10 @@ module EffectiveOrganizationsOrganization
28
32
  has_many :representatives, -> { Effective::Representative.sorted },
29
33
  class_name: 'Effective::Representative', inverse_of: :organization, dependent: :delete_all
30
34
 
31
- has_many :users, through: :representatives
35
+ accepts_nested_attributes_for :representatives, allow_destroy: true
36
+
37
+ has_many :users, through: :representatives,
38
+ source_type: (@effective_organizations_organization_opts[:users_source_type] || (name.start_with?('Effective') ? '::User' : "#{name.split('::').first}::Organization"))
32
39
 
33
40
  effective_resource do
34
41
  title :string
@@ -53,4 +60,13 @@ module EffectiveOrganizationsOrganization
53
60
  title.presence || 'New Organization'
54
61
  end
55
62
 
63
+ def representative(user:)
64
+ representatives.find { |rep| rep.user_id == user.id }
65
+ end
66
+
67
+ # Find or build
68
+ def build_representative(user:)
69
+ representative(user: user) || representatives.build(user: user)
70
+ end
71
+
56
72
  end
@@ -6,7 +6,11 @@ module EffectiveOrganizationsUser
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  module Base
9
- def effective_organizations_user
9
+ def effective_organizations_user(organizations_source_type: nil)
10
+ @effective_organizations_user_opts = {
11
+ organizations_source_type: organizations_source_type
12
+ }
13
+
10
14
  include ::EffectiveOrganizationsUser
11
15
  end
12
16
  end
@@ -20,8 +24,11 @@ module EffectiveOrganizationsUser
20
24
  has_many :representatives, -> { Effective::Representative.sorted },
21
25
  class_name: 'Effective::Representative', inverse_of: :user, dependent: :delete_all
22
26
 
27
+ accepts_nested_attributes_for :representatives, allow_destroy: true
28
+
23
29
  # App scoped
24
- has_many :organizations, through: :representatives
30
+ has_many :organizations, through: :representatives,
31
+ source_type: @effective_organizations_user_opts[:organizations_source_type] || "#{name.split('::').first}::Organization"
25
32
  end
26
33
 
27
34
  def representative(organization:)
@@ -3,5 +3,6 @@ module Effective
3
3
  self.table_name = EffectiveOrganizations.organizations_table_name.to_s
4
4
 
5
5
  effective_organizations_organization
6
+
6
7
  end
7
8
  end
@@ -7,9 +7,10 @@ module Effective
7
7
 
8
8
  log_changes(to: :organization) if respond_to?(:log_changes)
9
9
 
10
- belongs_to :organization, counter_cache: true
10
+ belongs_to :organization, polymorphic: true, counter_cache: true
11
11
  belongs_to :user, polymorphic: true
12
12
 
13
+ accepts_nested_attributes_for :organization
13
14
  accepts_nested_attributes_for :user
14
15
 
15
16
  effective_resource do
@@ -28,8 +29,8 @@ module Effective
28
29
  validates :organization, presence: true
29
30
  validates :user, presence: true
30
31
 
31
- validates :user_id, if: -> { user_id && user_type && organization_id },
32
- uniqueness: { scope: [:organization_id], message: 'already belongs to this organization' }
32
+ validates :user_id, if: -> { user_id && user_type && organization_id && organization_type },
33
+ uniqueness: { scope: [:organization_id, :organization_type], message: 'already belongs to this organization' }
33
34
 
34
35
  def to_s
35
36
  user.to_s
@@ -40,5 +41,10 @@ module Effective
40
41
  self.user = user_type.constantize.new(attributes)
41
42
  end
42
43
 
44
+ def build_organization(attributes = {})
45
+ raise('please assign organization_type first') if organization_type.blank?
46
+ self.organization = organization_type.constantize.new(attributes)
47
+ end
48
+
43
49
  end
44
50
  end
@@ -0,0 +1,6 @@
1
+ - categories = EffectiveOrganizations.Organization.categories
2
+
3
+ - if categories.present?
4
+ = f.select :category, categories, required: true
5
+
6
+ = f.text_field :title
@@ -1,14 +1,9 @@
1
1
  = effective_form_with(model: [:admin, organization], engine: true) do |f|
2
- - categories = EffectiveOrganizations.Organization.categories
3
-
4
- - if categories.present?
5
- = f.select :category, categories, required: true
6
-
7
- = f.text_field :title
2
+ = render 'admin/organizations/fields', f: f
8
3
 
9
4
  = effective_submit(f)
10
5
 
11
6
  - if organization.persisted?
12
7
  %h2 Representatives
13
- - datatable = Admin::EffectiveRepresentativesDatatable.new(organization_id: organization.id)
8
+ - datatable = Admin::EffectiveRepresentativesDatatable.new(organization_id: organization.id, organization_type: organization.class.name)
14
9
  = render_inline_datatable(datatable)
@@ -4,12 +4,13 @@
4
4
  = f.hidden_field :user_id
5
5
  = f.hidden_field :user_type
6
6
  = f.hidden_field :organization_id
7
+ = f.hidden_field :organization_type
7
8
 
8
9
  - if f.object.new_record?
9
10
  - unless inline_datatable? && inline_datatable.attributes[:organization_id].present?
10
- = f.select :organization_id, EffectiveOrganizations.Organization.sorted
11
+ = f.select :organization, { 'Organizations' => EffectiveOrganizations.Organization.sorted }, polymorphic: true
11
12
 
12
- = f.checks :roles, EffectiveRoles.roles_collection(f.object)
13
+ = f.checks :roles, EffectiveRoles.roles_collection(f.object, skip_disabled: true)
13
14
 
14
15
  - unless inline_datatable? && inline_datatable.attributes[:user_id].present?
15
16
  = f.radios :new_representative_user_action, ['Invite new user', 'Add existing user'], inline: true, label: 'Representative'
@@ -28,7 +29,7 @@
28
29
  - unless inline_datatable? && inline_datatable.attributes[:user_id].present?
29
30
  = f.static_field :user
30
31
 
31
- = f.checks :roles, EffectiveRoles.roles_collection(f.object)
32
+ = f.checks :roles, EffectiveRoles.roles_collection(f.object, skip_disabled: true)
32
33
 
33
34
  - unless inline_datatable? && inline_datatable.attributes[:user_id].present?
34
35
  = f.fields_for :user, f.object.user do |fu|
@@ -0,0 +1,6 @@
1
+ - categories = EffectiveOrganizations.Organization.categories
2
+
3
+ - if categories.present?
4
+ = f.select :category, categories, required: true
5
+
6
+ = f.text_field :title
@@ -1,16 +1,11 @@
1
1
  - url = (organization.persisted? ? effective_organizations.organization_path(organization) : effective_organizations.organizations_path)
2
2
 
3
3
  = effective_form_with(model: organization, url: url) do |f|
4
- - categories = EffectiveOrganizations.Organization.categories
5
-
6
- - if categories.present?
7
- = f.select :category, categories, required: true
8
-
9
- = f.text_field :title
4
+ = render 'effective/organizations/fields', f: f
10
5
 
11
6
  = f.submit
12
7
 
13
8
  - if organization.persisted?
14
9
  %h2 Representatives
15
- - datatable = EffectiveRepresentativesDatatable.new(organization_id: organization.id)
10
+ - datatable = EffectiveRepresentativesDatatable.new(organization: organization)
16
11
  = render_inline_datatable(datatable)
@@ -4,12 +4,13 @@
4
4
  = f.hidden_field :user_id
5
5
  = f.hidden_field :user_type
6
6
  = f.hidden_field :organization_id
7
+ = f.hidden_field :organization_type
7
8
 
8
9
  - if f.object.new_record?
9
10
  - unless inline_datatable? && inline_datatable.attributes[:organization_id].present?
10
- = f.select :organization_id, EffectiveOrganizations.Organization.sorted
11
+ = f.select :organization, { 'Organizations' => EffectiveOrganizations.Organization.sorted }, polymorphic: true
11
12
 
12
- = f.checks :roles, EffectiveRoles.roles_collection(f.object)
13
+ = f.checks :roles, EffectiveRoles.roles_collection(f.object, skip_disabled: true)
13
14
 
14
15
  - unless inline_datatable? && inline_datatable.attributes[:user_id].present?
15
16
  = f.hidden_field :new_representative_user_action, value: 'Invite new user'
@@ -24,7 +25,7 @@
24
25
  - unless inline_datatable? && inline_datatable.attributes[:user_id].present?
25
26
  = f.static_field :user
26
27
 
27
- = f.checks :roles, EffectiveRoles.roles_collection(f.object)
28
+ = f.checks :roles, EffectiveRoles.roles_collection(f.object, skip_disabled: true)
28
29
 
29
30
  = f.fields_for :user, f.object.user do |fu|
30
31
  = render 'effective/representatives/user_fields', f: fu
@@ -18,7 +18,6 @@ class CreateEffectiveOrganizations < ActiveRecord::Migration[6.0]
18
18
  # Representatives
19
19
  create_table :representatives do |t|
20
20
  t.integer :organization_id
21
- t.string :organization_type
22
21
 
23
22
  t.integer :user_id
24
23
  t.string :user_type
@@ -29,7 +28,7 @@ class CreateEffectiveOrganizations < ActiveRecord::Migration[6.0]
29
28
  t.datetime :created_at
30
29
  end
31
30
 
32
- add_index :representatives, [:organization_id, :organization_type]
31
+ add_index :representatives, [:organization_id]
33
32
  add_index :representatives, [:user_id, :user_type]
34
33
 
35
34
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveOrganizations
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.0.6'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_organizations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-10 00:00:00.000000000 Z
11
+ date: 2021-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: effective_roles
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: sqlite3
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -178,11 +192,13 @@ files:
178
192
  - app/models/concerns/effective_organizations_user.rb
179
193
  - app/models/effective/organization.rb
180
194
  - app/models/effective/representative.rb
195
+ - app/views/admin/organizations/_fields.html.haml
181
196
  - app/views/admin/organizations/_form.html.haml
182
197
  - app/views/admin/organizations/_form_organization.html.haml
183
198
  - app/views/admin/representatives/_form.html.haml
184
199
  - app/views/admin/representatives/_user_fields.html.haml
185
200
  - app/views/effective/organizations/_dashboard.html.haml
201
+ - app/views/effective/organizations/_fields.html.haml
186
202
  - app/views/effective/organizations/_form.html.haml
187
203
  - app/views/effective/organizations/_form_organization.html.haml
188
204
  - app/views/effective/representatives/_form.html.haml