jackbox 0.9.6.3 → 0.9.6.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NmRkNTJhNTE1MDYyNjZhNjg3OTJlNzc5NTRiNzU3ZDVhNmE4ZTMzZA==
5
- data.tar.gz: !binary |-
6
- NTJlZWM5NGJkZTA2YzA3NWJlY2RmMDY3NTc0NzE3ODJmODg3MzgzMw==
2
+ SHA1:
3
+ metadata.gz: 3ad85cf59fad7a3d581d62ed26afd80a2c753552
4
+ data.tar.gz: 4aeaaa6afe5317d230af20778946e574ad5d730b
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- YTc1M2M2MGZjMzIzMDMxYWExMzgyNGU2MjQyYTMxZGZiMzNiZGJmMWU3ZmEx
10
- N2M4YTg5ZmU5MWFlM2NhMzUxOTNlZWU5OTM1NGRjZTE5MTM0MjIzNjQ4Mjc4
11
- NDdmM2M1ZDc4YzFjM2QyODY5NWJmMzQzOWFhNjczZmYwMjdjZTI=
12
- data.tar.gz: !binary |-
13
- MzE0MGI3Zjg1ZGRmNzNjZTM0OTM0MGZlMGI3YTgzOTMwNjI5MjQzOTllMDVk
14
- ZjQ0NjJmYWYzYWU3NDZkMWM5NTliNjA1NWI2MzZlZDQ2ZGFkNjg5NTZjMDU5
15
- YmUxNmUxZDBmNjdkMzYwM2QzZWExMjhjOGE4MmFkNDNhMjg0NWY=
6
+ metadata.gz: 869801891af0e806593ab8561ae88cfb0c0935a5a2649204bfdab4c7a2e83f50e77159e56558dc4c58ca03c74b2078b99dce2e171d0e0556b6ad35e1fcfb21ac
7
+ data.tar.gz: ff8ecf25e9e2da21ef8f70588abb06a0d9d6f2b372720923acce2f3108092a074b5f22f69e2812c14e509d1b25e1165dbdc94e6df77fe306dff7041a85132769
data/CHANGES.txt CHANGED
@@ -1,6 +1,42 @@
1
- 0.9.6.3 RC2
1
+ 0.9.6.5 RC3
2
+
3
+ . Established The Rules of JITI
4
+ . Changed the focus from Injector based to a Trait metaphor.
5
+ . Fixed inheritance problem with multiple same tag ancestors.
6
+ . Masked out external module intrusion
7
+ . Ironed out some problems with external ancestor masking
8
+ . can only base it on external modules
9
+ . but cannot intrude further down the chain
10
+ . Cleaned up and split up inheritance examples.
11
+ . All Tests passing
12
+ . broke down *some* of the long examples to please those who complained
13
+ (this was originally done because it was easier for testing trait life-cycles)
14
+ . Resolved JITI Decorators to error out in case of colluding ancestors
15
+ . Refactored #decorate once more
16
+ . Trimmed down directory structure
17
+ . Cleaned up examples and code
18
+ . FIXED issues with VMC under Ruby 1.9 and 2.0 related to flat_map underlying flatten bug
19
+ . Changed "fixed" a couple of #metaclass issues where:
20
+ . #root was getting defined multiple times
21
+ . errors were preventing the class from being returned
22
+ . also changed how the #metaclass? test is done
23
+ . Moved basic #dup and #clone to Object to avoid constant redefinitions
24
+ . Based Injector on Abstract to avoid normal instantiation
25
+ . Done fixing some issues with the #with construct:
26
+ . was obliterating target definitions of #method_missing on return. Now uses #decorate
27
+ . #decorate was suppressing to many warnings with long #suppress_warnings block
28
+ . now handles #with a, b, c ... do
29
+ . dropped *params to #with
30
+ . Fixed an issue with Equality
31
+ . Fixed #undecorate for object instances
32
+ . Brought in some re-class fixes
33
+ . Changed ReadMe
34
+ . Fixed ejection of inherited tags
35
+ . when you eject the original all inherited tags go with it
36
+ . Cleaned up comments
2
37
 
3
- Cleaned up inheritance, vmc, and patterns examples.
38
+
39
+ 0.9.6.3 RC2
4
40
 
5
41
  . Got re-classes working with rspec 3.4
6
42
  . Changed the jack #inspect method. Cleaned up examples
@@ -97,8 +133,6 @@ Cleaned up inheritance, vmc, and patterns examples.
97
133
  . Started work on optimizations
98
134
  . Added sym_for object method to Module class
99
135
  . Solved automatic tag tracing
100
- . Override module_eval to work with functor(&code)
101
- . added functor definition
102
136
  . Added the notion of equality to Injectors
103
137
  . Reworked examples to:
104
138
  . comply with new equality operators
data/LICENSE.lic CHANGED
Binary file
data/README.md CHANGED
@@ -16,39 +16,54 @@ Copyright © 2014, 2015 LHA. All rights reserved.
16
16
  <a href="http://jackbox.us"><h1>Jackbox</h1></a>
17
17
 
18
18
  ---
19
- <h2 style="font-family:Impact">Ruby Modular Closures, Code Injectors, Re-Classes and other programmer morphins</h2>
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 single and powerful 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 functions at this time take this idea and materialize it around the concepts of code injectors, re-classes, 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 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.
22
22
 
23
- To make it easier to grasp, code injectors can perhaps be thought of as a form of **Modular Closures©** or of **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) the presence of injector code in targets with mechanisms involving injector ejection and directives. They give your code the ability to capture its 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 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.
24
24
 
25
- Re-classes 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 injectors and helper functions, re-classes can be refined multiple times. Capabilities can be added and removed in blocks. Moreover, these re-classes acquire introspecting abilities. A re-class can be tested for existence, can tell you what injectors it uses, and finally can be overridden with a more relevant one.
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.
26
26
 
27
- 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 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 to resolve methods in more ways than regular Ruby modules can through the use of the VMC (Virtual Method Cache). See below.
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.
28
28
 
29
- Finally, we also present for the first time in our history the concept of Just-In-Time Inheritance. This is a feature which allows the introduction 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 injectors can be built with the use of this and the previous versioned inheritance, and can be readily applicable to any target.
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, to protect our intellectual property. But, as our business model evolves we will be considering open sourcing it. Our guiding principle through out it all has been keeping new constructs to a minimum. We take an outer minimalistic approach that in reality takes a lot more behind the scenes to make things work. Simplicity takes a lot. 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 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.
32
+
33
+ Advantages Of Trait Based Programming
34
+ ------------------------------------
35
+ * 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
+ * With Traits runtime versioning is possible and traits can be upgraded with new versions of the trait.
39
+ * Traits enable new and different coding patterns.
40
+
41
+ ---
32
42
 
33
43
  Basic Methods
34
44
  --------------------------
35
45
  There are some basic methods to Jackbox. These are just rudimentary helpers, which in effect appear to be a form of syntax sugar for every day things. But, behind their apparent sugar coating lie some additional capabilities as shown the deeper you delve into Jackbox. For more on them read the following sections, but their preliminary descriptions follow here:
36
46
 
37
47
  #### #decorate :sym, &blk
38
- This method allows for decorations to be placed on a single method, be it an instance or class method without too much fuss. One important thing about #decorate is that it works like #define_method, but in addition, it also makes possible the use of Ruby's #super within the body of the decorator. It really presents a better alternative and can be used instead of #alias\_method\_chain.
48
+ This method allows for decorations to be placed on a single method whether an instance or class method without too much fuss. One important thing about #decorate is that it works like #define_method, but in addition, it also makes possible the use of Ruby's #super within the body of the decorator. It really presents a better alternative and can be used instead of #alias\_method\_chain.
39
49
 
40
50
  At the class level:
41
-
51
+
52
+ class One
53
+ def foo
54
+ 'foo'
55
+ end
56
+ end
57
+
42
58
  class One
43
59
  decorate :foo do
44
60
  super() + 'decoration ' # super available within decoration
45
61
  end
46
62
  end
47
63
 
48
- One.new.foo
49
- #=> foo decoration
64
+ One.new.foo.should == 'foo decoration'
50
65
 
