jackbox 0.9.6.6 → 0.9.6.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3ad85cf59fad7a3d581d62ed26afd80a2c753552
4
- data.tar.gz: 4aeaaa6afe5317d230af20778946e574ad5d730b
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzlkNjhiN2MyYzI0YjM4NTIxOTczMmViYWY2ZGIyNzI1OWYyOTYwYg==
5
+ data.tar.gz: !binary |-
6
+ NDAzMmMzOWY3MTcxMmY4ZGRmMTk1ZDRhNzMzNWI0YTIyMWI4Y2E1Yw==
5
7
  SHA512:
6
- metadata.gz: 869801891af0e806593ab8561ae88cfb0c0935a5a2649204bfdab4c7a2e83f50e77159e56558dc4c58ca03c74b2078b99dce2e171d0e0556b6ad35e1fcfb21ac
7
- data.tar.gz: ff8ecf25e9e2da21ef8f70588abb06a0d9d6f2b372720923acce2f3108092a074b5f22f69e2812c14e509d1b25e1165dbdc94e6df77fe306dff7041a85132769
8
+ metadata.gz: !binary |-
9
+ MGM2ZWU1Y2E2MzRiYzk0Njc2ZDI4NzU0OTkzNDA1YjJiZDI1ZmFlODBmMTA0
10
+ YzVjODY2OTUyYWU5MGY1MjcyMzQzNGViNWQ2NjNmODAwODE2OTM1YTkwZGY4
11
+ NzAxZjA2MTUxODBhZWJkMTdiMmMwYmQ3MTc5ZjI5YTQxY2NmNzQ=
12
+ data.tar.gz: !binary |-
13
+ Y2UxMWVjZDE1ODVmY2E4NDViYjEwZjkzNzlkZDMyOTFmNjgwZTI4Y2ZkYTBi
14
+ NmQ4ODc0YzgyMjA1MjZjOGYzNTdjYjdhNjY5MjU3Y2NhNmUxMzkzOWExODMy
15
+ ODdhNDI2YzEwOGJiNjk4ZGQxZTc5NzYxOTZjZjA2ZTk5OGJkNWQ=
Binary file
@@ -1,5 +1,11 @@
1
1
 
2
- Copyright © 2014, 2015 LHA (Lou Henry Alvarez). All rights reserved.
2
+ Copyright © 2014, 2015, 2016, LHA (Lou Henry Alvarez). All rights reserved.
3
+
4
+ This means you cannot reproduce the whole or any part of the source code or
5
+ documentation at any time, or under any circumstances, without the express
6
+ permission of the author. Use of the binary encoded form of the software is
7
+ free of charge for a limited time, while the binary license and keys expire,
8
+ and is subject to the above Copyright notice and afore terms for reproduction.
3
9
 
4
10
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
5
11
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
data/README.md CHANGED
@@ -18,24 +18,24 @@ Copyright © 2014, 2015 LHA. All rights reserved.
18
18
  ---
19
19
  <h2 style="font-family:Impact">Modular Closures©, Ruby Traits©, Code Injectors, Class Constructors, and other Ruby programmer morphins</h2>
20
20
 
21
- The defining thought behind Jackbox is a simple one: If Ruby is like Play-Doh, with Jackbox we want to turn it into <a href="https://en.wikipedia.org/wiki/Plasticine">Plasticine</a>. The library functionality at this time takes this idea and materializes it in the concepts of traits/injectors, class constructors, the application of versioning to runtimes, and a just-in-time inheritance model that together with the helper functions that bring them together, provide some new and interesting capabilities.
21
+ The defining thought behind Jackbox is a simple one: If Ruby is like Play-Doh, with Jackbox we aim to turn it into <a href="https://en.wikipedia.org/wiki/Plasticine">Plasticine</a>. The library functionality at this time takes this idea and materializes it in the concepts of trait injectors, class constructors, the application of versioning to objects, and a model of mix-in inheritance which includes a just-in-time inheritance form that together with the helper functions that bring them together, provide some new and interesting capabilities.
22
22
 
23
- To make it easier to grasp, **Ruby Traits** and code injectors can perhaps be thought of as a form of **Modular Closures** which is closures which can also serve as modules. These modular closures most of all propose some additional properties to the idea of a mix-in. For instance, they make it possible to solve several general problems in some areas of OOP, overcoming traditional Ruby shortcomings with the GOF Decorator and Strategy Patterns, and enabling **some new code patterns** of our own. They instrument control over code presence or the presence of trait/injector code in targets with mechanisms involving trait/injector canceling or ejection and also trait directives to for example remain as silent traits and to force the reactivation of traits. They give your code the ability to capture the surrounding context and mix it into an indiscriminate target. They extend Ruby's mix-in and method resolution over and beyond what is possible with regular modules.
23
+ To make it easier to grasp, **Ruby Traits©** and code injectors can perhaps be thought of as a form of **Modular Closures©** which is to say, closures which can also serve as modules. These modular closures most of all propose some additional properties to the idea of a mix-in. For instance, they make it possible to solve several general problems in some areas of OOP, overcoming traditional Ruby shortcomings with the GOF Decorator and Strategy Patterns, and enabling **some new code patterns** of our own. They instrument control over code presence or the presence of trait injector code in targets with mechanisms involving trait injector canceling or ejection and directives to for example remain as silent traits, and to later force the reactivation of traits. They give your code the ability to capture the surrounding context and mix it into an indiscriminate target. They extend Ruby's mix-in and method resolution over and beyond what is possible with regular modules.
24
24
 
25
- **Class constructors** on the other hand present an alternative way to refine a class. They provide similar benefits to refinements with a different underpinning. Together with Jackbox code traits and helper functions, class constructors can be refined multiple times. Capabilities can be added and removed in blocks. Moreover, these constructors acquire introspecting abilities. A class constructor can be tested for existence, can tell you what traits it uses, and finally can be overridden with a more relevant one. Constructors also work with Ruby 1.9 and related technologies.
25
+ Following on this we introduce the concept of Injector Versioning. This is a feature which allows you to redefine parts of your program in local isolation and without it affecting others. See Trait/Injector Versioning below. Runtimes can morph their capabilities as they learn about themselves, and they can do so in blocks as granular or as coarse as needed. These blocks can be updated, ejected, silenced, or re-injected with more function. This versioning also provides a form of inheritance. We have called this versioned inheritance and it allows newer versions to inherit from previous ones, be tagged and labeled, and this way be capable of reuse. All this is further enhanced by the ability of Jackbox to resolve methods through the use of the VMC (Virtual Method Cache). See below.
26
26
 
27
- Following on this we introduce the concept of Trait/Injector Versioning. This is a feature which allows you to redefine parts of your program in local isolation and without it affecting others. See Trait/Injector Versioning below. Runtimes can morph their capabilities as they learn about themselves, and they can do so in blocks as granular or as coarse as needed. These blocks can be updated, ejected, silenced, or re-injected with more function. This versioning also provides a form of inheritance. We have called this versioned inheritance and it allows newer versions to inherit from previous ones, be tagged and labeled, and this way be capable of reuse. All this is further enhanced by the ability of Jackbox to resolve methods through the use of the VMC (Virtual Method Cache). See below.
27
+ **Class constructors** on the other hand present an alternative way to refine a class. They provide similar benefits to refinements with a different underpinning. Together with Jackbox trait injectors and helper functions, class constructors can be refined multiple times. Capabilities can be added and removed in blocks. Moreover, these constructors acquire introspecting abilities. A class constructor can be tested for existence, can tell you what traits it uses, and finally can be overridden with a more relevant one. Constructors also work with all Ruby versions including with Ruby 1.9 and related technologies.
28
28
 
29
29
  Finally, we also present the concept of **Just-In-Time Inheritance©**. This is a feature which allows the production of an ancestor hierarchy similar to what you find in Ruby classes just as it is needed by your code. With it you can override previous members of a tag and expect to have access to its super members as part of the call, just like you would with classes. But, this inheritance is all going on in the mix-in --the Modular Closure. Families of traits can be built with the use of this and the previous versioned inheritance, and unlike class inheritance be readily applicable to any target.
30
30
 
31
- We have chosen to keep the code obfuscated **for now** because we are a small company with fewer resources and we need to protect our germinating intellectual property. But, as our business model evolves we will be considering open sourcing it. We take great pride in providing significant value at minimal cost. Our guiding principle through out it all has been keeping new constructs to a minimum. We took an outer minimalistic approach requiring a lot more behind the scenes. Simplicity takes work. We hope that all this work is to your liking.
31
+ We have chosen to keep the code obfuscated **for now** because we are a small company and we need to protect our germinating intellectual property. We take great pride in providing significant value at minimal cost. Our guiding principle through out it all has been keeping new constructs to a minimum. There are enough libraries out there which enhance the base Ruby language in many interesting ways but which also bring with them added complexity and a big learning curve. We on the other hand took an outer minimalistic approach requiring a lot more behind the scenes. Simplicity takes work. We hope that all this work is to your liking.
32
32
 
33
- Advantages Of Trait Based Programming
33
+ Advantages Of Trait Injector Based Programming
34
34
  ------------------------------------
35
+ * Trait Injectors avoid the perils of monkey patching. You can just create a new version of the trait and leave the old one alone.
35
36
  * Traits are inherited from their ancestors and can be mixed in with any target.
36
- * With Traits you avoid the perils of monkey patching. You can just create a new version of the trait and leave the old one alone.
37
- * Traits can be silenced and reactivated.
38
37
  * With Traits runtime versioning is possible and traits can be upgraded with new versions of the trait.
38
+ * Traits can be injected, extended, cancelled, silenced, reinstated, tagged, versioned, and updated.
39
39
  * Traits enable new and different coding patterns.
40
40
 
41
41
  ---
@@ -83,7 +83,7 @@ It also works like so:
83
83
 
84
84
 
85
85
  #### #with obj, &blk
