dry-behaviour 0.4.2 → 0.5.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/.rubocop.yml +3 -0
- data/README.md +2 -0
- data/dry-behaviour.gemspec +1 -0
- data/lib/dry/behaviour/black_tie.rb +3 -16
- data/lib/dry/behaviour/cerberus.rb +112 -0
- data/lib/dry/behaviour/version.rb +1 -1
- data/lib/dry/behaviour.rb +26 -4
- data/lib/dry/errors/not_guardable.rb +15 -0
- data/lib/dry/errors/not_matched.rb +9 -0
- data/lib/dry/errors.rb +4 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c27c0695413fb84924f27453989fd85836066d0f
|
4
|
+
data.tar.gz: 48217fe055d56c59e4842e28f9eddbc597a48a80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9e4ca4ed497dd76ed2caf2797d1570f3e7184ae4fe54497b15e84f9a7a3199858c2bfff2cab124a8b3d8d6bbb10617dec3bd397a36ae2580da9b40927f35fb2
|
7
|
+
data.tar.gz: 38f727f407f90ff7398798410962e08b8d32e95d99fcf1c3eabc6722c1dc6673c4a978a0bb502cf4cea1ec0e7c4960b9c94ba6b373e0df411e972e081268b661
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
data/dry-behaviour.gemspec
CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.require_paths = ['lib']
|
27
27
|
|
28
28
|
spec.add_development_dependency 'bundler', '~> 1.10'
|
29
|
+
spec.add_development_dependency 'awesome_print', '~> 1.7'
|
29
30
|
spec.add_development_dependency 'rake', '~> 10.0'
|
30
31
|
spec.add_development_dependency 'rspec'
|
31
32
|
spec.add_development_dependency 'pry'
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Dry
|
2
|
-
# rubocop:disable Style/AsciiIdentifiers
|
3
2
|
# rubocop:disable Style/MultilineBlockChain
|
4
|
-
# rubocop:disable
|
5
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
3
|
+
# rubocop:disable Metrics/MethodLength
|
6
4
|
# rubocop:disable Metrics/CyclomaticComplexity
|
7
5
|
# rubocop:disable Metrics/AbcSize
|
8
6
|
# rubocop:disable Style/MethodName
|
@@ -38,7 +36,6 @@ module Dry
|
|
38
36
|
BlackTie.protocols[self].each do |method, *_| # FIXME: CHECK PARAMS CORRESPONDENCE HERE
|
39
37
|
singleton_class.send :define_method, method do |receiver = nil, *args|
|
40
38
|
impl = implementation_for(receiver)
|
41
|
-
|
42
39
|
raise Dry::Protocol::NotImplemented.new(:protocol, inspect, receiver.class) unless impl
|
43
40
|
begin
|
44
41
|
impl[method].(*args.unshift(receiver))
|
@@ -57,14 +54,7 @@ module Dry
|
|
57
54
|
|
58
55
|
DELEGATE_METHOD = lambda do |klazz, (source, target)|
|
59
56
|
klazz.class_eval do
|
60
|
-
define_method
|
61
|
-
case
|
62
|
-
when !args.empty? && !params.empty? then this.send(target, *args, **params, &λ)
|
63
|
-
when !args.empty? then this.send(target, *args, &λ)
|
64
|
-
when !params.empty? then this.send(target, **params, &λ)
|
65
|
-
else this.send(target, &λ)
|
66
|
-
end
|
67
|
-
end
|
57
|
+
define_method(source, &Dry::DEFINE_METHOD.curry[target])
|
68
58
|
end
|
69
59
|
end
|
70
60
|
|
@@ -77,7 +67,6 @@ module Dry
|
|
77
67
|
end.enable
|
78
68
|
end
|
79
69
|
|
80
|
-
|
81
70
|
def defimpl(protocol = nil, target: nil, delegate: [], map: {})
|
82
71
|
raise if target.nil? || !block_given? && delegate.empty? && map.empty?
|
83
72
|
|
@@ -128,8 +117,6 @@ module Dry
|
|
128
117
|
# rubocop:enable Style/MethodName
|
129
118
|
# rubocop:enable Metrics/AbcSize
|
130
119
|
# rubocop:enable Metrics/CyclomaticComplexity
|
131
|
-
# rubocop:enable Metrics/
|
132
|
-
# rubocop:enable Style/EmptyCaseCondition
|
120
|
+
# rubocop:enable Metrics/MethodLength
|
133
121
|
# rubocop:enable Style/MultilineBlockChain
|
134
|
-
# rubocop:enable Style/AsciiIdentifiers
|
135
122
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Dry
|
2
|
+
module Cerberus
|
3
|
+
POSTPONE_FIX_CLAUSES = lambda do |guarded|
|
4
|
+
TracePoint.new(:end) do |tp|
|
5
|
+
if tp.self == guarded
|
6
|
+
H.umbrellas(guarded)
|
7
|
+
tp.disable
|
8
|
+
end
|
9
|
+
end.enable
|
10
|
+
end
|
11
|
+
|
12
|
+
@adding_alias = false
|
13
|
+
|
14
|
+
def guarded_methods
|
15
|
+
@guarded_methods ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# [[:req, :p], [:opt, :po], [:rest, :a], [:key, :when], [:keyrest, :b], [:block, :cb]]
|
19
|
+
def method_added(name)
|
20
|
+
return if @adding_alias
|
21
|
+
|
22
|
+
m = instance_method(name)
|
23
|
+
key = m.parameters.any? { |(k, v)| k == :key && v == :when } ? H.extract_when(m) : m.arity
|
24
|
+
key = case key
|
25
|
+
when String then instance_eval(key)
|
26
|
+
when -Float::INFINITY...0 then nil
|
27
|
+
else key
|
28
|
+
end
|
29
|
+
(guarded_methods[name] ||= {})[key] = m
|
30
|
+
|
31
|
+
@adding_alias = true
|
32
|
+
alias_name = H.alias_name(m, guarded_methods[name].size.pred)
|
33
|
+
alias_method alias_name, name
|
34
|
+
private alias_name
|
35
|
+
@adding_alias = false
|
36
|
+
end
|
37
|
+
|
38
|
+
module H
|
39
|
+
CONCAT = '_★_'.freeze
|
40
|
+
|
41
|
+
module_function
|
42
|
+
|
43
|
+
def extract_when(m)
|
44
|
+
file, line = m.source_location
|
45
|
+
raise ::Dry::Guards::NotGuardable.new(m, :nil) if file.nil?
|
46
|
+
|
47
|
+
File.readlines(file)[line - 1..-1].join(' ')[/(?<=when:).*/].tap do |guard|
|
48
|
+
raise ::Dry::Guards::NotGuardable.new(m, :when_is_nil) if guard.nil?
|
49
|
+
clause = parse_hash guard
|
50
|
+
raise ::Dry::Guards::NotGuardable.new(m, :when_not_hash) unless clause.is_a?(String)
|
51
|
+
guard.replace clause
|
52
|
+
end
|
53
|
+
rescue Errno::ENOENT => e
|
54
|
+
raise ::Dry::Guards::NotGuardable.new(m, e)
|
55
|
+
end
|
56
|
+
|
57
|
+
def alias_name(m, idx)
|
58
|
+
:"#{m && m.name}#{CONCAT}#{idx}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_hash(input)
|
62
|
+
input.each_codepoint
|
63
|
+
.drop_while { |cp| cp != 123 }
|
64
|
+
.each_with_object(['', 0]) do |cp, acc|
|
65
|
+
case cp
|
66
|
+
when 123 then acc[-1] = acc.last.succ
|
67
|
+
when 125 then acc[-1] = acc.last.pred
|
68
|
+
end
|
69
|
+
acc.first << cp
|
70
|
+
break acc.first if acc.last.zero?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def umbrellas(guarded)
|
75
|
+
guarded.guarded_methods.reject! do |_, hash|
|
76
|
+
next unless hash.size == 1 && hash.keys.first.is_a?(Integer)
|
77
|
+
guarded.send :remove_method, alias_name(hash.values.first, 0)
|
78
|
+
end
|
79
|
+
# guarded.guarded_methods.each(&H.method(:umbrella).to_proc.curry[guarded])
|
80
|
+
guarded.guarded_methods.each do |name, clauses|
|
81
|
+
H.umbrella(guarded, name, clauses)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def umbrella(guarded, name, clauses)
|
86
|
+
guarded.prepend(Module.new do
|
87
|
+
define_method name do |*args, **params, &cb|
|
88
|
+
found = clauses.each_with_index.detect do |(hash, m), idx|
|
89
|
+
next if m.arity >= 0 && m.arity != args.size
|
90
|
+
break [[hash, m], idx] if case hash
|
91
|
+
when NilClass then m.arity < 0
|
92
|
+
when Integer then hash == args.size
|
93
|
+
end
|
94
|
+
next if hash.nil?
|
95
|
+
hash.all? do |param, condition|
|
96
|
+
idx = m.parameters.index { |_type, var| var == param }
|
97
|
+
# rubocop:disable Style/CaseEquality
|
98
|
+
# rubocop:disable Style/RescueModifier
|
99
|
+
idx && condition === args[idx] rescue false # FIXME: more accurate
|
100
|
+
# rubocop:enable Style/RescueModifier
|
101
|
+
# rubocop:enable Style/CaseEquality
|
102
|
+
end
|
103
|
+
end
|
104
|
+
raise ::Dry::Guards::NotMatched.new(*args, **params, &cb) unless found
|
105
|
+
::Dry::DEFINE_METHOD.(H.alias_name(found.first.last, found.last), self, *args, **params, &cb)
|
106
|
+
end
|
107
|
+
end)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
private_constant :H
|
111
|
+
end
|
112
|
+
end
|
data/lib/dry/behaviour.rb
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
require 'dry/behaviour/version'
|
2
|
-
require 'dry/errors
|
3
|
-
require 'dry/errors/not_protocol'
|
2
|
+
require 'dry/errors'
|
4
3
|
require 'dry/behaviour/black_tie'
|
4
|
+
require 'dry/behaviour/cerberus'
|
5
5
|
|
6
6
|
module Dry
|
7
|
+
# rubocop:disable Style/AsciiIdentifiers
|
8
|
+
# rubocop:disable Style/EmptyCaseCondition
|
9
|
+
DEFINE_METHOD = lambda do |target, this, *args, **params, &λ|
|
10
|
+
case
|
11
|
+
when !args.empty? && !params.empty? then this.send(target, *args, **params, &λ)
|
12
|
+
when !args.empty? then this.send(target, *args, &λ)
|
13
|
+
when !params.empty? then this.send(target, **params, &λ)
|
14
|
+
else this.send(target, &λ)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# rubocop:enable Style/EmptyCaseCondition
|
18
|
+
# rubocop:enable Style/AsciiIdentifiers
|
19
|
+
|
7
20
|
module Protocol
|
8
21
|
def self.included(base)
|
9
|
-
base.singleton_class.prepend(Dry::BlackTie)
|
22
|
+
base.singleton_class.prepend(::Dry::BlackTie)
|
10
23
|
end
|
11
24
|
|
12
25
|
class << self
|
@@ -16,10 +29,19 @@ module Dry
|
|
16
29
|
end
|
17
30
|
# rubocop:enable Style/AsciiIdentifiers
|
18
31
|
|
32
|
+
# rubocop:disable Style/RaiseArgs
|
19
33
|
def implemented_for?(protocol, receiver)
|
20
|
-
raise NotProtocol.new(protocol) unless protocol < ::Dry::Protocol
|
34
|
+
raise ::Dry::Protocol::NotProtocol.new(protocol) unless protocol < ::Dry::Protocol
|
21
35
|
!protocol.implementation_for(receiver).nil?
|
22
36
|
end
|
37
|
+
# rubocop:enable Style/RaiseArgs
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module Guards
|
42
|
+
def self.included(base)
|
43
|
+
::Dry::Cerberus::POSTPONE_FIX_CLAUSES.(base)
|
44
|
+
base.singleton_class.prepend(::Dry::Cerberus)
|
23
45
|
end
|
24
46
|
end
|
25
47
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Dry
|
2
|
+
module Guards
|
3
|
+
class NotGuardable < StandardError
|
4
|
+
def initialize(method, cause)
|
5
|
+
reason = case cause
|
6
|
+
when :nil then 'source location is inavailable'
|
7
|
+
when Errno::ENOENT then "source file could not be read [#{cause.message}]"
|
8
|
+
when :when_is_nil then 'when clause is missing'
|
9
|
+
when :when_not_hash then 'when clause is not a hash'
|
10
|
+
end
|
11
|
+
super "Can’t guard method “#{method.inspect}” (#{reason}.)"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/dry/errors.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-behaviour
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aleksei Matiushkin
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-
|
13
|
+
date: 2017-04-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -26,6 +26,20 @@ dependencies:
|
|
26
26
|
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '1.10'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: awesome_print
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '1.7'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '1.7'
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: rake
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,8 +106,12 @@ files:
|
|
92
106
|
- dry-behaviour.gemspec
|
93
107
|
- lib/dry/behaviour.rb
|
94
108
|
- lib/dry/behaviour/black_tie.rb
|
109
|
+
- lib/dry/behaviour/cerberus.rb
|
95
110
|
- lib/dry/behaviour/version.rb
|
111
|
+
- lib/dry/errors.rb
|
112
|
+
- lib/dry/errors/not_guardable.rb
|
96
113
|
- lib/dry/errors/not_implemented.rb
|
114
|
+
- lib/dry/errors/not_matched.rb
|
97
115
|
- lib/dry/errors/not_protocol.rb
|
98
116
|
homepage: https://kantox.com/
|
99
117
|
licenses:
|