dry-behaviour 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7dd8efa2d46ccb80e8b8c4ed83140993496836d
4
- data.tar.gz: c9b800c20027b9e549eddd506dad0501edd73cf2
3
+ metadata.gz: c27c0695413fb84924f27453989fd85836066d0f
4
+ data.tar.gz: 48217fe055d56c59e4842e28f9eddbc597a48a80
5
5
  SHA512:
6
- metadata.gz: c877197d35aa0c34ccb43949914e5b73504fb946c4c4d6678f6b1c6c8a5e5a6c2849843431412264a3b0e7543543a9317195185bd4970f6f6fe4944b007b11dc
7
- data.tar.gz: 4825b89db273d9d5c85039723ac8914d3582cc9cfb6b3179dbc3c15517944a90b4335d864e52f80d81bc62d1336fa3c2ba8c51d676f46439c5d26b8b090d6d5d
6
+ metadata.gz: a9e4ca4ed497dd76ed2caf2797d1570f3e7184ae4fe54497b15e84f9a7a3199858c2bfff2cab124a8b3d8d6bbb10617dec3bd397a36ae2580da9b40927f35fb2
7
+ data.tar.gz: 38f727f407f90ff7398798410962e08b8d32e95d99fcf1c3eabc6722c1dc6673c4a978a0bb502cf4cea1ec0e7c4960b9c94ba6b373e0df411e972e081268b661
data/.rubocop.yml CHANGED
@@ -1 +1,4 @@
1
1
  inherit_from: .rubocop_todo.yml
2
+
3
+ Style/Documentation:
4
+ Enabled: false
data/README.md CHANGED
@@ -64,6 +64,8 @@ expect(Protocols::Adder.add_default(1)).to eq(6)
64
64
 
65
65
  ## Changelog
66
66
 
67
+ ### `0.5.0` :: Guards
68
+
67
69
  ### `0.4.2` :: Removed the forgotten debug output :(
68
70
 
69
71
  ### `0.4.1` :: Protocol-wide methods are allowed to call from implementation
@@ -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 Style/EmptyCaseCondition
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 source do |this, *args, **params, &λ|
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/PerceivedComplexity
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
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Behaviour
3
- VERSION = '0.4.2'.freeze
3
+ VERSION = '0.5.0'.freeze
4
4
  end
5
5
  end
data/lib/dry/behaviour.rb CHANGED
@@ -1,12 +1,25 @@
1
1
  require 'dry/behaviour/version'
2
- require 'dry/errors/not_implemented'
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
@@ -0,0 +1,9 @@
1
+ module Dry
2
+ module Guards
3
+ class NotMatched < StandardError
4
+ def initialize(*args, **params, &cb)
5
+ super "Clause not matched. Parameters supplied: [args: #{args.inspect}, params: #{params.inspect}, cb: #{cb.inspect}]"
6
+ end
7
+ end
8
+ end
9
+ end
data/lib/dry/errors.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'dry/errors/not_implemented'
2
+ require 'dry/errors/not_protocol'
3
+ require 'dry/errors/not_guardable'
4
+ require 'dry/errors/not_matched'
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.2
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-01-16 00:00:00.000000000 Z
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: