apiwork 0.3.1 → 0.5.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/apiwork/adapter/serializer/resource/base.rb +15 -0
  3. data/lib/apiwork/adapter/serializer/resource/default/contract_builder.rb +4 -3
  4. data/lib/apiwork/adapter/standard/capability/writing/contract_builder.rb +13 -9
  5. data/lib/apiwork/api/base.rb +105 -17
  6. data/lib/apiwork/api/element.rb +35 -4
  7. data/lib/apiwork/api/object.rb +72 -7
  8. data/lib/apiwork/api/router.rb +16 -0
  9. data/lib/apiwork/configuration/validatable.rb +1 -0
  10. data/lib/apiwork/configuration.rb +2 -0
  11. data/lib/apiwork/contract/element.rb +19 -4
  12. data/lib/apiwork/contract/object/coercer.rb +31 -2
  13. data/lib/apiwork/contract/object/deserializer.rb +5 -1
  14. data/lib/apiwork/contract/object/transformer.rb +15 -2
  15. data/lib/apiwork/contract/object/validator.rb +49 -11
  16. data/lib/apiwork/contract/object.rb +79 -9
  17. data/lib/apiwork/element.rb +34 -1
  18. data/lib/apiwork/export/base.rb +1 -4
  19. data/lib/apiwork/export/builder_mapper.rb +184 -0
  20. data/lib/apiwork/export/open_api.rb +9 -2
  21. data/lib/apiwork/export/sorbus.rb +5 -1
  22. data/lib/apiwork/export/sorbus_mapper.rb +3 -7
  23. data/lib/apiwork/export/type_analysis.rb +20 -6
  24. data/lib/apiwork/export/type_script.rb +4 -1
  25. data/lib/apiwork/export/type_script_mapper.rb +25 -2
  26. data/lib/apiwork/export/zod.rb +9 -0
  27. data/lib/apiwork/export/zod_mapper.rb +22 -1
  28. data/lib/apiwork/introspection/api.rb +18 -0
  29. data/lib/apiwork/introspection/dump/action.rb +1 -1
  30. data/lib/apiwork/introspection/dump/api.rb +2 -0
  31. data/lib/apiwork/introspection/dump/param.rb +36 -20
  32. data/lib/apiwork/introspection/dump/resource.rb +7 -4
  33. data/lib/apiwork/introspection/dump/type.rb +31 -25
  34. data/lib/apiwork/introspection/param/array.rb +26 -0
  35. data/lib/apiwork/introspection/param/base.rb +15 -25
  36. data/lib/apiwork/introspection/param/binary.rb +36 -0
  37. data/lib/apiwork/introspection/param/boolean.rb +36 -0
  38. data/lib/apiwork/introspection/param/date.rb +36 -0
  39. data/lib/apiwork/introspection/param/date_time.rb +36 -0
  40. data/lib/apiwork/introspection/param/decimal.rb +26 -0
  41. data/lib/apiwork/introspection/param/integer.rb +26 -0
  42. data/lib/apiwork/introspection/param/number.rb +26 -0
  43. data/lib/apiwork/introspection/param/record.rb +71 -0
  44. data/lib/apiwork/introspection/param/string.rb +26 -0
  45. data/lib/apiwork/introspection/param/time.rb +36 -0
  46. data/lib/apiwork/introspection/param/uuid.rb +36 -0
  47. data/lib/apiwork/introspection/param.rb +1 -0
  48. data/lib/apiwork/introspection.rb +17 -4
  49. data/lib/apiwork/object.rb +246 -4
  50. data/lib/apiwork/representation/attribute.rb +2 -2
  51. data/lib/apiwork/representation/base.rb +107 -2
  52. data/lib/apiwork/representation/element.rb +15 -5
  53. data/lib/apiwork/version.rb +1 -1
  54. metadata +6 -4
@@ -71,7 +71,7 @@ module Apiwork
71
71
  # The allowed values.
72
72
  # @param example [String, nil] (nil)
73
73
  # The example value. Metadata included in exports.