51
- Or, at the object level:
66
+ Or, at the instance level:
52
67
 
53
68
  one = One.new
54
69
 
@@ -56,43 +71,40 @@ Or, at the object level:
56
71
  super() + arg # again the use of super is possible
57
72
  end
58
73
 
59
- one.foo('after')
60
- #=> foo decoration after
74
+ one.foo('after').should == 'foo decoration after'
61
75
 
62
76
  It also works like so:
63
77
 
64
- Object.decorate :inspect do
65
- puts super() + " is your object"
78
+ Object.decorate :to_s do
79
+ super() + " is your object"
66
80
  end
67
-
68
- Object.new.inspect
69
- #=> #<Object:0x00000101787e20> is your object
81
+
82
+ Object.new.to_s.should match(/is your object/)
70
83
 
71
84
 
72
85
  #### #with obj, &blk
73
- 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.
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.
74
87
 
75
88
  Here is some sample usage code:
76
89
 
77
90
  class One
78
- def foo(arg)
91
+ def meth1(arg)
79
92
  'in One ' + arg
80
93
  end
81
94
  end
82
95
 
83
96
  class Two
84
- def faa(arg)
97
+ def meth2(arg)
85
98
  'and in Two ' + arg
86
99
  end
87
100
  def meth
88
101
  with One.new do # context of One and Two available simultaneously!!!
89
- return foo faa 'with something'
102
+ return meth1 meth2 'with something'
90
103
  end # return object
91
104
  end
92
105
  end
93
106
 
94
- Two.new.meth
95
- #=> 'in One and in Two with something'
107
+ Two.new.meth.should == 'in One and in Two with something'
96
108
 
97
109
  Use it to define function:
98
110
 
@@ -132,7 +144,7 @@ Use it with **#decorate** on singleton classes like this:
132
144
 
133
145
 
134
146
  #### #lets sym=nil, &blk
135
- We could say, this is simple syntax sugar. It adds readability to some constructs. 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 termed Re-Classes. 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 in spirit, aside it use with respect to re-classes, #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 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:
136
148
 
137
149
  To define local functions/lambdas. Define symbols in local scope:
138
150
 
@@ -157,59 +169,59 @@ Can be used to define a special values or pseudo-immutable strings:
157
169
  lets(:faa){ 'some important string' }
158
170
 
159
171
 
160
- Injectors
172
+ Traits/Injectors
161
173
  ----------
162
- Injectors are the main tool in Jackbox at the time of this writing. These again are a form of mix-in that has 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 Injectors, as well as elaborate on some of the other properties of injectors. But, it is essential to understand there are some syntactical differences to Injectors with respect to regular modules. We will show them first, with some examples:
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:
163
175
 
164
- **INJECTORS ARE DECLARED IN THE FOLLOWING WAYS:**
176
+ **TRAITS/INJECTORS ARE DECLARED IN THE FOLLOWING WAYS:**
165
177
 
166
178
 
167
- injector :name
179
+ trait :name
168
180
 
169
181
  # or...
170
182
 
171
- Name = injector :name
183
+ Name = trait :name
172
184
 
173
185
  # or even ...
174
186
 
175
- facet :Name # capitalized method, using alias #facet
187
+ injector :Name # capitalized method, using alias #trait
176
188
 
177
189
 
178
- 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 injector directives.
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.
179
191
 
180
192
  # somewhere in your code
181
- include Injectors
193
+ include Traits
182
194
 
183
- injector :my_injector # define the injector
195
+ trait :my_trait # define the trait
184
196
 
185
- my_injector do
197
+ my_trait do
186
198
  def bar
187
199
  :a_bar
188
200
  end
189
201
  end
190
202
 
191
203
  # later on...
192
- widget.enrich my_injector # apply the injector
204
+ widget.extend my_trait # apply the trait
205
+
193
206
  widget.bar
194
207
  # => bar
195
208
 
196
209
  # or...
197
210
 
198
- Mine = my_injector
211
+ Mine = my_trait
199
212
  class Target
200
- inject Mine # apply the injector
213
+ inject Mine # apply the trait
201
214
  end
202
215
 
203
216
  Target.new.bar
204
217
  # => bar
205
218
 
206
- # etc ...
207
219
 
208
- **INJECTORS HAVE PROLONGATIONS:**
220
+ **TRAITS/INJECTORS HAVE PROLONGATIONS:**
209
221
 
210
- injector :my_injector
222
+ trait :my_trait
211
223
 
212
- my_injector do # first prolongation
224
+ my_trait do # first prolongation
213
225
 
214
226
  def another_method
215
227
  end
@@ -218,119 +230,115 @@ Their use and semantics are somewhat defined by the following snippet. But, to
218
230
 
219
231
  # ...
220
232
 
221
- my_injector do # another prolongation
233
+ my_trait do # another prolongation
222
234
 
223
235
  def yet_another_method
224
236
  end
225
237
 
226
238
  end
227
239
 
228
- These prolongations become versions when they are applied or when they are tagged. See Tagging.
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.
229
241
 
230
- #### #injector :sym
231
- This is a global function. It defines an object of type Injector with the name of symbol. Use it when you want to generate an Injector object for later use. The symbol can then be used as a handle to the injector whenever you need to prolong the injector by adding methods to it or apply it to another object to create a version. Additionally, this symbol plays a role in defining the injector's scope. Injectors with capitalized names like :Function, :Style, etc have a global scope. That is they are available throughout the program:
242
+ #### #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:
232
244
 
233
245
  class A
234
- injector :Function
246
+ trait :Function # defined
235
247
  end
236
248
 
237
249
  class B
238
- include Function()
250
+ include Function() # applied
239
251
  end
240
252
 
241
- # This is perfectly valid with injectors
253
+ # This is perfectly valid with traits
242
254
 
243
- On the other hand Injectors with a lower case name are only available __from__ the scope in which they were defined, like the following example shows:
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:
244
256
 
245
- class AA
246
- injector :form
257
+ class A
258
+ trait :form
247
259
  end
248
260
 
249
- class BB
261
+ class B
250
262
  include form # This genenerates and ERROR!
251
263
  end
252
264
 
253
- class BB
254
- include AA.form
265
+ class B
266
+ include A.form # This is valid however!
255
267
  end
256
268
 
257
- # This is also perfectly valid with injectors
258
269
 
259
- 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 injectors you may want to use them instead depending on context to make clear your intent. Also inject is public while include is not.
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.
260
271
 
261
- #### #include/inject *jack
262
- This method is analogous to ruby's #include but its use is reserved for Injectors. The scope of this method is the same as the scope of #include, and its intended use like include's is for class definitions. Use it to "include" an Injector into a receiving class. Takes multiple injectors.
272
+ #### #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.
263
274
 
264
- #### #extend/enrich *jack
265
- 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 an injector. Takes multiple injectors.
275
+ #### #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.
266
277
 
267
- **IMPORTANT NOTE: Injector lookup follows the method and not the constant lookup algorithm.**
278
+ **IMPORTANT NOTE: Trait Injector lookup follows the method and not the constant lookup algorithm.**
268
279
 
269
280
  If you need to follow constant lookup, here is the code for that:
270
281
 
271
- Name = injector :sym .... # this also creates a hard tag (see below)
282
+ Name = trait :sym .... # this also creates a hard tag (see below)
272
283
 
273
- ### Injector Versioning
284
+ ### Trait/Injector Versioning
274
285
 
275
- One of the most valuable properties of injectors is Injector Versioning. Versioning is the term used to identify a feature in the code that produces an artifact of injection which contains a certain set of methods with their associated outputs, and represents a snapshot of that injector up until the point it's applied to an object. From, that point on the object contains only that version of methods from that injector, and any subsequent overrides to those methods are only members of the "prolongation" of the injector and do not become part of the object of injection unless some form of re-injection occurs. Newer versions of an injector's methods only become part of newer objects or newer injections into existing targets. With Jackbox Injector Versioning two different versions of the same code object can be running simultaneously.
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.
276
287
 
277
288
  We'll use some examples to illustrate the point. This is how versioning occurs:
278
289
 
