surrounded 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eec57f6f3717bca03404f2acde19afb3cb2a08d4
4
- data.tar.gz: 7c6fef1028b60551bcc1cafdb5e7e9099d1ab7ef
3
+ metadata.gz: 0ffb676825adabf9eeb3f049b5bee0db872d4647
4
+ data.tar.gz: 77088aade1c2e9079b07546681e8e1997b7aab1c
5
5
  SHA512:
6
- metadata.gz: 2fe9113c6aaed6c7a09c687045cb54ba792bc59521b8917b09fdbc43a7d17c065cb54eac1a437a1fabcd9d4f094e78e9d67bf75333553ffef6cc63b7d000524e
7
- data.tar.gz: 6472c2aef734c56f55d9dc2a0f9992b7e4f693f2ff677685586a3fee3a872cb9bf2602f6cb05e9b68b82a040fc0bf2be32b6aec0c3d4f7ef849df02874a95f65
6
+ metadata.gz: 2a890fa34e1b6ebd9c1f6646f2fb5d0ea2ea6992e7b8d9a63d84d023267eb2ea1e9898e808ec98ece3995cea8e801a5bfc9da3f4da108cbb3445b336c40b1eb2
7
+ data.tar.gz: 439627c4612ddfade5819fe832a1799127d179a50ca92fa386efadf60ea51f31a2c9fcbd487581182831eda7ddbee4a8f57cdcb367a3c4cb667cdd44e92e35c0
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Build Status](https://travis-ci.org/saturnflyer/surrounded.png?branch=master)](https://travis-ci.org/saturnflyer/surrounded)
4
4
  [![Code Climate](https://codeclimate.com/github/saturnflyer/surrounded.png)](https://codeclimate.com/github/saturnflyer/surrounded)
5
5
  [![Coverage Status](https://coveralls.io/repos/saturnflyer/surrounded/badge.png)](https://coveralls.io/r/saturnflyer/surrounded)
6
+ [![Gem Version](https://badge.fury.io/rb/surrounded.png)](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(Exmployee)
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
- Thread.current[:context] = self
111
+ employee.store_context(self)
111
112
  employee.quit
112
- Thread.current[:context] = nil
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 `Thread` variables, do you?
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 `Thread` variables.
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
- gem 'surrounded'
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(:user, :other_user, :listener)
21
+ setup(:admin, :other_user, :listener)
22
22
 
23
23
  trigger :do_something do
24
- user.something
24
+ admin.something
25
25
  end
26
26
 
27
- module User
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
- use_case = :SomeOverrideUseCase
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
@@ -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 setup(*setup_args)
15
- attr_reader(*setup_args)
16
- private(*setup_args)
20
+ def apply_roles_on(which)
21
+ @policy = which
22
+ end
17
23
 
18
- define_method(:initialize){ |*args|
19
- Hash[setup_args.zip(args)].each{ |role, object|
24
+ def initialize(*setup_args)
25
+ private_attr_reader(*setup_args)
20
26
 
21
- role_module_name = Context.classify_string(role)
22
- klass = self.class
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
- if mod = klass.const_defined?(role_module_name) && !mod.is_a?(Class)
25
- object = Context.modify(object, klass.const_get(role_module_name))
26
- end
36
+ define_method(:initialize){ |*args|
37
+ map_roles(setup_args.zip(args))
27
38
 
28
- roles[role.to_s] = object
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
- (Thread.current[:context] ||= []).unshift(self)
57
+ apply_roles if policy == :trigger
58
+
44
59
  self.send("trigger_#{name}", *args)
60
+
45
61
  ensure
46
- (Thread.current[:context] ||= []).shift
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
- roles.values.include?(accessor) && roles[name.to_s]
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 roles
82
- @roles ||= {}
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
@@ -0,0 +1,6 @@
1
+ require 'triad'
2
+ module Surrounded
3
+ module Context
4
+ class InvalidRole < ::Triad::KeyNotPresent; end
5
+ end
6
+ end
@@ -1,3 +1,3 @@
1
1
  module Surrounded
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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 = Struct.new(:name)
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
- setup(:user, :other_user)
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 not use classes as roles' do
146
+ it 'will use classes as roles' do
140
147
  ClassRoleAssignmentContext = Class.new do
141
148
  extend Surrounded::Context
142
149
 
143
- setup(:thing, :the_test)
150
+ initialize(:thing, :the_test)
144
151
 
145
152
  trigger :check_user_response do
146
- the_test.refute thing.respond_to?(:method_from_class), 'did respond to :method_from_class'
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
 
@@ -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
- Thread.current[:context] = [context]
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
- Thread.current[:context] = []
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
@@ -9,9 +9,12 @@ end
9
9
  require 'surrounded'
10
10
  require 'surrounded/context'
11
11
 
12
- User = Struct.new(:name)
13
12
  class User
14
13
  include Surrounded
14
+ def initialize(name)
15
+ @name = name
16
+ end
17
+ attr_reader :name
15
18
  end
16
19
 
17
20
  class TestContext
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.1.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-06-12 00:00:00.000000000 Z
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: