deco_lite 0.3.0 → 0.3.3
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/.gitignore +1 -1
- data/.reek.yml +1 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +3 -3
- data/README.md +138 -19
- data/lib/deco_lite/field_assignable.rb +2 -4
- data/lib/deco_lite/field_conflictable.rb +2 -0
- data/lib/deco_lite/field_requireable.rb +3 -1
- data/lib/deco_lite/hash_loadable.rb +2 -0
- data/lib/deco_lite/hashable.rb +5 -1
- data/lib/deco_lite/model.rb +0 -1
- data/lib/deco_lite/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77c440561731ca11332b76f0ee373e4568d4e84c11930d530d1dde9d5c097eb9
|
4
|
+
data.tar.gz: f771f70f192ae1c2b2d7a935933f7157cde64c6f44e9dc60d50e66d58e8abb05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6aa192d5c0d1c68860a4e41c31c3997307e90560d293ba6d2e1f5d30a82f537358988cfba766f34415c8321041f3844c2ac88d176a818647f13474f86171c3f6
|
7
|
+
data.tar.gz: e7910463ad7ede8f002af8bc0f46c220bcb715dce09d1690c10b3a3af7db66577acd372a607698470b2fd168a8295d3b24c1fd705601064f02e2f54e0f5ef350
|
data/.gitignore
CHANGED
data/.reek.yml
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
### 0.3.2
|
2
|
+
* Changes
|
3
|
+
* Refactor FieldAssignable to remove call to FieldCreatable#create_field_accessor as this breaks single responsibility rule; which, in this case, makes sense to remove. FieldCreatable#create_field_accessor can be called wherever creation of a attr_accessor is needed.
|
4
|
+
* Refactor specs in keeping with above changes.
|
5
|
+
* README.md changes.
|
6
|
+
* Bugs
|
7
|
+
* Fix bug where loading fields with the options: { fields: :strict } option raises an error for field that already exists.
|
8
|
+
|
9
|
+
### 0.3.1
|
10
|
+
* Changes
|
11
|
+
* Added `DecoLite::FieldRequireable::MISSING_REQUIRED_FIELD_ERROR_TYPE` for required field type errors.
|
12
|
+
* Update README.md with more examples.
|
13
|
+
|
1
14
|
### 0.3.0
|
2
15
|
* Changes
|
3
16
|
* `DecoLite::Model#new` how accepts a :hash named parameter that will load the Hash as if calling `DecoLite::Model.new.load!(hash: <hash>)`.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
deco_lite (0.3.
|
4
|
+
deco_lite (0.3.3)
|
5
5
|
activemodel (~> 7.0, >= 7.0.3.1)
|
6
6
|
activesupport (~> 7.0, >= 7.0.3.1)
|
7
7
|
immutable_struct_ex (~> 0.2.0)
|
@@ -25,7 +25,7 @@ GEM
|
|
25
25
|
docile (1.4.0)
|
26
26
|
i18n (1.12.0)
|
27
27
|
concurrent-ruby (~> 1.0)
|
28
|
-
immutable_struct_ex (0.2.
|
28
|
+
immutable_struct_ex (0.2.3)
|
29
29
|
json (2.6.2)
|
30
30
|
kwalify (0.7.2)
|
31
31
|
mad_flatter (1.0.1.pre.beta)
|
@@ -63,7 +63,7 @@ GEM
|
|
63
63
|
diff-lcs (>= 1.2.0, < 2.0)
|
64
64
|
rspec-support (~> 3.11.0)
|
65
65
|
rspec-support (3.11.0)
|
66
|
-
rubocop (1.35.
|
66
|
+
rubocop (1.35.1)
|
67
67
|
json (~> 2.3)
|
68
68
|
parallel (~> 1.10)
|
69
69
|
parser (>= 3.1.2.1)
|
data/README.md
CHANGED
@@ -13,13 +13,13 @@
|
|
13
13
|
|
14
14
|
## Introduction
|
15
15
|
|
16
|
-
DecoLite is in development. I wouldn't expect breaking changes before v1.0.0; however, I can't completely rule this out. Currently, DecoLite only supports Hashes whose keys are `Symbols`, contain no embedded spaces, and conform to Ruby `attr_accessor` naming conventions. However, I
|
16
|
+
DecoLite is in development. I wouldn't expect breaking changes before v1.0.0; however, I can't completely rule this out. Currently, DecoLite only supports Hashes whose keys are `Symbols`, contain no embedded spaces, and conform to Ruby `attr_accessor` naming conventions. However, I might try work out a reasonable solution for all this in future releases if the need is there.
|
17
17
|
|
18
|
-
TBD: Documentation regarding `DecoLite::Model` options, `DecoLite::Model#load!` options: how these work, and how they play together (in the meantime, see the specs).
|
18
|
+
TBD: Documentation regarding `DecoLite::Model` options, `DecoLite::Model#load!` options: how these work, and how they play together (e.g. options `fields: :merge` and `fields: :strict` for example; in the meantime, see the specs).
|
19
19
|
|
20
20
|
_Deco_ is a little gem that allows you to use the provided `DecoLite::Model` class (`include ActiveModel::Model`) to create Decorator classes which can be instantiated and used. Inherit from `DecoLite::Model` to create your own unique classes with custom functionality. A `DecoLite::Model` includes `ActiveModel::Model`, so validation can be applied using [ActiveModel validation helpers](https://api.rubyonrails.org/v6.1.3/classes/ActiveModel/Validations/HelperMethods.html) you are familiar with; or, you can roll your own - just like any other ActiveModel.
|
21
21
|
|
22
|
-
A `DecoLite::Model` will allow you to consume a Ruby Hash that you supply via the `DecoLite::Model#load!` method. Your supplied Ruby Hashes are used to create `attr_accessor` attributes (_"fields"_) on the model. Each attribute created, is then assigned its value from the Hash loaded.
|
22
|
+
A `DecoLite::Model` will allow you to consume a Ruby Hash that you supply via the initializer (`DecoLite::Model#new`) or via the `DecoLite::Model#load!` method. Your supplied Ruby Hashes are used to create `attr_accessor` attributes (_"fields"_) on the model. Each attribute created, is then assigned its value from the Hash loaded. Any number of hashes can be consumed using the `DecoLite::Model#load!` method.
|
23
23
|
|
24
24
|
`attr_accessor` names created are _mangled_ to include namespacing. This creates unique attribute names for nested Hashes that may include non-unique keys. For example:
|
25
25
|
|
@@ -34,10 +34,11 @@ family = {
|
|
34
34
|
}
|
35
35
|
}
|
36
36
|
```
|
37
|
-
Given the above example, DecoLite will produce the following `attr_accessors` on the `DecoLite::Model` object
|
37
|
+
Given the above example, DecoLite will produce the following `attr_accessors` on the `DecoLite::Model` object and assign the values:
|
38
38
|
|
39
39
|
```ruby
|
40
|
-
|
40
|
+
# Or DecoLite::Model.new.load!(hash: family)
|
41
|
+
model = DecoLite::Model.new(hash: family)
|
41
42
|
|
42
43
|
model.name #=> 'John Doe'
|
43
44
|
model.respond_to? :name= #=> true
|
@@ -52,17 +53,21 @@ model.wife_age #=> 30
|
|
52
53
|
model.respond_to? :wife_age= #=> true
|
53
54
|
```
|
54
55
|
|
55
|
-
`DecoLite::Model#load!` can be called _multiple times_, on the same model, with different Hashes. This could potentially cause `attr_accessor` name clashes. In order to ensure unique `attr_accessor` names, a _"namespace"_ may be _explicitly_ provided to ensure uniqueness.
|
56
|
+
`DecoLite::Model#load!` can be called _multiple times_, on the same model, with different Hashes. This could potentially cause `attr_accessor` name clashes. In order to ensure unique `attr_accessor` names, a _"namespace"_ may be _explicitly_ provided to ensure uniqueness.
|
57
|
+
|
58
|
+
For example, **continuing from the previous example;** if we were to call `DecoLite::Model#load!` a _second time_ with the following Hash, this would potentially produce `attr_accessor` name clashes:
|
56
59
|
|
57
60
|
```ruby
|
58
61
|
grandpa = {
|
59
62
|
name: 'Henry Doe',
|
60
63
|
age: 85,
|
61
64
|
}
|
62
|
-
# The :name and :age Hash keys above will produce :name/:name= and :age/:age= attr_accessors
|
65
|
+
# The :name and :age Hash keys above will produce :name/:name= and :age/:age= attr_accessors
|
66
|
+
# and clash because these were already added to the model when "John Doe" was loaded with
|
67
|
+
# the first call to DecoLite::Model.new(hash: family).
|
63
68
|
```
|
64
69
|
|
65
|
-
However, passing a `
|
70
|
+
However, passing a `:namespace` option (for example `namespace: :grandpa`) to the `DecoLite::Model#load!` method, would produce the following `attr_accessors`, ensuring their uniqueness:
|
66
71
|
|
67
72
|
```ruby
|
68
73
|
model.load!(hash: grandpa, options: { namespace: :grandpa })
|
@@ -87,6 +92,11 @@ model.respond_to? :wife_name= #=> true
|
|
87
92
|
model.wife_age #=> 30
|
88
93
|
model.respond_to? :wife_age= #=> true
|
89
94
|
```
|
95
|
+
|
96
|
+
### For more examples and usage
|
97
|
+
|
98
|
+
For more examples and usage, see the [Examples and usage](#examples-and-usage) and [Mode examples and usage](#more-examples-and-usage) sections; there is also an "I want to..." section with examples you might encounter when using `DecoLite`.
|
99
|
+
|
90
100
|
## Use cases
|
91
101
|
|
92
102
|
### General
|
@@ -108,9 +118,7 @@ class ViewModel < DecoLite::Model
|
|
108
118
|
end
|
109
119
|
end
|
110
120
|
|
111
|
-
view_model = ViewModel.new
|
112
|
-
|
113
|
-
view_model.load!(hash: { first: 'John', last: 'Doe' })
|
121
|
+
view_model = ViewModel.new(hash: { first: 'John', last: 'Doe' })
|
114
122
|
|
115
123
|
view_model.valid?
|
116
124
|
#=> true
|
@@ -188,22 +196,131 @@ model.wife_info_address #=> 1 street, boonton, nj 07005
|
|
188
196
|
|
189
197
|
#### Add validators to my model
|
190
198
|
|
191
|
-
Simply add your `ActiveModel` validators just like you would any other `ActiveModel::Model` validator. However, be aware that (currently), any attribute (field)
|
199
|
+
Simply add your `ActiveModel` validators just like you would any other `ActiveModel::Model` validator. However, be aware that (currently), any attribute (field) having an _explicit validation_ associated with it, will automatically cause an `attr_accessor` to be created for that field; this is to avoid `NoMethodErrors` when calling a validation method on the model (e.g. `#valid?`, `#validate`, etc.) *before* the data is loaded to create the associated `attr_accessors`:
|
192
200
|
|
193
201
|
```ruby
|
194
202
|
class Model < DecoLite::Model
|
195
203
|
validates :first, :last, :address, presence: true
|
204
|
+
validates :age, numericality: true
|
196
205
|
end
|
197
206
|
|
198
|
-
|
207
|
+
# No :address
|
208
|
+
model = Model.new(hash: { first: 'John', last: 'Doe', age: 25 })
|
209
|
+
model.respond_to? :address
|
210
|
+
#=> true
|
211
|
+
|
212
|
+
model.valid?
|
213
|
+
#=> false
|
214
|
+
model.errors.full_messages
|
215
|
+
#=> ["Address can't be blank"]
|
199
216
|
|
200
|
-
|
217
|
+
model.load!(hash: { address: '123 park road, anytown, nj 01234' })
|
201
218
|
model.validate
|
202
|
-
#=>
|
219
|
+
#=> true
|
220
|
+
```
|
203
221
|
|
204
|
-
|
222
|
+
#### Validate whether or not certain fields were loaded
|
223
|
+
|
224
|
+
To be clear, this example does not validate the _data_ associated with the fields loaded; rather, this example validates whether or not the _fields themselves_ (`attr_accessors`) were created on your model as a result of loading data into your model. If you only want to validate the _data_ loaded into your model, simply add `ActiveModel` validation, just like you would any other `ActiveModel` model, see the [Add validators to my model](#add-validators-to-my-model) section.
|
225
|
+
|
226
|
+
If you want to validate whether or not particular _fields_ were added to your model as attributes (`attr_accessor`), as a result of `#load!`ing data into your model, you need to do a few things:
|
227
|
+
- Create a `DecoLite::Model` subclass.
|
228
|
+
- Override the `DecoLite::Model#required_fields` method to return the field names you want to validate.
|
229
|
+
- Use the `required_fields: nil` option when instantiating your model object.
|
230
|
+
- DO NOT add `ActiveModel` validators that _explicitly_ reference any field returned from `DecoLite::Model#required_fields`; this will cause `attr_accessors` to be created for these fields; consequently, `DecoLite::FieldRequireable#validate_required_fields` will *never* return any errors because these fields will exist as attributes on your model. In other words, do not add (for example) `validates :first, :last, :address, presence: true` to your model if you need to validate whether or not the data you load into your model includs fields `:first`, `:last` and `:address`.
|
231
|
+
|
232
|
+
For example:
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
class Model < DecoLite::Model
|
236
|
+
# :age field is optional and it's value is optional.
|
237
|
+
validates :age, numericality: { only_integer: true }, allow_blank: true
|
238
|
+
|
239
|
+
def required_fields
|
240
|
+
# We want to ensure these fields were included as Hash keys during loading.
|
241
|
+
%i(first last address)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
Option `required_fields: :auto` is the default which will automatically create `attr_accessors` for any field returned from the `DecoLite::Model#required_fields` method; therefore, we need to set the `:required_fields` option to `nil` (i.e. `required_fields: nil`). This will prohibit `DecoLite::Model` from automatically creating `attr_accessors` for `:first`, `:last` and `:address`, and achieve the results we want:
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
model = Model.new(options: { required_fields: nil })
|
250
|
+
|
251
|
+
model.validate
|
252
|
+
#=> false
|
253
|
+
model.errors.full_messages
|
254
|
+
#=> ["First field is missing", "Last field is missing", "Address field is missing"]
|
255
|
+
|
256
|
+
# If we load data that includes :first, :last, and :address Hash keys even with
|
257
|
+
# nil data, our ":<field> field is missing" errors go away; in this scenario,
|
258
|
+
# we're validating the presence of the FIELDS, not the data associated with
|
259
|
+
# these fields!
|
260
|
+
model.load!(hash: { first: nil, last: nil, address: nil })
|
205
261
|
model.validate
|
206
262
|
#=> true
|
263
|
+
model.errors.full_messages
|
264
|
+
#=> []
|
265
|
+
|
266
|
+
user = {
|
267
|
+
first: 'John',
|
268
|
+
last: 'Doe',
|
269
|
+
address: '123 anystreet, anytown, nj 01234',
|
270
|
+
age: 'x'
|
271
|
+
}
|
272
|
+
model.load!(hash: user)
|
273
|
+
model.validate
|
274
|
+
#=> false
|
275
|
+
model.errors.full_messages
|
276
|
+
#=> ["Age is not a number"]
|
277
|
+
```
|
278
|
+
#### Validate whether or not certain fields were loaded _and_ validate the data associated with these same fields
|
279
|
+
|
280
|
+
If you simply want to validate the _data_ loaded into your model, simply add `ActiveModel` validation, just like you would any other `ActiveModel` model, see the [Add validators to my model](#add-validators-to-my-model) section.
|
281
|
+
|
282
|
+
If you want to validate whether or not particular fields were loaded _and_ field data associated with these same fields, you'll have to use custom validation (e.g. override `DecoLite::FieldRequireable#validate_required_fields` and manually add your own validation and errors). This is because `DecoLite::Model#new` will automatically create `attr_accessors` for any attribute (field) that has an _explicit_ `ActiveModel` validation associated with it, and return false positives when you validate your model. In addition to this, you will need to do several other things outlined in the [Validate whether or not certain fields were loaded](#validate-whether-or-not-certain-fields-were-loaded) section.
|
283
|
+
|
284
|
+
For example:
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
class Model < DecoLite::Model
|
288
|
+
def required_fields
|
289
|
+
%i(first last address age)
|
290
|
+
end
|
291
|
+
|
292
|
+
def validate_required_fields
|
293
|
+
super
|
294
|
+
|
295
|
+
first = self.try(:first)
|
296
|
+
errors.add(:first, "can't be blank") if first.nil?
|
297
|
+
|
298
|
+
last = self.try(:last)
|
299
|
+
errors.add(:last, "can't be blank") if last.nil?
|
300
|
+
|
301
|
+
address = self.try(:address)
|
302
|
+
errors.add(:address, "can't be blank") if address.nil?
|
303
|
+
|
304
|
+
age = self.try(:age)
|
305
|
+
errors.add(:age, "can't be blank") if age.nil?
|
306
|
+
errors.add(:age, 'is not a number') unless /\d+/ =~ age
|
307
|
+
end
|
308
|
+
end
|
309
|
+
model = Model.new(options: { required_fields: nil })
|
310
|
+
|
311
|
+
model.validate
|
312
|
+
#=> false
|
313
|
+
|
314
|
+
model.errors.full_messages
|
315
|
+
#=> ["First field is missing",
|
316
|
+
"Last field is missing",
|
317
|
+
"Address field is missing",
|
318
|
+
"Age field is missing",
|
319
|
+
"First can't be blank",
|
320
|
+
"Last can't be blank",
|
321
|
+
"Address can't be blank",
|
322
|
+
"Age can't be blank",
|
323
|
+
"Age is not a number"]
|
207
324
|
```
|
208
325
|
|
209
326
|
#### Manually define attributes (fields) on my model
|
@@ -224,19 +341,21 @@ class JustBecauseYouCanDoesntMeanYouShould < DecoLite::Model
|
|
224
341
|
end
|
225
342
|
```
|
226
343
|
|
227
|
-
However, the above is unnecessary as this can be easily accomplished using `DecoLite::Model#load!`:
|
344
|
+
However, the above is unnecessary as this can be easily accomplished by passing a `Hash` to the initializer or by using `DecoLite::Model#load!`:
|
345
|
+
|
228
346
|
```ruby
|
229
|
-
model = Class.new(DecoLite::Model).new
|
347
|
+
model = Class.new(DecoLite::Model).new(hash:{ existing_field: :value })
|
230
348
|
|
231
349
|
model.field_names
|
232
350
|
#=> [:existing_field]
|
233
351
|
|
234
352
|
model.existing_field
|
235
|
-
#=> :
|
353
|
+
#=> :value
|
236
354
|
|
237
355
|
model.respond_to? :existing_field=
|
238
356
|
#=> true
|
239
357
|
```
|
358
|
+
|
240
359
|
## Installation
|
241
360
|
|
242
361
|
Add this line to your application's Gemfile:
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'field_creatable'
|
4
3
|
require_relative 'field_retrievable'
|
5
4
|
|
6
5
|
module DecoLite
|
7
6
|
# Defines methods to assign model field values dynamically.
|
8
7
|
module FieldAssignable
|
9
|
-
include FieldCreatable
|
10
8
|
include FieldRetrievable
|
11
9
|
|
12
10
|
def set_field_values(hash:, field_info:, options:)
|
@@ -16,10 +14,10 @@ module DecoLite
|
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
17
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
19
18
|
def set_field_value(field_name:, value:, options:)
|
20
|
-
# Create our fields before we send.
|
21
|
-
create_field_accessor field_name: field_name, options: options
|
22
19
|
send("#{field_name}=", value)
|
23
20
|
end
|
21
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
24
22
|
end
|
25
23
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'field_name_namespaceable'
|
4
|
+
require_relative 'field_requireable'
|
4
5
|
require_relative 'fields_optionable'
|
5
6
|
|
6
7
|
module DecoLite
|
@@ -9,6 +10,7 @@ module DecoLite
|
|
9
10
|
module FieldConflictable
|
10
11
|
include FieldNameNamespaceable
|
11
12
|
include FieldsOptionable
|
13
|
+
include FieldRequireable
|
12
14
|
|
13
15
|
def validate_field_conflicts!(field_name:, options:)
|
14
16
|
return unless field_conflict?(field_name: field_name, options: options)
|
@@ -4,6 +4,8 @@ module DecoLite
|
|
4
4
|
# Provides methods to manage fields that must be defined from
|
5
5
|
# the dynamically loaded data.
|
6
6
|
module FieldRequireable
|
7
|
+
MISSING_REQUIRED_FIELD_ERROR_TYPE = :missing_required_field
|
8
|
+
|
7
9
|
# Returns field names that will be used to validate the presence of
|
8
10
|
# dynamically created fields from loaded objects.
|
9
11
|
#
|
@@ -23,7 +25,7 @@ module DecoLite
|
|
23
25
|
required_fields.each do |field_name|
|
24
26
|
next if required_field_exist? field_name: field_name
|
25
27
|
|
26
|
-
errors.add(field_name, 'field is missing', type:
|
28
|
+
errors.add(field_name, 'field is missing', type: MISSING_REQUIRED_FIELD_ERROR_TYPE)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -2,11 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'mad_flatter'
|
4
4
|
require_relative 'field_assignable'
|
5
|
+
require_relative 'field_creatable'
|
5
6
|
|
6
7
|
module DecoLite
|
7
8
|
# Provides methods to load and return information about a given hash.
|
8
9
|
module HashLoadable
|
9
10
|
include FieldAssignable
|
11
|
+
include FieldCreatable
|
10
12
|
|
11
13
|
private
|
12
14
|
|
data/lib/deco_lite/hashable.rb
CHANGED
@@ -5,7 +5,11 @@ module DecoLite
|
|
5
5
|
module Hashable
|
6
6
|
def to_h
|
7
7
|
field_names.each_with_object({}) do |field_name, hash|
|
8
|
-
|
8
|
+
field_value = public_send(field_name)
|
9
|
+
|
10
|
+
field_name, field_value = yield [field_name, field_value] if block_given?
|
11
|
+
|
12
|
+
hash[field_name] = field_value
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
data/lib/deco_lite/model.rb
CHANGED
data/lib/deco_lite/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deco_lite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gene M. Angelo, Jr.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|