86
- There is also a new version of the #with construct. The important thing to remember about #with is it has a primary context which is the object passed to it, and a secondary context which is the object you are making the call from. This allows you to work **with** both contexts at the same time. The other important thing about #with is that it allows you to directly place definitions on and returns the same object you passed into it, or the result of the last evaluation in the #with block on the alternative.
86
+ There is also a new version of the #with construct. The important thing to remember about #with is it has a primary context which is the object passed to it, and a secondary context which is the object you are making the call from. This allows you to work **with** both contexts at the same time and works as a shortcut between contexts. Used in this fashion it can abstract some of the tediousness of an explicit self in some calls. The other thing about #with is that it allows you to directly place definitions on the object you pass in using its most natural form based on whether it's an instance of Object or Module. Then it returns the same object you passed into it after the block has done processing it. You can also pass multiple objects: #with a, b, c for example and the same block applies to all returning a, b, c afterwards. There are some other nuances to #with later on.
87
87
 
88
88
  Here is some sample usage code:
89
89
 
@@ -144,8 +144,19 @@ Use it with **#decorate** on singleton classes like this:
144
144
 
145
145
 
146
146
  #### #lets sym=nil, &blk
147
- We could say, this is simple syntax sugar. It adds readability to some constructs, and it allows the creation of local or global procs using a more friendly syntax. But #lets, also opens the door to a new coding pattern using class constructors. See below. The important thing about #lets is that it always defines some lambda/proc/method. It's use differs from that of #define_method only in spirit, aside its use with respect to class constructors, #lets is mostly for one liners. Here are some examples:
147
+ We could say, this is simple syntax sugar. It adds readability to some constructs, and it allows the creation of local or global procs using a more friendly syntax. But #lets, also opens the door to a new coding pattern using class constructors. See below. The important thing about #lets is that it always defines some lambda/proc/method. It's use apparently differs from that of #define_method only in spirit, but its use with respect to class constructors shows another side to #lets. Aside from this however, #lets is mostly for one liners. Here are some examples:
148
148
 
149
+ As a shortcut for define_method. Use it for short functional definitions:
150
+
151
+ lets( :meth ){ |arg| arg * 2 } # read as: lets define symbol :meth to be ....
152
+ meth(3)
153
+ # => 6
154
+
155
+ Can be used to define a special values or pseudo-immutable strings:
156
+
157
+ lets(:foo){ 3+Math::Pi } # read as: lets set :foo to value
158
+ lets(:faa){ 'some important string' }
159
+
149
160
  To define local functions/lambdas. Define symbols in local scope:
150
161
 
151
162
  def main
@@ -157,42 +168,33 @@ To define local functions/lambdas. Define symbols in local scope:
157
168
  #...
158
169
  end
159
170
 
160
- As a shortcut for define_method. Use it for short functional definitions:
161
-
162
- lets( :meth ){ |arg| arg * 2 } # read as: lets define symbol :meth to be ....
163
- meth(3)
164
- # => 6
165
-
166
- Can be used to define a special values or pseudo-immutable strings:
171
+ Also see Class Constructors below.
167
172
 
168
- lets(:foo){ 3+Math::Pi } # read as: lets set :foo to value
169
- lets(:faa){ 'some important string' }
170
-
171
173
 
172
- Traits/Injectors
174
+ Trait Injectors
173
175
  ----------
174
- Traits are the main tool in Jackbox at the time of this writing. These again are a form of mix-in that have properties of both a closure and a module. They can also be thought of as an **extended closure** if you will or as a special kind of mix-in if you want. In the sections below we will discuss some of the methods available to you with Jackbox in connection with Traits, as well as elaborate on some of the other properties of traits. But, it is essential to understand there are some syntactical differences to Traits/Injectors with respect to regular modules. We will show them first, with some examples:
176
+ Trait Injectors are the main tool in Jackbox at the time of this writing. These again are a form of mix-in that have properties of both a closure and a module. They can also be thought of as an **extended closure** if you will or as a special kind of mix-in if you like. In the sections below we will discuss some of the methods available to you with Jackbox in connection with Trait Injectors, as well as elaborate on some of the other properties of traits. But, it is essential to understand there are some syntactical differences to Trait Injectors with respect to regular modules. We will show them first, with some examples:
175
177
 
176
- **TRAITS/INJECTORS ARE DECLARED IN THE FOLLOWING WAYS:**
178
+ **TRAIT INJECTORS ARE DECLARED IN THE FOLLOWING WAYS:**
177
179
 
178
180
 
179
- trait :name
181
+ injector :name
180
182
 
181
183
  # or...
182
184
 
183
- Name = trait :name
185
+ Name = injector :name
184
186
 
185
187
  # or even ...
186
188
 
187
- injector :Name # capitalized method, using alias #trait
189
+ trait :Name # capitalized method, using alias #trait
188
190
 
189
191
 
190
- Their use and semantics are somewhat defined by the following snippet. But, to fully understand their implications to your code, you have to understand the sections on trait versioning, their behavior under inheritance, and also trait directives.
192
+ Their use and semantics are somewhat defined by the following snippet. But, to fully understand their implications to your code, you have to understand the sections on injector versioning, their behavior under inheritance, and also trait directives.
191
193
 
192
194
  # somewhere in your code
193
- include Traits
195
+ include Injectors
194
196
 
195
- trait :my_trait # define the trait
197
+ injector :my_trait # define the injector
196
198
 
197
199
  my_trait do
198
200
  def bar
@@ -201,7 +203,7 @@ Their use and semantics are somewhat defined by the following snippet. But, to
201
203
  end
202
204
 
203
205
  # later on...
204
- widget.extend my_trait # apply the trait
206
+ widget.extend my_trait # apply the injector
205
207
 
206
208
  widget.bar
207
209
  # => bar
@@ -210,16 +212,16 @@ Their use and semantics are somewhat defined by the following snippet. But, to
210
212
 
211
213
  Mine = my_trait
212
214
  class Target
213
- inject Mine # apply the trait
215
+ inject Mine # apply the injector
214
216
  end
215
217
 
216
218
  Target.new.bar
217
219
  # => bar
218
220
 
219
221
 
220
- **TRAITS/INJECTORS HAVE PROLONGATIONS:**
222
+ **TRAIT INJECTORS HAVE PROLONGATIONS:**
221
223
 
222
- trait :my_trait
224
+ injector :my_trait
223
225
 
224
226
  my_trait do # first prolongation
225
227
 
@@ -237,22 +239,22 @@ Their use and semantics are somewhat defined by the following snippet. But, to
237
239
 
238
240
  end
239
241
 
240
- These prolongations become versions once applied or tagged. See Tagging/Naming below. In lieu of this they remain in the Virtual Method Cache (see below) in an un-versioned state available to any client.
242
+ Prolongations become versions once applied or tagged. See Tagging/Naming below. But, in lieu of this, they remain in the Virtual Method Cache (see below) in an un-versioned state and are available to any client.
241
243
 
242
244
  #### #trait/#injector :sym
243
- This is a global function. It defines an object of type Trait/Injector with the name of :symbol. Use it when you want to generate an Trait/Injector object for later use. The symbol can then be used as a handle to the trait whenever you need to prolong the trait by adding methods to it, or to apply it to a target generating a version. Additionally, this symbol plays a role in defining the trait's scope. Traits/Injectors with capitalized names like :Function, :Style, etc have a global scope. That is, they are available throughout the program, regardless of where they are defined. Here is the code:
245
+ This is a global function. It defines an object of type Injector with the name of :symbol. Use it when you want to generate a Trait Injector object for later use. The symbol can then be used as a handle to the trait whenever you need to prolong the trait by adding methods to it, or to apply it to a target generating a version. Additionally, this symbol plays a role in defining the trait's scope. Injectors with capitalized names like :Function, :Style, etc have a global scope. That is, they are available throughout the program, regardless of where they are defined. Here is the code:
244
246
 
245
247
  class A
246
- trait :Function # defined
248
+ injector :Function # defined
247
249
  end
248
250
 
249
251
  class B
250
252
  include Function() # applied
251
253
  end
252
254
 
253
- # This is perfectly valid with traits
255
+ # This is perfectly valid with trait injectors
254
256
 
255
- On the other hand Traits/Injectors with a lower case name are only available __from__ the scope in which they were defined, like the following example shows:
257
+ On the other hand Traits Injectors with a lower case name are only available __from__ the scope in which they were defined, like the following example shows:
256
258
 
257
259
  class A
258
260
  trait :form
@@ -267,13 +269,13 @@ On the other hand Traits/Injectors with a lower case name are only available __f
267
269
  end
268
270
 
269
271
 
270
- For all this to happen Jackbox also introduces some additional Ruby constructs, namely the keywords #inject and #enrich. These can be thought as simply new corollaries to #include and #extend. In fact they can be used interchangeably. If you're working with traits you may want to use them instead, depending on context, to make clear your intent. Also #inject is public on classes (not on other traits) while #include is not.
272
+ For all this to happen Jackbox also introduces some additional constructs, namely the keywords #inject and #enrich. These can be thought as simply new corollaries to #include and #extend. In fact they can be used interchangeably. If you're working with traits you may want to use them instead, depending on context, to make clear your intent. Also #inject is public on classes (not on other traits) while #include is not.
271
273
 
272
274
  #### #include/inject *t
273
- This method is analogous to ruby's #include but its use is reserved for Trait Injectors. The scope of this method is the same as the scope of #include, and its intended use like that of #include is for class definitions. Use it to "include" a trait into a receiving class. Also takes multiple traits.
275
+ This method is analogous to ruby's #include but its use is reserved for Trait Injectors. The scope of this method is the same as the scope of #include, and its intended use like that of #include is for class definitions. Use it to "include" a trait into a receiving class. Also takes multiple traits. The order of precedence is like that of ruby's #include from left to right.
274
276
 
275
277
  #### #extend/enrich *t
276
- This method in turn is analogous to ruby's #extend. The scope of this method is also the same as that of #extend, and its intended use if for object definition. Use it to "extend" the receiver of a trait. Also takes multiple traits.
278
+ This method in turn is analogous to ruby's #extend. The scope of this method is also the same as that of #extend, and its intended use is for object definition. Use it to "extend" the receiver of a trait. Also takes multiple traits with the same order of precedence.
277
279
 
278
280
  **IMPORTANT NOTE: Trait Injector lookup follows the method and not the constant lookup algorithm.**
