reviewkit 0.1.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/CHANGELOG.md +23 -0
- data/CODE_OF_CONDUCT.md +123 -0
- data/CONTRIBUTING.md +44 -0
- data/MIT-LICENSE +20 -0
- data/README.md +335 -0
- data/Rakefile +7 -0
- data/SECURITY.md +18 -0
- data/app/assets/builds/reviewkit/application.css +2 -0
- data/app/assets/javascripts/reviewkit/application.js +12 -0
- data/app/assets/javascripts/reviewkit/controllers/file_nav_controller.js +24 -0
- data/app/assets/javascripts/reviewkit/controllers/review_index_controller.js +84 -0
- data/app/assets/tailwind/reviewkit/application.css +865 -0
- data/app/controllers/reviewkit/application_controller.rb +80 -0
- data/app/controllers/reviewkit/comments_controller.rb +147 -0
- data/app/controllers/reviewkit/review_threads_controller.rb +277 -0
- data/app/controllers/reviewkit/reviews_controller.rb +142 -0
- data/app/helpers/reviewkit/application_helper.rb +12 -0
- data/app/helpers/reviewkit/asset_helper.rb +39 -0
- data/app/helpers/reviewkit/diff_helper.rb +230 -0
- data/app/helpers/reviewkit/flash_helper.rb +36 -0
- data/app/helpers/reviewkit/frame_helper.rb +37 -0
- data/app/helpers/reviewkit/icon_helper.rb +107 -0
- data/app/helpers/reviewkit/review_thread_helper.rb +54 -0
- data/app/models/concerns/reviewkit/notifies_lifecycle_events.rb +39 -0
- data/app/models/reviewkit/application_record.rb +7 -0
- data/app/models/reviewkit/comment.rb +29 -0
- data/app/models/reviewkit/current.rb +7 -0
- data/app/models/reviewkit/document.rb +79 -0
- data/app/models/reviewkit/review.rb +66 -0
- data/app/models/reviewkit/review_thread.rb +75 -0
- data/app/services/reviewkit/diffs/intraline_budget.rb +40 -0
- data/app/services/reviewkit/diffs/intraline_diff.rb +220 -0
- data/app/services/reviewkit/diffs/split_diff.rb +112 -0
- data/app/services/reviewkit/reviews/create.rb +57 -0
- data/app/views/layouts/reviewkit/application.html.erb +15 -0
- data/app/views/reviewkit/comments/_comment.html.erb +53 -0
- data/app/views/reviewkit/comments/_edit_form.html.erb +26 -0
- data/app/views/reviewkit/comments/_form.html.erb +16 -0
- data/app/views/reviewkit/review_threads/_bucket.html.erb +53 -0
- data/app/views/reviewkit/review_threads/_bucket_frame.html.erb +13 -0
- data/app/views/reviewkit/review_threads/_bucket_row.html.erb +55 -0
- data/app/views/reviewkit/review_threads/_edit_form.html.erb +29 -0
- data/app/views/reviewkit/review_threads/_thread.html.erb +87 -0
- data/app/views/reviewkit/reviews/_document.html.erb +41 -0
- data/app/views/reviewkit/reviews/_document_split.html.erb +73 -0
- data/app/views/reviewkit/reviews/_document_unified.html.erb +57 -0
- data/app/views/reviewkit/reviews/_edit_form.html.erb +35 -0
- data/app/views/reviewkit/reviews/_index_content.html.erb +160 -0
- data/app/views/reviewkit/reviews/_review_sidebar.html.erb +70 -0
- data/app/views/reviewkit/reviews/_show_content.html.erb +164 -0
- data/app/views/reviewkit/reviews/index.html.erb +11 -0
- data/app/views/reviewkit/reviews/show.html.erb +11 -0
- data/app/views/reviewkit/shared/_flash.html.erb +10 -0
- data/bin/console +4 -0
- data/bin/lint +4 -0
- data/bin/rails +14 -0
- data/bin/setup +9 -0
- data/bin/test +4 -0
- data/config/importmap.rb +6 -0
- data/config/routes.rb +24 -0
- data/db/migrate/20260331181500_create_reviewkit_reviews.rb +19 -0
- data/db/migrate/20260331181600_create_reviewkit_documents.rb +23 -0
- data/db/migrate/20260331181700_create_reviewkit_review_threads.rb +23 -0
- data/db/migrate/20260331181800_create_reviewkit_comments.rb +15 -0
- data/db/migrate/20260401093000_add_description_to_reviewkit_reviews.rb +7 -0
- data/lib/generators/reviewkit/controllers/controllers_generator.rb +24 -0
- data/lib/generators/reviewkit/controllers/templates/comments_controller_extension.rb +13 -0
- data/lib/generators/reviewkit/controllers/templates/review_threads_controller_extension.rb +13 -0
- data/lib/generators/reviewkit/controllers/templates/reviews_controller_extension.rb +19 -0
- data/lib/generators/reviewkit/install/install_generator.rb +52 -0
- data/lib/generators/reviewkit/install/templates/importmap.rb +3 -0
- data/lib/generators/reviewkit/install/templates/reviewkit.rb +19 -0
- data/lib/generators/reviewkit/models/models_generator.rb +24 -0
- data/lib/generators/reviewkit/models/templates/comment_extension.rb +21 -0
- data/lib/generators/reviewkit/models/templates/review_extension.rb +22 -0
- data/lib/generators/reviewkit/models/templates/review_thread_extension.rb +21 -0
- data/lib/generators/reviewkit/views/views_generator.rb +15 -0
- data/lib/reviewkit/configuration.rb +33 -0
- data/lib/reviewkit/engine.rb +67 -0
- data/lib/reviewkit/version.rb +5 -0
- data/lib/reviewkit.rb +26 -0
- data/lib/tasks/reviewkit_tasks.rake +12 -0
- data/sig/reviewkit.rbs +129 -0
- metadata +238 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<main class="<%= reviewkit_frame_request? ? "reviewkit-page reviewkit-page--embedded" : "reviewkit-page" %>">
|
|
2
|
+
<% additions = review.documents.sum(&:additions_count) %>
|
|
3
|
+
<% deletions = review.documents.sum(&:deletions_count) %>
|
|
4
|
+
<% frame_id = reviewkit_review_frame_id(review) %>
|
|
5
|
+
|
|
6
|
+
<section class="reviewkit-review-layout" data-controller="reviewkit--file-nav">
|
|
7
|
+
<%= render "reviewkit/reviews/review_sidebar",
|
|
8
|
+
review: review,
|
|
9
|
+
selected_document: @selected_document,
|
|
10
|
+
view_mode: @view_mode,
|
|
11
|
+
frame_id: frame_id %>
|
|
12
|
+
|
|
13
|
+
<div class="reviewkit-review-main">
|
|
14
|
+
<% if reviewkit_frame_request? && (flash[:notice].present? || flash[:alert].present?) %>
|
|
15
|
+
<%= render "reviewkit/shared/flash" %>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<header class="reviewkit-review-header">
|
|
19
|
+
<div class="reviewkit-review-header__top">
|
|
20
|
+
<% if @editing_review %>
|
|
21
|
+
<%= render "reviewkit/reviews/edit_form",
|
|
22
|
+
review: review,
|
|
23
|
+
frame_id: frame_id,
|
|
24
|
+
selected_document: @selected_document,
|
|
25
|
+
view_mode: @view_mode %>
|
|
26
|
+
<% else %>
|
|
27
|
+
<div class="space-y-2">
|
|
28
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
29
|
+
<h1 class="reviewkit-review-title"><%= review.title %></h1>
|
|
30
|
+
<span class="<%= reviewkit_status_pill_class(review) %>"><%= review.status %></span>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<% if review.description.present? %>
|
|
34
|
+
<p class="reviewkit-review-description"><%= review.description %></p>
|
|
35
|
+
<% end %>
|
|
36
|
+
</div>
|
|
37
|
+
<% end %>
|
|
38
|
+
|
|
39
|
+
<div class="reviewkit-review-actions">
|
|
40
|
+
<% unless @editing_review %>
|
|
41
|
+
<% approval_blocked = review.open_threads_count.positive? %>
|
|
42
|
+
|
|
43
|
+
<% unless review.approved? %>
|
|
44
|
+
<%= button_to "Approve",
|
|
45
|
+
approve_review_path(review, document_id: @selected_document&.id, view: @view_mode),
|
|
46
|
+
method: :patch,
|
|
47
|
+
class: "reviewkit-button reviewkit-button--success",
|
|
48
|
+
disabled: approval_blocked,
|
|
49
|
+
title: ("Resolve or mark open threads outdated before approving." if approval_blocked),
|
|
50
|
+
form: { data: { turbo_frame: frame_id } } %>
|
|
51
|
+
<% end %>
|
|
52
|
+
|
|
53
|
+
<% unless review.rejected? %>
|
|
54
|
+
<%= button_to "Reject",
|
|
55
|
+
reject_review_path(review, document_id: @selected_document&.id, view: @view_mode),
|
|
56
|
+
method: :patch,
|
|
57
|
+
class: "reviewkit-button reviewkit-button--danger",
|
|
58
|
+
form: { data: { turbo_frame: frame_id } } %>
|
|
59
|
+
<% end %>
|
|
60
|
+
|
|
61
|
+
<div class="reviewkit-inline-actions" aria-label="Review actions">
|
|
62
|
+
<%= link_to edit_review_path(review, document_id: @selected_document&.id, view: @view_mode),
|
|
63
|
+
class: "reviewkit-inline-icon-button",
|
|
64
|
+
data: { turbo_frame: frame_id },
|
|
65
|
+
aria: { label: "Edit review" },
|
|
66
|
+
title: "Edit review" do %>
|
|
67
|
+
<%= reviewkit_pencil_icon %>
|
|
68
|
+
<% end %>
|
|
69
|
+
|
|
70
|
+
<%= link_to review_path(review),
|
|
71
|
+
class: "reviewkit-inline-icon-button reviewkit-inline-icon-button--danger",
|
|
72
|
+
data: {
|
|
73
|
+
turbo_confirm: "Delete this review?",
|
|
74
|
+
turbo_frame: "_top",
|
|
75
|
+
turbo_method: :delete
|
|
76
|
+
},
|
|
77
|
+
aria: { label: "Delete review" },
|
|
78
|
+
title: "Delete review" do %>
|
|
79
|
+
<%= reviewkit_trash_icon %>
|
|
80
|
+
<% end %>
|
|
81
|
+
</div>
|
|
82
|
+
<% end %>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="reviewkit-review-meta">
|
|
87
|
+
<% if review.external_reference.present? %>
|
|
88
|
+
<span class="reviewkit-review-meta-item">
|
|
89
|
+
<span class="font-semibold text-slate-700">Reference:</span> <%= review.external_reference %>
|
|
90
|
+
</span>
|
|
91
|
+
<% end %>
|
|
92
|
+
<% if review.creator.present? %>
|
|
93
|
+
<span class="reviewkit-review-meta-item">
|
|
94
|
+
<span class="font-semibold text-slate-700">Author:</span> <%= reviewkit_actor_label(review.creator) %>
|
|
95
|
+
</span>
|
|
96
|
+
<% end %>
|
|
97
|
+
<span class="reviewkit-review-meta-item"><%= pluralize(review.documents.size, "file") %> changed</span>
|
|
98
|
+
<span class="reviewkit-review-meta-item"><%= pluralize(review.open_threads_count, "open thread") %></span>
|
|
99
|
+
<span class="reviewkit-review-meta-item"><%= pluralize(review.resolved_threads_count, "resolved thread") %></span>
|
|
100
|
+
<span class="reviewkit-review-meta-item">Updated <%= time_ago_in_words(review.updated_at) %> ago</span>
|
|
101
|
+
</div>
|
|
102
|
+
</header>
|
|
103
|
+
|
|
104
|
+
<div class="reviewkit-review-mobile-summary xl:hidden">
|
|
105
|
+
<span><%= pluralize(review.documents.size, "file") %></span>
|
|
106
|
+
<span><%= pluralize(review.open_threads_count, "open thread") %></span>
|
|
107
|
+
<span><%= pluralize(review.resolved_threads_count, "resolved thread") %></span>
|
|
108
|
+
<% if review.external_reference.present? %>
|
|
109
|
+
<span><%= review.external_reference %></span>
|
|
110
|
+
<% end %>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<% if review.metadata.present? %>
|
|
114
|
+
<details class="reviewkit-sidebar-details xl:hidden">
|
|
115
|
+
<summary>Review metadata</summary>
|
|
116
|
+
<pre class="reviewkit-code-panel"><%= JSON.pretty_generate(review.metadata) %></pre>
|
|
117
|
+
</details>
|
|
118
|
+
<% end %>
|
|
119
|
+
|
|
120
|
+
<div class="reviewkit-files-header">
|
|
121
|
+
<div class="flex items-center gap-2">
|
|
122
|
+
<h2 class="reviewkit-files-title">Files changed</h2>
|
|
123
|
+
<span class="reviewkit-files-count"><%= review.documents.size %></span>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div class="reviewkit-files-header__controls">
|
|
127
|
+
<div class="reviewkit-files-diffstat" aria-label="<%= "#{additions} additions and #{deletions} deletions" %>">
|
|
128
|
+
<span class="font-medium text-emerald-700">+<%= additions %></span>
|
|
129
|
+
<span class="font-medium text-rose-700">-<%= deletions %></span>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div class="reviewkit-view-toggle" role="group" aria-label="Diff layout">
|
|
133
|
+
<%= link_to "Split",
|
|
134
|
+
review_path(review, document_id: @selected_document&.id, view: "split"),
|
|
135
|
+
class: [ "reviewkit-view-toggle__button", ("reviewkit-view-toggle__button--active" if @view_mode == "split") ].compact.join(" "),
|
|
136
|
+
data: { turbo_frame: frame_id } %>
|
|
137
|
+
<%= link_to "Unified",
|
|
138
|
+
review_path(review, document_id: @selected_document&.id, view: "unified"),
|
|
139
|
+
class: [ "reviewkit-view-toggle__button", ("reviewkit-view-toggle__button--active" if @view_mode == "unified") ].compact.join(" "),
|
|
140
|
+
data: { turbo_frame: frame_id } %>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<section class="reviewkit-files-list">
|
|
146
|
+
<% if @selected_document.present? %>
|
|
147
|
+
<%= render "reviewkit/reviews/document",
|
|
148
|
+
document: @selected_document,
|
|
149
|
+
frame_id: frame_id,
|
|
150
|
+
open_thread_line_code: @open_thread_line_code,
|
|
151
|
+
open_thread_side: @open_thread_side,
|
|
152
|
+
review: review,
|
|
153
|
+
view_mode: @view_mode,
|
|
154
|
+
thread_index: thread_index %>
|
|
155
|
+
<% else %>
|
|
156
|
+
<div class="reviewkit-panel reviewkit-empty-state">
|
|
157
|
+
<p class="reviewkit-kicker">No files available</p>
|
|
158
|
+
<h2 class="text-2xl font-semibold tracking-tight text-slate-950">This review does not have any documents yet.</h2>
|
|
159
|
+
</div>
|
|
160
|
+
<% end %>
|
|
161
|
+
</section>
|
|
162
|
+
</div>
|
|
163
|
+
</section>
|
|
164
|
+
</main>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<% unless reviewkit_frame_request? %>
|
|
2
|
+
<% content_for :head do %>
|
|
3
|
+
<%= reviewkit_page_assets %>
|
|
4
|
+
<% end %>
|
|
5
|
+
|
|
6
|
+
<%= reviewkit_page_module_tag %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= reviewkit_wrap_in_frame do %>
|
|
10
|
+
<%= render "reviewkit/reviews/index_content", reviews: @reviews %>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<% unless reviewkit_frame_request? %>
|
|
2
|
+
<% content_for :head do %>
|
|
3
|
+
<%= reviewkit_page_assets %>
|
|
4
|
+
<% end %>
|
|
5
|
+
|
|
6
|
+
<%= reviewkit_page_module_tag %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= turbo_frame_tag reviewkit_review_frame_id(@review) do %>
|
|
10
|
+
<%= render "reviewkit/reviews/show_content", review: @review, thread_index: @thread_index %>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<% flash.each do |type, message| %>
|
|
2
|
+
<% next if message.blank? %>
|
|
3
|
+
|
|
4
|
+
<div class="<%= reviewkit_flash_shell_class %>" data-reviewkit-flash-type="<%= type %>">
|
|
5
|
+
<div class="<%= reviewkit_flash_class(type) %>" role="<%= reviewkit_flash_role(type) %>">
|
|
6
|
+
<span class="reviewkit-flash__label"><%= reviewkit_flash_label(type) %></span>
|
|
7
|
+
<span><%= message %></span>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<% end %>
|
data/bin/console
ADDED
data/bin/lint
ADDED
data/bin/rails
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
|
3
|
+
# installed from the root of your application.
|
|
4
|
+
|
|
5
|
+
ENGINE_ROOT = File.expand_path("..", __dir__)
|
|
6
|
+
ENGINE_PATH = File.expand_path("../lib/reviewkit/engine", __dir__)
|
|
7
|
+
APP_PATH = File.expand_path("../spec/dummy/config/application", __dir__)
|
|
8
|
+
|
|
9
|
+
# Set up gems listed in the Gemfile.
|
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
|
11
|
+
require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
|
|
12
|
+
|
|
13
|
+
require "rails/all"
|
|
14
|
+
require "rails/engine/commands"
|
data/bin/setup
ADDED
data/bin/test
ADDED
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
|
|
2
|
+
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
|
|
3
|
+
|
|
4
|
+
pin "reviewkit/application", to: "reviewkit/application.js", preload: true
|
|
5
|
+
pin "reviewkit/controllers/file_nav_controller", to: "reviewkit/controllers/file_nav_controller.js"
|
|
6
|
+
pin "reviewkit/controllers/review_index_controller", to: "reviewkit/controllers/review_index_controller.js"
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Reviewkit::Engine.routes.draw do
|
|
4
|
+
root to: "reviews#index"
|
|
5
|
+
|
|
6
|
+
resources :reviews, only: %i[index show edit update destroy] do
|
|
7
|
+
member do
|
|
8
|
+
patch :approve
|
|
9
|
+
patch :reject
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
resources :review_threads, only: :create, path: "threads"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
resources :review_threads, only: %i[show edit update destroy] do
|
|
16
|
+
member do
|
|
17
|
+
patch :mark_outdated
|
|
18
|
+
patch :resolve
|
|
19
|
+
patch :reopen
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
resources :comments, only: %i[create show edit update destroy]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateReviewkitReviews < ActiveRecord::Migration[8.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :reviewkit_reviews do |t|
|
|
6
|
+
t.string :title, null: false
|
|
7
|
+
t.string :status, null: false, default: "draft"
|
|
8
|
+
t.references :reviewable, polymorphic: true, null: true
|
|
9
|
+
t.string :external_reference
|
|
10
|
+
t.references :creator, polymorphic: true, null: true
|
|
11
|
+
t.json :metadata, null: false, default: {}
|
|
12
|
+
|
|
13
|
+
t.timestamps
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
add_index :reviewkit_reviews, :status
|
|
17
|
+
add_index :reviewkit_reviews, :external_reference
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateReviewkitDocuments < ActiveRecord::Migration[8.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :reviewkit_documents do |t|
|
|
6
|
+
t.references :review, null: false, foreign_key: { to_table: :reviewkit_reviews }
|
|
7
|
+
t.string :path, null: false
|
|
8
|
+
t.string :language, null: false, default: "plaintext"
|
|
9
|
+
t.string :status, null: false, default: "modified"
|
|
10
|
+
t.integer :position, null: false, default: 0
|
|
11
|
+
t.text :old_content
|
|
12
|
+
t.text :new_content
|
|
13
|
+
t.json :diff_cache, null: false, default: {}
|
|
14
|
+
t.json :metadata, null: false, default: {}
|
|
15
|
+
|
|
16
|
+
t.timestamps
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
add_index :reviewkit_documents, %i[review_id position]
|
|
20
|
+
add_index :reviewkit_documents, %i[review_id path], unique: true
|
|
21
|
+
add_index :reviewkit_documents, :status
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateReviewkitReviewThreads < ActiveRecord::Migration[8.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :reviewkit_review_threads do |t|
|
|
6
|
+
t.references :review, null: false, foreign_key: { to_table: :reviewkit_reviews }
|
|
7
|
+
t.references :document, null: false, foreign_key: { to_table: :reviewkit_documents }
|
|
8
|
+
t.string :status, null: false, default: "open"
|
|
9
|
+
t.string :side, null: false, default: "new"
|
|
10
|
+
t.integer :old_line
|
|
11
|
+
t.integer :new_line
|
|
12
|
+
t.string :line_code, null: false
|
|
13
|
+
t.datetime :resolved_at
|
|
14
|
+
t.references :resolved_by, polymorphic: true, null: true
|
|
15
|
+
t.json :metadata, null: false, default: {}
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
add_index :reviewkit_review_threads, %i[document_id line_code]
|
|
21
|
+
add_index :reviewkit_review_threads, :status
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateReviewkitComments < ActiveRecord::Migration[8.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :reviewkit_comments do |t|
|
|
6
|
+
t.references :review_thread, null: false, foreign_key: { to_table: :reviewkit_review_threads }
|
|
7
|
+
t.references :author, polymorphic: true, null: true
|
|
8
|
+
t.text :body, null: false
|
|
9
|
+
t.datetime :edited_at
|
|
10
|
+
t.json :metadata, null: false, default: {}
|
|
11
|
+
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Reviewkit
|
|
6
|
+
module Generators
|
|
7
|
+
class ControllersGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
def copy_controller_extensions
|
|
11
|
+
template "reviews_controller_extension.rb", "app/controllers/concerns/reviewkit/reviews_controller_extension.rb"
|
|
12
|
+
template "review_threads_controller_extension.rb", "app/controllers/concerns/reviewkit/review_threads_controller_extension.rb"
|
|
13
|
+
template "comments_controller_extension.rb", "app/controllers/concerns/reviewkit/comments_controller_extension.rb"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def print_instructions
|
|
17
|
+
say ""
|
|
18
|
+
say "Reviewkit controller extensions installed.", :green
|
|
19
|
+
say "The engine will automatically prepend these modules on reload."
|
|
20
|
+
say "Use them to extend permitted params, scopes, redirects, and review flow behavior."
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reviewkit
|
|
4
|
+
module ReviewsControllerExtension
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def permitted_review_attributes
|
|
8
|
+
super
|
|
9
|
+
# Example:
|
|
10
|
+
# super + %i[review_type]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def review_transition_failure_message(review)
|
|
14
|
+
super
|
|
15
|
+
# Example:
|
|
16
|
+
# "#{super} Resolve content approval blockers before marking this review approved."
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Reviewkit
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
class_option :mount,
|
|
11
|
+
type: :boolean,
|
|
12
|
+
default: true,
|
|
13
|
+
desc: "Mount Reviewkit::Engine into the host app routes."
|
|
14
|
+
class_option :mount_path,
|
|
15
|
+
type: :string,
|
|
16
|
+
default: "/reviewkit",
|
|
17
|
+
desc: "Mount path to use when inserting the engine route."
|
|
18
|
+
|
|
19
|
+
def copy_initializer
|
|
20
|
+
template "reviewkit.rb", "config/initializers/reviewkit.rb"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ensure_importmap
|
|
24
|
+
return if File.exist?(File.join(destination_root, "config", "importmap.rb"))
|
|
25
|
+
|
|
26
|
+
template "importmap.rb", "config/importmap.rb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def mount_engine
|
|
30
|
+
return unless options[:mount]
|
|
31
|
+
|
|
32
|
+
route %(mount Reviewkit::Engine => "#{options[:mount_path]}")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def install_migrations
|
|
36
|
+
rake "reviewkit:install:migrations"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def print_instructions
|
|
40
|
+
say ""
|
|
41
|
+
say "Reviewkit installed.", :green
|
|
42
|
+
say "Next steps:"
|
|
43
|
+
say " 1. Run bundle exec rails db:migrate"
|
|
44
|
+
say " 2. Visit #{options[:mount_path]} once you create review data"
|
|
45
|
+
say " 3. Keep javascript_importmap_tags in your layout or use reviewkit_assets for a custom Reviewkit layout"
|
|
46
|
+
say " 4. Optionally run rails g reviewkit:views to copy the shipped UI into the host app"
|
|
47
|
+
say " 5. Optionally run rails g reviewkit:models for host-side validations, scopes, and callbacks"
|
|
48
|
+
say " 6. Optionally run rails g reviewkit:controllers to extend permitted params and controller flow"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Reviewkit.configure do |config|
|
|
4
|
+
config.current_actor = lambda do |controller|
|
|
5
|
+
controller.respond_to?(:current_user, true) ? controller.send(:current_user) : nil
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
config.authorize_action = lambda do |_controller, _action, _record = nil, **_context|
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
config.layout = "reviewkit/application"
|
|
13
|
+
|
|
14
|
+
# Keep intraline highlighting conservative by default so large reviews
|
|
15
|
+
# fall back to line-level diffs before they become expensive.
|
|
16
|
+
config.intraline_limits.max_review_files = 50
|
|
17
|
+
config.intraline_limits.max_changed_lines = 50
|
|
18
|
+
config.intraline_limits.max_line_length = 500
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Reviewkit
|
|
6
|
+
module Generators
|
|
7
|
+
class ModelsGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
def copy_model_extensions
|
|
11
|
+
template "review_extension.rb", "app/models/concerns/reviewkit/review_extension.rb"
|
|
12
|
+
template "review_thread_extension.rb", "app/models/concerns/reviewkit/review_thread_extension.rb"
|
|
13
|
+
template "comment_extension.rb", "app/models/concerns/reviewkit/comment_extension.rb"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def print_instructions
|
|
17
|
+
say ""
|
|
18
|
+
say "Reviewkit model extensions installed.", :green
|
|
19
|
+
say "The engine will automatically include these concerns on reload."
|
|
20
|
+
say "Use them to add validations, scopes, and standard Active Record callbacks."
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reviewkit
|
|
4
|
+
module CommentExtension
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
# Example:
|
|
9
|
+
#
|
|
10
|
+
# before_validation :normalize_comment_source
|
|
11
|
+
# after_update :track_comment_edits, if: :saved_change_to_body?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def normalize_comment_source
|
|
17
|
+
# Example:
|
|
18
|
+
# metadata["source"] ||= "host_app"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reviewkit
|
|
4
|
+
module ReviewExtension
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
# Example:
|
|
9
|
+
#
|
|
10
|
+
# validates :review_type, presence: true
|
|
11
|
+
#
|
|
12
|
+
# after_update :notify_host_workflow, if: :saved_change_to_status?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def notify_host_workflow
|
|
18
|
+
# Example:
|
|
19
|
+
# HostReviewSyncJob.perform_later(id) if approved?
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Reviewkit
|
|
4
|
+
module ReviewThreadExtension
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
# Example:
|
|
9
|
+
#
|
|
10
|
+
# validate :ensure_thread_context_is_present
|
|
11
|
+
# after_update :notify_thread_status_change, if: :saved_change_to_status?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def ensure_thread_context_is_present
|
|
17
|
+
# Example:
|
|
18
|
+
# errors.add(:metadata, "must include a resource_id") if metadata["resource_id"].blank?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Reviewkit
|
|
6
|
+
module Generators
|
|
7
|
+
class ViewsGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("../../../../", __dir__)
|
|
9
|
+
|
|
10
|
+
def copy_views
|
|
11
|
+
directory "app/views/reviewkit", "app/views/reviewkit"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|