grizzly-rb 1.0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +58 -0
  3. data/.gitignore +13 -0
  4. data/.rubocop.yml +23 -0
  5. data/Gemfile +10 -0
  6. data/Gemfile.lock +56 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +192 -0
  9. data/Rakefile +16 -0
  10. data/benchmark.rb +59 -0
  11. data/benchmark.txt +73 -0
  12. data/bin/console +22 -0
  13. data/bin/convert +15 -0
  14. data/bin/setup +11 -0
  15. data/bin/spec/convert +27 -0
  16. data/bin/spec/reset +9 -0
  17. data/bin/spec/setup +20 -0
  18. data/bin/test +38 -0
  19. data/config/mspec_override.rb +44 -0
  20. data/config/skipped_tests.yml +104 -0
  21. data/conversion/Gemfile +7 -0
  22. data/conversion/Gemfile.lock +53 -0
  23. data/conversion/bin/rspec +27 -0
  24. data/conversion/lib/cops/array_initialization.rb +95 -0
  25. data/conversion/lib/cops/enumerator_initialization.rb +56 -0
  26. data/conversion/lib/cops/grep_implementation.rb +36 -0
  27. data/conversion/lib/cops/instance_of_array.rb +24 -0
  28. data/conversion/lib/cops/subclass_initialization.rb +47 -0
  29. data/conversion/lib/cops.rb +6 -0
  30. data/conversion/spec/.DS_Store +0 -0
  31. data/conversion/spec/cops/array_initialization_spec.rb +154 -0
  32. data/conversion/spec/cops/enumerator_initialization_spec.rb +105 -0
  33. data/conversion/spec/cops/grep_implementation_spec.rb +57 -0
  34. data/conversion/spec/cops/instance_of_array_spec.rb +38 -0
  35. data/conversion/spec/cops/subclass_initialization_spec.rb +26 -0
  36. data/conversion/spec/spec_helper.rb +104 -0
  37. data/examples/transactions.rb +278 -0
  38. data/grizzly-group.gemspec +28 -0
  39. data/lib/grizzly/collection.rb +77 -0
  40. data/lib/grizzly/enumerable.rb +120 -0
  41. data/lib/grizzly/enumerator-backup.rb +114 -0
  42. data/lib/grizzly/enumerator.rb +63 -0
  43. data/lib/grizzly/lazy_enumerator.rb +40 -0
  44. data/lib/grizzly/version.rb +3 -0
  45. data/lib/grizzly.rb +8 -0
  46. metadata +89 -0
