surrounded 0.1.0 → 0.2.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 +4 -4
- data/README.md +64 -10
- data/examples/rails.rb +4 -30
- data/examples/wrappers.rb +39 -0
- data/lib/surrounded.rb +43 -5
- data/lib/surrounded/context.rb +126 -31
- data/lib/surrounded/context/role_map.rb +21 -0
- data/lib/surrounded/context_errors.rb +6 -0
- data/lib/surrounded/version.rb +1 -1
- data/surrounded.gemspec +2 -0
- data/test/example_threaded_test.rb +70 -0
- data/test/example_wrapper_test.rb +30 -0
- data/test/surrounded_context_test.rb +15 -6
- data/test/surrounded_test.rb +33 -3
- data/test/test_helper.rb +4 -1
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ffb676825adabf9eeb3f049b5bee0db872d4647
|
4
|
+
data.tar.gz: 77088aade1c2e9079b07546681e8e1997b7aab1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a890fa34e1b6ebd9c1f6646f2fb5d0ea2ea6992e7b8d9a63d84d023267eb2ea1e9898e808ec98ece3995cea8e801a5bfc9da3f4da108cbb3445b336c40b1eb2
|
7
|
+
data.tar.gz: 439627c4612ddfade5819fe832a1799127d179a50ca92fa386efadf60ea51f31a2c9fcbd487581182831eda7ddbee4a8f57cdcb367a3c4cb667cdd44e92e35c0
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/saturnflyer/surrounded)
|
4
4
|
[](https://codeclimate.com/github/saturnflyer/surrounded)
|
5
5
|
[](https://coveralls.io/r/saturnflyer/surrounded)
|
6
|
+
[](http://badge.fury.io/rb/surrounded)
|
6
7
|
|
7
8
|
## Create encapsulated environments for your objects.
|
8
9
|
|
@@ -45,7 +46,7 @@ class MyEnvironment
|
|
45
46
|
attr_reader :employee, :boss
|
46
47
|
private :employee, :boss
|
47
48
|
def initialize(employee, boss)
|
48
|
-
@employee = employee.extend(
|
49
|
+
@employee = employee.extend(Employee)
|
49
50
|
@boss = boss
|
50
51
|
end
|
51
52
|
|
@@ -107,9 +108,9 @@ class MyEnvironment
|
|
107
108
|
# other stuff from above is still here...
|
108
109
|
|
109
110
|
def shove_it
|
110
|
-
|
111
|
+
employee.store_context(self)
|
111
112
|
employee.quit
|
112
|
-
|
113
|
+
employee.remove_context
|
113
114
|
end
|
114
115
|
|
115
116
|
module Employee
|
@@ -141,7 +142,7 @@ _OK. I think I understand. So I can change business logic just by changing the p
|
|
141
142
|
|
142
143
|
Damn right.
|
143
144
|
|
144
|
-
But you don't want to continually set those
|
145
|
+
But you don't want to continually set those context details, do you?
|
145
146
|
|
146
147
|
_No. That's annoying._
|
147
148
|
|
@@ -158,7 +159,7 @@ class MyEnvironment
|
|
158
159
|
end
|
159
160
|
```
|
160
161
|
|
161
|
-
By using this `trigger` keyword, our block is the code we care about, but internally the method is written to set the `
|
162
|
+
By using this `trigger` keyword, our block is the code we care about, but internally the method is written to set the `@__surroundings__` collection.
|
162
163
|
|
163
164
|
_Hmm. I don't like having to do that._
|
164
165
|
|
@@ -172,21 +173,74 @@ context.triggers #=> [:shove_it]
|
|
172
173
|
|
173
174
|
You might find that useful for dynamically defining user interfaces.
|
174
175
|
|
176
|
+
## Policies for the application of role methods
|
177
|
+
|
178
|
+
There are 2 approaches to applying new behavior to your objects.
|
179
|
+
|
180
|
+
By default your context will add methods to an object before a trigger is run
|
181
|
+
and behaviors will be removed after the trigger is run.
|
182
|
+
|
183
|
+
Alternatively you may set the behaviors to be added during the initialize method
|
184
|
+
of your context.
|
185
|
+
|
186
|
+
Here's how it works:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
class ActiviatingAccount
|
190
|
+
extend Surrounded::Context
|
191
|
+
|
192
|
+
apply_roles_on(:trigger) # this is the default
|
193
|
+
# apply_roles_on(:initialize) # set this to apply behavior from the start
|
194
|
+
|
195
|
+
setup(:activator, :account)
|
196
|
+
|
197
|
+
module Activator
|
198
|
+
def some_behavior; end
|
199
|
+
end
|
200
|
+
|
201
|
+
def non_trigger_method
|
202
|
+
activator.some_behavior # not available unless you apply roles on initialize
|
203
|
+
end
|
204
|
+
|
205
|
+
trigger :some_trigger_method do
|
206
|
+
activator.some_behavior # always available
|
207
|
+
end
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
_Why are those options there?_
|
212
|
+
|
213
|
+
When you initialize a context and apply behavior at the same time, you'll need
|
214
|
+
to remove that behavior. For example, if you are using Casting AND you apply roles on initialize:
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
context = ActiviatingAccount.new(current_user, Account.find(123))
|
218
|
+
context.do_something
|
219
|
+
current_user.some_behavior # this method is still available
|
220
|
+
current_user.uncast # you'll have to manually cleanup
|
221
|
+
```
|
222
|
+
|
223
|
+
But if you go with the default and apply behaviors on trigger, your roles will be cleaned up automatically:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
context = ActiviatingAccount.new(current_user, Account.find(123))
|
227
|
+
context.do_something
|
228
|
+
current_user.some_behavior # NoMethodError
|
229
|
+
```
|
230
|
+
|
175
231
|
## Dependencies
|
176
232
|
|
177
233
|
The dependencies are minimal. The plan is to keep it that way but allow you to configure things as you need.
|
178
234
|
|
179
235
|
If you're using [Casting](http://github.com/saturnflyer/casting), for example, Surrounded will attempt to use that before extending an object, but it will still work without it.
|
180
236
|
|
181
|
-
## To Do
|
182
|
-
|
183
|
-
Casting provides a way to remove features outside of a block. For now, the code doesn't attempt to `uncast` an object. It will in the future though.
|
184
|
-
|
185
237
|
## Installation
|
186
238
|
|
187
239
|
Add this line to your application's Gemfile:
|
188
240
|
|
189
|
-
|
241
|
+
```ruby
|
242
|
+
gem 'surrounded'
|
243
|
+
```
|
190
244
|
|
191
245
|
And then execute:
|
192
246
|
|
data/examples/rails.rb
CHANGED
@@ -18,13 +18,13 @@ end
|
|
18
18
|
class SomeUseCase
|
19
19
|
extend Surrounded::Context
|
20
20
|
|
21
|
-
setup(:
|
21
|
+
setup(:admin, :other_user, :listener)
|
22
22
|
|
23
23
|
trigger :do_something do
|
24
|
-
|
24
|
+
admin.something
|
25
25
|
end
|
26
26
|
|
27
|
-
module
|
27
|
+
module Admin
|
28
28
|
def something
|
29
29
|
puts "Hello, #{other_user}"
|
30
30
|
listener.redirect_to('/')
|
@@ -33,33 +33,7 @@ class SomeUseCase
|
|
33
33
|
end
|
34
34
|
|
35
35
|
class SomethingController < ApplicationController
|
36
|
-
use_case = :SomeUseCase
|
37
36
|
def create
|
38
|
-
|
39
|
-
trigger :do_something, current_user, User.last
|
40
|
-
end
|
41
|
-
|
42
|
-
def trigger(meth, *args)
|
43
|
-
use_case.new(*(args << self)).send(meth)
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.use_case=(name)
|
47
|
-
@_default_use_case_name = name
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.use_case
|
51
|
-
@_default_use_case_name.to_s.constantize
|
52
|
-
end
|
53
|
-
|
54
|
-
def use_case=(name)
|
55
|
-
@_use_case_name = name
|
56
|
-
end
|
57
|
-
|
58
|
-
def use_case
|
59
|
-
if defined?(@_use_case_name)
|
60
|
-
@_use_case_name.to_s.constantize
|
61
|
-
else
|
62
|
-
self.class.use_case
|
63
|
-
end
|
37
|
+
SomeUseCase.new(current_user, User.last, self).do_something
|
64
38
|
end
|
65
39
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# If you want to use wrappers, here's how you could
|
2
|
+
|
3
|
+
require 'surrounded/context'
|
4
|
+
require 'delegate'
|
5
|
+
class WrapperContext
|
6
|
+
extend Surrounded::Context
|
7
|
+
|
8
|
+
apply_roles_on(:trigger)
|
9
|
+
setup(:admin, :task)
|
10
|
+
|
11
|
+
class Admin < SimpleDelegator
|
12
|
+
def some_admin_method
|
13
|
+
puts 'hello from the admin wrapper!'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
trigger :do_something do
|
18
|
+
admin.some_admin_method
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def add_role_methods(obj, wrapper)
|
24
|
+
assign_role(role_name(wrapper), wrapper.new(obj))
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_role_methods(obj, wrapper)
|
28
|
+
# in this case, the obj is already wrapped
|
29
|
+
core_object = self.send(role_name(wrapper)).__getobj__
|
30
|
+
assign_role(role_name(wrapper), core_object)
|
31
|
+
end
|
32
|
+
|
33
|
+
def role_name(klass)
|
34
|
+
klass.name.split("::").last.gsub(/([A-Z])/){ "_#{$1.downcase}" }.sub(/^_/,'')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context = WrapperContext.new(Object.new, Object.new)
|
39
|
+
context.do_something
|
data/lib/surrounded.rb
CHANGED
@@ -1,8 +1,51 @@
|
|
1
1
|
require "surrounded/version"
|
2
2
|
|
3
3
|
module Surrounded
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval {
|
6
|
+
extend Surrounded::Contextual
|
7
|
+
}
|
8
|
+
unless klass.is_a?(Class)
|
9
|
+
def klass.extended(object)
|
10
|
+
Surrounded.create_surroundings(object)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.extended(object)
|
16
|
+
Surrounded.create_surroundings(object)
|
17
|
+
end
|
18
|
+
|
19
|
+
module Contextual
|
20
|
+
def new(*args)
|
21
|
+
instance = super
|
22
|
+
Surrounded.create_surroundings(instance)
|
23
|
+
instance
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def store_context(ctxt)
|
28
|
+
surroundings.unshift(ctxt)
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_context
|
32
|
+
surroundings.shift
|
33
|
+
end
|
34
|
+
|
4
35
|
private
|
5
36
|
|
37
|
+
def self.create_surroundings(obj)
|
38
|
+
obj.instance_variable_set(:@__surroundings__, [])
|
39
|
+
end
|
40
|
+
|
41
|
+
def surroundings
|
42
|
+
@__surroundings__
|
43
|
+
end
|
44
|
+
|
45
|
+
def context
|
46
|
+
surroundings.first || NullContext.new
|
47
|
+
end
|
48
|
+
|
6
49
|
def method_missing(meth, *args, &block)
|
7
50
|
context.role?(meth){} || super
|
8
51
|
end
|
@@ -11,11 +54,6 @@ module Surrounded
|
|
11
54
|
!!context.role?(meth){} || super
|
12
55
|
end
|
13
56
|
|
14
|
-
def context
|
15
|
-
Thread.current[:context] ||= []
|
16
|
-
Thread.current[:context].first || NullContext.new
|
17
|
-
end
|
18
|
-
|
19
57
|
class NullContext < BasicObject
|
20
58
|
def role?(*args)
|
21
59
|
nil
|
data/lib/surrounded/context.rb
CHANGED
@@ -1,36 +1,50 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'surrounded/context/role_map'
|
2
3
|
module Surrounded
|
3
4
|
module Context
|
4
5
|
def self.extended(base)
|
5
6
|
base.send(:include, InstanceMethods)
|
7
|
+
base.singleton_class.send(:alias_method, :setup, :initialize)
|
6
8
|
end
|
7
9
|
|
8
10
|
def triggers
|
9
11
|
@triggers.dup
|
10
12
|
end
|
11
13
|
|
14
|
+
def policy
|
15
|
+
@policy ||= :trigger
|
16
|
+
end
|
17
|
+
|
12
18
|
private
|
13
19
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
20
|
+
def apply_roles_on(which)
|
21
|
+
@policy = which
|
22
|
+
end
|
17
23
|
|
18
|
-
|
19
|
-
|
24
|
+
def initialize(*setup_args)
|
25
|
+
private_attr_reader(*setup_args)
|
20
26
|
|
21
|
-
|
22
|
-
|
27
|
+
# I want this to work so I can set the arity on initialize:
|
28
|
+
# class_eval %Q<
|
29
|
+
# def initialize(#{*setup_args})
|
30
|
+
# arguments = parameters.map{|arg| eval(arg[1].to_s) }
|
31
|
+
# map_roles(setup_args.zip(arguments))
|
32
|
+
# apply_roles if policy == :initialize
|
33
|
+
# end
|
34
|
+
# >
|
23
35
|
|
24
|
-
|
25
|
-
|
26
|
-
end
|
36
|
+
define_method(:initialize){ |*args|
|
37
|
+
map_roles(setup_args.zip(args))
|
27
38
|
|
28
|
-
|
29
|
-
instance_variable_set("@#{role}", object)
|
30
|
-
}
|
39
|
+
apply_roles if policy == :initialize
|
31
40
|
}
|
32
41
|
end
|
33
42
|
|
43
|
+
def private_attr_reader(*method_names)
|
44
|
+
attr_reader(*method_names)
|
45
|
+
private(*method_names)
|
46
|
+
end
|
47
|
+
|
34
48
|
def trigger(name, *args, &block)
|
35
49
|
store_trigger(name)
|
36
50
|
|
@@ -40,10 +54,12 @@ module Surrounded
|
|
40
54
|
|
41
55
|
define_method(name, *args){
|
42
56
|
begin
|
43
|
-
|
57
|
+
apply_roles if policy == :trigger
|
58
|
+
|
44
59
|
self.send("trigger_#{name}", *args)
|
60
|
+
|
45
61
|
ensure
|
46
|
-
|
62
|
+
remove_roles if policy == :trigger
|
47
63
|
end
|
48
64
|
}
|
49
65
|
end
|
@@ -53,23 +69,11 @@ module Surrounded
|
|
53
69
|
@triggers << name
|
54
70
|
end
|
55
71
|
|
56
|
-
def self.classify_string(string)
|
57
|
-
string.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase }
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.modify(obj, mod)
|
61
|
-
return obj if mod.is_a?(Class)
|
62
|
-
if obj.respond_to?(:cast_as)
|
63
|
-
obj.cast_as(mod)
|
64
|
-
else
|
65
|
-
obj.extend(mod)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
72
|
module InstanceMethods
|
70
73
|
def role?(name, &block)
|
74
|
+
return false unless role_map.role?(name)
|
71
75
|
accessor = eval('self', block.binding)
|
72
|
-
|
76
|
+
role_map.role_player?(accessor) && role_map.assigned_player(name)
|
73
77
|
end
|
74
78
|
|
75
79
|
def triggers
|
@@ -78,8 +82,99 @@ module Surrounded
|
|
78
82
|
|
79
83
|
private
|
80
84
|
|
81
|
-
def
|
82
|
-
@
|
85
|
+
def role_map
|
86
|
+
@role_map ||= RoleMap.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def map_roles(role_object_array)
|
90
|
+
role_object_array.each do |role, object|
|
91
|
+
map_role(role, role_behavior_name(role), object)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def map_role(role, mod_name, object)
|
96
|
+
instance_variable_set("@#{role}", object)
|
97
|
+
role_map.update(role, mod_name, object)
|
98
|
+
end
|
99
|
+
|
100
|
+
def policy
|
101
|
+
@policy ||= self.class.policy
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_interface(role, behavior, object)
|
105
|
+
applicator = behavior.is_a?(Class) ? method(:add_class_interface) : method(:add_module_interface)
|
106
|
+
|
107
|
+
role_player = applicator.call(object, behavior)
|
108
|
+
map_role(role, role_module_basename(behavior), role_player)
|
109
|
+
role_player.store_context(self)
|
110
|
+
role_player
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_module_interface(obj, mod)
|
114
|
+
adder_name = module_extension_methods.find{|meth| obj.respond_to?(meth) }
|
115
|
+
return obj if !adder_name
|
116
|
+
|
117
|
+
obj.method(adder_name).call(mod)
|
118
|
+
obj
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_class_interface(obj, klass)
|
122
|
+
wrapper_name = wrap_methods.find{|meth| klass.respond_to?(meth) }
|
123
|
+
return obj if !wrapper_name
|
124
|
+
|
125
|
+
klass.method(wrapper_name).call(obj)
|
126
|
+
end
|
127
|
+
|
128
|
+
def remove_interface(role, behavior, object)
|
129
|
+
remover_name = (module_removal_methods + unwrap_methods).find{|meth| object.respond_to?(meth) }
|
130
|
+
return object if !remover_name
|
131
|
+
|
132
|
+
object.remove_context
|
133
|
+
role_player = object.method(remover_name).call
|
134
|
+
|
135
|
+
map_role(role, role_module_basename(behavior), role_player)
|
136
|
+
|
137
|
+
role_player
|
138
|
+
end
|
139
|
+
|
140
|
+
def apply_roles
|
141
|
+
traverse_map method(:add_interface)
|
142
|
+
end
|
143
|
+
|
144
|
+
def remove_roles
|
145
|
+
traverse_map method(:remove_interface)
|
146
|
+
end
|
147
|
+
|
148
|
+
def traverse_map(applicator)
|
149
|
+
role_map.each do |role, mod_name, object|
|
150
|
+
if self.class.const_defined?(mod_name)
|
151
|
+
applicator.call(role, self.class.const_get(mod_name), object)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def module_extension_methods
|
157
|
+
[:cast_as, :extend]
|
158
|
+
end
|
159
|
+
|
160
|
+
def wrap_methods
|
161
|
+
[:new]
|
162
|
+
end
|
163
|
+
|
164
|
+
def module_removal_methods
|
165
|
+
[:uncast]
|
166
|
+
end
|
167
|
+
|
168
|
+
def unwrap_methods
|
169
|
+
[:__getobj__]
|
170
|
+
end
|
171
|
+
|
172
|
+
def role_behavior_name(role)
|
173
|
+
role.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase }.sub(/_\d+/,'')
|
174
|
+
end
|
175
|
+
|
176
|
+
def role_module_basename(mod)
|
177
|
+
mod.to_s.split('::').last
|
83
178
|
end
|
84
179
|
end
|
85
180
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'triad'
|
2
|
+
require 'surrounded/context_errors'
|
3
|
+
module Surrounded
|
4
|
+
module Context
|
5
|
+
class RoleMap < Triad
|
6
|
+
def role?(role)
|
7
|
+
keys.include?(role)
|
8
|
+
end
|
9
|
+
|
10
|
+
def role_player?(object)
|
11
|
+
!values(object).empty?
|
12
|
+
rescue ::Triad::ValueNotPresent
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def assigned_player(role)
|
17
|
+
values(role).first
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/surrounded/version.rb
CHANGED
data/surrounded.gemspec
CHANGED
@@ -18,6 +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", "~> 0.1.2"
|
22
|
+
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
24
|
spec.add_development_dependency "rake"
|
23
25
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ThreadedContext
|
4
|
+
extend Surrounded::Context
|
5
|
+
|
6
|
+
def initialize(leader, members)
|
7
|
+
role_names = [:leader, :members]
|
8
|
+
role_players = [leader, members]
|
9
|
+
|
10
|
+
role_names.concat(members.map{|member| :"member_#{member.object_id}" })
|
11
|
+
role_players.concat(members)
|
12
|
+
|
13
|
+
map_roles(role_names.zip(role_players))
|
14
|
+
|
15
|
+
end
|
16
|
+
private_attr_reader :leader, :members
|
17
|
+
|
18
|
+
trigger :meet do
|
19
|
+
result = []
|
20
|
+
result << leader.greet
|
21
|
+
result << members.concurrent_map do |member|
|
22
|
+
result << member.greet
|
23
|
+
end
|
24
|
+
result.flatten.join(' ')
|
25
|
+
end
|
26
|
+
|
27
|
+
module Leader
|
28
|
+
def greet
|
29
|
+
"Hello everyone. I am #{name}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Member
|
34
|
+
def greet
|
35
|
+
"Hello #{leader.name}, I am #{name}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Members
|
40
|
+
include Surrounded
|
41
|
+
|
42
|
+
def concurrent_map
|
43
|
+
map do |member|
|
44
|
+
Thread.new do
|
45
|
+
yield member
|
46
|
+
end
|
47
|
+
end.each(&:join)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ThreadedContext do
|
53
|
+
let(:jim){ User.new('Jim') }
|
54
|
+
let(:amy){ User.new('Amy') }
|
55
|
+
let(:guille){ User.new('Guille') }
|
56
|
+
let(:jason){ User.new('Jason') }
|
57
|
+
let(:dave){ User.new('Dave') }
|
58
|
+
|
59
|
+
let(:greeter){ jim }
|
60
|
+
let(:members){ [amy, guille, jason, dave] }
|
61
|
+
|
62
|
+
it 'works in multi-threaded environments' do
|
63
|
+
meeting = ThreadedContext.new(jim, members)
|
64
|
+
|
65
|
+
result = meeting.meet
|
66
|
+
|
67
|
+
assert_includes result, 'Hello everyone. I am Jim'
|
68
|
+
assert_includes result, 'Hello Jim, I am Amy'
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# If you want to use wrappers, here's how you could
|
4
|
+
require 'delegate'
|
5
|
+
class WrapperContext
|
6
|
+
extend Surrounded::Context
|
7
|
+
|
8
|
+
apply_roles_on(:trigger)
|
9
|
+
setup(:admin, :task)
|
10
|
+
|
11
|
+
class Admin < SimpleDelegator
|
12
|
+
include Surrounded
|
13
|
+
def some_admin_method
|
14
|
+
'hello from the admin wrapper!'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
trigger :do_something do
|
19
|
+
admin.some_admin_method
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe WrapperContext do
|
24
|
+
let(:context){
|
25
|
+
WrapperContext.new(Object.new, Object.new)
|
26
|
+
}
|
27
|
+
it 'wraps objects and allows them to respond to new methods' do
|
28
|
+
assert_equal 'hello from the admin wrapper!', context.do_something
|
29
|
+
end
|
30
|
+
end
|
@@ -83,8 +83,7 @@ describe Surrounded::Context, '#role?' do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
require 'casting'
|
86
|
-
CastingUser
|
87
|
-
class CastingUser
|
86
|
+
class CastingUser < User
|
88
87
|
include Casting::Client
|
89
88
|
delegate_missing_methods
|
90
89
|
end
|
@@ -92,7 +91,7 @@ end
|
|
92
91
|
class RoleAssignmentContext
|
93
92
|
extend Surrounded::Context
|
94
93
|
|
95
|
-
|
94
|
+
initialize(:user, :other_user)
|
96
95
|
|
97
96
|
trigger :user_ancestors do
|
98
97
|
user.singleton_class.ancestors
|
@@ -118,7 +117,15 @@ class RoleAssignmentContext
|
|
118
117
|
end
|
119
118
|
end
|
120
119
|
|
120
|
+
describe Surrounded::Context, '.setup' do
|
121
|
+
it 'defines an initialize method accepting the same arguments' do
|
122
|
+
skip "not yet implemented. may not be possible"
|
123
|
+
assert_equal 2, RoleAssignmentContext.instance_method(:initialize).arity
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
121
127
|
describe Surrounded::Context, 'assigning roles' do
|
128
|
+
include Surrounded
|
122
129
|
let(:user){ CastingUser.new("Jim") }
|
123
130
|
let(:other_user){ User.new("Guille") }
|
124
131
|
let(:context){ RoleAssignmentContext.new(user, other_user) }
|
@@ -136,17 +143,19 @@ describe Surrounded::Context, 'assigning roles' do
|
|
136
143
|
assert context.check_other_user_response
|
137
144
|
end
|
138
145
|
|
139
|
-
it 'will
|
146
|
+
it 'will use classes as roles' do
|
140
147
|
ClassRoleAssignmentContext = Class.new do
|
141
148
|
extend Surrounded::Context
|
142
149
|
|
143
|
-
|
150
|
+
initialize(:thing, :the_test)
|
144
151
|
|
145
152
|
trigger :check_user_response do
|
146
|
-
the_test.
|
153
|
+
the_test.assert_respond_to thing, :method_from_class
|
147
154
|
end
|
148
155
|
|
149
156
|
class Thing
|
157
|
+
include Surrounded
|
158
|
+
def initialize(obj); end
|
150
159
|
def method_from_class; end
|
151
160
|
end
|
152
161
|
|
data/test/surrounded_test.rb
CHANGED
@@ -5,7 +5,6 @@ describe "Surrounded", 'without context' do
|
|
5
5
|
let(:jim){ User.new("Jim") }
|
6
6
|
|
7
7
|
it "never has context roles" do
|
8
|
-
Thread.current[:context] = []
|
9
8
|
assert_nil jim.send(:context).role?('anything')
|
10
9
|
end
|
11
10
|
|
@@ -21,16 +20,18 @@ describe "Surrounded" do
|
|
21
20
|
}
|
22
21
|
|
23
22
|
before do
|
24
|
-
|
23
|
+
jim.store_context(context)
|
24
|
+
guille.store_context(context)
|
25
25
|
end
|
26
26
|
|
27
27
|
it "has access to objects in the context" do
|
28
28
|
assert_equal jim.other_user, guille
|
29
29
|
end
|
30
|
+
|
30
31
|
it "responds to messages for roles on the context" do
|
31
32
|
assert jim.respond_to?(:other_user)
|
32
33
|
|
33
|
-
|
34
|
+
jim.remove_context
|
34
35
|
|
35
36
|
refute jim.respond_to?(:other_user)
|
36
37
|
end
|
@@ -40,4 +41,33 @@ describe "Surrounded" do
|
|
40
41
|
external_user.user
|
41
42
|
}
|
42
43
|
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "Surrounded", "added to an existing object" do
|
47
|
+
it "allows the object to store its context" do
|
48
|
+
object = Object.new
|
49
|
+
assert_raises(NoMethodError){
|
50
|
+
object.store_context(self)
|
51
|
+
}
|
52
|
+
object.extend(Surrounded)
|
53
|
+
assert object.store_context(self)
|
54
|
+
assert object.remove_context
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module SpecialSurrounding
|
59
|
+
include Surrounded
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "Surrounded", "added to an object through another module" do
|
63
|
+
it "allows the object to store its context" do
|
64
|
+
object = Array.new
|
65
|
+
assert_raises(NoMethodError){
|
66
|
+
object.store_context(self)
|
67
|
+
}
|
68
|
+
object.extend(SpecialSurrounding)
|
69
|
+
assert object.store_context(self)
|
70
|
+
assert object.remove_context
|
71
|
+
assert object.send(:context)
|
72
|
+
end
|
43
73
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surrounded
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.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-
|
11
|
+
date: 2013-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: triad
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.2
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,10 +67,15 @@ files:
|
|
53
67
|
- README.md
|
54
68
|
- Rakefile
|
55
69
|
- examples/rails.rb
|
70
|
+
- examples/wrappers.rb
|
56
71
|
- lib/surrounded.rb
|
57
72
|
- lib/surrounded/context.rb
|
73
|
+
- lib/surrounded/context/role_map.rb
|
74
|
+
- lib/surrounded/context_errors.rb
|
58
75
|
- lib/surrounded/version.rb
|
59
76
|
- surrounded.gemspec
|
77
|
+
- test/example_threaded_test.rb
|
78
|
+
- test/example_wrapper_test.rb
|
60
79
|
- test/surrounded_context_test.rb
|
61
80
|
- test/surrounded_test.rb
|
62
81
|
- test/test_helper.rb
|
@@ -85,6 +104,9 @@ signing_key:
|
|
85
104
|
specification_version: 4
|
86
105
|
summary: Create encapsulated environments for your objects.
|
87
106
|
test_files:
|
107
|
+
- test/example_threaded_test.rb
|
108
|
+
- test/example_wrapper_test.rb
|
88
109
|
- test/surrounded_context_test.rb
|
89
110
|
- test/surrounded_test.rb
|
90
111
|
- test/test_helper.rb
|
112
|
+
has_rdoc:
|