dry-core 0.4.7
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 +7 -0
- data/.gitignore +10 -0
- data/.inch.yml +4 -0
- data/.rspec +3 -0
- data/.rubocop.yml +31 -0
- data/.travis.yml +31 -0
- data/CHANGELOG.md +153 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +47 -0
- data/Rakefile +6 -0
- data/dry-core.gemspec +34 -0
- data/lib/dry-core.rb +1 -0
- data/lib/dry/core.rb +8 -0
- data/lib/dry/core/cache.rb +66 -0
- data/lib/dry/core/class_attributes.rb +83 -0
- data/lib/dry/core/class_builder.rb +105 -0
- data/lib/dry/core/constants.rb +77 -0
- data/lib/dry/core/deprecations.rb +224 -0
- data/lib/dry/core/descendants_tracker.rb +76 -0
- data/lib/dry/core/errors.rb +11 -0
- data/lib/dry/core/extensions.rb +61 -0
- data/lib/dry/core/inflector.rb +138 -0
- data/lib/dry/core/memoizable.rb +68 -0
- data/lib/dry/core/version.rb +5 -0
- metadata +126 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'concurrent/array'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Core
|
5
|
+
# An implementation of descendants tracker, heavily inspired
|
6
|
+
# by the descendants_tracker gem.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# class Base
|
11
|
+
# extend Dry::Core::DescendantsTracker
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class A < Base
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class B < Base
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# class C < A
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Base.descendants # => [C, B, A]
|
24
|
+
# A.descendants # => [C]
|
25
|
+
# B.descendants # => []
|
26
|
+
#
|
27
|
+
module DescendantsTracker
|
28
|
+
class << self
|
29
|
+
# @api private
|
30
|
+
def setup(target)
|
31
|
+
target.instance_variable_set(:@descendants, Concurrent::Array.new)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
def extended(base)
|
38
|
+
super
|
39
|
+
|
40
|
+
DescendantsTracker.setup(base)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the descendants of this class
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# descendants = Parent.descendants
|
48
|
+
#
|
49
|
+
# @return [Array<Class>]
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
attr_reader :descendants
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
def add_descendant(descendant)
|
58
|
+
ancestor = superclass
|
59
|
+
if ancestor.respond_to?(:add_descendant, true)
|
60
|
+
ancestor.add_descendant(descendant)
|
61
|
+
end
|
62
|
+
descendants.unshift(descendant)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# @api private
|
68
|
+
def inherited(descendant)
|
69
|
+
super
|
70
|
+
|
71
|
+
DescendantsTracker.setup(descendant)
|
72
|
+
add_descendant(descendant)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Core
|
5
|
+
# Define extensions that can be later enabled by the user.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
#
|
9
|
+
# class Foo
|
10
|
+
# extend Dry::Core::Extensions
|
11
|
+
#
|
12
|
+
# register_extension(:bar) do
|
13
|
+
# def bar; :bar end
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Foo.new.bar # => NoMethodError
|
18
|
+
# Foo.load_extensions(:bar)
|
19
|
+
# Foo.new.bar # => :bar
|
20
|
+
#
|
21
|
+
module Extensions
|
22
|
+
# @api private
|
23
|
+
def self.extended(obj)
|
24
|
+
super
|
25
|
+
obj.instance_variable_set(:@__available_extensions__, {})
|
26
|
+
obj.instance_variable_set(:@__loaded_extensions__, Set.new)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Register an extension
|
30
|
+
#
|
31
|
+
# @param [Symbol] name extension name
|
32
|
+
# @yield extension block. This block guaranteed not to be called more than once
|
33
|
+
def register_extension(name, &block)
|
34
|
+
@__available_extensions__[name] = block
|
35
|
+
end
|
36
|
+
|
37
|
+
# Whether an extension is available
|
38
|
+
#
|
39
|
+
# @param [Symbol] name extension name
|
40
|
+
# @return [Boolean] Extension availability
|
41
|
+
def available_extension?(name)
|
42
|
+
@__available_extensions__.key?(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Enables specified extensions. Already enabled extensions remain untouched
|
46
|
+
#
|
47
|
+
# @param [Array<Symbol>] extensions list of extension names
|
48
|
+
def load_extensions(*extensions)
|
49
|
+
extensions.each do |ext|
|
50
|
+
block = @__available_extensions__.fetch(ext) do
|
51
|
+
raise ArgumentError, "Unknown extension: #{ext.inspect}"
|
52
|
+
end
|
53
|
+
unless @__loaded_extensions__.include?(ext)
|
54
|
+
block.call
|
55
|
+
@__loaded_extensions__ << ext
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Dry
|
2
|
+
module Core
|
3
|
+
# Helper module providing thin interface around an inflection backend.
|
4
|
+
module Inflector
|
5
|
+
# List of supported backends
|
6
|
+
BACKENDS = {
|
7
|
+
activesupport: [
|
8
|
+
'active_support/inflector',
|
9
|
+
proc { ::ActiveSupport::Inflector }
|
10
|
+
],
|
11
|
+
dry_inflector: [
|
12
|
+
'dry/inflector',
|
13
|
+
proc { Dry::Inflector.new }
|
14
|
+
],
|
15
|
+
inflecto: [
|
16
|
+
'inflecto',
|
17
|
+
proc { ::Inflecto }
|
18
|
+
]
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
# Try to activate a backend
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
def self.realize_backend(path, backend_factory)
|
25
|
+
require path
|
26
|
+
rescue LoadError
|
27
|
+
nil
|
28
|
+
else
|
29
|
+
backend_factory.call
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set up first available backend
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
def self.detect_backend
|
36
|
+
BACKENDS.inject(nil) do |backend, (_, (path, factory))|
|
37
|
+
backend || realize_backend(path, factory)
|
38
|
+
end || raise(LoadError,
|
39
|
+
"No inflector library could be found: "\
|
40
|
+
"please install either the `inflecto` or `activesupport` gem.")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Set preferred backend
|
44
|
+
#
|
45
|
+
# @param [Symbol] name backend name (:activesupport or :inflecto)
|
46
|
+
def self.select_backend(name = nil)
|
47
|
+
if name && !BACKENDS.key?(name)
|
48
|
+
raise NameError, "Invalid inflector library selection: '#{name}'"
|
49
|
+
end
|
50
|
+
@inflector = name ? realize_backend(*BACKENDS[name]) : detect_backend
|
51
|
+
end
|
52
|
+
|
53
|
+
# Inflector accessor. Lazily initializes a backend
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
def self.inflector
|
57
|
+
defined?(@inflector) ? @inflector : select_backend
|
58
|
+
end
|
59
|
+
|
60
|
+
# Transform string to camel case
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# Dry::Core::Inflector.camelize('foo_bar') # => 'FooBar'
|
64
|
+
#
|
65
|
+
# @param [String] input input string
|
66
|
+
# @return Transformed string
|
67
|
+
def self.camelize(input)
|
68
|
+
inflector.camelize(input)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Transform string to snake case
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# Dry::Core::Inflector.underscore('FooBar') # => 'foo_bar'
|
75
|
+
#
|
76
|
+
# @param [String] input input string
|
77
|
+
# @return Transformed string
|
78
|
+
def self.underscore(input)
|
79
|
+
inflector.underscore(input)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Get a singlular form of a word
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
# Dry::Core::Inflector.singularize('chars') # => 'char'
|
86
|
+
#
|
87
|
+
# @param [String] input input string
|
88
|
+
# @return Transformed string
|
89
|
+
def self.singularize(input)
|
90
|
+
inflector.singularize(input)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get a plural form of a word
|
94
|
+
#
|
95
|
+
# @example
|
96
|
+
# Dry::Core::Inflector.pluralize('string') # => 'strings'
|
97
|
+
#
|
98
|
+
# @param [String] input input string
|
99
|
+
# @return Transformed string
|
100
|
+
def self.pluralize(input)
|
101
|
+
inflector.pluralize(input)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Remove namespaces from a constant name
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# Dry::Core::Inflector.demodulize('Deeply::Nested::Name') # => 'Name'
|
108
|
+
#
|
109
|
+
# @param [String] input input string
|
110
|
+
# @return Unnested constant name
|
111
|
+
def self.demodulize(input)
|
112
|
+
inflector.demodulize(input)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Get a constant value by its name
|
116
|
+
#
|
117
|
+
# @example
|
118
|
+
# Dry::Core::Inflector.constantize('Foo::Bar') # => Foo::Bar
|
119
|
+
#
|
120
|
+
# @param [String] input input constant name
|
121
|
+
# @return Constant value
|
122
|
+
def self.constantize(input)
|
123
|
+
inflector.constantize(input)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Transform a file path to a constant name
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# Dry::Core::Inflector.classify('foo/bar') # => 'Foo::Bar'
|
130
|
+
#
|
131
|
+
# @param [String] input input string
|
132
|
+
# @return Constant name
|
133
|
+
def self.classify(input)
|
134
|
+
inflector.classify(input)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Dry
|
2
|
+
module Core
|
3
|
+
module Memoizable
|
4
|
+
MEMOIZED_HASH = {}.freeze
|
5
|
+
|
6
|
+
module ClassInterface
|
7
|
+
def memoize(*names)
|
8
|
+
prepend(Memoizer.new(self, names))
|
9
|
+
end
|
10
|
+
|
11
|
+
def new(*)
|
12
|
+
obj = super
|
13
|
+
obj.instance_variable_set(:'@__memoized__', MEMOIZED_HASH.dup)
|
14
|
+
obj
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.included(klass)
|
19
|
+
super
|
20
|
+
klass.extend(ClassInterface)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :__memoized__
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
class Memoizer < Module
|
27
|
+
attr_reader :klass
|
28
|
+
attr_reader :names
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
def initialize(klass, names)
|
32
|
+
@names = names
|
33
|
+
@klass = klass
|
34
|
+
define_memoizable_names!
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
def define_memoizable_names!
|
41
|
+
names.each do |name|
|
42
|
+
meth = klass.instance_method(name)
|
43
|
+
|
44
|
+
if meth.parameters.size > 0
|
45
|
+
define_method(name) do |*args|
|
46
|
+
name_with_args = :"#{name}_#{args.hash}"
|
47
|
+
|
48
|
+
if __memoized__.key?(name_with_args)
|
49
|
+
__memoized__[name_with_args]
|
50
|
+
else
|
51
|
+
__memoized__[name_with_args] = super(*args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
define_method(name) do
|
56
|
+
if __memoized__.key?(name)
|
57
|
+
__memoized__[name]
|
58
|
+
else
|
59
|
+
__memoized__[name] = super()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dry-core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nikita Shilnikov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.12'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.12'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description: A toolset of small support modules used throughout the dry-rb ecosystem.
|
70
|
+
email:
|
71
|
+
- fg@flashgordon.ru
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".inch.yml"
|
78
|
+
- ".rspec"
|
79
|
+
- ".rubocop.yml"
|
80
|
+
- ".travis.yml"
|
81
|
+
- CHANGELOG.md
|
82
|
+
- CONTRIBUTING.md
|
83
|
+
- Gemfile
|
84
|
+
- LICENSE.txt
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- dry-core.gemspec
|
88
|
+
- lib/dry-core.rb
|
89
|
+
- lib/dry/core.rb
|
90
|
+
- lib/dry/core/cache.rb
|
91
|
+
- lib/dry/core/class_attributes.rb
|
92
|
+
- lib/dry/core/class_builder.rb
|
93
|
+
- lib/dry/core/constants.rb
|
94
|
+
- lib/dry/core/deprecations.rb
|
95
|
+
- lib/dry/core/descendants_tracker.rb
|
96
|
+
- lib/dry/core/errors.rb
|
97
|
+
- lib/dry/core/extensions.rb
|
98
|
+
- lib/dry/core/inflector.rb
|
99
|
+
- lib/dry/core/memoizable.rb
|
100
|
+
- lib/dry/core/version.rb
|
101
|
+
homepage: https://github.com/dry-rb/dry-core
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata:
|
105
|
+
allowed_push_host: https://rubygems.org
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 2.1.0
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.7.6
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: A toolset of small support modules used throughout the dry-rb ecosystem.
|
126
|
+
test_files: []
|