279
281
 
@@ -281,15 +283,15 @@ If you need to follow constant lookup, here is the code for that:
281
283
 
282
284
  Name = trait :sym .... # this also creates a hard tag (see below)
283
285
 
284
- ### Trait/Injector Versioning
286
+ ### Trait Injector Versioning
285
287
 
286
- One of the most valuable properties of Jackbox is Trait Injector Versioning. Versioning is the term used to identify a feature in the code that produces an artifact which contains a certain set of methods and associated outputs, and which represents a snapshot of that trait up until the point it's applied to an object. From, that point on the object contains only that version of trait methods, and any subsequent overrides to those methods on the trait are only members of the "prolongation" of that trait and do not become part of previous targets unless some form of trait re-injection occurs. Newer versions of a trait only become part of newer targets or newer trait injections into existing targets. With Jackbox Trait/Injector Versioning, two different versions of the same code object can be running simultaneously.
288
+ One of the most valuable properties of Jackbox is Trait Injector Versioning. Versioning is the term used to identify a feature in the code that produces an artifact which contains a certain set of methods and associated outputs, and which represents a snapshot of that trait up until the point it's applied to an object. From, that point on the object contains only that version of injector methods, and any subsequent overrides to those methods on the trait injector are only members of the "prolongation" of that trait and do not become part of previous targets unless some form of trait re-injection occurs. Newer versions of a trait only become part of newer targets or newer trait injections into existing targets. With Jackbox Injector Versioning, two different versions of the same code object can be running simultaneously.
287
289
 
288
290
  We'll use some examples to illustrate the point. This is how versioning occurs:
289
291
 
290
- # trait declaration
292
+ # trait injector declaration
291
293
  #___________________
292
- trait :my_trait do
294
+ injector :my_trait do
293
295
  def bar
294
296
  :a_bar # version bar.1
295
297
  end
@@ -298,10 +300,15 @@ We'll use some examples to illustrate the point. This is how versioning occurs:
298
300
  end
299
301
  end
300
302
 
303
+ ###############################################
304
+ # First object has a preferred version #
305
+ ###############################################
306
+
301
307
  object1.extend my_trait # apply the trait --first snapshot
302
308
  object1.bar.should == :a_bar # pass the test
303
309
 
304
- # trait prolongation
310
+
311
+ # trait injector prolongation
305
312
  #__________________
306
313
  my_trait do
307
314
  def bar
@@ -310,9 +317,14 @@ We'll use some examples to illustrate the point. This is how versioning occurs:
310
317
  # ...
311
318
  end
312
319
 
320
+ ###############################################
321
+ # Second object has now its preferred version #
322
+ ###############################################
323
+
313
324
  object2.extend my_trait # apply the trait --second snapshot
314
325
  object2.bar.should == :some_larger_bar # pass the test
315
326
 
327
+
316
328
  ###############################################
317
329
  # First object has kept its preferred version #
318
330
  ###############################################
@@ -332,13 +344,11 @@ When trait re-injection occurs, and only then does the new version of the #bar m
332
344
 
333
345
  object1.bar.should == :some_larger_bar # bar.2 now available
334
346
 
335
- Re-injection on classes is a little bit trickier, because class injection is more pervasive. To re-inject a class with a trait we must use the Strategy Pattern (see below) or use private #update's. See the sections below as well as the rspec files for more on this.
347
+ Re-injection on classes is a little bit trickier, because class injection is more pervasive. To re-inject a class with a new trait we must use the Strategy Pattern (see below or the rspec files) or we have to use private #update's. Here is an example of Injector Versioning as it pertains to classes:
336
348
 
337
- Here is an example of Injector Versioning as it pertains to classes:
338
-
339
- # trait declaration:
349
+ # trait injector declaration:
340
350
  #___________________
341
- trait :Versions do
351
+ injector :Versions do
342
352
  def meth arg # version meth.1
343
353
  arg ** arg
344
354
  end
@@ -348,7 +358,8 @@ Here is an example of Injector Versioning as it pertains to classes:
348
358
  inject Versions() # apply --snapshot
349
359
  end
350
360
 
351
- # trait extension:
361
+
362
+ # trait injector prolongation:
352
363
  #_________________
353
364
  Versions do
354
365
  def meth arg1, arg2 # version meth.2 ... redefines meth.1
@@ -384,7 +395,7 @@ To update the class, we then do the following:
384
395
 
385
396
  ### Tagging/Naming
386
397
 
387
- The use of Tags is central to the concept of Versioning. Tagging happens in the following ways:
398
+ The use of Tags is central to the concept of Versioning. In a similar fashion to a version control system tags play a role in formalizing a set of changes, but unlike its more mundane sibling these changes represent actual object function and methods. Once a prolongation is tagged the it is always available in the same state and can be depended upon by any object needing that version of the functionality. More importantly once a tag is defined it should not be changed, and Jackbox goes to some lengths to prevent you from doing this. Of course this is Ruby out very own intent is to move from Play-doh to Plasticine. Tagging happens in the following ways:
388
399
 
389
400
  Version1 = trait :function do
390
401
  def meth arg
@@ -401,13 +412,13 @@ The use of Tags is central to the concept of Versioning. Tagging happens in the
401
412
  end
402
413
  end
403
414
 
404
- Version1 and Version2 are two different hard tags of the same Trait Injector. They introduce a more formal approach to trait versioning and also pave the way for the inheritance models described in the introduction. Aside from hard tags, there are also soft tags (see below).
415
+ Version1 and Version2 are two different hard tags of the same Trait Injector. They introduce a more formal approach to trait versioning and also pave the way for the two different inheritance models described in the introduction. Aside from hard tags, there are also soft tags (see below).
405
416
 
406
417
  ### Local Binding
407
418
 
408
- Before we move on, we also want to give some further treatment to trait local-binding. That is, the binding of a traits' methods is local to the prolongation/version in which they are located before the versioning occurs. Here, is the code:
419
+ Before we move on, we also want to give some further treatment to injector local-binding. That is, the binding of a traits' methods is local to the prolongation/version in which they are located before the versioning occurs. Here, is what we mean by that:
409
420
 
410
- **Note: In the following examples we use the notion of version naming/tagging. Once a version is tagged it shouldn't be modified. These tags comprise entities along the hierarchical structure of a trait.**
421
+ **Note: In the following examples we use the notion of version naming/tagging. Once a version is tagged it shouldn't be modified. These tags comprise entities along the hierarchical structure of a trait explained further below.**
411
422
 
412
423
 
413
424
  # trait declaration
@@ -420,6 +431,7 @@ Before we move on, we also want to give some further treatment to trait local-bi
420
431
  end
421
432
  o = Object.new.extend Version1 # apply --snapshot (like above)
422
433
 
434
+
423
435
 
424
436
  # trait prolongation
425
437
  #_____________________
@@ -434,6 +446,7 @@ Before we move on, we also want to give some further treatment to trait local-bi
434
446
  end
435
447
  end
436
448
  p = Object.new.extend Version2 # apply --snapshot (like above)
449
+
437
450
 
438
451
  ####################################################
439
452
  # #compound.1 bound to the right version #basic.2 #
@@ -446,13 +459,15 @@ Before we move on, we also want to give some further treatment to trait local-bi
446
459
  o.compound.should == 11 # compound.1 --bound locally to basic.2
447
460
 
448
461
 
462
+ Versioning together with local-binding allow the metamorphosis of traits to fit the particular purpose at hand, keeping those local modifications isolated from the rest of your program, and allowing your code to naturally evolve with your program. They cancel the need to monkey patch anything. If you need a local version of some code just open up a prolongation create the new version, inject it in to your targets, and leave the older versions and clients untouched.
463
+
449
464
  ### Virtual Method Cache (VMC)
450
465
 
451
- When you are working with a trait injector in irb/pry it is often easier to just add methods to the trait without actually having to re-apply the trait to the the target to see the result. This is just what the Virtual Method Cache is for **among other things.** The VMC allows working with traits like you would with regular modules. It also enhances the normal method resolution of modules into that of modules in the chain but not directly applied to the target. Here is what the code looks like:
466
+ When you are working with a trait injector in irb/pry it is often easier to just add methods to the trait without actually having to re-apply the trait to the the target to see the result. This is just what the Virtual Method Cache is for **among other things.** The VMC allows working with traits like you would with regular modules. It also enhances the normal method resolution of modules into that of modules in the chain but who are not directly applied to the target. Here is what the VMC looks like:
452
467
 
453
468
  # definition
454
469
  #_______________
455
- trait :SomeTrait
470
+ injector :SomeTrait
456
471
 
457
472
  # application
458
473
  #_______________
@@ -463,14 +478,14 @@ When you are working with a trait injector in irb/pry it is often easier to just
463
478
  obj = MyClass.new
464
479
 
465
480
  SomeMethods do
466
- def spm1 # #spm1 is only defined in the virtual cache
467
- :result # It is not actually part of the class yet!!
481
+ def spm1 # #spm1 is only defined in the Virtual Method Cache
482
+ :result # It is not actually part of the MyClass yet!!
468
483
  end # until this version/prolongation is applied
469
484
  end
470
485
 
471
486
  expect(obj.spm1).to eq(:result) # yet my obj can use it --no problem
472
487
 
473
- The key idea here is that the virtual method cache is the same for all versions of the Injector and all its applications. This is what allows working with traits as if they were regular modules. If we redefine VMC methods they are also redefined for all versions. To actually lock the method versions into place you must apply the Injector with the methods defined in it that you want the version to have. To then change that application of the trait you then re-inject the target. But the VMC, provides a scratch pad of methods for you to work with. The VMC also provides extended method resolution to the trait. To understand what we mean by this, take a look at following code:
488
+ The key idea here is that the Virtual Method Cache is the same for all versions of the Injector and all its applications. This is what allows working with traits as if they were regular modules. If we redefine VMC methods they are also redefined for all versions. To actually lock the method versions into place you must apply the Injector with the methods defined in it that you want the version to have. To then change that application of the trait you then re-inject the target. But the VMC, provides a scratch pad of methods for you to work with. The VMC also provides extended method resolution to the injector. To understand what we mean by this, take a look at following code:
474
489
 
