alba 1.1.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/dependabot.yml +26 -0
- data/.github/workflows/perf.yml +21 -0
- data/.rubocop.yml +18 -8
- data/.yardopts +2 -0
- data/CHANGELOG.md +40 -0
- data/Gemfile +4 -3
- data/README.md +377 -14
- data/SECURITY.md +12 -0
- data/alba.gemspec +2 -2
- data/benchmark/collection.rb +441 -0
- data/benchmark/{local.rb → single_resource.rb} +120 -15
- data/codecov.yml +3 -0
- data/docs/migrate_from_active_model_serializers.md +359 -0
- data/docs/migrate_from_jbuilder.md +223 -0
- data/gemfiles/all.gemfile +1 -1
- data/gemfiles/without_active_support.gemfile +1 -1
- data/gemfiles/without_oj.gemfile +1 -1
- data/lib/alba/association.rb +14 -17
- data/lib/alba/default_inflector.rb +36 -0
- data/lib/alba/deprecation.rb +14 -0
- data/lib/alba/key_transform_factory.rb +33 -0
- data/lib/alba/many.rb +1 -1
- data/lib/alba/resource.rb +226 -83
- data/lib/alba/typed_attribute.rb +61 -0
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +82 -21
- data/script/perf_check.rb +174 -0
- data/sider.yml +2 -4
- metadata +20 -9
- data/lib/alba/key_transformer.rb +0 -32
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
[![CI](https://github.com/okuramasafumi/alba/actions/workflows/main.yml/badge.svg)](https://github.com/okuramasafumi/alba/actions/workflows/main.yml)
|
3
3
|
[![codecov](https://codecov.io/gh/okuramasafumi/alba/branch/master/graph/badge.svg?token=3D3HEZ5OXT)](https://codecov.io/gh/okuramasafumi/alba)
|
4
4
|
[![Maintainability](https://api.codeclimate.com/v1/badges/fdab4cc0de0b9addcfe8/maintainability)](https://codeclimate.com/github/okuramasafumi/alba/maintainability)
|
5
|
+
[![Inline docs](http://inch-ci.org/github/okuramasafumi/alba.svg?branch=main)](http://inch-ci.org/github/okuramasafumi/alba)
|
5
6
|
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/okuramasafumi/alba)
|
6
7
|
![GitHub](https://img.shields.io/github/license/okuramasafumi/alba)
|
7
8
|
|
@@ -59,18 +60,16 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramas
|
|
59
60
|
|
60
61
|
## Features
|
61
62
|
|
62
|
-
* Resource-based serialization
|
63
|
-
* Arbitrary attribute definition
|
64
|
-
* One and many association with the ability to define them inline
|
65
|
-
* Adding condition and filter to association
|
66
|
-
* Parameters can be injected and used in attributes and associations
|
67
63
|
* Conditional attributes and associations
|
68
64
|
* Selectable backend
|
69
65
|
* Key transformation
|
70
66
|
* Root key inference
|
71
67
|
* Error handling
|
68
|
+
* Nil handling
|
72
69
|
* Resource name inflection based on association name
|
73
70
|
* Circular associations control
|
71
|
+
* [Experimental] Types for validation and conversion
|
72
|
+
* Layout
|
74
73
|
* No runtime dependencies
|
75
74
|
|
76
75
|
## Anti features
|
@@ -102,6 +101,16 @@ You can set a backend like this:
|
|
102
101
|
Alba.backend = :oj
|
103
102
|
```
|
104
103
|
|
104
|
+
#### Encoder configuration
|
105
|
+
|
106
|
+
You can also set JSON encoder directly with a Proc.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Alba.encoder = ->(object) { JSON.generate(object) }
|
110
|
+
```
|
111
|
+
|
112
|
+
You can consider setting a backend with Symbol as a shortcut to set encoder.
|
113
|
+
|
105
114
|
#### Inference configuration
|
106
115
|
|
107
116
|
You can enable inference feature using `enable_inference!` method.
|
@@ -122,7 +131,7 @@ Alba.on_error :ignore
|
|
122
131
|
|
123
132
|
For the details, see [Error handling section](#error-handling)
|
124
133
|
|
125
|
-
### Simple serialization with key
|
134
|
+
### Simple serialization with root key
|
126
135
|
|
127
136
|
```ruby
|
128
137
|
class User
|
@@ -139,7 +148,7 @@ end
|
|
139
148
|
class UserResource
|
140
149
|
include Alba::Resource
|
141
150
|
|
142
|
-
|
151
|
+
root_key :user
|
143
152
|
|
144
153
|
attributes :id, :name
|
145
154
|
|
@@ -202,12 +211,41 @@ UserResource.new(user).serialize
|
|
202
211
|
# => '{"id":1,"articles":[{"title":"Hello World!"},{"title":"Super nice"}]}'
|
203
212
|
```
|
204
213
|
|
214
|
+
You can define associations inline if you don't need a class for association.
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
class ArticleResource
|
218
|
+
include Alba::Resource
|
219
|
+
|
220
|
+
attributes :title
|
221
|
+
end
|
222
|
+
|
223
|
+
class UserResource
|
224
|
+
include Alba::Resource
|
225
|
+
|
226
|
+
attributes :id
|
227
|
+
|
228
|
+
many :articles, resource: ArticleResource
|
229
|
+
end
|
230
|
+
|
231
|
+
# This class works the same as `UserResource`
|
232
|
+
class AnotherUserResource
|
233
|
+
include Alba::Resource
|
234
|
+
|
235
|
+
attributes :id
|
236
|
+
|
237
|
+
many :articles do
|
238
|
+
attributes :title
|
239
|
+
end
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
205
243
|
### Inline definition with `Alba.serialize`
|
206
244
|
|
207
245
|
`Alba.serialize` method is a shortcut to define everything inline.
|
208
246
|
|
209
247
|
```ruby
|
210
|
-
Alba.serialize(user,
|
248
|
+
Alba.serialize(user, root_key: :foo) do
|
211
249
|
attributes :id
|
212
250
|
many :articles do
|
213
251
|
attributes :title, :body
|
@@ -216,6 +254,13 @@ end
|
|
216
254
|
# => '{"foo":{"id":1,"articles":[{"title":"Hello World!","body":"Hello World!!!"},{"title":"Super nice","body":"Really nice!"}]}}'
|
217
255
|
```
|
218
256
|
|
257
|
+
`Alba.serialize` can be used when you don't know what kind of object you serialize. For example:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
Alba.serialize(something)
|
261
|
+
# => Same as `FooResource.new(something).serialize` when `something` is an instance of `Foo`.
|
262
|
+
```
|
263
|
+
|
219
264
|
Although this might be useful sometimes, it's generally recommended to define a class for Resource.
|
220
265
|
|
221
266
|
### Inheritance and Ignorance
|
@@ -239,20 +284,21 @@ class GenericFooResource
|
|
239
284
|
attributes :id, :name, :body
|
240
285
|
end
|
241
286
|
|
242
|
-
class
|
287
|
+
class RestrictedFooResource < GenericFooResource
|
243
288
|
ignoring :id, :body
|
244
289
|
end
|
245
290
|
|
246
|
-
|
291
|
+
RestrictedFooResource.new(foo).serialize
|
247
292
|
# => '{"name":"my foo"}'
|
248
|
-
end
|
249
293
|
```
|
250
294
|
|
251
|
-
###
|
295
|
+
### Key transformation
|
252
296
|
|
253
|
-
|
297
|
+
If you want to use `transform_keys` DSL and you already have `active_support` installed, key transformation will work out of the box, using `ActiveSupport::Inflector`. If `active_support` is not around, you have 2 possibilities:
|
298
|
+
* install it
|
299
|
+
* use a [custom inflector](#custom-inflector)
|
254
300
|
|
255
|
-
With `
|
301
|
+
With `transform_keys` DSL, you can transform attribute keys.
|
256
302
|
|
257
303
|
```ruby
|
258
304
|
class User
|
@@ -278,8 +324,69 @@ UserResourceCamel.new(user).serialize
|
|
278
324
|
# => '{"id":1,"firstName":"Masafumi","lastName":"Okura"}'
|
279
325
|
```
|
280
326
|
|
327
|
+
You can also transform root key when:
|
328
|
+
|
329
|
+
* `Alba.enable_inference!` is called
|
330
|
+
* `root_key!` is called in Resource class
|
331
|
+
* `root` option of `transform_keys` is set to true or `Alba.enable_root_key_transformation!` is called.
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
Alba.enable_inference!
|
335
|
+
|
336
|
+
class BankAccount
|
337
|
+
attr_reader :account_number
|
338
|
+
|
339
|
+
def initialize(account_number)
|
340
|
+
@account_number = account_number
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
class BankAccountResource
|
345
|
+
include Alba::Resource
|
346
|
+
|
347
|
+
root_key!
|
348
|
+
|
349
|
+
attributes :account_number
|
350
|
+
transform_keys :dash, root: true
|
351
|
+
end
|
352
|
+
|
353
|
+
bank_account = BankAccount.new(123_456_789)
|
354
|
+
BankAccountResource.new(bank_account).serialize
|
355
|
+
# => '{"bank-account":{"account-number":123456789}}'
|
356
|
+
```
|
357
|
+
|
358
|
+
This behavior to transform root key will become default at version 2.
|
359
|
+
|
281
360
|
Supported transformation types are :camel, :lower_camel and :dash.
|
282
361
|
|
362
|
+
#### Custom inflector
|
363
|
+
|
364
|
+
A custom inflector can be plugged in as follows...
|
365
|
+
```ruby
|
366
|
+
Alba.inflector = MyCustomInflector
|
367
|
+
```
|
368
|
+
...and has to implement following interface (the parameter `key` is of type `String`):
|
369
|
+
```ruby
|
370
|
+
module InflectorInterface
|
371
|
+
def camelize(key)
|
372
|
+
raise "Not implemented"
|
373
|
+
end
|
374
|
+
|
375
|
+
def camelize_lower(key)
|
376
|
+
raise "Not implemented"
|
377
|
+
end
|
378
|
+
|
379
|
+
def dasherize(key)
|
380
|
+
raise "Not implemented"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
```
|
385
|
+
For example you could use `Dry::Inflector`, which implements exactly the above interface. If you are developing a `Hanami`-Application `Dry::Inflector` is around. In this case the following would be sufficient:
|
386
|
+
```ruby
|
387
|
+
Alba.inflector = Dry::Inflector.new
|
388
|
+
```
|
389
|
+
|
283
390
|
### Filtering attributes
|
284
391
|
|
285
392
|
You can filter attributes by overriding `Alba::Resource#converter` method, but it's a bit tricky.
|
@@ -343,6 +450,20 @@ user = User.new(1, nil, nil)
|
|
343
450
|
UserResource.new(user).serialize # => '{"id":1}'
|
344
451
|
```
|
345
452
|
|
453
|
+
### Default
|
454
|
+
|
455
|
+
Alba doesn't support default value for attributes, but it's easy to set a default value.
|
456
|
+
|
457
|
+
```ruby
|
458
|
+
class FooResource
|
459
|
+
attribute :bar do |foo|
|
460
|
+
foo.bar || 'default bar'
|
461
|
+
end
|
462
|
+
end
|
463
|
+
```
|
464
|
+
|
465
|
+
We believe this is clearer than using some (not implemented yet) DSL such as `default` because there are some conditions where default values should be applied (`nil`, `blank?`, `empty?` etc.)
|
466
|
+
|
346
467
|
### Inference
|
347
468
|
|
348
469
|
After `Alba.enable_inference!` called, Alba tries to infer root key and association resource name.
|
@@ -451,12 +572,254 @@ Alba.on_error do |error, object, key, attribute, resource_class|
|
|
451
572
|
end
|
452
573
|
```
|
453
574
|
|
575
|
+
### Nil handling
|
576
|
+
|
577
|
+
Sometimes we want to convert `nil` to different values such as empty string. Alba provides a flexible way to handle `nil`.
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
class User
|
581
|
+
attr_reader :id, :name, :age
|
582
|
+
|
583
|
+
def initialize(id, name = nil, age = nil)
|
584
|
+
@id = id
|
585
|
+
@name = name
|
586
|
+
@age = age
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
class UserResource
|
591
|
+
include Alba::Resource
|
592
|
+
|
593
|
+
on_nil { '' }
|
594
|
+
|
595
|
+
root_key :user, :users
|
596
|
+
|
597
|
+
attributes :id, :name, :age
|
598
|
+
end
|
599
|
+
|
600
|
+
UserResource.new(User.new(1)).serialize
|
601
|
+
# => '{"user":{"id":1,"name":"","age":""}}'
|
602
|
+
```
|
603
|
+
|
604
|
+
You can get various information via block parameters.
|
605
|
+
|
606
|
+
```ruby
|
607
|
+
class UserResource
|
608
|
+
include Alba::Resource
|
609
|
+
|
610
|
+
on_nil do |object, key|
|
611
|
+
if key == age
|
612
|
+
20
|
613
|
+
else
|
614
|
+
"User#{object.id}"
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
root_key :user, :users
|
619
|
+
|
620
|
+
attributes :id, :name, :age
|
621
|
+
end
|
622
|
+
|
623
|
+
UserResource.new(User.new(1)).serialize
|
624
|
+
# => '{"user":{"id":1,"name":"User1","age":20}}'
|
625
|
+
```
|
626
|
+
|
627
|
+
You can also set global nil handler.
|
628
|
+
|
629
|
+
```ruby
|
630
|
+
Alba.on_nil { 'default name' }
|
631
|
+
|
632
|
+
class Foo
|
633
|
+
attr_reader :name
|
634
|
+
def initialize(name)
|
635
|
+
@name = name
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
class FooResource
|
640
|
+
include Alba::Resource
|
641
|
+
|
642
|
+
key :foo
|
643
|
+
|
644
|
+
attributes :name
|
645
|
+
end
|
646
|
+
|
647
|
+
FooResource.new(Foo.new).serialize
|
648
|
+
# => '{"foo":{"name":"default name"}}'
|
649
|
+
|
650
|
+
class FooResource2
|
651
|
+
include Alba::Resource
|
652
|
+
|
653
|
+
key :foo
|
654
|
+
|
655
|
+
on_nil { '' } # This is applied instead of global handler
|
656
|
+
|
657
|
+
attributes :name
|
658
|
+
end
|
659
|
+
|
660
|
+
FooResource2.new(Foo.new).serialize
|
661
|
+
# => '{"foo":{"name":""}}'
|
662
|
+
```
|
663
|
+
|
664
|
+
### Metadata
|
665
|
+
|
666
|
+
You can set a metadata with `meta` DSL or `meta` option.
|
667
|
+
|
668
|
+
```ruby
|
669
|
+
class UserResource
|
670
|
+
include Alba::Resource
|
671
|
+
|
672
|
+
root_key :user, :users
|
673
|
+
|
674
|
+
attributes :id, :name
|
675
|
+
|
676
|
+
meta do
|
677
|
+
if object.is_a?(Enumerable)
|
678
|
+
{size: object.size}
|
679
|
+
else
|
680
|
+
{foo: :bar}
|
681
|
+
end
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
685
|
+
user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
|
686
|
+
UserResource.new([user]).serialize
|
687
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1}}'
|
688
|
+
|
689
|
+
# You can merge metadata with `meta` option
|
690
|
+
|
691
|
+
UserResource.new([user]).serialize(meta: {foo: :bar})
|
692
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1,"foo":"bar"}}'
|
693
|
+
|
694
|
+
# You can set metadata with `meta` option alone
|
695
|
+
|
696
|
+
class UserResourceWithoutMeta
|
697
|
+
include Alba::Resource
|
698
|
+
|
699
|
+
root_key :user, :users
|
700
|
+
|
701
|
+
attributes :id, :name
|
702
|
+
end
|
703
|
+
|
704
|
+
UserResource.new([user]).serialize(meta: {foo: :bar})
|
705
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"foo":"bar"}}'
|
706
|
+
```
|
707
|
+
|
708
|
+
You can use `object` method to access the underlying object and `params` to access the params in `meta` block.
|
709
|
+
|
710
|
+
Note that setting root key is required when setting a metadata.
|
711
|
+
|
454
712
|
### Circular associations control
|
455
713
|
|
714
|
+
**Note that this feature works correctly since version 1.3. In previous versions it doesn't work as expected.**
|
715
|
+
|
456
716
|
You can control circular associations with `within` option. `within` option is a nested Hash such as `{book: {authors: books}}`. In this example, Alba serializes a book's authors' books. This means you can reference `BookResource` from `AuthorResource` and vice versa. This is really powerful when you have a complex data structure and serialize certain parts of it.
|
457
717
|
|
458
718
|
For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/master/test/usecases/circular_association_test.rb)
|
459
719
|
|
720
|
+
### Experimental support of types
|
721
|
+
|
722
|
+
You can validate and convert input with types.
|
723
|
+
|
724
|
+
```ruby
|
725
|
+
class User
|
726
|
+
attr_reader :id, :name, :age, :bio, :admin, :created_at
|
727
|
+
|
728
|
+
def initialize(id, name, age, bio = '', admin = false) # rubocop:disable Style/OptionalBooleanParameter
|
729
|
+
@id = id
|
730
|
+
@name = name
|
731
|
+
@age = age
|
732
|
+
@admin = admin
|
733
|
+
@bio = bio
|
734
|
+
@created_at = Time.new(2020, 10, 10)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
class UserResource
|
739
|
+
include Alba::Resource
|
740
|
+
|
741
|
+
attributes :name, id: [String, true], age: [Integer, true], bio: String, admin: [:Boolean, true], created_at: [String, ->(object) { object.strftime('%F') }]
|
742
|
+
end
|
743
|
+
|
744
|
+
user = User.new(1, 'Masafumi OKURA', '32', 'Ruby dev')
|
745
|
+
UserResource.new(user).serialize
|
746
|
+
# => '{"name":"Masafumi OKURA","id":"1","age":32,"bio":"Ruby dev","admin":false,"created_at":"2020-10-10"}'
|
747
|
+
```
|
748
|
+
|
749
|
+
Notice that `id` and `created_at` are converted to String and `age` is converted to Integer.
|
750
|
+
|
751
|
+
If type is not correct and auto conversion is disabled (default), `TypeError` occurs.
|
752
|
+
|
753
|
+
```ruby
|
754
|
+
user = User.new(1, 'Masafumi OKURA', '32', nil) # bio is nil and auto conversion is disabled for bio
|
755
|
+
UserResource.new(user).serialize
|
756
|
+
# => TypeError, 'Attribute bio is expected to be String but actually nil.'
|
757
|
+
```
|
758
|
+
|
759
|
+
Note that this feature is experimental and interfaces are subject to change.
|
760
|
+
|
761
|
+
### Layout
|
762
|
+
|
763
|
+
Sometimes we'd like to serialize JSON into a template. In other words, we need some structure OUTSIDE OF serialized JSON. IN HTML world, we call it a "layout".
|
764
|
+
|
765
|
+
Alba supports serializing JSON in a layout. You need a file for layout and then to specify file with `layout` method.
|
766
|
+
|
767
|
+
```erb
|
768
|
+
{
|
769
|
+
"header": "my_header",
|
770
|
+
"body": <%= serialized_json %>
|
771
|
+
}
|
772
|
+
```
|
773
|
+
|
774
|
+
```ruby
|
775
|
+
class FooResource
|
776
|
+
include Alba::Resource
|
777
|
+
layout file: 'my_layout.json.erb'
|
778
|
+
end
|
779
|
+
```
|
780
|
+
|
781
|
+
Note that layout files are treated as `json` and `erb` and evaluated in a context of the resource, meaning
|
782
|
+
|
783
|
+
* A layout file must be a valid JSON
|
784
|
+
* You must write `<%= serialized_json %>` in a layout to put serialized JSON string into a layout
|
785
|
+
* You can access `params` in a layout so that you can add virtually any objects to a layout
|
786
|
+
* When you access `params`, it's usually a Hash. You can use `encode` method in a layout to convert `params` Hash into a JSON with the backend you use
|
787
|
+
* You can also access `object`, the underlying object for the resource
|
788
|
+
|
789
|
+
In case you don't want to have a file for layout, Alba lets you define and apply layouts inline:
|
790
|
+
|
791
|
+
```ruby
|
792
|
+
class FooResource
|
793
|
+
include Alba::Resource
|
794
|
+
layout inline: proc do
|
795
|
+
{
|
796
|
+
header: 'my header',
|
797
|
+
body: serializable_hash
|
798
|
+
}
|
799
|
+
end
|
800
|
+
end
|
801
|
+
```
|
802
|
+
|
803
|
+
In the example above, we specify a Proc which returns a Hash as an inline layout. In the Proc we can use `serializable_hash` method to access a Hash right before serialization.
|
804
|
+
|
805
|
+
You can also use a Proc which returns String, not a Hash, for an inline layout.
|
806
|
+
|
807
|
+
```ruby
|
808
|
+
class FooResource
|
809
|
+
include Alba::Resource
|
810
|
+
layout inline: proc do
|
811
|
+
%({
|
812
|
+
"header": "my header",
|
813
|
+
"body": #{serialized_json}
|
814
|
+
})
|
815
|
+
end
|
816
|
+
end
|
817
|
+
```
|
818
|
+
|
819
|
+
It looks similar to file layout but you must use string interpolation for method calls since it's not an ERB.
|
820
|
+
|
821
|
+
Also note that we use percentage notation here to use double quotes. Using single quotes in inline string layout causes the error which might be resolved in other ways.
|
822
|
+
|
460
823
|
### Caching
|
461
824
|
|
462
825
|
Currently, Alba doesn't support caching, primarily due to the behavior of `ActiveRecord::Relation`'s cache. See [the issue](https://github.com/rails/rails/issues/41784).
|
data/SECURITY.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Security Policy
|
2
|
+
|
3
|
+
## Supported Versions
|
4
|
+
|
5
|
+
| Version | Supported |
|
6
|
+
| ------- | ------------------ |
|
7
|
+
| 1.x.y | :white_check_mark: |
|
8
|
+
| < 1.0 | :x: |
|
9
|
+
|
10
|
+
## Reporting a Vulnerability
|
11
|
+
|
12
|
+
If you find a vulnerability of Alba, please contact me (OKURA Masafumi) via [email](masafumi.o1988@gmail.com). I'll report back within a few days.
|
data/alba.gemspec
CHANGED
@@ -7,14 +7,14 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.email = ['masafumi.o1988@gmail.com']
|
8
8
|
|
9
9
|
spec.summary = 'Alba is the fastest JSON serializer for Ruby.'
|
10
|
-
spec.description = "Alba is
|
10
|
+
spec.description = "Alba is the fastest JSON serializer for Ruby. It focuses on performance, flexibility and usability."
|
11
11
|
spec.homepage = 'https://github.com/okuramasafumi/alba'
|
12
12
|
spec.license = 'MIT'
|
13
13
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
14
14
|
|
15
15
|
spec.metadata['homepage_uri'] = spec.homepage
|
16
16
|
spec.metadata['source_code_uri'] = 'https://github.com/okuramasafumi/alba'
|
17
|
-
spec.metadata['changelog_uri'] = 'https://github.com/okuramasafumi/alba/blob/
|
17
|
+
spec.metadata['changelog_uri'] = 'https://github.com/okuramasafumi/alba/blob/main/CHANGELOG.md'
|
18
18
|
|
19
19
|
# Specify which files should be added to the gem when it is released.
|
20
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|