behaves 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +29 -0
- data/lib/behaves.rb +61 -11
- data/lib/behaves/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c594bfaa286e772150c5e2c31c24fe2b4684cd4fbd68f16341cc30b6c9ebfe0
|
4
|
+
data.tar.gz: 5aa8758aee6a60747332ac0515a73a1bc3a63d3336870237b13290b09f1cda39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87e3bf7f5c4c218c7e2cd8de1540577455cb51340bfc51f28fc6043a025315af8d07fe8802cdd750488152eb9649b219147534cc17ea08682c7b8b410312e476
|
7
|
+
data.tar.gz: 004aa2b59f31bd916b4df9380ef8d9a5bb3f6a24d88a09c549a85fbfd5b5a6cfc74d379d6c24681e19e6461aac9856be72253358ef66690da4e625f183b058c6
|
data/Gemfile.lock
CHANGED
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
|
|
data/lib/behaves.rb
CHANGED
@@ -2,8 +2,15 @@ require 'behaves/version'
|
|
2
2
|
require 'set'
|
3
3
|
|
4
4
|
module Behaves
|
5
|
-
def implements(*methods)
|
6
|
-
@
|
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 {
|
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
|
-
|
24
|
-
|
25
|
-
unimplemented = required - implemented
|
33
|
+
unimplemented = required - implemented(scope)
|
26
34
|
|
27
35
|
return if unimplemented.empty?
|
28
36
|
|
29
|
-
|
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("@
|
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."
|
data/lib/behaves/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2019-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|