74
- # @param format [Symbol, nil] (nil) [:date, :datetime, :email, :hostname, :ipv4, :ipv6, :password, :url, :uuid]
74
+ # @param format [Symbol, nil] (nil) [:date, :datetime, :email, :hostname, :ipv4, :ipv6, :password, :text, :url, :uuid]
75
75
  # Format hint for exports. Does not change the type, but exports may add validation or documentation based on it.
76
76
  # Valid formats by type: `:string`.
77
77
  # @param max [Integer, nil] (nil)
@@ -144,7 +144,7 @@ module Apiwork
144
144
  # The allowed values.
145
145
  # @param example [String, nil] (nil)
146
146
  # The example value. Metadata included in exports.
147
- # @param format [Symbol, nil] (nil) [:date, :datetime, :email, :hostname, :ipv4, :ipv6, :password, :url, :uuid]
147
+ # @param format [Symbol, nil] (nil) [:date, :datetime, :email, :hostname, :ipv4, :ipv6, :password, :text, :url, :uuid]
148
148
  # Format hint for exports. Does not change the type, but exports may add validation or documentation based on it.
149
149
  # Valid formats by type: `:string`.
150
150
  # @param max [Integer, nil] (nil)
@@ -1129,6 +1129,103 @@ module Apiwork
1129
1129
  )
1130
1130
  end
1131
1131
 
1132
+ # @api public
1133
+ # Defines an unknown.
1134
+ #
1135
+ # @param name [Symbol]
1136
+ # The name.
1137
+ # @param as [Symbol, nil] (nil)
1138
+ # The target attribute name.
1139
+ # @param default [Object, nil] (nil)
1140
+ # The default value.
1141
+ # @param deprecated [Boolean] (false)
1142
+ # Whether deprecated. Metadata included in exports.
1143
+ # @param description [String, nil] (nil)
1144
+ # The description. Metadata included in exports.
1145
+ # @param example [Object, nil] (nil)
1146
+ # The example value. Metadata included in exports.
1147
+ # @param nullable [Boolean] (false)
1148
+ # Whether the value can be `null`.
1149
+ # @param optional [Boolean] (false)
1150
+ # Whether the param is optional.
1151
+ # @param required [Boolean] (false)
1152
+ # Whether the param is required.
1153
+ # @return [void]
1154
+ #
1155
+ # @example Opaque metadata
1156
+ # unknown :metadata
1157
+ def unknown(
1158
+ name,
1159
+ as: nil,
1160
+ default: nil,
1161
+ deprecated: false,
1162
+ description: nil,
1163
+ example: nil,
1164
+ nullable: false,
1165
+ optional: false,
1166
+ required: false
1167
+ )
1168
+ param(
1169
+ name,
1170
+ as:,
1171
+ default:,
1172
+ deprecated:,
1173
+ description:,
1174
+ example:,
1175
+ nullable:,
1176
+ optional:,
1177
+ required:,
1178
+ type: :unknown,
1179
+ )
1180
+ end
1181
+
1182
+ # @api public
1183
+ # Defines an optional unknown.
1184
+ #
1185
+ # @param name [Symbol]
1186
+ # The name.
1187
+ # @param as [Symbol, nil] (nil)
1188
+ # The target attribute name.
1189
+ # @param default [Object, nil] (nil)
1190
+ # The default value.
1191
+ # @param deprecated [Boolean] (false)
1192
+ # Whether deprecated. Metadata included in exports.
1193
+ # @param description [String, nil] (nil)
1194
+ # The description. Metadata included in exports.
1195
+ # @param example [Object, nil] (nil)
1196
+ # The example value. Metadata included in exports.
1197
+ # @param nullable [Boolean] (false)
1198
+ # Whether the value can be `null`.
1199
+ # @param required [Boolean] (false)
1200
+ # Whether the param is required.
1201
+ # @return [void]
1202
+ #
1203
+ # @example Optional metadata
1204
+ # unknown? :metadata
1205
+ def unknown?(
1206
+ name,
1207
+ as: nil,
1208
+ default: nil,
1209
+ deprecated: false,
1210
+ description: nil,
1211
+ example: nil,
1212
+ nullable: false,
1213
+ required: false
1214
+ )
1215
+ param(
1216
+ name,
1217
+ as:,
1218
+ default:,
1219
+ deprecated:,
1220
+ description:,
1221
+ example:,
1222
+ nullable:,
1223
+ required:,
1224
+ optional: true,
1225
+ type: :unknown,
1226
+ )
1227
+ end
1228
+
1132
1229
  # @api public
