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
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/array/wrap'
|
4
|
+
require_relative '../../enumerable_set_instruction'
|
5
|
+
require_relative './operators'
|
6
|
+
|
7
|
+
class ActiveSet
|
8
|
+
module Filtering
|
9
|
+
module Enumerable
|
10
|
+
class SetInstruction < EnumerableSetInstruction
|
11
|
+
def item_matches_query?(item)
|
12
|
+
return query_result_for(item, query_attribute_for(instruction_value)) unless operator_hash.key?(:reducer)
|
13
|
+
|
14
|
+
Array.wrap(instruction_value).public_send(operator_hash[:reducer]) do |value|
|
15
|
+
query_result_for(item, query_attribute_for(value))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_item
|
20
|
+
return @set_item if defined? @set_item
|
21
|
+
|
22
|
+
@set_item = @set.find(&:present?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def attribute_instance
|
26
|
+
return set_item if @attribute_instruction.associations_array.empty?
|
27
|
+
return @attribute_model if defined? @attribute_model
|
28
|
+
|
29
|
+
@attribute_model = @attribute_instruction
|
30
|
+
.associations_array
|
31
|
+
.reduce(set_item) do |obj, assoc|
|
32
|
+
obj.public_send(assoc)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def attribute_class
|
37
|
+
attribute_instance&.class
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def query_result_for(item, value)
|
43
|
+
result = if operator_method == :cover? && value.is_a?(Range)
|
44
|
+
value
|
45
|
+
.public_send(
|
46
|
+
operator_method,
|
47
|
+
object_attribute_for(item)
|
48
|
+
)
|
49
|
+
else
|
50
|
+
object_attribute_for(item)
|
51
|
+
.public_send(
|
52
|
+
operator_method,
|
53
|
+
value
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
return result unless operator_hash.key?(:result_transformer)
|
58
|
+
|
59
|
+
operator_hash[:result_transformer].call(result)
|
60
|
+
end
|
61
|
+
|
62
|
+
def object_attribute_for(item)
|
63
|
+
attribute = guarantee_attribute_type(attribute_value_for(item))
|
64
|
+
return attribute unless operator_hash.key?(:object_attribute_transformer)
|
65
|
+
|
66
|
+
operator_hash[:object_attribute_transformer].call(attribute)
|
67
|
+
end
|
68
|
+
|
69
|
+
def query_attribute_for(value)
|
70
|
+
attribute = guarantee_attribute_type(value)
|
71
|
+
return attribute unless operator_hash.key?(:query_attribute_transformer)
|
72
|
+
|
73
|
+
operator_hash[:query_attribute_transformer].call(attribute)
|
74
|
+
end
|
75
|
+
|
76
|
+
def operator_method
|
77
|
+
operator_hash.dig(:operator) || :'=='
|
78
|
+
end
|
79
|
+
|
80
|
+
def operator_hash
|
81
|
+
instruction_operator = @attribute_instruction.operator
|
82
|
+
|
83
|
+
Operators.get(instruction_operator)
|
84
|
+
end
|
85
|
+
|
86
|
+
def guarantee_attribute_type(attribute)
|
87
|
+
# Booleans don't respond to many operator methods,
|
88
|
+
# so we cast them to integers
|
89
|
+
return 1 if attribute == true
|
90
|
+
return 0 if attribute == false
|
91
|
+
return attribute.map { |a| guarantee_attribute_type(a) } if attribute.respond_to?(:each)
|
92
|
+
|
93
|
+
attribute
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './set_instruction'
|
4
|
+
require 'active_support/core_ext/module/delegation'
|
5
|
+
|
6
|
+
class ActiveSet
|
7
|
+
module Filtering
|
8
|
+
module Enumerable
|
9
|
+
class Strategy
|
10
|
+
delegate :attribute_instance,
|
11
|
+
:attribute_class,
|
12
|
+
:instruction_value,
|
13
|
+
:attribute_value_for,
|
14
|
+
:operator,
|
15
|
+
:attribute,
|
16
|
+
:set_item,
|
17
|
+
:resource_for,
|
18
|
+
to: :@set_instruction
|
19
|
+
|
20
|
+
def initialize(set, attribute_instruction)
|
21
|
+
@set = set
|
22
|
+
@attribute_instruction = attribute_instruction
|
23
|
+
@set_instruction = SetInstruction.new(attribute_instruction, set)
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute
|
27
|
+
return false unless @set.respond_to? :select
|
28
|
+
|
29
|
+
if execute_filter_operation?
|
30
|
+
set = filter_operation
|
31
|
+
elsif execute_intersect_operation?
|
32
|
+
begin
|
33
|
+
set = intersect_operation
|
34
|
+
rescue TypeError # thrown if intersecting with a non-Array
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
else
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
|
41
|
+
set
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def execute_filter_operation?
|
47
|
+
return false unless attribute_instance
|
48
|
+
return false unless attribute_instance.respond_to?(attribute)
|
49
|
+
return false if attribute_instance.method(attribute).arity.positive?
|
50
|
+
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def execute_intersect_operation?
|
55
|
+
return false unless attribute_class
|
56
|
+
return false unless attribute_class.respond_to?(attribute)
|
57
|
+
return false if attribute_class.method(attribute).arity.zero?
|
58
|
+
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
def filter_operation
|
63
|
+
@set.select do |item|
|
64
|
+
@set_instruction.item_matches_query?(item)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def intersect_operation
|
69
|
+
@set & other_set
|
70
|
+
end
|
71
|
+
|
72
|
+
def other_set
|
73
|
+
other_set = attribute_class.public_send(
|
74
|
+
attribute,
|
75
|
+
instruction_value
|
76
|
+
)
|
77
|
+
if attribute_class != set_item.class
|
78
|
+
other_set = begin
|
79
|
+
@set.select { |item| resource_for(item: item)&.presence_in other_set }
|
80
|
+
rescue ArgumentError # thrown if other_set is doesn't respond to #include?, like when nil
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
other_set
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../attribute_instruction'
|
4
|
-
require_relative './
|
5
|
-
require_relative './
|
4
|
+
require_relative './enumerable/strategy'
|
5
|
+
require_relative './active_record/strategy'
|
6
6
|
|
7
7
|
class ActiveSet
|
8
8
|
module Filtering
|
@@ -17,7 +17,7 @@ class ActiveSet
|
|
17
17
|
.map { |k, v| AttributeInstruction.new(k, v) }
|
18
18
|
|
19
19
|
activerecord_filtered_set = attribute_instructions.reduce(@set) do |set, attribute_instruction|
|
20
|
-
maybe_set_or_false =
|
20
|
+
maybe_set_or_false = ActiveRecord::Strategy.new(set, attribute_instruction).execute
|
21
21
|
next set unless maybe_set_or_false
|
22
22
|
|
23
23
|
attribute_instruction.processed = true
|
@@ -27,7 +27,7 @@ class ActiveSet
|
|
27
27
|
return activerecord_filtered_set if attribute_instructions.all?(&:processed?)
|
28
28
|
|
29
29
|
attribute_instructions.reject(&:processed?).reduce(activerecord_filtered_set) do |set, attribute_instruction|
|
30
|
-
maybe_set_or_false =
|
30
|
+
maybe_set_or_false = Enumerable::Strategy.new(set, attribute_instruction).execute
|
31
31
|
next set unless maybe_set_or_false
|
32
32
|
|
33
33
|
attribute_instruction.processed = true
|
@@ -24,7 +24,7 @@ require 'active_support/core_ext/array/wrap'
|
|
24
24
|
# => { "key"=>"value", "nested.key"=>"nested_value", "array.0"=>0, "array.1"=>1, "array.2"=>2 }
|
25
25
|
|
26
26
|
# refactored from https://stackoverflow.com/a/23861946/2884386
|
27
|
-
def flatten_keys_of(input, keys = [], output = {}, flattener: ->(*
|
27
|
+
def flatten_keys_of(input, keys = [], output = {}, flattener: ->(*k) { k }, flatten_arrays: false)
|
28
28
|
if input.is_a?(Hash)
|
29
29
|
input.each do |key, value|
|
30
30
|
flatten_keys_of(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actionset
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Margheim
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -270,6 +270,11 @@ files:
|
|
270
270
|
- lib/action_set/helpers/pagination/page_size_for_helper.rb
|
271
271
|
- lib/action_set/helpers/pagination/path_for_helper.rb
|
272
272
|
- lib/action_set/helpers/pagination/prev_page_link_for_helper.rb
|
273
|
+
- lib/action_set/helpers/pagination/record_description_for_helper.rb
|
274
|
+
- lib/action_set/helpers/pagination/record_first_for_helper.rb
|
275
|
+
- lib/action_set/helpers/pagination/record_last_for_helper.rb
|
276
|
+
- lib/action_set/helpers/pagination/record_range_for_helper.rb
|
277
|
+
- lib/action_set/helpers/pagination/record_size_for_helper.rb
|
273
278
|
- lib/action_set/helpers/pagination/total_pages_for_helper.rb
|
274
279
|
- lib/action_set/helpers/params/current_helper.rb
|
275
280
|
- lib/action_set/helpers/params/form_for_object_helper.rb
|
@@ -286,8 +291,15 @@ files:
|
|
286
291
|
- lib/active_set/enumerable_set_instruction.rb
|
287
292
|
- lib/active_set/exporting/csv_strategy.rb
|
288
293
|
- lib/active_set/exporting/operation.rb
|
289
|
-
- lib/active_set/filtering/
|
290
|
-
- lib/active_set/filtering/
|
294
|
+
- lib/active_set/filtering/active_record/operators.rb
|
295
|
+
- lib/active_set/filtering/active_record/query_column.rb
|
296
|
+
- lib/active_set/filtering/active_record/query_value.rb
|
297
|
+
- lib/active_set/filtering/active_record/set_instruction.rb
|
298
|
+
- lib/active_set/filtering/active_record/strategy.rb
|
299
|
+
- lib/active_set/filtering/constants.rb
|
300
|
+
- lib/active_set/filtering/enumerable/operators.rb
|
301
|
+
- lib/active_set/filtering/enumerable/set_instruction.rb
|
302
|
+
- lib/active_set/filtering/enumerable/strategy.rb
|
291
303
|
- lib/active_set/filtering/operation.rb
|
292
304
|
- lib/active_set/paginating/active_record_strategy.rb
|
293
305
|
- lib/active_set/paginating/enumerable_strategy.rb
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../active_record_set_instruction'
|
4
|
-
require 'active_support/core_ext/module/delegation'
|
5
|
-
|
6
|
-
class ActiveSet
|
7
|
-
module Filtering
|
8
|
-
class ActiveRecordStrategy
|
9
|
-
delegate :attribute_model,
|
10
|
-
:arel_column,
|
11
|
-
:arel_operator,
|
12
|
-
:arel_value,
|
13
|
-
:arel_type,
|
14
|
-
:initial_relation,
|
15
|
-
:attribute,
|
16
|
-
to: :@set_instruction
|
17
|
-
|
18
|
-
def initialize(set, attribute_instruction)
|
19
|
-
@set = set
|
20
|
-
@attribute_instruction = attribute_instruction
|
21
|
-
@set_instruction = ActiveRecordSetInstruction.new(attribute_instruction, set)
|
22
|
-
end
|
23
|
-
|
24
|
-
def execute
|
25
|
-
return false unless @set.respond_to? :to_sql
|
26
|
-
|
27
|
-
if execute_filter_operation?
|
28
|
-
statement = filter_operation
|
29
|
-
elsif execute_intersect_operation?
|
30
|
-
begin
|
31
|
-
statement = intersect_operation
|
32
|
-
rescue ArgumentError # thrown if merging a non-ActiveRecord::Relation
|
33
|
-
return false
|
34
|
-
end
|
35
|
-
else
|
36
|
-
return false
|
37
|
-
end
|
38
|
-
|
39
|
-
statement
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def execute_filter_operation?
|
45
|
-
return false unless attribute_model
|
46
|
-
return false unless attribute_model.respond_to?(:attribute_names)
|
47
|
-
return false unless attribute_model.attribute_names.include?(attribute)
|
48
|
-
|
49
|
-
true
|
50
|
-
end
|
51
|
-
|
52
|
-
def execute_intersect_operation?
|
53
|
-
return false unless attribute_model
|
54
|
-
return false unless attribute_model.respond_to?(attribute)
|
55
|
-
return false if attribute_model.method(attribute).arity.zero?
|
56
|
-
|
57
|
-
true
|
58
|
-
end
|
59
|
-
|
60
|
-
def filter_operation
|
61
|
-
initial_relation
|
62
|
-
.where(
|
63
|
-
arel_column.send(
|
64
|
-
arel_operator,
|
65
|
-
arel_value
|
66
|
-
)
|
67
|
-
)
|
68
|
-
end
|
69
|
-
|
70
|
-
def intersect_operation
|
71
|
-
# NOTE: If merging relations that contain duplicate column conditions,
|
72
|
-
# the second condition will replace the first.
|
73
|
-
# e.g. Thing.where(id: [1,2]).merge(Thing.where(id: [2,3]))
|
74
|
-
# => [Thing<2>, Thing<3>] NOT [Thing<2>]
|
75
|
-
initial_relation
|
76
|
-
.merge(
|
77
|
-
attribute_model.public_send(
|
78
|
-
attribute,
|
79
|
-
arel_value
|
80
|
-
)
|
81
|
-
)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../enumerable_set_instruction'
|
4
|
-
require 'active_support/core_ext/module/delegation'
|
5
|
-
|
6
|
-
class ActiveSet
|
7
|
-
module Filtering
|
8
|
-
class EnumerableStrategy
|
9
|
-
delegate :attribute_instance,
|
10
|
-
:attribute_class,
|
11
|
-
:attribute_value,
|
12
|
-
:attribute_value_for,
|
13
|
-
:operator,
|
14
|
-
:attribute,
|
15
|
-
to: :@set_instruction
|
16
|
-
|
17
|
-
def initialize(set, attribute_instruction)
|
18
|
-
@set = set
|
19
|
-
@attribute_instruction = attribute_instruction
|
20
|
-
@set_instruction = EnumerableSetInstruction.new(attribute_instruction, set)
|
21
|
-
end
|
22
|
-
|
23
|
-
def execute
|
24
|
-
return false unless @set.respond_to? :select
|
25
|
-
|
26
|
-
if execute_filter_operation?
|
27
|
-
set = filter_operation
|
28
|
-
elsif execute_intersect_operation?
|
29
|
-
begin
|
30
|
-
set = intersect_operation
|
31
|
-
rescue TypeError # thrown if intersecting with a non-Array
|
32
|
-
return false
|
33
|
-
end
|
34
|
-
else
|
35
|
-
return false
|
36
|
-
end
|
37
|
-
|
38
|
-
set
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def execute_filter_operation?
|
44
|
-
return false unless attribute_instance
|
45
|
-
return false unless attribute_instance.respond_to?(attribute)
|
46
|
-
return false if attribute_instance.method(attribute).arity.positive?
|
47
|
-
|
48
|
-
true
|
49
|
-
end
|
50
|
-
|
51
|
-
def execute_intersect_operation?
|
52
|
-
return false unless attribute_class
|
53
|
-
return false unless attribute_class.respond_to?(attribute)
|
54
|
-
return false if attribute_class.method(attribute).arity.zero?
|
55
|
-
|
56
|
-
true
|
57
|
-
end
|
58
|
-
|
59
|
-
def filter_operation
|
60
|
-
@set.select do |item|
|
61
|
-
attribute_value_for(item)
|
62
|
-
.public_send(
|
63
|
-
operator,
|
64
|
-
attribute_value
|
65
|
-
)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def intersect_operation
|
70
|
-
other_set = attribute_class
|
71
|
-
.public_send(
|
72
|
-
attribute,
|
73
|
-
attribute_value
|
74
|
-
)
|
75
|
-
@set & other_set
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|