behaves 0.2.0 → 0.3.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
  SHA256:
3
- metadata.gz: cd22ae5f7d6af9cc2a76a8d7b73d7ec25af7c759afac262731ef56c79f1d9a3b
4
- data.tar.gz: b5abbc034aa95a5067bed0ce7eb2f5472c6b3e89333d8128806499ea25d38fdc
3
+ metadata.gz: 4c594bfaa286e772150c5e2c31c24fe2b4684cd4fbd68f16341cc30b6c9ebfe0
4
+ data.tar.gz: 5aa8758aee6a60747332ac0515a73a1bc3a63d3336870237b13290b09f1cda39
5
5
  SHA512:
6
- metadata.gz: a809170ad8c43554ee5a6845ec558ce803a766ec216a088c7fb21b6b92c355dc5d618b340a48fdf5eb4cb80079d40c429622d8a344998a3a278454aa84414129
7
- data.tar.gz: 1ed4e8d27bd2171cc0e2020c1fa869ff099c887ebaeb2ef35c4d639c715d5b5e3787910e272a99b7a5c0b5fe0e31fc0af29715f1de40364652bd32bca113c4ac
6
+ metadata.gz: 87e3bf7f5c4c218c7e2cd8de1540577455cb51340bfc51f28fc6043a025315af8d07fe8802cdd750488152eb9649b219147534cc17ea08682c7b8b410312e476
7
+ data.tar.gz: 004aa2b59f31bd916b4df9380ef8d9a5bb3f6a24d88a09c549a85fbfd5b5a6cfc74d379d6c24681e19e6461aac9856be72253358ef66690da4e625f183b058c6
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- behaves (0.1.1)
4
+ behaves (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -144,6 +144,35 @@ This extends to more than just method implementation too, you can do anything yo
144
144
 
145
145
  *Do note that if you use this extensively, you might be better off using inheritance, since this will create more `Method` objects than inheritance.*
146
146
 
147
+ ### Private Behaviors
148
+
149
+ Private behaviors can be defined like so:
150
+
151
+ ```ruby
152
+ class Interface
153
+ extend Behaves
154
+
155
+ implements :foo
156
+ implements :bar, private: true
157
+ end
158
+
159
+ class Implementor
160
+ extend Behaves
161
+
162
+ behaves_like Interface
163
+
164
+ def foo
165
+ 123
166
+ end
167
+
168
+ private
169
+
170
+ def bar
171
+ 456
172
+ end
173
+ end
174
+ ```
175
+
147
176
  ## Tips
148
177
  If you do not want to type `extend Behaves` every time, you can monkey patch `Behaves` onto `Object` class, like so:
149
178
 
@@ -2,8 +2,15 @@ require 'behaves/version'
2
2
  require 'set'
3
3
 
4
4
  module Behaves
5
- def implements(*methods)
6
- @behaviors ||= Set.new(methods)
5
+ def implements(*methods, **opts)
6
+ @_public_behaviors ||= Set.new
7
+ @_private_behaviors ||= Set.new
8
+
9
+ if opts[:private] == true
10
+ @_private_behaviors += Set.new(methods)
11
+ else
12
+ @_public_behaviors += Set.new(methods)
13
+ end
7
14
  end
8
15
 
9
16
  def inject_behaviors (&block)
@@ -12,25 +19,68 @@ module Behaves
12
19
 
13
20
  def behaves_like(klass)
14
21
  add_injected_behaviors(klass)
15
- at_exit { check_for_unimplemented(klass) }
22
+ at_exit {
23
+ check_for_unimplemented(klass, :public)
24
+ check_for_unimplemented(klass, :private)
25
+ }
16
26
  end
17
27
 
18
28
  private
19
29
 
20
- def check_for_unimplemented(klass)
21
- required = defined_behaviors(klass)
30
+ def check_for_unimplemented(klass, scope)
31
+ required = defined_behaviors(klass, scope)
22
32
 
23
- implemented = Set.new(self.instance_methods - Object.instance_methods)
24
-
25
- unimplemented = required - implemented
33
+ unimplemented = required - implemented(scope)
26
34
 
27
35
  return if unimplemented.empty?
28
36
 
29
- raise NotImplementedError, "Expected `#{self}` to behave like `#{klass}`, but `#{unimplemented.to_a.join(', ')}` are not implemented."
37
+
38
+ # basic "unimplemented method" error message
39
+
40
+ err = <<~ERR
41
+ \n\n Expected `#{self}` to behave like `#{klass}`, but the following #{scope} methods are unimplemented:\n
42
+ #{unimplemented.to_a.map{|method| " * `#{method}`"}.join("\n")}\n
43
+ ERR
44
+
45
+
46
+ # add "wrong scope" message
47
+
48
+ other_scopes = (scope == :public) ? [:private] : [:public]
49
+ methods_in_other_scopes = Set.new( other_scopes.map{|s| implemented(s)}.map(&:to_a).flatten.compact )
50
+ methods_in_wrong_scope = unimplemented && methods_in_other_scopes
51
+
52
+ if !methods_in_wrong_scope.empty?
53
+ err += <<~ERR
54
+ \n The following methods appear to be defined, but in the wrong scope:\n
55
+ #{methods_in_wrong_scope.to_a.map{|method| " * `#{method}`"}.join("\n")}\n\n
56
+ ERR
57
+ end
58
+
59
+
60
+ raise NotImplementedError, err
61
+ end
62
+
63
+ def implemented(scope)
64
+ lookups = {
65
+ public: :instance_methods,
66
+ private: :private_instance_methods,
67
+ }
68
+
69
+ method_lookup_sym = lookups.fetch(scope) do
70
+ raise ArgumentError.new <<~ERR
71
+
72
+ Invalid `scope`: #{scope}
73
+ Valid `scope`s include: #{lookups.keys.map{|sym| "`#{sym.inspect}`"}.join(', ')}
74
+ ERR
75
+ end
76
+
77
+ methods = self.send(method_lookup_sym, false)
78
+
79
+ Set.new(methods)
30
80
  end
31
81
 
32
- def defined_behaviors(klass)
33
- if behaviors = klass.instance_variable_get("@behaviors")
82
+ def defined_behaviors(klass, scope)
83
+ if behaviors = klass.instance_variable_get(:"@_#{scope}_behaviors")
34
84
  behaviors
35
85
  else
36
86
  raise NotImplementedError, "Expected `#{klass}` to define behaviors, but none found."
@@ -1,3 +1,3 @@
1
1
  module Behaves
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: behaves
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edison Yap
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-18 00:00:00.000000000 Z
11
+ date: 2019-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry