surrounded 1.1.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 +4 -4
  2. data/Changelog.md +11 -20
  3. data/LICENSE.txt +1 -1
  4. data/README.md +3 -3
  5. data/Rakefile +12 -5
  6. data/lib/surrounded/access_control.rb +12 -11
  7. data/lib/surrounded/context/forwarding.rb +10 -10
  8. data/lib/surrounded/context/initializing.rb +8 -6
  9. data/lib/surrounded/context/name_collision_detector.rb +17 -17
  10. data/lib/surrounded/context/negotiator.rb +7 -8
  11. data/lib/surrounded/context/role_builders.rb +7 -7
  12. data/lib/surrounded/context/role_map.rb +6 -8
  13. data/lib/surrounded/context/trigger_controls.rb +11 -13
  14. data/lib/surrounded/context.rb +61 -56
  15. data/lib/surrounded/east_oriented.rb +4 -4
  16. data/lib/surrounded/exceptions.rb +1 -1
  17. data/lib/surrounded/shortcuts.rb +6 -8
  18. data/lib/surrounded/version.rb +1 -1
  19. data/lib/surrounded.rb +7 -7
  20. data/surrounded.gemspec +21 -15
  21. data/test/{casting_role_player_test.rb → casting_test_helper.rb} +4 -3
  22. data/test/collection_role_players_test.rb +16 -16
  23. data/test/context_access_test.rb +31 -30
  24. data/test/context_forwarding_test.rb +30 -30
  25. data/test/context_reuse_test.rb +14 -14
  26. data/test/context_shortcuts_test.rb +18 -16
  27. data/test/east_oriented_triggers_test.rb +14 -13
  28. data/test/example_delegate_class_test.rb +8 -8
  29. data/test/example_proxy_test.rb +25 -23
  30. data/test/example_threaded_test.rb +13 -13
  31. data/test/example_wrapper_test.rb +7 -7
  32. data/test/initialization_test.rb +24 -25
  33. data/test/name_collisions_test.rb +48 -42
  34. data/test/non_surrounded_role_player_test.rb +8 -8
  35. data/test/override_methods_test.rb +9 -9
  36. data/test/role_context_method_test.rb +129 -119
  37. data/test/surrounded_context_test.rb +71 -62
  38. data/test/surrounded_test.rb +13 -15
  39. data/test/test_helper.rb +5 -4
  40. data/test/threaded_context_test.rb +70 -0
  41. metadata +8 -38
  42. data/.codeclimate.yml +0 -4
  43. data/.github/workflows/codeql-analysis.yml +0 -70
  44. data/.github/workflows/test.yml +0 -18
  45. data/.gitignore +0 -19
  46. data/.pullreview.yml +0 -4
  47. data/.simplecov +0 -3
  48. data/Gemfile +0 -9
  49. data/examples/bottles.rb +0 -135
  50. data/examples/rails.rb +0 -57
@@ -1,15 +1,14 @@
1
- require 'set'
2
- require 'surrounded/exceptions'
3
- require 'surrounded/context/role_map'
4
- require 'surrounded/context/seclusion'
5
- require 'surrounded/context/role_builders'
6
- require 'surrounded/context/initializing'
7
- require 'surrounded/context/forwarding'
8
- require 'surrounded/context/trigger_controls'
9
- require 'surrounded/access_control'
10
- require 'surrounded/shortcuts'
11
- require 'surrounded/east_oriented'
12
- require 'surrounded/context/name_collision_detector'
1
+ require "surrounded/exceptions"
2
+ require "surrounded/context/role_map"
3
+ require "surrounded/context/seclusion"
4
+ require "surrounded/context/role_builders"
5
+ require "surrounded/context/initializing"
6
+ require "surrounded/context/forwarding"
7
+ require "surrounded/context/trigger_controls"
8
+ require "surrounded/access_control"
9
+ require "surrounded/shortcuts"
10
+ require "surrounded/east_oriented"
11
+ require "surrounded/context/name_collision_detector"
13
12
 
