effective_committees 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +186 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/effective_committees_manifest.js +3 -0
  6. data/app/assets/javascripts/effective_committees/base.js +0 -0
  7. data/app/assets/javascripts/effective_committeesjs +1 -0
  8. data/app/assets/stylesheets/effective_committees/base.scss +0 -0
  9. data/app/assets/stylesheets/effective_committees.scss +1 -0
  10. data/app/controllers/admin/committee_files_controller.rb +16 -0
  11. data/app/controllers/admin/committee_folders_controller.rb +16 -0
  12. data/app/controllers/admin/committee_members_controller.rb +16 -0
  13. data/app/controllers/admin/committees_controller.rb +22 -0
  14. data/app/controllers/effective/committee_folders_controller.rb +7 -0
  15. data/app/controllers/effective/committee_members_controller.rb +19 -0
  16. data/app/controllers/effective/committees_controller.rb +17 -0
  17. data/app/datatables/admin/effective_committee_files_datatable.rb +24 -0
  18. data/app/datatables/admin/effective_committee_folders_datatable.rb +28 -0
  19. data/app/datatables/admin/effective_committee_members_datatable.rb +32 -0
  20. data/app/datatables/admin/effective_committees_datatable.rb +34 -0
  21. data/app/datatables/effective_committee_members_datatable.rb +42 -0
  22. data/app/datatables/effective_committees_datatable.rb +31 -0
  23. data/app/helpers/effective_committees_helper.rb +3 -0
  24. data/app/models/concerns/effective_committees_user.rb +40 -0
  25. data/app/models/effective/committee.rb +55 -0
  26. data/app/models/effective/committee_file.rb +40 -0
  27. data/app/models/effective/committee_folder.rb +42 -0
  28. data/app/models/effective/committee_member.rb +76 -0
  29. data/app/views/admin/committee_files/_form.html.haml +14 -0
  30. data/app/views/admin/committee_folders/_form.html.haml +21 -0
  31. data/app/views/admin/committee_members/_form.html.haml +31 -0
  32. data/app/views/admin/committee_members/_user_fields.html.haml +7 -0
  33. data/app/views/admin/committees/_fields.html.haml +7 -0
  34. data/app/views/admin/committees/_form.html.haml +20 -0
  35. data/app/views/admin/committees/_form_committee.html.haml +6 -0
  36. data/app/views/effective/committee_folders/_committee_folder.html.haml +24 -0
  37. data/app/views/effective/committee_members/_form.html.haml +35 -0
  38. data/app/views/effective/committee_members/_user_fields.html.haml +7 -0
  39. data/app/views/effective/committees/_committee.html.haml +23 -0
  40. data/app/views/effective/committees/_dashboard.html.haml +10 -0
  41. data/app/views/effective/committees/_fields.html.haml +2 -0
  42. data/app/views/effective/committees/_form.html.haml +8 -0
  43. data/app/views/effective/committees/_form_committee.html.haml +11 -0
  44. data/app/views/effective/committees/index.html.haml +21 -0
  45. data/config/effective_committees.rb +13 -0
  46. data/config/routes.rb +24 -0
  47. data/db/migrate/01_create_effective_committees.rb.erb +14 -0
  48. data/db/seeds.rb +1 -0
  49. data/lib/effective_committees/engine.rb +18 -0
  50. data/lib/effective_committees/version.rb +3 -0
  51. data/lib/effective_committees.rb +18 -0
  52. data/lib/generators/effective_committees/install_generator.rb +31 -0
  53. data/lib/generators/templates/effective_committees_mailer_preview.rb +4 -0
  54. data/lib/tasks/effective_committees_tasks.rake +8 -0
  55. metadata +250 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b793fe3d40958ea8fc7e853979a10ef40cdbdfc043893b93690ad62f8d5c7604