1133
1230
  # Defines an object.
1134
1231
  #
@@ -1267,6 +1364,22 @@ module Apiwork
1267
1364
  # decimal :price
1268
1365
  # end
1269
1366
  # end
1367
+ #
1368
+ # @example Array of discriminated union
1369
+ # array :notifications do
1370
+ # union discriminator: :type do
1371
+ # variant tag: 'email' do
1372
+ # object do
1373
+ # string :address
1374
+ # end
1375
+ # end
1376
+ # variant tag: 'sms' do
1377
+ # object do
1378
+ # string :phone
1379
+ # end
1380
+ # end
1381
+ # end
1382
+ # end
1270
1383
  def array(
1271
1384
  name,
1272
1385
  as: nil,
@@ -1320,6 +1433,22 @@ module Apiwork
1320
1433
  # array? :labels do
1321
1434
  # string
1322
1435
  # end
1436
+ #
1437
+ # @example Optional array of discriminated union
1438
+ # array? :items do
1439
+ # union discriminator: :type do
1440
+ # variant tag: 'text' do
1441
+ # object do
1442
+ # string :content
1443
+ # end
1444
+ # end
1445
+ # variant tag: 'image' do
1446
+ # object do
1447
+ # string :url
1448
+ # end
1449
+ # end
1450
+ # end
1451
+ # end
1323
1452
  def array?(
1324
1453
  name,
1325
1454
  as: nil,
@@ -1346,6 +1475,113 @@ module Apiwork
1346
1475
  )
1347
1476
  end
1348
1477
 
1478
+ # @api public
1479
+ # Defines a record.
1480
+ #
1481
+ # @param name [Symbol]
1482
+ # The name.
1483
+ # @param as [Symbol, nil] (nil)
1484
+ # The target attribute name.
1485
+ # @param default [Object, nil] (nil)
1486
+ # The default value.
1487
+ # @param deprecated [Boolean] (false)
1488
+ # Whether deprecated. Metadata included in exports.
1489
+ # @param description [String, nil] (nil)
1490
+ # The description. Metadata included in exports.
1491
+ # @param nullable [Boolean] (false)
1492
+ # Whether the value can be `null`.
1493
+ # @param optional [Boolean] (false)
1494
+ # Whether the param is optional.
1495
+ # @param required [Boolean] (false)
1496
+ # Whether the param is required.
1497
+ # @yield block defining value type
1498
+ # @return [void]
1499
+ #
1500
+ # @example Record of integers
1501
+ # record :scores do
1502
+ # integer
1503
+ # end
1504
+ #
1505
+ # @example Record of objects
1506
+ # record :settings do
1507
+ # object do
1508
+ # string :value
1509
+ # boolean :enabled
1510
+ # end
1511
+ # end
1512
+ def record(
1513
+ name,
1514
+ as: nil,
1515
+ default: nil,
1516
+ deprecated: false,
1517
+ description: nil,
1518
+ nullable: false,
1519
+ optional: false,
1520
+ required: false,
1521
+ &block
1522
+ )
1523
+ param(
1524
+ name,
1525
+ as:,
1526
+ default:,
1527
+ deprecated:,
1528
+ description:,
1529
+ nullable:,
1530
+ optional:,
1531
+ required:,
1532
+ type: :record,
1533
+ &block
1534
+ )
1535
+ end
1536
+
1537
+ # @api public
1538
+ # Defines an optional record.
1539
+ #
1540
+ # @param name [Symbol]
1541
+ # The name.
1542
+ # @param as [Symbol, nil] (nil)
1543
+ # The target attribute name.
1544
+ # @param default [Object, nil] (nil)
1545
+ # The default value.
1546
+ # @param deprecated [Boolean] (false)
1547
+ # Whether deprecated. Metadata included in exports.
1548
+ # @param description [String, nil] (nil)
1549
+ # The description. Metadata included in exports.
1550
+ # @param nullable [Boolean] (false)
1551
+ # Whether the value can be `null`.
1552
+ # @param required [Boolean] (false)
1553
+ # Whether the param is required.
1554
+ # @yield block defining value type
1555
+ # @return [void]
1556
+ #
1557
+ # @example Optional record
1558
+ # record? :metadata do
1559
+ # string
1560
+ # end
1561
+ def record?(
1562
+ name,
1563
+ as: nil,
1564
+ default: nil,
1565
+ deprecated: false,
1566
+ description: nil,
1567
+ nullable: false,
1568
+ required: false,
1569
+ &block
1570
+ )
1571
+ param(
1572
+ name,
1573
+ as:,
1574
+ default:,
1575
+ deprecated:,
1576
+ description:,
1577
+ nullable:,
1578
+ required:,
1579
+ optional: true,
1580
+ type: :record,
1581
+ &block
1582
+ )
1583
+ end
1584
+
1349
1585
  # @api public
1350
1586
  # Defines a union.
1351
1587
  #
@@ -1552,6 +1788,8 @@ module Apiwork
1552
1788
  optional: false,
1553
1789
  required: false
1554
1790
  )
