rep 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +11 -0
- data/README.md +8 -14
- data/Rakefile +18 -1
- data/docs/index.html +478 -0
- data/docs/lib/rep.html +478 -0
- data/docs/lib/rep/version.html +44 -0
- data/docs/rep.html +478 -0
- data/docs/rep/version.html +44 -0
- data/lib/rep.rb +180 -16
- data/lib/rep/version.rb +1 -1
- data/rep.gemspec +4 -0
- data/test/lib/rep_test.rb +47 -13
- data/test/test_helper.rb +0 -6
- metadata +72 -2
data/docs/lib/rep.html
ADDED
@@ -0,0 +1,478 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
5
|
+
<title>rep.rb</title>
|
6
|
+
<link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<div id='container'>
|
10
|
+
<div id="background"></div>
|
11
|
+
<div id="jump_to">
|
12
|
+
Jump To …
|
13
|
+
<div id="jump_wrapper">
|
14
|
+
<div id="jump_page">
|
15
|
+
<a class="source" href="rep.html">rep.rb</a>
|
16
|
+
<a class="source" href="rep/version.html">version.rb</a>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<table cellspacing=0 cellpadding=0>
|
21
|
+
<thead>
|
22
|
+
<tr>
|
23
|
+
<th class=docs><h1>rep.rb</h1></th>
|
24
|
+
<th class=code></th>
|
25
|
+
</tr>
|
26
|
+
</thead>
|
27
|
+
<tbody>
|
28
|
+
<tr id='section-1'>
|
29
|
+
<td class=docs>
|
30
|
+
<div class="pilwrap">
|
31
|
+
<a class="pilcrow" href="#section-1">¶</a>
|
32
|
+
</div>
|
33
|
+
<p><strong>Rep</strong> is a small module to endow any class to make json quickly. It solves four problems:</p>
|
34
|
+
|
35
|
+
<ol>
|
36
|
+
<li>Enumerating top level keys for a json structure</li>
|
37
|
+
<li>Providing a convention for the value of those keys</li>
|
38
|
+
<li>Defining <code>attr_accessor</code>’s that are prefilled from an options hash given to <code>#initialize</code></li>
|
39
|
+
<li>Sharing instances to help GC</li>
|
40
|
+
</ol>
|
41
|
+
|
42
|
+
|
43
|
+
<p>The code is available on <a href="http://github.com/myobie/rep">github</a>.</p>
|
44
|
+
</td>
|
45
|
+
<td class=code>
|
46
|
+
<div class='highlight'><pre></pre></div>
|
47
|
+
</td>
|
48
|
+
</tr>
|
49
|
+
<tr id='section-2'>
|
50
|
+
<td class=docs>
|
51
|
+
<div class="pilwrap">
|
52
|
+
<a class="pilcrow" href="#section-2">¶</a>
|
53
|
+
</div>
|
54
|
+
<p><code>Forwardable</code> is in the stdlib and allows ruby objects to delegate methods off to other objects. An example:</p>
|
55
|
+
|
56
|
+
<pre><code>class A
|
57
|
+
extend Forwardable
|
58
|
+
delegate [:length, :first] => :@array
|
59
|
+
def initialize(array = [])
|
60
|
+
@array = array
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
A.new([1,2,3]).length # => 3
|
65
|
+
A.new([1,2,3]).first # => 1
|
66
|
+
</code></pre>
|
67
|
+
</td>
|
68
|
+
<td class=code>
|
69
|
+
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'forwardable'</span></pre></div>
|
70
|
+
</td>
|
71
|
+
</tr>
|
72
|
+
<tr id='section-3'>
|
73
|
+
<td class=docs>
|
74
|
+
<div class="pilwrap">
|
75
|
+
<a class="pilcrow" href="#section-3">¶</a>
|
76
|
+
</div>
|
77
|
+
<p><code>JSON::generate</code> and <code>JSON::decode</code> are much safer to use than <code>Object#to_json</code>.</p>
|
78
|
+
</td>
|
79
|
+
<td class=code>
|
80
|
+
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'json'</span>
|
81
|
+
|
82
|
+
<span class="nb">require</span> <span class="s1">'rep/version'</span>
|
83
|
+
<span class="k">module</span> <span class="nn">Rep</span></pre></div>
|
84
|
+
</td>
|
85
|
+
</tr>
|
86
|
+
<tr id='section-4'>
|
87
|
+
<td class=docs>
|
88
|
+
<div class="pilwrap">
|
89
|
+
<a class="pilcrow" href="#section-4">¶</a>
|
90
|
+
</div>
|
91
|
+
<p>All classes that <code>include Rep</code> are extended with <code>Forwardable</code>,
|
92
|
+
given some aliases, endowned with <code>HashieSupport</code> if Hashie is loaded,
|
93
|
+
and setup an empty <code>#parse_opts</code> because it is required for <code>::shared</code>.</p>
|
94
|
+
</td>
|
95
|
+
<td class=code>
|
96
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">included</span><span class="p">(</span><span class="n">klass</span><span class="p">)</span>
|
97
|
+
<span class="n">klass</span><span class="o">.</span><span class="n">extend</span> <span class="no">Forwardable</span>
|
98
|
+
<span class="n">klass</span><span class="o">.</span><span class="n">extend</span> <span class="no">ClassMethods</span>
|
99
|
+
<span class="n">klass</span><span class="o">.</span><span class="n">instance_eval</span> <span class="p">{</span>
|
100
|
+
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
|
101
|
+
<span class="k">alias</span> <span class="n">forward</span> <span class="n">delegate</span>
|
102
|
+
|
103
|
+
<span class="k">unless</span> <span class="n">defined?</span><span class="p">(</span><span class="n">fields</span><span class="p">)</span>
|
104
|
+
<span class="k">alias</span> <span class="n">fields</span> <span class="n">json_fields</span>
|
105
|
+
<span class="k">end</span>
|
106
|
+
|
107
|
+
<span class="k">if</span> <span class="n">defined?</span><span class="p">(</span><span class="no">Hashie</span><span class="p">)</span>
|
108
|
+
<span class="kp">include</span> <span class="no">HashieSupport</span>
|
109
|
+
<span class="k">end</span>
|
110
|
+
<span class="k">end</span>
|
111
|
+
|
112
|
+
<span class="k">unless</span> <span class="n">defined?</span><span class="p">(</span><span class="n">parse_opts</span><span class="p">)</span>
|
113
|
+
<span class="k">def</span> <span class="nf">parse_opts</span><span class="p">(</span><span class="n">opts</span> <span class="o">=</span> <span class="p">{});</span> <span class="k">end</span>
|
114
|
+
<span class="k">end</span>
|
115
|
+
<span class="p">}</span>
|
116
|
+
<span class="k">end</span></pre></div>
|
117
|
+
</td>
|
118
|
+
</tr>
|
119
|
+
<tr id='section-5'>
|
120
|
+
<td class=docs>
|
121
|
+
<div class="pilwrap">
|
122
|
+
<a class="pilcrow" href="#section-5">¶</a>
|
123
|
+
</div>
|
124
|
+
<p>Since a goal is to be able to share instances, we need an easy way to reset a
|
125
|
+
shared instance back to factory defaults. If you memoize any methods that are
|
126
|
+
not declared as json fields, then overried this method and set any memoized
|
127
|
+
variables to nil, then super.</p>
|
128
|
+
</td>
|
129
|
+
<td class=code>
|
130
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">reset_for_json!</span>
|
131
|
+
<span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">all_json_methods</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">method_name</span><span class="o">|</span>
|
132
|
+
<span class="nb">instance_variable_set</span><span class="p">(</span><span class="ss">:"@</span><span class="si">#{</span><span class="n">method_name</span><span class="si">}</span><span class="ss">"</span><span class="p">,</span> <span class="kp">nil</span><span class="p">)</span>
|
133
|
+
<span class="k">end</span>
|
134
|
+
<span class="k">end</span></pre></div>
|
135
|
+
</td>
|
136
|
+
</tr>
|
137
|
+
<tr id='section-6'>
|
138
|
+
<td class=docs>
|
139
|
+
<div class="pilwrap">
|
140
|
+
<a class="pilcrow" href="#section-6">¶</a>
|
141
|
+
</div>
|
142
|
+
<p>All the work of generating a hash from an instance is packaged up in one method. Since
|
143
|
+
fields can be aliases in the format <code>{ :json_key_name => :method_name }</code>, there
|
144
|
+
is some fancy logic to determine the <code>field_name</code> and <code>method_name</code> variables.</p>
|
145
|
+
|
146
|
+
<pre><code>{ :one => :foo }.to_a # => [[:one, :foo]]
|
147
|
+
</code></pre>
|
148
|
+
|
149
|
+
<p>Right now it will raise if either a field doesn’t have a method to provide it’s value or
|
150
|
+
if there are no json fields setup for the particular set (which defaults to <code>:default</code>).</p>
|
151
|
+
</td>
|
152
|
+
<td class=code>
|
153
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">to_hash</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="ss">:default</span><span class="p">)</span>
|
154
|
+
<span class="k">if</span> <span class="n">fields</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">json_fields</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
|
155
|
+
<span class="n">fields</span><span class="o">.</span><span class="n">reduce</span><span class="p">({})</span> <span class="k">do</span> <span class="o">|</span><span class="n">memo</span><span class="p">,</span> <span class="n">field</span><span class="o">|</span>
|
156
|
+
<span class="n">field_name</span><span class="p">,</span> <span class="n">method_name</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Hash</span><span class="p">)</span> <span class="p">?</span> <span class="n">field</span><span class="o">.</span><span class="n">to_a</span><span class="o">.</span><span class="n">first</span> <span class="p">:</span> <span class="o">[</span><span class="n">field</span><span class="p">,</span> <span class="n">field</span><span class="o">]</span>
|
157
|
+
<span class="k">begin</span>
|
158
|
+
<span class="n">memo</span><span class="o">[</span><span class="n">field_name</span><span class="o">]</span> <span class="o">=</span> <span class="nb">send</span><span class="p">(</span><span class="n">method_name</span><span class="p">)</span>
|
159
|
+
<span class="k">rescue</span> <span class="no">NoMethodError</span> <span class="o">=></span> <span class="n">e</span>
|
160
|
+
<span class="n">message</span> <span class="o">=</span> <span class="s2">"There is no method named '</span><span class="si">#{</span><span class="n">method_name</span><span class="si">}</span><span class="s2">' for the class '</span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="si">}</span><span class="s2">' for the '</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">' list of fields : </span><span class="si">#{</span><span class="n">e</span><span class="o">.</span><span class="n">message</span><span class="si">}</span><span class="s2">"</span>
|
161
|
+
<span class="k">raise</span> <span class="no">NoMethodError</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">method_name</span><span class="p">,</span> <span class="n">e</span><span class="o">.</span><span class="n">args</span><span class="p">)</span>
|
162
|
+
<span class="k">end</span>
|
163
|
+
<span class="n">memo</span>
|
164
|
+
<span class="k">end</span>
|
165
|
+
<span class="k">else</span>
|
166
|
+
<span class="k">raise</span> <span class="s2">"There are no json fields under the name: </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span>
|
167
|
+
<span class="k">end</span>
|
168
|
+
<span class="k">end</span>
|
169
|
+
|
170
|
+
<span class="k">def</span> <span class="nf">to_json</span>
|
171
|
+
<span class="no">JSON</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="n">to_hash</span><span class="p">)</span>
|
172
|
+
<span class="k">end</span>
|
173
|
+
|
174
|
+
<span class="k">module</span> <span class="nn">ClassMethods</span></pre></div>
|
175
|
+
</td>
|
176
|
+
</tr>
|
177
|
+
<tr id='section-7'>
|
178
|
+
<td class=docs>
|
179
|
+
<div class="pilwrap">
|
180
|
+
<a class="pilcrow" href="#section-7">¶</a>
|
181
|
+
</div>
|
182
|
+
<p>Defines an attr_accessor with a default value. The default for default is nil. Example:</p>
|
183
|
+
|
184
|
+
<pre><code>class A
|
185
|
+
register_accessor :name => "No Name"
|
186
|
+
end
|
187
|
+
|
188
|
+
A.new.name # => "No Name"
|
189
|
+
</code></pre>
|
190
|
+
</td>
|
191
|
+
<td class=code>
|
192
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">register_accessor</span><span class="p">(</span><span class="n">acc</span><span class="p">)</span>
|
193
|
+
<span class="nb">name</span><span class="p">,</span> <span class="n">default</span> <span class="o">=</span> <span class="n">acc</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Hash</span><span class="p">)</span> <span class="p">?</span> <span class="n">acc</span><span class="o">.</span><span class="n">to_a</span><span class="o">.</span><span class="n">first</span> <span class="p">:</span> <span class="o">[</span><span class="n">acc</span><span class="p">,</span> <span class="kp">nil</span><span class="o">]</span>
|
194
|
+
<span class="kp">attr_accessor</span> <span class="nb">name</span>
|
195
|
+
<span class="k">if</span> <span class="n">default</span>
|
196
|
+
<span class="n">define_method</span> <span class="nb">name</span> <span class="k">do</span>
|
197
|
+
<span class="n">var_name</span> <span class="o">=</span> <span class="ss">:"@</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="ss">"</span>
|
198
|
+
<span class="nb">instance_variable_get</span><span class="p">(</span><span class="n">var_name</span><span class="p">)</span> <span class="o">||</span> <span class="nb">instance_variable_set</span><span class="p">(</span><span class="n">var_name</span><span class="p">,</span> <span class="n">default</span><span class="p">)</span>
|
199
|
+
<span class="k">end</span>
|
200
|
+
<span class="k">end</span>
|
201
|
+
<span class="k">end</span></pre></div>
|
202
|
+
</td>
|
203
|
+
</tr>
|
204
|
+
<tr id='section-8'>
|
205
|
+
<td class=docs>
|
206
|
+
<div class="pilwrap">
|
207
|
+
<a class="pilcrow" href="#section-8">¶</a>
|
208
|
+
</div>
|
209
|
+
<p>Defines an <code>#initialize</code> method that accepts a Hash argument and copies some keys out into <code>attr_accessors</code>.
|
210
|
+
If your class already has an <code>#iniatialize</code> method then this will overwrite it (so don’t use it). <code>#initialize_with</code>
|
211
|
+
does not have to be used to use any other parts of Rep.</p>
|
212
|
+
</td>
|
213
|
+
<td class=code>
|
214
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">initialize_with</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
|
215
|
+
<span class="vi">@initializiation_args</span> <span class="o">=</span> <span class="n">args</span></pre></div>
|
216
|
+
</td>
|
217
|
+
</tr>
|
218
|
+
<tr id='section-9'>
|
219
|
+
<td class=docs>
|
220
|
+
<div class="pilwrap">
|
221
|
+
<a class="pilcrow" href="#section-9">¶</a>
|
222
|
+
</div>
|
223
|
+
<p>Remember what args we normally initialize with so we can refer to them when building shared instances.</p>
|
224
|
+
</td>
|
225
|
+
<td class=code>
|
226
|
+
<div class='highlight'><pre> <span class="n">define_singleton_method</span> <span class="ss">:initializiation_args</span> <span class="k">do</span>
|
227
|
+
<span class="vi">@initializiation_args</span>
|
228
|
+
<span class="k">end</span></pre></div>
|
229
|
+
</td>
|
230
|
+
</tr>
|
231
|
+
<tr id='section-10'>
|
232
|
+
<td class=docs>
|
233
|
+
<div class="pilwrap">
|
234
|
+
<a class="pilcrow" href="#section-10">¶</a>
|
235
|
+
</div>
|
236
|
+
<p>Create an <code>attr_accessor</code> for each one. Defaults can be provided using the Hash version { :arg => :default_value }</p>
|
237
|
+
</td>
|
238
|
+
<td class=code>
|
239
|
+
<div class='highlight'><pre> <span class="n">args</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="o">|</span> <span class="n">register_accessor</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="p">}</span>
|
240
|
+
|
241
|
+
<span class="n">define_method</span><span class="p">(</span><span class="ss">:initialize</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">opts</span> <span class="o">=</span> <span class="p">{}</span><span class="o">|</span> <span class="n">parse_opts</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span> <span class="p">}</span></pre></div>
|
242
|
+
</td>
|
243
|
+
</tr>
|
244
|
+
<tr id='section-11'>
|
245
|
+
<td class=docs>
|
246
|
+
<div class="pilwrap">
|
247
|
+
<a class="pilcrow" href="#section-11">¶</a>
|
248
|
+
</div>
|
249
|
+
<p><code>#parse_opts</code> is responsable for getting the <code>attr_accessor</code> values prefilled. Since defaults can be specified, it
|
250
|
+
must negotiate Hashes and use the first key of the hash for the <code>attr_accessor</code>’s name.</p>
|
251
|
+
</td>
|
252
|
+
<td class=code>
|
253
|
+
<div class='highlight'><pre> <span class="n">define_method</span> <span class="ss">:parse_opts</span> <span class="k">do</span> <span class="o">|</span><span class="n">opts</span><span class="o">|</span>
|
254
|
+
<span class="vi">@rep_options</span> <span class="o">=</span> <span class="n">opts</span>
|
255
|
+
<span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">initializiation_args</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">field</span><span class="o">|</span>
|
256
|
+
<span class="nb">name</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Hash</span><span class="p">)</span> <span class="p">?</span> <span class="n">field</span><span class="o">.</span><span class="n">to_a</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">first</span> <span class="p">:</span> <span class="n">field</span>
|
257
|
+
<span class="nb">instance_variable_set</span><span class="p">(</span><span class="ss">:"@</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="ss">"</span><span class="p">,</span> <span class="n">opts</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span><span class="p">)</span>
|
258
|
+
<span class="k">end</span>
|
259
|
+
<span class="k">end</span>
|
260
|
+
<span class="k">end</span></pre></div>
|
261
|
+
</td>
|
262
|
+
</tr>
|
263
|
+
<tr id='section-12'>
|
264
|
+
<td class=docs>
|
265
|
+
<div class="pilwrap">
|
266
|
+
<a class="pilcrow" href="#section-12">¶</a>
|
267
|
+
</div>
|
268
|
+
<p><code>#json_fields</code> setups up some class instance variables to remember sets of top level keys for json structures. Example:</p>
|
269
|
+
|
270
|
+
<pre><code>class A
|
271
|
+
json_fields [:one, :two, :three] => :default
|
272
|
+
end
|
273
|
+
|
274
|
+
A.json_fields(:default) # => [:one, :two, :three]
|
275
|
+
</code></pre>
|
276
|
+
|
277
|
+
<p>There is a general assumption that each top level key’s value is provided by a method of the same name on an instance
|
278
|
+
of the class. If this is not true, a Hash syntax can be used to alias to a different method name. Example:</p>
|
279
|
+
|
280
|
+
<pre><code>class A
|
281
|
+
json_fields [{ :one => :the_real_one_method }, :two, { :three => :some_other_three }] => :default
|
282
|
+
end
|
283
|
+
</code></pre>
|
284
|
+
|
285
|
+
<p>Once can also set multiple sets of fields. Example:</p>
|
286
|
+
|
287
|
+
<pre><code>class A
|
288
|
+
json_fields [:one, :two, :three] => :default
|
289
|
+
json_fields [:five, :two, :six] => :other
|
290
|
+
end
|
291
|
+
</code></pre>
|
292
|
+
|
293
|
+
<p>And all fields are returned by calling <code>#json_fields</code> with no args. Example:</p>
|
294
|
+
|
295
|
+
<pre><code>A.json_fields # => { :default => [:one, :two, :three], :other => [:five, :two, :six] }
|
296
|
+
</code></pre>
|
297
|
+
</td>
|
298
|
+
<td class=code>
|
299
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">json_fields</span><span class="p">(</span><span class="n">arg</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
|
300
|
+
<span class="k">if</span> <span class="n">arg</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Hash</span><span class="p">)</span>
|
301
|
+
<span class="n">fields</span><span class="p">,</span> <span class="nb">name</span> <span class="o">=</span> <span class="n">arg</span><span class="o">.</span><span class="n">to_a</span><span class="o">.</span><span class="n">first</span>
|
302
|
+
<span class="vi">@json_fields</span> <span class="o">||=</span> <span class="p">{}</span>
|
303
|
+
<span class="vi">@json_fields</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="n">fields</span><span class="o">].</span><span class="n">flatten</span>
|
304
|
+
<span class="k">elsif</span> <span class="n">arg</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Symbol</span><span class="p">)</span>
|
305
|
+
<span class="vi">@json_fields</span> <span class="o">||=</span> <span class="p">{}</span>
|
306
|
+
<span class="vi">@json_fields</span><span class="o">[</span><span class="n">arg</span><span class="o">]</span>
|
307
|
+
<span class="k">elsif</span> <span class="n">arg</span> <span class="o">===</span> <span class="kp">nil</span>
|
308
|
+
<span class="vi">@json_fields</span> <span class="o">||</span> <span class="p">{}</span>
|
309
|
+
<span class="k">else</span></pre></div>
|
310
|
+
</td>
|
311
|
+
</tr>
|
312
|
+
<tr id='section-13'>
|
313
|
+
<td class=docs>
|
314
|
+
<div class="pilwrap">
|
315
|
+
<a class="pilcrow" href="#section-13">¶</a>
|
316
|
+
</div>
|
317
|
+
<p>TODO: make an exception class</p>
|
318
|
+
</td>
|
319
|
+
<td class=code>
|
320
|
+
<div class='highlight'><pre> <span class="k">raise</span> <span class="s2">"You can only use a Hash to set fields, a Symbol to retrieve them, or no argument to retrieve all fields for all names"</span>
|
321
|
+
<span class="k">end</span>
|
322
|
+
<span class="k">end</span></pre></div>
|
323
|
+
</td>
|
324
|
+
</tr>
|
325
|
+
<tr id='section-14'>
|
326
|
+
<td class=docs>
|
327
|
+
<div class="pilwrap">
|
328
|
+
<a class="pilcrow" href="#section-14">¶</a>
|
329
|
+
</div>
|
330
|
+
<p><code>#flat_json_fields</code> is just a utility method to DRY up the next two methods, because their code is almost exactly the same,
|
331
|
+
it is not intended for use directly and might be confusing.</p>
|
332
|
+
</td>
|
333
|
+
<td class=code>
|
334
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">flat_json_fields</span><span class="p">(</span><span class="n">side</span> <span class="o">=</span> <span class="ss">:right</span><span class="p">)</span>
|
335
|
+
<span class="n">side_number</span> <span class="o">=</span> <span class="n">side</span> <span class="o">==</span> <span class="ss">:right</span> <span class="p">?</span> <span class="mi">1</span> <span class="p">:</span> <span class="mi">0</span>
|
336
|
+
|
337
|
+
<span class="n">json_fields</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">memo</span><span class="p">,</span> <span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">fields</span><span class="p">)</span><span class="o">|</span>
|
338
|
+
<span class="n">memo</span> <span class="o">+</span> <span class="n">fields</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">field</span><span class="o">|</span>
|
339
|
+
<span class="k">if</span> <span class="n">field</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Hash</span><span class="p">)</span>
|
340
|
+
<span class="n">field</span><span class="o">.</span><span class="n">to_a</span><span class="o">.</span><span class="n">first</span><span class="o">[</span><span class="n">side_number</span><span class="o">]</span> <span class="c1"># [name, method_name]</span>
|
341
|
+
<span class="k">else</span>
|
342
|
+
<span class="n">field</span>
|
343
|
+
<span class="k">end</span>
|
344
|
+
<span class="k">end</span>
|
345
|
+
<span class="k">end</span><span class="o">.</span><span class="n">uniq</span>
|
346
|
+
<span class="k">end</span></pre></div>
|
347
|
+
</td>
|
348
|
+
</tr>
|
349
|
+
<tr id='section-15'>
|
350
|
+
<td class=docs>
|
351
|
+
<div class="pilwrap">
|
352
|
+
<a class="pilcrow" href="#section-15">¶</a>
|
353
|
+
</div>
|
354
|
+
<p>We need a way to get a flat, uniq'ed list of all the fields accross all field sets. This is that.</p>
|
355
|
+
</td>
|
356
|
+
<td class=code>
|
357
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">all_json_fields</span>
|
358
|
+
<span class="n">flat_json_fields</span><span class="p">(</span><span class="ss">:left</span><span class="p">)</span>
|
359
|
+
<span class="k">end</span></pre></div>
|
360
|
+
</td>
|
361
|
+
</tr>
|
362
|
+
<tr id='section-16'>
|
363
|
+
<td class=docs>
|
364
|
+
<div class="pilwrap">
|
365
|
+
<a class="pilcrow" href="#section-16">¶</a>
|
366
|
+
</div>
|
367
|
+
<p>We need a wya to get a flat, uniq'ed list of all the method names accross all field sets. This is that.</p>
|
368
|
+
</td>
|
369
|
+
<td class=code>
|
370
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">all_json_methods</span>
|
371
|
+
<span class="n">flat_json_fields</span><span class="p">(</span><span class="ss">:right</span><span class="p">)</span>
|
372
|
+
<span class="k">end</span></pre></div>
|
373
|
+
</td>
|
374
|
+
</tr>
|
375
|
+
<tr id='section-17'>
|
376
|
+
<td class=docs>
|
377
|
+
<div class="pilwrap">
|
378
|
+
<a class="pilcrow" href="#section-17">¶</a>
|
379
|
+
</div>
|
380
|
+
<p>An easy way to save on GC is to use the same instance to turn an array of objects into hashes instead
|
381
|
+
of instantiating a new object for every object in the array. Here is an example of it’s usage:</p>
|
382
|
+
|
383
|
+
<pre><code>class BookRep
|
384
|
+
initialize_with :book_model
|
385
|
+
fields :title => :default
|
386
|
+
forward :title => :book_model
|
387
|
+
end
|
388
|
+
|
389
|
+
BookRep.shared(:book_model => Book.first).to_hash # => { :title => "Moby Dick" }
|
390
|
+
BookRep.shared(:book_model => Book.last).to_hash # => { :title => "Lost Horizon" }
|
391
|
+
</code></pre>
|
392
|
+
|
393
|
+
<p>This should terrify you. If it doesn’t, then this example will:</p>
|
394
|
+
|
395
|
+
<pre><code>book1 = BookRep.shared(:book_model => Book.first)
|
396
|
+
book2 = BookRep.shared(:book_model => Book.last)
|
397
|
+
|
398
|
+
boo1.object_id === book2.object_id # => true
|
399
|
+
</code></pre>
|
400
|
+
|
401
|
+
<p><strong>It really is a shared object.</strong></p>
|
402
|
+
|
403
|
+
<p>You really shouldn’t use this method directly for anything.</p>
|
404
|
+
</td>
|
405
|
+
<td class=code>
|
406
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">shared</span><span class="p">(</span><span class="n">opts</span> <span class="o">=</span> <span class="p">{})</span>
|
407
|
+
<span class="vi">@pointer</span> <span class="o">=</span> <span class="p">(</span><span class="no">Thread</span><span class="o">.</span><span class="n">current</span><span class="o">[</span><span class="ss">:rep_shared_instances</span><span class="o">]</span> <span class="o">||=</span> <span class="p">{})</span>
|
408
|
+
<span class="vi">@pointer</span><span class="o">[</span><span class="nb">object_id</span><span class="o">]</span> <span class="o">||=</span> <span class="kp">new</span>
|
409
|
+
<span class="vi">@pointer</span><span class="o">[</span><span class="nb">object_id</span><span class="o">].</span><span class="n">reset_for_json!</span>
|
410
|
+
<span class="vi">@pointer</span><span class="o">[</span><span class="nb">object_id</span><span class="o">].</span><span class="n">parse_opts</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span>
|
411
|
+
<span class="vi">@pointer</span><span class="o">[</span><span class="nb">object_id</span><span class="o">]</span>
|
412
|
+
<span class="k">end</span></pre></div>
|
413
|
+
</td>
|
414
|
+
</tr>
|
415
|
+
<tr id='section-18'>
|
416
|
+
<td class=docs>
|
417
|
+
<div class="pilwrap">
|
418
|
+
<a class="pilcrow" href="#section-18">¶</a>
|
419
|
+
</div>
|
420
|
+
<p>The fanciest thing in this entire library is this <code>#to_proc</code> method. Here is an example of it’s usage:</p>
|
421
|
+
|
422
|
+
<pre><code>class BookRep
|
423
|
+
initialize_with :book_model
|
424
|
+
fields :title => :default
|
425
|
+
forward :title => :book_model
|
426
|
+
end
|
427
|
+
|
428
|
+
Book.all.map(&BookRep) # => [{ :title => "Moby Dick" }, { :title => "Lost Horizon " }]
|
429
|
+
</code></pre>
|
430
|
+
|
431
|
+
<p>And now I will explain how it works. Any object can have a to_proc method and when you call <code>#map</code> on an
|
432
|
+
array and hand it a proc it will in turn hand each object as an argument to that proc. What I’ve decided
|
433
|
+
to do with this object is use it the options for a shared instance to make a hash.</p>
|
434
|
+
|
435
|
+
<p>Since I know the different initialization argumants from a call to <code>initialize_with</code>, I can infer by order
|
436
|
+
which object is which option. Then I can create a Hash to give to <code>parse_opts</code> through the <code>shared</code> method.
|
437
|
+
I hope that makes sense.</p>
|
438
|
+
|
439
|
+
<p>It allows for extremely clean Rails controllers like this:</p>
|
440
|
+
|
441
|
+
<pre><code>class PhotosController < ApplicationController
|
442
|
+
respond_to :json, :html
|
443
|
+
|
444
|
+
def index
|
445
|
+
@photos = Photo.paginate(page: params[:page], per_page: 20)
|
446
|
+
respond_with @photos.map(&PhotoRep)
|
447
|
+
end
|
448
|
+
|
449
|
+
def show
|
450
|
+
@photo = Photo.find(params[:id])
|
451
|
+
respond_with PhotoRep.new(photo: @photo)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
</code></pre>
|
455
|
+
|
456
|
+
</td>
|
457
|
+
<td class=code>
|
458
|
+
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">to_proc</span>
|
459
|
+
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|</span><span class="n">obj</span><span class="o">|</span>
|
460
|
+
<span class="n">arr</span> <span class="o">=</span> <span class="o">[</span><span class="n">obj</span><span class="o">].</span><span class="n">flatten</span>
|
461
|
+
<span class="n">init_args</span> <span class="o">=</span> <span class="vi">@initializiation_args</span><span class="o">[</span><span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="p">(</span><span class="n">arr</span><span class="o">.</span><span class="n">length</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">]</span>
|
462
|
+
<span class="n">opts</span> <span class="o">=</span> <span class="no">Hash</span><span class="o">[</span><span class="n">init_args</span><span class="o">.</span><span class="n">zip</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span><span class="o">]</span>
|
463
|
+
<span class="n">shared</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span><span class="o">.</span><span class="n">to_hash</span>
|
464
|
+
<span class="p">}</span>
|
465
|
+
<span class="k">end</span>
|
466
|
+
<span class="k">end</span>
|
467
|
+
|
468
|
+
<span class="k">module</span> <span class="nn">HashieSupport</span>
|
469
|
+
<span class="k">def</span> <span class="nf">to_hash</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="ss">:default</span><span class="p">)</span>
|
470
|
+
<span class="no">Hashie</span><span class="o">::</span><span class="no">Mash</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="k">super</span><span class="p">)</span>
|
471
|
+
<span class="k">end</span>
|
472
|
+
<span class="k">end</span>
|
473
|
+
<span class="k">end</span></pre></div>
|
474
|
+
</td>
|
475
|
+
</tr>
|
476
|
+
</table>
|
477
|
+
</div>
|
478
|
+
</body>
|