surrounded 1.0.0 → 1.1.1

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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/Changelog.md +11 -11
  3. data/LICENSE.txt +1 -1
  4. data/README.md +39 -9
  5. data/Rakefile +12 -5
  6. data/lib/surrounded/access_control.rb +19 -26
  7. data/lib/surrounded/context/forwarding.rb +10 -10
  8. data/lib/surrounded/context/initializing.rb +10 -7
  9. data/lib/surrounded/context/name_collision_detector.rb +17 -17
  10. data/lib/surrounded/context/negotiator.rb +13 -13
  11. data/lib/surrounded/context/role_builders.rb +8 -7
  12. data/lib/surrounded/context/role_map.rb +20 -13
  13. data/lib/surrounded/context/seclusion.rb +20 -0
  14. data/lib/surrounded/context/trigger_controls.rb +14 -16
  15. data/lib/surrounded/context.rb +64 -72
  16. data/lib/surrounded/east_oriented.rb +4 -4
  17. data/lib/surrounded/exceptions.rb +1 -1
  18. data/lib/surrounded/shortcuts.rb +15 -5
  19. data/lib/surrounded/version.rb +2 -2
  20. data/lib/surrounded.rb +7 -7
  21. data/surrounded.gemspec +21 -16
  22. data/test/{casting_role_player_test.rb → casting_test_helper.rb} +4 -3
  23. data/test/collection_role_players_test.rb +16 -16
  24. data/test/context_access_test.rb +31 -30
  25. data/test/context_forwarding_test.rb +30 -30
  26. data/test/context_reuse_test.rb +14 -14
  27. data/test/context_shortcuts_test.rb +38 -11
  28. data/test/east_oriented_triggers_test.rb +14 -13
  29. data/test/example_delegate_class_test.rb +8 -8
  30. data/test/example_proxy_test.rb +25 -23
  31. data/test/example_threaded_test.rb +13 -13
  32. data/test/example_wrapper_test.rb +7 -7
  33. data/test/initialization_test.rb +25 -26
  34. data/test/name_collisions_test.rb +48 -42
  35. data/test/non_surrounded_role_player_test.rb +8 -8
  36. data/test/override_methods_test.rb +9 -9
  37. data/test/role_context_method_test.rb +144 -98
  38. data/test/surrounded_context_test.rb +71 -62
  39. data/test/surrounded_test.rb +13 -15
  40. data/test/test_helper.rb +8 -5
  41. data/test/threaded_context_test.rb +70 -0
  42. metadata +9 -52
  43. data/.codeclimate.yml +0 -4
  44. data/.gitignore +0 -19
  45. data/.pullreview.yml +0 -4
  46. data/.simplecov +0 -3
  47. data/.travis.yml +0 -19
  48. data/Gemfile +0 -10
  49. data/examples/bottles.rb +0 -145
  50. data/examples/rails.rb +0 -57
@@ -1,34 +1,33 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class KeywordContext
4
4
  extend Surrounded::Context
5
5
 
6
6
  keyword_initialize(:user, :other_user) do
7
- @defined_by_initializer_block = 'yup'
7
+ @defined_by_initializer_block = "yup"
8
8
  end
9
9
  end
10
10
 
11
- describe Surrounded::Context, '.initialize' do
12
-
13
- it 'applies a provided block to the instance' do
14
- context = KeywordContext.new(user: User.new('Jim'), other_user: User.new('Amy'))
15
- assert_equal 'yup', context.instance_variable_get(:@defined_by_initializer_block)
11
+ describe Surrounded::Context, ".initialize" do
12
+ it "applies a provided block to the instance" do
13
+ context = KeywordContext.new(user: User.new("Jim"), other_user: User.new("Amy"))
14
+ assert_equal "yup", context.instance_variable_get(:@defined_by_initializer_block)
16
15
  end
17
16
 
18
- it 'keeps track of the original initialize arguments' do
19
- jim = User.new('Jim')
20
- amy = User.new('Amy')
17
+ it "keeps track of the original initialize arguments" do
18
+ jim = User.new("Jim")
19
+ amy = User.new("Amy")
21
20
  context = KeywordContext.new(user: jim, other_user: amy)
