pairer 1.0.0
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 +7 -0
- data/LICENSE +7 -0
- data/README.md +134 -0
- data/Rakefile +20 -0
- data/app/assets/config/pairer_manifest.js +3 -0
- data/app/assets/images/pairer/favicon.ico +0 -0
- data/app/assets/images/pairer/sweep.png +0 -0
- data/app/assets/javascripts/pairer/application.js +47 -0
- data/app/assets/stylesheets/pairer/application.scss +82 -0
- data/app/assets/stylesheets/pairer/utility.scss +221 -0
- data/app/controllers/pairer/application_controller.rb +31 -0
- data/app/controllers/pairer/boards_controller.rb +202 -0
- data/app/controllers/pairer/sessions_controller.rb +32 -0
- data/app/helpers/pairer/application_helper.rb +4 -0
- data/app/jobs/pairer/application_job.rb +5 -0
- data/app/jobs/pairer/cleanup_boards_job.rb +9 -0
- data/app/mailers/pairer/application_mailer.rb +5 -0
- data/app/models/pairer/application_record.rb +19 -0
- data/app/models/pairer/board.rb +226 -0
- data/app/models/pairer/group.rb +45 -0
- data/app/models/pairer/person.rb +7 -0
- data/app/views/layouts/pairer/application.html.slim +53 -0
- data/app/views/layouts/pairer/application.js.erb +5 -0
- data/app/views/pairer/boards/_current_groups.html.slim +11 -0
- data/app/views/pairer/boards/_group.html.slim +17 -0
- data/app/views/pairer/boards/_group_lock_button.html.slim +4 -0
- data/app/views/pairer/boards/_person.html.slim +9 -0
- data/app/views/pairer/boards/_recently_accessed_boards.html.slim +26 -0
- data/app/views/pairer/boards/_role.html.slim +6 -0
- data/app/views/pairer/boards/_stats.html.slim +37 -0
- data/app/views/pairer/boards/create_group.js.erb +3 -0
- data/app/views/pairer/boards/create_person.js.erb +7 -0
- data/app/views/pairer/boards/delete_group.js.erb +7 -0
- data/app/views/pairer/boards/index.html.slim +28 -0
- data/app/views/pairer/boards/lock_group.js.erb +1 -0
- data/app/views/pairer/boards/lock_person.js.erb +1 -0
- data/app/views/pairer/boards/show.html.slim +240 -0
- data/app/views/pairer/boards/update_group.js.erb +0 -0
- data/app/views/pairer/exceptions/show.html.slim +8 -0
- data/app/views/pairer/sessions/sign_in.html.slim +10 -0
- data/app/views/pairer/shared/_flash.html.slim +6 -0
- data/app/views/pairer/shared/svg/_broom.html.slim +2 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +26 -0
- data/db/migrate/20210821001344_add_pairer_tables.rb +34 -0
- data/db/seeds.rb +0 -0
- data/lib/pairer/config.rb +38 -0
- data/lib/pairer/engine.rb +47 -0
- data/lib/pairer/version.rb +3 -0
- data/lib/pairer.rb +24 -0
- data/lib/tasks/pairer_engine_tasks.rake +4 -0
- metadata +261 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
- session_key = :pairer_board_access_list
|
2
|
+
|
3
|
+
- if session[session_key]
|
4
|
+
- begin
|
5
|
+
- access_list_by_time = session[session_key].to_a.sort_by{|_id,time| Time.parse(time)}.reverse.to_h
|
6
|
+
|
7
|
+
- if access_list_by_time.any?
|
8
|
+
- access_list_by_time.each do |id, time|
|
9
|
+
- if Time.parse(time) < 30.days.ago
|
10
|
+
- session[session_key] = access_list_by_time = access_list_by_time.except(id)
|
11
|
+
|
12
|
+
- boards = Pairer::Board.where(org_id: session[:pairer_current_org_id], public_id: access_list_by_time.keys).map{|x| [x.public_id, x]}.to_h
|
13
|
+
|
14
|
+
- if boards.any?
|
15
|
+
.recently-accessed-boards.well.well-sm.space-above5
|
16
|
+
h1.space-above Recently Accessed Boards
|
17
|
+
|
18
|
+
ul style="list-style: none; padding-left: 0"
|
19
|
+
- access_list_by_time.each do |public_id, _time|
|
20
|
+
- board = boards[public_id]
|
21
|
+
|
22
|
+
- if board
|
23
|
+
li style="margin-top: 10px;" = link_to "#{board.name}", pairer.board_path(public_id), style: "color: #333; text-decoration: underline;"
|
24
|
+
- rescue => e
|
25
|
+
- session.delete(session_key)
|
26
|
+
- raise(e)
|
@@ -0,0 +1,6 @@
|
|
1
|
+
span.role data-role-name=role
|
2
|
+
= role
|
3
|
+
|
4
|
+
span.space-left2.delete
|
5
|
+
= link_to board_path(@board, remove_role_name: role), data: {method: :patch, confirm: "Are you sure you want to remove '#{role}' role ?"}, title: "Delete Role", class: 'btn btn-xs btn-danger' do
|
6
|
+
i.icon-trash
|
@@ -0,0 +1,37 @@
|
|
1
|
+
.stats
|
2
|
+
h3.space-above5
|
3
|
+
.pull-left
|
4
|
+
| Stats
|
5
|
+
= link_to "Reset Stats and Groups", board_path(@board, clear_board: true), class: 'btn btn-danger btn-sm space-left2', data: {method: :patch}
|
6
|
+
|
7
|
+
p
|
8
|
+
= form_tag board_path(@board), method: :patch, class: 'form-inline space-above3' do
|
9
|
+
small
|
10
|
+
span.space-right Statistics will only consider last
|
11
|
+
input.form-control.change-submit name="board[num_iterations_to_track]" value=@board.num_iterations_to_track style="width: 40px; height: 30px; padding: 0 7px; text-align: center;"
|
12
|
+
span.space-left iterations
|
13
|
+
|
14
|
+
- stats = @board.stats
|
15
|
+
|
16
|
+
- if stats.empty?
|
17
|
+
p Cannot show stats with only 1 person on the board
|
18
|
+
- else
|
19
|
+
table.table.table-condensed.table-bordered#stats style="max-width: 400px;"
|
20
|
+
thead
|
21
|
+
th Pair
|
22
|
+
th style="width: 90px;" Count
|
23
|
+
tbody
|
24
|
+
- people_by_id = @board.people.map{|x| [x.to_param, x] }.to_h
|
25
|
+
|
26
|
+
- stats.each do |person_ids, count|
|
27
|
+
tr
|
28
|
+
td = person_ids.map{|person_id| people_by_id[person_id]&.name || "<Person Removed>" }.sort.join(", ")
|
29
|
+
td.text-center = count
|
30
|
+
|
31
|
+
- if Rails.env.development?
|
32
|
+
.well.space-above5
|
33
|
+
h4 Debug Info
|
34
|
+
|
35
|
+
= "Number of Tracked Groups: #{@board.tracked_groups.size}"
|
36
|
+
br
|
37
|
+
= "Tracked Groups Iteration Numbers: #{@board.tracked_groups.collect(&:board_iteration_number).uniq.join(", ")}"
|
@@ -0,0 +1,7 @@
|
|
1
|
+
if(<%= raw @person.persisted? %>){
|
2
|
+
$(".unassigned-lists-container .person-list").append("<%= j render 'pairer/boards/person', person: @person %>")
|
3
|
+
|
4
|
+
$("form.new-person-form").find("input","select","textarea").val('')
|
5
|
+
}else if(<%= raw @person.errors[:name].present? %>){
|
6
|
+
alert("Name <%= @person.errors[:name].first %>");
|
7
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
.text-center style="max-width: 600px; margin: 0 auto;"
|
2
|
+
.well.well-sm
|
3
|
+
h1.space-above Find Existing Board
|
4
|
+
|
5
|
+
= form_tag nil, method: :get, class: 'form-inline' do
|
6
|
+
.form-group
|
7
|
+
label Board Password
|
8
|
+
input.form-control name="password" type="password" autofocus=true
|
9
|
+
|
10
|
+
button.btn.btn-sm.btn-success.space-left3 type="submit" Find Board
|
11
|
+
|
12
|
+
.well.well-sm.space-above5
|
13
|
+
h1.space-above Create New Board
|
14
|
+
|
15
|
+
.hint.small.space-below2 You can give out this password to give others access to your new board
|
16
|
+
|
17
|
+
= form_tag boards_path, method: :post, class: 'form-inline' do
|
18
|
+
.form-group
|
19
|
+
label Board Password
|
20
|
+
|
21
|
+
input.form-control.password name="password" type="password"
|
22
|
+
i.icon-eye-open.space-left title="Toggle Password Visibility" onclick="$('input.password').attr('type', ($('input.password').attr('type') == 'text' ? 'password' : 'text'))"
|
23
|
+
|
24
|
+
button.btn.btn-sm.btn-default.space-left3 type="submit"
|
25
|
+
i.icon-plus.space-right
|
26
|
+
span Create Board
|
27
|
+
|
28
|
+
= render "recently_accessed_boards"
|
@@ -0,0 +1 @@
|
|
1
|
+
$(".group-row[data-group-id=<%= @group.public_id %>] .group-lock-btn").replaceWith("<%= j render "group_lock_button", group: @group %>");
|
@@ -0,0 +1 @@
|
|
1
|
+
$(".person[data-person-id=<%= @person.public_id %>]").replaceWith("<%= j render "person", person: @person %>")
|
@@ -0,0 +1,240 @@
|
|
1
|
+
.pull-right
|
2
|
+
.text-right
|
3
|
+
button.btn.btn-default.btn-sm.space-right2 type="button" onclick=("$('.password-form').toggle()") Change Password
|
4
|
+
= link_to "Delete Board", board_path(@board), class: "btn btn-danger btn-sm", data: {method: :delete, confirm: "Are you sure you want to delete this board?"}
|
5
|
+
|
6
|
+
= form_tag board_path(@board), method: :patch, class: 'form-inline password-form', style: 'display:none' do
|
7
|
+
h3 Change Board Password
|
8
|
+
|
9
|
+
label Board Password
|
10
|
+
input.form-control.password name="board[password]" type="password" value=@board.password style="width: 100px;"
|
11
|
+
i.icon-eye-open.space-left onclick="$('input.password').attr('type', ($('input.password').attr('type') == 'text' ? 'password' : 'text'))"
|
12
|
+
|
13
|
+
span.space-left2
|
14
|
+
button.btn.btn-xs.btn-success type="submit" Update
|
15
|
+
|
16
|
+
= form_tag board_path(@board), method: :patch, class: 'form-inline' do
|
17
|
+
- if @board.errors.any?
|
18
|
+
.well.well-sm
|
19
|
+
div.bold Errors:
|
20
|
+
= @board.errors.full_messages.join("<br>").html_safe
|
21
|
+
|
22
|
+
div
|
23
|
+
.form-group
|
24
|
+
label Board Name
|
25
|
+
input.form-control.change-submit name="board[name]" value=@board.name style="width: 180px;"
|
26
|
+
|
27
|
+
br.visible-xs
|
28
|
+
span.space-left3.hidden-xs
|
29
|
+
|
30
|
+
.form-group
|
31
|
+
label Group Size
|
32
|
+
input.form-control.change-submit name="board[group_size]" value=@board.group_size style="width: 50px;"
|
33
|
+
|
34
|
+
.row
|
35
|
+
.col-sm-9.groups-container
|
36
|
+
h3
|
37
|
+
.pull-left.space-right2 Groups
|
38
|
+
= link_to create_group_board_path(@board), data: {method: :post}, remote: true, class: 'btn btn-sm btn-default' do
|
39
|
+
i.icon-plus.space-right
|
40
|
+
span Add Group
|
41
|
+
|
42
|
+
.text-center.space-below3 style="margin-top: -53px;"
|
43
|
+
= link_to shuffle_board_path(@board), class: "btn btn-success", data: {method: :post} do
|
44
|
+
i.icon-refresh.space-right
|
45
|
+
span Shuffle
|
46
|
+
|
47
|
+
= render "current_groups"
|
48
|
+
|
49
|
+
div
|
50
|
+
= link_to create_group_board_path(@board), data: {method: :post}, remote: true, class: 'btn btn-sm btn-default' do
|
51
|
+
i.icon-plus.space-right
|
52
|
+
span Add Group
|
53
|
+
|
54
|
+
.hidden-xs = render 'stats'
|
55
|
+
|
56
|
+
.col-sm-3.unassigned-lists-container
|
57
|
+
.well.space-above3.board-roles-container
|
58
|
+
h3.space-above
|
59
|
+
.pull-left.space-right3 Roles
|
60
|
+
|
61
|
+
= form_tag board_path(@board), method: :patch, class: 'form-inline space-below space-left3' do
|
62
|
+
= text_field_tag :add_role_name, nil, placeholder: "Role Name", style: "width: 150px;", class: 'form-control'
|
63
|
+
|
64
|
+
span.space-left
|
65
|
+
button.btn.btn-sm.btn-default type="submit" Add
|
66
|
+
|
67
|
+
- group_roles = @board.current_groups.flat_map(&:roles_array)
|
68
|
+
- unassigned_roles = @board.roles_array - group_roles
|
69
|
+
.roles-list data-prev-roles=unassigned_roles style="min-height: 25px;"
|
70
|
+
- unassigned_roles.each do |role|
|
71
|
+
= render 'role', role: role
|
72
|
+
|
73
|
+
.board-people-container
|
74
|
+
h3.space-above5
|
75
|
+
.pull-left.space-right3 People
|
76
|
+
|
77
|
+
= form_tag create_person_board_path(@board), remote: true, class: 'form-inline new-person-form space-below space-left3' do
|
78
|
+
= text_field_tag :name, nil, placeholder: "Name", style: "width: 150px;", class: 'form-control'
|
79
|
+
|
80
|
+
span.space-left
|
81
|
+
button.btn.btn-sm.btn-default type="submit" Add
|
82
|
+
|
83
|
+
- group_person_ids = @board.current_groups.flat_map(&:person_ids_array)
|
84
|
+
- unassigned_people = @board.people.select{|x| group_person_ids.exclude?(x.public_id) }.sort_by{|x| x.name }
|
85
|
+
.person-list data-prev-person-ids=unassigned_people.map(&:public_id) style="min-height: 40px;"
|
86
|
+
- unassigned_people.each do |person|
|
87
|
+
= render 'person', person: person
|
88
|
+
|
89
|
+
.visible-xs = render 'stats'
|
90
|
+
|
91
|
+
css:
|
92
|
+
.role,
|
93
|
+
.person{
|
94
|
+
display: inline-block;
|
95
|
+
border: 1px solid black;
|
96
|
+
border-radius: 4px;
|
97
|
+
padding: 0px 10px 3px 10px;
|
98
|
+
margin: 5px 5px;
|
99
|
+
line-height: 40px;
|
100
|
+
cursor: grab;
|
101
|
+
user-select: none;
|
102
|
+
|
103
|
+
box-shadow: 4px 4px rgba(0,0,0, 0.2);
|
104
|
+
}
|
105
|
+
|
106
|
+
.groups-container .person .delete,
|
107
|
+
.groups-container .role .delete{
|
108
|
+
display: none;
|
109
|
+
}
|
110
|
+
|
111
|
+
.group-sweep-btn{
|
112
|
+
background-color: none;
|
113
|
+
color: #333;
|
114
|
+
padding: 3px 5px;
|
115
|
+
line-height: 1;
|
116
|
+
font-size: 20px;
|
117
|
+
border: 1px solid #999;
|
118
|
+
}
|
119
|
+
.group-sweep-btn:hover{
|
120
|
+
background-color: #ede175;
|
121
|
+
color: #333;
|
122
|
+
}
|
123
|
+
.group-lock-btn{
|
124
|
+
padding: 5px 9px;
|
125
|
+
}
|
126
|
+
|
127
|
+
.btn-highlight{
|
128
|
+
background-color: orange;
|
129
|
+
color: white;
|
130
|
+
}
|
131
|
+
.btn-highlight:hover{
|
132
|
+
background-color: darkorange;
|
133
|
+
}
|
134
|
+
|
135
|
+
@media (min-width: 768px){
|
136
|
+
.unassigned-lists-container{
|
137
|
+
min-width: 360px;
|
138
|
+
}
|
139
|
+
.groups-container{
|
140
|
+
max-width: calc(100% - 360px);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
javascript:
|
145
|
+
$(function(){
|
146
|
+
|
147
|
+
$('.change-submit').on('change', function(){
|
148
|
+
var item = $(this);
|
149
|
+
setTimeout(function(){
|
150
|
+
if(item.val() == "" && item.is('select[multiple]')){
|
151
|
+
item.attr('disabled', true);
|
152
|
+
$("<input name='"+item.attr('name')+"' value='"+item.val()+"' />").insertBefore(item);
|
153
|
+
}
|
154
|
+
item.closest("form").submit();
|
155
|
+
}, 1);
|
156
|
+
});
|
157
|
+
|
158
|
+
window.init_sortable_lists = function(){
|
159
|
+
$(".person-list").sortable({
|
160
|
+
connectWith: ".person-list",
|
161
|
+
items: "> .person",
|
162
|
+
revert: true,
|
163
|
+
update: function(){
|
164
|
+
var group_id = $(this).data('group-id');
|
165
|
+
var prev_person_ids = $(this).data('prev-person-ids');
|
166
|
+
|
167
|
+
var $el = $(this);
|
168
|
+
|
169
|
+
if(!group_id){
|
170
|
+
// When on the unassigned list
|
171
|
+
return true; // true only skips ajax update, false cancels the entire item drag/move
|
172
|
+
}
|
173
|
+
|
174
|
+
var person_ids = [];
|
175
|
+
|
176
|
+
$el.find('.person').each(function(i, item){
|
177
|
+
person_ids.push($(item).data('person-id'));
|
178
|
+
});
|
179
|
+
|
180
|
+
if(equals(prev_person_ids.sort(), person_ids.sort())){
|
181
|
+
return true; // true only skips ajax update, false cancels the entire item drag/move
|
182
|
+
}
|
183
|
+
|
184
|
+
if(person_ids.length === 0){
|
185
|
+
person_ids.push(""); // to ensure empty array value make the params
|
186
|
+
}
|
187
|
+
|
188
|
+
$.ajax({
|
189
|
+
url: "#{update_group_board_path(@board, format: :js)}",
|
190
|
+
method: "POST",
|
191
|
+
data: {group_id: group_id, person_ids: person_ids},
|
192
|
+
}).done(function(){
|
193
|
+
$el.data('prev-person-ids', person_ids);
|
194
|
+
});
|
195
|
+
},
|
196
|
+
});
|
197
|
+
|
198
|
+
$(".roles-list").sortable({
|
199
|
+
connectWith: ".roles-list",
|
200
|
+
items: "> .role",
|
201
|
+
revert: true,
|
202
|
+
update: function(){
|
203
|
+
var group_id = $(this).data('group-id');
|
204
|
+
var prev_roles = $(this).data('prev-roles');
|
205
|
+
|
206
|
+
$el = $(this);
|
207
|
+
|
208
|
+
if(!group_id){
|
209
|
+
// When on the unassigned list
|
210
|
+
return true; // true only skips ajax update, false cancels the entire item drag/move
|
211
|
+
}
|
212
|
+
|
213
|
+
var roles = [];
|
214
|
+
|
215
|
+
$el.find('.role').each(function(i, item){
|
216
|
+
roles.push($(item).data('role-name'));
|
217
|
+
});
|
218
|
+
|
219
|
+
if(equals(prev_roles.sort(), roles.sort())){
|
220
|
+
return true; // true only skips ajax update, false cancels the entire item drag/move
|
221
|
+
}
|
222
|
+
|
223
|
+
if(roles.length === 0){
|
224
|
+
roles.push(""); // to ensure empty array value make the params
|
225
|
+
}
|
226
|
+
|
227
|
+
$.ajax({
|
228
|
+
url: "#{update_group_board_path(@board, format: :js)}",
|
229
|
+
method: "POST",
|
230
|
+
data: {group_id: group_id, roles: roles},
|
231
|
+
}).done(function(){
|
232
|
+
$el.data('prev-roles', roles);
|
233
|
+
});
|
234
|
+
},
|
235
|
+
});
|
236
|
+
};
|
237
|
+
|
238
|
+
window.init_sortable_lists();
|
239
|
+
|
240
|
+
});
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
.text-center style="max-width: 600px; margin: 0 auto;"
|
2
|
+
.well.well-sm
|
3
|
+
h1.space-above Sign In
|
4
|
+
|
5
|
+
= form_tag nil, method: :post, class: 'form-inline' do
|
6
|
+
.form-group
|
7
|
+
label Organization ID
|
8
|
+
input.form-control type="password" name="org_id" autofocus=true
|
9
|
+
|
10
|
+
button.btn.btn-sm.btn-success.space-left2 type="submit" Submit
|
@@ -0,0 +1,2 @@
|
|
1
|
+
svg width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32" class="iconify iconify--la"
|
2
|
+
path fill="currentColor" d="m28.281 2.281l-10 10L17 11v-.031l-.031-.031c-.64-.57-1.477-.844-2.282-.844c-.804 0-1.582.3-2.187.906l-.156.125l-.5.5l-.344.281L2.375 19l-.875.719L12.281 30.5l.719-.875l7.063-9.063l.03.032l1-1h.032l.031-.032c1.14-1.285 1.149-3.257-.062-4.468l-1.375-1.375l10-10zm-13.593 9.813a1.39 1.39 0 0 1 .906.312c.011.008.02.024.031.031l4.063 4.063c.375.375.41 1.172 0 1.688c-.016.019-.016.042-.032.062l-.312.281l-5.782-5.781l.344-.344c.192-.191.473-.304.781-.312zM12.03 14.03l5.938 5.938l-5.875 7.5l-1.438-1.438l2.156-2.25l-1.437-1.375l-2.125 2.219l-1.313-1.313l3.875-3.906L10.406 18L6.5 21.875l-1.969-1.969z"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Pairer::Engine.routes.draw do
|
2
|
+
get :sign_in, to: "sessions#sign_in"
|
3
|
+
post :sign_in, to: "sessions#sign_in"
|
4
|
+
get :sign_out, to: "sessions#sign_out"
|
5
|
+
|
6
|
+
resources :boards, controller: :boards, except: [:new, :edit] do
|
7
|
+
member do
|
8
|
+
post :shuffle
|
9
|
+
post :create_person
|
10
|
+
post :lock_person
|
11
|
+
delete :delete_person
|
12
|
+
post :create_group
|
13
|
+
post :lock_group
|
14
|
+
delete :delete_group
|
15
|
+
post :update_group
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/robots', to: 'application#robots', constraints: ->(req){ req.format == :text }
|
20
|
+
|
21
|
+
match '*a', to: 'application#render_404', via: :get
|
22
|
+
|
23
|
+
get "/", to: "boards#index"
|
24
|
+
|
25
|
+
root "boards#index"
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class AddPairerTables < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
|
4
|
+
create_table :pairer_boards do |t|
|
5
|
+
t.string :name, :password
|
6
|
+
t.text :roles
|
7
|
+
t.integer :current_iteration_number, null: false
|
8
|
+
t.integer :group_size
|
9
|
+
t.integer :num_iterations_to_track, null: false
|
10
|
+
t.timestamps
|
11
|
+
t.string :public_id, index: true
|
12
|
+
t.string :org_id
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :pairer_people do |t|
|
16
|
+
t.references :board
|
17
|
+
t.string :name
|
18
|
+
t.timestamps
|
19
|
+
t.boolean :locked, default: false, null: false
|
20
|
+
t.string :public_id, index: true
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :pairer_groups do |t|
|
24
|
+
t.references :board
|
25
|
+
t.integer :board_iteration_number
|
26
|
+
t.timestamps
|
27
|
+
t.boolean :locked, default: false, null: false
|
28
|
+
t.text :person_ids
|
29
|
+
t.text :roles
|
30
|
+
t.string :public_id, index: true
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/db/seeds.rb
ADDED
File without changes
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Pairer
|
2
|
+
class Config
|
3
|
+
|
4
|
+
@@allowed_org_ids = []
|
5
|
+
mattr_reader :allowed_org_ids
|
6
|
+
|
7
|
+
def self.allowed_org_ids=(val)
|
8
|
+
if val.is_a?(Array)
|
9
|
+
@@allowed_org_ids = val.collect(&:presence).compact
|
10
|
+
else
|
11
|
+
raise "Must be an array"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
@@hash_id_salt = true
|
16
|
+
mattr_reader :hash_id_salt
|
17
|
+
|
18
|
+
def self.hash_id_salt=(val)
|
19
|
+
if val.is_a?(String)
|
20
|
+
@@hash_id_salt = val
|
21
|
+
else
|
22
|
+
raise ArgumentError.new("hash_id_salt must be String")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
@@max_iterations_to_track = 100
|
27
|
+
mattr_reader :max_iterations_to_track
|
28
|
+
|
29
|
+
def self.max_iterations_to_track=(val)
|
30
|
+
if val.is_a?(Integer) && val >= 1
|
31
|
+
@@max_iterations_to_track = val
|
32
|
+
else
|
33
|
+
raise "Must be a positive integer"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'slim'
|
2
|
+
require 'sassc-rails'
|
3
|
+
require 'bootstrap-sass'
|
4
|
+
require 'bootswatch-rails'
|
5
|
+
require 'hashids'
|
6
|
+
|
7
|
+
module Pairer
|
8
|
+
class Engine < ::Rails::Engine
|
9
|
+
isolate_namespace Pairer
|
10
|
+
|
11
|
+
initializer "pairer.assets.precompile" do |app|
|
12
|
+
app.config.assets.precompile << "pairer_manifest.js" ### manifest file required
|
13
|
+
app.config.assets.precompile << "pairer/favicon.ico"
|
14
|
+
|
15
|
+
### Automatically precompile assets in specified folders
|
16
|
+
["app/assets/images/"].each do |folder|
|
17
|
+
dir = app.root.join(folder)
|
18
|
+
|
19
|
+
if Dir.exist?(dir)
|
20
|
+
Dir.glob(File.join(dir, "**/*")).each do |f|
|
21
|
+
asset_name = f.to_s
|
22
|
+
.split(folder).last # Remove fullpath
|
23
|
+
.sub(/^\/*/, '') ### Remove leading '/'
|
24
|
+
|
25
|
+
app.config.assets.precompile << asset_name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
initializer "pairer.append_migrations" do |app|
|
32
|
+
### Automatically load all migrations into main rails app
|
33
|
+
|
34
|
+
if !app.root.to_s.match?(root.to_s)
|
35
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
36
|
+
app.config.paths["db/migrate"] << expanded_path
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
initializer "pairer.load_static_assets" do |app|
|
42
|
+
### Expose static assets
|
43
|
+
app.middleware.use ::ActionDispatch::Static, "#{root}/public"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/pairer.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "pairer/engine"
|
2
|
+
require "pairer/config"
|
3
|
+
|
4
|
+
module Pairer
|
5
|
+
|
6
|
+
def self.config(&block)
|
7
|
+
c = Pairer::Config
|
8
|
+
|
9
|
+
if block_given?
|
10
|
+
block.call(c)
|
11
|
+
else
|
12
|
+
return c
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
if RUBY_VERSION.to_f <= 2.6 && !Array.new.respond_to?(:intersection)
|
19
|
+
Array.class_eval do
|
20
|
+
def intersection(other)
|
21
|
+
self & other
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|