475
490
  class Client
476
491
  include trait :J1
@@ -494,16 +509,16 @@ The key idea here is that the virtual method cache is the same for all versions
494
509
  Client.new.n2m1
495
510
  Client.new.n3m1
496
511
 
497
- Think of how this would be different with regular modules. For this to happen using regular Ruby modules K1 and L1 should have to be defined and included prior to their inclusion into our client. And no it is not just a matter of moving the include to the beginning of each container.
512
+ Think of how this would be different with regular modules. For this to happen using regular Ruby modules, K1 and L1 should have to be defined and included prior to their inclusion into our client. And no it is not just a matter of moving the include to the beginning of each container.
498
513
 
499
514
  #### #define\_method sym, &blk
500
- There is one more interesting property to method definition with Trait Injectors however. The use of #define\_method to define/re-define methods in any prolongation affects the entire trait and all its versions. This also preserves a fundamental tenet of traits: take some local context, enclose it, and use the trait to introduce it to some indiscriminate target, and additionally has some other uses as we'll see with in our description of patterns and trait composition.
515
+ There is one more interesting property to method definition with Trait Injectors however. The use of #define\_method to define/re-define methods in any prolongation affects the entire trait and all its versions except for any hard tagged as these are exactly that hard. This also preserves a fundamental tenet of traits: take some local context, enclose it, and use the trait to introduce it to some indiscriminate target, and additionally has some other uses as we'll see with in our description of Jackbox patterns and trait composition.
501
516
 
502
517
  Here is an example of the difference with #define\_method:
503
518
 
504
519
  # define trait
505
520
  #_________________________
506
- trait :some_trait do
521
+ injector :some_trait do
507
522
  def meth
508
523
  :meth
509
524
  end
@@ -549,13 +564,12 @@ Here is an example of the difference with #define\_method:
549
564
 
550
565
  Client.new.foo_bar.should == 'fooooo and barrrrr'
551
566
 
567
+ This also allows the introduction of what we have termed a new coding pattern based on the late binding of decorators (see below).
552
568
 
553
- Versioning together with local-binding allow the metamorphosis of traits to fit the particular purpose at hand, keeping those local modifications isolated from the rest of your program, and allowing your code to naturally evolve with your program. They cancel the need to monkey patch anything. If you need a local version of some code just open up a prolongation create the new version, inject it in to your targets, and leave the older versions and clients untouched.
554
-
555
- ### Trait/Injector introspection
569
+ ### Trait Injector introspection
556
570
  Trait Injectors have the ability to speak about themselves. Moreover traits can speak about their members just like any module or class, and can also inject their receivers with introspecting capabilities. Every injected/enriched object or module/class can enumerate its traits, and traits can enumerate their members, and so forth.
557
571
 
558
- trait :Function do
572
+ injector :Function do
559
573
  def far
560
574
  end
561
575
  def close
@@ -589,10 +603,10 @@ Trait Injectors have the ability to speak about themselves. Moreover traits can
589
603
 
590
604
  # later on...
591
605
 
592
- Child.eject *Child.traits
606
+ Child.cancel *Child.traits
593
607
 
594
- #### #traits *sym
595
- Called with no arguments returns a list of traits. A call with a list of trait symbols however returns an array of actual Trait Injector objects matching the names supplied in a LIFO fashion. The method also extends into a sub-mini API. An example use goes like this:
608
+ #### #traits/#injectors *sym
609
+ Called with no arguments returns a list of traits. A call with a list of trait symbols however returns an array of actual Trait Injector objects matching the names supplied in a LIFO fashion. The method also extends into a micro API. An example use goes like this:
596
610
 
597
611
  #traits --(in this target)
598
612
 
@@ -648,7 +662,7 @@ Called with no arguments returns a list of traits. A call with a list of trait
648
662
  # aliased to last_by_sym
649
663
 
650
664
  #### #history alias #versions
651
- This method returns a trace of all the hosted Trait Injectors which is ordered based on the order in which they are created. It also includes the pseudo-hosted hard tags and soft tags which can also be specifically accessed through the #tags method below. It is primarily a view of all existing versions of a trait. Here is the code:
665
+ This method returns a trace of all the applied trait injector versions ordered based on the order in which they are created. It also includes the version hard tags and soft tags which can also be specifically accessed through the #tags method below. It is primarily a view of all existing versions of a trait. Here is the code:
652
666
 
653
667
  # create our trait and
654
668
  # host it a couple of times
@@ -680,7 +694,7 @@ This method returns a trace of all the hosted Trait Injectors which is ordered b
680
694
  expect(HistorySample().history.last).to equal(HistorySample())
681
695
 
682
696
  #### #tags
683
- This method traces the tags only. The method also extends into a sub-mini API returning hard and soft tags independently. Here is the code:
697
+ This method traces tags only. It also extends into a micro API returning hard and soft tags independently. Here is the code:
684
698
 
685
699
  # at this point from the above...
686
700
 
@@ -700,7 +714,7 @@ This method traces the tags only. The method also extends into a sub-mini API r
700
714
  HistorySample().tags.hard
