trmnl-api 0.19.0 → 0.20.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3c03e9ded479cf04ef8e035dbc2370b7ea85f6e6c100e923151a0b12c4449ea
4
- data.tar.gz: ce9207dfa7ccfddb34c9459cb6a62681b99aefcfceaa2d1ed99a0b75c028d61b
3
+ metadata.gz: f763f8369338b964c61941391955b24c4ce660b8652176414184d70515a33c0d
4
+ data.tar.gz: 1f56f30717b729c32dd782c12ac04bb6b1009dc6abc1bcf772c167da41600310
5
5
  SHA512:
6
- metadata.gz: c8475df46de7907f16ec20200e9b331e7cd740778d09ae83816654cc23ac7d9604b927ccf10d2b6a2f799d9f12f25bbc899543c576f2972c996ae864eadb655b
7
- data.tar.gz: a72e2f57727fa4fd2db7b600e93b26a7ee6b098b937341f30a1de6d90f21cfa397563ac35860fdd41c44795f4aa362048cf7a5037b43b5bd09f2555063259f9a
6
+ metadata.gz: 191da096d070d71e5b1fcc8b7b671a7ac2306a0e0b24207042ed2ea2dbe5560f15b2912dfb0486929b3db5d491d70a2c86185c677614b114083b752087481e6d
7
+ data.tar.gz: 330b466f275c45fdf634fe96cb345d5c40a70cfd6427a85d8a75168775d4d7e84ef93deae659c641a54557edf7d9be62fcd0a5b6c08285d650227b1c33dc6f29
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -154,6 +154,84 @@ client.display token: "secret"
154
154
  # )
155
155
  ----
156
156
 
157
+ ==== Firmware Flashes
158
+
159
+ Allows you to obtain firmware flashes. Example:
160
+
161
+ [source,ruby]
162
+ ----
163
+ client = TRMNL::API.new
164
+ client.firmware_flashes
165
+
166
+ # Success(
167
+ # [
168
+ # #<data TRMNL::API::Models::Firmware::Flash:0x000024c0
169
+ # chip_family = "ESP32-S3",
170
+ # label = "TRMNL X",
171
+ # name = "trmnl_x",
172
+ # versions = [
173
+ # #<data TRMNL::API::Models::Firmware::Version:0x00002510
174
+ # label = "FW1.7.7",
175
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.7.7.bin"
176
+ # >,
177
+ # #<data TRMNL::API::Models::Firmware::Version:0x00002540
178
+ # label = "FW1.7.9",
179
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.7.9.bin"
180
+ # >,
181
+ # #<data TRMNL::API::Models::Firmware::Version:0x00002570
182
+ # label = "FW1.8.1",
183
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.1.bin"
184
+ # >,
185
+ # #<data TRMNL::API::Models::Firmware::Version:0x000025a0
186
+ # label = "FW1.8.2",
187
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.2.bin"
188
+ # >,
189
+ # #<data TRMNL::API::Models::Firmware::Version:0x000025d0
190
+ # label = "FW1.8.5",
191
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.5.bin"
192
+ # >,
193
+ # #<data TRMNL::API::Models::Firmware::Version:0x00002600
194
+ # label = "FW1.8.6",
195
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.6.bin"
196
+ # >,
197
+ # #<data TRMNL::API::Models::Firmware::Version:0x00002630
198
+ # label = "FW1.8.8",
199
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.8.bin"
200
+ # >,
201
+ # #<data TRMNL::API::Models::Firmware::Version:0x00002660
202
+ # label = "FW1.8.9",
203
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.9.bin"
204
+ # >,
205
+ # #<data TRMNL::API::Models::Firmware::Version:0x00002690
206
+ # label = "FW1.8.10",
207
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.10.bin"
208
+ # >,
209
+ # #<data TRMNL::API::Models::Firmware::Version:0x000026c0
210
+ # label = "FW1.8.12",
211
+ # uri = "https://trmnl-fw.s3.us-east-2.amazonaws.com/trmnl_x/flash/FW1.8.12.bin"
212
+ # >
213
+ # ]
214
+ # >
215
+ # ]
216
+ # )
217
+ ----
218
+
219
+ ==== Firmware Latest
220
+
221
+ Allows you to obtain the latest stable firmware version. Example:
222
+
223
+ [source,ruby]
224
+ ----
225
+ client = TRMNL::API.new
226
+ client.firmware_latest
227
+
228
+ # Success(
229
+ # #<data TRMNL::API::Models::Firmware::Latest
230
+ # url="https://trmnl-fw.s3.us-east-2.amazonaws.com/FW1.8.12.bin",
231
+ # version="1.4.8">
232
+ # )
233
+ ----
234
+
157
235
  ==== IP Addresses
