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/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">&#39;rubygems&#39;</span>
2
+ <span class="nb">require</span> <span class="s1">&#39;redis&#39;</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">&#39;yajl/json_gem&#39;</span>
4
+ <span class="k">rescue</span> <span class="no">LoadError</span>
5
+ <span class="nb">require</span> <span class="s1">&#39;json&#39;</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">&#39;::&#39;</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> &mdash;
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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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 &mdash;
25
+ <em>nil</em>, <em>true</em>, <em>false</em>, <em>Number</em>, <em>String</em>, <em>Hash</em>, <em>Array</em> &mdash; 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">&quot;</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">&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="vi">@clazz</span> <span class="o">&amp;&amp;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">&lt;</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">&lt;&lt;</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">&quot;_</span><span class="si">#{</span><span class="vi">@reverse</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;</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">&quot;</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">&#39;, &#39;</span><span class="p">)</span>
138
+ <span class="s2">&quot;</span><span class="se">\#</span><span class="s2">&lt;</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">&gt;&quot;</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">&quot;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;@association_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</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">&quot;</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">&quot;</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">&quot;@association_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</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">&quot;</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">&quot;</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">&quot;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;_reverse_association_of_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;</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">&quot;</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">&quot;</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">&quot;</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">&quot;_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;_reverse_association_of_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;</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">&quot;</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">&quot;_add&quot;</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">&quot;_</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">=&quot;</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">&quot;</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">&quot;</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">&quot;_remove&quot;</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">&quot;_</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">=&quot;</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">&quot;_reverse_association_of_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">=&quot;</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">&quot;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">&quot;</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">&quot;</span><span class="si">#{</span><span class="n">key_prefix</span><span class="si">}</span><span class="s2">:seq&quot;</span><span class="p">)</span>
236
+ <span class="s2">&quot;</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">&quot;</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">&#39;::&#39;</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