701
715
  => [(|HistorySample|:#234435),(|HistorySample|:#876679)]
702
716
 
703
- The reason for hard tags is related to inheritance while that of soft tags is connected to composition. For more on this take a look at the Solutions Pattern below for an application of soft tags and at JITI for hard tags and its connection to inheritance. See also the Jackbox blog at <a href="http://jackbox.us">http://jackbox.us</a> and the rspec files for the project.
717
+ Hard tags are an aid to inheritance while soft tags help with composition. For more on this take a look at the Solutions Pattern below for an application of soft tags and at JITI (Just-In-Time Inheritance) for and example of hard tags and its connection to inheritance. See also the Jackbox blog at <a href="http://jackbox.us">http://jackbox.us</a> and the rspec files for the project.
704
718
 
705
719
  #### #precedent and #progenitor (alias #pre, #pro)
706
720
  The #pre method gets the previous element in the history. Here is the code:
@@ -713,43 +727,218 @@ The #pre method gets the previous element in the history. Here is the code:
713
727
 
714
728
  # expect the following
715
729
  expect(HistorySample().history.last.precedent).to equal(HistorySample().history.first)
716
-
717
- The #pro method gets the version from which a particular trait was generated. This may not necessarily be the precedent. Take a look at the following code.
730
+
731
+ For more on this see the rspec files.
718
732
 
719
- # create the trait
720
- trait :Progample
721
-
722
- # expect the following
723
- expect(Progample().history).to be_empty
724
- expect(Progample().precedent).to equal(Progample().spec)
725
- expect(Progample().progenitor).to equal(Progample().spec)
726
-
727
- # create some history
728
- extend Progample(), Progample()
733
+ ### Other Capabilities of Trait Injectors
729
734
 
730
- # expect the following
731
- expect(Progample().history.size).to eq(2)
732
- expect(Progample().history.first.progenitor).to equal(Progample().spec)
733
- expect(Progample().history.last.pro).to equal(Progample().spec)
734
- expect(Progample().history.last.pre).to equal(Progample().history.first)
735
- expect(Progample().history.first).to_not equal(Progample().spec)
736
-
737
- Furthermore:
735
+ The functionality of Trait Injectors can be removed from individual targets, whether class or instance targets, in various different ways. This allows for whole 'classes' of functionality to be removed or withdrawn and then made available again at whim and under programer control. The first method of injector withdrawal is trait canceling or ejection. This is where a trait is completely removed from a target precipitating further calls on the trait to generate an error. Second there is trait silencing and reactivation. This on the other hand allows for the temporary quieting of a trait but which may need to be reactivated at a later time. Trait canceling or ejection can take place at the instance or the class level.
738
736
 
739
- Tag = Progample()
740
- expect(Tag.pro).to equal(Progample().spec)
741
-
742
- class A
743
- inject Tag
737
+ Here we have a Trait Injector removed after an #enrich to individual instance:
738
+
739
+ class Coffee
740
+ def cost
741
+ 1.00
742
+ end
743
+ end
744
+ trait :milk do
745
+ def cost
746
+ super() + 0.50
747
+ end
744
748
  end
745
749
 
746
- expect(A.traits.first.pro).to equal(Tag)
747
-
748
- For more on this see the rspec files.
750
+ cup = Coffee.new.enrich(milk)
751
+ friends_cup = Coffee.new.enrich(milk)
752
+
753
+ cup.cost.should == 1.50
754
+ friends_cup.cost.should == 1.50
755
+
756
+ cup.cancel :milk
757
+
758
+ cup.cost.should == 1.00
759
+
760
+ # friends cup didn't change price
761
+ friends_cup.cost.should == 1.50
762
+
763
+ Here it is removed after an #inject at the class level:
764
+
765
+ # create the injection
766
+ class Home
767
+ trait :layout do
768
+ def fractal
769
+ end
770
+ end
771
+ inject layout
772
+ end
773
+ expect{Home.new.fractal}.to_not raise_error
774
+
775
+ # build
776
+ my_home = Home.new
777
+ friends = Home.new
778
+
779
+ # eject the code
780
+ class Home
781
+ eject :layout
782
+ end
783
+
784
+ # the result
785
+ expect{my_home.fractal}.to raise_error
786
+ expect{friends.fractal}.to raise_error
787
+ expect{Home.new.fractal}.to raise_error
788
+
789
+
790
+ The code for these examples makes use of the #cancel alias #eject method which opens the door to this additional functionality provided by traits. See also the Strategy Pattern just below this. It is important to keep in mind that ejection is "permanent" (not really, can always be re-injected) and that this permanence is more of its intent. There are other ways to control code presence in targets through the use of Injector Directives. See below. For more on this also see the rspec examples.
791
+
792
+ #### #cancel *sym (alias #eject)
793
+ This method cancels or ejects trait function from a single object or class. It is in scope on any classes injected or enriched with a trait. Its effect is that of completely removing one of our modular closures from the ancestor chain. Once this is done method calls on the trait will raise an error.
794
+
795
+ ### Injector Directives
796
+ Once you have a trait handle you can also use it to issue directives to the trait. These directives can have a profound effect on your code. There are directives to silence a trait, to reactivate it, to create a soft tag, or to completely obliterate the trait including the handle to it.
797
+
798
+ #### :silence/:collapse directive
799
+ This description produces similar results to the one for trait ejection (see above) except that further trait method calls DO NOT raise an error. They just quietly return nil. Here are a couple of different cases:
800
+
801
+ The case with multiple object instances:
802
+
803
+ trait :copiable do
804
+ def object_copy
805
+ 'a dubious copy'
806
+ end
807
+ end
808
+
809
+ o1 = Object.new.enrich(copiable)
810
+ o2 = Object.new.enrich(copiable)
811
+
812
+ o1.object_copy.should == 'a dubious copy'
813
+ o2.object_copy.should == 'a dubious copy'
814
+
815
+ copiable :silence
816
+
817
+ o1.object_copy.should == nil
818
+ o2.object_copy.should == nil
819
+
820
+
821
+ The case with a class receiver:
822
+
823
+ class SomeClass
824
+ trait :code do
825
+ def tester
826
+ 'boo'
827
+ end
828
+ end
829
+
830
+ inject code
831
+ end
832
+
833
+ a = SomeClass.new
834
+ b = SomeClass.new
835
+
836
+ # collapse
837
+ SomeClass.code :collapse
838
+
839
+ a.tester.should == nil
840
+ b.tester.should == nil
841
+
842
+
843
+ #### :active/:rebuild directive
844
+ Trait Injectors that have been silenced or collapsed can at a later point be reactivated. Here are a couple of cases:
845
+
846
+ The case with multiple object receivers:
847
+
848
+ trait :reenforcer do
849
+ def thick_walls
850
+ '=====|||====='
851
+ end
852
+ end
853
+
854
+ o1 = Object.new.enrich(reenforcer)
855
+ o2 = Object.new.enrich(reenforcer)
856
+
857
+ reenforcer :collapse
858
+
859
+ o1.thick_walls.should == nil
860
+ o2.thick_walls.should == nil
861
+
862
+ reenforcer :rebuild
863
+
864
+ o1.thick_walls.should == '=====|||====='
865
+ o2.thick_walls.should == '=====|||====='
866
+
867
+
868
+ The case with a class receiver:
869
+
870
+ class SomeBloatedObject
871
+ trait :ThinFunction do
872
+ def perform
873
+ 'do the deed'
874
+ end
875
+ end
876
+ inject ThinFunction()
877
+ end
878
+ SomeBloatedObject.ThinFunction :silence # alias to :collapse
879
+
880
+ tester = SomeBloatedObject.new
881
+ tester.perform.should == nil
882
+
883
+ SomeBloatedObject.ThinFunction :active # alias to :rebuild
884
+ tester.perform.should == 'do the deed'
885
+
886
+ #### :tag/:version directive
887
+ This directive creates a soft tagged version of a trait. For more on this see Soft Tags below.
888
+
889
+ #### :implode directive
890
+ This directive totally destroys the trait including the handle to it. Use it carefully!
891
+
892
+ class Model
893
+ def feature
894
+ 'a standard feature'
895
+ end
896
+ end
897
+
898
+ trait :extras do
899
+ def feature
900
+ super() + ' plus some extras'
901
+ end
902
+ end
903
+
904
+ car = Model.new.enrich(extras)
905
+ car.feature.should == 'a standard feature plus some extras'
906
+
907
+ extras :implode
908
+
909
+ # total implosion
910
+ car.feature.should == 'a standard feature'
911
+
912
+ expect{extras}.to raise_error(NameError, /extras/)
913
+ expect{ new_car = Model.new.enrich(extras) }.to raise_error(NameError, /extras/)
914
+ expect{
915
+ extras do
916
+ def foo
917
+ end
918
+ end
919
+ }.to raise_error(NameError, /extras/)
920
+
921
+ ### Soft Tags
922
+ Just like hard tags above but a name is not needed. They come about by the :tag directive from above and are intended as markers for versions of algorithms that need to be accessed at a later time on demand. For more on this see the Solutions Pattern below.
923
+
924
+ jack :SomeJack do
925
+ def foo
926
+ :foo
927
+ end
928
+ end
929
+ SomeJack :tag # Unnamed version
930
+
931
+ SomeJack(:tag) do # New unnamed version
932
+ def foo
933
+ :foooooooo
934
+ end
935
+ end
749
936
 
750
- ### Trait/Injector Equality and Difference
937
+ Accessible through trait#tags (an Array). Also available **trait#tags.hard** and **trait#tags.soft**. See introspection above.
938
+
939
+ ### Trait Injector Equality and Difference
751
940
 
752
- Injectors can be compared. This allows further introspection capabilities which can be used to determine if a certain piece of code possesses a certain block of capabilities, test if those are equal to some other component's capabilities, or determine what the difference is. This is similar to what you would find with simple modules, but capabilities are compared on a method basis. It only follows that if traits can be applied and withdrawn from any target we should be able to test for their similarities to other traits. Injector difference on the complement, finds the actual delta between traits and returns and array with those differences.
941
+ Injectors can be compared. This allows further introspection capabilities which can be used to determine if a certain piece of code possesses a certain block of capabilities, test if those are "equal" to some other component's capabilities in terms of duck types, or determine what the difference is. This is similar to what you would find with simple modules, but capabilities are compared on a method basis. It only follows that if traits can be applied and withdrawn from any target we should be able to test for their similarities to other traits. Injector difference is the complement to all this, and it finds the actual delta between traits and returns those differences.
753
942
 
754
943
  Here is how equality is defined:
755
944
 
@@ -777,7 +966,7 @@ Here is how equality is defined:
777
966
 
778
967
 
779
968
 
780
- Inequality is based on a trait's methods. Once you add method definitions to a trait, that trait tests as inequality to it precedent or progenitor provided this is not the original trait. The original trait is the #pre and #pro to all others. It always tests as equal to its handle, but versions past or since do not. A different trait with the same methods is also not equal to the trait.
969
+ Inequality is based on a trait injector's methods. Once you add method definitions to a trait, that trait tests as an inequality to its precedent provided this is not the original (#spec) injector. This original trait is also the #pre and #pro to all others. It always tests as equal to its handle, but versions past or since do not. A different injector with the same methods is also not equal to the trait.
781
970
 
782
971
  Here is how inequality is defined:
783
972
 
@@ -805,7 +994,7 @@ Here is how inequality is defined:
805
994
  E().should == E() # always
806
995
  E().should == E().spec
807
996
 
808
- Difference is deeper than simple inequality. It returns the actual delta between what you have and what you pass in to the call as an array of two elements. The first element is the methods common to both operands, the second is the delta from the first to the second. The method also extends into a sub-mini API. Furthermore, the elements of the array which are arrays themselves also return a partial trait from their payload which can be used in further trait injection. Here is how difference is defined:
997
+ Difference is deeper than simple inequality. It returns the actual delta between what you have and what you pass in to the call as an array of two elements. The first element is comprised of the methods common to both operands, the second is the delta from the first to the second. This method also exposes a micro API. Furthermore, the elements of the array which are special arrays themselves also return a partial trait with their actual injector payload which can be used in further trait injection. Here is how difference is defined:
809
998
 
810
999
  #### #trait.diff ver=nil
811
1000
 
@@ -882,54 +1071,52 @@ Difference is deeper than simple inequality. It returns the actual delta betwee
882
1071
  #diff.join.injector
883
1072
  #diff.delta.injector
884
1073
 
885
- # a tag as precedent
886
- ETag5 = E()
1074
+ # a tag as precedent
1075
+ ETag5 = E()
887
1076
 
888
1077
 
889
- # if E() definitions **
890
- E do
891
- def foo
892
- :foo
893
- end
894
- def bar
895
- :bar
896
- end
897
- end
1078
+ # if E() definitions **
1079
+ E do
1080
+ def foo
1081
+ :foo
1082
+ end
1083
+ def bar
1084
+ :bar
1085
+ end
1086
+ end
898
1087
 
899
1088
 
900
- # then
901
- E().diff.should_not be_empty
1089
+ # then
1090
+ E().diff.should_not be_empty
902
1091
 
903
- # being that
904
- E().diff.join.should be_empty
905
- E().diff.delta.should_not be_empty
906
- # as for
907
- E().diff.delta.injector.instance_methods.should == [:foo, :bar]
908
- # and
909
- E().diff.delta.injector.should_not eq(E().diff.join.injector)
1092
+ # being that
1093
+ E().diff.join.should be_empty
1094
+ E().diff.delta.should_not be_empty
1095
+ # as for
1096
+ E().diff.delta.injector.instance_methods.should == [:foo, :bar]
1097
+ # and
1098
+ E().diff.delta.injector.should_not eq(E().diff.join.injector)
910
1099
 
911
- # being that
912
- E().diff.join.injector.instance_methods.should be_empty
913
- E().diff.delta.injector.instance_methods.should_not be_empty
1100
+ # being that
1101
+ E().diff.join.injector.instance_methods.should be_empty
1102
+ E().diff.delta.injector.instance_methods.should_not be_empty
914
1103
 
915
- # allows the following
916
- class Incomplete
917
- inject E().diff.delta.injector
918
- end
919
- # and
920
- Incomplete.new.foo.should eq(:foo)
1104
+ # allows the following
1105
+ class Incomplete
1106
+ inject E().diff.delta.injector
1107
+ end
1108
+ # and
1109
+ Incomplete.new.foo.should eq(:foo)
921
1110
 
922
- # being that
923
- E().diff.delta.injector.should be_instance_of(Injector)
924
- E().diff.delta.injector.should be_instance_of(Trait)
1111
+ # being that
1112
+ E().diff.delta.injector.should be_instance_of(Injector)
1113
+ E().diff.delta.injector.should be_instance_of(Trait)
925
1114
 
926
1115
 
927
1116
 
928
- The version argument can have the following forms: negative index (-1, etc), or another version. By default, it uses the previous version. NOTE: the previous version of an un-altered trait is equal to the trait.
929
-
930
- Again, for more on this see the rspec files.
1117
+ The version argument can be negative index (-1, etc), or another version. By default, it uses the previous version. NOTE: the previous version of an un-altered trait is equal to the trait. Again, for more on this see the rspec files.
931
1118
 
932
- ### Trait/Injector composition
1119
+ ### Trait Injector composition
933
1120
  The composition of multiple traits into an object can be specified as follows:
934
1121
 
935
1122
  include Injectors
@@ -981,14 +1168,72 @@ The composition of multiple traits into an object can be specified as follows:
981
1168
  end
982
1169
  end
983
1170
 
984
- One thing to note is the difference between defining function through the VMC, which allows working with traits already applied to targets and predefining function of a trait before application and versioning. The first allows a flow similar to that of regular modules and the later makes use of the true nature of traits and allows customizing traits to their targets.l
1171
+ For full examples see the rspec code. One thing to note is the difference between defining function through the VMC, which allows working with traits already applied to targets and predefining function of a trait before application and versioning. The first allows a flow similar to that of regular modules and the later makes use of the true nature of trait injectors and allows customizing trait injectors to their targets.
1172
+
1173
+ ### Unbound Dependency (Soft Ancestors)
1174
+
1175
+ Another important feature of trait injectors is a property of injectors to combine with other traits in multiple ways (with different function or precedence) without actually permanently binding to any of their ancestors or their function. This on-the-fly soft binding allows for multiple combinations of form and function from the same basic components. This is another area where Jackbox mix-ins go beyond what is possible with regular modules. In the following example we combine the same injectors in this (soft) way to derive different function.
1176
+
1177
+ #####
1178
+ # injector declarations
1179
+
1180
+ injector :drive do
1181
+ def drive_result
1182
+ payload * 2
1183
+ end
1184
+ end
1185
+
1186
+ trait :strings do
1187
+ def payload
1188
+ '+++++'
1189
+ end
1190
+ end
1191
+
1192
+ trait :numbers do
1193
+ def payload
1194
+ 10
1195
+ end
1196
+ end
1197
+
1198
+ #####
1199
+ # soft ancestry declarations
1200
+
1201
+ Result1 = drive strings
1202
+ Result2 = drive numbers
1203
+
1204
+
1205
+ class Driven
1206
+ include Result1
1207
+ end
1208
+
1209
+ Driven.new.drive_result.should == '++++++++++'
1210
+
1211
+
1212
+ class Driven
1213
+ update Result2
1214
+ end
1215
+
1216
+ Driven.new.drive_result.should == 20
1217
+
1218
+
1219
+ #################################
1220
+ # remain unbound to ancestry
1221
+ #################################
1222
+
1223
+ drive.ancestors.should == [drive]
1224
+
1225
+ #################################
1226
+ # ----------amazing-------------
1227
+ #################################
1228
+
1229
+ Granted this is a somewhat contrived example but you get the idea. Think again how is this different than what is available from modules. With modules once one module is included in another that relationship is permanent. With Jackbox trait injectors and soft ancestors the same components can be reused in multiple bindings tying their function locally only but remaining globally decoupled.
985
1230
 
986
1231
  ### Inheritance
987
- Inheritance with traits comes in two forms. The first form comes from the normal versioning of a trait. The second comes from JITI which follows a model similar to what you find in regular classes. With versioning a trait inherits all the function from its progenitor allowing customization of only the parts needed for the application at hand but cannot call upon previous versions of itself. With JITI this later dimension of access to its ancestry is possible but we must be aware of colluding ancestry when creating decorators. Jackbox itself warns you of this however.
1232
+ Inheritance with trait injectors comes in two flavors. The first form comes from the normal versioning of a trait. The second comes from JITI which follows a model similar to what you find in regular classes. With versioning a trait injector inherits all the function from its progenitor allowing customization of only the parts needed for the specific application at hand but cannot call upon previous versions of itself. With JITI this later dimension of access to its own ancestry is possible.
988
1233
 
989
- But before this, the behavior of Trait Injectors under normal class inheritance is also of interest. Trait injectors act upon a class and its children. Introspection on traits under class inheritance is achieved with the use of the :all directive on the #trait/#injectors method. This behavior is partially specified by what follows:
1234
+ But before we get into all this, the behavior of Trait Injectors under normal class inheritance is also of interest. Trait injectors act upon a class and its children. Introspection on traits under class inheritance is possible and is achieved with the :all directive on the #traits/#injectors method. The behavior of trait injectors under normal class inheritance is partially specified by what follows:
990
1235
 
991
- trait :j
1236
+ injector :j
992
1237
 
993
1238
  class C
994
1239
  end
@@ -1014,9 +1259,9 @@ But before this, the behavior of Trait Injectors under normal class inheritance
1014
1259
  C.new.foo.should == 'foo'
1015
1260
  D.new.foo.should == 'foo'
1016
1261
 
1017
- For mote on this as also see the rspec files.
1262
+ For more on this also see the rspec files.
1018
1263
 
1019
- More importantly though is the following example of trait inheritance due to versioning. As previously stated, the concept of tag/naming also plays an important role with inheritance, as illustrated in the following code:
1264
+ More importantly though is the inheritance native to injectors. The following example touches on the first form of native trait injector inheritance, the inheritance due to versioning. Here we have an example of how inheritance due to versioning can be harnessed to promote the re-use of common elements. Like previously stated, the concept of tagging/naming also plays an important role with inheritance as illustrated in the following code:
1020
1265
 
1021
1266
  trait :player do
1022
1267
  def sound
@@ -1044,21 +1289,21 @@ More importantly though is the following example of trait inheritance due to ver
1044
1289
  end
1045
1290
  end
1046
1291
 
1047
- class JukeBox < BoomBox
1048
- inject CDPlayer
1292
+ class JukeBox < BoomBox
1293
+ inject CDPlayer # Mixed class and injector inheritance models
1049
1294
  end
1050
1295
 
1051
1296
 
1052
- The different versions inherit all of the pre-existing methods from the current trait and freeze that function. We can either Tag/Name it of simply include/extend into a target but the function is frozen at that time. Tags cannot be modified or more clearly shouldn't be modified. Classes retain the frozen version of the trait until the time an update is made. Of course, there is always #define\_method and the VMC. For more on all this see, the Rspec examples.
1297
+ The different versions inherit all of the pre-existing methods from the previous one and "freeze" that function. We can either Tag/Name them for later use or simply include/extend then into a new target, but the function is fixed at that time. Tags cannot be modified or more clearly shouldn't be modified. Classes retain the fixed version of the trait until the time an update is made. But, trait injectors then also inherit new methods through the VMC (see above). For more on all this see, the Rspec examples.
1053
1298
 
1054
1299
  ### Just-In-Time Inheritance (JITI)
1055
1300
 
1056
- This flavor of the inheritance model allows our modular closures to have similar properties to the inheritance of classes. With it you can expect to have access to the trait's super/ancestor members as part of the call, just like you would with classes. In addition to the inheritance resulting from versioning, JITI presents a more complete scenario adding color to the inheritance picture painted by trait injectors. The key takeaway here is that traits are a form of mix-in that share an enhanced but similar inheritance model with classes. You can version them to gain access to versioned inheritance or you can tag and then override its members to access an ancestor chain comprised of all previous tags. As always we will use some example code to illustrate:
1301
+ This flavor of the inheritance model on the other hand allows our modular closures to have similar properties to the inheritance of classes. With it you can expect to have access to the trait's super/ancestor members as part of the call, just like you would with class inheritance. In addition to the inheritance resulting from versioning, JITI presents a more complete scenario adding color to the inheritance picture painted by trait injectors with one caveat, we must be aware of colluding ancestry when creating decorators based on JITI. This very narrow case is explained in the specs and Jackbox itself warns you of this however. The key takeaway here is: trait injectors are a form of mix-in that share a similar inheritance model with classes, but all happening in the mix-in however. You can version them to gain access to versioned inheritance or you can tag and then override its members to access an ancestor chain comprised of all previous tags. As always we will use some example code to illustrate:
1057
1302
 
1058
1303
  #
1059
- # Our Trait
1304
+ # Our Trait Injector
1060
1305
  #
1061
- Tag1 = trait :Functionality do
1306
+ Tag1 = injector :Functionality do
1062
1307
  def m1
1063
1308
  1
1064
1309
  end
@@ -1076,18 +1321,15 @@ This flavor of the inheritance model allows our modular closures to have similar
1076
1321
  'other' # -- same ancestors as before
1077
1322
  end
1078
1323
  end
1079
-
1080
1324
  expect(Functionality().ancestors).to eql( [Functionality()] )
1081
1325
 
1082
- # test it
1326
+ #####
1327
+ # test function
1083
1328
 
1084
1329
  o = Object.new.extend(Functionality())
1085
1330
 
1086
- # inherited
1087
1331
  o.m1.should == 1
1088
1332
  o.m2.should == :m2
1089
-
1090
- # current
1091
1333
  o.other.should == 'other'
1092
1334
 
1093
1335
 
@@ -1096,7 +1338,7 @@ This flavor of the inheritance model allows our modular closures to have similar
1096
1338
  #
1097
1339
  Tag2 = Functionality do
1098
1340
  def m1 # The :m1 override invokes JIT Inheritance
1099
- super + 1 # -- Tag1 is summoned into ancestor chain
1341
+ super + 2 # -- Tag1 is summoned into ancestor chain
1100
1342
  end # -- allows the use of super
1101
1343
 
1102
1344
  def m3
@@ -1104,12 +1346,13 @@ This flavor of the inheritance model allows our modular closures to have similar
1104
1346
  end
1105
1347
  end
1106
1348
 
1349
+ #####
1107
1350
  # test it
1108
1351
 
1109
1352
  p = Object.new.extend(Tag2)
1110
1353
 
1111
1354
  # JIT inherited
1112
- p.m1.should == 2
1355
+ p.m1.should == 3
1113
1356
 
1114
1357
  # regular inheritance
1115
1358
  p.m2.should == :m2
@@ -1123,24 +1366,20 @@ This flavor of the inheritance model allows our modular closures to have similar
1123
1366
 
1124
1367
  JITI (Just-In-Time Inheritance) is governed by a set of rules framing its behavior. Here are these rules and their descriptions:
1125
1368
 
1126
- 1. JITI works like class inheritance but as a mix-in. It holds onto method definitions of earlier version hard tags. It lets you override or rebase (start fresh) individual methods at any level. It works under object extension. It works under class inclusion.
1127
- 2. The trait handle is always in sync with the last hard tag until purposefully changed. This also means the handle definitions use the last hard tag as a departing base for any further changes.
1128
- 3. It allows initial external basing and also external base substitution. A trait can be based on an external trait or even module serving as a shell or casing for external function as long as any internal definitions don't overwrite the external ones.
1129
- 4. But, It forces internal basing once applied. Definitions internal to the trait always take precedence over external definitions by the same signature. This blocks external ancestor intrusion enforcing internal trait consistency.
1130
- 5. It keeps the VMC in proper working order. Like all traits, the VMC is always available as a cache of methods available globally to all versions of the trait.
1131
- 6. Directives are allowed. Also like all traits, JITI traits respond to normal trait injector directives.
1369
+ 1. JITI works like class inheritance but at the mix-in. It holds onto method definitions of earlier version hard tags. It lets you override or rebase (start fresh) individual methods at any level. It works for object extension. It works for class inclusion.
1370
+ 2. The trait injector handle (its access method) is always in sync with the last hard tag. This also means the injector definitions use the last hard tag as a departing base for any further changes.
1371
+ 3. It allows initial external basing and also external base substitution. A trait injector can be based on an external trait or even module serving as a shell or casing for external function as long as any internal definitions don't overwrite the external ones.
1372
+ 4. It forces internal basing once instated. Definitions internal to the trait injector always take precedence over external definitions by the same signature. This blocks external ancestor intrusion enforcing internal injector consistency.
1373
+ 5. It works with the VMC. Like all trait injectors, the VMC is always available, acting as a cache of methods available globally to all versions of the injector and which fills in those version methods from the main injector onto its descendants, supplementing this inheritance models as well.
1374
+ 6. It works with directives. Like all trait injectors, JITI trait injectors respond to normal injector directives. Here they work to cancel and reinstate entire trait hierarchies.
1132
1375
 
1133
- For more on this please see the rspec files in the project, or on the gem itself, and also visit our blog at http://jackbox.us
1134
-
1135
- But, this is the basic idea here. Traits are an extended closure which can be used as a mix-in, prolonged to add function, and shaped, versioned, tagged, and inherited to fit the purpose at hand. With Traits however you avoid the perils of monkey patching. You can just create a new version of the trait and leave the old one alone. Combining work flows of both modules and traits is also possible through the use of the VMC and the special version of #define_method. Moreover, using traits Jackbox also goes on to solve some traditional shortcomings of Ruby with some GOF(Gang of Four) object patterns.
1376
+ For more on all of this please see the rspec files in the GitHub project, or visit our blog at http://jackbox.us. But, this is the basic idea here. Trait injectors are an extended modular closure which can be used as a mix-in and prolonged to add function, shaped, versioned, tagged, and overridden to fit the purpose at hand. With trait injectors however you avoid the perils of monkey patching. You can just create a new version of the injector and leave the old one alone. They allow you to combine work flows from both modules and traits through the use of the VMC. They provide inheritance in enhanced forms and a compositional model that loosely couples member traits. Finally, they work under code directives. Moreover, using trait injectors Jackbox also goes on to solve some traditional shortcomings of Ruby with some of the GOF(Gang of Four) object oriented patterns.
1136
1377
 
1137
1378
  ---
1138
1379
 
1139
1380
 
1140
1381
  ### The GOF Decorator Pattern:
1141
- Traditionally this is only partially solved in Ruby through PORO decorators or the use of modules. However, there are the problems of loss of class identity for the former and the limitations on the times it can be re-applied to the same object for the latter. With Jackbox this is solved. A trait used as a decorator does not confuse class identity for the receiver. Decorators are useful in several areas of OOP: presentation layers, string processing, command processors to name a few.
1142
-
1143
- Here is the code:
1382
+ Traditionally this is only partially solved in Ruby through PORO decorators or the use of modules. However, there are the problems of loss of class identity for the former and the limitations on the times it can be re-applied to the same object for the latter. Here are a couple of articles on the matter: <a href="https://robots.thoughtbot.com/evaluating-alternative-decorator-implementations-in">Evaluating Alternative Decorator Implementations</a> and <a href="http://nithinbekal.com/posts/ruby-decorators/">Decorator Pattern in Ruby</a> With Jackbox however all of this is solved. A trait injector used as a decorator does not confuse class identity for the receiver. Decorators are useful in several areas of OOP: presentation layers, string processing, command processors to name a few. Here is the code:
1144
1383
 
1145
1384
  class Coffee
1146
1385
  def cost
@@ -1182,194 +1421,6 @@ Additionally, these same decorators can then be re-applied MULTIPLE TIMES to the
1182
1421
  # and other markup in html or...
1183
1422
 
1184
1423
 
1185
- ### Other Capabilities of Trait Injectors
1186
-
1187
- The functionality of Trait Injectors can be removed from individual targets, whether class or instance targets, in various different ways. This allows for whole 'classes' of functionality to be removed and made un-available and then available again at whim and under programer control. First we have trait canceling or ejection. This is where a trait is completely removed from a target precipitating further calls on the trait to generate an error. Second there is trait silencing and reactivation. This on the other hand allows for the temporary quieting of a trait but which may need to be reactivated at a later time.
1188
-
1189
- Trait canceling or ejection can take place at the instance or the class level. Here we have a Trait Injector removed after an #enrich to individual instance:
1190
-
1191
- class Coffee
1192
- def cost
1193
- 1.00
1194
- end
1195
- end
1196
- trait :milk do
1197
- def cost
1198
- super() + 0.50
1199
- end
1200
- end
1201
-
1202
- cup = Coffee.new.enrich(milk)
1203
- friends_cup = Coffee.new.enrich(milk)
1204
-
1205
- cup.cost.should == 1.50
1206
- friends_cup.cost.should == 1.50
1207
-
1208
- cup.cancel :milk
1209
-
1210
- cup.cost.should == 1.00
1211
-
1212
- # friends cup didn't change price
1213
- friends_cup.cost.should == 1.50
1214
-
1215
- Here it is removed after an #inject at the class level:
1216
-
1217
- # create the injection
1218
- class Home
1219
- trait :layout do
1220
- def fractal
1221
- end
1222
- end
1223
- inject layout
1224
- end
1225
- expect{Home.new.fractal}.to_not raise_error
1226
-
1227
- # build
1228
- my_home = Home.new
1229
- friends = Home.new
1230
-
1231
- # eject the code
1232
- class Home
1233
- eject :layout
1234
- end
1235
-
1236
- # the result
1237
- expect{my_home.fractal}.to raise_error
1238
- expect{friends.fractal}.to raise_error
1239
- expect{Home.new.fractal}.to raise_error
1240
-
1241
-
1242
- The code for these examples makes use of the #cancel alias #eject method which opens the door to this additional functionality provided by traits. See also the Strategy Pattern just below this. It is important to keep in mind that ejection is "permanent" (not really, can always be re-injected) and that this permanence is more of its intent. There are other ways to control code presence in targets through the use of Injector Directives. See below. For more on this also see the rspec examples.
1243
-
1244
- #### #cancel *sym (alias #eject)
1245
- This method cancels or ejects trait function from a single object or class. It is in scope on any classes injected or enriched with a trait. Its effect is that of completely removing one of our modular closures from the ancestor chain. Once this is done method calls on the trait will raise an error.
1246
-
1247
- ### Injector Directives
1248
- Once you have a trait handle you can also use it to issue directives to the trait. These directives can have a profound effect on your code. There are directives to silence a trait, to reactivate it, to create a soft tag, or to completely obliterate the trait including the handle to it.
1249
-
1250
- #### :silence/:collapse directive
1251
- This description produces similar results to the one for trait ejection (see above) except that further trait method calls DO NOT raise an error. They just quietly return nil. Here are a couple of different cases:
1252
-
1253
- The case with multiple object instances:
1254
-
1255
- trait :copiable do
1256
- def object_copy
1257
- 'a dubious copy'
1258
- end
1259
- end
1260
-
1261
- o1 = Object.new.enrich(copiable)
1262
- o2 = Object.new.enrich(copiable)
1263
-
1264
- o1.object_copy.should == 'a dubious copy'
1265
- o2.object_copy.should == 'a dubious copy'
1266
-
1267
- copiable :silence
1268
-
1269
- o1.object_copy.should == nil
1270
- o2.object_copy.should == nil
1271
-
1272
-
1273
- The case with a class receiver:
1274
-
1275
- class SomeClass
1276
- trait :code do
1277
- def tester
1278
- 'boo'
1279
- end
1280
- end
1281
-
1282
- inject code
1283
- end
1284
-
1285
- a = SomeClass.new
1286
- b = SomeClass.new
1287
-
1288
- # collapse
1289
- SomeClass.code :collapse
1290
-
1291
- a.tester.should == nil
1292
- b.tester.should == nil
1293
-
1294
-
1295
- #### :active/:rebuild directive
1296
- Trait Injectors that have been silenced or collapsed can at a later point be reactivated. Here are a couple of cases:
1297
-
1298
- The case with multiple object receivers:
1299
-
1300
- trait :reenforcer do
1301
- def thick_walls
1302
- '=====|||====='
1303
- end
1304
- end
1305
-
1306
- o1 = Object.new.enrich(reenforcer)
1307
- o2 = Object.new.enrich(reenforcer)
1308
-
1309
- reenforcer :collapse
1310
-
1311
- o1.thick_walls.should == nil
1312
- o2.thick_walls.should == nil
1313
-
1314
- reenforcer :rebuild
1315
-
1316
- o1.thick_walls.should == '=====|||====='
1317
- o2.thick_walls.should == '=====|||====='
1318
-
1319
-
1320
- The case with a class receiver:
1321
-
1322
- class SomeBloatedObject
1323
- trait :ThinFunction do
1324
- def perform
1325
- 'do the deed'
1326
- end
1327
- end
1328
- inject ThinFunction()
1329
- end
1330
- SomeBloatedObject.ThinFunction :silence # alias to :collapse
1331
-
1332
- tester = SomeBloatedObject.new
1333
- tester.perform.should == nil
1334
-
1335
- SomeBloatedObject.ThinFunction :active # alias to :rebuild
1336
- tester.perform.should == 'do the deed'
1337
-
1338
- #### :tag/:version directive
1339
- This directive creates a soft tagged version of a trait. For more on this see Soft Tags below.
1340
-
1341
- #### :implode directive
1342
- This directive totally destroys the trait including the handle to it. Use it carefully!
1343
-
1344
- class Model
1345
- def feature
1346
- 'a standard feature'
1347
- end
1348
- end
1349
-
1350
- trait :extras do
1351
- def feature
1352
- super() + ' plus some extras'
1353
- end
1354
- end
1355
-
1356
- car = Model.new.enrich(extras)
1357
- car.feature.should == 'a standard feature plus some extras'
1358
-
1359
- extras :implode
1360
-
1361
- # total implosion
1362
- car.feature.should == 'a standard feature'
1363
-
1364
- expect{extras}.to raise_error(NameError, /extras/)
1365
- expect{ new_car = Model.new.enrich(extras) }.to raise_error(NameError, /extras/)
1366
- expect{
1367
- extras do
1368
- def foo
1369
- end
1370
- end
1371
- }.to raise_error(NameError, /extras/)
1372
-
1373
1424
  ### The GOF Strategy Pattern:
1374
1425
  Another pattern that Jackbox helps with is the GOF Strategy Pattern. This is a pattern which changes the guts of an object as opposed to just changing its outer shell. Traditional examples of this pattern in Ruby use PORO component injection within constructors, and then a form of delegation. With Jackbox Trait Injectors all this is eliminated.
1375
1426
 
@@ -1426,25 +1477,6 @@ But, with #cancel/#eject it is possible to have an even more general alternate i
1426
1477
  cup.brew
1427
1478
  cup.strategy.should == 'sweedish'
1428
1479
 
1429
- ### Soft Tags
1430
- Just like hard tags above but a name is not needed:
1431
-
1432
- jack :SomeJack do
1433
- def foo
1434
- :foo
1435
- end
1436
- end
1437
- SomeJack :tag # Unnamed version
1438
-
1439
- SomeJack(:tag) do # New unnamed version
1440
- def foo
1441
- :foooooooo
1442
- end
1443
- end
1444
-
1445
- Accessible through trait#tags (an Array). Also available **trait#tags.hard** and **trait#tags.soft**. See introspection above.
1446
-
1447
- ---
1448
1480
  ### Patterns of a Different Flavor
1449
1481
 
1450
1482
  Jackbox Traits also make possible some additional coding patterns. Although not part of the traditional GOF set these new patterns are only possible thanks to languages like Ruby that although not as flexible as Lisp, permit the morphing of normal forms into newer ones. We hope that as Ruby evolves it continues to give programmers more power redefining the language itself. Here are some new patterns:
@@ -1473,7 +1505,7 @@ __1) Late Decorator.-__ Another flow that also benefits from #define\_method in
1473
1505
 
1474
1506
  w.cost.should == 15
1475
1507
 
1476
- The actual decorating trait function is late bound and defined only after some other data is available.
1508
+ The actual decorating trait function is late bound and defined only after some other process has completed.
1477
1509
 
1478
1510
  __2) The Super Pattern.-__ No. This is not a superlative kind of pattern. Simply, the use of #super can be harnessed into a pattern of controlled recursion, like in the following example:
