u-struct 0.12.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7788d4a3412a447721c8d375a1844b9f000816ade2780925835e2a6ca222329c
4
- data.tar.gz: 938d1c98f2f495aeca842a29a352e62a5f6565e96d7aed9ad01f27e8cadd08a8
3
+ metadata.gz: 3bae44de8a2fcfcebd5838c5aa282a16ca45e96f373cfb914f84849d1f05b46d
4
+ data.tar.gz: 7c4aa02beaaebf9a455d707d5e7cc654e7b3d3932b4555588aad0db3eff04806
5
5
  SHA512:
6
- metadata.gz: a9c449b61842f114a0d90ce5b8c69d07c33599727cfb26e15a9d1455f79f7cd308d15b3d7d85f508a30c8b6717bf182b5a00dd960fb814d9cc648823fc882f01
7
- data.tar.gz: b201b72d06fde813dfca89ff231a742c1d6fba92883133c4f12419e924830238c507d85f6d8e2e06c303033d3812df42e147acabe7742d5bfbc360163c739d34
6
+ metadata.gz: d08cb82f8392d7f960cb570ca2ddea82e931dd97c2ab9b6125a56aaca5a231d100d1e5f15bafa482214d0d63cd5c7d4f14d01bbbed3d5d07a2b60dc7f46024e9
7
+ data.tar.gz: 5bdc2d780807cbf09be36ed2d79453396a756642d3b088af74a79dc1d755328fa78786ed94167a1f7e8292b417614d17c606693f46ee4d8244c69ff7675d5900
data/CHANGELOG.md CHANGED
@@ -39,11 +39,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
39
39
 
