warped 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +25 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +25 -19
- data/README.md +116 -270
- data/app/assets/config/warped_manifest.js +2 -0
- data/app/assets/javascript/warped/controllers/filter_controller.js +76 -0
- data/app/assets/javascript/warped/controllers/filters_controller.js +21 -0
- data/app/assets/javascript/warped/index.js +2 -0
- data/app/assets/stylesheets/warped/application.css +15 -0
- data/app/assets/stylesheets/warped/base.css +23 -0
- data/app/assets/stylesheets/warped/filters.css +115 -0
- data/app/assets/stylesheets/warped/pagination.css +74 -0
- data/app/assets/stylesheets/warped/search.css +33 -0
- data/app/assets/stylesheets/warped/table.css +114 -0
- data/app/views/warped/_actions.html.erb +9 -0
- data/app/views/warped/_cell.html.erb +3 -0
- data/app/views/warped/_column.html.erb +35 -0
- data/app/views/warped/_filters.html.erb +21 -0
- data/app/views/warped/_hidden_fields.html.erb +19 -0
- data/app/views/warped/_pagination.html.erb +34 -0
- data/app/views/warped/_row.html.erb +19 -0
- data/app/views/warped/_search.html.erb +21 -0
- data/app/views/warped/_table.html.erb +52 -0
- data/app/views/warped/filters/_filter.html.erb +40 -0
- data/config/importmap.rb +3 -0
- data/docs/controllers/FILTERABLE.md +193 -0
- data/docs/controllers/PAGEABLE.md +70 -0
- data/docs/controllers/README.md +8 -0
- data/docs/controllers/SEARCHABLE.md +95 -0
- data/docs/controllers/SORTABLE.md +94 -0
- data/docs/controllers/TABULATABLE.md +28 -0
- data/docs/controllers/views/PARTIALS.md +285 -0
- data/docs/jobs/README.md +22 -0
- data/docs/services/README.md +81 -0
- data/lib/generators/warped/install_generator.rb +1 -1
- data/lib/warped/api/filter/base/value.rb +52 -0
- data/lib/warped/api/filter/base.rb +84 -0
- data/lib/warped/api/filter/boolean.rb +41 -0
- data/lib/warped/api/filter/date.rb +26 -0
- data/lib/warped/api/filter/date_time.rb +32 -0
- data/lib/warped/api/filter/decimal.rb +31 -0
- data/lib/warped/api/filter/factory.rb +38 -0
- data/lib/warped/api/filter/integer.rb +38 -0
- data/lib/warped/api/filter/string.rb +25 -0
- data/lib/warped/api/filter/time.rb +25 -0
- data/lib/warped/api/filter.rb +14 -0
- data/lib/warped/api/sort/value.rb +40 -0
- data/lib/warped/api/sort.rb +65 -0
- data/lib/warped/controllers/filterable/ui.rb +46 -0
- data/lib/warped/controllers/filterable.rb +79 -42
- data/lib/warped/controllers/pageable/ui.rb +70 -0
- data/lib/warped/controllers/pageable.rb +11 -11
- data/lib/warped/controllers/searchable/ui.rb +37 -0
- data/lib/warped/controllers/searchable.rb +2 -0
- data/lib/warped/controllers/sortable/ui.rb +53 -0
- data/lib/warped/controllers/sortable.rb +53 -33
- data/lib/warped/controllers/tabulatable/ui.rb +54 -0
- data/lib/warped/controllers/tabulatable.rb +13 -27
- data/lib/warped/emails/components/align.rb +21 -0
- data/lib/warped/emails/components/base.rb +116 -0
- data/lib/warped/emails/components/button.rb +58 -0
- data/lib/warped/emails/components/divider.rb +15 -0
- data/lib/warped/emails/components/heading.rb +65 -0
- data/lib/warped/emails/components/layouts/columns.rb +36 -0
- data/lib/warped/emails/components/layouts/cta.rb +38 -0
- data/lib/warped/emails/components/layouts/main.rb +34 -0
- data/lib/warped/emails/components/link.rb +36 -0
- data/lib/warped/emails/components/spacer.rb +15 -0
- data/lib/warped/emails/components/stepper.rb +104 -0
- data/lib/warped/emails/components/table.rb +37 -0
- data/lib/warped/emails/components/text.rb +67 -0
- data/lib/warped/emails/helpers.rb +26 -0
- data/lib/warped/emails/slottable.rb +61 -0
- data/lib/warped/emails/styleable.rb +160 -0
- data/lib/warped/engine.rb +19 -0
- data/lib/warped/queries/filter.rb +32 -12
- data/lib/warped/table/action.rb +33 -0
- data/lib/warped/table/column.rb +34 -0
- data/lib/warped/version.rb +1 -1
- data/lib/warped.rb +2 -0
- data/warped.gemspec +1 -1
- metadata +73 -7
- data/lib/warped/emails/.keep +0 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warped
|
4
|
+
module Filter
|
5
|
+
class Factory
|
6
|
+
TYPES = %i[string integer float decimal date time date_time boolean].freeze
|
7
|
+
|
8
|
+
def self.build(kind, *args, **kwargs)
|
9
|
+
new(kind).build(*args, **kwargs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(kind = nil)
|
13
|
+
@kind = kind
|
14
|
+
validate_kind!
|
15
|
+
end
|
16
|
+
|
17
|
+
def build(*args, **kwargs)
|
18
|
+
filter_class.new(*args, **kwargs)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :kind
|
24
|
+
|
25
|
+
def validate_kind!
|
26
|
+
return if kind.nil?
|
27
|
+
|
28
|
+
raise ArgumentError, "#{kind} is not a valid filter type" unless TYPES.include?(kind)
|
29
|
+
end
|
30
|
+
|
31
|
+
def filter_class
|
32
|
+
return Base if kind.nil?
|
33
|
+
|
34
|
+
Filter.const_get(kind.to_s.camelize)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Filter
|
7
|
+
class Integer < Base
|
8
|
+
RELATIONS = %w[eq neq gt gte lt lte between in not_in is_null is_not_null].freeze
|
9
|
+
|
10
|
+
def cast(value)
|
11
|
+
return if value.blank?
|
12
|
+
|
13
|
+
casted_value = case value
|
14
|
+
when ::Integer
|
15
|
+
value
|
16
|
+
when ::String
|
17
|
+
value.to_i if value.match?(/\A-?\d+\z/)
|
18
|
+
when ::Float, ::BigDecimal
|
19
|
+
value.to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
check_casted_value!(casted_value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def html_type
|
26
|
+
"number"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def check_casted_value!(value)
|
32
|
+
raise ValueError, "#{value} cannot be casted to #{kind}" if value.nil? && strict
|
33
|
+
|
34
|
+
value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Filter
|
7
|
+
class String < Base
|
8
|
+
RELATIONS = Queries::Filter::RELATIONS
|
9
|
+
|
10
|
+
def cast(value)
|
11
|
+
return if value.blank?
|
12
|
+
|
13
|
+
value.to_s
|
14
|
+
rescue StandardError
|
15
|
+
raise ValueError, "#{value} cannot be casted to #{kind}" if strict
|
16
|
+
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def html_type
|
21
|
+
"text"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Filter
|
7
|
+
class Time < Base
|
8
|
+
RELATIONS = %w[eq neq gt gte lt lte between is_null is_not_null].freeze
|
9
|
+
|
10
|
+
def cast(value)
|
11
|
+
return if value.blank?
|
12
|
+
|
13
|
+
::Time.parse(value)
|
14
|
+
rescue StandardError
|
15
|
+
raise ValueError, "#{value} cannot be casted to #{kind}" if strict
|
16
|
+
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def html_type
|
21
|
+
"time"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Filter
|
7
|
+
class ValueError < StandardError; end
|
8
|
+
class RelationError < StandardError; end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
delegate :build, to: Factory
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
class Sort
|
7
|
+
class Value
|
8
|
+
attr_reader :sort
|
9
|
+
|
10
|
+
delegate :name, :alias_name, :parameter_name, to: :sort
|
11
|
+
|
12
|
+
# @param sort [Warped::Sort] The sort object
|
13
|
+
# @param direction [String] The sort direction.
|
14
|
+
def initialize(sort, direction)
|
15
|
+
@sort = sort
|
16
|
+
@direction = direction
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] The sort direction.
|
20
|
+
def direction
|
21
|
+
sort.direction!(@direction)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [String] The opposite sort direction.
|
25
|
+
def opposite_direction
|
26
|
+
sort.opposite_direction(direction)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] Whether the sort is ascending.
|
30
|
+
def asc?
|
31
|
+
%w[asc asc_nulls_first asc_nulls_last].include?(direction)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean] Whether the sort is descending.
|
35
|
+
def desc?
|
36
|
+
%w[desc desc_nulls_first desc_nulls_last].include?(direction)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
class Sort
|
7
|
+
class DirectionError < StandardError; end
|
8
|
+
|
9
|
+
SORT_DIRECTIONS = %w[asc desc].freeze
|
10
|
+
NULLS_SORT_DIRECTION = %w[asc_nulls_first asc_nulls_last desc_nulls_first desc_nulls_last].freeze
|
11
|
+
|
12
|
+
attr_accessor :name, :alias_name
|
13
|
+
|
14
|
+
# @return [Array<String>] The valid sort directions.
|
15
|
+
def self.directions
|
16
|
+
@directions ||= SORT_DIRECTIONS + NULLS_SORT_DIRECTION
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param name [String] The name of the sort.
|
20
|
+
# @param alias_name [String] The alias name of the sort, used for renaming the sort key in the URL params
|
21
|
+
def initialize(name, alias_name: nil)
|
22
|
+
raise ArgumentError, "name cannot be nil" if name.nil?
|
23
|
+
|
24
|
+
@name = name.to_s
|
25
|
+
@alias_name = alias_name&.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String] The name to use in the URL params.
|
29
|
+
def parameter_name
|
30
|
+
alias_name.presence || name
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param direction [String] The sort direction.
|
34
|
+
# @return [String] The sort direction.
|
35
|
+
# @raise [DirectionError] If the direction is invalid.
|
36
|
+
def direction!(direction)
|
37
|
+
raise DirectionError, "Invalid direction: #{direction}" unless valid_direction?(direction.to_s)
|
38
|
+
|
39
|
+
direction.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param direction [String] The sort direction.
|
43
|
+
# @return [String] The opposite sort direction.
|
44
|
+
def opposite_direction(direction)
|
45
|
+
opposite_directions[direction]
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def valid_direction?(relation)
|
51
|
+
self.class.directions.include?(relation.to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
def opposite_directions
|
55
|
+
@opposite_directions ||= {
|
56
|
+
"asc" => "desc",
|
57
|
+
"desc" => "asc",
|
58
|
+
"asc_nulls_first" => "desc_nulls_last",
|
59
|
+
"asc_nulls_last" => "desc_nulls_first",
|
60
|
+
"desc_nulls_first" => "asc_nulls_last",
|
61
|
+
"desc_nulls_last" => "asc_nulls_first"
|
62
|
+
}.freeze
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Controllers
|
7
|
+
module Filterable
|
8
|
+
module Ui
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
include Filterable
|
11
|
+
|
12
|
+
included do
|
13
|
+
helper_method :filters, :filtered?, :filter_url_params, :filterable_by
|
14
|
+
end
|
15
|
+
|
16
|
+
# @see Filterable#filter
|
17
|
+
def filter(...)
|
18
|
+
@filtered = true
|
19
|
+
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean] Whether the current action is filtered.
|
24
|
+
def filtered?
|
25
|
+
@filtered ||= false
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Hash] The filters for the current action.
|
29
|
+
def filter_url_params(**options)
|
30
|
+
url_params = {}
|
31
|
+
current_action_filter_values.each_with_object(url_params) do |filter_value, hsh|
|
32
|
+
if filter_value.value.is_a?(Array)
|
33
|
+
filter_value.value.each { |value| hsh["#{filter_value.parameter_name}[]"] = value }
|
34
|
+
else
|
35
|
+
hsh[filter_value.parameter_name] = filter_value.value
|
36
|
+
end
|
37
|
+
|
38
|
+
hsh["#{filter_value.parameter_name}.rel"] = filter_value.relation
|
39
|
+
end
|
40
|
+
|
41
|
+
url_params.merge!(options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "active_support/concern"
|
4
4
|
require "active_support/core_ext/class/attribute"
|
5
5
|
require "active_support/core_ext/enumerable"
|
6
|
+
require "active_support/core_ext/hash/indifferent_access"
|
7
|
+
require "active_support/core_ext/object/blank"
|
6
8
|
|
7
9
|
module Warped
|
8
10
|
module Controllers
|
@@ -25,7 +27,7 @@ module Warped
|
|
25
27
|
# GET /users?name=John
|
26
28
|
# GET /users?created_at=2020-01-01
|
27
29
|
# GET /users?accounts.kind=premium
|
28
|
-
# GET /users?accounts.kind=premium&accounts.kind.rel=
|
30
|
+
# GET /users?accounts.kind=premium&accounts.kind.rel=neq
|
29
31
|
#
|
30
32
|
# Filters can be combined:
|
31
33
|
# GET /users?name=John&created_at=2020-01-01
|
@@ -40,7 +42,7 @@ module Warped
|
|
40
42
|
# class UsersController < ApplicationController
|
41
43
|
# include Filterable
|
42
44
|
#
|
43
|
-
# filterable_by :name, :created_at, 'accounts.kind' => 'kind'
|
45
|
+
# filterable_by :name, :created_at, 'accounts.kind' => { alias_name: 'kind' }
|
44
46
|
#
|
45
47
|
# def index
|
46
48
|
# scope = filter(User.joins(:account))
|
@@ -64,9 +66,9 @@ module Warped
|
|
64
66
|
# To use the operands, you must pass a parameter appended with `.rel`, and the value of a valid operand.
|
65
67
|
#
|
66
68
|
# Example requests:
|
67
|
-
# GET /users?created_at=2020-01-01&created_at.rel
|
68
|
-
# GET /users?created_at=2020-01-01&created_at.rel
|
69
|
-
# GET /users?created_at=2020-01-01&created_at.rel=
|
69
|
+
# GET /users?created_at=2020-01-01&created_at.rel=gt
|
70
|
+
# GET /users?created_at=2020-01-01&created_at.rel=lt
|
71
|
+
# GET /users?created_at=2020-01-01&created_at.rel=neq
|
70
72
|
#
|
71
73
|
# When the operand relation requires multiple values, like +in+, +not_in+, or +between+,
|
72
74
|
# you can pass an array of values.
|
@@ -74,67 +76,102 @@ module Warped
|
|
74
76
|
# Example requests:
|
75
77
|
# GET /users?created_at[]=2020-01-01&created_at[]=2020-01-03&created_at.rel=in
|
76
78
|
# GET /users?created_at[]=2020-01-01&created_at[]=2020-01-03&created_at.rel=between
|
79
|
+
#
|
80
|
+
# Setting types and casting:
|
81
|
+
# By default, the filter values are cast to strings. If you want to cast the values to a specific type,
|
82
|
+
# and validate that the values are of the correct type, you can pass the kind of the filter to
|
83
|
+
# the +filterable_by+ method.
|
84
|
+
#
|
85
|
+
# Example:
|
86
|
+
#
|
87
|
+
# class UsersController < ApplicationController
|
88
|
+
# include Filterable
|
89
|
+
#
|
90
|
+
# filterable_by :name, :created_at, 'accounts.active' => { kind: :integer, alias_name: 'active' }
|
91
|
+
#
|
92
|
+
# def index
|
93
|
+
# scope = filter(User.joins(:account))
|
94
|
+
# render json: scope
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# Example requests:
|
99
|
+
# GET /users?active=1
|
100
|
+
#
|
101
|
+
# The +kind+ parameter will be cast to an integer.
|
102
|
+
# If the value is not an integer, an error will be raised, and the response will be a 400 Bad Request.
|
103
|
+
#
|
104
|
+
# In order to change the error message, you can rescue from the +Filter::ValueError+ exception, or
|
105
|
+
# override the +render_invalid_filter_value+ method.
|
106
|
+
#
|
107
|
+
# def render_invalid_filter_value(exception)
|
108
|
+
# render action_name, status: :bad_request
|
109
|
+
# end
|
110
|
+
#
|
77
111
|
module Filterable
|
78
112
|
extend ActiveSupport::Concern
|
79
113
|
|
80
114
|
included do
|
81
|
-
class_attribute :
|
82
|
-
class_attribute :
|
115
|
+
class_attribute :filters, default: []
|
116
|
+
class_attribute :strict_filtering, default: false
|
117
|
+
|
118
|
+
helper_method :current_action_filters, :current_action_filter_values
|
83
119
|
end
|
84
120
|
|
85
121
|
class_methods do
|
86
122
|
# @param keys [Array<Symbol,String,Hash>]
|
87
123
|
# @param mapped_keys [Hash<Symbol,String>]
|
88
|
-
def filterable_by(*keys, **mapped_keys)
|
89
|
-
self.
|
90
|
-
|
124
|
+
def filterable_by(*keys, strict: nil, **mapped_keys)
|
125
|
+
self.strict_filtering = strict unless strict.nil?
|
126
|
+
|
127
|
+
self.filters = keys.map do |field|
|
128
|
+
Warped::Filter.build(nil, field, strict:)
|
129
|
+
end
|
130
|
+
|
131
|
+
complex_filters = mapped_keys.with_indifferent_access
|
132
|
+
|
133
|
+
self.filters += complex_filters.map do |field_name, opts|
|
134
|
+
kind = opts[:kind]
|
135
|
+
alias_name = opts[:alias_name]
|
136
|
+
|
137
|
+
Warped::Filter.build(kind, field_name, alias_name:, strict:)
|
138
|
+
end
|
91
139
|
end
|
92
140
|
end
|
93
141
|
|
94
142
|
# @param scope [ActiveRecord::Relation]
|
95
|
-
# @param filter_conditions [Array<
|
96
|
-
# @option filter_conditions [Symbol,String] :field
|
97
|
-
# @option filter_conditions [String,Integer,Array<String,Integer>] :value
|
98
|
-
# @option filter_conditions [String] :relation
|
143
|
+
# @param filter_conditions [Array<Warped::Filter::Base>|nil]
|
99
144
|
# @return [ActiveRecord::Relation]
|
100
|
-
def filter(scope, filter_conditions:
|
101
|
-
|
145
|
+
def filter(scope, filter_conditions: nil)
|
146
|
+
action_filters = filter_conditions.presence || filters
|
147
|
+
@current_action_filters = action_filters
|
148
|
+
@current_action_filter_values = parse_filter_params
|
149
|
+
|
150
|
+
Warped::Queries::Filter.call(scope, filter_conditions: current_action_filter_values.map(&:to_h))
|
102
151
|
end
|
103
152
|
|
104
|
-
# @param fields [Array<Symbol,String>]
|
105
153
|
# @return [Array<Hash>]
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
next if filter_value(filter_opt).blank? && %w[is_null is_not_null].exclude?(filter_rel_value(filter_opt))
|
111
|
-
|
112
|
-
{
|
113
|
-
field:,
|
114
|
-
value: filter_value(filter_opt),
|
115
|
-
relation: filter_rel_value(filter_opt).presence || (filter_value(filter_opt).is_a?(Array) ? "in" : "=")
|
116
|
-
}
|
117
|
-
end
|
118
|
-
end
|
154
|
+
def parse_filter_params
|
155
|
+
current_action_filters.filter_map do |filter|
|
156
|
+
raw_value = params[filter.parameter_name]
|
157
|
+
raw_relation = params["#{filter.parameter_name}.rel"]
|
119
158
|
|
120
|
-
|
159
|
+
filter_value = filter.class::Value.new(filter, raw_relation.presence || "eq", raw_value.presence)
|
121
160
|
|
122
|
-
|
123
|
-
filter.is_a?(Array) ? filter.first : filter
|
124
|
-
end
|
161
|
+
next if filter_value.empty?
|
125
162
|
|
126
|
-
|
127
|
-
|
163
|
+
filter_value
|
164
|
+
end
|
128
165
|
end
|
129
166
|
|
130
|
-
|
131
|
-
|
132
|
-
|
167
|
+
# @return [Array<Warped::Filter::Base>]
|
168
|
+
def current_action_filters
|
169
|
+
@current_action_filters ||= []
|
133
170
|
end
|
134
171
|
|
135
|
-
|
136
|
-
|
137
|
-
|
172
|
+
# @return [Array<Warped::Filter::Base::Value>]
|
173
|
+
def current_action_filter_values
|
174
|
+
@current_action_filter_values ||= []
|
138
175
|
end
|
139
176
|
end
|
140
177
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Controllers
|
7
|
+
module Pageable
|
8
|
+
module Ui
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
include Pageable
|
12
|
+
|
13
|
+
included do
|
14
|
+
helper_method :pagination, :paginated?, :paginate_url_params
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Hash] The paginate_url_params
|
18
|
+
def paginate_url_params(**options)
|
19
|
+
url_params = { page:, per_page: }
|
20
|
+
url_params.merge!(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see Pageable#pagination
|
24
|
+
# @return [Hash]
|
25
|
+
def pagination
|
26
|
+
super.tap do |hsh|
|
27
|
+
hsh[:series] = series(hsh[:page], hsh[:total_pages])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @see Pageable#paginate
|
32
|
+
def paginate(...)
|
33
|
+
@paginated = true
|
34
|
+
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Boolean] Whether the current action is paginated.
|
39
|
+
def paginated?
|
40
|
+
@paginated ||= false
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @param page [Integer]
|
46
|
+
# @param total_pages [Integer]
|
47
|
+
# @return [Array]
|
48
|
+
# the series method returns an array of page numbers and :gap symbols
|
49
|
+
# the current page is a string, the others are integers
|
50
|
+
# series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
51
|
+
def series(page, total_pages)
|
52
|
+
current_page = [page, total_pages].min
|
53
|
+
return [] if total_pages.zero?
|
54
|
+
return ["1"] if total_pages == 1
|
55
|
+
|
56
|
+
if total_pages <= 9
|
57
|
+
(1..(current_page - 1)).to_a + [current_page.to_s] + ((current_page + 1)..total_pages).to_a
|
58
|
+
elsif current_page <= 5
|
59
|
+
[*(1..6).to_a.map { |i| i == page ? i.to_s : i }, :gap, total_pages]
|
60
|
+
elsif current_page >= total_pages - 4
|
61
|
+
[1, :gap, *(total_pages - 5..total_pages).to_a.map { |i| i == current_page ? i.to_s : i }]
|
62
|
+
else
|
63
|
+
[1, :gap, current_page - 2, current_page - 1, current_page.to_s, current_page + 1, current_page + 2, :gap,
|
64
|
+
total_pages]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -14,7 +14,7 @@ module Warped
|
|
14
14
|
#
|
15
15
|
# def index
|
16
16
|
# scope = paginate(User.all)
|
17
|
-
# render json: scope, root: :users, meta:
|
17
|
+
# render json: scope, root: :users, meta: pagination
|
18
18
|
# end
|
19
19
|
# end
|
20
20
|
#
|
@@ -33,7 +33,7 @@ module Warped
|
|
33
33
|
#
|
34
34
|
# def index
|
35
35
|
# scope = paginate(User.all)
|
36
|
-
# render json: scope, root: :users, meta:
|
36
|
+
# render json: scope, root: :users, meta: pagination
|
37
37
|
# end
|
38
38
|
# end
|
39
39
|
#
|
@@ -44,7 +44,7 @@ module Warped
|
|
44
44
|
#
|
45
45
|
# def index
|
46
46
|
# scope = paginate(User.all)
|
47
|
-
# render json: scope, root: :users, meta:
|
47
|
+
# render json: scope, root: :users, meta: pagination
|
48
48
|
# end
|
49
49
|
#
|
50
50
|
# private
|
@@ -62,17 +62,17 @@ module Warped
|
|
62
62
|
#
|
63
63
|
# def index
|
64
64
|
# scope = paginate(User.all, per_page: 50)
|
65
|
-
# render json: scope, root: :users, meta:
|
65
|
+
# render json: scope, root: :users, meta: pagination
|
66
66
|
# end
|
67
67
|
#
|
68
68
|
# def other_index
|
69
69
|
# # The default per_page value is used.
|
70
70
|
# scope = paginate(User.all)
|
71
|
-
# render json: scope, root: :users, meta:
|
71
|
+
# render json: scope, root: :users, meta: pagination
|
72
72
|
# end
|
73
73
|
# end
|
74
74
|
#
|
75
|
-
# The pagination metadata can be accessed by calling the +
|
75
|
+
# The pagination metadata can be accessed by calling the +pagination+ method.
|
76
76
|
# It includes the following keys:
|
77
77
|
# - +total_count+: The total number of records in the collection.
|
78
78
|
# - +total_pages+: The total number of pages.
|
@@ -80,7 +80,7 @@ module Warped
|
|
80
80
|
# - +prev_page+: The previous page number.
|
81
81
|
# - +page+: The current page number.
|
82
82
|
# - +per_page+: The number of records per page.
|
83
|
-
# *Warning*: The +
|
83
|
+
# *Warning*: The +pagination+ method will raise an +ArgumentError+ if the method +paginate+ was not
|
84
84
|
# called within the action.
|
85
85
|
module Pageable
|
86
86
|
extend ActiveSupport::Concern
|
@@ -96,7 +96,7 @@ module Warped
|
|
96
96
|
# @param per_page [String,Integer,nil] The number of records per page.
|
97
97
|
# @return [ActiveRecord::Relation] The paginated scope.
|
98
98
|
def paginate(scope, page: self.page, per_page: self.per_page)
|
99
|
-
@
|
99
|
+
@pagination, paginated_scope = Queries::Paginate.call(scope, page:, per_page:)
|
100
100
|
paginated_scope
|
101
101
|
end
|
102
102
|
|
@@ -112,7 +112,7 @@ module Warped
|
|
112
112
|
#
|
113
113
|
# @return [String,Integer] The number of records per page.
|
114
114
|
def per_page
|
115
|
-
params[:per_page].presence ||
|
115
|
+
params[:per_page].presence || default_per_page
|
116
116
|
end
|
117
117
|
|
118
118
|
# Retrieves pagination metadata.
|
@@ -120,8 +120,8 @@ module Warped
|
|
120
120
|
# @return [Hash] Metadata about the pagination.
|
121
121
|
# @raise [ArgumentError] If pagination was not performed.
|
122
122
|
# @see Warped::Queries::Paginate#metadata
|
123
|
-
def
|
124
|
-
return @
|
123
|
+
def pagination
|
124
|
+
return @pagination if @pagination.present?
|
125
125
|
|
126
126
|
raise ActionController::BadRequest, "Pagination was not performed"
|
127
127
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Controllers
|
7
|
+
module Searchable
|
8
|
+
module Ui
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
include Searchable
|
12
|
+
|
13
|
+
included do
|
14
|
+
helper_method :searched?, :search_url_params
|
15
|
+
end
|
16
|
+
|
17
|
+
# @see Searchable#search
|
18
|
+
def search(...)
|
19
|
+
@searched = true
|
20
|
+
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] Whether the current action is searched.
|
25
|
+
def searched?
|
26
|
+
@searched ||= false
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Hash] The search_url_params
|
30
|
+
def search_url_params(**options)
|
31
|
+
url_params = { search_param => search_term }
|
32
|
+
url_params.merge!(options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|