1791
+ reference_type = to || name
1792
+
1555
1793
  param(
1556
1794
  name,
1557
1795
  as:,
@@ -1561,7 +1799,8 @@ module Apiwork
1561
1799
  nullable:,
1562
1800
  optional:,
1563
1801
  required:,
1564
- type: to || name,
1802
+ custom_type: reference_type,
1803
+ type: reference_type,
1565
1804
  )
1566
1805
  end
1567
1806
 
@@ -1598,6 +1837,8 @@ module Apiwork
1598
1837
  nullable: false,
1599
1838
  required: false
1600
1839
  )
1840
+ reference_type = to || name
1841
+
1601
1842
  param(
1602
1843
  name,
1603
1844
  as:,
@@ -1606,8 +1847,9 @@ module Apiwork
1606
1847
  description:,
1607
1848
  nullable:,
1608
1849
  required:,
1850
+ custom_type: reference_type,
1609
1851
  optional: true,
1610
- type: to || name,
1852
+ type: reference_type,
1611
1853
  )
1612
1854
  end
1613
1855
 
@@ -18,7 +18,7 @@ module Apiwork
18
18
  decimal: %i[float double],
19
19
  integer: %i[int32 int64],
20
20
  number: %i[float double],
21
- string: %i[email uuid url date datetime ipv4 ipv6 password hostname],
21
+ string: %i[date datetime email hostname ipv4 ipv6 password text url uuid],
22
22
  }.freeze
23
23
 
24
24
  # @!attribute [r] description
@@ -117,7 +117,7 @@ module Apiwork
117
117
  element.validate!
118
118
  @element = element
119
119
  type = element.type
120
- @of = element.inner&.type if element.type == :array
120
+ @of = element.inner&.type if [:array, :record].include?(element.type)
121
121
  end
122
122
 
123
123
  if owner_representation_class.model_class.present?
@@ -69,6 +69,7 @@ module Apiwork
69
69
  class_attribute :inheritance, default: nil, instance_accessor: false
70
70
 
71
71
  class_attribute :_adapter_config, default: {}, instance_accessor: false
72
+ class_attribute :type_definitions, default: {}, instance_accessor: false
72
73
 
73
74
  # @!attribute [r] context
74
75
  # @api public
@@ -197,11 +198,11 @@ module Apiwork
197
198
  # The example. Metadata included in exports.
198
199
  # @param filterable [Boolean] (false)
199
200
  # Whether the attribute is filterable.
