warped 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/Gemfile.lock +2 -2
- data/README.md +104 -0
- 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 +82 -3
- data/docs/controllers/views/PARTIALS.md +285 -0
- 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 +10 -32
- data/lib/warped/controllers/filterable.rb +75 -42
- data/lib/warped/controllers/pageable/ui.rb +13 -3
- data/lib/warped/controllers/pageable.rb +1 -1
- data/lib/warped/controllers/searchable/ui.rb +3 -1
- data/lib/warped/controllers/sortable/ui.rb +21 -26
- data/lib/warped/controllers/sortable.rb +53 -33
- data/lib/warped/controllers/tabulatable/ui.rb +4 -0
- data/lib/warped/controllers/tabulatable.rb +6 -9
- data/lib/warped/engine.rb +19 -0
- data/lib/warped/queries/filter.rb +3 -3
- 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 +1 -0
- data/warped.gemspec +1 -1
- metadata +44 -6
@@ -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
|
@@ -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))
|
@@ -74,71 +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
|
-
self.mapped_filter_fields = mapped_keys.to_a
|
91
|
-
end
|
92
|
-
end
|
124
|
+
def filterable_by(*keys, strict: nil, **mapped_keys)
|
125
|
+
self.strict_filtering = strict unless strict.nil?
|
93
126
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
# @option filter_conditions [String,Integer,Array<String,Integer>] :value
|
98
|
-
# @option filter_conditions [String] :relation
|
99
|
-
# @return [ActiveRecord::Relation]
|
100
|
-
def filter(scope, filter_conditions: filter_conditions(*filter_fields, *mapped_filter_fields))
|
101
|
-
Warped::Queries::Filter.call(scope, filter_conditions:)
|
102
|
-
end
|
127
|
+
self.filters = keys.map do |field|
|
128
|
+
Warped::Filter.build(nil, field, strict:)
|
129
|
+
end
|
103
130
|
|
104
|
-
|
105
|
-
# @return [Array<Hash>]
|
106
|
-
def filter_conditions(*fields)
|
107
|
-
fields.filter_map do |filter_opt|
|
108
|
-
field = filter_name(filter_opt)
|
131
|
+
complex_filters = mapped_keys.with_indifferent_access
|
109
132
|
|
110
|
-
|
133
|
+
self.filters += complex_filters.map do |field_name, opts|
|
134
|
+
kind = opts[:kind]
|
135
|
+
alias_name = opts[:alias_name]
|
111
136
|
|
112
|
-
|
113
|
-
|
114
|
-
value: filter_value(filter_opt),
|
115
|
-
relation: filter_rel_value(filter_opt).presence || (filter_value(filter_opt).is_a?(Array) ? "in" : "eq")
|
116
|
-
}
|
137
|
+
Warped::Filter.build(kind, field_name, alias_name:, strict:)
|
138
|
+
end
|
117
139
|
end
|
118
140
|
end
|
119
141
|
|
120
|
-
|
121
|
-
|
142
|
+
# @param scope [ActiveRecord::Relation]
|
143
|
+
# @param filter_conditions [Array<Warped::Filter::Base>|nil]
|
144
|
+
# @return [ActiveRecord::Relation]
|
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))
|
122
151
|
end
|
123
152
|
|
124
|
-
|
153
|
+
# @return [Array<Hash>]
|
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"]
|
125
158
|
|
126
|
-
|
127
|
-
filter.is_a?(Array) ? filter.first : filter
|
128
|
-
end
|
159
|
+
filter_value = filter.class::Value.new(filter, raw_relation.presence || "eq", raw_value.presence)
|
129
160
|
|
130
|
-
|
131
|
-
|
161
|
+
next if filter_value.empty?
|
162
|
+
|
163
|
+
filter_value
|
164
|
+
end
|
132
165
|
end
|
133
166
|
|
134
|
-
|
135
|
-
|
136
|
-
|
167
|
+
# @return [Array<Warped::Filter::Base>]
|
168
|
+
def current_action_filters
|
169
|
+
@current_action_filters ||= []
|
137
170
|
end
|
138
171
|
|
139
|
-
|
140
|
-
|
141
|
-
|
172
|
+
# @return [Array<Warped::Filter::Base::Value>]
|
173
|
+
def current_action_filter_values
|
174
|
+
@current_action_filter_values ||= []
|
142
175
|
end
|
143
176
|
end
|
144
177
|
end
|
@@ -14,24 +14,28 @@ module Warped
|
|
14
14
|
helper_method :pagination, :paginated?, :paginate_url_params
|
15
15
|
end
|
16
16
|
|
17
|
+
# @return [Hash] The paginate_url_params
|
17
18
|
def paginate_url_params(**options)
|
18
19
|
url_params = { page:, per_page: }
|
19
20
|
url_params.merge!(options)
|
20
|
-
url_params
|
21
21
|
end
|
22
22
|
|
23
|
+
# @see Pageable#pagination
|
24
|
+
# @return [Hash]
|
23
25
|
def pagination
|
24
26
|
super.tap do |hsh|
|
25
27
|
hsh[:series] = series(hsh[:page], hsh[:total_pages])
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
31
|
+
# @see Pageable#paginate
|
29
32
|
def paginate(...)
|
30
33
|
@paginated = true
|
31
34
|
|
32
35
|
super
|
33
36
|
end
|
34
37
|
|
38
|
+
# @return [Boolean] Whether the current action is paginated.
|
35
39
|
def paginated?
|
36
40
|
@paginated ||= false
|
37
41
|
end
|
@@ -45,13 +49,19 @@ module Warped
|
|
45
49
|
# the current page is a string, the others are integers
|
46
50
|
# series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
47
51
|
def series(page, total_pages)
|
52
|
+
current_page = [page, total_pages].min
|
48
53
|
return [] if total_pages.zero?
|
49
54
|
return ["1"] if total_pages == 1
|
50
55
|
|
51
56
|
if total_pages <= 9
|
52
|
-
(1..(
|
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 }]
|
53
62
|
else
|
54
|
-
[1, :gap,
|
63
|
+
[1, :gap, current_page - 2, current_page - 1, current_page.to_s, current_page + 1, current_page + 2, :gap,
|
64
|
+
total_pages]
|
55
65
|
end
|
56
66
|
end
|
57
67
|
end
|
@@ -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.
|
@@ -14,20 +14,22 @@ module Warped
|
|
14
14
|
helper_method :searched?, :search_url_params
|
15
15
|
end
|
16
16
|
|
17
|
+
# @see Searchable#search
|
17
18
|
def search(...)
|
18
19
|
@searched = true
|
19
20
|
|
20
21
|
super
|
21
22
|
end
|
22
23
|
|
24
|
+
# @return [Boolean] Whether the current action is searched.
|
23
25
|
def searched?
|
24
26
|
@searched ||= false
|
25
27
|
end
|
26
28
|
|
29
|
+
# @return [Hash] The search_url_params
|
27
30
|
def search_url_params(**options)
|
28
31
|
url_params = { search_param => search_term }
|
29
32
|
url_params.merge!(options)
|
30
|
-
url_params
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/concern"
|
4
|
+
require "active_support/core_ext/object/blank"
|
4
5
|
|
5
6
|
module Warped
|
6
7
|
module Controllers
|
@@ -11,46 +12,40 @@ module Warped
|
|
11
12
|
include Sortable
|
12
13
|
|
13
14
|
included do
|
14
|
-
helper_method :sorted?, :
|
15
|
+
helper_method :attribute_name, :sorted?, :sorted_field?, :sortable_field?, :sort_url_params
|
15
16
|
end
|
16
17
|
|
18
|
+
# @see Sortable#sort
|
17
19
|
def sort(...)
|
18
20
|
@sorted = true
|
19
21
|
|
20
22
|
super
|
21
23
|
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
# @param parameter_name [String]
|
26
|
+
# @return [Boolean]
|
27
|
+
def sorted_field?(parameter_name)
|
28
|
+
current_action_sort_value.parameter_name == parameter_name.to_s
|
25
29
|
end
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
# @param parameter_name [String]
|
32
|
+
# @return [Boolean] Whether the parameter_name is sortable.
|
33
|
+
def sortable_field?(parameter_name)
|
34
|
+
current_action_sorts.any? { |sort| sort.parameter_name == parameter_name.to_s }
|
31
35
|
end
|
32
36
|
|
33
|
-
|
34
|
-
|
37
|
+
# @return [Boolean] Whether the current action is sorted.
|
38
|
+
def sorted?
|
39
|
+
@sorted ||= false
|
35
40
|
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
param_sort_key = mapped_sort_field_param.presence || sort_field_param.presence
|
45
|
-
|
46
|
-
return [] unless param_sort_key.present?
|
47
|
-
|
48
|
-
[
|
49
|
-
{
|
50
|
-
key: param_sort_key,
|
51
|
-
value: sort_direction
|
52
|
-
}
|
53
|
-
]
|
42
|
+
# @return [Hash] The sort_url_params
|
43
|
+
def sort_url_params(**options)
|
44
|
+
url_params = {
|
45
|
+
sort_key: current_action_sort_value.parameter_name,
|
46
|
+
sort_direction: current_action_sort_value.direction
|
47
|
+
}
|
48
|
+
url_params.merge!(options)
|
54
49
|
end
|
55
50
|
end
|
56
51
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "active_support/concern"
|
4
4
|
require "active_support/core_ext/class/attribute"
|
5
|
+
require "active_support/core_ext/object/blank"
|
5
6
|
|
6
7
|
module Warped
|
7
8
|
module Controllers
|
@@ -35,7 +36,7 @@ module Warped
|
|
35
36
|
# class UsersController < ApplicationController
|
36
37
|
# include Sortable
|
37
38
|
#
|
38
|
-
# sortable_by :name, :created_at, 'accounts.referrals_count' => 'referrals'
|
39
|
+
# sortable_by :name, :created_at, 'accounts.referrals_count' => { alias_name: 'referrals' }
|
39
40
|
#
|
40
41
|
# def index
|
41
42
|
# scope = sort(User.joins(:account))
|
@@ -55,60 +56,79 @@ module Warped
|
|
55
56
|
extend ActiveSupport::Concern
|
56
57
|
|
57
58
|
included do
|
58
|
-
class_attribute :
|
59
|
-
class_attribute :
|
60
|
-
class_attribute :
|
61
|
-
|
59
|
+
class_attribute :sorts, default: []
|
60
|
+
class_attribute :default_sort, default: Sort.new("id")
|
61
|
+
class_attribute :default_sort_direction, default: "desc"
|
62
|
+
|
63
|
+
attr_reader :current_action_sorts
|
64
|
+
|
65
|
+
helper_method :current_action_sorts, :current_action_sort_value, :default_sort, :default_sort_direction
|
66
|
+
|
67
|
+
rescue_from Sort::DirectionError, with: :render_invalid_sort_direction
|
62
68
|
end
|
63
69
|
|
64
70
|
class_methods do
|
65
71
|
# @param keys [Array<Symbol,String>]
|
66
72
|
# @param mapped_keys [Hash<Symbol,String>]
|
67
73
|
def sortable_by(*keys, **mapped_keys)
|
68
|
-
self.
|
69
|
-
|
74
|
+
self.sorts = keys.map do |field|
|
75
|
+
Warped::Sort.new(field)
|
76
|
+
end
|
77
|
+
|
78
|
+
self.sorts += mapped_keys.map do |field, opts|
|
79
|
+
Warped::Sort.new(field, alias_name: opts[:alias_name])
|
80
|
+
end
|
81
|
+
|
82
|
+
return if self.sorts.any? { |sort| sort.name == default_sort.name }
|
83
|
+
|
84
|
+
self.sorts.push(default_sort)
|
70
85
|
end
|
71
86
|
end
|
72
87
|
|
73
88
|
# @param scope [ActiveRecord::Relation] The scope to sort.
|
74
|
-
# @param
|
75
|
-
# @param sort_direction [String, Symbol] The sort direction.
|
89
|
+
# @param sort_conditions [Array<Warped::Sort::Base>|nil] The sort conditions.
|
76
90
|
# @return [ActiveRecord::Relation]
|
77
|
-
def sort(scope,
|
78
|
-
|
79
|
-
|
80
|
-
validate_sort_key!
|
91
|
+
def sort(scope, sort_conditions: nil)
|
92
|
+
action_sorts = sort_conditions.presence || sorts
|
93
|
+
@current_action_sorts = action_sorts
|
81
94
|
|
82
|
-
Queries::Sort.call(scope, sort_key
|
95
|
+
Queries::Sort.call(scope, sort_key: current_action_sort_value.name,
|
96
|
+
sort_direction: current_action_sort_value.direction)
|
83
97
|
end
|
84
98
|
|
85
|
-
|
99
|
+
# @return [Warped::Sort::Value] The current sort value.
|
100
|
+
def current_action_sort_value
|
101
|
+
@current_action_sort_value ||= begin
|
102
|
+
sort_obj = current_action_sorts.find do |sort|
|
103
|
+
params[:sort_key] == sort.parameter_name
|
104
|
+
end
|
86
105
|
|
87
|
-
|
88
|
-
|
89
|
-
|
106
|
+
if sort_obj.present?
|
107
|
+
Sort::Value.new(sort_obj, params[:sort_direction] || default_sort_direction)
|
108
|
+
else
|
109
|
+
Sort::Value.new(default_sort, default_sort_direction)
|
110
|
+
end
|
111
|
+
end
|
90
112
|
end
|
91
113
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
114
|
+
protected
|
115
|
+
|
116
|
+
# @param exception [Sort::DirectionError]
|
117
|
+
def render_invalid_sort_direction(exception)
|
118
|
+
render json: { error: exception.message }, status: :bad_request
|
96
119
|
end
|
97
120
|
|
98
121
|
private
|
99
122
|
|
100
|
-
|
101
|
-
|
123
|
+
# @return [Warped::Sort] The current sort object.
|
124
|
+
def current_sort
|
125
|
+
@current_sort ||= begin
|
126
|
+
sort_obj = sorts.find do |sort|
|
127
|
+
params[:sort_key] == sort.parameter_name
|
128
|
+
end
|
102
129
|
|
103
|
-
|
104
|
-
|
105
|
-
raise ActionController::BadRequest, message
|
106
|
-
end
|
107
|
-
|
108
|
-
def valid_sort_key?
|
109
|
-
sort_key == default_sort_key.to_s ||
|
110
|
-
sort_fields.include?(sort_key) ||
|
111
|
-
mapped_sort_fields[sort_key].present?
|
130
|
+
sort_obj.presence || Warped::Sort.new(default_sort_key)
|
131
|
+
end
|
112
132
|
end
|
113
133
|
end
|
114
134
|
end
|
@@ -31,6 +31,8 @@ module Warped
|
|
31
31
|
}
|
32
32
|
end
|
33
33
|
|
34
|
+
# @param options [Hash] Additional hash of options to include in the tabulation url_params
|
35
|
+
# @return [Hash] The tabulation url_params
|
34
36
|
def tabulate_url_params(**options)
|
35
37
|
base = paginate_url_params
|
36
38
|
base.merge!(search_url_params)
|
@@ -41,6 +43,8 @@ module Warped
|
|
41
43
|
base.tap(&:compact_blank!)
|
42
44
|
end
|
43
45
|
|
46
|
+
# @param options [Hash] Additional hash of options to include in the tabulation query
|
47
|
+
# @return [String] The tabulation query string
|
44
48
|
def tabulate_query(**options)
|
45
49
|
tabulate_url_params(**options).to_query
|
46
50
|
end
|
@@ -13,7 +13,7 @@ module Warped
|
|
13
13
|
# class UsersController < ApplicationController
|
14
14
|
# include Tabulatable
|
15
15
|
#
|
16
|
-
# tabulatable_by :
|
16
|
+
# tabulatable_by :email, :age, 'posts.created_at', 'posts.id' => { alias_name: 'post_id', kind: :integer }
|
17
17
|
#
|
18
18
|
# def index
|
19
19
|
# users = User.left_joins(:posts).group(:id)
|
@@ -31,8 +31,8 @@ module Warped
|
|
31
31
|
# class PostsController < ApplicationController
|
32
32
|
# include Tabulatable
|
33
33
|
#
|
34
|
-
# tabulatable_by :title, :content, :created_at, user: 'users.name'
|
35
|
-
# filterable_by :created_at, user: 'users.name'
|
34
|
+
# tabulatable_by :title, :content, :created_at, user: { alias_name: 'users.name' }
|
35
|
+
# filterable_by :created_at, user: { alias_name: 'users.name' }
|
36
36
|
#
|
37
37
|
# def index
|
38
38
|
# posts = Post.left_joins(:user).group(:id)
|
@@ -50,16 +50,13 @@ module Warped
|
|
50
50
|
|
51
51
|
included do
|
52
52
|
class_attribute :tabulate_fields, default: []
|
53
|
-
class_attribute :mapped_tabulate_fields, default:
|
53
|
+
class_attribute :mapped_tabulate_fields, default: {}
|
54
54
|
end
|
55
55
|
|
56
56
|
class_methods do
|
57
57
|
def tabulatable_by(*keys, **mapped_keys)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
filterable_by(*keys, **mapped_keys) if filter_fields.empty? && mapped_filter_fields.empty?
|
62
|
-
sortable_by(*keys, **mapped_keys) if sort_fields.empty? && mapped_sort_fields.empty?
|
58
|
+
filterable_by(*keys, **mapped_keys) if filters.empty?
|
59
|
+
sortable_by(*keys, **mapped_keys) if sorts.empty?
|
63
60
|
end
|
64
61
|
end
|
65
62
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
isolate_namespace Warped
|
8
|
+
|
9
|
+
initializer "warped.assets" do
|
10
|
+
if Rails.application.config.respond_to?(:assets)
|
11
|
+
Rails.application.config.assets.precompile += %w[warped_manifest.js]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
initializer "warped.importmap", before: "importmap" do |app|
|
16
|
+
app.config.importmap.paths << Engine.root.join("config/importmap.rb") if Rails.application.respond_to?(:importmap)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -25,7 +25,7 @@ module Warped
|
|
25
25
|
# @see RELATIONS
|
26
26
|
# To see the list of available relations, check the +RELATIONS+ constant.
|
27
27
|
class Filter
|
28
|
-
RELATIONS =
|
28
|
+
RELATIONS = ::Warped::Filter::Base::RELATIONS.freeze
|
29
29
|
|
30
30
|
# @param scope [ActiveRecord::Relation] the scope to filter
|
31
31
|
# @param filter_conditions [Array<Hash>] the conditions to filter by
|
@@ -85,11 +85,11 @@ module Warped
|
|
85
85
|
when "is_not_null"
|
86
86
|
scope.where.not(field => nil)
|
87
87
|
when "gt"
|
88
|
-
scope.where.not(field =>
|
88
|
+
scope.where.not(field => ..value)
|
89
89
|
when "gte"
|
90
90
|
scope.where(field => value..)
|
91
91
|
when "lt"
|
92
|
-
scope.where(field =>
|
92
|
+
scope.where.not(field => value..)
|
93
93
|
when "lte"
|
94
94
|
scope.where(field => ..value)
|
95
95
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
|
5
|
+
module Warped
|
6
|
+
module Table
|
7
|
+
class Action
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
delegate :[], to: :options
|
11
|
+
|
12
|
+
# @param name [String | Symbol | Proc] The name of the action
|
13
|
+
# @param path [String | Symbol | Proc] The path of the action
|
14
|
+
def initialize(name, path, **options)
|
15
|
+
@name = name
|
16
|
+
@path = path
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def path(...)
|
21
|
+
return @path.call(...) if @path.is_a?(Proc)
|
22
|
+
|
23
|
+
@path
|
24
|
+
end
|
25
|
+
|
26
|
+
def name(...)
|
27
|
+
return @name.call(...) if @name.is_a?(Proc)
|
28
|
+
|
29
|
+
@name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/blank"
|
4
|
+
require "active_support/core_ext/string/inflections"
|
5
|
+
|
6
|
+
module Warped
|
7
|
+
module Table
|
8
|
+
class Column
|
9
|
+
attr_reader :parameter_name, :method, :options
|
10
|
+
|
11
|
+
# @param parameter_name [String] The parameter name to be used by the column
|
12
|
+
# @param display_name [String] The display name of the column
|
13
|
+
# @param method [String, Symbol, Proc] The method to be called on the record to get the content of the column
|
14
|
+
def initialize(parameter_name, display_name = nil, method: nil, **options)
|
15
|
+
@parameter_name = parameter_name
|
16
|
+
@display_name = display_name
|
17
|
+
@options = options
|
18
|
+
@method = method.presence || parameter_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def display_name
|
22
|
+
@display_name.presence || parameter_name.to_s.humanize
|
23
|
+
end
|
24
|
+
|
25
|
+
def content_for(record)
|
26
|
+
if method.is_a?(Proc)
|
27
|
+
method.call(record)
|
28
|
+
else
|
29
|
+
record.public_send(method)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/warped/version.rb
CHANGED
data/lib/warped.rb
CHANGED
@@ -28,5 +28,6 @@ loader = Zeitwerk::Loader.for_gem
|
|
28
28
|
loader.ignore("#{__dir__}/generators")
|
29
29
|
loader.ignore("lib/warped/railtie.rb") unless defined?(Rails::Railtie)
|
30
30
|
loader.collapse("#{__dir__}/warped/emails/components")
|
31
|
+
loader.collapse("#{__dir__}/warped/api")
|
31
32
|
loader.setup
|
32
33
|
loader.eager_load
|
data/warped.gemspec
CHANGED
@@ -28,6 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
29
|
spec.require_paths = ["lib"]
|
30
30
|
|
31
|
-
spec.add_dependency "rails", ">=
|
31
|
+
spec.add_dependency "rails", ">= 7.1.0", "<= 8.0"
|
32
32
|
spec.add_dependency "zeitwerk", ">= 2.4"
|
33
33
|
end
|