actionset 0.8.2 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|