279
- # injector declaration
290
+ # trait declaration
280
291
  #___________________
281
- injector :my_injector do
292
+ trait :my_trait do
282
293
  def bar
283
- :a_bar # version bar.1
294
+ :a_bar # version bar.1
284
295
  end
285
296
  def foo
286
297
  # ...
287
298
  end
288
299
  end
289
300
 
290
- object1.enrich my_injector # apply the injector --first snapshot
291
- object1.bar.should == :a_bar # pass the test
301
+ object1.extend my_trait # apply the trait --first snapshot
302
+ object1.bar.should == :a_bar # pass the test
292
303
 
293
- # injector prolongation
304
+ # trait prolongation
294
305
  #__________________
295
- my_injector do
306
+ my_trait do
296
307
  def bar
297
- :some_larger_bar # version bar.2 ... re-defines bar
308
+ :some_larger_bar # version bar.2 ... re-defines bar
298
309
  end
299
310
  # ...
300
311
  end
301
312
 
302
- object2.enrich my_injector # apply the injector --second snapshot
303
- object2.bar.should == :some_larger_bar
304
-
305
- # result
306
-
307
- object1.bar.should == :a_bar # bar.1 is still the one
313
+ object2.extend my_trait # apply the trait --second snapshot
314
+ object2.bar.should == :some_larger_bar # pass the test
308
315
 
309
316
  ###############################################
310
317
  # First object has kept its preferred version #
311
318
  ###############################################
319
+
320
+ object1.bar.should == :a_bar # bar.1 is still the one
312
321
 
313
322
 
314
- When re-injection occurs, and only then does the new version of the #bar method come into play. But the object remains unaffected otherwise, keeping its preferred version of methods. The new version is available for further injections down the line and to newer client code. Internal local-binding is preserved. If re-injection is executed then clients of the previous version get updated with the newer one. Here is the code:
323
+ When trait re-injection occurs, and only then does the new version of the #bar method come into play. But the object remains unaffected otherwise, keeping its preferred version of methods. The new version is available for further injections down the line and to newer client code but existing targets are untouched. Internal local-binding is also preserved. If a trait is then re-injected on an instance only then does the instance get updated with the newer version. Here is the code:
315
324
 
316
325
  # re-injection
317
326
  #_________________
318
- object1.enrich my_injector # re-injection --third snapshot
319
-
320
- object1.bar.should == :some_larger_bar # bar.2 now available
327
+ object1.extend my_trait # re-injection --third snapshot
321
328
 
322
329
  ###############################################
323
330
  # First object now has the updated version #
324
331
  ###############################################
325
332
 
333
+ object1.bar.should == :some_larger_bar # bar.2 now available
326
334
 
327
- Re-injection on classes is a little bit trickier. Why? Because class injection should be more pervasive --we don't necessarily want to be redefining a class at every step. To re-inject a class we must use the Strategy Pattern (see below) or use a private update. See the sections below as well as the rspec files for more on this.
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.
328
336
 
329
337
  Here is an example of Injector Versioning as it pertains to classes:
330
338
 
331
- # injector declaration:
339
+ # trait declaration:
332
340
  #___________________
333
- injector :Versions do
341
+ trait :Versions do
334
342
  def meth arg # version meth.1
335
343
  arg ** arg
336
344
  end
@@ -340,7 +348,7 @@ Here is an example of Injector Versioning as it pertains to classes:
340
348
  inject Versions() # apply --snapshot
341
349
  end
342
350
 
343
- # injector prolongation:
351
+ # trait extension:
344
352
  #_________________
345
353
  Versions do
346
354
  def meth arg1, arg2 # version meth.2 ... redefines meth.1
@@ -352,14 +360,13 @@ Here is an example of Injector Versioning as it pertains to classes:
352
360
  inject Versions() # apply --snapshot
353
361
  end
354
362
 
355
- # result
363
+ ##############################################
364
+ # Two different trait versions coexisting #
365
+ ##############################################
356
366
 
357
- Two.new.meth(2,4).should == 8 # meth.2
358
367
  One.new.meth(3).should == 27 # meth.1
368
+ Two.new.meth(2,4).should == 8 # meth.2
359
369
 
360
- ##############################################
361
- # Two different injector versions coexisting #
362
- ##############################################
363
370
 
364
371
  To update the class, we then do the following:
365
372
 
@@ -367,19 +374,19 @@ To update the class, we then do the following:
367
374
  update Versions() # private call to #update
368
375
  end
369
376
 
370
- One.new.meth(2,4).should == 8 # meth.2
371
- Two.new.meth(2,4).should == 8 # meth.2
372
-
373
377
  ##############################################
374
378
  # class One is now updated to the latest #
375
379
  ##############################################
380
+
381
+ One.new.meth(2,4).should == 8 # meth.2
382
+ Two.new.meth(2,4).should == 8 # meth.2
376
383
 
377
384
 
378
385
  ### Tagging/Naming
379
386
 
380
- The use of Tags is central to the concept of Injector Versioning. Tagging happens in the following ways:
387
+ The use of Tags is central to the concept of Versioning. Tagging happens in the following ways:
381
388
 
382
- Version1 = jack :function do
389
+ Version1 = trait :function do
383
390
  def meth arg
384
391
  arg
385
392
  end
@@ -394,28 +401,27 @@ The use of Tags is central to the concept of Injector Versioning. Tagging happe
394
401
  end
395
402
  end
396
403
 
397
- Version1 and Version2 are two different hard versions/tags of the same Injector. They introduce a more formal incantation of injector versioning and also pave the way for Versioned Inheritance as described in the introduction. More on this below. Aside from these hard tags, there are also soft tags (see below).
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).
398
405
 
399
406
  ### Local Binding
400
407
 
401
- Before we move on, we also want to give some further treatment to injector local-binding. That is, the binding of an injectors' methods is local to the prolongation/version in which they are located before the versioning occurs. Here, is the code:
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:
402
409
 
403
- **Note: In the following examples we use the notion of version naming/tagging. This allows you to tag different versions/prolongations of an Injector for later use. Once a version is tagged it shouldn't be modified**
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.**
404
411
 
405
412
 
406
- # injector declaration
413
+ # trait declaration
407
414
  #_____________________
408
415
 
409
- Version1 = injector :functionality do
416
+ Version1 = trait :functionality do
410
417
  def basic arg # version basic.1
411
418
  arg * 2
412
419
  end
413
420
  end
421
+ o = Object.new.extend Version1 # apply --snapshot (like above)
414
422
 
415
- o = Object.new.enrich Version1 # apply --snapshot (like above)
416
- o.basic(1).should == 2 # basic.1
417
423
 
418
- # injector prolongation
424
+ # trait prolongation
419
425
  #_____________________
420
426
 
421
427
  Version2 = functionality do
@@ -427,54 +433,57 @@ Before we move on, we also want to give some further treatment to injector local
427
433
  basic(3) + 2
428
434
  end
429
435
  end
436
+ p = Object.new.extend Version2 # apply --snapshot (like above)
430
437
 
431
- p = Object.new.enrich Version2 # apply --snapshot (like above)
438
+ ####################################################
439
+ # #compound.1 bound to the right version #basic.2 #
440
+ ####################################################
441
+
432
442
  p.basic(1).should == 3 # basic.2
433
443
  p.compound.should == 11 # compound.1 --bound locally to basic.2
434
444
 
435
445
  o.basic(1).should == 2 # basic.1
436
446
  o.compound.should == 11 # compound.1 --bound locally to basic.2
437
447
 
438
- ####################################################
439
- # #compound.1 bound to the right version #basic.2 #
440
- ####################################################
441
-
442
448
 
443
449
  ### Virtual Method Cache (VMC)
444
450
 
445
- When you are working with an Injector in irb/pry it is often easier to just add methods to the injector without actually having to re-apply the injector to the the target to see the result. This is just what the Jackbox Virtual Method Cache is for among other things. Here is what the code looks like:
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:
446
452
 
447
453
  # definition
448
- facet :SpecialMethods
454
+ #_______________
455
+ trait :SomeTrait
449
456
 
457
+ # application
458
+ #_______________
450
459
  class MyClass