1479
1511
 
@@ -1492,21 +1524,25 @@ __2) The Super Pattern.-__ No. This is not a superlative kind of pattern. Simp
1492
1524
 
1493
1525
  __3) The Solutions Pattern.-__ For a specific example of what can be accomplished using this workflow please refer to the rspec directory under the transformers spec. Here is the basic flow:
1494
1526
 
1495
- jack :Solution
1527
+ jack :Solution do
1528
+ def meth
1529
+ 1
1530
+ end
1531
+ end
1496
1532
 
1497
1533
  Solution( :tag ) do
1498
1534
  def solution
1499
- 1
1535
+ meth + 1
1500
1536
  end
1501
1537
  end
1502
1538
  Solution( :tag ) do
1503
1539
  def solution
1504
- 2
1540
+ meth + 2
1505
1541
  end
1506
1542
  end
1507
1543
  Solution( :tag ) do
1508
1544
  def solution
1509
- 3
1545
+ meth + 3
1510
1546
  end
1511
1547
  end
1512
1548
 
@@ -1586,18 +1622,97 @@ The important thing to remember here is that #String() is a method now. We can r
1586
1622
 
1587
1623
  For more on this see, the rspec files and the Jackbox blog at <a href="http://jackbox.us">http://jackbox.us</a>.
