surrounded 0.6.0 → 0.7.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5b50df20142b16850a46c0824115d7286d5c5db0
4
- data.tar.gz: bbf2f02bc6d2bf089f8a1a6c33627cac0efb8e43
3
+ metadata.gz: 31db490fd3a2fd39b290aa7c5f9368e1ce3fa963
4
+ data.tar.gz: eef7f29cd0632c9066686d00b873629ef80f7942
5
5
  SHA512:
6
- metadata.gz: 4d1badd06972d938d14bc50ea50d8d074c882a4ebf2728724de48131e875f55f75dcdd019fbcdd64322eb6591d414ec9fccbe12cc106518fa3d309a93ab31294
7
- data.tar.gz: b3aa0cfaaec0178fbe799fff2b64d16f4565be58eb6d95e29eb46542a7ef35a7bc50ddc80bae8b51ff5b614277ff3079846d4a872697e8d2c11364f3984781ea
6
+ metadata.gz: 6719cd08a3549f2b23a5193e86e656fb88480d3321193fad86709e09fdfe9ccf07b68659a5c51efe2b59e6c27904227246b23d17ece00882b411262123d9f881
7
+ data.tar.gz: 71ae69d703a4c4502ddc000f06294c6c87f047253c2c4a86b7acee3a6d3d0106abf4b9e4578bfe8320e109afbdd1f77caf99a16ecb237f1b1b604204f1087da3
data/.travis.yml CHANGED
@@ -2,14 +2,15 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+ - 2.1.0
5
6
  - jruby-19mode # JRuby in 1.9 mode
6
7
  - ruby-head
7
8
  - jruby-head
8
- - rbx-19mode
9
+ - rbx
9
10
  env:
10
11
  - COVERALLS=true
11
12
  matrix:
12
13
  allow_failures:
13
14
  - rvm: ruby-head
14
15
  - rvm: jruby-head
15
- - rvm: rbx-19mode
16
+ - rvm: rbx
data/Gemfile CHANGED
@@ -7,4 +7,8 @@ group :test do
7
7
  gem 'casting'
8
8
  end
9
9
 
10
+ platforms :rbx do
11
+ gem 'rubysl', '~> 2.0'
12
+ end
13
+
10
14
  gemspec
data/README.md CHANGED
@@ -275,6 +275,40 @@ This will allow you to write methods like you normally would. They are aliased i
275
275
 
276
276
  This works like Ruby's `public`,`protected`, and `private` keywords in that you can send symbols of method names to it. But `trigger` does not alter the parsing of the document like those core keywords do. In other words, you can't merely type `trigger` on one line, and have methods added afterward be treated as trigger methods.
277
277
 
278
+ ## Access Control for Triggers
279
+
280
+ If you decide to build a user interface from the available triggers, you'll find you need to know what triggers are available.
281
+
282
+ Fortunately, you can make it easy.
283
+
284
+ By running `protect_triggers` you'll be able to define when triggers may or may not be run. You can still run them, but they'll raise an error. Here's an example.
285
+
286
+ ```ruby
287
+ class MyEnvironment
288
+ extend Surrounded::Context
289
+ protect_triggers
290
+
291
+ def shove_it
292
+ employee.quit
293
+ end
294
+ trigger :shove_it
295
+
296
+ disallow :shove_it do
297
+ employee.bank_balance < 100
298
+ end
299
+ end
300
+ ```
301
+
302
+ Then, when the employee role's `bank_balance` is less than `100`, the available triggers won't include `:shove_it`.
303
+
304
+ You can compare the instance of the context by listing `all_triggers` and `triggers` to see what could be possible and what's currently possible.
305
+
306
+ Alternatively, if you just want to define your own methods without the DSL using `disallow`, you can just follow the pattern of `disallow_#{method_name}?` when creating your own protection.
307
+
308
+ In fact, that's exactly what happens with the `disallow` keyword. After using it here, we'd have a `disallow_shove_it?` method defined.
309
+
310
+ If you call the disallowed trigger directly, you'll raise a `Surrounded::Context::AccessError` exception and the code in your trigger will not be run.
311
+
278
312
  ## Where roles exist
279
313
 
280
314
  By using `Surrounded::Context` you are declaring a relationship between the objects inside playing your defined roles.
