actionset 0.8.1 → 0.8.2
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/.rubocop.yml +5 -5
- data/CHANGELOG +2 -0
- data/Gemfile.lock +61 -63
- data/README.md +26 -0
- data/actionset.gemspec +1 -1
- data/lib/action_set.rb +1 -1
- data/lib/active_set.rb +25 -2
- data/lib/active_set/active_record_set_instruction.rb +4 -0
- data/lib/active_set/attribute_instruction.rb +1 -1
- data/lib/active_set/enumerable_set_instruction.rb +3 -1
- data/lib/active_set/filtering/operation.rb +1 -4
- data/lib/active_set/sorting/active_record_strategy.rb +17 -3
- data/lib/active_set/sorting/enumerable_strategy.rb +12 -2
- data/lib/active_set/sorting/operation.rb +1 -2
- data/lib/helpers/flatten_keys_of.rb +53 -0
- data/lib/helpers/transform_to_sortable_numeric.rb +3 -3
- metadata +3 -4
- data/lib/helpers/throws.rb +0 -19
- data/lib/patches/core_ext/hash/flatten_keys.rb +0 -58
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dffd9096dbb67844e4cee728dee2dbea434a32e2
|
|
4
|
+
data.tar.gz: a6bcb0b49df525a8d3e6e74f06a067310674fd87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7400ae1649bde246c247752feabb37adc3c290ce464aee8271980c265965be920ac2cdae6c06b009fbbc59df371ec7e1aad075f4035a2e624ead3f6254e0011
|
|
7
|
+
data.tar.gz: ca373cfc982b4455ec40c5df823c94ccc4a16e0c2fc1fafeb2eb3cc9ea65b8be664df7135d6ef52c9e1b5cca12e959ae23eda6815809cb62e93baae0acec5d96
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
TargetRubyVersion: 2.4
|
|
3
|
+
Exclude:
|
|
4
|
+
- 'actionset.gemspec'
|
|
5
|
+
- 'spec/**/*'
|
|
3
6
|
Metrics/LineLength:
|
|
4
7
|
Max: 120
|
|
5
8
|
# To make it possible to copy or click on URIs in the code, we allow lines
|
|
@@ -10,13 +13,10 @@ Metrics/LineLength:
|
|
|
10
13
|
- https
|
|
11
14
|
Style/Documentation:
|
|
12
15
|
Enabled: false
|
|
16
|
+
Metrics/MethodLength:
|
|
17
|
+
Enabled: false
|
|
13
18
|
Style/SignalException:
|
|
14
19
|
EnforcedStyle: semantic
|
|
15
|
-
Metrics/BlockLength:
|
|
16
|
-
Exclude:
|
|
17
|
-
- 'activeset.gemspec'
|
|
18
|
-
- 'spec/**/*_spec.rb'
|
|
19
|
-
- '**/schema.rb'
|
|
20
20
|
Naming/VariableNumber:
|
|
21
21
|
EnforcedStyle: snake_case
|
|
22
22
|
Style/DateTime:
|
data/CHANGELOG
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
actionset (0.8.
|
|
4
|
+
actionset (0.8.2)
|
|
5
5
|
activesupport (>= 4.0.2)
|
|
6
6
|
railties
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
actioncable (5.1.
|
|
12
|
-
actionpack (= 5.1.
|
|
11
|
+
actioncable (5.1.7)
|
|
12
|
+
actionpack (= 5.1.7)
|
|
13
13
|
nio4r (~> 2.0)
|
|
14
14
|
websocket-driver (~> 0.6.1)
|
|
15
|
-
actionmailer (5.1.
|
|
16
|
-
actionpack (= 5.1.
|
|
17
|
-
actionview (= 5.1.
|
|
18
|
-
activejob (= 5.1.
|
|
15
|
+
actionmailer (5.1.7)
|
|
16
|
+
actionpack (= 5.1.7)
|
|
17
|
+
actionview (= 5.1.7)
|
|
18
|
+
activejob (= 5.1.7)
|
|
19
19
|
mail (~> 2.5, >= 2.5.4)
|
|
20
20
|
rails-dom-testing (~> 2.0)
|
|
21
|
-
actionpack (5.1.
|
|
22
|
-
actionview (= 5.1.
|
|
23
|
-
activesupport (= 5.1.
|
|
21
|
+
actionpack (5.1.7)
|
|
22
|
+
actionview (= 5.1.7)
|
|
23
|
+
activesupport (= 5.1.7)
|
|
24
24
|
rack (~> 2.0)
|
|
25
25
|
rack-test (>= 0.6.3)
|
|
26
26
|
rails-dom-testing (~> 2.0)
|
|
27
27
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
28
|
-
actionview (5.1.
|
|
29
|
-
activesupport (= 5.1.
|
|
28
|
+
actionview (5.1.7)
|
|
29
|
+
activesupport (= 5.1.7)
|
|
30
30
|
builder (~> 3.1)
|
|
31
31
|
erubi (~> 1.4)
|
|
32
32
|
rails-dom-testing (~> 2.0)
|
|
33
33
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
34
|
-
activejob (5.1.
|
|
35
|
-
activesupport (= 5.1.
|
|
34
|
+
activejob (5.1.7)
|
|
35
|
+
activesupport (= 5.1.7)
|
|
36
36
|
globalid (>= 0.3.6)
|
|
37
|
-
activemodel (5.1.
|
|
38
|
-
activesupport (= 5.1.
|
|
39
|
-
activerecord (5.1.
|
|
40
|
-
activemodel (= 5.1.
|
|
41
|
-
activesupport (= 5.1.
|
|
37
|
+
activemodel (5.1.7)
|
|
38
|
+
activesupport (= 5.1.7)
|
|
39
|
+
activerecord (5.1.7)
|
|
40
|
+
activemodel (= 5.1.7)
|
|
41
|
+
activesupport (= 5.1.7)
|
|
42
42
|
arel (~> 8.0)
|
|
43
|
-
activesupport (5.1.
|
|
43
|
+
activesupport (5.1.7)
|
|
44
44
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
45
45
|
i18n (>= 0.7, < 2)
|
|
46
46
|
minitest (~> 5.1)
|
|
47
47
|
tzinfo (~> 1.1)
|
|
48
|
-
addressable (2.
|
|
48
|
+
addressable (2.6.0)
|
|
49
49
|
public_suffix (>= 2.0.2, < 4.0)
|
|
50
50
|
ansi (1.5.0)
|
|
51
51
|
arel (8.0.0)
|
|
52
52
|
ast (2.4.0)
|
|
53
53
|
builder (3.2.3)
|
|
54
|
-
capybara (3.
|
|
54
|
+
capybara (3.19.0)
|
|
55
55
|
addressable
|
|
56
56
|
mini_mime (>= 0.1.3)
|
|
57
57
|
nokogiri (~> 1.8)
|
|
@@ -63,27 +63,27 @@ GEM
|
|
|
63
63
|
json
|
|
64
64
|
simplecov
|
|
65
65
|
url
|
|
66
|
-
combustion (
|
|
66
|
+
combustion (1.1.0)
|
|
67
67
|
activesupport (>= 3.0.0)
|
|
68
68
|
railties (>= 3.0.0)
|
|
69
69
|
thor (>= 0.14.6)
|
|
70
|
-
concurrent-ruby (1.1.
|
|
70
|
+
concurrent-ruby (1.1.5)
|
|
71
71
|
crass (1.0.4)
|
|
72
|
-
database_cleaner (1.
|
|
72
|
+
database_cleaner (1.7.0)
|
|
73
73
|
diff-lcs (1.3)
|
|
74
|
-
docile (1.1
|
|
75
|
-
erubi (1.
|
|
76
|
-
factory_bot (
|
|
77
|
-
activesupport (>=
|
|
78
|
-
faker (1.
|
|
74
|
+
docile (1.3.1)
|
|
75
|
+
erubi (1.8.0)
|
|
76
|
+
factory_bot (5.0.2)
|
|
77
|
+
activesupport (>= 4.2.0)
|
|
78
|
+
faker (1.9.3)
|
|
79
79
|
i18n (>= 0.7)
|
|
80
|
-
globalid (0.4.
|
|
80
|
+
globalid (0.4.2)
|
|
81
81
|
activesupport (>= 4.2.0)
|
|
82
82
|
hirb (0.7.3)
|
|
83
|
-
i18n (1.
|
|
83
|
+
i18n (1.6.0)
|
|
84
84
|
concurrent-ruby (~> 1.0)
|
|
85
|
-
jaro_winkler (1.5.
|
|
86
|
-
json (2.
|
|
85
|
+
jaro_winkler (1.5.2)
|
|
86
|
+
json (2.2.0)
|
|
87
87
|
loofah (2.2.3)
|
|
88
88
|
crass (~> 1.0.2)
|
|
89
89
|
nokogiri (>= 1.5.9)
|
|
@@ -94,60 +94,59 @@ GEM
|
|
|
94
94
|
mini_portile2 (2.4.0)
|
|
95
95
|
minitest (5.11.3)
|
|
96
96
|
nio4r (2.3.1)
|
|
97
|
-
nokogiri (1.
|
|
97
|
+
nokogiri (1.10.3)
|
|
98
98
|
mini_portile2 (~> 2.4.0)
|
|
99
|
-
parallel (1.
|
|
100
|
-
parser (2.
|
|
99
|
+
parallel (1.17.0)
|
|
100
|
+
parser (2.6.3.0)
|
|
101
101
|
ast (~> 2.4.0)
|
|
102
|
-
powerpack (0.1.2)
|
|
103
102
|
public_suffix (3.0.3)
|
|
104
|
-
rack (2.0.
|
|
103
|
+
rack (2.0.7)
|
|
105
104
|
rack-test (1.1.0)
|
|
106
105
|
rack (>= 1.0, < 3)
|
|
107
|
-
rails (5.1.
|
|
108
|
-
actioncable (= 5.1.
|
|
109
|
-
actionmailer (= 5.1.
|
|
110
|
-
actionpack (= 5.1.
|
|
111
|
-
actionview (= 5.1.
|
|
112
|
-
activejob (= 5.1.
|
|
113
|
-
activemodel (= 5.1.
|
|
114
|
-
activerecord (= 5.1.
|
|
115
|
-
activesupport (= 5.1.
|
|
106
|
+
rails (5.1.7)
|
|
107
|
+
actioncable (= 5.1.7)
|
|
108
|
+
actionmailer (= 5.1.7)
|
|
109
|
+
actionpack (= 5.1.7)
|
|
110
|
+
actionview (= 5.1.7)
|
|
111
|
+
activejob (= 5.1.7)
|
|
112
|
+
activemodel (= 5.1.7)
|
|
113
|
+
activerecord (= 5.1.7)
|
|
114
|
+
activesupport (= 5.1.7)
|
|
116
115
|
bundler (>= 1.3.0)
|
|
117
|
-
railties (= 5.1.
|
|
116
|
+
railties (= 5.1.7)
|
|
118
117
|
sprockets-rails (>= 2.0.0)
|
|
119
118
|
rails-dom-testing (2.0.3)
|
|
120
119
|
activesupport (>= 4.2.0)
|
|
121
120
|
nokogiri (>= 1.6)
|
|
122
121
|
rails-html-sanitizer (1.0.4)
|
|
123
122
|
loofah (~> 2.2, >= 2.2.2)
|
|
124
|
-
railties (5.1.
|
|
125
|
-
actionpack (= 5.1.
|
|
126
|
-
activesupport (= 5.1.
|
|
123
|
+
railties (5.1.7)
|
|
124
|
+
actionpack (= 5.1.7)
|
|
125
|
+
activesupport (= 5.1.7)
|
|
127
126
|
method_source
|
|
128
127
|
rake (>= 0.8.7)
|
|
129
128
|
thor (>= 0.18.1, < 2.0)
|
|
130
129
|
rainbow (3.0.0)
|
|
131
|
-
rake (
|
|
130
|
+
rake (12.3.2)
|
|
132
131
|
ransack (2.1.1)
|
|
133
132
|
actionpack (>= 5.0)
|
|
134
133
|
activerecord (>= 5.0)
|
|
135
134
|
activesupport (>= 5.0)
|
|
136
135
|
i18n
|
|
137
|
-
regexp_parser (1.
|
|
136
|
+
regexp_parser (1.4.0)
|
|
138
137
|
rspec (3.8.0)
|
|
139
138
|
rspec-core (~> 3.8.0)
|
|
140
139
|
rspec-expectations (~> 3.8.0)
|
|
141
140
|
rspec-mocks (~> 3.8.0)
|
|
142
141
|
rspec-core (3.8.0)
|
|
143
142
|
rspec-support (~> 3.8.0)
|
|
144
|
-
rspec-expectations (3.8.
|
|
143
|
+
rspec-expectations (3.8.3)
|
|
145
144
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
146
145
|
rspec-support (~> 3.8.0)
|
|
147
146
|
rspec-mocks (3.8.0)
|
|
148
147
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
149
148
|
rspec-support (~> 3.8.0)
|
|
150
|
-
rspec-rails (3.8.
|
|
149
|
+
rspec-rails (3.8.2)
|
|
151
150
|
actionpack (>= 3.0)
|
|
152
151
|
activesupport (>= 3.0)
|
|
153
152
|
railties (>= 3.0)
|
|
@@ -156,17 +155,16 @@ GEM
|
|
|
156
155
|
rspec-mocks (~> 3.8.0)
|
|
157
156
|
rspec-support (~> 3.8.0)
|
|
158
157
|
rspec-support (3.8.0)
|
|
159
|
-
rubocop (0.
|
|
158
|
+
rubocop (0.68.1)
|
|
160
159
|
jaro_winkler (~> 1.5.1)
|
|
161
160
|
parallel (~> 1.10)
|
|
162
161
|
parser (>= 2.5, != 2.5.1.1)
|
|
163
|
-
powerpack (~> 0.1)
|
|
164
162
|
rainbow (>= 2.2.2, < 4.0)
|
|
165
163
|
ruby-progressbar (~> 1.7)
|
|
166
|
-
unicode-display_width (
|
|
164
|
+
unicode-display_width (>= 1.4.0, < 1.6)
|
|
167
165
|
ruby-progressbar (1.10.0)
|
|
168
|
-
simplecov (0.
|
|
169
|
-
docile (~> 1.1
|
|
166
|
+
simplecov (0.16.1)
|
|
167
|
+
docile (~> 1.1)
|
|
170
168
|
json (>= 1.8, < 3)
|
|
171
169
|
simplecov-html (~> 0.10.0)
|
|
172
170
|
simplecov-console (0.4.2)
|
|
@@ -181,12 +179,12 @@ GEM
|
|
|
181
179
|
actionpack (>= 4.0)
|
|
182
180
|
activesupport (>= 4.0)
|
|
183
181
|
sprockets (>= 3.0.0)
|
|
184
|
-
sqlite3 (1.
|
|
182
|
+
sqlite3 (1.4.1)
|
|
185
183
|
thor (0.20.3)
|
|
186
184
|
thread_safe (0.3.6)
|
|
187
185
|
tzinfo (1.2.5)
|
|
188
186
|
thread_safe (~> 0.1)
|
|
189
|
-
unicode-display_width (1.
|
|
187
|
+
unicode-display_width (1.5.0)
|
|
190
188
|
url (0.3.2)
|
|
191
189
|
websocket-driver (0.6.5)
|
|
192
190
|
websocket-extensions (>= 0.1.0)
|
data/README.md
CHANGED
|
@@ -108,6 +108,32 @@ For pagination, like filtering, we don't enforce any view-layer specifics. You s
|
|
|
108
108
|
</nav>
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
## Configuration
|
|
112
|
+
|
|
113
|
+
### ActiveSet.configuration.on_asc_sort_nils_come
|
|
114
|
+
|
|
115
|
+
Example usage in an initializer:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
ActiveSet.configure do |c|
|
|
119
|
+
c.on_asc_sort_nils_come = :first
|
|
120
|
+
end
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
When `ActiveSet.configuration.on_asc_sort_nils_come == :last` (this is the default), null values will be sorted as if larger than any non-null value.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
ASC => [-2, -1, 1, 2, nil]
|
|
127
|
+
DESC => [nil, 2, 1, -1, -2]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Otherwise sort nulls as if smaller than any non-null value.
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
ASC => [nil, -2, -1, 1, 2]
|
|
134
|
+
DESC => [2, 1, -1, -2, nil]
|
|
135
|
+
```
|
|
136
|
+
|
|
111
137
|
## Development
|
|
112
138
|
|
|
113
139
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
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.8.
|
|
9
|
+
spec.version = '0.8.2'
|
|
10
10
|
spec.authors = ['Stephen Margheim']
|
|
11
11
|
spec.email = ['stephen.margheim@gmail.com']
|
|
12
12
|
|
data/lib/action_set.rb
CHANGED
|
@@ -53,7 +53,7 @@ module ActionSet
|
|
|
53
53
|
private
|
|
54
54
|
|
|
55
55
|
def filter_instructions_for(set)
|
|
56
|
-
filter_params.
|
|
56
|
+
flatten_keys_of(filter_params).reject { |_, v| v.try(:empty?) }.each_with_object({}) do |(keypath, value), memo|
|
|
57
57
|
typecast_value = if value.respond_to?(:each)
|
|
58
58
|
value.map { |v| filter_typecasted_value_for(keypath, v, set) }
|
|
59
59
|
else
|
data/lib/active_set.rb
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'active_support/core_ext/hash/reverse_merge'
|
|
4
|
-
require '
|
|
5
|
-
require 'helpers/throws'
|
|
4
|
+
require 'helpers/flatten_keys_of'
|
|
6
5
|
require 'active_set/attribute_instruction'
|
|
7
6
|
require 'active_set/filtering/operation'
|
|
8
7
|
require 'active_set/sorting/operation'
|
|
@@ -12,6 +11,30 @@ require 'active_set/exporting/operation'
|
|
|
12
11
|
class ActiveSet
|
|
13
12
|
include Enumerable
|
|
14
13
|
|
|
14
|
+
class Configuration
|
|
15
|
+
attr_accessor :on_asc_sort_nils_come
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@on_asc_sort_nils_come = :last
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
attr_writer :configuration
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.configuration
|
|
27
|
+
@configuration ||= Configuration.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.configure
|
|
31
|
+
yield(configuration)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.reset_configuration
|
|
35
|
+
@configuration = Configuration.new
|
|
36
|
+
end
|
|
37
|
+
|
|
15
38
|
attr_reader :set, :view, :instructions
|
|
16
39
|
|
|
17
40
|
def initialize(set, view: nil, instructions: {})
|
|
@@ -31,23 +31,27 @@ class ActiveSet
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# rubocop:disable Lint/UnderscorePrefixedVariableName
|
|
34
35
|
def arel_column
|
|
35
36
|
_arel_column = arel_table[@attribute_instruction.attribute]
|
|
36
37
|
return _arel_column.lower if case_insensitive_operation?
|
|
37
38
|
|
|
38
39
|
_arel_column
|
|
39
40
|
end
|
|
41
|
+
# rubocop:enable Lint/UnderscorePrefixedVariableName
|
|
40
42
|
|
|
41
43
|
def arel_operator
|
|
42
44
|
@attribute_instruction.operator(default: :eq)
|
|
43
45
|
end
|
|
44
46
|
|
|
47
|
+
# rubocop:disable Lint/UnderscorePrefixedVariableName
|
|
45
48
|
def arel_value
|
|
46
49
|
_arel_value = @attribute_instruction.value
|
|
47
50
|
return _arel_value.downcase if case_insensitive_operation?
|
|
48
51
|
|
|
49
52
|
_arel_value
|
|
50
53
|
end
|
|
54
|
+
# rubocop:enable Lint/UnderscorePrefixedVariableName
|
|
51
55
|
|
|
52
56
|
def case_insensitive_operation?
|
|
53
57
|
@attribute_instruction.case_insensitive? && arel_type.presence_in(%i[string text])
|
|
@@ -10,16 +10,18 @@ class ActiveSet
|
|
|
10
10
|
|
|
11
11
|
def attribute_value_for(item)
|
|
12
12
|
item_value = @attribute_instruction
|
|
13
|
-
|
|
13
|
+
.value_for(item: item)
|
|
14
14
|
item_value = item_value.downcase if case_insensitive_operation_for?(item_value)
|
|
15
15
|
item_value
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# rubocop:disable Lint/UnderscorePrefixedVariableName
|
|
18
19
|
def attribute_value
|
|
19
20
|
_attribute_value = @attribute_instruction.value
|
|
20
21
|
_attribute_value = _attribute_value.downcase if case_insensitive_operation_for?(_attribute_value)
|
|
21
22
|
_attribute_value
|
|
22
23
|
end
|
|
24
|
+
# rubocop:enable Lint/UnderscorePrefixedVariableName
|
|
23
25
|
|
|
24
26
|
def case_insensitive_operation_for?(value)
|
|
25
27
|
return false unless @attribute_instruction.case_insensitive?
|
|
@@ -12,10 +12,8 @@ class ActiveSet
|
|
|
12
12
|
@instructions_hash = instructions_hash
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# rubocop:disable Metrics/MethodLength
|
|
16
15
|
def execute
|
|
17
|
-
attribute_instructions = @instructions_hash
|
|
18
|
-
.flatten_keys
|
|
16
|
+
attribute_instructions = flatten_keys_of(@instructions_hash)
|
|
19
17
|
.map { |k, v| AttributeInstruction.new(k, v) }
|
|
20
18
|
|
|
21
19
|
activerecord_filtered_set = attribute_instructions.reduce(@set) do |set, attribute_instruction|
|
|
@@ -36,7 +34,6 @@ class ActiveSet
|
|
|
36
34
|
maybe_set_or_false
|
|
37
35
|
end
|
|
38
36
|
end
|
|
39
|
-
# rubocop:enable Metrics/MethodLength
|
|
40
37
|
|
|
41
38
|
def operation_instructions
|
|
42
39
|
@instructions_hash.symbolize_keys
|
|
@@ -41,17 +41,21 @@ class ActiveSet
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
# https://stackoverflow.com/a/44912964/2884386
|
|
44
|
-
#
|
|
44
|
+
# When ActiveSet.configuration.on_asc_sort_nils_come == :last
|
|
45
|
+
# null values to be sorted as if larger than any non-null value.
|
|
45
46
|
# ASC => [-2, -1, 1, 2, nil]
|
|
46
47
|
# DESC => [nil, 2, 1, -1, -2]
|
|
48
|
+
# Otherwise sort nulls as if smaller than any non-null value.
|
|
49
|
+
# ASC => [nil, -2, -1, 1, 2]
|
|
50
|
+
# DESC => [2, 1, -1, -2, nil]
|
|
47
51
|
def order_operation_for(set_instruction)
|
|
48
52
|
attribute_model = set_instruction.attribute_model
|
|
49
53
|
|
|
50
54
|
arel_column = set_instruction.arel_column
|
|
51
55
|
arel_direction = direction_operator(set_instruction.value)
|
|
52
|
-
nil_sorter = arel_column.send(arel_direction == :asc ? :eq : :not_eq, nil)
|
|
53
56
|
|
|
54
|
-
attribute_model.order(
|
|
57
|
+
attribute_model.order(nil_sorter_for(arel_column, arel_direction))
|
|
58
|
+
.order(arel_column.send(arel_direction))
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
def direction_operator(direction)
|
|
@@ -59,6 +63,16 @@ class ActiveSet
|
|
|
59
63
|
|
|
60
64
|
:asc
|
|
61
65
|
end
|
|
66
|
+
|
|
67
|
+
def nil_sorter_for(column, direction)
|
|
68
|
+
nil_sorter_operator = if ActiveSet.configuration.on_asc_sort_nils_come == :last
|
|
69
|
+
direction == :asc ? :eq : :not_eq
|
|
70
|
+
else
|
|
71
|
+
direction == :asc ? :not_eq : :eq
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
column.send(nil_sorter_operator, nil)
|
|
75
|
+
end
|
|
62
76
|
end
|
|
63
77
|
end
|
|
64
78
|
end
|
|
@@ -21,11 +21,15 @@ class ActiveSet
|
|
|
21
21
|
value_for_comparison = sortable_numeric_for(set_instruction, item)
|
|
22
22
|
direction_multiplier = direction_multiplier(set_instruction.value)
|
|
23
23
|
|
|
24
|
-
#
|
|
24
|
+
# When ActiveSet.configuration.on_asc_sort_nils_come == :last
|
|
25
|
+
# null values to be sorted as if larger than any non-null value.
|
|
25
26
|
# ASC => [-2, -1, 1, 2, nil]
|
|
26
27
|
# DESC => [nil, 2, 1, -1, -2]
|
|
28
|
+
# Otherwise sort nulls as if smaller than any non-null value.
|
|
29
|
+
# ASC => [nil, -2, -1, 1, 2]
|
|
30
|
+
# DESC => [2, 1, -1, -2, nil]
|
|
27
31
|
if value_for_comparison.nil?
|
|
28
|
-
[direction_multiplier, 0]
|
|
32
|
+
[direction_multiplier * nil_sorter, 0]
|
|
29
33
|
else
|
|
30
34
|
[0, value_for_comparison * direction_multiplier]
|
|
31
35
|
end
|
|
@@ -44,6 +48,12 @@ class ActiveSet
|
|
|
44
48
|
|
|
45
49
|
1
|
|
46
50
|
end
|
|
51
|
+
|
|
52
|
+
def nil_sorter
|
|
53
|
+
return 1 if ActiveSet.configuration.on_asc_sort_nils_come == :last
|
|
54
|
+
|
|
55
|
+
-1
|
|
56
|
+
end
|
|
47
57
|
end
|
|
48
58
|
end
|
|
49
59
|
end
|
|
@@ -13,8 +13,7 @@ class ActiveSet
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def execute
|
|
16
|
-
attribute_instructions = @instructions_hash
|
|
17
|
-
.flatten_keys
|
|
16
|
+
attribute_instructions = flatten_keys_of(@instructions_hash)
|
|
18
17
|
.map { |k, v| AttributeInstruction.new(k, v) }
|
|
19
18
|
|
|
20
19
|
activerecord_strategy = ActiveRecordStrategy.new(@set, attribute_instructions)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/array/wrap'
|
|
4
|
+
|
|
5
|
+
# Returns a flat hash where all nested keys are flattened into an array of keys.
|
|
6
|
+
#
|
|
7
|
+
# hash = { key: 'value', nested: { key: 'nested_value' }, array: [0, 1, 2] }
|
|
8
|
+
# flatten_keys_of(hash)
|
|
9
|
+
# => { [:key]=>"value", [:nested, :key]=>"nested_value", [:array]=>[0, 1, 2] }
|
|
10
|
+
#
|
|
11
|
+
# Can also pass a Proc to change how nested keys are flattened:
|
|
12
|
+
# flatten_keys_of(hash, flattener: ->(*keys) { keys.join('.') })
|
|
13
|
+
# => { "key"=>"value", "nested.key"=>"nested_value", "array"=>[0, 1, 2] }
|
|
14
|
+
# flatten_keys_of(hash, flattener: ->(*keys) { keys.join('-') })
|
|
15
|
+
# => { "key"=>"value", "nested-key"=>"nested_value", "array"=>[0, 1, 2] }
|
|
16
|
+
# flatten_keys_of(hash, flattener: ->(*keys) { keys.map(&:to_s).reduce { |memo, key| memo + "[#{key}]" } })
|
|
17
|
+
# => { "key"=>"value", "nested[key]"=>"nested_value", "array"=>[0, 1, 2] }
|
|
18
|
+
#
|
|
19
|
+
# Can also determine if array values should be flattened as well:
|
|
20
|
+
# hash = { person: { age: '28', siblings: ['Tom', 'Sally'] } }
|
|
21
|
+
# flatten_keys_of(hash, flatten_arrays: true)
|
|
22
|
+
# => { [:key]=>"value", [:nested, :key]=>"nested_value", [:array, 0]=>0, [:array, 1]=>1, [:array, 2]=>2 }
|
|
23
|
+
# flatten_keys_of(hash, flattener: ->(*keys) { keys.join('.') }, flatten_arrays: true)
|
|
24
|
+
# => { "key"=>"value", "nested.key"=>"nested_value", "array.0"=>0, "array.1"=>1, "array.2"=>2 }
|
|
25
|
+
|
|
26
|
+
# refactored from https://stackoverflow.com/a/23861946/2884386
|
|
27
|
+
def flatten_keys_of(input, keys = [], output = {}, flattener: ->(*keys) { keys }, flatten_arrays: false)
|
|
28
|
+
if input.is_a?(Hash)
|
|
29
|
+
input.each do |key, value|
|
|
30
|
+
flatten_keys_of(
|
|
31
|
+
value,
|
|
32
|
+
keys + Array.wrap(key),
|
|
33
|
+
output,
|
|
34
|
+
flattener: flattener,
|
|
35
|
+
flatten_arrays: flatten_arrays
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
elsif input.is_a?(Array) && flatten_arrays
|
|
39
|
+
input.each_with_index do |value, index|
|
|
40
|
+
flatten_keys_of(
|
|
41
|
+
value,
|
|
42
|
+
keys + Array.wrap(index),
|
|
43
|
+
output,
|
|
44
|
+
flattener: flattener,
|
|
45
|
+
flatten_arrays: flatten_arrays
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
return output.merge!(flattener.call(*keys) => input)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
output
|
|
53
|
+
end
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# transform_to_sortable_numeric(Date.new(2000, 12, 25))
|
|
14
14
|
# => 977720400000
|
|
15
15
|
|
|
16
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/
|
|
16
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
17
17
|
def transform_to_sortable_numeric(value)
|
|
18
18
|
# https://www.justinweiss.com/articles/4-simple-memoization-patterns-in-ruby-and-one-gem/#and-what-about-parameters
|
|
19
19
|
@sortable_numeric ||= Hash.new do |h, key|
|
|
@@ -35,8 +35,7 @@ def transform_to_sortable_numeric(value)
|
|
|
35
35
|
end
|
|
36
36
|
@sortable_numeric[value]
|
|
37
37
|
end
|
|
38
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/
|
|
39
|
-
# rubocop:enable Style/AsciiComments
|
|
38
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
40
39
|
|
|
41
40
|
def string_to_sortable_numeric(string)
|
|
42
41
|
string # 'aB09ü'
|
|
@@ -46,6 +45,7 @@ def string_to_sortable_numeric(string)
|
|
|
46
45
|
.reduce(&:concat) # "097.066048057252"
|
|
47
46
|
.to_r # (24266512014313/250000000000)
|
|
48
47
|
end
|
|
48
|
+
# rubocop:enable Style/AsciiComments
|
|
49
49
|
|
|
50
50
|
def time_to_sortable_numeric(time)
|
|
51
51
|
# https://stackoverflow.com/a/30604935/2884386
|
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.8.
|
|
4
|
+
version: 0.8.2
|
|
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-06-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -295,9 +295,8 @@ files:
|
|
|
295
295
|
- lib/active_set/sorting/active_record_strategy.rb
|
|
296
296
|
- lib/active_set/sorting/enumerable_strategy.rb
|
|
297
297
|
- lib/active_set/sorting/operation.rb
|
|
298
|
-
- lib/helpers/
|
|
298
|
+
- lib/helpers/flatten_keys_of.rb
|
|
299
299
|
- lib/helpers/transform_to_sortable_numeric.rb
|
|
300
|
-
- lib/patches/core_ext/hash/flatten_keys.rb
|
|
301
300
|
- pagination.png
|
|
302
301
|
homepage: https://github.com/fractaledmind/actionset
|
|
303
302
|
licenses:
|
data/lib/helpers/throws.rb
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Returns a Boolean for whether the block raises the Exception expected
|
|
4
|
-
#
|
|
5
|
-
# throws?(StandardError) { raise }
|
|
6
|
-
# => true
|
|
7
|
-
# throws?(NameError) { raise NameError }
|
|
8
|
-
# => true
|
|
9
|
-
# throws?(NoMethodError) { raise NameError }
|
|
10
|
-
# => false
|
|
11
|
-
# throws?(StandardError) { 'foo' }
|
|
12
|
-
# => false
|
|
13
|
-
|
|
14
|
-
def throws?(exception)
|
|
15
|
-
yield
|
|
16
|
-
false
|
|
17
|
-
rescue StandardError => e
|
|
18
|
-
e.is_a? exception
|
|
19
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'active_support/core_ext/array/wrap'
|
|
4
|
-
|
|
5
|
-
class Hash
|
|
6
|
-
# Returns a flat hash where all nested keys are collapsed into an array of keys.
|
|
7
|
-
#
|
|
8
|
-
# hash = { person: { name: { first: 'Rob' }, age: '28' } }
|
|
9
|
-
# hash.flatten_keys_to_array
|
|
10
|
-
# => {[:person, :name, :first] => "Rob", [:person, :age]=>"28" }
|
|
11
|
-
def flatten_keys_to_array
|
|
12
|
-
_flatten_keys(self)
|
|
13
|
-
end
|
|
14
|
-
alias flatten_keys flatten_keys_to_array
|
|
15
|
-
|
|
16
|
-
# Returns a flat hash where all nested keys are collapsed into a dot-separated string of keys.
|
|
17
|
-
#
|
|
18
|
-
# hash = { person: { name: { first: 'Rob' }, age: '28' } }
|
|
19
|
-
# hash.flatten_keys_to_dotpath
|
|
20
|
-
# => { 'person.name.first' => "Rob", 'person.age'=>"28" }
|
|
21
|
-
def flatten_keys_to_dotpath
|
|
22
|
-
_flatten_keys(self, ->(*keys) { keys.join('.') })
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Returns a flat hash where all nested keys are collapsed into a dast-separated string of keys.
|
|
26
|
-
#
|
|
27
|
-
# hash = { person: { name: { first: 'Rob' }, age: '28' } }
|
|
28
|
-
# hash.flatten_keys_to_html_attribute
|
|
29
|
-
# => { 'person-name-first' => "Rob", 'person-age'=>"28" }
|
|
30
|
-
def flatten_keys_to_html_attribute
|
|
31
|
-
_flatten_keys(self, ->(*keys) { keys.join('-') })
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Returns a flat hash where all nested keys are collapsed into a string of keys
|
|
35
|
-
# fitting the Rails request param pattern.
|
|
36
|
-
#
|
|
37
|
-
# hash = { person: { name: { first: 'Rob' }, age: '28' } }
|
|
38
|
-
# hash.flatten_keys_to_rails_param
|
|
39
|
-
# => { 'person[name][first]' => "Rob", 'person[age]'=>"28" }
|
|
40
|
-
def flatten_keys_to_rails_param
|
|
41
|
-
_flatten_keys(self, ->(*keys) { keys.map(&:to_s).reduce { |memo, key| memo + "[#{key}]" } })
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
# refactored from https://stackoverflow.com/a/23861946/2884386
|
|
47
|
-
def _flatten_keys(input, keypath_gen = ->(*keys) { keys }, keys = [], output = {})
|
|
48
|
-
if input.is_a? Hash
|
|
49
|
-
input.each { |k, v| _flatten_keys(v, keypath_gen, keys + Array(k), output) }
|
|
50
|
-
# elsif input.is_a? Array
|
|
51
|
-
# input.each_with_index { |v, i| _flatten_keys(v, keypath_gen, keys + Array(i), output) }
|
|
52
|
-
else
|
|
53
|
-
return output.merge!(keypath_gen.call(*keys) => input)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
output
|
|
57
|
-
end
|
|
58
|
-
end
|