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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3364c250a02c0589ae2cbaf9645235cfd83f7406
4
- data.tar.gz: 28bcbe612f92d77a9df5d4a5294ebc22420013a2
2
+ SHA256:
3
+ metadata.gz: 152c9c728084be8069384cf825cedf5ab827d4fd28b0710cbdd19229d8284bc7
4
+ data.tar.gz: 82a50b285aa5328084481c438d78f8ed3e17ba362bcad45c14efaf5ad1bf0fc3
5
5
  SHA512:
6
- metadata.gz: bf99487271fdcc488dbf5b8dc36d8f7fdd66354bb218bf753682de69cc281d63c9808e3c5f3b7a23c7e71a31f61fa8df0643d9ca4b049c47d35e5734c3f63e79
7
- data.tar.gz: 5ce4fcdc96205adf8e24d31de75e88ce5e42877266c4714a8066084f61d3e598140bf2830f8f7193c63e318d61c9fb627278e3f5e43b7785d954544e895128dd
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2016 'Jim Gay'
1
+ Copyright (c) 2013-2022 'Jim Gay'
2
2
 
3
3
  MIT License
4
4
 
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://travis-ci.org/saturnflyer/surrounded.png?branch=master)](https://travis-ci.org/saturnflyer/surrounded)
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
- Currently initializing contexts does not require the use of keyword arguments, _but it will in the future_.
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 initialize now by using `keyword_initialize`:
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
- keyword_initialize :employee, :boss
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(employee: user1, boss: user2)
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, account)
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 with keyword arguments
719
- keyword_initialize(:activator, :account)
720
- # this makes the following instance method signature with required keyword arguments
721
- def initialize(activator:, account:)
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 keyword_initialize (will be changed to initialize)
836
+ # with initialize (also keyword_initialize)
820
837
  context = ActiviatingAccount.new(activator: some_object, account: some_account)
821
- # with initialize (this will be moved to initialize_without_keywords)
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.triggers # => lists a Set of all triggers (the same as if protect_triggers was _not_ used)
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 (thet contsant holding the behaviors), and then the name of the role which refers to the role playing object: `remove_behavior_role`.
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)