remodel 0.1.0 → 0.1.1

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/docs/docco.css ADDED
@@ -0,0 +1,185 @@
1
+ /*--------------------- Layout and Typography ----------------------------*/
2
+ body {
3
+ font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
4
+ font-size: 16px;
5
+ line-height: 24px;
6
+ color: #252519;
7
+ margin: 0; padding: 0;
8
+ }
9
+ a {
10
+ color: #261a3b;
11
+ }
12
+ a:visited {
13
+ color: #261a3b;
14
+ }
15
+ p {
16
+ margin: 0 0 15px 0;
17
+ }
18
+ h1, h2, h3, h4, h5, h6 {
19
+ margin: 40px 0 15px 0;
20
+ }
21
+ h3, h4, h5, h6 {
22
+ margin-top: 20px;
23
+ }
24
+ #container {
25
+ position: relative;
26
+ }
27
+ #background {
28
+ position: fixed;
29
+ top: 0; left: 580px; right: 0; bottom: 0;
30
+ background: #f5f5ff;
31
+ border-left: 1px solid #e5e5ee;
32
+ z-index: -1;
33
+ }
34
+ #jump_to, #jump_page {
35
+ background: white;
36
+ -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
37
+ -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
38
+ font: 10px Arial;
39
+ text-transform: uppercase;
40
+ cursor: pointer;
41
+ text-align: right;
42
+ }
43
+ #jump_to, #jump_wrapper {
44
+ position: fixed;
45
+ right: 0; top: 0;
46
+ padding: 5px 10px;
47
+ }
48
+ #jump_wrapper {
49
+ padding: 0;
50
+ display: none;
51
+ }
52
+ #jump_to:hover #jump_wrapper {
53
+ display: block;
54
+ }
55
+ #jump_page {
56
+ padding: 5px 0 3px;
57
+ margin: 0 0 25px 25px;
58
+ }
59
+ #jump_page .source {
60
+ display: block;
61
+ padding: 5px 10px;
62
+ text-decoration: none;
63
+ border-top: 1px solid #eee;
64
+ }
65
+ #jump_page .source:hover {
66
+ background: #f5f5ff;
67
+ }
68
+ #jump_page .source:first-child {
69
+ }
70
+ table td {
71
+ border: 0;
72
+ outline: 0;
73
+ }
74
+ td.docs, th.docs {
75
+ max-width: 500px;
76
+ min-width: 500px;
77
+ min-height: 5px;
78
+ padding: 10px 25px 1px 50px;
79
+ vertical-align: top;
80
+ text-align: left;
81
+ }
82
+ .docs pre {
83
+ margin: 15px 0 15px;
84
+ padding-left: 15px;
85
+ }
86
+ .docs p tt, .docs p code {
87
+ background: #f8f8ff;
88
+ border: 1px solid #dedede;
89
+ font-size: 12px;
90
+ padding: 0 0.2em;
91
+ }
92
+ .octowrap {
93
+ position: relative;
94
+ }
95
+ .octothorpe {
96
+ font: 12px Arial;
97
+ text-decoration: none;
98
+ color: #454545;
99
+ position: absolute;
100
+ top: 3px; left: -20px;
101
+ padding: 1px 2px;
102
+ opacity: 0;
103
+ -webkit-transition: opacity 0.2s linear;
104
+ }
105
+ td.docs:hover .octothorpe {
106
+ opacity: 1;
107
+ }
108
+ td.code, th.code {
109
+ padding: 14px 15px 16px 50px;
110
+ width: 100%;
111
+ vertical-align: top;
112
+ background: #f5f5ff;
113
+ border-left: 1px solid #e5e5ee;
114
+ }
115
+ pre, tt, code {
116
+ font-size: 12px; line-height: 18px;
117
+ font-family: Monaco, Consolas, "Lucida Console", monospace;
118
+ margin: 0; padding: 0;
119
+ }
120
+
121
+
122
+ /*---------------------- Syntax Highlighting -----------------------------*/
123
+ td.linenos { background-color: #f0f0f0; padding-right: 10px; }
124
+ span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
125
+ body .hll { background-color: #ffffcc }
126
+ body .c { color: #408080; font-style: italic } /* Comment */
127
+ /*body .err { border: 1px solid #FF0000 } /* Error */
128
+ body .k { color: #954121 } /* Keyword */
129
+ body .o { color: #666666 } /* Operator */
130
+ body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
131
+ body .cp { color: #BC7A00 } /* Comment.Preproc */
132
+ body .c1 { color: #408080; font-style: italic } /* Comment.Single */
133
+ body .cs { color: #408080; font-style: italic } /* Comment.Special */
134
+ body .gd { color: #A00000 } /* Generic.Deleted */
135
+ body .ge { font-style: italic } /* Generic.Emph */
136
+ body .gr { color: #FF0000 } /* Generic.Error */
137
+ body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
138
+ body .gi { color: #00A000 } /* Generic.Inserted */
139
+ body .go { color: #808080 } /* Generic.Output */
140
+ body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
141
+ body .gs { font-weight: bold } /* Generic.Strong */
142
+ body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
143
+ body .gt { color: #0040D0 } /* Generic.Traceback */
144
+ body .kc { color: #954121 } /* Keyword.Constant */
145
+ body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
146
+ body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
147
+ body .kp { color: #954121 } /* Keyword.Pseudo */
148
+ body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
149
+ body .kt { color: #B00040 } /* Keyword.Type */
150
+ body .m { color: #666666 } /* Literal.Number */
151
+ body .s { color: #219161 } /* Literal.String */
152
+ body .na { color: #7D9029 } /* Name.Attribute */
153
+ body .nb { color: #954121 } /* Name.Builtin */
154
+ body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
155
+ body .no { color: #880000 } /* Name.Constant */
156
+ body .nd { color: #AA22FF } /* Name.Decorator */
157
+ body .ni { color: #999999; font-weight: bold } /* Name.Entity */
158
+ body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
159
+ body .nf { color: #0000FF } /* Name.Function */
160
+ body .nl { color: #A0A000 } /* Name.Label */
161
+ body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
162
+ body .nt { color: #954121; font-weight: bold } /* Name.Tag */
163
+ body .nv { color: #19469D } /* Name.Variable */
164
+ body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
165
+ body .w { color: #bbbbbb } /* Text.Whitespace */
166
+ body .mf { color: #666666 } /* Literal.Number.Float */
167
+ body .mh { color: #666666 } /* Literal.Number.Hex */
168
+ body .mi { color: #666666 } /* Literal.Number.Integer */
169
+ body .mo { color: #666666 } /* Literal.Number.Oct */
170
+ body .sb { color: #219161 } /* Literal.String.Backtick */
171
+ body .sc { color: #219161 } /* Literal.String.Char */
172
+ body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
173
+ body .s2 { color: #219161 } /* Literal.String.Double */
174
+ body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
175
+ body .sh { color: #219161 } /* Literal.String.Heredoc */
176
+ body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
177
+ body .sx { color: #954121 } /* Literal.String.Other */
178
+ body .sr { color: #BB6688 } /* Literal.String.Regex */
179
+ body .s1 { color: #219161 } /* Literal.String.Single */
180
+ body .ss { color: #19469D } /* Literal.String.Symbol */
181
+ body .bp { color: #954121 } /* Name.Builtin.Pseudo */
182
+ body .vc { color: #19469D } /* Name.Variable.Class */
183
+ body .vg { color: #19469D } /* Name.Variable.Global */
184
+ body .vi { color: #19469D } /* Name.Variable.Instance */
185
+ body .il { color: #666666 } /* Literal.Number.Integer.Long */
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/lib/remodel.rb CHANGED
@@ -1,15 +1,59 @@
1
1
  require 'rubygems'
2
2
  require 'redis'
3
- require 'yajl'
3
+ require 'date'
4
+
5
+ # Use the superfast [YAJL][yajl] lib to parse [JSON][json], if available.
6
+ #
7
+ # [yajl]: http://github.com/brianmario/yajl-ruby
8
+ # [json]: http://json.org/
9
+ begin
10
+ require 'yajl/json_gem'
11
+ rescue LoadError
12
+ require 'json'
13
+ end
14
+
15
+ #### Monkey patches
16
+
17
+ # Define `Boolean` as the superclass of `true` and `false`.
18
+ module Boolean; end
19
+ true.extend(Boolean)
20
+ false.extend(Boolean)
21
+
22
+ # Find the `Class` object for a given class name, which can be a `String` or `Symbol` (or `Class`).
23
+ def Class.[](clazz)
24
+ return clazz if clazz.nil? or clazz.is_a?(Class)
25
+ clazz.to_s.split('::').inject(Kernel) { |mod, name| mod.const_get(name) }
26
+ end
27
+
28
+ #### Remodel
4
29
 
5
30
  module Remodel
31
+
32
+ # By default, we expect to find the redis server on `localhost:6379` &mdash;
33
+ # otherwise you will have to set `Remodel.redis` to a suitably initialized redis client.
34
+ def self.redis
35
+ @redis ||= Redis.new
36
+ end
6
37
 
38
+ def self.redis=(redis)
39
+ @redis = redis
40
+ end
41
+
42
+ # Custom errors
7
43
  class Error < ::StandardError; end
8
44
  class EntityNotFound < Error; end
9
45
  class EntityNotSaved < Error; end
10
46
  class InvalidKeyPrefix < Error; end
11
47
  class InvalidType < Error; end
12
48
 
49
+ #### Mapper
50
+
51
+ # A mapper converts a given value into a native JSON value &mdash;
52
+ # *nil*, *true*, *false*, *Number*, *String*, *Hash*, *Array* &mdash; via `pack`,
53
+ # and back again via `unpack`.
54
+ #
55
+ # Without any arguments, `Mapper.new` returns the identity mapper, which maps every value into itself.
56
+ # If `clazz` is set, the mapper rejects any value which is not of the given type.
13
57
  class Mapper
14
58
  def initialize(clazz = nil, pack_method = nil, unpack_method = nil)
15
59
  @clazz = clazz
@@ -28,44 +72,66 @@ module Remodel
28
72
  @unpack_method ? @clazz.send(@unpack_method, value) : value
29
73
  end
30
74
  end
31
-
75
+
76
+ # So let's define some handy mappers for common types, and a way to look them up.
77
+ # If no mapper is defined for a given class, the identity mapper is used.
78
+ def self.mapper_by_class
79
+ @mapper_by_class ||= Hash.new(Mapper.new).merge(
80
+ Boolean => Mapper.new(Boolean),
81
+ String => Mapper.new(String),
82
+ Integer => Mapper.new(Integer),
83
+ Float => Mapper.new(Float),
84
+ Array => Mapper.new(Array),
85
+ Hash => Mapper.new(Hash),
86
+ Date => Mapper.new(Date, :to_s, :parse),
87
+ Time => Mapper.new(Time, :to_i, :at)
88
+ )
89
+ end
90
+
91
+ def self.mapper_for(clazz)
92
+ mapper_by_class[Class[clazz]]
93
+ end
94
+
95
+ #### HasMany
96
+
97
+ # Represents the many-end of a many-to-one or many-to-many association.
32
98
  class HasMany < Array
33
99
  def initialize(this, clazz, key, reverse = nil)
34
- super fetch(clazz, key)
100
+ super _fetch(clazz, key)
35
101
  @this, @clazz, @key, @reverse = this, clazz, key, reverse
36
102
  end
37
-
103
+
38
104
  def create(attributes = {})
39
105
  add(@clazz.create(attributes))
40
106
  end
41
-
107
+
42
108
  def add(entity)
43
- add_to_reverse_association_of(entity) if @reverse
109
+ _add_to_reverse_association_of(entity) if @reverse
44
110
  _add(entity)
45
111
  end
46
112
 
47
113
  private
48
-
114
+
49
115
  def _add(entity)
50
116
  self << entity
51
117
  Remodel.redis.rpush(@key, entity.key)
52
118
  entity
53
119
  end
54
-
120
+
55
121
  def _remove(entity)
56
122
  delete_if { |x| x.key = entity.key }
57
123
  Remodel.redis.lrem(@key, 0, entity.key)
58
124
  end
59
-
60
- def add_to_reverse_association_of(entity)
125
+
126
+ def _add_to_reverse_association_of(entity)
61
127
  if entity.send(@reverse).is_a? HasMany
62
128
  entity.send(@reverse).send(:_add, @this)
63
129
  else
64
130
  entity.send("_#{@reverse}=", @this)
65
131
  end
66
132
  end
67
-
68
- def fetch(clazz, key)
133
+
134
+ def _fetch(clazz, key)
69
135
  keys = Remodel.redis.lrange(key, 0, -1)
70
136
  values = keys.empty? ? [] : Remodel.redis.mget(keys)
71
137
  keys.zip(values).map do |key, json|
@@ -74,6 +140,9 @@ module Remodel
74
140
  end
75
141
  end
76
142
 
143
+ #### Entity
144
+
145
+ # The superclass of all persistent remodel entities.
77
146
  class Entity
78
147
  attr_accessor :key
79
148
 
@@ -85,12 +154,21 @@ module Remodel
85
154
  end
86
155
  end
87
156
 
157
+ def id
158
+ key && key.split(':').last.to_i
159
+ end
160
+
88
161
  def save
89
162
  @key = self.class.next_key unless @key
90
163
  Remodel.redis.set(@key, to_json)
91
164
  self
92
165
  end
93
166
 
167
+ def update(properties)
168
+ properties.each { |name, value| send("#{name}=", value) }
169
+ save
170
+ end
171
+
94
172
  def reload
95
173
  raise EntityNotSaved unless @key
96
174
  initialize(self.class.parse(self.class.fetch(@key)), @key)
@@ -99,14 +177,19 @@ module Remodel
99
177
  end
100
178
  self
101
179
  end
180
+
181
+ def delete
182
+ raise EntityNotSaved unless @key
183
+ Remodel.redis.del(@key)
184
+ end
102
185
 
103
186
  def to_json
104
- Yajl::Encoder.encode(self.class.pack(@attributes))
187
+ JSON.generate(self.class.pack(@attributes))
105
188
  end
106
189
 
107
190
  def inspect
108
191
  properties = @attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(', ')
109
- "\#<#{self.class.name}(#{key}) #{properties}>"
192
+ "\#<#{self.class.name}(#{id}) #{properties}>"
110
193
  end
111
194
 
112
195
  def self.create(attributes = {})
@@ -114,13 +197,24 @@ module Remodel
114
197
  end
115
198
 
116
199
  def self.find(key)
200
+ key = "#{key_prefix}:#{key}" if key.kind_of? Integer
117
201
  restore(key, fetch(key))
118
202
  end
203
+
204
+ def self.all
205
+ keys = Remodel.redis.keys("#{key_prefix}:*").select { |k| k =~ /:[0-9]+$/ }
206
+ values = keys.empty? ? [] : Remodel.redis.mget(keys)
207
+ keys.zip(values).map do |key, json|
208
+ restore(key, json) if json
209
+ end.compact
210
+ end
119
211
 
120
212
  def self.restore(key, json)
121
213
  new(parse(json), key)
122
214
  end
123
215
 
216
+ #### DSL for subclasses
217
+
124
218
  protected
125
219
 
126
220
  def self.set_key_prefix(prefix)
@@ -142,7 +236,7 @@ module Remodel
142
236
  if instance_variable_defined? var
143
237
  instance_variable_get(var)
144
238
  else
145
- clazz = Remodel.find_class(options[:class])
239
+ clazz = Class[options[:class]]
146
240
  instance_variable_set(var, HasMany.new(self, clazz, "#{key}:#{name}", options[:reverse]))
147
241
  end
148
242
  end
@@ -155,7 +249,7 @@ module Remodel
155
249
  if instance_variable_defined? var
156
250
  instance_variable_get(var)
157
251
  else
158
- clazz = Remodel.find_class(options[:class])
252
+ clazz = Class[options[:class]]
159
253
  value_key = Remodel.redis.get("#{key}:#{name}")
160
254
  instance_variable_set(var, clazz.find(value_key)) if value_key
161
255
  end
@@ -174,10 +268,8 @@ module Remodel
174
268
  remove_instance_variable(var) if instance_variable_defined? var
175
269
  Remodel.redis.del("#{key}:#{name}")
176
270
  end
177
- end
271
+ end; private "_#{name}="
178
272
 
179
- private "_#{name}="
180
-
181
273
  if options[:reverse]
182
274
  define_method("_reverse_association_of_#{name}=") do |value|
183
275
  if value
@@ -197,29 +289,31 @@ module Remodel
197
289
  end
198
290
  end
199
291
  end
200
- end
201
-
202
- private "_reverse_association_of_#{name}="
292
+ end; private "_reverse_association_of_#{name}="
203
293
  end
204
294
  end
205
295
 
296
+ #### Helper methods
297
+
206
298
  private
207
299
 
208
300
  def self.fetch(key)
209
301
  Remodel.redis.get(key) || raise(EntityNotFound, "no #{name} with key #{key}")
210
302
  end
211
303
 
304
+ # Each entity has its own sequence to generate unique ids.
212
305
  def self.next_key
213
- counter = Remodel.redis.incr("#{key_prefix}:seq")
214
- "#{key_prefix}:#{counter}"
306
+ id = Remodel.redis.incr("#{key_prefix}:seq")
307
+ "#{key_prefix}:#{id}"
215
308
  end
216
309
 
310
+ # Default key prefix is the first letter of the class name, in lowercase.
217
311
  def self.key_prefix
218
312
  @key_prefix ||= name.split('::').last[0,1].downcase
219
313
  end
220
314
 
221
315
  def self.parse(json)
222
- unpack(Yajl::Parser.parse(json))
316
+ unpack(JSON.parse(json))
223
317
  end
224
318
 
225
319
  def self.pack(attributes)
@@ -239,41 +333,10 @@ module Remodel
239
333
  result
240
334
  end
241
335
 
336
+ # Lazy init
242
337
  def self.mapper
243
338
  @mapper ||= {}
244
339
  end
245
340
  end
246
-
247
- def self.redis=(redis)
248
- @redis = redis
249
- end
250
-
251
- def self.redis
252
- @redis ||= Redis.new
253
- end
254
-
255
- private
256
-
257
- # converts String, Symbol or Class into Class
258
- def self.find_class(clazz)
259
- return nil unless clazz
260
- clazz.to_s.split('::').inject(Kernel) { |mod, name| mod.const_get(name) }
261
- end
262
-
263
- def self.mapper_for(clazz)
264
- mapper_by_class[find_class(clazz)]
265
- end
266
-
267
- def self.mapper_by_class
268
- @mapper_by_class ||= Hash.new(Mapper.new).merge(
269
- String => Mapper.new(String),
270
- Integer => Mapper.new(Integer),
271
- Float => Mapper.new(Float),
272
- Array => Mapper.new(Array),
273
- Hash => Mapper.new(Hash),
274
- Date => Mapper.new(Date, :to_s, :parse),
275
- Time => Mapper.new(Time, :to_i, :at)
276
- )
277
- end
278
-
341
+
279
342
  end
data/remodel.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{remodel}
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tim Lossen"]
12
- s.date = %q{2010-03-23}
12
+ s.date = %q{2010-04-28}
13
13
  s.default_executable = %q{redis-monitor.rb}
14
14
  s.description = %q{build your domain model in ruby, persist your objects to redis.}
15
15
  s.email = %q{tim@lossen.de}
@@ -25,6 +25,8 @@ Gem::Specification.new do |s|
25
25
  "Rakefile",
26
26
  "VERSION",
27
27
  "bin/redis-monitor.rb",
28
+ "docs/docco.css",
29
+ "docs/remodel.html",
28
30
  "example/book.rb",
29
31
  "lib/remodel.rb",
30
32
  "remodel.gemspec",
@@ -33,6 +35,7 @@ Gem::Specification.new do |s|
33
35
  "test/test_many_to_many.rb",
34
36
  "test/test_many_to_one.rb",
35
37
  "test/test_mappers.rb",
38
+ "test/test_monkeypatches.rb",
36
39
  "test/test_one_to_many.rb",
37
40
  "test/test_one_to_one.rb"
38
41
  ]
@@ -47,6 +50,7 @@ Gem::Specification.new do |s|
47
50
  "test/test_many_to_many.rb",
48
51
  "test/test_many_to_one.rb",
49
52
  "test/test_mappers.rb",
53
+ "test/test_monkeypatches.rb",
50
54
  "test/test_one_to_many.rb",
51
55
  "test/test_one_to_one.rb"
52
56
  ]
data/test/test_entity.rb CHANGED
@@ -20,39 +20,15 @@ class TestEntity < Test::Unit::TestCase
20
20
  foo = Foo.new :z => 3
21
21
  assert foo.instance_eval { !@attributes.key? :z }
22
22
  end
23
- end
24
-
25
- context "reload" do
26
- setup do
27
- @foo = Foo.create :x => 'hello', :y => true
28
- end
29
-
30
- should "reload all properties" do
31
- redis.set @foo.key, %q({"x":23,"y":"adios"})
32
- @foo.reload
33
- assert_equal 23, @foo.x
34
- assert_equal 'adios', @foo.y
35
- end
36
23
 
37
- should "keep the key" do
38
- key = @foo.key
39
- @foo.reload
40
- assert_equal key, @foo.key
24
+ should "not set the key" do
25
+ foo = Foo.new :x => 23
26
+ assert_equal nil, foo.key
41
27
  end
42
28
 
43
- should "stay the same object" do
44
- id = @foo.object_id
45
- @foo.reload
46
- assert_equal id, @foo.object_id
47
- end
48
-
49
- should "raise EntityNotFound if the entity does not exist any more" do
50
- redis.del @foo.key
51
- assert_raise(Remodel::EntityNotFound) { @foo.reload }
52
- end
53
-
54
- should "raise EntityNotSaved if the entity was never saved" do
55
- assert_raise(Remodel::EntityNotSaved) { Foo.new.reload }
29
+ should "not set the id" do
30
+ foo = Foo.new :x => 23
31
+ assert_equal nil, foo.id
56
32
  end
57
33
  end
58
34
 
@@ -72,6 +48,12 @@ class TestEntity < Test::Unit::TestCase
72
48
  assert_equal 'b:2', Bar.create.key
73
49
  end
74
50
 
51
+ should "give the entity an id which is unique per entity class" do
52
+ assert_equal 1, Foo.create.id
53
+ assert_equal 1, Bar.create.id
54
+ assert_equal 2, Bar.create.id
55
+ end
56
+
75
57
  should "store the entity under its key" do
76
58
  foo = Foo.create :x => 'hello', :y => false
77
59
  assert redis.exists(foo.key)
@@ -115,33 +97,127 @@ class TestEntity < Test::Unit::TestCase
115
97
  end
116
98
  end
117
99
 
118
- context "#set_key_prefix" do
119
- should "use the given key prefix" do
120
- class Custom < Remodel::Entity; set_key_prefix 'my'; end
121
- assert_match /^my:\d+$/, Custom.create.key
122
- end
123
-
124
- should "ensure that the prefix is letters only" do
125
- assert_raise(Remodel::InvalidKeyPrefix) do
126
- class InvalidPrefix < Remodel::Entity; set_key_prefix '666'; end
127
- end
128
- end
129
- end
100
+ context "reload" do
101
+ setup do
102
+ @foo = Foo.create :x => 'hello', :y => true
103
+ end
104
+
105
+ should "reload all properties" do
106
+ redis.set @foo.key, %q({"x":23,"y":"adios"})
107
+ @foo.reload
108
+ assert_equal 23, @foo.x
109
+ assert_equal 'adios', @foo.y
110
+ end
111
+
112
+ should "keep the key" do
113
+ key = @foo.key
114
+ @foo.reload
115
+ assert_equal key, @foo.key
116
+ end
117
+
118
+ should "stay the same object" do
119
+ id = @foo.object_id
120
+ @foo.reload
121
+ assert_equal id, @foo.object_id
122
+ end
123
+
124
+ should "raise EntityNotFound if the entity does not exist any more" do
125
+ redis.del @foo.key
126
+ assert_raise(Remodel::EntityNotFound) { @foo.reload }
127
+ end
128
+
129
+ should "raise EntityNotSaved if the entity was never saved" do
130
+ assert_raise(Remodel::EntityNotSaved) { Foo.new.reload }
131
+ end
132
+ end
133
+
134
+ context "update" do
135
+ setup do
136
+ redis.flushdb
137
+ @foo = Foo.create :x => 'Tim', :y => true
138
+ end
139
+
140
+ should "set the given properties" do
141
+ @foo.update(:x => 12, :y => 'Jan')
142
+ assert_equal 12, @foo.x
143
+ assert_equal 'Jan', @foo.y
144
+ end
145
+
146
+ should "save the entity" do
147
+ @foo.update(:x => 12, :y => 'Jan')
148
+ @foo.reload
149
+ assert_equal 12, @foo.x
150
+ assert_equal 'Jan', @foo.y
151
+ end
152
+ end
130
153
 
154
+ context "delete" do
155
+ setup do
156
+ redis.flushdb
157
+ @foo = Foo.create :x => 'Tim', :y => true
158
+ end
159
+
160
+ should "delete the given entity" do
161
+ @foo.delete
162
+ assert_nil redis.get(@foo.key)
163
+ end
164
+
165
+ should "ensure that the entity is persistent" do
166
+ assert_raise(Remodel::EntityNotSaved) { Foo.new.delete }
167
+ end
168
+ end
169
+
170
+ context "#set_key_prefix" do
171
+ should "use the given key prefix" do
172
+ class Custom < Remodel::Entity; set_key_prefix 'my'; end
173
+ assert_match /^my:\d+$/, Custom.create.key
174
+ end
175
+
176
+ should "ensure that the prefix is letters only" do
177
+ assert_raise(Remodel::InvalidKeyPrefix) do
178
+ class InvalidPrefix < Remodel::Entity; set_key_prefix '666'; end
179
+ end
180
+ end
181
+ end
182
+
131
183
  context "find" do
132
184
  setup do
133
185
  redis.flushdb
134
186
  @foo = Foo.create :x => 'hello', :y => 123
187
+ @bar = Foo.create :x => 'hallo', :y => 124
135
188
  end
136
189
 
137
- should "load an entity from redis" do
190
+ should "find and load an entity by key" do
138
191
  foo = Foo.find(@foo.key)
139
192
  assert_equal foo.x, @foo.x
140
193
  assert_equal foo.y, @foo.y
141
194
  end
142
195
 
143
- should "raise EntityNotFound if the key does not exist" do
144
- assert_raise(Remodel::EntityNotFound) { Foo.find(23) }
196
+ should "find and load an entity by id" do
197
+ foo = Foo.find(@foo.id)
198
+ assert_equal foo.x, @foo.x
199
+ assert_equal foo.y, @foo.y
200
+ end
201
+
202
+ should "reject a key which does not exist" do
203
+ assert_raise(Remodel::EntityNotFound) { Foo.find('x:66') }
204
+ end
205
+
206
+ should "reject an id which does not exist" do
207
+ assert_raise(Remodel::EntityNotFound) { Foo.find(66) }
208
+ end
209
+ end
210
+
211
+ context "all" do
212
+ setup do
213
+ redis.flushdb
214
+ 17.times { |i| Foo.create :x => 'hello', :y => i }
215
+ 5.times { |i| Bar.create }
216
+ end
217
+
218
+ should "find all entities of the given class" do
219
+ assert_equal 17, Foo.all.size
220
+ assert_equal 5, Bar.all.size
145
221
  end
146
222
  end
147
223
 
data/test/test_mappers.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  class Item < Remodel::Entity
4
+ property :boolean, :class => Boolean
4
5
  property :string, :class => String
5
6
  property :integer, :class => Integer
6
7
  property :float, :class => Float
@@ -41,6 +42,7 @@ class TestMappers < Test::Unit::TestCase
41
42
 
42
43
  should "handle nil values" do
43
44
  item = Item.create
45
+ assert_nil item.boolean
44
46
  assert_nil item.string
45
47
  assert_nil item.integer
46
48
  assert_nil item.float
@@ -51,6 +53,7 @@ class TestMappers < Test::Unit::TestCase
51
53
  end
52
54
 
53
55
  should "reject invalid types" do
56
+ assert_raise(Remodel::InvalidType) { Item.create :boolean => 'hello' }
54
57
  assert_raise(Remodel::InvalidType) { Item.create :string => true }
55
58
  assert_raise(Remodel::InvalidType) { Item.create :integer => 33.5 }
56
59
  assert_raise(Remodel::InvalidType) { Item.create :float => 5 }
@@ -0,0 +1,30 @@
1
+ require 'helper'
2
+
3
+ class TestMonkeypatches < Test::Unit::TestCase
4
+
5
+ context "Boolean" do
6
+ should "be the superclass of both true and false" do
7
+ assert true.is_a?(Boolean)
8
+ assert false.is_a?(Boolean)
9
+ end
10
+ end
11
+
12
+ context "Class[]" do
13
+ should "return given Class objects" do
14
+ assert_equal String, Class[String]
15
+ end
16
+
17
+ should "return the Class object for a given String" do
18
+ assert_equal String, Class['String']
19
+ end
20
+
21
+ should "return the Class object for a given Symbol" do
22
+ assert_equal String, Class[:String]
23
+ end
24
+
25
+ should "work for nested classes" do
26
+ assert_equal Remodel::Entity, Class['Remodel::Entity']
27
+ end
28
+ end
29
+
30
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Lossen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-23 00:00:00 +01:00
12
+ date: 2010-04-28 00:00:00 +02:00
13
13
  default_executable: redis-monitor.rb
14
14
  dependencies: []
15
15
 
@@ -29,6 +29,8 @@ files:
29
29
  - Rakefile
30
30
  - VERSION
31
31
  - bin/redis-monitor.rb
32
+ - docs/docco.css
33
+ - docs/remodel.html
32
34
  - example/book.rb
33
35
  - lib/remodel.rb
34
36
  - remodel.gemspec
@@ -37,6 +39,7 @@ files:
37
39
  - test/test_many_to_many.rb
38
40
  - test/test_many_to_one.rb
39
41
  - test/test_mappers.rb
42
+ - test/test_monkeypatches.rb
40
43
  - test/test_one_to_many.rb
41
44
  - test/test_one_to_one.rb
42
45
  has_rdoc: true
@@ -73,5 +76,6 @@ test_files:
73
76
  - test/test_many_to_many.rb
74
77
  - test/test_many_to_one.rb
75
78
  - test/test_mappers.rb
79
+ - test/test_monkeypatches.rb
76
80
  - test/test_one_to_many.rb
77
81
  - test/test_one_to_one.rb