4
+ data.tar.gz: 414192d935ae2fc8f6b5b24617a97c219e72a069728c8e3a22241ef7449301bb
5
+ SHA512:
6
+ metadata.gz: 784a0ccd91b3d7fdff1023a75f41506a771b17293ad73cb4c14de8d1184cd1c580a10c46d29072302c182ed906d13910f6837e191169306e052fbf77b30a5a74
7
+ data.tar.gz: 0b09e1874e3a24d4e29b71e12ecddf4920634d4c57fcdbf0a1c5d6e49210fb5e5b18c80d9d4507d0fbec1bd8852f19b18d4c13a0befe7530c0e6924c4d5d7495
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Code and Effect Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # Effective Committees
2
+
3
+ Committees are groups of users that can all share files.
4
+
5
+ ## Getting Started
6
+
7
+ This requires Rails 6+ and Twitter Bootstrap 4 and just works with Devise.
8
+
9
+ Please first install the [effective_datatables](https://github.com/code-and-effect/effective_datatables) gem.
10
+
11
+ Please download and install the [Twitter Bootstrap4](http://getbootstrap.com)
12
+
13
+ Add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'haml-rails' # or try using gem 'hamlit-rails'
17
+ gem 'effective_committees'
18
+ ```
19
+
20
+ Run the bundle command to install it:
21
+
22
+ ```console
23
+ bundle install
24
+ ```
25
+
26
+ Then run the generator:
27
+
28
+ ```ruby
29
+ rails generate effective_committees:install
30
+ ```
31
+
32
+ The generator will install an initializer which describes all configuration options and creates a database migration.
33
+
34
+ If you want to tweak the table names, manually adjust both the configuration file and the migration now.
35
+
36
+ Then migrate the database:
37
+
38
+ ```ruby
39
+ rake db:migrate
40
+ ```
41
+
42
+ Please add the following to your User model:
43
+
44
+ ```
45
+ effective_committees_user
46
+
47
+ Use the following datatables to display to your user their applicants dues:
48
+
49
+ ```haml
50
+ %h2 My Committees
51
+ = render 'effective/committees/dashboard'
52
+ ```
53
+
54
+ and
55
+
56
+ ```
57
+ Add a link to the admin menu:
58
+
59
+ ```haml
60
+ - if can? :admin, :effective_committees
61
+ = nav_dropdown 'Committees' do
62
+ - if can? :index, Effective::Committee
63
+ = nav_link_to 'Committees', effective_committees.admin_committees_path
64
+
65
+ - if can? :index, Effective::CommitteeMember
66
+ = nav_link_to 'Committee Members', effective_committees.admin_committee_members_path
67
+
68
+ - if can? :index, Effective::CommitteeFolder
69
+ = nav_link_to 'Committee Folders', effective_committees.admin_committee_folders_path
70
+
71
+ - if can? :index, Effective::CommitteeFile
72
+ = nav_link_to 'Committee Files', effective_committees.admin_committee_files_path
73
+ ```
74
+
75
+ ## Configuration
76
+
77
+ ## Authorization
78
+
79
+ All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
80
+
81
+ ## Effective Roles
82
+
83
+ This gem works with effective roles for the representative roles.
84
+
85
+ Configure your `config/initializers/effective_roles.rb` something like this:
86
+
87
+ ```
88
+ EffectiveRoles.setup do |config|
89
+ config.roles = [:admin, :reserved, :owner, :billing] # Only add to the end of this array. Never prepend roles.
90
+
91
+ # config.role_descriptions
92
+ # ========================
93
+ # This setting configures the text that is displayed by form helpers (see README.md)
94
+
95
+ config.role_descriptions = {
96
+ 'User' => {
97
+ # User roles
98
+ admin: 'can log in to the /admin section of the website. full access to everything.',
99
+ },
100
+ 'Effective::CommitteeMember' => {
101
+ owner: 'committee owner. full access to everything.',
102
+ billing: 'the billing contact. full access to everything.'
103
+ }
104
+ }
105
+
106
+ # config.assignable_roles
107
+ # Which roles can be assigned by whom
108
+ # =======================
109
+ # When current_user is passed into a form helper function (see README.md)
110
+ # this setting determines which roles that current_user may assign
111
+ config.assignable_roles = {
112
+ 'User' => { admin: [:admin] },
113
+
114
+ 'Effective::CommitteeMember' => {
115
+ admin: [:owner, :billing],
116
+ owner: [:owner, :billing],
117
+ billing: [:billing]
118
+ }
119
+ }
120
+ end
121
+ ```
122
+
123
+ ## Permissions
124
+
125
+ The permissions you actually want to define are as follows (using CanCan):
126
+
127
+ ```ruby
128
+ if user.persisted?
129
+ can :index, Effective::Committee
130
+ can(:show, Effective::Committee) { |committee| user.committees.include?(committee) }
131
+
132
+ can([:edit, :update], Effective::Committee) do |committee|
133
+ user.committee_member(committee: committee)&.is?(:owner)
134
+ end
135
+
136
+ can :index, Effective::CommitteeMember
137
+
138
+ can(:show, Effective::CommitteeMember) { |member| user.committees.include?(member.committee) }
139
+ can(:new, Effective::CommitteeMember)
140
+
141
+ can([:create, :edit, :update], Effective::CommitteeMember) do |cm|
142
+ user.committee_member(committee: cm.committee)&.is?(:owner)
143
+ end
144
+
145
+ can(:destroy, Effective::CommitteeMember) do |member|
146
+ user.committee_member(committee: member.committee)&.is?(:owner) && !member.is?(:owner)
147
+ end
148
+
149
+ can(:show, Effective::CommitteeFolder) { |folder| user.committees.include?(folder.committee) }
150
+ can(:show, Effective::CommitteeFile) { |file| user.committees.include?(file.committee) }
151
+ end
152
+
153
+ if user.admin?
154
+ can :admin, :effective_committees
155
+
156
+ can(crud - [:destroy], Effective::Committee)
157
+ can(:destroy, Effective::Committee) { |committee| committee.committee_members_count == 0 }
158
+
159
+ can(crud - [:destroy], Effective::CommitteeFolder)
160
+ can(:destroy, Effective::CommitteeFolder) { |folder| folder.committee_files_count == 0 }
161
+
162
+ can(crud, Effective::CommitteeMember)
163
+ can(crud, Effective::CommitteeFile)
164
+ end
165
+ ```
166
+
167
+ ## License
168
+
169
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
170
+
171
+ ## Testing
172
+
173
+ Run tests by:
174
+
175
+ ```ruby
176
+ rails test
177
+ ```
178
+
179
+ ## Contributing
180
+
181
+ 1. Fork it
182
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
183
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
184
+ 4. Push to the branch (`git push origin my-new-feature`)
185
+ 5. Bonus points for test coverage
186
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
9
+
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
18
+ task default: :test
@@ -0,0 +1,3 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
3
+ //= link_tree ../images
@@ -0,0 +1 @@
1
+ //= require_tree ./effective_committees
@@ -0,0 +1 @@
1
+ @import 'effective_committees/base';
@@ -0,0 +1,16 @@
1
+ module Admin
2
+ class CommitteeFilesController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_committees) }
5
+
6
+ include Effective::CrudController
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ model = (params.key?(:effective_committee_file) ? :effective_committee_file : :committee_file)
12
+ params.require(model).permit!
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Admin
2
+ class CommitteeFoldersController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_committees) }
5
+
6
+ include Effective::CrudController
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ model = (params.key?(:effective_committee_folder) ? :effective_committee_folder : :committee_folder)
12
+ params.require(model).permit!
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Admin
2
+ class CommitteeMembersController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_committees) }
5
+
6
+ include Effective::CrudController
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ model = (params.key?(:effective_committee_member) ? :effective_committee_member : :committee_member)
12
+ params.require(model).permit!
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module Admin
2
+ class CommitteesController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_committees) }
5
+
6
+ include Effective::CrudController
7
+
8
+ submit :save, 'Save'
9
+ submit :save, 'Save and Add New', redirect: :new
10
+ submit :save, 'Save and View', redirect: -> { effective_committees.committee_path(resource) }
11
+
12
+ resource_scope -> { Effective::Committee.deep.all }
13
+
14
+ private
15
+
16
+ def permitted_params
17
+ model = (params.key?(:effective_committee) ? :effective_committee : :committee)
18
+ params.require(model).permit!
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ module Effective
2
+ class CommitteeFoldersController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+
5
+ include Effective::CrudController
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module Effective
2
+ class CommitteeMembersController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+
5
+ include Effective::CrudController
6
+
7
+ resource_scope -> {
8
+ committees = Effective::Committee.deep.where(id: current_user.committees)
9
+ Effective::CommitteeMember.deep.where(committee: committees)
10
+ }
11
+
12
+ private
13
+
14
+ def permitted_params
15
+ params.require(:effective_committee_member).permit!
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Effective
2
+ class CommitteesController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+
5
+ include Effective::CrudController
6
+
7
+ resource_scope -> { Effective::Committee.all.deep }
8
+
9
+ private
10
+
11
+ def permitted_params
12
+ model = (params.key?(:effective_committee) ? :effective_committee : :committee)
13
+ params.require(model).permit!
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module Admin
2
+ class EffectiveCommitteeFilesDatatable < Effective::Datatable
3
+ datatable do
4
+ col :updated_at, visible: false
5
+ col :created_at, visible: false
6
+
7
+ col :id, visible: false
8
+
9
+ col :committee, search: :string
10
+ col :committee_folder, search: :string, label: 'Folder'
11
+
12
+ col :file
13
+ col :title
14
+ col :notes
15
+
16
+ actions_col
17
+ end
18
+
19
+ collection do
20
+ Effective::CommitteeFile.deep.all
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ module Admin
2
+ class EffectiveCommitteeFoldersDatatable < Effective::Datatable
3
+ datatable do
4
+ reorder :position
5
+
6
+ col :updated_at, visible: false
7
+ col :created_at, visible: false
8
+
9
+ col :id, visible: false
10
+
11
+ col :committee, search: :string
12
+
13
+ col :title, label: 'Folder'
14
+ col :slug, visible: false
15
+ col :body
16
+
17
+ col :committee_files, label: 'Files', search: :string
18
+ col :committee_files_count, label: 'Files Count', visible: false
19
+
20
+ actions_col
21
+ end
22
+
23
+ collection do
24
+ Effective::CommitteeFolder.deep.all
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ module Admin
2
+ class EffectiveCommitteeMembersDatatable < Effective::Datatable
3
+ datatable do
4
+ col :id, visible: false
5
+ col :user_id, visible: false
6
+
7
+ col :committee
8
+ col :user
9
+
10
+ unless attributes[:user_id]
11
+ col :email do |committee_member|
12
+ mail_to(committee_member.user.email)
13
+ end
14
+ end
15
+
16
+ if EffectiveCommittees.use_effective_roles
17
+ col :roles, search: roles_collection
18
+ end
19
+
20
+ actions_col
21
+ end
22
+
23
+ collection do
24
+ Effective::CommitteeMember.deep.all
25
+ end
26
+
27
+ def roles_collection
28
+ EffectiveRoles.roles_collection(Effective::CommitteeMember.new).map(&:second)
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ module Admin
2
+ class EffectiveCommitteesDatatable < Effective::Datatable
3
+ datatable do
4
+
5
+ col :updated_at, visible: false
6
+ col :created_at, visible: false
7
+
8
+ col :id, visible: false
9
+
10
+ col :title, label: 'Committee'
11
+ col :slug, visible: false
12
+ col :body
13
+
14
+ col :committee_members, label: 'Members', search: :string
15
+ col :committee_members_count, label: 'Members Count', visible: false
16
+
17
+ col :committee_folders, label: 'Folders', search: :string
18
+ col :committee_folders_count, label: 'Folders Count', visible: false
19
+
20
+ col :committee_files, label: 'Files', search: :string
21
+ col :committee_files_count, label: 'Files Count', visible: false
22
+
23
+ actions_col do |committee|
24
+ dropdown_link_to('View Committee', effective_committees.committee_path(committee), target: '_blank')
25
+ end
26
+
27
+ end
28
+
29
+ collection do
30
+ Effective::Committee.deep.all
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ class EffectiveCommitteeMembersDatatable < Effective::Datatable
2
+ datatable do
3
+ col :id, visible: false
4
+
5
+ col :committee
6
+ col :user
7
+
8
+ unless attributes[:user_id]
9
+ col :email do |committee_member|
10
+ mail_to(committee_member.user.email)
11
+ end
12
+ end
13
+
14
+ if EffectiveCommittees.use_effective_roles
15
+ col :roles, search: roles_collection
16
+ end
17
+
18
+ unless attributes[:actions] == false
19
+ actions_col
20
+ end
21
+
22
+ end
23
+
24
+ collection do
25
+ scope = Effective::CommitteeMember.deep.all.where(committee: current_user.committees)
26
+
27
+ if attributes[:committee_id]
28
+ scope = scope.where(committee_id: attributes[:committee_id])
29
+ end
30
+
31
+ if attributes[:user_id]
32
+ scope = scope.where(user_id: attributes[:user_id])
33
+ end
34
+
35
+ scope
36
+ end
37
+
38
+ def roles_collection
39
+ EffectiveRoles.roles_collection(Effective::CommitteeMember.new).map(&:second)
40
+ end
41
+
42
+ end
@@ -0,0 +1,31 @@
1
+ # Dashboard Committees
2
+ class EffectiveCommitteesDatatable < Effective::Datatable
3
+ datatable do
4
+ order :title
5
+
6
+ col :id, visible: false
7
+
8
+ col :title, label: 'Committee'
9
+
10
+ col :committee_members, search: :string, label: 'Members'
11
+
12
+ col :committee_folders, search: :string, label: 'Folders'
13
+
14
+ col :committee_folders_count, label: 'Folders', visible: false do |committee|
15
+ pluralize(committee.committee_folders_count, 'folders')
16
+ end
17
+
18
+ col :committee_files, search: :string, label: 'Files', visible: false
19
+
20
+ col :committee_files_count, label: 'Files' do |committee|
21
+ pluralize(committee.committee_files_count, 'files')
22
+ end
23
+
24
+ actions_col
25
+ end
26
+
27
+ collection do
28
+ Effective::Committee.deep.where(id: current_user.committees)
29
+ end
30
+
31
+ end
@@ -0,0 +1,3 @@
1
+ module EffectiveCommitteesHelper
2
+
3
+ end
@@ -0,0 +1,40 @@
1
+ # EffectiveCommitteesUser
2
+ #
3
+ # Mark your user model with effective_committees_user to get all the includes
4
+
5
+ module EffectiveCommitteesUser
6
+ extend ActiveSupport::Concern
7
+
8
+ module Base
9
+ def effective_committees_user
10
+ include ::EffectiveCommitteesUser
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def effective_committees_user?; true; end
16
+ end
17
+
18
+ included do
19
+ has_many :committee_members, -> { Effective::CommitteeMember.sorted },
20
+ class_name: 'Effective::CommitteeMember', inverse_of: :user, dependent: :delete_all
21
+
22
+ accepts_nested_attributes_for :committee_members, allow_destroy: true
23
+ end
24
+
25
+ # Instance Methods
26
+
27
+ def committee_member(committee:)
28
+ committee_members.find { |rep| rep.committee_id == committee.id }
29
+ end
30
+
31
+ # Find or build
32
+ def build_committee_member(committee:)
33
+ committee_member(committee: committee) || committee_members.build(committee: committee)
34
+ end
35
+
36
+ def committees
37
+ committee_members.reject(&:marked_for_destruction?).map(&:committee)
38
+ end
39
+
40
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Effective
4
+ class Committee < ActiveRecord::Base
5
+ self.table_name = EffectiveCommittees.committees_table_name.to_s
6
+
7
+ acts_as_slugged
8
+
9
+ log_changes if respond_to?(:log_changes)
10
+ has_rich_text :body
11
+
12
+ has_many :committee_members, -> { Effective::CommitteeMember.sorted }, class_name: 'Effective::CommitteeMember', inverse_of: :committee, dependent: :delete_all
13
+ accepts_nested_attributes_for :committee_members, allow_destroy: true
14
+
15
+ has_many :committee_folders, -> { Effective::CommitteeFolder.sorted }, class_name: 'Effective::CommitteeFolder', inverse_of: :committee, dependent: :delete_all
16
+ accepts_nested_attributes_for :committee_folders, allow_destroy: true
17
+
18
+ has_many :committee_files, -> { Effective::CommitteeFile.sorted }, class_name: 'Effective::CommitteeFile', inverse_of: :committee, dependent: :delete_all
19
+ accepts_nested_attributes_for :committee_files, allow_destroy: true
20
+
21
+ effective_resource do
22
+ title :string
23
+ slug :string
24
+
25
+ committee_members_count :integer # Counter Cache
26
+ committee_folders_count :integer # Counter Cache
27
+ committee_files_count :integer # Counter Cache
28
+
29
+ timestamps
30
+ end
31
+
32
+ scope :sorted, -> { order(:title) }
33
+ scope :deep, -> { includes(:committee_members) }
34
+
35
+ validates :title, presence: true, uniqueness: true, length: { maximum: 255 }
36
+
37
+ def to_s
38
+ title.presence || 'New Committee'
39
+ end
40
+
41
+ def committee_member(user:)
42
+ committee_members.find { |member| member.user_id == user.id }
43
+ end
44
+
45
+ # Find or build
46
+ def build_committee_member(user:)
47
+ committee_member(user: user) || committee_members.build(user: user)
48
+ end
49
+
50
+ def users
51
+ committee_members.reject(&:marked_for_destruction?).map(&:user)
52
+ end
53
+
54
+ end
55
+ end