Class: Plutonium::Resource::QueryObject
- Inherits:
-
Object
- Object
- Plutonium::Resource::QueryObject
- Defined in:
- lib/plutonium/resource/query_object.rb
Instance Attribute Summary collapse
-
#default_scope_name ⇒ Object
Returns the value of attribute default_scope_name.
-
#default_sort_config ⇒ Object
Returns the value of attribute default_sort_config.
-
#request_path ⇒ Object
readonly
The path every URL this object builds is anchored to — the collection page, which is not necessarily the current request (see Plutonium::Core::Controller#current_page_path).
-
#search_filter ⇒ Object
readonly
Returns the value of attribute search_filter.
-
#search_query ⇒ Object
readonly
Returns the value of attribute search_query.
Instance Method Summary collapse
-
#active_filter_descriptions ⇒ Object
Returns an array of hashes describing each currently active filter.
-
#all_scope_selected? ⇒ Boolean
Returns true if user explicitly selected "All" scope (no filtering).
-
#apply(scope, params, context: nil) ⇒ Object
Applies the defined filters and sorts to the given scope.
-
#build_url(**options) ⇒ String
Builds a URL with the given options for search and sorting.
-
#define_filter(name, body) ⇒ Object
Defines a filter with the given name and body.
-
#define_scope(name, body = nil, condition: nil) ⇒ Object
Defines a scope with the given name and body.
-
#define_search(body) ⇒ Object
Defines a search filter with the given body.
-
#define_sorter(name, body = nil, using: nil) ⇒ Object
Defines a sort with the given name and body.
- #filter_definitions ⇒ Object
-
#initialize(resource_class, params, request_path) {|_self| ... } ⇒ QueryObject
constructor
Initializes a QueryObject with the given resource_class and parameters.
- #scope_conditions ⇒ Object
- #scope_definitions ⇒ Object
-
#scope_visible?(name, view_context) ⇒ Boolean
Display-only visibility gate for a scope, mirroring condition: on actions.
-
#selected_scope ⇒ Object
Returns the currently selected scope (may be default if none explicitly selected).
- #sort_definitions ⇒ Object
-
#sort_params_for(name) ⇒ Hash?
Provides sorting parameters for the given field name.
-
#sorted_ascending_only_by?(name) ⇒ Boolean
Whether the collection is ordered by
nameASCENDING and by nothing else — the invariant a drag-reorder depends on.
Constructor Details
#initialize(resource_class, params, request_path) {|_self| ... } ⇒ QueryObject
Initializes a QueryObject with the given resource_class and parameters.
20 21 22 23 24 25 26 27 28 29 |
# File 'lib/plutonium/resource/query_object.rb', line 20 def initialize(resource_class, params, request_path, &) @resource_class = resource_class @params = params @request_path = request_path define_standard_queries yield self if block_given? extract_filter_params extract_sort_params end |
Instance Attribute Details
#default_scope_name ⇒ Object
Returns the value of attribute default_scope_name.
14 15 16 |
# File 'lib/plutonium/resource/query_object.rb', line 14 def default_scope_name @default_scope_name end |
#default_sort_config ⇒ Object
Returns the value of attribute default_sort_config.
14 15 16 |
# File 'lib/plutonium/resource/query_object.rb', line 14 def default_sort_config @default_sort_config end |
#request_path ⇒ Object (readonly)
The path every URL this object builds is anchored to — the collection
page, which is not necessarily the current request (see
Plutonium::Core::Controller#current_page_path). Exposed so the
components rendering alongside these URLs — the search form's action,
the filter pills' clear links — address the same page instead of each
re-deriving it from request.
12 13 14 |
# File 'lib/plutonium/resource/query_object.rb', line 12 def request_path @request_path end |
#search_filter ⇒ Object (readonly)
Returns the value of attribute search_filter.
4 5 6 |
# File 'lib/plutonium/resource/query_object.rb', line 4 def search_filter @search_filter end |
#search_query ⇒ Object (readonly)
Returns the value of attribute search_query.
4 5 6 |
# File 'lib/plutonium/resource/query_object.rb', line 4 def search_query @search_query end |
Instance Method Details
#active_filter_descriptions ⇒ Object
Returns an array of hashes describing each currently active filter. Each hash has: name, label, value_label, clear_url
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/plutonium/resource/query_object.rb', line 154 def active_filter_descriptions filter_definitions.filter_map do |name, filter| name = name.to_sym filter_params = params[name] next unless filter_params.present? value_label = case filter_params when Hash, ActionController::Parameters entries = filter_params.to_h.reject { |_, v| v.blank? } next if entries.empty? # Single-input filters defer to the filter's `humanize_value` # (e.g. Association resolves ids to labels, Boolean translates # "true" -> "Yes"). Multi-input filters keep input-name # qualifiers (e.g. "from 2024, to 2025"). if entries.size == 1 humanized = filter.humanize_value(entries.values.first) next if humanized.blank? humanized else entries.map { |k, v| "#{k.to_s.humanize.downcase} #{v}" }.join(", ") end when Array entries = filter_params.reject(&:blank?) next if entries.empty? humanized = filter.humanize_value(entries) next if humanized.blank? humanized else next if filter_params.to_s.blank? humanized = filter.humanize_value(filter_params) next if humanized.blank? humanized end { name: name, label: name.to_s.humanize, value_label: value_label, clear_url: build_url(name => nil) } end end |
#all_scope_selected? ⇒ Boolean
Returns true if user explicitly selected "All" scope (no filtering)
143 |
# File 'lib/plutonium/resource/query_object.rb', line 143 def all_scope_selected? = @all_scope_selected |
#apply(scope, params, context: nil) ⇒ Object
Applies the defined filters and sorts to the given scope.
120 121 122 123 124 125 126 127 128 |
# File 'lib/plutonium/resource/query_object.rb', line 120 def apply(scope, params, context: nil) params = deep_compact(params.with_indifferent_access) scope = search_filter.apply(scope, search: params[:search]) if search_filter && params[:search] # Use selected_scope which includes the default when no explicit selection effective_scope = @selected_scope_filter scope = scope_definitions[effective_scope].apply(scope, context:) if effective_scope && scope_definitions[effective_scope] scope = apply_sorts(scope, params) apply_filters(scope, params) end |
#build_url(**options) ⇒ String
Builds a URL with the given options for search and sorting.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/plutonium/resource/query_object.rb', line 79 def build_url(**) q = {} q[:search] = .key?(:search) ? [:search].presence : search_query q[:scope] = if .key?(:scope) [:scope].presence else selected_scope_filter end if .delete(:replace) q[:sort_directions] = {} q[:sort_fields] = [] else q[:sort_directions] = selected_sort_directions.dup q[:sort_fields] = selected_sort_fields.dup end (q, ) filter_keys = filter_definitions.keys.map(&:to_sym) filter_overrides = .slice(*filter_keys).stringify_keys q.merge! params.with_indifferent_access.slice(*filter_definitions.keys) q.merge!(filter_overrides) compacted = deep_compact({q: q}) # Preserve explicit "All" selection (scope: nil in options means show all) if .key?(:scope) && [:scope].nil? compacted[:q] ||= {} compacted[:q][:scope] = "" end query_params = compacted.to_param "#{@request_path}?#{query_params}" end |
#define_filter(name, body) ⇒ Object
Defines a filter with the given name and body.
35 36 37 |
# File 'lib/plutonium/resource/query_object.rb', line 35 def define_filter(name, body, &) filter_definitions[name] = build_query(body, &) end |
#define_scope(name, body = nil, condition: nil) ⇒ Object
Defines a scope with the given name and body.
44 45 46 47 48 |
# File 'lib/plutonium/resource/query_object.rb', line 44 def define_scope(name, body = nil, condition: nil, **) body ||= name scope_definitions[name] = build_query(body) scope_conditions[name] = condition if condition end |
#define_search(body) ⇒ Object
Defines a search filter with the given body.
68 69 70 71 72 |
# File 'lib/plutonium/resource/query_object.rb', line 68 def define_search(body) @search_filter = build_query(body) do |query| query.input :search end end |
#define_sorter(name, body = nil, using: nil) ⇒ Object
Defines a sort with the given name and body.
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/plutonium/resource/query_object.rb', line 54 def define_sorter(name, body = nil, using: nil) if body.nil? sort_field = using || determine_sort_field(name) body = ->(scope, direction:) { scope.order(sort_field => direction) } end sort_definitions[name] = build_query(body) do |query| query.input :direction end end |
#filter_definitions ⇒ Object
148 |
# File 'lib/plutonium/resource/query_object.rb', line 148 def filter_definitions = @filter_definitions ||= {}.with_indifferent_access |
#scope_conditions ⇒ Object
132 |
# File 'lib/plutonium/resource/query_object.rb', line 132 def scope_conditions = @scope_conditions ||= {}.with_indifferent_access |
#scope_definitions ⇒ Object
130 |
# File 'lib/plutonium/resource/query_object.rb', line 130 def scope_definitions = @scope_definitions ||= {}.with_indifferent_access |
#scope_visible?(name, view_context) ⇒ Boolean
Display-only visibility gate for a scope, mirroring condition: on actions. Returns true when no condition is set.
136 137 138 139 140 |
# File 'lib/plutonium/resource/query_object.rb', line 136 def scope_visible?(name, view_context) condition = scope_conditions[name] return true if condition.nil? Plutonium::Action::ConditionContext.new(view_context, nil).instance_exec(&condition) end |
#selected_scope ⇒ Object
Returns the currently selected scope (may be default if none explicitly selected)
146 |
# File 'lib/plutonium/resource/query_object.rb', line 146 def selected_scope = @selected_scope_filter |
#sort_definitions ⇒ Object
150 |
# File 'lib/plutonium/resource/query_object.rb', line 150 def sort_definitions = @sort_definitions ||= {}.with_indifferent_access |
#sort_params_for(name) ⇒ Hash?
Provides sorting parameters for the given field name.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/plutonium/resource/query_object.rb', line 201 def sort_params_for(name) return unless sort_definitions[name] multi = selected_sort_fields.size > 1 && selected_sort_fields.include?(name.to_s) { url: build_url(sort: name, replace: true), multi_url: build_url(sort: name), reset_url: build_url(sort: name, reset: true), position: selected_sort_fields.index(name.to_s), direction: selected_sort_directions[name], multi: multi } end |
#sorted_ascending_only_by?(name) ⇒ Boolean
Whether the collection is ordered by name ASCENDING and by nothing
else — the invariant a drag-reorder depends on. Dropping a row "between
the two rows either side of it" only means anything when the visual
order IS the stored order: under a title sort the neighbours say nothing
about position, and under a DESCENDING position sort they say the
opposite of what the write assumes.
An explicit selection wins; with none, the resource's default_sort is
what the user is looking at (field/direction form only — a default_sort
block is opaque, so it cannot be claimed to satisfy this).
229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/plutonium/resource/query_object.rb', line 229 def sorted_ascending_only_by?(name) fields = selected_sort_fields.map(&:to_s) if fields.any? return false unless fields == [name.to_s] direction = selected_sort_directions[name.to_sym] || selected_sort_directions[name.to_s] || "ASC" return direction.to_s.casecmp("asc").zero? end return false unless default_sort_config.is_a?(Array) field, direction = default_sort_config field.to_s == name.to_s && (direction || :asc).to_s.casecmp("asc").zero? end |