158
236
 
159
237
  Allows you obtain a list of public IP addresses for all TRMNL core servers because plugin poll requests will only originate from these IPs. Example:
@@ -177,22 +255,6 @@ client.ip_addresses
177
255
  # )
178
256
  ----
179
257
 
180
- ==== Latest Firmware
181
-
182
- Allows you to obtain the latest stable firmware version. Example:
183
-
184
- [source,ruby]
185
- ----
186
- client = TRMNL::API.new
187
- client.latest_firmware
188
-
189
- # Success(
190
- # #<data TRMNL::API::Models::LatestFirmware
191
- # url="https://trmnl-fw.s3.us-east-2.amazonaws.com/FW1.4.8.bin",
192
- # version="1.4.8">
193
- # )
194
- ----
195
-
196
258
  ==== Log
197
259
 
198
260
  Allows you to create a log entry (which is what the device reports when it captures an error). You must supply your device's API token as the `token`. Example:
@@ -468,7 +530,19 @@ To test, run:
468
530
  bin/rake
469
531
  ----
470
532
 
533
+ == link:https://alchemists.io/policies/security[Security]
534
+
535
+ == link:https://alchemists.io/policies/code_of_conduct[Code of Conduct]
536
+
537
+ == link:https://alchemists.io/policies/contributions[Contributions]
538
+
539
+ == link:https://alchemists.io/policies/developer_certificate_of_origin[Developer Certificate of Origin]
540
+
541
+ == link:https://alchemists.io/projects/trmnl-api/versions[Versions]
542
+
543
+ == link:https://alchemists.io/community[Community]
544
+
471
545
  == Credits
472
546
 
473
547
  * Built with link:https://alchemists.io/projects/gemsmith[Gemsmith].
474
- * Engineered by link:https://trmnl.com/developers[TRMNL].
548
+ * Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann]
@@ -13,8 +13,9 @@ module TRMNL
13
13
  categories: Endpoints::Category,
14
14
  current_screen: Endpoints::CurrentScreen,
15
15
  display: Endpoints::Display,
16
+ firmware_flashes: Endpoints::Firmware::Flash,
17
+ firmware_latest: Endpoints::Firmware::Latest,
16
18
  ip_addresses: Endpoints::IPAddress,
17
- latest_firmware: Endpoints::LatestFirmware,
18
19
  log: Endpoints::Log,
19
20
  models: Endpoints::Model,
20
21
  palettes: Endpoints::Palette,
@@ -40,9 +41,11 @@ module TRMNL
40
41
 
41
42
  def display(**) = endpoints.fetch(__method__).call(**)
42
43
 
43
- def ip_addresses = endpoints.fetch(__method__).call
44
+ def firmware_flashes = endpoints.fetch(__method__).call
45
+
46
+ def firmware_latest = endpoints.fetch(__method__).call
44
47
 
45
- def latest_firmware = endpoints.fetch(__method__).call
48
+ def ip_addresses = endpoints.fetch(__method__).call
46
49
 
47
50
  def log(**) = endpoints.fetch(__method__).call(**)
48
51
 
@@ -23,10 +23,14 @@ module TRMNL
23
23
  end
24
24
 
25
25
  namespace :schemas do
26
+ namespace :firmware do
27
+ register :flash, Schemas::Firmware::Flash
28
+ register :latest, Schemas::Firmware::Latest
29
+ end
30
+
26
31
  register :category, Schemas::Category
27
32
  register :current_screen, Schemas::CurrentScreen
28
33
  register :display, Schemas::Display
29
- register :latest_firmware, Schemas::LatestFirmware
30
34
  register :ip_address, Schemas::IPAddress
31
35
  register :model, Schemas::Model
32
36
  register :palette, Schemas::Palette
@@ -35,9 +39,13 @@ module TRMNL
35
39
  end
36
40
 
37
41
  namespace :models do
