actionset 0.8.2 → 0.9.1
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/CHANGELOG +24 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +7 -1
- data/actionset.gemspec +1 -1
- data/lib/action_set.rb +36 -7
- data/lib/action_set/attribute_value.rb +7 -1
- data/lib/action_set/helpers/helper_methods.rb +2 -1
- data/lib/action_set/helpers/pagination/record_description_for_helper.rb +20 -0
- data/lib/action_set/helpers/pagination/record_first_for_helper.rb +20 -0
- data/lib/action_set/helpers/pagination/record_last_for_helper.rb +26 -0
- data/lib/action_set/helpers/pagination/record_range_for_helper.rb +25 -0
- data/lib/action_set/helpers/pagination/record_size_for_helper.rb +9 -0
- data/lib/action_set/helpers/pagination/total_pages_for_helper.rb +3 -1
- data/lib/active_set/active_record_set_instruction.rb +30 -37
- data/lib/active_set/attribute_instruction.rb +2 -2
- data/lib/active_set/enumerable_set_instruction.rb +13 -26
- data/lib/active_set/filtering/active_record/operators.rb +277 -0
- data/lib/active_set/filtering/active_record/query_column.rb +35 -0
- data/lib/active_set/filtering/active_record/query_value.rb +47 -0
- data/lib/active_set/filtering/active_record/set_instruction.rb +29 -0
- data/lib/active_set/filtering/active_record/strategy.rb +87 -0
- data/lib/active_set/filtering/constants.rb +349 -0
- data/lib/active_set/filtering/enumerable/operators.rb +308 -0
- data/lib/active_set/filtering/enumerable/set_instruction.rb +98 -0
- data/lib/active_set/filtering/enumerable/strategy.rb +90 -0
- data/lib/active_set/filtering/operation.rb +4 -4
- data/lib/helpers/flatten_keys_of.rb +1 -1
- metadata +16 -4
- data/lib/active_set/filtering/active_record_strategy.rb +0 -85
- data/lib/active_set/filtering/enumerable_strategy.rb +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1eb88ba89dc96b42af254762a42011c32153b788
|
4
|
+
data.tar.gz: 174a5c1c743fff20cf343ca82abe3461a040d342
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c39ddbc29032e67bb292ce87c79734a399dab2cf4467aa0e63168036a794908b840cc544394e24eb6da3c238293cded4a18b867591a0cfbef7e72b812990404
|
7
|
+
data.tar.gz: e982edf3262db42fcb15d871047a33d79a35344841e8a218d6b485d10315fc2cdf3b52d8f63303f9def653220a52ff318b05e69c94f9e8bf352dc59492ffb301
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
v 0.9.1
|
2
|
+
- ensure that our RubyGems build is stable, since we had a TravisCI build problem in the last version
|
3
|
+
v 0.9.0
|
4
|
+
- Add Enumerable support for the base filtering operations
|
5
|
+
+ EQ, NOT_EQ, EQ_ANY, EQ_ALL, NOT_EQ_ANY, NOT_EQ_ALL
|
6
|
+
+ IN, NOT_IN, IN_ANY, IN_ALL, NOT_IN_ANY, NOT_IN_ALL
|
7
|
+
+ MATCHES, DOES_NOT_MATCH, MATCHES_ANY, MATCHES_ALL, DOES_NOT_MATCH_ANY, DOES_NOT_MATCH_ALL
|
8
|
+
+ LT, LTEQ, LT_ANY, LT_ALL, LTEQ_ANY, LTEQ_ALL
|
9
|
+
+ GT, GTEQ, GT_ANY, GT_ALL, GTEQ_ANY, GTEQ_ALL
|
10
|
+
+ BETWEEN, NOT_BETWEEN
|
11
|
+
- Add unary predicate filtering operators, for both ActiveRecord and Enumerable
|
12
|
+
+ IS_TRUE, IS_FALSE
|
13
|
+
+ IS_NULL, NOT_NULL
|
14
|
+
+ IS_PRESENT, IS_BLANK
|
15
|
+
- Add computed matcher filtering operators, for both ActiveRecord and Enumerable
|
16
|
+
+ MATCH_START, MATCH_START_ANY, MATCH_START_ALL, MATCH_NOT_START, MATCH_NOT_START_ANY, MATCH_NOT_START_ALL
|
17
|
+
+ MATCH_END, MATCH_END_ANY, MATCH_END_ALL, MATCH_NOT_END, MATCH_NOT_END_ANY, MATCH_NOT_END_ALL
|
18
|
+
+ MATCH_CONTAIN, MATCH_CONTAIN_ANY, MATCH_CONTAIN_ALL, MATCH_NOT_CONTAIN, MATCH_NOT_CONTAIN_ANY, MATCH_NOT_CONTAIN_ALL
|
19
|
+
- Add view helpers for the range of records shown on the current page
|
20
|
+
- Use ActiveRecord's data dictionary to look up database column types when converting filter params to filter instructions
|
21
|
+
- Allow app to define type hints for filter attributes when converting filter params to filter instructions
|
22
|
+
- Allow sort params to be passed in short or long form
|
23
|
+
+ e.g. { attribute: x, direction: x } or { attribute: direction }
|
24
|
+
- Fix enumerable intersection filtering when working across associations
|
1
25
|
v 0.8.2
|
2
26
|
- add `ActiveSet.configuration.on_asc_sort_nils_come` configuration
|
3
27
|
v 0.8.1
|
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
data/actionset.gemspec
CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.platform = Gem::Platform::RUBY
|
8
8
|
spec.name = 'actionset'
|
9
|
-
spec.version = '0.
|
9
|
+
spec.version = '0.9.1'
|
10
10
|
spec.authors = ['Stephen Margheim']
|
11
11
|
spec.email = ['stephen.margheim@gmail.com']
|
12
12
|
|
data/lib/action_set.rb
CHANGED
@@ -30,7 +30,7 @@ module ActionSet
|
|
30
30
|
|
31
31
|
def sort_set(set)
|
32
32
|
active_set = ensure_active_set(set)
|
33
|
-
active_set = active_set.sort(
|
33
|
+
active_set = active_set.sort(sort_instructions) if sort_params.any?
|
34
34
|
active_set
|
35
35
|
end
|
36
36
|
|
@@ -64,18 +64,19 @@ module ActionSet
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
67
|
+
def sort_instructions
|
68
|
+
if sort_params.key?(:attribute) && sort_params.key?(:direction)
|
69
|
+
{ sort_params[:attribute] => sort_params[:direction] }
|
70
|
+
else
|
71
|
+
sort_params.transform_values { |v| v.remove('ending') }
|
72
|
+
end
|
73
73
|
end
|
74
74
|
|
75
75
|
def paginate_instructions
|
76
76
|
paginate_params.transform_values(&:to_i)
|
77
77
|
end
|
78
78
|
|
79
|
+
# rubocop:disable Metrics/AbcSize
|
79
80
|
def export_instructions
|
80
81
|
{}.tap do |struct|
|
81
82
|
struct[:format] = export_params[:format] || request.format.symbol
|
@@ -93,10 +94,38 @@ module ActionSet
|
|
93
94
|
elsif respond_to?(:export_set_columns, true)
|
94
95
|
send(:export_set_columns)
|
95
96
|
else
|
97
|
+
# :nocov:
|
96
98
|
[{}]
|
99
|
+
# :nocov:
|
97
100
|
end
|
98
101
|
end
|
99
102
|
end
|
103
|
+
# rubocop:enable Metrics/AbcSize
|
104
|
+
|
105
|
+
def filter_typecasted_value_for(keypath, value, set)
|
106
|
+
klass = klass_for_keypath(keypath, value, set)
|
107
|
+
ActionSet::AttributeValue.new(value)
|
108
|
+
.cast(to: klass)
|
109
|
+
end
|
110
|
+
|
111
|
+
def klass_for_keypath(keypath, value, set)
|
112
|
+
if respond_to?(:filter_set_types, true)
|
113
|
+
type_declarations = public_send(:filter_set_types)
|
114
|
+
types = type_declarations['types'] || type_declarations[:types]
|
115
|
+
klass = types[keypath.join('.')]
|
116
|
+
return klass if klass
|
117
|
+
end
|
118
|
+
|
119
|
+
if set.is_a?(ActiveRecord::Relation) || set.view.is_a?(ActiveRecord::Relation)
|
120
|
+
klass_type = set.model.columns_hash.fetch(keypath, nil)&.type
|
121
|
+
return klass_type.class if klass_type
|
122
|
+
end
|
123
|
+
|
124
|
+
instruction = ActiveSet::AttributeInstruction.new(keypath, value)
|
125
|
+
item_with_value = set.find { |i| !instruction.value_for(item: i).nil? }
|
126
|
+
item_value = instruction.value_for(item: item_with_value)
|
127
|
+
item_value.class
|
128
|
+
end
|
100
129
|
|
101
130
|
def filter_params
|
102
131
|
params.fetch(:filter, {}).to_unsafe_hash
|
@@ -11,7 +11,7 @@ module ActionSet
|
|
11
11
|
def cast(to:)
|
12
12
|
adapters.reduce(nil) do |_, adapter|
|
13
13
|
mayble_value_or_nil = adapter.new(@raw, to).process
|
14
|
-
next if mayble_value_or_nil.nil?
|
14
|
+
next nil if mayble_value_or_nil.nil?
|
15
15
|
|
16
16
|
return mayble_value_or_nil
|
17
17
|
end
|
@@ -62,7 +62,9 @@ module ActionSet
|
|
62
62
|
begin
|
63
63
|
require 'active_model/type'
|
64
64
|
rescue LoadError
|
65
|
+
# :nocov:
|
65
66
|
require 'active_record/type'
|
67
|
+
# :nocov:
|
66
68
|
end
|
67
69
|
|
68
70
|
def initialize(raw, target)
|
@@ -106,13 +108,17 @@ module ActionSet
|
|
106
108
|
def init_typecaster(const_name)
|
107
109
|
type_class.const_get(const_name).new
|
108
110
|
rescue StandardError
|
111
|
+
# :nocov:
|
109
112
|
nil
|
113
|
+
# :nocov:
|
110
114
|
end
|
111
115
|
|
112
116
|
def type_class
|
113
117
|
ActiveModel::Type
|
114
118
|
rescue NameError
|
119
|
+
# :nocov:
|
115
120
|
ActiveRecord::Type
|
121
|
+
# :nocov:
|
116
122
|
end
|
117
123
|
end
|
118
124
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative './sort/link_for_helper'
|
4
4
|
require_relative './pagination/links_for_helper'
|
5
|
-
require_relative './pagination/
|
5
|
+
require_relative './pagination/record_description_for_helper'
|
6
6
|
require_relative './params/form_for_object_helper'
|
7
7
|
require_relative './export/path_for_helper'
|
8
8
|
|
@@ -11,6 +11,7 @@ module ActionSet
|
|
11
11
|
module HelperMethods
|
12
12
|
include Sort::LinkForHelper
|
13
13
|
include Pagination::LinksForHelper
|
14
|
+
include Pagination::RecordDescriptionForHelper
|
14
15
|
include Params::FormForObjectHelper
|
15
16
|
include Export::PathForHelper
|
16
17
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './record_range_for_helper'
|
4
|
+
require_relative './record_size_for_helper'
|
5
|
+
|
6
|
+
module Pagination
|
7
|
+
module RecordDescriptionForHelper
|
8
|
+
include Pagination::RecordRangeForHelper
|
9
|
+
include Pagination::RecordSizeForHelper
|
10
|
+
|
11
|
+
def pagination_record_description_for(set)
|
12
|
+
[
|
13
|
+
pagination_record_range_for(set),
|
14
|
+
'of',
|
15
|
+
"<strong>#{pagination_record_size_for(set)}</strong>",
|
16
|
+
'records'
|
17
|
+
].join(' ').html_safe
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './current_page_for_helper'
|
4
|
+
require_relative './page_size_for_helper'
|
5
|
+
|
6
|
+
module Pagination
|
7
|
+
module RecordFirstForHelper
|
8
|
+
include Pagination::CurrentPageForHelper
|
9
|
+
include Pagination::PageSizeForHelper
|
10
|
+
|
11
|
+
def pagination_record_first_for(set)
|
12
|
+
current_page = pagination_current_page_for(set)
|
13
|
+
page_size = pagination_page_size_for(set)
|
14
|
+
|
15
|
+
return 1 if current_page == 1
|
16
|
+
|
17
|
+
((current_page - 1) * page_size) + 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './current_page_for_helper'
|
4
|
+
require_relative './record_size_for_helper'
|
5
|
+
require_relative './page_size_for_helper'
|
6
|
+
require_relative './total_pages_for_helper'
|
7
|
+
|
8
|
+
module Pagination
|
9
|
+
module RecordLastForHelper
|
10
|
+
include Pagination::RecordSizeForHelper
|
11
|
+
include Pagination::CurrentPageForHelper
|
12
|
+
include Pagination::PageSizeForHelper
|
13
|
+
include Pagination::TotalPagesForHelper
|
14
|
+
|
15
|
+
def pagination_record_last_for(set)
|
16
|
+
record_size = pagination_record_size_for(set)
|
17
|
+
current_page = pagination_current_page_for(set)
|
18
|
+
page_size = pagination_page_size_for(set)
|
19
|
+
total_pages = pagination_total_pages_for(set)
|
20
|
+
|
21
|
+
return record_size if current_page >= total_pages
|
22
|
+
|
23
|
+
current_page * page_size
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './record_first_for_helper'
|
4
|
+
require_relative './record_last_for_helper'
|
5
|
+
|
6
|
+
module Pagination
|
7
|
+
module RecordRangeForHelper
|
8
|
+
include Pagination::CurrentPageForHelper
|
9
|
+
include Pagination::TotalPagesForHelper
|
10
|
+
include Pagination::RecordFirstForHelper
|
11
|
+
include Pagination::RecordLastForHelper
|
12
|
+
|
13
|
+
def pagination_record_range_for(set)
|
14
|
+
current_page = pagination_current_page_for(set)
|
15
|
+
total_pages = pagination_total_pages_for(set)
|
16
|
+
return 'None' if current_page > total_pages
|
17
|
+
|
18
|
+
[
|
19
|
+
pagination_record_first_for(set),
|
20
|
+
'–',
|
21
|
+
pagination_record_last_for(set)
|
22
|
+
].join(' ').html_safe
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative './record_size_for_helper'
|
3
4
|
require_relative './page_size_for_helper'
|
4
5
|
|
5
6
|
module Pagination
|
6
7
|
module TotalPagesForHelper
|
8
|
+
include Pagination::RecordSizeForHelper
|
7
9
|
include Pagination::PageSizeForHelper
|
8
10
|
|
9
11
|
def pagination_total_pages_for(set)
|
10
|
-
total_set_size = set
|
12
|
+
total_set_size = pagination_record_size_for(set)
|
11
13
|
return 1 if total_set_size.zero?
|
12
14
|
|
13
15
|
(total_set_size.to_f / pagination_page_size_for(set)).ceil
|
@@ -9,15 +9,41 @@ class ActiveSet
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def initial_relation
|
12
|
-
return @
|
12
|
+
return @initial_relation if defined? @initial_relation
|
13
13
|
|
14
|
-
@
|
14
|
+
@initial_relation = if @attribute_instruction.associations_array.empty?
|
15
|
+
@set
|
16
|
+
else
|
17
|
+
@set.eager_load(@attribute_instruction.associations_hash)
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
21
|
+
def arel_column
|
22
|
+
return @arel_column if defined? @arel_column
|
23
|
+
|
24
|
+
arel_column = arel_table[@attribute_instruction.attribute]
|
25
|
+
arel_column = arel_column.lower if case_insensitive_operation?
|
26
|
+
|
27
|
+
@arel_column = arel_column
|
28
|
+
end
|
29
|
+
|
30
|
+
def attribute_model
|
31
|
+
return @set.klass if @attribute_instruction.associations_array.empty?
|
32
|
+
return @attribute_model if defined? @attribute_model
|
33
|
+
|
34
|
+
@attribute_model = @attribute_instruction
|
35
|
+
.associations_array
|
36
|
+
.reduce(@set) do |obj, assoc|
|
37
|
+
obj.reflections[assoc.to_s]&.klass
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
17
43
|
def arel_type
|
18
44
|
attribute_model
|
19
|
-
|
20
|
-
|
45
|
+
&.columns_hash[@attribute_instruction.attribute]
|
46
|
+
&.type
|
21
47
|
end
|
22
48
|
|
23
49
|
def arel_table
|
@@ -31,41 +57,8 @@ class ActiveSet
|
|
31
57
|
end
|
32
58
|
end
|
33
59
|
|
34
|
-
# rubocop:disable Lint/UnderscorePrefixedVariableName
|
35
|
-
def arel_column
|
36
|
-
_arel_column = arel_table[@attribute_instruction.attribute]
|
37
|
-
return _arel_column.lower if case_insensitive_operation?
|
38
|
-
|
39
|
-
_arel_column
|
40
|
-
end
|
41
|
-
# rubocop:enable Lint/UnderscorePrefixedVariableName
|
42
|
-
|
43
|
-
def arel_operator
|
44
|
-
@attribute_instruction.operator(default: :eq)
|
45
|
-
end
|
46
|
-
|
47
|
-
# rubocop:disable Lint/UnderscorePrefixedVariableName
|
48
|
-
def arel_value
|
49
|
-
_arel_value = @attribute_instruction.value
|
50
|
-
return _arel_value.downcase if case_insensitive_operation?
|
51
|
-
|
52
|
-
_arel_value
|
53
|
-
end
|
54
|
-
# rubocop:enable Lint/UnderscorePrefixedVariableName
|
55
|
-
|
56
60
|
def case_insensitive_operation?
|
57
61
|
@attribute_instruction.case_insensitive? && arel_type.presence_in(%i[string text])
|
58
62
|
end
|
59
|
-
|
60
|
-
def attribute_model
|
61
|
-
return @set.klass if @attribute_instruction.associations_array.empty?
|
62
|
-
return @attribute_model if defined? @attribute_model
|
63
|
-
|
64
|
-
@attribute_model = @attribute_instruction
|
65
|
-
.associations_array
|
66
|
-
.reduce(@set) do |obj, assoc|
|
67
|
-
obj.reflections[assoc.to_s]&.klass
|
68
|
-
end
|
69
|
-
end
|
70
63
|
end
|
71
64
|
end
|
@@ -32,11 +32,11 @@ class ActiveSet
|
|
32
32
|
@attribute = attribute
|
33
33
|
end
|
34
34
|
|
35
|
-
def operator
|
35
|
+
def operator
|
36
36
|
return @operator if defined? @operator
|
37
37
|
|
38
38
|
attribute_instruction = @keypath.last
|
39
|
-
@operator =
|
39
|
+
@operator = attribute_instruction[operator_regex, 1]&.to_sym
|
40
40
|
end
|
41
41
|
|
42
42
|
def options
|
@@ -9,40 +9,27 @@ class ActiveSet
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def attribute_value_for(item)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
@item_values ||= Hash.new do |h, key|
|
13
|
+
item_value = @attribute_instruction.value_for(item: key)
|
14
|
+
item_value = item_value.downcase if case_insensitive_operation_for?(item_value)
|
15
|
+
h[key] = item_value
|
16
|
+
end
|
17
|
+
|
18
|
+
@item_values[item]
|
16
19
|
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def instruction_value
|
22
|
+
return @instruction_value if defined? @instruction_value
|
23
|
+
|
24
|
+
instruction_value = @attribute_instruction.value
|
25
|
+
instruction_value = instruction_value.downcase if case_insensitive_operation_for?(instruction_value)
|
26
|
+
@instruction_value = instruction_value
|
23
27
|
end
|
24
|
-
# rubocop:enable Lint/UnderscorePrefixedVariableName
|
25
28
|
|
26
29
|
def case_insensitive_operation_for?(value)
|
27
30
|
return false unless @attribute_instruction.case_insensitive?
|
28
31
|
|
29
32
|
value.is_a?(String) || value.is_a?(Symbol)
|
30
33
|
end
|
31
|
-
|
32
|
-
def attribute_instance
|
33
|
-
set_item = @set.find(&:present?)
|
34
|
-
return set_item if @attribute_instruction.associations_array.empty?
|
35
|
-
return @attribute_model if defined? @attribute_model
|
36
|
-
|
37
|
-
@attribute_model = @attribute_instruction
|
38
|
-
.associations_array
|
39
|
-
.reduce(set_item) do |obj, assoc|
|
40
|
-
obj.public_send(assoc)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def attribute_class
|
45
|
-
attribute_instance&.class
|
46
|
-
end
|
47
34
|
end
|
48
35
|
end
|