@@ -438,12 +472,21 @@ class ActiviatingAccount
438
472
  # pass an array of arrays with role name symbol and the object for that role
439
473
  map_roles([[:activator, activator],[:account, account]])
440
474
  end
475
+
476
+ # if you want to stick with the `initialize` shortcut you can define these methods
477
+ # for special initialization behavior.
478
+ def preinitialize
479
+ # to happen before any role mapping
480
+ end
481
+ def postinitialize
482
+ # to happen after any role mapping
483
+ end
441
484
 
442
485
  role :activator do # module by default
443
486
  def some_behavior; end
444
487
  end
445
488
 
446
- # role :activator, :module do
489
+ # role_methods :activator, :module do # alternatively use role_methods if you choose
447
490
  # def some_behavior; end
448
491
  # end
449
492
  #
@@ -457,16 +500,19 @@ class ActiviatingAccount
457
500
  #
458
501
  # use your own classes if you don't want SimpleDelegator
459
502
  # class SomeSpecialRole
460
- # include Surrounded # you must remember this
503
+ # include Surrounded # <-- you must remember this in your own classes
461
504
  # # Surrounded assumes SomeSpecialRole.new(some_special_role)
462
505
  # def initialize(...);
463
506
  # # ... your code here
464
507
  # end
465
508
  # end
466
509
 
467
- # works as a trigger (assigning the current context) only if set_methods_as_triggers is set
510
+ # if you use a regular method and want to use context-specific behavior,
511
+ # you must handle storing the context yourself:
468
512
  def regular_method
513
+ apply_roles # handles the adding of all the roles and behaviors
469
514
  activator.some_behavior # behavior not available unless you apply roles on initialize
515
+ remove_roles # handles the removal of all roles and behaviors
470
516
  end
471
517
 
472
518
  trigger :some_trigger_method do
@@ -480,7 +526,18 @@ class ActiviatingAccount
480
526
  def regular_non_trigger
481
527
  activator.some_behavior # behavior always available with the following line
482
528
  end
483
- trigger :regular_non_trigger # turns the method into a trigger
529
+ trigger :regular_non_trigger # turns the method into a trigger
530
+
531
+ # create restrictions on what triggers may be used
532
+ protect_triggers # <-- this is required if you want to protect your triggers this way.
533
+ disallow :some_trigger_method do
534
+ # whatever conditional code for the instance of the context
535
+ end
536
+
537
+ # or define your own method without the `disallow` keyword
538
+ def disallow_some_trigger_method?
539
+ # whatever conditional code for the instance of the context
540
+ end
484
541
  end
485
542
  ```
486
543
 
@@ -0,0 +1,48 @@
1
+ module Surrounded
2
+ module AccessControl
3
+ def self.extended(base)
4
+ base.send(:include, AccessMethods)
5
+ end
6
+
7
+ private
8
+
9
+ def disallow(*names, &block)
10
+ names.map do |name|
11
+ define_method("disallow_#{name}?", &block)
12
+ end
13
+ end
14
+
15
+ def redo_method(name)
16
+ class_eval %{
17
+ def #{name}
18
+ begin
19
+ apply_roles if __apply_role_policy == :trigger
20
+
21
+ method_restrictor = "disallow_#{name}?"
22
+ if self.respond_to?(method_restrictor, true) && self.send(method_restrictor)
23
+ raise ::Surrounded::Context::AccessError.new("access to `#{name}' is not allowed")
24
+ end
25
+
26
+ self.send("__trigger_#{name}")
27
+
28
+ ensure
29
+ remove_roles if __apply_role_policy == :trigger
30
+ end
31
+ end
32
+ }
33
+ end
34
+
35
+ module AccessMethods
36
+ def all_triggers
37
+ self.class.triggers
38
+ end
39
+
40
+ def triggers
41
+ all_triggers.select {|name|
42
+ method_restrictor = "disallow_#{name}?"
43
+ !self.respond_to?(method_restrictor, true) || !self.send(method_restrictor)
44
+ }.to_set
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,5 +1,6 @@
1
1
  require 'set'
2
2
  require 'surrounded/context/role_map'
3
+ require 'surrounded/access_control'
3
4
 
4
5
  # Some features are only available in versions of Ruby
