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 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: