as_method 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -0
- data/lib/as_method/configuration.rb +39 -0
- data/lib/as_method/generate_module.rb +81 -0
- data/lib/as_method/get_service_object.rb +1 -1
- data/lib/as_method/helpers.rb +1 -25
- data/lib/as_method/method_name/validate.rb +4 -2
- data/lib/as_method/validate_service_object.rb +1 -1
- data/lib/as_method/version.rb +1 -1
- data/lib/as_method.rb +11 -0
- metadata +4 -4
- data/lib/as_method/find_or_define_module.rb +0 -36
- data/lib/as_method/templates/includable_module.rb.erb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c681302ea5c6e934c05ec30e0b38cdbfd47d18edd7018eccf845f3f806dc0f10
|
4
|
+
data.tar.gz: 1884622331ca3549ec834b64d314b6b322707e0264357e931eaff341466a55ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 597472c6887db1c5a53d95f09d152b9cdef12734ba04f390707fef324b4e45d373caab26bd7099b89ac50d0328ed6c6455b9e91316055f8c2ec7900f4f6df965
|
7
|
+
data.tar.gz: abee934d4c0da5f4e3fa1f85ed9a74107c76fc1a61e0c597801944e725bde6e5a81fd1405f642fd6ae65db87abe431a41afb8354edb6a4f76d730f07ccd2bd23
|
data/README.md
CHANGED
@@ -85,3 +85,22 @@ However, be aware of the following points:
|
|
85
85
|
- Dependencies are somewhat implicit as they are defined in a separate file.
|
86
86
|
- Object constructors get modified, as explained in detail [here](https://dry-rb.org/gems/dry-auto_inject/0.6/how-does-it-work/)."
|
87
87
|
- Resulting methods don't invoke 'call' on objects, they return callables instead.
|
88
|
+
|
89
|
+
## Benchmark results:
|
90
|
+
|
91
|
+
1000 iterations of a Test Service Object call. It does nothing but calls two other service objects, they call two other service objects each, and so on. (Depth 11 levels, 2049 unique classes, 4095 calls).
|
92
|
+
|
93
|
+
```
|
94
|
+
user system total real memory
|
95
|
+
pure Ruby 3.689327 0.047354 3.736681 ( 3.744128) 27440
|
96
|
+
as_method 6.219795 0.038531 6.258326 ( 6.298066) 37652
|
97
|
+
dry-auto_inject 22.121456 0.119032 22.240488 (22.467116) 55408
|
98
|
+
```
|
99
|
+
|
100
|
+
Ruby version: 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin22]
|
101
|
+
|
102
|
+
## TODO:
|
103
|
+
|
104
|
+
- extra spec for circular dependencies (SO1 includes SO2, SO2 includes SO1, directly and via modules)
|
105
|
+
- extra spec for Base Service Object class that includes another SO
|
106
|
+
- spec for AsMethod::Allow (extending a base class and a module)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable
|
4
|
+
module AsMethod
|
5
|
+
|
6
|
+
Configuration = Struct.new(
|
7
|
+
:object_selector,
|
8
|
+
:object_validator,
|
9
|
+
:method_name_generator,
|
10
|
+
:method_name_validator,
|
11
|
+
:module_template
|
12
|
+
)
|
13
|
+
|
14
|
+
DEFAULT_CONFIGURATION = Configuration.new.tap do |config|
|
15
|
+
config.object_selector = ->(object) { object }
|
16
|
+
config.object_validator = ValidateServiceObject
|
17
|
+
config.method_name_generator = MethodName::GenerateFromClassName
|
18
|
+
config.method_name_validator = MethodName::Validate
|
19
|
+
config.module_template = <<~RUBY
|
20
|
+
module <%= module_name %>
|
21
|
+
|
22
|
+
def self.object
|
23
|
+
::<%= object %>
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.ruby2_keywords(*); end if RUBY_VERSION < "2.7"
|
27
|
+
|
28
|
+
ruby2_keywords def <%= method_name %>(*args, &block)
|
29
|
+
::<%= object %>.call(*args, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
<%= method_access %> :<%= method_name %>
|
33
|
+
|
34
|
+
end
|
35
|
+
RUBY
|
36
|
+
end.freeze
|
37
|
+
|
38
|
+
end
|
39
|
+
# rubocop:enable
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module AsMethod
|
6
|
+
class GenerateModule
|
7
|
+
def self.call(**args)
|
8
|
+
new(**args).call
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(object:, method_name: nil, method_access: :private)
|
12
|
+
@_object = object
|
13
|
+
@_method_name = method_name
|
14
|
+
@_method_access = method_access
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
instance_eval(generate_module_source_code) unless module_exists?
|
19
|
+
|
20
|
+
includable_module
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def includable_module
|
26
|
+
Object.const_get(module_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_module_source_code
|
30
|
+
payload = {
|
31
|
+
object: object,
|
32
|
+
module_name: module_name,
|
33
|
+
method_name: method_name,
|
34
|
+
method_access: method_access,
|
35
|
+
}
|
36
|
+
::ERB.new(config.module_template).result_with_hash(payload)
|
37
|
+
end
|
38
|
+
|
39
|
+
def object
|
40
|
+
@object ||= validate_object!(select_object(@_object))
|
41
|
+
end
|
42
|
+
|
43
|
+
def select_object(object)
|
44
|
+
config.object_selector.call(object)
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_object!(object)
|
48
|
+
config.object_validator.call(object)
|
49
|
+
end
|
50
|
+
|
51
|
+
def generate_method_name(object_name)
|
52
|
+
config.method_name_generator.call(object_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_method_name!(method_name)
|
56
|
+
config.method_name_validator.call(method_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_access
|
60
|
+
@method_access ||= @_method_access.to_s.tap do |access|
|
61
|
+
raise ArgumentError unless %w[private public protected].include?(access)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def method_name
|
66
|
+
@method_name ||= validate_method_name!(@_method_name&.to_s || generate_method_name(object.name))
|
67
|
+
end
|
68
|
+
|
69
|
+
def module_name
|
70
|
+
@module_name ||= "::#{object.name}::As#{method_access.capitalize}Method__#{ModuleName::GenerateFromSnakeCase.call(method_name)}" # rubocop:disable Layout/LineLength
|
71
|
+
end
|
72
|
+
|
73
|
+
def module_exists?
|
74
|
+
Object.const_defined?(module_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def config
|
78
|
+
AsMethod.config
|
79
|
+
end
|
80
|
+
end # ... GenerateModule
|
81
|
+
end # ... AsMethod
|
@@ -54,7 +54,7 @@ module AsMethod
|
|
54
54
|
raise NameError, format(AMBIGUOUS_NAME_ERROR_MSG, @obj.inspect, @method_name.inspect)
|
55
55
|
end
|
56
56
|
|
57
|
-
(cmethod || imethod).owner.
|
57
|
+
(cmethod || imethod).owner.object
|
58
58
|
end
|
59
59
|
end # ... GetServiceObject
|
60
60
|
end # ... AsMethod
|
data/lib/as_method/helpers.rb
CHANGED
@@ -14,34 +14,10 @@ module AsMethod
|
|
14
14
|
GetServiceObject.call(self, method_name)
|
15
15
|
end
|
16
16
|
|
17
|
-
# rubocop:disable Metrics/AbcSize
|
18
17
|
def as_method(object, name: nil, access: :private)
|
19
|
-
|
20
|
-
|
21
|
-
ValidateServiceObject.call(object)
|
22
|
-
|
23
|
-
# generate method name from object, if none given
|
24
|
-
method_name = name&.to_s || MethodName::GenerateFromClassName.call(object.name)
|
25
|
-
|
26
|
-
MethodName::Validate.call(method_name)
|
27
|
-
|
28
|
-
module_name = "::#{object.name}::As#{access.capitalize}Method__#{ModuleName::GenerateFromSnakeCase.call(method_name)}" # rubocop:disable Layout/LineLength
|
29
|
-
|
30
|
-
# define module, or return existing module:
|
31
|
-
includable_module = FindOrDefineModule.call \
|
32
|
-
module_template: File.read(File.join(__dir__, "templates", "includable_module.rb.erb")),
|
33
|
-
module_name: module_name,
|
34
|
-
vars: {
|
35
|
-
object: object,
|
36
|
-
method_name: method_name,
|
37
|
-
method_access: access,
|
38
|
-
}
|
39
|
-
|
40
|
-
# return generated module ready for inclusion:
|
18
|
+
includable_module = GenerateModule.call(object: object, method_name: name, method_access: access)
|
41
19
|
included_modules.include?(includable_module) ? Kernel : includable_module
|
42
20
|
end
|
43
|
-
# rubocop:enable Metrics/AbcSize
|
44
|
-
|
45
21
|
end # ... ClassMethods
|
46
22
|
end # ... Helpers
|
47
23
|
end # ... AsMethod
|
@@ -9,8 +9,10 @@ module AsMethod
|
|
9
9
|
SPECIAL_NAMES = %w{[] ! ~ + ** - * / % << >> & | ^ < <= >= > == === != =~ !~ <=>}.freeze
|
10
10
|
|
11
11
|
def self.call(name)
|
12
|
-
|
13
|
-
|
12
|
+
fail ArgumentError unless name.is_a?(String)
|
13
|
+
|
14
|
+
return name if name =~ REGULAR_NAME_REGEX
|
15
|
+
return name if SPECIAL_NAMES.include?(name)
|
14
16
|
|
15
17
|
fail ArgumentError, "invalid method name #{name.inspect}"
|
16
18
|
end
|
@@ -8,7 +8,7 @@ module AsMethod
|
|
8
8
|
fail TypeError, "#{module_or_class} must be a Class or a Module"
|
9
9
|
end
|
10
10
|
|
11
|
-
return if module_or_class.respond_to?(:call)
|
11
|
+
return module_or_class if module_or_class.respond_to?(:call)
|
12
12
|
|
13
13
|
fail NoMethodError, "Expected #{module_or_class} to respond to #call method"
|
14
14
|
end
|
data/lib/as_method/version.rb
CHANGED
data/lib/as_method.rb
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
module AsMethod
|
4
4
|
autoload :Allow, "as_method/allow"
|
5
|
+
autoload :Configuration, "as_method/configuration"
|
6
|
+
autoload :DEFAULT_CONFIGURATION, "as_method/configuration"
|
5
7
|
autoload :Helpers, "as_method/helpers"
|
6
8
|
autoload :FindOrDefineModule, "as_method/find_or_define_module"
|
7
9
|
autoload :ValidateServiceObject, "as_method/validate_service_object"
|
10
|
+
autoload :GenerateModule, "as_method/generate_module"
|
8
11
|
autoload :GetServiceObject, "as_method/get_service_object"
|
9
12
|
autoload :VERSION, "as_method/version"
|
10
13
|
|
@@ -18,4 +21,12 @@ module AsMethod
|
|
18
21
|
autoload :GenerateFromClassName, "as_method/method_name/generate_from_class_name"
|
19
22
|
end
|
20
23
|
|
24
|
+
def self.configure
|
25
|
+
yield(config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.config
|
29
|
+
@config ||= DEFAULT_CONFIGURATION.dup
|
30
|
+
end
|
31
|
+
|
21
32
|
end # ... AsMethod
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: as_method
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Gorodulin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Bring Service Objects to your classes as methods.
|
14
14
|
email:
|
@@ -22,7 +22,8 @@ files:
|
|
22
22
|
- README.md
|
23
23
|
- lib/as_method.rb
|
24
24
|
- lib/as_method/allow.rb
|
25
|
-
- lib/as_method/
|
25
|
+
- lib/as_method/configuration.rb
|
26
|
+
- lib/as_method/generate_module.rb
|
26
27
|
- lib/as_method/get_service_object.rb
|
27
28
|
- lib/as_method/helpers.rb
|
28
29
|
- lib/as_method/method_name/generate_from_class_name.rb
|
@@ -30,7 +31,6 @@ files:
|
|
30
31
|
- lib/as_method/module_name/generate_from_snake_case.rb
|
31
32
|
- lib/as_method/module_name/strip_namespace.rb
|
32
33
|
- lib/as_method/setup.rb
|
33
|
-
- lib/as_method/templates/includable_module.rb.erb
|
34
34
|
- lib/as_method/validate_service_object.rb
|
35
35
|
- lib/as_method/version.rb
|
36
36
|
homepage: https://github.com/gorodulin/as_method
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "erb"
|
4
|
-
|
5
|
-
module AsMethod
|
6
|
-
class FindOrDefineModule
|
7
|
-
def self.call(**args)
|
8
|
-
new.call(**args)
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(module_template:, module_name:, vars:)
|
12
|
-
@vars = vars
|
13
|
-
@module_name = module_name
|
14
|
-
@module_template = module_template
|
15
|
-
|
16
|
-
instance_eval(generate_module_source_code) unless module_exists?
|
17
|
-
|
18
|
-
includable_module
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def generate_module_source_code
|
24
|
-
payload = @vars.merge(module_name: @module_name)
|
25
|
-
::ERB.new(@module_template).result_with_hash(payload)
|
26
|
-
end
|
27
|
-
|
28
|
-
def includable_module
|
29
|
-
Object.const_get(@module_name)
|
30
|
-
end
|
31
|
-
|
32
|
-
def module_exists?
|
33
|
-
Object.const_defined?(@module_name)
|
34
|
-
end
|
35
|
-
end # ... FindOrDefineModule
|
36
|
-
end # ... AsMethod
|
@@ -1,15 +0,0 @@
|
|
1
|
-
|
2
|
-
module <%= module_name %>
|
3
|
-
|
4
|
-
@_injectable_method = "<%= method_name %>"
|
5
|
-
@_injectable_object = <%= object %>
|
6
|
-
|
7
|
-
def self.ruby2_keywords(*); end if RUBY_VERSION < "2.7"
|
8
|
-
|
9
|
-
ruby2_keywords def <%= method_name %>(*args, &block)
|
10
|
-
::<%= object %>.call(*args, &block)
|
11
|
-
end
|
12
|
-
|
13
|
-
<%= method_access %> :<%= method_name %>
|
14
|
-
|
15
|
-
end
|