42
+ namespace :firmware do
43
+ register :flash, Models::Firmware::Flash
44
+ register :latest, Models::Firmware::Latest
45
+ end
46
+
38
47
  register :current_screen, Models::CurrentScreen
39
48
  register :display, Models::Display
40
- register :latest_firmware, Models::LatestFirmware
41
49
  register :ip_address, Models::IPAddress
42
50
  register :model, Models::Model
43
51
  register :palette, Models::Palette
@@ -9,10 +9,14 @@ module TRMNL
9
9
  module Container
10
10
  extend Containable
11
11
 
12
+ namespace :firmware do
13
+ register(:flashes) { Firmwares::Flash.new }
14
+ register(:latest) { Firmware.new }
15
+ end
16
+
12
17
  register(:categories) { Category.new }
13
18
  register(:current_screen) { CurrentScreen.new }
14
19
  register(:display) { Display.new }
15
- register(:firmware) { Firmware.new }
16
20
  register(:ip_addresses) { IPAddress.new }
17
21
  register(:log) { Log.new }
18
22
  register(:models) { Model.new }
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "inspectable"
4
+ require "pipeable"
5
+
6
+ module TRMNL
7
+ module API
8
+ module Endpoints
9
+ module Firmware
10
+ # Handles API request/response.
11
+ class Flash
12
+ include TRMNL::API::Dependencies[
13
+ :requester,
14
+ schema: "schemas.firmware.flash",
15
+ model: "models.firmware.flash"
16
+ ]
17
+
18
+ include Inspectable[schema: :type]
19
+ include Pipeable
20
+
21
+ def call
22
+ pipe(
23
+ requester.get("firmware/flash"),
24
+ try(:parse, catch: JSON::ParserError),
25
+ validate(schema, as: :to_h),
26
+ as(:fetch, :data, :models),
27
+ as(:fetch, :models),
28
+ map { |attributes| model.for(**attributes) }
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "inspectable"
4
+ require "pipeable"
5
+
6
+ module TRMNL
7
+ module API
8
+ module Endpoints
9
+ module Firmware
10
+ # Handles API request/response.
11
+ class Latest
12
+ include TRMNL::API::Dependencies[
13
+ :requester,
14
+ schema: "schemas.firmware.latest",
15
+ model: "models.firmware.latest"
16
+ ]
17
+
18
+ include Inspectable[schema: :type]
19
+ include Pipeable
20
+
21
+ def call
22
+ pipe requester.get("firmware/latest"),
23
+ try(:parse, catch: JSON::ParserError),
24
+ validate(schema, as: :to_h),
25
+ to(model, :for)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "inspectable"
4
4
  require "pipeable"
5
- require "refinements/hash"
6
5
 
7
6
  module TRMNL
8
7
  module API
@@ -18,8 +17,6 @@ module TRMNL
18
17
  include Inspectable[schema: :type]
19
18
  include Pipeable
20
19
 
21
- using Refinements::Hash
22
-
23
20
  def call **parameters
24
21
  parameters.transform_keys! sort: "sort-by"
25
22
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "core"
4
+
5
+ module TRMNL
6
+ module API
7
+ module Models
8
+ module Firmware
9
+ # Models the data of the API response.
10
+ Flash = Data.define :name, :label, :chip_family, :versions do
11
+ def self.for **attributes
12
+ versions = Array(attributes.delete(:versions)).map { Version.for(**it) }
13
+ new(**attributes.transform_keys!(keyname: :name, chipFamily: :chip_family), versions:)
14
+ end
15
+
16
+ def initialize name: nil, label: nil, chip_family: nil, versions: Core::EMPTY_ARRAY
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TRMNL
4
+ module API
5
+ module Models
6
+ module Firmware
7
+ # Models the data of the API response.
8
+ Latest = ::Data.define :url, :version do
9
+ def self.for(attributes) = new(**attributes)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TRMNL
4
+ module API
5
+ module Models
6
+ module Firmware
7
+ # Models the data of the API response.
8
+ Version = Data.define :label, :uri do
9
+ def self.for(**attributes) = new(**attributes.transform_keys!(version: :label, url: :uri))
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -6,7 +6,7 @@ module TRMNL
6
6
  # Models the data of the API response.
7
7
  IPAddress = ::Data.define :version_4, :version_6 do
8
8
  def self.for attributes
9
- new(**attributes.transform_keys(ipv4: :version_4, ipv6: :version_6))
9
+ new(**attributes.transform_keys!(ipv4: :version_4, ipv6: :version_6))
10
10
  end
11
11
  end
12
12
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "core"
4
- require "refinements/hash"
5
4
 
6
5
  module TRMNL
7
6
  module API
@@ -24,7 +23,7 @@ module TRMNL
24
23
  :palette_names,
25
24
  :css
26
25
  ) do