22
21
  tracked = context.send(:initializer_arguments)
23
22
  assert_equal jim, tracked[:user]
24
23
  assert_equal amy, tracked[:other_user]
25
24
  end
26
25
 
27
- it 'raises errors with missing keywords' do
28
- err = assert_raises(ArgumentError){
29
- KeywordContext.new(other_user: User.new('Amy'))
26
+ it "raises errors with missing keywords" do
27
+ err = assert_raises(ArgumentError) {
28
+ KeywordContext.new(other_user: User.new("Amy"))
30
29
  }
31
- assert_match(/missing keyword: user/, err.message)
30
+ assert_match(/missing keyword: :?user/, err.message)
32
31
  end
33
32
  end
34
33
 
@@ -36,7 +35,7 @@ class NonKeyworder
36
35
  extend Surrounded::Context
37
36
 
38
37
  initialize_without_keywords :this, :that do
39
- self.instance_variable_set(:@defined_by_initializer_block, 'yes')
38
+ instance_variable_set(:@defined_by_initializer_block, "yes")
40
39
  end
41
40
 
42
41
  trigger :access_other_object do
@@ -44,23 +43,23 @@ class NonKeyworder
44
43
  end
45
44
  end
46
45
 
47
- describe Surrounded::Context, 'non-keyword initializers' do
48
- it 'defines an initialize method accepting the same arguments' do
46
+ describe Surrounded::Context, "non-keyword initializers" do
47
+ it "defines an initialize method accepting the same arguments" do
49
48
  assert_equal 2, NonKeyworder.instance_method(:initialize).arity
50
49
  end
51
50
 
52
- it 'works without keyword arguments' do
53
- assert NonKeyworder.new(User.new('Jim'), User.new('Guille'))
51
+ it "works without keyword arguments" do
52
+ assert NonKeyworder.new(User.new("Jim"), User.new("Guille"))
54
53
  end
55
54
 
56
- it 'evaluates a given block' do
57
- assert_equal 'yes', NonKeyworder.new(User.new('Jim'), User.new('Guille')).instance_variable_get(:@defined_by_initializer_block)
55
+ it "evaluates a given block" do
56
+ assert_equal "yes", NonKeyworder.new(User.new("Jim"), User.new("Guille")).instance_variable_get(:@defined_by_initializer_block)
58
57
  end
59
58
 
60
- it 'allows rebinding with a hash' do
61
- context = NonKeyworder.new(User.new('Jim'), User.new('Guille'))
62
- expect(context.access_other_object).must_equal 'Guille'
63
- context.rebind(this: User.new('Amy'), that: User.new('Elizabeth'))
64
- expect(context.access_other_object).must_equal 'Elizabeth'
59
+ it "allows rebinding with a hash" do
60
+ context = NonKeyworder.new(User.new("Jim"), User.new("Guille"))
61
+ expect(context.access_other_object).must_equal "Guille"
62
+ context.rebind(this: User.new("Amy"), that: User.new("Elizabeth"))
63
+ expect(context.access_other_object).must_equal "Elizabeth"
65
64
  end
66
65
  end
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
  ##
3
3
  # The problem is as follows: When an object already contains a method
4
4
  # that has the same name as an actor in the context, then the object
@@ -13,19 +13,22 @@ require 'test_helper'
13
13
  class HasNameCollision
14
14
  include Surrounded
15
15
 
16
- attr_accessor :will_collide # should always return nil
16
+ attr_reader :will_collide # should always return nil
17
17
 
18
18
  def assert_has_role
19
19
  true
20
20
  end
21
21
 
22
- def will_collide=(_); end
22
+ def will_collide=(_)
23
+ @will_collide = nil
24
+ end
23
25
  end
24
26
 
25
27
  class ShouldCollide
26
28
  include Surrounded
29
+
27
30
  def collide
28
- return 'Method called in ShouldCollide'
31
+ "Method called in ShouldCollide"
29
32
  end
30
33
  end
31
34
 
@@ -66,111 +69,114 @@ class ContextWithMultipleCollisions
66
69
  end
67
70
 
68
71
  class First
72
+ def second
73
+ end
69
74
 
70
- def second;end
71
- def third;end
75
+ def third
76
+ end
72
77
  end
73
78
 
74
79
  class Second
75
- def first;end
80
+ def first
81
+ end
76
82
 
77
- def third;end
83
+ def third
84
+ end
78
85
  end
79
86
 
80
87
  class Third
81
- def first;end
82
- def second;end
88
+ def first
89
+ end
83
90
 
91
+ def second
92
+ end
84
93
  end
85
94
 
86
- describe 'handling name collisions' do
87
-
88
- let(:new_context_with_collision){
95
+ describe "handling name collisions" do
96
+ let(:new_context_with_collision) {
89
97
  ContextOverridesName.new(base: HasNameCollision.new, will_collide: ShouldCollide.new)
90
98
  }
91
99
 
92
100
  after do
93
- ContextOverridesName.instance_eval{
101
+ ContextOverridesName.instance_eval {
94
102
  remove_instance_variable :@handler if defined?(@handler)
95
103
  }
96
104
  end
97
105
 
98
106
  def set_handler(handler)
99
- ContextOverridesName.instance_eval{
107
+ ContextOverridesName.instance_eval {
100
108
  on_name_collision handler
101
109
  }
102
110
  end
103
111
 
104
- it 'is works properly without handling collisions' do
112
+ it "is works properly without handling collisions" do
105
113
  assert new_context_with_collision.check_setup
106
114
  end
107
115
 
108
- it 'allows a name collision' do
109
- err = assert_raises(NoMethodError){
116
+ it "allows a name collision" do
117
+ err = assert_raises(NoMethodError) {
110
118
  new_context_with_collision.induce_collision
111
119
  }
112
- assert_match(/undefined method \`collide' for nil:NilClass/, err.message)
120
+ assert_match(/undefined method [`']collide['`] for nil/, err.message)
113
121
  end
114
122
 
115
- it 'can raise an exception' do
123
+ it "can raise an exception" do
116
124
  set_handler :raise
117
- assert_raises(ContextOverridesName::NameCollisionError){
125
+ assert_raises(ContextOverridesName::NameCollisionError) {
118
126
  new_context_with_collision
119
127
  }
120
128
  end
121
129
 
122
- it 'can print a warning' do
130
+ it "can print a warning" do
123
131
  set_handler :warn
124
132
  assert_output(nil, "base has name collisions with [:will_collide]\n") {
125
133
  new_context_with_collision
126
134
  }
127
135
  end
128
136
 
129
- it 'can ignore collisions' do
137
+ it "can ignore collisions" do
130
138
  set_handler :nothing
131
139
  assert_output(nil, nil) {
132
140
  new_context_with_collision
133
141
  }
134
142
  end
135
143
 
136
- it 'raises an error with an unknown handler' do
144
+ it "raises an error with an unknown handler" do
137
145
  set_handler :barf
138
146
  err = assert_raises(ArgumentError) {
139
147
  new_context_with_collision
140
148
  }
141
- expect(err.message).must_match(/your name collision handler was set to \`barf' but there is no instance nor class method of that name/)
149
+ expect(err.message).must_match(/your name collision handler was set to `barf' but there is no instance nor class method of that name/)
142
150
  end
143
151
 
144
- let(:create_context_with_multiple_collisions){
152
+ let(:create_context_with_multiple_collisions) {
145
153
  ContextWithMultipleCollisions.new(first: First.new, second: Second.new, third: Third.new)
146
154
  }
147
155
 
148
- it 'can handle multiple collisions' do
149
- expected_message = <<ERR
150
- first has name collisions with [:second, :third]
151
- second has name collisions with [:first, :third]
152
- third has name collisions with [:first, :second]
153
- ERR
154
- assert_output(nil, expected_message){
156
+ it "can handle multiple collisions" do
157
+ expected_message = <<~ERR
158
+ first has name collisions with [:second, :third]
159
+ second has name collisions with [:first, :third]
160
+ third has name collisions with [:first, :second]
161
+ ERR
162
+ assert_output(nil, expected_message) {
155
163
  create_context_with_multiple_collisions
156
164
  }
157
165
  end
158
166
 
159
- it 'can use a class method' do
160
- class ContextOverridesName
161
- def self.class_method_handler(message)
162
- puts message
163
- end
167
+ it "can use a class method" do
168
+ ContextOverridesName.define_singleton_method(:class_method_handler) do |message|
169
+ puts message
164
170
  end
165
171
  set_handler :class_method_handler
166
- assert_output("base has name collisions with [:will_collide]\n"){
172
+ assert_output("base has name collisions with [:will_collide]\n") {
167
173
  new_context_with_collision
168
174
  }
169
175
  end
170
176
 
171
- it 'can use a proc' do
172
- set_handler ->(message){ puts "message from a proc: #{message}"}
173
- assert_output("message from a proc: base has name collisions with [:will_collide]\n"){
177
+ it "can use a proc" do
178
+ set_handler ->(message) { puts "message from a proc: #{message}" }
179
+ assert_output("message from a proc: base has name collisions with [:will_collide]\n") {
174
180
  new_context_with_collision
175
181
  }
176
182
  end
@@ -1,16 +1,16 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class BareObjectContext
4
4
  extend Surrounded::Context
5
5
 
6
6
  def initialize(number, string, user)
7
- map_roles(:number => number, :string => string, :user => user)
7
+ map_roles(number: number, string: string, user: user)
8
8
  end
9
9
  private_attr_reader :number, :string, :user
10
10
 
11
11
  role :user do
12
12
  def output
13
- [number.to_s, string, name].join(' - ')
13
+ [number.to_s, string, name].join(" - ")
14
14
  end
15
15
  end
16
16
 
@@ -19,9 +19,9 @@ class BareObjectContext
19
19
  end
20
20
  end
21
21
 
22
- describe Surrounded::Context, 'skips affecting non-surrounded objects' do
23
- it 'works with non-surrounded objects' do
24
- context = BareObjectContext.new(123,'hello', User.new('Jim'))
25
- assert_equal '123 - hello - Jim', context.output
22
+ describe Surrounded::Context, "skips affecting non-surrounded objects" do
23
+ it "works with non-surrounded objects" do
24
+ context = BareObjectContext.new(123, "hello", User.new("Jim"))
25
+ assert_equal "123 - hello - Jim", context.output
26
26
  end
27
- end
27
+ end
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class PrependedRoles
4
4
  extend Surrounded::Context
@@ -45,18 +45,18 @@ class PrependedRoles
45
45
  end
46
46
  end
47
47
 
48
- describe Surrounded::Context, 'custom role application' do
49
- let(:user){ User.new('Jim') }
50
- let(:other){ User.new('Amy') }
48
+ describe Surrounded::Context, "custom role application" do
49
+ let(:user) { User.new("Jim") }
50
+ let(:other) { User.new("Amy") }
51
51
 
52
- let(:context){ PrependedRoles.new(user: user, other: other) }
52
+ let(:context) { PrependedRoles.new(user: user, other: other) }
53
53
 
54
- it 'allows you to override existing methods on a role player' do
54
+ it "allows you to override existing methods on a role player" do
55
55
  assert_equal "Not what you thought, Jim", context.get_name
56
56
  assert_equal "Jim", user.name
57
57
  end
58
58
 
59
- it 'allows you to override the way a role is mapped' do
60
- assert_equal 'OtherStuff Amy', context.other_title
59
+ it "allows you to override the way a role is mapped" do
60
+ assert_equal "OtherStuff Amy", context.other_title
61
61
  end
62
- end
62
+ end
@@ -1,99 +1,142 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
- describe Surrounded::Context, '.role' do
4
- class RoleContextTester
5
- extend Surrounded::Context
6
-
7
- role_methods :admin do
8
- end
3
+ class RoleContextTester
4
+ extend Surrounded::Context
5
+
6
+ role_methods :admin do
9
7
  end
10
-
11
- describe 'modules' do
12
- it 'creates a module' do
8
+ end
9
+
10
+ describe Surrounded::Context, ".role" do
11
+ describe "modules" do
12
+ it "creates a module" do
13
13
  role = RoleContextTester.const_get(:Admin)
14
14
  refute_instance_of Class, role
15
15
  assert_kind_of Module, role
16
16
  end
17
-
18
- it 'marks the constant private' do
19
- error = assert_raises(NameError){
17
+
18
+ it "marks the constant private" do
19
+ error = assert_raises(NameError) {
20
20
  RoleContextTester::Admin
21
21
  }
22
22
  assert_match(/private constant/i, error.message)
23
23
  end
24
24
  end
25
-
26
- class WrapperRoleContext
27
- extend Surrounded::Context
28
-
29
- role :admin, :wrap do
30
-
31
- end
25
+ end
26
+
27
+ class WrapperRoleContext
28
+ extend Surrounded::Context
29
+
30
+ role :admin, :wrap do
32
31
  end
33
-
34
- describe 'wrappers' do
35
- it 'creates a wrapper' do
36
- role = WrapperRoleContext.const_get('Admin')
32
+ end
33
+
34
+ describe Surrounded::Context, ".role" do
35
+ describe "wrappers" do
36
+ it "creates a wrapper" do
37
+ role = WrapperRoleContext.const_get(:Admin)
37
38
  assert_includes(role.ancestors, SimpleDelegator)
38
39
  end
39
-
40
- it 'marks the constant private' do
41
- error = assert_raises(NameError){
40
+
41
+ it "marks the constant private" do
42
+ error = assert_raises(NameError) {
42
43
  WrapperRoleContext::Admin
43
44
  }
44
45
  assert_match(/private constant/i, error.message)
45
46
  end
46
47
  end
47
- class InterfaceContext
48
- extend Surrounded::Context
49
-
50
- initialize(:admin, :other)
51
-
52
- role :admin, :interface do
53
- def hello
54
- 'hello from admin'
55
- end
48
+ end
49
+
50
+ class InterfaceContext
51
+ extend Surrounded::Context
52
+
53
+ initialize(:admin, :other)
54
+
55
+ role :admin, :interface do
56
+ def hello
57
+ "hello from admin"
56
58
  end
57
-
58
- trigger :admin_hello do
59
- admin.hello
59
+
60
+ def splat_args(*args)
61
+ args
60
62
  end
61
- end
62
-
63
- class Hello
64
- include Surrounded
65
- def hello
66
- 'hello'
63
+
64
+ def keyword_args(**kwargs)
65
+ kwargs
66
+ end
67
+
68
+ def mixed_args(*args, **kwargs)
69
+ [args, kwargs]
67
70
  end
68
71
  end
69
-
70
- describe 'interfaces' do
71
- let(:context){
72
+
73
+ trigger :admin_hello do
74
+ admin.hello
75
+ end
76
+
77
+ trigger :splat_args do |*args|
78
+ admin.splat_args(*args)
79
+ end
80
+
81
+ trigger :keyword_args do |**kwargs|
82
+ admin.keyword_args(**kwargs)
83
+ end
84
+
85
+ trigger :mixed_args do |*args, **kwargs|
86
+ admin.mixed_args(*args, **kwargs)
87
+ end
88
+ end
89
+
90
+ class Hello
91
+ include Surrounded
92
+
93
+ def hello
94
+ "hello"
95
+ end
96
+ end
97
+
98
+ describe Surrounded::Context, ".role" do
99
+ describe "interfaces" do
100
+ let(:context) {
72
101
  InterfaceContext.new(admin: Hello.new, other: Hello.new)
73
102
  }
74
- it 'sets interface objects to use interface methods before singleton methods' do
75
- assert_equal 'hello from admin', context.admin_hello
103
+ it "sets interface objects to use interface methods before singleton methods" do
104
+ assert_equal "hello from admin", context.admin_hello
76
105
  end
77
-
78
- it 'marks the inteface constant private' do
79
- error = assert_raises(NameError){
106
+
107
+ it "marks the inteface constant private" do
108
+ error = assert_raises(NameError) {
80
109
  InterfaceContext::AdminInterface
81
110
  }
82
111
  assert_match(/private constant/i, error.message)
83
112
  end
84
-
85
- it 'creates a private accessor method' do
113
+
114
+ it "creates a private accessor method" do
86
115
  assert context.respond_to?(:admin, true)
87
116
  end
117
+
118
+ it "works with multiple args" do
119
+ assert_equal context.splat_args("one", "two"), %w[one two]
120
+ end
121
+
122
+ it "works with multiple keyword args" do
123
+ assert_equal context.keyword_args(one: "one", two: "two"), {one: "one", two: "two"}
124
+ end
125
+
126
+ it "works with multiple mixed args" do
127
+ assert_equal context.mixed_args("one", :two, three: "three", four: "four"), [["one", :two], {three: "three", four: "four"}]
128
+ end
88
129
  end
89
-
90
- describe 'unknown' do
91
- it 'raises an error' do
92
- class UnknownRole
93
- extend Surrounded::Context
94
- end
130
+ end
131
+
132
+ class UnknownRole
133
+ extend Surrounded::Context
134
+ end
95
135
 
96
- err = _{
136
+ describe Surrounded::Context, ".role" do
137
+ describe "unknown" do
138
+ it "raises an error" do
139
+ err = _ {
97
140
  UnknownRole.instance_eval do
98
141
  role :admin, :unknown do
99
142
  end
@@ -102,51 +145,54 @@ describe Surrounded::Context, '.role' do
102
145
  _(err.cause).must_be_kind_of NameError
103
146
  end
104
147
  end
105
-
106
- describe 'custom default' do
148
+ end
149
+
150
+ class CustomDefaultWrap
151
+ extend Surrounded::Context
152
+
153
+ self.default_role_type = :wrap
154
+
155
+ initialize(:admin, :the_test)
156
+
157
+ role :admin do
158
+ end
159
+
160
+ trigger :check_admin_type do
161
+ the_test.assert_kind_of SimpleDelegator, admin
162
+ end
163
+ end
164
+
165
+ describe Surrounded::Context, ".role" do
166
+ describe "custom default" do
107
167
  include Surrounded # the test is a role player here
108
-
109
- it 'allows the use of custom default role types' do
110
- class CustomDefaultWrap
168
+
169
+ it "allows the use of custom default role types" do
170
+ context = CustomDefaultWrap.new(admin: Object.new, the_test: self)
171
+ context.check_admin_type
172
+ end
173
+
174
+ it "allows the setting of custom default role type for all Surrounded::Contexts" do
175
+ old_default = Surrounded::Context.default_role_type
176
+ Surrounded::Context.default_role_type = :wrap
177
+
178
+ # Define the class after setting the global default so it picks up the new default
179
+ custom_global_default_class = Class.new do
111
180
  extend Surrounded::Context
112
-
113
- self.default_role_type = :wrap
114
-
181
+
115
182
  initialize(:admin, :the_test)
116
-
183
+
117
184
  role :admin do
118
185
  end
119
-
186
+
120
187
  trigger :check_admin_type do
121
188
  the_test.assert_kind_of SimpleDelegator, admin
122
189
  end
123
190
  end
124
- context = CustomDefaultWrap.new(admin: Object.new, the_test: self)
191
+
192
+ context = custom_global_default_class.new(admin: Object.new, the_test: self)
125
193
  context.check_admin_type
126
- end
127
-
128
- it 'allows the setting of custom default role type for all Surrounded::Contexts' do
129
- begin
130
- old_default = Surrounded::Context.default_role_type
131
- Surrounded::Context.default_role_type = :wrap
132
- class CustomGlobalDefault
133
- extend Surrounded::Context
134
-
135
- initialize(:admin, :the_test)
136
-
137
- role :admin do
138
- end
139
-
140
- trigger :check_admin_type do
141
- the_test.assert_kind_of SimpleDelegator, admin
142
- end
143
- end
144
-
145
- context = CustomGlobalDefault.new(admin: Object.new, the_test: self)
146
- context.check_admin_type
147
- ensure
148
- Surrounded::Context.default_role_type = old_default
149
- end
194
+ ensure
195
+ Surrounded::Context.default_role_type = old_default
150
196
  end
151
197
  end
152
- end
198
+ end