treaty 0.0.1 → 0.1.0

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -18
  3. data/Rakefile +4 -2
  4. data/lib/treaty/attribute/base.rb +172 -0
  5. data/lib/treaty/attribute/builder/base.rb +142 -0
  6. data/lib/treaty/attribute/collection.rb +65 -0
  7. data/lib/treaty/attribute/helper_mapper.rb +72 -0
  8. data/lib/treaty/attribute/option/base.rb +159 -0
  9. data/lib/treaty/attribute/option/modifiers/as_modifier.rb +87 -0
  10. data/lib/treaty/attribute/option/modifiers/default_modifier.rb +103 -0
  11. data/lib/treaty/attribute/option/registry.rb +128 -0
  12. data/lib/treaty/attribute/option/registry_initializer.rb +90 -0
  13. data/lib/treaty/attribute/option/validators/inclusion_validator.rb +80 -0
  14. data/lib/treaty/attribute/option/validators/required_validator.rb +94 -0
  15. data/lib/treaty/attribute/option/validators/type_validator.rb +153 -0
  16. data/lib/treaty/attribute/option_normalizer.rb +150 -0
  17. data/lib/treaty/attribute/option_orchestrator.rb +186 -0
  18. data/lib/treaty/attribute/validation/attribute_validator.rb +144 -0
  19. data/lib/treaty/attribute/validation/base.rb +93 -0
  20. data/lib/treaty/attribute/validation/nested_array_validator.rb +194 -0
  21. data/lib/treaty/attribute/validation/nested_object_validator.rb +103 -0
  22. data/lib/treaty/attribute/validation/nested_transformer.rb +240 -0
  23. data/lib/treaty/attribute/validation/orchestrator/base.rb +196 -0
  24. data/lib/treaty/base.rb +9 -0
  25. data/lib/treaty/configuration.rb +17 -0
  26. data/lib/treaty/context/callable.rb +24 -0
  27. data/lib/treaty/context/dsl.rb +12 -0
  28. data/lib/treaty/context/workspace.rb +28 -0
  29. data/lib/treaty/controller/dsl.rb +38 -0
  30. data/lib/treaty/engine.rb +37 -0
  31. data/lib/treaty/exceptions/base.rb +8 -0
  32. data/lib/treaty/exceptions/class_name.rb +11 -0
  33. data/lib/treaty/exceptions/deprecated.rb +8 -0
  34. data/lib/treaty/exceptions/execution.rb +8 -0
  35. data/lib/treaty/exceptions/method_name.rb +8 -0
  36. data/lib/treaty/exceptions/nested_attributes.rb +8 -0
  37. data/lib/treaty/exceptions/strategy.rb +8 -0
  38. data/lib/treaty/exceptions/unexpected.rb +8 -0
  39. data/lib/treaty/exceptions/validation.rb +8 -0
  40. data/lib/treaty/info/builder.rb +122 -0
  41. data/lib/treaty/info/dsl.rb +26 -0
  42. data/lib/treaty/info/result.rb +13 -0
  43. data/lib/treaty/request/attribute/attribute.rb +24 -0
  44. data/lib/treaty/request/attribute/builder.rb +22 -0
  45. data/lib/treaty/request/attribute/validation/orchestrator.rb +27 -0
  46. data/lib/treaty/request/attribute/validator.rb +50 -0
  47. data/lib/treaty/request/factory.rb +32 -0
  48. data/lib/treaty/request/scope/collection.rb +21 -0
  49. data/lib/treaty/request/scope/factory.rb +42 -0
  50. data/lib/treaty/response/attribute/attribute.rb +24 -0
  51. data/lib/treaty/response/attribute/builder.rb +22 -0
  52. data/lib/treaty/response/attribute/validation/orchestrator.rb +27 -0
  53. data/lib/treaty/response/attribute/validator.rb +44 -0
  54. data/lib/treaty/response/factory.rb +38 -0
  55. data/lib/treaty/response/scope/collection.rb +21 -0
  56. data/lib/treaty/response/scope/factory.rb +42 -0
  57. data/lib/treaty/result.rb +22 -0
  58. data/lib/treaty/strategy.rb +31 -0
  59. data/lib/treaty/support/loader.rb +24 -0
  60. data/lib/treaty/version.rb +8 -1
  61. data/lib/treaty/versions/collection.rb +15 -0
  62. data/lib/treaty/versions/dsl.rb +30 -0
  63. data/lib/treaty/versions/execution/request.rb +151 -0
  64. data/lib/treaty/versions/executor.rb +14 -0
  65. data/lib/treaty/versions/factory.rb +93 -0
  66. data/lib/treaty/versions/resolver.rb +72 -0
  67. data/lib/treaty/versions/semantic.rb +22 -0
  68. data/lib/treaty/versions/workspace.rb +40 -0
  69. data/lib/treaty.rb +3 -3
  70. metadata +184 -27
  71. data/.standard.yml +0 -3
  72. data/CHANGELOG.md +0 -5
  73. data/CODE_OF_CONDUCT.md +0 -84
  74. data/LICENSE.txt +0 -21
  75. data/sig/treaty.rbs +0 -4
  76. data/treaty.gemspec +0 -35
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Request
5
+ module Attribute
6
+ module Validation
7
+ class Orchestrator < Treaty::Attribute::Validation::Orchestrator::Base
8
+ private
9
+
10
+ def collection_of_scopes
11
+ return Treaty::Request::Scope::Collection.new if version_factory.request_factory.nil?
12
+
13
+ version_factory.request_factory.collection_of_scopes
14
+ end
15
+
16
+ def scope_data_for(name)
17
+ # If the scope is :_self, it's the root level.
18
+ return data if name == :_self
19
+
20
+ # Otherwise, fetch data from the named scope.
21
+ data.fetch(name, {})
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Request
5
+ module Attribute
6
+ class Validator < Treaty::Attribute::Validation::Base
7
+ def self.validate!(...)
8
+ new(...).validate!
9
+ end
10
+
11
+ def initialize(controller:, version_factory:)
12
+ super(version_factory:)
13
+
14
+ @controller = controller
15
+ end
16
+
17
+ def validate!
18
+ validate_request_attributes!
19
+ end
20
+
21
+ private
22
+
23
+ def request_data
24
+ @request_data ||= begin
25
+ @controller.params.to_unsafe_h
26
+ rescue NoMethodError
27
+ @controller.params
28
+ end
29
+ end
30
+
31
+ def validate_request_attributes!
32
+ return request_data unless adapter_strategy?
33
+ return request_data unless request_attributes_exist?
34
+
35
+ # For adapter strategy:
36
+ Validation::Orchestrator.validate!(
37
+ version_factory: @version_factory,
38
+ data: request_data
39
+ )
40
+ end
41
+
42
+ def request_attributes_exist?
43
+ return false if @version_factory.request_factory&.collection_of_scopes&.empty?
44
+
45
+ @version_factory.request_factory.collection_of_scopes.exists?
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Request
5
+ class Factory
6
+ def scope(name, &block)
7
+ @scope_factory = Scope::Factory.new(name)
8
+
9
+ @scope_factory.instance_eval(&block) if block_given?
10
+
11
+ collection_of_scopes << @scope_factory
12
+
13
+ @scope_factory = nil
14
+ end
15
+
16
+ def collection_of_scopes
17
+ @collection_of_scopes ||= Scope::Collection.new
18
+ end
19
+
20
+ ##########################################################################
21
+
22
+ def method_missing(name, *, &_block)
23
+ # TODO: It needs to be implemented.
24
+ puts "Unknown request block method: #{name}"
25
+ end
26
+
27
+ def respond_to_missing?(name, *)
28
+ super
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Request
5
+ module Scope
6
+ class Collection
7
+ extend Forwardable
8
+
9
+ def_delegators :@collection, :<<, :to_h, :each, :find, :empty?
10
+
11
+ def initialize(collection = Set.new)
12
+ @collection = collection
13
+ end
14
+
15
+ def exists?
16
+ !empty?
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Request
5
+ module Scope
6
+ class Factory
7
+ attr_reader :name
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ end
12
+
13
+ def attribute(name, type, *helpers, **options, &block)
14
+ collection_of_attributes << Attribute::Attribute.new(
15
+ name,
16
+ type,
17
+ *helpers,
18
+ nesting_level: 0,
19
+ **options,
20
+ &block
21
+ )
22
+ end
23
+
24
+ def collection_of_attributes
25
+ @collection_of_attributes ||= Treaty::Attribute::Collection.new
26
+ end
27
+
28
+ ########################################################################
29
+
30
+ def method_missing(type, *helpers, **options, &block)
31
+ name = helpers.shift
32
+
33
+ attribute(name, type, *helpers, **options, &block)
34
+ end
35
+
36
+ def respond_to_missing?(name, *)
37
+ super
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ module Attribute
6
+ class Attribute < Treaty::Attribute::Base
7
+ private
8
+
9
+ def apply_defaults!
10
+ # For response: optional by default (false).
11
+ # TODO: It is necessary to implement a translation system (I18n).
12
+ @options[:required] ||= { is: false, message: nil }
13
+ end
14
+
15
+ def process_nested_attributes(&block)
16
+ return unless object_or_array?
17
+
18
+ builder = Builder.new(collection_of_attributes, @nesting_level + 1)
19
+ builder.instance_eval(&block)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ module Attribute
6
+ class Builder < Treaty::Attribute::Builder::Base
7
+ private
8
+
9
+ def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
10
+ Attribute.new(
11
+ name,
12
+ type,
13
+ *helpers,
14
+ nesting_level:,
15
+ **options,
16
+ &block
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ module Attribute
6
+ module Validation
7
+ class Orchestrator < Treaty::Attribute::Validation::Orchestrator::Base
8
+ private
9
+
10
+ def collection_of_scopes
11
+ return Treaty::Response::Scope::Collection.new if version_factory.response_factory.nil?
12
+
13
+ version_factory.response_factory.collection_of_scopes
14
+ end
15
+
16
+ def scope_data_for(name)
17
+ # If the scope is :_self, it's the root level.
18
+ return data if name == :_self
19
+
20
+ # Otherwise, fetch data from the named scope.
21
+ data.fetch(name, {})
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ module Attribute
6
+ class Validator < Treaty::Attribute::Validation::Base
7
+ def self.validate!(version_factory:, response_data: {})
8
+ new(
9
+ version_factory:,
10
+ response_data:
11
+ ).validate!
12
+ end
13
+
14
+ def initialize(version_factory:, response_data: {})
15
+ super(version_factory:)
16
+
17
+ @response_data = response_data
18
+ end
19
+
20
+ def validate!
21
+ validate_response_attributes!
22
+ end
23
+
24
+ private
25
+
26
+ def validate_response_attributes!
27
+ return @response_data unless response_attributes_exist?
28
+
29
+ # For adapter strategy:
30
+ Validation::Orchestrator.validate!(
31
+ version_factory: @version_factory,
32
+ data: @response_data
33
+ )
34
+ end
35
+
36
+ def response_attributes_exist?
37
+ return false if @version_factory.response_factory&.collection_of_scopes&.empty?
38
+
39
+ @version_factory.response_factory.collection_of_scopes.exists?
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ class Factory
6
+ attr_reader :status
7
+
8
+ def initialize(status)
9
+ @status = status
10
+ end
11
+
12
+ def scope(name, &block)
13
+ @scope_factory = Scope::Factory.new(name)
14
+
15
+ @scope_factory.instance_eval(&block) if block_given?
16
+
17
+ collection_of_scopes << @scope_factory
18
+
19
+ @scope_factory = nil
20
+ end
21
+
22
+ def collection_of_scopes
23
+ @collection_of_scopes ||= Scope::Collection.new
24
+ end
25
+
26
+ ##########################################################################
27
+
28
+ def method_missing(name, *, &_block)
29
+ # TODO: It needs to be implemented.
30
+ puts "Unknown response block method: #{name}"
31
+ end
32
+
33
+ def respond_to_missing?(name, *)
34
+ super
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ module Scope
6
+ class Collection
7
+ extend Forwardable
8
+
9
+ def_delegators :@collection, :<<, :to_h, :each, :find, :empty?
10
+
11
+ def initialize(collection = Set.new)
12
+ @collection = collection
13
+ end
14
+
15
+ def exists?
16
+ !empty?
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ module Scope
6
+ class Factory
7
+ attr_reader :name
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ end
12
+
13
+ def attribute(name, type, *helpers, **options, &block)
14
+ collection_of_attributes << Attribute::Attribute.new(
15
+ name,
16
+ type,
17
+ *helpers,
18
+ nesting_level: 0,
19
+ **options,
20
+ &block
21
+ )
22
+ end
23
+
24
+ def collection_of_attributes
25
+ @collection_of_attributes ||= Treaty::Attribute::Collection.new
26
+ end
27
+
28
+ ########################################################################
29
+
30
+ def method_missing(type, *helpers, **options, &block)
31
+ name = helpers.shift
32
+
33
+ attribute(name, type, *helpers, **options, &block)
34
+ end
35
+
36
+ def respond_to_missing?(name, *)
37
+ super
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ class Result
5
+ attr_reader :data, :status
6
+
7
+ def initialize(data:, status:)
8
+ @data = data
9
+ @status = status
10
+ end
11
+
12
+ def inspect
13
+ "#<#{self.class.name} #{draw_result}>"
14
+ end
15
+
16
+ private
17
+
18
+ def draw_result
19
+ "@data=#{@data.inspect}, @status=#{@status.inspect}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ class Strategy
5
+ DIRECT = :direct
6
+ ADAPTER = :adapter
7
+
8
+ LIST = [DIRECT, ADAPTER].freeze
9
+
10
+ attr_reader :code
11
+
12
+ def initialize(code)
13
+ @code = code
14
+ end
15
+
16
+ def validate!
17
+ return self if LIST.include?(@code)
18
+
19
+ # TODO: It is necessary to implement a translation system (I18n).
20
+ raise Treaty::Exceptions::Strategy, "Unknown strategy: #{@code}"
21
+ end
22
+
23
+ def direct?
24
+ @code == DIRECT
25
+ end
26
+
27
+ def adapter?
28
+ @code == ADAPTER
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+
5
+ lib_dir = File.expand_path("../..", __dir__)
6
+
7
+ loader = Zeitwerk::Loader.new
8
+
9
+ loader.tag = "treaty"
10
+
11
+ loader.inflector = Zeitwerk::GemInflector.new(
12
+ File.expand_path("web.rb", lib_dir)
13
+ )
14
+
15
+ loader.inflector.inflect(
16
+ "dsl" => "DSL",
17
+ "version" => "VERSION"
18
+ )
19
+
20
+ loader.ignore(__dir__)
21
+
22
+ loader.push_dir(lib_dir)
23
+
24
+ loader.setup
@@ -1,5 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Treaty
4
- VERSION = "0.0.1"
4
+ module VERSION
5
+ MAJOR = 0
6
+ MINOR = 1
7
+ PATCH = 0
8
+ PRE = nil
9
+
10
+ STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
+ end
5
12
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Versions
5
+ class Collection
6
+ extend Forwardable
7
+
8
+ def_delegators :@collection, :<<, :map, :find
9
+
10
+ def initialize(collection = Set.new)
11
+ @collection = collection
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Versions
5
+ module DSL
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ base.include(Workspace)
9
+ end
10
+
11
+ module ClassMethods
12
+ private
13
+
14
+ def version(version, default: false, &block)
15
+ @version_factory = Factory.new(version:, default:)
16
+
17
+ @version_factory.instance_eval(&block)
18
+
19
+ collection_of_versions << @version_factory
20
+
21
+ @version_factory = nil
22
+ end
23
+
24
+ def collection_of_versions
25
+ @collection_of_versions ||= Collection.new
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Versions
5
+ module Execution
6
+ class Request
7
+ def self.execute!(...)
8
+ new(...).execute!
9
+ end
10
+
11
+ def initialize(version_factory:, validated_params:)
12
+ @version_factory = version_factory
13
+ @validated_params = validated_params
14
+ end
15
+
16
+ def execute!
17
+ raise_executor_missing_error! if @version_factory.executor.nil?
18
+
19
+ extract_data_from_result
20
+ end
21
+
22
+ private
23
+
24
+ def extract_data_from_result
25
+ return execution_result if executor.is_a?(Proc)
26
+ return execution_result.data if execution_result.respond_to?(:data)
27
+
28
+ execution_result
29
+ end
30
+
31
+ ########################################################################
32
+
33
+ def execution_result
34
+ @execution_result ||=
35
+ if executor.is_a?(Proc)
36
+ execute_proc
37
+ elsif servactory_service?
38
+ execute_servactory
39
+ else
40
+ execute_regular_class
41
+ end
42
+ end
43
+
44
+ ########################################################################
45
+
46
+ def executor
47
+ @executor ||= resolve_executor(@version_factory.executor.executor)
48
+ end
49
+
50
+ ########################################################################
51
+
52
+ def resolve_executor(executor) # rubocop:disable Metrics/MethodLength
53
+ return executor if executor.is_a?(Proc) || executor.is_a?(Class)
54
+
55
+ if executor.is_a?(String) || executor.is_a?(Symbol)
56
+ string_executor = executor.to_s
57
+
58
+ if string_executor.empty?
59
+ # TODO: It is necessary to implement a translation system (I18n).
60
+ raise Treaty::Exceptions::Execution,
61
+ "Executor cannot be an empty string"
62
+ end
63
+
64
+ constant_name = normalize_constant_name(executor)
65
+
66
+ begin
67
+ constant_name.constantize
68
+ rescue NameError
69
+ # TODO: It is necessary to implement a translation system (I18n).
70
+ raise Treaty::Exceptions::Execution,
71
+ "Executor class `#{constant_name}` not found"
72
+ end
73
+ else
74
+ # TODO: It is necessary to implement a translation system (I18n).
75
+ raise Treaty::Exceptions::Execution,
76
+ "Invalid executor type: #{executor.class}. " \
77
+ "Expected Proc, Class, String, or Symbol"
78
+ end
79
+ end
80
+
81
+ ########################################################################
82
+
83
+ def normalize_constant_name(name)
84
+ string = name.to_s
85
+
86
+ return string if string.include?("::")
87
+ return string.split("/").map(&:camelize).join("::") if string.include?("/")
88
+
89
+ string
90
+ end
91
+
92
+ ########################################################################
93
+ ########################################################################
94
+ ########################################################################
95
+
96
+ def execute_proc
97
+ executor.call(params: @validated_params)
98
+ rescue StandardError => e
99
+ # TODO: It is necessary to implement a translation system (I18n).
100
+ raise Treaty::Exceptions::Execution, e.message
101
+ end
102
+
103
+ def execute_servactory
104
+ executor.call!(params: @validated_params)
105
+ rescue ApplicationService::Exceptions::Input => e
106
+ # TODO: It is necessary to implement a translation system (I18n).
107
+ raise Treaty::Exceptions::Execution, e.message
108
+ rescue ApplicationService::Exceptions::Internal => e # rubocop:disable Lint/DuplicateBranch
109
+ # TODO: It is necessary to implement a translation system (I18n).
110
+ raise Treaty::Exceptions::Execution, e.message
111
+ rescue ApplicationService::Exceptions::Output => e # rubocop:disable Lint/DuplicateBranch
112
+ # TODO: It is necessary to implement a translation system (I18n).
113
+ raise Treaty::Exceptions::Execution, e.message
114
+ rescue ApplicationService::Exceptions::Failure => e # rubocop:disable Lint/DuplicateBranch
115
+ # TODO: It is necessary to implement a translation system (I18n).
116
+ raise Treaty::Exceptions::Execution, e.message
117
+ end
118
+
119
+ def execute_regular_class
120
+ method_name = @version_factory.executor.method
121
+
122
+ unless executor.respond_to?(method_name)
123
+ # TODO: It is necessary to implement a translation system (I18n).
124
+ raise Treaty::Exceptions::Execution,
125
+ "Method '#{method_name}' not found in class '#{executor}'"
126
+ end
127
+
128
+ executor.public_send(method_name, params: @validated_params)
129
+ rescue StandardError => e
130
+ # TODO: It is necessary to implement a translation system (I18n).
131
+ raise Treaty::Exceptions::Execution, e.message
132
+ end
133
+
134
+ ########################################################################
135
+ ########################################################################
136
+ ########################################################################
137
+
138
+ def raise_executor_missing_error!
139
+ # TODO: It is necessary to implement a translation system (I18n).
140
+ raise Treaty::Exceptions::Execution,
141
+ "Executor is not defined for version #{@version_factory.version}"
142
+ end
143
+
144
+ def servactory_service?
145
+ executor.respond_to?(:servactory?) &&
146
+ executor.servactory?
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end