-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
370 lines (337 loc) · 8.72 KB
/
index.html
File metadata and controls
370 lines (337 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>ruby refinements</title>
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/solarized.css">
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="lib/css/zenburn.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
<style>
.reveal pre {
box-shadow: none;
width: 100%;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h3>let's do ruby refinements!</h3>
<img src="Ruby_Ready.png" style="border:none; background: none; box-shadow: none; height: 200px;"></section>
<section>
<h4>ruby has a nice central metaphor</h4>
<pre>
<code data-trim data-noescape class="ruby">
receiver = Receiver.new
receiver.message
# or, implicit receiver
message
# => NameError: undefined local variable or method `message' for main:Object
# this metaphor also applies to core types
[1, 2, 3].first
</code>
</pre>
</section>
<section>
<h4>but it has some limits...</h4>
<pre>
<code data-trim data-noescape class="ruby">
class Cat
def meow
"meow"
end
def louder
upcase + " said the cat"
end
end
c = Cat.new
c.meow.louder
# => NoMethodError: undefined method `louder' for "meow":String
</code>
</pre>
</section>
<section>
<h4>solution 1: wrap it in a method</h4>
<pre>
<code data-trim data-noescape class="ruby">
class Cat
def meow
"meow"
end
def louder(meow)
meow.upcase + " said the cat"
end
end
c = Cat.new
c.louder(c.meow)
# => "MEOW said the cat"
</code>
</pre>
</section>
<section>
<h4>solution 2: return self & use side-effects</h4>
<pre>
<code data-trim data-noescape class="ruby">
class Cat
attr_reader :meow
def meow
@meow = "meow"
self
end
def louder
@meow.upcase + " said the cat"
end
end
c = Cat.new
c.meow.louder
# => "MEOW said the cat"
</code>
</pre>
</section>
<section>
<h4>solution 3: monkeypatch</h4>
<pre>
<code data-trim data-noescape class="ruby">
class String
def louder
upcase + " said the cat"
end
end
</code>
</pre>
<pre>
<code data-trim data-noescape class="ruby">
class Cat
def meow
"meow"
end
end
c = Cat.new
c.meow.louder
# => "MEOW said the cat"
</code>
</pre>
</section>
<section>
<ul>
<li>solution 1: <span class="fragment">boilerplate 😫💢💔😟</span></li>
<li>solution 2: <span class="fragment">boilerplate 😱💩☣️</span></li>
<li>solution 3: <span class="fragment">no boilerplate!!! 😍💯💯👍</span></li>
</ul>
</section>
<section>
<h5>monkeypatching is bad!!!!</h5>
<img src="JasperHelmetVisor.png" style="border:none; background: none; box-shadow: none; height: 400px;">
<img src="Worried_Ruby.png" style="border:none; background: none; box-shadow: none; height: 200px;">
</section>
<section>
<h4>this is bad</h4>
<pre>
<code data-trim data-noescape class="ruby">
class Array
def first
if rand(1..10) == 1
'wat'
else
super
end
end
end
</code>
</pre>
</section>
<section>
<h4>fine!! i love boilerplate! ill just write that FOREVER</h4>
<img src="Mad_Ruby.png" style="border:none; background: none; box-shadow: none;">
</section>
<section>
<h4>hold up. you can...</h4>
<img src="NewGarnetRev.png" style="border:none; background: none; box-shadow: none;">
</section>
<section>
<h4>✨refine it✨</h4>
<pre>
<code data-trim data-noescape class="ruby">
module Loud
refine String do
def louder
upcase + " said the cat"
end
end
end
</code>
</pre>
<pre>
<code data-trim data-noescape class="ruby">
class Cat
def meow
"meow"
end
end
<mark>using Loud</mark>
c = Cat.new
c.meow.louder
# => "MEOW said the cat"
</code>
</pre>
</section>
<section>
<h4><a href="https://docs.ruby-lang.org/en/2.4.0/syntax/refinements_rdoc.html">refinements</a> will respect scope</h4>
<pre>
<code data-trim data-noescape class="ruby">
# not activated here
class Foo
# not activated here
def foo
# not activated here
end
using M
# activated here
def bar
# activated here
end
# activated here
end
# not activated here
</code>
</pre>
<h4 class="fragment">(which is why it beats monkeypatching 🍌)</h4>
</section>
<section>
<h3>examples</h3>
<img src="ruby.png" style="border:none; background: none; box-shadow: none; height: 200px;">
</section>
<section>
<h4>a real life example (part one)</h4>
<pre>
<code data-trim data-noescape class="ruby">
module HashFmap
refine Hash do
def fmap &block
self.reduce({}) { |memo, (k,v)| memo.merge!({ k => block.call(v) }) }
end
end
end
</code>
</pre>
<pre>
<code data-trim data-noescape class="ruby">
h = {:foo=>"bar", :biz=>"baz"}
h.map {|k, v| v.upcase }
# => ["BAR", "BAZ"]
using HashFmap
h.fmap(&:upcase)
# => {:foo=>"BAR", :biz=>"BAZ"}
</code>
</pre>
</section>
<section>
<h4>a real life example (part two)</h4>
<pre>
<code data-trim data-noescape class="ruby">
module ThirdPartyAPIParams
refine ModelOne do
def params
{ ParamOne: 21 }
end
end
refine ModelTwo do
def params
{ ParamTwo: 22 }
end
end
end
</code>
</pre>
<pre>
<code data-trim data-noescape class="ruby">
class Worker
using ThirdPartyAPIParams
def perform(id, model)
m = model.find(id)
APIClient.post(m.params)
end
end
</code>
</pre>
</section>
<section>
<h4>a real life example (part three)</h4>
<pre>
<code data-trim data-noescape class="ruby">
module MyWeirdFormatting
refine Thing do
def as_weird_format
to_s.chars.map(&:ord).map {|c| c.to_s(2) }.join
end
end
end
</code>
</pre>
<pre>
<code data-trim data-noescape class="ruby">
class ThingService
using MyWeirdFormatting
attr_accessor :thing
def initialize(thing)
@thing = thing
end
def store
$db.store(thing.id, thing.as_weird_format)
end
end
</code>
</pre>
</section>
<section>
<h3>re: why not just use a mixin or inheritence??</h3>
<p class="fragment">Do you <i>really</i> need every method on your class everywhere your class appears?</p>
<p class="fragment"><em>Really really??</em></p>
<p class="fragment">My opinion: when an object has a smaller "surface area" of methods at base, and I extend its behavior explicitly in select contexts, I find it easier to reason about.</p>
</section>
<section>
<pre>
<code data-trim data-noescape class="ruby">
pry(main)> my_refined_object.methods
# => [ :to_s, :as_json, :inherited ]
pry(main)> my_object_bloated_with_mixins.methods
# => [ :to_s, :as_json, :random_method, :irrelevant, :wont_work, :inherited , :idc, :definitely_wrong, :mysterious ]
</code>
</pre>
<p>Literally, pry into some code and call .methods on it. What's truly useful for your receiver to accept as a message <em>in that calling context?</em></p>
</section>
<section>
<h4>thanks for listening!</h4>
<img src="ruby2.png" style="border:none; background: none; box-shadow: none; height: 200px;">
<h4>questions?</h4>
</section>
</div>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.js"></script>
<script>
// More info https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
history: true,
// More info https://github.com/hakimel/reveal.js#dependencies
dependencies: [
{ src: 'plugin/markdown/marked.js' },
{ src: 'plugin/markdown/markdown.js' },
{ src: 'plugin/notes/notes.js', async: true },
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
]
});
</script>
</body>
</html>