surrounded 0.8.0 → 0.8.1

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
2
  SHA1:
3
- metadata.gz: 3d96d8394dfb54007979fd795564e48c386e622e
4
- data.tar.gz: ab5db5c3c15e471fe40aadd917253b7a8a46df42
3
+ metadata.gz: f382683688ed856434a815e352dcc47f4acba5bc
4
+ data.tar.gz: c4701d73fe34507e5f4d57c06166c76a9f17d151
5
5
  SHA512:
6
- metadata.gz: 2852a2bee0f27cf373d458b4b7d2a80a0c8607ae944093866bee4315f63ca64e62b1ec06f9411d0143d08e7ce90689425a1ca45fcfb9ef35e6c8dd6f855ed641
7
- data.tar.gz: 9bbd4161276d7900c0278db06ce84a3f630f2903ae542b2e8e7b53415b7d2ae4f172c942b284c3b7fd4e36f5378547551ebc3ee0b0dfdd5b3af7adb287f7ab08
6
+ metadata.gz: eff04f84a73c9f73b933e5e30491524d6641bc42e7e16a84a33245a4cc4ab5b2f916ae0e545ca3669134e0e97041701811380988c9fca34ec27a8e07a9a6c804
7
+ data.tar.gz: 29dcf1aa2d0705efd2111b83f940ea6d67818966d0fc52c51392ab096783ee300ad0476e9ad19d3e1fe0f415d3a4fc2024b0d5e9e02287e50ccba89fd8ffd65d
@@ -2,7 +2,6 @@ module Surrounded
2
2
  module AccessControl
3
3
  def self.extended(base)
4
4
  base.send(:include, AccessMethods)
5
- base.const_set(:'AccessError', Class.new(StandardError))
6
5
  end
7
6
 
8
7
  private
@@ -58,20 +58,12 @@ module Surrounded
58
58
  attr_writer :default_role_type
59
59
  end
60
60
 
61
- # Additional Features
62
-
63
61
  # Provide the ability to create access control methods for your triggers.
64
62
  def protect_triggers; self.extend(::Surrounded::AccessControl); end
65
63
 
66
64
  # Automatically create class methods for each trigger method.
67
65
  def shortcut_triggers; self.extend(::Surrounded::Shortcuts); end
68
66
 
69
- def private_const_set(name, const)
70
- const = const_set(name, const)
71
- private_constant name.to_sym
72
- const
73
- end
74
-
75
67
  def default_role_type
76
68
  @default_role_type ||= Surrounded::Context.default_role_type
77
69
  end
@@ -81,6 +73,32 @@ module Surrounded
81
73
  @default_role_type = type
82
74
  end
83
75
 
