dry-core 0.6.0 → 0.8.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/CHANGELOG.md +42 -1
- data/README.md +5 -4
- data/dry-core.gemspec +2 -2
- data/lib/dry/core/basic_object.rb +144 -0
- data/lib/dry/core/cache.rb +4 -3
- data/lib/dry/core/class_attributes.rb +2 -2
- data/lib/dry/core/class_builder.rb +2 -4
- data/lib/dry/core/constants.rb +2 -2
- data/lib/dry/core/deprecations.rb +17 -13
- data/lib/dry/core/equalizer.rb +2 -1
- data/lib/dry/core/memoizable.rb +163 -30
- data/lib/dry/core/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f8c83e96398be4753d6ac800d164612021350dcabb3b2cdd3caa448984bb6cc
|
4
|
+
data.tar.gz: 1fa35af111908ebf221e6c494b53c253172df017a5ffcb2f4fe16ff0b0c29649
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09529feae5672f027a2da7d0c9e3172f0575813fda68f689f1cdf3dfd5b0301686539bd6d843aef0c862c14831af1a166a2caf9f6f44e68521903d835195018b'
|
7
|
+
data.tar.gz: e5a2d5fa7d7c7ffca14b5fec56fe495627fb829beb9ca6b20014e9a9b66f6bbce1506df72edbc481db3816839b011be3a9c144f8e3f62f764b392b30e37fbce5
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,46 @@
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
2
|
|
3
|
+
## 0.8.0
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- `Dry::Core::BasicObject` ported from hanami-utils (@jodosha)
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
|
11
|
+
- [BREAKING] [descendants tracker] switch to using `Class#subclasses` on Ruby 3.1+.
|
12
|
+
This changes the order of returned subclasses (immediate subclasses now go first) (@flash-gordon)
|
13
|
+
|
14
|
+
|
15
|
+
[Compare v0.7.1...v0.8.0](https://github.com/dry-rb/dry-core/compare/v0.7.1...master)
|
16
|
+
|
17
|
+
## 0.7.1 2021-07-10
|
18
|
+
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
|
22
|
+
- [memoizable] memoizable correctly handles cases where a method
|
23
|
+
has unnamed params (e.g. happens when the new `...` syntax is used) (@flash-gordon)
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
[Compare v0.7.0...v0.7.1](https://github.com/dry-rb/dry-core/compare/v0.7.0...v0.7.1)
|
28
|
+
|
29
|
+
## 0.7.0 2021-07-08
|
30
|
+
|
31
|
+
|
32
|
+
### Fixed
|
33
|
+
|
34
|
+
- [memoizable] warnings when using keyword arguments (@flash-gordon)
|
35
|
+
- [deprecations] warnings show more relevant information about caller by default (@timriley)
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
|
39
|
+
- Minimal Ruby version is 2.6
|
40
|
+
- [memoizable] memoization of block-accepting methods is deprecated (@flash-gordon)
|
41
|
+
|
42
|
+
[Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-core/compare/v0.6.0...v0.7.0)
|
43
|
+
|
3
44
|
## 0.6.0 2021-06-03
|
4
45
|
|
5
46
|
|
@@ -16,7 +57,7 @@
|
|
16
57
|
|
17
58
|
[Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-core/compare/v0.5.0...v0.6.0)
|
18
59
|
|
19
|
-
## 0.5.0
|
60
|
+
## 0.5.0 2020-12-12
|
20
61
|
|
21
62
|
|
22
63
|
### Added
|
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
<!--- this file is synced from dry-rb/template-gem project -->
|
1
2
|
[gem]: https://rubygems.org/gems/dry-core
|
2
3
|
[actions]: https://github.com/dry-rb/dry-core/actions
|
3
4
|
[codacy]: https://www.codacy.com/gh/dry-rb/dry-core
|
@@ -10,19 +11,19 @@
|
|
10
11
|
[][actions]
|
11
12
|
[][codacy]
|
12
13
|
[][codacy]
|
13
|
-
[][inchpages]
|
14
15
|
|
15
16
|
## Links
|
16
17
|
|
17
|
-
* [User documentation](
|
18
|
+
* [User documentation](https://dry-rb.org/gems/dry-core)
|
18
19
|
* [API documentation](http://rubydoc.info/gems/dry-core)
|
19
20
|
|
20
21
|
## Supported Ruby versions
|
21
22
|
|
22
23
|
This library officially supports the following Ruby versions:
|
23
24
|
|
24
|
-
* MRI
|
25
|
-
* jruby
|
25
|
+
* MRI `>= 2.7.0`
|
26
|
+
* jruby `>= 9.3` (postponed until 2.7 is supported)
|
26
27
|
|
27
28
|
## License
|
28
29
|
|
data/dry-core.gemspec
CHANGED
@@ -22,11 +22,11 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
25
|
-
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-core/blob/
|
25
|
+
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-core/blob/main/CHANGELOG.md"
|
26
26
|
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-core"
|
27
27
|
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-core/issues"
|
28
28
|
|
29
|
-
spec.required_ruby_version = ">= 2.
|
29
|
+
spec.required_ruby_version = ">= 2.7.0"
|
30
30
|
|
31
31
|
# to update dependencies edit project.yml
|
32
32
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This implementation was imported from `hanami-utils` gem.
|
4
|
+
module Dry
|
5
|
+
module Core
|
6
|
+
# BasicObject
|
7
|
+
#
|
8
|
+
# @since 0.8.0
|
9
|
+
class BasicObject < ::BasicObject
|
10
|
+
# Lookups constants at the top-level namespace, if they are missing in the
|
11
|
+
# current context.
|
12
|
+
#
|
13
|
+
# @param name [Symbol] the constant name
|
14
|
+
#
|
15
|
+
# @return [Object, Module] the constant
|
16
|
+
#
|
17
|
+
# @raise [NameError] if the constant cannot be found
|
18
|
+
#
|
19
|
+
# @since 0.8.0
|
20
|
+
# @api private
|
21
|
+
#
|
22
|
+
# @see https://ruby-doc.org/core/Module.html#method-i-const_missing
|
23
|
+
def self.const_missing(name)
|
24
|
+
::Object.const_get(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the class for debugging purposes.
|
28
|
+
#
|
29
|
+
# @since 0.8.0
|
30
|
+
#
|
31
|
+
# @see http://ruby-doc.org/core/Object.html#method-i-class
|
32
|
+
def class
|
33
|
+
(class << self; self; end).superclass
|
34
|
+
end
|
35
|
+
|
36
|
+
# Bare minimum inspect for debugging purposes.
|
37
|
+
#
|
38
|
+
# @return [String] the inspect string
|
39
|
+
#
|
40
|
+
# @since 0.8.0
|
41
|
+
#
|
42
|
+
# @see http://ruby-doc.org/core/Object.html#method-i-inspect
|
43
|
+
inspect_method = ::Kernel.instance_method(:inspect)
|
44
|
+
define_method(:inspect) do
|
45
|
+
original = inspect_method.bind_call(self)
|
46
|
+
"#{original[0...-1]}#{__inspect}>"
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!macro [attach] instance_of?(class)
|
50
|
+
#
|
51
|
+
# Determines if self is an instance of given class or module
|
52
|
+
#
|
53
|
+
# @param class [Class,Module] the class of module to verify
|
54
|
+
#
|
55
|
+
# @return [TrueClass,FalseClass] the result of the check
|
56
|
+
#
|
57
|
+
# @raise [TypeError] if the given argument is not of the expected types
|
58
|
+
#
|
59
|
+
# @since 0.8.0
|
60
|
+
#
|
61
|
+
# @see http://ruby-doc.org/core/Object.html#method-i-instance_of-3F
|
62
|
+
define_method :instance_of?, ::Object.instance_method(:instance_of?)
|
63
|
+
|
64
|
+
# @!macro [attach] is_a?(class)
|
65
|
+
#
|
66
|
+
# Determines if self is of the type of the object class or module
|
67
|
+
#
|
68
|
+
# @param class [Class,Module] the class of module to verify
|
69
|
+
#
|
70
|
+
# @return [TrueClass,FalseClass] the result of the check
|
71
|
+
#
|
72
|
+
# @raise [TypeError] if the given argument is not of the expected types
|
73
|
+
#
|
74
|
+
# @since 0.8.0
|
75
|
+
#
|
76
|
+
# @see http://ruby-doc.org/core/Object.html#method-i-is_a-3F
|
77
|
+
define_method :is_a?, ::Object.instance_method(:is_a?)
|
78
|
+
|
79
|
+
# @!macro [attach] kind_of?(class)
|
80
|
+
#
|
81
|
+
# Determines if self is of the kind of the object class or module
|
82
|
+
#
|
83
|
+
# @param class [Class,Module] the class of module to verify
|
84
|
+
#
|
85
|
+
# @return [TrueClass,FalseClass] the result of the check
|
86
|
+
#
|
87
|
+
# @raise [TypeError] if the given argument is not of the expected types
|
88
|
+
#
|
89
|
+
# @since 0.8.0
|
90
|
+
#
|
91
|
+
# @see http://ruby-doc.org/core/Object.html#method-i-kind_of-3F
|
92
|
+
define_method :kind_of?, ::Object.instance_method(:kind_of?)
|
93
|
+
|
94
|
+
# Alias for __id__
|
95
|
+
#
|
96
|
+
# @return [Fixnum] the object id
|
97
|
+
#
|
98
|
+
# @since 0.8.0
|
99
|
+
#
|
100
|
+
# @see http://ruby-doc.org/core/Object.html#method-i-object_id
|
101
|
+
def object_id
|
102
|
+
__id__
|
103
|
+
end
|
104
|
+
|
105
|
+
# Interface for pp
|
106
|
+
#
|
107
|
+
# @param printer [PP] the Pretty Printable printer
|
108
|
+
# @return [String] the pretty-printable inspection of the object
|
109
|
+
#
|
110
|
+
# @since 0.8.0
|
111
|
+
#
|
112
|
+
# @see https://ruby-doc.org/stdlib/libdoc/pp/rdoc/PP.html
|
113
|
+
def pretty_print(printer)
|
114
|
+
printer.text(inspect)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns true if responds to the given method.
|
118
|
+
#
|
119
|
+
# @return [TrueClass,FalseClass] the result of the check
|
120
|
+
#
|
121
|
+
# @since 0.8.0
|
122
|
+
#
|
123
|
+
# @see http://ruby-doc.org/core/Object.html#method-i-respond_to-3F
|
124
|
+
def respond_to?(method_name, include_all = false) # rubocop:disable Style/OptionalBooleanParameter
|
125
|
+
respond_to_missing?(method_name, include_all)
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# Must be overridden by descendants
|
131
|
+
#
|
132
|
+
# @since 0.8.0
|
133
|
+
# @api private
|
134
|
+
def respond_to_missing?(_method_name, _include_all)
|
135
|
+
::Kernel.raise ::NotImplementedError
|
136
|
+
end
|
137
|
+
|
138
|
+
# @since 0.8.0
|
139
|
+
# @api private
|
140
|
+
def __inspect # rubocop:disable Style/EmptyMethod
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/dry/core/cache.rb
CHANGED
@@ -46,7 +46,8 @@ module Dry
|
|
46
46
|
# this means you shouldn't pass Procs in args unless you're sure
|
47
47
|
# they are always the same instances, otherwise you introduce a memory leak
|
48
48
|
#
|
49
|
-
# @return [Object] block's return value (cached for subsequent calls with
|
49
|
+
# @return [Object] block's return value (cached for subsequent calls with
|
50
|
+
# the same argument values)
|
50
51
|
def fetch_or_store(*args, &block)
|
51
52
|
cache.fetch_or_store(args.hash, &block)
|
52
53
|
end
|
@@ -59,8 +60,8 @@ module Dry
|
|
59
60
|
# @yield An arbitrary block
|
60
61
|
#
|
61
62
|
# @return [Object] block's return value
|
62
|
-
def fetch_or_store(
|
63
|
-
self.class.fetch_or_store(
|
63
|
+
def fetch_or_store(...)
|
64
|
+
self.class.fetch_or_store(...)
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
@@ -67,7 +67,7 @@ module Dry
|
|
67
67
|
# defines :one, coerce: Dry::Types['coercible.string']
|
68
68
|
# end
|
69
69
|
#
|
70
|
-
def defines(*args, type: ::Object, coerce: IDENTITY)
|
70
|
+
def defines(*args, type: ::Object, coerce: IDENTITY) # rubocop:disable Metrics/PerceivedComplexity
|
71
71
|
unless coerce.respond_to?(:call)
|
72
72
|
raise ::ArgumentError, "Non-callable coerce option: #{coerce.inspect}"
|
73
73
|
end
|
@@ -83,7 +83,7 @@ module Dry
|
|
83
83
|
else
|
84
84
|
nil
|
85
85
|
end
|
86
|
-
elsif type === value
|
86
|
+
elsif type === value # rubocop:disable Style/CaseEquality
|
87
87
|
instance_variable_set(ivar, coerce.call(value))
|
88
88
|
else
|
89
89
|
raise InvalidClassAttributeValue.new(name, value)
|
@@ -6,9 +6,7 @@ module Dry
|
|
6
6
|
class ClassBuilder
|
7
7
|
ParentClassMismatch = Class.new(TypeError)
|
8
8
|
|
9
|
-
attr_reader :name
|
10
|
-
attr_reader :parent
|
11
|
-
attr_reader :namespace
|
9
|
+
attr_reader :name, :parent, :namespace
|
12
10
|
|
13
11
|
def initialize(name:, parent: nil, namespace: nil)
|
14
12
|
@name = name
|
@@ -83,7 +81,7 @@ module Dry
|
|
83
81
|
def create_base(namespace, name, parent)
|
84
82
|
begin
|
85
83
|
namespace.const_get(name)
|
86
|
-
rescue NameError
|
84
|
+
rescue NameError # rubocop:disable Lint/SuppressedException
|
87
85
|
end
|
88
86
|
|
89
87
|
if namespace.const_defined?(name, false)
|
data/lib/dry/core/constants.rb
CHANGED
@@ -25,7 +25,7 @@ module Dry
|
|
25
25
|
# An empty set
|
26
26
|
EMPTY_SET = ::Set.new.freeze
|
27
27
|
# An empty string
|
28
|
-
EMPTY_STRING = ""
|
28
|
+
EMPTY_STRING = ""
|
29
29
|
# Identity function
|
30
30
|
IDENTITY = (-> x { x }).freeze
|
31
31
|
|
@@ -42,7 +42,7 @@ module Dry
|
|
42
42
|
# end
|
43
43
|
Undefined = Object.new.tap do |undefined|
|
44
44
|
# @api private
|
45
|
-
Self = -> { Undefined }
|
45
|
+
Self = -> { Undefined } # rubocop:disable Lint/ConstantDefinitionInBlock
|
46
46
|
|
47
47
|
# @api public
|
48
48
|
def undefined.to_s
|
@@ -4,7 +4,7 @@ require "logger"
|
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Core
|
7
|
-
# An extension for
|
7
|
+
# An extension for issuing warnings on using deprecated methods.
|
8
8
|
#
|
9
9
|
# @example
|
10
10
|
#
|
@@ -37,12 +37,11 @@ module Dry
|
|
37
37
|
# Defaults to "deprecated"
|
38
38
|
# @param [Integer] Caller frame to add to the message
|
39
39
|
def warn(msg, tag: nil, uplevel: nil)
|
40
|
-
caller_info = uplevel.nil? ? nil :
|
41
|
-
tag = "[#{tag || "deprecated"}]"
|
40
|
+
caller_info = uplevel.nil? ? nil : "#{caller_locations(uplevel + 2, 1)[0]} "
|
41
|
+
tag = "[#{tag || "deprecated"}] "
|
42
42
|
hint = msg.gsub(/^\s+/, "")
|
43
|
-
|
44
|
-
|
45
|
-
)
|
43
|
+
|
44
|
+
logger.warn("#{caller_info}#{tag}#{hint}")
|
46
45
|
end
|
47
46
|
|
48
47
|
# Wraps arguments with a standard message format and prints a warning
|
@@ -50,6 +49,10 @@ module Dry
|
|
50
49
|
# @param [Object] name what is deprecated
|
51
50
|
# @param [String] msg additional message usually containing upgrade instructions
|
52
51
|
def announce(name, msg, tag: nil, uplevel: nil)
|
52
|
+
# Bump the uplevel (if provided) by one to account for the uplevel calculation
|
53
|
+
# taking place one frame deeper in `.warn`
|
54
|
+
uplevel += 1 if uplevel
|
55
|
+
|
53
56
|
warn(deprecation_message(name, msg), tag: tag, uplevel: uplevel)
|
54
57
|
end
|
55
58
|
|
@@ -99,7 +102,7 @@ module Dry
|
|
99
102
|
# @param [#warn] logger
|
100
103
|
#
|
101
104
|
# @api public
|
102
|
-
def set_logger!(output = $stderr)
|
105
|
+
def set_logger!(output = $stderr) # rubocop:disable Naming/AccessorMethodName
|
103
106
|
if output.respond_to?(:warn)
|
104
107
|
@logger = output
|
105
108
|
else
|
@@ -115,8 +118,9 @@ module Dry
|
|
115
118
|
end
|
116
119
|
|
117
120
|
# @api private
|
118
|
-
class Tagged < Module
|
121
|
+
class Tagged < ::Module
|
119
122
|
def initialize(tag)
|
123
|
+
super()
|
120
124
|
@tag = tag
|
121
125
|
end
|
122
126
|
|
@@ -152,8 +156,8 @@ module Dry
|
|
152
156
|
# @option [String] message optional deprecation message
|
153
157
|
def deprecate(old_name, new_name = nil, message: nil)
|
154
158
|
full_msg = Deprecations.deprecated_name_message(
|
155
|
-
"#{
|
156
|
-
new_name ? "#{
|
159
|
+
"#{name}##{old_name}",
|
160
|
+
new_name ? "#{name}##{new_name}" : nil,
|
157
161
|
message
|
158
162
|
)
|
159
163
|
mod = self
|
@@ -185,8 +189,8 @@ module Dry
|
|
185
189
|
# @option [String] message optional deprecation message
|
186
190
|
def deprecate_class_method(old_name, new_name = nil, message: nil)
|
187
191
|
full_msg = Deprecations.deprecated_name_message(
|
188
|
-
"#{
|
189
|
-
new_name ? "#{
|
192
|
+
"#{name}.#{old_name}",
|
193
|
+
new_name ? "#{name}.#{new_name}" : nil,
|
190
194
|
message
|
191
195
|
)
|
192
196
|
|
@@ -210,7 +214,7 @@ module Dry
|
|
210
214
|
remove_const(constant_name)
|
211
215
|
|
212
216
|
full_msg = Deprecations.deprecated_name_message(
|
213
|
-
"#{
|
217
|
+
"#{name}::#{constant_name}",
|
214
218
|
message
|
215
219
|
)
|
216
220
|
|
data/lib/dry/core/equalizer.rb
CHANGED
@@ -14,7 +14,7 @@ module Dry
|
|
14
14
|
|
15
15
|
module Core
|
16
16
|
# Define equality, equivalence and inspection methods
|
17
|
-
class Equalizer < Module
|
17
|
+
class Equalizer < ::Module
|
18
18
|
# Initialize an Equalizer with the given keys
|
19
19
|
#
|
20
20
|
# Will use the keys with which it is initialized to define #cmp?,
|
@@ -29,6 +29,7 @@ module Dry
|
|
29
29
|
#
|
30
30
|
# @api private
|
31
31
|
def initialize(*keys, **options)
|
32
|
+
super()
|
32
33
|
@keys = keys.uniq
|
33
34
|
define_methods(**options)
|
34
35
|
freeze
|
data/lib/dry/core/memoizable.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
|
3
5
|
module Dry
|
4
6
|
module Core
|
5
7
|
module Memoizable
|
6
8
|
MEMOIZED_HASH = {}.freeze
|
9
|
+
PARAM_PLACEHOLDERS = %i[* ** &].freeze
|
7
10
|
|
8
11
|
module ClassInterface
|
9
12
|
module Base
|
@@ -27,7 +30,7 @@ module Dry
|
|
27
30
|
|
28
31
|
def new(*)
|
29
32
|
obj = super
|
30
|
-
obj.instance_variable_set(
|
33
|
+
obj.instance_variable_set(:@__memoized__, MEMOIZED_HASH.dup)
|
31
34
|
obj
|
32
35
|
end
|
33
36
|
|
@@ -48,9 +51,16 @@ module Dry
|
|
48
51
|
end
|
49
52
|
|
50
53
|
# @api private
|
51
|
-
class Memoizer < Module
|
54
|
+
class Memoizer < ::Module
|
55
|
+
KERNEL = {
|
56
|
+
singleton: ::Kernel.instance_method(:singleton_class),
|
57
|
+
ivar_set: ::Kernel.instance_method(:instance_variable_set),
|
58
|
+
frozen: ::Kernel.instance_method(:frozen?)
|
59
|
+
}.freeze
|
60
|
+
|
52
61
|
# @api private
|
53
62
|
def initialize(klass, names)
|
63
|
+
super()
|
54
64
|
names.each do |name|
|
55
65
|
define_memoizable(
|
56
66
|
method: klass.instance_method(name)
|
@@ -61,46 +71,169 @@ module Dry
|
|
61
71
|
private
|
62
72
|
|
63
73
|
# @api private
|
74
|
+
# rubocop:disable Metrics/AbcSize
|
75
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
64
76
|
def define_memoizable(method:)
|
65
|
-
|
66
|
-
|
67
|
-
|
77
|
+
parameters = method.parameters
|
78
|
+
mod = self
|
79
|
+
kernel = KERNEL
|
80
|
+
|
81
|
+
if parameters.empty?
|
82
|
+
key = method.name.hash.abs
|
68
83
|
|
69
|
-
|
70
|
-
|
84
|
+
define_method(method.name) do
|
85
|
+
value = super()
|
86
|
+
|
87
|
+
if kernel[:frozen].bind(self).call
|
88
|
+
# It's not possible to modify singleton classes
|
89
|
+
# of frozen objects
|
90
|
+
mod.remove_method(method.name)
|
91
|
+
mod.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
92
|
+
def #{method.name} # def slow_calc
|
93
|
+
cached = @__memoized__[#{key}] # cached = @__memoized__[12345678]
|
94
|
+
#
|
95
|
+
if cached || @__memoized__.key?(#{key}) # if cached || @__memoized__.key?(12345678)
|
96
|
+
cached # cached
|
97
|
+
else # else
|
98
|
+
@__memoized__[#{key}] = super # @__memoized__[12345678] = super
|
99
|
+
end # end
|
100
|
+
end # end
|
101
|
+
RUBY
|
71
102
|
else
|
72
|
-
|
103
|
+
# We make an attr_reader for computed value.
|
104
|
+
# Readers are "special-cased" in ruby so such
|
105
|
+
# access will be the fastest way, faster than you'd
|
106
|
+
# expect :)
|
107
|
+
attr_name = :"__memozed_#{key}__"
|
108
|
+
ivar_name = :"@#{attr_name}"
|
109
|
+
kernel[:ivar_set].bind(self).(ivar_name, value)
|
110
|
+
eigenclass = kernel[:singleton].bind(self).call
|
111
|
+
eigenclass.attr_reader(attr_name)
|
112
|
+
eigenclass.alias_method(method.name, attr_name)
|
113
|
+
eigenclass.remove_method(attr_name)
|
73
114
|
end
|
115
|
+
|
116
|
+
value
|
74
117
|
end
|
75
|
-
|
118
|
+
else
|
119
|
+
mapping = parameters.to_h { |k, v = nil| [k, v] }
|
120
|
+
params, binds = declaration(parameters, mapping)
|
121
|
+
last_param = parameters.last
|
76
122
|
|
77
|
-
|
78
|
-
|
123
|
+
if last_param[0].eql?(:block) && !last_param[1].eql?(:&)
|
124
|
+
Deprecations.warn(<<~WARN)
|
125
|
+
Memoization for block-accepting methods isn't safe.
|
126
|
+
Every call creates a new block instance bloating cached results.
|
127
|
+
In the future, blocks will still be allowed but won't participate in
|
128
|
+
cache key calculation.
|
129
|
+
WARN
|
130
|
+
end
|
131
|
+
|
132
|
+
m = module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
133
|
+
def #{method.name}(#{params.join(", ")}) # def slow_calc(arg1, arg2, arg3)
|
134
|
+
key = [:"#{method.name}", #{binds.join(", ")}].hash # key = [:slow_calc, arg1, arg2, arg3].hash
|
135
|
+
#
|
136
|
+
if @__memoized__.key?(key) # if @__memoized__.key?(key)
|
137
|
+
@__memoized__[key] # @__memoized__[key]
|
138
|
+
else # else
|
139
|
+
@__memoized__[key] = super # @__memoized__[key] = super
|
140
|
+
end # end
|
141
|
+
end # end
|
142
|
+
RUBY
|
143
|
+
|
144
|
+
if respond_to?(:ruby2_keywords, true) && mapping.key?(:reyrest)
|
145
|
+
ruby2_keywords(method.name)
|
146
|
+
end
|
147
|
+
|
148
|
+
m
|
79
149
|
end
|
80
150
|
end
|
151
|
+
# rubocop:enable Metrics/AbcSize
|
152
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
81
153
|
|
82
154
|
# @api private
|
83
|
-
def
|
84
|
-
params
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
155
|
+
def declaration(definition, lookup)
|
156
|
+
params = []
|
157
|
+
binds = []
|
158
|
+
defined = {}
|
159
|
+
|
160
|
+
definition.each do |type, name|
|
161
|
+
mapped_type = map_bind_type(type, name, lookup, defined) do
|
162
|
+
raise ::NotImplementedError, "type: #{type}, name: #{name}"
|
163
|
+
end
|
164
|
+
|
165
|
+
if mapped_type
|
166
|
+
defined[mapped_type] = true
|
167
|
+
bind = name_from_param(name) || make_bind_name(binds.size)
|
168
|
+
|
169
|
+
binds << bind
|
170
|
+
params << param(bind, mapped_type)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
[params, binds]
|
175
|
+
end
|
176
|
+
|
177
|
+
# @api private
|
178
|
+
def name_from_param(name)
|
179
|
+
if PARAM_PLACEHOLDERS.include?(name)
|
180
|
+
nil
|
181
|
+
else
|
182
|
+
name
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# @api private
|
187
|
+
def make_bind_name(idx)
|
188
|
+
:"__lv_#{idx}__"
|
189
|
+
end
|
190
|
+
|
191
|
+
# @api private
|
192
|
+
def map_bind_type(type, name, original_params, defined_types) # rubocop:disable Metrics/PerceivedComplexity
|
193
|
+
case type
|
194
|
+
when :req
|
195
|
+
:reqular
|
196
|
+
when :rest, :keyreq, :keyrest
|
197
|
+
type
|
198
|
+
when :block
|
199
|
+
if name.eql?(:&)
|
200
|
+
# most likely this is a case of delegation
|
201
|
+
# rather than actual block
|
202
|
+
nil
|
100
203
|
else
|
101
|
-
|
204
|
+
type
|
102
205
|
end
|
103
|
-
|
206
|
+
when :opt
|
207
|
+
if original_params.key?(:rest) || defined_types[:rest]
|
208
|
+
nil
|
209
|
+
else
|
210
|
+
:rest
|
211
|
+
end
|
212
|
+
when :key
|
213
|
+
if original_params.key?(:keyrest) || defined_types[:keyrest]
|
214
|
+
nil
|
215
|
+
else
|
216
|
+
:keyrest
|
217
|
+
end
|
218
|
+
else
|
219
|
+
yield
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# @api private
|
224
|
+
def param(name, type)
|
225
|
+
case type
|
226
|
+
when :reqular
|
227
|
+
name
|
228
|
+
when :rest
|
229
|
+
"*#{name}"
|
230
|
+
when :keyreq
|
231
|
+
"#{name}:"
|
232
|
+
when :keyrest
|
233
|
+
"**#{name}"
|
234
|
+
when :block
|
235
|
+
"&#{name}"
|
236
|
+
end
|
104
237
|
end
|
105
238
|
end
|
106
239
|
end
|
data/lib/dry/core/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nikita Shilnikov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- dry-core.gemspec
|
80
80
|
- lib/dry-core.rb
|
81
81
|
- lib/dry/core.rb
|
82
|
+
- lib/dry/core/basic_object.rb
|
82
83
|
- lib/dry/core/cache.rb
|
83
84
|
- lib/dry/core/class_attributes.rb
|
84
85
|
- lib/dry/core/class_builder.rb
|
@@ -96,7 +97,7 @@ licenses:
|
|
96
97
|
- MIT
|
97
98
|
metadata:
|
98
99
|
allowed_push_host: https://rubygems.org
|
99
|
-
changelog_uri: https://github.com/dry-rb/dry-core/blob/
|
100
|
+
changelog_uri: https://github.com/dry-rb/dry-core/blob/main/CHANGELOG.md
|
100
101
|
source_code_uri: https://github.com/dry-rb/dry-core
|
101
102
|
bug_tracker_uri: https://github.com/dry-rb/dry-core/issues
|
102
103
|
post_install_message:
|
@@ -107,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
108
|
requirements:
|
108
109
|
- - ">="
|
109
110
|
- !ruby/object:Gem::Version
|
110
|
-
version: 2.
|
111
|
+
version: 2.7.0
|
111
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
113
|
requirements:
|
113
114
|
- - ">="
|