1588
1624
 
1589
- #### #reclass?(klass)
1625
+ #### #constructor?/#reclass?(klass)
1590
1626
 
1591
1627
  This helper verifies a certain re-class exists within the current namespace. It returns a boolean. Example:
1592
1628
 
1593
1629
  module One
1594
- if reclass? String
1630
+ if constructor? String
1595
1631
  String('our string')
1596
1632
  end
1597
1633
  end
1598
1634
 
1635
+ __5. The Web Widget Pattern.__
1636
+ This example uses Jackbox Ruby Traits to render web controls. There are a couple of different variations possible which we show in the rspec files. Here we'll use the one based on JITI. Here is the code:
1637
+
1638
+ # some data
1639
+
1640
+ def database_content # could be any model
1641
+ %{car truck airplane boat}
1642
+ end
1643
+
1644
+ # rendering helper controls
1645
+
1646
+ class MyWidget
1647
+ def initialize(content)
1648
+ @content = content
1649
+ end
1650
+
1651
+ def render
1652
+ "<div id='MyWidget'>#{@content}</div>"
1653
+ end
1654
+ end
1655
+
1599
1656
 
1600
- For more information and additional examples see the rspec examples on this project. There you'll find a long list of over __250__ rspec examples and code showcasing features of Jackbox Injectors along with some additional descriptions.
1657
+ MainFace = trait :WidgetFace do # our trait
1658
+
1659
+ attr_accessor :font, :width, :height
1660
+
1661
+ def dim(width, heigth)
1662
+ @width, @heigth = width, heigth
1663
+ end
1664
+
1665
+ def render
1666
+ %{
1667
+ <style>
1668
+ #MyWidget{
1669
+ font: 14px, #{@font};
1670
+ width:#{@width};
1671
+ height: #{@heigth}
1672
+ }
1673
+ </style>
1674
+ #{super()}
1675
+ }
1676
+ end
1677
+ end
1678
+
1679
+ # somewhere in a view
1680
+
1681
+ browser = 'Safari' # the user selected media
1682
+ @content = database_content
1683
+
1684
+
1685
+ my_widget = case browser
1686
+ when match(/Safari|Firefox|IE/)
1687
+
1688
+ MyWidget.new(@content).enrich(WidgetFace() do
1689
+
1690
+ def render # override invoking JIT inheritance
1691
+ dim '600px', '200px' # normal inherited method call
1692
+ @font = 'helvetica'
1693
+
1694
+ super()
1695
+ end
1696
+ end)
1697
+
1698
+ else
1699
+
1700
+ MyWidget.new(@content).enrich(WidgetFace() do
1701
+
1702
+ def render # override invoking JIT inheritance
1703
+ dim '200px', '600px' # normal inherited method call
1704
+ @font ='arial'
1705
+
1706
+ super()
1707
+ end
1708
+ end)
1709
+
1710
+ end
1711
+
1712
+ WidgetFace(:implode)
1713
+
1714
+
1715
+ For more information and additional examples see the rspec examples on this project. There you'll find a long list of over __250__ rspec examples and code showcasing features of Jackbox Trait Injectors along with some additional descriptions.
1601
1716
 
