surrounded 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -7
- data/lib/surrounded.rb +3 -1
- data/lib/surrounded/access_control.rb +1 -1
- data/lib/surrounded/context.rb +12 -0
- data/lib/surrounded/context/initializing.rb +0 -1
- data/lib/surrounded/context/negotiator.rb +10 -4
- data/lib/surrounded/context/role_builders.rb +11 -9
- data/lib/surrounded/context_errors.rb +9 -1
- data/lib/surrounded/version.rb +1 -1
- data/test/example_proxy_test.rb +28 -4
- data/test/role_context_method_test.rb +3 -2
- data/test/surrounded_context_test.rb +21 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8dcba3d11c7217ae50aa36d19f84c1a835734a22
|
4
|
+
data.tar.gz: c6981dbd112701103491e1919fa2819a334aedc5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 887d99c878678e8f8a49dd71b8214fc4766ba5028ff65071f9c0634946256503e0aee8ff1c55ef37751aee730026a117db76d9f6424239333b4fcdbe56f6435b
|
7
|
+
data.tar.gz: cf09e9f3710d6f726c9c58dbd08cb3087b14873aa9248a37f1d753d9c004a5a568d3a1204e14ccc2ed053f74f7be32e8d91764187e0e5cc8af64612f1b23f1b3
|
data/README.md
CHANGED
@@ -553,17 +553,18 @@ Using the `role` method to define modules and classes takes care of the setup fo
|
|
553
553
|
role :source, :interface do
|
554
554
|
def transfer
|
555
555
|
self.balance -= amount
|
556
|
-
|
556
|
+
# not able to access destination
|
557
|
+
# destination.balance += amount
|
557
558
|
self
|
558
559
|
end
|
559
560
|
end
|
560
561
|
```
|
561
562
|
|
562
|
-
The `:interface` option is a special object which has all of the standard Object methods removed (excepting `__send__` and `object_id`) so that other methods will be pulled from the ones that you define, or from the object it attempts to proxy.
|
563
|
+
The `:interface` option is a special object which has all of the standard Object methods removed (excepting ones like `__send__` and `object_id`) so that other methods will be pulled from the ones that you define, or from the object it attempts to proxy.
|
563
564
|
|
564
565
|
Notice that the `:interface` allows you to return `self` whereas the `:wrap` acts more like a wrapper and forces you to deal with that shortcoming by using it's wrapped-object-accessor method: `__getobj__`.
|
565
566
|
|
566
|
-
The downside of using an interface is that it is still a wrapper. All of your defined role methods are executed in the context of the object playing the role, but the interface has it's own identity.
|
567
|
+
The downside of using an interface is that it is still a wrapper and it doesn't have access to the other objects in the context. All of your defined role methods are executed in the context of the object playing the role, but the interface has it's own identity.
|
567
568
|
|
568
569
|
If you'd like to choose one and use it all the time, you can set the default:
|
569
570
|
|
@@ -571,13 +572,13 @@ If you'd like to choose one and use it all the time, you can set the default:
|
|
571
572
|
class MoneyTransfer
|
572
573
|
extend Surrounded::Context
|
573
574
|
|
574
|
-
self.default_role_type = :
|
575
|
+
self.default_role_type = :wrapper # also :wrap, :interface, or :module
|
575
576
|
|
576
577
|
role :source do
|
577
578
|
def transfer
|
578
579
|
self.balance -= amount
|
579
580
|
destination.balance += amount
|
580
|
-
|
581
|
+
__getobj__
|
581
582
|
end
|
582
583
|
end
|
583
584
|
end
|
@@ -586,7 +587,7 @@ end
|
|
586
587
|
Or, if you like, you can choose the default for your entire project:
|
587
588
|
|
588
589
|
```ruby
|
589
|
-
Surrounded::Context.default_role_type = :
|
590
|
+
Surrounded::Context.default_role_type = :wrap
|
590
591
|
|
591
592
|
class MoneyTransfer
|
592
593
|
extend Surrounded::Context
|
@@ -595,7 +596,7 @@ class MoneyTransfer
|
|
595
596
|
def transfer
|
596
597
|
self.balance -= amount
|
597
598
|
destination.balance += amount
|
598
|
-
|
599
|
+
__getobj__
|
599
600
|
end
|
600
601
|
end
|
601
602
|
end
|
@@ -717,6 +718,7 @@ class ActiviatingAccount
|
|
717
718
|
disallow :some_trigger_method do
|
718
719
|
# whatever conditional code for the instance of the context
|
719
720
|
end
|
721
|
+
# you could also use `guard` instead of `disallow`
|
720
722
|
|
721
723
|
# or define your own method without the `disallow` keyword
|
722
724
|
def disallow_some_trigger_method?
|
data/lib/surrounded.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "surrounded/version"
|
2
2
|
require "surrounded/context"
|
3
|
+
require "singleton"
|
3
4
|
|
4
5
|
# This module should be added to objects which will enter
|
5
6
|
# into context objects.
|
@@ -28,7 +29,7 @@ module Surrounded
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def context
|
31
|
-
surroundings.first || NullContext.
|
32
|
+
surroundings.first || NullContext.instance
|
32
33
|
end
|
33
34
|
|
34
35
|
def method_missing(meth, *args, &block)
|
@@ -40,6 +41,7 @@ module Surrounded
|
|
40
41
|
end
|
41
42
|
|
42
43
|
class NullContext
|
44
|
+
include Singleton
|
43
45
|
def role?(*args)
|
44
46
|
nil
|
45
47
|
end
|
data/lib/surrounded/context.rb
CHANGED
@@ -116,8 +116,20 @@ module Surrounded
|
|
116
116
|
self.class.triggers
|
117
117
|
end
|
118
118
|
|
119
|
+
def rebind(options_hash)
|
120
|
+
clear_instance_variables
|
121
|
+
initialize(options_hash.to_a)
|
122
|
+
rescue ArgumentError
|
123
|
+
initialize(*options_hash.values)
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
119
127
|
private
|
120
128
|
|
129
|
+
def clear_instance_variables
|
130
|
+
instance_variables.each{|ivar| remove_instance_variable(ivar) }
|
131
|
+
end
|
132
|
+
|
121
133
|
def role_map
|
122
134
|
@role_map ||= role_mapper_class.new
|
123
135
|
end
|
@@ -14,24 +14,30 @@ module Surrounded
|
|
14
14
|
end
|
15
15
|
}, __FILE__, num
|
16
16
|
end
|
17
|
+
klass.send(:define_method, :__behaviors__) do
|
18
|
+
mod
|
19
|
+
end
|
17
20
|
klass
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
24
|
|
22
|
-
identity =
|
25
|
+
identity = %w[__send__ object_id equal?]
|
26
|
+
method_access = %w[respond_to? method __behaviors__]
|
27
|
+
|
28
|
+
reserved_methods = (identity + method_access).join('|')
|
23
29
|
|
24
30
|
# Remove all methods except the identity methods
|
25
31
|
instance_methods.reject{ |m|
|
26
|
-
m.to_s =~ /#{
|
32
|
+
m.to_s =~ /#{reserved_methods}/
|
27
33
|
}.each do |meth|
|
28
34
|
undef_method meth
|
29
35
|
end
|
30
36
|
|
31
37
|
private
|
32
38
|
|
33
|
-
def initialize(object
|
34
|
-
@object, @behaviors = object,
|
39
|
+
def initialize(object)
|
40
|
+
@object, @behaviors = object, __behaviors__
|
35
41
|
end
|
36
42
|
|
37
43
|
def method_missing(meth, *args, &block)
|
@@ -13,7 +13,7 @@ module Surrounded
|
|
13
13
|
meth.call(name, &block)
|
14
14
|
end
|
15
15
|
rescue NameError => e
|
16
|
-
raise e.
|
16
|
+
raise InvalidRoleType, e.message
|
17
17
|
end
|
18
18
|
alias_method :role_methods, :role
|
19
19
|
|
@@ -36,20 +36,22 @@ module Surrounded
|
|
36
36
|
klass.send(:include, Surrounded)
|
37
37
|
end
|
38
38
|
|
39
|
-
# Create an object which will bind methods to the role player
|
39
|
+
# Create an object which will bind methods to the role player.
|
40
|
+
#
|
41
|
+
# This object will behave differently that a wrapper or delegate_class.
|
42
|
+
# The interface object should only be used for objects whose methods
|
43
|
+
# _will not_ call to the other objects in the context.
|
44
|
+
# Because the interface methods are applied individually to an object,
|
45
|
+
# that object is unaware of the other objects in the context and cannot
|
46
|
+
# access them from any of its methods.
|
40
47
|
def interface(name, &block)
|
41
48
|
# AdminInterface
|
42
49
|
interface_name = RoleName(name, 'Interface')
|
43
50
|
behavior = private_const_set(interface_name, Module.new(&block))
|
44
51
|
|
45
52
|
require 'surrounded/context/negotiator'
|
46
|
-
|
47
|
-
|
48
|
-
# AdminInterfaceProxy
|
49
|
-
proxy = private_const_set(RoleName(interface_name, 'Proxy'), Negotiator.for_role(behavior))
|
50
|
-
define_method(name) do
|
51
|
-
instance_variable_set("@#{name}", proxy.new(role_map.assigned_player(name), behavior))
|
52
|
-
end
|
53
|
+
# Admin
|
54
|
+
private_const_set(RoleName(name), Negotiator.for_role(behavior))
|
53
55
|
end
|
54
56
|
|
55
57
|
private
|
@@ -2,7 +2,15 @@ require 'triad'
|
|
2
2
|
module Surrounded
|
3
3
|
module Context
|
4
4
|
class InvalidRole < ::Triad::ItemNotPresent; end
|
5
|
-
|
5
|
+
class InvalidRoleType < ::StandardError
|
6
|
+
unless method_defined?(:cause)
|
7
|
+
def initialize(msg=nil)
|
8
|
+
super
|
9
|
+
@cause = $!
|
10
|
+
end
|
11
|
+
attr_reader :cause
|
12
|
+
end
|
13
|
+
end
|
6
14
|
class AccessError < ::StandardError; end
|
7
15
|
end
|
8
16
|
end
|
data/lib/surrounded/version.rb
CHANGED
data/test/example_proxy_test.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
# If you want to use wrappers, here's how you could
|
4
3
|
class ProxyContext
|
5
4
|
extend Surrounded::Context
|
6
5
|
|
@@ -34,10 +33,17 @@ class ProxyContext
|
|
34
33
|
trigger :admin_missing_method do
|
35
34
|
admin.method_that_does_not_exist
|
36
35
|
end
|
36
|
+
|
37
|
+
trigger :admin_responds? do
|
38
|
+
admin.respond_to?(:talking_to_others)
|
39
|
+
end
|
40
|
+
|
41
|
+
trigger :get_admin_method do
|
42
|
+
admin.method(:talking_to_others)
|
43
|
+
end
|
37
44
|
end
|
38
45
|
|
39
46
|
ProxyUser = Class.new do
|
40
|
-
include Surrounded
|
41
47
|
def initialize(name)
|
42
48
|
@name = name
|
43
49
|
end
|
@@ -68,7 +74,25 @@ describe ProxyContext do
|
|
68
74
|
assert_match(/ProxyUser.*name="Jim"/, err.message)
|
69
75
|
end
|
70
76
|
|
71
|
-
it '
|
72
|
-
|
77
|
+
it 'fails access to other objects in the context' do
|
78
|
+
err = _{ context.talking }.must_raise NameError
|
79
|
+
assert_match(%r{undefined local variable or method `task'}, err.message)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'sets roles to respond to role methods' do
|
83
|
+
assert context.admin_responds?
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'is able to grab methods from the object' do
|
87
|
+
assert_equal :talking_to_others, context.get_admin_method.name
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'works with frozen and primitive objects' do
|
91
|
+
context.rebind(admin: "brrr".freeze, task: task)
|
92
|
+
assert context.get_admin_method
|
93
|
+
context.rebind(admin: nil, task: task)
|
94
|
+
assert context.get_admin_method
|
95
|
+
context.rebind(admin: true, task: task)
|
96
|
+
assert context.get_admin_method
|
73
97
|
end
|
74
98
|
end
|
@@ -89,14 +89,15 @@ describe Surrounded::Context, '.role' do
|
|
89
89
|
|
90
90
|
describe 'unknown' do
|
91
91
|
it 'raises an error' do
|
92
|
-
|
92
|
+
err = _{
|
93
93
|
class UnknownRole
|
94
94
|
extend Surrounded::Context
|
95
95
|
|
96
96
|
role :admin, :unknown do
|
97
97
|
end
|
98
98
|
end
|
99
|
-
}
|
99
|
+
}.must_raise Surrounded::Context::InvalidRoleType
|
100
|
+
_(err.cause).must_be_kind_of NameError
|
100
101
|
end
|
101
102
|
end
|
102
103
|
|
@@ -259,6 +259,27 @@ describe Surrounded::Context, 'auto-assigning roles for collections' do
|
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|
262
|
+
describe Surrounded::Context, 'reusing context object' do
|
263
|
+
let(:user){ User.new("Jim") }
|
264
|
+
let(:other_user){ User.new("Guille") }
|
265
|
+
let(:context){ TestContext.new(user, other_user) }
|
266
|
+
|
267
|
+
it 'allows rebinding new players' do
|
268
|
+
expect(context.access_other_object).must_equal 'Guille'
|
269
|
+
context.rebind(user: User.new('Amy'), other_user: User.new('Elizabeth'))
|
270
|
+
expect(context.access_other_object).must_equal 'Elizabeth'
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'clears internal storage when rebinding' do
|
274
|
+
originals = context.instance_variables.map{|var| context.instance_variable_get(var) }
|
275
|
+
context.rebind(user: User.new('Amy'), other_user: User.new('Elizabeth'))
|
276
|
+
new_ivars = context.instance_variables.map{|var| context.instance_variable_get(var) }
|
277
|
+
originals.zip(new_ivars).each do |original_ivar, new_ivar|
|
278
|
+
expect(original_ivar).wont_equal new_ivar
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
262
283
|
begin
|
263
284
|
class Keyworder
|
264
285
|
extend Surrounded::Context
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surrounded
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "'Jim Gay'"
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: triad
|