smart_core 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/.travis.yml +13 -9
- data/CHANGELOG.md +6 -0
- data/README.md +65 -2
- data/lib/smart_core/operation/attribute.rb +66 -0
- data/lib/smart_core/operation/attribute_definer.rb +160 -0
- data/lib/smart_core/operation/attribute_set.rb +92 -0
- data/lib/smart_core/operation/exceptions.rb +39 -0
- data/lib/smart_core/operation/failure.rb +29 -0
- data/lib/smart_core/operation/initialization_dsl.rb +124 -0
- data/lib/smart_core/operation/instance_builder.rb +161 -0
- data/lib/smart_core/operation/result.rb +44 -0
- data/lib/smart_core/operation/success.rb +71 -0
- data/lib/smart_core/operation.rb +65 -0
- data/lib/smart_core/validator/attribute_set.rb +2 -2
- data/lib/smart_core/validator/dsl.rb +2 -2
- data/lib/smart_core/validator/instance_builder.rb +114 -0
- data/lib/smart_core/validator.rb +8 -23
- data/lib/smart_core/version.rb +1 -1
- data/lib/smart_core.rb +1 -0
- data/smart_core.gemspec +5 -4
- metadata +28 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2ff8c25822c588b21c3d5785f04998fb1bb838e30b6046cc7e1706891133c6b
|
4
|
+
data.tar.gz: 307a1f6b397dba1339c9e6d4ea701343b349c22261b59e42ae35ccce735b7ff7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7755b70015cc2cb2c0cc76c9e3436cda0f7d76170f9109e3e20655ce6e29c16a530b1b522398355329eaa64e5cd139153ba3c93043372b6e734dbfe4994b47a8
|
7
|
+
data.tar.gz: e580d37f900e4e969464b094d4fc4d4795edcc6bbd90e482716dfcbc7147e327543dcf7e8c29ae5905a15ccd29a362b1b3873cc68972e742c3a5fc579696062b
|
data/.rspec
CHANGED
data/.travis.yml
CHANGED
@@ -2,15 +2,19 @@ language: ruby
|
|
2
2
|
sudo: false
|
3
3
|
before_install: gem install bundler
|
4
4
|
cache: bundler
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
- 2.
|
9
|
-
- 2.
|
10
|
-
-
|
11
|
-
-
|
12
|
-
|
13
|
-
-
|
5
|
+
matrix:
|
6
|
+
fast_finish: true
|
7
|
+
include:
|
8
|
+
- rvm: 2.3.8
|
9
|
+
- rvm: 2.4.5
|
10
|
+
- rvm: 2.5.3
|
11
|
+
- rvm: 2.6.0
|
12
|
+
- rvm: ruby-head
|
13
|
+
- rvm: jruby-head
|
14
|
+
allow_failures:
|
15
|
+
- rvm: ruby-head
|
16
|
+
- rvm: jruby-head
|
17
|
+
script: bundle exec rspec
|
14
18
|
notifications:
|
15
19
|
slack:
|
16
20
|
secure: CUM3xnSJvn/y6Z+epQrO5uSk1EUuP4S+ugcS+48nlWD6eEzhYZwcBAK9tvD6kYGOPzoWyKSbW6Is1kzSunNr6f8ICc8HtrVHzoHE8UCzwZ7FpjgD0JgMgC4+xLLBO6lV0Hc7dNh4KLAhy4Zvbr09DPjcG8UW9/QgUdn+wBf9LHMgA+2SpBPEzMwz6pMbWW/+kkoCz2KU877kEfuFFGfvoOEFWTxUUhuCYi5a1SmTjm9A7CFTEWNkX8yHJwdgVgONjvm9lnhTeV0DvMeEOhk8Kx/hzQ2OGIicXZXWyZFkzNzpQm5xE12Hs4rY7mGPbo3bsYRDH9Ys6yf4fpNf0oZ/1agzl/gnDzacA2wL2b+eq1uZblE/fdwrNp+GIH0R+Ec2rsVKM7kh71sA6Xp1pzFb2DnHj5O4QFaPMsLyIZqD0BkVt4pNfwi4ZUB56sWkYeRQGm8x2Beh02IyFfxI65lY5kZrW6GjXx5eFeOHcmzQzeADWLSBf4iw01G/A/QqwZzlZ3fm0fRJi8BKDh3Jj0IrGndE9Bl+E7uOClKKngTPxOVLOLo/vY5mfI8M94f99kr0J45XkoAdc0SQfi3hDa/tHaakI5GJsmByjgOQwV7OU/2wzJoXAg/RPYjsYcwnTgzm9bLXPhQzfM8rUS5vLTOWIW+N0QKmWml7eWVtiedcwXA=
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [0.2.0] - 2019-01-20
|
5
|
+
### Added
|
6
|
+
- **Service object** abstraction (`SmartCore::Operation`);
|
7
|
+
- **Validator**
|
8
|
+
- Support for `&block` attribute at object instantiation;
|
9
|
+
|
4
10
|
## [0.1.0] - 2019-01-05
|
5
11
|
### Added
|
6
12
|
- Validation object abstraction;
|
data/README.md
CHANGED
@@ -1,12 +1,75 @@
|
|
1
|
-
# SmartCore · [](https://travis-ci.org/0exp/smart_core) [](https://coveralls.io/github/0exp/smart_core?branch=master)
|
1
|
+
# SmartCore · [](https://badge.fury.io/rb/smart_core) [](https://travis-ci.org/0exp/smart_core) [](https://coveralls.io/github/0exp/smart_core?branch=master)
|
2
2
|
|
3
3
|
In active development.
|
4
4
|
|
5
|
+
---
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'smart_core'
|
11
|
+
```
|
12
|
+
|
13
|
+
```shell
|
14
|
+
bundle install
|
15
|
+
# --- or ---
|
16
|
+
gem install smart_core
|
17
|
+
```
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require 'smart_core'
|
21
|
+
```
|
22
|
+
|
23
|
+
---
|
24
|
+
|
5
25
|
#### Completed abstractions:
|
6
26
|
|
7
|
-
- **
|
27
|
+
- [**Operation Object**](#operation-object) (aka `Service Object`) (`SmartCore::Operation`)
|
28
|
+
- attribute definition DSL (`param`, `option`, `params`, `options`);
|
29
|
+
- yieldable result object abstraction (`Success`, `Failure`, `#success?`, `#failure?`);
|
30
|
+
- yieldable `#call` (and `.call`);
|
31
|
+
- inheritance works as expected `:)`;
|
32
|
+
- no dependencies;
|
33
|
+
|
34
|
+
- [**Validation Object**](#validation-object) (`SmartCore::Validator`)
|
8
35
|
- support for nested validations;
|
9
36
|
- inheritance works as expected `:)`;
|
10
37
|
- command-style DSL;
|
11
38
|
- thread-safe;
|
12
39
|
- no dependencies;
|
40
|
+
|
41
|
+
---
|
42
|
+
|
43
|
+
#### Operation Object
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class Service < SmartCore::Operation
|
47
|
+
# soon...
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
#### Validation Object
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class Validator < SmartCore::Validator
|
55
|
+
# soon...
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
---
|
60
|
+
|
61
|
+
## Contributing
|
62
|
+
|
63
|
+
- Fork it ( https://github.com/0exp/smart_core/fork )
|
64
|
+
- Create your feature branch (`git checkout -b feature/my-new-feature`)
|
65
|
+
- Commit your changes (`git commit -am 'Add some feature'`)
|
66
|
+
- Push to the branch (`git push origin feature/my-new-feature`)
|
67
|
+
- Create new Pull Request
|
68
|
+
|
69
|
+
## License
|
70
|
+
|
71
|
+
Released under MIT License.
|
72
|
+
|
73
|
+
## Authors
|
74
|
+
|
75
|
+
[Rustam Ibragimov](https://github.com/0exp)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation::Attribute
|
6
|
+
# @return [Symbol]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.2.0
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @return [Hash<Symbol,Any>]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.2.0
|
16
|
+
attr_reader :options
|
17
|
+
|
18
|
+
# @param name [String, Symbol]
|
19
|
+
# @param options [Hash<Symbol,Any>] Supported options:
|
20
|
+
# - :default (see #default_value) (proc or object)
|
21
|
+
# @return [void]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
# @since 0.2.0
|
25
|
+
def initialize(name, **options)
|
26
|
+
unless name.is_a?(Symbol) || name.is_a?(String)
|
27
|
+
raise(
|
28
|
+
SmartCore::Operation::IncorrectAttributeNameError,
|
29
|
+
'Attribute name should be a symbol or a string'
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
@name = name
|
34
|
+
@options = options # TODO: check for unsupported options (and fail if found)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Boolean]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
# @since 0.2.0
|
41
|
+
def has_default_value?
|
42
|
+
options.key?(:default)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Any]
|
46
|
+
#
|
47
|
+
# @raise [SmartCore::Operation::ArgumentError]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
# @since 0.2.0
|
51
|
+
def default_value
|
52
|
+
default_value = options.fetch(:default) do
|
53
|
+
raise(SmartCore::Operation::ArgumentError, 'Default value is not provided.')
|
54
|
+
end
|
55
|
+
|
56
|
+
default_value.is_a?(Proc) ? default_value.call : default_value
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [SmartCore::Operation::Attribute]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
# @since 0.2.0
|
63
|
+
def dup
|
64
|
+
self.class.new(name, **options)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation::AttributeDefiner
|
6
|
+
# @param operation_klass [Class<SmartCore::Operation>]
|
7
|
+
# @return [void]
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
# @since 0.2.0
|
11
|
+
def initialize(operation_klass)
|
12
|
+
@operation_klass = operation_klass
|
13
|
+
@definition_lock = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param param_name [Symbol, String]
|
17
|
+
# @return [void]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
# @since 0.2.0
|
21
|
+
def define_param(param_name)
|
22
|
+
thread_safe do
|
23
|
+
parameter = build_attribute(param_name)
|
24
|
+
prevent_intersection_with_already_defined_option(parameter)
|
25
|
+
append_parameter(parameter)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param param_names [Array<String, Symbol>]
|
30
|
+
# @return [void]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.2.0
|
34
|
+
def define_params(*param_names)
|
35
|
+
thread_safe do
|
36
|
+
parameters = param_names.map do |param_name|
|
37
|
+
build_attribute(param_name).tap do |parameter|
|
38
|
+
prevent_intersection_with_already_defined_option(parameter)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
parameters.each { |parameter| append_parameter(parameter) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param option_name [Symbol, String]
|
47
|
+
# @return [void]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
# @since 0.2.0
|
51
|
+
def define_option(option_name, **options)
|
52
|
+
thread_safe do
|
53
|
+
option = build_attribute(option_name, **options)
|
54
|
+
prevent_intersection_with_already_defined_param(option)
|
55
|
+
append_option(option)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param option_names [Array<String, Symbol>]
|
60
|
+
# @return [void]
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
# @since 0.2.0
|
64
|
+
def define_options(*option_names)
|
65
|
+
thread_safe do
|
66
|
+
options = option_names.map do |option_name|
|
67
|
+
build_attribute(option_name).tap do |option|
|
68
|
+
prevent_intersection_with_already_defined_param(option)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
options.each { |option| append_option(option) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# @return [Class<SmartCore::Operation>]
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
# @since 0.2.0
|
82
|
+
attr_reader :operation_klass
|
83
|
+
|
84
|
+
# @return [Mutex]
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
# @since 0.2.0
|
88
|
+
attr_reader :definition_lock
|
89
|
+
|
90
|
+
# @param attribute_name [Symbol, String]
|
91
|
+
# @param attribute_options [Hash<Symbol,Any>]
|
92
|
+
# @return [SmartCore::Operation::Attribute]
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
# @since 0.2.0
|
96
|
+
def build_attribute(attribute_name, **attribute_options)
|
97
|
+
SmartCore::Operation::Attribute.new(attribute_name, **attribute_options)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param parameter [SmartCore::Operation::Attribute]
|
101
|
+
# @return [void]
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
# @since 0.2.0
|
105
|
+
def append_parameter(parameter)
|
106
|
+
operation_klass.__params__ << parameter
|
107
|
+
operation_klass.send(:attr_reader, parameter.name)
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param option [SmartCore::Operation::Attribute]
|
111
|
+
# @return [void]
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
# @since 0.2.0
|
115
|
+
def append_option(option)
|
116
|
+
operation_klass.__options__ << option
|
117
|
+
operation_klass.send(:attr_reader, option.name)
|
118
|
+
end
|
119
|
+
|
120
|
+
# @param parameter [SmartCore::Operation::Attribute]
|
121
|
+
# @return [void]
|
122
|
+
#
|
123
|
+
# @raise [SmartCore::Operation::OptionOverlapError]
|
124
|
+
#
|
125
|
+
# @api private
|
126
|
+
# @since 0.2.0
|
127
|
+
def prevent_intersection_with_already_defined_option(parameter)
|
128
|
+
if operation_klass.__options__.conflicts_with?(parameter)
|
129
|
+
raise(
|
130
|
+
SmartCore::Operation::OptionOverlapError,
|
131
|
+
"You have already defined option with :#{parameter.name} name"
|
132
|
+
)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# @param option [SmartCore::Operation::Attribute]
|
137
|
+
# @return [void]
|
138
|
+
#
|
139
|
+
# @raise [SmartCore::Operation::ParamOverlapError]
|
140
|
+
#
|
141
|
+
# @api private
|
142
|
+
# @since 0.2.0
|
143
|
+
def prevent_intersection_with_already_defined_param(option)
|
144
|
+
if operation_klass.__params__.conflicts_with?(option)
|
145
|
+
raise(
|
146
|
+
SmartCore::Operation::ParamOverlapError,
|
147
|
+
"You have already defined param with :#{option.name} name"
|
148
|
+
)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# @param block [Proc]
|
153
|
+
# @return [Any]
|
154
|
+
#
|
155
|
+
# @api private
|
156
|
+
# @since 0.2.0
|
157
|
+
def thread_safe(&block)
|
158
|
+
definition_lock.synchronize(&block)
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation::AttributeSet
|
6
|
+
# @since 0.2.0
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# @return [Hash<Symbol, SmartCore::Operation::Attribute>]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @since 0.2.0
|
13
|
+
attr_reader :attributes
|
14
|
+
|
15
|
+
# @return [void]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @since 0.2.0
|
19
|
+
def initialize
|
20
|
+
@attributes = {}
|
21
|
+
@access_lock = Mutex.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param attribute [SmartCore::Operation::Attribute]
|
25
|
+
# @return [void]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.2.0
|
29
|
+
def add_attribute(attribute)
|
30
|
+
thread_safe { attributes[attribute.name] = attribute }
|
31
|
+
end
|
32
|
+
alias_method :<<, :add_attribute
|
33
|
+
|
34
|
+
# @param attribute_set [SmartCore::Operation::AttributeSet]
|
35
|
+
# @return [void]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @sinec 0.2.0
|
39
|
+
def concat(attribute_set)
|
40
|
+
thread_safe { attributes.merge!(attribute_set.dup.attributes) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param attribute [SmartCore::Operation::Attribute]
|
44
|
+
# @return [Boolean]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 0.2.0
|
48
|
+
def conflicts_with?(attribute)
|
49
|
+
thread_safe { attributes.key?(attribute.name) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [SmartCore::Operation::AttributeSet]
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @since 0.2.0
|
56
|
+
def dup
|
57
|
+
thread_safe do
|
58
|
+
self.class.new.tap do |duplicate|
|
59
|
+
attributes.each_value do |attribute|
|
60
|
+
duplicate.add_attribute(attribute.dup)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Integer]
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
# @since 0.2.0
|
70
|
+
def size
|
71
|
+
thread_safe { attributes.size }
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Enumerable]
|
75
|
+
#
|
76
|
+
# @api private
|
77
|
+
# @since 0.2.0
|
78
|
+
def each(&block)
|
79
|
+
thread_safe { block_given? ? attributes.each_value(&block) : attributes.each_value }
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# @param block [Proc]
|
85
|
+
# @return [Any]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
# @since 0.2.0
|
89
|
+
def thread_safe(&block)
|
90
|
+
@access_lock.synchronize(&block)
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SmartCore::Operation
|
4
|
+
# @api public
|
5
|
+
# @since 0.2.0
|
6
|
+
Error = Class.new(StandardError)
|
7
|
+
|
8
|
+
# @api public
|
9
|
+
# @since 0.2.0
|
10
|
+
ArgumentError = Class.new(::ArgumentError)
|
11
|
+
|
12
|
+
# @api public
|
13
|
+
# @since 0.2.0
|
14
|
+
ParameterError = Class.new(ArgumentError)
|
15
|
+
|
16
|
+
# @api public
|
17
|
+
# @since 0.2.0
|
18
|
+
ParamOverlapError = Class.new(ParameterError)
|
19
|
+
|
20
|
+
# @api public
|
21
|
+
# @since 0.2.0
|
22
|
+
OptionError = Class.new(ArgumentError)
|
23
|
+
|
24
|
+
# @api public
|
25
|
+
# @since 0.2.0
|
26
|
+
OptionOverlapError = Class.new(OptionError)
|
27
|
+
|
28
|
+
# @api public
|
29
|
+
# @since 0.2.0
|
30
|
+
IncorrectAttributeNameError = Class.new(Error)
|
31
|
+
|
32
|
+
# @api public
|
33
|
+
# @since 0.2.0
|
34
|
+
ResultMethodIntersectionError = Class.new(Error)
|
35
|
+
|
36
|
+
# @api public
|
37
|
+
# @since 0.2.0
|
38
|
+
IncompatibleResultObjectKeyError = Class.new(Error)
|
39
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation::Failure < SmartCore::Operation::Result
|
6
|
+
# @return [Array<Symbol|Any>]
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
# @since 0.2.0
|
10
|
+
alias_method :errors, :__result_attributes__
|
11
|
+
|
12
|
+
# @param errors [Array<Symbol|Any>]
|
13
|
+
# @return [void]
|
14
|
+
#
|
15
|
+
# @api pubic
|
16
|
+
# @since 0.2.0
|
17
|
+
def initialize(*errors)
|
18
|
+
super(*errors)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @yield [nil]
|
22
|
+
# @return [Boolean]
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
# @since 0.2.0
|
26
|
+
def failure?
|
27
|
+
true.tap { yield(self) if block_given? }
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SmartCore::Operation
|
4
|
+
# @api private
|
5
|
+
# @since 0.2.0
|
6
|
+
module InitializationDSL
|
7
|
+
class << self
|
8
|
+
# @param base_klass [Class]
|
9
|
+
# @return [void]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
# @since 0.2.0
|
13
|
+
def included(base_klass) # rubocop:disable Metrics/AbcSize
|
14
|
+
base_klass.extend(DSLMethods)
|
15
|
+
base_klass.singleton_class.prepend(InitializationMethods)
|
16
|
+
|
17
|
+
base_klass.instance_variable_set(:@__attr_definer__, AttributeDefiner.new(base_klass))
|
18
|
+
base_klass.instance_variable_set(:@__params__, AttributeSet.new)
|
19
|
+
base_klass.instance_variable_set(:@__options__, AttributeSet.new)
|
20
|
+
|
21
|
+
base_klass.singleton_class.prepend(Module.new do
|
22
|
+
# @param child_klass [Class]
|
23
|
+
# @return [void]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
# @since 0.2.0
|
27
|
+
def inherited(child_klass)
|
28
|
+
child_klass.singleton_class.prepend(InitializationMethods)
|
29
|
+
|
30
|
+
child_klass.instance_variable_set(:@__attr_definer__, AttributeDefiner.new(child_klass))
|
31
|
+
child_klass.instance_variable_set(:@__params__, AttributeSet.new)
|
32
|
+
child_klass.instance_variable_set(:@__options__, AttributeSet.new)
|
33
|
+
|
34
|
+
child_klass.__params__.concat(__params__)
|
35
|
+
child_klass.__options__.concat(__options__)
|
36
|
+
|
37
|
+
super(child_klass)
|
38
|
+
end
|
39
|
+
end)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @api private
|
44
|
+
# @since 0.2.0
|
45
|
+
module InitializationMethods
|
46
|
+
# @param parameters [Any]
|
47
|
+
# @param options [Hash<Symbol,Any>]
|
48
|
+
# @return [Any]
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
# @since 0.2.0
|
52
|
+
def new(*parameters, **options)
|
53
|
+
allocate.tap do |object|
|
54
|
+
InstanceBuilder.call(object, self, parameters, options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @api private
|
60
|
+
# @since 0.2.0
|
61
|
+
module DSLMethods
|
62
|
+
# @param param_name [String, Symbol]
|
63
|
+
# @return [void]
|
64
|
+
#
|
65
|
+
# @api public
|
66
|
+
# @since 0.2.0
|
67
|
+
def param(param_name)
|
68
|
+
__attr_definer__.define_param(param_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param param_names [Array<String, Symbol>]
|
72
|
+
# @return [void]
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
# @since 0.2.0
|
76
|
+
def params(*param_names)
|
77
|
+
__attr_definer__.define_params(*param_names)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param option_name [String, Symbol]
|
81
|
+
# @param options [Hash<Symbol,Any>]
|
82
|
+
# @return [void]
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
# @since 0.2.0
|
86
|
+
def option(option_name, **options)
|
87
|
+
__attr_definer__.define_option(option_name, **options)
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param option_names [Array<String, Symbol>]
|
91
|
+
# @return [void]
|
92
|
+
#
|
93
|
+
# @api public
|
94
|
+
# @since 0.2.0
|
95
|
+
def options(*option_names)
|
96
|
+
__attr_definer__.define_options(*option_names)
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [SmartCore::Operation::AttributeSet]
|
100
|
+
#
|
101
|
+
# @api private
|
102
|
+
# @since 0.2.0
|
103
|
+
def __params__
|
104
|
+
@__params__
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [SmartCore::Operation::AttributeSet]
|
108
|
+
#
|
109
|
+
# @api private
|
110
|
+
# @since 0.2.0
|
111
|
+
def __options__
|
112
|
+
@__options__
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [SmartCore::Operation::AttributeDefiner]
|
116
|
+
#
|
117
|
+
# @api private
|
118
|
+
# @since 0.2.0
|
119
|
+
def __attr_definer__
|
120
|
+
@__attr_definer__
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation::InstanceBuilder
|
6
|
+
class << self
|
7
|
+
# @param operation_object [SmartCore::Operation]
|
8
|
+
# @param operation_klass [Class<SmartCore::Operation>]
|
9
|
+
# @param parameters [Array<Any>]
|
10
|
+
# @param options [Hash<Symbol,Any>]
|
11
|
+
# @return [SmartCore::Operation]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @since 0.2.0
|
15
|
+
def call(operation_object, operation_klass, parameters, options)
|
16
|
+
new(operation_object, operation_klass, parameters, options).call
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param operation_object [SmartCore::Operation]
|
21
|
+
# @param operation_klass [Class<SmartCore::Operation>]
|
22
|
+
# @param parameters [Array<Any>]
|
23
|
+
# @param options [Hash<Symbol,Any>]
|
24
|
+
# @return [void]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @since 0.2.0
|
28
|
+
def initialize(operation_object, operation_klass, parameters, options)
|
29
|
+
@operation_object = operation_object
|
30
|
+
@operation_klass = operation_klass
|
31
|
+
@parameters = parameters
|
32
|
+
@options = options
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [SmartCore::Operation]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @since 0.2.0
|
39
|
+
def call
|
40
|
+
operation_object.tap do
|
41
|
+
prevent_parameters_incomparability
|
42
|
+
initialize_parameters
|
43
|
+
initialize_options
|
44
|
+
call_original_methods
|
45
|
+
make_operation_caller_yieldable
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# @return [SmartCore::Operation]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
# @since 0.2.0
|
55
|
+
attr_reader :operation_object
|
56
|
+
|
57
|
+
# @return [Class<SmartCore::Operation>]
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
# @since 0.2.0
|
61
|
+
attr_reader :operation_klass
|
62
|
+
|
63
|
+
# @return [Array<Any>]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
# @since 0.2.0
|
67
|
+
attr_reader :parameters
|
68
|
+
|
69
|
+
# @return [Hash<Symbol,Any>]
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
# @since 0.2.0
|
73
|
+
attr_reader :options
|
74
|
+
|
75
|
+
# An array of the required option attribute names (parameters without default values)
|
76
|
+
#
|
77
|
+
# @return [Array<Symbol>]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
# @since 0.2.0
|
81
|
+
def required_options
|
82
|
+
operation_klass.__options__.each.reject(&:has_default_value?).map(&:name)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Integer]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
# @since 0.2.0
|
89
|
+
def required_attributes_count
|
90
|
+
operation_klass.__params__.size
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [void]
|
94
|
+
#
|
95
|
+
# @raise [SmartCore::Operation::AttributeError]
|
96
|
+
# @raise [SmartCore::Operation::ParameterError]
|
97
|
+
# @raise [SmartCore::Operation::OptionError]
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
# @since 0.2.0
|
101
|
+
def prevent_parameters_incomparability
|
102
|
+
raise(
|
103
|
+
SmartCore::Operation::ParameterError,
|
104
|
+
"Wrong number of parameters " \
|
105
|
+
"(given #{parameters.size}, expected #{required_attributes_count})"
|
106
|
+
) unless parameters.size == required_attributes_count
|
107
|
+
|
108
|
+
missing_options = required_options.reject { |option| options.key?(option) }
|
109
|
+
|
110
|
+
raise(
|
111
|
+
SmartCore::Operation::OptionError,
|
112
|
+
"Missing options: :#{missing_options.join(', :')}"
|
113
|
+
) unless missing_options.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return [void]
|
117
|
+
#
|
118
|
+
# @api private
|
119
|
+
# @since 0.2.0
|
120
|
+
def initialize_parameters
|
121
|
+
parameter_names = operation_klass.__params__.map(&:name)
|
122
|
+
parameter_pairs = Hash[parameter_names.zip(parameters)]
|
123
|
+
|
124
|
+
parameter_pairs.each_pair do |parameter_name, parameter_value|
|
125
|
+
operation_object.instance_variable_set("@#{parameter_name}", parameter_value)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# @return [void]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
# @since 0.2.0
|
133
|
+
def initialize_options
|
134
|
+
operation_klass.__options__.each do |option|
|
135
|
+
option_name = option.name
|
136
|
+
option_value = options.fetch(option_name) { option.default_value }
|
137
|
+
|
138
|
+
operation_object.instance_variable_set("@#{option_name}", option_value)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# @return [void]
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
# @since 0.2.0
|
146
|
+
def call_original_methods
|
147
|
+
operation_object.send(:initialize, *parameters, **options)
|
148
|
+
end
|
149
|
+
|
150
|
+
# @return [void]
|
151
|
+
#
|
152
|
+
# @api private
|
153
|
+
# @since 0.2.0
|
154
|
+
def make_operation_caller_yieldable
|
155
|
+
operation_object.singleton_class.prepend(Module.new do
|
156
|
+
def call
|
157
|
+
super.tap { |result| yield(result) if block_given? }
|
158
|
+
end
|
159
|
+
end)
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation::Result
|
6
|
+
# @return [Array<Any>]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.2.0
|
10
|
+
attr_reader :__result_attributes__
|
11
|
+
|
12
|
+
# @return [Hash<Symbol,Any>]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.2.0
|
16
|
+
attr_reader :__result_options__
|
17
|
+
|
18
|
+
# @param result_attributes [Array<Any>]
|
19
|
+
# @param result_options [Hash<Symbol,Any>]
|
20
|
+
# @return [void]
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
# @since 0.2.0
|
24
|
+
def initialize(*result_attributes, **result_options)
|
25
|
+
@__result_attributes__ = result_attributes
|
26
|
+
@__result_options__ = result_options
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean]
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
# @since 0.2.0
|
33
|
+
def success?
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Boolean]
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
# @since 0.2.0
|
41
|
+
def failure?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation::Success < SmartCore::Operation::Result
|
6
|
+
# @param result_options [Hash<Symbol,Any>]
|
7
|
+
# @return [void]
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
# @since 0.2.0
|
11
|
+
def initialize(**result_options)
|
12
|
+
__prevent_method_overlapping__(result_options)
|
13
|
+
super(**result_options) # NOTE: initialize result object
|
14
|
+
__define_virtual_result_data_accessors__(result_options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @yield [nil]
|
18
|
+
# @return [Boolean]
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
# @since 0.2.0
|
22
|
+
def success?
|
23
|
+
true.tap { yield(self) if block_given? }
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @param result_options [Hash<Symbol,Any>]
|
29
|
+
# @return [void]
|
30
|
+
#
|
31
|
+
# @raise [SmartCore::Operation::IncompatibleResultKeyError]
|
32
|
+
# @raise [SmartCore::Operation::ResultMethodIntersectionError]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
# @since 0.2.0
|
36
|
+
def __prevent_method_overlapping__(result_options)
|
37
|
+
overlappings = result_options.each_key.each_with_object([]) do |key, overlap|
|
38
|
+
overlap << key if self.class.__core_methods__.include?(key)
|
39
|
+
end
|
40
|
+
|
41
|
+
raise(
|
42
|
+
SmartCore::Operation::ResultMethodIntersectionError,
|
43
|
+
"Result keys can not overlap core methods " \
|
44
|
+
"(overlapping keys: #{overlappings.join(', ')})."
|
45
|
+
) if overlappings.any?
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param result_options [Hash<Symbol,Any>]
|
49
|
+
# @return [void]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
# @since 0.2.0
|
53
|
+
def __define_virtual_result_data_accessors__(result_options)
|
54
|
+
result_options.each_key do |result_attribute_name|
|
55
|
+
define_singleton_method(result_attribute_name) do
|
56
|
+
__result_options__[result_attribute_name]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
core_methods = (
|
62
|
+
instance_methods(false) | private_instance_methods(false) |
|
63
|
+
superclass.instance_methods(false) | superclass.private_instance_methods(false)
|
64
|
+
).freeze
|
65
|
+
|
66
|
+
# @return [Array<Symbol>]
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
# @since 0.2.0
|
70
|
+
define_singleton_method(:__core_methods__) { core_methods }
|
71
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Operation
|
6
|
+
require_relative 'operation/exceptions'
|
7
|
+
require_relative 'operation/attribute'
|
8
|
+
require_relative 'operation/attribute_set'
|
9
|
+
require_relative 'operation/result'
|
10
|
+
require_relative 'operation/success'
|
11
|
+
require_relative 'operation/failure'
|
12
|
+
require_relative 'operation/instance_builder'
|
13
|
+
require_relative 'operation/attribute_definer'
|
14
|
+
require_relative 'operation/initialization_dsl'
|
15
|
+
|
16
|
+
# @since 0.2.0
|
17
|
+
include InitializationDSL
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# @param arguments [Any]
|
21
|
+
# @param options [Hash<Symbol,Any>]
|
22
|
+
# @param block [Proc]
|
23
|
+
# @return [SmartCore::Operation::Success, SmartCore::Operation::Failure]
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
# @since 0.2.0
|
27
|
+
def call(*arguments, **options, &block)
|
28
|
+
new(*arguments, **options).call(&block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [void]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
# @since 0.2.0
|
36
|
+
def initialize(*, **); end
|
37
|
+
|
38
|
+
# @return [SmartCore::Operation::Success, SmartCore::Operation::Failure]
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
# @since 0.2.0
|
42
|
+
def call
|
43
|
+
Success()
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# @param result_data [Hash<Symbol,Any>]
|
49
|
+
# @return [SmartCore::Operation::Success]
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
# @since 0.2.0
|
53
|
+
def Success(**result_data) # rubocop:disable Naming/MethodName
|
54
|
+
SmartCore::Operation::Success.new(**result_data)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param errors [Array<Symbol|Any>]
|
58
|
+
# @return [SmartCore::Operation::Failure]
|
59
|
+
#
|
60
|
+
# @api public
|
61
|
+
# @since 0.2.0
|
62
|
+
def Failure(*errors) # rubocop:disable Naming/MethodName
|
63
|
+
SmartCore::Operation::Failure.new(*errors)
|
64
|
+
end
|
65
|
+
end
|
@@ -6,7 +6,7 @@ class SmartCore::Validator::AttributeSet
|
|
6
6
|
# @since 0.1.0
|
7
7
|
include Enumerable
|
8
8
|
|
9
|
-
# @return [Hash<Symbol,
|
9
|
+
# @return [Hash<Symbol,SmartCore::Validator::Attribute>]
|
10
10
|
#
|
11
11
|
# @api private
|
12
12
|
# @since 0.1.0
|
@@ -21,7 +21,7 @@ class SmartCore::Validator::AttributeSet
|
|
21
21
|
@access_lock = Mutex.new
|
22
22
|
end
|
23
23
|
|
24
|
-
# @param attribute [
|
24
|
+
# @param attribute [SmartCore::Validator::Attribute]
|
25
25
|
# @return [void]
|
26
26
|
#
|
27
27
|
# @api private
|
@@ -31,7 +31,7 @@ class SmartCore::Validator
|
|
31
31
|
# @param attribute_name [String, Symbol]
|
32
32
|
# @return [void]
|
33
33
|
#
|
34
|
-
# @api
|
34
|
+
# @api public
|
35
35
|
# @since 0.1.0
|
36
36
|
def attribute(attribute_name, default: nil)
|
37
37
|
attribute = SmartCore::Validator::Attribute.new(attribute_name, default)
|
@@ -86,7 +86,7 @@ class SmartCore::Validator
|
|
86
86
|
#
|
87
87
|
# @see SmartCore::Validator::Commands::ValidateWith
|
88
88
|
#
|
89
|
-
# @api
|
89
|
+
# @api public
|
90
90
|
# @since 0.1.0
|
91
91
|
def validate_with(validating_klass, &nested_validations)
|
92
92
|
commands << Commands::ValidateWith.new(validating_klass, nested_validations)
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.2.0
|
5
|
+
class SmartCore::Validator
|
6
|
+
class InstanceBuilder
|
7
|
+
class << self
|
8
|
+
# @param validator_object [SmartCore::Validator]
|
9
|
+
# @param validator_klass [Class<SmartCore::Validator>]
|
10
|
+
# @param arguments [Array<Any>]
|
11
|
+
# @param options [Hash<Symbol,Any>]
|
12
|
+
# @param block [Proc]
|
13
|
+
# @return [SmartCore::Validator]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
# @since 0.2.0
|
17
|
+
def call(validator_object, validator_klass, arguments, options, block)
|
18
|
+
new(validator_object, validator_klass, arguments, options, block).call
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param validator_object [SmartCore::Validator]
|
23
|
+
# @param validator_klass [Class<SmartCore::Validator>]
|
24
|
+
# @param arguments [Array<Any>]
|
25
|
+
# @param options [Hash<Symbol,Any>]
|
26
|
+
# @param block [Proc]
|
27
|
+
# @return [void]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
# @since 0.2.0
|
31
|
+
def initialize(validator_object, validator_klass, arguments, options, block)
|
32
|
+
@validator_object = validator_object
|
33
|
+
@validator_klass = validator_klass
|
34
|
+
@arguments = arguments
|
35
|
+
@options = options
|
36
|
+
@block = block
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [SmartCore::Validator]
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
# @since 0.2.0
|
43
|
+
def call
|
44
|
+
validator_object.tap do
|
45
|
+
initialize_core_attributes
|
46
|
+
initialize_custom_attributes
|
47
|
+
invoke_original_methods
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# @return [SmartCore::Validator]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
# @since 0.2.0
|
57
|
+
attr_reader :validator_object
|
58
|
+
|
59
|
+
# @return [Class<SmartCore::Validator>]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
# @since 0.2.0
|
63
|
+
attr_reader :validator_klass
|
64
|
+
|
65
|
+
# @return [Array<Any>]
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
# @since 0.2.0
|
69
|
+
attr_reader :arguments
|
70
|
+
|
71
|
+
# @return [Hash<Symbol,Any>]
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
# @since 0.2.0
|
75
|
+
attr_reader :options
|
76
|
+
|
77
|
+
# @return [Proc]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
# @since 0.2.0
|
81
|
+
attr_reader :block
|
82
|
+
|
83
|
+
# @return [void]
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
# @since 0.2.0
|
87
|
+
def initialize_core_attributes
|
88
|
+
validator_object.instance_variable_set(:@__validation_errors__, ErrorSet.new)
|
89
|
+
validator_object.instance_variable_set(:@__invokation_lock__, Mutex.new)
|
90
|
+
validator_object.instance_variable_set(:@__access_lock__, Mutex.new)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [void]
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
# @since 0.2.0
|
97
|
+
def initialize_custom_attributes
|
98
|
+
validator_klass.attributes.each do |attribute|
|
99
|
+
attribute_name = attribute.name
|
100
|
+
attribute_value = options.fetch(attribute_name) { attribute.default_value }
|
101
|
+
|
102
|
+
validator_object.instance_variable_set("@#{attribute_name}", attribute_value)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [void]
|
107
|
+
#
|
108
|
+
# @api private
|
109
|
+
# @since 0.2.0
|
110
|
+
def invoke_original_methods
|
111
|
+
validator_object.send(:initialize, *arguments, **options, &block)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/smart_core/validator.rb
CHANGED
@@ -11,37 +11,22 @@ class SmartCore::Validator
|
|
11
11
|
require_relative 'validator/invoker'
|
12
12
|
require_relative 'validator/commands'
|
13
13
|
require_relative 'validator/dsl'
|
14
|
+
require_relative 'validator/instance_builder'
|
14
15
|
|
15
16
|
# @since 0.1.0
|
16
17
|
extend DSL
|
17
18
|
|
18
19
|
class << self
|
19
|
-
# @param
|
20
|
-
# @param options [Hash<Symbol,
|
20
|
+
# @param arguments [Any]
|
21
|
+
# @param options [Hash<Symbol,Object>]
|
22
|
+
# @param block [Proc]
|
21
23
|
# @return [void]
|
22
24
|
#
|
23
25
|
# @api public
|
24
26
|
# @since 0.1.0
|
25
|
-
def new(*arguments, **options)
|
27
|
+
def new(*arguments, **options, &block)
|
26
28
|
allocate.tap do |object|
|
27
|
-
|
28
|
-
object.instance_variable_set(:@__invokation_lock__, Mutex.new)
|
29
|
-
object.instance_variable_set(:@__access_lock__, Mutex.new)
|
30
|
-
|
31
|
-
attributes.each do |attribute|
|
32
|
-
attribute_name = attribute.name
|
33
|
-
|
34
|
-
attribute_value =
|
35
|
-
if options.key?(attribute_name)
|
36
|
-
options[attribute_name]
|
37
|
-
else
|
38
|
-
attribute.default_value
|
39
|
-
end
|
40
|
-
|
41
|
-
object.instance_variable_set("@#{attribute_name}", attribute_value)
|
42
|
-
end
|
43
|
-
|
44
|
-
object.send(:initialize, *arguments, **options)
|
29
|
+
InstanceBuilder.call(object, self, arguments, options, block)
|
45
30
|
end
|
46
31
|
end
|
47
32
|
end
|
@@ -56,7 +41,7 @@ class SmartCore::Validator
|
|
56
41
|
#
|
57
42
|
# @api public
|
58
43
|
# @since 0.1.0
|
59
|
-
def initialize(*,
|
44
|
+
def initialize(*, **, &block); end
|
60
45
|
|
61
46
|
# @return [Boolean]
|
62
47
|
#
|
@@ -91,7 +76,7 @@ class SmartCore::Validator
|
|
91
76
|
end
|
92
77
|
end
|
93
78
|
|
94
|
-
# @return [Hash<Symbol,
|
79
|
+
# @return [Hash<Symbol,Object>]
|
95
80
|
#
|
96
81
|
# @api private
|
97
82
|
# @since 0.1.0
|
data/lib/smart_core/version.rb
CHANGED
data/lib/smart_core.rb
CHANGED
data/smart_core.gemspec
CHANGED
@@ -26,9 +26,10 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
spec.add_development_dependency 'coveralls', '~> 0.8.22'
|
28
28
|
spec.add_development_dependency 'simplecov', '~> 0.16.1'
|
29
|
-
spec.add_development_dependency 'armitage-rubocop', '~> 0.
|
29
|
+
spec.add_development_dependency 'armitage-rubocop', '~> 0.17.0'
|
30
30
|
spec.add_development_dependency 'rspec', '~> 3.8.0'
|
31
|
-
|
32
|
-
spec.add_development_dependency '
|
33
|
-
spec.add_development_dependency '
|
31
|
+
|
32
|
+
spec.add_development_dependency 'bundler'
|
33
|
+
spec.add_development_dependency 'rake'
|
34
|
+
spec.add_development_dependency 'pry'
|
34
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rustam Ibragimov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.17.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.17.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,44 +70,44 @@ dependencies:
|
|
70
70
|
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rake
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: pry
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0
|
103
|
+
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0
|
110
|
+
version: '0'
|
111
111
|
description: "(in active development) A set of common abstractions"
|
112
112
|
email:
|
113
113
|
- iamdaiver@icloud.com
|
@@ -128,6 +128,16 @@ files:
|
|
128
128
|
- bin/console
|
129
129
|
- bin/setup
|
130
130
|
- lib/smart_core.rb
|
131
|
+
- lib/smart_core/operation.rb
|
132
|
+
- lib/smart_core/operation/attribute.rb
|
133
|
+
- lib/smart_core/operation/attribute_definer.rb
|
134
|
+
- lib/smart_core/operation/attribute_set.rb
|
135
|
+
- lib/smart_core/operation/exceptions.rb
|
136
|
+
- lib/smart_core/operation/failure.rb
|
137
|
+
- lib/smart_core/operation/initialization_dsl.rb
|
138
|
+
- lib/smart_core/operation/instance_builder.rb
|
139
|
+
- lib/smart_core/operation/result.rb
|
140
|
+
- lib/smart_core/operation/success.rb
|
131
141
|
- lib/smart_core/validator.rb
|
132
142
|
- lib/smart_core/validator/attribute.rb
|
133
143
|
- lib/smart_core/validator/attribute_set.rb
|
@@ -141,6 +151,7 @@ files:
|
|
141
151
|
- lib/smart_core/validator/dsl.rb
|
142
152
|
- lib/smart_core/validator/error_set.rb
|
143
153
|
- lib/smart_core/validator/exceptions.rb
|
154
|
+
- lib/smart_core/validator/instance_builder.rb
|
144
155
|
- lib/smart_core/validator/invoker.rb
|
145
156
|
- lib/smart_core/version.rb
|
146
157
|
- smart_core.gemspec
|
@@ -163,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
174
|
- !ruby/object:Gem::Version
|
164
175
|
version: '0'
|
165
176
|
requirements: []
|
166
|
-
rubygems_version: 3.0.
|
177
|
+
rubygems_version: 3.0.2
|
167
178
|
signing_key:
|
168
179
|
specification_version: 4
|
169
180
|
summary: "(in active development) A set of common abstractions"
|