actionset 0.8.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -5
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG +67 -0
  6. data/Gemfile.lock +134 -126
  7. data/README.md +26 -0
  8. data/Rakefile +8 -1
  9. data/actionset.gemspec +2 -2
  10. data/lib/action_set.rb +8 -23
  11. data/lib/action_set/attribute_value.rb +7 -1
  12. data/lib/action_set/filter_instructions.rb +72 -0
  13. data/lib/action_set/helpers/helper_methods.rb +2 -1
  14. data/lib/action_set/helpers/pagination/record_description_for_helper.rb +20 -0
  15. data/lib/action_set/helpers/pagination/record_first_for_helper.rb +20 -0
  16. data/lib/action_set/helpers/pagination/record_last_for_helper.rb +26 -0
  17. data/lib/action_set/helpers/pagination/record_range_for_helper.rb +25 -0
  18. data/lib/action_set/helpers/pagination/record_size_for_helper.rb +9 -0
  19. data/lib/action_set/helpers/pagination/total_pages_for_helper.rb +3 -1
  20. data/lib/action_set/sort_instructions.rb +44 -0
  21. data/lib/active_set.rb +25 -2
  22. data/lib/active_set/active_record_set_instruction.rb +33 -32
  23. data/lib/active_set/attribute_instruction.rb +3 -3
  24. data/lib/active_set/enumerable_set_instruction.rb +13 -24
  25. data/lib/active_set/filtering/active_record/operators.rb +280 -0
  26. data/lib/active_set/filtering/active_record/query_column.rb +35 -0
  27. data/lib/active_set/filtering/active_record/query_value.rb +47 -0
  28. data/lib/active_set/filtering/active_record/set_instruction.rb +29 -0
  29. data/lib/active_set/filtering/active_record/strategy.rb +87 -0
  30. data/lib/active_set/filtering/constants.rb +349 -0
  31. data/lib/active_set/filtering/enumerable/operators.rb +308 -0
  32. data/lib/active_set/filtering/enumerable/set_instruction.rb +98 -0
  33. data/lib/active_set/filtering/enumerable/strategy.rb +90 -0
  34. data/lib/active_set/filtering/operation.rb +5 -8
  35. data/lib/active_set/paginating/active_record_strategy.rb +0 -2
  36. data/lib/active_set/sorting/active_record_strategy.rb +27 -3
  37. data/lib/active_set/sorting/enumerable_strategy.rb +12 -2
  38. data/lib/active_set/sorting/operation.rb +1 -2
  39. data/lib/helpers/flatten_keys_of.rb +53 -0
  40. data/lib/helpers/transform_to_sortable_numeric.rb +3 -3
  41. metadata +26 -13
  42. data/lib/active_set/filtering/active_record_strategy.rb +0 -85
  43. data/lib/active_set/filtering/enumerable_strategy.rb +0 -79
  44. data/lib/helpers/throws.rb +0 -19
  45. data/lib/patches/core_ext/hash/flatten_keys.rb +0 -58
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActiveSet
4
+ module Filtering
5
+ module ActiveRecord
6
+ module QueryValue
7
+ def query_value
8
+ return @query_value if defined? @query_value
9
+
10
+ query_value = @attribute_instruction.value
11
+ query_value = query_attribute_for(query_value)
12
+ query_value = query_value.downcase if case_insensitive_operation?
13
+
14
+ @query_value = query_value
15
+ end
16
+
17
+ private
18
+
19
+ def query_attribute_for(value)
20
+ return value unless operator_hash.key?(:query_attribute_transformer)
21
+
22
+ context = {
23
+ raw: value,
24
+ sql: to_sql_str(value),
25
+ type: arel_type
26
+ }
27
+
28
+ operator_hash[:query_attribute_transformer].call(context)
29
+ end
30
+
31
+ def to_sql_str(value)
32
+ return value.map { |a| to_sql_str(a) } if value.respond_to?(:map)
33
+
34
+ arel_node = Arel::Nodes::Casted.new(value, arel_column)
35
+ sql_value = arel_node.to_sql
36
+ unwrap_sql_str(sql_value)
37
+ end
38
+
39
+ def unwrap_sql_str(sql_str)
40
+ return sql_str unless sql_str[0] == "'" && sql_str[-1] == "'"
41
+
42
+ sql_str[1..-2]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../active_record_set_instruction'
4
+ require_relative './operators'
5
+ require_relative './query_value'
6
+ require_relative './query_column'
7
+
8
+ class ActiveSet
9
+ module Filtering
10
+ module ActiveRecord
11
+ class SetInstruction < ActiveRecordSetInstruction
12
+ include QueryValue
13
+ include QueryColumn
14
+
15
+ def arel_operator
16
+ operator_hash.fetch(:operator, :eq)
17
+ end
18
+
19
+ private
20
+
21
+ def operator_hash
22
+ instruction_operator = @attribute_instruction.operator
23
+
24
+ Operators.get(instruction_operator)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,87 @@
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 ActiveRecord
9
+ class Strategy
10
+ delegate :attribute_model,
11
+ :query_column,
12
+ :arel_operator,
13
+ :query_value,
14
+ :arel_type,
15
+ :initial_relation,
16
+ :attribute,
17
+ to: :@set_instruction
18
+
19
+ def initialize(set, attribute_instruction)
20
+ @set = set
21
+ @attribute_instruction = attribute_instruction
22
+ @set_instruction = SetInstruction.new(attribute_instruction, set)
23
+ end
24
+
25
+ def execute
26
+ return false unless @set.respond_to? :to_sql
27
+
28
+ if execute_filter_operation?
29
+ statement = filter_operation
30
+ elsif execute_intersect_operation?
31
+ begin
32
+ statement = intersect_operation
33
+ rescue ArgumentError # thrown if merging a non-ActiveRecord::Relation
34
+ return false
35
+ end
36
+ else
37
+ return false
38
+ end
39
+
40
+ statement
41
+ end
42
+
43
+ private
44
+
45
+ def execute_filter_operation?
46
+ return false unless attribute_model
47
+ return false unless attribute_model.respond_to?(:attribute_names)
48
+ return false unless attribute_model.attribute_names.include?(attribute)
49
+
50
+ true
51
+ end
52
+
53
+ def execute_intersect_operation?
54
+ return false unless attribute_model
55
+ return false unless attribute_model.respond_to?(attribute)
56
+ return false if attribute_model.method(attribute).arity.zero?
57
+
58
+ true
59
+ end
60
+
61
+ def filter_operation
62
+ initial_relation
63
+ .where(
64
+ query_column.send(
65
+ arel_operator,
66
+ query_value
67
+ )
68
+ )
69
+ end
70
+
71
+ def intersect_operation
72
+ # NOTE: If merging relations that contain duplicate column conditions,
73
+ # the second condition will replace the first.
74
+ # e.g. Thing.where(id: [1,2]).merge(Thing.where(id: [2,3]))
75
+ # => [Thing<2>, Thing<3>] NOT [Thing<2>]
76
+ initial_relation
77
+ .merge(
78
+ attribute_model.public_send(
79
+ attribute,
80
+ query_value
81
+ )
82
+ )
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,349 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActiveSet
4
+ module Filtering
5
+ # rubocop:disable Metrics/ModuleLength
6
+ module Constants
7
+ BLANK_VALUES = [nil, ''].freeze
8
+
9
+ PREDICATES = {
10
+ EQ: {
11
+ type: :binary,
12
+ compound: false,
13
+ behavior: :inclusive,
14
+ shorthand: :'=='
15
+ },
16
+ NOT_EQ: {
17
+ type: :binary,
18
+ compound: false,
19
+ behavior: :exclusive,
20
+ shorthand: :'!='
21
+ },
22
+ EQ_ANY: {
23
+ type: :binary,
24
+ compound: true,
25
+ behavior: :inclusive,
26
+ shorthand: :'E=='
27
+ },
28
+ EQ_ALL: {
29
+ type: :binary,
30
+ compound: true,
31
+ behavior: :exclusive,
32
+ shorthand: :'A=='
33
+ },
34
+ NOT_EQ_ANY: {
35
+ type: :binary,
36
+ compound: true,
37
+ behavior: :inclusive,
38
+ shorthand: :'E!='
39
+ },
40
+ NOT_EQ_ALL: {
41
+ type: :binary,
42
+ compound: true,
43
+ behavior: :exclusive,
44
+ shorthand: :'A!='
45
+ },
46
+
47
+ IN: {
48
+ type: :binary,
49
+ compound: true,
50
+ behavior: :inclusive,
51
+ shorthand: :'<<'
52
+ },
53
+ NOT_IN: {
54
+ type: :binary,
55
+ compound: true,
56
+ behavior: :exclusive,
57
+ shorthand: :'!<'
58
+ },
59
+ IN_ANY: {
60
+ type: :binary,
61
+ compound: true,
62
+ behavior: :inclusive,
63
+ shorthand: :'E<<'
64
+ },
65
+ IN_ALL: {
66
+ type: :binary,
67
+ compound: true,
68
+ behavior: :exclusive,
69
+ shorthand: :'A<<'
70
+ },
71
+ NOT_IN_ANY: {
72
+ type: :binary,
73
+ compound: true,
74
+ behavior: :inclusive,
75
+ shorthand: :'E!<'
76
+ },
77
+ NOT_IN_ALL: {
78
+ type: :binary,
79
+ compound: true,
80
+ behavior: :exclusive,
81
+ shorthand: :'A!<'
82
+ },
83
+
84
+ MATCHES: {
85
+ type: :binary,
86
+ compound: false,
87
+ behavior: :inclusive,
88
+ shorthand: :'=~'
89
+ },
90
+ DOES_NOT_MATCH: {
91
+ type: :binary,
92
+ compound: false,
93
+ behavior: :exclusive,
94
+ shorthand: :'!~'
95
+ },
96
+ MATCHES_ANY: {
97
+ type: :binary,
98
+ compound: true,
99
+ behavior: :inclusive,
100
+ shorthand: :'E=~'
101
+ },
102
+ MATCHES_ALL: {
103
+ type: :binary,
104
+ compound: true,
105
+ behavior: :exclusive,
106
+ shorthand: :'A=~'
107
+ },
108
+ DOES_NOT_MATCH_ANY: {
109
+ type: :binary,
110
+ compound: true,
111
+ behavior: :inclusive,
112
+ shorthand: :'E!~'
113
+ },
114
+ DOES_NOT_MATCH_ALL: {
115
+ type: :binary,
116
+ compound: true,
117
+ behavior: :exclusive,
118
+ shorthand: :'A!~'
119
+ },
120
+
121
+ LT: {
122
+ type: :binary,
123
+ compound: false,
124
+ behavior: :exclusive,
125
+ shorthand: :'<'
126
+ },
127
+ LTEQ: {
128
+ type: :binary,
129
+ compound: false,
130
+ behavior: :inclusive,
131
+ shorthand: :'<='
132
+ },
133
+ LT_ANY: {
134
+ type: :binary,
135
+ compound: true,
136
+ behavior: :inconclusive,
137
+ shorthand: :'E<'
138
+ },
139
+ LT_ALL: {
140
+ type: :binary,
141
+ compound: true,
142
+ behavior: :exclusive,
143
+ shorthand: :'A<'
144
+ },
145
+ LTEQ_ANY: {
146
+ type: :binary,
147
+ compound: true,
148
+ behavior: :inclusive,
149
+ shorthand: :'E<='
150
+ },
151
+ LTEQ_ALL: {
152
+ type: :binary,
153
+ compound: true,
154
+ behavior: :inconclusive,
155
+ shorthand: :'A<='
156
+ },
157
+
158
+ GT: {
159
+ type: :binary,
160
+ compound: false,
161
+ behavior: :exclusive,
162
+ shorthand: :'>'
163
+ },
164
+ GTEQ: {
165
+ type: :binary,
166
+ compound: false,
167
+ behavior: :inclusive,
168
+ shorthand: :'>='
169
+ },
170
+ GT_ANY: {
171
+ type: :binary,
172
+ compound: true,
173
+ behavior: :inconclusive,
174
+ shorthand: :'E>'
175
+ },
176
+ GT_ALL: {
177
+ type: :binary,
178
+ compound: true,
179
+ behavior: :exclusive,
180
+ shorthand: :'A>'
181
+ },
182
+ GTEQ_ANY: {
183
+ type: :binary,
184
+ compound: true,
185
+ behavior: :inclusive,
186
+ shorthand: :'E>='
187
+ },
188
+ GTEQ_ALL: {
189
+ type: :binary,
190
+ compound: true,
191
+ behavior: :inconclusive,
192
+ shorthand: :'A>='
193
+ },
194
+
195
+ BETWEEN: {
196
+ type: :binary,
197
+ compound: true,
198
+ behavior: :inclusive,
199
+ shorthand: :'..'
200
+ },
201
+ NOT_BETWEEN: {
202
+ type: :binary,
203
+ compound: true,
204
+ behavior: :exclusive,
205
+ shorthand: :'!.'
206
+ },
207
+
208
+ IS_TRUE: {
209
+ type: :unary,
210
+ operator: :eq,
211
+ compound: false,
212
+ behavior: :inconclusive
213
+ },
214
+ IS_FALSE: {
215
+ type: :unary,
216
+ operator: :eq,
217
+ compound: false,
218
+ behavior: :inconclusive
219
+ },
220
+
221
+ IS_NULL: {
222
+ type: :unary,
223
+ operator: :eq,
224
+ compound: false,
225
+ behavior: :exclusive,
226
+ query_attribute_transformer: proc { |_| nil }
227
+ },
228
+ NOT_NULL: {
229
+ type: :unary,
230
+ operator: :not_eq,
231
+ compound: false,
232
+ behavior: :inclusive,
233
+ query_attribute_transformer: proc { |_| nil }
234
+ },
235
+
236
+ IS_PRESENT: {
237
+ type: :unary,
238
+ operator: :not_eq_all,
239
+ compound: false,
240
+ behavior: :inclusive
241
+ },
242
+ IS_BLANK: {
243
+ type: :unary,
244
+ operator: :eq_any,
245
+ compound: false,
246
+ behavior: :exclusive
247
+ },
248
+
249
+ MATCH_START: {
250
+ type: :binary,
251
+ compound: false,
252
+ behavior: :inclusive
253
+ },
254
+ MATCH_START_ANY: {
255
+ type: :binary,
256
+ compound: true,
257
+ behavior: :inclusive
258
+ },
259
+ MATCH_START_ALL: {
260
+ type: :binary,
261
+ compound: true,
262
+ behavior: :exclusive
263
+ },
264
+ MATCH_NOT_START: {
265
+ type: :binary,
266
+ compound: false,
267
+ behavior: :exclusive
268
+ },
269
+ MATCH_NOT_START_ANY: {
270
+ type: :binary,
271
+ compound: true,
272
+ behavior: :inclusive
273
+ },
274
+ MATCH_NOT_START_ALL: {
275
+ type: :binary,
276
+ compound: true,
277
+ behavior: :exclusive
278
+ },
279
+ MATCH_END: {
280
+ type: :binary,
281
+ compound: false,
282
+ behavior: :inclusive
283
+ },
284
+ MATCH_END_ANY: {
285
+ type: :binary,
286
+ compound: true,
287
+ behavior: :inclusive
288
+ },
289
+ MATCH_END_ALL: {
290
+ type: :binary,
291
+ compound: true,
292
+ behavior: :exclusive
293
+ },
294
+ MATCH_NOT_END: {
295
+ type: :binary,
296
+ compound: false,
297
+ behavior: :exclusive
298
+ },
299
+ MATCH_NOT_END_ANY: {
300
+ type: :binary,
301
+ compound: true,
302
+ behavior: :inclusive
303
+ },
304
+ MATCH_NOT_END_ALL: {
305
+ type: :binary,
306
+ compound: true,
307
+ behavior: :exclusive
308
+ },
309
+ MATCH_CONTAIN: {
310
+ type: :binary,
311
+ compound: false,
312
+ behavior: :inclusive,
313
+ shorthand: :'~*'
314
+ },
315
+ MATCH_CONTAIN_ANY: {
316
+ type: :binary,
317
+ compound: true,
318
+ behavior: :inclusive,
319
+ shorthand: :'E~*'
320
+ },
321
+ MATCH_CONTAIN_ALL: {
322
+ type: :binary,
323
+ compound: true,
324
+ behavior: :exclusive,
325
+ shorthand: :'A~*'
326
+ },
327
+ MATCH_NOT_CONTAIN: {
328
+ type: :binary,
329
+ compound: false,
330
+ behavior: :exclusive,
331
+ shorthand: :'!*'
332
+ },
333
+ MATCH_NOT_CONTAIN_ANY: {
334
+ type: :binary,
335
+ compound: true,
336
+ behavior: :inclusive,
337
+ shorthand: :'E!*'
338
+ },
339
+ MATCH_NOT_CONTAIN_ALL: {
340
+ type: :binary,
341
+ compound: true,
342
+ behavior: :exclusive,
343
+ shorthand: :'A!*'
344
+ }
345
+ }.freeze
346
+ end
347
+ # rubocop:enable Metrics/ModuleLength
348
+ end
349
+ end