shale 0.9.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +179 -48
- data/exe/shaleb +19 -4
- data/lib/shale/error.rb +20 -8
- data/lib/shale/mapper.rb +7 -7
- data/lib/shale/mapping/delegates.rb +95 -0
- data/lib/shale/mapping/descriptor/dict.rb +10 -1
- data/lib/shale/mapping/descriptor/xml.rb +13 -2
- data/lib/shale/mapping/dict.rb +14 -4
- data/lib/shale/mapping/dict_base.rb +6 -3
- data/lib/shale/mapping/dict_group.rb +1 -1
- data/lib/shale/mapping/validator.rb +10 -3
- data/lib/shale/mapping/xml.rb +22 -6
- data/lib/shale/mapping/xml_base.rb +21 -12
- data/lib/shale/schema/compiler/complex.rb +52 -8
- data/lib/shale/schema/compiler/xml_complex.rb +5 -4
- data/lib/shale/schema/json_compiler.rb +27 -13
- data/lib/shale/schema/json_generator.rb +2 -2
- data/lib/shale/schema/xml_compiler.rb +46 -18
- data/lib/shale/schema/xml_generator.rb +3 -3
- data/lib/shale/schema.rb +10 -4
- data/lib/shale/type/complex.rb +165 -24
- data/lib/shale/utils.rb +22 -4
- data/lib/shale/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e3b4d27ab7e8cc9b4873dfa60b52bdbafe50c2885085c4ec77ac6887351aab1
|
4
|
+
data.tar.gz: 6ce71a10ebf53633930b6d5a92ebb5f736aab44c47d6910a25238209192a4b92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23c3b182c72c0e1d5102eb667541caccfb56e836e636bd083027425a60a12ed39ffce1ab3540adc0659c6b1bcbcf5255d192bbdc2d39bfc10c762ee4bdaaaa1e
|
7
|
+
data.tar.gz: ef7481e6076cc8f426c1dd0fb002d984594cbf3d59743a31450e8989048ffdd6cdc8fc735e90b7529c1935d7251c6751e70f7c3734d5c3f7df4b8124cc4b8256
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## [1.0.0] - 2023-07-15
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Support for Ruby 3.2
|
5
|
+
- Support for delegating fields to nested attributes
|
6
|
+
- JSON and XML schema namespace mapping support
|
7
|
+
- Allow to set render_nil defaults
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- Use `ShaleError` as a base class for exceptions
|
11
|
+
- Use model instead of mapper names for schema types
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
- Fix compilation error for bundled JSON schemas
|
15
|
+
- Fix type inference for XML schema when default namespace is used
|
16
|
+
- Fix XML schema handling with a period in the element name
|
17
|
+
|
1
18
|
## [0.9.0] - 2022-10-31
|
2
19
|
|
3
20
|
### Added
|
data/README.md
CHANGED
@@ -63,6 +63,7 @@ $ gem install shale
|
|
63
63
|
* [Using XML namespaces](#using-xml-namespaces)
|
64
64
|
* [Rendering nil values](#rendering-nil-values)
|
65
65
|
* [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
|
66
|
+
* [Delegating fields to child attributes](#delegating-fields-to-child-attributes)
|
66
67
|
* [Additional options](#additional-options)
|
67
68
|
* [Using custom models](#using-custom-models)
|
68
69
|
* [Supported types](#supported-types)
|
@@ -692,6 +693,49 @@ puts person.to_xml(pretty: true)
|
|
692
693
|
# </person>
|
693
694
|
```
|
694
695
|
|
696
|
+
If you want to change how nil values are rendered for all mappings you can use `render_nil` method:
|
697
|
+
|
698
|
+
```ruby
|
699
|
+
class Base < Shale::Mapper
|
700
|
+
json do
|
701
|
+
# change render_nil default for all JSON mappings inheriting from Base class
|
702
|
+
render_nil true
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
class Person < Base
|
707
|
+
attribute :first_name, Shale::Type::String
|
708
|
+
attribute :last_name, Shale::Type::String
|
709
|
+
attribute :age, Shale::Type::Integer
|
710
|
+
|
711
|
+
json do
|
712
|
+
# override default from Base class
|
713
|
+
render_nil false
|
714
|
+
|
715
|
+
map 'first_name', to: :first_name
|
716
|
+
map 'last_name', to: :last_name
|
717
|
+
map 'age', to: :age, render_nil: true # override default
|
718
|
+
end
|
719
|
+
end
|
720
|
+
```
|
721
|
+
|
722
|
+
:warning: The default affects only the mappings declared after setting the default value e.g.
|
723
|
+
|
724
|
+
```ruby
|
725
|
+
class Person < Base
|
726
|
+
attribute :first_name, Shale::Type::String
|
727
|
+
attribute :last_name, Shale::Type::String
|
728
|
+
|
729
|
+
json do
|
730
|
+
render_nil false
|
731
|
+
map 'first_name', to: :first_name # render_nil will be false for this mapping
|
732
|
+
|
733
|
+
render_nil true
|
734
|
+
map 'last_name', to: :last_name # render_nil will be true for this mapping
|
735
|
+
end
|
736
|
+
end
|
737
|
+
```
|
738
|
+
|
695
739
|
### Using methods to extract and generate data
|
696
740
|
|
697
741
|
If you need full controll over extracting and generating data from/to document,
|
@@ -865,6 +909,42 @@ DATA
|
|
865
909
|
# => #<Person:0x00007f9bc3086d60 @name="John Doe">
|
866
910
|
```
|
867
911
|
|
912
|
+
### Delegating fields to child attributes
|
913
|
+
|
914
|
+
To delegate fields to child complex types you can use `receiver: :child` declaration:
|
915
|
+
|
916
|
+
```ruby
|
917
|
+
class Address < Shale::Mapper
|
918
|
+
attribute :city, Shale::Type::String
|
919
|
+
attribute :street, Shale::Type::String
|
920
|
+
end
|
921
|
+
|
922
|
+
class Person < Shale::Mapper
|
923
|
+
attribute :name, Shale::Type::String
|
924
|
+
attribute :address, Address
|
925
|
+
|
926
|
+
json do
|
927
|
+
map 'name', to: :name
|
928
|
+
map 'city', to: :city, receiver: :address
|
929
|
+
map 'street', to: :street, receiver: :address
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
person = Person.from_json(<<~DATA)
|
934
|
+
{
|
935
|
+
"name": "John Doe",
|
936
|
+
"city": "London",
|
937
|
+
"street": "Oxford Street"
|
938
|
+
}
|
939
|
+
DATA
|
940
|
+
|
941
|
+
# =>
|
942
|
+
#
|
943
|
+
# #<Person:0x00007f9bc3086d60
|
944
|
+
# @name="John Doe",
|
945
|
+
# @address=#<Address:0x0000000102cbd218 @city="London", @street="Oxford Street">>
|
946
|
+
```
|
947
|
+
|
868
948
|
### Additional options
|
869
949
|
|
870
950
|
You can control which attributes to render and parse by
|
@@ -1212,7 +1292,9 @@ Shale::Schema::JSONGenerator.register_json_type(MyEmailType, MyEmailJSONType)
|
|
1212
1292
|
|
1213
1293
|
:warning: Only **[Draft 2020-12](https://json-schema.org/draft/2020-12/schema)** JSON Schema is supported
|
1214
1294
|
|
1215
|
-
To generate Shale data model from JSON Schema use
|
1295
|
+
To generate Shale data model from JSON Schema use `Shale::Schema.from_json`.
|
1296
|
+
You can pass `root_name: 'Foobar'` to change the name of the root type and
|
1297
|
+
`namespace_mapping: {}` to map schemas to Ruby modules:
|
1216
1298
|
|
1217
1299
|
```ruby
|
1218
1300
|
require 'shale/schema'
|
@@ -1223,7 +1305,11 @@ schema = <<~SCHEMA
|
|
1223
1305
|
"properties": {
|
1224
1306
|
"firstName": { "type": "string" },
|
1225
1307
|
"lastName": { "type": "string" },
|
1226
|
-
"address": {
|
1308
|
+
"address": { "$ref": "http://bar.com" }
|
1309
|
+
},
|
1310
|
+
"$defs": {
|
1311
|
+
"Address": {
|
1312
|
+
"$id": "http://bar.com",
|
1227
1313
|
"type": "object",
|
1228
1314
|
"properties": {
|
1229
1315
|
"street": { "type": "string" },
|
@@ -1234,38 +1320,53 @@ schema = <<~SCHEMA
|
|
1234
1320
|
}
|
1235
1321
|
SCHEMA
|
1236
1322
|
|
1237
|
-
Shale::Schema.from_json(
|
1323
|
+
Shale::Schema.from_json(
|
1324
|
+
[schema],
|
1325
|
+
root_name: 'Person',
|
1326
|
+
namespace_mapping: {
|
1327
|
+
nil => 'Api::Foo', # default schema (without ID)
|
1328
|
+
'http://bar.com' => 'Api::Bar',
|
1329
|
+
}
|
1330
|
+
)
|
1238
1331
|
|
1239
1332
|
# =>
|
1240
1333
|
#
|
1241
1334
|
# {
|
1242
|
-
# "address" => "
|
1335
|
+
# "api/bar/address" => "
|
1243
1336
|
# require 'shale'
|
1244
1337
|
#
|
1245
|
-
#
|
1246
|
-
#
|
1247
|
-
#
|
1338
|
+
# module Api
|
1339
|
+
# module Bar
|
1340
|
+
# class Address < Shale::Mapper
|
1341
|
+
# attribute :street, Shale::Type::String
|
1342
|
+
# attribute :city, Shale::Type::String
|
1248
1343
|
#
|
1249
|
-
#
|
1250
|
-
#
|
1251
|
-
#
|
1344
|
+
# json do
|
1345
|
+
# map 'street', to: :street
|
1346
|
+
# map 'city', to: :city
|
1347
|
+
# end
|
1348
|
+
# end
|
1252
1349
|
# end
|
1253
1350
|
# end
|
1254
1351
|
# ",
|
1255
|
-
# "person" => "
|
1352
|
+
# "api/foo/person" => "
|
1256
1353
|
# require 'shale'
|
1257
1354
|
#
|
1258
|
-
# require_relative 'address'
|
1355
|
+
# require_relative '../bar/address'
|
1259
1356
|
#
|
1260
|
-
#
|
1261
|
-
#
|
1262
|
-
#
|
1263
|
-
#
|
1357
|
+
# module Api
|
1358
|
+
# module Foo
|
1359
|
+
# class Person < Shale::Mapper
|
1360
|
+
# attribute :first_name, Shale::Type::String
|
1361
|
+
# attribute :last_name, Shale::Type::String
|
1362
|
+
# attribute :address, Api::Bar::Address
|
1264
1363
|
#
|
1265
|
-
#
|
1266
|
-
#
|
1267
|
-
#
|
1268
|
-
#
|
1364
|
+
# json do
|
1365
|
+
# map 'firstName', to: :first_name
|
1366
|
+
# map 'lastName', to: :last_name
|
1367
|
+
# map 'address', to: :address
|
1368
|
+
# end
|
1369
|
+
# end
|
1269
1370
|
# end
|
1270
1371
|
# end
|
1271
1372
|
# "
|
@@ -1275,7 +1376,7 @@ Shale::Schema.from_json([schema], root_name: 'Person')
|
|
1275
1376
|
You can also use a command line tool to do it:
|
1276
1377
|
|
1277
1378
|
```
|
1278
|
-
$ shaleb -c -i schema.json -r Person
|
1379
|
+
$ shaleb -c -i schema.json -r Person -m http://bar.com=Api::Bar,=Api::Foo
|
1279
1380
|
```
|
1280
1381
|
|
1281
1382
|
### Generating XML Schema
|
@@ -1346,22 +1447,39 @@ Shale::Schema::XMLGenerator.register_xml_type(MyEmailType, 'myEmailXMLType')
|
|
1346
1447
|
|
1347
1448
|
### Compiling XML Schema into Shale model
|
1348
1449
|
|
1349
|
-
To generate Shale data model from XML Schema use
|
1450
|
+
To generate Shale data model from XML Schema use `Shale::Schema.from_xml`.
|
1451
|
+
You can pass `namespace_mapping: {}` to map XML namespaces to Ruby modules:
|
1350
1452
|
|
1351
1453
|
```ruby
|
1352
1454
|
require 'shale/schema'
|
1353
1455
|
|
1354
|
-
|
1355
|
-
<xs:schema
|
1456
|
+
schema1 = <<~SCHEMA
|
1457
|
+
<xs:schema
|
1458
|
+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
1459
|
+
xmlns:bar="http://bar.com"
|
1460
|
+
elementFormDefault="qualified"
|
1461
|
+
>
|
1462
|
+
<xs:import namespace="http://bar.com" />
|
1463
|
+
|
1356
1464
|
<xs:element name="Person" type="Person" />
|
1357
1465
|
|
1358
1466
|
<xs:complexType name="Person">
|
1359
1467
|
<xs:sequence>
|
1360
|
-
<xs:element name="
|
1361
|
-
<xs:element
|
1362
|
-
<xs:element name="Address" type="Address" />
|
1468
|
+
<xs:element name="Name" type="xs:string" />
|
1469
|
+
<xs:element ref="bar:Address" />
|
1363
1470
|
</xs:sequence>
|
1364
1471
|
</xs:complexType>
|
1472
|
+
</xs:schema>
|
1473
|
+
SCHEMA
|
1474
|
+
|
1475
|
+
schema2 = <<~SCHEMA
|
1476
|
+
<xs:schema
|
1477
|
+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
1478
|
+
xmlns:bar="http://bar.com"
|
1479
|
+
targetNamespace="http://bar.com"
|
1480
|
+
elementFormDefault="qualified"
|
1481
|
+
>
|
1482
|
+
<xs:element name="Address" type="bar:Address" />
|
1365
1483
|
|
1366
1484
|
<xs:complexType name="Address">
|
1367
1485
|
<xs:sequence>
|
@@ -1372,42 +1490,55 @@ schema = <<~SCHEMA
|
|
1372
1490
|
</xs:schema>
|
1373
1491
|
SCHEMA
|
1374
1492
|
|
1375
|
-
Shale::Schema.from_xml(
|
1493
|
+
Shale::Schema.from_xml(
|
1494
|
+
[schema1, schema2],
|
1495
|
+
namespace_mapping: {
|
1496
|
+
nil => 'Api::Foo', # no namespace
|
1497
|
+
'http://bar.com' => 'Api::Bar',
|
1498
|
+
}
|
1499
|
+
)
|
1376
1500
|
|
1377
1501
|
# =>
|
1378
1502
|
#
|
1379
1503
|
# {
|
1380
|
-
# "address" => "
|
1504
|
+
# "api/bar/address" => "
|
1381
1505
|
# require 'shale'
|
1382
1506
|
#
|
1383
|
-
#
|
1384
|
-
#
|
1385
|
-
#
|
1507
|
+
# module Api
|
1508
|
+
# module Bar
|
1509
|
+
# class Address < Shale::Mapper
|
1510
|
+
# attribute :street, Shale::Type::String
|
1511
|
+
# attribute :city, Shale::Type::String
|
1386
1512
|
#
|
1387
|
-
#
|
1388
|
-
#
|
1513
|
+
# xml do
|
1514
|
+
# root 'Address'
|
1515
|
+
# namespace 'http://bar.com', 'bar'
|
1389
1516
|
#
|
1390
|
-
#
|
1391
|
-
#
|
1517
|
+
# map_element 'Street', to: :street
|
1518
|
+
# map_element 'City', to: :city
|
1519
|
+
# end
|
1520
|
+
# end
|
1392
1521
|
# end
|
1393
1522
|
# end
|
1394
1523
|
# ",
|
1395
|
-
# "person" => "
|
1524
|
+
# "api/foo/person" => "
|
1396
1525
|
# require 'shale'
|
1397
1526
|
#
|
1398
|
-
# require_relative 'address'
|
1527
|
+
# require_relative '../bar/address'
|
1399
1528
|
#
|
1400
|
-
#
|
1401
|
-
#
|
1402
|
-
#
|
1403
|
-
#
|
1529
|
+
# module Api
|
1530
|
+
# module Foo
|
1531
|
+
# class Person < Shale::Mapper
|
1532
|
+
# attribute :name, Shale::Type::String
|
1533
|
+
# attribute :address, Api::Bar::Address
|
1404
1534
|
#
|
1405
|
-
#
|
1406
|
-
#
|
1535
|
+
# xml do
|
1536
|
+
# root 'Person'
|
1407
1537
|
#
|
1408
|
-
#
|
1409
|
-
#
|
1410
|
-
#
|
1538
|
+
# map_element 'Name', to: :name
|
1539
|
+
# map_element 'Address', to: :address, prefix: 'bar', namespace: 'http://bar.com'
|
1540
|
+
# end
|
1541
|
+
# end
|
1411
1542
|
# end
|
1412
1543
|
# end
|
1413
1544
|
# "
|
@@ -1417,7 +1548,7 @@ Shale::Schema.from_xml([schema])
|
|
1417
1548
|
You can also use a command line tool to do it:
|
1418
1549
|
|
1419
1550
|
```
|
1420
|
-
$ shaleb -c -f xml -i schema.xml
|
1551
|
+
$ shaleb -c -f xml -i schema.xml -m http://bar.com=Api::Bar,=Api::Foo
|
1421
1552
|
```
|
1422
1553
|
|
1423
1554
|
## Contributing
|
data/exe/shaleb
CHANGED
@@ -36,8 +36,8 @@ ARGV << '-h' if ARGV.empty?
|
|
36
36
|
OptionParser.new do |opts|
|
37
37
|
opts.banner = <<~BANNER
|
38
38
|
Usage: shaleb [options]
|
39
|
-
example generate schema from Shale model: shaleb -g -i data_model.rb -
|
40
|
-
example generate Shale model from schema: shaleb -c -i schema1.json,schema2.json -
|
39
|
+
example generate schema from Shale model: shaleb -g -i data_model.rb -r MyRoot
|
40
|
+
example generate Shale model from schema: shaleb -c -i schema1.json,schema2.json -r MyRoot
|
41
41
|
BANNER
|
42
42
|
|
43
43
|
opts.on('-g', '--generate', 'generate schema from Shale model')
|
@@ -47,6 +47,7 @@ OptionParser.new do |opts|
|
|
47
47
|
opts.on('-r ROOT', '--root ROOT', 'Shale model class name')
|
48
48
|
opts.on('-f FORMAT', '--format FORMAT', 'Schema format: JSON (default), XML')
|
49
49
|
opts.on('-p', '--pretty', 'Pretty print generated schema')
|
50
|
+
opts.on('-m MAPPING', '--mapping', Array, 'Namespace mapping')
|
50
51
|
|
51
52
|
opts.on('-v', '--version', 'Show version') do
|
52
53
|
puts "shaleb version #{Shale::VERSION}"
|
@@ -71,11 +72,24 @@ if params[:compile]
|
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
75
|
+
if params[:mapping]
|
76
|
+
namespace_mapping = params[:mapping]
|
77
|
+
.to_h { |e| [*e.split('='), nil][0, 2] }
|
78
|
+
.transform_keys { |key| key.empty? ? nil : key }
|
79
|
+
end
|
80
|
+
|
74
81
|
if params[:format] == 'xml'
|
75
82
|
load_xml_parser
|
76
|
-
models = Shale::Schema.from_xml(
|
83
|
+
models = Shale::Schema.from_xml(
|
84
|
+
schemas,
|
85
|
+
namespace_mapping: namespace_mapping
|
86
|
+
)
|
77
87
|
else
|
78
|
-
models = Shale::Schema.from_json(
|
88
|
+
models = Shale::Schema.from_json(
|
89
|
+
schemas,
|
90
|
+
root_name: params[:root],
|
91
|
+
namespace_mapping: namespace_mapping
|
92
|
+
)
|
79
93
|
end
|
80
94
|
|
81
95
|
if params[:output]
|
@@ -84,6 +98,7 @@ if params[:compile]
|
|
84
98
|
|
85
99
|
models.each do |name, model|
|
86
100
|
output_path = File.join(dir, "#{name}.rb")
|
101
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
87
102
|
File.write(output_path, model)
|
88
103
|
end
|
89
104
|
else
|
data/lib/shale/error.rb
CHANGED
@@ -53,10 +53,16 @@ module Shale
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
# Shale base error class
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
class ShaleError < StandardError
|
60
|
+
end
|
61
|
+
|
56
62
|
# Error for trying to assign not callable object as an attribute's default
|
57
63
|
#
|
58
64
|
# @api private
|
59
|
-
class DefaultNotCallableError <
|
65
|
+
class DefaultNotCallableError < ShaleError
|
60
66
|
# Initialize error object
|
61
67
|
#
|
62
68
|
# @param [String] record
|
@@ -71,36 +77,42 @@ module Shale
|
|
71
77
|
# Error for passing incorrect model type
|
72
78
|
#
|
73
79
|
# @api private
|
74
|
-
class IncorrectModelError <
|
80
|
+
class IncorrectModelError < ShaleError
|
75
81
|
end
|
76
82
|
|
77
83
|
# Error for passing incorrect arguments to map functions
|
78
84
|
#
|
79
85
|
# @api private
|
80
|
-
class IncorrectMappingArgumentsError <
|
86
|
+
class IncorrectMappingArgumentsError < ShaleError
|
87
|
+
end
|
88
|
+
|
89
|
+
# Error for using incorrect type
|
90
|
+
#
|
91
|
+
# @api private
|
92
|
+
class NotAShaleMapperError < ShaleError
|
81
93
|
end
|
82
94
|
|
83
|
-
#
|
95
|
+
# Raised when receiver attribute is not defined
|
84
96
|
#
|
85
97
|
# @api private
|
86
|
-
class
|
98
|
+
class AttributeNotDefinedError < ShaleError
|
87
99
|
end
|
88
100
|
|
89
101
|
# Schema compilation error
|
90
102
|
#
|
91
103
|
# @api private
|
92
|
-
class SchemaError <
|
104
|
+
class SchemaError < ShaleError
|
93
105
|
end
|
94
106
|
|
95
107
|
# Parsing error
|
96
108
|
#
|
97
109
|
# @api private
|
98
|
-
class ParseError <
|
110
|
+
class ParseError < ShaleError
|
99
111
|
end
|
100
112
|
|
101
113
|
# Adapter error
|
102
114
|
#
|
103
115
|
# @api private
|
104
|
-
class AdapterError <
|
116
|
+
class AdapterError < ShaleError
|
105
117
|
end
|
106
118
|
end
|
data/lib/shale/mapper.rb
CHANGED
@@ -151,7 +151,7 @@ module Shale
|
|
151
151
|
# @raise [DefaultNotCallableError] when attribute's default is not callable
|
152
152
|
#
|
153
153
|
# @example
|
154
|
-
#
|
154
|
+
# class Person < Shale::Mapper
|
155
155
|
# attribute :first_name, Shale::Type::String
|
156
156
|
# attribute :last_name, Shale::Type::String
|
157
157
|
# attribute :age, Shale::Type::Integer, default: -> { 1 }
|
@@ -200,7 +200,7 @@ module Shale
|
|
200
200
|
# @param [Proc] block
|
201
201
|
#
|
202
202
|
# @example
|
203
|
-
#
|
203
|
+
# class Person < Shale::Mapper
|
204
204
|
# attribute :first_name, Shale::Type::String
|
205
205
|
# attribute :last_name, Shale::Type::String
|
206
206
|
# attribute :age, Shale::Type::Integer
|
@@ -224,7 +224,7 @@ module Shale
|
|
224
224
|
# @param [Proc] block
|
225
225
|
#
|
226
226
|
# @example
|
227
|
-
#
|
227
|
+
# class Person < Shale::Mapper
|
228
228
|
# attribute :first_name, Shale::Type::String
|
229
229
|
# attribute :last_name, Shale::Type::String
|
230
230
|
# attribute :age, Shale::Type::Integer
|
@@ -248,7 +248,7 @@ module Shale
|
|
248
248
|
# @param [Proc] block
|
249
249
|
#
|
250
250
|
# @example
|
251
|
-
#
|
251
|
+
# class Person < Shale::Mapper
|
252
252
|
# attribute :first_name, Shale::Type::String
|
253
253
|
# attribute :last_name, Shale::Type::String
|
254
254
|
# attribute :age, Shale::Type::Integer
|
@@ -272,7 +272,7 @@ module Shale
|
|
272
272
|
# @param [Proc] block
|
273
273
|
#
|
274
274
|
# @example
|
275
|
-
#
|
275
|
+
# class Person < Shale::Mapper
|
276
276
|
# attribute :first_name, Shale::Type::String
|
277
277
|
# attribute :last_name, Shale::Type::String
|
278
278
|
# attribute :age, Shale::Type::Integer
|
@@ -296,7 +296,7 @@ module Shale
|
|
296
296
|
# @param [Proc] block
|
297
297
|
#
|
298
298
|
# @example
|
299
|
-
#
|
299
|
+
# class Person < Shale::Mapper
|
300
300
|
# attribute :first_name, Shale::Type::String
|
301
301
|
# attribute :last_name, Shale::Type::String
|
302
302
|
# attribute :age, Shale::Type::Integer
|
@@ -320,7 +320,7 @@ module Shale
|
|
320
320
|
# @param [Proc] block
|
321
321
|
#
|
322
322
|
# @example
|
323
|
-
#
|
323
|
+
# class Person < Shale::Mapper
|
324
324
|
# attribute :first_name, Shale::Type::String
|
325
325
|
# attribute :last_name, Shale::Type::String
|
326
326
|
# attribute :age, Shale::Type::Integer
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shale
|
4
|
+
module Mapping
|
5
|
+
# Class for handling attribute delegation
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class Delegates
|
9
|
+
# Class representing individual delegation
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class Delegate
|
13
|
+
# Return receiver_attribute
|
14
|
+
#
|
15
|
+
# @return [Shale::Attribute]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
attr_reader :receiver_attribute
|
19
|
+
|
20
|
+
# Return attribute setter on a delegate
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
attr_reader :setter
|
26
|
+
|
27
|
+
# Return value to set on a delegate
|
28
|
+
#
|
29
|
+
# @return [any]
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
attr_reader :value
|
33
|
+
|
34
|
+
# Initialize instance
|
35
|
+
#
|
36
|
+
# @param [Shale::Attribute] receiver_attribute
|
37
|
+
# @param [String] setter
|
38
|
+
# @param [any] value
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
def initialize(receiver_attribute, setter, value)
|
42
|
+
@receiver_attribute = receiver_attribute
|
43
|
+
@setter = setter
|
44
|
+
@value = value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Initialize instance
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
def initialize
|
52
|
+
@delegates = []
|
53
|
+
end
|
54
|
+
|
55
|
+
# Add single value to delegate
|
56
|
+
#
|
57
|
+
# @param [Shale::Attribute] receiver_attribute
|
58
|
+
# @param [String] setter
|
59
|
+
# @param [any] value
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def add(receiver_attribute, setter, value)
|
63
|
+
@delegates << Delegate.new(receiver_attribute, setter, value)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add collection to delegate
|
67
|
+
#
|
68
|
+
# @param [Shale::Attribute] receiver_attribute
|
69
|
+
# @param [String] setter
|
70
|
+
# @param [any] value
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
def add_collection(receiver_attribute, setter, value)
|
74
|
+
delegate = @delegates.find do |e|
|
75
|
+
e.receiver_attribute == receiver_attribute && e.setter == setter
|
76
|
+
end
|
77
|
+
|
78
|
+
if delegate
|
79
|
+
delegate.value << value
|
80
|
+
else
|
81
|
+
@delegates << Delegate.new(receiver_attribute, setter, [value])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Iterate over delegates and yield a block
|
86
|
+
#
|
87
|
+
# @param [Proc] block
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
def each(&block)
|
91
|
+
@delegates.each(&block)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -21,6 +21,13 @@ module Shale
|
|
21
21
|
# @api private
|
22
22
|
attr_reader :attribute
|
23
23
|
|
24
|
+
# Return receiver name
|
25
|
+
#
|
26
|
+
# @return [Symbol]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
attr_reader :receiver
|
30
|
+
|
24
31
|
# Return method symbol
|
25
32
|
#
|
26
33
|
# @return [Symbol]
|
@@ -46,14 +53,16 @@ module Shale
|
|
46
53
|
#
|
47
54
|
# @param [String] name
|
48
55
|
# @param [Symbol, nil] attribute
|
56
|
+
# @param [Symbol, nil] receiver
|
49
57
|
# @param [Hash, nil] methods
|
50
58
|
# @param [String, nil] group
|
51
59
|
# @param [true, false] render_nil
|
52
60
|
#
|
53
61
|
# @api private
|
54
|
-
def initialize(name:, attribute:, methods:, group:, render_nil:)
|
62
|
+
def initialize(name:, attribute:, receiver:, methods:, group:, render_nil:)
|
55
63
|
@name = name
|
56
64
|
@attribute = attribute
|
65
|
+
@receiver = receiver
|
57
66
|
@group = group
|
58
67
|
@render_nil = render_nil
|
59
68
|
|