14
13
  # Extend your classes with Surrounded::Context to handle their
15
14
  # initialization and application of behaviors to the role players
@@ -27,16 +26,13 @@ module Surrounded
27
26
  include InstanceMethods
28
27
 
29
28
  trigger_mod = Module.new
30
- const_set('TriggerMethods', trigger_mod)
29
+ const_set(:TriggerMethods, trigger_mod)
31
30
  include trigger_mod
32
31
 
33
32
  extend TriggerControls
34
-
35
33
  }
36
34
  end
37
35
 
38
- private
39
-
40
36
  # Set the default type of implementation for role methods for all contexts.
41
37
  def self.default_role_type
42
38
  @default_role_type ||= :module
@@ -46,6 +42,8 @@ module Surrounded
46
42
  attr_writer :default_role_type
47
43
  end
48
44
 
45
+ private
46
+
49
47
  def default_role_type
50
48
  @default_role_type ||= Surrounded::Context.default_role_type
51
49
  end
@@ -56,13 +54,19 @@ module Surrounded
56
54
  end
57
55
 
58
56
  # Provide the ability to create access control methods for your triggers.
59
- def protect_triggers; self.extend(::Surrounded::AccessControl); end
57
+ def protect_triggers
58
+ extend(::Surrounded::AccessControl)
59
+ end
60
60
 
61
61
  # Automatically create class methods for each trigger method.
62
- def shortcut_triggers; self.extend(::Surrounded::Shortcuts); end
62
+ def shortcut_triggers
63
+ extend(::Surrounded::Shortcuts)
64
+ end
63
65
 
64
66
  # Automatically return the context object from trigger methods.
65
- def east_oriented_triggers; self.extend(::Surrounded::EastOriented); end
67
+ def east_oriented_triggers
68
+ extend(::Surrounded::EastOriented)
69
+ end
66
70
 
67
71
  # === Utility shortcuts
68
72
 
@@ -91,7 +95,7 @@ module Surrounded
91
95
  # is allowed to inquire about the roles.
92
96
  def role?(name, &block)
93
97
  return false unless role_map.role?(name)
94
- accessor = block.binding.eval('self')
98
+ accessor = block.binding.eval("self")
95
99
  role_map.role_player?(accessor) && role_map.assigned_player(name)
96
100
  end
97
101
 
@@ -119,7 +123,7 @@ module Surrounded
119
123
  private
120
124
 
121
125
  def clear_instance_variables
122
- instance_variables.each{|ivar| remove_instance_variable(ivar) }
126
+ instance_variables.each { |ivar| remove_instance_variable(ivar) }
123
127
  end
124
128
 
125
129
  def role_map
@@ -129,8 +133,8 @@ module Surrounded
129
133
  def map_roles(role_object_array)
130
134
  detect_collisions role_object_array
131
135
  role_object_array.to_a.each do |role, object|
132
- if self.respond_to?("map_role_#{role}")
133
- self.send("map_role_#{role}", object)
136
+ if respond_to?("map_role_#{role}")
137
+ send("map_role_#{role}", object)
134
138
  else
135
139
  map_role(role, role_behavior_name(role), object)
136
140
  map_role_collection(role, role_behavior_name(role), object)
@@ -155,13 +159,13 @@ module Surrounded
155
159
 
156
160
  def apply_behavior(role, behavior, object)
157
161
  if behavior && role_const_defined?(behavior)
158
- applicator = if self.respond_to?("apply_behavior_#{role}")
159
- method("apply_behavior_#{role}")
160
- elsif role_const(behavior).is_a?(Class)
161
- method(:apply_class_behavior)
162
- else
163
- method(:apply_module_behavior)
164
- end
162
+ applicator = if respond_to?("apply_behavior_#{role}")
163
+ method("apply_behavior_#{role}")
164
+ elsif role_const(behavior).is_a?(Class)
165
+ method(:apply_class_behavior)
166
+ else
167
+ method(:apply_module_behavior)
168
+ end
165
169
 
