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.
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.2"
5
+ - "1.9.3"
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ # uncomment this line if your project needs to run something other than `rake`:
11
+ # script: bundle exec rspec spec
data/README.md CHANGED
@@ -52,13 +52,7 @@ Or install it yourself as:
52
52
 
53
53
  ## Usage
54
54
 
55
- `include Rep` into any class and it is endowed with a `#to_json` method,
56
- among other things. You describe the top level keys you want for your
57
- json with the `::fields` method. The values for the fields are expected
58
- to be returned from methods on the object of the same name.
59
-
60
- If a class has `fields :one => :default`, then `def one; 1; end` is
61
- expected.
55
+ `include Rep` into any class. See [nathanherald.com/rep](http://nathanherald.com/rep) for complete docs on every method.
62
56
 
63
57
  ## Examples
64
58
 
@@ -70,7 +64,7 @@ class PhotoRep
70
64
 
71
65
  initialize_with :photo
72
66
 
73
- json_fields [:url, :title, :exif, :location, :user] => :default
67
+ fields [:url, :title, :exif, :location, :user] => :default
74
68
 
75
69
  forward [:title, :exif, :location] => :photo
76
70
  forward :user => :user_rep
@@ -91,20 +85,19 @@ class UserRep
91
85
 
92
86
  initialize_with :user
93
87
 
94
- json_fields [:name, :email, :location] => :default
95
- json_fields [:id, :admin].concat(json_fields(:default)) => :admin
88
+ fields [:name, :email, :location] => :default
89
+ fields [:id, :admin].concat(fields(:default)) => :admin
96
90
 
97
- forward json_fields(:admin) => :user
91
+ forward fields(:admin) => :user
98
92
  end
99
93
 
100
94
  # You can now do crazy stuff like
101
-
102
95
  UserRep.new(user: User.first).to_hash.keys # => [:name, :email, :location]
103
96
 
104
97
  # To save from creating lots of objects, you can use a shared class that is reset fresh
105
98
  UserRep.shared(user: User.first).to_hash # => { name: "Nathan Herald:, ...
106
99
 
107
- # You can use class to proc (that makes a hash using the shared class)
100
+ # You can use class to proc (that makes a hash using the shared instance)
108
101
  User.all.map(&UserRep) # => [{ name: "Nathan Herald" ...
109
102
 
110
103
  # or maybe find all photos which will embed all users (and only ever make one instance each of PhotoRep and UserRep)
@@ -117,8 +110,9 @@ You don't have to have a Rep per model and Rep's can represent multiple objects
117
110
 
118
111
  ```ruby
119
112
  class ProjectReport
113
+ include Rep
120
114
  initialize_with :project, :active_users, :orders
121
- fields [:name, :date, :count, :total_gross_cost, :cost_per_active_user]
115
+ fields [:name, :date, :count, :total_gross_cost, :cost_per_active_user] => :default
122
116
  forward :date => :project
123
117
  forward :count => :orders
124
118
 
data/Rakefile CHANGED
@@ -1,8 +1,25 @@
1
1
  require "bundler/gem_tasks"
2
+
2
3
  require 'rake/testtask'
3
4
  Rake::TestTask.new do |t|
4
5
  t.libs << 'test'
5
6
  t.test_files = FileList['test/**/*_test.rb']
6
7
  t.verbose = true
7
8
  end
8
- task default: 'test'
9
+
10
+ require 'rdiscount'
11
+ require 'rocco/tasks'
12
+
13
+ desc "Build Rocco Docs"
14
+ Rocco::make 'docs'
15
+
16
+ require 'fileutils'
17
+ desc "copy the rocco files from lib into the base docs folder"
18
+ task :copy_to_index do
19
+ `cp -R docs/lib/* docs/`
20
+ `cp docs/rep.html docs/index.html`
21
+ end
22
+
23
+ task :docs => [:rocco, :copy_to_index]
24
+
25
+ task :default => 'test'
@@ -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 &hellip;
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">&#182;</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>&rsquo;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">&#182;</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] =&gt; :@array
59
+ def initialize(array = [])
60
+ @array = array
61
+ end
62
+ end
63
+
64
+ A.new([1,2,3]).length # =&gt; 3
65
+ A.new([1,2,3]).first # =&gt; 1
66
+ </code></pre>
67
+ </td>
68
+ <td class=code>
69
+ <div class='highlight'><pre><span class="nb">require</span> <span class="s1">&#39;forwardable&#39;</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">&#182;</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">&#39;json&#39;</span>
81
+
82
+ <span class="nb">require</span> <span class="s1">&#39;rep/version&#39;</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">&#182;</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">&lt;&lt;</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">&#182;</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">:&quot;@</span><span class="si">#{</span><span class="n">method_name</span><span class="si">}</span><span class="ss">&quot;</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">&#182;</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 =&gt; :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 =&gt; :foo }.to_a # =&gt; [[:one, :foo]]
147
+ </code></pre>
148
+
149
+ <p>Right now it will raise if either a field doesn&rsquo;t have a method to provide it&rsquo;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">=&gt;</span> <span class="n">e</span>
160
+ <span class="n">message</span> <span class="o">=</span> <span class="s2">&quot;There is no method named &#39;</span><span class="si">#{</span><span class="n">method_name</span><span class="si">}</span><span class="s2">&#39; for the class &#39;</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">&#39; for the &#39;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&#39; 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">&quot;</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">&quot;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">&quot;</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">&#182;</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 =&gt; "No Name"
186
+ end
187
+
188
+ A.new.name # =&gt; "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">:&quot;@</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="ss">&quot;</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">&#182;</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&rsquo;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">&#182;</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">&#182;</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">&#182;</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>&rsquo;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">:&quot;@</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="ss">&quot;</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">&#182;</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] =&gt; :default
272
+ end
273
+
274
+ A.json_fields(:default) # =&gt; [:one, :two, :three]
275
+ </code></pre>
276
+
277
+ <p>There is a general assumption that each top level key&rsquo;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 =&gt; :the_real_one_method }, :two, { :three =&gt; :some_other_three }] =&gt; :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] =&gt; :default
289
+ json_fields [:five, :two, :six] =&gt; :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 # =&gt; { :default =&gt; [:one, :two, :three], :other =&gt; [: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">&#182;</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">&quot;You can only use a Hash to set fields, a Symbol to retrieve them, or no argument to retrieve all fields for all names&quot;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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&rsquo;s usage:</p>
382
+
383
+ <pre><code>class BookRep
384
+ initialize_with :book_model
385
+ fields :title =&gt; :default
386
+ forward :title =&gt; :book_model
387
+ end
388
+
389
+ BookRep.shared(:book_model =&gt; Book.first).to_hash # =&gt; { :title =&gt; "Moby Dick" }
390
+ BookRep.shared(:book_model =&gt; Book.last).to_hash # =&gt; { :title =&gt; "Lost Horizon" }
391
+ </code></pre>
392
+
393
+ <p>This should terrify you. If it doesn&rsquo;t, then this example will:</p>
394
+
395
+ <pre><code>book1 = BookRep.shared(:book_model =&gt; Book.first)
396
+ book2 = BookRep.shared(:book_model =&gt; Book.last)
397
+
398
+ boo1.object_id === book2.object_id # =&gt; true
399
+ </code></pre>
400
+
401
+ <p><strong>It really is a shared object.</strong></p>
402
+
403
+ <p>You really shouldn&rsquo;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">&#182;</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&rsquo;s usage:</p>
421
+
422
+ <pre><code>class BookRep
423
+ initialize_with :book_model
424
+ fields :title =&gt; :default
425
+ forward :title =&gt; :book_model
426
+ end
427
+
428
+ Book.all.map(&amp;BookRep) # =&gt; [{ :title =&gt; "Moby Dick" }, { :title =&gt; "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&rsquo;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 &lt; 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(&amp;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>