surrounded 0.9.11 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/test.yml +18 -0
- data/Changelog.md +15 -1
- data/Gemfile +0 -7
- data/LICENSE.txt +1 -1
- data/README.md +77 -46
- data/examples/bottles.rb +107 -117
- data/lib/surrounded/access_control.rb +9 -17
- data/lib/surrounded/context/initializing.rb +4 -7
- data/lib/surrounded/context/negotiator.rb +6 -5
- data/lib/surrounded/context/role_builders.rb +4 -11
- data/lib/surrounded/context/role_map.rb +16 -7
- data/lib/surrounded/context/seclusion.rb +20 -0
- data/lib/surrounded/context/trigger_controls.rb +3 -3
- data/lib/surrounded/context.rb +12 -23
- data/lib/surrounded/shortcuts.rb +16 -4
- data/lib/surrounded/version.rb +2 -2
- data/surrounded.gemspec +0 -1
- data/test/collection_role_players_test.rb +1 -1
- data/test/context_access_test.rb +1 -1
- data/test/context_forwarding_test.rb +1 -1
- data/test/context_reuse_test.rb +1 -1
- data/test/context_shortcuts_test.rb +27 -2
- data/test/east_oriented_triggers_test.rb +4 -4
- data/test/example_delegate_class_test.rb +1 -1
- data/test/example_proxy_test.rb +3 -3
- data/test/example_threaded_test.rb +1 -1
- data/test/example_wrapper_test.rb +1 -1
- data/test/initialization_test.rb +37 -29
- data/test/override_methods_test.rb +1 -1
- data/test/role_context_method_test.rb +40 -4
- data/test/surrounded_context_test.rb +7 -7
- data/test/surrounded_test.rb +3 -3
- data/test/test_helper.rb +4 -6
- metadata +9 -23
- data/.travis.yml +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 152c9c728084be8069384cf825cedf5ab827d4fd28b0710cbdd19229d8284bc7
|
4
|
+
data.tar.gz: 82a50b285aa5328084481c438d78f8ed3e17ba362bcad45c14efaf5ad1bf0fc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f7cf841bf2c4ceeb27fd6bd2f11c4041cad7c28e1ce71574b5bbc1c201b0944a2481966b1c2349b3cbfd465a98f7627f1621fae0e0e8d2c2c9fcde42859577f
|
7
|
+
data.tar.gz: aa91c16fefb0cdc19f1b597d2fffd876c2f0bce15bcbcaacb3a713570acac350d8ff434a8c3d6177d4c9e1a79c620611f012d1aff2bcd24fef10ddf696bcd13b
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# For most projects, this workflow file will not need changing; you simply need
|
2
|
+
# to commit it to your repository.
|
3
|
+
#
|
4
|
+
# You may wish to alter this file to override the set of languages analyzed,
|
5
|
+
# or to provide custom queries or build logic.
|
6
|
+
#
|
7
|
+
# ******** NOTE ********
|
8
|
+
# We have attempted to detect the languages in your repository. Please check
|
9
|
+
# the `language` matrix defined below to confirm you have the correct set of
|
10
|
+
# supported CodeQL languages.
|
11
|
+
#
|
12
|
+
name: "CodeQL"
|
13
|
+
|
14
|
+
on:
|
15
|
+
push:
|
16
|
+
branches: [ master ]
|
17
|
+
pull_request:
|
18
|
+
# The branches below must be a subset of the branches above
|
19
|
+
branches: [ master ]
|
20
|
+
schedule:
|
21
|
+
- cron: '41 18 * * 4'
|
22
|
+
|
23
|
+
jobs:
|
24
|
+
analyze:
|
25
|
+
name: Analyze
|
26
|
+
runs-on: ubuntu-latest
|
27
|
+
permissions:
|
28
|
+
actions: read
|
29
|
+
contents: read
|
30
|
+
security-events: write
|
31
|
+
|
32
|
+
strategy:
|
33
|
+
fail-fast: false
|
34
|
+
matrix:
|
35
|
+
language: [ 'ruby' ]
|
36
|
+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
37
|
+
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
38
|
+
|
39
|
+
steps:
|
40
|
+
- name: Checkout repository
|
41
|
+
uses: actions/checkout@v3
|
42
|
+
|
43
|
+
# Initializes the CodeQL tools for scanning.
|
44
|
+
- name: Initialize CodeQL
|
45
|
+
uses: github/codeql-action/init@v2
|
46
|
+
with:
|
47
|
+
languages: ${{ matrix.language }}
|
48
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
49
|
+
# By default, queries listed here will override any specified in a config file.
|
50
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
51
|
+
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
52
|
+
|
53
|
+
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
54
|
+
# If this step fails, then you should remove it and run the build manually (see below)
|
55
|
+
- name: Autobuild
|
56
|
+
uses: github/codeql-action/autobuild@v2
|
57
|
+
|
58
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
59
|
+
# 📚 https://git.io/JvXDl
|
60
|
+
|
61
|
+
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
62
|
+
# and modify them (or add more) to build your code if your project
|
63
|
+
# uses a compiled language
|
64
|
+
|
65
|
+
#- run: |
|
66
|
+
# make bootstrap
|
67
|
+
# make release
|
68
|
+
|
69
|
+
- name: Perform CodeQL Analysis
|
70
|
+
uses: github/codeql-action/analyze@v2
|
@@ -0,0 +1,18 @@
|
|
1
|
+
name: spec
|
2
|
+
|
3
|
+
on: push
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: [ '2.7', '3.0', '3.1' ]
|
11
|
+
name: Ruby ${{ matrix.ruby }} sample
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: ${{ matrix.ruby }}
|
17
|
+
bundler-cache: true
|
18
|
+
- run: bundle exec rake
|
data/Changelog.md
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
+
## [1.1.0]
|
6
|
+
|
7
|
+
- Dropped support for Ruby below 2.7 to take advantage of the ellipsis for argument forwarding
|
8
|
+
- Memoize the Negotiator methods for interface
|
9
|
+
|
10
|
+
## [1.0.1]
|
11
|
+
|
12
|
+
- Fix a bug where shortcut_triggers would not work with keyword initialize
|
13
|
+
|
14
|
+
## [1.0.0]
|
15
|
+
|
16
|
+
- Drop deprecations around Context initialize method. It now requires keyword arguments. Non-keyword argumennts may be used with initialize_without_keywords
|
17
|
+
- Remove code supporting exception cause it InvalidRoleType prior to ruby 2.1
|
18
|
+
|
5
19
|
## [0.9.11]
|
6
20
|
|
7
21
|
- Rely on the standard library Forwardable to setup how the RoleMap forwards messages to the container.
|
@@ -11,4 +25,4 @@ All notable changes to this project will be documented in this file.
|
|
11
25
|
## [0.9.10]
|
12
26
|
|
13
27
|
- Do something with name collisions when a role player has an existing method of another role in the context.
|
14
|
-
- Move InvalidRoleType exception under the host context class namespace. This allows you to rescue from your own namespace.
|
28
|
+
- Move InvalidRoleType exception under the host context class namespace. This allows you to rescue from your own namespace.
|
data/Gemfile
CHANGED
@@ -2,15 +2,8 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
group :test do
|
4
4
|
gem 'minitest'
|
5
|
-
# gem 'mutant', git: 'https://github.com/kbrock/mutant.git', ref: 'minitest'
|
6
5
|
gem "simplecov"
|
7
|
-
gem 'coveralls', :require => false
|
8
6
|
gem 'casting'
|
9
|
-
gem 'rubinius-coverage', :platform => :rbx
|
10
|
-
end
|
11
|
-
|
12
|
-
platforms :rbx do
|
13
|
-
gem 'rubysl', '~> 2.0'
|
14
7
|
end
|
15
8
|
|
16
9
|
gemspec
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# ![Surrounded](http://saturnflyer.github.io/surrounded/images/surrounded.png "Surrounded")
|
2
2
|
## Be in control of business logic.
|
3
3
|
|
4
|
-
[![Build Status](https://
|
4
|
+
[![Build Status](https://github.com/saturnflyer/surrounded/actions/workflows/test.yml/badge.svg)](https://github.com/saturnflyer/surrounded/actions)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/saturnflyer/surrounded.png)](https://codeclimate.com/github/saturnflyer/surrounded)
|
6
|
-
[![Coverage Status](https://coveralls.io/repos/saturnflyer/surrounded/badge.png)](https://coveralls.io/r/saturnflyer/surrounded)
|
7
|
-
[![Gem Version](https://badge.fury.io/rb/surrounded.png)](http://badge.fury.io/rb/surrounded)
|
8
6
|
|
9
7
|
Surrounded is designed to help you better manage your business logic by keeping cohesive behaviors together. Bring objects together to implement your use cases and gain behavior only when necessary.
|
10
8
|
|
@@ -43,7 +41,7 @@ Here, you've specified the order when initializing so you can use it like this:
|
|
43
41
|
```ruby
|
44
42
|
user1 = User.find(1)
|
45
43
|
user2 = User.find(2)
|
46
|
-
context = Employment.new(user1, user2)
|
44
|
+
context = Employment.new(employee: user1, boss: user2)
|
47
45
|
```
|
48
46
|
|
49
47
|
That ensures that `user1` will become (and have all the features of) the `employee` and `user2` will become (and have all the features of) the `boss`.
|
@@ -53,20 +51,20 @@ There are 2 things left to do:
|
|
53
51
|
1. define behaviors for each role and
|
54
52
|
2. define how you can trigger their actions
|
55
53
|
|
56
|
-
|
54
|
+
Initializing contexts does not require the use of keyword arguments, but you may opt out.
|
57
55
|
|
58
|
-
You should consider using explicit names when
|
56
|
+
You should consider using explicit names when initializing now by using `initialize_without_keywords`:
|
59
57
|
|
60
58
|
```ruby
|
61
59
|
class Employment
|
62
60
|
extend Surrounded::Context
|
63
61
|
|
64
|
-
|
62
|
+
initialize_without_keywords :employee, :boss
|
65
63
|
end
|
66
64
|
|
67
65
|
user1 = User.find(1)
|
68
66
|
user2 = User.find(2)
|
69
|
-
context = Employment.new(
|
67
|
+
context = Employment.new(user1, user2)
|
70
68
|
```
|
71
69
|
|
72
70
|
This will allow you to prepare your accessing code to use keywords.
|
@@ -135,7 +133,7 @@ end
|
|
135
133
|
You'll need to define way to trigger these behaviors to occur so that you can use them.
|
136
134
|
|
137
135
|
```ruby
|
138
|
-
context = Employment.new(user1, user2)
|
136
|
+
context = Employment.new(employee: user1, boss: user2)
|
139
137
|
|
140
138
|
context.plan_weekend_work
|
141
139
|
```
|
@@ -249,7 +247,7 @@ By adding `Surrounded::Context` you can shortcut all this work.
|
|
249
247
|
```ruby
|
250
248
|
class Employment
|
251
249
|
extend Surrounded::Context
|
252
|
-
|
250
|
+
|
253
251
|
initialize(:employee, :boss)
|
254
252
|
|
255
253
|
module Employee
|
@@ -269,7 +267,7 @@ Well, it just so happens that you can. This code will work just fine:
|
|
269
267
|
```ruby
|
270
268
|
class Employment
|
271
269
|
extend Surrounded::Context
|
272
|
-
|
270
|
+
|
273
271
|
initialize(:employee, :boss)
|
274
272
|
|
275
273
|
class Employee < SimpleDelegator
|
@@ -285,7 +283,7 @@ But the syntax can be even simpler than that if you want.
|
|
285
283
|
```ruby
|
286
284
|
class Employment
|
287
285
|
extend Surrounded::Context
|
288
|
-
|
286
|
+
|
289
287
|
initialize(:employee, :boss)
|
290
288
|
|
291
289
|
role :employee do
|
@@ -299,7 +297,7 @@ By default, this code will create a module for you named `Employee`. If you want
|
|
299
297
|
```ruby
|
300
298
|
class Employment
|
301
299
|
extend Surrounded::Context
|
302
|
-
|
300
|
+
|
303
301
|
initialize(:employee, :boss)
|
304
302
|
|
305
303
|
wrap :employee do
|
@@ -313,7 +311,7 @@ But if you're making changes and you decide to move from a module to a wrapper o
|
|
313
311
|
```ruby
|
314
312
|
class Employment
|
315
313
|
extend Surrounded::Context
|
316
|
-
|
314
|
+
|
317
315
|
initialize(:employee, :boss)
|
318
316
|
|
319
317
|
role :employee, :wrapper do
|
@@ -346,7 +344,7 @@ end
|
|
346
344
|
|
347
345
|
Now the `User` instances will be able to implicitly access objects in their environment.
|
348
346
|
|
349
|
-
Via `method_missing` those `User` instances can access a `context` object it stores in an internal collection.
|
347
|
+
Via `method_missing` those `User` instances can access a `context` object it stores in an internal collection.
|
350
348
|
|
351
349
|
Inside of the `Employment` context we saw above, the `employee` and `boss` objects are instances of `User` for this example.
|
352
350
|
|
@@ -442,7 +440,7 @@ context.triggers #=> [:plan_weekend_work]
|
|
442
440
|
|
443
441
|
You might find that useful for dynamically defining user interfaces.
|
444
442
|
|
445
|
-
Sometimes I'd rather not use this DSL, however. I want to just write regular methods.
|
443
|
+
Sometimes I'd rather not use this DSL, however. I want to just write regular methods.
|
446
444
|
|
447
445
|
We can do that too. You'll need to opt in to this by specifying `trigger :your_method_name` for the methods you want to use.
|
448
446
|
|
@@ -454,7 +452,7 @@ class Employment
|
|
454
452
|
employee.quit
|
455
453
|
end
|
456
454
|
trigger :plan_weekend_work
|
457
|
-
|
455
|
+
|
458
456
|
# or in Ruby 2.x
|
459
457
|
trigger def plan_weekend_work
|
460
458
|
employee.quit
|
@@ -487,12 +485,12 @@ By running `protect_triggers` you'll be able to define when triggers may or may
|
|
487
485
|
class Employment
|
488
486
|
extend Surrounded::Context
|
489
487
|
protect_triggers
|
490
|
-
|
488
|
+
|
491
489
|
def plan_weekend_work
|
492
490
|
employee.quit
|
493
491
|
end
|
494
492
|
trigger :plan_weekend_work
|
495
|
-
|
493
|
+
|
496
494
|
disallow :plan_weekend_work do
|
497
495
|
employee.bank_balance > 1000000
|
498
496
|
end
|
@@ -627,14 +625,30 @@ class Organization
|
|
627
625
|
extend Surrounded::Context
|
628
626
|
|
629
627
|
initialize_without_keywords :leader, :members
|
630
|
-
|
628
|
+
|
631
629
|
role :members do
|
632
630
|
# special behavior for the collection
|
633
631
|
end
|
634
|
-
|
632
|
+
|
635
633
|
role :member do
|
636
634
|
# special behavior to be applied to each member in the collection
|
637
|
-
end
|
635
|
+
end
|
636
|
+
end
|
637
|
+
```
|
638
|
+
|
639
|
+
If you want to change the way the singular verson of a role is used, override `singularize_name`:
|
640
|
+
|
641
|
+
```ruby
|
642
|
+
class Organization
|
643
|
+
extend Surrounded::Context
|
644
|
+
|
645
|
+
def singularize_name(name)
|
646
|
+
if name == "my special rule"
|
647
|
+
# do your thing
|
648
|
+
else
|
649
|
+
super # use the default
|
650
|
+
end
|
651
|
+
end
|
638
652
|
end
|
639
653
|
```
|
640
654
|
|
@@ -643,7 +657,7 @@ end
|
|
643
657
|
If you create a context object and need to use the same type of object with new role players, you may use the `rebind` method. It will clear any instance_variables from your context object and map the given objects to their names:
|
644
658
|
|
645
659
|
```ruby
|
646
|
-
context = Employment.new(current_user, the_boss)
|
660
|
+
context = Employment.new(employee: current_user, boss: the_boss)
|
647
661
|
context.rebind(employee: another_user, boss: someone_else) # same context, new players
|
648
662
|
```
|
649
663
|
|
@@ -671,7 +685,7 @@ class ExpensiveCalculation
|
|
671
685
|
end
|
672
686
|
end
|
673
687
|
end
|
674
|
-
ExpensiveCalculation.new(some_object, some_collection).send_to_background(:do_expensive_calculation)
|
688
|
+
ExpensiveCalculation.new(leader: some_object, members: some_collection).send_to_background(:do_expensive_calculation)
|
675
689
|
```
|
676
690
|
|
677
691
|
The above example is merely pseudo-code to show how `initializer_arguments` can be used. Customize it according to your own needs.
|
@@ -686,14 +700,14 @@ Surrounded::Context.default_role_type = :module # also :wrap, :wrapper, or :inte
|
|
686
700
|
|
687
701
|
class ActiviatingAccount
|
688
702
|
extend Surrounded::Context
|
689
|
-
|
703
|
+
|
690
704
|
# set the default role type only for this class
|
691
705
|
self.default_role_type = :module # also :wrap, :wrapper, or :interface
|
692
706
|
|
693
707
|
# shortcut initialization code
|
694
708
|
initialize(:activator, :account)
|
695
709
|
# or handle it yourself
|
696
|
-
def initialize(activator
|
710
|
+
def initialize(activator:, account:)
|
697
711
|
# this must be done to handle the mapping of roles to objects
|
698
712
|
# pass an array of arrays with role name symbol and the object for that role
|
699
713
|
map_roles([[:activator, activator],[:account, account]])
|
@@ -707,18 +721,21 @@ class ActiviatingAccount
|
|
707
721
|
# these also must be done if you create your own initialize method.
|
708
722
|
# this is a shortcut for using attr_reader and private
|
709
723
|
private_attr_reader :activator, :account
|
710
|
-
|
724
|
+
|
711
725
|
# If you need to mix default initialzation and extra work use a block
|
712
726
|
initialize :activator, :account do
|
713
727
|
map_roles(:third_party => get_some_other_object)
|
728
|
+
# explicitly set a single role
|
729
|
+
map_role(:something_new, 'SomeRoleConstant', object_to_play_the_role)
|
714
730
|
end
|
731
|
+
|
715
732
|
# but remember to set the extra accessors:
|
716
|
-
private_attr_reader :third_party
|
733
|
+
private_attr_reader :third_party, :something_new
|
717
734
|
|
718
|
-
# initialize
|
719
|
-
|
720
|
-
# this makes the following instance method signature with
|
721
|
-
def initialize(activator
|
735
|
+
# initialize without keyword arguments
|
736
|
+
initialize_without_keywords(:activator, :account)
|
737
|
+
# this makes the following instance method signature with positional arguments
|
738
|
+
def initialize(activator, account)
|
722
739
|
# ...
|
723
740
|
end
|
724
741
|
|
@@ -756,7 +773,7 @@ class ActiviatingAccount
|
|
756
773
|
# end
|
757
774
|
# end
|
758
775
|
|
759
|
-
# if you use a regular method and want to use context-specific behavior,
|
776
|
+
# if you use a regular method and want to use context-specific behavior,
|
760
777
|
# you must handle storing the context yourself:
|
761
778
|
def regular_method
|
762
779
|
apply_behaviors # handles the adding of all the roles and behaviors
|
@@ -771,30 +788,30 @@ class ActiviatingAccount
|
|
771
788
|
trigger :some_trigger_method do
|
772
789
|
activator.some_behavior # behavior always available
|
773
790
|
end
|
774
|
-
|
791
|
+
|
775
792
|
trigger def some_other_trigger
|
776
793
|
activator.some_behavior # behavior always available
|
777
794
|
end
|
778
|
-
|
795
|
+
|
779
796
|
def regular_non_trigger
|
780
797
|
activator.some_behavior # behavior always available with the following line
|
781
798
|
end
|
782
799
|
trigger :regular_non_trigger # turns the method into a trigger
|
783
|
-
|
800
|
+
|
784
801
|
# create restrictions on what triggers may be used
|
785
802
|
protect_triggers # <-- this is required if you want to protect your triggers this way.
|
786
803
|
disallow :some_trigger_method do
|
787
804
|
# whatever conditional code for the instance of the context
|
788
805
|
end
|
789
806
|
# you could also use `guard` instead of `disallow`
|
790
|
-
|
807
|
+
|
791
808
|
# or define your own method without the `disallow` keyword
|
792
809
|
def disallow_some_trigger_method?
|
793
810
|
# whatever conditional code for the instance of the context
|
794
811
|
end
|
795
812
|
# Prefer using `disallow` because it will wrap role players in their roles for you;
|
796
813
|
# the `disallow_some_trigger_method?` defined above, does not.
|
797
|
-
|
814
|
+
|
798
815
|
# Create shortcuts for triggers as class methods
|
799
816
|
# so you can do ActiviatingAccount.some_trigger_method(activator, account)
|
800
817
|
# This will make all triggers shortcuts.
|
@@ -804,11 +821,11 @@ class ActiviatingAccount
|
|
804
821
|
instance = self.new(activator, account)
|
805
822
|
instance.some_trigger_method
|
806
823
|
end
|
807
|
-
|
824
|
+
|
808
825
|
# Set triggers to always return the context object
|
809
826
|
# so you can enforce East-oriented style or Tell, Don't Ask
|
810
827
|
east_oriented_triggers
|
811
|
-
|
828
|
+
|
812
829
|
# Forward context instance methods as triggers to role players
|
813
830
|
forward_trigger :role_name, :method_name
|
814
831
|
forward_trigger :role_name, :method_name, :alternative_trigger_name_for_method_name
|
@@ -816,14 +833,14 @@ class ActiviatingAccount
|
|
816
833
|
forwarding [:list, :of, :methods, :to, :forward] => :role_name
|
817
834
|
end
|
818
835
|
|
819
|
-
# with
|
836
|
+
# with initialize (also keyword_initialize)
|
820
837
|
context = ActiviatingAccount.new(activator: some_object, account: some_account)
|
821
|
-
# with
|
838
|
+
# with initialize_without_keywords
|
822
839
|
context = ActiviatingAccount.new(some_object, some_account)
|
823
840
|
context.triggers # => lists a Set of triggers
|
824
841
|
# when using protect_triggers
|
825
842
|
context.triggers # => lists a Set of triggers which may currently be called
|
826
|
-
context.
|
843
|
+
context.all_triggers # => lists a Set of all triggers (the same as if protect_triggers was _not_ used)
|
827
844
|
context.allow?(:trigger_name) # => returns a boolean if the trigger may be run
|
828
845
|
|
829
846
|
# reuse the context object with new role players
|
@@ -834,6 +851,20 @@ context.rebind(activator: another_object, account: another_account)
|
|
834
851
|
|
835
852
|
The dependencies are minimal. The plan is to keep it that way but allow you to configure things as you need. The [Triad](http://github.com/saturnflyer/triad) project was written specifically to manage the mapping of roles and objects to the modules which contain the behaviors. It is used in Surrounded to keep track of role player, roles, and role constant names but it is not a hard requirement. You may implement your own but presently you'll need to dive into the implementation to fully understand how. Future updates may provide better support and guidance.
|
836
853
|
|
854
|
+
If you want to override the class used for mapping roles to behaviors, override the `role_map` method.
|
855
|
+
|
856
|
+
```ruby
|
857
|
+
class MyContext
|
858
|
+
extend Surrounded::Context
|
859
|
+
|
860
|
+
def role_map
|
861
|
+
@container ||= role_mapper_class.new(base: MySpecialDataContainer)
|
862
|
+
end
|
863
|
+
end
|
864
|
+
```
|
865
|
+
|
866
|
+
The class you provide will be initialized with `new` and is expected to implement the methods: `:update`, `:each`, `:values`, and `:keys`.
|
867
|
+
|
837
868
|
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.
|
838
869
|
|
839
870
|
## Support for other ways to apply behavior
|
@@ -917,9 +948,9 @@ class MyCustomContext
|
|
917
948
|
end
|
918
949
|
```
|
919
950
|
|
920
|
-
You can remember the method name by the convention that `remove` or `apply` describes it's function, `behavior` refers to the first argument (
|
951
|
+
You can remember the method name by the convention that `remove` or `apply` describes it's function, `behavior` refers to the first argument (the constant holding the behaviors), and then the name of the role which refers to the role playing object: `remove_behavior_role`.
|
921
952
|
|
922
|
-
##Name collisions between methods and roles
|
953
|
+
## Name collisions between methods and roles
|
923
954
|
|
924
955
|
Lets say that you wish to create a context as below, intending to use instances of the following two classes as role players:
|
925
956
|
|
@@ -1072,7 +1103,7 @@ And then execute:
|
|
1072
1103
|
Or install it yourself as:
|
1073
1104
|
|
1074
1105
|
$ gem install surrounded
|
1075
|
-
|
1106
|
+
|
1076
1107
|
## Installation for Rails
|
1077
1108
|
|
1078
1109
|
See [surrounded-rails](https://github.com/saturnflyer/surrounded-rails)
|