-
Notifications
You must be signed in to change notification settings - Fork 1
/
type_transmogrification.html
122 lines (106 loc) · 2.59 KB
/
type_transmogrification.html
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
<html>
<head>
<title>Ruby DSL Patterns - type transmogrification</title>
<link rel="stylesheet" type="text/css" href="styles.css"></link>
<script src="clean_pre.js"></script>
</head>
<body>
<h1>type transmogrification</h1>
<h2>description</h2>
<p>Transform types as needed as part of a fluent interface call.</p>
<h2>methods</h2>
<ul>
<li>
<p>method chaining returning a different type in each chain</p>
<h3>unit conversion</h3>
<p>
<pre><code class='ruby'>
# Getting "Flour" unit to recipe
recipe.add 200.pounds.of "Flour"
# Open Numeric class to add methods and aliases
class Numeric
def gram
self
end
alias_method :grams, :gram
def pound
self * 453.59237
end
alias_method :pounds, :pound
alias_method :lb, :pound
alias_method :lbs, :pound
def of ingredient
if ingredient.kind_of? String
ingredient = Ingredient.new(ingredient)
end
ingredient.quantity = self
ingredient
end
end
</code></pre>
</p>
</li>
</ul>
<h2>problems</h2>
<ul>
<li>
<p>Who returns what?</p>
<p>
<pre><code class='ruby'>
# Problem: What would each method in sequence return?
1.pound.of "Flour"
</code></pre>
</p>
</li>
<li>
<p>Character noise</p>
<p>
<pre><code class='ruby'>
# Problem: Must remember to put qoutes around our objec type?
1.pound.of "Flour"
# Solution 1: Use const_missing on Object
1.pound.of Flour
# Any constant that's missing becomes an ingredient! Yikes! That's an anti-pattern!
class Object
def self.const_missing(sym)
Ingredient.new(sym.to_s)
end
end
# Solution 2: Mix const_missing in (don't provide universe wide access to whacky stuff you've implemented for your dsl)
module IngredientBuilder
def self.append_features(target)
def target.const_missing(name)
Ingredient.new(name.to_s)
end
super
end
end
class TestIngredients < Test::Unit::TestCase
include IngredientBuilder
def test_ingredient_factory
i = Flour
assert i.kind_of? Ingredient
assert_equal(i.name, "Flour")
end
end
# Trick: Smarter const factories
module SmartIngredientBuilder
INGREDIENTS = {
"Floor" => "Flour", "Flower" => "Flour", "Flur" => "Flour"
}
def self.append_ffeatures(target)
def target.const_missing(name)
i = INGREDIENTS.keys.find do |val|
name.to_s == val
end
return Ingredient.new(INGREDIENT[i] unless i.nil?
raise "No such ingredient"
end
end
end
</code></pre>
</p>
</li>
</ul>
</body>
</html>