200
- # @param format [Symbol, nil] (nil) [:date, :datetime, :double, :email, :float, :hostname, :int32, :int64, :ipv4, :ipv6, :password, :url, :uuid]
201
+ # @param format [Symbol, nil] (nil) [:date, :datetime, :double, :email, :float, :hostname, :int32, :int64, :ipv4, :ipv6, :password, :text, :url, :uuid]
201
202
  # Format hint for exports. Does not change the type, but exports may add validation or
202
203
  # documentation based on it. Valid formats by type: `:decimal`/`:number` (`:double`, `:float`),
203
204
  # `:integer` (`:int32`, `:int64`), `:string` (`:date`, `:datetime`, `:email`, `:hostname`,
204
- # `:ipv4`, `:ipv6`, `:password`, `:url`, `:uuid`).
205
+ # `:ipv4`, `:ipv6`, `:password`, `:text`, `:url`, `:uuid`).
205
206
  # @param max [Integer, nil] (nil)
206
207
  # The maximum. For `:array`: size. For `:decimal`, `:integer`, `:number`: value. For `:string`: length.
207
208
  # @param min [Integer, nil] (nil)
@@ -540,6 +541,110 @@ module Apiwork
540
541
  )
541
542
  end
542
543
 
544
+ # @api public
545
+ # Defines an object type for this representation.
546
+ #
547
+ # The type is copied to the contract that uses this representation. Attributes can reference
548
+ # it by name via `type:`. In exports, the type is scoped to the contract.
549
+ #
550
+ # @param name [Symbol]
551
+ # The object name.
552
+ # @param deprecated [Boolean] (false)
553
+ # Whether deprecated. Metadata included in exports.
554
+ # @param description [String, nil] (nil)
555
+ # The description. Metadata included in exports.
556
+ # @param example [Object, nil] (nil)
557
+ # The example. Metadata included in exports.
558
+ # @yieldparam object [API::Object]
559
+ # @return [void]
560
+ #
561
+ # @example Define and reference
562
+ # object :address do
563
+ # string :street
564
+ # string :city
565
+ # string :postal_code
566
+ # end
567
+ #
568
+ # attribute :shipping_address, type: :address
569
+ # attribute :billing_address, type: :address
570
+ def object(
571
+ name,
572
+ deprecated: false,
573
+ description: nil,
574
+ example: nil,
575
+ &block
576
+ )
577
+ self.type_definitions = type_definitions.merge(
578
+ name.to_sym => {
579
+ block:,
580
+ kind: :object,
581
+ options: {
582
+ deprecated:,
583
+ description:,
584
+ example:,
585
+ },
586
+ },
587
+ )
588
+ end
589
+
590
+ # @api public
591
+ # Defines a union type for this representation.
592
+ #
593
+ # The type is copied to the contract that uses this representation. Attributes can reference
594
+ # it by name via `type:`. In exports, the type is scoped to the contract.
595
+ #
596
+ # @param name [Symbol]
597
+ # The union name.
598
+ # @param deprecated [Boolean] (false)
599
+ # Whether deprecated. Metadata included in exports.
600
+ # @param description [String, nil] (nil)
601
+ # The description. Metadata included in exports.
602
+ # @param discriminator [Symbol, nil] (nil)
603
+ # The discriminator field name.
604
+ # @param example [Object, nil] (nil)
605
+ # The example. Metadata included in exports.
606
+ # @yieldparam union [API::Union]
607
+ # @return [void]
608
+ #
609
+ # @example Define and reference
610
+ # union :content_block, discriminator: :kind do
611
+ # variant tag: 'text' do
612
+ # object do
613
+ # string :body
614
+ # end
615
+ # end
616
+ # variant tag: 'image' do
617
+ # object do
618
+ # string :url
619
+ # integer :width
620
+ # integer :height
621
+ # end
622
+ # end
623
+ # end
624
+ #
625
+ # attribute :content, type: :content_block
626
+ def union(
627
+ name,
628
+ deprecated: false,
629
+ description: nil,
630
+ discriminator: nil,
631
+ example: nil,
632
+ &block
633
+ )
634
+ self.type_definitions = type_definitions.merge(
635
+ name.to_sym => {
636
+ block:,
637
+ kind: :union,
638
+ options: {
639
+ deprecated:,
640
+ description:,
641
+ discriminator:,
642
+ example:,
643
+ },
644
+ },
645
+ )
646
+ end
647
+
543
648
  # @api public