27
- def self.for(attributes) = new(**attributes.transform_keys(palette_ids: :palette_names))
26
+ def self.for(attributes) = new(**attributes.transform_keys!(palette_ids: :palette_names))
28
27
 
29
28
  def initialize(**)
30
29
  super
@@ -1,16 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "core"
4
- require "refinements/hash"
5
4
 
6
5
  module TRMNL
7
6
  module API
8
7
  module Models
9
8
  # Models the data of the API response.
10
9
  Palette = Struct.new :name, :label, :grays, :colors, :framework_class do
11
- using Refinements::Hash
12
-
13
- def self.for(attributes) = new(**attributes.transform_keys(name: :label, id: :name))
10
+ def self.for(attributes) = new(**attributes.transform_keys!(name: :label, id: :name))
14
11
 
15
12
  def initialize(**)
16
13
  super
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema"
4
+
5
+ module TRMNL
6
+ module API
7
+ module Schemas
8
+ module Firmware
9
+ # Validates API response.
10
+ Flash = Dry::Schema.JSON do
11
+ required(:data).filled(:hash) do
12
+ required(:models).filled(:array).each(:hash) do
13
+ required(:keyname).filled :string
14
+ required(:label).filled :string
15
+ required(:chipFamily).filled :string
16
+ required(:versions).filled(:array).each(:hash) do
17
+ required(:version).filled :string
18
+ required(:url).filled :string
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/schema"
4
+
5
+ module TRMNL
6
+ module API
7
+ module Schemas
8
+ module Firmware
9
+ # Validates API response.
10
+ Latest = Dry::Schema.JSON do
11
+ required(:url).filled :string
12
+ required(:version).filled :string
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
data/trmnl-api.gemspec CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "trmnl-api"
5
- spec.version = "0.19.0"
6
- spec.authors = ["TRMNL"]
7
- spec.email = ["engineering@trmnl.com"]
8
- spec.homepage = "https://github.com/usetrmnl/trmnl-api"
5
+ spec.version = "0.20.0"
6
+ spec.authors = ["Brooke Kuhlmann"]
7
+ spec.email = ["brooke@alchemists.io"]
8
+ spec.homepage = "https://alchemists.io/projects/trmnl-api"
9
9
  spec.summary = "A monadic TRMNL API client."
10
10
  spec.license = "MIT"
11
11
 
