mayak 0.1.0 → 0.2.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 +4 -4
- data/README.md +7 -227
- data/lib/mayak/codec.rb +70 -0
- data/lib/mayak/csv/body.rb +15 -0
- data/lib/mayak/csv/codec.rb +10 -0
- data/lib/mayak/csv/column.rb +16 -0
- data/lib/mayak/csv/decoder.rb +154 -0
- data/lib/mayak/csv/document.rb +28 -0
- data/lib/mayak/csv/encoder.rb +35 -0
- data/lib/mayak/csv/header.rb +54 -0
- data/lib/mayak/csv/parse_error.rb +9 -0
- data/lib/mayak/csv/row.rb +20 -0
- data/lib/mayak/decoder.rb +40 -4
- data/lib/mayak/encoder.rb +15 -15
- data/lib/mayak/http/decoder.rb +11 -11
- data/lib/mayak/http/encoder.rb +8 -29
- data/lib/mayak/json/encoder.rb +2 -23
- data/lib/mayak/lazy/README.md +226 -0
- data/lib/mayak.rb +0 -1
- data/mayak.gemspec +1 -2
- metadata +13 -17
- data/lib/mayak/http/codec.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c803fe79f01fbe1d57d090db09b47e5e8500656819f61951e120fd10860680e
|
4
|
+
data.tar.gz: 8dcaeaa4cbf92d629113c7faedaa1048dad5a69aaca38e82fbadd8609631be3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 144fb12c3b37c4cd8f8abdcca91042178647273ee26f8abc23bf3f83373ad5e04affda9aa1a58331fc619f0c362156b57f5b6d5b66bde15bfd5b1c5b966ef042
|
7
|
+
data.tar.gz: e50fe76a28b4591a0ee5b3de40da4b88bff7a05303d7673e00d9873b6abd41a6d6b94d0367660d61bd2777e2d3871f399a83b9f3fbcb1ad9bbaa97b868021184
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
[gem]: https://rubygems.org/gems/mayak
|
2
|
+
[actions]: https://github.com/dnvkv/mayak/actions
|
3
|
+
|
4
|
+
# Mayak [][gem] [][actions]
|
2
5
|
|
3
6
|
### Overview
|
4
7
|
|
@@ -38,234 +41,11 @@ Mayak consists from separate classes and interfaces as well as separate modules
|
|
38
41
|
|
39
42
|
[Documentation](./lib/mayak/http/README.md)
|
40
43
|
|
41
|
-
####
|
42
|
-
|
43
|
-
##### Lazy
|
44
|
-
|
45
|
-
`Lazy` classs represents a value that evaluates only during it's access, and evaluates only once
|
46
|
-
during the first access. Basically, `Lazy` wraps a block of code (thunk) that returns a value (`Lazy` has single type parameter of a value), and executes only when the value accessed for the first time
|
47
|
-
and then stores it afterward.
|
48
|
-
|
49
|
-
In order to build `Lazy` a type parameter of value holded should be provided as well as a block that computes a value of the type.
|
50
|
-
Note that the block is not executed right away.
|
51
|
-
|
52
|
-
```ruby
|
53
|
-
lazy1 = ::Mayak::Lazy[Integer].new { 1 }
|
54
|
-
|
55
|
-
buffer = []
|
56
|
-
lazy2 = ::Mayak::Lazy[Integer].new do
|
57
|
-
buffer << 1
|
58
|
-
1
|
59
|
-
end
|
60
|
-
buffer
|
61
|
-
#> []
|
62
|
-
```
|
63
|
-
|
64
|
-
To access the value call `#value`. If the value is not yet computed, provided block will be executed and its result will be stored. Further invokations
|
65
|
-
of this method won't execute the block again.
|
66
|
-
|
67
|
-
```ruby
|
68
|
-
buffer = []
|
69
|
-
lazy = ::Mayak::Lazy[Integer].new do
|
70
|
-
buffer << 1
|
71
|
-
1
|
72
|
-
end
|
73
|
-
buffer
|
74
|
-
#> []
|
75
|
-
|
76
|
-
# Will execute the block and return the computed value.
|
77
|
-
lazy.value
|
78
|
-
#> 1
|
79
|
-
buffer
|
80
|
-
#> [1]
|
81
|
-
|
82
|
-
# Will return the memoized value, but won't call the block again
|
83
|
-
lazy.value
|
84
|
-
#> 1
|
85
|
-
buffer
|
86
|
-
#> [1]
|
87
|
-
```
|
88
|
-
|
89
|
-
`Lazy` can be used in situations, when we want to inject some dependency into some class or method, but it may not be used, and the computation or aacquisition of the dependency may be cosftul. In this cases, it's acquisitation may be wrapped in lazy.
|
90
|
-
|
91
|
-
In more imperative style
|
92
|
-
```ruby
|
93
|
-
sig { params(env_variable: String, file_content: ::Mayak::Lazy[String], default: String).returns(String) }
|
94
|
-
def fetch_config(env_variable, file_content, default)
|
95
|
-
from_environment = ENV[env_variable]
|
96
|
-
if env.empty?
|
97
|
-
file_config = ::Core::Json.parse(file_content.value).success_or({})
|
98
|
-
from_file = file_config["configuration"]
|
99
|
-
if from_file.empty?
|
100
|
-
default
|
101
|
-
else
|
102
|
-
from_file
|
103
|
-
end
|
104
|
-
else
|
105
|
-
from_environment
|
106
|
-
end
|
107
|
-
end
|
108
|
-
```
|
109
|
-
|
110
|
-
Using Mayak monads:
|
111
|
-
```ruby
|
112
|
-
include ::Mayak::Monads::Maybe::Mixin
|
113
|
-
|
114
|
-
sig { params(env_variable: String, file_content: ::Mayak::Lazy[String], default: String).returns(String) }
|
115
|
-
def fetch_config(env_variable, file_content, default)
|
116
|
-
Maybe(ENV[env_variable])
|
117
|
-
.recover_with_maybe(::Core::Json.parse(file_content.value).to_maybe)
|
118
|
-
.flat_map { |json| Maybe(json["configuration"]) }
|
119
|
-
.value_or(default)
|
120
|
-
end
|
121
|
-
```
|
122
|
-
|
123
|
-
This method receives name of environment variable, and file content as lazy value. The method
|
124
|
-
tries to read the environment variable, and if it's not present and reads the file content to find the configuration.
|
125
|
-
`Lazy` allows to incapsulate behaviour of reading from file, so it can be passed as dependency, method `#fetch_config` doesn't
|
126
|
-
know anything about reading from file, but because of usage of `lazy` we can postpone it's execution thus avoiding unnecessary work.
|
127
|
-
|
128
|
-
`Lazy` can be transformed via methods `#map` and `#flat_map`.
|
129
|
-
|
130
|
-
Method `#map` allows to transform value inside `Lazy` without triggering executing. Note that `#map` returns
|
131
|
-
a new instance without mutating previous `Lazy`.
|
132
|
-
```ruby
|
133
|
-
int_lazy = ::Mayak::Lazy[Integer].new do
|
134
|
-
puts("On initialize")
|
135
|
-
1
|
136
|
-
end
|
137
|
-
string_lazy = int_lazy.map do |int|
|
138
|
-
puts("On mapping")
|
139
|
-
int.to_s
|
140
|
-
end
|
141
|
-
int_lazy.value # 1
|
142
|
-
#> On initialize
|
143
|
-
|
144
|
-
string_lazy.value # "1"
|
145
|
-
#> On initialize
|
146
|
-
#> On mapping
|
147
|
-
|
148
|
-
sig { params(file_content: ::Mayak::Lazy[String]).returns(::Mayak::Lazy[Maybe[String]]) }
|
149
|
-
def file_content_config(file_content)
|
150
|
-
file_content.map do |file_content|
|
151
|
-
::Core::Json
|
152
|
-
.parse(file_content.value)
|
153
|
-
.to_maybe
|
154
|
-
.flat_map { |json| Maybe(json["configuration"]) }
|
155
|
-
end
|
156
|
-
end
|
157
|
-
```
|
158
|
-
|
159
|
-
Method `#flat_map` allows to chain lazy computations. It receives a block, that builds a new `Lazy` value from the value of original
|
160
|
-
`Lazy` and returns a new instance of `Lazy`.
|
161
|
-
|
162
|
-
```ruby
|
163
|
-
sig { params(env_name: String).returns(::Mayak::Lazy[String]) }
|
164
|
-
def lazy_env(env_name)
|
165
|
-
::Mayak::Lazy[String].new { ENV[env_name] }
|
166
|
-
end
|
167
|
-
|
168
|
-
env_variable_name = ::Mayak::Lazy[String].new { "VARIABLE" }
|
169
|
-
env_variable = env_variable_name.flat_map { |env_name| lazy_env(env_name) }
|
170
|
-
```
|
171
|
-
|
172
|
-
This may be useful when want to perform a lazy computation based on result of some other lazy computation without enforcing the evaluation.
|
173
|
-
|
174
|
-
For example we have a file that contains list of file names. We can build a lazy computation that read all lines from this code.
|
175
|
-
```ruby
|
176
|
-
sig { params(file_name: String).returns(::Mayak::Lazy[T::Array[String]]) }
|
177
|
-
def read_file_lines(file_name)
|
178
|
-
::Mayak::Lazy[T::Array[String]].new { File.read(file_name).split }
|
179
|
-
end
|
180
|
-
```
|
181
|
-
|
182
|
-
Let's we want to read all filenames from the root file, and then read the first file lazily. In this cases, the lazy computation can be chained via `#flat_map`:
|
183
|
-
|
184
|
-
```ruby
|
185
|
-
sig { params(file_name: String).returns(::Mayak::Lazy[T::Array[String]]) }
|
186
|
-
def read_first_file(file_name)
|
187
|
-
read_file_lines(file_name).flat_map do |file_names|
|
188
|
-
Maybe(file_names.first)
|
189
|
-
.filter(&:empty?)
|
190
|
-
.map { |file| read_file_lines(file) }
|
191
|
-
.value_or(::Mayak::Lazy[T::Array[String]].new { [] })
|
192
|
-
end
|
193
|
-
end
|
194
|
-
```
|
195
|
-
|
196
|
-
In order to combine two lazies of different types into a single one, method `#combine` can be used.
|
197
|
-
This method receives another lazy (it can be lazy of different type), and a block
|
198
|
-
and returns a lazy containing result of applying passed blocked to values calculated by lazies.
|
199
|
-
|
200
|
-
```ruby
|
201
|
-
class ConfigFiles < T::Struct
|
202
|
-
const :database_config_file, ::File
|
203
|
-
const :server_config_file, ::File
|
204
|
-
end
|
205
|
-
|
206
|
-
sig { returns(::Mayak::Lazy[File]) }
|
207
|
-
def database_config_file
|
208
|
-
::Mayak::Lazy[File].new { File.new(DATABASE_CONFIG_FILE_NAME, "r") }
|
209
|
-
end
|
210
|
-
|
211
|
-
sig { returns(::Mayak::Lazy[File]) }
|
212
|
-
def server_config_file
|
213
|
-
::Mayak::Lazy[File].new { File.new(SERVER_CONFIG_FILE_NAME, "r") }
|
214
|
-
end
|
215
|
-
|
216
|
-
sig { returns(::Mayak::Lazy[ConfigFiles]) }
|
217
|
-
def config_files
|
218
|
-
database_config_file.combine(server_config_file) do |db_file, server_file|
|
219
|
-
ConfigFiles.new(
|
220
|
-
database_config_file: database_config_file,
|
221
|
-
server_config_file: server_file
|
222
|
-
)
|
223
|
-
end
|
224
|
-
end
|
225
|
-
```
|
226
|
-
|
227
|
-
The same behaviour can be achieved with a method `.combine_two`:
|
228
|
-
|
229
|
-
```ruby
|
230
|
-
sig { returns(::Mayak::Lazy[ConfigFiles]) }
|
231
|
-
def config_files
|
232
|
-
::Mayak::Lazy.combine_two(database_config_file, server_config_file) do |db_file, server_file|
|
233
|
-
ConfigFiles.new(
|
234
|
-
database_config_file: database_config_file,
|
235
|
-
server_config_file: server_file
|
236
|
-
)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
```
|
240
|
-
|
241
|
-
There are also methods `.combine_three`, `.combine_four` upto `.combine_sevel` to combine multiple lazies of diffent types.
|
44
|
+
#### Lazy
|
242
45
|
|
243
|
-
|
244
|
-
as `Array#reduce`: receives an array of lazies containing the same type, initial value of result type, and a block
|
245
|
-
receiving accumulator value of result type, and value of next lazy.
|
46
|
+
[Documentation](./lib/mayak/lazy/README.md)
|
246
47
|
|
247
|
-
|
248
|
-
sig { returns(::Mayak::Lazy[Integer]) }
|
249
|
-
def lazy
|
250
|
-
::Mayak::Lazy.combine_many(
|
251
|
-
[::Mayak::Lazy[Integer].new(1), ::Mayak::Lazy[Integer].new(2), ::Mayak::Lazy[Integer].new(3)],
|
252
|
-
0
|
253
|
-
) { |acc, value| acc + value }
|
254
|
-
end
|
255
|
-
|
256
|
-
lazy.value # 10
|
257
|
-
```
|
258
|
-
|
259
|
-
If you need to transform array of lazies of some value into lazy of array of the value, you can use `.sequence` method.
|
260
|
-
|
261
|
-
```ruby
|
262
|
-
sig { returns(::Mayak::Lazy[T::Array[Integer]]) }
|
263
|
-
def lazy
|
264
|
-
::Mayak::Lazy.sequence([::Mayak::Lazy[Integer].new(1), ::Mayak::Lazy[Integer].new(2), ::Mayak::Lazy[Integer].new(3)])
|
265
|
-
end
|
266
|
-
|
267
|
-
lazy.value # [1, 2, 3]
|
268
|
-
```
|
48
|
+
#### Miscellaneous
|
269
49
|
|
270
50
|
##### Function
|
271
51
|
In some situations Sorbet can not infer a type of proc passed:
|
data/lib/mayak/codec.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Codec
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Generic
|
8
|
+
extend T::Helpers
|
9
|
+
|
10
|
+
abstract!
|
11
|
+
|
12
|
+
Entity = type_member
|
13
|
+
Protocol = type_member
|
14
|
+
|
15
|
+
sig { abstract.params(entity: Entity).returns(Protocol) }
|
16
|
+
def encode(entity)
|
17
|
+
end
|
18
|
+
|
19
|
+
sig {
|
20
|
+
abstract
|
21
|
+
.params(response: Protocol)
|
22
|
+
.returns(Mayak::Monads::Try[Entity])
|
23
|
+
}
|
24
|
+
def decode(response)
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { returns(::Mayak::Encoder[Entity, Protocol]) }
|
28
|
+
def to_encoder
|
29
|
+
::Mayak::Encoder::Implementation[Entity, Protocol].new do |entity|
|
30
|
+
encode(entity)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { returns(::Mayak::Decoder[Protocol, Entity]) }
|
35
|
+
def to_decoder
|
36
|
+
::Mayak::Decoder::Implementation[Protocol, Entity].new do |protocol|
|
37
|
+
decode(protocol)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class FromPair < T::Struct
|
42
|
+
extend T::Sig
|
43
|
+
extend T::Generic
|
44
|
+
extend T::Helpers
|
45
|
+
|
46
|
+
Entity = type_member
|
47
|
+
Protocol = type_member
|
48
|
+
|
49
|
+
include ::Mayak::Codec
|
50
|
+
|
51
|
+
const :encoder, ::Mayak::Encoder[Entity, Protocol]
|
52
|
+
const :decoder, ::Mayak::Decoder[Protocol, Entity]
|
53
|
+
|
54
|
+
|
55
|
+
sig { override.params(entity: Entity).returns(Protocol) }
|
56
|
+
def encode(entity)
|
57
|
+
encoder.encode(entity)
|
58
|
+
end
|
59
|
+
|
60
|
+
sig {
|
61
|
+
override
|
62
|
+
.params(response: Protocol)
|
63
|
+
.returns(Mayak::Monads::Try[Entity])
|
64
|
+
}
|
65
|
+
def decode(response)
|
66
|
+
decoder.decode(response)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Csv
|
6
|
+
class Column < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Value = type_member
|
11
|
+
|
12
|
+
const :name, String
|
13
|
+
const :serializer, T.proc.params(value: Value).returns(String)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Csv
|
6
|
+
module Decoder
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Value = type_member
|
11
|
+
|
12
|
+
abstract!
|
13
|
+
|
14
|
+
sig {
|
15
|
+
abstract
|
16
|
+
.params(csv: T.any(String, T::Enumerable[String]))
|
17
|
+
.returns(Mayak::Monads::Try[T::Array[Value]])
|
18
|
+
}
|
19
|
+
def decode(csv)
|
20
|
+
end
|
21
|
+
|
22
|
+
sig {
|
23
|
+
type_parameters(:NewValue)
|
24
|
+
.params(blk: T.proc.params(arg: Value).returns(T.type_parameter(:NewValue)))
|
25
|
+
.returns(::Mayak::Csv::Decoder[T.type_parameter(:NewValue)])
|
26
|
+
}
|
27
|
+
def map(&blk)
|
28
|
+
::Mayak::Csv::Decoder::FromFunction[T.type_parameter(:NewValue)].new(fn: -> (csv) do
|
29
|
+
decode(csv).map do |values|
|
30
|
+
values.map { |value| blk.call(value) }
|
31
|
+
end
|
32
|
+
end)
|
33
|
+
end
|
34
|
+
|
35
|
+
sig {
|
36
|
+
type_parameters(:NewValue)
|
37
|
+
.params(blk: T.proc.params(arg: Value).returns(Mayak::Monads::Try[T.type_parameter(:NewValue)]))
|
38
|
+
.returns(::Mayak::Csv::Decoder[T.type_parameter(:NewValue)])
|
39
|
+
}
|
40
|
+
def map_try(&blk)
|
41
|
+
::Mayak::Csv::Decoder::FromFunction[T.type_parameter(:NewValue)].new(fn: -> (csv) do
|
42
|
+
decode(csv).flat_map do |values|
|
43
|
+
Mayak::Monads::Try.sequence(
|
44
|
+
values.map { |value| blk.call(value) }
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end)
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { params(separator: String).returns(Mayak::Csv::Decoder[T::Hash[String, String]]) }
|
51
|
+
def self.hash_decoder_strict(separator: ",")
|
52
|
+
HashDecoderStrict.new(separator: separator)
|
53
|
+
end
|
54
|
+
|
55
|
+
sig { params(separator: String).returns(Mayak::Csv::Decoder[T::Hash[String, T.nilable(String)]]) }
|
56
|
+
def self.hash_decoder(separator: ",")
|
57
|
+
HashDecoder.new(separator: separator)
|
58
|
+
end
|
59
|
+
|
60
|
+
class HashDecoder < T::Struct
|
61
|
+
extend T::Sig
|
62
|
+
extend T::Generic
|
63
|
+
|
64
|
+
Value = type_member {{ fixed: T::Hash[String, T.nilable(String)] }}
|
65
|
+
|
66
|
+
const :separator, String, default: ","
|
67
|
+
|
68
|
+
include ::Mayak::Csv::Decoder
|
69
|
+
|
70
|
+
sig {
|
71
|
+
override
|
72
|
+
.params(csv: T.any(String, T::Enumerable[String]))
|
73
|
+
.returns(Mayak::Monads::Try[T::Array[Value]])
|
74
|
+
}
|
75
|
+
def decode(csv)
|
76
|
+
csv_string = begin
|
77
|
+
case csv
|
78
|
+
when String then csv
|
79
|
+
else csv.to_a.join("\n")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
Mayak::Monads::Try::Success.new(CSV.parse(csv_string, headers: :first_row, col_sep: separator).map(&:to_h))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class HashDecoderStrict < T::Struct
|
87
|
+
extend T::Sig
|
88
|
+
extend T::Generic
|
89
|
+
|
90
|
+
Value = type_member {{ fixed: T::Hash[String, String] }}
|
91
|
+
|
92
|
+
const :separator, String, default: ","
|
93
|
+
|
94
|
+
include ::Mayak::Csv::Decoder
|
95
|
+
|
96
|
+
sig {
|
97
|
+
override
|
98
|
+
.params(csv: T.any(String, T::Enumerable[String]))
|
99
|
+
.returns(Mayak::Monads::Try[T::Array[Value]])
|
100
|
+
}
|
101
|
+
def decode(csv)
|
102
|
+
lines = begin
|
103
|
+
case csv
|
104
|
+
when String then csv.split("\n")
|
105
|
+
else csv.to_a
|
106
|
+
end
|
107
|
+
end
|
108
|
+
header, *rows = lines
|
109
|
+
return Mayak::Monads::Try::Success.new([]) if rows.nil? || rows.empty?
|
110
|
+
return Mayak::Monads::Try::Success.new([]) if header.nil? || header.empty?
|
111
|
+
keys = header.split(separator)
|
112
|
+
parse_results = rows.map.with_index do |row, index|
|
113
|
+
values = row.split(separator)
|
114
|
+
if values.length != keys.length
|
115
|
+
Mayak::Monads::Try::Failure.new(
|
116
|
+
::Mayak::Csv::ParseError.new(build_error_message(keys.length, row.length, index))
|
117
|
+
)
|
118
|
+
else
|
119
|
+
keys.zip(values).to_h
|
120
|
+
end
|
121
|
+
end
|
122
|
+
Mayak::Monads::Try.sequence(parse_results).map(&:to_h)
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
sig { params(keys_length: Integer, rows_length: Integer, index: Integer).returns(String) }
|
128
|
+
def build_error_message(keys_length, rows_length, index)
|
129
|
+
"Invalid number of columns on line #{index + 2}: expected #{keys_length}, found #{rows_length}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class FromFunction < T::Struct
|
134
|
+
extend T::Sig
|
135
|
+
extend T::Generic
|
136
|
+
|
137
|
+
Value = type_member
|
138
|
+
|
139
|
+
include ::Mayak::Csv::Decoder
|
140
|
+
|
141
|
+
const :fn, T.proc.params(csv: T.any(String, T::Enumerable[String])).returns(Mayak::Monads::Try[T::Array[Value]])
|
142
|
+
|
143
|
+
sig {
|
144
|
+
override
|
145
|
+
.params(csv: T.any(String, T::Enumerable[String]))
|
146
|
+
.returns(Mayak::Monads::Try[T::Array[Value]])
|
147
|
+
}
|
148
|
+
def decode(csv)
|
149
|
+
fn.call(csv)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Csv
|
6
|
+
class Document < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Value = type_member
|
11
|
+
|
12
|
+
const :header, Header[Value]
|
13
|
+
const :body, Body[Value]
|
14
|
+
|
15
|
+
sig { params(separator: String).returns(String) }
|
16
|
+
def serialize_to_csv(separator: ",")
|
17
|
+
buffer = String.new
|
18
|
+
buffer << header.serialize_to_csv(separator: separator)
|
19
|
+
buffer << "\n"
|
20
|
+
body.rows.each do |row|
|
21
|
+
buffer << row.serialize_to_csv(separator: separator)
|
22
|
+
buffer << "\n"
|
23
|
+
end
|
24
|
+
buffer
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Csv
|
6
|
+
module Encoder
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Value = type_member
|
11
|
+
|
12
|
+
abstract!
|
13
|
+
|
14
|
+
sig { abstract.params(values: T::Enumerable[Value]).returns(String) }
|
15
|
+
def encode(values)
|
16
|
+
end
|
17
|
+
|
18
|
+
class FromFunction < T::Struct
|
19
|
+
extend T::Sig
|
20
|
+
extend T::Generic
|
21
|
+
|
22
|
+
Value = type_member
|
23
|
+
|
24
|
+
include ::Mayak::Csv::Encoder
|
25
|
+
|
26
|
+
const :fn, T.proc.params(value: T::Enumerable[Value]).returns(String)
|
27
|
+
|
28
|
+
sig { override.params(values: T::Enumerable[Value]).returns(String) }
|
29
|
+
def encode(values)
|
30
|
+
fn.call(values)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Csv
|
6
|
+
class Header < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Value = type_member
|
11
|
+
|
12
|
+
const :columns, T::Array[Column[Value]]
|
13
|
+
|
14
|
+
sig {
|
15
|
+
params(
|
16
|
+
name: String,
|
17
|
+
serializer: T.proc.params(value: Value).returns(String)
|
18
|
+
).returns(::Mayak::Csv::Header[Value])
|
19
|
+
}
|
20
|
+
def with_column(name, &serializer)
|
21
|
+
new_column = ::Mayak::Csv::Column[Value].new(
|
22
|
+
name: name,
|
23
|
+
serializer: -> (value) { serializer.call(value) }
|
24
|
+
)
|
25
|
+
Header[Value].new(columns: columns.concat([new_column]))
|
26
|
+
end
|
27
|
+
|
28
|
+
sig { params(values: T::Enumerable[Value]).returns(Body[Value]) }
|
29
|
+
def build_body(values)
|
30
|
+
rows = values.map do |value|
|
31
|
+
Row.new(cells: columns.map { |column| [column, column.serializer.call(value)] })
|
32
|
+
end
|
33
|
+
Body.new(rows: rows)
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { params(values: T::Enumerable[Value]).returns(Document[Value]) }
|
37
|
+
def build_document(values)
|
38
|
+
Document[Value].new(header: self, body: build_body(values))
|
39
|
+
end
|
40
|
+
|
41
|
+
sig { params(separator: String).returns(String) }
|
42
|
+
def serialize_to_csv(separator: ",")
|
43
|
+
columns.map(&:name).join(separator)
|
44
|
+
end
|
45
|
+
|
46
|
+
sig { params(separator: String).returns(::Mayak::Csv::Encoder[Value]) }
|
47
|
+
def to_encoder(separator: ",")
|
48
|
+
::Mayak::Csv::Encoder::FromFunction[Value].new(
|
49
|
+
fn: -> (values) { build_document(values).serialize_to_csv(separator: separator) }
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mayak
|
5
|
+
module Csv
|
6
|
+
class Row < T::Struct
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
9
|
+
|
10
|
+
Value = type_member
|
11
|
+
|
12
|
+
const :cells, T::Array[[Column[Value], String]]
|
13
|
+
|
14
|
+
sig { params(separator: String).returns(String) }
|
15
|
+
def serialize_to_csv(separator: ",")
|
16
|
+
cells.map { |cell| cell[1] }.join(separator)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/mayak/decoder.rb
CHANGED
@@ -9,15 +9,51 @@ module Mayak
|
|
9
9
|
|
10
10
|
abstract!
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
In = type_member(:in)
|
13
|
+
Out = type_member(:out)
|
14
14
|
|
15
15
|
sig {
|
16
16
|
abstract
|
17
|
-
.params(response:
|
18
|
-
.returns(Mayak::Monads::Try[
|
17
|
+
.params(response: In)
|
18
|
+
.returns(Mayak::Monads::Try[Out])
|
19
19
|
}
|
20
20
|
def decode(response)
|
21
21
|
end
|
22
|
+
|
23
|
+
sig {
|
24
|
+
type_parameters(:In2)
|
25
|
+
.params(blk: T.proc.params(arg0: Out).returns(T.type_parameter(:In2)))
|
26
|
+
.returns(::Mayak::Decoder[In, T.type_parameter(:In2)])
|
27
|
+
}
|
28
|
+
def map(&blk)
|
29
|
+
::Mayak::Decoder::Implementation[In, T.type_parameter(:In2)].new do |entity|
|
30
|
+
decode(entity).map { |result| blk.call(result) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Implementation
|
35
|
+
extend T::Sig
|
36
|
+
extend T::Generic
|
37
|
+
extend T::Helpers
|
38
|
+
|
39
|
+
include ::Mayak::Decoder
|
40
|
+
|
41
|
+
In = type_member
|
42
|
+
Out = type_member
|
43
|
+
|
44
|
+
sig { params(function: T.proc.params(response: In).returns(::Mayak::Monads::Try[Out])).void }
|
45
|
+
def initialize(&function)
|
46
|
+
@function = T.let(function, T.proc.params(response: In).returns(::Mayak::Monads::Try[Out]))
|
47
|
+
end
|
48
|
+
|
49
|
+
sig {
|
50
|
+
override
|
51
|
+
.params(response: In)
|
52
|
+
.returns(Mayak::Monads::Try[Out])
|
53
|
+
}
|
54
|
+
def decode(response)
|
55
|
+
@function.call(response)
|
56
|
+
end
|
57
|
+
end
|
22
58
|
end
|
23
59
|
end
|
data/lib/mayak/encoder.rb
CHANGED
@@ -9,40 +9,40 @@ module Mayak
|
|
9
9
|
|
10
10
|
abstract!
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
In = type_member(:in)
|
13
|
+
Out = type_member(:out)
|
14
14
|
|
15
|
-
sig { abstract.params(
|
16
|
-
def encode(
|
15
|
+
sig { abstract.params(input: In).returns(Out) }
|
16
|
+
def encode(input)
|
17
17
|
end
|
18
18
|
|
19
19
|
sig {
|
20
|
-
type_parameters(:
|
21
|
-
.params(blk: T.proc.params(arg0:
|
22
|
-
.returns(::Mayak::Encoder[
|
20
|
+
type_parameters(:In2)
|
21
|
+
.params(blk: T.proc.params(arg0: Out).returns(T.type_parameter(:In2)))
|
22
|
+
.returns(::Mayak::Encoder[In, T.type_parameter(:In2)])
|
23
23
|
}
|
24
|
-
def
|
25
|
-
::Mayak::Encoder::
|
24
|
+
def map(&blk)
|
25
|
+
::Mayak::Encoder::Implementation[In, T.type_parameter(:In2)].new do |entity|
|
26
26
|
blk.call(encode(entity))
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
class
|
30
|
+
class Implementation
|
31
31
|
extend T::Sig
|
32
32
|
extend T::Generic
|
33
33
|
extend T::Helpers
|
34
34
|
|
35
35
|
include ::Mayak::Encoder
|
36
36
|
|
37
|
-
|
38
|
-
|
37
|
+
In = type_member
|
38
|
+
Out = type_member
|
39
39
|
|
40
|
-
sig { params(function: T.proc.params(
|
40
|
+
sig { params(function: T.proc.params(in: In).returns(Out)).void }
|
41
41
|
def initialize(&function)
|
42
|
-
@function = T.let(function, T.proc.params(
|
42
|
+
@function = T.let(function, T.proc.params(in: In).returns(Out))
|
43
43
|
end
|
44
44
|
|
45
|
-
sig { override.params(entity:
|
45
|
+
sig { override.params(entity: In).returns(Out) }
|
46
46
|
def encode(entity)
|
47
47
|
@function.call(entity)
|
48
48
|
end
|
data/lib/mayak/http/decoder.rb
CHANGED
@@ -14,8 +14,8 @@ module Mayak
|
|
14
14
|
|
15
15
|
include ::Mayak::Decoder
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
In = type_member(:in) {{ fixed: ::Mayak::Http::Request }}
|
18
|
+
Out = type_member(:out)
|
19
19
|
|
20
20
|
sig {
|
21
21
|
type_parameters(:A)
|
@@ -33,13 +33,13 @@ module Mayak
|
|
33
33
|
|
34
34
|
include ::Mayak::Http::Decoder
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
In = type_member(:in) {{ fixed: ::Mayak::Http::Request }}
|
37
|
+
Out = type_member(:out) {{ fixed: ::Mayak::Http::Request }}
|
38
38
|
|
39
39
|
sig {
|
40
40
|
override
|
41
|
-
.params(response:
|
42
|
-
.returns(Mayak::Monads::Try[
|
41
|
+
.params(response: In)
|
42
|
+
.returns(Mayak::Monads::Try[Out])
|
43
43
|
}
|
44
44
|
def decode(response)
|
45
45
|
Mayak::Monads::Try::Success.new(response)
|
@@ -53,15 +53,15 @@ module Mayak
|
|
53
53
|
|
54
54
|
include ::Mayak::Http::Decoder
|
55
55
|
|
56
|
-
|
57
|
-
|
56
|
+
In = type_member {{ fixed: ::Mayak::Http::Request }}
|
57
|
+
Out = type_member
|
58
58
|
|
59
|
-
const :decoder, T.proc.params(arg: String).returns(Mayak::Monads::Try[
|
59
|
+
const :decoder, T.proc.params(arg: String).returns(Mayak::Monads::Try[Out])
|
60
60
|
|
61
61
|
sig {
|
62
62
|
override
|
63
|
-
.params(response:
|
64
|
-
.returns(Mayak::Monads::Try[
|
63
|
+
.params(response: In)
|
64
|
+
.returns(Mayak::Monads::Try[Out])
|
65
65
|
}
|
66
66
|
def decode(response)
|
67
67
|
decoder.call(response.body || "")
|
data/lib/mayak/http/encoder.rb
CHANGED
@@ -12,8 +12,8 @@ module Mayak
|
|
12
12
|
|
13
13
|
include ::Mayak::Encoder
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
In = type_member
|
16
|
+
Out = type_member {{ fixed: Mayak::Http::Response }}
|
17
17
|
|
18
18
|
class IdentityEncoder
|
19
19
|
extend T::Sig
|
@@ -22,36 +22,15 @@ module Mayak
|
|
22
22
|
|
23
23
|
include ::Mayak::Http::Encoder
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
In = type_member {{ fixed: ::Mayak::Http::Response }}
|
26
|
+
Out = type_member {{ fixed: Mayak::Http::Response }}
|
27
27
|
|
28
|
-
sig { override.params(entity:
|
28
|
+
sig { override.params(entity: In).returns(Out) }
|
29
29
|
def encode(entity)
|
30
30
|
entity
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
class FromFunction
|
35
|
-
extend T::Sig
|
36
|
-
extend T::Generic
|
37
|
-
extend T::Helpers
|
38
|
-
|
39
|
-
include ::Mayak::Http::Encoder
|
40
|
-
|
41
|
-
ResponseEntity = type_member
|
42
|
-
ResponseType = type_member {{ fixed: Mayak::Http::Response }}
|
43
|
-
|
44
|
-
sig { params(function: T.proc.params(response: ResponseEntity).returns(ResponseType)).void }
|
45
|
-
def initialize(&function)
|
46
|
-
@function = T.let(function, T.proc.params(response: ResponseEntity).returns(ResponseType))
|
47
|
-
end
|
48
|
-
|
49
|
-
sig { override.params(entity: ResponseEntity).returns(ResponseType) }
|
50
|
-
def encode(entity)
|
51
|
-
@function.call(entity)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
34
|
class FromHashSerializableJson < T::Struct
|
56
35
|
extend T::Sig
|
57
36
|
extend T::Generic
|
@@ -62,10 +41,10 @@ module Mayak
|
|
62
41
|
const :default_status, Integer
|
63
42
|
const :default_headers, T::Hash[String, String]
|
64
43
|
|
65
|
-
|
66
|
-
|
44
|
+
In = type_member {{ fixed: ::Mayak::HashSerializable }}
|
45
|
+
Out = type_member {{ fixed: Mayak::Http::Response }}
|
67
46
|
|
68
|
-
sig { override.params(entity:
|
47
|
+
sig { override.params(entity: In).returns(Out) }
|
69
48
|
def encode(entity)
|
70
49
|
Mayak::Http::Response.new(
|
71
50
|
status: default_status,
|
data/lib/mayak/json/encoder.rb
CHANGED
@@ -14,29 +14,8 @@ module Mayak
|
|
14
14
|
|
15
15
|
include ::Mayak::Encoder
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
class FromFunction
|
21
|
-
extend T::Sig
|
22
|
-
extend T::Generic
|
23
|
-
extend T::Helpers
|
24
|
-
|
25
|
-
include ::Mayak::Encoder
|
26
|
-
|
27
|
-
ResponseEntity = type_member
|
28
|
-
ResponseType = type_member {{ fixed: ::Mayak::Json::JsonType }}
|
29
|
-
|
30
|
-
sig { params(function: T.proc.params(response: ResponseEntity).returns(ResponseType)).void }
|
31
|
-
def initialize(&function)
|
32
|
-
@function = T.let(function, T.proc.params(response: ResponseEntity).returns(ResponseType))
|
33
|
-
end
|
34
|
-
|
35
|
-
sig { override.params(entity: ResponseEntity).returns(ResponseType) }
|
36
|
-
def encode(entity)
|
37
|
-
@function.call(entity)
|
38
|
-
end
|
39
|
-
end
|
17
|
+
In = type_member
|
18
|
+
Out = type_member {{ fixed: ::Mayak::Json::JsonType }}
|
40
19
|
end
|
41
20
|
end
|
42
21
|
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# Lazy
|
2
|
+
|
3
|
+
`Lazy` classs represents a value that evaluates only during it's access, and evaluates only once
|
4
|
+
during the first access. Basically, `Lazy` wraps a block of code (thunk) that returns a value (`Lazy` has single type parameter of a value), and executes only when the value accessed for the first time
|
5
|
+
and then stores it afterward.
|
6
|
+
|
7
|
+
In order to build `Lazy` a type parameter of value holded should be provided as well as a block that computes a value of the type.
|
8
|
+
Note that the block is not executed right away.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
lazy1 = ::Mayak::Lazy[Integer].new { 1 }
|
12
|
+
|
13
|
+
buffer = []
|
14
|
+
lazy2 = ::Mayak::Lazy[Integer].new do
|
15
|
+
buffer << 1
|
16
|
+
1
|
17
|
+
end
|
18
|
+
buffer
|
19
|
+
#> []
|
20
|
+
```
|
21
|
+
|
22
|
+
To access the value call `#value`. If the value is not yet computed, provided block will be executed and its result will be stored. Further invokations
|
23
|
+
of this method won't execute the block again.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
buffer = []
|
27
|
+
lazy = ::Mayak::Lazy[Integer].new do
|
28
|
+
buffer << 1
|
29
|
+
1
|
30
|
+
end
|
31
|
+
buffer
|
32
|
+
#> []
|
33
|
+
|
34
|
+
# Will execute the block and return the computed value.
|
35
|
+
lazy.value
|
36
|
+
#> 1
|
37
|
+
buffer
|
38
|
+
#> [1]
|
39
|
+
|
40
|
+
# Will return the memoized value, but won't call the block again
|
41
|
+
lazy.value
|
42
|
+
#> 1
|
43
|
+
buffer
|
44
|
+
#> [1]
|
45
|
+
```
|
46
|
+
|
47
|
+
`Lazy` can be used in situations, when we want to inject some dependency into some class or method, but it may not be used, and the computation or aacquisition of the dependency may be cosftul. In this cases, it's acquisitation may be wrapped in lazy.
|
48
|
+
|
49
|
+
In more imperative style
|
50
|
+
```ruby
|
51
|
+
sig { params(env_variable: String, file_content: ::Mayak::Lazy[String], default: String).returns(String) }
|
52
|
+
def fetch_config(env_variable, file_content, default)
|
53
|
+
from_environment = ENV[env_variable]
|
54
|
+
if env.empty?
|
55
|
+
file_config = ::Core::Json.parse(file_content.value).success_or({})
|
56
|
+
from_file = file_config["configuration"]
|
57
|
+
if from_file.empty?
|
58
|
+
default
|
59
|
+
else
|
60
|
+
from_file
|
61
|
+
end
|
62
|
+
else
|
63
|
+
from_environment
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
Using Mayak monads:
|
69
|
+
```ruby
|
70
|
+
include ::Mayak::Monads::Maybe::Mixin
|
71
|
+
|
72
|
+
sig { params(env_variable: String, file_content: ::Mayak::Lazy[String], default: String).returns(String) }
|
73
|
+
def fetch_config(env_variable, file_content, default)
|
74
|
+
Maybe(ENV[env_variable])
|
75
|
+
.recover_with_maybe(::Core::Json.parse(file_content.value).to_maybe)
|
76
|
+
.flat_map { |json| Maybe(json["configuration"]) }
|
77
|
+
.value_or(default)
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
This method receives name of environment variable, and file content as lazy value. The method
|
82
|
+
tries to read the environment variable, and if it's not present and reads the file content to find the configuration.
|
83
|
+
`Lazy` allows to incapsulate behaviour of reading from file, so it can be passed as dependency, method `#fetch_config` doesn't
|
84
|
+
know anything about reading from file, but because of usage of `lazy` we can postpone it's execution thus avoiding unnecessary work.
|
85
|
+
|
86
|
+
`Lazy` can be transformed via methods `#map` and `#flat_map`.
|
87
|
+
|
88
|
+
Method `#map` allows to transform value inside `Lazy` without triggering executing. Note that `#map` returns
|
89
|
+
a new instance without mutating previous `Lazy`.
|
90
|
+
```ruby
|
91
|
+
int_lazy = ::Mayak::Lazy[Integer].new do
|
92
|
+
puts("On initialize")
|
93
|
+
1
|
94
|
+
end
|
95
|
+
string_lazy = int_lazy.map do |int|
|
96
|
+
puts("On mapping")
|
97
|
+
int.to_s
|
98
|
+
end
|
99
|
+
int_lazy.value # 1
|
100
|
+
#> On initialize
|
101
|
+
|
102
|
+
string_lazy.value # "1"
|
103
|
+
#> On initialize
|
104
|
+
#> On mapping
|
105
|
+
|
106
|
+
sig { params(file_content: ::Mayak::Lazy[String]).returns(::Mayak::Lazy[Maybe[String]]) }
|
107
|
+
def file_content_config(file_content)
|
108
|
+
file_content.map do |file_content|
|
109
|
+
::Core::Json
|
110
|
+
.parse(file_content.value)
|
111
|
+
.to_maybe
|
112
|
+
.flat_map { |json| Maybe(json["configuration"]) }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
Method `#flat_map` allows to chain lazy computations. It receives a block, that builds a new `Lazy` value from the value of original
|
118
|
+
`Lazy` and returns a new instance of `Lazy`.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
sig { params(env_name: String).returns(::Mayak::Lazy[String]) }
|
122
|
+
def lazy_env(env_name)
|
123
|
+
::Mayak::Lazy[String].new { ENV[env_name] }
|
124
|
+
end
|
125
|
+
|
126
|
+
env_variable_name = ::Mayak::Lazy[String].new { "VARIABLE" }
|
127
|
+
env_variable = env_variable_name.flat_map { |env_name| lazy_env(env_name) }
|
128
|
+
```
|
129
|
+
|
130
|
+
This may be useful when want to perform a lazy computation based on result of some other lazy computation without enforcing the evaluation.
|
131
|
+
|
132
|
+
For example we have a file that contains list of file names. We can build a lazy computation that read all lines from this code.
|
133
|
+
```ruby
|
134
|
+
sig { params(file_name: String).returns(::Mayak::Lazy[T::Array[String]]) }
|
135
|
+
def read_file_lines(file_name)
|
136
|
+
::Mayak::Lazy[T::Array[String]].new { File.read(file_name).split }
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
Let's we want to read all filenames from the root file, and then read the first file lazily. In this cases, the lazy computation can be chained via `#flat_map`:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
sig { params(file_name: String).returns(::Mayak::Lazy[T::Array[String]]) }
|
144
|
+
def read_first_file(file_name)
|
145
|
+
read_file_lines(file_name).flat_map do |file_names|
|
146
|
+
Maybe(file_names.first)
|
147
|
+
.filter(&:empty?)
|
148
|
+
.map { |file| read_file_lines(file) }
|
149
|
+
.value_or(::Mayak::Lazy[T::Array[String]].new { [] })
|
150
|
+
end
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
In order to combine two lazies of different types into a single one, method `#combine` can be used.
|
155
|
+
This method receives another lazy (it can be lazy of different type), and a block
|
156
|
+
and returns a lazy containing result of applying passed blocked to values calculated by lazies.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
class ConfigFiles < T::Struct
|
160
|
+
const :database_config_file, ::File
|
161
|
+
const :server_config_file, ::File
|
162
|
+
end
|
163
|
+
|
164
|
+
sig { returns(::Mayak::Lazy[File]) }
|
165
|
+
def database_config_file
|
166
|
+
::Mayak::Lazy[File].new { File.new(DATABASE_CONFIG_FILE_NAME, "r") }
|
167
|
+
end
|
168
|
+
|
169
|
+
sig { returns(::Mayak::Lazy[File]) }
|
170
|
+
def server_config_file
|
171
|
+
::Mayak::Lazy[File].new { File.new(SERVER_CONFIG_FILE_NAME, "r") }
|
172
|
+
end
|
173
|
+
|
174
|
+
sig { returns(::Mayak::Lazy[ConfigFiles]) }
|
175
|
+
def config_files
|
176
|
+
database_config_file.combine(server_config_file) do |db_file, server_file|
|
177
|
+
ConfigFiles.new(
|
178
|
+
database_config_file: database_config_file,
|
179
|
+
server_config_file: server_file
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
The same behaviour can be achieved with a method `.combine_two`:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
sig { returns(::Mayak::Lazy[ConfigFiles]) }
|
189
|
+
def config_files
|
190
|
+
::Mayak::Lazy.combine_two(database_config_file, server_config_file) do |db_file, server_file|
|
191
|
+
ConfigFiles.new(
|
192
|
+
database_config_file: database_config_file,
|
193
|
+
server_config_file: server_file
|
194
|
+
)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
There are also methods `.combine_three`, `.combine_four` upto `.combine_sevel` to combine multiple lazies of diffent types.
|
200
|
+
|
201
|
+
If you need to combined multiple lazies containing the same value, you can use `.combine_many`. It works
|
202
|
+
as `Array#reduce`: receives an array of lazies containing the same type, initial value of result type, and a block
|
203
|
+
receiving accumulator value of result type, and value of next lazy.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
sig { returns(::Mayak::Lazy[Integer]) }
|
207
|
+
def lazy
|
208
|
+
::Mayak::Lazy.combine_many(
|
209
|
+
[::Mayak::Lazy[Integer].new(1), ::Mayak::Lazy[Integer].new(2), ::Mayak::Lazy[Integer].new(3)],
|
210
|
+
0
|
211
|
+
) { |acc, value| acc + value }
|
212
|
+
end
|
213
|
+
|
214
|
+
lazy.value # 10
|
215
|
+
```
|
216
|
+
|
217
|
+
If you need to transform array of lazies of some value into lazy of array of the value, you can use `.sequence` method.
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
sig { returns(::Mayak::Lazy[T::Array[Integer]]) }
|
221
|
+
def lazy
|
222
|
+
::Mayak::Lazy.sequence([::Mayak::Lazy[Integer].new(1), ::Mayak::Lazy[Integer].new(2), ::Mayak::Lazy[Integer].new(3)])
|
223
|
+
end
|
224
|
+
|
225
|
+
lazy.value # [1, 2, 3]
|
226
|
+
```
|
data/lib/mayak.rb
CHANGED
@@ -30,7 +30,6 @@ require_relative 'mayak/http/request'
|
|
30
30
|
require_relative 'mayak/http/response'
|
31
31
|
require_relative 'mayak/http/verb'
|
32
32
|
require_relative 'mayak/http/client'
|
33
|
-
require_relative 'mayak/http/codec'
|
34
33
|
|
35
34
|
require_relative 'mayak/monads/maybe'
|
36
35
|
require_relative 'mayak/monads/result'
|
data/mayak.gemspec
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "mayak"
|
7
|
-
spec.version = "0.
|
7
|
+
spec.version = "0.2.0"
|
8
8
|
spec.summary = "Set of fully typed utility classes and interfaces integrated with Sorbet."
|
9
9
|
spec.description = spec.summary
|
10
10
|
spec.authors = ["Daniil Bober"]
|
@@ -18,6 +18,5 @@ Gem::Specification.new do |spec|
|
|
18
18
|
|
19
19
|
spec.add_development_dependency "bundler"
|
20
20
|
spec.add_development_dependency "rspec"
|
21
|
-
spec.add_development_dependency "parlour"
|
22
21
|
spec.add_development_dependency "tapioca"
|
23
22
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mayak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniil Bober
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sorbet-runtime
|
@@ -66,20 +66,6 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: parlour
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: tapioca
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -107,9 +93,19 @@ files:
|
|
107
93
|
- lib/mayak/caching/README.md
|
108
94
|
- lib/mayak/caching/lru_cache.rb
|
109
95
|
- lib/mayak/caching/unbounded_cache.rb
|
96
|
+
- lib/mayak/codec.rb
|
110
97
|
- lib/mayak/collections/README.md
|
111
98
|
- lib/mayak/collections/priority_queue.rb
|
112
99
|
- lib/mayak/collections/queue.rb
|
100
|
+
- lib/mayak/csv/body.rb
|
101
|
+
- lib/mayak/csv/codec.rb
|
102
|
+
- lib/mayak/csv/column.rb
|
103
|
+
- lib/mayak/csv/decoder.rb
|
104
|
+
- lib/mayak/csv/document.rb
|
105
|
+
- lib/mayak/csv/encoder.rb
|
106
|
+
- lib/mayak/csv/header.rb
|
107
|
+
- lib/mayak/csv/parse_error.rb
|
108
|
+
- lib/mayak/csv/row.rb
|
113
109
|
- lib/mayak/decoder.rb
|
114
110
|
- lib/mayak/encoder.rb
|
115
111
|
- lib/mayak/failable_function.rb
|
@@ -117,7 +113,6 @@ files:
|
|
117
113
|
- lib/mayak/hash_serializable.rb
|
118
114
|
- lib/mayak/http/README.md
|
119
115
|
- lib/mayak/http/client.rb
|
120
|
-
- lib/mayak/http/codec.rb
|
121
116
|
- lib/mayak/http/decoder.rb
|
122
117
|
- lib/mayak/http/encoder.rb
|
123
118
|
- lib/mayak/http/request.rb
|
@@ -126,6 +121,7 @@ files:
|
|
126
121
|
- lib/mayak/json.rb
|
127
122
|
- lib/mayak/json/encoder.rb
|
128
123
|
- lib/mayak/lazy.rb
|
124
|
+
- lib/mayak/lazy/README.md
|
129
125
|
- lib/mayak/monads/README.md
|
130
126
|
- lib/mayak/monads/maybe.rb
|
131
127
|
- lib/mayak/monads/result.rb
|
data/lib/mayak/http/codec.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# typed: strong
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative 'encoder'
|
5
|
-
require_relative 'decoder'
|
6
|
-
|
7
|
-
module Mayak
|
8
|
-
module Http
|
9
|
-
module Codec
|
10
|
-
extend T::Sig
|
11
|
-
extend T::Generic
|
12
|
-
extend T::Helpers
|
13
|
-
|
14
|
-
abstract!
|
15
|
-
|
16
|
-
include ::Mayak::Http::Encoder
|
17
|
-
include ::Mayak::Http::Decoder
|
18
|
-
|
19
|
-
ResponseEntity = type_member
|
20
|
-
ResponseType = type_member {{ fixed: ::Mayak::Http::Response }}
|
21
|
-
RequestType = type_member {{ fixed: ::Mayak::Http::Request }}
|
22
|
-
RequestEntity = type_member
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|