544
649
  # The type name for this representation.
545
650
  #
@@ -11,10 +11,11 @@ module Apiwork
11
11
  # Only complex types are allowed at the top level:
12
12
  # - {#object} for key-value structures
13
13
  # - {#array} for ordered collections
14
+ # - {#record} for key-value maps with typed values
14
15
  # - {#union} for polymorphic structures
15
16
  #
16
17
  # Inside these blocks, the full type DSL is available including
17
- # nested objects, arrays, primitives, and unions.
18
+ # nested objects, arrays, records, primitives, and unions.
18
19
  #
19
20
  # @see API::Element Block context for array elements
20
21
  # @see API::Object Block context for object fields
@@ -74,15 +75,15 @@ module Apiwork
74
75
  # end
75
76
  class Element < Apiwork::Element
76
77
  def validate!
77
- raise ConfigurationError, 'must define exactly one type (object, array, or union)' unless @defined
78
+ raise ConfigurationError, 'must define exactly one type (object, array, record, or union)' unless @defined
78
79
  end
79
80
 
80
81
  # @api public
81
82
  # Defines the element type.
82
83
  #
83
- # Only complex types (:object, :array, :union) are allowed.
84
+ # Only complex types (:object, :array, :record, :union) are allowed.
84
85
  #
85
- # @param type [Symbol] [:array, :object, :union]
86
+ # @param type [Symbol] [:array, :object, :record, :union]
86
87
  # The element type.
87
88
  # @param discriminator [Symbol, nil] (nil)
88
89
  # The discriminator field name. Unions only.
@@ -110,6 +111,15 @@ module Apiwork
110
111
  @inner = inner
111
112
  @shape = inner.shape
112
113
  @defined = true
114
+ when :record
115
+ raise ConfigurationError, 'record requires a block' unless block
116
+
117
+ inner = API::Element.new
118
+ block.arity.positive? ? yield(inner) : inner.instance_eval(&block)
119
+ inner.validate!
120
+ @type = :record
121
+ @inner = inner
122
+ @defined = true
113
123
  when :union
114
124
  raise ConfigurationError, 'union requires a block' unless block
115
125
 
@@ -120,7 +130,7 @@ module Apiwork
120
130
  @discriminator = discriminator
121
131
  @defined = true
122
132
  else
123
- raise ConfigurationError, "Representation::Element only supports :object, :array, :union - got #{type.inspect}"
133
+ raise ConfigurationError, "Representation::Element only supports :object, :array, :record, :union - got #{type.inspect}"
124
134
  end
125
135
  end
126
136
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Apiwork
4
- VERSION = '0.3.1'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apiwork
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - skiftle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-04 00:00:00.000000000 Z
11
+ date: 2026-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.0'
47
+ version: '2.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.0'
54
+ version: '2.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -261,6 +261,7 @@ files:
261
261
  - lib/apiwork/error_code/registry.rb
262
262
  - lib/apiwork/export.rb
263
263
  - lib/apiwork/export/base.rb
264
+ - lib/apiwork/export/builder_mapper.rb
264
265
  - lib/apiwork/export/open_api.rb
265
266
  - lib/apiwork/export/pipeline.rb
266
267
  - lib/apiwork/export/pipeline/writer.rb
@@ -306,6 +307,7 @@ files:
306
307
  - lib/apiwork/introspection/param/literal.rb
307
308
  - lib/apiwork/introspection/param/number.rb
308
309
  - lib/apiwork/introspection/param/object.rb
310
+ - lib/apiwork/introspection/param/record.rb
309
311
  - lib/apiwork/introspection/param/reference.rb
310
312
  - lib/apiwork/introspection/param/string.rb
311
313
  - lib/apiwork/introspection/param/time.rb