remodel 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +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
|