40
40
  [⬆️  Back to Top](#changelog-)
41
41
 
42
+ ## [1.0.0] - 2021-01-19
43
+
44
+ - Review and update docs and examples. ;)
45
+
46
+ [⬆️  Back to Top](#changelog-)
47
+
42
48
  ## [0.12.0] - 2021-12-22
43
49
 
44
- ### Added
50
+ ### Added
45
51
 
46
- - Add `Micro::Struct.instance` to create a struct instance from a given hash.
52
+ - Add `Micro::Struct.instance` to create a struct instance from a given hash.
47
53
  This could be useful to create constants or a singleton value.
48
54
 
49
55
  ```ruby
@@ -52,7 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
52
58
 
53
59
  person1.first_name = 'John'
54
60
 
55
- person1.first_name # => "John"
61
+ person1.first_name # => "John"
56
62
  ```
57
63
 
58
64
  You can use the instance method after defining some struct feature.
@@ -83,7 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
83
89
  person4.name # => "Rodrigo Serradura"
84
90
  ```
85
91
 
86
- - Add `Micro::Struct.with(:exposed_features)` to expose the struct's configured features.
92
+ - Add `Micro::Struct.with(:exposed_features)` to expose the struct's configured features.
87
93
  Via the methods: `.features` and `.__features__`.
88
94
 
89
95
  ```ruby
@@ -113,7 +119,7 @@ Person.features.options?(:to_ary, :readonly) # => false
113
119
 
114
120
  ## [0.11.0] - 2021-12-19
115
121
 
116
- ### Added
122
+ ### Added
117
123
 
118
124
  - Reduce the required Ruby version to `>= 2.2.0`.
119
125
  - Set up a CI with Github actions.
data/README.md CHANGED
@@ -20,6 +20,156 @@
20
20
  </a>
21
21
  </p>
22
22
 
23
+ # Table of contents: <!-- omit in toc -->
24
+ - [Introduction](#introduction)
25
+ - [Project Motivation](#project-motivation)
26
+ - [Installation](#installation)
27
+ - [Usage](#usage)
28
+ - [`Micro::Struct.new`](#microstructnew)
29
+ - [`optional:` option](#optional-option)
30
+ - [`required:` option](#required-option)
31
+ - [Defining custom methods/behavior](#defining-custom-methodsbehavior)
32
+ - [`Micro::Struct.with`](#microstructwith)
33
+ - [`:to_ary`](#to_ary)
34
+ - [`:to_hash`](#to_hash)
35
+ - [`:to_proc`](#to_proc)
36
+ - [`:readonly`](#readonly)
37
+ - [`:instance_copy`](#instance_copy)
38
+ - [`:exposed_features`](#exposed_features)
39
+ - [`Micro::Struct.instance()` or `Micro::Struct.with(...).instance()`](#microstructinstance-or-microstructwithinstance)
40
+ - [TL;DR](#tldr)
41
+ - [FAQ](#faq)
42
+ - [How to overwrite the Struct `.new` method?](#how-to-overwrite-the-struct-new-method)
43
+ - [Can I overwrite the Struct initializer?](#can-i-overwrite-the-struct-initializer)
44
+ - [Development](#development)
45
+ - [Contributing](#contributing)
46
+ - [License](#license)
47
+ - [Code of Conduct](#code-of-conduct)
48
+
49
+ ## Introduction
50
+
51
+ Ruby Struct is a versatile data structure because it can behave like an Array, Hash, and ordinary object. e.g.
52
+
53
+ ```ruby
54
+ Person = Struct.new(:first_name, :last_name)
55
+
56
+ person = Person.new('Rodrigo', 'Serradura')
57
+ # #<struct Person first_name="Rodrigo", last_name="Serradura">
58
+
59
+ # -- Ordinary object behavior --
60
+
61
+ person.first_name # "Rodrigo"
62
+ person.last_name # "Serradura"
63
+
64
+ person.first_name = 'John' # "John"
65
+ person.last_name = 'Doe' # "Doe"
66
+
67
+ person
68
+ # #<struct Person first_name="John", last_name="Doe">
69
+
70
+ # -- Hash behavior --
71
+
72
+ person[:first_name] # "Doe"
73
+ person['last_name'] # "John"
74
+
75
+ person[:first_name] = 'Rodrigo' # "Rodrigo"
76
+ person['last_name'] = 'Serradura' # "Serradura"
77
+
78
+ person
79
+ # #<struct Person first_name="Rodrigo", last_name="Serradura">
80
+
81
+
82
+ # Transforming a Struct into a Hash
83
+ person.to_h
84
+ # {:first_name=>"Rodrigo", :last_name=>"Serradura"}
85
+
86
+ # -- Array behavior --
87
+
88
+ person[0] # "Rodrigo"
89
+ person[1] # "Serradura"
90
+
91
+ person[0] = 'John' # "John"
92
+ person[1] = 'Doe' # "Doe"
93
+
94
+ person
95
+ # #<struct Person first_name="John", last_name="Doe">
96
+
97
+ # Transforming a Struct into an Array
98
+ person.to_a
99
+ # ["John", "Doe"]
100
+ ```
101
+
102
+ Because of these characteristics, structs could be excellent candidates to create different kinds of POROs (Plain Old Ruby Objects). But, it is very common to see developers avoiding its usage because of some of its behaviors, like setters or the constructor's positional arguments. The addition of keywords arguments on its constructor ([available on Ruby >= 2.5](https://www.bigbinary.com/blog/ruby-2-5-allows-creating-structs-with-keyword-arguments)) improved the experience to instantiate Struct objects. But, as it doesn't require all the arguments, some developers can still avoid its usage.
103
+
104
+ Look at the example showing the Struct's `keyword_init:` option creating a constructor with optional keyword arguments:
105
+
106
+ ```ruby
107
+ Person = Struct.new(:first_name, :last_name, keyword_init: true)
108
+
109
+ Person.superclass # Struct
110
+
111
+ Person.new
112
+ # #<struct Person first_name=nil, last_name=nil>
113
+
114
+ # Because of this, you will only see an exception
115
+ # if you pass one or more invalid keywords.
116
+
117
+ Person.new(foo: 1, bar: 2)
118
+ # ArgumentError (unknown keywords: foo, bar)
119
+ ```
120
+
121
+ ### Project Motivation
122
+
123
+ So, given this introduction, the idea of this project is to provide a way of creating Ruby Structs with some [powerful features](#microstructwith). And to start, let's see how the `Micro::Struct.new()` works.
124
+
125
+ ```ruby
126
+ require 'u-struct'
127
+
128
+ Person = Micro::Struct.new(:first_name, :last_name)
129
+
130
+ Person.superclass
131
+ # Struct
132
+
133
+ Person.new
134
+ # ArgumentError (missing keywords: :first_name, :last_name)
135
+ ```
136
+
137
+ As you can see, the struct instantiation raised an error because all of the keywords arguments are required.
138
+
139
+ But, if you need one or many optional arguments, you can use the `optional:` option to define them. e.g.
140
+
141
+ ```ruby
142
+ Person = Micro::Struct.new(:first_name, optional: :last_name)
143
+
144
+ Person.new
145
+ # ArgumentError (missing keyword: :first_name)
146
+
147
+ Person.new(first_name: 'Rodrigo')
148
+ # #<struct Person first_name="Rodrigo", last_name=nil>
149
+ ```
150
+
151
+ If you want a Struct only with optional members (or attributes), as the `keyword_init:` option does.
152
+
153
+ You can declare all attributes within the `optional:` option.
154
+
155
+ ```ruby
156
+ Person = Micro::Struct.new(optional: [:first_name, :last_name])
157
+
158
+ Person.new
159
+ # #<struct Person first_name=nil, last_name=nil>
160
+ ```
161
+
162
+ You can also use the `required:` option to define required attributes.
163
+
164
+ ```ruby
165
+ Person = Micro::Struct.new(
166
+ required: [:first_name, :last_name],
167
+ optional: [:age]
168
+ )
169
+ ```
170
+
171
+ So, what did you think? If you liked it, continue the reading to understand what this gem can do for you.
172
+
23
173
  ## Installation
24
174
 
25
175
  Add this line to your application's Gemfile:
@@ -36,45 +186,368 @@ Or install it yourself as:
36
186
 
37
187
  $ gem install u-struct
38
188
 
189
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
190
+
39
191
  ## Usage
40
192
 
193
+ ### `Micro::Struct.new`
194
+
195
+ Like `Struct.new`, you will use `Micro::Struct.new` to create your Struct classes.
196
+
197
+ The key difference is: Structs created from `Micro::Struct` will use keyword arguments in their constructors.
198
+
199
+ ```ruby
200
+ Person = Struct.new(:name) # Person
201
+ Persona = Micro::Struct.new(:name) # Persona
202
+
203
+ Person.ancestors # [Person, Struct, Enumerable, Object, Kernel, BasicObject]
204
+ Persona.ancestors # [Person, Struct, Enumerable, Object, Kernel, BasicObject]
205
+
206
+ Person.new('Rodrigo') # #<struct Person name="Rodrigo">
207
+ Persona.new(name: 'Rodrigo') # #<struct Person name="Rodrigo">
208
+
209
+ Person.new # #<struct Person name=nil>
210
+
211
+ Persona.new # ArgumentError (missing keyword: :name)
212
+ ```
213
+
214
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
215
+
216
+ #### `optional:` option
217
+
218
+ But if you need optional attributes, you can use this to define them.
219
+
220
+ ```ruby
221
+ Person = Micro::Struct.new(:name, optional: :age)
222
+
223
+ Person.new
224
+ # ArgumentError (missing keyword: :name)
225
+
226
+ Person.new(name: 'John')
227
+ # #<struct Person name="John", age=nil>
228
+ ```
229
+
230
+ Use an array to define multiple optional attributes.
231
+
232
+ ```ruby
233
+ Person = Micro::Struct.new(:name, optional: [:age, :nickname])
234
+
235
+ Person.new
236
+ # ArgumentError (missing keyword: :name)
237
+
238
+ Person.new(name: 'John')
239
+ # #<struct Person name="John", age=nil, nickname=nil>
240
+ ```
241
+
242
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
243
+
244
+ #### `required:` option
245
+
246
+ It is an alternative way to define required attributes. Use a symbol to define one or an array to define multiple attributes.
247
+
248
+ ```ruby
249
+ Person = Micro::Struct.new(
250
+ required: [:first_name, :last_name],
251
+ optional: [:age]
252
+ )
253
+
254
+ Person.new
255
+ # ArgumentError (missing keywords: :first_name, :last_name)
256
+
257
+ Person.new first_name: 'John', last_name: 'Doe'
258
+ # #<struct Person first_name="John", last_name="Doe", age=nil>
259
+ ```
260
+
261
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
262
+
263
+ #### Defining custom methods/behavior
264
+
265
+ The `Micro::Struct.new` accepts a block as a regular Struct, and you can use it to define some custom behavior/methods.
266
+
267
+ ```ruby
268
+ Person = Micro::Struct.new(:first_name, :last_name, optional: :age) do
269
+ def name
270
+ "#{first_name} #{last_name}"
271
+ end
272
+ end
273
+
274
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
275
+ # #<struct Person first_name="Rodrigo", last_name="Serradura", age=nil>
276
+
277
+ person.first_name # "Rodrigo"
278
+ person.last_name # "Serradura"
279
+ person.name # "Rodrigo Serradura"
280
+ ```
281
+
282
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
283
+
284
+ ### `Micro::Struct.with`
285
+
286
+ This method can do two things: first, it can create Struct factories; second, it sets some special behavior to their structs.
287
+
288
+ These are all of the available features which you can use (pick one, many, or all of them):
289
+ - [`:to_ary`](#to_ary)
290
+ - [`:to_hash`](#to_hash)
291
+ - [`:to_proc`](#to_proc)
292
+ - [`:readonly`](#readonly)
293
+ - [`:instance_copy`](#instance_copy)
294
+ - [`:exposed_features`](#exposed_features)
295
+
41
296
  ```ruby
42
- # Like in a regular Struct, you can define one or many attributes.
43
- # But all of them will be required by default.
297
+ ReadonlyStruct = Micro::Struct.with(:readonly, :instance_copy)
298
+
299
+ Person = ReadonlyStruct.new(:first_name, :last_name)
300
+
301
+ Person.new # ArgumentError (missing keywords: :first_name, :last_name)
302
+
303
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Rodrigues')
304
+ # #<struct Person first_name="Rodrigo", last_name="Rodrigues">
305
+
306
+ person.last_name = ''
307
+ # NoMethodError (private method `last_name=' called for #<struct Person ...>)
308
+
309
+ person[:last_name] = ''
310
+ # NoMethodError (private method `[]=' called for #<struct Person ...>)
311
+
312
+ person.with(last_name: 'Serradura')
313
+ # #<struct Person first_name="Rodrigo", last_name="Serradura">
314
+ ```
315
+
316
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
317
+
318
+ #### `:to_ary`
319
+
320
+ Defines a `#to_ary` method which will invoke the struct `#to_a` method, so if you overwrite the `#to_a` method you will also affect it.
321
+
322
+ The `#to_ary` makes Ruby know how to deconstruct an object like an array.
323
+
324
+ ```ruby
325
+ Person = Micro::Struct.with(:to_ary).new(:first_name, :last_name)
326
+
327
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
44
328
 
329
+ first_name, last_name = person
330
+
331
+ p first_name # "Rodrigo"
332
+ p last_name # "Serradura"
333
+
334
+ *first_and_last_name = person
335
+
336
+ p first_and_last_name # ["Rodrigo", "Serradura"]
337
+ ```
338
+
339
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
340
+
341
+ #### `:to_hash`
342
+
343
+ Defines a `#to_hash` method which will invoke the struct `#to_h` method, so if you overwrite the `#to_a` method you will also affect it.
344
+
345
+ The `#to_hash` makes Ruby know how to deconstruct an object like a hash.
346
+
347
+ ```ruby
348
+ Person = Micro::Struct.with(:to_hash).new(:first_name, :last_name)
349
+
350
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
351
+
352
+ def greet(first_name:, last_name:)
353
+ puts "Hi #{first_name} #{last_name}!"
354
+ end
355
+
356
+ greet(**person)
357
+ # Hi Rodrigo Serradura!
358
+ ```
359
+
360
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
361
+
362
+ #### `:to_proc`
363
+
364
+ The `#to_proc` tells Ruby how to invoke it as a block replacement (by using `&`).
365
+
366
+ The lambda returned from the `#to_proc` will require a hash as its argument.
367
+
368
+ ```ruby
369
+ Person = Micro::Struct.with(:to_proc).new(:first_name, :last_name)
370
+
371
+ [
372
+ {first_name: 'John', last_name: 'Doe'},
373
+ {first_name: 'Mary', last_name: 'Doe'}
374
+ ].map(&Person)
375
+ # [
376
+ # #<struct Person::Struct first_name="John", last_name="Doe">,
377
+ # #<struct Person::Struct first_name="Mary", last_name="Doe">
378
+ # ]
379
+ ```
380
+
381
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
382
+
383
+ #### `:readonly`
384
+
385
+ This feature sets the Struct members' writers as private.
386
+
387
+ ```ruby
388
+ Person = Micro::Struct.with(:readonly).new(:first_name, :last_name)
389
+
390
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Rodrigues')
391
+ # #<struct Person first_name="Rodrigo", last_name="Rodrigues">
392
+
393
+ person.last_name = ''
394
+ # NoMethodError (private method `last_name=' called for #<struct Person ...>)
395
+
396
+ person[:last_name] = ''
397
+ # NoMethodError (private method `[]=' called for #<struct Person ...>)
398
+ ```
399
+
400
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
401
+
402
+ #### `:instance_copy`
403
+
404
+ Creates the `#with` method, which will instantiate a struct of the same kind and reuse its current state.
405
+
406
+ ```ruby
407
+ Person = Micro::Struct.with(:instance_copy).new(:first_name, :last_name)
408
+
409
+ person = Person.new(first_name: 'Rodrigo', last_name: 'Serradura')
410
+ # => #<struct Person::Struct first_name="Rodrigo", last_name="Serradura">
411
+
412
+ person.first_name = 'John'
413
+ # => "John"
414
+
415
+ person.inspect
416
+ # => #<struct Person::Struct first_name="John", last_name="Serradura">
417
+
418
+ new_person = person.with(last_name: 'Doe')
419
+ # => #<struct Person::Struct first_name="John", last_name="Doe">
420
+
421
+ person === new_person # => false
422
+ person.equal?(new_person) # => false
423
+
424
+ person.last_name # => "Serradura"
425
+ new_person.last_name # => "Doe"
426
+ ```
427
+
428
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
429
+
430
+ #### `:exposed_features`
431
+
432
+ This feature exposes the struct's configured features. Via the methods: `.features` and `.__features__`.
433
+
434
+ ```ruby
435
+ Person = Micro::Struct.with(:exposed_features, :readonly, :to_proc).new(:name)
436
+
437
+ Person.features
438
+ # => #<struct Micro::Struct::Features::Exposed
439
+ # names=[:readonly, :to_proc],
440
+ # options={:to_ary=>false, :to_hash=>false, :to_proc=>true, :readonly=>true, :instance_copy=>false}>
441
+
442
+ Person.__features__.equal?(Person.features) # `.__features__` is an alias of `.features` method
443
+
444
+ Person.features.names # => [:readonly, :to_proc]
445
+ Person.features.options # => {:to_ary=>false, :to_hash=>false, :to_proc=>true, :readonly=>true, :instance_copy=>false}
446
+
447
+ Person.features.option?(:to_proc) # => true
448
+ Person.features.option?(:readonly) # => true
449
+
450
+ Person.features.options?(:to_proc) # => true
451
+ Person.features.options?(:readonly) # => true
452
+
453
+ Person.features.options?(:to_proc, :readonly) # => true
454
+ Person.features.options?(:to_ary, :readonly) # => false
455
+ ```
456
+
457
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
458
+
459
+ ### `Micro::Struct.instance()` or `Micro::Struct.with(...).instance()`
460
+
461
+ Creates a struct instance from a given hash. This could be useful to create constants or a singleton value.
462
+
463
+ ```ruby
464
+ person1 = Micro::Struct.instance(first_name: 'Rodrigo', last_name: 'Serradura')
465
+ # => #<struct first_name="Rodrigo", last_name="Serradura">
466
+
467
+ person1.first_name = 'John'
468
+
469
+ person1.first_name # => "John"
470
+ ```
471
+
472
+ You can also use the instance method after defining some struct feature ([`Micro::Struct.with`](#microstructwith)).
473
+
474
+ ```ruby
475
+ person2 = Micro::Struct.with(:readonly).instance(first_name: 'Rodrigo', last_name: 'Serradura')
476
+ # => #<struct first_name="Rodrigo", last_name="Serradura">
477
+
478
+ person2.first_name = 'John'
479
+ # NoMethodError (private method `first_name=' called for #<struct first_name="Rodrigo", last_name="Serradura">)
480
+ ```
481
+
482
+ And if you need some custom behavior, use a block to define them.
483
+
484
+ ```ruby
485
+ person3 = Micro::Struct.instance(first_name: 'Rodrigo', last_name: 'Serradura') do
486
+ def name
487
+ "#{first_name} #{last_name}"
488
+ end
489
+ end
490
+
491
+ person4 = Micro::Struct.with(:readonly).instance(first_name: 'Rodrigo', last_name: 'Serradura') do
492
+ def name
493
+ "#{first_name} #{last_name}"
494
+ end
495
+ end
496
+
497
+ person3.name # => "Rodrigo Serradura"
498
+ person4.name # => "Rodrigo Serradura"
499
+ ```
500
+
501
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
502
+
503
+ ### TL;DR
504
+
505
+ Like in a regular Struct, you can define one or many attributes.
506
+ But all of them will be required by default.
507
+
508
+ ```ruby
45
509
  Micro::Struct.new(:first_name, :last_name, ...)
510
+ ```
46
511
 
47
- # Use the `optional:` arg if you want some optional attributes.
512
+ Use the `optional:` arg if you want some optional attributes.
48
513
 
514
+ ```ruby
49
515
  Micro::Struct.new(:first_name, :last_name, optional: :gender)
50
516
 
51
517
  # Using `optional:` to define all attributes are optional.
52
518
 
53
519
  Micro::Struct.new(optional: [:first_name, :last_name])
520
+ ```
54
521
 
55
- # Use the `required:` arg to define required attributes.
522
+ Use the `required:` arg to define required attributes.
56
523
 
524
+ ```ruby
57
525
  Micro::Struct.new(
58
526
  required: [:first_name, :last_name],
59
527
  optional: [:gender, :age]
60
528
  )
529
+ ```
61
530
 
62
- # You can also pass a block to define custom methods.
531
+ You can also pass a block to define custom methods.
63
532
 
533
+ ```ruby
64
534
  Micro::Struct.new(:name) {}
535
+ ```
65
536
 
66
- # Available features (use one, many, or all) to create Structs with a special behavior:
67
- # .with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy, :exposed_features)
537
+ Available features (use one, many, or all) to create Structs with a special behavior:
68
538
 
69
- Micro::Struct.with(:to_ary).new(:name)
70
- Micro::Struct.with(:to_ary, :to_hash).new(:name)
71
- Micro::Struct.with(:to_ary, :to_hash, :to_proc).new(:name)
72
- Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly).new(:name)
73
- Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy).new(:name)
74
- Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy, :exposed_features).new(:name)
539
+ ```ruby
540
+ Micro::Struct.with(:to_ary)
541
+ Micro::Struct.with(:to_ary, :to_hash)
542
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc)
543
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly)
544
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy)
545
+ Micro::Struct.with(:to_ary, :to_hash, :to_proc, :readonly, :instance_copy, :exposed_features)
546
+ ```
75
547
 
76
- # All of the possible combinations to create a Ruby Struct. ;)
548
+ All of the possible combinations to create a Ruby Struct using `Micro::Struct`:
77
549
 
550
+ ```ruby
78
551
  Micro::Struct.new(*required)
79
552
  Micro::Struct.new(*required) {}
80
553
 
@@ -89,26 +562,114 @@ Micro::Struct.new(*required, optional: *) {}
89
562
 
90
563
  Micro::Struct.new(required: *, optional: *)
91
564
  Micro::Struct.new(required: *, optional: *) {}
565
+ ```
92
566
 
93
- # Any options above can be used by the `.new()` method of the struct creator returned by the `.with()` method.
567
+ Any options above can be used by the `.new()` method of the struct creator returned by the `.with()` method.
94
568
 
569
+ ```ruby
95
570
  Micro::Struct.with(*features).new(...) {}
96
571
  ```
97
572
 
573
+ Use `Micro::Struct.instance()` or `Micro::Struct.with(...).instance()` to create a struct instance from a given hash.
574
+
575
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
576
+
577
+ ## FAQ
578
+
579
+ ### How to overwrite the Struct `.new` method?
580
+
581
+ The `.new` is an alias for the `.__new__` method, so you can use `.__new__` when overwriting it.
582
+
583
+ ```ruby
584
+ module RGB
585
+ Number = ::Struct.new(:value) { def to_s; '%02x' % value; end }
586
+
587
+ Color = Micro::Struct.new(:red, :green, :blue) do
588
+ def to_hex
589
+ "##{red}#{green}#{blue}"
590
+ end
591
+ end
592
+
593
+ module Color
594
+ def self.new(r, g, b)
595
+ __new__(
596
+ red: Number.new(r),
597
+ green: Number.new(g),
598
+ blue: Number.new(b),
599
+ )
600
+ end
601
+ end
602
+ end
603
+
604
+ rgb_color = RGB::Color.new(1,5,255)
605
+ # => #<struct RGB::Color::Struct red=#<struct RGB::Number value=1>, green=#<struct RGB::Number value=5>, blue=#<struct RGB::Number value=255>>
606
+
607
+ rgb_color.to_hex
608
+ # => "#0105ff"
609
+ ```
610
+
611
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
612
+
613
+ ### Can I overwrite the Struct initializer?
614
+
615
+ Yes, you can, but the initializer must handle the arguments as positional ones.
616
+
617
+ ```ruby
618
+ RGBColor = Micro::Struct.with(:readonly, :to_ary).new(:red, :green, :blue) do
619
+ Number = ->(value) do
620
+ return value if value.is_a?(::Integer) && value >= 0 && value <= 255
621
+
622
+ raise TypeError, "#{value} must be an Integer(>= 0 and <= 255)"
623
+ end
624
+
625
+ def initialize(r, g, b)
626
+ super(Number[r], Number[g], Number[b])
627
+ end
628
+
629
+ def to_hex
630
+ '#%02x%02x%02x' % self
631
+ end
632
+ end
633
+
634
+ rgb_color = RGBColor.new(red: 1, green: 1, blue: 255)
635
+ # #<struct RGBColor red=1, green=1, blue=255>
636
+
637
+ r, g, b = rgb_color
638
+
639
+ [r,g,b]
640
+ # [1, 1, 255]
641
+
642
+ rgb_color.to_hex
643
+ # "#0101ff"
644
+
645
+ RGBColor.new(red: 1, green: -1, blue: 255)
646
+ # TypeError (-1 must be an Integer(>= 0 and <= 255))
647
+ ```
648
+
649
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
650
+
98
651
  ## Development
99
652
 
100
653
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
101
654
 
102
655
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
103
656
 
657
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
658
+
104
659
  ## Contributing
105
660
 
106
661
  Bug reports and pull requests are welcome on GitHub at https://github.com/serradura/u-struct. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/serradura/u-struct/blob/master/CODE_OF_CONDUCT.md).
107
662
 
663
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
664
+
108
665
  ## License
109
666
 
110
667
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
111
668
 
669
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
670
+
112
671
  ## Code of Conduct
113
672
 
114
673
  Everyone interacting in the Micro::Struct project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/u-struct/blob/master/CODE_OF_CONDUCT.md).
674
+
675
+ [⬆️ &nbsp;Back to Top](#table-of-contents-)
data/examples/rgb_1.rb CHANGED
@@ -6,12 +6,13 @@ gemfile do
6
6
  source 'https://rubygems.org'
7
7
 
8
8
  gem 'u-struct', path: '..'
9
- gem 'kind'
10
9
  end
11
10
 
12
11
  RGBColor = Micro::Struct.with(:readonly, :to_ary).new(:red, :green, :blue) do
13
- Number = Kind.object(name: 'Integer(>= 0 and <= 255)') do |value|
14
- value.is_a?(::Integer) && value >= 0 && value <= 255
12
+ Number = ->(value) do
13
+ return value if value.is_a?(::Integer) && value >= 0 && value <= 255
14
+
15
+ raise TypeError, "#{value} must be an Integer(>= 0 and <= 255)"
15
16
  end
16
17
 
17
18
  def initialize(r, g, b)
@@ -49,5 +50,5 @@ puts
49
50
  begin
50
51
  RGBColor.new(red: 1, green: -1, blue: 255)
51
52
  rescue => exception
52
- puts exception # Kind::Error (-1 expected to be a kind of Integer(>= 0 and <= 255))
53
+ puts exception # TypeError (-1 must be an Integer(>= 0 and <= 255))
53
54
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Struct
5
- VERSION = '0.12.0'
5
+ VERSION = '1.0.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-22 00:00:00.000000000 Z
11
+ date: 2022-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -96,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  requirements: []
99
- rubygems_version: 3.2.22
99
+ rubygems_version: 3.3.4
100
100
  signing_key:
101
101
  specification_version: 4
102
102
  summary: Create powered Ruby structs.