From 866328521159a18e7e0bf1e971b197b00d5aaafe Mon Sep 17 00:00:00 2001 From: Erin Sullivan Date: Tue, 31 Mar 2026 10:02:30 -0400 Subject: [PATCH 1/4] `Actions` and `Action` presenters, and specs. --- lib/search/presenters.rb | 2 +- lib/search/presenters/actions.rb | 25 +++++++++++++++++++ lib/search/presenters/{ => actions}/action.rb | 2 +- .../presenters/page/datastore_static.rb | 10 +------- lib/search/presenters/page/list.rb | 2 +- spec/presenters/actions/action_spec.rb | 16 ++++++++++++ spec/presenters/actions_spec.rb | 20 +++++++++++++++ 7 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 lib/search/presenters/actions.rb rename lib/search/presenters/{ => actions}/action.rb (74%) create mode 100644 spec/presenters/actions/action_spec.rb create mode 100644 spec/presenters/actions_spec.rb diff --git a/lib/search/presenters.rb b/lib/search/presenters.rb index 2045f935..1124eedf 100644 --- a/lib/search/presenters.rb +++ b/lib/search/presenters.rb @@ -2,7 +2,7 @@ module Search module Presenters end end -require "search/presenters/action" +require "search/presenters/actions" require "search/presenters/affiliations" require "search/presenters/breadcrumbs" require "search/presenters/icons" diff --git a/lib/search/presenters/actions.rb b/lib/search/presenters/actions.rb new file mode 100644 index 00000000..3472eb59 --- /dev/null +++ b/lib/search/presenters/actions.rb @@ -0,0 +1,25 @@ +class Search::Presenters::Actions + ACTIONS = [ + ["email", "Email"], + ["text", "Text"], + ["citation", "Citation"], + ["ris", "Export Citation (EndNote, Zotero, etc.)"], + ["link", "Copy link"], + ["toggle-selected", "Toggle selected"] + ] + + def initialize(exclude = []) + @exclude = exclude || [] + @actions = ACTIONS + .reject { |uid, _| @exclude.include?(uid) } + .map { |uid, text| Action.new(uid: uid, text: text) } + end + + include Enumerable + + def each(&block) + @actions.each(&block) + end +end + +require "search/presenters/actions/action" diff --git a/lib/search/presenters/action.rb b/lib/search/presenters/actions/action.rb similarity index 74% rename from lib/search/presenters/action.rb rename to lib/search/presenters/actions/action.rb index 64f0e2e3..c1cd8ebb 100644 --- a/lib/search/presenters/action.rb +++ b/lib/search/presenters/actions/action.rb @@ -1,4 +1,4 @@ -class Search::Presenters::Action +class Search::Presenters::Actions::Action attr_reader :uid, :text def initialize(uid:, text:) @uid = uid diff --git a/lib/search/presenters/page/datastore_static.rb b/lib/search/presenters/page/datastore_static.rb index 861f7b84..0c17e53b 100644 --- a/lib/search/presenters/page/datastore_static.rb +++ b/lib/search/presenters/page/datastore_static.rb @@ -1,13 +1,5 @@ class Search::Presenters::Page class DatastoreStatic < self - ACTIONS = [ - ["email", "Email"], - ["text", "Text"], - ["citation", "Citation"], - ["ris", "Export Citation (EndNote, Zotero, etc.)"], - ["link", "Copy link"], - ["toggle-selected", "Toggle selected"] - ].map { |uid, text| Search::Presenters::Action.new(uid: uid, text: text) } def self.for(slug:, uri:, patron:) datastore = Search::Datastores.find(slug) new(datastore: datastore, uri: uri, patron: patron) @@ -37,7 +29,7 @@ def page_title end def actions - ACTIONS + Search::Presenters::Actions.new(nil) end private diff --git a/lib/search/presenters/page/list.rb b/lib/search/presenters/page/list.rb index cda7ae2b..7ba4e11d 100644 --- a/lib/search/presenters/page/list.rb +++ b/lib/search/presenters/page/list.rb @@ -27,7 +27,7 @@ def ris_action_url end def actions - ACTIONS.reject { |action| action.uid == "link" } + Search::Presenters::Actions.new(["link"]) end def show_holdings? diff --git a/spec/presenters/actions/action_spec.rb b/spec/presenters/actions/action_spec.rb new file mode 100644 index 00000000..699e3a2a --- /dev/null +++ b/spec/presenters/actions/action_spec.rb @@ -0,0 +1,16 @@ +RSpec.describe Search::Presenters::Actions::Action do + before(:each) do + @uid = "email" + @text = "Email" + end + + subject do + described_class.new(uid: @uid, text: @text) + end + + context "#to_s" do + it "returns the text of the action" do + expect(subject.to_s).to eq(@text) + end + end +end diff --git a/spec/presenters/actions_spec.rb b/spec/presenters/actions_spec.rb new file mode 100644 index 00000000..a51ad00d --- /dev/null +++ b/spec/presenters/actions_spec.rb @@ -0,0 +1,20 @@ +RSpec.describe Search::Presenters::Actions do + before(:each) do + @exclude = ["link"] + end + + subject do + described_class.new(@exclude) + end + + context "#each" do + it "iterates over the actions with the body" do + actions = [] + subject.each do |action| + actions << action.uid + end + + expect(actions).to eq(["email", "text", "citation", "ris", "toggle-selected"]) + end + end +end From 4f5606cc60fc44d4ed76fe54f8b39f82ad58cd93 Mon Sep 17 00:00:00 2001 From: Erin Sullivan Date: Tue, 7 Apr 2026 15:51:48 -0400 Subject: [PATCH 2/4] `Search::Presenters::Actions::Action::Citation` class and specs. --- lib/search/presenters/actions/action.rb | 2 ++ .../presenters/actions/action/citation.rb | 13 +++++++ .../presenters/page/datastore_static.rb | 4 +++ lib/search/presenters/page/record.rb | 8 +++-- lib/search/presenters/page/results.rb | 4 +++ .../actions/action/citation_spec.rb | 35 +++++++++++++++++++ .../partials/actions/action/citation/_csl.erb | 10 +----- .../partials/actions/action/ris/_textarea.erb | 10 +----- 8 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 lib/search/presenters/actions/action/citation.rb create mode 100644 spec/presenters/actions/action/citation_spec.rb diff --git a/lib/search/presenters/actions/action.rb b/lib/search/presenters/actions/action.rb index c1cd8ebb..b6970cbe 100644 --- a/lib/search/presenters/actions/action.rb +++ b/lib/search/presenters/actions/action.rb @@ -9,3 +9,5 @@ def to_s text end end + +require "search/presenters/actions/action/citation" diff --git a/lib/search/presenters/actions/action/citation.rb b/lib/search/presenters/actions/action/citation.rb new file mode 100644 index 00000000..b9a5130a --- /dev/null +++ b/lib/search/presenters/actions/action/citation.rb @@ -0,0 +1,13 @@ +class Search::Presenters::Actions::Action::Citation + def initialize(results) + @results = results || [] + end + + def csl + @results.map { |record| record.csl } + end + + def ris + @results.map { |record| record.ris } + end +end diff --git a/lib/search/presenters/page/datastore_static.rb b/lib/search/presenters/page/datastore_static.rb index 0c17e53b..73928371 100644 --- a/lib/search/presenters/page/datastore_static.rb +++ b/lib/search/presenters/page/datastore_static.rb @@ -32,6 +32,10 @@ def actions Search::Presenters::Actions.new(nil) end + def citation + Search::Presenters::Actions::Action::Citation.new(nil) + end + private def title_parts diff --git a/lib/search/presenters/page/record.rb b/lib/search/presenters/page/record.rb index e4108ba3..9973e4bd 100644 --- a/lib/search/presenters/page/record.rb +++ b/lib/search/presenters/page/record.rb @@ -35,14 +35,18 @@ def breadcrumbs Search::Presenters::Breadcrumbs.new(current_page: CURRENT_PAGE, uri: @uri) end - def meta_tags - record.meta_tags + def citation + Search::Presenters::Actions::Action::Citation.new([@record]) end def ris_action_url "#{@uri}/ris" end + def meta_tags + record.meta_tags + end + private def title_parts diff --git a/lib/search/presenters/page/results.rb b/lib/search/presenters/page/results.rb index 4be9e9bd..150c7143 100644 --- a/lib/search/presenters/page/results.rb +++ b/lib/search/presenters/page/results.rb @@ -33,6 +33,10 @@ def scripts super.push("datastores/results/scripts.js") end + def citation + Search::Presenters::Actions::Action::Citation.new(entries) + end + def ris_action_url end diff --git a/spec/presenters/actions/action/citation_spec.rb b/spec/presenters/actions/action/citation_spec.rb new file mode 100644 index 00000000..59324708 --- /dev/null +++ b/spec/presenters/actions/action/citation_spec.rb @@ -0,0 +1,35 @@ +RSpec.describe Search::Presenters::Actions::Action::Citation do + before(:each) do + @results = [ + OpenStruct.new(csl: "CSL Citation 1", ris: "RIS Citation 1"), + OpenStruct.new(csl: "CSL Citation 2", ris: "RIS Citation 2"), + OpenStruct.new(csl: "CSL Citation 3", ris: "RIS Citation 3") + ] + end + + subject do + described_class.new(@results) + end + + context "#csl" do + it "returns an array of CSL citations" do + expect(subject.csl).to eq(@results.map { |record| record.csl }) + end + + it "returns an empty array if there are no results" do + empty_subject = described_class.new([]) + expect(empty_subject.csl).to eq([]) + end + end + + context "#ris" do + it "returns an array of RIS citations" do + expect(subject.ris).to eq(@results.map { |record| record.ris }) + end + + it "returns an empty array if there are no results" do + empty_subject = described_class.new([]) + expect(empty_subject.ris).to eq([]) + end + end +end diff --git a/views/datastores/partials/actions/action/citation/_csl.erb b/views/datastores/partials/actions/action/citation/_csl.erb index 60eb6f52..363967f2 100644 --- a/views/datastores/partials/actions/action/citation/_csl.erb +++ b/views/datastores/partials/actions/action/citation/_csl.erb @@ -1,9 +1 @@ -<% - csl = [] - if @record - csl = [@record.csl] - elsif defined?(@presenter.entries) - csl = @presenter.entries.map { |entry| entry.csl } - end -%> - + diff --git a/views/datastores/partials/actions/action/ris/_textarea.erb b/views/datastores/partials/actions/action/ris/_textarea.erb index a0a97c0b..94bb095a 100644 --- a/views/datastores/partials/actions/action/ris/_textarea.erb +++ b/views/datastores/partials/actions/action/ris/_textarea.erb @@ -1,9 +1 @@ -<% - ris = [] - if @record - ris = [@record.ris] - elsif defined?(@presenter.entries) - ris = @presenter.entries.map { |entry| entry.ris } - end -%> - + From 2ff346a5fbc429ffcf3e15e0cf461e07e6ebb56f Mon Sep 17 00:00:00 2001 From: Erin Sullivan Date: Wed, 8 Apr 2026 08:33:43 -0400 Subject: [PATCH 3/4] Using `Array` and `filter_map`. --- lib/search/presenters/actions.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/search/presenters/actions.rb b/lib/search/presenters/actions.rb index 3472eb59..d39185fc 100644 --- a/lib/search/presenters/actions.rb +++ b/lib/search/presenters/actions.rb @@ -9,10 +9,10 @@ class Search::Presenters::Actions ] def initialize(exclude = []) - @exclude = exclude || [] - @actions = ACTIONS - .reject { |uid, _| @exclude.include?(uid) } - .map { |uid, text| Action.new(uid: uid, text: text) } + exclude = Array(exclude) + @actions = ACTIONS.filter_map do |uid, text| + Action.new(uid: uid, text: text) unless exclude.include?(uid) + end end include Enumerable From bbbcb6d7a6cf875b8564a26c26de1088795d2c26 Mon Sep 17 00:00:00 2001 From: Erin Sullivan Date: Wed, 8 Apr 2026 14:55:26 -0400 Subject: [PATCH 4/4] `Search::Presenters::Actions::Action::Link` --- lib/search/presenters/actions/action.rb | 1 + lib/search/presenters/actions/action/link.rb | 14 +++++++++++++ .../presenters/page/datastore_static.rb | 4 ++++ spec/presenters/actions/action/link_spec.rb | 21 +++++++++++++++++++ .../partials/actions/action/_link.erb | 11 +--------- 5 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 lib/search/presenters/actions/action/link.rb create mode 100644 spec/presenters/actions/action/link_spec.rb diff --git a/lib/search/presenters/actions/action.rb b/lib/search/presenters/actions/action.rb index b6970cbe..97730956 100644 --- a/lib/search/presenters/actions/action.rb +++ b/lib/search/presenters/actions/action.rb @@ -11,3 +11,4 @@ def to_s end require "search/presenters/actions/action/citation" +require "search/presenters/actions/action/link" diff --git a/lib/search/presenters/actions/action/link.rb b/lib/search/presenters/actions/action/link.rb new file mode 100644 index 00000000..918615e7 --- /dev/null +++ b/lib/search/presenters/actions/action/link.rb @@ -0,0 +1,14 @@ +class Search::Presenters::Actions::Action::Link + def initialize(uri) + @uri = uri + end + + def uri + if @uri.path.include?("/record") + # Remove query parameters if the URI contains `/record` + Addressable::URI.parse(@uri).tap { |u| u.query = nil }.to_s + else + @uri.to_s + end + end +end diff --git a/lib/search/presenters/page/datastore_static.rb b/lib/search/presenters/page/datastore_static.rb index 73928371..5bd2bb79 100644 --- a/lib/search/presenters/page/datastore_static.rb +++ b/lib/search/presenters/page/datastore_static.rb @@ -36,6 +36,10 @@ def citation Search::Presenters::Actions::Action::Citation.new(nil) end + def copy_link + Search::Presenters::Actions::Action::Link.new(@uri) + end + private def title_parts diff --git a/spec/presenters/actions/action/link_spec.rb b/spec/presenters/actions/action/link_spec.rb new file mode 100644 index 00000000..0c010610 --- /dev/null +++ b/spec/presenters/actions/action/link_spec.rb @@ -0,0 +1,21 @@ +RSpec.describe Search::Presenters::Actions::Action::Link do + url = "/catalog?query=example" + before(:each) do + @uri = URI.parse(url) + end + + subject do + described_class.new(@uri) + end + + context "#uri" do + it "returns the full URI as a string" do + expect(subject.uri).to eq(url) + end + + it "returns the URI without query parameters if it contains `/record`" do + @uri = URI.parse("/record/123?query=example") + expect(subject.uri).to eq("/record/123") + end + end +end diff --git a/views/datastores/partials/actions/action/_link.erb b/views/datastores/partials/actions/action/_link.erb index 6d0558fa..234dd33e 100644 --- a/views/datastores/partials/actions/action/_link.erb +++ b/views/datastores/partials/actions/action/_link.erb @@ -1,15 +1,6 @@ -<% - # Get the full URL with paths - uri = S.base_url + request.path - # Check if not viewing a full record, and there are query parameters - if !request.path.include?("/record") && !request.query_string.empty? - # Apply the query parameters to the full path - uri = uri + "?" + request.query_string - end -%> <%= erb :"datastores/partials/actions/_alert", locals: { type: "success", message: "Link copied!" } %>