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