ick 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,4 +8,7 @@
8
8
  * 3 minor enhancement:
9
9
  * refactored Wrapped into Wrap with runtime options
10
10
  * added ArrayWrapper and Tee classes
11
+ * 4 minor enhancement
12
+ * fixed manifest.txt
13
+ * TODO: build the manifest algorithmically!
11
14
 
@@ -6,7 +6,13 @@ Rakefile
6
6
  config/hoe.rb
7
7
  config/requirements.rb
8
8
  lib/ick.rb
9
+ lib/ick/advisor.rb
10
+ lib/ick/base.rb
11
+ lib/ick/guard.rb
12
+ lib/ick/sugar.rb
13
+ lib/ick/tee.rb
9
14
  lib/ick/version.rb
15
+ lib/ick/wrap.rb
10
16
  log/debug.log
11
17
  script/destroy
12
18
  script/generate
data/lib/ick.rb CHANGED
@@ -2,6 +2,7 @@ $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  require 'ick/base'
4
4
  require 'ick/wrap'
5
+ require 'ick/advisor'
5
6
  require 'ick/guard'
6
7
  require 'ick/tee'
7
8
  require 'ick/sugar'
@@ -0,0 +1,43 @@
1
+ module Ick
2
+ class Advisor < Wrapper
3
+ @@arounds = lambda { |callback, receiver, sym, *args|
4
+ callback.call()
5
+ }
6
+ def self.around target = nil, args = {}, &proc # { |callback, receiver, sym, *args| ... }
7
+ old_arounds = @@arounds
8
+ if target.kind_of?(Symbol)
9
+ proc = lambda { |callback, receiver, sym, *args|
10
+ self.send(target, sym, *args, &proc)
11
+ }
12
+ elsif target.kind_of?(Class)
13
+ proc = lambda { |callback, receiver, sym, *args|
14
+ target.filter(receiver, sym, *args, &proc)
15
+ }
16
+ end
17
+ @@arounds = if args[:only]
18
+ lambda { |callback, receiver, sym, *args|
19
+ new_callback = lambda { old_arounds.call(callback, receiver, sym, *args) }
20
+ Array(args[:only]).include(sym) ? proc.call(callback, receiver, sym, *args) : callback.call()
21
+ }
22
+ elsif args[:except]
23
+ lambda { |callback, receiver, sym, *args|
24
+ new_callback = lambda { old_arounds.call(callback, receiver, sym, *args) }
25
+ Array(args[:only]).include(sym) ? new_callback.call() : proc.call(new_callback, receiver, sym, *args)
26
+ }
27
+ else
28
+ lambda { |callback, receiver, sym, *args|
29
+ new_callback = lambda { old_arounds.call(callback, receiver, sym, *args) }
30
+ proc.call(new_callback, receiver, sym, *args)
31
+ }
32
+ end
33
+ end
34
+ def __invoke__(sym, *args, &block)
35
+ @@arounds.call(
36
+ lambda { @value.__send__(sym, *args, &block) },
37
+ @value,
38
+ sym,
39
+ *args
40
+ )
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,108 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'singleton'
4
+
5
+ module Ick
6
+
7
+ class Base
8
+ include Singleton
9
+
10
+ def returns(value, result)
11
+ raise 'implemented by subclass or by calling meta-method'
12
+ end
13
+
14
+ def evaluate(value, proc)
15
+ raise 'implemented by subclass or by calling meta-method'
16
+ end
17
+
18
+ def invoke(value = nil, &proc)
19
+ result = evaluate(value, proc)
20
+ returns(value, result)
21
+ end
22
+
23
+ def self.evaluates_in_calling_environment
24
+ define_method :evaluate do |value, proc|
25
+ proc.call(value)
26
+ end
27
+ true
28
+ end
29
+
30
+ def self.evaluates_in_value_environment
31
+ define_method :evaluate do |value, proc|
32
+ value.instance_eval(&proc)
33
+ end
34
+ true
35
+ end
36
+
37
+ def self.returns_value
38
+ define_method :returns do |value, result|
39
+ value
40
+ end
41
+ true
42
+ end
43
+
44
+ def self.returns_result
45
+ define_method :returns do |value, result|
46
+ result
47
+ end
48
+ true
49
+ end
50
+
51
+ #snarfed from Ruby On Rails
52
+ def self.underscore(camel_cased_word)
53
+ camel_cased_word.to_s.gsub(/::/, '/').
54
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
55
+ tr("-", "_").
56
+ downcase
57
+ end
58
+
59
+ def self.belongs_to clazz
60
+ method_name = self.underscore(self.name.split('::')[-1])
61
+ unless clazz.method_defined?(method_name)
62
+ clazz.class_eval "
63
+ def #{method_name}(value=self,&proc)
64
+ if block_given?
65
+ #{self.name}.instance.invoke(value, &proc)
66
+ else
67
+ Invoker.new(value, #{self.name})
68
+ end
69
+ end"
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ class Invoker
76
+
77
+ instance_methods.reject { |m| m =~ /^__/ }.each { |m| undef_method m }
78
+
79
+ def initialize(value, clazz)
80
+ @value = value
81
+ @clazz = clazz
82
+ end
83
+
84
+ def method_missing(sym, *args, &block)
85
+ @clazz.instance.invoke(@value) { |value|
86
+ value.__send__(sym, *args, &block)
87
+ }
88
+ end
89
+
90
+ end
91
+
92
+ class Let < Base
93
+ evaluates_in_calling_environment and returns_result
94
+ end
95
+
96
+ class Returning < Base
97
+ evaluates_in_calling_environment and returns_value
98
+ end
99
+
100
+ class My < Base
101
+ evaluates_in_value_environment and returns_result
102
+ end
103
+
104
+ class Inside < Base
105
+ evaluates_in_value_environment and returns_value
106
+ end
107
+
108
+ end
@@ -0,0 +1,53 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'singleton'
4
+
5
+ module Ick
6
+
7
+ class GuardWrapper < IdentityWrapper
8
+ def initialize(value, default, proc)
9
+ @proc = proc
10
+ @default = default
11
+ super(value)
12
+ end
13
+ def __rewrap__(new_value)
14
+ self.__class.new(new_value, @default, @proc) #contagious
15
+ end
16
+ def __invoke__(sym, *args, &block)
17
+ @proc.call(@value, sym) ? super : @default
18
+ end
19
+ end
20
+
21
+ class Guard < Wrap
22
+ include Singleton
23
+
24
+ def self.guard_with(&proc)
25
+ @guard_proc = proc
26
+ end
27
+
28
+ def self.guard
29
+ @guard_proc
30
+ end
31
+
32
+ def invoke(value, &proc)
33
+ invoke_wrapped(value, GuardWrapper, nil, self.class.guard, &proc)
34
+ end
35
+
36
+ end
37
+
38
+ class Maybe < Guard
39
+ guard_with { |value, sym| value.nil? == false }
40
+ evaluates_in_calling_environment and returns_result
41
+ end
42
+
43
+ class Try < Guard
44
+ guard_with { |value, sym| value.respond_to?(sym) == true }
45
+ evaluates_in_calling_environment and returns_result
46
+ end
47
+
48
+ class Please < Ick::Guard
49
+ guard_with { |value, sym| value.respond_to?(sym) == true } # defines your guard condition
50
+ evaluates_in_value_environment and returns_result # defines its behaviour
51
+ end
52
+
53
+ end
@@ -0,0 +1,7 @@
1
+ module Ick
2
+ def self.sugarize
3
+ [Let, Returning, My, Inside, Maybe, Try, Please, Tee].each do |clazz|
4
+ clazz.belongs_to Object
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ module Ick
2
+
3
+ =begin
4
+ WARNING: UNTESTED
5
+ =end
6
+ class Tee < Wrap
7
+
8
+ def invoke(*values, &proc)
9
+ invoke_wrapped(values, ArrayWrapper, nil, &proc)
10
+ end
11
+
12
+ def unwrap(wrapped, wrapper_class)
13
+ wrapped.respond_to?(:__value__) ? wrapped.__value__.first : wrapped
14
+ end
15
+
16
+ evaluates_in_calling_environment and returns_result
17
+
18
+ def self.belongs_to clazz
19
+ method_name = self.underscore(self.name.split('::')[-1])
20
+ unless clazz.method_defined?(method_name)
21
+ clazz.class_eval "
22
+ def #{method_name}(*values,&proc)
23
+ values = [self] if values.empty?
24
+ if block_given?
25
+ #{self.name}.instance.invoke(*values, &proc)
26
+ else
27
+ Invoker.new(values, #{self.name})
28
+ end
29
+ end"
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -2,7 +2,7 @@ module Ick #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 2
5
- TINY = 0
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,92 @@
1
+ module Ick
2
+
3
+ class Wrapper
4
+
5
+ alias :__respond_to? :respond_to?
6
+ alias :__instance_eval :instance_eval
7
+ alias :__class :class
8
+ alias :__inspect :inspect
9
+ instance_methods.reject { |m| m =~ /^__/ }.each { |m| undef_method m }
10
+ alias :instance_eval :__instance_eval
11
+
12
+ def initialize(value, *additional_attributes)
13
+ @value = value
14
+ end
15
+
16
+ def __value__
17
+ @value
18
+ end
19
+ def __invocation__
20
+ @invocation
21
+ end
22
+ def __rewrap__(value)
23
+ raise 'implemented by subclass'
24
+ end
25
+ def __invoke__(sym, *args, &block)
26
+ raise 'implemented by subclass'
27
+ end
28
+ def method_missing(sym, *args, &block)
29
+ __rewrap__(__invoke__(sym, *args, &block))
30
+ end
31
+ def respond_to?(sym)
32
+ @value.respond_to?(sym) || self.__respond_to?(sym)
33
+ end
34
+
35
+ def self.is_contagious
36
+ define_method(:__rewrap__) { |value|
37
+ self.__class.new(value)
38
+ }
39
+ end
40
+
41
+ def self.is_not_contagious
42
+ define_method(:__rewrap__) { |value| value }
43
+ end
44
+
45
+ end
46
+
47
+ class IdentityWrapper < Wrapper
48
+
49
+ def __invoke__(sym, *args, &block)
50
+ @value.__send__(sym, *args, &block)
51
+ end
52
+
53
+ end
54
+
55
+ class ArrayWrapper < Wrapper
56
+
57
+ def initialize(values, *additional_attributes)
58
+ super(values)
59
+ end
60
+
61
+ def __invoke__(sym, *args, &block)
62
+ p "#{sym} -> #{@value.inspect}"
63
+ @value.map { |_| _.__send__(sym, *args, &block) }
64
+ end
65
+
66
+ is_contagious
67
+
68
+ end
69
+
70
+ class Wrap < Base
71
+
72
+ def wrap(value, wrapper_class, *additional_arguments)
73
+ wrapper_class.new(value, *additional_arguments)
74
+ end
75
+
76
+ def unwrap(wrapped, wrapper_class)
77
+ wrapped.respond_to?(:__value__) ? wrapped.__value__ : wrapped
78
+ end
79
+
80
+ def invoke_wrapped(value, wrapper_class, *additional_arguments, &proc)
81
+ wrapped_value = wrap(value, wrapper_class, *additional_arguments)
82
+ wrapped_result = evaluate(wrapped_value, proc)
83
+ returns(unwrap(wrapped_value, wrapper_class), unwrap(wrapped_result, wrapper_class))
84
+ end
85
+
86
+ def invoke
87
+ raise 'subclass should override and invoke invoke_wrapped'
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -125,15 +125,28 @@ class TestIck < Test::Unit::TestCase
125
125
  )
126
126
  end
127
127
 
128
- def test_tee
129
-
130
- box1 = Box.new(1)
131
- box2 = Box.new(2)
132
-
133
- tee(box1,box2) { |box| box.value = 7 }
134
- assert_equal(7, box1.value)
135
- assert_equal(7, box2.value)
136
-
137
- end
128
+ # def test_tee_basic
129
+ # box1 = Box.new(1)
130
+ # box2 = Box.new(2)
131
+ # tee(box1,box2) { |box| box.value = 7 }
132
+ # assert_equal(7, box1.value)
133
+ # assert_equal(7, box2.value)
134
+ # end
135
+
136
+ # def test_tee_semantics
137
+ # first_log = []
138
+ # second_log = []
139
+ # [first_log, second_log].map do |value|
140
+ # log << "first line of output"
141
+ # log << "second line of output"
142
+ # end
143
+ # assert_equal(, log)
144
+ # out = []
145
+ # tee(1, 2, 3) do |value|
146
+ # out << "a#{value}"
147
+ # out << "b#{value}"
148
+ # end
149
+ # assert_equal(%w(a1 b1 c1 a2 b2 c2), out)
150
+ # end
138
151
 
139
152
  end
@@ -33,7 +33,7 @@
33
33
  <h1>Invocation Construction Kit</h1>
34
34
  <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/ick"; return false'>
35
35
  <p>Get Version</p>
36
- <a href="http://rubyforge.org/projects/ick" class="numbers">0.2.0</a>
36
+ <a href="http://rubyforge.org/projects/ick" class="numbers">0.2.1</a>
37
37
  </div>
38
38
  <h1>&#x2192; &#8216;ick&#8217;</h1>
39
39
 
@@ -66,7 +66,7 @@
66
66
  <p>Although Ruby borrows many of its features from Lisp and its syntax from Algol, it does not have block-local variables. In other words, if you declare a variable anywhere inside of a method, that variable is visible everywhere in that method. This is a problem, because it encourages writing methods where the instance variables create lot of dependencies between different expressions. Those methods can be hard to understand and refactor.</p>
67
67
 
68
68
 
69
- <p>Ick solves this problem by providing four block structure methods: #let, #returning, #my, and #inside. These methods take an expression and bind it to a variable inside of a block. For example, if you want someone&#8217;s phone number only if they are a friend:</p>
69
+ <p>Ick solves this problem by providing a block structure method: #let. #let takes an expression and binds it to a variable inside of a block. For example, if you want someone&#8217;s phone number only if they are a friend:</p>
70
70
 
71
71
 
72
72
  <p><pre class='syntax'>
@@ -74,10 +74,7 @@
74
74
  </pre></p>
75
75
 
76
76
 
77
- <p>This code makes it clear that you only need the <code>person</code> variable inside the block. If you want to refactor this code, you know that the entire expression can move without breaking another piece of code. We&#8217;ll elaborate on the differences between #let, #returning, #my, and #inside below.</p>
78
-
79
-
80
- <p>(The four methods were inspired by <a href="http://blog.rubyenrails.nl/articles/2008/02/18/our-daily-method-10-object-r-rs-ds-s">Michiel de Mare&#8217;s post on the same subject</a>, although Ick&#8217;s nomenclature is not compatible with Michiel&#8217;s. Michiel&#8217;s #rsss, #rrss, #rsds, and #rrds are called #returning, #let, #inside, and #my in Ick.)</p>
77
+ <p>This code makes it clear that you only need the <code>person</code> variable inside the block. If you want to refactor this code, you know that the entire expression can move without breaking another piece of code. Ick provides three other block structure methods (#returning, #my, and #inside) that are detailed on the <a href="inside.html">Inside Ick</a> page.</p>
81
78
 
82
79
 
83
80
  <h2>Guarded Evaluation</h2>
@@ -184,112 +181,34 @@
184
181
  <p>The point is, <code>try(program) { responsibly }</code>. You choose which classes to open and which methods to add. <a href="http://avdi.org/devblog/2008/02/25/full-disclosure/">All I’m saying is this: before re-opening a class, did you go through the rest of your toolbox first?</a></p>
185
182
 
186
183
 
187
- <h2>More about the four block structures</h2>
188
-
189
-
190
- <p>There are two binary decisions to be made about every block: First, do you want to evaluate the block in the calling environment (which is how almost every block is evaluated in Ruby), or do you want to evaluate the block in the value&#8217;s context. In other words, does <em>self</em> stay the same, or does it become the value in the block?</p>
191
-
192
-
193
- <p>The methods #try and #maybe are both implemented as <em>evaluates_in_calling_environment</em>, because that is least surprising. But when you&#8217;re rolling your own, you might want to change that to make things more sugary. For example, here is a different version of #try:</p>
194
-
195
-
196
- <p><pre class='syntax'>
197
- <span class="keyword">class </span><span class="class">Please</span> <span class="punct">&lt;</span> <span class="constant">Ick</span><span class="punct">::</span><span class="constant">Guard</span>
198
- <span class="ident">guard_with</span> <span class="punct">{</span> <span class="punct">|</span><span class="ident">value</span><span class="punct">,</span> <span class="ident">sym</span><span class="punct">|</span> <span class="ident">value</span><span class="punct">.</span><span class="ident">respond_to?</span><span class="punct">(</span><span class="ident">sym</span><span class="punct">)</span> <span class="punct">==</span> <span class="constant">true</span> <span class="punct">}</span>
199
- <span class="ident">evaluates_in_value_environment</span> <span class="keyword">and</span> <span class="ident">returns_result</span>
200
- <span class="ident">belongs_to</span> <span class="constant">Object</span>
201
- <span class="keyword">end</span>
202
-
203
- <span class="ident">please</span><span class="punct">(...)</span> <span class="punct">{</span> <span class="ident">may</span><span class="punct">.</span><span class="ident">i</span><span class="punct">.</span><span class="ident">have</span><span class="punct">.</span><span class="ident">some</span><span class="punct">.</span><span class="ident">more</span> <span class="punct">}</span>
204
- </pre></p>
205
-
206
-
207
- <p>The method #please executes in the value&#8217;s environment, and thus it can call methods directly.</p>
208
-
209
-
210
- <p>You already saw #let, it takes your expression and binds it to a parameter, then it evaluates a block in the calling environment, just as #try evaluates and guards its block in the calling environment. If you want something that behaves like #let but evaluates in the value&#8217;s environment just like #please, you can use #my:</p>
211
-
212
-
213
- <p><pre class='syntax'>
214
- <span class="ident">my</span><span class="punct">(</span><span class="constant">Person</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">,</span> <span class="punct">...))</span> <span class="keyword">do</span>
215
- <span class="ident">first_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Charles</span><span class="punct">'</span>
216
- <span class="ident">last_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Babbage</span><span class="punct">'</span>
217
- <span class="ident">friends</span> <span class="punct">&lt;&lt;</span> <span class="punct">'</span><span class="string">Ada Lovelace</span><span class="punct">'</span>
218
- <span class="keyword">end</span>
219
- </pre></p>
220
-
221
-
222
- <p>This will return Charles Babbage&#8217;s friends. On the surface, <em>evaluates_in_value_environment</em> is about the syntactic sugar of dropping an instance variable. But with a little thought, you can come up with some really cool way to (mis)use this capability.</p>
223
-
224
-
225
- <p>So #let and #my both pass an expression to a block and return the result. Given that they both &#8216;declare&#8217; <em>returns_result</em>, this is not surprising. But there is another choice: <em>returns_value</em> instead of <em>returns_result</em>. Ruby on Rails includes the popular #returning method, and it works the same in Ick:</p>
184
+ <h2>Is Ick for me?</h2>
226
185
 
227
186
 
228
- <p><pre class='syntax'>
229
- <span class="ident">returning</span><span class="punct">(</span><span class="constant">Person</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">,</span> <span class="punct">...))</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">p</span><span class="punct">|</span>
230
- <span class="ident">p</span><span class="punct">.</span><span class="ident">first_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Charles</span><span class="punct">'</span>
231
- <span class="ident">p</span><span class="punct">.</span><span class="ident">last_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Babbage</span><span class="punct">'</span>
232
- <span class="ident">p</span><span class="punct">.</span><span class="ident">friends</span> <span class="punct">&lt;&lt;</span> <span class="punct">'</span><span class="string">Ada Lovelace</span><span class="punct">'</span>
233
- <span class="keyword">end</span>
234
- </pre></p>
235
-
236
-
237
- <p>This returns the person record, not the list of friends. The block is evaluated strictly for side effects. And what happens if we want to return the value and also evaluate in the value&#8217;s environment?</p>
238
-
239
-
240
- <p><pre class='syntax'>
241
- <span class="ident">inside</span><span class="punct">(</span><span class="constant">Person</span><span class="punct">.</span><span class="ident">find</span><span class="punct">(</span><span class="symbol">:first</span><span class="punct">,</span> <span class="punct">...))</span> <span class="keyword">do</span>
242
- <span class="ident">first_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Charles</span><span class="punct">'</span>
243
- <span class="ident">last_name</span> <span class="punct">=</span> <span class="punct">'</span><span class="string">Babbage</span><span class="punct">'</span>
244
- <span class="ident">friends</span> <span class="punct">&lt;&lt;</span> <span class="punct">'</span><span class="string">Ada Lovelace</span><span class="punct">'</span>
245
- <span class="keyword">end</span>
246
- </pre></p>
247
-
248
-
249
- <p>The method #inside returns the value and evaluates the block in the value&#8217;s environment.</p>
250
-
251
-
252
- <h2>Under the Hood</h2>
187
+ <p>Ick does provide a number of very convenient methods for abstracting evaluation. If you adopt them for your project, your code will be more readable, more succinct, and easier to refactor. That being said, it will not make your teeth whiter or help you sleep if you have a newborn.</p>
253
188
 
254
189
 
255
- <p>Ick is actually a construction kit. By all means install the gem and go wild with #let, #returning, #my, #inside, #try, and #maybe. But have a look under the hood. It&#8217;s easy to build your own methods.</p>
190
+ <p><em>Every programming problem can be solved with another layer of abstraction, except the problem of too many layers of abstraction</em></p>
256
191
 
257
192
 
258
- <h3>Every programming problem can be solved with another layer of abstraction, except the problem of too many layers of abstraction</h3>
193
+ <p>The important caveat about Ick is that its implementation adds abstraction. If all you want are two or three specific methods, writing them as simply and as directly as possible makes for a very simple implementation. Ick uses classes and templates instead of simple methods so that when you are ready to start writing your own abstractions, you can easily use the pieces that Ick has built-in. If Ick was written as a collection of cool methods, you would not be able to extend it without copying and pasting.</p>
259
194
 
260
195
 
261
- <p>Ick uses classes and template methods to replicate what can be done in a few lines of explicit code. For example, Object#returning is implemented in Rails as:</p>
262
-
263
-
264
- <p><pre class='syntax'>
265
- <span class="keyword">class </span><span class="class">Object</span>
266
- <span class="keyword">def </span><span class="method">returning</span><span class="punct">(</span><span class="ident">value</span><span class="punct">)</span>
267
- <span class="keyword">yield</span><span class="punct">(</span><span class="ident">value</span><span class="punct">)</span>
268
- <span class="ident">value</span>
269
- <span class="keyword">end</span>
270
- <span class="keyword">end</span>
271
- </pre></p>
272
-
273
-
274
- <p>So why bother with Ick? Well, Ick is a construction kit. if you want to make a method just like Object#returning, only <em>X</em> (for some value of X), you can&#8217;t do that without copying, pasting, and modifying. Ick&#8217;s classes are included specifically so you can subclass things and make your own new kinds of methods that are variations of the existing methods.</p>
275
-
276
-
277
- <p>Thus, the extra abstraction is appropriate if you want to use the built-in methods as a starting point for your own exploratory programming. And if you don&#8217;t care, you just want the methods, by all means install the gem and just use them. Don&#8217;t worry about the implementation unless you identify it as a performance problem.</p>
196
+ <p>Ick raises how you handle things to the level of first-class objects in Ruby, so you can mix and match and separate concerns as you see fit. Logging, permissions, error handling&#8230; These are some of the places you can take Ick. Have fun.</p>
278
197
 
279
198
 
280
- <h3>Where do you want to go today?</h3>
199
+ <h2>Where can I read about what&#8217;s going on inside Ick?</h2>
281
200
 
282
201
 
283
- <p>The point behind abstracting invocation and evaluation is that you can <em>separate concerns</em>. For example, which methods to chain is one concern. How to handle nil or an object that does not respond to a method is a separate concern. Should you raise and handle and exception? Return nil? log an error? Why should error handling and logging be intermingled with your code?</p>
202
+ <p><a href="inside.html">Inside Ick</a></p>
284
203
 
285
204
 
286
- <p>With Ick, you can separate the two issues. You can even make the handling pluggable. For example, if instead of calling #let you call your own method, you could sometimes invoke <code>Ick::Let</code> with a block and other times invoke your own handler, perhaps one that logs every method called.</p>
205
+ <h2>Administrivia</h2>
287
206
 
288
207
 
289
- <p>Ick raises how you handle things to the level of first-class objects in Ruby, so you can mix and match and separate concerns as you see fit. Logging, permissions, error handling&#8230; These are some of the places you can take Ick. Have fun.</p>
208
+ <h3>Home Sweet Home</h3>
290
209
 
291
210
 
292
- <h2>Administrivia</h2>
211
+ <p><a href="http://ick.rubyforge.org">ick.rubyforge.org</a></p>
293
212
 
294
213
 
295
214
  <h3>How to submit patches</h3>
@@ -22,15 +22,13 @@ h2. Block Structured Ruby
22
22
 
23
23
  Although Ruby borrows many of its features from Lisp and its syntax from Algol, it does not have block-local variables. In other words, if you declare a variable anywhere inside of a method, that variable is visible everywhere in that method. This is a problem, because it encourages writing methods where the instance variables create lot of dependencies between different expressions. Those methods can be hard to understand and refactor.
24
24
 
25
- Ick solves this problem by providing four block structure methods: #let, #returning, #my, and #inside. These methods take an expression and bind it to a variable inside of a block. For example, if you want someone's phone number only if they are a friend:
25
+ Ick solves this problem by providing a block structure method: #let. #let takes an expression and binds it to a variable inside of a block. For example, if you want someone's phone number only if they are a friend:
26
26
 
27
27
  <pre syntax="ruby">
28
28
  let(Person.find(:first, ...)) { |person| person.phone_number if person.friend? }
29
29
  </pre>
30
30
 
31
- This code makes it clear that you only need the @person@ variable inside the block. If you want to refactor this code, you know that the entire expression can move without breaking another piece of code. We'll elaborate on the differences between #let, #returning, #my, and #inside below.
32
-
33
- (The four methods were inspired by "Michiel de Mare's post on the same subject":http://blog.rubyenrails.nl/articles/2008/02/18/our-daily-method-10-object-r-rs-ds-s, although Ick's nomenclature is not compatible with Michiel's. Michiel's #rsss, #rrss, #rsds, and #rrds are called #returning, #let, #inside, and #my in Ick.)
31
+ This code makes it clear that you only need the @person@ variable inside the block. If you want to refactor this code, you know that the entire expression can move without breaking another piece of code. Ick provides three other block structure methods (#returning, #my, and #inside) that are detailed on the "Inside Ick":inside.html page.
34
32
 
35
33
  h2. Guarded Evaluation
36
34
 
@@ -112,88 +110,25 @@ If you simply want everything you see here working exactly as it's shown, simply
112
110
 
113
111
  The point is, @try(program) { responsibly }@. You choose which classes to open and which methods to add. "All I’m saying is this: before re-opening a class, did you go through the rest of your toolbox first?":http://avdi.org/devblog/2008/02/25/full-disclosure/
114
112
 
115
- h2. More about the four block structures
116
-
117
- There are two binary decisions to be made about every block: First, do you want to evaluate the block in the calling environment (which is how almost every block is evaluated in Ruby), or do you want to evaluate the block in the value's context. In other words, does _self_ stay the same, or does it become the value in the block?
118
-
119
- The methods #try and #maybe are both implemented as _evaluates_in_calling_environment_, because that is least surprising. But when you're rolling your own, you might want to change that to make things more sugary. For example, here is a different version of #try:
120
-
121
- <pre syntax="ruby">
122
- class Please < Ick::Guard
123
- guard_with { |value, sym| value.respond_to?(sym) == true }
124
- evaluates_in_value_environment and returns_result
125
- belongs_to Object
126
- end
127
-
128
- please(...) { may.i.have.some.more }
129
- </pre>
130
-
131
- The method #please executes in the value's environment, and thus it can call methods directly.
113
+ h2. Is Ick for me?
132
114
 
133
- You already saw #let, it takes your expression and binds it to a parameter, then it evaluates a block in the calling environment, just as #try evaluates and guards its block in the calling environment. If you want something that behaves like #let but evaluates in the value's environment just like #please, you can use #my:
115
+ Ick does provide a number of very convenient methods for abstracting evaluation. If you adopt them for your project, your code will be more readable, more succinct, and easier to refactor. That being said, it will not make your teeth whiter or help you sleep if you have a newborn.
134
116
 
135
- <pre syntax="ruby">
136
- my(Person.find(:first, ...)) do
137
- first_name = 'Charles'
138
- last_name = 'Babbage'
139
- friends << 'Ada Lovelace'
140
- end
141
- </pre>
142
-
143
- This will return Charles Babbage's friends. On the surface, _evaluates_in_value_environment_ is about the syntactic sugar of dropping an instance variable. But with a little thought, you can come up with some really cool way to (mis)use this capability.
144
-
145
- So #let and #my both pass an expression to a block and return the result. Given that they both 'declare' _returns_result_, this is not surprising. But there is another choice: _returns_value_ instead of _returns_result_. Ruby on Rails includes the popular #returning method, and it works the same in Ick:
146
-
147
- <pre syntax="ruby">
148
- returning(Person.find(:first, ...)) do |p|
149
- p.first_name = 'Charles'
150
- p.last_name = 'Babbage'
151
- p.friends << 'Ada Lovelace'
152
- end
153
- </pre>
117
+ _Every programming problem can be solved with another layer of abstraction, except the problem of too many layers of abstraction_
154
118
 
155
- This returns the person record, not the list of friends. The block is evaluated strictly for side effects. And what happens if we want to return the value and also evaluate in the value's environment?
119
+ The important caveat about Ick is that its implementation adds abstraction. If all you want are two or three specific methods, writing them as simply and as directly as possible makes for a very simple implementation. Ick uses classes and templates instead of simple methods so that when you are ready to start writing your own abstractions, you can easily use the pieces that Ick has built-in. If Ick was written as a collection of cool methods, you would not be able to extend it without copying and pasting.
156
120
 
157
- <pre syntax="ruby">
158
- inside(Person.find(:first, ...)) do
159
- first_name = 'Charles'
160
- last_name = 'Babbage'
161
- friends << 'Ada Lovelace'
162
- end
163
- </pre>
164
-
165
- The method #inside returns the value and evaluates the block in the value's environment.
166
-
167
- h2. Under the Hood
168
-
169
- Ick is actually a construction kit. By all means install the gem and go wild with #let, #returning, #my, #inside, #try, and #maybe. But have a look under the hood. It's easy to build your own methods.
170
-
171
- h3. Every programming problem can be solved with another layer of abstraction, except the problem of too many layers of abstraction
172
-
173
- Ick uses classes and template methods to replicate what can be done in a few lines of explicit code. For example, Object#returning is implemented in Rails as:
174
-
175
- <pre syntax="ruby">
176
- class Object
177
- def returning(value)
178
- yield(value)
179
- value
180
- end
181
- end
182
- </pre>
183
-
184
- So why bother with Ick? Well, Ick is a construction kit. if you want to make a method just like Object#returning, only _X_ (for some value of X), you can't do that without copying, pasting, and modifying. Ick's classes are included specifically so you can subclass things and make your own new kinds of methods that are variations of the existing methods.
185
-
186
- Thus, the extra abstraction is appropriate if you want to use the built-in methods as a starting point for your own exploratory programming. And if you don't care, you just want the methods, by all means install the gem and just use them. Don't worry about the implementation unless you identify it as a performance problem.
121
+ Ick raises how you handle things to the level of first-class objects in Ruby, so you can mix and match and separate concerns as you see fit. Logging, permissions, error handling... These are some of the places you can take Ick. Have fun.
187
122
 
188
- h3. Where do you want to go today?
123
+ h2. Where can I read about what's going on inside Ick?
189
124
 
190
- The point behind abstracting invocation and evaluation is that you can _separate concerns_. For example, which methods to chain is one concern. How to handle nil or an object that does not respond to a method is a separate concern. Should you raise and handle and exception? Return nil? log an error? Why should error handling and logging be intermingled with your code?
125
+ "Inside Ick":inside.html
191
126
 
192
- With Ick, you can separate the two issues. You can even make the handling pluggable. For example, if instead of calling #let you call your own method, you could sometimes invoke @Ick::Let@ with a block and other times invoke your own handler, perhaps one that logs every method called.
127
+ h2. Administrivia
193
128
 
194
- Ick raises how you handle things to the level of first-class objects in Ruby, so you can mix and match and separate concerns as you see fit. Logging, permissions, error handling... These are some of the places you can take Ick. Have fun.
129
+ h3. Home Sweet Home
195
130
 
196
- h2. Administrivia
131
+ "ick.rubyforge.org":http://ick.rubyforge.org
197
132
 
198
133
  h3. How to submit patches
199
134
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: ick
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2008-03-08 00:00:00 -05:00
6
+ version: 0.2.1
7
+ date: 2008-03-09 00:00:00 -05:00
8
8
  summary: Invocation Construction Kit
9
9
  require_paths:
10
10
  - lib
@@ -38,7 +38,13 @@ files:
38
38
  - config/hoe.rb
39
39
  - config/requirements.rb
40
40
  - lib/ick.rb
41
+ - lib/ick/advisor.rb
42
+ - lib/ick/base.rb
43
+ - lib/ick/guard.rb
44
+ - lib/ick/sugar.rb
45
+ - lib/ick/tee.rb
41
46
  - lib/ick/version.rb
47
+ - lib/ick/wrap.rb
42
48
  - log/debug.log
43
49
  - script/destroy
44
50
  - script/generate