166
170
  role_player = applicator.call(role_const(behavior), object)
167
171
  map_role(role, behavior, role_player)
@@ -170,7 +174,7 @@ module Surrounded
170
174
  end
171
175
 
172
176
  def apply_module_behavior(mod, obj)
173
- adder_name = module_extension_methods.find{|meth| obj.respond_to?(meth) }
177
+ adder_name = module_extension_methods.find { |meth| obj.respond_to?(meth) }
174
178
  return obj unless adder_name
175
179
 
176
180
  obj.method(adder_name).call(mod)
@@ -178,7 +182,7 @@ module Surrounded
178
182
  end
179
183
 
180
184
  def apply_class_behavior(klass, obj)
181
- wrapper_name = wrap_methods.find{|meth| klass.respond_to?(meth) }
185
+ wrapper_name = wrap_methods.find { |meth| klass.respond_to?(meth) }
182
186
  return obj if !wrapper_name
183
187
  klass.method(wrapper_name).call(obj)
184
188
  end
@@ -190,11 +194,11 @@ module Surrounded
190
194
  end
191
195
  end
192
196
 
193
- role_player = if self.respond_to?("remove_behavior_#{role}")
194
- self.send("remove_behavior_#{role}", role_const(behavior), object)
195
- elsif remover_name
196
- object.send(remover_name)
197
- end
197
+ role_player = if respond_to?("remove_behavior_#{role}")
198
+ send("remove_behavior_#{role}", role_const(behavior), object)
199
+ elsif remover_name
200
+ object.send(remover_name)
201
+ end
198
202
 
199
203
  role_player || object
200
204
  end
@@ -203,7 +207,7 @@ module Surrounded
203
207
  role_map.each do |role, mod_name, object|
204
208
  player = apply_behavior(role, mod_name, object)
205
209
  if player.respond_to?(:store_context, true)
206
- player.__send__(:store_context) do; end
210
+ player.__send__(:store_context) {}
207
211
  end
208
212
  end
209
213
  end
@@ -211,7 +215,7 @@ module Surrounded
211
215
  def remove_behaviors
212
216
  role_map.each do |role, mod_name, player|
213
217
  if player.respond_to?(:remove_context, true)
214
- player.__send__(:remove_context) do; end
218
+ player.__send__(:remove_context) {}
215
219
  end
216
220
  remove_behavior(role, mod_name, player)
217
221
  end
@@ -242,7 +246,7 @@ module Surrounded
242
246
  end
243
247
 
244
248
  def role_module_basename(mod)
245
- mod.to_s.split('::').last
249
+ mod.to_s.split("::").last
246
250
  end
247
251
 
248
252
  def role_const(name)
@@ -262,33 +266,34 @@ module Surrounded
262
266
  name.singularize
263
267
  else
264
268
  # good enough for now but should be updated with better rules
265
- name.to_s.tap do |string|
266
- if string =~ /ies\z/
267
- string.sub!(/ies\z/,'y')
268
- elsif string =~ /s\z/
269
- string.sub!(/s\z/,'')
270
- end
269
+ string = name.to_s.dup
270
+ if string.end_with?("ies")
271
+ string.sub(/ies\z/, "y")
272
+ elsif string.end_with?("s")
273
+ string.sub(/s\z/, "")
274
+ else
275
+ string
271
276
  end
272
277
  end
273
278
  end
274
279
  end
275
280
 
276
281
  class RoleName
277
- def initialize(string, suffix=nil)
278
- @string = string.
279
- to_s.
280
- split(/_/).
281
- map{|part|
282
- part.capitalize
283
- }.
284
- join.
285
- sub(/_\d+/,'') + suffix.to_s
282
+ def initialize(string, suffix = nil)
283
+ @string = string
284
+ .to_s
285
+ .split("_")
286
+ .map { |part|
287
+ part.capitalize
288
+ }
289
+ .join
290
+ .sub(/_\d+/, "") + suffix.to_s
286
291
  end
