From f122635caf4d720598e33f522f9ca81b258d0449 Mon Sep 17 00:00:00 2001 From: Jay Varner Date: Mon, 10 Aug 2026 16:14:14 -0400 Subject: [PATCH 1/5] Upgrade administrate to 1.0.0 (#14) * Clear administrate 1.0 upgrade blockers Vendors an in-house ActsAsTaggableField replacing the unmaintained administrate-field-acts_as_taggable gem (last released 2021, hard-pins administrate < 1.0.0), preserving the same selectize-based tag UI. Bumps administrate-field-jsonb to 0.4.8, which allows administrate < 2.0 instead of < 1.0.0. Also fixes two unrelated pre-existing bugs found while adding test coverage: reversed accessible_action? arguments in the media show and mentions edit views, and a flat-vs-nested iteration bug in the mentions form that raised NoMethodError on new/edit. Media's show page also called a nonexistent Medium#url; it now prefers the attached ActiveStorage image, falling back to the link column. Co-Authored-By: Claude Sonnet 5 * Upgrade administrate to 1.0.0 Restores sprockets-rails/sassc-rails as direct dependencies - administrate 1.0 dropped them transitively (it now bundles its own precompiled assets), but the app's own asset pipeline (app/assets/config/manifest.js) relies on them directly, and administrate-field-jsonb's stylesheet chain needs real Sass compilation. Drops the now-dead `//= require selectize` manifest line; administrate 1.0 bundles its own copy inside its single built JS file. Also fixes the admin system-test harness: it authenticated by embedding credentials in the visited URL (this API-only app has no session-based admin login), which worked fine until Turbo - newly bundled in 1.0 - started calling history.replaceState on every page load. Chrome rejects replaceState targets carrying userinfo, and since Administrate's JS is one bundled IIFE, that uncaught exception aborted everything after it, including selectize's own setup. Real admin users never hit this (their location.href never carries credentials - Chrome only uses them for the auth handshake), but it broke the tests. Switched to Selenium's CDP-based driver.register(username:, password:) so system specs authenticate without touching the URL. Verified manually in a real browser in addition to the full suite (322 examples, 0 failures). Co-Authored-By: Claude Sonnet 5 --------- Co-authored-by: Claude Sonnet 5 --- Gemfile | 11 +++- Gemfile.lock | 34 +++++------ app/assets/config/manifest.js | 4 +- .../javascripts/acts_as_taggable_field.js | 20 +++++++ .../stylesheets/acts_as_taggable_field.css | 9 +++ app/dashboards/mention_dashboard.rb | 2 +- app/fields/acts_as_taggable_field.rb | 56 +++++++++++++++++ app/views/admin/media/show.html.erb | 5 +- app/views/admin/mentions/_form.html.erb | 10 ++-- app/views/admin/mentions/edit.html.erb | 2 +- .../acts_as_taggable_field/_form.html.erb | 6 ++ .../acts_as_taggable_field/_index.html.erb | 1 + .../acts_as_taggable_field/_show.html.erb | 5 ++ spec/fields/acts_as_taggable_field_spec.rb | 60 +++++++++++++++++++ spec/requests/admin/about_pages_spec.rb | 4 +- spec/requests/admin/media_spec.rb | 7 --- spec/requests/admin/mentions_spec.rb | 12 ---- spec/support/capybara.rb | 11 ++++ spec/system/admin/letters_spec.rb | 13 ++-- 19 files changed, 209 insertions(+), 63 deletions(-) create mode 100644 app/assets/javascripts/acts_as_taggable_field.js create mode 100644 app/assets/stylesheets/acts_as_taggable_field.css create mode 100644 app/fields/acts_as_taggable_field.rb create mode 100644 app/views/fields/acts_as_taggable_field/_form.html.erb create mode 100644 app/views/fields/acts_as_taggable_field/_index.html.erb create mode 100644 app/views/fields/acts_as_taggable_field/_show.html.erb create mode 100644 spec/fields/acts_as_taggable_field_spec.rb diff --git a/Gemfile b/Gemfile index b667cf6..d293c44 100644 --- a/Gemfile +++ b/Gemfile @@ -48,6 +48,12 @@ gem 'administrate' gem 'administrate-field-jsonb' gem 'trix-rails', require: 'trix' +# administrate < 1.0 pulled these in transitively via sassc-rails; administrate 1.0 +# bundles its own assets and dropped that dependency, but the app's own asset +# pipeline (app/assets/config/manifest.js) still relies on Sprockets/Sass directly. +gem 'sprockets-rails' +gem 'sassc-rails' + group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem 'debug', platforms: %i[mri mingw x64_mingw] @@ -67,6 +73,9 @@ group :test do # chromedriver binary itself (Selenium Manager) - no separate webdriver gem needed. gem 'capybara' gem 'selenium-webdriver' + # CDP support, needed for Selenium's driver.register(username:, password:) - used to + # authenticate admin system specs without embedding credentials in the visited URL. + gem 'selenium-devtools' end gem 'pundit', '~> 2.2' @@ -79,8 +88,6 @@ gem 'rspec_api_documentation', '~> 6.1' gem 'acts-as-taggable-on', '~> 11.0.0' -gem 'administrate-field-acts_as_taggable' - gem 'administrate-field-list', '~> 0.0.6' gem 'httparty', '~> 0.20.0' diff --git a/Gemfile.lock b/Gemfile.lock index 609e992..2fb177b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,22 +78,16 @@ GEM zeitwerk (>= 2.4, < 3.0) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) - administrate (0.20.1) - actionpack (>= 6.0, < 8.0) - actionview (>= 6.0, < 8.0) - activerecord (>= 6.0, < 8.0) - jquery-rails (~> 4.6.0) + administrate (1.0.0) + actionpack (>= 6.0, < 9.0) + actionview (>= 6.0, < 9.0) + activerecord (>= 6.0, < 9.0) kaminari (~> 1.2.2) - sassc-rails (~> 2.1) - selectize-rails (~> 0.6) administrate-field-active_storage (0.4.2) administrate (>= 0.2.2) rails (>= 7.0) - administrate-field-acts_as_taggable (0.0.4) - acts-as-taggable-on (>= 6.0) - administrate (< 1.0.0) - administrate-field-jsonb (0.4.6) - administrate (< 1.0.0) + administrate-field-jsonb (0.4.8) + administrate (< 2.0) rails (>= 4.2) administrate-field-list (0.0.6) administrate @@ -205,10 +199,6 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) - jquery-rails (4.6.0) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) json (2.7.2) kaminari (1.2.2) activesupport (>= 4.1.0) @@ -408,7 +398,8 @@ GEM activemodel (>= 6.1) hashie securerandom (0.3.1) - selectize-rails (0.12.6) + selenium-devtools (0.151.0) + selenium-webdriver (~> 4.2) selenium-webdriver (4.46.0) base64 (~> 0.2) logger (~> 1.4) @@ -428,8 +419,9 @@ GEM fugit (~> 1.8, >= 1.11.1) globalid (>= 1.0.1) sidekiq (>= 6.5.0) - sprockets (4.2.1) + sprockets (4.2.2) concurrent-ruby (~> 1.0) + logger rack (>= 2.2.4, < 4) sprockets-rails (3.5.2) actionpack (>= 6.1) @@ -439,7 +431,7 @@ GEM strscan (3.1.0) systemu (2.6.5) thor (1.3.1) - tilt (2.4.0) + tilt (2.8.0) timeout (0.4.1) trix-rails (2.4.0) rails (> 4.1) @@ -481,7 +473,6 @@ DEPENDENCIES addressable (~> 2.8) administrate administrate-field-active_storage (~> 0.4.1) - administrate-field-acts_as_taggable administrate-field-jsonb administrate-field-list (~> 0.0.6) aws-sdk-rails (~> 5.0) @@ -512,11 +503,14 @@ DEPENDENCIES rubocop-rails rubocop-rspec rubocop-rspec_rails + sassc-rails searchkick + selenium-devtools selenium-webdriver shoulda-matchers sidekiq (>= 7.2.2, < 8) sidekiq-cron (~> 2.4.0) + sprockets-rails trix-rails tzinfo-data uuid (~> 2.3) diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index 41bd56d..7dc11fe 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -4,10 +4,8 @@ //= link_tree ../../../vendor/javascript .js //= link administrate-field-jsonb/application.css //= link administrate-field-jsonb/application.js -//= link administrate-field-taggable/application.js -//= link administrate-field-taggable/application.css +//= link acts_as_taggable_field.js //= link administrate-field-active_storage/application.css -//= require selectize //= require trix // application.js // application.css diff --git a/app/assets/javascripts/acts_as_taggable_field.js b/app/assets/javascripts/acts_as_taggable_field.js new file mode 100644 index 0000000..bf7e5cb --- /dev/null +++ b/app/assets/javascripts/acts_as_taggable_field.js @@ -0,0 +1,20 @@ +// Selectize init for ActsAsTaggableField's text input, ported from the +// unmaintained administrate-field-acts_as_taggable gem's application.js. +$(function() { + $(".field-unit--acts-as-taggable-field .field-unit__field > input").each(function(elem) { + var $this = $(this); + var opts = $this.data('tag-options'); + + $this.selectize({ + delimiter: ", ", + persist: false, + options: opts, + create: function(input) { + return { + value: input, + text: input + }; + } + }); + }) +}); diff --git a/app/assets/stylesheets/acts_as_taggable_field.css b/app/assets/stylesheets/acts_as_taggable_field.css new file mode 100644 index 0000000..6867434 --- /dev/null +++ b/app/assets/stylesheets/acts_as_taggable_field.css @@ -0,0 +1,9 @@ +.taggable_field .tag { + text-align: center; + font-size: 0.85em; + margin: 0 3px 3px 0; + padding: 2px 6px; + background: #f2f2f2; + color: #303030; + border: 0 solid #d0d0d0; +} diff --git a/app/dashboards/mention_dashboard.rb b/app/dashboards/mention_dashboard.rb index b3283c4..abae96a 100644 --- a/app/dashboards/mention_dashboard.rb +++ b/app/dashboards/mention_dashboard.rb @@ -18,7 +18,7 @@ class MentionDashboard < Administrate::BaseDashboard id: Field::Number, created_at: Field::DateTime, updated_at: Field::DateTime, - tags: Field::ActsAsTaggable + tags: ActsAsTaggableField }.freeze # COLLECTION_ATTRIBUTES diff --git a/app/fields/acts_as_taggable_field.rb b/app/fields/acts_as_taggable_field.rb new file mode 100644 index 0000000..528a3a3 --- /dev/null +++ b/app/fields/acts_as_taggable_field.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'administrate/field/text' +require 'administrate/engine' + +# In-house replacement for the unmaintained administrate-field-acts_as_taggable gem +# (last released 2021, hard-pins `administrate < 1.0.0`). Behavior is ported as-is +# from that gem's lib/administrate/field/acts_as_taggable.rb. +class ActsAsTaggableField < Administrate::Field::Text + class Engine < ::Rails::Engine + if defined?(Administrate::Engine) + Administrate::Engine.add_javascript 'acts_as_taggable_field' + Administrate::Engine.add_stylesheet 'acts_as_taggable_field' + end + end + + def context + options.fetch(:context, @attribute) + end + + # acts_as_taggable_on exposes a `_list` virtual attribute + # (e.g. `tag_list` for the `:tags` context) for reading/writing a comma-delimited + # tag string - this field operates on that attribute rather than the raw `tags` + # association. + def attribute + "#{super.to_s.singularize}_list" + end + + def self.permitted_attribute(attr, **_opts) + "#{attr.to_s.singularize}_list" + end + + def tags + data + end + + def name + context.to_s + end + + def delimited + tags.join(', ').to_s + end + + def truncate + delimited[0...truncation_length] + end + + def tag_options + return [] unless defined?(ActsAsTaggableOn::Tag) + + ActsAsTaggableOn::Tag.for_context(context).order(:name).map do |t| + { text: t.name, value: t.name } + end + end +end diff --git a/app/views/admin/media/show.html.erb b/app/views/admin/media/show.html.erb index 17a3432..a2435a1 100644 --- a/app/views/admin/media/show.html.erb +++ b/app/views/admin/media/show.html.erb @@ -25,12 +25,13 @@ as well as a link to its edit page. t("administrate.actions.edit_resource", name: page.page_title), [:edit, namespace, page.resource], class: "button", - ) if accessible_action?(:edit) && accessible_action?(:edit, page.resource) %> + ) if accessible_action?(page.resource, :edit) %>
- <%= page.resource.alt_text %> + <% media_url = page.resource.image.attached? ? url_for(page.resource.image) : page.resource.link %> + <%= page.resource.alt_text %>
<%= page.resource.caption %>
<% page.attributes.each do |title, attributes| %> diff --git a/app/views/admin/mentions/_form.html.erb b/app/views/admin/mentions/_form.html.erb index 5d798ea..3ee5e47 100644 --- a/app/views/admin/mentions/_form.html.erb +++ b/app/views/admin/mentions/_form.html.erb @@ -33,10 +33,12 @@ and renders all form fields for a resource's editable attributes. <% end %> - <% page.attributes(controller.action_name).each do |attribute| -%> -
- <%= render_field attribute, f: f %> -
+ <% page.attributes(controller.action_name).each do |title, attributes| -%> + <% attributes.each do |attribute| %> +
+ <%= render_field attribute, f: f %> +
+ <% end -%> <% end -%>
diff --git a/app/views/admin/mentions/edit.html.erb b/app/views/admin/mentions/edit.html.erb index 62e2be2..f443901 100644 --- a/app/views/admin/mentions/edit.html.erb +++ b/app/views/admin/mentions/edit.html.erb @@ -34,7 +34,7 @@ It displays a header, and renders the `_form` partial to do the heavy lifting. t("administrate.actions.show_resource", name: page.page_title), [namespace, page.resource], class: "button", - ) if accessible_action?(:show) && accessible_action?(:show, page.resource) %> + ) if accessible_action?(page.resource, :show) %>
diff --git a/app/views/fields/acts_as_taggable_field/_form.html.erb b/app/views/fields/acts_as_taggable_field/_form.html.erb new file mode 100644 index 0000000..f84fb00 --- /dev/null +++ b/app/views/fields/acts_as_taggable_field/_form.html.erb @@ -0,0 +1,6 @@ +
+ <%= f.label field.attribute %> +
+
+ <%= f.text_field field.attribute, value: field.delimited, data: { tag_options: field.tag_options } %> +
diff --git a/app/views/fields/acts_as_taggable_field/_index.html.erb b/app/views/fields/acts_as_taggable_field/_index.html.erb new file mode 100644 index 0000000..d683ccc --- /dev/null +++ b/app/views/fields/acts_as_taggable_field/_index.html.erb @@ -0,0 +1 @@ +<%= field.truncate %> diff --git a/app/views/fields/acts_as_taggable_field/_show.html.erb b/app/views/fields/acts_as_taggable_field/_show.html.erb new file mode 100644 index 0000000..0651443 --- /dev/null +++ b/app/views/fields/acts_as_taggable_field/_show.html.erb @@ -0,0 +1,5 @@ +
+ <% field.tags.each do |tag| %> + <%= tag.name -%> + <% end %> +
diff --git a/spec/fields/acts_as_taggable_field_spec.rb b/spec/fields/acts_as_taggable_field_spec.rb new file mode 100644 index 0000000..7abbc56 --- /dev/null +++ b/spec/fields/acts_as_taggable_field_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'administrate/field/text' + +RSpec.describe ActsAsTaggableField do + describe '#attribute' do + it 'singularizes the underlying attribute and appends _list' do + field = described_class.new(:tags, [], nil) + expect(field.attribute).to eq('tag_list') + end + end + + describe '.permitted_attribute' do + it 'singularizes the attribute and appends _list' do + expect(described_class.permitted_attribute(:tags)).to eq('tag_list') + end + end + + describe '#tags' do + it 'returns the raw data' do + field = described_class.new(:tags, %w[a b], nil) + expect(field.tags).to eq(%w[a b]) + end + end + + describe '#delimited' do + it 'joins tag names with a comma and space' do + mention = create(:mention, tag_list: 'foo, bar') + field = described_class.new(:tags, mention.tags, nil) + + expect(field.delimited).to eq('foo, bar') + end + + it 'returns an empty string when there are no tags' do + field = described_class.new(:tags, [], nil) + expect(field.delimited).to eq('') + end + end + + describe '#truncate' do + it 'truncates the delimited tag list to the default length' do + mention = create(:mention, tag_list: 'a' * 60) + field = described_class.new(:tags, mention.tags, nil) + + expect(field.truncate.length).to eq(50) + end + end + + describe '#tag_options' do + it 'lists tags used in the tags context, sorted by name' do + create(:mention, tag_list: 'zebra, apple') + field = described_class.new(:tags, [], nil) + + expect(field.tag_options).to eq( + [{ text: 'apple', value: 'apple' }, { text: 'zebra', value: 'zebra' }] + ) + end + end +end diff --git a/spec/requests/admin/about_pages_spec.rb b/spec/requests/admin/about_pages_spec.rb index 716a164..d3159e9 100644 --- a/spec/requests/admin/about_pages_spec.rb +++ b/spec/requests/admin/about_pages_spec.rb @@ -46,10 +46,10 @@ expect(response.body).to include(%(href="#{new_admin_about_page_path}")) end - it 'still renders the "destroy" link on the show page' do + it 'still renders the "destroy" button on the show page' do about_page = create(:about_page) get admin_about_page_path(about_page), headers: admin_auth_headers - expect(response.body).to include(%(href="#{admin_about_page_path(about_page)}")) + expect(response.body).to include(%(action="#{admin_about_page_path(about_page)}")) .and include('button--danger') end end diff --git a/spec/requests/admin/media_spec.rb b/spec/requests/admin/media_spec.rb index 164afbd..96a15c7 100644 --- a/spec/requests/admin/media_spec.rb +++ b/spec/requests/admin/media_spec.rb @@ -18,13 +18,6 @@ describe 'GET show' do it 'renders successfully when authenticated' do - pending( - 'known bug: admin/media/show.html.erb:28 calls accessible_action?(:edit) with one ' \ - 'argument before the correct two-argument call - Administrate::ApplicationHelper#' \ - 'accessible_action? requires (target, action_name), so this raises ArgumentError ' \ - "before the &&'d correct call, or page.resource.url (line 33, Medium has no #url " \ - 'method), is ever reached' - ) medium = create(:medium) get admin_medium_path(medium), headers: admin_auth_headers expect(response).to have_http_status(:ok) diff --git a/spec/requests/admin/mentions_spec.rb b/spec/requests/admin/mentions_spec.rb index d071911..bc9506c 100644 --- a/spec/requests/admin/mentions_spec.rb +++ b/spec/requests/admin/mentions_spec.rb @@ -26,12 +26,6 @@ describe 'GET new' do it 'renders successfully when authenticated' do - pending( - 'known bug: admin/mentions/_form.html.erb:36 iterates page.attributes(...) as a ' \ - 'flat list of fields, but it actually yields [title, attributes] pairs (see ' \ - 'admin/letters/_form.html.erb for the correct nested form), so `attribute.html_class` ' \ - 'raises NoMethodError on the Array' - ) get new_admin_mention_path, headers: admin_auth_headers expect(response).to have_http_status(:ok) end @@ -39,12 +33,6 @@ describe 'GET edit' do it 'renders successfully when authenticated' do - pending( - 'known bug: admin/mentions/edit.html.erb:37 calls ' \ - 'accessible_action?(:show) with one argument before the correct two-argument call - ' \ - 'Administrate::ApplicationHelper#accessible_action? requires (target, action_name), so ' \ - 'this raises ArgumentError before the &&\'d correct call is ever reached' - ) mention = create(:mention) get edit_admin_mention_path(mention), headers: admin_auth_headers expect(response).to have_http_status(:ok) diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index dfddde6..0346da3 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -24,5 +24,16 @@ config.before(:each, :js, type: :system) do driven_by Capybara.javascript_driver + + # Admin::ApplicationController gates every admin route behind HTTP Basic Auth, and + # this app has no session-based login to drive instead. Visiting with credentials + # embedded in the URL (http://user:pass@host/path) used to work, but Administrate + # 1.0 bundles Turbo, which calls history.replaceState on every page load - Chrome + # rejects replaceState targets that carry userinfo, and since Administrate's JS is + # one bundled IIFE, that uncaught exception aborts everything after it, including + # selectize's own setup. Real admin users never hit this (their location.href never + # carries credentials - Chrome only uses them for the auth handshake). Registering + # credentials via CDP instead keeps the visited URL clean and sidesteps it entirely. + Capybara.current_session.driver.browser.register(username: 'test', password: 'test') end end diff --git a/spec/system/admin/letters_spec.rb b/spec/system/admin/letters_spec.rb index 03b77fd..1b865bb 100644 --- a/spec/system/admin/letters_spec.rb +++ b/spec/system/admin/letters_spec.rb @@ -9,11 +9,6 @@ # break (jquery-ujs -> Turbo/Stimulus, changed asset bundling in 1.0) - and the only # way to actually verify it is a real browser, since no request spec executes JS. RSpec.describe 'Admin::Letters entity picker', :js do - def visit_with_basic_auth(path) - server = Capybara.current_session.server - visit "http://test:test@#{server.host}:#{server.port}#{path}" - end - def selectize "jQuery('.field-unit--has-many-through-field select')[0].selectize" end @@ -21,7 +16,7 @@ def selectize it 'initializes jQuery and the selectize widget on the entities field' do letter = create(:letter) - visit_with_basic_auth(edit_admin_letter_path(letter)) + visit edit_admin_letter_path(letter) expect(page.execute_script('return window.jQuery !== undefined')).to be true expect(page).to have_css('.selectize-control') @@ -33,7 +28,7 @@ def selectize entity = create(:person_entity, label: 'Zzz System Spec Findable Entity') Entity.reindex - visit_with_basic_auth(edit_admin_letter_path(letter)) + visit edit_admin_letter_path(letter) expect(page).to have_css('.selectize-control') page.execute_script("#{selectize}.addItem('#{entity.id}')") @@ -49,7 +44,7 @@ def selectize letter.save! Entity.reindex - visit_with_basic_auth(edit_admin_letter_path(letter)) + visit edit_admin_letter_path(letter) expect(page).to have_css(".item[data-value='#{entity.id}']") find(".item[data-value='#{entity.id}']").click @@ -68,7 +63,7 @@ def selectize letter.save! Entity.reindex - visit_with_basic_auth(edit_admin_letter_path(letter)) + visit edit_admin_letter_path(letter) expect(page).to have_css(".item[data-value='#{entity.id}']") find(".item[data-value='#{entity.id}']").click From b6473ef36b755ed0162ba286ab0d7c16b9262cdb Mon Sep 17 00:00:00 2001 From: Jay Varner Date: Tue, 11 Aug 2026 09:15:30 -0400 Subject: [PATCH 2/5] Clean out unused code and fill in gap in test coverage. --- Gemfile | 22 +-- Gemfile.lock | 21 --- .../admin/application_controller.rb | 2 - app/policies/application_policy.rb | 53 ------- config/initializers/rspec_api_documention.rb | 15 -- spec/acceptance_helper.rb | 12 -- spec/jobs/import_data_job_spec.rb | 7 - spec/models/big_sam_spec.rb | 7 - spec/models/collection_spec.rb | 7 - spec/models/event_type_spec.rb | 7 - spec/models/file_folder_spec.rb | 7 - spec/models/letter_collection_spec.rb | 7 - spec/models/letter_destination_spec.rb | 7 - spec/models/letter_file_spec.rb | 7 - spec/models/letter_origin_spec.rb | 7 - spec/models/letter_owner_spec.rb | 7 - spec/models/letter_publisher_spec.rb | 7 - spec/models/letter_recipient_spec.rb | 7 - spec/models/letter_sender_spec.rb | 7 - spec/models/mention_spec.rb | 7 - spec/requests/repositories_spec.rb | 130 ++++------------ .../markdow_example.mustache | 85 ----------- .../api_blueprint_index.mustache | 80 ---------- .../html_example.mustache | 140 ------------------ .../html_index.mustache | 33 ----- .../markdown_example.mustache | 85 ----------- .../markdown_index.mustache | 15 -- .../slate_example.mustache | 84 ----------- .../slate_index.mustache | 8 - .../textile_example.mustache | 80 ---------- .../textile_index.mustache | 15 -- 31 files changed, 31 insertions(+), 947 deletions(-) delete mode 100644 app/policies/application_policy.rb delete mode 100644 config/initializers/rspec_api_documention.rb delete mode 100644 spec/acceptance_helper.rb delete mode 100644 spec/jobs/import_data_job_spec.rb delete mode 100644 spec/models/big_sam_spec.rb delete mode 100644 spec/models/collection_spec.rb delete mode 100644 spec/models/event_type_spec.rb delete mode 100644 spec/models/file_folder_spec.rb delete mode 100644 spec/models/letter_collection_spec.rb delete mode 100644 spec/models/letter_destination_spec.rb delete mode 100644 spec/models/letter_file_spec.rb delete mode 100644 spec/models/letter_origin_spec.rb delete mode 100644 spec/models/letter_owner_spec.rb delete mode 100644 spec/models/letter_publisher_spec.rb delete mode 100644 spec/models/letter_recipient_spec.rb delete mode 100644 spec/models/letter_sender_spec.rb delete mode 100644 spec/models/mention_spec.rb delete mode 100644 templates/rspec_api_documentation/markdow_example.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/api_blueprint_index.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/html_example.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/html_index.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/markdown_example.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/markdown_index.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/slate_example.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/slate_index.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/textile_example.mustache delete mode 100644 templates/rspec_api_documentation/rspec_api_documentation/textile_index.mustache diff --git a/Gemfile b/Gemfile index d293c44..9b3924d 100644 --- a/Gemfile +++ b/Gemfile @@ -8,8 +8,6 @@ ruby '3.3.4' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main' gem 'rails', '~> 7.2.1' -gem 'csv' - # Use postgresql as the database for Active Record gem 'pg', '~> 1.1' @@ -20,18 +18,6 @@ gem 'searchkick' # Use the Puma web server [https://github.com/puma/puma] gem 'puma', '~> 5.0' -# Build JSON APIs with ease [https://github.com/rails/jbuilder] -# gem 'jbuilder' - -# Use Redis adapter to run Action Cable in production -# gem 'redis', '~> 4.0' - -# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] -# gem 'kredis' - -# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] -# gem 'bcrypt', '~> 3.1.7' - # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] @@ -51,8 +37,8 @@ gem 'trix-rails', require: 'trix' # administrate < 1.0 pulled these in transitively via sassc-rails; administrate 1.0 # bundles its own assets and dropped that dependency, but the app's own asset # pipeline (app/assets/config/manifest.js) still relies on Sprockets/Sass directly. -gem 'sprockets-rails' gem 'sassc-rails' +gem 'sprockets-rails' group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem @@ -78,14 +64,10 @@ group :test do gem 'selenium-devtools' end -gem 'pundit', '~> 2.2' - gem 'kaminari', '~> 1.2' gem 'jbuilder', '~> 2.11' -gem 'rspec_api_documentation', '~> 6.1' - gem 'acts-as-taggable-on', '~> 11.0.0' gem 'administrate-field-list', '~> 0.0.6' @@ -102,8 +84,6 @@ gem 'active_record_doctor', '~> 1.10' gem 'sidekiq', '>=7.2.2', '<8' -gem 'uuid', '~> 2.3' - gem 'addressable', '~> 2.8' gem 'importmap-rails', '~> 2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 2fb177b..a5c643d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -131,7 +131,6 @@ GEM cronex (0.15.0) tzinfo unicode (>= 0.4.4.5) - csv (3.3.0) date (3.3.4) debug (1.9.2) irb (~> 1.10) @@ -217,8 +216,6 @@ GEM loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) - macaddr (1.7.2) - systemu (~> 2.6.5) mail (2.8.1) mini_mime (>= 0.1.1) net-imap @@ -236,7 +233,6 @@ GEM multi_json (1.21.1) multi_xml (0.7.1) bigdecimal (~> 3.1) - mustache (1.1.1) namae (1.2.0) racc (~> 1.7) net-http (0.9.1) @@ -274,8 +270,6 @@ GEM public_suffix (6.0.1) puma (5.6.8) nio4r (~> 2.0) - pundit (2.4.0) - activesupport (>= 3.0.0) raabro (1.4.0) racc (1.8.1) rack (2.2.9) @@ -331,10 +325,6 @@ GEM roo (2.10.1) nokogiri (~> 1) rubyzip (>= 1.3.0, < 3.0.0) - rspec (3.13.0) - rspec-core (~> 3.13.0) - rspec-expectations (~> 3.13.0) - rspec-mocks (~> 3.13.0) rspec-core (3.13.0) rspec-support (~> 3.13.0) rspec-expectations (3.13.2) @@ -352,10 +342,6 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rspec_api_documentation (6.1.0) - activesupport (>= 3.0.0) - mustache (~> 1.0, >= 0.99.4) - rspec (~> 3.0) rubocop (1.65.1) json (~> 2.3) language_server-protocol (>= 3.17.0) @@ -429,7 +415,6 @@ GEM sprockets (>= 3.0.0) stringio (3.1.1) strscan (3.1.0) - systemu (2.6.5) thor (1.3.1) tilt (2.8.0) timeout (0.4.1) @@ -441,8 +426,6 @@ GEM unicode-display_width (2.5.0) uri (1.1.1) useragent (0.16.10) - uuid (2.3.9) - macaddr (~> 1.0) webrick (1.8.1) websocket (1.2.11) websocket-driver (0.7.6) @@ -479,7 +462,6 @@ DEPENDENCIES aws-sdk-ses (~> 1.0) bootsnap capybara - csv debug elasticsearch (~> 8) factory_bot_rails @@ -492,12 +474,10 @@ DEPENDENCIES namae (~> 1.1) pg (~> 1.1) puma (~> 5.0) - pundit (~> 2.2) rack-cors rails (~> 7.2.1) roo (~> 2.9) rspec-rails - rspec_api_documentation (~> 6.1) rubocop rubocop-factory_bot rubocop-rails @@ -513,7 +493,6 @@ DEPENDENCIES sprockets-rails trix-rails tzinfo-data - uuid (~> 2.3) RUBY VERSION ruby 3.3.4p94 diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb index 40afcd6..cba7fbe 100644 --- a/app/controllers/admin/application_controller.rb +++ b/app/controllers/admin/application_controller.rb @@ -8,8 +8,6 @@ # you're free to overwrite the RESTful controller actions. module Admin class ApplicationController < Administrate::ApplicationController - # include Administrate::Punditize - before_action :authenticate_admin before_action do diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb deleted file mode 100644 index d91c019..0000000 --- a/app/policies/application_policy.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -class ApplicationPolicy - attr_reader :user, :record - - def initialize(user, record) - @user = user - @record = record - end - - def index? - true - end - - def show? - true - end - - def create? - true - end - - def new? - true - end - - def update? - true - end - - def edit? - true - end - - def destroy? - true - end - - class Scope - def initialize(user, scope) - @user = user - @scope = scope - end - - def resolve - raise NotImplementedError, "You must define #resolve in #{self.class}" - end - - private - - attr_reader :user, :scope - end -end diff --git a/config/initializers/rspec_api_documention.rb b/config/initializers/rspec_api_documention.rb deleted file mode 100644 index da707a1..0000000 --- a/config/initializers/rspec_api_documention.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -# Monkey patch to properly display json :( -# https://github.com/zipmark/rspec_api_documentation/issues/456 -module RspecApiDocumentation - class RackTestClient < ClientBase - def response_body - if last_response.headers['Content-Type'].include?('json') - last_response.body.encode('utf-8') - else - '[binary data]' - end - end - end -end diff --git a/spec/acceptance_helper.rb b/spec/acceptance_helper.rb deleted file mode 100644 index 53dba02..0000000 --- a/spec/acceptance_helper.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require 'rspec_api_documentation/dsl' - -RspecApiDocumentation.configure do |config| - config.format = %i[html markdown] - config.template_path = Rails.root.join('templates', 'rspec_api_documentation') - config.keep_source_order = true - config.docs_dir = Rails.public_path.join('docs') - config.api_name = 'Beckett Data API Documentation' -end diff --git a/spec/jobs/import_data_job_spec.rb b/spec/jobs/import_data_job_spec.rb deleted file mode 100644 index b858132..0000000 --- a/spec/jobs/import_data_job_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe ImportDataJob do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/big_sam_spec.rb b/spec/models/big_sam_spec.rb deleted file mode 100644 index 8cb2214..0000000 --- a/spec/models/big_sam_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe BigSam do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb deleted file mode 100644 index c6bfd2c..0000000 --- a/spec/models/collection_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Collection do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/event_type_spec.rb b/spec/models/event_type_spec.rb deleted file mode 100644 index 042fcf6..0000000 --- a/spec/models/event_type_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe EventType do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/file_folder_spec.rb b/spec/models/file_folder_spec.rb deleted file mode 100644 index 751e941..0000000 --- a/spec/models/file_folder_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe FileFolder do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_collection_spec.rb b/spec/models/letter_collection_spec.rb deleted file mode 100644 index 33ff10f..0000000 --- a/spec/models/letter_collection_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterCollection do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_destination_spec.rb b/spec/models/letter_destination_spec.rb deleted file mode 100644 index 61fd41f..0000000 --- a/spec/models/letter_destination_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterDestination do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_file_spec.rb b/spec/models/letter_file_spec.rb deleted file mode 100644 index 5db75d9..0000000 --- a/spec/models/letter_file_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterFile do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_origin_spec.rb b/spec/models/letter_origin_spec.rb deleted file mode 100644 index 69658c6..0000000 --- a/spec/models/letter_origin_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterOrigin do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_owner_spec.rb b/spec/models/letter_owner_spec.rb deleted file mode 100644 index bbab35d..0000000 --- a/spec/models/letter_owner_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterOwner do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_publisher_spec.rb b/spec/models/letter_publisher_spec.rb deleted file mode 100644 index b136a6c..0000000 --- a/spec/models/letter_publisher_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterPublisher do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_recipient_spec.rb b/spec/models/letter_recipient_spec.rb deleted file mode 100644 index 356335d..0000000 --- a/spec/models/letter_recipient_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterRecipient do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/letter_sender_spec.rb b/spec/models/letter_sender_spec.rb deleted file mode 100644 index 29a3ed8..0000000 --- a/spec/models/letter_sender_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe LetterSender do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/mention_spec.rb b/spec/models/mention_spec.rb deleted file mode 100644 index a302cff..0000000 --- a/spec/models/mention_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Mention do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/requests/repositories_spec.rb b/spec/requests/repositories_spec.rb index 4f3cd24..a3db5f5 100644 --- a/spec/requests/repositories_spec.rb +++ b/spec/requests/repositories_spec.rb @@ -2,128 +2,58 @@ require 'rails_helper' -# This spec was generated by rspec-rails when you ran the scaffold generator. -# It demonstrates how one might use RSpec to test the controller code that -# was generated by Rails when you ran the scaffold generator. -# -# It assumes that the implementation code is generated by the rails scaffold -# generator. If you are using any extension libraries to generate different -# controller code, this generated spec may or may not pass. -# -# It only uses APIs available in rails and/or rspec-rails. There are a number -# of tools you can use to make these specs even more expressive, but we're -# sticking to rails and rspec-rails APIs to keep things simple and stable. - RSpec.describe '/repositories' do - # This should return the minimal set of attributes required to create a valid - # Repository. As you add validations to Repository, be sure to - # adjust the attributes here as well. - let(:valid_attributes) { - skip('Add a hash of attributes valid for your model') - } - - let(:invalid_attributes) { - skip('Add a hash of attributes invalid for your model') - } - - # This should return the minimal set of values that should be in the headers - # in order to pass any filters (e.g. authentication) defined in - # RepositoriesController, or in your router and rack - # middleware. Be sure to keep this updated too. - let(:valid_headers) { - {} - } - describe 'GET /index' do - it 'renders a successful response' do - Repository.create! valid_attributes - get repositories_url, headers: valid_headers, as: :json + it 'renders a successful response with all repositories' do + create_list(:repository, 3) + get repositories_url, as: :json expect(response).to be_successful + expect(json.count).to eq(3) end end describe 'GET /show' do - it 'renders a successful response' do - repository = Repository.create! valid_attributes + it 'renders the requested repository' do + repository = create(:repository) get repository_url(repository), as: :json expect(response).to be_successful + expect(json[:id]).to eq(repository.id) + expect(json[:label]).to eq(repository.label) end - end - - describe 'POST /create' do - context 'with valid parameters' do - it 'creates a new Repository' do - expect { - post repositories_url, - params: { repository: valid_attributes }, headers: valid_headers, as: :json - }.to change(Repository, :count).by(1) - end - it 'renders a JSON response with the new repository' do - post repositories_url, - params: { repository: valid_attributes }, headers: valid_headers, as: :json - expect(response).to have_http_status(:created) - expect(response.content_type).to match(a_string_including('application/json')) - end + it 'renders a not_found response for an unknown id' do + get repository_url(SecureRandom.uuid), as: :json + expect(response).to have_http_status(:not_found) end + end - context 'with invalid parameters' do - it 'does not create a new Repository' do - expect { - post repositories_url, - params: { repository: invalid_attributes }, as: :json - }.not_to change(Repository, :count) - end - - it 'renders a JSON response with errors for the new repository' do - post repositories_url, - params: { repository: invalid_attributes }, headers: valid_headers, as: :json - expect(response).to have_http_status(:unprocessable_entity) - expect(response.content_type).to match(a_string_including('application/json')) - end + # RepositoriesController only overrides index/show - create/update/destroy fall + # through to ApplicationController's defaults, which all `head :not_implemented`. + describe 'POST /create' do + it 'does not create a new Repository' do + expect { + post repositories_url, params: { repository: { label: 'New Repository' } }, as: :json + }.not_to change(Repository, :count) + expect(response).to have_http_status(:not_implemented) end end describe 'PATCH /update' do - context 'with valid parameters' do - let(:new_attributes) { - skip('Add a hash of attributes valid for your model') - } - - it 'updates the requested repository' do - repository = Repository.create! valid_attributes - patch repository_url(repository), - params: { repository: new_attributes }, headers: valid_headers, as: :json - repository.reload - skip('Add assertions for updated state') - end - - it 'renders a JSON response with the repository' do - repository = Repository.create! valid_attributes - patch repository_url(repository), - params: { repository: new_attributes }, headers: valid_headers, as: :json - expect(response).to have_http_status(:ok) - expect(response.content_type).to match(a_string_including('application/json')) - end - end - - context 'with invalid parameters' do - it 'renders a JSON response with errors for the repository' do - repository = Repository.create! valid_attributes - patch repository_url(repository), - params: { repository: invalid_attributes }, headers: valid_headers, as: :json - expect(response).to have_http_status(:unprocessable_entity) - expect(response.content_type).to match(a_string_including('application/json')) - end + it 'does not update the requested repository' do + repository = create(:repository, label: 'Original') + patch repository_url(repository), params: { repository: { label: 'Changed' } }, as: :json + expect(response).to have_http_status(:not_implemented) + expect(repository.reload.label).to eq('Original') end end describe 'DELETE /destroy' do - it 'destroys the requested repository' do - repository = Repository.create! valid_attributes + it 'does not destroy the requested repository' do + repository = create(:repository) expect { - delete repository_url(repository), headers: valid_headers, as: :json - }.to change(Repository, :count).by(-1) + delete repository_url(repository), as: :json + }.not_to change(Repository, :count) + expect(response).to have_http_status(:not_implemented) end end end diff --git a/templates/rspec_api_documentation/markdow_example.mustache b/templates/rspec_api_documentation/markdow_example.mustache deleted file mode 100644 index 16263d0..0000000 --- a/templates/rspec_api_documentation/markdow_example.mustache +++ /dev/null @@ -1,85 +0,0 @@ -# {{ resource_name }} API -{{# resource_explanation }} - -{{{ resource_explanation }}} -{{/ resource_explanation }} - -## {{ description }} - -### {{ http_method }} {{ route }} -{{# explanation }} - -{{ explanation }} -{{/ explanation }} -{{# has_parameters? }} - -### Parameters - -| Name | Description | Required | Scope | Default | -|------|-------------|----------|-------|---------| -{{# parameters }} -| {{ name }} | {{ description }} | {{ required }} | {{ scope }} | | {{ default }} -{{/ parameters }} - -{{/ has_parameters? }} -{{# has_response_fields? }} - -### Response Fields - -| Name | Description | Scope | -|------|-------------|-------| -{{# response_fields }} -| {{ name }} | {{ description }} | {{ scope }} | -{{/ response_fields }} - -{{/ has_response_fields? }} -{{# requests }} -### Request - -#### Headers - -
{{ request_headers_text }}
- -#### Route - -
{{ request_method }} {{ request_path }}
-{{# request_query_parameters_text }} - -#### Query Parameters - -
{{ request_query_parameters_text }}
-{{/ request_query_parameters_text }} -{{# request_body }} - -#### Body - -
{{{ request_body }}}
-{{/ request_body }} -{{# curl }} - -#### cURL - -
{{ curl }}
-{{/ curl }} - -{{# response_status }} -### Response - -#### Headers - -
{{ response_headers_text }}
- -#### Status - -
{{ response_status }} {{ response_status_text}}
- -{{# response_body }} -#### Body - -~~~ -{{{ response_body }}} -~~~ - -{{/ response_body }} -{{/ response_status }} -{{/ requests }} \ No newline at end of file diff --git a/templates/rspec_api_documentation/rspec_api_documentation/api_blueprint_index.mustache b/templates/rspec_api_documentation/rspec_api_documentation/api_blueprint_index.mustache deleted file mode 100644 index 865f24a..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/api_blueprint_index.mustache +++ /dev/null @@ -1,80 +0,0 @@ -FORMAT: 1A -# {{ api_name }} -{{# sections }} - -# Group {{ resource_name }} -{{# resource_explanation }} - -{{{ resource_explanation }}} -{{/ resource_explanation }} -{{# description }} - -{{ description }} -{{/ description }} -{{# routes }} - -## {{ route_name }} [{{ route }}] -{{# description }} - -description: {{ description }} -{{/ description }} -{{# explanation }} - -explanation: {{ explanation }} -{{/ explanation }} -{{# has_parameters? }} - -+ Parameters -{{# parameters }} - + {{ name }}{{# example }}: {{ example }}{{/ example }}{{# properties_description }} ({{ properties_description }}){{/ properties_description }}{{# description }} - {{ description }}{{/ description }} -{{/ parameters }} -{{/ has_parameters? }} -{{# has_attributes? }} - -+ Attributes (object) -{{# attributes }} - + {{ name }}{{# example }}: {{ example }}{{/ example }}{{# properties_description }} ({{ properties_description }}){{/ properties_description }}{{# description }} - {{ description }}{{/ description }} -{{/ attributes }} -{{/ has_attributes? }} -{{# http_methods }} - -### {{ description }} [{{ http_method }}] -{{# examples }} -{{# requests }} -{{# has_request? }} - -+ Request {{ description }}{{# request_content_type }} ({{ request_content_type }}){{/ request_content_type }} -{{/ has_request? }} -{{# request_headers_text }} - - + Headers - - {{{ request_headers_text }}} -{{/ request_headers_text }} -{{# request_body }} - - + Body - - {{{ request_body }}} -{{/ request_body }} -{{# has_response? }} - -+ Response {{ response_status }} ({{ response_content_type }}) -{{/ has_response? }} -{{# response_headers_text }} - - + Headers - - {{{ response_headers_text }}} -{{/ response_headers_text }} -{{# response_body }} - - + Body - - {{{ response_body }}} -{{/ response_body }} -{{/ requests }} -{{/ examples }} -{{/ http_methods }} -{{/ routes }} -{{/ sections }} diff --git a/templates/rspec_api_documentation/rspec_api_documentation/html_example.mustache b/templates/rspec_api_documentation/rspec_api_documentation/html_example.mustache deleted file mode 100644 index 47474e0..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/html_example.mustache +++ /dev/null @@ -1,140 +0,0 @@ - - - - {{resource_name}} API - - - - -
-

{{resource_name}} API

- {{# resource_explanation }} - -

{{{ resource_explanation }}}

- {{/ resource_explanation }} - -
-

{{ description }}

-

{{ http_method }} {{ route }}

- {{# explanation }} -

- {{{ explanation }}} -

- {{/ explanation }} - - {{# has_parameters? }} -

Parameters

- - - - - - - - - - {{# parameters }} - - - {{# scope }} - {{ scope }}[{{ name }}] - {{/ scope }} - {{^ scope }} - {{ name }} - {{/ scope }} - - - - - {{/ parameters }} - -
NameDescriptionDefault
- {{ description }} - - {{ default }} -
- {{/ has_parameters? }} - - {{# has_response_fields? }} -

Response Fields

- - - - - - - - - - - {{# response_fields }} - - - - - - - {{/ response_fields }} - -
NameDefaultDescriptionNot Null
- {{# scope }} - {{ scope }}[{{ name }}] - {{/ scope }} - {{^ scope }} - {{ name }} - {{/ scope }} - - {{ default }} - - {{ description }} - - {{ not_null }} -
- {{/ has_response_fields? }} - - {{# requests }} -

Request

- - {{# request_headers_text }} -

Headers

-
{{ request_headers_text }}
- {{/ request_headers_text }} - -

Route

-
{{ request_method }} {{ request_path }}
- - {{# request_query_parameters_text }} -

Query Parameters

-
{{ request_query_parameters_text }}
- {{/ request_query_parameters_text }} - - {{# request_body }} -

Body

-
{{{ request_body }}}
- {{/ request_body }} - - {{# curl }} -

cURL

-
{{ curl }}
- {{/ curl }} - - {{# response_status }} -

Response

- {{# response_headers_text }} -

Headers

-
{{ response_headers_text }}
- {{/ response_headers_text }} -

Status

-
{{ response_status }} {{ response_status_text}}
- {{# response_body }} -

Body

-
{{ response_body }}
- {{/ response_body }} - {{/ response_status }} - {{/ requests }} -
-
- - diff --git a/templates/rspec_api_documentation/rspec_api_documentation/html_index.mustache b/templates/rspec_api_documentation/rspec_api_documentation/html_index.mustache deleted file mode 100644 index aaedde2..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/html_index.mustache +++ /dev/null @@ -1,33 +0,0 @@ - - - - {{ api_name }} - - - - -
-

{{ api_name }}

- {{{ api_explanation }}} - {{# sections }} -
-

{{ resource_name }}

- {{# resource_explanation }} - -

{{{ resource_explanation }}}

- {{/ resource_explanation }} - - -
- {{/ sections }} -
- - diff --git a/templates/rspec_api_documentation/rspec_api_documentation/markdown_example.mustache b/templates/rspec_api_documentation/rspec_api_documentation/markdown_example.mustache deleted file mode 100644 index 474ab56..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/markdown_example.mustache +++ /dev/null @@ -1,85 +0,0 @@ -# {{ resource_name }} API -{{# resource_explanation }} - -{{{ resource_explanation }}} -{{/ resource_explanation }} - -## {{ description }} - -### {{ http_method }} {{ route }} -{{# explanation }} - -{{ explanation }} -{{/ explanation }} -{{# has_parameters? }} - -### Parameters - -| Name | Description | Required | Default | -|------|-------------|----------|---------| -{{# parameters }} -| {{ name }} | {{ description }} | {{ required }} | {{ default }} -{{/ parameters }} - -{{/ has_parameters? }} -{{# has_response_fields? }} - -### Response Fields - -| Name | Default | Description | Not Null | -|------|---------|-------------|----------| -{{# response_fields }} -| {{ name }} | {{ default }} | {{ description }} | {{ not_null }} | -{{/ response_fields }} - -{{/ has_response_fields? }} -{{# requests }} -### Request - -#### Headers - -
{{ request_headers_text }}
- -#### Route - -
{{ request_method }} {{ request_path }}
-{{# request_query_parameters_text }} - -#### Query Parameters - -
{{ request_query_parameters_text }}
-{{/ request_query_parameters_text }} -{{# request_body }} - -#### Body - -
{{{ request_body }}}
-{{/ request_body }} -{{# curl }} - -#### cURL - -
{{ curl }}
-{{/ curl }} - -{{# response_status }} -### Response - -#### Headers - -
{{ response_headers_text }}
- -#### Status - -
{{ response_status }} {{ response_status_text}}
- -{{# response_body }} -#### Body - -~~~ -{{{ response_body }}} -~~~ - -{{/ response_body }} -{{/ response_status }} -{{/ requests }} diff --git a/templates/rspec_api_documentation/rspec_api_documentation/markdown_index.mustache b/templates/rspec_api_documentation/rspec_api_documentation/markdown_index.mustache deleted file mode 100644 index c88754d..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/markdown_index.mustache +++ /dev/null @@ -1,15 +0,0 @@ -# {{ api_name }} -{{{ api_explanation }}} - -{{# sections }} -## {{ resource_name }} -{{# resource_explanation }} - -{{{ resource_explanation }}} -{{/ resource_explanation }} - -{{# examples }} -* [{{ description }}]({{ dirname }}/{{ filename }}) -{{/ examples }} - -{{/ sections }} diff --git a/templates/rspec_api_documentation/rspec_api_documentation/slate_example.mustache b/templates/rspec_api_documentation/rspec_api_documentation/slate_example.mustache deleted file mode 100644 index 01fafb9..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/slate_example.mustache +++ /dev/null @@ -1,84 +0,0 @@ -## {{ description }} - -{{# explanation }} -{{{ explanation }}} -{{/ explanation }} - -### Request - -#### Endpoint - -{{# requests}} -```plaintext -{{ request_method }} {{ request_path }} -{{ request_headers_text }} -``` -{{/ requests}} - -`{{ http_method }} {{ route }}` - -#### Parameters - -{{# requests}} -{{# request_query_parameters_text }} - -```json -{{ request_query_parameters_text }} -``` -{{/ request_query_parameters_text }} -{{# request_body }} - -```json -{{{ request_body }}} -``` -{{/ request_body }} - -{{# has_parameters? }} - -| Name | Description | -|:-----|:------------| -{{# parameters }} -| {{#scope}}{{scope}}[{{/scope}}{{ name }}{{#scope}}]{{/scope}} {{# required }}*required*{{/ required }} | {{{ description }}} | -{{/ parameters }} - -{{/ has_parameters? }} -{{^ has_parameters? }} -None known. -{{/ has_parameters? }} - -{{# response_status}} - -### Response - -```plaintext -{{ response_headers_text }} -{{ response_status }} {{ response_status_text}} -``` - -{{# response_body}} - -```json -{{{ response_body }}} -``` -{{/response_body}} - -{{/ response_status}} - -{{# has_response_fields? }} - -#### Fields - -| Name | Description | -|:-----------|:--------------------| -{{# response_fields }} -| {{#scope}}{{scope}}[{{/scope}}{{ name }}{{#scope}}]{{/scope}} | {{{ description }}} | -{{/ response_fields }} - -{{/ has_response_fields? }} - -{{# curl }} -```shell -{{{ curl }}} -``` -{{/ curl }} -{{/ requests}} diff --git a/templates/rspec_api_documentation/rspec_api_documentation/slate_index.mustache b/templates/rspec_api_documentation/rspec_api_documentation/slate_index.mustache deleted file mode 100644 index be0e5ae..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/slate_index.mustache +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: {{ api_name }} -language_tabs: - - json: JSON - - shell: cURL ---- - -{{{ api_explanation }}} diff --git a/templates/rspec_api_documentation/rspec_api_documentation/textile_example.mustache b/templates/rspec_api_documentation/rspec_api_documentation/textile_example.mustache deleted file mode 100644 index a7e022b..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/textile_example.mustache +++ /dev/null @@ -1,80 +0,0 @@ -h1. {{ resource_name }} API -{{# resource_explanation }} - -{{{ resource_explanation }}} -{{/ resource_explanation }} - -h2. {{ description }} - -h3. {{ http_method }} {{ route }} - -{{# explanation }} -{{ explanation }} - -{{/ explanation }} -{{# has_parameters? }} -h3. Parameters -{{# parameters }} - -Name : {{ name }} {{# required }} *- required -*{{/ required }} -Description : {{ description }} -{{/ parameters }} - -{{/ has_parameters? }} -{{# has_response_fields? }} -h3. Response Fields -{{# response_fields }} - -Name : {{ name }} -Description : {{ description }} -{{/ response_fields }} - -{{/ has_response_fields? }} -{{# requests }} -h3. Request - -h4. Headers - -
{{ request_headers_text }}
- -h4. Route - -
{{ request_method }} {{ request_path }}
- -{{# request_query_parameters_text }} -h4. Query Parameters - -
{{ request_query_parameters_text }}
- -{{/ request_query_parameters_text }} -{{# request_body }} -h4. Body - -
{{{ request_body }}}
- -{{/ request_body }} -{{# curl }} -h4. cURL - -
{{ curl }}
- -{{/ curl }} -{{# response_status }} -h3. Response - -h4. Headers - -
{{ response_headers_text }}
- -h4. Status - -
{{ response_status }} {{ response_status_text}}
- -{{# response_body }} -h4. Body - -
{{{ response_body }}}
- -{{/ response_body }} -{{/ response_status }} -{{/ requests }} diff --git a/templates/rspec_api_documentation/rspec_api_documentation/textile_index.mustache b/templates/rspec_api_documentation/rspec_api_documentation/textile_index.mustache deleted file mode 100644 index 564b410..0000000 --- a/templates/rspec_api_documentation/rspec_api_documentation/textile_index.mustache +++ /dev/null @@ -1,15 +0,0 @@ -h1. {{ api_name }} -{{{ api_explanation }}} - -{{# sections }} -h2. {{ resource_name }} -{{# resource_explanation }} - -{{{ resource_explanation }}} -{{/ resource_explanation }} - -{{# examples }} -* "{{ description }}":{{ dirname }}/{{ filename }} -{{/ examples }} - -{{/ sections }} From 366a41b82da932588f21aa4a9eb8ad88c5cc814a Mon Sep 17 00:00:00 2001 From: Jay Varner Date: Tue, 11 Aug 2026 10:55:08 -0400 Subject: [PATCH 3/5] Include publisher in index --- app/models/published_letter.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/models/published_letter.rb b/app/models/published_letter.rb index 69ec01b..487764b 100644 --- a/app/models/published_letter.rb +++ b/app/models/published_letter.rb @@ -30,10 +30,10 @@ class PublishedLetter < ApplicationRecord has_many :letter_languages, foreign_key: 'letter_id', inverse_of: :letter, dependent: :destroy has_many :languages, -> { distinct }, through: :letter_languages - belongs_to :letter_file, foreign_key: 'letter_id', inverse_of: :letter, optional: true - belongs_to :file_folder, foreign_key: 'letter_id', inverse_of: :letter, optional: true - belongs_to :letter_owner, foreign_key: 'letter_id', inverse_of: :letter, optional: true - belongs_to :letter_publisher, foreign_key: 'letter_id', inverse_of: :letter, optional: true + belongs_to :letter_file, foreign_key: 'letter_id', inverse_of: :letters, optional: true + belongs_to :file_folder, foreign_key: 'letter_id', inverse_of: :letters, optional: true + belongs_to :letter_owner, foreign_key: 'letter_id', inverse_of: :letters, optional: true + belongs_to :letter_publisher, foreign_key: 'letter_id', inverse_of: :letters, optional: true def self.default_scope where(published: true) @@ -53,7 +53,8 @@ def search_data repositories: repositories.select(&:published).map(&:label), languages: languages.map(&:label), published:, - volume: volume.to_s + volume: volume.to_s, + publisher: letter_publisher } end From 6e4581ca7f7b7bdc2bd96c78046b31fba4268df3 Mon Sep 17 00:00:00 2001 From: Jay Varner Date: Tue, 11 Aug 2026 11:04:46 -0400 Subject: [PATCH 4/5] Index the publisher's label --- app/models/published_letter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/published_letter.rb b/app/models/published_letter.rb index 487764b..2253916 100644 --- a/app/models/published_letter.rb +++ b/app/models/published_letter.rb @@ -54,7 +54,7 @@ def search_data languages: languages.map(&:label), published:, volume: volume.to_s, - publisher: letter_publisher + publisher: letter_publisher.label } end From 0f4aea0a02647e6cd91e2a81bcbeeade0cb428c8 Mon Sep 17 00:00:00 2001 From: Jay Varner Date: Tue, 11 Aug 2026 11:16:19 -0400 Subject: [PATCH 5/5] Index publisher label --- app/models/published_letter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/published_letter.rb b/app/models/published_letter.rb index 2253916..f50edc6 100644 --- a/app/models/published_letter.rb +++ b/app/models/published_letter.rb @@ -54,7 +54,7 @@ def search_data languages: languages.map(&:label), published:, volume: volume.to_s, - publisher: letter_publisher.label + publisher: letter_publisher&.label } end