1602
1717
  ---
1603
1718
  ## Additional Tools
@@ -1627,28 +1742,13 @@ With Abstract the code goes like this:
1627
1742
  expect{Velocity.new}.to_not raise_error
1628
1743
  Velocity.new.speed.should == 35
1629
1744
 
1630
-
1631
- With Prefs you can add persistent properties to a class. These properties persist even through program termination. Here is the example code:
1632
-
1633
- module Jester
1634
- extend Prefs
1635
-
1636
- pref :value => 10
1637
- end
1638
-
1639
- Jester.value.should == 10
1640
- Jester.value = 3
1641
- Jester.value.should == 3
1642
- Jester.reset :value
1643
- Jester.value.should == 10
1644
-
1645
1745
  There is also command line utility called **jackup** that simply allows users to bring projects up to a *"Jackbox level"*. It inserts the right references and turns the targeted project into a bundler gem if it isn't already one also adding a couple of rake tasks.
1646
1746
 
1647
1747
  ## Availability
1648
1748
 
1649
1749
  Jackbox is current available for Linux, Mac, and Windows versions of Ruby 1.9.3 thru 2.2.1
1650
1750
 
1651
- ## Installation
1751
+ ## Installation and Testing
1652
1752
 
1653
1753
  Add this line to your application's Gemfile:
1654
1754
 
@@ -1664,8 +1764,11 @@ Or install it yourself as:
1664
1764
 
1665
1765
  And then execute the following command inside the project directory:
1666
1766
 
1667
- $jackup
1767
+ $ jackup
1768
+
1769
+ To run the test suite inside the gem directory, run the command test-jacks. This will run the test battery using rspec with Jackbox isolated within the confines of its own gem directory.
1668
1770
 
1771
+ $ test-jacks
1669
1772
 
1670
1773
 
1671
1774
  ## Support
@@ -1690,5 +1793,5 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
1690
1793
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1691
1794
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1692
1795
 
1693
- In the above copyright notice, the letters LHA are the english acronym
1796
+ In the above copyright notice, the letters LHA are the English acronym
1694
1797
  for Luis Enrique Alvarez (Barea) who is the author and owner of the copyright.