datory 1.0.0.rc10 → 1.0.0.rc12
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/README.md +122 -1
- data/lib/datory/attributes/attribute.rb +75 -5
- data/lib/datory/attributes/collection.rb +19 -1
- data/lib/datory/attributes/descriptor.rb +7 -3
- data/lib/datory/attributes/{tools → deserialization}/service_builder.rb +2 -2
- data/lib/datory/attributes/deserialization/service_factory.rb +36 -0
- data/lib/datory/attributes/dsl.rb +43 -2
- data/lib/datory/attributes/serialization/model.rb +31 -10
- data/lib/datory/attributes/serialization/serializator.rb +4 -4
- data/lib/datory/attributes/serialization/service_builder.rb +41 -0
- data/lib/datory/attributes/serialization/service_factory.rb +36 -0
- data/lib/datory/attributes/workspace.rb +21 -2
- data/lib/datory/base.rb +1 -0
- data/lib/datory/console.rb +2 -1
- data/lib/datory/context/callable.rb +41 -28
- data/lib/datory/context/workspace.rb +25 -10
- data/lib/datory/exceptions.rb +8 -0
- data/lib/datory/info/dsl.rb +32 -0
- data/lib/datory/info/result.rb +13 -0
- data/lib/datory/service/base.rb +6 -2
- data/lib/datory/service/builder.rb +66 -0
- data/lib/datory/version.rb +1 -1
- metadata +12 -8
- data/lib/datory/attributes/tools/service_factory.rb +0 -63
- data/lib/datory/utils.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 198bd72a333d4fa2fe2ee18935d586ab10f6185c7b51c6cb94f2627f5698fb52
|
4
|
+
data.tar.gz: 6aa7c4bf85587d02a38e79de2135c83bed01be5153a644c0892433887dd1b982
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed216465c59651df53b3170a9381a058a26b5dcaa48058b20300eae6111f9ef582b198391f3813f49e886759c18844a621c4b1a77d9d4a131080fda9ed2e5608
|
7
|
+
data.tar.gz: df02fd6e1eabc40683e506052d448aa9f563f857a52fc48972482b30e962a6571801f9c816ca805bbe1fed8b1acf4c8310d6784374281ecfeb075c1d182480ab
|
data/README.md
CHANGED
@@ -67,11 +67,132 @@ class UserLoginDto < Datory::Base
|
|
67
67
|
end
|
68
68
|
```
|
69
69
|
|
70
|
+
## Attribute declaration
|
71
|
+
|
72
|
+
### Basic
|
73
|
+
|
74
|
+
#### attribute
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
attribute :firstname, from: String, to: :first_name, as: String
|
78
|
+
```
|
79
|
+
|
80
|
+
#### string
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
string :first_name
|
84
|
+
```
|
85
|
+
|
86
|
+
#### integer
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
integer :attempts, min: 1, max: 10
|
90
|
+
```
|
91
|
+
|
92
|
+
#### float
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
float :interest_rate
|
96
|
+
```
|
97
|
+
|
98
|
+
### Helpers
|
99
|
+
|
100
|
+
#### uuid
|
101
|
+
|
102
|
+
It will also check that the value matches the UUID format.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
uuid :id
|
106
|
+
```
|
107
|
+
|
108
|
+
#### money
|
109
|
+
|
110
|
+
It will prepare two attributes `*_cents` and `*_currency`.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
money :price
|
114
|
+
```
|
115
|
+
|
116
|
+
#### duration
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
duration :lifetime
|
120
|
+
```
|
121
|
+
|
122
|
+
#### date
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
date :birth_date
|
126
|
+
```
|
127
|
+
|
128
|
+
#### time
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
time :registered_at
|
132
|
+
```
|
133
|
+
|
134
|
+
#### datetime
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
datetime :registered_at
|
138
|
+
```
|
139
|
+
|
140
|
+
### Nesting
|
141
|
+
|
142
|
+
#### one
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
one :company, include: UserCompanyDto
|
146
|
+
```
|
147
|
+
|
148
|
+
#### many
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
many :addresses, include: UserAddressDto
|
152
|
+
```
|
153
|
+
|
154
|
+
## Object information
|
155
|
+
|
156
|
+
### Info
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
UserDto.info
|
160
|
+
```
|
161
|
+
|
162
|
+
```
|
163
|
+
#<Datory::Info::Result:0x00000001069c45d0 @attributes={:id=>{:from=>String, :to=>:id, :as=>String, :include=>nil}, :firstname=>{:from=>String, :to=>:first_name, :as=>String, :include=>nil}, :lastname=>{:from=>String, :to=>:last_name, :as=>String, :include=>nil}, :email=>{:from=>String, :to=>:email, :as=>String, :include=>nil}, :phone=>{:from=>String, :to=>:phone, :as=>String, :include=>nil}, :website=>{:from=>String, :to=>:website, :as=>String, :include=>nil}, :birthDate=>{:from=>String, :to=>:birth_date, :as=>Date, :include=>nil}, :login=>{:from=>Hash, :to=>:login, :as=>[Datory::Result, Hash], :include=>Usual::Example1::UserLogin}, :company=>{:from=>Hash, :to=>:company, :as=>[Datory::Result, Hash], :include=>Usual::Example1::UserCompany}, :addresses=>{:from=>Array, :to=>:addresses, :as=>Array, :include=>Usual::Example1::UserAddress}}>
|
164
|
+
```
|
165
|
+
|
166
|
+
### Describe
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
UserDto.describe
|
170
|
+
```
|
171
|
+
|
172
|
+
```
|
173
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
174
|
+
| UserDto |
|
175
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
176
|
+
| Attribute | From | To | As | Include |
|
177
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
178
|
+
| id | String | id | String | |
|
179
|
+
| firstname | String | first_name | String | |
|
180
|
+
| lastname | String | last_name | String | |
|
181
|
+
| email | String | email | String | |
|
182
|
+
| phone | String | phone | String | |
|
183
|
+
| website | String | website | String | |
|
184
|
+
| birthDate | String | birth_date | Date | |
|
185
|
+
| login | Hash | login | [Datory::Result, Hash] | Usual::Example1::UserLogin |
|
186
|
+
| company | Hash | company | [Datory::Result, Hash] | Usual::Example1::UserCompany |
|
187
|
+
| addresses | Array | addresses | Array | Usual::Example1::UserAddress |
|
188
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
189
|
+
```
|
190
|
+
|
70
191
|
## Contributing
|
71
192
|
|
72
193
|
This project is intended to be a safe, welcoming space for collaboration.
|
73
194
|
Contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
74
|
-
We recommend reading the [contributing guide](./
|
195
|
+
We recommend reading the [contributing guide](./CONTRIBUTING.md) as well.
|
75
196
|
|
76
197
|
## License
|
77
198
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Datory
|
4
4
|
module Attributes
|
5
|
-
class Attribute
|
5
|
+
class Attribute # rubocop:disable Metrics/ClassLength
|
6
6
|
attr_reader :name, :options
|
7
7
|
|
8
8
|
def initialize(name, **options)
|
@@ -10,7 +10,30 @@ module Datory
|
|
10
10
|
@options = options
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def input_serialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
14
|
+
hash = {
|
15
|
+
as: options.fetch(:to, name),
|
16
|
+
type: options.fetch(:as, options.fetch(:from)),
|
17
|
+
required: options.fetch(:required, true),
|
18
|
+
consists_of: options.fetch(:consists_of, false)
|
19
|
+
}
|
20
|
+
|
21
|
+
if (min = options.fetch(:min, nil)).present?
|
22
|
+
hash[:min] = min
|
23
|
+
end
|
24
|
+
|
25
|
+
if (max = options.fetch(:max, nil)).present?
|
26
|
+
hash[:max] = max
|
27
|
+
end
|
28
|
+
|
29
|
+
if (format = options.fetch(:format, nil)).present?
|
30
|
+
hash[:format] = format
|
31
|
+
end
|
32
|
+
|
33
|
+
hash
|
34
|
+
end
|
35
|
+
|
36
|
+
def input_deserialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
14
37
|
hash = {
|
15
38
|
as: options.fetch(:to, name),
|
16
39
|
type: options.fetch(:from),
|
@@ -23,13 +46,52 @@ module Datory
|
|
23
46
|
from_type = options.fetch(:from, nil)
|
24
47
|
|
25
48
|
if [Set, Array].include?(from_type)
|
26
|
-
value.map { |item| include_class.
|
49
|
+
value.map { |item| include_class.deserialize(**item) }
|
27
50
|
else
|
28
|
-
include_class.
|
51
|
+
include_class.deserialize(**value)
|
29
52
|
end
|
30
53
|
end)
|
31
54
|
}
|
32
55
|
|
56
|
+
if (min = options.fetch(:min, nil)).present?
|
57
|
+
hash[:min] = min
|
58
|
+
end
|
59
|
+
|
60
|
+
if (max = options.fetch(:max, nil)).present?
|
61
|
+
hash[:max] = max
|
62
|
+
end
|
63
|
+
|
64
|
+
if (format = options.fetch(:format, nil)).present?
|
65
|
+
hash[:format] = format
|
66
|
+
end
|
67
|
+
|
68
|
+
hash
|
69
|
+
end
|
70
|
+
|
71
|
+
def output_serialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
72
|
+
hash = {
|
73
|
+
consists_of: if (consists_of_type = options.fetch(:consists_of, false)) == Hash
|
74
|
+
Datory::Result
|
75
|
+
else
|
76
|
+
consists_of_type
|
77
|
+
end,
|
78
|
+
type: if (as_type = options.fetch(:as, options.fetch(:from))) == Datory::Result
|
79
|
+
Hash
|
80
|
+
elsif (from_type = options.fetch(:from)).present?
|
81
|
+
from_type
|
82
|
+
else
|
83
|
+
as_type
|
84
|
+
end
|
85
|
+
}
|
86
|
+
|
87
|
+
if (min = options.fetch(:min, nil)).present?
|
88
|
+
hash[:min] = min
|
89
|
+
end
|
90
|
+
|
91
|
+
if (max = options.fetch(:max, nil)).present?
|
92
|
+
hash[:max] = max
|
93
|
+
end
|
94
|
+
|
33
95
|
if (format = options.fetch(:format, nil)).present?
|
34
96
|
hash[:format] = format
|
35
97
|
end
|
@@ -37,7 +99,7 @@ module Datory
|
|
37
99
|
hash
|
38
100
|
end
|
39
101
|
|
40
|
-
def
|
102
|
+
def output_deserialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
41
103
|
hash = {
|
42
104
|
consists_of: if (consists_of_type = options.fetch(:consists_of, false)) == Hash
|
43
105
|
Datory::Result
|
@@ -53,6 +115,14 @@ module Datory
|
|
53
115
|
end
|
54
116
|
}
|
55
117
|
|
118
|
+
if (min = options.fetch(:min, nil)).present?
|
119
|
+
hash[:min] = min
|
120
|
+
end
|
121
|
+
|
122
|
+
if (max = options.fetch(:max, nil)).present?
|
123
|
+
hash[:max] = max
|
124
|
+
end
|
125
|
+
|
56
126
|
if (format = options.fetch(:format, nil)).present?
|
57
127
|
hash[:format] = format
|
58
128
|
end
|
@@ -4,7 +4,7 @@ module Datory
|
|
4
4
|
module Attributes
|
5
5
|
class Collection
|
6
6
|
extend Forwardable
|
7
|
-
def_delegators :@collection, :<<, :each, :map, :to_h, :merge
|
7
|
+
def_delegators :@collection, :<<, :each, :map, :filter, :to_h, :merge
|
8
8
|
|
9
9
|
def initialize(collection = Set.new)
|
10
10
|
@collection = collection
|
@@ -13,6 +13,24 @@ module Datory
|
|
13
13
|
def names
|
14
14
|
map(&:name)
|
15
15
|
end
|
16
|
+
|
17
|
+
def internal_names
|
18
|
+
map { |attribute| attribute.options.fetch(:to, attribute.name) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def include_exist?
|
22
|
+
@include_exist ||= filter do |attribute| # rubocop:disable Performance/Count
|
23
|
+
include_class = attribute.options.fetch(:include, nil)
|
24
|
+
|
25
|
+
next false if include_class.nil?
|
26
|
+
|
27
|
+
if [Set, Array].include?(include_class)
|
28
|
+
include_class.any? { |item| item <= Datory::Base }
|
29
|
+
else
|
30
|
+
include_class <= Datory::Base
|
31
|
+
end
|
32
|
+
end.size.positive?
|
33
|
+
end
|
16
34
|
end
|
17
35
|
end
|
18
36
|
end
|
@@ -7,7 +7,7 @@ module Datory
|
|
7
7
|
new.describe(...)
|
8
8
|
end
|
9
9
|
|
10
|
-
def describe(collection_of_attributes:) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
10
|
+
def describe(service_class_name:, collection_of_attributes:) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
11
11
|
headings = []
|
12
12
|
rows = []
|
13
13
|
|
@@ -15,6 +15,7 @@ module Datory
|
|
15
15
|
headings << "From"
|
16
16
|
headings << "To"
|
17
17
|
headings << "As"
|
18
|
+
headings << "Include" if collection_of_attributes.include_exist?
|
18
19
|
|
19
20
|
collection_of_attributes.each do |attribute|
|
20
21
|
row = []
|
@@ -22,15 +23,18 @@ module Datory
|
|
22
23
|
row << attribute.name
|
23
24
|
|
24
25
|
from_type = attribute.options.fetch(:from)
|
26
|
+
include = attribute.options.fetch(:include, from_type)
|
25
27
|
|
26
28
|
row << from_type
|
27
29
|
row << attribute.options.fetch(:to, attribute.name)
|
28
|
-
row << attribute.options.fetch(:as,
|
30
|
+
row << attribute.options.fetch(:as, include)
|
31
|
+
|
32
|
+
row << (include if include <= Datory::Base) if collection_of_attributes.include_exist?
|
29
33
|
|
30
34
|
rows << row
|
31
35
|
end
|
32
36
|
|
33
|
-
Datory::Console.print_table(headings.uniq, rows)
|
37
|
+
Datory::Console.print_table(title: service_class_name, headings: headings.uniq, rows: rows)
|
34
38
|
end
|
35
39
|
end
|
36
40
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datory
|
4
|
+
module Attributes
|
5
|
+
module Deserialization
|
6
|
+
class ServiceFactory
|
7
|
+
def self.create(...)
|
8
|
+
new(...).create
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(model_class, collection_of_attributes)
|
12
|
+
@model_class = model_class
|
13
|
+
@collection_of_attributes = collection_of_attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
def create
|
17
|
+
return if @model_class.const_defined?(ServiceBuilder::SERVICE_CLASS_NAME)
|
18
|
+
|
19
|
+
class_sample = create_service_class
|
20
|
+
|
21
|
+
@model_class.const_set(ServiceBuilder::SERVICE_CLASS_NAME, class_sample)
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_service_class
|
25
|
+
collection_of_attributes = @collection_of_attributes
|
26
|
+
|
27
|
+
Class.new(Datory::Service::Builder) do
|
28
|
+
collection_of_attributes.each do |attribute|
|
29
|
+
prepare_deserialization_data_for(attribute)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -24,11 +24,24 @@ module Datory
|
|
24
24
|
########################################################################
|
25
25
|
|
26
26
|
def one(name, include:, to: nil)
|
27
|
-
attribute(
|
27
|
+
attribute(
|
28
|
+
name,
|
29
|
+
to: to.presence || name,
|
30
|
+
from: Hash,
|
31
|
+
include: include,
|
32
|
+
as: [Datory::Result, Hash]
|
33
|
+
)
|
28
34
|
end
|
29
35
|
|
30
36
|
def many(name, include:, to: nil)
|
31
|
-
attribute(
|
37
|
+
attribute(
|
38
|
+
name,
|
39
|
+
to: to.presence || name,
|
40
|
+
from: Array,
|
41
|
+
consists_of: [Datory::Result, Hash],
|
42
|
+
include: include,
|
43
|
+
as: Array
|
44
|
+
)
|
32
45
|
end
|
33
46
|
|
34
47
|
########################################################################
|
@@ -38,6 +51,34 @@ module Datory
|
|
38
51
|
string(name, **options)
|
39
52
|
end
|
40
53
|
|
54
|
+
def money(name, **options)
|
55
|
+
options_for_cents = options.merge(from: Integer)
|
56
|
+
options_for_currency = options.merge(from: [Symbol, String])
|
57
|
+
|
58
|
+
attribute(:"#{name}_cents", **options_for_cents)
|
59
|
+
attribute(:"#{name}_currency", **options_for_currency)
|
60
|
+
end
|
61
|
+
|
62
|
+
def duration(name, **options)
|
63
|
+
options = options.merge(from: String, as: ActiveSupport::Duration) # TODO: Add `format: :duration`
|
64
|
+
string(name, **options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def date(name, **options)
|
68
|
+
options = options.merge(from: String, as: Date) # TODO: Add `format: :date`
|
69
|
+
string(name, **options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def time(name, **options)
|
73
|
+
options = options.merge(from: String, as: Time) # TODO: Add `format: :time`
|
74
|
+
string(name, **options)
|
75
|
+
end
|
76
|
+
|
77
|
+
def datetime(name, **options)
|
78
|
+
options = options.merge(from: String, as: DateTime) # TODO: Add `format: :datetime`
|
79
|
+
string(name, **options)
|
80
|
+
end
|
81
|
+
|
41
82
|
########################################################################
|
42
83
|
|
43
84
|
def string(name, **options)
|
@@ -8,22 +8,26 @@ module Datory
|
|
8
8
|
new.prepare(...)
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
item
|
18
|
-
end
|
19
|
-
end
|
20
|
-
elsif data.is_a?(Hash)
|
11
|
+
def self.to_hash(...)
|
12
|
+
new.to_hash(...)
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare(data)
|
16
|
+
if data.is_a?(Hash)
|
21
17
|
build(data)
|
22
18
|
else
|
23
19
|
data
|
24
20
|
end
|
25
21
|
end
|
26
22
|
|
23
|
+
def to_hash(data)
|
24
|
+
if data.is_a?(Datory::Attributes::Serialization::Model)
|
25
|
+
parse(data)
|
26
|
+
else
|
27
|
+
data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
27
31
|
def build(attributes = {}) # rubocop:disable Metrics/MethodLength
|
28
32
|
attributes.each do |key, value|
|
29
33
|
self.class.send(:attr_accessor, key)
|
@@ -42,6 +46,23 @@ module Datory
|
|
42
46
|
|
43
47
|
self
|
44
48
|
end
|
49
|
+
|
50
|
+
def parse(data) # rubocop:disable Metrics/MethodLength
|
51
|
+
data.instance_variables.to_h do |key|
|
52
|
+
value = data.instance_variable_get(key)
|
53
|
+
|
54
|
+
value =
|
55
|
+
if value.is_a?(Array)
|
56
|
+
value.map! { |item| Datory::Attributes::Serialization::Model.to_hash(item) }
|
57
|
+
elsif value.is_a?(Datory::Attributes::Serialization::Model)
|
58
|
+
Datory::Attributes::Serialization::Model.to_hash(value)
|
59
|
+
else
|
60
|
+
value
|
61
|
+
end
|
62
|
+
|
63
|
+
[key.to_s.delete_prefix("@").to_sym, value]
|
64
|
+
end
|
65
|
+
end
|
45
66
|
end
|
46
67
|
end
|
47
68
|
end
|
@@ -20,17 +20,17 @@ module Datory
|
|
20
20
|
end
|
21
21
|
else
|
22
22
|
@collection_of_attributes.to_h do |attribute|
|
23
|
-
|
23
|
+
attribute.options.fetch(:to, attribute.name)
|
24
24
|
include_class = attribute.options.fetch(:include, nil)
|
25
25
|
output_formatter = attribute.options.fetch(:output, nil)
|
26
26
|
|
27
|
-
value = model.public_send(
|
27
|
+
value = model.public_send(attribute.name)
|
28
28
|
|
29
29
|
value =
|
30
30
|
if include_class.present?
|
31
|
-
|
31
|
+
from_type = attribute.options.fetch(:from)
|
32
32
|
|
33
|
-
if [Set, Array].include?(
|
33
|
+
if [Set, Array].include?(from_type)
|
34
34
|
value.map { |item| include_class.serialize(item) }
|
35
35
|
else
|
36
36
|
include_class.serialize(value)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datory
|
4
|
+
module Attributes
|
5
|
+
module Serialization
|
6
|
+
class ServiceBuilder
|
7
|
+
SERVICE_CLASS_NAME = "SBuilder"
|
8
|
+
|
9
|
+
def self.build!(...)
|
10
|
+
new(...).build!
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(context, model, collection_of_attributes)
|
14
|
+
@context = context
|
15
|
+
@model = model
|
16
|
+
@collection_of_attributes = collection_of_attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
def build!
|
20
|
+
ServiceFactory.create(@context.class, @collection_of_attributes)
|
21
|
+
|
22
|
+
attributes = Datory::Attributes::Serialization::Model.to_hash(@model)
|
23
|
+
|
24
|
+
unnecessary_attributes = attributes.keys.difference(@collection_of_attributes.internal_names)
|
25
|
+
|
26
|
+
unnecessary_attributes.each do |key|
|
27
|
+
attributes.delete(key)
|
28
|
+
end
|
29
|
+
|
30
|
+
builder_class.call!(**attributes)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def builder_class
|
36
|
+
"#{@context.class.name}::#{SERVICE_CLASS_NAME}".constantize
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datory
|
4
|
+
module Attributes
|
5
|
+
module Serialization
|
6
|
+
class ServiceFactory
|
7
|
+
def self.create(...)
|
8
|
+
new(...).create
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(model_class, collection_of_attributes)
|
12
|
+
@model_class = model_class
|
13
|
+
@collection_of_attributes = collection_of_attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
def create
|
17
|
+
return if @model_class.const_defined?(ServiceBuilder::SERVICE_CLASS_NAME)
|
18
|
+
|
19
|
+
class_sample = create_service_class
|
20
|
+
|
21
|
+
@model_class.const_set(ServiceBuilder::SERVICE_CLASS_NAME, class_sample)
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_service_class
|
25
|
+
collection_of_attributes = @collection_of_attributes
|
26
|
+
|
27
|
+
Class.new(Datory::Service::Builder) do
|
28
|
+
collection_of_attributes.each do |attribute|
|
29
|
+
prepare_serialization_data_for(attribute)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -5,10 +5,29 @@ module Datory
|
|
5
5
|
module Workspace
|
6
6
|
private
|
7
7
|
|
8
|
-
def
|
8
|
+
def serialize(model:, collection_of_attributes:)
|
9
9
|
super
|
10
10
|
|
11
|
-
|
11
|
+
model = Serialization::ServiceBuilder.build!(self, model, collection_of_attributes)
|
12
|
+
|
13
|
+
Serialization::Serializator.serialize(
|
14
|
+
model: model,
|
15
|
+
collection_of_attributes: collection_of_attributes
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def deserialize(incoming_attributes:, collection_of_attributes:)
|
20
|
+
super
|
21
|
+
|
22
|
+
Deserialization::ServiceBuilder.build!(self, incoming_attributes, collection_of_attributes)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_model(attributes:)
|
26
|
+
super
|
27
|
+
|
28
|
+
attributes.each do |attribute_name, attribute_value|
|
29
|
+
define_singleton_method(attribute_name) { attribute_value }
|
30
|
+
end
|
12
31
|
end
|
13
32
|
end
|
14
33
|
end
|
data/lib/datory/base.rb
CHANGED
data/lib/datory/console.rb
CHANGED
@@ -4,8 +4,9 @@ require "terminal-table"
|
|
4
4
|
|
5
5
|
module Datory
|
6
6
|
class Console
|
7
|
-
def self.print_table(headings
|
7
|
+
def self.print_table(headings:, rows:, title: nil)
|
8
8
|
table = Terminal::Table.new(
|
9
|
+
title: title,
|
9
10
|
headings: headings,
|
10
11
|
rows: rows,
|
11
12
|
style: { border_x: "~", border_i: "~" }
|
@@ -3,63 +3,76 @@
|
|
3
3
|
module Datory
|
4
4
|
module Context
|
5
5
|
module Callable
|
6
|
-
def serialize(model)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
def serialize(model) # rubocop:disable Metrics/MethodLength
|
7
|
+
if [Set, Array].include?(model.class)
|
8
|
+
model.map do |model_item|
|
9
|
+
serialize(model_item)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
context = send(:new)
|
13
|
+
model = Datory::Attributes::Serialization::Model.prepare(model)
|
14
|
+
_serialize(context, model)
|
15
|
+
end
|
16
|
+
rescue Datory::Service::Exceptions::Input,
|
17
|
+
Datory::Service::Exceptions::Internal,
|
18
|
+
Datory::Service::Exceptions::Output => e
|
19
|
+
raise Datory::Exceptions::SerializationError.new(message: e.message)
|
13
20
|
end
|
14
21
|
|
15
|
-
def deserialize(json)
|
22
|
+
def deserialize(json) # rubocop:disable Metrics/MethodLength
|
16
23
|
if [Set, Array].include?(json.class)
|
17
24
|
json.map do |json_item|
|
18
25
|
deserialize(json_item)
|
19
26
|
end
|
20
27
|
else
|
28
|
+
context = send(:new)
|
21
29
|
hash = JSON.parse(json.to_json)
|
22
|
-
build(**hash)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_model(**attributes)
|
27
|
-
context = send(:new)
|
28
30
|
|
29
|
-
|
30
|
-
context.define_singleton_method(attribute_name) { attribute_value }
|
31
|
+
_deserialize(context, **hash)
|
31
32
|
end
|
32
|
-
|
33
|
-
|
33
|
+
rescue Datory::Service::Exceptions::Input,
|
34
|
+
Datory::Service::Exceptions::Internal,
|
35
|
+
Datory::Service::Exceptions::Output => e
|
36
|
+
raise Datory::Exceptions::DeserializationError.new(message: e.message)
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
# context = send(:new)
|
38
|
-
#
|
39
|
-
# _build!(context, **attributes)
|
40
|
-
# end
|
41
|
-
|
42
|
-
def build(attributes = {})
|
39
|
+
def to_model(**attributes)
|
43
40
|
context = send(:new)
|
44
41
|
|
45
|
-
|
42
|
+
_to_model(context, **attributes)
|
46
43
|
end
|
47
44
|
|
48
45
|
def describe
|
49
46
|
Datory::Attributes::Descriptor.describe(
|
47
|
+
service_class_name: name,
|
50
48
|
collection_of_attributes: collection_of_attributes
|
51
49
|
)
|
52
50
|
end
|
53
51
|
|
54
52
|
private
|
55
53
|
|
56
|
-
def
|
54
|
+
def _serialize(context, model)
|
55
|
+
context.send(
|
56
|
+
:_serialize,
|
57
|
+
model: model,
|
58
|
+
collection_of_attributes: collection_of_attributes
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def _deserialize(context, **attributes)
|
57
63
|
context.send(
|
58
|
-
:
|
64
|
+
:_deserialize,
|
59
65
|
incoming_attributes: attributes.symbolize_keys,
|
60
66
|
collection_of_attributes: collection_of_attributes
|
61
67
|
)
|
62
68
|
end
|
69
|
+
|
70
|
+
def _to_model(context, **attributes)
|
71
|
+
context.send(
|
72
|
+
:_to_model,
|
73
|
+
attributes: attributes
|
74
|
+
)
|
75
|
+
end
|
63
76
|
end
|
64
77
|
end
|
65
78
|
end
|
@@ -16,24 +16,39 @@ module Datory
|
|
16
16
|
attr_reader :incoming_attributes,
|
17
17
|
:collection_of_attributes
|
18
18
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
def _serialize(model:, collection_of_attributes:)
|
20
|
+
serialize(
|
21
|
+
model: model,
|
22
|
+
collection_of_attributes: collection_of_attributes
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def _deserialize(incoming_attributes:, collection_of_attributes:)
|
27
|
+
deserialize(
|
24
28
|
incoming_attributes: incoming_attributes,
|
25
29
|
collection_of_attributes: collection_of_attributes
|
26
30
|
)
|
27
31
|
end
|
28
32
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
def _to_model(attributes:)
|
34
|
+
to_model(
|
35
|
+
attributes: attributes
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def serialize(model:, collection_of_attributes:, **)
|
40
|
+
@model = model
|
41
|
+
@collection_of_attributes = collection_of_attributes
|
42
|
+
end
|
43
|
+
|
44
|
+
def deserialize(incoming_attributes:, collection_of_attributes:, **)
|
34
45
|
@incoming_attributes = incoming_attributes
|
35
46
|
@collection_of_attributes = collection_of_attributes
|
36
47
|
end
|
48
|
+
|
49
|
+
def to_model(attributes:)
|
50
|
+
@attributes = attributes
|
51
|
+
end
|
37
52
|
end
|
38
53
|
end
|
39
54
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datory
|
4
|
+
module Info
|
5
|
+
module DSL
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def info # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
12
|
+
Datory::Info::Result.new(
|
13
|
+
attributes: collection_of_attributes.to_h do |attribute|
|
14
|
+
type_from = attribute.options.fetch(:from)
|
15
|
+
|
16
|
+
options = {
|
17
|
+
from: type_from,
|
18
|
+
to: attribute.options.fetch(:to, attribute.name),
|
19
|
+
as: attribute.options.fetch(:as, type_from),
|
20
|
+
min: attribute.options.fetch(:min, nil),
|
21
|
+
max: attribute.options.fetch(:max, nil),
|
22
|
+
include: attribute.options.fetch(:include, nil)
|
23
|
+
}
|
24
|
+
|
25
|
+
[attribute.name, options]
|
26
|
+
end
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/datory/service/base.rb
CHANGED
@@ -15,11 +15,15 @@ module Datory
|
|
15
15
|
result_class Datory::Result
|
16
16
|
|
17
17
|
input_option_helpers [
|
18
|
-
Servactory::ToolKit::DynamicOptions::Format.use
|
18
|
+
Servactory::ToolKit::DynamicOptions::Format.use,
|
19
|
+
Servactory::ToolKit::DynamicOptions::Min.use,
|
20
|
+
Servactory::ToolKit::DynamicOptions::Max.use
|
19
21
|
]
|
20
22
|
|
21
23
|
output_option_helpers [
|
22
|
-
Servactory::ToolKit::DynamicOptions::Format.use
|
24
|
+
Servactory::ToolKit::DynamicOptions::Format.use,
|
25
|
+
Servactory::ToolKit::DynamicOptions::Min.use,
|
26
|
+
Servactory::ToolKit::DynamicOptions::Max.use
|
23
27
|
]
|
24
28
|
|
25
29
|
predicate_methods_enabled false
|
@@ -3,6 +3,72 @@
|
|
3
3
|
module Datory
|
4
4
|
module Service
|
5
5
|
class Builder < Base
|
6
|
+
TRANSFORMATIONS = {
|
7
|
+
SERIALIZATION: {
|
8
|
+
Symbol => ->(value) { value.to_sym },
|
9
|
+
String => ->(value) { value.to_s },
|
10
|
+
Integer => ->(value) { value.to_i },
|
11
|
+
Float => ->(value) { value.to_f },
|
12
|
+
Date => ->(value) { value.to_s },
|
13
|
+
Time => ->(value) { value.to_s },
|
14
|
+
DateTime => ->(value) { value.to_s },
|
15
|
+
ActiveSupport::Duration => ->(value) { value.iso8601 }
|
16
|
+
},
|
17
|
+
DESERIALIZATION: {
|
18
|
+
Symbol => ->(value) { value.to_sym },
|
19
|
+
String => ->(value) { value.to_s },
|
20
|
+
Integer => ->(value) { value.to_i },
|
21
|
+
Float => ->(value) { value.to_f },
|
22
|
+
Date => ->(value) { Date.parse(value) },
|
23
|
+
Time => ->(value) { Time.parse(value) },
|
24
|
+
DateTime => ->(value) { DateTime.parse(value) },
|
25
|
+
ActiveSupport::Duration => ->(value) { ActiveSupport::Duration.parse(value) }
|
26
|
+
}
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
private_constant :TRANSFORMATIONS
|
30
|
+
|
31
|
+
def self.prepare_serialization_data_for(attribute) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
32
|
+
serialized_name = attribute.name
|
33
|
+
deserialized_name = attribute.options.fetch(:to, serialized_name)
|
34
|
+
method_name = :"assign_#{serialized_name}_output"
|
35
|
+
|
36
|
+
input deserialized_name, **attribute.input_serialization_options
|
37
|
+
|
38
|
+
output serialized_name, **attribute.output_serialization_options
|
39
|
+
|
40
|
+
make method_name
|
41
|
+
|
42
|
+
define_method(method_name) do
|
43
|
+
value = inputs.public_send(deserialized_name)
|
44
|
+
|
45
|
+
value = TRANSFORMATIONS.fetch(:SERIALIZATION).fetch(value.class, ->(v) { v }).call(value)
|
46
|
+
|
47
|
+
outputs.public_send(:"#{serialized_name}=", value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.prepare_deserialization_data_for(attribute) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
52
|
+
serialized_name = attribute.name
|
53
|
+
deserialized_name = attribute.options.fetch(:to, serialized_name)
|
54
|
+
method_name = :"assign_#{deserialized_name}_output"
|
55
|
+
|
56
|
+
input serialized_name, **attribute.input_deserialization_options
|
57
|
+
|
58
|
+
output deserialized_name, **attribute.output_deserialization_options
|
59
|
+
|
60
|
+
make method_name
|
61
|
+
|
62
|
+
define_method(method_name) do
|
63
|
+
value = inputs.public_send(deserialized_name)
|
64
|
+
|
65
|
+
type_as = attribute.options.fetch(:as, nil)
|
66
|
+
|
67
|
+
value = TRANSFORMATIONS.fetch(:DESERIALIZATION).fetch(type_as, ->(v) { v }).call(value)
|
68
|
+
|
69
|
+
outputs.public_send(:"#{deserialized_name}=", value)
|
70
|
+
end
|
71
|
+
end
|
6
72
|
end
|
7
73
|
end
|
8
74
|
end
|
data/lib/datory/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anton Sokolov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -50,14 +50,14 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - '='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 2.5.0.
|
53
|
+
version: 2.5.0.rc7
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - '='
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 2.5.0.
|
60
|
+
version: 2.5.0.rc7
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: terminal-table
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -213,11 +213,13 @@ files:
|
|
213
213
|
- lib/datory/attributes/attribute.rb
|
214
214
|
- lib/datory/attributes/collection.rb
|
215
215
|
- lib/datory/attributes/descriptor.rb
|
216
|
+
- lib/datory/attributes/deserialization/service_builder.rb
|
217
|
+
- lib/datory/attributes/deserialization/service_factory.rb
|
216
218
|
- lib/datory/attributes/dsl.rb
|
217
219
|
- lib/datory/attributes/serialization/model.rb
|
218
220
|
- lib/datory/attributes/serialization/serializator.rb
|
219
|
-
- lib/datory/attributes/
|
220
|
-
- lib/datory/attributes/
|
221
|
+
- lib/datory/attributes/serialization/service_builder.rb
|
222
|
+
- lib/datory/attributes/serialization/service_factory.rb
|
221
223
|
- lib/datory/attributes/workspace.rb
|
222
224
|
- lib/datory/base.rb
|
223
225
|
- lib/datory/console.rb
|
@@ -225,11 +227,13 @@ files:
|
|
225
227
|
- lib/datory/context/dsl.rb
|
226
228
|
- lib/datory/context/workspace.rb
|
227
229
|
- lib/datory/engine.rb
|
230
|
+
- lib/datory/exceptions.rb
|
231
|
+
- lib/datory/info/dsl.rb
|
232
|
+
- lib/datory/info/result.rb
|
228
233
|
- lib/datory/result.rb
|
229
234
|
- lib/datory/service/base.rb
|
230
235
|
- lib/datory/service/builder.rb
|
231
236
|
- lib/datory/service/exceptions.rb
|
232
|
-
- lib/datory/utils.rb
|
233
237
|
- lib/datory/version.rb
|
234
238
|
homepage: https://github.com/servactory/datory
|
235
239
|
licenses:
|
@@ -256,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
256
260
|
- !ruby/object:Gem::Version
|
257
261
|
version: '0'
|
258
262
|
requirements: []
|
259
|
-
rubygems_version: 3.5.
|
263
|
+
rubygems_version: 3.5.9
|
260
264
|
signing_key:
|
261
265
|
specification_version: 4
|
262
266
|
summary: A set of tools for building reliable services of any complexity
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Datory
|
4
|
-
module Attributes
|
5
|
-
module Tools
|
6
|
-
class ServiceFactory
|
7
|
-
def self.create(...)
|
8
|
-
new(...).create
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(model_class, collection_of_attributes)
|
12
|
-
@model_class = model_class
|
13
|
-
@collection_of_attributes = collection_of_attributes
|
14
|
-
end
|
15
|
-
|
16
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
17
|
-
def create
|
18
|
-
return if @model_class.const_defined?(ServiceBuilder::SERVICE_CLASS_NAME)
|
19
|
-
|
20
|
-
collection_of_attributes = @collection_of_attributes
|
21
|
-
|
22
|
-
class_sample = Class.new(Datory::Service::Builder) do
|
23
|
-
collection_of_attributes.each do |attribute|
|
24
|
-
input_internal_name = attribute.options.fetch(:to, attribute.name)
|
25
|
-
|
26
|
-
input attribute.name, **attribute.input_options
|
27
|
-
|
28
|
-
output input_internal_name, **attribute.output_options
|
29
|
-
|
30
|
-
make :"assign_#{input_internal_name}_output"
|
31
|
-
|
32
|
-
define_method(:"assign_#{input_internal_name}_output") do
|
33
|
-
value = inputs.public_send(input_internal_name)
|
34
|
-
|
35
|
-
option_as = attribute.options.fetch(:as, nil)
|
36
|
-
|
37
|
-
value = Datory::Utils.transform_value_with(value, option_as)
|
38
|
-
|
39
|
-
outputs.public_send(:"#{input_internal_name}=", value)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# output :model, type: Class
|
44
|
-
|
45
|
-
# make :assign_model_name
|
46
|
-
|
47
|
-
# def assign_model_name
|
48
|
-
# model_class_name_array = self.class.name.split("::")
|
49
|
-
# model_class_name_array.pop
|
50
|
-
# model_class_name = model_class_name_array.join("::")
|
51
|
-
# model_class = model_class_name.constantize
|
52
|
-
#
|
53
|
-
# outputs.model = model_class
|
54
|
-
# end
|
55
|
-
end
|
56
|
-
|
57
|
-
@model_class.const_set(ServiceBuilder::SERVICE_CLASS_NAME, class_sample)
|
58
|
-
end
|
59
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
data/lib/datory/utils.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Datory
|
4
|
-
module Utils
|
5
|
-
module_function
|
6
|
-
|
7
|
-
TRANSFORMATIONS = {
|
8
|
-
Symbol => ->(value) { value.to_sym },
|
9
|
-
String => ->(value) { value.to_s },
|
10
|
-
Integer => ->(value) { value.to_i },
|
11
|
-
Float => ->(value) { value.to_f },
|
12
|
-
Date => ->(value) { Date.parse(value) },
|
13
|
-
Time => ->(value) { Time.parse(value) },
|
14
|
-
DateTime => ->(value) { DateTime.parse(value) },
|
15
|
-
ActiveSupport::Duration => ->(value) { ActiveSupport::Duration.parse(value) }
|
16
|
-
}.freeze
|
17
|
-
|
18
|
-
private_constant :TRANSFORMATIONS
|
19
|
-
|
20
|
-
def transform_value_with(value, type)
|
21
|
-
TRANSFORMATIONS.fetch(type, ->(v) { v }).call(value)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|