remodel-h 0.1.4
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/.gitignore +2 -0
- data/LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +27 -0
- data/VERSION +1 -0
- data/bin/redis-monitor.rb +25 -0
- data/docs/docco.css +185 -0
- data/docs/remodel.html +269 -0
- data/example/book.rb +13 -0
- data/lib/remodel/entity.rb +198 -0
- data/lib/remodel/has_many.rb +71 -0
- data/lib/remodel/mapper.rb +29 -0
- data/lib/remodel.rb +77 -0
- data/remodel-h.gemspec +72 -0
- data/remodel.gemspec +72 -0
- data/test/helper.rb +19 -0
- data/test/test_entity.rb +313 -0
- data/test/test_many_to_many.rb +77 -0
- data/test/test_many_to_one.rb +107 -0
- data/test/test_mappers.rb +67 -0
- data/test/test_monkeypatches.rb +30 -0
- data/test/test_one_to_many.rb +96 -0
- data/test/test_one_to_one.rb +57 -0
- metadata +85 -0
data/docs/remodel.html
ADDED
@@ -0,0 +1,269 @@
|
|
1
|
+
<!DOCTYPE html> <html> <head> <title>remodel.rb</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <table cellpadding="0" cellspacing="0"> <thead> <tr> <th class="docs"> <h1> remodel.rb </h1> </th> <th class="code"> </th> </tr> </thead> <tbody> <tr id="section-1"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-1">#</a> </div> </td> <td class="code"> <div class="highlight"><pre><span class="nb">require</span> <span class="s1">'rubygems'</span>
|
2
|
+
<span class="nb">require</span> <span class="s1">'redis'</span></pre></div> </td> </tr> <tr id="section-2"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-2">#</a> </div> <p>Use the superfast <a href="http://github.com/brianmario/yajl-ruby">YAJL</a> lib to parse <a href="http://json.org/">JSON</a>, if available.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">begin</span>
|
3
|
+
<span class="nb">require</span> <span class="s1">'yajl/json_gem'</span>
|
4
|
+
<span class="k">rescue</span> <span class="no">LoadError</span>
|
5
|
+
<span class="nb">require</span> <span class="s1">'json'</span>
|
6
|
+
<span class="k">end</span></pre></div> </td> </tr> <tr id="section-3"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-3">#</a> </div> <h3>Monkey patches</h3> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-4"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-4">#</a> </div> <p>Define <code>Boolean</code> as the superclass of <code>true</code> and <code>false</code>.</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">module</span> <span class="nn">Boolean</span><span class="p">;</span> <span class="k">end</span>
|
7
|
+
<span class="kp">true</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="no">Boolean</span><span class="p">)</span>
|
8
|
+
<span class="kp">false</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="no">Boolean</span><span class="p">)</span></pre></div> </td> </tr> <tr id="section-5"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-5">#</a> </div> <p>Find the <code>Class</code> object for a given class name, which can be a <code>String</code> or <code>Symbol</code> (or <code>Class</code>).</p> </td> <td class="code"> <div class="highlight"><pre><span class="k">def</span> <span class="nc">Class</span><span class="o">.</span><span class="nf">[]</span><span class="p">(</span><span class="n">clazz</span><span class="p">)</span>
|
9
|
+
<span class="k">return</span> <span class="n">clazz</span> <span class="k">if</span> <span class="n">clazz</span><span class="o">.</span><span class="n">nil?</span> <span class="ow">or</span> <span class="n">clazz</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Class</span><span class="p">)</span>
|
10
|
+
<span class="n">clazz</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'::'</span><span class="p">)</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="no">Kernel</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">mod</span><span class="p">,</span> <span class="nb">name</span><span class="o">|</span> <span class="n">mod</span><span class="o">.</span><span class="n">const_get</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="p">}</span>
|
11
|
+
<span class="k">end</span></pre></div> </td> </tr> <tr id="section-6"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-6">#</a> </div> <h3>Remodel</h3> </td> <td class="code"> <div class="highlight"><pre><span class="k">module</span> <span class="nn">Remodel</span>
|
12
|
+
</pre></div> </td> </tr> <tr id="section-7"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-7">#</a> </div> <p>By default, we expect to find the redis server on <code>localhost:6379</code> —
|
13
|
+
otherwise you will have to set <code>Remodel.redis</code> to a suitably initialized redis client.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">redis</span>
|
14
|
+
<span class="vi">@redis</span> <span class="o">||=</span> <span class="no">Redis</span><span class="o">.</span><span class="n">new</span>
|
15
|
+
<span class="k">end</span>
|
16
|
+
|
17
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">redis</span><span class="o">=</span><span class="p">(</span><span class="n">redis</span><span class="p">)</span>
|
18
|
+
<span class="vi">@redis</span> <span class="o">=</span> <span class="n">redis</span>
|
19
|
+
<span class="k">end</span></pre></div> </td> </tr> <tr id="section-8"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-8">#</a> </div> <p>Custom errors</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">class</span> <span class="nc">Error</span> <span class="o"><</span> <span class="o">::</span><span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
|
20
|
+
<span class="k">class</span> <span class="nc">EntityNotFound</span> <span class="o"><</span> <span class="no">Error</span><span class="p">;</span> <span class="k">end</span>
|
21
|
+
<span class="k">class</span> <span class="nc">EntityNotSaved</span> <span class="o"><</span> <span class="no">Error</span><span class="p">;</span> <span class="k">end</span>
|
22
|
+
<span class="k">class</span> <span class="nc">InvalidKeyPrefix</span> <span class="o"><</span> <span class="no">Error</span><span class="p">;</span> <span class="k">end</span>
|
23
|
+
<span class="k">class</span> <span class="nc">InvalidType</span> <span class="o"><</span> <span class="no">Error</span><span class="p">;</span> <span class="k">end</span>
|
24
|
+
</pre></div> </td> </tr> <tr id="section-9"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-9">#</a> </div> <h3>Mapper</h3> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-10"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-10">#</a> </div> <p>A mapper converts a given value into a native JSON value —
|
25
|
+
<em>nil</em>, <em>true</em>, <em>false</em>, <em>Number</em>, <em>String</em>, <em>Hash</em>, <em>Array</em> — via <code>pack</code>,
|
26
|
+
and back again via <code>unpack</code>.</p>
|
27
|
+
|
28
|
+
<p>Without any arguments, <code>Mapper.new</code> returns the identity mapper, which maps every value into itself.
|
29
|
+
If <code>clazz</code> is set, the mapper rejects any value which is not of the given type.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">class</span> <span class="nc">Mapper</span>
|
30
|
+
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">clazz</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="n">pack_method</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="n">unpack_method</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
|
31
|
+
<span class="vi">@clazz</span> <span class="o">=</span> <span class="n">clazz</span>
|
32
|
+
<span class="vi">@pack_method</span> <span class="o">=</span> <span class="n">pack_method</span>
|
33
|
+
<span class="vi">@unpack_method</span> <span class="o">=</span> <span class="n">unpack_method</span>
|
34
|
+
<span class="k">end</span>
|
35
|
+
|
36
|
+
<span class="k">def</span> <span class="nf">pack</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
37
|
+
<span class="k">return</span> <span class="kp">nil</span> <span class="k">if</span> <span class="n">value</span><span class="o">.</span><span class="n">nil?</span>
|
38
|
+
<span class="k">raise</span><span class="p">(</span><span class="no">InvalidType</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="n">value</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2"> is not a </span><span class="si">#{</span><span class="vi">@clazz</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="k">if</span> <span class="vi">@clazz</span> <span class="o">&&</span> <span class="o">!</span><span class="n">value</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="vi">@clazz</span><span class="p">)</span>
|
39
|
+
<span class="vi">@pack_method</span> <span class="p">?</span> <span class="n">value</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="vi">@pack_method</span><span class="p">)</span> <span class="p">:</span> <span class="n">value</span>
|
40
|
+
<span class="k">end</span>
|
41
|
+
|
42
|
+
<span class="k">def</span> <span class="nf">unpack</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
43
|
+
<span class="k">return</span> <span class="kp">nil</span> <span class="k">if</span> <span class="n">value</span><span class="o">.</span><span class="n">nil?</span>
|
44
|
+
<span class="vi">@unpack_method</span> <span class="p">?</span> <span class="vi">@clazz</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="vi">@unpack_method</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="p">:</span> <span class="n">value</span>
|
45
|
+
<span class="k">end</span>
|
46
|
+
<span class="k">end</span></pre></div> </td> </tr> <tr id="section-11"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-11">#</a> </div> <p>So let's define some handy mappers for common types, and a way to look them up.
|
47
|
+
If no mapper is defined for a given class, the identity mapper is used.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">mapper_by_class</span>
|
48
|
+
<span class="vi">@mapper_by_class</span> <span class="o">||=</span> <span class="no">Hash</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">)</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span>
|
49
|
+
<span class="no">Boolean</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Boolean</span><span class="p">),</span>
|
50
|
+
<span class="nb">String</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">String</span><span class="p">),</span>
|
51
|
+
<span class="nb">Integer</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">Integer</span><span class="p">),</span>
|
52
|
+
<span class="nb">Float</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">Float</span><span class="p">),</span>
|
53
|
+
<span class="nb">Array</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">Array</span><span class="p">),</span>
|
54
|
+
<span class="no">Hash</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Hash</span><span class="p">),</span>
|
55
|
+
<span class="no">Date</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Date</span><span class="p">,</span> <span class="ss">:to_s</span><span class="p">,</span> <span class="ss">:parse</span><span class="p">),</span>
|
56
|
+
<span class="no">Time</span> <span class="o">=></span> <span class="no">Mapper</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">Time</span><span class="p">,</span> <span class="ss">:to_i</span><span class="p">,</span> <span class="ss">:at</span><span class="p">)</span>
|
57
|
+
<span class="p">)</span>
|
58
|
+
<span class="k">end</span>
|
59
|
+
|
60
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">mapper_for</span><span class="p">(</span><span class="n">clazz</span><span class="p">)</span>
|
61
|
+
<span class="n">mapper_by_class</span><span class="o">[</span><span class="no">Class</span><span class="o">[</span><span class="n">clazz</span><span class="o">]]</span>
|
62
|
+
<span class="k">end</span></pre></div> </td> </tr> <tr id="section-12"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-12">#</a> </div> <h3>HasMany</h3> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-13"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-13">#</a> </div> <p>Represents the many-end of a many-to-one or many-to-many association.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">class</span> <span class="nc">HasMany</span> <span class="o"><</span> <span class="nb">Array</span>
|
63
|
+
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">clazz</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">reverse</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
|
64
|
+
<span class="k">super</span> <span class="n">_fetch</span><span class="p">(</span><span class="n">clazz</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span>
|
65
|
+
<span class="vi">@this</span><span class="p">,</span> <span class="vi">@clazz</span><span class="p">,</span> <span class="vi">@key</span><span class="p">,</span> <span class="vi">@reverse</span> <span class="o">=</span> <span class="n">this</span><span class="p">,</span> <span class="n">clazz</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">reverse</span>
|
66
|
+
<span class="k">end</span>
|
67
|
+
|
68
|
+
<span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">attributes</span> <span class="o">=</span> <span class="p">{})</span>
|
69
|
+
<span class="n">add</span><span class="p">(</span><span class="vi">@clazz</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">attributes</span><span class="p">))</span>
|
70
|
+
<span class="k">end</span>
|
71
|
+
|
72
|
+
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">entity</span><span class="p">)</span>
|
73
|
+
<span class="n">_add_to_reverse_association_of</span><span class="p">(</span><span class="n">entity</span><span class="p">)</span> <span class="k">if</span> <span class="vi">@reverse</span>
|
74
|
+
<span class="n">_add</span><span class="p">(</span><span class="n">entity</span><span class="p">)</span>
|
75
|
+
<span class="k">end</span>
|
76
|
+
|
77
|
+
<span class="kp">private</span>
|
78
|
+
|
79
|
+
<span class="k">def</span> <span class="nf">_add</span><span class="p">(</span><span class="n">entity</span><span class="p">)</span>
|
80
|
+
<span class="nb">self</span> <span class="o"><<</span> <span class="n">entity</span>
|
81
|
+
<span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">rpush</span><span class="p">(</span><span class="vi">@key</span><span class="p">,</span> <span class="n">entity</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
82
|
+
<span class="n">entity</span>
|
83
|
+
<span class="k">end</span>
|
84
|
+
|
85
|
+
<span class="k">def</span> <span class="nf">_remove</span><span class="p">(</span><span class="n">entity</span><span class="p">)</span>
|
86
|
+
<span class="n">delete_if</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">entity</span><span class="o">.</span><span class="n">key</span> <span class="p">}</span>
|
87
|
+
<span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">lrem</span><span class="p">(</span><span class="vi">@key</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">entity</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
88
|
+
<span class="k">end</span>
|
89
|
+
|
90
|
+
<span class="k">def</span> <span class="nf">_add_to_reverse_association_of</span><span class="p">(</span><span class="n">entity</span><span class="p">)</span>
|
91
|
+
<span class="k">if</span> <span class="n">entity</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="vi">@reverse</span><span class="p">)</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">HasMany</span>
|
92
|
+
<span class="n">entity</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="vi">@reverse</span><span class="p">)</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">:_add</span><span class="p">,</span> <span class="vi">@this</span><span class="p">)</span>
|
93
|
+
<span class="k">else</span>
|
94
|
+
<span class="n">entity</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"_</span><span class="si">#{</span><span class="vi">@reverse</span><span class="si">}</span><span class="s2">="</span><span class="p">,</span> <span class="vi">@this</span><span class="p">)</span>
|
95
|
+
<span class="k">end</span>
|
96
|
+
<span class="k">end</span>
|
97
|
+
|
98
|
+
<span class="k">def</span> <span class="nf">_fetch</span><span class="p">(</span><span class="n">clazz</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span>
|
99
|
+
<span class="n">keys</span> <span class="o">=</span> <span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">lrange</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
|
100
|
+
<span class="n">values</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="n">empty?</span> <span class="p">?</span> <span class="o">[]</span> <span class="p">:</span> <span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">mget</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span>
|
101
|
+
<span class="n">keys</span><span class="o">.</span><span class="n">zip</span><span class="p">(</span><span class="n">values</span><span class="p">)</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">json</span><span class="o">|</span>
|
102
|
+
<span class="n">clazz</span><span class="o">.</span><span class="n">restore</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">json</span><span class="p">)</span> <span class="k">if</span> <span class="n">json</span>
|
103
|
+
<span class="k">end</span><span class="o">.</span><span class="n">compact</span>
|
104
|
+
<span class="k">end</span>
|
105
|
+
<span class="k">end</span>
|
106
|
+
</pre></div> </td> </tr> <tr id="section-14"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-14">#</a> </div> <h3>Entity</h3> </td> <td class="code"> <div class="highlight"><pre></pre></div> </td> </tr> <tr id="section-15"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-15">#</a> </div> <p>The superclass of all persistent remodel entities.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">class</span> <span class="nc">Entity</span>
|
107
|
+
<span class="kp">attr_accessor</span> <span class="ss">:key</span>
|
108
|
+
|
109
|
+
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">attributes</span> <span class="o">=</span> <span class="p">{},</span> <span class="n">key</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
|
110
|
+
<span class="vi">@attributes</span> <span class="o">=</span> <span class="p">{}</span>
|
111
|
+
<span class="vi">@key</span> <span class="o">=</span> <span class="n">key</span>
|
112
|
+
<span class="n">attributes</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">name</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
|
113
|
+
<span class="nb">send</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="k">if</span> <span class="nb">respond_to?</span> <span class="s2">"</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span>
|
114
|
+
<span class="k">end</span>
|
115
|
+
<span class="k">end</span>
|
116
|
+
|
117
|
+
<span class="k">def</span> <span class="nf">save</span>
|
118
|
+
<span class="vi">@key</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">next_key</span> <span class="k">unless</span> <span class="vi">@key</span>
|
119
|
+
<span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="vi">@key</span><span class="p">,</span> <span class="n">to_json</span><span class="p">)</span>
|
120
|
+
<span class="nb">self</span>
|
121
|
+
<span class="k">end</span>
|
122
|
+
|
123
|
+
<span class="k">def</span> <span class="nf">reload</span>
|
124
|
+
<span class="k">raise</span> <span class="no">EntityNotSaved</span> <span class="k">unless</span> <span class="vi">@key</span>
|
125
|
+
<span class="kp">initialize</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="vi">@key</span><span class="p">)),</span> <span class="vi">@key</span><span class="p">)</span>
|
126
|
+
<span class="nb">instance_variables</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">var</span><span class="o">|</span>
|
127
|
+
<span class="n">remove_instance_variable</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="k">if</span> <span class="n">var</span> <span class="o">=~</span> <span class="sr">/^@association_/</span>
|
128
|
+
<span class="k">end</span>
|
129
|
+
<span class="nb">self</span>
|
130
|
+
<span class="k">end</span>
|
131
|
+
|
132
|
+
<span class="k">def</span> <span class="nf">to_json</span>
|
133
|
+
<span class="no">JSON</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="vi">@attributes</span><span class="p">))</span>
|
134
|
+
<span class="k">end</span>
|
135
|
+
|
136
|
+
<span class="k">def</span> <span class="nf">inspect</span>
|
137
|
+
<span class="n">properties</span> <span class="o">=</span> <span class="vi">@attributes</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="nb">name</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span> <span class="s2">"</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">value</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">)</span>
|
138
|
+
<span class="s2">"</span><span class="se">\#</span><span class="s2"><</span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">(</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">) </span><span class="si">#{</span><span class="n">properties</span><span class="si">}</span><span class="s2">>"</span>
|
139
|
+
<span class="k">end</span>
|
140
|
+
|
141
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">create</span><span class="p">(</span><span class="n">attributes</span> <span class="o">=</span> <span class="p">{})</span>
|
142
|
+
<span class="kp">new</span><span class="p">(</span><span class="n">attributes</span><span class="p">)</span><span class="o">.</span><span class="n">save</span>
|
143
|
+
<span class="k">end</span>
|
144
|
+
|
145
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">find</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
146
|
+
<span class="n">restore</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">fetch</span><span class="p">(</span><span class="n">key</span><span class="p">))</span>
|
147
|
+
<span class="k">end</span>
|
148
|
+
|
149
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">restore</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">json</span><span class="p">)</span>
|
150
|
+
<span class="kp">new</span><span class="p">(</span><span class="n">parse</span><span class="p">(</span><span class="n">json</span><span class="p">),</span> <span class="n">key</span><span class="p">)</span>
|
151
|
+
<span class="k">end</span>
|
152
|
+
</pre></div> </td> </tr> <tr id="section-16"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-16">#</a> </div> <h3>DSL for subclasses</h3> </td> <td class="code"> <div class="highlight"><pre> <span class="kp">protected</span>
|
153
|
+
|
154
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">set_key_prefix</span><span class="p">(</span><span class="n">prefix</span><span class="p">)</span>
|
155
|
+
<span class="k">raise</span><span class="p">(</span><span class="no">InvalidKeyPrefix</span><span class="p">,</span> <span class="n">prefix</span><span class="p">)</span> <span class="k">unless</span> <span class="n">prefix</span> <span class="o">=~</span> <span class="sr">/^[a-z]+$/</span>
|
156
|
+
<span class="vi">@key_prefix</span> <span class="o">=</span> <span class="n">prefix</span>
|
157
|
+
<span class="k">end</span>
|
158
|
+
|
159
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">property</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
|
160
|
+
<span class="nb">name</span> <span class="o">=</span> <span class="nb">name</span><span class="o">.</span><span class="n">to_sym</span>
|
161
|
+
<span class="n">mapper</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span> <span class="o">=</span> <span class="no">Remodel</span><span class="o">.</span><span class="n">mapper_for</span><span class="p">(</span><span class="n">options</span><span class="o">[</span><span class="ss">:class</span><span class="o">]</span><span class="p">)</span>
|
162
|
+
<span class="n">define_method</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="p">{</span> <span class="vi">@attributes</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span> <span class="p">}</span>
|
163
|
+
<span class="n">define_method</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span> <span class="vi">@attributes</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</span> <span class="p">}</span>
|
164
|
+
<span class="k">end</span>
|
165
|
+
|
166
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">has_many</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
167
|
+
<span class="n">var</span> <span class="o">=</span> <span class="s2">"@association_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span><span class="o">.</span><span class="n">to_sym</span>
|
168
|
+
|
169
|
+
<span class="n">define_method</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="k">do</span>
|
170
|
+
<span class="k">if</span> <span class="n">instance_variable_defined?</span> <span class="n">var</span>
|
171
|
+
<span class="nb">instance_variable_get</span><span class="p">(</span><span class="n">var</span><span class="p">)</span>
|
172
|
+
<span class="k">else</span>
|
173
|
+
<span class="n">clazz</span> <span class="o">=</span> <span class="no">Class</span><span class="o">[</span><span class="n">options</span><span class="o">[</span><span class="ss">:class</span><span class="o">]]</span>
|
174
|
+
<span class="nb">instance_variable_set</span><span class="p">(</span><span class="n">var</span><span class="p">,</span> <span class="no">HasMany</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="n">clazz</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">options</span><span class="o">[</span><span class="ss">:reverse</span><span class="o">]</span><span class="p">))</span>
|
175
|
+
<span class="k">end</span>
|
176
|
+
<span class="k">end</span>
|
177
|
+
<span class="k">end</span>
|
178
|
+
|
179
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">has_one</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
180
|
+
<span class="n">var</span> <span class="o">=</span> <span class="s2">"@association_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span><span class="o">.</span><span class="n">to_sym</span>
|
181
|
+
|
182
|
+
<span class="n">define_method</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span> <span class="k">do</span>
|
183
|
+
<span class="k">if</span> <span class="n">instance_variable_defined?</span> <span class="n">var</span>
|
184
|
+
<span class="nb">instance_variable_get</span><span class="p">(</span><span class="n">var</span><span class="p">)</span>
|
185
|
+
<span class="k">else</span>
|
186
|
+
<span class="n">clazz</span> <span class="o">=</span> <span class="no">Class</span><span class="o">[</span><span class="n">options</span><span class="o">[</span><span class="ss">:class</span><span class="o">]]</span>
|
187
|
+
<span class="n">value_key</span> <span class="o">=</span> <span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
188
|
+
<span class="nb">instance_variable_set</span><span class="p">(</span><span class="n">var</span><span class="p">,</span> <span class="n">clazz</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">value_key</span><span class="p">))</span> <span class="k">if</span> <span class="n">value_key</span>
|
189
|
+
<span class="k">end</span>
|
190
|
+
<span class="k">end</span>
|
191
|
+
|
192
|
+
<span class="n">define_method</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
|
193
|
+
<span class="nb">send</span><span class="p">(</span><span class="s2">"_reverse_association_of_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="k">if</span> <span class="n">options</span><span class="o">[</span><span class="ss">:reverse</span><span class="o">]</span>
|
194
|
+
<span class="nb">send</span><span class="p">(</span><span class="s2">"_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
195
|
+
<span class="k">end</span>
|
196
|
+
|
197
|
+
<span class="n">define_method</span><span class="p">(</span><span class="s2">"_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
|
198
|
+
<span class="k">if</span> <span class="n">value</span>
|
199
|
+
<span class="nb">instance_variable_set</span><span class="p">(</span><span class="n">var</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
|
200
|
+
<span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">value</span><span class="o">.</span><span class="n">key</span><span class="p">)</span>
|
201
|
+
<span class="k">else</span>
|
202
|
+
<span class="n">remove_instance_variable</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="k">if</span> <span class="n">instance_variable_defined?</span> <span class="n">var</span>
|
203
|
+
<span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">del</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
204
|
+
<span class="k">end</span>
|
205
|
+
<span class="k">end</span><span class="p">;</span> <span class="kp">private</span> <span class="s2">"_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span>
|
206
|
+
|
207
|
+
<span class="k">if</span> <span class="n">options</span><span class="o">[</span><span class="ss">:reverse</span><span class="o">]</span>
|
208
|
+
<span class="n">define_method</span><span class="p">(</span><span class="s2">"_reverse_association_of_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span>
|
209
|
+
<span class="k">if</span> <span class="n">value</span>
|
210
|
+
<span class="n">association</span> <span class="o">=</span> <span class="n">value</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">options</span><span class="o">[</span><span class="ss">:reverse</span><span class="o">]</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
211
|
+
<span class="k">if</span> <span class="n">association</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">HasMany</span>
|
212
|
+
<span class="n">association</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"_add"</span><span class="p">,</span> <span class="nb">self</span><span class="p">)</span>
|
213
|
+
<span class="k">else</span>
|
214
|
+
<span class="n">value</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"_</span><span class="si">#{</span><span class="n">options</span><span class="o">[</span><span class="ss">:reverse</span><span class="o">]</span><span class="si">}</span><span class="s2">="</span><span class="p">,</span> <span class="nb">self</span><span class="p">)</span>
|
215
|
+
<span class="k">end</span>
|
216
|
+
<span class="k">else</span>
|
217
|
+
<span class="k">if</span> <span class="n">old_value</span> <span class="o">=</span> <span class="nb">send</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
|
218
|
+
<span class="n">association</span> <span class="o">=</span> <span class="n">old_value</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">options</span><span class="o">[</span><span class="ss">:reverse</span><span class="o">]</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
219
|
+
<span class="k">if</span> <span class="n">association</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">HasMany</span>
|
220
|
+
<span class="n">association</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"_remove"</span><span class="p">,</span> <span class="nb">self</span><span class="p">)</span>
|
221
|
+
<span class="k">else</span>
|
222
|
+
<span class="n">old_value</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">"_</span><span class="si">#{</span><span class="n">options</span><span class="o">[</span><span class="ss">:reverse</span><span class="o">]</span><span class="si">}</span><span class="s2">="</span><span class="p">,</span> <span class="kp">nil</span><span class="p">)</span>
|
223
|
+
<span class="k">end</span>
|
224
|
+
<span class="k">end</span>
|
225
|
+
<span class="k">end</span>
|
226
|
+
<span class="k">end</span><span class="p">;</span> <span class="kp">private</span> <span class="s2">"_reverse_association_of_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">="</span>
|
227
|
+
<span class="k">end</span>
|
228
|
+
<span class="k">end</span>
|
229
|
+
</pre></div> </td> </tr> <tr id="section-17"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-17">#</a> </div> <h3>Helper methods</h3> </td> <td class="code"> <div class="highlight"><pre> <span class="kp">private</span>
|
230
|
+
|
231
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">fetch</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
232
|
+
<span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="o">||</span> <span class="k">raise</span><span class="p">(</span><span class="no">EntityNotFound</span><span class="p">,</span> <span class="s2">"no </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2"> with key </span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
233
|
+
<span class="k">end</span>
|
234
|
+
</pre></div> </td> </tr> <tr id="section-18"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-18">#</a> </div> <p>Each entity has its own counter to generate a sequence of keys.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">next_key</span>
|
235
|
+
<span class="n">counter</span> <span class="o">=</span> <span class="no">Remodel</span><span class="o">.</span><span class="n">redis</span><span class="o">.</span><span class="n">incr</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">key_prefix</span><span class="si">}</span><span class="s2">:seq"</span><span class="p">)</span>
|
236
|
+
<span class="s2">"</span><span class="si">#{</span><span class="n">key_prefix</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">counter</span><span class="si">}</span><span class="s2">"</span>
|
237
|
+
<span class="k">end</span>
|
238
|
+
</pre></div> </td> </tr> <tr id="section-19"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-19">#</a> </div> <p>Default key prefix is the first letter of the class name, in lowercase.</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">key_prefix</span>
|
239
|
+
<span class="vi">@key_prefix</span> <span class="o">||=</span> <span class="nb">name</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'::'</span><span class="p">)</span><span class="o">.</span><span class="n">last</span><span class="o">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="o">].</span><span class="n">downcase</span>
|
240
|
+
<span class="k">end</span>
|
241
|
+
|
242
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">json</span><span class="p">)</span>
|
243
|
+
<span class="n">unpack</span><span class="p">(</span><span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">json</span><span class="p">))</span>
|
244
|
+
<span class="k">end</span>
|
245
|
+
|
246
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">pack</span><span class="p">(</span><span class="n">attributes</span><span class="p">)</span>
|
247
|
+
<span class="n">result</span> <span class="o">=</span> <span class="p">{}</span>
|
248
|
+
<span class="n">attributes</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">name</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
|
249
|
+
<span class="n">result</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span> <span class="o">=</span> <span class="n">mapper</span><span class="o">[</span><span class="nb">name</span><span class="o">].</span><span class="n">pack</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
250
|
+
<span class="k">end</span>
|
251
|
+
<span class="n">result</span>
|
252
|
+
<span class="k">end</span>
|
253
|
+
|
254
|
+
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">unpack</span><span class="p">(</span><span class="n">attributes</span><span class="p">)</span>
|
255
|
+
<span class="n">result</span> <span class="o">=</span> <span class="p">{}</span>
|
256
|
+
<span class="n">attributes</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">name</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span>
|
257
|
+
<span class="nb">name</span> <span class="o">=</span> <span class="nb">name</span><span class="o">.</span><span class="n">to_sym</span>
|
258
|
+
<span class="n">result</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span> <span class="o">=</span> <span class="n">mapper</span><span class="o">[</span><span class="nb">name</span><span class="o">].</span><span class="n">unpack</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
259
|
+
<span class="k">end</span>
|
260
|
+
<span class="n">result</span>
|
261
|
+
<span class="k">end</span>
|
262
|
+
</pre></div> </td> </tr> <tr id="section-20"> <td class="docs"> <div class="octowrap"> <a class="octothorpe" href="#section-20">#</a> </div> <p>Lazy init</p> </td> <td class="code"> <div class="highlight"><pre> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">mapper</span>
|
263
|
+
<span class="vi">@mapper</span> <span class="o">||=</span> <span class="p">{}</span>
|
264
|
+
<span class="k">end</span>
|
265
|
+
<span class="k">end</span>
|
266
|
+
|
267
|
+
<span class="k">end</span>
|
268
|
+
|
269
|
+
</pre></div> </td> </tr> </tbody> </table> </div> </body> </html>
|
data/example/book.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/remodel"
|
2
|
+
|
3
|
+
class Book < Remodel::Entity
|
4
|
+
has_many :chapters, :class => 'Chapter', :reverse => :book
|
5
|
+
property :title, :class => 'String'
|
6
|
+
property :year, :class => 'Integer'
|
7
|
+
end
|
8
|
+
|
9
|
+
class Chapter < Remodel::Entity
|
10
|
+
has_one :book, :class => Book, :reverse => :chapters
|
11
|
+
property :title, :class => String
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,198 @@
|
|
1
|
+
module Remodel
|
2
|
+
|
3
|
+
# The superclass of all persistent remodel entities.
|
4
|
+
class Entity
|
5
|
+
attr_accessor :key
|
6
|
+
|
7
|
+
def initialize(attributes = {}, key = nil)
|
8
|
+
@attributes = {}
|
9
|
+
@key = key
|
10
|
+
attributes = self.class.default_values.merge(attributes) if key.nil?
|
11
|
+
attributes.each do |name, value|
|
12
|
+
send("#{name}=", value) if respond_to? "#{name}="
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def id
|
17
|
+
key && key.split(':').last.to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
@key = self.class.next_key unless @key
|
22
|
+
Remodel.redis.hset(Remodel.context, @key, to_json)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def update(properties)
|
27
|
+
properties.each { |name, value| send("#{name}=", value) }
|
28
|
+
save
|
29
|
+
end
|
30
|
+
|
31
|
+
def reload
|
32
|
+
raise EntityNotSaved unless @key
|
33
|
+
initialize(self.class.parse(self.class.fetch(@key)), @key)
|
34
|
+
instance_variables.each do |var|
|
35
|
+
remove_instance_variable(var) if var =~ /^@association_/
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete
|
41
|
+
raise EntityNotSaved unless @key
|
42
|
+
Remodel.redis.hdel(Remodel.context, @key)
|
43
|
+
end
|
44
|
+
|
45
|
+
def as_json
|
46
|
+
{ :id => id }.merge(@attributes)
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_json
|
50
|
+
JSON.generate(self.class.pack(@attributes))
|
51
|
+
end
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
properties = @attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(', ')
|
55
|
+
"\#<#{self.class.name}(#{id}) #{properties}>"
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.create(attributes = {})
|
59
|
+
new(attributes).save
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.find(key)
|
63
|
+
key = "#{key_prefix}:#{key}" if key.kind_of? Integer
|
64
|
+
restore(key, fetch(key))
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.restore(key, json)
|
68
|
+
new(parse(json), key)
|
69
|
+
end
|
70
|
+
|
71
|
+
protected # --- DSL for subclasses ---
|
72
|
+
|
73
|
+
def self.set_key_prefix(prefix)
|
74
|
+
raise(InvalidKeyPrefix, prefix) unless prefix =~ /^[a-z]+$/
|
75
|
+
@key_prefix = prefix
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.property(name, options = {})
|
79
|
+
name = name.to_sym
|
80
|
+
mapper[name] = Remodel.mapper_for(options[:class])
|
81
|
+
default_values[name] = options[:default] if options.has_key?(:default)
|
82
|
+
define_method(name) { @attributes[name] }
|
83
|
+
define_method("#{name}=") { |value| @attributes[name] = value }
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.has_many(name, options)
|
87
|
+
var = "@association_#{name}".to_sym
|
88
|
+
|
89
|
+
define_method(name) do
|
90
|
+
if instance_variable_defined? var
|
91
|
+
instance_variable_get(var)
|
92
|
+
else
|
93
|
+
clazz = Class[options[:class]]
|
94
|
+
instance_variable_set(var, HasMany.new(self, clazz, "#{key}:#{name}", options[:reverse]))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.has_one(name, options)
|
100
|
+
var = "@association_#{name}".to_sym
|
101
|
+
|
102
|
+
define_method(name) do
|
103
|
+
if instance_variable_defined? var
|
104
|
+
instance_variable_get(var)
|
105
|
+
else
|
106
|
+
clazz = Class[options[:class]]
|
107
|
+
value_key = Remodel.redis.hget(Remodel.context, "#{key}:#{name}")
|
108
|
+
instance_variable_set(var, clazz.find(value_key)) if value_key
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
define_method("#{name}=") do |value|
|
113
|
+
send("_reverse_association_of_#{name}=", value) if options[:reverse]
|
114
|
+
send("_#{name}=", value)
|
115
|
+
end
|
116
|
+
|
117
|
+
define_method("_#{name}=") do |value|
|
118
|
+
if value
|
119
|
+
instance_variable_set(var, value)
|
120
|
+
Remodel.redis.hset(Remodel.context, "#{key}:#{name}", value.key)
|
121
|
+
else
|
122
|
+
remove_instance_variable(var) if instance_variable_defined? var
|
123
|
+
Remodel.redis.hdel(Remodel.context, "#{key}:#{name}")
|
124
|
+
end
|
125
|
+
end; private "_#{name}="
|
126
|
+
|
127
|
+
if options[:reverse]
|
128
|
+
define_method("_reverse_association_of_#{name}=") do |value|
|
129
|
+
if old_value = send(name)
|
130
|
+
association = old_value.send("#{options[:reverse]}")
|
131
|
+
if association.is_a? HasMany
|
132
|
+
association.send("_remove", self)
|
133
|
+
else
|
134
|
+
old_value.send("_#{options[:reverse]}=", nil)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
if value
|
138
|
+
association = value.send("#{options[:reverse]}")
|
139
|
+
if association.is_a? HasMany
|
140
|
+
association.send("_add", self)
|
141
|
+
else
|
142
|
+
value.send("_#{options[:reverse]}=", self)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end; private "_reverse_association_of_#{name}="
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
private # --- Helper methods ---
|
150
|
+
|
151
|
+
def self.fetch(key)
|
152
|
+
Remodel.redis.hget(Remodel.context, key) || raise(EntityNotFound, "no #{name} with key #{key}")
|
153
|
+
end
|
154
|
+
|
155
|
+
# Each entity has its own sequence to generate unique ids.
|
156
|
+
def self.next_key
|
157
|
+
id = Remodel.redis.hincrby(Remodel.context, "#{key_prefix}:seq", 1)
|
158
|
+
"#{key_prefix}:#{id}"
|
159
|
+
end
|
160
|
+
|
161
|
+
# Default key prefix is the first letter of the class name, in lowercase.
|
162
|
+
def self.key_prefix
|
163
|
+
@key_prefix ||= name.split('::').last[0,1].downcase
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.parse(json)
|
167
|
+
unpack(JSON.parse(json))
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.pack(attributes)
|
171
|
+
result = {}
|
172
|
+
attributes.each do |name, value|
|
173
|
+
result[name] = mapper[name].pack(value)
|
174
|
+
end
|
175
|
+
result
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.unpack(attributes)
|
179
|
+
result = {}
|
180
|
+
attributes.each do |name, value|
|
181
|
+
name = name.to_sym
|
182
|
+
result[name] = mapper[name].unpack(value)
|
183
|
+
end
|
184
|
+
result
|
185
|
+
end
|
186
|
+
|
187
|
+
# Lazy init
|
188
|
+
def self.mapper
|
189
|
+
@mapper ||= {}
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.default_values
|
193
|
+
@default_values ||= {}
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|