datory 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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: []