287
292
 
288
293
  def to_str
289
294
  @string
290
295
  end
291
- alias to_s to_str
296
+ alias_method :to_s, :to_str
292
297
 
293
298
  def to_sym
294
299
  @string.to_sym
@@ -3,11 +3,11 @@ module Surrounded
3
3
  # Always return the context object to ensure that a context may
4
4
  # not be asked for information.
5
5
  def trigger_return_content(name, *args, &block)
6
- %{
6
+ %(
7
7
  #{super}
8
-
8
+
9
9
  self
10
- }
10
+ )
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -1,7 +1,7 @@
1
1
  module Surrounded
2
2
  module Exceptions
3
3
  def self.define(klass, exceptions:, namespace: Surrounded::Context)
4
- Array(exceptions).each{ |exception|
4
+ Array(exceptions).each { |exception|
5
5
  unless klass.const_defined?(exception)
6
6
  klass.const_set(exception, Class.new(namespace.const_get(exception)))
7
7
  end
@@ -1,21 +1,19 @@
1
1
  module Surrounded
2
2
  module Shortcuts
3
3
  private
4
-
4
+
5
5
  def define_shortcut(name)
6
6
  # if keyword initialize
7
- if instance_method(:initialize).parameters.dig(0,0) == :keyreq
7
+ if instance_method(:initialize).parameters.dig(0, 0) == :keyreq
8
8
  singleton_class.send(:define_method, name) do |**args|
9
- instance = begin
10
- self.new(**args)
11
- end
9
+ instance = new(**args)
10
+
12
11
  instance.public_send(name)
13
12
  end
14
13
  else # non-keyword initialize
15
14
  singleton_class.send(:define_method, name) do |*args|
16
- instance = begin
17
- self.new(*args)
18
- end
15
+ instance = new(*args)
16
+
19
17
  instance.public_send(name)
20
18
  end
21
19
  end
@@ -1,5 +1,5 @@
1
1
  module Surrounded
2
- VERSION = "1.1.0"
2
+ VERSION = "1.1.1"
3
3
 
4
4
  def self.version
5
5
  VERSION
data/lib/surrounded.rb CHANGED
@@ -3,23 +3,22 @@ require "surrounded/context"
3
3
  require "singleton"
4
4
 
5
5
  # This module should be added to objects which will enter
6
- # into context objects.
6
+ # into context objects.
7
7
  #
8
8
  # Its main purpose is to keep a reference to the context
9
9
  # and to implement method_missing to handle the relationship
10
10
  # to other objects in the context.
11
11
  module Surrounded
12
-
13
12
  private
14
13
 
15
14
  def store_context(&block)
16
- accessor = block.binding.eval('self')
15
+ accessor = block.binding.eval("self")
17
16
  surroundings.unshift(accessor)
18
17
  self
19
18
  end
20
19
 
21
20
  def remove_context(&block)
22
- accessor = block.binding.eval('self')
21
+ accessor = block.binding.eval("self")
23
22
  surroundings.shift if surroundings.include?(accessor)
24
23
  self
25
24
  end
@@ -33,15 +32,16 @@ module Surrounded
33
32
  end
34
33
 
35
34
  def method_missing(meth, *args, &block)
36
- context.role?(meth){} || super
35
+ context.role?(meth) {} || super
37
36
  end
38
37
 
39
- def respond_to_missing?(meth, include_private=false)
40
- !!context.role?(meth){} || super
38
+ def respond_to_missing?(meth, include_private = false)
39
+ !!context.role?(meth) {} || super
41
40
  end
42
41
 
43
42
  class NullContext
44
43
  include Singleton
44
+
45
45
  def role?(*args)
46
46
  nil
47
47
  end
data/surrounded.gemspec CHANGED
@@ -1,24 +1,30 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'surrounded/version'
3
+ require "surrounded/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "surrounded"
8
- spec.version = Surrounded.version
9
- spec.authors = ["'Jim Gay'"]
10
- spec.email = ["jim@saturnflyer.com"]
11
- spec.description = %q{Gives an object implicit access to other objects in it's environment.}
12
- spec.summary = %q{Create encapsulated environments for your objects.}
13
- spec.homepage = "http://github.com/saturnflyer/surrounded"
14
- spec.license = "MIT"
6
+ spec.name = "surrounded"
7
+ spec.version = Surrounded.version
8
+ spec.authors = ["'Jim Gay'"]
9
+ spec.email = ["jim@saturnflyer.com"]
10
+ spec.description = "Gives an object implicit access to other objects in it's environment."
11
+ spec.summary = "Create encapsulated environments for your objects."
12
+ spec.homepage = "http://github.com/saturnflyer/surrounded"
13
+ spec.license = "MIT"
15
14
 
16
- spec.files = `git ls-files`.split($/)
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.files = Dir[
16
+ "lib/**/*",
17
+ "Rakefile",
18
+ "README.md",
19
+ "Changelog.md",
20
+ "LICENSE.txt",
21
+ "test/**/*",
22
+ "surrounded.gemspec"
23
+ ]
24
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
25
  spec.require_paths = ["lib"]
20
26
 
21
- spec.add_dependency "triad", "~> 0.3.0"
27
+ spec.add_dependency "triad", ">= 0.3.0"
22
28
 
23
29
  spec.add_development_dependency "rake"
24
30
  end
@@ -1,7 +1,8 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
- require 'casting'
3
+ require "casting"
4
4
  class CastingUser < User
5
5
  include Casting::Client
6
+
6
7
  delegate_missing_methods
7
- end
8
+ end
@@ -1,4 +1,5 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
+ require "casting_test_helper"
2
3
 
3
4
  class CollectionContext
4
5
  extend Surrounded::Context
@@ -10,7 +11,7 @@ class CollectionContext
10
11
  end
11
12
 
12
13
  trigger :get_member_show do
13
- members.map(&:show).join(', ')
14
+ members.map(&:show).join(", ")
14
15
  end
15
16
 
16
17
  role :members do
@@ -25,27 +26,26 @@ class CollectionContext
25
26
  end
26
27
  end
27
28
 
28
- role :others do; end
29
- role :other do; end
30
-
29
+ role :others, &proc {}
30
+ role :other, &proc {}
31
31
  end
32
32
 
33
- describe Surrounded::Context, 'auto-assigning roles for collections' do
34
- let(:member_one){ User.new('Jim') }
35
- let(:member_two){ User.new('Amy') }
36
- let(:members){ [member_one, member_two] }
33
+ describe Surrounded::Context, "auto-assigning roles for collections" do
34
+ let(:member_one) { User.new("Jim") }
35
+ let(:member_two) { User.new("Amy") }
36
+ let(:members) { [member_one, member_two] }
37
37
 
38
- let(:other_one){ User.new('Guille') }
39
- let(:other_two){ User.new('Jason') }
40
- let(:others){ [other_one, other_two] }
38
+ let(:other_one) { User.new("Guille") }
39
+ let(:other_two) { User.new("Jason") }
40
+ let(:others) { [other_one, other_two] }
41
41
 
42
- let(:context){ CollectionContext.new(members: members, others: others) }
42
+ let(:context) { CollectionContext.new(members: members, others: others) }
43
43
 
44
- it 'assigns the collection role to collections' do
44
+ it "assigns the collection role to collections" do
45
45
  assert_equal members.size, context.get_members_count
46
46
  end
47
47
 
48
- it 'assigns a defined role to each item in a role player collection' do
48
+ it "assigns a defined role to each item in a role player collection" do
49
49
  assert_equal "member show, member show", context.get_member_show
50
50
  end
51
- end
51
+ end
@@ -1,62 +1,63 @@
1
- require 'test_helper'
2
- require 'minitest/mock'
1
+ require "test_helper"
2
+ require "minitest/mock"
3
3
 
4
4
  class FilteredContext
5
5
  extend Surrounded::Context
6
+
6
7
  protect_triggers
7
-
8
+
8
9
  initialize :user, :other_user
9
-
10
+
10
11
  trigger :if_ready do
11
- 'ready'
12
+ "ready"
12
13
  end
13
-
14
+
14
15
  guard :if_ready do
15
- user.name != 'Amy'
16
+ user.name != "Amy"
16
17
  end
17
-
18
+
18
19
  trigger :check_disallow_behavior do
19
20
  # used for disallow check
20
21
  end
21
-
22
+
22
23
  disallow :check_disallow_behavior do
23
24
  user.special
24
25
  end
25
-
26
+
26
27
  trigger :unguarded do
27
28
  # used for disallow check
28
29
  end
29
30
 
30
31
  role :user do
31
32
  def special
32
- 'special user method'
33
+ "special user method"
33
34
  end
34
35
  end
35
36
  end
36
37
 
37
- describe Surrounded::Context, 'access control' do
38
- let(:user){ User.new("Jim") }
39
- let(:other_user){ User.new("Guille") }
40
- let(:context){ FilteredContext.new(user: user, other_user: other_user) }
41
-
42
- it 'includes triggers when allowed' do
38
+ describe Surrounded::Context, "access control" do
39
+ let(:user) { User.new("Jim") }
40
+ let(:other_user) { User.new("Guille") }
41
+ let(:context) { FilteredContext.new(user: user, other_user: other_user) }
42
+
43
+ it "includes triggers when allowed" do
43
44
  context.stub(:disallow_if_ready?, false) do
44
45
  assert context.triggers.include?(:if_ready)
45
46
  end
46
47
  end
47
48
 
48
- it 'excludes triggers when not allowed' do
49
+ it "excludes triggers when not allowed" do
49
50
  refute context.triggers.include?(:if_ready)
50
51
  end
51
-
52
- it 'raises error specific to the context class when trigger method not allowed' do
53
- error = assert_raises(::FilteredContext::AccessError){
52
+
53
+ it "raises error specific to the context class when trigger method not allowed" do
54
+ error = assert_raises(::FilteredContext::AccessError) {
54
55
  context.if_ready
55
56
  }
56
57
  assert_match(/access to FilteredContext#if_ready is not allowed/i, error.message)
57
58
  end
58
-
59
- it 'supports rescuing from Surrounded defined error when trigger method not allowed' do
59
+
60
+ it "supports rescuing from Surrounded defined error when trigger method not allowed" do
60
61
  begin
61
62
  context.if_ready
62
63
  rescue ::Surrounded::Context::AccessError => error
@@ -64,20 +65,20 @@ describe Surrounded::Context, 'access control' do
64
65
  end
65
66
  assert_match(/access to FilteredContext#if_ready is not allowed/i, error.message)
66
67
  end
67
-
68
- it 'applies roles in disallow blocks' do
69
- assert_equal 'special user method', context.disallow_check_disallow_behavior?
68
+
69
+ it "applies roles in disallow blocks" do
70
+ assert_equal "special user method", context.disallow_check_disallow_behavior?
70
71
  end
71
72
 
72
- it 'lets you ask if the object will allow a method' do
73
+ it "lets you ask if the object will allow a method" do
73
74
  assert context.allow?(:unguarded)
74
75
  refute context.allow?(:check_disallow_behavior)
75
76
  end
76
77
 
77
- it 'complains if you ask about an undefined method' do
78
- error = assert_raises(NoMethodError){
78
+ it "complains if you ask about an undefined method" do
79
+ error = assert_raises(NoMethodError) {
79
80
  context.allow?(:not_a_defined_method)
80
81
  }
81
82
  assert_match(/undefined method `not_a_defined_method' for #<#{context.class}/, error.message)
82
83
  end
83
- end
84
+ end