12
12
  spec.metadata = {
13
13
  "bug_tracker_uri" => "https://github.com/usetrmnl/trmnl-api/issues",
14
- "changelog_uri" => "https://github.com/usetrmnl/trmnl-api/tags",
15
- "homepage_uri" => "https://github.com/usetrmnl/trmnl-api",
14
+ "changelog_uri" => "https://alchemists.io/projects/trmnl-api/versions",
15
+ "homepage_uri" => "https://alchemists.io/projects/trmnl-api",
16
16
  "label" => "TRMNL API",
17
17
  "rubygems_mfa_required" => "true",
18
18
  "source_code_uri" => "https://github.com/usetrmnl/trmnl-api"
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trmnl-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
- - TRMNL
7
+ - Brooke Kuhlmann
8
8
  bindir: bin
9
9
  cert_chain:
10
10
  - |
@@ -190,7 +190,7 @@ dependencies:
190
190
  - !ruby/object:Gem::Version
191
191
  version: '2.8'
192
192
  email:
193
- - engineering@trmnl.com
193
+ - brooke@alchemists.io
194
194
  executables: []
195
195
  extensions: []
196
196
  extra_rdoc_files:
@@ -211,8 +211,9 @@ files:
211
211
  - lib/trmnl/api/endpoints/current_screen.rb
212
212
  - lib/trmnl/api/endpoints/dependencies.rb
213
213
  - lib/trmnl/api/endpoints/display.rb
214
+ - lib/trmnl/api/endpoints/firmware/flash.rb
215
+ - lib/trmnl/api/endpoints/firmware/latest.rb
214
216
  - lib/trmnl/api/endpoints/ip_address.rb
215
- - lib/trmnl/api/endpoints/latest_firmware.rb
216
217
  - lib/trmnl/api/endpoints/log.rb
217
218
  - lib/trmnl/api/endpoints/model.rb
218
219
  - lib/trmnl/api/endpoints/palette.rb
@@ -221,8 +222,10 @@ files:
221
222
  - lib/trmnl/api/locale_reducer.rb
222
223
  - lib/trmnl/api/models/current_screen.rb
223
224
  - lib/trmnl/api/models/display.rb
225
+ - lib/trmnl/api/models/firmware/flash.rb
226
+ - lib/trmnl/api/models/firmware/latest.rb
227
+ - lib/trmnl/api/models/firmware/version.rb
224
228
  - lib/trmnl/api/models/ip_address.rb
225
- - lib/trmnl/api/models/latest_firmware.rb
226
229
  - lib/trmnl/api/models/model.rb
227
230
  - lib/trmnl/api/models/palette.rb
228
231
  - lib/trmnl/api/models/recipe.rb
@@ -235,8 +238,9 @@ files:
235
238
  - lib/trmnl/api/schemas/category.rb
236
239
  - lib/trmnl/api/schemas/current_screen.rb
237
240
  - lib/trmnl/api/schemas/display.rb
241
+ - lib/trmnl/api/schemas/firmware/flash.rb
242
+ - lib/trmnl/api/schemas/firmware/latest.rb
238
243
  - lib/trmnl/api/schemas/ip_address.rb
239
- - lib/trmnl/api/schemas/latest_firmware.rb
240
244
  - lib/trmnl/api/schemas/model.rb
241
245
  - lib/trmnl/api/schemas/palette.rb
242
246
  - lib/trmnl/api/schemas/recipe.rb
@@ -244,13 +248,13 @@ files:
244
248
  - lib/trmnl/api/schemas/setup.rb
245
249
  - lib/trmnl/api/types.rb
246
250
  - trmnl-api.gemspec
247
- homepage: https://github.com/usetrmnl/trmnl-api
251
+ homepage: https://alchemists.io/projects/trmnl-api
248
252
  licenses:
249
253
  - MIT
250
254
  metadata:
251
255
  bug_tracker_uri: https://github.com/usetrmnl/trmnl-api/issues
252
- changelog_uri: https://github.com/usetrmnl/trmnl-api/tags
253
- homepage_uri: https://github.com/usetrmnl/trmnl-api
256
+ changelog_uri: https://alchemists.io/projects/trmnl-api/versions
257
+ homepage_uri: https://alchemists.io/projects/trmnl-api
254
258
  label: TRMNL API
255
259
  rubygems_mfa_required: 'true'
256
260
  source_code_uri: https://github.com/usetrmnl/trmnl-api
@@ -268,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
268
272
  - !ruby/object:Gem::Version
269
273
  version: '0'
270
274
  requirements: []
271
- rubygems_version: 4.0.16
275
+ rubygems_version: 4.0.17
272
276
  specification_version: 4
273
277
  summary: A monadic TRMNL API client.
274
278
  test_files: []
metadata.gz.sig CHANGED
Binary file
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "inspectable"
4
- require "pipeable"
5
-
6
- module TRMNL
7
- module API
8
- module Endpoints
9
- # Handles API request/response.
10
- class LatestFirmware
11
- include TRMNL::API::Dependencies[
12
- :requester,
13
- schema: "schemas.latest_firmware",
14
- model: "models.latest_firmware"
15
- ]
16
-
17
- include Inspectable[schema: :type]
18
- include Pipeable
19
-
20
- def call
21
- pipe requester.get("firmware/latest"),
22
- try(:parse, catch: JSON::ParserError),
23
- validate(schema, as: :to_h),
24
- to(model, :for)
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TRMNL
4
- module API
5
- module Models
6
- # Models the data of the API response.
7
- LatestFirmware = ::Data.define :url, :version do
8
- def self.for(attributes) = new(**attributes)
9
- end
10
- end
11
- end
12
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dry/schema"
4
-
5
- module TRMNL
6
- module API
7
- module Schemas
8
- # Validates API response.
9
- LatestFirmware = Dry::Schema.JSON do
10
- required(:url).filled :string
11
- required(:version).filled :string
12
- end
13
- end
14
- end
15
- end