actionset 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d8befffeaabf8526df7cdd15f501f2e9e99732b
4
- data.tar.gz: 9dc431b8b1af60ed1d6d57d8ba9e66ac6e6a0688
3
+ metadata.gz: dffd9096dbb67844e4cee728dee2dbea434a32e2
4
+ data.tar.gz: a6bcb0b49df525a8d3e6e74f06a067310674fd87
5
5
  SHA512:
6
- metadata.gz: 076e19f851950c2a0c2ac377c51a04eaf16198060ce8a1e6a766339f659c61eab1ae125c34005db809b27b0b0c02aad85e9ad1502f13c11ad04a821be6d40d0d
7
- data.tar.gz: c2de49f8fb3a36ecf86dfc00c51a880b675ea8f7db129de84a0eace0565c9175528cd53fcf25096ff04151c727189ce09c53ac54b50221df20784e8ba8e52270
6
+ metadata.gz: f7400ae1649bde246c247752feabb37adc3c290ce464aee8271980c265965be920ac2cdae6c06b009fbbc59df371ec7e1aad075f4035a2e624ead3f6254e0011
7
+ data.tar.gz: ca373cfc982b4455ec40c5df823c94ccc4a16e0c2fc1fafeb2eb3cc9ea65b8be664df7135d6ef52c9e1b5cca12e959ae23eda6815809cb62e93baae0acec5d96
@@ -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
@@ -1,3 +1,5 @@
1
+ v 0.8.2
2
+ - add `ActiveSet.configuration.on_asc_sort_nils_come` configuration
1
3
  v 0.8.1
2
4
  - add a case-insensitive filtering/sorting option (`/i/`)
3
5
  - standardize how filtering by class scopes behaves
@@ -1,57 +1,57 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- actionset (0.8.1)
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.6.1)
12
- actionpack (= 5.1.6.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.6.1)
16
- actionpack (= 5.1.6.1)
17
- actionview (= 5.1.6.1)
18
- activejob (= 5.1.6.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.6.1)
22
- actionview (= 5.1.6.1)
23
- activesupport (= 5.1.6.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.6.1)
29
- activesupport (= 5.1.6.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.6.1)
35
- activesupport (= 5.1.6.1)
34
+ activejob (5.1.7)
35
+ activesupport (= 5.1.7)
36
36
  globalid (>= 0.3.6)
37
- activemodel (5.1.6.1)
38
- activesupport (= 5.1.6.1)
39
- activerecord (5.1.6.1)
40
- activemodel (= 5.1.6.1)
41
- activesupport (= 5.1.6.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.6.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.5.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.12.0)
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 (0.7.0)
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.4)
70
+ concurrent-ruby (1.1.5)
71
71
  crass (1.0.4)
72
- database_cleaner (1.6.2)
72
+ database_cleaner (1.7.0)
73
73
  diff-lcs (1.3)
74
- docile (1.1.5)
75
- erubi (1.7.1)
76
- factory_bot (4.8.2)
77
- activesupport (>= 3.0.0)
78
- faker (1.8.7)
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.1)
80
+ globalid (0.4.2)
81
81
  activesupport (>= 4.2.0)
82
82
  hirb (0.7.3)
83
- i18n (1.2.0)
83
+ i18n (1.6.0)
84
84
  concurrent-ruby (~> 1.0)
85
- jaro_winkler (1.5.1)
86
- json (2.1.0)
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.9.0)
97
+ nokogiri (1.10.3)
98
98
  mini_portile2 (~> 2.4.0)
99
- parallel (1.12.1)
100
- parser (2.5.3.0)
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.6)
103
+ rack (2.0.7)
105
104
  rack-test (1.1.0)
106
105
  rack (>= 1.0, < 3)
107
- rails (5.1.6.1)
108
- actioncable (= 5.1.6.1)
109
- actionmailer (= 5.1.6.1)
110
- actionpack (= 5.1.6.1)
111
- actionview (= 5.1.6.1)
112
- activejob (= 5.1.6.1)
113
- activemodel (= 5.1.6.1)
114
- activerecord (= 5.1.6.1)
115
- activesupport (= 5.1.6.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.6.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.6.1)
125
- actionpack (= 5.1.6.1)
126
- activesupport (= 5.1.6.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 (10.5.0)
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.3.0)
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.2)
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.1)
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.61.1)
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 (~> 1.4.0)
164
+ unicode-display_width (>= 1.4.0, < 1.6)
167
165
  ruby-progressbar (1.10.0)
168
- simplecov (0.15.1)
169
- docile (~> 1.1.0)
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.3.13)
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.4.0)
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.
@@ -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.1'
9
+ spec.version = '0.8.2'
10
10
  spec.authors = ['Stephen Margheim']
11
11
  spec.email = ['stephen.margheim@gmail.com']
12
12
 
@@ -53,7 +53,7 @@ module ActionSet
53
53
  private
54
54
 
55
55
  def filter_instructions_for(set)
56
- filter_params.flatten_keys.reject { |_, v| v.try(:empty?) }.each_with_object({}) do |(keypath, value), memo|
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
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/core_ext/hash/reverse_merge'
4
- require 'patches/core_ext/hash/flatten_keys'
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])
@@ -92,7 +92,7 @@ class ActiveSet
92
92
  private
93
93
 
94
94
  def operator_regex
95
- %r{\((.*?)\)}
95
+ /\((.*?)\)/
96
96
  end
97
97
 
98
98
  def options_regex
@@ -10,16 +10,18 @@ class ActiveSet
10
10
 
11
11
  def attribute_value_for(item)
12
12
  item_value = @attribute_instruction
13
- .value_for(item: item)
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
- # Force null values to be sorted as if larger than any non-null value
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(nil_sorter).order(arel_column.send(arel_direction))
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
- # Force null values to be sorted as if larger than any non-null value
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/MethodLength, Metrics/PerceivedComplexity
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/MethodLength, Metrics/PerceivedComplexity
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.1
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-05-11 00:00:00.000000000 Z
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/throws.rb
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:
@@ -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