76
+ # Set the time to apply roles to objects. Either :trigger or :initialize.
77
+ # Defaults to :trigger
78
+ def apply_roles_on(which)
79
+ @__apply_role_policy = which
80
+ end
81
+
82
+ def __apply_role_policy
83
+ @__apply_role_policy ||= :trigger
84
+ end
85
+
86
+ # Shorthand for creating an instance level initialize method which
87
+ # handles the mapping of the given arguments to their named role.
88
+ def initialize(*setup_args)
89
+ private_attr_reader(*setup_args)
90
+
91
+ class_eval "
92
+ def initialize(#{setup_args.join(',')})
93
+ preinitialize
94
+ arguments = method(__method__).parameters.map{|arg| eval(arg[1].to_s) }
95
+ @role_map = RoleMap.new
96
+ map_roles(#{setup_args}.zip(arguments))
97
+ postinitialize
98
+ end
99
+ ", __FILE__, __LINE__
100
+ end
101
+
84
102
  # Create a named behavior for a role using the standard library SimpleDelegator.
85
103
  def wrap(name, &block)
86
104
  require 'delegate'
@@ -121,35 +139,6 @@ module Surrounded
121
139
  end
122
140
  alias_method :role_methods, :role
123
141
 
124
- def apply_roles_on(which)
125
- @__apply_role_policy = which
126
- end
127
-
128
- def __apply_role_policy
129
- @__apply_role_policy ||= :trigger
130
- end
131
-
132
- # Shorthand for creating an instance level initialize method which
133
- # handles the mapping of the given arguments to their named role.
134
- def initialize(*setup_args)
135
- private_attr_reader(*setup_args)
136
-
137
- class_eval "
138
- def initialize(#{setup_args.join(',')})
139
- preinitialize
140
- arguments = method(__method__).parameters.map{|arg| eval(arg[1].to_s) }
141
- @role_map = RoleMap.new
142
- map_roles(#{setup_args}.zip(arguments))
143
- postinitialize
144
- end
145
- ", __FILE__, __LINE__
146
- end
147
-
148
- def private_attr_reader(*method_names)
149
- attr_reader(*method_names)
150
- private(*method_names)
151
- end
152
-
153
142
  # Creates a context instance method which will apply behaviors to role players
154
143
  # before execution and remove the behaviors after execution.
155
144
  #
@@ -186,12 +175,6 @@ module Surrounded
186
175
  def store_trigger(*names)
187
176
  @triggers.merge(names)
188
177
  end
189
-
190
- def role_const(name)
191
- if const_defined?(name)
192
- const_get(name)
193
- end
194
- end
195
178
 
196
179
  def define_trigger_method(name, &block)
197
180
  unless triggers.include?(name) || name.nil?
@@ -217,6 +200,30 @@ module Surrounded
217
200
  end
218
201
  }, __FILE__, __LINE__
219
202
  end
203
+
204
+ # === Utility shortcuts
205
+
206
+ # Set a named constant and make it private
207
+ def private_const_set(name, const)
208
+ unless self.const_defined?(name, false)
209
+ const = const_set(name, const)
210
+ private_constant name.to_sym
211
+ end
212
+ const
213
+ end
214
+
215
+ # Create attr_reader for the named methods and make them private
216
+ def private_attr_reader(*method_names)
217
+ attr_reader(*method_names)
218
+ private(*method_names)
219
+ end
220
+
221
+ # Conditional const_get for a named role behavior
222
+ def role_const(name)
223
+ if const_defined?(name)
224
+ const_get(name)
225
+ end
226
+ end
220
227
 
221
228
  module InstanceMethods
222
229
  # Check whether a given name is a role inside the context.
@@ -1,3 +1,3 @@
1
1
  module Surrounded
2
- VERSION = "0.8.0"
2
+ VERSION = "0.8.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: surrounded
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - "'Jim Gay'"
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-27 00:00:00.000000000 Z
11
+ date: 2014-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: triad
@@ -67,16 +67,6 @@ files:
67
67
  - README.md
68
68
  - Rakefile
69
69
  - examples/rails.rb
70
- - images/body-bg.png
71
- - images/highlight-bg.jpg
72
- - images/hr.png
73
- - images/octocat-icon.png
74
- - images/surrounded.png
75
- - images/tar-gz-icon.png
76
- - images/zip-icon.png
77
- - index.html
78
- - javascripts/main.js
79
- - javascripts/scale.fix.js
80
70
  - lib/surrounded.rb
81
71
  - lib/surrounded/access_control.rb
82
72
  - lib/surrounded/context.rb
@@ -85,11 +75,6 @@ files:
85
75
  - lib/surrounded/context_errors.rb
86
76
  - lib/surrounded/shortcuts.rb
87
77
  - lib/surrounded/version.rb
88
- - params.json
89
- - stylesheets/print.css
90
- - stylesheets/pygment_trac.css
91
- - stylesheets/styles.css
92
- - stylesheets/stylesheet.css
93
78
  - surrounded.gemspec
94
79
  - test/context_access_test.rb
95
80
  - test/context_shortcuts_test.rb
data/images/body-bg.png DELETED
Binary file
Binary file
data/images/hr.png DELETED
Binary file
Binary file
Binary file
Binary file
data/images/zip-icon.png DELETED
Binary file
data/index.html DELETED
@@ -1,544 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <meta http-equiv="X-UA-Compatible" content="chrome=1">
6
- <title>Surrounded by saturnflyer</title>
7
-
8
- <link rel="stylesheet" href="stylesheets/styles.css">
9
- <link rel="stylesheet" href="stylesheets/pygment_trac.css">
10
- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
11
- <!--[if lt IE 9]>
12
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
- <![endif]-->
14
- </head>
15
- <body>
16
- <div class="wrapper">
17
- <header>
18
- <h1>Surrounded</h1>
19
- <p>Gives an object implicit access to other objects in it's environment.</p>
20
-
21
- <p class="view"><a href="https://github.com/saturnflyer/surrounded">View the Project on GitHub <small>saturnflyer/surrounded</small></a></p>
22
-
23
-
24
- <ul>
25
- <li><a href="https://github.com/saturnflyer/surrounded/zipball/master">Download <strong>ZIP File</strong></a></li>
26
- <li><a href="https://github.com/saturnflyer/surrounded/tarball/master">Download <strong>TAR Ball</strong></a></li>
27
- <li><a href="https://github.com/saturnflyer/surrounded">View On <strong>GitHub</strong></a></li>
28
- </ul>
29
- </header>
30
- <section>
31
- <h1>
32
- <a name="" class="anchor" href="#"><span class="octicon octicon-link"></span></a><img src="http://saturnflyer.github.io/surrounded/images/surrounded.png" alt="Surrounded" title="Surrounded">
33
- </h1>
34
-
35
- <h2>
36
- <a name="bring-your-own-complexity" class="anchor" href="#bring-your-own-complexity"><span class="octicon octicon-link"></span></a>Bring your own complexity</h2>
37
-
38
- <p><a href="https://travis-ci.org/saturnflyer/surrounded"><img src="https://travis-ci.org/saturnflyer/surrounded.png?branch=master" alt="Build Status"></a>
39
- <a href="https://codeclimate.com/github/saturnflyer/surrounded"><img src="https://codeclimate.com/github/saturnflyer/surrounded.png" alt="Code Climate"></a>
40
- <a href="https://coveralls.io/r/saturnflyer/surrounded"><img src="https://coveralls.io/repos/saturnflyer/surrounded/badge.png" alt="Coverage Status"></a>
41
- <a href="http://badge.fury.io/rb/surrounded"><img src="https://badge.fury.io/rb/surrounded.png" alt="Gem Version"></a></p>
42
-
43
- <h1>
44
- <a name="surrounded-aims-to-make-things-simple-and-get-out-of-your-way" class="anchor" href="#surrounded-aims-to-make-things-simple-and-get-out-of-your-way"><span class="octicon octicon-link"></span></a>Surrounded aims to make things simple and get out of your way.</h1>
45
-
46
- <p>Most of what you care about is defining the behavior of objects. How they interact is important.
47
- The purpose of this library is to clear away the details of getting things setup and to allow you to make changes to the way you handle roles.</p>
48
-
49
- <p>There are two main parts to this library. </p>
50
-
51
- <ol>
52
- <li>
53
- <code>Surrounded</code> gives objects an implicit awareness of other objects in their environments.</li>
54
- <li>
55
- <code>Surrounded::Context</code> helps you create objects which encapsulate other objects. These <em>are</em> the environments.</li>
56
- </ol><p>First, take a look at creating contexts. This is where you'll spend most of your time.</p>
57
-
58
- <h2>
59
- <a name="easily-create-encapsulated-environments-for-your-objects" class="anchor" href="#easily-create-encapsulated-environments-for-your-objects"><span class="octicon octicon-link"></span></a>Easily create encapsulated environments for your objects.</h2>
60
-
61
- <p>Typical initialization of an environment, or a Context in DCI, has a lot of code. For example:</p>
62
-
63
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
64
-
65
- <span class="kp">attr_reader</span> <span class="ss">:employee</span><span class="p">,</span> <span class="ss">:boss</span>
66
- <span class="kp">private</span> <span class="ss">:employee</span><span class="p">,</span> <span class="ss">:boss</span>
67
- <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">employee</span><span class="p">,</span> <span class="n">boss</span><span class="p">)</span>
68
- <span class="vi">@employee</span> <span class="o">=</span> <span class="n">employee</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="no">Employee</span><span class="p">)</span>
69
- <span class="vi">@boss</span> <span class="o">=</span> <span class="n">boss</span>
70
- <span class="k">end</span>
71
-
72
- <span class="k">module</span> <span class="nn">Employee</span>
73
- <span class="c1"># extra behavior here...</span>
74
- <span class="k">end</span>
75
- <span class="k">end</span>
76
- </pre></div>
77
-
78
- <p>This code allows the MyEnvironment class to create instances where it will have an <code>employee</code> and a <code>boss</code> role internally. These are set to <code>attr_reader</code>s and are made private.</p>
79
-
80
- <p>The <code>employee</code> is extended with behaviors defined in the <code>Employee</code> module, and in this case there's no extra stuff for the <code>boss</code> so it doesn't get extended with anything.</p>
81
-
82
- <p>Most of the time you'll follow a pattern like this. Some objects will get extra behavior and some won't. The modules that you use to provide the behavior will match the names you use for the roles to which you assign objects.</p>
83
-
84
- <p>By adding <code>Surrounded::Context</code> you can shortcut all this work.</p>
85
-
86
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
87
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
88
-
89
- <span class="kp">initialize</span><span class="p">(</span><span class="ss">:employee</span><span class="p">,</span> <span class="ss">:boss</span><span class="p">)</span>
90
-
91
- <span class="k">module</span> <span class="nn">Employee</span>
92
- <span class="c1"># extra behavior here...</span>
93
- <span class="k">end</span>
94
- <span class="k">end</span>
95
- </pre></div>
96
-
97
- <p>Surrounded gives you an <code>initialize</code> class method which does all the setup work for you.</p>
98
-
99
- <h2>
100
- <a name="managing-roles" class="anchor" href="#managing-roles"><span class="octicon octicon-link"></span></a>Managing Roles</h2>
101
-
102
- <p><em>I don't want to use modules. Can't I use something like SimpleDelegator?</em></p>
103
-
104
- <p>Well, it just so happens that you can. This code will work just fine:</p>
105
-
106
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
107
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
108
-
109
- <span class="kp">initialize</span><span class="p">(</span><span class="ss">:employee</span><span class="p">,</span> <span class="ss">:boss</span><span class="p">)</span>
110
-
111
- <span class="k">class</span> <span class="nc">Employee</span> <span class="o">&lt;</span> <span class="no">SimpleDelegator</span>
112
- <span class="c1"># extra behavior here...</span>
113
- <span class="k">end</span>
114
- <span class="k">end</span>
115
- </pre></div>
116
-
117
- <p>Instead of extending the <code>employee</code> object, Surrounded will run <code>Employee.new(employee)</code> to create the wrapper for you. You'll need to include the <code>Surrounded</code> module in your wrapper, but we'll get to that.</p>
118
-
119
- <p>But the syntax can be even simpler than that if you want.</p>
120
-
121
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
122
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
123
-
124
- <span class="kp">initialize</span><span class="p">(</span><span class="ss">:employee</span><span class="p">,</span> <span class="ss">:boss</span><span class="p">)</span>
125
-
126
- <span class="n">role</span> <span class="ss">:employee</span> <span class="k">do</span>
127
- <span class="c1"># extra behavior here...</span>
128
- <span class="k">end</span>
129
- <span class="k">end</span>
130
- </pre></div>
131
-
132
- <p>By default, this code will create a module for you named <code>Employee</code>. If you want to use a wrapper, you can do this:</p>
133
-
134
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
135
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
136
-
137
- <span class="kp">initialize</span><span class="p">(</span><span class="ss">:employee</span><span class="p">,</span> <span class="ss">:boss</span><span class="p">)</span>
138
-
139
- <span class="n">wrap</span> <span class="ss">:employee</span> <span class="k">do</span>
140
- <span class="c1"># extra behavior here...</span>
141
- <span class="k">end</span>
142
- <span class="k">end</span>
143
- </pre></div>
144
-
145
- <p>But if you're making changes and you decide to move from a module to a wrapper or from a wrapper to a module, you'll need to change that method call. Instead, you could just tell it which type of role to use:</p>
146
-
147
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
148
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
149
-
150
- <span class="kp">initialize</span><span class="p">(</span><span class="ss">:employee</span><span class="p">,</span> <span class="ss">:boss</span><span class="p">)</span>
151
-
152
- <span class="n">role</span> <span class="ss">:employee</span><span class="p">,</span> <span class="ss">:wrapper</span> <span class="k">do</span>
153
- <span class="c1"># extra behavior here...</span>
154
- <span class="k">end</span>
155
- <span class="k">end</span>
156
- </pre></div>
157
-
158
- <p>The default available types are <code>:module</code>, <code>:wrap</code> or <code>:wrapper</code>, and <code>:interface</code>. We'll get to <code>interface</code> below. The <code>:wrap</code> and <code>:wrapper</code> types are the same and they'll both create classes which inherit from SimpleDelegator <em>and</em> include Surrounded for you.</p>
159
-
160
- <p>These are minor little changes which highlight how simple it is to use Surrounded.</p>
161
-
162
- <p><em>Well... I want to use <a href="https://github.com/saturnflyer/casting">Casting</a> so I get the benefit of modules without extending objects. Can I do that?</em></p>
163
-
164
- <p>Yup. The ability to use Casting is built-in. If the objects you provide to your context respond to <code>cast_as</code> then Surrounded will use that.</p>
165
-
166
- <p><em>Ok. So is that it?</em></p>
167
-
168
- <p>There's a lot more. Let's look at the individual objects and what they need for this to be valuable...</p>
169
-
170
- <h2>
171
- <a name="objects-access-to-their-environments" class="anchor" href="#objects-access-to-their-environments"><span class="octicon octicon-link"></span></a>Objects' access to their environments</h2>
172
-
173
- <p>Add <code>Surrounded</code> to your objects to give them awareness of other objects.</p>
174
-
175
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">User</span>
176
- <span class="kp">include</span> <span class="no">Surrounded</span>
177
- <span class="k">end</span>
178
- </pre></div>
179
-
180
- <p>Now the <code>User</code> instances will be able to implicitly access objects in their environment.</p>
181
-
182
- <p>Via <code>method_missing</code> those <code>User</code> instances can access a <code>context</code> object it stores in an internal collection. </p>
183
-
184
- <p>Inside of the <code>MyEnvironment</code> context we saw above, the <code>employee</code> and <code>boss</code> objects are instances of <code>User</code> for this example.</p>
185
-
186
- <p>Because the <code>User</code> class includes <code>Surrounded</code>, the instances of that class will be able to access other objects in the same context implicitly.</p>
187
-
188
- <p>Let's make our context look like this:</p>
189
-
190
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
191
- <span class="c1"># other stuff from above is still here...</span>
192
-
193
- <span class="k">def</span> <span class="nf">shove_it</span>
194
- <span class="n">employee</span><span class="o">.</span><span class="n">quit</span>
195
- <span class="k">end</span>
196
-
197
- <span class="n">role</span> <span class="ss">:employee</span> <span class="k">do</span>
198
- <span class="k">def</span> <span class="nf">quit</span>
199
- <span class="n">say</span><span class="p">(</span><span class="s2">"I'm sick of this place, </span><span class="si">#{</span><span class="n">boss</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
200
- <span class="n">stomp</span>
201
- <span class="n">throw_papers</span>
202
- <span class="n">say</span><span class="p">(</span><span class="s2">"I quit!"</span><span class="p">)</span>
203
- <span class="k">end</span>
204
- <span class="k">end</span>
205
- <span class="k">end</span>
206
- </pre></div>
207
-
208
- <p>What's happening in there is that when the <code>shove_it</code> method is called on the instance of <code>MyEnvironment</code>, the <code>employee</code> has the ability to refer to <code>boss</code> because it is in the same context, e.g. the same environment.</p>
209
-
210
- <p>The behavior defined in the <code>Employee</code> module assumes that it may access other objects in it's local environment. The <code>boss</code> object, for example, is never explicitly passed in as an argument.</p>
211
-
212
- <p>What <code>Surrounded</code> does for us is to make the relationship between objects and gives them the ability to access each other. Adding new or different roles to the context now only requires that we add them to the context and nothing else. No explicit references must be passed to each individual method. The objects are aware of the other objects around them and can refer to them by their role name.</p>
213
-
214
- <p>I didn't mention how the context is set, however.</p>
215
-
216
- <h2>
217
- <a name="tying-objects-together" class="anchor" href="#tying-objects-together"><span class="octicon octicon-link"></span></a>Tying objects together</h2>
218
-
219
- <p>Your context will have methods of it's own which will trigger actions on the objects inside, but we need those trigger methods to set the accessible context for each of the contained objects.</p>
220
-
221
- <p>Here's an example of what we want:</p>
222
-
223
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
224
- <span class="c1"># other stuff from above is still here...</span>
225
-
226
- <span class="k">def</span> <span class="nf">shove_it</span>
227
- <span class="n">employee</span><span class="o">.</span><span class="n">store_context</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
228
- <span class="n">employee</span><span class="o">.</span><span class="n">quit</span>
229
- <span class="n">employee</span><span class="o">.</span><span class="n">remove_context</span>
230
- <span class="k">end</span>
231
-
232
- <span class="n">role</span> <span class="ss">:employee</span> <span class="k">do</span>
233
- <span class="k">def</span> <span class="nf">quit</span>
234
- <span class="n">say</span><span class="p">(</span><span class="s2">"I'm sick of this place, </span><span class="si">#{</span><span class="n">boss</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
235
- <span class="n">stomp</span>
236
- <span class="n">throw_papers</span>
237
- <span class="n">say</span><span class="p">(</span><span class="s2">"I quit!"</span><span class="p">)</span>
238
- <span class="k">end</span>
239
- <span class="k">end</span>
240
- <span class="k">end</span>
241
- </pre></div>
242
-
243
- <p>Now that the <code>employee</code> has a reference to the context, it won't blow up when it hits <code>boss</code> inside that <code>quit</code> method.</p>
244
-
245
- <p>We saw how we were able to clear up a lot of that repetitive work with the <code>initialize</code> method, so this is how we do it here:</p>
246
-
247
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
248
- <span class="c1"># other stuff from above is still here...</span>
249
-
250
- <span class="n">trigger</span> <span class="ss">:shove_it</span> <span class="k">do</span>
251
- <span class="n">employee</span><span class="o">.</span><span class="n">quit</span>
252
- <span class="k">end</span>
253
-
254
- <span class="n">role</span> <span class="ss">:employee</span> <span class="k">do</span>
255
- <span class="k">def</span> <span class="nf">quit</span>
256
- <span class="n">say</span><span class="p">(</span><span class="s2">"I'm sick of this place, </span><span class="si">#{</span><span class="n">boss</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
257
- <span class="n">stomp</span>
258
- <span class="n">throw_papers</span>
259
- <span class="n">say</span><span class="p">(</span><span class="s2">"I quit!"</span><span class="p">)</span>
260
- <span class="k">end</span>
261
- <span class="k">end</span>
262
- <span class="k">end</span>
263
- </pre></div>
264
-
265
- <p>By using this <code>trigger</code> keyword, our block is the code we care about, but internally the method is created to first set all the objects' current contexts.</p>
266
-
267
- <p>The context will also store the triggers so that you can, for example, provide details outside of the environment about what triggers exist.</p>
268
-
269
- <div class="highlight highlight-ruby"><pre><span class="n">context</span> <span class="o">=</span> <span class="no">MyEnvironment</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">current_user</span><span class="p">,</span> <span class="n">the_boss</span><span class="p">)</span>
270
- <span class="n">context</span><span class="o">.</span><span class="n">triggers</span> <span class="c1">#=&gt; [:shove_it]</span>
271
- </pre></div>
272
-
273
- <p>You might find that useful for dynamically defining user interfaces.</p>
274
-
275
- <p>Sometimes I'd rather not use this DSL, however. I want to just write regular methods. </p>
276
-
277
- <p>We can do that too. You'll need to opt in to this by specifying <code>set_methods_as_triggers</code> for the context class.</p>
278
-
279
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MyEnvironment</span>
280
- <span class="c1"># other stuff from above is still here...</span>
281
-
282
- <span class="n">set_methods_as_triggers</span>
283
-
284
- <span class="k">def</span> <span class="nf">shove_it</span>
285
- <span class="n">employee</span><span class="o">.</span><span class="n">quit</span>
286
- <span class="k">end</span>
287
-
288
- <span class="n">role</span> <span class="ss">:employee</span> <span class="k">do</span>
289
- <span class="k">def</span> <span class="nf">quit</span>
290
- <span class="n">say</span><span class="p">(</span><span class="s2">"I'm sick of this place, </span><span class="si">#{</span><span class="n">boss</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">!"</span><span class="p">)</span>
291
- <span class="n">stomp</span>
292
- <span class="n">throw_papers</span>
293
- <span class="n">say</span><span class="p">(</span><span class="s2">"I quit!"</span><span class="p">)</span>
294
- <span class="k">end</span>
295
- <span class="k">end</span>
296
- <span class="k">end</span>
297
- </pre></div>
298
-
299
- <p>This will allow you to write methods like you normally would. They are aliased internally with a prefix and the method name that you use is rewritten to add and remove the context for the objects in this context. The public API of your class remains the same, but the extra feature of wrapping your method is handled for you.</p>
300
-
301
- <p>This will treat all instance methods defined on your context the same way, so be aware of that.</p>
302
-
303
- <h2>
304
- <a name="where-roles-exist" class="anchor" href="#where-roles-exist"><span class="octicon octicon-link"></span></a>Where roles exist</h2>
305
-
306
- <p>By using <code>Surrounded::Context</code> you are declaring a relationship between the objects inside playing your defined roles.</p>
307
-
308
- <p>Because all the behavior is defined internally and only relevant internally, those relationships don't exist outside of the environment.</p>
309
-
310
- <p>Surrounded makes all of your role modules and classes private constants. It's not a good idea to try to reuse behavior defined for one context in another area.</p>
311
-
312
- <h2>
313
- <a name="the-role-dsl" class="anchor" href="#the-role-dsl"><span class="octicon octicon-link"></span></a>The role DSL</h2>
314
-
315
- <p>Using the <code>role</code> method to define modules and classes takes care of the setup for you. This way you can swap between implementations:</p>
316
-
317
- <div class="highlight highlight-ruby"><pre>
318
- <span class="c1"># this uses modules</span>
319
- <span class="n">role</span> <span class="ss">:source</span> <span class="k">do</span>
320
- <span class="k">def</span> <span class="nf">transfer</span>
321
- <span class="nb">self</span><span class="o">.</span><span class="n">balance</span> <span class="o">-=</span> <span class="n">amount</span>
322
- <span class="n">destination</span><span class="o">.</span><span class="n">balance</span> <span class="o">+=</span> <span class="n">amount</span>
323
- <span class="nb">self</span>
324
- <span class="k">end</span>
325
- <span class="k">end</span>
326
-
327
- <span class="c1"># this uses SimpleDelegator and Surrounded</span>
328
- <span class="n">role</span> <span class="ss">:source</span><span class="p">,</span> <span class="ss">:wrap</span> <span class="k">do</span>
329
- <span class="k">def</span> <span class="nf">transfer</span>
330
- <span class="nb">self</span><span class="o">.</span><span class="n">balance</span> <span class="o">-=</span> <span class="n">amount</span>
331
- <span class="n">destination</span><span class="o">.</span><span class="n">balance</span> <span class="o">+=</span> <span class="n">amount</span>
332
- <span class="n">__getobj__</span>
333
- <span class="k">end</span>
334
- <span class="k">end</span>
335
-
336
- <span class="c1"># this uses a special interface object which pulls</span>
337
- <span class="c1"># methods from a module and applies them to your object.</span>
338
- <span class="n">role</span> <span class="ss">:source</span><span class="p">,</span> <span class="ss">:interface</span> <span class="k">do</span>
339
- <span class="k">def</span> <span class="nf">transfer</span>
340
- <span class="nb">self</span><span class="o">.</span><span class="n">balance</span> <span class="o">-=</span> <span class="n">amount</span>
341
- <span class="n">destination</span><span class="o">.</span><span class="n">balance</span> <span class="o">+=</span> <span class="n">amount</span>
342
- <span class="nb">self</span>
343
- <span class="k">end</span>
344
- <span class="k">end</span>
345
- </pre></div>
346
-
347
- <p>The <code>:interface</code> option is a special object which has all of its methods removed (excepting <code>__send__</code> and <code>object_id</code>) so that other methods will be pulled from the ones that you define, or from the object it attempts to proxy.</p>
348
-
349
- <p>Notice that the <code>:interface</code> allows you to return <code>self</code> whereas the <code>:wrap</code> acts more like a wrapper and forces you to deal with that shortcoming by using it's wrapped-object-accessor method: <code>__getobj__</code>.</p>
350
-
351
- <p>If you'd like to choose one and use it all the time, you can set the default:</p>
352
-
353
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">MoneyTransfer</span>
354
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
355
-
356
- <span class="nb">self</span><span class="o">.</span><span class="n">default_role_type</span> <span class="o">=</span> <span class="ss">:interface</span> <span class="c1"># also :wrap, :wrapper, or :module</span>
357
-
358
- <span class="n">role</span> <span class="ss">:source</span> <span class="k">do</span>
359
- <span class="k">def</span> <span class="nf">transfer</span>
360
- <span class="nb">self</span><span class="o">.</span><span class="n">balance</span> <span class="o">-=</span> <span class="n">amount</span>
361
- <span class="n">destination</span><span class="o">.</span><span class="n">balance</span> <span class="o">+=</span> <span class="n">amount</span>
362
- <span class="nb">self</span>
363
- <span class="k">end</span>
364
- <span class="k">end</span>
365
- <span class="k">end</span>
366
- </pre></div>
367
-
368
- <p>Or, if you like, you can choose the default for your entire project:</p>
369
-
370
- <div class="highlight highlight-ruby"><pre><span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span><span class="o">.</span><span class="n">default_role_type</span> <span class="o">=</span> <span class="ss">:interface</span>
371
-
372
- <span class="k">class</span> <span class="nc">MoneyTransfer</span>
373
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
374
-
375
- <span class="n">role</span> <span class="ss">:source</span> <span class="k">do</span>
376
- <span class="k">def</span> <span class="nf">transfer</span>
377
- <span class="nb">self</span><span class="o">.</span><span class="n">balance</span> <span class="o">-=</span> <span class="n">amount</span>
378
- <span class="n">destination</span><span class="o">.</span><span class="n">balance</span> <span class="o">+=</span> <span class="n">amount</span>
379
- <span class="nb">self</span>
380
- <span class="k">end</span>
381
- <span class="k">end</span>
382
- <span class="k">end</span>
383
- </pre></div>
384
-
385
- <h2>
386
- <a name="policies-for-the-application-of-role-methods" class="anchor" href="#policies-for-the-application-of-role-methods"><span class="octicon octicon-link"></span></a>Policies for the application of role methods</h2>
387
-
388
- <p>There are 2 approaches to applying new behavior to your objects.</p>
389
-
390
- <p>By default your context will add methods to an object before a trigger is run
391
- and behaviors will be removed after the trigger is run.</p>
392
-
393
- <p>Alternatively you may set the behaviors to be added during the initialize method
394
- of your context.</p>
395
-
396
- <p>Here's how it works:</p>
397
-
398
- <div class="highlight highlight-ruby"><pre><span class="k">class</span> <span class="nc">ActiviatingAccount</span>
399
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
400
-
401
- <span class="n">apply_roles_on</span><span class="p">(</span><span class="ss">:trigger</span><span class="p">)</span> <span class="c1"># this is the default</span>
402
- <span class="c1"># apply_roles_on(:initialize) # set this to apply behavior from the start</span>
403
-
404
- <span class="kp">initialize</span><span class="p">(</span><span class="ss">:activator</span><span class="p">,</span> <span class="ss">:account</span><span class="p">)</span>
405
-
406
- <span class="n">role</span> <span class="ss">:activator</span> <span class="k">do</span>
407
- <span class="k">def</span> <span class="nf">some_behavior</span><span class="p">;</span> <span class="k">end</span>
408
- <span class="k">end</span>
409
-
410
- <span class="k">def</span> <span class="nf">non_trigger_method</span>
411
- <span class="n">activator</span><span class="o">.</span><span class="n">some_behavior</span> <span class="c1"># not available unless you apply roles on initialize</span>
412
- <span class="k">end</span>
413
-
414
- <span class="n">trigger</span> <span class="ss">:some_trigger_method</span> <span class="k">do</span>
415
- <span class="n">activator</span><span class="o">.</span><span class="n">some_behavior</span> <span class="c1"># always available</span>
416
- <span class="k">end</span>
417
- <span class="k">end</span>
418
- </pre></div>
419
-
420
- <p><em>Why are those options there?</em></p>
421
-
422
- <p>When you initialize a context and apply behavior at the same time, you'll need
423
- to remove that behavior. For example, if you are using Casting AND you apply roles on initialize:</p>
424
-
425
- <div class="highlight highlight-ruby"><pre><span class="n">context</span> <span class="o">=</span> <span class="no">ActiviatingAccount</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">current_user</span><span class="p">,</span> <span class="no">Account</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="mi">123</span><span class="p">))</span>
426
- <span class="n">context</span><span class="o">.</span><span class="n">do_something</span>
427
- <span class="n">current_user</span><span class="o">.</span><span class="n">some_behavior</span> <span class="c1"># this method is still available</span>
428
- <span class="n">current_user</span><span class="o">.</span><span class="n">uncast</span> <span class="c1"># you'll have to manually cleanup</span>
429
- </pre></div>
430
-
431
- <p>But if you go with the default and apply behaviors on trigger, your roles will be cleaned up automatically:</p>
432
-
433
- <div class="highlight highlight-ruby"><pre><span class="n">context</span> <span class="o">=</span> <span class="no">ActiviatingAccount</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">current_user</span><span class="p">,</span> <span class="no">Account</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="mi">123</span><span class="p">))</span>
434
- <span class="n">context</span><span class="o">.</span><span class="n">do_something</span>
435
- <span class="n">current_user</span><span class="o">.</span><span class="n">some_behavior</span> <span class="c1"># NoMethodError</span>
436
- </pre></div>
437
-
438
- <h2>
439
- <a name="overview-in-code" class="anchor" href="#overview-in-code"><span class="octicon octicon-link"></span></a>Overview in code</h2>
440
-
441
- <p>Here's a view of the possibilities in code.</p>
442
-
443
- <div class="highlight highlight-ruby"><pre><span class="c1"># set default role type for *all* contexts in your program</span>
444
- <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span><span class="o">.</span><span class="n">default_role_type</span> <span class="o">=</span> <span class="ss">:module</span> <span class="c1"># also :wrap, :wrapper, or :interface</span>
445
-
446
- <span class="k">class</span> <span class="nc">ActiviatingAccount</span>
447
- <span class="kp">extend</span> <span class="ss">Surrounded</span><span class="p">:</span><span class="ss">:Context</span>
448
-
449
- <span class="n">apply_roles_on</span><span class="p">(</span><span class="ss">:trigger</span><span class="p">)</span> <span class="c1"># this is the default</span>
450
- <span class="c1"># apply_roles_on(:initialize) # set this to apply behavior from the start</span>
451
-
452
- <span class="n">set_methods_as_triggers</span> <span class="c1"># allows you to skip the 'trigger' dsl</span>
453
-
454
- <span class="c1"># set the default role type only for this class</span>
455
- <span class="nb">self</span><span class="o">.</span><span class="n">default_role_type</span> <span class="o">=</span> <span class="ss">:module</span> <span class="c1"># also :wrap, :wrapper, or :interface</span>
456
-
457
- <span class="kp">initialize</span><span class="p">(</span><span class="ss">:activator</span><span class="p">,</span> <span class="ss">:account</span><span class="p">)</span>
458
-
459
- <span class="n">role</span> <span class="ss">:activator</span> <span class="k">do</span> <span class="c1"># module by default</span>
460
- <span class="k">def</span> <span class="nf">some_behavior</span><span class="p">;</span> <span class="k">end</span>
461
- <span class="k">end</span>
462
-
463
- <span class="c1"># role :activator, :module do</span>
464
- <span class="c1"># def some_behavior; end</span>
465
- <span class="c1"># end</span>
466
- <span class="c1">#</span>
467
- <span class="c1"># role :activator, :wrap do</span>
468
- <span class="c1"># def some_behavior; end</span>
469
- <span class="c1"># end</span>
470
- <span class="c1">#</span>
471
- <span class="c1"># role :activator, :interface do</span>
472
- <span class="c1"># def some_behavior; end</span>
473
- <span class="c1"># end</span>
474
- <span class="c1">#</span>
475
- <span class="c1"># use your own classes if you don't want SimpleDelegator</span>
476
- <span class="c1"># class SomeSpecialRole</span>
477
- <span class="c1"># include Surrounded # you must remember this</span>
478
- <span class="c1"># # Surrounded assumes SomeSpecialRole.new(some_special_role)</span>
479
- <span class="c1"># def initialize(...);</span>
480
- <span class="c1"># # ... your code here</span>
481
- <span class="c1"># end</span>
482
- <span class="c1"># end</span>
483
-
484
- <span class="c1"># works as a trigger (assigning the current context) only if set_methods_as_triggers is set</span>
485
- <span class="k">def</span> <span class="nf">regular_method</span>
486
- <span class="n">activator</span><span class="o">.</span><span class="n">some_behavior</span> <span class="c1"># behavior not available unless you apply roles on initialize</span>
487
- <span class="k">end</span>
488
-
489
- <span class="n">trigger</span> <span class="ss">:some_trigger_method</span> <span class="k">do</span>
490
- <span class="n">activator</span><span class="o">.</span><span class="n">some_behavior</span> <span class="c1"># behavior always available</span>
491
- <span class="k">end</span>
492
- <span class="k">end</span>
493
- </pre></div>
494
-
495
- <h2>
496
- <a name="dependencies" class="anchor" href="#dependencies"><span class="octicon octicon-link"></span></a>Dependencies</h2>
497
-
498
- <p>The dependencies are minimal. The plan is to keep it that way but allow you to configure things as you need. The <a href="http://github.com/saturnflyer/triad">Triad</a> project was written specifically to manage the mapping of roles and objects to the modules which contain the behaviors.</p>
499
-
500
- <p>If you're using <a href="http://github.com/saturnflyer/casting">Casting</a>, for example, Surrounded will attempt to use that before extending an object, but it will still work without it.</p>
501
-
502
- <h2>
503
- <a name="installation" class="anchor" href="#installation"><span class="octicon octicon-link"></span></a>Installation</h2>
504
-
505
- <p>Add this line to your application's Gemfile:</p>
506
-
507
- <div class="highlight highlight-ruby"><pre><span class="n">gem</span> <span class="s1">'surrounded'</span>
508
- </pre></div>
509
-
510
- <p>And then execute:</p>
511
-
512
- <pre><code>$ bundle
513
- </code></pre>
514
-
515
- <p>Or install it yourself as:</p>
516
-
517
- <pre><code>$ gem install surrounded
518
- </code></pre>
519
-
520
- <h2>
521
- <a name="installation-for-rails" class="anchor" href="#installation-for-rails"><span class="octicon octicon-link"></span></a>Installation for Rails</h2>
522
-
523
- <p>See <a href="https://github.com/saturnflyer/surrounded-rails">surrounded-rails</a></p>
524
-
525
- <h2>
526
- <a name="contributing" class="anchor" href="#contributing"><span class="octicon octicon-link"></span></a>Contributing</h2>
527
-
528
- <ol>
529
- <li>Fork it</li>
530
- <li>Create your feature branch (<code>git checkout -b my-new-feature</code>)</li>
531
- <li>Commit your changes (<code>git commit -am 'Add some feature'</code>)</li>
532
- <li>Push to the branch (<code>git push origin my-new-feature</code>)</li>
533
- <li>Create new Pull Request</li>
534
- </ol>
535
- </section>
536
- <footer>
537
- <p>This project is maintained by <a href="https://github.com/saturnflyer">saturnflyer</a></p>
538
- <p><small>Hosted on GitHub Pages &mdash; Theme by <a href="https://github.com/orderedlist">orderedlist</a></small></p>
539
- </footer>
540
- </div>
541
- <script src="javascripts/scale.fix.js"></script>
542
-
543
- </body>
544
- </html>