451
- include SpecialMethods # application with no methods
460
+ include SomeTrait # Application (with no methods)
452
461
  end
453
462
 
454
463
  obj = MyClass.new
455
464
 
456
- SpecialMethods do
457
- def spm1 # spm1 is only defined in the virtual cache
465
+ SomeMethods do
466
+ def spm1 # #spm1 is only defined in the virtual cache
458
467
  :result # It is not actually part of the class yet!!
459
468
  end # until this version/prolongation is applied
460
469
  end
461
470
 
462
471
  expect(obj.spm1).to eq(:result) # yet my obj can use it --no problem
463
472
 
464
- The key idea here is that the virtual method cache is the same for all versions of the Injector and all its applications. If we redefine those 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. The VMC also provides extended method resolution to the modular closure mix-in. To understand what we mean by that take a look at following code:
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:
465
474
 
466
475
  class Client
467
- include jack :J1
476
+ include trait :J1
468
477
  end
469
478
  J1 do
470
479
  def n1m1
471
480
  end
472
- include facet :K1
481
+ include trait :K1
473
482
  end
474
483
  K1 do
475
484
  def n2m1
476
485
  end
477
- include facet :L1
486
+ include trait :L1
478
487
  end
479
488
  L1 do
480
489
  def n3m1
@@ -488,11 +497,13 @@ The key idea here is that the virtual method cache is the same for all versions
488
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.
489
498
 
490
499
  #### #define\_method sym, &blk
491
- There is one more interesting property to method definition on Injectors however. The use of #define\_method to re-define methods in any prolongation updates the entire injector and all its versions. This also preserves a fundamental tenet of injectors: take some local context, enclose it, and use the injector to introduce it to some indiscriminate target, and additionally has some other uses as we'll see with in our description of patterns and injector composition.
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.
492
501
 
493
502
  Here is an example of the difference with #define\_method:
494
503
 
495
- facet :some_facet do
504
+ # define trait
505
+ #_________________________
506
+ trait :some_trait do
496
507
  def meth
497
508
  :meth
498
509
  end
@@ -502,147 +513,200 @@ Here is an example of the difference with #define\_method:
502
513
  end
503
514
  end
504
515
 
505
- class Client ################################
506
- inject some_facet # Injector appplied #
507
- end # #
508
- ################################
516
+ class Client
517
+ inject some_trait # Injector appplied
518
+ end
519
+
520
+ # test it
521
+
509
522
  Client.new.meth.should == :meth
510
523
  Client.new.foo_bar.should == 'a foo and a bar'
511
524
 
512
-
513
- some_facet do
514
- def meth # New version
525
+ # new prolongation
526
+ #________________________
527
+ some_trait do
528
+ def meth
515
529
  puts :them
516
530
  end
517
531
 
518
- define_method :foo_bar do # New version
532
+ define_method :foo_bar do # new method version
519
533
  'fooooo and barrrrr'
520
534
  end
521
535
  end
522
- ################################
523
- # Like above! #
524
- Client.new.meth.should == :meth # No re-injection == No change #
525
- ################################
526
-
527
- ################################
528
- Client.new.foo_bar.should == # Different!!! #
529
- 'fooooo and barrrrr' # No re-injection == Change #
530
- # . Thanks to define_method #
531
- ################################
532
-
533
- Injector Versioning together with injector local-binding allow the metamorphosis of injectors to fit the particular purpose at hand and keeping those local modifications isolated from the rest of your program making your code to naturally evolve with your program.
534
-
535
- ### Injector introspection
536
- Injectors have the ability to speak about themselves. Moreover injectors can speak about their members just like any module or class, and can also inject their receivers with these introspecting capabilities. Every injected/enriched object or module/class can enumerate its injectors, and injectors can enumerate their members, and so forth.
537
-
538
- injector :Function do
536
+
537
+ ################################
538
+ # Like above! #
539
+ # No re-injection == No change #
540
+ ################################
541
+
542
+ Client.new.meth.should == :meth
543
+
544
+ ################################
545
+ # Different!!! #
546
+ # No re-injection == Change #
547
+ # . Thanks to define_method #
548
+ ################################
549
+
550
+ Client.new.foo_bar.should == 'fooooo and barrrrr'
551
+
552
+
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
556
+ 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
+
558
+ trait :Function do
539
559
  def far
540
560
  end
541
561
  def close
542
562
  end
543
563
  end
544
564
 
545
- injector :Style do
565
+ trait :Style do
546
566
  def pretty
547
567
  end
548
568
  end
549
569
 
550
- class Target
551
- inject Function(), Style()
570
+ class Parent
571
+ inject Function()
552
572
  end
553
573
 
554
- # class ?
574
+ class Child < Parent
575
+ inject Style()
576
+ end
577
+
578
+ # a trait's class
555
579
 
556
580
  Function().class.should == Injector
557
581
  Style().class.should == Injector
558
582
 
559
- #### #injectors *sym
560
- Called with no arguments returns a list of injectors. A call with a list of injector symbols however returns an array of actual Injector objects matching the names supplied in a LIFO fashion. An example use goes like this:
583
+ Injector == Trait
561
584
 
562
- # injectors (in this target) ?
585
+ # traits methods
563
586
 
