ruby_extended 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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