datory 1.0.0.rc1 → 1.0.0.rc3
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 +49 -1
- data/lib/datory/attributes/collection.rb +1 -13
- data/lib/datory/attributes/dsl.rb +5 -0
- data/lib/datory/attributes/serialization/serializator.rb +52 -0
- data/lib/datory/attributes/tools/service_builder.rb +33 -0
- data/lib/datory/attributes/tools/service_factory.rb +85 -0
- data/lib/datory/attributes/workspace.rb +1 -65
- data/lib/datory/context/callable.rb +12 -32
- data/lib/datory/version.rb +1 -1
- data/lib/datory.rb +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8562a68170d39d6d97a80eceae372d7f043cbb08db2890faa2d6d22a792748bf
|
4
|
+
data.tar.gz: 89ef0b5c9a73289a3567e01202a9a83beeaefee6db2f084168c6e755da4e3044
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c6ed842a16e265979fc1d5ebc4b517390884bac7118500d39cc48d6b180c7c842b08cadfdf853db111b2a56a8c016cbcbf9654d4b290272399f461d9eec48ba
|
7
|
+
data.tar.gz: 6428856321dd65efb127aca09955e5e34eb1d3cff3cdc2f8da0fd669e4556582ecef976919b60f6808ce84a602a9dc60f332ffcc42b0421c613b0dc43d1ff429
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<a href="https://github.com/servactory/datory/releases"><img src="https://img.shields.io/github/release-date/servactory/datory" alt="Release Date"></a>
|
4
4
|
</p>
|
5
5
|
|
6
|
-
## Documentation
|
6
|
+
## Documentation (soon)
|
7
7
|
|
8
8
|
See [datory.servactory.com](https://datory.servactory.com) for documentation.
|
9
9
|
|
@@ -15,6 +15,54 @@ See [datory.servactory.com](https://datory.servactory.com) for documentation.
|
|
15
15
|
gem "datory"
|
16
16
|
```
|
17
17
|
|
18
|
+
### Usage
|
19
|
+
|
20
|
+
#### Serialize
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
user = User.find(...)
|
24
|
+
|
25
|
+
UserDto.serialize(user)
|
26
|
+
```
|
27
|
+
|
28
|
+
#### Deserialize
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
UserDto.deserialize(json)
|
32
|
+
```
|
33
|
+
|
34
|
+
#### Examples
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class UserDto < Datory::Base
|
38
|
+
string :id
|
39
|
+
string :firstname, as: :first_name
|
40
|
+
string :lastname, as: :last_name
|
41
|
+
string :email
|
42
|
+
string :birthDate, as: :birth_date, output: ->(value:) { value.to_s }
|
43
|
+
|
44
|
+
one :login, include: UserLoginDto
|
45
|
+
|
46
|
+
many :addresses, include: UserAddressDto
|
47
|
+
|
48
|
+
string :phone
|
49
|
+
string :website
|
50
|
+
|
51
|
+
one :company, include: UserCompanyDto
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
class UserLoginDto < Datory::Base
|
57
|
+
string :uuid
|
58
|
+
string :username
|
59
|
+
string :password
|
60
|
+
string :md5
|
61
|
+
string :sha1
|
62
|
+
string :registered
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
18
66
|
## Contributing
|
19
67
|
|
20
68
|
This project is intended to be a safe, welcoming space for collaboration.
|
@@ -4,27 +4,15 @@ module Datory
|
|
4
4
|
module Attributes
|
5
5
|
class Collection
|
6
6
|
extend Forwardable
|
7
|
-
def_delegators :@collection, :<<, :each, :map, :
|
7
|
+
def_delegators :@collection, :<<, :each, :map, :to_h, :merge
|
8
8
|
|
9
9
|
def initialize(collection = Set.new)
|
10
10
|
@collection = collection
|
11
11
|
end
|
12
12
|
|
13
|
-
# def only(*names)
|
14
|
-
# Collection.new(filter { |input| names.include?(input.internal_name) })
|
15
|
-
# end
|
16
|
-
|
17
|
-
# def except(*names)
|
18
|
-
# Collection.new(filter { |input| !names.include?(input.internal_name) })
|
19
|
-
# end
|
20
|
-
|
21
13
|
def names
|
22
14
|
map(&:name)
|
23
15
|
end
|
24
|
-
|
25
|
-
# def find_by(name:)
|
26
|
-
# find { |input| input.internal_name == name }
|
27
|
-
# end
|
28
16
|
end
|
29
17
|
end
|
30
18
|
end
|
@@ -29,6 +29,11 @@ module Datory
|
|
29
29
|
collection_of_attributes << Attribute.new(name, **options)
|
30
30
|
end
|
31
31
|
|
32
|
+
def symbol(name, **options)
|
33
|
+
options = options.merge(type: Symbol)
|
34
|
+
attribute(name, **options)
|
35
|
+
end
|
36
|
+
|
32
37
|
def string(name, **options)
|
33
38
|
options = options.merge(type: String)
|
34
39
|
attribute(name, **options)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Datory
|
2
|
+
module Attributes
|
3
|
+
module Serialization
|
4
|
+
class Serializator
|
5
|
+
def self.serialize(model:, collection_of_attributes:)
|
6
|
+
new(collection_of_attributes: collection_of_attributes).serialize(model: model)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(collection_of_attributes:)
|
10
|
+
@collection_of_attributes = collection_of_attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
14
|
+
def serialize(model:)
|
15
|
+
if [Set, Array].include?(model.class)
|
16
|
+
model.map do |nested_model|
|
17
|
+
serialize(model: nested_model)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@collection_of_attributes.to_h do |attribute|
|
21
|
+
internal_name = attribute.options.fetch(:as, attribute.name)
|
22
|
+
include_class = attribute.options.fetch(:include, nil)
|
23
|
+
output_formatter = attribute.options.fetch(:output, nil)
|
24
|
+
|
25
|
+
value = model.public_send(internal_name)
|
26
|
+
|
27
|
+
value =
|
28
|
+
if include_class.present?
|
29
|
+
type = attribute.options.fetch(:type, nil)
|
30
|
+
|
31
|
+
if [Set, Array].include?(type)
|
32
|
+
value.map { |item| include_class.serialize(item) }
|
33
|
+
else
|
34
|
+
include_class.serialize(value)
|
35
|
+
end
|
36
|
+
elsif output_formatter.is_a?(Proc)
|
37
|
+
output_formatter.call(value: value)
|
38
|
+
elsif [Date, Time, DateTime].include?(value.class)
|
39
|
+
value.to_s
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
|
44
|
+
[attribute.name, value]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datory
|
4
|
+
module Attributes
|
5
|
+
module Tools
|
6
|
+
class ServiceBuilder
|
7
|
+
SERVICE_CLASS_NAME = "Builder"
|
8
|
+
|
9
|
+
def self.build!(...)
|
10
|
+
new(...).build!
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(context, incoming_attributes, collection_of_attributes)
|
14
|
+
@context = context
|
15
|
+
@incoming_attributes = incoming_attributes
|
16
|
+
@collection_of_attributes = collection_of_attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
def build!
|
20
|
+
ServiceFactory.create(@context.class, @collection_of_attributes)
|
21
|
+
|
22
|
+
builder_class.call!(**@incoming_attributes)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def builder_class
|
28
|
+
"#{@context.class.name}::#{SERVICE_CLASS_NAME}".constantize
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
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| # rubocop:disable Metrics/BlockLength
|
24
|
+
input_internal_name = attribute.options.fetch(:as, attribute.name)
|
25
|
+
|
26
|
+
input attribute.name,
|
27
|
+
as: input_internal_name,
|
28
|
+
type: attribute.options.fetch(:type),
|
29
|
+
required: attribute.options.fetch(:required, true),
|
30
|
+
consists_of: attribute.options.fetch(:consists_of, false),
|
31
|
+
prepare: (lambda do |value:|
|
32
|
+
include_class = attribute.options.fetch(:include, nil)
|
33
|
+
return value unless include_class.present?
|
34
|
+
|
35
|
+
type = attribute.options.fetch(:type, nil)
|
36
|
+
|
37
|
+
if [Set, Array].include?(type)
|
38
|
+
value.map { |item| include_class.build(**item) }
|
39
|
+
else
|
40
|
+
include_class.build(**value)
|
41
|
+
end
|
42
|
+
end)
|
43
|
+
|
44
|
+
output input_internal_name,
|
45
|
+
consists_of: (
|
46
|
+
if (type = attribute.options.fetch(:consists_of, false)) == Hash
|
47
|
+
Datory::Result
|
48
|
+
else
|
49
|
+
type
|
50
|
+
end
|
51
|
+
),
|
52
|
+
type: if (type = attribute.options.fetch(:type)) == Hash
|
53
|
+
Datory::Result
|
54
|
+
else
|
55
|
+
type
|
56
|
+
end
|
57
|
+
|
58
|
+
make :"assign_#{input_internal_name}_output"
|
59
|
+
|
60
|
+
define_method(:"assign_#{input_internal_name}_output") do
|
61
|
+
outputs.public_send(:"#{input_internal_name}=", inputs.public_send(input_internal_name))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# output :model, type: Class
|
66
|
+
|
67
|
+
# make :assign_model_name
|
68
|
+
|
69
|
+
# def assign_model_name
|
70
|
+
# model_class_name_array = self.class.name.split("::")
|
71
|
+
# model_class_name_array.pop
|
72
|
+
# model_class_name = model_class_name_array.join("::")
|
73
|
+
# model_class = model_class_name.constantize
|
74
|
+
#
|
75
|
+
# outputs.model = model_class
|
76
|
+
# end
|
77
|
+
end
|
78
|
+
|
79
|
+
@model_class.const_set(ServiceBuilder::SERVICE_CLASS_NAME, class_sample)
|
80
|
+
end
|
81
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -3,76 +3,12 @@
|
|
3
3
|
module Datory
|
4
4
|
module Attributes
|
5
5
|
module Workspace
|
6
|
-
class ServiceFactory
|
7
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
8
|
-
def self.create(model_class, class_name, collection_of_attributes)
|
9
|
-
return if model_class.const_defined?(class_name)
|
10
|
-
|
11
|
-
class_sample = Class.new(Datory::Service::Builder) do
|
12
|
-
collection_of_attributes.each do |attribute| # rubocop:disable Metrics/BlockLength
|
13
|
-
input_internal_name = attribute.options.fetch(:as, attribute.name)
|
14
|
-
|
15
|
-
input attribute.name,
|
16
|
-
as: input_internal_name,
|
17
|
-
type: attribute.options.fetch(:type),
|
18
|
-
required: attribute.options.fetch(:required, true),
|
19
|
-
consists_of: attribute.options.fetch(:consists_of, false),
|
20
|
-
prepare: (lambda do |value:|
|
21
|
-
include_class = attribute.options.fetch(:include, nil)
|
22
|
-
return value unless include_class.present?
|
23
|
-
|
24
|
-
type = attribute.options.fetch(:type, nil)
|
25
|
-
|
26
|
-
if [Set, Array].include?(type)
|
27
|
-
value.map { |item| include_class.build(**item) }
|
28
|
-
else
|
29
|
-
include_class.build(**value)
|
30
|
-
end
|
31
|
-
end)
|
32
|
-
|
33
|
-
output input_internal_name,
|
34
|
-
consists_of: (
|
35
|
-
if (type = attribute.options.fetch(:consists_of, false)) == Hash
|
36
|
-
Datory::Result
|
37
|
-
else
|
38
|
-
type
|
39
|
-
end
|
40
|
-
),
|
41
|
-
type: if (type = attribute.options.fetch(:type)) == Hash
|
42
|
-
Datory::Result
|
43
|
-
else
|
44
|
-
type
|
45
|
-
end
|
46
|
-
|
47
|
-
make :"assign_#{input_internal_name}_output"
|
48
|
-
|
49
|
-
define_method(:"assign_#{input_internal_name}_output") do
|
50
|
-
outputs.public_send(:"#{input_internal_name}=", inputs.public_send(input_internal_name))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
model_class.const_set(class_name, class_sample)
|
56
|
-
end
|
57
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
58
|
-
end
|
59
|
-
|
60
6
|
private
|
61
7
|
|
62
8
|
def build!(incoming_attributes:, collection_of_attributes:, **)
|
63
9
|
super
|
64
10
|
|
65
|
-
|
66
|
-
|
67
|
-
ServiceFactory.create(self.class, builder_class_name, collection_of_attributes)
|
68
|
-
|
69
|
-
builder_class = "#{self.class.name}::#{builder_class_name}".constantize
|
70
|
-
|
71
|
-
builder_class.call!(**incoming_attributes)
|
72
|
-
|
73
|
-
# Tools::Unnecessary.find!(self, attributes, collection_of_attributes)
|
74
|
-
# Tools::Rules.check!(self, collection_of_attributes)
|
75
|
-
# Tools::Validation.validate!(self, attributes, collection_of_attributes)
|
11
|
+
Tools::ServiceBuilder.build!(self, incoming_attributes, collection_of_attributes)
|
76
12
|
end
|
77
13
|
end
|
78
14
|
end
|
@@ -4,41 +4,21 @@ module Datory
|
|
4
4
|
module Context
|
5
5
|
module Callable
|
6
6
|
def serialize(model)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
include_class = attribute.options.fetch(:include, nil)
|
12
|
-
output_formatter = attribute.options.fetch(:output, nil)
|
13
|
-
|
14
|
-
value = model.public_send(internal_name)
|
15
|
-
|
16
|
-
value =
|
17
|
-
if include_class.present?
|
18
|
-
type = attribute.options.fetch(:type, nil)
|
19
|
-
|
20
|
-
if [Set, Array].include?(type)
|
21
|
-
value.map { |item| include_class.serialize(item) }
|
22
|
-
else
|
23
|
-
include_class.serialize(value)
|
24
|
-
end
|
25
|
-
elsif output_formatter.is_a?(Proc)
|
26
|
-
output_formatter.call(value: value)
|
27
|
-
elsif [Date, Time, DateTime].include?(value.class)
|
28
|
-
value.to_s
|
29
|
-
else
|
30
|
-
value
|
31
|
-
end
|
32
|
-
|
33
|
-
hash[attribute.name] = value
|
34
|
-
end
|
35
|
-
|
36
|
-
hash
|
7
|
+
Datory::Attributes::Serialization::Serializator.serialize(
|
8
|
+
model: model,
|
9
|
+
collection_of_attributes: collection_of_attributes
|
10
|
+
)
|
37
11
|
end
|
38
12
|
|
39
13
|
def deserialize(json)
|
40
|
-
|
41
|
-
|
14
|
+
if [Set, Array].include?(json.class)
|
15
|
+
json.map do |json_item|
|
16
|
+
deserialize(json_item)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
hash = JSON.parse(json.to_json)
|
20
|
+
build(**hash)
|
21
|
+
end
|
42
22
|
end
|
43
23
|
|
44
24
|
# def build!(attributes = {})
|
data/lib/datory/version.rb
CHANGED
data/lib/datory.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.rc3
|
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-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -48,16 +48,16 @@ dependencies:
|
|
48
48
|
name: servactory
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - '='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 2.5.0.rc2
|
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:
|
60
|
+
version: 2.5.0.rc2
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: zeitwerk
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -199,6 +199,9 @@ files:
|
|
199
199
|
- lib/datory/attributes/attribute.rb
|
200
200
|
- lib/datory/attributes/collection.rb
|
201
201
|
- lib/datory/attributes/dsl.rb
|
202
|
+
- lib/datory/attributes/serialization/serializator.rb
|
203
|
+
- lib/datory/attributes/tools/service_builder.rb
|
204
|
+
- lib/datory/attributes/tools/service_factory.rb
|
202
205
|
- lib/datory/attributes/workspace.rb
|
203
206
|
- lib/datory/base.rb
|
204
207
|
- lib/datory/context/callable.rb
|