564
- Target.injectors
565
- => [(#944120:|Function|), (#942460:|Style|)]
587
+ Function().instance_methods.should == [:far, :close]
588
+ Style().instance_methods.should == [:pretty]
589
+
590
+ # later on...
591
+
592
+ Child.eject *Child.traits
593
+
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:
596
+
597
+ #traits --(in this target)
566
598
 
567
- # injectors :name ?
599
+ Child.traits
600
+ => [(|Style|:#942460)]
568
601
 
569
- Target.injectors :Function
570
- => [(#944120:|Function|)] # same as #injectors.collect_by_name :name
571
-
572
- Target.injectors :all # all injectors in this class's hierarchy
573
- (see section on Inheritance)
574
-
575
- The method also extends into a minuscule API:
602
+ #traits :all --(all traits in hierarchy)
576
603
 
577
- Target.injectors.by_name.should == [:Function, :Style]
578
- # ...
579
- Target.injectors(:all).by_name
580
- # aliased to :sym_list
604
+ Child.traits :all
605
+ => [(|Function|:#944120), (|Style|:#942460)]
606
+
607
+ #traits *sym
581
608
 
582
- Target.injectors.collect_by_name :name # see above
583
- # ...
584
- Target.injectors(:all).collect_by_name :name
585
- # aliased to :all_by_sym
609
+ c = Child.new.extend Style()
610
+ c.traits :Style
611
+ => [(|Style|:#942460), (|Style|:#890234)]
612
+
586
613
 
587
- Target.injectors.find_by_name :Function # last one in first out
588
- => (#944120:|Function|)
589
- # ...
590
- Target.injectors(:all).find_by_name :name
591
- # aliased to last_by_sym
614
+ #traits.by\_name *sym --(names only)
592
615
 
593
- Function().instance_methods.should == [:far, :close]
594
- Style().instance_methods.should == [:pretty]
595
-
596
- # later on...
597
-
598
- # eject all injectors in target
599
- Target.injectors.each{ |j| Target.eject j }
616
+ Child.traits.by_name.should == [:Style]
617
+ Child.traits(:all).by_name.should == [:Function, :Style]
618
+ # also aliased to :sym_list
600
619
 
601
- # or..
620
+ #traits.collect\_by\_name *sym --(all #traits of :name, same as #traits *sym from above)
602
621
 
603
- Target.eject *Target.injectors
622
+ Child.traits.collect_by_name :Style
623
+ => [(|Style|:#942460)]
624
+
625
+ Child.traits :Style
626
+ => [(|Style|:#942460)]
627
+
628
+ Child.traits :Function
629
+ => nil
630
+
631
+ Child.traits :all, :Function
632
+ => [(|Function|:#944120)]
633
+
634
+ Child.traits(:all).collect_by_name :Function
635
+ => [(|Function|:#944120)]
636
+ # also aliased to :all_by_sym
637
+
638
+ #traits.find\_by\_name *sym --(highest ranking trait by :name)
604
639
 
640
+ Child.traits.find_by_name :Style # last one in first out
641
+ => (|Style|:#942460)
642
+
643
+ Child.traits.find_by_name :Function
644
+ =>nil
645
+
646
+ Child.traits(:all).find_by_name :Function
647
+ => (|Function|:#944120)
648
+ # aliased to last_by_sym
649
+
605
650
  #### #history alias #versions
606
- This method returns a trace of all the target hosted Injectors which is ordered based on the order in which they are created. It includes tags and soft tags which can be specifically accessed thru the #tags method below. Here is the code:
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:
607
652
 
608
- # create our injector
609
- injector :HistorySample
610
-
653
+ # create our trait and
611
654
  # host it a couple of times
612
- extend( HistorySample(), HistorySample() )
655
+
656
+ trait :HistorySample
657
+ extend HistorySample(), HistorySample()
613
658
 
614
659
  # expect the following
615
- expect(injectors).to eq(HistorySample().history)
660
+
616
661
  expect(HistorySample().history.size).to eq(2)
662
+ expect(traits).to eq(HistorySample().history)
663
+ expect(HistorySample().history.last).to eql(traits.last)
617
664
  expect(HistorySample().history.last).to eql(HistorySample())
618
- expect(HistorySample().history.last).to_not eq(HistorySample().spec)
619
665
 
620
- # create a tag
666
+ # create a hard tag
667
+
621
668
  HistorySampleTag = HistorySample()
622
669
 
623
670
  expect(HistorySample().history.size).to eq(3)
624
671
  expect(HistorySample().history.last).to equal(HistorySampleTag)
625
672
 
626
- #### #tags
627
- This method traces the tags only. Here is the code:
628
-
629
- # at this point from the above
630
- expect(HistorySample().tags.size).to eq(1)
673
+ # create a soft tags
631
674
 
632
675
  HistorySample(:tag) do
633
676
  # some definitions
634
677
  end
635
678
 
636
- # expect the following
637
- expect(HistorySample().tags.size).to eq(2)
679
+ expect(HistorySample().history.size).to eq(4)
680
+ expect(HistorySample().history.last).to equal(HistorySample())
681
+
682
+ #### #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:
638
684
 
639
- Take a look at the Transformers Pattern below for an application of this and also the Jackbox blog at <a href="http://jackbox.us">http://jackbox.us</a>
685
+ # at this point from the above...
686
+
687
+ #tags
688
+
689
+ expect(HistorySample().tags.size).to eq(3)
690
+
691
+ #tags.hard
692
+
693
+ expect(HistorySample().tags.hard.size).to eq(1)
694
+ HistorySample().tags.hard
695
+ => [(HistorySampleTag:|HistorySample|)]
696
+
697
+ #tags.soft
698
+
699
+ expect(HistorySample().tags.soft.size).to eq(2)
700
+ HistorySample().tags.hard
701
+ => [(|HistorySample|:#234435),(|HistorySample|:#876679)]
702
+
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.
640
704
 
641
705
  #### #precedent and #progenitor (alias #pre, #pro)
642
706
  The #pre method gets the previous element in the history. Here is the code:
643
707
 
644
- # create the injector
645
- injector :HistorySample
708
+ # create the trait
709
+ trait :HistorySample
646
710
 
647
711
  # create some history
648
712
  extend HistorySample(), HistorySample()
@@ -650,13 +714,14 @@ The #pre method gets the previous element in the history. Here is the code:
650
714
  # expect the following
651
715
  expect(HistorySample().history.last.precedent).to equal(HistorySample().history.first)
652
716
 
653
- The #pro method is a little different. It gets the version from which a particular injector was generated. This may not necessarily be the precedent. Take a look at the following code.
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.
654
718
 
655
- # create the injector
656
- injector :Progample
719
+ # create the trait
720
+ trait :Progample
657
721
 
658
722
  # expect the following
659
723
  expect(Progample().history).to be_empty
724
+ expect(Progample().precedent).to equal(Progample().spec)
660
725
  expect(Progample().progenitor).to equal(Progample().spec)
661
726
 
662
727
  # create some history
@@ -665,93 +730,215 @@ The #pro method is a little different. It gets the version from which a particu
665
730
  # expect the following
666
731
  expect(Progample().history.size).to eq(2)
667
732
  expect(Progample().history.first.progenitor).to equal(Progample().spec)
668
- expect(Progample().history.last.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:
669
738
 
739
+ Tag = Progample()
740
+ expect(Tag.pro).to equal(Progample().spec)
741
+
742
+ class A
743
+ inject Tag
744
+ end
745
+
746
+ expect(A.traits.first.pro).to equal(Tag)
747
+
670
748
  For more on this see the rspec files.
671
749
 
672
- ### Injector Equality and Difference
750
+ ### Trait/Injector Equality and Difference
673
751
 
674
- 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 test what the difference is. It only follows that if injectors can be applied and withdrawn from any target we should be able to test for their similarities to other injectors. Injector difference tests for the actual delta between injectors and returns and array with those differences. Here is how equality is defined:
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.
753
+
754
+ Here is how equality is defined:
675
755
 
676
756
  # equality
677
757
  ######################################
678
- E().should == E()
679
- E().should == E().spec
680
758
 
681
- E(:tag).should == E()
682
- if ETag1 = E()
683
- ETag1.should == E()
684
- end
685
- extend E()
686
- injectors.first.should == E()
759
+ E().should == E()
760
+ E().should == E().spec
761
+ E().should == E().pre
762
+
763
+ # if
764
+ ETag1 = E() # with no definitions
765
+ # then
766
+ E().should == ETag1 # same thing
767
+
768
+ # if
769
+ extend E() # with no definitions
770
+ # then
771
+ injectors.first.should == E() # same
772
+
773
+ # but
774
+ E().should == E() # always
775
+ E().should == E().spec
776
+ E(:tag).should == E()
777
+
778
+
779
+
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.
781
+
782
+ Here is how inequality is defined:
687
783
 
784
+ # inequality
785
+ ######################################
786
+
787
+ E().should_not == F()
688
788
 
689
- # ** definition **
789
+ # if some E () definitions **
690
790
  E() do
691
791
  def foo
692
792
  end
693
793
  end
694
- # ** definition **
695
794
 
795
+ # then (from above)
796
+ ETag1.should_not == E()
696
797
 
697
- # inequality
698
- ######################################
699
- E().should == E()
700
- ETag1.should_not == E()
701
- injectors.first.should_not == E()
798
+ # furthermore
799
+ traits.first.should_not == E()
702
800
 
703
- E(:tag).should == E()
704
- E().should_not == F()
801
+ # and
802
+ E().should_not == E().pre
705
803
 
804
+ # but
805
+ E().should == E() # always
806
+ E().should == E().spec
706
807
 
707
- Here is how difference is defined:
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:
708
809
 
709
- # Injector#diff returns an Array
710
- ######################################
711
- E().diff.class.should be(Array)
810
+ #### #trait.diff ver=nil
712
811
 
812
+ # difference
813
+ ##################################
713
814
 
714
- # equality in the converse expression
715
- ######################################
716
- E().diff(E()).should be_empty
815
+ #diff(ver=nil) --( The argument ver=nil defaults to the previous version )
816
+
817
+ E().diff.class.should be(Array)
717
818
 
718
- # because
719
- E().should == E() # like above
720
819
 
820
+ #diff.empty? --( Is the delta empty? The join could still exist (see below). )
821
+
822
+ E().diff.should be_empty
823
+
721
824
 
722
- # unless changed E()== E().pre or E().spec
723
- ######################################
724
- E().diff.should be_empty
825
+ # because
826
+ E().diff.delta.should be_empty
827
+ E().diff.join.should be_empty
828
+
829
+
830
+ # a tag to compare
831
+ ETag3 = E()
725
832
 
726
- # because
727
- E().diff.should == E().diff(E().pre)
728
- E().pre.should equal( E().spec )
729
- # and
730
- E().should == E().spec # like above
731
833
 
834
+ # if some E() definitions **
835
+ E do
836
+ def foo
837
+ end
838
+ end
732
839
 
733
- # unless there is a delta it cannot be loaded?
734
- ######################################
735
- E().diff.should_not be_loaded
736
840
 
737
- # because
738
- E().diff.delta.should be_empty
841
+ # E is changed so...
842
+ E().diff(ETag3).should_not be_empty
843
+
844
+
845
+ # because (like above)
846
+ ETag3.should_not == E()
847
+
848
+ # and
849
+
850
+
851
+ #diff.delta --( The difference in methods )
852
+
853
+ E().diff(ETag3).delta.should == [:foo]
854
+
855
+
856
+ #diff.loaded? --( Is there both a join and a delta? )
857
+
858
+ E().diff(ETag3).should_not be_loaded
859
+
860
+ # because
861
+
862
+
863
+ #diff.join --( The methods common to both )
864
+
865
+ E().diff(ETag3).join.should == []
866
+
867
+
868
+ # even though
869
+ E().diff(ETag3).delta.should == [:foo]
870
+
871
+
872
+ # furthermore
873
+ E().diff.should == [[], [:foo]]
874
+
875
+
876
+ # being that
877
+ E().diff.should eq( E().diff(E().precedent) )
878
+ # and
879
+ E().progenitor.should equal(E().spec)
880
+
881
+
882
+ #diff.join.injector
883
+ #diff.delta.injector
884
+
885
+ # a tag as precedent
886
+ ETag5 = E()
887
+
888
+
889
+ # if E() definitions **
890
+ E do
891
+ def foo
892
+ :foo
893
+ end
894
+ def bar
895
+ :bar
896
+ end
897
+ end
898
+
899
+
900
+ # then
901
+ E().diff.should_not be_empty
902
+
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)
910
+
911
+ # being that
912
+ E().diff.join.injector.instance_methods.should be_empty
913
+ E().diff.delta.injector.instance_methods.should_not be_empty
739
914
 
740
- # ....
915
+ # allows the following
916
+ class Incomplete
917
+ inject E().diff.delta.injector
918
+ end
919
+ # and
920
+ Incomplete.new.foo.should eq(:foo)
921
+
922
+ # being that
923
+ E().diff.delta.injector.should be_instance_of(Injector)
924
+ E().diff.delta.injector.should be_instance_of(Trait)
925
+
741
926
 
927
+
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.
742
929
 
743
930
  Again, for more on this see the rspec files.
744
931
 
745
- ### Injector composition
746
- The composition of multiple injectors into an object can be specified as follows:
932
+ ### Trait/Injector composition
933
+ The composition of multiple traits into an object can be specified as follows:
747
934
 
748
935
  include Injectors
749
936
 
750
- # declare injectors
751
- injector :FuelSystem # capitalized methods
752
- injector :Engines
753
- injector :Capsule
754
- injector :Landing
937
+ # declare traits
938
+ trait :FuelSystem # capitalized methods
939
+ trait :Engines
940
+ trait :Capsule
941
+ trait :Landing
755
942
 
756
943
  # compose the object
757
944
  class SpaceShip
@@ -759,12 +946,12 @@ The composition of multiple injectors into an object can be specified as follows
759
946
  inject FuelSystem(), Engines(), Capsule(), Langing() # capitalized method use
760
947
 
761
948
  def launch
762
- gas_tank fuel_lines burners ignition :go
949
+ gas_tank fuel_lines burners ignition :go # call through the VMC
763
950
  self
764
951
  end
765
952
  end
766
953
 
767
- Spaceship.injectors.by_name == [:FuelSystem, :Engines, :Capsule, :Landing]
954
+ Spaceship.traits.by_name == [:FuelSystem, :Engines, :Capsule, :Landing]
768
955
 
769
956
  # define functionality
770
957
  FuelSystem do
@@ -789,25 +976,30 @@ The composition of multiple injectors into an object can be specified as follows
789
976
  var = 'wheels'
790
977
 
791
978
  Landing do
792
- define_method :gear do # a clolsure !!
979
+ define_method :gear do # a closure of surrounding context
793
980
  var
794
981
  end
795
982
  end
983
+
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
796
985
 
797
986
  ### Inheritance
798
- The behavior of Injectors under inheritance is partially specified by what follows:
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.
988
+
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:
990
+
991
+ trait :j
799
992
 
800
- injector :j
801
-
802
993
  class C
803
994
  end
995
+
804
996
  C.inject j { #foo pre-defined at time of injection
805
997
  def foo
806
998
  'foo'
807
999
  end
808
1000
  }
809
- C.injectors.by_name.should == [:j]
810
- C.new.injectors.by_name.should == [:j]
1001
+ C.traits.by_name.should == [:j]
1002
+ C.new.traits.by_name.should == [:j]
811
1003
 
812
1004
  C.new.foo.should == 'foo'
813
1005
 
@@ -815,29 +1007,30 @@ The behavior of Injectors under inheritance is partially specified by what follo
815
1007
 
816
1008
  class D < C # methods are inherited from j
817
1009
  end
818
- D.injectors.by_name.should == []
819
- D.injectors(:all).by_name == [:j]
1010
+ D.traits.by_name.should == []
1011
+ D.traits(:all).by_name == [:j]
820
1012
 
821
1013
  # New Objects
822
1014
  C.new.foo.should == 'foo'
823
1015
  D.new.foo.should == 'foo'
824
1016
 
1017
+ For mote on this as also see the rspec files.
825
1018
 
826
- More importantly though is the following:
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:
827
1020
 
828
- facet :player do
1021
+ trait :player do
829
1022
  def sound
830
1023
  'Lets make some music'
831
1024
  end
832
1025
  end
833
1026
 
834
- TapePlayer = player do # TapePlayer Tag
1027
+ TapePlayer = player do # TapePlayer version tag
835
1028
  def play # --inherirts #sound
836
1029
  return 'Tape playing...' + sound()
837
1030
  end
838
1031
  end
839
1032
 
840
- CDPlayer = player do # CDPlayer Tag
1033
+ CDPlayer = player do # CDPlayer version tag
841
1034
  def play # --also inherits #sound
842
1035
  return 'CD playing...' + sound()
843
1036
  end
@@ -851,19 +1044,21 @@ More importantly though is the following:
851
1044
  end
852
1045
  end
853
1046
 
854
- # ...
1047
+ class JukeBox < BoomBox
1048
+ inject CDPlayer
1049
+ end
855
1050
 
856
-
857
- From all this, the important thing to take is that injectors provide a sort of versioned inheritance. The version inherits all of the pre-existing methods from the injector and freezes 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 injector until the time an update is made. Of course, there is always #define\_method. For more on all this see, the Rspec examples.
858
1051
 
859
- ### JIT inheritance
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.
860
1053
 
861
- 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 its super members as part of the call, just like you would with classes. In addition to the inheritance resulting from versioning, JIT inheritance presents a more complete scenario adding color to the picture painted by code injectors. The key takeaway here is this: Code Injectors are mix-ins that share a similar inheritance model with classes. You can version them to gain access to versioned inheritance or you can override its members to access an ancestor chain comprised of all previous tags. As always we will use some example code to illustrate:
1054
+ ### Just-In-Time Inheritance (JITI)
1055
+
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:
862
1057
 
863
1058
  #
864
- # Our Modular Closure
1059
+ # Our Trait
865
1060
  #
866
- Tag1 = jack :Tagger do
1061
+ Tag1 = trait :Functionality do
867
1062
  def m1
868
1063
  1
869
1064
  end
@@ -874,19 +1069,19 @@ This flavor of the inheritance model allows our modular closures to have similar
874
1069
  end
875
1070
 
876
1071
  #
877
- # Normal Versioned Injector inheritance
1072
+ # Normal versioned inheritance
878
1073
  #
879
- Tagger do
1074
+ Functionality do
880
1075
  def other
881
1076
  'other' # -- same ancestors as before
882
1077
  end
883
1078
  end
884
1079
 
885
- expect(Tagger().ancestors).to eql( [Tagger()] )
1080
+ expect(Functionality().ancestors).to eql( [Functionality()] )
886
1081
 
887
1082
  # test it
888
1083
 
889
- o = Object.new.extend(Tagger())
1084
+ o = Object.new.extend(Functionality())
890
1085
 
891
1086
  # inherited
892
1087
  o.m1.should == 1
@@ -897,10 +1092,10 @@ This flavor of the inheritance model allows our modular closures to have similar
897
1092
 
898
1093
 
899
1094
  #
900
- # JIT inheritance
1095
+ # JITI
901
1096
  #
902
- Tag2 = Tagger do
903
- def m1 # The :m1 override invokes JIT inheritance
1097
+ Tag2 = Functionality do
1098
+ def m1 # The :m1 override invokes JIT Inheritance
904
1099
  super + 1 # -- Tag1 is summoned into ancestor chain
905
1100
  end # -- allows the use of super
906
1101
 
@@ -921,70 +1116,84 @@ This flavor of the inheritance model allows our modular closures to have similar
921
1116
  p.m3.should == 'em3'
922
1117
  p.other.should == 'other'
923
1118
 
924
- expect(Tagger().ancestors).to eql( [Tagger(), Tag1] )
1119
+ expect(Functionality().ancestors).to eql( [Functionality(), Tag1] )
925
1120
  expect(Tag2.ancestors).to eql( [Tag2, Tag1] )
926
1121
 
927
- For more on this please see the rspec files in the project page or on the gem itself.
1122
+ ### The Rules of JITI
928
1123
 
929
- ---
930
- But, this is the basic idea here. An extended closure which can be used as a mix-in, prolonged to add function, and versioned, tagged, and inherited to fit the purpose at hand. Using this approach Jackbox also goes on to solve the Decorator Pattern problem in the Ruby language.
1124
+ JITI (Just-In-Time Inheritance) is governed by a set of rules framing its behavior. Here are these rules and their descriptions:
1125
+
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.
1132
+
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.
931
1136
 
932
1137
  ---
933
1138
 
934
1139
 
935
1140
  ### The GOF Decorator Pattern:
936
- 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. An injector used as a decorator does not confuse class identity for the receiver. Decorators are useful in several areas of OOP: presentation layers, stream processing, command processors to name a few.
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.
937
1142
 
938
- Here is the code for that:
1143
+ Here is the code:
939
1144
 
940
- class Coffee
941
- def cost
942
- 1.50
943
- end
944
- end
1145
+ class Coffee
1146
+ def cost
1147
+ 1.50
1148
+ end
1149
+ end
945
1150
 
946
- injector :milk do
947
- def cost
948
- super() + 0.30
949
- end
950
- end
951
- injector :vanilla do
952
- def cost
953
- super() + 0.15
954
- end
955
- end
956
-
957
- cup = Coffee.new.enrich(milk).enrich(vanilla)
958
- cup.should be_instance_of(Coffee)
1151
+ trait :milk do
1152
+ def cost
1153
+ super() + 0.30
1154
+ end
1155
+ end
1156
+ trait :vanilla do
1157
+ def cost
1158
+ super() + 0.15
1159
+ end
1160
+ end
959
1161
 
960
- cup.cost.should == 1.95
1162
+ cup = Coffee.new.enrich(milk).enrich(vanilla)
1163
+ cup.should be_instance_of(Coffee)
961
1164
 
1165
+ cup.cost.should == 1.95
962
1166
 
963
- Furthermore, these same decorators can be then re-applied MULTIPLE TIMES to the same receiver. This is something that is normally not possible with the regular Ruby base language. Here are further examples:
964
1167
 
965
- cup = Coffee.new.enrich(milk).enrich(vanilla).enrich(vanilla)
966
-
967
- # or even...
968
-
969
- cup = Coffee.new.enrich milk, vanilla, vanilla
1168
+ Additionally, these same decorators can then be re-applied MULTIPLE TIMES to the same receiver. This is something that is normally not possible with the regular Ruby base language. Here is the code:
1169
+
1170
+ cup = Coffee.new.enrich(milk).enrich(vanilla).enrich(vanilla)
1171
+
1172
+ # or even...
1173
+
1174
+ cup = Coffee.new.enrich milk, vanilla, vanilla
970
1175
 
971
- cup.cost.should == 2.10
972
- cup.should be_instance_of(Coffee)
973
- cup.injectors.should == [:milk, :vanilla, :vanilla]
1176
+ cup.cost.should == 2.10
1177
+ cup.should be_instance_of(Coffee)
1178
+ cup.traits.should == [:milk, :vanilla, :vanilla]
1179
+
1180
+ # Important Note:
1181
+ # vanilla and coffe can be div tags
1182
+ # and other markup in html or...
974
1183
 
975
1184
 
976
- ### Other Capabilities of Injectors
1185
+ ### Other Capabilities of Trait Injectors
977
1186
 
978
- The functionality of Injectors can be removed from individual targets be them class targets 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.
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.
979
1188
 
980
- Here is an Injector removed after an #enrich to individual instance:
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:
981
1190
 
982
1191
  class Coffee
983
1192
  def cost
984
1193
  1.00
985
1194
  end
986
1195
  end
987
- injector :milk do
1196
+ trait :milk do
988
1197
  def cost
989
1198
  super() + 0.50
990
1199
  end
@@ -996,7 +1205,7 @@ Here is an Injector removed after an #enrich to individual instance:
996
1205
  cup.cost.should == 1.50
997
1206
  friends_cup.cost.should == 1.50
998
1207
 
999
- cup.eject :milk
1208
+ cup.cancel :milk
1000
1209
 
1001
1210
  cup.cost.should == 1.00
1002
1211
 
@@ -1007,7 +1216,7 @@ Here it is removed after an #inject at the class level:
1007
1216
 
1008
1217
  # create the injection
1009
1218
  class Home
1010
- injector :layout do
1219
+ trait :layout do
1011
1220
  def fractal
1012
1221
  end
1013
1222
  end
@@ -1030,20 +1239,20 @@ Here it is removed after an #inject at the class level:
1030
1239
  expect{Home.new.fractal}.to raise_error
1031
1240
 
1032
1241
 
1033
- The code for these examples makes use of the #eject method which is also opens the door to some additional functionality provided by injectors. See 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 is more of its intent. For temporary withdrawal of an injector you should use injector directives as shown below.
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.
1034
1243
 
1035
- #### #eject *sym
1036
- This method ejects injector function from a single object or class. It is in scope on any classes injected or enriched by an injector. Its effect is that of completely removing one of our modular closures from the ancestor chain. Once this is done method calls on the injector will came back with an error. 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.
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.
1037
1246
 
1038
1247
  ### Injector Directives
1039
- Once you have an injector handle you can also use it to issue directives to the injector. These directives can have a profound effect on your code.
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.
1040
1249
 
1041
- #### :collapse directive
1042
- This description produces similar results to the one for injector ejection (see above) except that further injector method calls DO NOT raise an error. They just quietly return nil. Here are a couple of different cases:
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:
1043
1252
 
1044
- The case with multiple objects
1253
+ The case with multiple object instances:
1045
1254
 
1046
- injector :copiable do
1255
+ trait :copiable do
1047
1256
  def object_copy
1048
1257
  'a dubious copy'
1049
1258
  end
@@ -1064,7 +1273,7 @@ The case with multiple objects
1064
1273
  The case with a class receiver:
1065
1274
 
1066
1275
  class SomeClass
1067
- injector :code do
1276
+ trait :code do
1068
1277
  def tester
1069
1278
  'boo'
1070
1279
  end
@@ -1082,19 +1291,13 @@ The case with a class receiver:
1082
1291
  a.tester.should == nil
1083
1292
  b.tester.should == nil
1084
1293
 
1085
- # further
1086
- SomeClass.eject :code
1087
- expect{ a.tester }.to raise_error
1088
- expect{ b.tester }.to raise_error
1089
-
1090
-
1091
1294
 
1092
- #### :rebuild directive
1093
- Injectors that have been collapsed can at a later point then be reconstituted. Here are a couple of cases:
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:
1094
1297
 
1095
1298
  The case with multiple object receivers:
1096
1299
 
1097
- injector :reenforcer do
1300
+ trait :reenforcer do
1098
1301
  def thick_walls
1099
1302
  '=====|||====='
1100
1303
  end
@@ -1117,7 +1320,7 @@ The case with multiple object receivers:
1117
1320
  The case with a class receiver:
1118
1321
 
1119
1322
  class SomeBloatedObject
1120
- injector :ThinFunction do
1323
+ trait :ThinFunction do
1121
1324
  def perform
1122
1325
  'do the deed'
1123
1326
  end
@@ -1132,9 +1335,11 @@ The case with a class receiver:
1132
1335
  SomeBloatedObject.ThinFunction :active # alias to :rebuild
1133
1336
  tester.perform.should == 'do the deed'
1134
1337
 
1338
+ #### :tag/:version directive
1339
+ This directive creates a soft tagged version of a trait. For more on this see Soft Tags below.
1135
1340
 
1136
1341
  #### :implode directive
1137
- This directive totally destroys the injector including the handle to it. Use it carefully!
1342
+ This directive totally destroys the trait including the handle to it. Use it carefully!
1138
1343
 
1139
1344
  class Model
1140
1345
  def feature
@@ -1142,7 +1347,7 @@ This directive totally destroys the injector including the handle to it. Use it
1142
1347
  end
1143
1348
  end
1144
1349
 
1145
- injector :extras do
1350
+ trait :extras do
1146
1351
  def feature
1147
1352
  super() + ' plus some extras'
1148
1353
  end
@@ -1166,7 +1371,7 @@ This directive totally destroys the injector including the handle to it. Use it
1166
1371
  }.to raise_error(NameError, /extras/)
1167
1372
 
1168
1373
  ### The GOF Strategy Pattern:
1169
- Another pattern that Jackbox helps with is the GOF Strategy Pattern. This is a pattern with changes the guts of an object as opposed to just changing its face. Traditional examples of this pattern use PORO component injection within constructors.
1374
+ 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.
1170
1375
 
1171
1376
  Here are a couple alternate implementations:
1172
1377
 
@@ -1189,7 +1394,7 @@ Here are a couple alternate implementations:
1189
1394
  cup.strategy.should == 'normal'
1190
1395
 
1191
1396
 
1192
- injector :sweedish do
1397
+ trait :sweedish do
1193
1398
  def brew
1194
1399
  @strategy = 'sweedish'
1195
1400
  end
@@ -1199,11 +1404,10 @@ Here are a couple alternate implementations:
1199
1404
  cup.brew
1200
1405
  cup.strategy.should == ('sweedish')
1201
1406
 
1202
-
1203
- But, with #eject it is possible to have an even more general alternate implementation. This time we completely replace the current strategy by actually ejecting it out of the class and then injecting a new one:
1407
+ But, with #cancel/#eject it is possible to have an even more general alternate implementation. This time we completely replace the current strategy by actually ejecting it out of the class and then injecting a new one:
1204
1408
 
1205
1409
  class Tea < Coffee # Tea is a type of coffee!! ;~Q)
1206
- injector :SpecialStrategy do
1410
+ trait :SpecialStrategy do
1207
1411
  def brew
1208
1412
  @strategy = 'special'
1209
1413
  end
@@ -1230,19 +1434,20 @@ Just like hard tags above but a name is not needed:
1230
1434
  :foo
1231
1435
  end
1232
1436
  end
1437
+ SomeJack :tag # Unnamed version
1233
1438
 
1234
- SomeJack(:tag) do # New Version, not named
1439
+ SomeJack(:tag) do # New unnamed version
1235
1440
  def foo
1236
1441
  :foooooooo
1237
1442
  end
1238
1443
  end
1239
1444
 
1240
- Accessible through Injector#tags (an Array). Also available **injector#tags.hard** and **injector#tags.soft**.
1445
+ Accessible through trait#tags (an Array). Also available **trait#tags.hard** and **trait#tags.soft**. See introspection above.
1241
1446
 
1242
1447
  ---
1243
1448
  ### Patterns of a Different Flavor
1244
1449
 
1245
- There are also some additional coding patterns possible with Jackbox Injectors. Although not part of the traditional GOF set these new patterns are only possible now thanks to languages like Ruby that permit the morphing of traditional forms into newer constructs. Here are some new patterns:
1450
+ 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:
1246
1451
 
1247
1452
  __1) Late Decorator.-__ Another flow that also benefits from #define\_method in an interesting way is the following:
1248
1453
 
@@ -1253,7 +1458,7 @@ __1) Late Decorator.-__ Another flow that also benefits from #define\_method in
1253
1458
  end
1254
1459
  w = Widget.new
1255
1460
 
1256
- injector :decorator
1461
+ trait :decorator
1257
1462
 
1258
1463
  w.enrich decorator, decorator, decorator, decorator
1259
1464
 
@@ -1261,18 +1466,18 @@ __1) Late Decorator.-__ Another flow that also benefits from #define\_method in
1261
1466
  bid = 3.5
1262
1467
 
1263
1468
  decorator do
1264
- define_method :cost do # defines function on all injectors of the class
1469
+ define_method :cost do # defines function on all traits of the class
1265
1470
  super() + bid
1266
1471
  end
1267
1472
  end
1268
1473
 
1269
1474
  w.cost.should == 15
1270
1475
 
1271
- The actual injector function is late bound and defined only after some other data is available.
1476
+ The actual decorating trait function is late bound and defined only after some other data is available.
1272
1477
 
1273
1478
  __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:
1274
1479
 
1275
- facet :Superb
1480
+ trait :Superb
1276
1481
 
1277
1482
  Superb do
1278
1483
  def process string, additives, index
@@ -1285,7 +1490,7 @@ __2) The Super Pattern.-__ No. This is not a superlative kind of pattern. Simp
1285
1490
  Superb().process( 'food ', 'aeiu', 0 ).should == 'fuud fiid feed faad '
1286
1491
  Superb(:implode)
1287
1492
 
1288
- __3) The Transformer 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:
1493
+ __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:
1289
1494
 
1290
1495
  jack :Solution
1291
1496
 
@@ -1329,7 +1534,7 @@ __3) The Transformer Pattern.-__ For a specific example of what can be accompli
1329
1534
 
1330
1535
  Client.solve
1331
1536
 
1332
- __4) The Re-Classing Pattern.-__ Our base method #lets has one more interesting use which allows for an alternative way to refine classes. We have termed this Re-Classing. Look at the following code:
1537
+ __4) The Class Constructor Pattern.-__ Our base method #lets has one more interesting use which allows for an alternative way to refine classes. We have originally termed this Re-Classing but after further consideration and user input we have refocused the intent of this pattern and we now define it as class constructors. Look at the following code:
1333
1538
 
1334
1539
  module Work
1335
1540
  lets String do
@@ -1357,7 +1562,7 @@ __4) The Re-Classing Pattern.-__ Our base method #lets has one more interesting
1357
1562
  str = 'Men-At-Work'
1358
1563
 
1359
1564
 
1360
- The important thing to remember here is that #String() is a method now. We can redefine it, name-space it, test for its presence, etc. We can also use it to redefine the re-class's methods.
1565
+ The important thing to remember here is that #String() is a method now. We can redefine it, name-space it, test for its presence, etc. We can also use it to redefine the class's methods.
1361
1566
 
1362
1567
  jack :Log do
1363
1568
  require 'logger'
@@ -1383,7 +1588,7 @@ For more on this see, the rspec files and the Jackbox blog at <a href="http://ja
1383
1588
 
1384
1589
  #### #reclass?(klass)
1385
1590
 
1386
- This helper verifies a certain re-class exists within the current namespace. It returns a boolean. Ex:
1591
+ This helper verifies a certain re-class exists within the current namespace. It returns a boolean. Example:
1387
1592
 
1388
1593
  module One
1389
1594
  if reclass? String
@@ -1392,8 +1597,7 @@ This helper verifies a certain re-class exists within the current namespace. It
1392
1597
  end
1393
1598
 
1394
1599
 
1395
- ---
1396
- For more information and additional examples see the rspec examples on this project. There you'll find a long list of over __200__ rspec examples and code showcasing some additional features of Jackbox Injectors along with some additional descriptions.
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.
1397
1601
 
1398
1602
  ---
1399
1603
  ## Additional Tools