@@ -0,0 +1,278 @@
1
+ require 'bundler/inline'
2
+
3
+ gemfile do
4
+ source 'https://rubygems.org'
5
+ gem 'faker'
6
+ gem 'byebug'
7
+ gem 'grizzly-rb', path: '.', require: 'grizzly'
8
+ end
9
+
10
+ require "bigdecimal"
11
+
12
+ # Model Definitions
13
+
14
+ Transaction = Struct.new(:date, :price, :shares, :holding, keyword_init: true) do
15
+ def to_s
16
+ line = "%s | %s | %4s | %2s x %s = %5s"
17
+ format line, date, holding.ticker, type, shares.abs, price, value
18
+ end
19
+
20
+ def type
21
+ shares.negative? ? 'SELL' : 'BUY'
22
+ end
23
+
24
+ def value
25
+ shares * price
26
+ end
27
+
28
+ def ticker
29
+ holding.ticker
30
+ end
31
+
32
+ def industry
33
+ holding.industry
34
+ end
35
+ end
36
+
37
+ Holding = Struct.new(:ticker, :industry, :name, keyword_init: true) do
38
+ def to_s
39
+ "#{ticker} - #{name}"
40
+ end
41
+ end
42
+
43
+ class Portfolio < Grizzly::Collection
44
+ def total_shares
45
+ sum(&:shares)
46
+ end
47
+
48
+ def average_cost
49
+ value / BigDecimal(total_shares)
50
+ end
51
+
52
+ def value
53
+ sum(&:value)
54
+ end
55
+ end
56
+
57
+ # Seed data - holdings and transactions
58
+
59
+ @holdings = {}
60
+ @holdings["MAG"] = Holding.new(ticker: "MAG", industry: "Finance", name: "Maggio Group")
61
+ @holdings["ARM"] = Holding.new(ticker: "ARM", industry: "Finance", name: "Armstrong and Sons")
62
+ @holdings["CON"] = Holding.new(ticker: "CON", industry: "Finance", name: "Conn LLC")
63
+ @holdings["THP"] = Holding.new(ticker: "THP", industry: "Mining", name: "Tremblay-Hilpert")
64
+ @holdings["FTB"] = Holding.new(ticker: "FTB", industry: "Mining", name: "Fritsch-Bosco")
65
+ @holdings["MGK"] = Holding.new(ticker: "MGK", industry: "Mining", name: "MacGyver-Kilback")
66
+ @holdings["SKI"] = Holding.new(ticker: "SKI", industry: "Technology", name: "Skiles LLC")
67
+ @holdings["VFL"] = Holding.new(ticker: "VFL", industry: "Technology", name: "Von-Flatley")
68
+
69
+ def setup(holdings = @holdings)
70
+ transactions = []
71
+ until transactions.count >= 100 && transactions.group_by(&:holding).all? { |holding, t| t.sum(&:shares) > 0 }
72
+ next if (shares = Faker::Number.between(from: -2, to: 5)).zero?
73
+ transactions << Transaction.new(
74
+ date: Faker::Date.between(from: '2022-01-01', to: '2022-12-31'),
75
+ price: Faker::Number.between(from: 200, to: 300),
76
+ shares: shares,
77
+ holding: holdings[holdings.keys.sample]
78
+ )
79
+ end
80
+ transactions
81
+ end
82
+
83
+ portfolio = Portfolio.new(setup)
84
+
85
+ # Using grizzly-rb library to print financial data.
86
+
87
+ puts "=== TRANSACTIONS SORTED BY DATE ==="
88
+ puts
89
+ puts portfolio.sort_by!(&:date)
90
+ puts
91
+
92
+ # === TRANSACTIONS SORTED BY DATE ===
93
+
94
+ # 2022-01-04 | THP | BUY | 3 x 231 = 693
95
+ # 2022-01-14 | SKI | BUY | 2 x 207 = 414
96
+ # 2022-01-27 | FTB | BUY | 1 x 269 = 269
97
+ # 2022-02-01 | MGK | BUY | 3 x 252 = 756
98
+ # 2022-02-02 | FTB | SELL | 2 x 294 = -588
99
+ # 2022-02-05 | FTB | BUY | 2 x 251 = 502
100
+ # 2022-02-05 | VFL | BUY | 5 x 278 = 1390
101
+ # 2022-02-14 | ARM | BUY | 5 x 220 = 1100
102
+ # 2022-02-16 | THP | BUY | 3 x 278 = 834
103
+ # 2022-02-17 | MAG | BUY | 4 x 224 = 896
104
+ # 2022-02-27 | SKI | BUY | 5 x 269 = 1345
105
+ # 2022-02-28 | MAG | SELL | 1 x 278 = -278
106
+ # 2022-03-01 | CON | BUY | 3 x 293 = 879
107
+ # 2022-03-06 | SKI | BUY | 4 x 206 = 824
108
+ # 2022-03-21 | VFL | BUY | 3 x 218 = 654
109
+ # 2022-03-30 | MGK | BUY | 2 x 225 = 450
110
+ # 2022-03-31 | MAG | BUY | 3 x 287 = 861
111
+ # 2022-04-06 | MGK | BUY | 5 x 212 = 1060
112
+ # 2022-04-08 | VFL | BUY | 1 x 272 = 272
113
+ # 2022-04-12 | CON | SELL | 1 x 280 = -280
114
+ # 2022-04-14 | VFL | BUY | 4 x 252 = 1008
115
+ # 2022-04-26 | VFL | SELL | 2 x 285 = -570
116
+ # 2022-04-26 | MAG | BUY | 3 x 214 = 642
117
+ # 2022-04-28 | VFL | BUY | 1 x 289 = 289
118
+ # 2022-04-28 | CON | BUY | 4 x 263 = 1052
119
+ # 2022-04-28 | SKI | BUY | 2 x 231 = 462
120
+ # 2022-05-01 | VFL | BUY | 2 x 256 = 512
121
+ # 2022-05-02 | CON | BUY | 3 x 220 = 660
122
+ # 2022-05-05 | THP | SELL | 2 x 214 = -428
123
+ # 2022-05-10 | SKI | SELL | 1 x 255 = -255
124
+ # 2022-05-16 | MGK | BUY | 1 x 276 = 276
125
+ # 2022-05-23 | MAG | BUY | 3 x 236 = 708
126
+ # 2022-05-24 | FTB | BUY | 3 x 204 = 612
127
+ # 2022-05-29 | THP | BUY | 2 x 206 = 412
128
+ # 2022-05-29 | ARM | BUY | 2 x 211 = 422
129
+ # 2022-06-04 | MAG | BUY | 4 x 234 = 936
130
+ # 2022-06-07 | CON | BUY | 3 x 218 = 654
131
+ # 2022-06-07 | VFL | BUY | 4 x 287 = 1148
132
+ # 2022-06-07 | THP | SELL | 2 x 241 = -482
133
+ # 2022-06-09 | SKI | BUY | 5 x 290 = 1450
134
+ # 2022-06-11 | MAG | BUY | 2 x 292 = 584
135
+ # 2022-06-15 | MAG | BUY | 2 x 238 = 476
136
+ # 2022-06-19 | MGK | BUY | 2 x 206 = 412
137
+ # 2022-06-20 | VFL | BUY | 3 x 293 = 879
138
+ # 2022-06-25 | CON | BUY | 5 x 201 = 1005
139
+ # 2022-07-01 | SKI | BUY | 2 x 225 = 450
140
+ # 2022-07-06 | VFL | BUY | 4 x 212 = 848
141
+ # 2022-07-07 | CON | BUY | 4 x 219 = 876
142
+ # 2022-07-08 | FTB | BUY | 2 x 261 = 522
143
+ # 2022-07-09 | CON | BUY | 3 x 263 = 789
144
+ # 2022-07-11 | SKI | BUY | 2 x 288 = 576
145
+ # 2022-07-13 | CON | SELL | 1 x 215 = -215
146
+ # 2022-07-15 | MGK | BUY | 4 x 241 = 964
147
+ # 2022-07-17 | CON | BUY | 2 x 295 = 590
148
+ # 2022-07-18 | CON | BUY | 4 x 254 = 1016
149
+ # 2022-07-22 | FTB | SELL | 2 x 295 = -590
150
+ # 2022-08-01 | MAG | BUY | 3 x 247 = 741
151
+ # 2022-08-02 | THP | BUY | 2 x 208 = 416
152
+ # 2022-08-03 | MGK | SELL | 1 x 239 = -239
153
+ # 2022-08-04 | MAG | BUY | 1 x 293 = 293
154
+ # 2022-08-08 | FTB | BUY | 4 x 259 = 1036
155
+ # 2022-08-09 | THP | BUY | 5 x 249 = 1245
156
+ # 2022-08-24 | SKI | BUY | 1 x 228 = 228
157
+ # 2022-08-26 | FTB | BUY | 1 x 285 = 285
158
+ # 2022-08-28 | CON | BUY | 2 x 297 = 594
159
+ # 2022-08-28 | SKI | BUY | 5 x 283 = 1415
160
+ # 2022-08-29 | VFL | BUY | 1 x 251 = 251
161
+ # 2022-09-01 | MGK | BUY | 3 x 201 = 603
162
+ # 2022-09-01 | THP | BUY | 5 x 229 = 1145
163
+ # 2022-09-02 | MGK | BUY | 5 x 228 = 1140
164
+ # 2022-09-07 | THP | BUY | 1 x 215 = 215
165
+ # 2022-09-13 | FTB | BUY | 4 x 292 = 1168
166
+ # 2022-09-15 | VFL | BUY | 4 x 218 = 872
167
+ # 2022-09-20 | THP | SELL | 1 x 279 = -279
168
+ # 2022-09-24 | SKI | BUY | 4 x 214 = 856
169
+ # 2022-09-28 | FTB | BUY | 3 x 300 = 900
170
+ # 2022-10-07 | THP | BUY | 5 x 236 = 1180
171
+ # 2022-10-15 | MGK | BUY | 2 x 208 = 416
172
+ # 2022-10-15 | VFL | SELL | 2 x 217 = -434
173
+ # 2022-10-18 | CON | SELL | 2 x 232 = -464
174
+ # 2022-10-19 | MAG | BUY | 1 x 241 = 241
175
+ # 2022-10-22 | SKI | BUY | 2 x 225 = 450
176
+ # 2022-10-22 | THP | BUY | 5 x 289 = 1445
177
+ # 2022-10-23 | ARM | BUY | 2 x 235 = 470
178
+ # 2022-11-01 | THP | BUY | 4 x 267 = 1068
179
+ # 2022-11-02 | MAG | BUY | 2 x 206 = 412
180
+ # 2022-11-10 | VFL | SELL | 1 x 289 = -289
181
+ # 2022-11-13 | THP | SELL | 1 x 209 = -209
182
+ # 2022-11-15 | THP | BUY | 2 x 253 = 506
183
+ # 2022-11-15 | CON | BUY | 5 x 258 = 1290
184
+ # 2022-11-19 | MAG | BUY | 5 x 246 = 1230
185
+ # 2022-11-22 | MGK | SELL | 1 x 292 = -292
186
+ # 2022-11-30 | SKI | BUY | 5 x 273 = 1365
187
+ # 2022-12-02 | CON | SELL | 1 x 245 = -245
188
+ # 2022-12-03 | VFL | BUY | 5 x 295 = 1475
189
+ # 2022-12-08 | MAG | BUY | 3 x 217 = 651
190
+ # 2022-12-13 | ARM | BUY | 5 x 285 = 1425
191
+ # 2022-12-14 | SKI | SELL | 1 x 237 = -237
192
+ # 2022-12-26 | ARM | BUY | 3 x 214 = 642
193
+ # 2022-12-30 | MAG | BUY | 4 x 229 = 916
194
+
195
+
196
+ puts "=== TRANSACTIONS FOR MAG ==="
197
+ puts
198
+ mag = portfolio.select { |t| t.holding == @holdings["MAG"] }
199
+ puts "%s %10.2f, shares: %4s, cost average: %8.2f" % [@holdings["MAG"], portfolio.value, portfolio.total_shares, portfolio.average_cost]
200
+ puts mag
201
+ puts
202
+
203
+ # === TRANSACTIONS FOR MAG ===
204
+
205
+ # MAG - Maggio Group 56640.00, shares: 230, cost average: 246.26
206
+ # 2022-02-17 | MAG | BUY | 4 x 224 = 896
207
+ # 2022-02-28 | MAG | SELL | 1 x 278 = -278
208
+ # 2022-03-31 | MAG | BUY | 3 x 287 = 861
209
+ # 2022-04-26 | MAG | BUY | 3 x 214 = 642
210
+ # 2022-05-23 | MAG | BUY | 3 x 236 = 708
211
+ # 2022-06-04 | MAG | BUY | 4 x 234 = 936
212
+ # 2022-06-11 | MAG | BUY | 2 x 292 = 584
213
+ # 2022-06-15 | MAG | BUY | 2 x 238 = 476
214
+ # 2022-08-01 | MAG | BUY | 3 x 247 = 741
215
+ # 2022-08-04 | MAG | BUY | 1 x 293 = 293
216
+ # 2022-10-19 | MAG | BUY | 1 x 241 = 241
217
+ # 2022-11-02 | MAG | BUY | 2 x 206 = 412
218
+ # 2022-11-19 | MAG | BUY | 5 x 246 = 1230
219
+ # 2022-12-08 | MAG | BUY | 3 x 217 = 651
220
+ # 2022-12-30 | MAG | BUY | 4 x 229 = 916
221
+
222
+
223
+ puts "=== PORTFOLIO GROUPED BY TICKER === "
224
+ puts
225
+ portfolio
226
+ .sort_by(&:ticker)
227
+ .group_by(&:holding)
228
+ .each do |holding, portfolio|
229
+ puts "%-25s %10.2f, shares: %4s, cost average: %8.2f" % [holding.name, portfolio.value, portfolio.total_shares, portfolio.average_cost]
230
+ end
231
+ puts '-' * 74
232
+ puts "%-25s %10.2f, shares: %4s, cost average: %8.2f" % ["Portfolio total value:", portfolio.value, portfolio.total_shares, portfolio.average_cost]
233
+ puts
234
+
235
+ # === PORTFOLIO GROUPED BY TICKER ===
236
+
237
+ # Portfolio total value: 56640.00, shares: 230, cost average: 246.26
238
+ # Armstrong and Sons 4059.00, shares: 17, cost average: 238.76
239
+ # Conn LLC 8201.00, shares: 33, cost average: 248.52
240
+ # Fritsch-Bosco 4116.00, shares: 16, cost average: 257.25
241
+ # Maggio Group 9309.00, shares: 39, cost average: 238.69
242
+ # MacGyver-Kilback 5546.00, shares: 25, cost average: 221.84
243
+ # Skiles LLC 9343.00, shares: 37, cost average: 252.51
244
+ # Tremblay-Hilpert 7761.00, shares: 31, cost average: 250.35
245
+ # Von-Flatley 8305.00, shares: 32, cost average: 259.53
246
+
247
+
248
+ puts "=== PORTFOLIO GROUPED BY INDUSTRY ==="
249
+ puts
250
+
251
+ portfolio
252
+ .sort_by { |transaction| [transaction.industry, transaction.ticker] }
253
+ .group_by(&:industry)
254
+ .each do |(industry, portfolio)|
255
+ puts "%-25s %10.2f, shares: %4s, cost average: %8.2f" % [industry, portfolio.value, portfolio.total_shares, portfolio.average_cost]
256
+ portfolio
257
+ .group_by(&:holding)
258
+ .each do |(holding, portfolio)|
259
+ puts " - %-21s %10.2f, shares: %4s, cost average: %8.2f" % [holding.name, portfolio.value, portfolio.total_shares, portfolio.average_cost]
260
+ end
261
+ puts
262
+ end
263
+
264
+ # === PORTFOLIO GROUPED BY INDUSTRY ===
265
+
266
+ # Finance 21569.00, shares: 89, cost average: 242.35
267
+ # - Armstrong and Sons 4059.00, shares: 17, cost average: 238.76
268
+ # - Conn LLC 8201.00, shares: 33, cost average: 248.52
269
+ # - Maggio Group 9309.00, shares: 39, cost average: 238.69
270
+
271
+ # Mining 17423.00, shares: 72, cost average: 241.99
272
+ # - Fritsch-Bosco 4116.00, shares: 16, cost average: 257.25
273
+ # - MacGyver-Kilback 5546.00, shares: 25, cost average: 221.84
274
+ # - Tremblay-Hilpert 7761.00, shares: 31, cost average: 250.35
275
+
276
+ # Technology 17648.00, shares: 69, cost average: 255.77
277
+ # - Skiles LLC 9343.00, shares: 37, cost average: 252.51
278
+ # - Von-Flatley 8305.00, shares: 32, cost average: 259.53
@@ -0,0 +1,28 @@
1
+ require_relative 'lib/grizzly/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "grizzly-rb"
5
+ spec.version = Grizzly::VERSION
6
+ spec.authors = ["Alexandre Barret"]
7
+ spec.email = ["alexbarret@duck.com"]
8
+
9
+ spec.summary = "An Array subclass for Ruby"
10
+ spec.homepage = "https://github.com/AlexB52/grizzly-rb"
11
+ spec.license = "MIT"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/AlexB52/grizzly-rb"
18
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,77 @@
1
+
2
+ module Grizzly
3
+ class Collection < Array
4
+ include Grizzly::Enumerable
5
+
6
+ %i[
7
+ each collect! map! sort_by! * pop shift drop_while take_while bsearch_index
8
+ bsearch combination delete_if each_index keep_if find_index index permutation
9
+ repeated_combination repeated_permutation reverse_each rindex max min
10
+ ].each do |method_name|
11
+ define_method(method_name) do |*args, &block|
12
+ subgroup super(*args, &block)
13
+ end
14
+ end
15
+
16
+ %i[
17
+ values_at rotate compact reverse intersection & | + union - difference
18
+ flatten drop take
19
+ ].each do |method_name|
20
+ define_method(method_name) do |*args|
21
+ new_collection super(*args)
22
+ end
23
+ end
24
+
25
+ def transpose(*args)
26
+ result = super
27
+ if self.all? { |collection| collection.is_a?(self.class) }
28
+ result = result.map { |collection| new_collection(collection) }
29
+ end
30
+ new_collection(result)
31
+ end
32
+
33
+ def product(*args)
34
+ result = super
35
+ return result if is_self?(result)
36
+ return result unless result.is_a?(Array)
37
+
38
+ result.map {|product| new_collection(product)}
39
+ end
40
+
41
+ def last(*args)
42
+ result = super
43
+ return result if args == []
44
+
45
+ subgroup(result)
46
+ end
47
+
48
+ def slice(*args)
49
+ result = super
50
+ return result if args.count == 1 && args.first.is_a?(Integer)
51
+
52
+ subgroup(result)
53
+ end
54
+
55
+ def slice!(*args)
56
+ result = super
57
+ return result if args.count == 1 && args.first.is_a?(Integer)
58
+
59
+ subgroup(result)
60
+ end
61
+
62
+ def [](*args)
63
+ result = super
64
+ return result if args.count == 1 && args.first.is_a?(Integer)
65
+
66
+ subgroup(result)
67
+ end
68
+
69
+ def sample(*args, **karg)
70
+ subgroup(super)
71
+ end
72
+
73
+ def shuffle(*args, **kargs)
74
+ new_collection(super)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,120 @@
1
+ module Grizzly
2
+ module Enumerable
3
+ include ::Enumerable
4
+
5
+ %i[
6
+ max min minmax_by sort_by find_all filter_map reject reject! filter filter!
7
+ select select! take_while reverse_each min_by max_by find_index drop_while
8
+ chunk chunk_while each_slice each_cons each_entry each_with_index flat_map
9
+ collect_concat find detect uniq collect map
10
+ ].each do |method_name|
11
+ define_method(method_name) do |*args, &block|
12
+ subgroup super(*args, &block)
13
+ end
14
+ end
15
+
16
+ %i[minmax sort].each do |method_name|
17
+ define_method(method_name) do |*args, &block|
18
+ subgroup super(*args, &block)
19
+ end
20
+ end
21
+
22
+ %i[to_enum slice_when slice_before slice_after].each do |method_name|
23
+ define_method(method_name) do |*args, &block|
24
+ new_enumerator super(*args, &block)
25
+ end
26
+ end
27
+
28
+ def grep(*args)
29
+ raise NotImplementedError, "#{__method__} can't be supported. See https://bugs.ruby-lang.org/issues/11808"
30
+ end
31
+
32
+ def grep_v(*args)
33
+ raise NotImplementedError, "#{__method__} can't be supported. See https://bugs.ruby-lang.org/issues/11808"
34
+ end
35
+
36
+ def zip(*other_arrays)
37
+ result = super
38
+ return result unless result.is_a?(Array)
39
+
40
+ type = :self
41
+ other_arrays.each do |array|
42
+ case array
43
+ when self.class
44
+ when Grizzly::Collection
45
+ type = :group
46
+ else
47
+ type = :array
48
+ break
49
+ end
50
+ end
51
+
52
+ case type
53
+ when :self then result.map { |a| new_collection(a) }
54
+ when :group then result.map { |a| Grizzly::Collection.new(a) }
55
+ else result
56
+ end
57
+ end
58
+
59
+ def partition(*args)
60
+ result = super
61
+ return new_enumerator(result) if result.is_a?(::Enumerator)
62
+ return result unless result.is_a?(Array)
63
+
64
+ [new_collection(result[0]), new_collection(result[1])]
65
+ end
66
+
67
+ def group_by(*args)
68
+ result = super
69
+ return new_enumerator(result) if result.is_a?(::Enumerator)
70
+ return result unless result.is_a?(Hash)
71
+
72
+ result.each do |key, value|
73
+ result[key] = new_collection(value)
74
+ end
75
+ end
76
+
77
+ def first(*args)
78
+ result = super
79
+ return result if args == []
80
+
81
+ subgroup(result)
82
+ end
83
+
84
+ # Lazy related methods
85
+
86
+ def lazy
87
+ new_lazy_enumerator(super)
88
+ end
89
+
90
+ private
91
+
92
+ def subgroup(result)
93
+ return result if is_self?(result)
94
+ return new_enumerator(result) if result.is_a?(::Enumerator)
95
+ return result unless result.is_a?(Array)
96
+
97
+ new_collection(result)
98
+ end
99
+
100
+ def instantiating_class
101
+ self.class
102
+ end
103
+
104
+ def new_collection(array)
105
+ instantiating_class.new(array)
106
+ end
107
+
108
+ def new_enumerator(enum)
109
+ Grizzly::Enumerator.new(enum, instantiating_class: instantiating_class)
110
+ end
111
+
112
+ def new_lazy_enumerator(lazy)
113
+ Grizzly::LazyEnumerator.new(lazy, instantiating_class: instantiating_class)
114
+ end
115
+
116
+ def is_self?(obj)
117
+ obj.object_id.eql? object_id
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,114 @@
1
+ require 'forwardable'
2
+
3
+ module Grizzly
4
+ class Enumerator
5
+ extend Forwardable
6
+
7
+ def_delegators :@enum, *%i{
8
+ + first feed to_a
9
+ next next_values
10
+ peek peek_values
11
+ }
12
+
13
+ attr_reader :enum, :obj, :method_name, :args
14
+ def initialize(obj, method_name = :each, *args, size: nil, &block)
15
+ @obj = obj
16
+ @args = args
17
+ @size = size
18
+ @method_name = method_name
19
+ if block_given?
20
+ @enum = ::Enumerator.new(obj, &block)
21
+ else
22
+ @enum = obj.to_enum(method_name, *args)
23
+ end
24
+ end
25
+
26
+ def each(*args, &block)
27
+ unless block_given?
28
+ return args.any? ? new_enumerator(enum, __method__, *args) : self
29
+ end
30
+
31
+ enum.each(*args, &block)
32
+ end
33
+
34
+ def with_index(offset = 0, &block)
35
+ unless block_given?
36
+ return new_enumerator(self, __method__, offset)
37
+ end
38
+
39
+ enum.with_index(offset, &block)
40
+ end
41
+
42
+ def each_with_index(&block)
43
+ with_index(&block)
44
+ end
45
+
46
+ def with_object(object, &block)
47
+ unless block_given?
48
+ return new_enumerator(self, __method__, object)
49
+ end
50
+
51
+ enum.with_object(object, &block)
52
+ end
53
+ alias :each_with_object :with_object
54
+
55
+ def inspect
56
+ enum.inspect.gsub('Enumerator', self.class.to_s)
57
+ end
58
+
59
+ def size
60
+ return obj.call if obj.respond_to?(:call)
61
+
62
+ @size || enum.size || obj.size if obj.respond_to?(:size)
63
+ end
64
+
65
+ def rewind
66
+ enum.rewind && self
67
+ end
68
+
69
+ private
70
+
71
+ def new_enumerator(obj, method_name, *args)
72
+ self.class.new obj, method_name, *args, size: size
73
+ end
74
+ end
75
+
76
+ # class Enumerator < Enumerator
77
+ # extend Forwardable
78
+
79
+ # OVERRIDE_METHODS = %i{with_index with_object each_with_index each_with_object}
80
+ # ENUMERATOR_METHODS = Enumerator.public_instance_methods(false)
81
+ # ENUMERABLE_METHODS = Enumerable.public_instance_methods
82
+
83
+ # alias :old_enum :to_enum
84
+
85
+ # def_delegators :@enum, *ENUMERATOR_METHODS
86
+
87
+ # ENUMERABLE_METHODS.each do |method_name|
88
+ # define_method(method_name) do |*args, &block|
89
+ # return to_enum(__method__, *args) unless block
90
+
91
+ # @object.send(__method__, *args, &block)
92
+ # end
93
+ # end
94
+
95
+ # OVERRIDE_METHODS.each do |method_name|
96
+ # define_method(method_name) do |*args, &block|
97
+ # return to_enum(__method__, *args) unless block
98
+
99
+ # @enum.send(:each, *args, &block)
100
+ # end
101
+ # end
102
+
103
+ # attr_reader :object, :enum
104
+
105
+ # def initialize(object, method_name, *args)
106
+ # @object = object
107
+ # @enum = object.to_enum(method_name, *args)
108
+ # end
109
+
110
+ # def to_enum(method_name, *args)
111
+ # self.class.new @object, method_name, *args
112
+ # end
113
+ # end
114
+ end
@@ -0,0 +1,63 @@
1
+ require 'forwardable'
2
+
3
+ module Grizzly
4
+ class Enumerator
5
+ extend Forwardable
6
+ include ::Grizzly::Enumerable
7
+
8
+ def_delegators :@enum, *%i{
9
+ + first feed to_a
10
+ next next_values
11
+ peek peek_values
12
+ size
13
+ }
14
+
15
+ attr_reader :enum, :instantiating_class
16
+ def initialize(enum, instantiating_class: Array)
17
+ @enum = enum
18
+ @instantiating_class = instantiating_class
19
+ end
20
+
21
+ def each(*args, &block)
22
+ return self if args.empty? && !block_given?
23
+
24
+ unless block_given?
25
+ return new_enumerator(enum.each(*args))
26
+ end
27
+
28
+ enum.each(*args, &block)
29
+ end
30
+
31
+ def with_index(offset = 0, &block)
32
+ unless block_given?
33
+ return new_enumerator(@enum.with_index(offset))
34
+ end
35
+
36
+ enum.with_index(offset, &block)
37
+ end
38
+
39
+ def each_with_index(&block)
40
+ with_index(&block)
41
+ end
42
+
43
+ def rewind
44
+ enum.rewind && self
45
+ end
46
+
47
+ def with_object(object, &block)
48
+ unless block_given?
49
+ return new_enumerator(@enum.with_object(object))
50
+ end
51
+
52
+ enum.with_object(object, &block)
53
+ end
54
+
55
+ def each_with_object(object, &block)
56
+ with_object(object, &block)
57
+ end
58
+
59
+ def inspect
60
+ enum.inspect.gsub(/(?<!Grizzly::)Enumerator/, self.class.to_s)
61
+ end
62
+ end
63
+ end