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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e6931d46c3dd6929ceb0e0954c116eda8a1af5e8e04126adbec8a12a9505afd
4
- data.tar.gz: cc93ae958f1d03eac21840ef6219bead651fbf78d00bb8a09f9f3adbf431fe69
3
+ metadata.gz: 198bd72a333d4fa2fe2ee18935d586ab10f6185c7b51c6cb94f2627f5698fb52
4
+ data.tar.gz: 6aa7c4bf85587d02a38e79de2135c83bed01be5153a644c0892433887dd1b982
5
5
  SHA512:
6
- metadata.gz: 4f93c7d0bfb210d5b7f71fe0334f9e5d92aaa4d2d915ea7b1c9e116734ac46e863cb59405a95c8ba30c355dd712b830d03db6901bd9a740cc179bf4722c25cc3
7
- data.tar.gz: d94430f29e119f6e64601c4f3c9258a4b9e8f4e9168edf416e7f6c12628d510158a639ecd1e101e38e79eb7c7439ad84422c2a0f5ae22b977b8eee6e4109c1bc
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](./website/docs/CONTRIBUTING.md) as well.
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 input_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
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.build(**item) }
49
+ value.map { |item| include_class.deserialize(**item) }
27
50
  else
28
- include_class.build(**value)
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 output_options # rubocop:disable Metrics/MethodLength
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, attribute.options.fetch(:include, from_type))
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
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Datory
4
4
  module Attributes
5
- module Tools
5
+ module Deserialization
6
6
  class ServiceBuilder
7
- SERVICE_CLASS_NAME = "Builder"
7
+ SERVICE_CLASS_NAME = "DBuilder"
8
8
 
9
9
  def self.build!(...)
10
10
  new(...).build!
@@ -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(name, to: to.presence || name, from: Hash, include: include)
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(name, to: to.presence || name, from: Array, consists_of: Hash, include: include)
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 prepare(data) # rubocop:disable Metrics/MethodLength
12
- if data.is_a?(Array)
13
- data.map do |item|
14
- if item.is_a?(Hash)
15
- build(item)
16
- else
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
- internal_name = attribute.options.fetch(:to, attribute.name)
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(internal_name)
27
+ value = model.public_send(attribute.name)
28
28
 
29
29
  value =
30
30
  if include_class.present?
31
- type = attribute.options.fetch(:type, nil)
31
+ from_type = attribute.options.fetch(:from)
32
32
 
33
- if [Set, Array].include?(type)
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 build!(incoming_attributes:, collection_of_attributes:, **)
8
+ def serialize(model:, collection_of_attributes:)
9
9
  super
10
10
 
11
- Tools::ServiceBuilder.build!(self, incoming_attributes, collection_of_attributes)
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
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Datory
4
4
  class Base
5
+ include Info::DSL
5
6
  include Context::DSL
6
7
  include Attributes::DSL
7
8
  end
@@ -4,8 +4,9 @@ require "terminal-table"
4
4
 
5
5
  module Datory
6
6
  class Console
7
- def self.print_table(headings, rows)
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
- model = Datory::Attributes::Serialization::Model.prepare(model)
8
-
9
- Datory::Attributes::Serialization::Serializator.serialize(
10
- model: model,
11
- collection_of_attributes: collection_of_attributes
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
- attributes.each do |attribute_name, attribute_value|
30
- context.define_singleton_method(attribute_name) { attribute_value }
31
+ _deserialize(context, **hash)
31
32
  end
32
-
33
- context
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
- # def build!(attributes = {})
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
- _build!(context, **attributes)
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 _build!(context, **attributes)
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
- :_build!,
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 _build!(
20
- incoming_attributes:,
21
- collection_of_attributes:
22
- )
23
- build!(
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 build!(
30
- incoming_attributes:,
31
- collection_of_attributes:,
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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Exceptions
5
+ class SerializationError < Datory::Service::Exceptions::Input; end
6
+ class DeserializationError < Datory::Service::Exceptions::Input; end
7
+ end
8
+ 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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Info
5
+ class Result
6
+ attr_reader :attributes
7
+
8
+ def initialize(attributes:)
9
+ @attributes = attributes
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
@@ -5,7 +5,7 @@ module Datory
5
5
  MAJOR = 1
6
6
  MINOR = 0
7
7
  PATCH = 0
8
- PRE = "rc10"
8
+ PRE = "rc12"
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
11
  end
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.rc10
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-25 00:00:00.000000000 Z
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.rc6
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.rc6
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/tools/service_builder.rb
220
- - lib/datory/attributes/tools/service_factory.rb
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.6
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