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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63bac757a4c7f7b9cb953a51123e31dc3612fa22
4
- data.tar.gz: 1afa12d700b0756ce4e22becf4555169d8e4fa10
3
+ metadata.gz: 8dcba3d11c7217ae50aa36d19f84c1a835734a22
4
+ data.tar.gz: c6981dbd112701103491e1919fa2819a334aedc5
5
5
  SHA512:
6
- metadata.gz: 4d508948a224105b736246b96f0a0a8dec5d9bccd9f2000429071135481fe09a3ecac7963e37e684940411a1d02bf89f5697810593469acbc47b7013e4a95820
7
- data.tar.gz: 1f68568474713853a086aef3c3e09ce292fbd66dca77c00c68f32afe11a30af593d159ba76c56f31765dd049063646b214f0bf556a302f9fd07be242274f4db1
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
- destination.balance += amount
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 = :interface # also :wrap, :wrapper, or :module
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
- self
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 = :interface
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
- self
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.new
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
@@ -14,7 +14,7 @@ module Surrounded
14
14
  define_access_method(name, &block)
15
15
  end
16
16
  end
17
- alias_method :guard, :disallow
17
+ alias guard disallow
18
18
 
19
19
  def trigger_return_content(name, *args, &block)
20
20
  %{
@@ -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
@@ -10,7 +10,6 @@ module Surrounded
10
10
  line = __LINE__
11
11
  mod.class_eval "
12
12
  def initialize(#{setup_args.join(',')})
13
- @role_map = role_mapper_class.new
14
13
  map_roles(#{setup_args.to_s}.zip([#{setup_args.join(',')}]))
15
14
  end
16
15
  ", __FILE__, line
@@ -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 = "__send__|object_id"
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 =~ /#{identity}/
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, behaviors)
34
- @object, @behaviors = object, behaviors
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.extend(InvalidRoleType)
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
- undef_method(name)
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
- module InvalidRoleType; end
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
@@ -1,5 +1,5 @@
1
1
  module Surrounded
2
- VERSION = "0.9.4"
2
+ VERSION = "0.9.5"
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -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 'allows access to other objects in the context' do
72
- assert_equal 'GTD', context.talking
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
- assert_raises(Surrounded::Context::InvalidRoleType){
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
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-15 00:00:00.000000000 Z
11
+ date: 2015-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: triad