shale 1.1.0 → 1.2.1

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: 60a0f059693d5ea371ffca8b475ebb261ee99096509e588d2f1163a96bf8fbc0
4
- data.tar.gz: c1af0c61c7d9a93bd601188c50916d4f702228052541adee34456c4d7dac8b9a
3
+ metadata.gz: 527c634afc670223747f11c77d1fd5e1470c26faec18594c1c31594503883799
4
+ data.tar.gz: ee6e31c70608520549b9bf90c00e98fd43821797b8b31a7493d0538ce40767a8
5
5
  SHA512:
6
- metadata.gz: 31bd59c352febaf287eda07a79f56e5fe942fd86623f532e48c25ff5e946bb53fc879c3f8bb13121989268057beb4ee740f8aac7b1275dda8b9a2d21a5f75aaf
7
- data.tar.gz: 127b46cdeb0960643aa60410597f7ace1815dd19e3845cd0cb7c2ea3d2d44b4326333656bdf96209effcf17daabbc5a70de9f430acd42e21d961e2c356d9be9f
6
+ metadata.gz: bf35ec1503adfe41ab81d6ad27515158980937bcbce04f8ca8014343b8fae0404433c8a667a2d30492c4aa359bd2e1cf6e836e900ce57574a811ac88da28ac93
7
+ data.tar.gz: ea9e5799d4c93611ae0d1c390e835248f460e565aa68f6556c06ff0ef9057c0f42a68da02b6005d809594945548ec7dc659db775a135dc58c7f5920b18ec8eaf
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## [1.2.1] - 2025-01-09
2
+
3
+ ### Fixed
4
+ - [kjeldahl] Fixes issue where multiples namespace prefixes like ns1 and ns10 are replaced wrongly
5
+
6
+ ## [1.2.0] - 2024-10-31
7
+
8
+ ### Added
9
+ - Allow to pass adapter specific options
10
+ - Allow to pass `additional_properties` option to JSON Schema generator
11
+ - Allow to pass `description` option to JSON Schema generator
12
+ - Support for symbol type aliases in attribute mappings
13
+
1
14
  ## [1.1.0] - 2024-02-17
2
15
 
3
16
  ### Added
data/README.md CHANGED
@@ -65,6 +65,7 @@ $ gem install shale
65
65
  * [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
66
66
  * [Delegating fields to child attributes](#delegating-fields-to-child-attributes)
67
67
  * [Additional options](#additional-options)
68
+ * [Overriding attribute methods](#overriding-attribute-methods)
68
69
  * [Using custom models](#using-custom-models)
69
70
  * [Supported types](#supported-types)
70
71
  * [Writing your own type](#writing-your-own-type)
@@ -82,17 +83,17 @@ $ gem install shale
82
83
  require 'shale'
83
84
 
84
85
  class Address < Shale::Mapper
85
- attribute :city, Shale::Type::String
86
- attribute :street, Shale::Type::String
87
- attribute :zip, Shale::Type::String
86
+ attribute :city, :string
87
+ attribute :street, :string
88
+ attribute :zip, :string
88
89
  end
89
90
 
90
91
  class Person < Shale::Mapper
91
- attribute :first_name, Shale::Type::String
92
- attribute :last_name, Shale::Type::String
93
- attribute :age, Shale::Type::Integer
94
- attribute :married, Shale::Type::Boolean, default: -> { false }
95
- attribute :hobbies, Shale::Type::String, collection: true
92
+ attribute :first_name, :string
93
+ attribute :last_name, :string
94
+ attribute :age, :integer
95
+ attribute :married, :boolean, default: -> { false }
96
+ attribute :hobbies, :string, collection: true
96
97
  attribute :address, Address
97
98
  end
98
99
  ```
@@ -208,8 +209,8 @@ person.to_yaml
208
209
  ### Converting TOML to object
209
210
 
210
211
  To use TOML with Shale you have to set adapter you want to use.
211
- Out of the box Shale suports [Tomlib](https://github.com/kgiszczak/tomlib).
212
- It also comes with adapter for [toml-rb](https://github.com/emancu/toml-rb) if you prefer that.
212
+ It comes with adapters for [Tomlib](https://github.com/kgiszczak/tomlib) and
213
+ [toml-rb](https://github.com/emancu/toml-rb).
213
214
  For details see [Adapters](#adapters) section.
214
215
 
215
216
  To set it, first make sure Tomlib gem is installed:
@@ -221,8 +222,8 @@ $ gem install tomlib
221
222
  then setup adapter:
222
223
 
223
224
  ```ruby
224
- require 'tomlib'
225
- Shale.toml_adapter = Tomlib
225
+ require 'sahle/adapter/tomlib'
226
+ Shale.toml_adapter = Shale::Adapter::Tomlib
226
227
 
227
228
  # Alternatively if you'd like to use toml-rb, use:
228
229
  require 'shale/adapter/toml_rb'
@@ -442,8 +443,8 @@ By default keys are named the same as attributes. To use custom keys use:
442
443
 
443
444
  ```ruby
444
445
  class Person < Shale::Mapper
445
- attribute :first_name, Shale::Type::String
446
- attribute :last_name, Shale::Type::String
446
+ attribute :first_name, :string
447
+ attribute :last_name, :string
447
448
 
448
449
  json do
449
450
  map 'firstName', to: :first_name
@@ -456,8 +457,8 @@ end
456
457
 
457
458
  ```ruby
458
459
  class Person < Shale::Mapper
459
- attribute :first_name, Shale::Type::String
460
- attribute :last_name, Shale::Type::String
460
+ attribute :first_name, :string
461
+ attribute :last_name, :string
461
462
 
462
463
  yaml do
463
464
  map 'firstName', to: :first_name
@@ -470,8 +471,8 @@ end
470
471
 
471
472
  ```ruby
472
473
  class Person < Shale::Mapper
473
- attribute :first_name, Shale::Type::String
474
- attribute :last_name, Shale::Type::String
474
+ attribute :first_name, :string
475
+ attribute :last_name, :string
475
476
 
476
477
  toml do
477
478
  map 'firstName', to: :first_name
@@ -488,8 +489,8 @@ to `:first_name` attribute and the second column to `:last_name`.
488
489
 
489
490
  ```ruby
490
491
  class Person < Shale::Mapper
491
- attribute :first_name, Shale::Type::String
492
- attribute :last_name, Shale::Type::String
492
+ attribute :first_name, :string
493
+ attribute :last_name, :string
493
494
 
494
495
  csv do
495
496
  map 'firstName', to: :first_name
@@ -502,8 +503,8 @@ end
502
503
 
503
504
  ```ruby
504
505
  class Person < Shale::Mapper
505
- attribute :first_name, Shale::Type::String
506
- attribute :last_name, Shale::Type::String
506
+ attribute :first_name, :string
507
+ attribute :last_name, :string
507
508
 
508
509
  hsh do
509
510
  map 'firstName', to: :first_name
@@ -518,9 +519,9 @@ XML is more complicated format than JSON or YAML. To map elements, attributes an
518
519
 
519
520
  ```ruby
520
521
  class Address < Shale::Mapper
521
- attribute :street, Shale::Type::String
522
- attribute :city, Shale::Type::String
523
- attribute :zip, Shale::Type::String
522
+ attribute :street, :string
523
+ attribute :city, :string
524
+ attribute :zip, :string
524
525
 
525
526
  xml do
526
527
  map_content to: :street
@@ -530,10 +531,10 @@ class Address < Shale::Mapper
530
531
  end
531
532
 
532
533
  class Person < Shale::Mapper
533
- attribute :first_name, Shale::Type::String
534
- attribute :last_name, Shale::Type::String
535
- attribute :age, Shale::Type::Integer
536
- attribute :hobbies, Shale::Type::String, collection: true
534
+ attribute :first_name, :string
535
+ attribute :last_name, :string
536
+ attribute :age, :integer
537
+ attribute :hobbies, :string, collection: true
537
538
  attribute :address, Address
538
539
 
539
540
  xml do
@@ -572,7 +573,7 @@ You can use `cdata: true` option on `map_element` and `map_content` to handle CD
572
573
 
573
574
  ```ruby
574
575
  class Address < Shale::Mapper
575
- attribute :content, Shale::Type::String
576
+ attribute :content, :string
576
577
 
577
578
  xml do
578
579
  map_content to: :content, cdata: true
@@ -580,7 +581,7 @@ class Address < Shale::Mapper
580
581
  end
581
582
 
582
583
  class Person < Shale::Mapper
583
- attribute :first_name, Shale::Type::String
584
+ attribute :first_name, :string
584
585
  attribute :address, Address
585
586
 
586
587
  xml do
@@ -606,9 +607,9 @@ To map namespaced elements and attributes use `namespace` and `prefix` propertie
606
607
 
607
608
  ```ruby
608
609
  class Person < Shale::Mapper
609
- attribute :first_name, Shale::Type::String
610
- attribute :last_name, Shale::Type::String
611
- attribute :age, Shale::Type::Integer
610
+ attribute :first_name, :string
611
+ attribute :last_name, :string
612
+ attribute :age, :integer
612
613
 
613
614
  xml do
614
615
  root 'person'
@@ -633,11 +634,11 @@ explicitly declare it on `map_attribute`).
633
634
 
634
635
  ```ruby
635
636
  class Person < Shale::Mapper
636
- attribute :first_name, Shale::Type::String
637
- attribute :middle_name, Shale::Type::String
638
- attribute :last_name, Shale::Type::String
639
- attribute :age, Shale::Type::Integer
640
- attribute :hobby, Shale::Type::String
637
+ attribute :first_name, :string
638
+ attribute :middle_name, :string
639
+ attribute :last_name, :string
640
+ attribute :age, :integer
641
+ attribute :hobby, :string
641
642
 
642
643
  xml do
643
644
  root 'person'
@@ -673,9 +674,9 @@ For CSV the default is to render `nil` elements.
673
674
 
674
675
  ```ruby
675
676
  class Person < Shale::Mapper
676
- attribute :first_name, Shale::Type::String
677
- attribute :last_name, Shale::Type::String
678
- attribute :age, Shale::Type::Integer
677
+ attribute :first_name, :string
678
+ attribute :last_name, :string
679
+ attribute :age, :integer
679
680
 
680
681
  json do
681
682
  map 'first_name', to: :first_name, render_nil: true
@@ -723,9 +724,9 @@ class Base < Shale::Mapper
723
724
  end
724
725
 
725
726
  class Person < Base
726
- attribute :first_name, Shale::Type::String
727
- attribute :last_name, Shale::Type::String
728
- attribute :age, Shale::Type::Integer
727
+ attribute :first_name, :string
728
+ attribute :last_name, :string
729
+ attribute :age, :integer
729
730
 
730
731
  json do
731
732
  # override default from Base class
@@ -742,8 +743,8 @@ end
742
743
 
743
744
  ```ruby
744
745
  class Person < Base
745
- attribute :first_name, Shale::Type::String
746
- attribute :last_name, Shale::Type::String
746
+ attribute :first_name, :string
747
+ attribute :last_name, :string
747
748
 
748
749
  json do
749
750
  render_nil false
@@ -762,9 +763,9 @@ you can use methods to do so:
762
763
 
763
764
  ```ruby
764
765
  class Person < Shale::Mapper
765
- attribute :hobbies, Shale::Type::String, collection: true
766
- attribute :street, Shale::Type::String
767
- attribute :city, Shale::Type::String
766
+ attribute :hobbies, :string, collection: true
767
+ attribute :street, :string
768
+ attribute :city, :string
768
769
 
769
770
  json do
770
771
  map 'hobbies', using: { from: :hobbies_from_json, to: :hobbies_to_json }
@@ -853,7 +854,7 @@ You can also pass a `context` object that will be available in extractor/generat
853
854
 
854
855
  ```ruby
855
856
  class Person < Shale::Mapper
856
- attribute :password, Shale::Type::String
857
+ attribute :password, :string
857
858
 
858
859
  json do
859
860
  map 'password', using: { from: :password_from_json, to: :password_to_json }
@@ -883,7 +884,7 @@ If you want to work on multiple elements at a time you can group them using `gro
883
884
 
884
885
  ```ruby
885
886
  class Person < Shale::Mapper
886
- attribute :name, Shale::Type::String
887
+ attribute :name, :string
887
888
 
888
889
  json do
889
890
  group from: :name_from_json, to: :name_to_json do
@@ -934,12 +935,12 @@ To delegate fields to child complex types you can use `receiver: :child` declara
934
935
 
935
936
  ```ruby
936
937
  class Address < Shale::Mapper
937
- attribute :city, Shale::Type::String
938
- attribute :street, Shale::Type::String
938
+ attribute :city, :string
939
+ attribute :street, :string
939
940
  end
940
941
 
941
942
  class Person < Shale::Mapper
942
- attribute :name, Shale::Type::String
943
+ attribute :name, :string
943
944
  attribute :address, Address
944
945
 
945
946
  json do
@@ -1059,8 +1060,8 @@ names and shouldn't be included in the returned collection. It also accepts all
1059
1060
 
1060
1061
  ```ruby
1061
1062
  class Person
1062
- attribute :first_name, Shale::Type::String
1063
- attribute :last_name, Shale::Type::String
1063
+ attribute :first_name, :string
1064
+ attribute :last_name, :string
1064
1065
  end
1065
1066
 
1066
1067
  people = Person.from_csv(<<~DATA, headers: true, col_sep: '|')
@@ -1085,6 +1086,63 @@ Person.to_csv(people, headers: true, col_sep: '|')
1085
1086
  # James|Sixpack
1086
1087
  ```
1087
1088
 
1089
+ Most adapters accept options specific to them. Eg. if you want to be able to work
1090
+ with NaN values in JSON:
1091
+
1092
+ ```ruby
1093
+ class Person
1094
+ attribute :age, :float
1095
+ end
1096
+
1097
+ person = Person.from_json('{"age": NaN}', allow_nan: true)
1098
+
1099
+ # =>
1100
+ #
1101
+ # #<Person:0x0000000113d7a488 @age=Float::NAN>
1102
+
1103
+ Person.to_json(person, allow_nan: true)
1104
+
1105
+ # =>
1106
+ #
1107
+ # {
1108
+ # "age": NaN
1109
+ # }
1110
+ ```
1111
+
1112
+ ### Overriding attribute methods
1113
+
1114
+ It's possible to override an attribute method to change its output:
1115
+
1116
+ ```ruby
1117
+ class Person < Shale::Mapper
1118
+ attribute :gender, :string
1119
+
1120
+ def gender
1121
+ if super == 'm'
1122
+ 'male'
1123
+ else
1124
+ 'female'
1125
+ end
1126
+ end
1127
+ end
1128
+
1129
+ puts Person.from_json('{"gender":"m"}')
1130
+
1131
+ # =>
1132
+ #
1133
+ # #<Person:0x00007f9bc3086d60
1134
+ # @gender="male">
1135
+ ```
1136
+
1137
+ Be conscious that the original attribute value will be lost after its transformation though:
1138
+
1139
+ ```ruby
1140
+ puts User.from_json('{"gender":"m"}').to_json
1141
+ # => {"gender":"male"}
1142
+ ```
1143
+
1144
+ It'll no longer return gender `m`.
1145
+
1088
1146
  ### Using custom models
1089
1147
 
1090
1148
  By default Shale combines mapper and model into one class. If you want to use your own classes
@@ -1102,15 +1160,15 @@ end
1102
1160
  class AddressMapper < Shale::Mapper
1103
1161
  model Address
1104
1162
 
1105
- attribute :street, Shale::Type::String
1106
- attribute :city, Shale::Type::String
1163
+ attribute :street, :string
1164
+ attribute :city, :string
1107
1165
  end
1108
1166
 
1109
1167
  class PersonMapper < Shale::Mapper
1110
1168
  model Person
1111
1169
 
1112
- attribute :first_name, Shale::Type::String
1113
- attribute :last_name, Shale::Type::String
1170
+ attribute :first_name, :string
1171
+ attribute :last_name, :string
1114
1172
  attribute :address, AddressMapper
1115
1173
  end
1116
1174
 
@@ -1150,12 +1208,21 @@ PersonMapper.to_json(person, pretty: true)
1150
1208
 
1151
1209
  Shale supports these types out of the box:
1152
1210
 
1153
- - `Shale::Type::Boolean`
1154
- - `Shale::Type::Date`
1155
- - `Shale::Type::Float`
1156
- - `Shale::Type::Integer`
1157
- - `Shale::Type::String`
1158
- - `Shale::Type::Time`
1211
+ - `:boolean` (`Shale::Type::Boolean`)
1212
+ - `:date` (`Shale::Type::Date`)
1213
+ - `:float` (`Shale::Type::Float`)
1214
+ - `:integer` (`Shale::Type::Integer`)
1215
+ - `:string` (`Shale::Type::String`)
1216
+ - `:time` (`Shale::Type::Time`)
1217
+
1218
+ The symbol type alias and the type class are interchangeable:
1219
+
1220
+ ```ruby
1221
+ class Person < Shale::Mapper
1222
+ attribute :age, Shale::Type::Integer
1223
+ # attribute :age, :integer
1224
+ end
1225
+ ```
1159
1226
 
1160
1227
  ### Writing your own type
1161
1228
 
@@ -1171,6 +1238,31 @@ class MyIntegerType < Shale::Type::Value
1171
1238
  end
1172
1239
  ```
1173
1240
 
1241
+ Then you can use it in your model:
1242
+
1243
+ ```ruby
1244
+ class Person < Shale::Mapper
1245
+ attribute :age, MyIntegerType
1246
+ end
1247
+ ```
1248
+
1249
+ You can also register your own type with a symbol alias if you
1250
+ intend to use it often.
1251
+
1252
+ ```ruby
1253
+ require 'shale/type'
1254
+
1255
+ Shale::Type.register(:my_integer, MyIntegerType)
1256
+ ```
1257
+
1258
+ Then you can use it like this:
1259
+
1260
+ ```ruby
1261
+ class Person < Shale::Mapper
1262
+ attribute :age, :my_integer
1263
+ end
1264
+ ```
1265
+
1174
1266
  ### Adapters
1175
1267
 
1176
1268
  Shale uses adapters for parsing and generating documents.
@@ -1323,18 +1415,18 @@ require 'shale/schema'
1323
1415
  class PersonMapper < Shale::Mapper
1324
1416
  model Person
1325
1417
 
1326
- attribute :first_name, Shale::Type::String
1327
- attribute :last_name, Shale::Type::String
1328
- attribute :address, Shale::Type::String
1329
- attribute :age, Shale::Type::Integer
1418
+ attribute :first_name, :string
1419
+ attribute :last_name, :string
1420
+ attribute :address, :string
1421
+ attribute :age, :integer
1330
1422
 
1331
1423
  json do
1332
- properties max_properties: 5
1424
+ properties max_properties: 5, additional_properties: false
1333
1425
 
1334
1426
  map "first_name", to: :first_name, schema: { required: true }
1335
1427
  map "last_name", to: :last_name, schema: { required: true }
1336
- map "address", to: :age, schema: { max_length: 128 }
1337
- map "age", to: :age, schema: { minimum: 1, maximum: 150 }
1428
+ map "address", to: :address, schema: { max_length: 128, description: "Street, home number, city and country" }
1429
+ map "age", to: :age, schema: { minimum: 1, maximum: 150, description: "Person age" }
1338
1430
  end
1339
1431
  end
1340
1432
 
@@ -1352,7 +1444,6 @@ Shale::Schema.to_json(
1352
1444
  # "$defs": {
1353
1445
  # "Person": {
1354
1446
  # "type": "object",
1355
- # "maxProperties": 5,
1356
1447
  # "properties": {
1357
1448
  # "first_name": {
1358
1449
  # "type": "string"
@@ -1360,23 +1451,30 @@ Shale::Schema.to_json(
1360
1451
  # "last_name": {
1361
1452
  # "type": "string"
1362
1453
  # },
1363
- # "age": {
1454
+ # "address": {
1364
1455
  # "type": [
1365
- # "integer",
1456
+ # "string",
1366
1457
  # "null"
1367
1458
  # ],
1368
- # "minimum": 1,
1369
- # "maximum": 150
1370
- # },
1371
- # "address": {
1459
+ # "maxLength": 128,
1460
+ # "description": "Street, home number, city and country"
1461
+ # },
1462
+ # "age": {
1372
1463
  # "type": [
1373
- # "string",
1464
+ # "integer",
1374
1465
  # "null"
1375
1466
  # ],
1376
- # "maxLength": 128
1467
+ # "minimum": 1,
1468
+ # "maximum": 150,
1469
+ # "description": "Person age"
1377
1470
  # }
1378
1471
  # },
1379
- # "required": ["first_name", "last_name"]
1472
+ # "required": [
1473
+ # "first_name",
1474
+ # "last_name"
1475
+ # ],
1476
+ # "maxProperties": 5,
1477
+ # "additionalProperties": false
1380
1478
  # }
1381
1479
  # }
1382
1480
  # }
@@ -11,27 +11,30 @@ module Shale
11
11
  # Parse JSON into Hash
12
12
  #
13
13
  # @param [String] json JSON document
14
+ # @param [Hash] options
14
15
  #
15
16
  # @return [Hash]
16
17
  #
17
18
  # @api private
18
- def self.load(json)
19
- ::JSON.parse(json)
19
+ def self.load(json, **options)
20
+ ::JSON.parse(json, **options)
20
21
  end
21
22
 
22
23
  # Serialize Hash into JSON
23
24
  #
24
25
  # @param [Hash] obj Hash object
25
- # @param [true, false] pretty
26
+ # @param [Hash] options
26
27
  #
27
28
  # @return [String]
28
29
  #
29
30
  # @api private
30
- def self.dump(obj, pretty: false)
31
- if pretty
32
- ::JSON.pretty_generate(obj)
31
+ def self.dump(obj, **options)
32
+ json_options = options.except(:pretty)
33
+
34
+ if options[:pretty]
35
+ ::JSON.pretty_generate(obj, **json_options)
33
36
  else
34
- ::JSON.generate(obj)
37
+ ::JSON.generate(obj, **json_options)
35
38
  end
36
39
  end
37
40
  end
@@ -4,29 +4,31 @@ require 'toml-rb'
4
4
 
5
5
  module Shale
6
6
  module Adapter
7
- # TOML adapter
7
+ # TomlRB adapter
8
8
  #
9
9
  # @api public
10
10
  class TomlRB
11
11
  # Parse TOML into Hash
12
12
  #
13
13
  # @param [String] toml TOML document
14
+ # @param [Hash] options
14
15
  #
15
16
  # @return [Hash]
16
17
  #
17
18
  # @api private
18
- def self.load(toml)
19
+ def self.load(toml, **_options)
19
20
  ::TomlRB.parse(toml)
20
21
  end
21
22
 
22
23
  # Serialize Hash into TOML
23
24
  #
24
25
  # @param [Hash] obj Hash object
26
+ # @param [Hash] options
25
27
  #
26
28
  # @return [String]
27
29
  #
28
30
  # @api private
29
- def self.dump(obj)
31
+ def self.dump(obj, **_options)
30
32
  ::TomlRB.dump(obj)
31
33
  end
32
34
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tomlib'
4
+
5
+ module Shale
6
+ module Adapter
7
+ # Tomlib adapter
8
+ #
9
+ # @api public
10
+ class Tomlib
11
+ # Parse TOML into Hash
12
+ #
13
+ # @param [String] toml TOML document
14
+ # @param [Hash] options
15
+ #
16
+ # @return [Hash]
17
+ #
18
+ # @api private
19
+ def self.load(toml, **_options)
20
+ ::Tomlib.load(toml)
21
+ end
22
+
23
+ # Serialize Hash into TOML
24
+ #
25
+ # @param [Hash] obj Hash object
26
+ # @param [Hash] options
27
+ #
28
+ # @return [String]
29
+ #
30
+ # @api private
31
+ def self.dump(obj, **options)
32
+ ::Tomlib.dump(obj, **options)
33
+ end
34
+ end
35
+ end
36
+ end
data/lib/shale/error.rb CHANGED
@@ -9,7 +9,8 @@ module Shale
9
9
 
10
10
  # To use Tomlib:
11
11
  # Make sure tomlib is installed eg. execute: gem install tomlib
12
- Shale.toml_adapter = Tomlib
12
+ require 'shale/adapter/tomlib'
13
+ Shale.toml_adapter = Shale::Adapter::Tomlib
13
14
 
14
15
  # To use toml-rb:
15
16
  # Make sure toml-rb is installed eg. execute: gem install toml-rb
@@ -104,6 +105,18 @@ module Shale
104
105
  class NotAShaleMapperError < ShaleError
105
106
  end
106
107
 
108
+ # Error for registering class that is not a valid Type::Value
109
+ #
110
+ # @api private
111
+ class NotATypeValueError < ShaleError
112
+ end
113
+
114
+ # Error for using unknown symbol type
115
+ #
116
+ # @api private
117
+ class UnknownTypeError < ShaleError
118
+ end
119
+
107
120
  # Raised when receiver attribute is not defined
108
121
  #
109
122
  # @api private
data/lib/shale/mapper.rb CHANGED
@@ -5,6 +5,7 @@ require_relative 'error'
5
5
  require_relative 'utils'
6
6
  require_relative 'mapping/dict'
7
7
  require_relative 'mapping/xml'
8
+ require_relative 'type'
8
9
  require_relative 'type/complex'
9
10
 
10
11
  module Shale
@@ -12,16 +13,16 @@ module Shale
12
13
  #
13
14
  # @example
14
15
  # class Address < Shale::Mapper
15
- # attribute :city, Shale::Type::String
16
- # attribute :street, Shale::Type::String
17
- # attribute :state, Shale::Type::Integer
18
- # attribute :zip, Shale::Type::String
16
+ # attribute :city, :string
17
+ # attribute :street, :string
18
+ # attribute :state, :string
19
+ # attribute :zip, :string
19
20
  # end
20
21
  #
21
22
  # class Person < Shale::Mapper
22
- # attribute :first_name, Shale::Type::String
23
- # attribute :last_name, Shale::Type::String
24
- # attribute :age, Shale::Type::Integer
23
+ # attribute :first_name, :string
24
+ # attribute :last_name, :string
25
+ # attribute :age, :integer
25
26
  # attribute :address, Address
26
27
  # end
27
28
  #
@@ -144,18 +145,19 @@ module Shale
144
145
  # Define attribute on class
145
146
  #
146
147
  # @param [Symbol] name Name of the attribute
147
- # @param [Shale::Type::Value] type Type of the attribute
148
+ # @param [Symbol, Class<Shale::Type::Value>] type Type of the attribute
148
149
  # @param [Boolean] collection Is the attribute a collection
149
150
  # @param [Proc] default Default value for the attribute
150
151
  #
151
152
  # @raise [DefaultNotCallableError] when attribute's default is not callable
153
+ # @raise [UnknownTypeError] when type is a symbol and not found in the registry
152
154
  #
153
155
  # @example
154
156
  # class Person < Shale::Mapper
155
- # attribute :first_name, Shale::Type::String
156
- # attribute :last_name, Shale::Type::String
157
- # attribute :age, Shale::Type::Integer, default: -> { 1 }
158
- # attribute :hobbies, Shale::Type::String, collection: true
157
+ # attribute :first_name, :string
158
+ # attribute :last_name, :string
159
+ # attribute :age, :integer, default: -> { 1 }
160
+ # attribute :hobbies, :string, collection: true
159
161
  # end
160
162
  #
161
163
  # person = Person.new
@@ -177,6 +179,10 @@ module Shale
177
179
  raise DefaultNotCallableError.new(to_s, name)
178
180
  end
179
181
 
182
+ if type.is_a?(Symbol)
183
+ type = Type.lookup(type)
184
+ end
185
+
180
186
  @attributes[name] = Attribute.new(name, type, collection, default)
181
187
 
182
188
  @hash_mapping.map(name.to_s, to: name) unless @hash_mapping.finalized?
@@ -201,9 +207,9 @@ module Shale
201
207
  #
202
208
  # @example
203
209
  # class Person < Shale::Mapper
204
- # attribute :first_name, Shale::Type::String
205
- # attribute :last_name, Shale::Type::String
206
- # attribute :age, Shale::Type::Integer
210
+ # attribute :first_name, :string
211
+ # attribute :last_name, :string
212
+ # attribute :age, :integer
207
213
  #
208
214
  # hsh do
209
215
  # map 'firstName', to: :first_name
@@ -225,9 +231,9 @@ module Shale
225
231
  #
226
232
  # @example
227
233
  # class Person < Shale::Mapper
228
- # attribute :first_name, Shale::Type::String
229
- # attribute :last_name, Shale::Type::String
230
- # attribute :age, Shale::Type::Integer
234
+ # attribute :first_name, :string
235
+ # attribute :last_name, :string
236
+ # attribute :age, :integer
231
237
  #
232
238
  # json do
233
239
  # map 'firstName', to: :first_name
@@ -249,9 +255,9 @@ module Shale
249
255
  #
250
256
  # @example
251
257
  # class Person < Shale::Mapper
252
- # attribute :first_name, Shale::Type::String
253
- # attribute :last_name, Shale::Type::String
254
- # attribute :age, Shale::Type::Integer
258
+ # attribute :first_name, :string
259
+ # attribute :last_name, :string
260
+ # attribute :age, :integer
255
261
  #
256
262
  # yaml do
257
263
  # map 'first_name', to: :first_name
@@ -273,9 +279,9 @@ module Shale
273
279
  #
274
280
  # @example
275
281
  # class Person < Shale::Mapper
276
- # attribute :first_name, Shale::Type::String
277
- # attribute :last_name, Shale::Type::String
278
- # attribute :age, Shale::Type::Integer
282
+ # attribute :first_name, :string
283
+ # attribute :last_name, :string
284
+ # attribute :age, :integer
279
285
  #
280
286
  # toml do
281
287
  # map 'first_name', to: :first_name
@@ -297,9 +303,9 @@ module Shale
297
303
  #
298
304
  # @example
299
305
  # class Person < Shale::Mapper
300
- # attribute :first_name, Shale::Type::String
301
- # attribute :last_name, Shale::Type::String
302
- # attribute :age, Shale::Type::Integer
306
+ # attribute :first_name, :string
307
+ # attribute :last_name, :string
308
+ # attribute :age, :integer
303
309
  #
304
310
  # csv do
305
311
  # map 'first_name', to: :first_name
@@ -321,9 +327,9 @@ module Shale
321
327
  #
322
328
  # @example
323
329
  # class Person < Shale::Mapper
324
- # attribute :first_name, Shale::Type::String
325
- # attribute :last_name, Shale::Type::String
326
- # attribute :age, Shale::Type::Integer
330
+ # attribute :first_name, :string
331
+ # attribute :last_name, :string
332
+ # attribute :age, :integer
327
333
  #
328
334
  # xml do
329
335
  # root 'Person'
@@ -67,13 +67,15 @@ module Shale
67
67
  # @param [Integer] min_properties
68
68
  # @param [Integer] max_properties
69
69
  # @param [Hash] dependent_required
70
+ # @param [Boolean] additional_properties
70
71
  #
71
72
  # @api public
72
- def properties(min_properties: nil, max_properties: nil, dependent_required: nil)
73
+ def properties(min_properties: nil, max_properties: nil, dependent_required: nil, additional_properties: nil)
73
74
  @root = {
74
75
  min_properties: min_properties,
75
76
  max_properties: max_properties,
76
77
  dependent_required: dependent_required,
78
+ additional_properties: additional_properties,
77
79
  }
78
80
  end
79
81
 
@@ -15,7 +15,8 @@ module Shale
15
15
  #
16
16
  # @api private
17
17
  def as_type
18
- { 'type' => 'boolean' }
18
+ { 'type' => 'boolean',
19
+ 'description' => schema[:description] }.compact
19
20
  end
20
21
  end
21
22
  end
@@ -46,7 +46,8 @@ module Shale
46
46
  'maxItems' => schema[:max_items],
47
47
  'uniqueItems' => schema[:unique],
48
48
  'minContains' => schema[:min_contains],
49
- 'maxContains' => schema[:max_contains] }.compact
49
+ 'maxContains' => schema[:max_contains],
50
+ 'description' => schema[:description] }.compact
50
51
  end
51
52
  end
52
53
  end
@@ -15,7 +15,9 @@ module Shale
15
15
  #
16
16
  # @api private
17
17
  def as_type
18
- { 'type' => 'string', 'format' => 'date' }
18
+ { 'type' => 'string',
19
+ 'format' => 'date',
20
+ 'description' => schema[:description] }.compact
19
21
  end
20
22
  end
21
23
  end
@@ -20,7 +20,8 @@ module Shale
20
20
  'exclusiveMaximum' => schema[:exclusive_maximum],
21
21
  'minimum' => schema[:minimum],
22
22
  'maximum' => schema[:maximum],
23
- 'multipleOf' => schema[:multiple_of] }.compact
23
+ 'multipleOf' => schema[:multiple_of],
24
+ 'description' => schema[:description] }.compact
24
25
  end
25
26
  end
26
27
  end
@@ -20,7 +20,8 @@ module Shale
20
20
  'exclusiveMaximum' => schema[:exclusive_maximum],
21
21
  'minimum' => schema[:minimum],
22
22
  'maximum' => schema[:maximum],
23
- 'multipleOf' => schema[:multiple_of] }.compact
23
+ 'multipleOf' => schema[:multiple_of],
24
+ 'description' => schema[:description] }.compact
24
25
  end
25
26
  end
26
27
  end
@@ -40,6 +40,8 @@ module Shale
40
40
  'minProperties' => @root[:min_properties],
41
41
  'maxProperties' => @root[:max_properties],
42
42
  'dependentRequired' => @root[:dependent_required],
43
+ 'description' => @root[:description],
44
+ 'additionalProperties' => @root[:additional_properties],
43
45
  }.compact
44
46
  end
45
47
  end
@@ -19,7 +19,8 @@ module Shale
19
19
  'format' => schema[:format],
20
20
  'minLength' => schema[:min_length],
21
21
  'maxLength' => schema[:max_length],
22
- 'pattern' => schema[:pattern] }.compact
22
+ 'pattern' => schema[:pattern],
23
+ 'description' => schema[:description] }.compact
23
24
  end
24
25
  end
25
26
  end
@@ -15,7 +15,9 @@ module Shale
15
15
  #
16
16
  # @api private
17
17
  def as_type
18
- { 'type' => 'string', 'format' => 'date-time' }
18
+ { 'type' => 'string',
19
+ 'format' => 'date-time',
20
+ 'description' => schema[:description] }.compact
19
21
  end
20
22
  end
21
23
  end
@@ -15,7 +15,8 @@ module Shale
15
15
  #
16
16
  # @api private
17
17
  def as_type
18
- { 'type' => %w[boolean integer number object string] }
18
+ { 'type' => %w[boolean integer number object string],
19
+ 'description' => schema[:description] }.compact
19
20
  end
20
21
  end
21
22
  end
@@ -422,7 +422,7 @@ module Shale
422
422
  # @api private
423
423
  def replace_ns_prefixes(type, namespaces)
424
424
  namespaces.each do |prefix, name|
425
- type = type.sub(/^#{prefix}/, name)
425
+ type = type.sub(/^#{prefix}:/, "#{name}:")
426
426
  end
427
427
 
428
428
  if namespaces.key?('xmlns') && !type.include?(':')
@@ -29,5 +29,7 @@ module Shale
29
29
  !FALSE_VALUES.include?(value) unless value.nil?
30
30
  end
31
31
  end
32
+
33
+ register(:boolean, Boolean)
32
34
  end
33
35
  end
@@ -257,13 +257,14 @@ module Shale
257
257
  # @param [Array<Symbol>] only
258
258
  # @param [Array<Symbol>] except
259
259
  # @param [any] context
260
+ # @param [Hash] json_options
260
261
  #
261
262
  # @return [model instance]
262
263
  #
263
264
  # @api public
264
- def from_json(json, only: nil, except: nil, context: nil)
265
+ def from_json(json, only: nil, except: nil, context: nil, **json_options)
265
266
  of_json(
266
- Shale.json_adapter.load(json),
267
+ Shale.json_adapter.load(json, **json_options),
267
268
  only: only,
268
269
  except: except,
269
270
  context: context
@@ -277,14 +278,15 @@ module Shale
277
278
  # @param [Array<Symbol>] except
278
279
  # @param [any] context
279
280
  # @param [true, false] pretty
281
+ # @param [Hash] json_options
280
282
  #
281
283
  # @return [String]
282
284
  #
283
285
  # @api public
284
- def to_json(instance, only: nil, except: nil, context: nil, pretty: false)
286
+ def to_json(instance, only: nil, except: nil, context: nil, pretty: false, **json_options)
285
287
  Shale.json_adapter.dump(
286
288
  as_json(instance, only: only, except: except, context: context),
287
- pretty: pretty
289
+ **json_options.merge(pretty: pretty)
288
290
  )
289
291
  end
290
292
 
@@ -294,13 +296,14 @@ module Shale
294
296
  # @param [Array<Symbol>] only
295
297
  # @param [Array<Symbol>] except
296
298
  # @param [any] context
299
+ # @param [Hash] yaml_options
297
300
  #
298
301
  # @return [model instance]
299
302
  #
300
303
  # @api public
301
- def from_yaml(yaml, only: nil, except: nil, context: nil)
304
+ def from_yaml(yaml, only: nil, except: nil, context: nil, **yaml_options)
302
305
  of_yaml(
303
- Shale.yaml_adapter.load(yaml),
306
+ Shale.yaml_adapter.load(yaml, **yaml_options),
304
307
  only: only,
305
308
  except: except,
306
309
  context: context
@@ -313,13 +316,15 @@ module Shale
313
316
  # @param [Array<Symbol>] only
314
317
  # @param [Array<Symbol>] except
315
318
  # @param [any] context
319
+ # @param [Hash] yaml_options
316
320
  #
317
321
  # @return [String]
318
322
  #
319
323
  # @api public
320
- def to_yaml(instance, only: nil, except: nil, context: nil)
324
+ def to_yaml(instance, only: nil, except: nil, context: nil, **yaml_options)
321
325
  Shale.yaml_adapter.dump(
322
- as_yaml(instance, only: only, except: except, context: context)
326
+ as_yaml(instance, only: only, except: except, context: context),
327
+ **yaml_options
323
328
  )
324
329
  end
325
330
 
@@ -329,14 +334,15 @@ module Shale
329
334
  # @param [Array<Symbol>] only
330
335
  # @param [Array<Symbol>] except
331
336
  # @param [any] context
337
+ # @param [Hash] toml_options
332
338
  #
333
339
  # @return [model instance]
334
340
  #
335
341
  # @api public
336
- def from_toml(toml, only: nil, except: nil, context: nil)
342
+ def from_toml(toml, only: nil, except: nil, context: nil, **toml_options)
337
343
  validate_toml_adapter
338
344
  of_toml(
339
- Shale.toml_adapter.load(toml),
345
+ Shale.toml_adapter.load(toml, **toml_options),
340
346
  only: only,
341
347
  except: except,
342
348
  context: context
@@ -349,14 +355,16 @@ module Shale
349
355
  # @param [Array<Symbol>] only
350
356
  # @param [Array<Symbol>] except
351
357
  # @param [any] context
358
+ # @param [Hash] toml_options
352
359
  #
353
360
  # @return [String]
354
361
  #
355
362
  # @api public
356
- def to_toml(instance, only: nil, except: nil, context: nil)
363
+ def to_toml(instance, only: nil, except: nil, context: nil, **toml_options)
357
364
  validate_toml_adapter
358
365
  Shale.toml_adapter.dump(
359
- as_toml(instance, only: only, except: except, context: context)
366
+ as_toml(instance, only: only, except: except, context: context),
367
+ **toml_options
360
368
  )
361
369
  end
362
370
 
@@ -1000,17 +1008,19 @@ module Shale
1000
1008
  # @param [Array<Symbol>] except
1001
1009
  # @param [any] context
1002
1010
  # @param [true, false] pretty
1011
+ # @param [Hash] json_options
1003
1012
  #
1004
1013
  # @return [String]
1005
1014
  #
1006
1015
  # @api public
1007
- def to_json(only: nil, except: nil, context: nil, pretty: false)
1016
+ def to_json(only: nil, except: nil, context: nil, pretty: false, **json_options)
1008
1017
  self.class.to_json(
1009
1018
  self,
1010
1019
  only: only,
1011
1020
  except: except,
1012
1021
  context: context,
1013
- pretty: pretty
1022
+ pretty: pretty,
1023
+ **json_options
1014
1024
  )
1015
1025
  end
1016
1026
 
@@ -1019,12 +1029,13 @@ module Shale
1019
1029
  # @param [Array<Symbol>] only
1020
1030
  # @param [Array<Symbol>] except
1021
1031
  # @param [any] context
1032
+ # @param [Hash] yaml_options
1022
1033
  #
1023
1034
  # @return [String]
1024
1035
  #
1025
1036
  # @api public
1026
- def to_yaml(only: nil, except: nil, context: nil)
1027
- self.class.to_yaml(self, only: only, except: except, context: context)
1037
+ def to_yaml(only: nil, except: nil, context: nil, **yaml_options)
1038
+ self.class.to_yaml(self, only: only, except: except, context: context, **yaml_options)
1028
1039
  end
1029
1040
 
1030
1041
  # Convert Object to TOML
@@ -1032,12 +1043,13 @@ module Shale
1032
1043
  # @param [Array<Symbol>] only
1033
1044
  # @param [Array<Symbol>] except
1034
1045
  # @param [any] context
1046
+ # @param [Hash] toml_options
1035
1047
  #
1036
1048
  # @return [String]
1037
1049
  #
1038
1050
  # @api public
1039
- def to_toml(only: nil, except: nil, context: nil)
1040
- self.class.to_toml(self, only: only, except: except, context: context)
1051
+ def to_toml(only: nil, except: nil, context: nil, **toml_options)
1052
+ self.class.to_toml(self, only: only, except: except, context: context, **toml_options)
1041
1053
  end
1042
1054
 
1043
1055
  # Convert Object to CSV
@@ -1045,6 +1057,8 @@ module Shale
1045
1057
  # @param [Array<Symbol>] only
1046
1058
  # @param [Array<Symbol>] except
1047
1059
  # @param [any] context
1060
+ # @param [true, false] headers
1061
+ # @param [Hash] csv_options
1048
1062
  #
1049
1063
  # @return [String]
1050
1064
  #
@@ -69,5 +69,7 @@ module Shale
69
69
  value&.iso8601
70
70
  end
71
71
  end
72
+
73
+ register(:date, Date)
72
74
  end
73
75
  end
@@ -25,5 +25,7 @@ module Shale
25
25
  end
26
26
  end
27
27
  end
28
+
29
+ register(:float, Float)
28
30
  end
29
31
  end
@@ -17,5 +17,7 @@ module Shale
17
17
  value&.to_i
18
18
  end
19
19
  end
20
+
21
+ register(:integer, Integer)
20
22
  end
21
23
  end
@@ -17,5 +17,7 @@ module Shale
17
17
  value&.to_s
18
18
  end
19
19
  end
20
+
21
+ register(:string, String)
20
22
  end
21
23
  end
@@ -69,5 +69,7 @@ module Shale
69
69
  value&.iso8601
70
70
  end
71
71
  end
72
+
73
+ register(:time, Time)
72
74
  end
73
75
  end
data/lib/shale/type.rb ADDED
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shale
4
+ module Type
5
+ class << self
6
+ # Register a symbol alias for a Shale::Type::Value class
7
+ #
8
+ # @param [Symbol] type Short type alias
9
+ # @param [Shale::Type::Value] klass Class to register
10
+ #
11
+ # @raise [NotATypeValueError] when klass is not a Shale::Type::Value
12
+ #
13
+ # @example
14
+ # class UnixTimestamp < Shale::Type::Value
15
+ # def self.cast(value)
16
+ # Time.at(value.to_i)
17
+ # end
18
+ # end
19
+ #
20
+ # Shale::Type.register(:unix_timestamp, UnixTimestamp)
21
+ #
22
+ # @api public
23
+ def register(type, klass)
24
+ @registry ||= {}
25
+
26
+ unless klass < Value
27
+ raise NotATypeValueError, "class '#{klass}' is not a valid Shale::Type::Value"
28
+ end
29
+
30
+ @registry[type] = klass
31
+ end
32
+
33
+ # Lookup a Shale::Type::Value class by type alias
34
+ #
35
+ # @param [Symbol] type Type alias
36
+ #
37
+ # @raise [UnknownTypeError] when type is not registered
38
+ #
39
+ # @return [Shale::Type::Value] Class registered for type
40
+ #
41
+ # @example
42
+ #
43
+ # Shale::Type.lookup(:unix_timestamp)
44
+ # # => UnixTimestamp
45
+ #
46
+ # @api public
47
+ def lookup(type)
48
+ klass = @registry[type]
49
+
50
+ raise UnknownTypeError, "unknown type '#{type}'" unless klass
51
+
52
+ klass
53
+ end
54
+ end
55
+ end
56
+ end
data/lib/shale/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Shale
4
4
  # @api private
5
- VERSION = '1.1.0'
5
+ VERSION = '1.2.1'
6
6
  end
data/lib/shale.rb CHANGED
@@ -4,6 +4,7 @@ require 'yaml'
4
4
 
5
5
  require_relative 'shale/mapper'
6
6
  require_relative 'shale/adapter/json'
7
+ require_relative 'shale/type'
7
8
  require_relative 'shale/type/boolean'
8
9
  require_relative 'shale/type/date'
9
10
  require_relative 'shale/type/float'
@@ -80,16 +81,17 @@ module Shale
80
81
  # @api public
81
82
  attr_writer :yaml_adapter
82
83
 
83
- # TOML adapter accessor.
84
+ # TOML adapter accessor. Available adapters are Shale::Adapter::Tomlib
85
+ # and Shale::Adapter::TomRB
84
86
  #
85
- # @param [@see Shale::Adapter::TomlRB] adapter
87
+ # @param [@see Shale::Adapter::Tomlib] adapter
86
88
  #
87
89
  # @example setting adapter
88
- # Shale.toml_adapter = Shale::Adapter::TomlRB
90
+ # Shale.toml_adapter = Shale::Adapter::Tomlib
89
91
  #
90
92
  # @example getting adapter
91
93
  # Shale.toml_adapter
92
- # # => Shale::Adapter::TomlRB
94
+ # # => Shale::Adapter::Tomlib
93
95
  #
94
96
  # @api public
95
97
  attr_accessor :toml_adapter
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shale
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kamil Giszczak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-17 00:00:00.000000000 Z
11
+ date: 2025-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -49,6 +49,7 @@ files:
49
49
  - lib/shale/adapter/rexml/document.rb
50
50
  - lib/shale/adapter/rexml/node.rb
51
51
  - lib/shale/adapter/toml_rb.rb
52
+ - lib/shale/adapter/tomlib.rb
52
53
  - lib/shale/attribute.rb
53
54
  - lib/shale/error.rb
54
55
  - lib/shale/mapper.rb
@@ -104,6 +105,7 @@ files:
104
105
  - lib/shale/schema/xml_generator/schema.rb
105
106
  - lib/shale/schema/xml_generator/typed_attribute.rb
106
107
  - lib/shale/schema/xml_generator/typed_element.rb
108
+ - lib/shale/type.rb
107
109
  - lib/shale/type/boolean.rb
108
110
  - lib/shale/type/complex.rb
109
111
  - lib/shale/type/date.rb