ruby_extended 1.1.0

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.
@@ -0,0 +1,5 @@
1
+ require 'ruby_extended/array'
2
+ require 'ruby_extended/hash'
3
+ require 'ruby_extended/number'
4
+ require 'ruby_extended/object'
5
+ require 'ruby_extended/string'
@@ -0,0 +1,197 @@
1
+ class Array
2
+ require 'unicode_utils'
3
+
4
+
5
+ def sort_lv(&block)
6
+ lang = [
7
+ %w[e ē], %w[u ū], %w[i ī], %w[a ā], %w[s š], %w[g ģ], %w[k ķ], %w[l ļ],
8
+ %w[z ž], %w[c č], %w[n ņ], %w[E Ē], %w[U Ū], %w[I I], %w[A Ā], %w[S Š],
9
+ %w[G Ģ], %w[K Ķ], %w[L Ļ], %w[Z Ž], %w[C Č], %w[N Ņ]
10
+ ]
11
+
12
+ self.sort_by do |item|
13
+ value = (block_given? ? yield(item) : item)
14
+ lang.each do |en, lv|
15
+ value = UnicodeUtils.downcase(value).gsub(lv, en)
16
+ end
17
+ value
18
+ end
19
+ end
20
+
21
+
22
+ def tabulate(num, o = {})
23
+ return nil if num < 1
24
+ return self if num == 1
25
+
26
+ split_by = o.has_key?(:split_by) ? o[:split_by] : :columns
27
+ direction = o.has_key?(:direction) ? o[:direction] : :horizontal
28
+ arr, res = self, []
29
+ i = (arr.length / num.to_f).round
30
+
31
+ if direction == :horizontal
32
+ ii = 0
33
+
34
+ if split_by == :columns
35
+ rows = ( (i == 1) ? [0] : (0..i - 1).to_a )
36
+ cells = ( (num == 1) ? [0] : (0..num - 1).to_a )
37
+ end
38
+
39
+ if split_by == :rows
40
+ rows = ( (num == 1) ? [0] : (0..num - 1).to_a )
41
+ cells = ( (i == 1) ? [0] : (0..i - 1).to_a )
42
+ end
43
+
44
+ rows.each do |row_i|
45
+ cells.each do |cell_i|
46
+ res[row_i] = [] if res[row_i].nil?
47
+ res[row_i][cell_i] = arr[ii]
48
+ ii = ii + 1
49
+ end
50
+ end
51
+
52
+ res
53
+ end
54
+
55
+ if direction == :vertical
56
+
57
+ if split_by == :columns
58
+ (0..(i - 1)).to_a.each do |r_i|
59
+ res[r_i] = (r_i).step(arr.size * 5, i).to_a.slice(0, num).map {|ii| arr[ii]}
60
+ end
61
+ end
62
+
63
+ if split_by == :rows
64
+ (0..(num - 1)).to_a.each do |r_i|
65
+ res[r_i] = (r_i).step(arr.length * 5, num).to_a.slice(0, i).map {|ii| arr[ii]}
66
+ end
67
+ end
68
+ end
69
+
70
+ res
71
+ end
72
+
73
+
74
+ def sample_index(count=1, offset: nil)
75
+ indexes = (0..(self.length - 1)).to_a
76
+ indexes = indexes.slice(offset + 1, indexes.length) if offset
77
+ return nil if indexes.nil?
78
+ result = indexes.sample(count)
79
+ result = result.first if count == 1
80
+ result
81
+ end
82
+
83
+
84
+ def uniq_by_key(key)
85
+ keys, res, found = [], [], false
86
+ self.each do |item|
87
+ found = true if item.has_key?(key)
88
+ next if keys.include?(item[key])
89
+ res << item
90
+ keys << item[key]
91
+ end
92
+ found ? res : self
93
+ end
94
+
95
+
96
+ def duplicates(options={})
97
+ self.select {|e| self.count(e) > 1}.uniq.map do |v|
98
+ options[:full] ? { item: v, count: self.count(v) } : v
99
+ end
100
+ end
101
+
102
+
103
+ def sum(o={})
104
+ res, str = [], false
105
+
106
+ self.map do |e|
107
+ ce = o[:key] ? e[o[:key]] : e
108
+ if o[:only_numbers]
109
+ res << ce if ce.is_a?(Numeric)
110
+ else
111
+ str = true if ce.kind_of?(String)
112
+ res << ce unless ce.nil?
113
+ end
114
+ end
115
+
116
+ str ? res.join('') : res.reduce(:+)
117
+ end
118
+
119
+
120
+ def to_tree(o = {})
121
+ o[:id_key] = :id unless o.has_key?(:id_key)
122
+ o[:parent_id_key] = :parent_id unless o.has_key?(:parent_id_key)
123
+ o[:children_key] = :children unless o.has_key?(:children_key)
124
+ o[:include_id] = true unless o.has_key?(:include_id)
125
+ o[:include_parent_id] = false unless o.has_key?(:include_parent_id)
126
+ o[:include_empty_children] = false unless o.has_key?(:include_empty_children)
127
+ o[:to_hash] = false unless o.has_key?(:to_hash)
128
+
129
+ res = Array.to_tree_children_loop(nil, self, o)
130
+ res = Array.to_tree_hash_loop(res, o) if o[:to_hash]
131
+ res
132
+ end
133
+
134
+
135
+ private
136
+
137
+
138
+ def self.to_tree_children_loop(parent_id, items, o)
139
+ res = []
140
+
141
+ items.select do |n|
142
+ (parent_id ? n[o[:parent_id_key]] == parent_id : n[o[:parent_id_key]].nil?)
143
+ end.each do |item|
144
+ new_item = {}
145
+
146
+ new_item[o[:id_key]] = item[o[:id_key]] if o[:to_hash] || o[:include_id]
147
+ new_item[o[:parent_id_key]] = parent_id if o[:to_hash] || o[:include_parent_id]
148
+
149
+ item.keys.reject do |key|
150
+ key == o[:id_key] || key == o[:parent_id_key]
151
+ end.each do |key|
152
+ new_item[key] = item[key]
153
+ end
154
+
155
+ children = Array.to_tree_children_loop(item[o[:id_key]], items, o)
156
+
157
+ if children.any? || (children.length == 0 && o[:include_empty_children])
158
+ new_item[o[:children_key]] = children
159
+ end
160
+
161
+ res << new_item
162
+ end
163
+
164
+ res
165
+ end
166
+
167
+
168
+ def self.to_tree_hash_loop(items, o)
169
+ res = {}
170
+
171
+ items.each do |item|
172
+ res = {}
173
+ id = item[o[:id_key]]
174
+ res[id] = {}
175
+
176
+
177
+ res[id][o[:id_key]] = item[o[:id_key]] if o[:include_id]
178
+ res[id][o[:parent_id_key]] = item[o[:parent_id_key]] if o[:include_parent_id]
179
+
180
+ item.keys.reject do |key|
181
+ (key == o[:id_key] || key == o[:parent_id_key])
182
+ end.each do |key|
183
+ res[id][key] = item[key]
184
+ end
185
+
186
+ if item[o[:children_key]]
187
+ res[id][o[:children_key]] = Array.to_tree_hash_loop(item[o[:children_key]], o)
188
+ else
189
+ res[id][o[:children_key]] = [] if o[:include_empty_children]
190
+ end
191
+ end
192
+
193
+ res
194
+ end
195
+
196
+
197
+ end
@@ -0,0 +1,30 @@
1
+ class Hash
2
+
3
+
4
+ def deep_find(find_key)
5
+ Hash.deep_find_loop(self, [], find_key).flatten
6
+ end
7
+
8
+
9
+ private
10
+
11
+
12
+ def self.deep_find_loop(object, path, find_key)
13
+ found = []
14
+
15
+ if object.is_a?(Array)
16
+ object.each_with_index do |sub_object, index|
17
+ found << Hash.deep_find_loop(sub_object, path + [index], find_key)
18
+ end
19
+ elsif object.is_a?(Hash)
20
+ object.keys.each do |key|
21
+ found << {value: object[key], path: path + [key]} if key == find_key
22
+ found << Hash.deep_find_loop(object[key], path + [key], find_key)
23
+ end
24
+ end
25
+
26
+ found
27
+ end
28
+
29
+
30
+ end
@@ -0,0 +1,54 @@
1
+ module NumericRubyExtended
2
+
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+
7
+ def no_zeros
8
+ self.to_f.to_s.gsub(/\.0+$/, '')
9
+ end
10
+
11
+ def to_money
12
+ '%.2f' % self.to_f
13
+ end
14
+
15
+ def percent_of(percent, options = {})
16
+ result = (self.to_f / 100.to_f * percent.to_f)
17
+ options[:decimal] ? result : result.to_i
18
+ end
19
+
20
+ def get_percent_from(value, options = {})
21
+ result = (self.to_f / value.to_f * 100)
22
+ options[:decimal] ? result : result.to_i
23
+ end
24
+
25
+ end
26
+ end
27
+
28
+
29
+ end
30
+
31
+
32
+ class Integer
33
+ include NumericRubyExtended
34
+ end
35
+
36
+
37
+ class Float
38
+ include NumericRubyExtended
39
+ end
40
+
41
+
42
+ class BigDecimal
43
+ include NumericRubyExtended
44
+ end
45
+
46
+
47
+ class Complex
48
+ include NumericRubyExtended
49
+ end
50
+
51
+
52
+ class Rational
53
+ include NumericRubyExtended
54
+ end
@@ -0,0 +1,32 @@
1
+ module ObjectRubyExtended
2
+
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+
7
+ def dig(*items)
8
+ res, all_items = self, items.flatten
9
+
10
+ all_items.each do |item|
11
+ next if res.nil?
12
+ res = res[item] rescue nil
13
+ end
14
+
15
+ res
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+
22
+ end
23
+
24
+
25
+ class Array
26
+ include ObjectRubyExtended
27
+ end
28
+
29
+
30
+ class Hash
31
+ include ObjectRubyExtended
32
+ end
@@ -0,0 +1,59 @@
1
+ class String
2
+
3
+
4
+ def strip_whitespace(options = {})
5
+ res = self
6
+ res = res.gsub("\u0020", '') # SPACE (U+0020)
7
+ res = res.gsub("\u00A0", '') # NO-BREAK SPACE (U+00A0)
8
+ res = res.gsub("\u1680", '') # OGHAM SPACE MARK (U+1680)
9
+ res = res.gsub("\u180E", '') # MONGOLIAN VOWEL SEPARATOR (U+180E)
10
+ res = res.gsub("\u2000", '') # EN QUAD (U+2000)
11
+ res = res.gsub("\u2002", '') # EN SPACE (nut) (U+2002)
12
+ res = res.gsub("\u2003", '') # EM SPACE (mutton) (U+2003)
13
+ res = res.gsub("\u2004", '') # THREE-PER-EM SPACE (thick space) (U+2004)
14
+ res = res.gsub("\u2005", '') # FOUR-PER-EM SPACE (mid space) (U+2005)
15
+ res = res.gsub("\u2006", '') # SIX-PER-EM SPACE (U+2006)
16
+ res = res.gsub("\u2007", '') # FIGURE SPACE (U+2007)
17
+ res = res.gsub("\u2008", '') # PUNCTUATION SPACE (U+2008)
18
+ res = res.gsub("\u2009", '') # THIN SPACE (U+2009)
19
+ res = res.gsub("\u200A", '') # HAIR SPACE (U+200A)
20
+ res = res.gsub("\u200B", '') # ZERO WIDTH SPACE (U+200B)
21
+ res = res.gsub("\u200F", '') # NARROW NO-BREAK SPACE (U+200F)
22
+ res = res.gsub("\u205F", '') # MEDIUM MATHEMATICAL SPACE (U+205F)
23
+ res = res.gsub("\u3000", '') # IDEOGRAPHIC SPACE (U+3000)
24
+ res = res.gsub("\uFEFF", '') # ZERO WIDTH NO-BREAK SPACE (U+FEFF)
25
+
26
+ if options[:visible]
27
+ res = res.gsub("\u2423", '') # OPEN BOX (U+2423)
28
+ res = res.gsub("\u2422", '') # BLANK SYMBOL (U+2422)
29
+ res = res.gsub("\u2420", '') # SYMBOL FOR SPACE (U+2420)
30
+ end
31
+
32
+ res
33
+ end
34
+
35
+
36
+ def strip_newline
37
+ res = self
38
+ res = res.delete("\n")
39
+ res = res.delete("\r")
40
+ res
41
+ end
42
+
43
+
44
+ def index_of(str)
45
+ res = []
46
+ self.scan(/#{str}/) { |c| res << [c, $~.offset(0)[0]] }
47
+ res.map(&:last)
48
+ end
49
+
50
+
51
+ def similarity_percent(b)
52
+ longer = [self.size, b.size].max
53
+ same = self.each_char.zip(b.each_char).select { |a, b| a == b }.size
54
+ percent = (longer - same) / self.size.to_f
55
+ (100.0 - (percent * 100.to_f))
56
+ end
57
+
58
+
59
+ end
@@ -0,0 +1,3 @@
1
+ module RubyExtended
2
+ VERSION = '1.1.0'
3
+ end
@@ -0,0 +1,24 @@
1
+ # Ruby Extended
2
+
3
+ ![Ruby Version](badges/ruby.svg)
4
+ ![Coverage](badges/coverage.svg)
5
+ [![License](badges/license.svg)](https://creativecommons.org/licenses/by/4.0/)
6
+
7
+ Extends ruby classes with useful methods.
8
+
9
+ ## Documentation
10
+ Documentation [available here](/documentation/readme.md).
11
+
12
+ ## Installation
13
+ Add this line to your application's Gemfile:
14
+ ```ruby
15
+ gem 'ruby_extended'
16
+ ```
17
+ And then execute:
18
+ ```bash
19
+ bundle
20
+ ```
21
+ Or install it yourself as:
22
+ ```bash
23
+ gem install ruby_extended
24
+ ```
@@ -0,0 +1,24 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'ruby_extended/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'ruby_extended'
6
+ s.version = RubyExtended::VERSION.dup
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = 'EdCordata'
9
+ s.description = 'Extend Ruby classes with helpful methods'
10
+ s.summary = 'Extend Ruby classes with helpful methods'
11
+ s.licenses = ['CC BY 4.0']
12
+ s.files = `git ls-files`.split("\n")
13
+ s.homepage = 'https://github.com/EdCordata-Ruby-Gems/ruby_extended'
14
+ s.metadata = {
15
+ 'documentation_uri' => 'https://github.com/EdCordata-Ruby-Gems/ruby_extended/blob/master/documentation/readme.md',
16
+ 'source_code_uri' => 'https://github.com/EdCordata-Ruby-Gems/ruby_extended',
17
+ 'bug_tracker_uri' => 'https://github.com/EdCordata-Ruby-Gems/ruby_extended/issues',
18
+ 'wiki_uri' => 'https://github.com/EdCordata-Ruby-Gems/ruby_extended/blob/master/documentation/readme.md',
19
+ }
20
+ s.require_paths = ['lib']
21
+ s.required_ruby_version = '>= 1.9.3'
22
+ s.rubygems_version = '1.6.2'
23
+ s.add_runtime_dependency 'unicode_utils', '~> 1'
24
+ end
@@ -0,0 +1,412 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+ require_relative '../lib/ruby_extended.rb'
5
+
6
+ RSpec.describe Array do
7
+
8
+
9
+ describe 'Array.sort_lv' do
10
+
11
+ it 'Should order Latvian special characters in array' do
12
+ expect(%w[a b ā c z č].sort_lv).to eql(%w[a ā b c č z])
13
+ end
14
+
15
+ it 'Should order Latvian characters in array while accepting block' do
16
+ input_arr = [ { n: 'a' }, { n: 'c' }, { n: 'ā' }, { n: 'č' }, { n: 'z' } ]
17
+ output_arr = [ { n: 'a' }, { n: 'ā' }, { n: 'c' }, { n: 'č' }, { n: 'z' } ]
18
+
19
+ expect(input_arr.sort_lv { |x| x[:n] }).to eql(output_arr)
20
+ end
21
+
22
+ end
23
+
24
+
25
+ describe 'Array.tabulate' do
26
+ input_arr = %w[a b c d e f g]
27
+
28
+ it 'Should return nil if tabulate value is 0' do
29
+ expect(input_arr.tabulate(0, split_by: :columns, direction: :horizontal)).to(be_nil)
30
+ expect(input_arr.tabulate(0, split_by: :rows, direction: :horizontal)).to(be_nil)
31
+ expect(input_arr.tabulate(0, split_by: :columns, direction: :vertical)).to(be_nil)
32
+ expect(input_arr.tabulate(0, split_by: :rows, direction: :vertical)).to(be_nil)
33
+ end
34
+
35
+ it 'Should return nil if tabulate value is bellow 1' do
36
+ expect(input_arr.tabulate(-1, split_by: :columns, direction: :horizontal)).to(be_nil)
37
+ expect(input_arr.tabulate(-1, split_by: :rows, direction: :horizontal)).to(be_nil)
38
+ expect(input_arr.tabulate(-1, split_by: :columns, direction: :vertical)).to(be_nil)
39
+ expect(input_arr.tabulate(-1, split_by: :rows, direction: :vertical)).to(be_nil)
40
+ end
41
+
42
+ it 'Should convert array to horizontal columns with value 2 (less as input)' do
43
+ output = [ ['a','b'], ['c','d'], ['e','f'], ['g',nil] ]
44
+ expect(input_arr.tabulate(2, split_by: :columns, direction: :horizontal)).to eql(output)
45
+ end
46
+
47
+ it 'Should convert array to horizontal rows with value 2 (less as input)' do
48
+ output = [ ['a','b','c','d'], ['e','f','g',nil] ]
49
+ expect(input_arr.tabulate(2, split_by: :rows, direction: :horizontal)).to eql(output)
50
+ end
51
+
52
+ it 'Should convert array to vertical columns with value 2 (less as input)' do
53
+ output = [ ['a','e'], ['b','f'], ['c','g'], ['d',nil] ]
54
+ expect(input_arr.tabulate(2, split_by: :columns, direction: :vertical)).to eql(output)
55
+ end
56
+
57
+ it 'Should convert array to vertical rows with value 2 (less as input)' do
58
+ output = [ ['a','c','e','g'], ['b','d','f',nil] ]
59
+ expect(input_arr.tabulate(2, split_by: :rows, direction: :vertical)).to eql(output)
60
+ end
61
+
62
+ it 'Should convert array to horizontal columns with value 7 (same as input)' do
63
+ output = [ ['a','b','c','d','e','f','g'] ]
64
+ expect(input_arr.tabulate(7, split_by: :columns, direction: :horizontal)).to eql(output)
65
+ end
66
+
67
+ it 'Should convert array to horizontal rows with value 7 (same as input)' do
68
+ output = [ ['a'],['b'],['c'],['d'],['e'],['f'],['g'] ]
69
+ expect(input_arr.tabulate(7, split_by: :rows, direction: :horizontal)).to eql(output)
70
+ end
71
+
72
+ it 'Should convert array to horizontal columns with value 8 (more than input)' do
73
+ output = [ ['a','b','c','d','e','f','g', nil] ]
74
+ expect(input_arr.tabulate(8, split_by: :columns, direction: :horizontal)).to eql(output)
75
+ end
76
+
77
+ it 'Should convert array to horizontal rows with value 8 (more than input)' do
78
+ output = [ ['a'],['b'],['c'],['d'],['e'],['f'],['g'], [nil] ]
79
+ expect(input_arr.tabulate(8, split_by: :rows, direction: :horizontal)).to eql(output)
80
+ end
81
+
82
+ it 'Should convert array to horizontal columns with value 9 (more than input)' do
83
+ output = [ ['a','b','c','d','e','f','g', nil, nil] ]
84
+ expect(input_arr.tabulate(9, split_by: :columns, direction: :horizontal)).to eql(output)
85
+ end
86
+
87
+ it 'Should convert array to horizontal rows with value 9 (more than input)' do
88
+ output = [ ['a'],['b'],['c'],['d'],['e'],['f'],['g'], [nil], [nil] ]
89
+ expect(input_arr.tabulate(9, split_by: :rows, direction: :horizontal)).to eql(output)
90
+ end
91
+
92
+ end
93
+
94
+
95
+ describe 'Array.sample_index' do
96
+ input_arr = %w[a b c d e f g h]
97
+
98
+ it 'Should return random index' do
99
+ expect(input_arr.sample_index(1).class).to be(Integer)
100
+
101
+ expect(input_arr.sample_index(1)).to(
102
+ be_between(0, (input_arr.length - 1))
103
+ )
104
+ end
105
+
106
+ it 'Should return two random indexes' do
107
+ expect(input_arr.sample_index(2)).to be_instance_of(Array)
108
+
109
+ expect(input_arr.sample_index(2).length).to be(2)
110
+ end
111
+
112
+ end
113
+
114
+
115
+ describe 'Array.uniq_by_key' do
116
+ input_arr = [ {a: 1, b: '123'}, {a: 1, b: 'abc'}, {a: 2, b: 'cde'} ]
117
+ output_arr = [ {a: 1, b: '123'}, {a: 2, b: 'cde'} ]
118
+
119
+ it 'Should return uniq hash items' do
120
+ expect(input_arr.uniq_by_key(:a)).to eql(output_arr)
121
+ end
122
+
123
+ it 'Should return original array if key not found' do
124
+ expect(input_arr.uniq_by_key(:x)).to eql(input_arr)
125
+ end
126
+
127
+ end
128
+
129
+
130
+ describe 'Array.duplicates' do
131
+ input_arr = ['a', 'a', 2, 2, 2, 2, 3, 'b', nil, nil]
132
+
133
+ it 'Should return array of duplicates' do
134
+ expect(input_arr.duplicates).to eql(['a', 2, nil])
135
+ end
136
+
137
+ it 'Should return hash with duplicates and count' do
138
+ expect(input_arr.duplicates(full: true)).to(
139
+ eql([
140
+ {item: 'a', count: 2},
141
+ {item: 2, count: 4},
142
+ {item: nil, count: 2}
143
+ ])
144
+ )
145
+ end
146
+
147
+ end
148
+
149
+
150
+ describe 'Array.sum' do
151
+
152
+ it 'Should sum array of integers' do
153
+ expect( [1, 2, 3].sum ).to eq(6)
154
+ end
155
+
156
+ it 'Should sum array if floats' do
157
+ expect( [0.1, 0.2, 1].sum ).to eq(1.3)
158
+ end
159
+
160
+ it 'Should sum array of integers, floats and strings' do
161
+ expect( %w[a b].sum ).to eq('ab')
162
+ expect( ['a', 'b', 2, 1.5, nil].sum ).to eq('ab21.5')
163
+ end
164
+
165
+ it 'Should sum hash by keys' do
166
+ expect( [ {a: 1}, {a: 'a'}, {a: 3} ].sum(key: :a) ).to eq('1a3')
167
+ expect( [ {a: 1}, {a: 'a'}, {b: 3} ].sum(key: :a) ).to eq('1a')
168
+ expect( [ {a: 1}, {a: 2}, {b: 3} ].sum(key: :a) ).to eq(3)
169
+ end
170
+
171
+ it 'Should recognize Float, Integer, BigDecimal, Complex and Rational' do
172
+ expect( [BigDecimal(1), Float(1), Integer(1), Complex(1), Rational(1)].sum ).to eq(5)
173
+ expect( [BigDecimal(1), Float(1), Integer(1), Complex(1), Rational(1), 'a'].sum(only_numbers: true) ).to eq(5)
174
+ end
175
+
176
+ it 'Should sum only the number values if :only_numbers option provided' do
177
+ expect( ['a', 'b', 5].sum(only_numbers: true) ).to eq(5)
178
+ expect( ['a', 1, 1.5, nil].sum(only_numbers: true) ).to eq(2.5)
179
+ end
180
+
181
+ it 'Should sum hash by keys, but only the number values if :only_numbers option provided' do
182
+ expect( [ {a: 1}, {a: 'a'}, {a: 3} ].sum(key: :a, only_numbers: true) ).to eq(4)
183
+ expect( [ {a: 1}, {a: 'a'}, {b: 3} ].sum(key: :a, only_numbers: true) ).to eq(1)
184
+ end
185
+
186
+ it 'Should return nil if hash key was not found' do
187
+ expect( [ {a: 1}, {a: 'a'}, {a: 3} ].sum(key: :b) ).to be_nil
188
+ end
189
+
190
+ end
191
+
192
+
193
+ describe 'Array.to_tree' do
194
+ input_arr = [
195
+ {id: 1, parent_id: nil, any_other_key: 'a'},
196
+ {id: 2, parent_id: 1, any_other_key: 'b'},
197
+ {id: 3, parent_id: 2, any_other_key: 'c'},
198
+ ]
199
+
200
+ input_arr_custom_keys = [
201
+ {id_x: 1, parent_id_x: nil, any_other_key: 'a'},
202
+ {id_x: 2, parent_id_x: 1, any_other_key: 'b'},
203
+ {id_x: 3, parent_id_x: 2, any_other_key: 'c'},
204
+ ]
205
+
206
+ it 'Should convert array from using parent_id to array, using children sub-array' do
207
+ expect(input_arr.to_tree).to(
208
+ eq([
209
+ {
210
+ id: 1,
211
+ any_other_key: 'a',
212
+ children: [
213
+ {
214
+ id: 2,
215
+ any_other_key: 'b',
216
+ children: [ {id: 3, any_other_key: 'c'} ]
217
+ }
218
+ ]
219
+ }
220
+ ])
221
+ )
222
+ end
223
+
224
+ it 'Should convert array from using parent_id to array, using children sub-array but with custom keys' do
225
+ expect(input_arr_custom_keys.to_tree(id_key: :id_x, parent_id_key: :parent_id_x, children_key: :children_x)).to(
226
+ eq([
227
+ {
228
+ id_x: 1,
229
+ any_other_key: 'a',
230
+ children_x: [
231
+ {
232
+ id_x: 2,
233
+ any_other_key: 'b',
234
+ children_x: [
235
+ {id_x: 3, any_other_key: 'c'}
236
+ ]
237
+ }
238
+ ]
239
+ }
240
+ ])
241
+ )
242
+ end
243
+
244
+ it 'Should convert array from using parent_id to array, using children sub-array but without id key' do
245
+ expect(input_arr.to_tree(include_id: false)).to(
246
+ eq([
247
+ {
248
+ any_other_key: 'a',
249
+ children: [
250
+ {
251
+ any_other_key: 'b',
252
+ children: [ {any_other_key: 'c'} ]
253
+ }
254
+ ]
255
+ }
256
+ ])
257
+ )
258
+ end
259
+
260
+ it 'Should convert array from using parent_id to array, using children sub-array but with parent_id key' do
261
+ expect(input_arr.to_tree(include_parent_id: true)).to(
262
+ eq([
263
+ {
264
+ id: 1,
265
+ any_other_key: 'a',
266
+ parent_id: nil,
267
+ children: [
268
+ {
269
+ id: 2,
270
+ any_other_key: 'b',
271
+ parent_id: 1,
272
+ children:
273
+ [ {id: 3, any_other_key: 'c', parent_id: 2} ]
274
+ }
275
+ ]
276
+ }
277
+ ])
278
+ )
279
+ end
280
+
281
+ it 'Should convert array from using parent_id to array, using children sub-array but with empty children key' do
282
+ expect(input_arr.to_tree(include_empty_children: true)).to(
283
+ eq([
284
+ {
285
+ id: 1,
286
+ any_other_key: 'a',
287
+ children:
288
+ [
289
+ {
290
+ id: 2,
291
+ any_other_key: 'b',
292
+ children:
293
+ [ {id: 3, any_other_key: 'c', children: []} ]
294
+ }
295
+ ]
296
+ }
297
+ ])
298
+ )
299
+ end
300
+
301
+ it 'Should convert array from using parent_id to hash, using children sub-array' do
302
+ expect(input_arr.to_tree(to_hash: true)).to(
303
+ eq({
304
+ 1 => {
305
+ id: 1,
306
+ any_other_key: 'a',
307
+ children: {
308
+ 2 => {
309
+ id: 2,
310
+ any_other_key: 'b',
311
+ children: {
312
+ 3 => {id: 3, any_other_key: 'c'}
313
+ }
314
+ }
315
+ }
316
+ }
317
+ })
318
+ )
319
+ end
320
+
321
+ it 'Should convert array from using parent_id to hash, using children sub-array but with custom keys' do
322
+ expect(input_arr_custom_keys.to_tree(to_hash: true, id_key: :id_x, parent_id_key: :parent_id_x, children_key: :children_x)).to(
323
+ eq({
324
+ 1 => {
325
+ id_x: 1,
326
+ any_other_key: 'a',
327
+ children_x: {
328
+ 2 => {
329
+ id_x: 2,
330
+ any_other_key: 'b',
331
+ children_x: {
332
+ 3 => {id_x: 3, any_other_key: 'c'}
333
+ }
334
+ }
335
+ }
336
+ }
337
+ })
338
+ )
339
+ end
340
+
341
+ it 'Should convert array from using parent_id to hash, using children sub-array but without id key' do
342
+ expect(input_arr.to_tree(to_hash: true, include_id: false)).to(
343
+ eq({
344
+ 1 => {
345
+ any_other_key: 'a',
346
+ children: {
347
+ 2 => {
348
+ any_other_key: 'b',
349
+ children: {
350
+ 3 => {any_other_key: 'c'}
351
+ }
352
+ }
353
+ }
354
+ }
355
+ })
356
+ )
357
+ end
358
+
359
+ it 'Should convert array from using parent_id to hash, using children sub-array but with parent_id key' do
360
+ expect(input_arr.to_tree(to_hash: true, include_parent_id: true)).to(
361
+ eq({
362
+ 1 => {
363
+ id: 1,
364
+ any_other_key: 'a',
365
+ parent_id: nil,
366
+ children: {
367
+ 2 => {
368
+ id: 2,
369
+ any_other_key: 'b',
370
+ parent_id: 1,
371
+ children: {
372
+ 3 => {
373
+ id: 3,
374
+ any_other_key: 'c',
375
+ parent_id: 2
376
+ }
377
+ }
378
+ }
379
+ }
380
+ }
381
+ })
382
+ )
383
+ end
384
+
385
+ it 'Should convert array from using parent_id to hash, using children sub-array but with empty children key' do
386
+ expect(input_arr.to_tree(to_hash: true, include_empty_children: true)).to(
387
+ eq({
388
+ 1 => {
389
+ id: 1,
390
+ any_other_key: 'a',
391
+ children: {
392
+ 2 => {
393
+ id: 2,
394
+ any_other_key: 'b',
395
+ children: {
396
+ 3 => {
397
+ id: 3,
398
+ any_other_key: 'c',
399
+ children: {}
400
+ }
401
+ }
402
+ }
403
+ }
404
+ }
405
+ })
406
+ )
407
+ end
408
+
409
+ end
410
+
411
+
412
+ end