datory 1.0.0.rc1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9a373fdef28cc23b1fede6c3c3405e92ca888b832792122bde0c9a2758aab365
4
+ data.tar.gz: 962d0fc90e6a130d249800d982176ece5b7e85226979e4e6a94c2a306b1b7a4d
5
+ SHA512:
6
+ metadata.gz: 947b785c440d6fef3672dc82cb25eb8c59e256f881974f32dc50673fe626274d0723b0b8e09c1c24ba4c6714b77eabfbf0a83a3399794dd41b8ef2039426ec3c
7
+ data.tar.gz: 9984cdcd0c95f0b3e5de163fe84b46e2275ce7e61c6764fcfcb38e0696832d9922f8313108ec2ae6505a18543ca36ac8c8a87d67cfa4d2f205cd41953aabc59b
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ <p align="center">
2
+ <a href="https://rubygems.org/gems/datory"><img src="https://img.shields.io/gem/v/datory?logo=rubygems&logoColor=fff" alt="Gem version"></a>
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
+ </p>
5
+
6
+ ## Documentation
7
+
8
+ See [datory.servactory.com](https://datory.servactory.com) for documentation.
9
+
10
+ ## Quick Start
11
+
12
+ ### Installation
13
+
14
+ ```ruby
15
+ gem "datory"
16
+ ```
17
+
18
+ ## Contributing
19
+
20
+ This project is intended to be a safe, welcoming space for collaboration.
21
+ Contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
22
+ We recommend reading the [contributing guide](./website/docs/CONTRIBUTING.md) as well.
23
+
24
+ ## License
25
+
26
+ Datory is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,99 @@
1
+ en:
2
+ servactory:
3
+ common:
4
+ undefined_method:
5
+ missing_name: "[%{service_class_name}] %{error_text}"
6
+ methods:
7
+ call:
8
+ not_used: "[%{service_class_name}] Nothing to perform. Use `make` or create a `call` method."
9
+ cannot_be_overwritten: "[%{service_class_name}] The following methods cannot be overwritten: %{list_of_methods}"
10
+ inputs:
11
+ undefined:
12
+ getter: "[%{service_class_name}] Undefined input attribute `%{input_name}`"
13
+ setter: "[%{service_class_name}] Undefined input attribute `%{input_name}`"
14
+ validations:
15
+ inclusion:
16
+ default_error: "[%{service_class_name}] Wrong value in `%{input_name}`, must be one of `%{input_inclusion}`"
17
+ must:
18
+ default_error: "[%{service_class_name}] Input `%{input_name}` must \"%{code}\""
19
+ syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{input_name}` input: %{exception_message}"
20
+ dynamic_options:
21
+ format:
22
+ default: "[%{service_class_name}] Input `%{input_name}` does not match `%{format_name}` format"
23
+ wrong_pattern: "[%{service_class_name}] Input `%{input_name}` does not match `%{format_name}` format"
24
+ unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for input `%{input_name}`"
25
+ min:
26
+ default: "[%{service_class_name}] Input `%{input_name}` received value `%{value}`, which is less than `%{option_value}`"
27
+ max:
28
+ default: "[%{service_class_name}] Input `%{input_name}` received value `%{value}`, which is greater than `%{option_value}`"
29
+ required:
30
+ default_error:
31
+ default: "[%{service_class_name}] Required input `%{input_name}` is missing"
32
+ for_collection: "[%{service_class_name}] Required element in input collection `%{input_name}` is missing"
33
+ type:
34
+ default_error:
35
+ default: "[%{service_class_name}] Wrong type of input `%{input_name}`, expected `%{expected_type}`, got `%{given_type}`"
36
+ for_collection:
37
+ wrong_type: "[%{service_class_name}] Wrong input collection type `%{input_name}`, expected `%{expected_type}`, got `%{given_type}`"
38
+ wrong_element_type: "[%{service_class_name}] Wrong type in input collection `%{input_name}`, expected `%{expected_type}`, got `%{given_type}`"
39
+ for_hash:
40
+ wrong_element_type: "[%{service_class_name}] Wrong type in input hash `%{input_name}`, expected `%{expected_type}` for `%{key_name}`, got `%{given_type}`"
41
+ tools:
42
+ find_unnecessary:
43
+ error: "[%{service_class_name}] Unexpected attributes: `%{unnecessary_attributes}`"
44
+ rules:
45
+ error: "[%{service_class_name}] Conflict in `%{input_name}` input options: `%{conflict_code}`"
46
+ internals:
47
+ undefined:
48
+ getter: "[%{service_class_name}] Undefined internal attribute `%{internal_name}`"
49
+ setter: "[%{service_class_name}] Undefined internal attribute `%{internal_name}`"
50
+ validations:
51
+ inclusion:
52
+ default_error: "[%{service_class_name}] Wrong value in `%{internal_name}`, must be one of `%{internal_inclusion}`"
53
+ must:
54
+ default_error: "[%{service_class_name}] Internal attribute `%{internal_name}` must \"%{code}\""
55
+ syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{internal_name}` internal attribute: %{exception_message}"
56
+ dynamic_options:
57
+ format:
58
+ default: "[%{service_class_name}] Internal attribute `%{internal_name}` does not match `%{format_name}` format"
59
+ wrong_pattern: "[%{service_class_name}] Internal attribute `%{internal_name}` does not match `%{format_name}` format"
60
+ unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for output attribute `%{internal_name}`"
61
+ min:
62
+ default: "[%{service_class_name}] Internal attribute `%{internal_name}` received value `%{value}`, which is less than `%{option_value}`"
63
+ max:
64
+ default: "[%{service_class_name}] Internal attribute `%{internal_name}` received value `%{value}`, which is greater than `%{option_value}`"
65
+ type:
66
+ default_error:
67
+ default: "[%{service_class_name}] Wrong type of internal attribute `%{internal_name}`, expected `%{expected_type}`, got `%{given_type}`"
68
+ for_collection:
69
+ wrong_type: "[%{service_class_name}] Wrong internal attribute collection type `%{internal_name}`, expected `%{expected_type}`, got `%{given_type}`"
70
+ wrong_element_type: "[%{service_class_name}] Wrong element type in internal attribute collection `%{internal_name}`, expected `%{expected_type}`, got `%{given_type}`"
71
+ for_hash:
72
+ wrong_element_type: "[%{service_class_name}] Wrong type in internal attribute hash `%{internal_name}`, expected `%{expected_type}` for `%{key_name}`, got `%{given_type}`"
73
+ outputs:
74
+ undefined:
75
+ getter: "[%{service_class_name}] Undefined output attribute `%{output_name}`"
76
+ setter: "[%{service_class_name}] Undefined output attribute `%{output_name}`"
77
+ validations:
78
+ inclusion:
79
+ default_error: "[%{service_class_name}] Wrong value in `%{output_name}`, must be one of `%{output_inclusion}`"
80
+ must:
81
+ default_error: "[%{service_class_name}] Output attribute `%{output_name}` must \"%{code}\""
82
+ syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{output_name}` output attribute: %{exception_message}"
83
+ dynamic_options:
84
+ format:
85
+ default: "[%{service_class_name}] Output attribute `%{output_name}` does not match `%{format_name}` format"
86
+ wrong_pattern: "[%{service_class_name}] Output attribute `%{output_name}` does not match `%{format_name}` format"
87
+ unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for output attribute `%{output_name}`"
88
+ min:
89
+ default: "[%{service_class_name}] Output attribute `%{output_name}` received value `%{value}`, which is less than `%{option_value}`"
90
+ max:
91
+ default: "[%{service_class_name}] Output attribute `%{output_name}` received value `%{value}`, which is greater than `%{option_value}`"
92
+ type:
93
+ default_error:
94
+ default: "[%{service_class_name}] Wrong type of output attribute `%{output_name}`, expected `%{expected_type}`, got `%{given_type}`"
95
+ for_collection:
96
+ wrong_type: "[%{service_class_name}] Wrong output attribute collection type `%{output_name}`, expected `%{expected_type}`, got `%{given_type}`"
97
+ wrong_element_type: "[%{service_class_name}] Wrong element type in output attribute collection `%{output_name}`, expected `%{expected_type}`, got `%{given_type}`"
98
+ for_hash:
99
+ wrong_element_type: "[%{service_class_name}] Wrong type in output attribute hash `%{output_name}`, expected `%{expected_type}` for `%{key_name}`, got `%{given_type}`"
@@ -0,0 +1,99 @@
1
+ ru:
2
+ servactory:
3
+ common:
4
+ undefined_method:
5
+ missing_name: "[%{service_class_name}] %{error_text}"
6
+ methods:
7
+ call:
8
+ not_used: "[%{service_class_name}] Нечего выполнять. Используйте `make` или создайте метод `call`."
9
+ cannot_be_overwritten: "[%{service_class_name}] Нельзя перезаписать следующие методы: %{list_of_methods}"
10
+ inputs:
11
+ undefined:
12
+ getter: "[%{service_class_name}] Неизвестный входящий атрибут `%{input_name}`"
13
+ setter: "[%{service_class_name}] Неизвестный входящий атрибут `%{input_name}`"
14
+ validations:
15
+ inclusion:
16
+ default_error: "[%{service_class_name}] Неправильное значение в `%{input_name}`, должно быть одним из `%{input_inclusion}`"
17
+ must:
18
+ default_error: "[%{service_class_name}] Инпут `%{input_name}` должен \"%{code}\""
19
+ syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` инпута `%{input_name}`: %{exception_message}"
20
+ dynamic_options:
21
+ format:
22
+ default: "[%{service_class_name}] Инпут `%{input_name}` не соответствует формату `%{format_name}`"
23
+ wrong_pattern: "[%{service_class_name}] Инпут `%{input_name}` не соответствует формату `%{format_name}`"
24
+ unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у инпута `%{input_name}`"
25
+ min:
26
+ default: "[%{service_class_name}] Инпут `%{input_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
27
+ max:
28
+ default: "[%{service_class_name}] Инпут `%{input_name}` получил значение `%{value}`, которое больше `%{option_value}`"
29
+ required:
30
+ default_error:
31
+ default: "[%{service_class_name}] Обязательный инпут `%{input_name}` отсутствует"
32
+ for_collection: "[%{service_class_name}] Обязательный элемент в коллекции инпута `%{input_name}` отсутствует"
33
+ type:
34
+ default_error:
35
+ default: "[%{service_class_name}] Неправильный тип инпута `%{input_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
36
+ for_collection:
37
+ wrong_type: "[%{service_class_name}] Неправильный тип коллекции инпута `%{input_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
38
+ wrong_element_type: "[%{service_class_name}] Неправильный тип в коллекции инпута `%{input_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
39
+ for_hash:
40
+ wrong_element_type: "[%{service_class_name}] Неправильный тип в хеше инпута `%{input_name}`, для `%{key_name}` ожидалось `%{expected_type}`, получено `%{given_type}`"
41
+
42
+ tools:
43
+ find_unnecessary:
44
+ error: "[%{service_class_name}] Неожиданные атрибуты: `%{unnecessary_attributes}`"
45
+ rules:
46
+ error: "[%{service_class_name}] Конфликт в опциях инпута `%{input_name}`: `%{conflict_code}`"
47
+ internals:
48
+ undefined:
49
+ getter: "[%{service_class_name}] Неизвестный внутренний атрибут `%{internal_name}`"
50
+ setter: "[%{service_class_name}] Неизвестный внутренний атрибут `%{internal_name}`"
51
+ validations:
52
+ inclusion:
53
+ default_error: "[%{service_class_name}] Неправильное значение в `%{internal_name}`, должно быть одним из `%{internal_inclusion}`"
54
+ must:
55
+ default_error: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` должен \"%{code}\""
56
+ syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` внутреннего атрибута `%{internal_name}`: %{exception_message}"
57
+ dynamic_options:
58
+ format:
59
+ default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` не соответствует формату `%{format_name}`"
60
+ wrong_pattern: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` не соответствует формату `%{format_name}`"
61
+ unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у внутреннего атрибута `%{internal_name}`"
62
+ min:
63
+ default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
64
+ max:
65
+ default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` получил значение `%{value}`, которое больше `%{option_value}`"
66
+ type:
67
+ default_error:
68
+ default: "[%{service_class_name}] Неправильный тип внутреннего атрибута `%{internal_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
69
+ for_collection:
70
+ wrong_type: "[%{service_class_name}] Неправильный тип коллекции внутреннего атрибута `%{internal_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
71
+ wrong_element_type: "[%{service_class_name}] Неправильный тип элемента в коллекции внутреннего атрибута `%{internal_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
72
+ for_hash:
73
+ wrong_element_type: "[%{service_class_name}] Неправильный тип в хеше внутреннего атрибута `%{internal_name}`, для `%{key_name}` ожидалось `%{expected_type}`, получено `%{given_type}`"
74
+ outputs:
75
+ undefined:
76
+ getter: "[%{service_class_name}] Неизвестный выходящий атрибут `%{output_name}`"
77
+ setter: "[%{service_class_name}] Неизвестный выходящий атрибут `%{output_name}`"
78
+ validations:
79
+ inclusion:
80
+ default_error: "[%{service_class_name}] Неправильное значение в `%{output_name}`, должно быть одним из `%{output_inclusion}`"
81
+ must:
82
+ default_error: "[%{service_class_name}] Выходящий атрибут `%{output_name}` должен \"%{code}\""
83
+ syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` выходящего атрибута `%{output_name}`: %{exception_message}"
84
+ dynamic_options:
85
+ format:
86
+ default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` не соответствует формату `%{format_name}`"
87
+ wrong_pattern: "[%{service_class_name}] Выходящий атрибут `%{output_name}` не соответствует формату `%{format_name}`"
88
+ unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у выходящего атрибута `%{output_name}`"
89
+ min:
90
+ default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
91
+ max:
92
+ default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` получил значение `%{value}`, которое больше `%{option_value}`"
93
+ type:
94
+ default: "[%{service_class_name}] Неправильный тип выходящего атрибута `%{output_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
95
+ for_collection:
96
+ wrong_type: "[%{service_class_name}] Неправильный тип коллекции выходящего атрибута `%{output_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
97
+ wrong_element_type: "[%{service_class_name}] Неправильный тип элемента в коллекции выходящего атрибута `%{output_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
98
+ for_hash:
99
+ wrong_element_type: "[%{service_class_name}] Неправильный тип в хеше выходящего атрибута `%{output_name}`, для `%{key_name}` ожидалось `%{expected_type}`, получено `%{given_type}`"
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Attributes
5
+ class Attribute
6
+ attr_reader :name, :options
7
+
8
+ def initialize(name, **options)
9
+ @name = name
10
+ @options = options
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Attributes
5
+ class Collection
6
+ extend Forwardable
7
+ def_delegators :@collection, :<<, :each, :map, :merge # :filter, :each, :map, :flat_map, :to_h, :merge, :find
8
+
9
+ def initialize(collection = Set.new)
10
+ @collection = collection
11
+ end
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
+ def names
22
+ map(&:name)
23
+ end
24
+
25
+ # def find_by(name:)
26
+ # find { |input| input.internal_name == name }
27
+ # end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Attributes
5
+ module DSL
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ base.include(Workspace)
9
+ end
10
+
11
+ module ClassMethods
12
+ def inherited(child)
13
+ super
14
+
15
+ child.send(:collection_of_attributes).merge(collection_of_attributes)
16
+ end
17
+
18
+ private
19
+
20
+ def singular(value)
21
+ @singular = value
22
+ end
23
+
24
+ def plural(value)
25
+ @plural = value
26
+ end
27
+
28
+ def attribute(name, **options)
29
+ collection_of_attributes << Attribute.new(name, **options)
30
+ end
31
+
32
+ def string(name, **options)
33
+ options = options.merge(type: String)
34
+ attribute(name, **options)
35
+ end
36
+
37
+ def integer(name, **options)
38
+ options = options.merge(type: Integer)
39
+ attribute(name, **options)
40
+ end
41
+
42
+ def float(name, **options)
43
+ options = options.merge(type: Float)
44
+ attribute(name, **options)
45
+ end
46
+
47
+ def date(name, **options)
48
+ options = options.merge(type: Date)
49
+ attribute(name, **options)
50
+ end
51
+
52
+ def time(name, **options)
53
+ options = options.merge(type: Time)
54
+ attribute(name, **options)
55
+ end
56
+
57
+ def datetime(name, **options)
58
+ options = options.merge(type: DateTime)
59
+ attribute(name, **options)
60
+ end
61
+
62
+ def one(name, include:, as: nil)
63
+ attribute(name, as: as.presence || name, type: Hash, include: include)
64
+ end
65
+
66
+ def many(name, include:, as: nil)
67
+ attribute(name, as: as.presence || name, type: Array, consists_of: Hash, include: include)
68
+ end
69
+
70
+ def collection_of_attributes
71
+ @collection_of_attributes ||= Collection.new
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Attributes
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
+ private
61
+
62
+ def build!(incoming_attributes:, collection_of_attributes:, **)
63
+ super
64
+
65
+ builder_class_name = "GBuilder"
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)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ class Base
5
+ include Context::DSL
6
+ include Attributes::DSL
7
+ end
8
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Context
5
+ module Callable
6
+ def serialize(model)
7
+ hash = {}
8
+
9
+ collection_of_attributes.each do |attribute|
10
+ internal_name = attribute.options.fetch(:as, attribute.name)
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
37
+ end
38
+
39
+ def deserialize(json)
40
+ hash = JSON.parse(json.to_json)
41
+ build(**hash)
42
+ end
43
+
44
+ # def build!(attributes = {})
45
+ # context = send(:new)
46
+ #
47
+ # _build!(context, **attributes)
48
+ # end
49
+
50
+ def build(attributes = {})
51
+ context = send(:new)
52
+
53
+ _build!(context, **attributes)
54
+ end
55
+
56
+ private
57
+
58
+ def _build!(context, **attributes)
59
+ context.send(
60
+ :_build!,
61
+ incoming_attributes: attributes.symbolize_keys,
62
+ collection_of_attributes: collection_of_attributes
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Context
5
+ module DSL
6
+ def self.included(base)
7
+ base.extend(Callable)
8
+ base.include(Workspace)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Context
5
+ module Workspace
6
+ def attributes
7
+ @attributes ||= Attributes.new(
8
+ context: self,
9
+ incoming_attributes: incoming_attributes,
10
+ collection_of_attributes: collection_of_attributes
11
+ )
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :incoming_attributes,
17
+ :collection_of_attributes
18
+
19
+ def _build!(
20
+ incoming_attributes:,
21
+ collection_of_attributes:
22
+ )
23
+ build!(
24
+ incoming_attributes: incoming_attributes,
25
+ collection_of_attributes: collection_of_attributes
26
+ )
27
+ end
28
+
29
+ def build!(
30
+ incoming_attributes:,
31
+ collection_of_attributes:,
32
+ **
33
+ )
34
+ @incoming_attributes = incoming_attributes
35
+ @collection_of_attributes = collection_of_attributes
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ class Result < Servactory::Result; end
5
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Service
5
+ class Base
6
+ include Servactory::DSL
7
+
8
+ configuration do
9
+ input_exception_class Datory::Service::Exceptions::Input
10
+ internal_exception_class Datory::Service::Exceptions::Internal
11
+ output_exception_class Datory::Service::Exceptions::Output
12
+
13
+ failure_class Datory::Service::Exceptions::Failure
14
+
15
+ result_class Datory::Result
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Service
5
+ class Builder < Base
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module Service
5
+ module Exceptions
6
+ class Input < Servactory::Exceptions::Input; end
7
+ class Output < Servactory::Exceptions::Output; end
8
+ class Internal < Servactory::Exceptions::Internal; end
9
+
10
+ class Failure < Servactory::Exceptions::Failure; end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datory
4
+ module VERSION
5
+ MAJOR = 1
6
+ MINOR = 0
7
+ PATCH = 0
8
+ PRE = "rc1"
9
+
10
+ STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
+ end
12
+ end
data/lib/datory.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+
5
+ require "active_support/all"
6
+
7
+ require "uri"
8
+
9
+ loader = Zeitwerk::Loader.for_gem
10
+ loader.inflector.inflect(
11
+ "dsl" => "DSL"
12
+ )
13
+ loader.setup
14
+
15
+ module Datory; end
16
+
17
+ require "datory/engine" if defined?(Rails::Engine)
metadata ADDED
@@ -0,0 +1,241 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: datory
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.rc1
5
+ platform: ruby
6
+ authors:
7
+ - Anton Sokolov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-04-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7.2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '5.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7.2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: i18n
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.14'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.14'
47
+ - !ruby/object:Gem::Dependency
48
+ name: servactory
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '2.4'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '2.4'
61
+ - !ruby/object:Gem::Dependency
62
+ name: zeitwerk
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.6'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.6'
75
+ - !ruby/object:Gem::Dependency
76
+ name: appraisal
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.5'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.5'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '13.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rbs
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 3.1.3
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 3.1.3
117
+ - !ruby/object:Gem::Dependency
118
+ name: rspec
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.12'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '3.12'
131
+ - !ruby/object:Gem::Dependency
132
+ name: rubocop
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '1.57'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '1.57'
145
+ - !ruby/object:Gem::Dependency
146
+ name: rubocop-performance
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '1.19'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '1.19'
159
+ - !ruby/object:Gem::Dependency
160
+ name: rubocop-rake
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.6'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '0.6'
173
+ - !ruby/object:Gem::Dependency
174
+ name: rubocop-rspec
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '2.24'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '2.24'
187
+ description: A set of tools for building reliable services of any complexity
188
+ email:
189
+ - profox.rus@gmail.com
190
+ executables: []
191
+ extensions: []
192
+ extra_rdoc_files: []
193
+ files:
194
+ - README.md
195
+ - Rakefile
196
+ - config/locales/en.yml
197
+ - config/locales/ru.yml
198
+ - lib/datory.rb
199
+ - lib/datory/attributes/attribute.rb
200
+ - lib/datory/attributes/collection.rb
201
+ - lib/datory/attributes/dsl.rb
202
+ - lib/datory/attributes/workspace.rb
203
+ - lib/datory/base.rb
204
+ - lib/datory/context/callable.rb
205
+ - lib/datory/context/dsl.rb
206
+ - lib/datory/context/workspace.rb
207
+ - lib/datory/result.rb
208
+ - lib/datory/service/base.rb
209
+ - lib/datory/service/builder.rb
210
+ - lib/datory/service/exceptions.rb
211
+ - lib/datory/version.rb
212
+ homepage: https://github.com/servactory/datory
213
+ licenses:
214
+ - MIT
215
+ metadata:
216
+ homepage_uri: https://github.com/servactory/datory
217
+ documentation_uri: https://datory.servactory.com
218
+ source_code_uri: https://github.com/servactory/datory
219
+ bug_tracker_uri: https://github.com/servactory/datory/issues
220
+ changelog_uri: https://github.com/servactory/datory/blob/master/CHANGELOG.md
221
+ rubygems_mfa_required: 'true'
222
+ post_install_message:
223
+ rdoc_options: []
224
+ require_paths:
225
+ - lib
226
+ required_ruby_version: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: 3.0.0
231
+ required_rubygems_version: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
236
+ requirements: []
237
+ rubygems_version: 3.5.6
238
+ signing_key:
239
+ specification_version: 4
240
+ summary: A set of tools for building reliable services of any complexity
241
+ test_files: []