Skip to content

Commit

Permalink
feat: add in Sample::Query model which is a form object and refactor …
Browse files Browse the repository at this point in the history
…the samples table component to no longer have a dependency on ransack
  • Loading branch information
ericenns committed Dec 11, 2024
1 parent 0483a39 commit 3c8740a
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 214 deletions.
39 changes: 13 additions & 26 deletions app/components/samples/table_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,15 @@
"w-4 h-4 mr-2.5 text-primary-600 bg-slate-100 border-slate-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600" %>
<% end %>

<% if column == :attachments_updated_at %>
<%= render Ransack::SortComponent.new(
ransack_obj: @q,
label: t(".#{column}"),
url: helpers.sorting_url(@q, :attachments_updated_at_nulls_last),
field: :attachments_updated_at_nulls_last,
data: {
turbo_action: "replace",
},
) %>
<% else %>
<%= render Ransack::SortComponent.new(
ransack_obj: @q,
label: t(".#{column}"),
url: helpers.sorting_url(@q, column),
field: column,
data: {
turbo_action: "replace",
},
) %>
<% end %>
<%= render SortComponent.new(
sort: @search_params[:sort],
label: t(".#{column}"),
url: sort_url(column),
field: column,
data: {
turbo_action: "replace",
},
) %>
<% end %>
<% end %>
<% @metadata_fields.each do |field| %>
Expand All @@ -62,11 +50,10 @@
scope: 'col',
classes: class_names('px-3 py-3')
) do %>
<%= render Ransack::SortComponent.new(
ransack_obj: @q,
<%= render SortComponent.new(
sort: @search_params[:sort],
label: field,
url:
helpers.sorting_url(@q, URI.encode_www_form_component("metadata_#{field}")),
url: sort_url("metadata_#{field}"),
field: "metadata_#{field}",
data: {
turbo_action: "replace",
Expand Down Expand Up @@ -127,7 +114,7 @@
) %>
</span>
<% end %>
<% elsif column == :project %>
<% elsif column == :project_id %>
<%= link_to sample.project.puid,
project_samples_path(sample.project),
data: {
Expand Down
24 changes: 19 additions & 5 deletions app/components/samples/table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ module Samples
class TableComponent < Component
include Ransack::Helpers::FormHelper

# rubocop:disable Naming/MethodParameterName,Metrics/ParameterLists
# rubocop:disable Metrics/ParameterLists
def initialize(
samples,
namespace,
pagy,
q,
has_samples: true,
abilities: {},
metadata_fields: [],
Expand All @@ -24,7 +23,6 @@ def initialize(
@samples = samples
@namespace = namespace
@pagy = pagy
@q = q
@has_samples = has_samples
@abilities = abilities
@metadata_fields = metadata_fields
Expand All @@ -34,9 +32,11 @@ def initialize(
@renders_row_actions = @row_actions.select { |_key, value| value }.count.positive?
@system_arguments = system_arguments

@sort_key, @sort_direction = search_params[:sort].split

@columns = columns
end
# rubocop:enable Naming/MethodParameterName,Metrics/ParameterLists
# rubocop:enable Metrics/ParameterLists

def system_arguments
{ tag: 'div' }.deep_merge(@system_arguments).tap do |args|
Expand Down Expand Up @@ -77,11 +77,25 @@ def select_samples_url(**)
end
end

def sort_url(field)
sort_string = if field.to_s == @sort_key && @sort_direction == 'asc'
"#{field} desc"
else
"#{field} asc"
end

if @namespace.type == 'Group'
group_samples_url(@namespace, q: { sort: sort_string })
else
namespace_project_samples_url(@namespace.parent, @namespace.project, q: { sort: sort_string })
end
end

private

def columns
columns = %i[puid name]
columns << :project if @namespace.type == 'Group'
columns << :project_id if @namespace.type == 'Group'
columns += %i[created_at updated_at attachments_updated_at]
columns
end
Expand Down
4 changes: 4 additions & 0 deletions app/components/sort_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= render Viral::BaseComponent.new(tag: 'a', href: url, type: "button", classes: class_names('inline-flex items-center'), **system_arguments) do %>
<%= label %>
<span class="flex-grow-0 ml-2 flex items-center"><%= viral_icon(name: icon, classes: "w-4 h-4 inline-block") %></span>
<% end %>
20 changes: 20 additions & 0 deletions app/components/sort_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

# Component for sort in table headers
class SortComponent < Component
attr_reader :label, :field, :url, :system_arguments

def initialize(sort:, label:, url:, field:, **system_arguments)
@sort_key, @sort_direction = sort.split
@label = label
@url = url
@field = field
@system_arguments = system_arguments
end

def icon
return unless @sort_key.to_s == URI.encode_www_form_component(@field.to_s)

@sort_direction == 'asc' ? 'arrow_up' : 'arrow_down'
end
end
1 change: 1 addition & 0 deletions app/controllers/concerns/storable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def store(session_key, value)
# @param value [Hash] the value to merge with the existing value in the session.
def update_store(search_key, value)
session[search_key] = (session[search_key] || {}).merge(value)
session[search_key].reject! { |_, val| val.blank? || val == [''] }
get_store(search_key)
end

Expand Down
24 changes: 14 additions & 10 deletions app/controllers/groups/samples_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ class SamplesController < Groups::ApplicationController
include Storable

before_action :group, :current_page
before_action :process_samples, only: %i[index search select]
include Sortable
before_action :query, only: %i[index search select]

def index
@timestamp = DateTime.current
@pagy, @samples = pagy_with_metadata_sort(@q.result)
@pagy, @samples = pagy(@query.results)
@has_samples = authorized_samples.count.positive?
end

Expand All @@ -27,7 +26,7 @@ def select
respond_to do |format|
format.turbo_stream do
if params[:select].present?
@selected_sample_ids = @q.result.where(updated_at: ..params[:timestamp].to_datetime).select(:id).pluck(:id)
@selected_sample_ids = @query.results.where(updated_at: ..params[:timestamp].to_datetime).select(:id).pluck(:id)

Check warning on line 29 in app/controllers/groups/samples_controller.rb

View workflow job for this annotation

GitHub Actions / runner / rubocop

[rubocop] reported by reviewdog 🐶 Line is too long. [124/120] Raw Output: app/controllers/groups/samples_controller.rb:29:121: C: Layout/LineLength: Line is too long. [124/120]
end
end
end
Expand All @@ -39,6 +38,10 @@ def group
@group = Group.find_by_full_path(params[:group_id]) # rubocop:disable Rails/DynamicFindBy
end

def authorized_projects
authorized_scope(Project, type: :relation, as: :group_projects, scope_options: { group: @group })
end

def authorized_samples
authorized_scope(Sample, type: :relation, as: :namespace_samples,
scope_options: { namespace: @group }).includes(project: { namespace: [{ parent: :route },
Expand Down Expand Up @@ -71,20 +74,21 @@ def set_metadata_fields
fields_for_namespace(namespace: @group, show_fields: @search_params && @search_params[:metadata].to_i == 1)
end

def process_samples
def query
authorize! @group, to: :sample_listing?

@search_params = search_params
set_metadata_fields
query_parser = Irida::SearchSyntax::Ransack.new(text: :name_or_puid_cont, metadata_fields: @fields)
@parsed_params = query_parser.parse(@search_params.fetch(:name_or_puid_cont, nil))
@q = authorized_samples.ransack(@search_params.except(:name_or_puid_cont).merge(@parsed_params))

@query = Sample::Query.new(@search_params.except(:metadata).merge({ project_ids: authorized_projects.select(:id) }))

Check warning on line 83 in app/controllers/groups/samples_controller.rb

View workflow job for this annotation

GitHub Actions / runner / rubocop

[rubocop] reported by reviewdog 🐶 Line is too long. [122/120] Raw Output: app/controllers/groups/samples_controller.rb:83:121: C: Layout/LineLength: Line is too long. [122/120]
end

def search_params
updated_params = update_store(search_key, params[:q].present? ? params[:q].to_unsafe_h : {})

if updated_params[:metadata].to_i.zero? && updated_params[:s].present? && updated_params[:s].match?(/metadata_/)
updated_params[:s] = default_sort
if !updated_params.key?(:sort) ||
(updated_params[:metadata].to_i.zero? && updated_params[:sort]&.match?(/metadata_/))
updated_params[:sort] = 'updated_at desc'
update_store(search_key, updated_params)
end
updated_params
Expand Down
23 changes: 11 additions & 12 deletions app/controllers/projects/samples_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ class SamplesController < Projects::ApplicationController # rubocop:disable Metr

before_action :sample, only: %i[show edit update view_history_version]
before_action :current_page
before_action :process_samples, only: %i[index search select]
include Sortable
before_action :query, only: %i[index search select]

def index
@timestamp = DateTime.current
@pagy, @samples = pagy_with_metadata_sort(@q.result)
@pagy, @samples = pagy(@query.results)
@has_samples = @project.samples.size.positive?
end

Expand Down Expand Up @@ -84,7 +83,7 @@ def select
respond_to do |format|
format.turbo_stream do
if params[:select].present?
@samples = @q.result.where(updated_at: ..params[:timestamp].to_datetime).select(:id)
@samples = @query.results.where(updated_at: ..params[:timestamp].to_datetime).select(:id)
end
end
end
Expand Down Expand Up @@ -138,22 +137,22 @@ def search_key
:"#{controller_name}_#{@project.id}_search_params"
end

def process_samples
def query
authorize! @project, to: :sample_listing?

@search_params = search_params

set_metadata_fields
query_parser = Irida::SearchSyntax::Ransack.new(text: :name_or_puid_cont, metadata_fields: @fields)
@parsed_params = query_parser.parse(@search_params.fetch(:name_or_puid_cont, nil))
@q = load_samples.ransack(@search_params.except(:name_or_puid_cont).merge(@parsed_params))

@query = Sample::Query.new(@search_params.except(:metadata).merge({ project_ids: [@project.id] }))
end

def search_params
updated_params = update_store(search_key, params[:q].present? ? params[:q].to_unsafe_h : {})
updated_params = update_store(search_key,
params[:q].present? ? params[:q].to_unsafe_h : {}).with_indifferent_access

if updated_params[:metadata].to_i.zero? && updated_params[:s].present? && updated_params[:s].match?(/metadata_/)
updated_params[:s] = default_sort
if !updated_params.key?(:sort) ||
(updated_params[:metadata].to_i.zero? && updated_params[:sort]&.match?(/metadata_/))
updated_params[:sort] = 'updated_at desc'
update_store(search_key, updated_params)
end
updated_params
Expand Down
52 changes: 52 additions & 0 deletions app/models/sample/query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

# model to represent sample search form
class Sample::Query # rubocop:disable Style/ClassAndModuleChildren
include ActiveModel::Model
include ActiveModel::Attributes

attribute :column, :string
attribute :direction, :string
attribute :name_or_puid_cont, :string
attribute :name_or_puid_in, default: -> { [] }
attribute :project_ids, default: -> { [] }
attribute :sort, :string, default: 'updated_at desc'

validates :direction, inclusion: { in: %w[asc desc] }
validates :project_ids, length: { minimum: 1 }

def initialize(...)
super
self.sort = sort
end

def sort=(value)
super
column, direction = sort.split
assign_attributes(column:, direction:)
end

def results
return Sample.none unless valid?

sort_samples.ransack(ransack_params).result
end

private

def ransack_params
{
name_or_puid_cont: name_or_puid_cont,
name_or_puid_in: name_or_puid_in
}.compact
end

def sort_samples(scope = Sample.where(project_id: project_ids))
if column.starts_with? 'metadata_'
field = column.gsub('metadata_', '')
scope.order(Sample.metadata_sort(field, dir))
else
scope.order("#{column} #{direction}")
end
end
end
3 changes: 1 addition & 2 deletions app/views/groups/samples/_table.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
samples,
group,
pagy,
@q,
has_samples: @has_samples,
abilities: {
select_samples:
allowed_to?(:submit_workflow?, group) ||
allowed_to?(:export_data?, group),
},
metadata_fields: @fields,
search_params: @parsed_params,
search_params: @search_params,
empty: {
title: t(:"groups.samples.table.no_samples"),
description: t(:"groups.samples.table.no_associated_samples"),
Expand Down
Loading

0 comments on commit 3c8740a

Please sign in to comment.