remodel 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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