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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +58 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +23 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +56 -0
- data/LICENSE.txt +21 -0
- data/README.md +192 -0
- data/Rakefile +16 -0
- data/benchmark.rb +59 -0
- data/benchmark.txt +73 -0
- data/bin/console +22 -0
- data/bin/convert +15 -0
- data/bin/setup +11 -0
- data/bin/spec/convert +27 -0
- data/bin/spec/reset +9 -0
- data/bin/spec/setup +20 -0
- data/bin/test +38 -0
- data/config/mspec_override.rb +44 -0
- data/config/skipped_tests.yml +104 -0
- data/conversion/Gemfile +7 -0
- data/conversion/Gemfile.lock +53 -0
- data/conversion/bin/rspec +27 -0
- data/conversion/lib/cops/array_initialization.rb +95 -0
- data/conversion/lib/cops/enumerator_initialization.rb +56 -0
- data/conversion/lib/cops/grep_implementation.rb +36 -0
- data/conversion/lib/cops/instance_of_array.rb +24 -0
- data/conversion/lib/cops/subclass_initialization.rb +47 -0
- data/conversion/lib/cops.rb +6 -0
- data/conversion/spec/.DS_Store +0 -0
- data/conversion/spec/cops/array_initialization_spec.rb +154 -0
- data/conversion/spec/cops/enumerator_initialization_spec.rb +105 -0
- data/conversion/spec/cops/grep_implementation_spec.rb +57 -0
- data/conversion/spec/cops/instance_of_array_spec.rb +38 -0
- data/conversion/spec/cops/subclass_initialization_spec.rb +26 -0
- data/conversion/spec/spec_helper.rb +104 -0
- data/examples/transactions.rb +278 -0
- data/grizzly-group.gemspec +28 -0
- data/lib/grizzly/collection.rb +77 -0
- data/lib/grizzly/enumerable.rb +120 -0
- data/lib/grizzly/enumerator-backup.rb +114 -0
- data/lib/grizzly/enumerator.rb +63 -0
- data/lib/grizzly/lazy_enumerator.rb +40 -0
- data/lib/grizzly/version.rb +3 -0
- data/lib/grizzly.rb +8 -0
- 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
|