5
6
  # where this method is true
@@ -30,6 +31,10 @@ module Surrounded
30
31
  class << self
31
32
  attr_writer :default_role_type
32
33
  end
34
+
35
+ def protect_triggers
36
+ self.extend(::Surrounded::AccessControl)
37
+ end
33
38
 
34
39
  def new(*args, &block)
35
40
  instance = allocate
@@ -3,5 +3,6 @@ module Surrounded
3
3
  module Context
4
4
  class InvalidRole < ::Triad::KeyNotPresent; end
5
5
  module InvalidRoleType; end
6
+ class AccessError < StandardError; end
6
7
  end
7
8
  end
@@ -1,3 +1,3 @@
1
1
  module Surrounded
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+ require 'minitest/mock'
3
+
4
+ class FilteredContext
5
+ extend Surrounded::Context
6
+ protect_triggers
7
+
8
+ initialize :user, :other_user
9
+
10
+ trigger :if_ready do
11
+ 'ready'
12
+ end
13
+
14
+ disallow :if_ready do
15
+ user.name != 'Amy'
16
+ end
17
+ end
18
+
19
+ describe Surrounded::Context, 'access control' do
20
+ let(:user){ User.new("Jim") }
21
+ let(:other_user){ User.new("Guille") }
22
+ let(:context){ FilteredContext.new(user, other_user) }
23
+
24
+ it 'includes triggers when allowed' do
25
+ context.stub(:disallow_if_ready?, false) do
26
+ assert context.triggers.include?(:if_ready)
27
+ end
28
+ end
29
+
30
+ it 'excludes triggers when not allowed' do
31
+ refute context.triggers.include?(:if_ready)
32
+ end
33
+
34
+ it 'raises errors when trigger method not allowed' do
35
+ error = assert_raises(Surrounded::Context::AccessError){
36
+ context.if_ready
37
+ }
38
+ assert_match(/access to `if_ready' is not allowed/i, error.message)
39
+ end
40
+ end
metadata CHANGED
@@ -1,55 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: surrounded
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
- - "'Jim Gay'"
7
+ - '''Jim Gay'''
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-16 00:00:00.000000000 Z
11
+ date: 2014-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: triad
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: 0.1.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.1.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  description: Gives an object implicit access to other objects in it's environment.
@@ -59,21 +59,23 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
- - ".gitignore"
63
- - ".simplecov"
64
- - ".travis.yml"
62
+ - .gitignore
63
+ - .simplecov
64
+ - .travis.yml
65
65
  - Gemfile
66
66
  - LICENSE.txt
67
67
  - README.md
68
68
  - Rakefile
69
69
  - examples/rails.rb
70
70
  - lib/surrounded.rb
71
+ - lib/surrounded/access_control.rb
71
72
  - lib/surrounded/context.rb
72
73
  - lib/surrounded/context/negotiator.rb
73
74
  - lib/surrounded/context/role_map.rb
74
75
  - lib/surrounded/context_errors.rb
75
76
  - lib/surrounded/version.rb
76
77
  - surrounded.gemspec
78
+ - test/context_access_test.rb
77
79
  - test/example_proxy_test.rb
78
80
  - test/example_threaded_test.rb
79
81
  - test/example_wrapper_test.rb
@@ -91,21 +93,22 @@ require_paths:
91
93
  - lib
92
94
  required_ruby_version: !ruby/object:Gem::Requirement
93
95
  requirements:
94
- - - ">="
96
+ - - '>='
95
97
  - !ruby/object:Gem::Version
96
98
  version: '0'
97
99
  required_rubygems_version: !ruby/object:Gem::Requirement
98
100
  requirements:
99
- - - ">="
101
+ - - '>='
100
102
  - !ruby/object:Gem::Version
101
103
  version: '0'
102
104
  requirements: []
103
105
  rubyforge_project:
104
- rubygems_version: 2.2.0
106
+ rubygems_version: 2.0.14
105
107
  signing_key:
106
108
  specification_version: 4
107
109
  summary: Create encapsulated environments for your objects.
108
110
  test_files:
111
+ - test/context_access_test.rb
109
112
  - test/example_proxy_test.rb
110
113
  - test/example_threaded_test.rb
111
114
  - test/example_wrapper_test.rb