surrounded 0.2.1 → 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/README.md +35 -3
- data/lib/surrounded/context.rb +33 -10
- data/lib/surrounded/context/negotiator.rb +28 -0
- data/lib/surrounded/version.rb +1 -1
- data/surrounded.gemspec +2 -1
- data/test/example_proxy_test.rb +55 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e6e141d7aa5fdf78d69d250369fa15f2f6b5f6d
|
4
|
+
data.tar.gz: 5251f5c762065460afa6da69f0da2bf2f68529dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7188a2fadebd4d332f402c8fba1cabb77968623a698a02a083ef59266a1ee8b4223216f5aa70d9c5886ddc8af1fef9058686a67ce512ab94e4571cc42e23da39
|
7
|
+
data.tar.gz: 0ca5c9b9ead86e6d87d0209c5fa0e59cb4b67ac957c0d0425f85704270f1879702cade79de3af98323df85ff1dc9fe2a3a22a7a19e789adf58bcdda534ea89d9
|
data/README.md
CHANGED
@@ -71,7 +71,7 @@ If you're going to be doing this a lot, it's painful. Here's what `Surrounded` d
|
|
71
71
|
class MyEnvironment
|
72
72
|
extend Surrounded::Context
|
73
73
|
|
74
|
-
|
74
|
+
initialize(:employee, :boss)
|
75
75
|
|
76
76
|
module Employee
|
77
77
|
# extra behavior here...
|
@@ -97,7 +97,7 @@ With that, all instances of `User` have implicit access to their surroundings.
|
|
97
97
|
|
98
98
|
_Yeah... How?_
|
99
99
|
|
100
|
-
Via `method_missing` those `User` instances can access a `context` object
|
100
|
+
Via `method_missing` those `User` instances can access a `context` object it stores in a `@__surroundings__` collection. I didn't mention how the context is set, however.
|
101
101
|
|
102
102
|
Your environment will have methods of it's own that will trigger actions on the objects inside, but we need those trigger methods to set the environment instance as the current context so that the objects it contains can access them.
|
103
103
|
|
@@ -192,7 +192,7 @@ class ActiviatingAccount
|
|
192
192
|
apply_roles_on(:trigger) # this is the default
|
193
193
|
# apply_roles_on(:initialize) # set this to apply behavior from the start
|
194
194
|
|
195
|
-
|
195
|
+
initialize(:activator, :account)
|
196
196
|
|
197
197
|
module Activator
|
198
198
|
def some_behavior; end
|
@@ -228,6 +228,38 @@ context.do_something
|
|
228
228
|
current_user.some_behavior # NoMethodError
|
229
229
|
```
|
230
230
|
|
231
|
+
## How's the performance?
|
232
|
+
|
233
|
+
I haven't really tested yet, but there are several ways you can add behavior to your objects.
|
234
|
+
|
235
|
+
There are a few defaults built in.
|
236
|
+
|
237
|
+
1. If you define modules for the added behavior, the code will run `object.extend(RoleInterface)`
|
238
|
+
2. If you are using [casting](http://github.com/saturnflyer/casting), the code will run `object.cast_as(RoleInterface)`
|
239
|
+
3. If you would rather use wrappers you can define classes and the code will run `RoleInterface.new(object)` and assumes that the `new` method takes 1 argument. You'll need to remember to `include Surrounded` in your classes, however.
|
240
|
+
4. If you want to use wrappers but would rather not muck about with including modules and whatnot, you can define them like this:
|
241
|
+
|
242
|
+
```
|
243
|
+
class SomeContext
|
244
|
+
extend Surrounded::Context
|
245
|
+
|
246
|
+
initialize(:admin, :user)
|
247
|
+
|
248
|
+
wrap :admin do
|
249
|
+
# special methods defined here
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
The `wrap` method will create a class of the given name (`Admin` in this case) and will inherit from `SimpleDelegator` from the Ruby standard library _and_ will `include Surrounded`.
|
254
|
+
|
255
|
+
Lastly, there's a 5th option if you're using Ruby 2.x: `interface`.
|
256
|
+
|
257
|
+
The `interface` method acts similarly to the `wrap` method in that it returns an object that is not actually the object you want. But an `interface` is different in that it will apply methods from a module instead of using methods defined in a SimpleDelegator subclass. How is that important? Well you are free to use things like instance variables in your methods because they will be executed in the context of the object. This is unlike methods in a SimpleDelegator where the wrapper maintains its own instance variables.
|
258
|
+
|
259
|
+
_Which should I use?_
|
260
|
+
|
261
|
+
Start with the default and see how it goes, then try another approach and measure the changes.
|
262
|
+
|
231
263
|
## Dependencies
|
232
264
|
|
233
265
|
The dependencies are minimal. The plan is to keep it that way but allow you to configure things as you need.
|
data/lib/surrounded/context.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'surrounded/context/role_map'
|
3
|
+
require 'redcard'
|
4
|
+
|
3
5
|
module Surrounded
|
4
6
|
module Context
|
5
7
|
def self.extended(base)
|
@@ -7,12 +9,14 @@ module Surrounded
|
|
7
9
|
base.singleton_class.send(:alias_method, :setup, :initialize)
|
8
10
|
end
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
+
def new(*)
|
13
|
+
instance = super
|
14
|
+
instance.instance_variable_set('@__apply_role_policy', __apply_role_policy)
|
15
|
+
instance
|
12
16
|
end
|
13
17
|
|
14
|
-
def
|
15
|
-
@
|
18
|
+
def triggers
|
19
|
+
@triggers.dup
|
16
20
|
end
|
17
21
|
|
18
22
|
private
|
@@ -24,8 +28,26 @@ module Surrounded
|
|
24
28
|
klass.send(:include, Surrounded)
|
25
29
|
end
|
26
30
|
|
31
|
+
if RedCard.check '2.0'
|
32
|
+
def interface(name, &block)
|
33
|
+
class_basename = name.to_s.gsub(/(?:^|_)([a-z])/){ $1.upcase }
|
34
|
+
interface_name = class_basename + 'Interface'
|
35
|
+
|
36
|
+
behavior = const_set(interface_name, Module.new(&block))
|
37
|
+
|
38
|
+
require 'surrounded/context/negotiator'
|
39
|
+
define_method(name) do
|
40
|
+
instance_variable_set("@#{name}", Negotiator.new(role_map.assigned_player(name), behavior))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
27
45
|
def apply_roles_on(which)
|
28
|
-
@
|
46
|
+
@__apply_role_policy = which
|
47
|
+
end
|
48
|
+
|
49
|
+
def __apply_role_policy
|
50
|
+
@__apply_role_policy ||= :trigger
|
29
51
|
end
|
30
52
|
|
31
53
|
def initialize(*setup_args)
|
@@ -33,9 +55,10 @@ module Surrounded
|
|
33
55
|
|
34
56
|
class_eval "
|
35
57
|
def initialize(#{setup_args.join(',')})
|
58
|
+
@__apply_role_policy = :#{__apply_role_policy}
|
36
59
|
arguments = method(__method__).parameters.map{|arg| eval(arg[1].to_s) }
|
37
60
|
map_roles(#{setup_args}.zip(arguments))
|
38
|
-
apply_roles if
|
61
|
+
apply_roles if __apply_role_policy == :initialize
|
39
62
|
end
|
40
63
|
"
|
41
64
|
end
|
@@ -54,12 +77,12 @@ module Surrounded
|
|
54
77
|
|
55
78
|
define_method(name, *args){
|
56
79
|
begin
|
57
|
-
apply_roles if
|
80
|
+
apply_roles if __apply_role_policy == :trigger
|
58
81
|
|
59
82
|
self.send("trigger_#{name}", *args)
|
60
83
|
|
61
84
|
ensure
|
62
|
-
remove_roles if
|
85
|
+
remove_roles if __apply_role_policy == :trigger
|
63
86
|
end
|
64
87
|
}
|
65
88
|
end
|
@@ -97,8 +120,8 @@ module Surrounded
|
|
97
120
|
role_map.update(role, mod_name, object)
|
98
121
|
end
|
99
122
|
|
100
|
-
def
|
101
|
-
@
|
123
|
+
def __apply_role_policy
|
124
|
+
@__apply_role_policy
|
102
125
|
end
|
103
126
|
|
104
127
|
def add_interface(role, behavior, object)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Surrounded
|
2
|
+
module Context
|
3
|
+
class Negotiator
|
4
|
+
identity = "__send__|object_id"
|
5
|
+
|
6
|
+
instance_methods.each do |meth|
|
7
|
+
unless meth.to_s =~ /#{identity}/
|
8
|
+
undef_method meth
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def initialize(object, behavior)
|
15
|
+
@object, @behavior = object, behavior
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(meth, *args, &block)
|
19
|
+
if @behavior.instance_methods.include?(meth)
|
20
|
+
the_method = @behavior.instance_method(meth)
|
21
|
+
the_method.bind(@object).call(*args, &block)
|
22
|
+
else
|
23
|
+
@object.send(meth, *args, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/surrounded/version.rb
CHANGED
data/surrounded.gemspec
CHANGED
@@ -18,7 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "triad",
|
21
|
+
spec.add_dependency "triad", "~> 0.1.2"
|
22
|
+
spec.add_dependency "redcard","~> 1.1.0"
|
22
23
|
|
23
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
24
25
|
spec.add_development_dependency "rake"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
if RedCard.check '2.0'
|
4
|
+
|
5
|
+
# If you want to use wrappers, here's how you could
|
6
|
+
class ProxyContext
|
7
|
+
extend Surrounded::Context
|
8
|
+
|
9
|
+
apply_roles_on(:trigger)
|
10
|
+
initialize(:admin, :task)
|
11
|
+
|
12
|
+
interface :admin do
|
13
|
+
def some_admin_method
|
14
|
+
"hello from #{name}, the admin interface!"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
trigger :do_something do
|
19
|
+
admin.some_admin_method
|
20
|
+
end
|
21
|
+
|
22
|
+
trigger :admin_name do
|
23
|
+
admin.name
|
24
|
+
end
|
25
|
+
|
26
|
+
trigger :admin_missing_method do
|
27
|
+
admin.method_that_does_not_exist
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
ProxyUser = Struct.new(:name)
|
32
|
+
|
33
|
+
describe ProxyContext do
|
34
|
+
let(:user){
|
35
|
+
ProxyUser.new('Jim')
|
36
|
+
}
|
37
|
+
let(:context){
|
38
|
+
ProxyContext.new(user, Object.new)
|
39
|
+
}
|
40
|
+
it 'proxys methods between objects and its interface' do
|
41
|
+
assert_equal 'hello from Jim, the admin interface!', context.do_something
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'forwards methods that the object responds to' do
|
45
|
+
assert_equal 'Jim', context.admin_name
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'passes missing methods up the ancestry of the object' do
|
49
|
+
err = ->{ context.admin_missing_method }.must_raise(NoMethodError)
|
50
|
+
|
51
|
+
assert_match 'ProxyUser name="Jim"', err.message
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end # RedCard.check '2.0'
|
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.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- '''Jim Gay'''
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: triad
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.1.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redcard
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,10 +84,12 @@ files:
|
|
70
84
|
- examples/wrappers.rb
|
71
85
|
- lib/surrounded.rb
|
72
86
|
- lib/surrounded/context.rb
|
87
|
+
- lib/surrounded/context/negotiator.rb
|
73
88
|
- lib/surrounded/context/role_map.rb
|
74
89
|
- lib/surrounded/context_errors.rb
|
75
90
|
- lib/surrounded/version.rb
|
76
91
|
- surrounded.gemspec
|
92
|
+
- test/example_proxy_test.rb
|
77
93
|
- test/example_threaded_test.rb
|
78
94
|
- test/example_wrapper_test.rb
|
79
95
|
- test/surrounded_context_test.rb
|
@@ -104,6 +120,7 @@ signing_key:
|
|
104
120
|
specification_version: 4
|
105
121
|
summary: Create encapsulated environments for your objects.
|
106
122
|
test_files:
|
123
|
+
- test/example_proxy_test.rb
|
107
124
|
- test/example_threaded_test.rb
|
108
125
|
- test/example_wrapper_test.rb
|
109
126
|
- test/surrounded_context_test.rb
|