dynamoid 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/Dynamoid.gemspec +65 -3
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +6 -0
  4. data/README.markdown +117 -22
  5. data/Rakefile +22 -9
  6. data/VERSION +1 -1
  7. data/doc/.nojekyll +0 -0
  8. data/doc/Dynamoid.html +300 -0
  9. data/doc/Dynamoid/Adapter.html +1387 -0
  10. data/doc/Dynamoid/Adapter/AwsSdk.html +1561 -0
  11. data/doc/Dynamoid/Adapter/Local.html +1487 -0
  12. data/doc/Dynamoid/Associations.html +131 -0
  13. data/doc/Dynamoid/Associations/Association.html +1706 -0
  14. data/doc/Dynamoid/Associations/BelongsTo.html +339 -0
  15. data/doc/Dynamoid/Associations/ClassMethods.html +723 -0
  16. data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +339 -0
  17. data/doc/Dynamoid/Associations/HasMany.html +339 -0
  18. data/doc/Dynamoid/Associations/HasOne.html +339 -0
  19. data/doc/Dynamoid/Components.html +202 -0
  20. data/doc/Dynamoid/Config.html +395 -0
  21. data/doc/Dynamoid/Config/Options.html +609 -0
  22. data/doc/Dynamoid/Criteria.html +131 -0
  23. data/doc/Dynamoid/Criteria/Chain.html +759 -0
  24. data/doc/Dynamoid/Criteria/ClassMethods.html +98 -0
  25. data/doc/Dynamoid/Document.html +512 -0
  26. data/doc/Dynamoid/Document/ClassMethods.html +581 -0
  27. data/doc/Dynamoid/Errors.html +118 -0
  28. data/doc/Dynamoid/Errors/DocumentNotValid.html +210 -0
  29. data/doc/Dynamoid/Errors/Error.html +130 -0
  30. data/doc/Dynamoid/Errors/InvalidField.html +133 -0
  31. data/doc/Dynamoid/Errors/MissingRangeKey.html +133 -0
  32. data/doc/Dynamoid/Fields.html +649 -0
  33. data/doc/Dynamoid/Fields/ClassMethods.html +264 -0
  34. data/doc/Dynamoid/Finders.html +128 -0
  35. data/doc/Dynamoid/Finders/ClassMethods.html +502 -0
  36. data/doc/Dynamoid/Indexes.html +308 -0
  37. data/doc/Dynamoid/Indexes/ClassMethods.html +351 -0
  38. data/doc/Dynamoid/Indexes/Index.html +1089 -0
  39. data/doc/Dynamoid/Persistence.html +653 -0
  40. data/doc/Dynamoid/Persistence/ClassMethods.html +568 -0
  41. data/doc/Dynamoid/Validations.html +399 -0
  42. data/doc/_index.html +439 -0
  43. data/doc/class_list.html +47 -0
  44. data/doc/css/common.css +1 -0
  45. data/doc/css/full_list.css +55 -0
  46. data/doc/css/style.css +322 -0
  47. data/doc/file.LICENSE.html +66 -0
  48. data/doc/file.README.html +279 -0
  49. data/doc/file_list.html +52 -0
  50. data/doc/frames.html +13 -0
  51. data/doc/index.html +279 -0
  52. data/doc/js/app.js +205 -0
  53. data/doc/js/full_list.js +173 -0
  54. data/doc/js/jquery.js +16 -0
  55. data/doc/method_list.html +1054 -0
  56. data/doc/top-level-namespace.html +105 -0
  57. data/lib/dynamoid.rb +2 -1
  58. data/lib/dynamoid/adapter.rb +77 -6
  59. data/lib/dynamoid/adapter/aws_sdk.rb +96 -16
  60. data/lib/dynamoid/adapter/local.rb +84 -15
  61. data/lib/dynamoid/associations.rb +53 -4
  62. data/lib/dynamoid/associations/association.rb +154 -26
  63. data/lib/dynamoid/associations/belongs_to.rb +32 -6
  64. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +29 -3
  65. data/lib/dynamoid/associations/has_many.rb +30 -4
  66. data/lib/dynamoid/associations/has_one.rb +26 -3
  67. data/lib/dynamoid/components.rb +7 -5
  68. data/lib/dynamoid/config.rb +15 -2
  69. data/lib/dynamoid/config/options.rb +8 -0
  70. data/lib/dynamoid/criteria.rb +7 -2
  71. data/lib/dynamoid/criteria/chain.rb +55 -8
  72. data/lib/dynamoid/document.rb +68 -7
  73. data/lib/dynamoid/errors.rb +17 -2
  74. data/lib/dynamoid/fields.rb +44 -1
  75. data/lib/dynamoid/finders.rb +32 -6
  76. data/lib/dynamoid/indexes.rb +22 -2
  77. data/lib/dynamoid/indexes/index.rb +48 -7
  78. data/lib/dynamoid/persistence.rb +111 -51
  79. data/lib/dynamoid/validations.rb +36 -0
  80. data/spec/app/models/address.rb +2 -1
  81. data/spec/app/models/camel_case.rb +11 -0
  82. data/spec/app/models/magazine.rb +4 -1
  83. data/spec/app/models/sponsor.rb +3 -1
  84. data/spec/app/models/subscription.rb +5 -1
  85. data/spec/app/models/user.rb +10 -1
  86. data/spec/dynamoid/associations/association_spec.rb +67 -1
  87. data/spec/dynamoid/associations/belongs_to_spec.rb +16 -1
  88. data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +7 -0
  89. data/spec/dynamoid/associations/has_many_spec.rb +14 -0
  90. data/spec/dynamoid/associations/has_one_spec.rb +10 -1
  91. data/spec/dynamoid/criteria_spec.rb +5 -1
  92. data/spec/dynamoid/document_spec.rb +23 -3
  93. data/spec/dynamoid/fields_spec.rb +10 -1
  94. data/spec/dynamoid/indexes/index_spec.rb +19 -0
  95. data/spec/dynamoid/persistence_spec.rb +24 -0
  96. data/spec/dynamoid/validations_spec.rb +36 -0
  97. metadata +105 -4
@@ -0,0 +1,279 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
+ <title>
7
+ File: README
8
+
9
+ &mdash; Documentation by YARD 0.7.5
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ relpath = '';
19
+ if (relpath != '') relpath += '/';
20
+ </script>
21
+
22
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
25
+
26
+
27
+ </head>
28
+ <body>
29
+ <script type="text/javascript" charset="utf-8">
30
+ if (window.top.frames.main) document.body.className = 'frames';
31
+ </script>
32
+
33
+ <div id="header">
34
+ <div id="menu">
35
+
36
+ <a href="_index.html" title="Index">Index</a> &raquo;
37
+ <span class="title">File: README</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a id="class_list_link" href="#">Class List</a>
46
+
47
+ <a id="method_list_link" href="#">Method List</a>
48
+
49
+ <a id="file_list_link" href="#">File List</a>
50
+
51
+ </div>
52
+ <div class="clear"></div>
53
+ </div>
54
+
55
+ <iframe id="search_frame"></iframe>
56
+
57
+ <div id="content"><div id='filecontents'><h1>Dynamoid</h1>
58
+
59
+ <p>Dynamoid is an ORM for Amazon's DynamoDB for Ruby applications. It provides similar functionality to ActiveRecord and improves on Amazon's existing <a href="http://docs.amazonwebservices.com/AWSRubySDK/latest/AWS/Record/HashModel.html">HashModel</a> by providing better searching tools, native association support, and a local adapter for offline development.</p>
60
+
61
+ <p>DynamoDB is not like other document-based databases you might know, and is very different indeed from relational databases. It sacrifices anything beyond the simplest relational queries and transactional support to provide a fast, cost-efficient, and highly durable storage solution. If your database requires complicated relational queries and transaction support, then this modest Gem cannot provide them for you, and neither can DynamoDB. In those cases you would do better to look elsewhere for your database needs.</p>
62
+
63
+ <p>But if you want a fast, scalable, simple, easy-to-use database (and a Gem that supports it) then look no further!</p>
64
+
65
+ <h2>Installation</h2>
66
+
67
+ <p>Installing Dynamoid is pretty simple. First include the Gem in your Gemfile:</p>
68
+
69
+ <pre class="code ruby"><code><span class='id identifier rubyid_gem'>gem</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>dynamoid</span><span class='tstring_end'>'</span></span>
70
+ </code></pre>
71
+
72
+ <p>Then you need to initialize it to get it going. Put code similar to this somewhere (a Rails initializer would be a great place for this if you're using Rails):</p>
73
+
74
+ <pre class="code ruby"><code> <span class='const'>Dynamoid</span><span class='period'>.</span><span class='id identifier rubyid_configure'>configure</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
75
+ <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_adapter'>adapter</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>local</span><span class='tstring_end'>'</span></span> <span class='comment'># This adapter allows offline development without connecting to the DynamoDB servers. Data is *NOT* persisted.
76
+ </span> <span class='comment'># config.adapter = 'aws_sdk' # This adapter establishes a connection to the DynamoDB servers using Amazon's own AWS gem.
77
+ </span> <span class='comment'># config.access_key = 'access_key' # If connecting to DynamoDB, your access key is required.
78
+ </span> <span class='comment'># config.secret_key = 'secret_key' # So is your secret key.
79
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_namespace'>namespace</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>dynamoid_app_development</span><span class='tstring_end'>&quot;</span></span> <span class='comment'># To namespace tables created by Dynamoid from other tables you might have.
80
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_warn_on_scan'>warn_on_scan</span> <span class='op'>=</span> <span class='kw'>true</span> <span class='comment'># Output a warning to the logger when you perform a scan rather than a query on a table.
81
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_partitioning'>partitioning</span> <span class='op'>=</span> <span class='kw'>true</span> <span class='comment'># Spread writes randomly across the database. See &quot;partitioning&quot; below for more.
82
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_partition_size'>partition_size</span> <span class='op'>=</span> <span class='int'>200</span> <span class='comment'># Determine the key space size that writes are randomly spread across.
83
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_read_capacity'>read_capacity</span> <span class='op'>=</span> <span class='int'>100</span> <span class='comment'># Read capacity for your tables
84
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_write_capacity'>write_capacity</span> <span class='op'>=</span> <span class='int'>20</span> <span class='comment'># Write capacity for your tables
85
+ </span> <span class='kw'>end</span>
86
+
87
+ </code></pre>
88
+
89
+ <p>Once you have the configuration set up, you need to move on to making models.</p>
90
+
91
+ <h2>Setup</h2>
92
+
93
+ <p>You <em>must</em> include <code>Dynamoid::Document</code> in every Dynamoid model.</p>
94
+
95
+ <pre class="code ruby"><code><span class='kw'>class</span> <span class='const'>User</span>
96
+ <span class='id identifier rubyid_include'>include</span> <span class='const'>Dynamoid</span><span class='op'>::</span><span class='const'>Document</span>
97
+
98
+ <span class='kw'>end</span>
99
+ </code></pre>
100
+
101
+ <h3>Fields</h3>
102
+
103
+ <p>You'll have to define all the fields on the model and the data type of each field. Every field on the object must be included here; if you miss any they'll be completely bypassed during DynamoDB's initialization and will not appear on the model objects.</p>
104
+
105
+ <p>By default, fields are assumed to be of type <code>:string</code>. But you can also use <code>:integer</code>, <code>:float</code>, <code>:set</code>, <code>:array</code>, <code>:datetime</code>, and <code>:serialized</code>. You get magic columns of id (string), created_at (datetime), and updated_at (datetime) for free.</p>
106
+
107
+ <pre class="code ruby"><code><span class='kw'>class</span> <span class='const'>User</span>
108
+ <span class='id identifier rubyid_include'>include</span> <span class='const'>Dynamoid</span><span class='op'>::</span><span class='const'>Document</span>
109
+
110
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:name</span>
111
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:email</span>
112
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:rank</span><span class='comma'>,</span> <span class='symbol'>:integer</span>
113
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:number</span><span class='comma'>,</span> <span class='symbol'>:float</span>
114
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:joined_at</span><span class='comma'>,</span> <span class='symbol'>:datetime</span>
115
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:hash</span><span class='comma'>,</span> <span class='symbol'>:serialized</span>
116
+
117
+ <span class='kw'>end</span>
118
+ </code></pre>
119
+
120
+ <h3>Indexes</h3>
121
+
122
+ <p>You can also define indexes on fields, combinations of fields, and one range field. Yes, only one range field: in DynamoDB tables can have at most one range index, so make good use of it! To make an index, just specify the fields you want it on, either single or in an array. If the entire index is a range, pass <code>:range =&gt; true</code>. Otherwise, pass the attribute that will become the range key. The only range attributes you can use right now are integers, floats, and datetimes. If you pass a string as a range key likely DynamoDB will complain a lot.</p>
123
+
124
+ <pre class="code ruby"><code>class User
125
+ include Dynamoid::Document
126
+
127
+ ...
128
+
129
+ index :name
130
+ index :email
131
+ index [:name, :email]
132
+ index :created_at, :range =&gt; true
133
+ index :name, :range =&gt; :joined_at
134
+
135
+ end
136
+ </code></pre>
137
+
138
+ <h3>Associations</h3>
139
+
140
+ <p>Just like in ActiveRecord (or your other favorite ORM), Dynamoid uses associations to create links between models.</p>
141
+
142
+ <p>The only supported associations (so far) are <code>has_many</code>, <code>has_one</code>, <code>has_and_belongs_to_many</code>, and <code>belongs_to</code>. Associations are very simple to create: just specify the type, the name, and then any options you'd like to pass to the association. If there's an inverse association either inferred or specified directly, Dynamoid will update both objects to point at each other.</p>
143
+
144
+ <pre class="code ruby"><code>class User
145
+ include Dynamoid::Document
146
+
147
+ ...
148
+
149
+ has_many :addresses
150
+ has_many :students, :class =&gt; User
151
+ belongs_to :teacher, :class_name =&gt; :user
152
+ belongs_to :group
153
+ has_one :role
154
+ has_and_belongs_to_many :friends, :inverse_of =&gt; :friending_users
155
+
156
+ end
157
+
158
+ class Address
159
+ include Dynamoid::Document
160
+
161
+ ...
162
+
163
+ belongs_to :address # Automatically links up with the user model
164
+
165
+ end
166
+ </code></pre>
167
+
168
+ <p>Contrary to what you'd expect, association information is always contained on the object specifying the association, even if it seems like the association has a foreign key. This is a side effect of DynamoDB's structure: it's very difficult to find foreign keys without an index. Usually you won't find this to be a problem, but it does mean that association methods that build new models will not work correctly -- for example, <code>user.addresses.new</code> returns an address that is not associated to the user. We'll be correcting this soon.</p>
169
+
170
+ <h3>Validations</h3>
171
+
172
+ <p>Dynamoid bakes in ActiveModel validations, just like ActiveRecord does.</p>
173
+
174
+ <pre class="code ruby"><code>class User
175
+ include Dynamoid::Document
176
+
177
+ ...
178
+
179
+ validates_presence_of :name
180
+ validates_format_of :email, :with =&gt; /@/
181
+ end
182
+ </code></pre>
183
+
184
+ <p>To see more usage and examples of ActiveModel validations, check out the <a href="http://api.rubyonrails.org/classes/ActiveModel/Validations.html">ActiveModel validation documentation</a>.</p>
185
+
186
+ <h3>Callbacks</h3>
187
+
188
+ <p>Dynamoid also employs ActiveModel callbacks. Right now, callbacks are defined on <code>save</code>, <code>update</code>, <code>destroy</code>, which allows you to do <code>before_</code> or <code>after_</code> any of those.</p>
189
+
190
+ <pre class="code ruby"><code>class User
191
+ include Dynamoid::Document
192
+
193
+ ...
194
+
195
+ before_save :set_default_password
196
+ after_create :notify_friends
197
+ after_destroy :delete_addresses
198
+ end
199
+ </code></pre>
200
+
201
+ <h2>Usage</h2>
202
+
203
+ <p>Dynamoid's syntax is very similar to ActiveRecord's.</p>
204
+
205
+ <pre class="code ruby"><code><span class='id identifier rubyid_u'>u</span> <span class='op'>=</span> <span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='symbol'>:name</span> <span class='op'>=&gt;</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Josh</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span>
206
+ <span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_email'>email</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>josh@joshsymonds.com</span><span class='tstring_end'>'</span></span>
207
+ <span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_save'>save</span>
208
+ </code></pre>
209
+
210
+ <p>Save forces persistence to the datastore: a unique ID is also assigned, but it is a string and not an auto-incrementing number.</p>
211
+
212
+ <pre class="code ruby"><code><span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_id'>id</span> <span class='comment'># =&gt; &quot;3a9f7216-4726-4aea-9fbc-8554ae9292cb&quot;
213
+ </span></code></pre>
214
+
215
+ <p>Along with persisting the model's attributes, indexes are automatically updated on save. To use associations, you use association methods very similar to ActiveRecord's:</p>
216
+
217
+ <pre class="code ruby"><code><span class='id identifier rubyid_address'>address</span> <span class='op'>=</span> <span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_addresses'>addresses</span><span class='period'>.</span><span class='id identifier rubyid_create'>create</span>
218
+ <span class='id identifier rubyid_address'>address</span><span class='period'>.</span><span class='id identifier rubyid_city'>city</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span>
219
+ <span class='id identifier rubyid_address'>address</span><span class='period'>.</span><span class='id identifier rubyid_save'>save</span>
220
+ </code></pre>
221
+
222
+ <p>Querying can be done in one of three ways:</p>
223
+
224
+ <pre class="code ruby"><code><span class='const'>Address</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='id identifier rubyid_address'>address</span><span class='period'>.</span><span class='id identifier rubyid_id'>id</span><span class='rparen'>)</span> <span class='comment'># Find directly by ID.
225
+ </span><span class='const'>Address</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='symbol'>:city</span> <span class='op'>=&gt;</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span> <span class='comment'># Find by any number of matching criteria... though presently only &quot;where&quot; is supported.
226
+ </span><span class='const'>Address</span><span class='period'>.</span><span class='id identifier rubyid_find_by_city'>find_by_city</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span> <span class='comment'># The same as above, but using ActiveRecord's older syntax.
227
+ </span></code></pre>
228
+
229
+ <p>And you can also query on associations:</p>
230
+
231
+ <pre class="code ruby"><code><span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_addresses'>addresses</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='symbol'>:city</span> <span class='op'>=&gt;</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
232
+ </code></pre>
233
+
234
+ <p>But keep in mind Dynamoid -- and document-based storage systems in general -- are not drop-in replacements for existing relational databases. The above query does not efficiently perform a conditional join, but instead finds all the user's addresses and naively filters them in Ruby. For large associations this is a performance hit compared to relational database engines.</p>
235
+
236
+ <p>If you have a range index, Dynamoid provides a number of additional other convenience methods to make your life a little easier:</p>
237
+
238
+ <pre class="code ruby"><code><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>created_at.gt</span><span class='tstring_end'>&quot;</span></span> <span class='op'>=&gt;</span> <span class='const'>DateTime</span><span class='period'>.</span><span class='id identifier rubyid_now'>now</span> <span class='op'>-</span> <span class='int'>1</span><span class='period'>.</span><span class='id identifier rubyid_day'>day</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
239
+ <span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>created_at.lt</span><span class='tstring_end'>&quot;</span></span> <span class='op'>=&gt;</span> <span class='const'>DateTime</span><span class='period'>.</span><span class='id identifier rubyid_now'>now</span> <span class='op'>-</span> <span class='int'>1</span><span class='period'>.</span><span class='id identifier rubyid_day'>day</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
240
+ </code></pre>
241
+
242
+ <p>It also supports .gte and .lte. Turning those into symbols and allowing a Rails SQL-style string syntax is in the works. You can only have one range argument per query, because of DynamoDB's inherent limitations, so use it sensibly!</p>
243
+
244
+ <h2>Partitioning, Provisioning, and Performance</h2>
245
+
246
+ <p>DynamoDB achieves much of its speed by relying on a random pattern of writes and reads: internally, hash keys are distributed across servers, and reading from two consecutive servers is much faster than reading from the same server twice. Of course, many of our applications request one key (like a commonly used role, a superuser, or a very popular product) much more frequently than other keys. In DynamoDB, this will result in lowered throughput and slower response times, and is a design pattern we should try to avoid.</p>
247
+
248
+ <p>Dynamoid attempts to obviate this problem transparently by employing a partitioning strategy to divide up keys randomly across DynamoDB's servers. Each ID is assigned an additional number (by default 0 to 199, but you can increase the partition size in Dynamoid's configuration) upon save; when read, all 200 hashes are retrieved simultaneously and the most recently updated one is returned to the application. This results in a significant net performance increase, and is usually invisible to the application itself. It does, however, bring up the important issue of provisioning your DynamoDB tables correctly.</p>
249
+
250
+ <p>When your read or write provisioning exceed your table's allowed throughput, DynamoDB will wait on connections until throughput is available again. This will appear as very, very slow requests and can be somewhat frustrating. Partitioning significantly increases the amount of throughput tables will experience; though DynamoDB will ignore keys that don't exist, if you have 20 partitioned keys representing one object, all will be retrieved every time the object is requested. Ensure that your tables are set up for this kind of throughput, or turn provisioning off, to make sure that DynamoDB doesn't throttle your requests.</p>
251
+
252
+ <h2>Credits</h2>
253
+
254
+ <p>Dynamoid borrows code, structure, and even its name very liberally from the truly amazing <a href="https://github.com/mongoid/mongoid">Mongoid</a>. Without Mongoid to crib from none of this would have been possible, and I hope they don't mind me reusing their very awesome ideas to make DynamoDB just as accessible to the Ruby world as MongoDB.</p>
255
+
256
+ <p>Also, without contributors the project wouldn't be nearly as awesome. So many thanks to:</p>
257
+
258
+ <ul>
259
+ <li><a href="https://github.com/ananthakumaran">Anantha Kumaran</a></li>
260
+ <li><a href="https://github.com/jasondew">Jason Dew</a></li>
261
+ </ul>
262
+
263
+ <h2>Running the tests</h2>
264
+
265
+ <p>The tests can be run in the simple predictable way with <code>rake</code>. However, if you provide environment variables for ACCESS_KEY and SECRET_KEY, the tests will use the aws_sdk adapter rather than the local adapter: <code>ACCESS_KEY=&lt;accesskey&gt; SECRET_KEY=&lt;secretkey&gt; rake</code>. Keep in mind this takes much, much longer than the local tests.</p>
266
+
267
+ <h2>Copyright</h2>
268
+
269
+ <p>Copyright (c) 2012 Josh Symonds. See LICENSE.txt for further details.</p>
270
+ </div></div>
271
+
272
+ <div id="footer">
273
+ Generated on Tue Mar 27 17:53:05 2012 by
274
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
275
+ 0.7.5 (ruby-1.9.3).
276
+ </div>
277
+
278
+ </body>
279
+ </html>
@@ -0,0 +1,52 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+
7
+ <link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen" charset="utf-8" />
8
+
9
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
10
+
11
+
12
+
13
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
14
+
15
+ <script type="text/javascript" charset="utf-8" src="js/full_list.js"></script>
16
+
17
+
18
+ <base id="base_target" target="_parent" />
19
+ </head>
20
+ <body>
21
+ <script type="text/javascript" charset="utf-8">
22
+ if (window.top.frames.main) {
23
+ document.getElementById('base_target').target = 'main';
24
+ document.body.className = 'frames';
25
+ }
26
+ </script>
27
+ <div id="content">
28
+ <h1 id="full_list_header">File List</h1>
29
+ <div id="nav">
30
+
31
+ <a target="_self" href="class_list.html">Classes</a>
32
+
33
+ <a target="_self" href="method_list.html">Methods</a>
34
+
35
+ <a target="_self" href="file_list.html">Files</a>
36
+
37
+ </div>
38
+ <div id="search">Search: <input type="text" /></div>
39
+
40
+ <ul id="full_list" class="files">
41
+
42
+
43
+ <li class="r1"><a href="index.html" title="README">README</a></li>
44
+
45
+
46
+ <li class="r2"><a href="file.LICENSE.html" title="LICENSE">LICENSE</a></li>
47
+
48
+
49
+ </ul>
50
+ </div>
51
+ </body>
52
+ </html>
data/doc/frames.html ADDED
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+ <title>Documentation by YARD 0.7.5</title>
8
+ </head>
9
+ <frameset cols="20%,*">
10
+ <frame name="list" src="class_list.html" />
11
+ <frame name="main" src="index.html" />
12
+ </frameset>
13
+ </html>
data/doc/index.html ADDED
@@ -0,0 +1,279 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
+ <title>
7
+ File: README
8
+
9
+ &mdash; Documentation by YARD 0.7.5
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ relpath = '';
19
+ if (relpath != '') relpath += '/';
20
+ </script>
21
+
22
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
25
+
26
+
27
+ </head>
28
+ <body>
29
+ <script type="text/javascript" charset="utf-8">
30
+ if (window.top.frames.main) document.body.className = 'frames';
31
+ </script>
32
+
33
+ <div id="header">
34
+ <div id="menu">
35
+
36
+ <a href="_index.html" title="Index">Index</a> &raquo;
37
+ <span class="title">File: README</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a id="class_list_link" href="#">Class List</a>
46
+
47
+ <a id="method_list_link" href="#">Method List</a>
48
+
49
+ <a id="file_list_link" href="#">File List</a>
50
+
51
+ </div>
52
+ <div class="clear"></div>
53
+ </div>
54
+
55
+ <iframe id="search_frame"></iframe>
56
+
57
+ <div id="content"><div id='filecontents'><h1>Dynamoid</h1>
58
+
59
+ <p>Dynamoid is an ORM for Amazon's DynamoDB for Ruby applications. It provides similar functionality to ActiveRecord and improves on Amazon's existing <a href="http://docs.amazonwebservices.com/AWSRubySDK/latest/AWS/Record/HashModel.html">HashModel</a> by providing better searching tools, native association support, and a local adapter for offline development.</p>
60
+
61
+ <p>DynamoDB is not like other document-based databases you might know, and is very different indeed from relational databases. It sacrifices anything beyond the simplest relational queries and transactional support to provide a fast, cost-efficient, and highly durable storage solution. If your database requires complicated relational queries and transaction support, then this modest Gem cannot provide them for you, and neither can DynamoDB. In those cases you would do better to look elsewhere for your database needs.</p>
62
+
63
+ <p>But if you want a fast, scalable, simple, easy-to-use database (and a Gem that supports it) then look no further!</p>
64
+
65
+ <h2>Installation</h2>
66
+
67
+ <p>Installing Dynamoid is pretty simple. First include the Gem in your Gemfile:</p>
68
+
69
+ <pre class="code ruby"><code><span class='id identifier rubyid_gem'>gem</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>dynamoid</span><span class='tstring_end'>'</span></span>
70
+ </code></pre>
71
+
72
+ <p>Then you need to initialize it to get it going. Put code similar to this somewhere (a Rails initializer would be a great place for this if you're using Rails):</p>
73
+
74
+ <pre class="code ruby"><code> <span class='const'>Dynamoid</span><span class='period'>.</span><span class='id identifier rubyid_configure'>configure</span> <span class='kw'>do</span> <span class='op'>|</span><span class='id identifier rubyid_config'>config</span><span class='op'>|</span>
75
+ <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_adapter'>adapter</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>local</span><span class='tstring_end'>'</span></span> <span class='comment'># This adapter allows offline development without connecting to the DynamoDB servers. Data is *NOT* persisted.
76
+ </span> <span class='comment'># config.adapter = 'aws_sdk' # This adapter establishes a connection to the DynamoDB servers using Amazon's own AWS gem.
77
+ </span> <span class='comment'># config.access_key = 'access_key' # If connecting to DynamoDB, your access key is required.
78
+ </span> <span class='comment'># config.secret_key = 'secret_key' # So is your secret key.
79
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_namespace'>namespace</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>dynamoid_app_development</span><span class='tstring_end'>&quot;</span></span> <span class='comment'># To namespace tables created by Dynamoid from other tables you might have.
80
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_warn_on_scan'>warn_on_scan</span> <span class='op'>=</span> <span class='kw'>true</span> <span class='comment'># Output a warning to the logger when you perform a scan rather than a query on a table.
81
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_partitioning'>partitioning</span> <span class='op'>=</span> <span class='kw'>true</span> <span class='comment'># Spread writes randomly across the database. See &quot;partitioning&quot; below for more.
82
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_partition_size'>partition_size</span> <span class='op'>=</span> <span class='int'>200</span> <span class='comment'># Determine the key space size that writes are randomly spread across.
83
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_read_capacity'>read_capacity</span> <span class='op'>=</span> <span class='int'>100</span> <span class='comment'># Read capacity for your tables
84
+ </span> <span class='id identifier rubyid_config'>config</span><span class='period'>.</span><span class='id identifier rubyid_write_capacity'>write_capacity</span> <span class='op'>=</span> <span class='int'>20</span> <span class='comment'># Write capacity for your tables
85
+ </span> <span class='kw'>end</span>
86
+
87
+ </code></pre>
88
+
89
+ <p>Once you have the configuration set up, you need to move on to making models.</p>
90
+
91
+ <h2>Setup</h2>
92
+
93
+ <p>You <em>must</em> include <code>Dynamoid::Document</code> in every Dynamoid model.</p>
94
+
95
+ <pre class="code ruby"><code><span class='kw'>class</span> <span class='const'>User</span>
96
+ <span class='id identifier rubyid_include'>include</span> <span class='const'>Dynamoid</span><span class='op'>::</span><span class='const'>Document</span>
97
+
98
+ <span class='kw'>end</span>
99
+ </code></pre>
100
+
101
+ <h3>Fields</h3>
102
+
103
+ <p>You'll have to define all the fields on the model and the data type of each field. Every field on the object must be included here; if you miss any they'll be completely bypassed during DynamoDB's initialization and will not appear on the model objects.</p>
104
+
105
+ <p>By default, fields are assumed to be of type <code>:string</code>. But you can also use <code>:integer</code>, <code>:float</code>, <code>:set</code>, <code>:array</code>, <code>:datetime</code>, and <code>:serialized</code>. You get magic columns of id (string), created_at (datetime), and updated_at (datetime) for free.</p>
106
+
107
+ <pre class="code ruby"><code><span class='kw'>class</span> <span class='const'>User</span>
108
+ <span class='id identifier rubyid_include'>include</span> <span class='const'>Dynamoid</span><span class='op'>::</span><span class='const'>Document</span>
109
+
110
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:name</span>
111
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:email</span>
112
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:rank</span><span class='comma'>,</span> <span class='symbol'>:integer</span>
113
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:number</span><span class='comma'>,</span> <span class='symbol'>:float</span>
114
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:joined_at</span><span class='comma'>,</span> <span class='symbol'>:datetime</span>
115
+ <span class='id identifier rubyid_field'>field</span> <span class='symbol'>:hash</span><span class='comma'>,</span> <span class='symbol'>:serialized</span>
116
+
117
+ <span class='kw'>end</span>
118
+ </code></pre>
119
+
120
+ <h3>Indexes</h3>
121
+
122
+ <p>You can also define indexes on fields, combinations of fields, and one range field. Yes, only one range field: in DynamoDB tables can have at most one range index, so make good use of it! To make an index, just specify the fields you want it on, either single or in an array. If the entire index is a range, pass <code>:range =&gt; true</code>. Otherwise, pass the attribute that will become the range key. The only range attributes you can use right now are integers, floats, and datetimes. If you pass a string as a range key likely DynamoDB will complain a lot.</p>
123
+
124
+ <pre class="code ruby"><code>class User
125
+ include Dynamoid::Document
126
+
127
+ ...
128
+
129
+ index :name
130
+ index :email
131
+ index [:name, :email]
132
+ index :created_at, :range =&gt; true
133
+ index :name, :range =&gt; :joined_at
134
+
135
+ end
136
+ </code></pre>
137
+
138
+ <h3>Associations</h3>
139
+
140
+ <p>Just like in ActiveRecord (or your other favorite ORM), Dynamoid uses associations to create links between models.</p>
141
+
142
+ <p>The only supported associations (so far) are <code>has_many</code>, <code>has_one</code>, <code>has_and_belongs_to_many</code>, and <code>belongs_to</code>. Associations are very simple to create: just specify the type, the name, and then any options you'd like to pass to the association. If there's an inverse association either inferred or specified directly, Dynamoid will update both objects to point at each other.</p>
143
+
144
+ <pre class="code ruby"><code>class User
145
+ include Dynamoid::Document
146
+
147
+ ...
148
+
149
+ has_many :addresses
150
+ has_many :students, :class =&gt; User
151
+ belongs_to :teacher, :class_name =&gt; :user
152
+ belongs_to :group
153
+ has_one :role
154
+ has_and_belongs_to_many :friends, :inverse_of =&gt; :friending_users
155
+
156
+ end
157
+
158
+ class Address
159
+ include Dynamoid::Document
160
+
161
+ ...
162
+
163
+ belongs_to :address # Automatically links up with the user model
164
+
165
+ end
166
+ </code></pre>
167
+
168
+ <p>Contrary to what you'd expect, association information is always contained on the object specifying the association, even if it seems like the association has a foreign key. This is a side effect of DynamoDB's structure: it's very difficult to find foreign keys without an index. Usually you won't find this to be a problem, but it does mean that association methods that build new models will not work correctly -- for example, <code>user.addresses.new</code> returns an address that is not associated to the user. We'll be correcting this soon.</p>
169
+
170
+ <h3>Validations</h3>
171
+
172
+ <p>Dynamoid bakes in ActiveModel validations, just like ActiveRecord does.</p>
173
+
174
+ <pre class="code ruby"><code>class User
175
+ include Dynamoid::Document
176
+
177
+ ...
178
+
179
+ validates_presence_of :name
180
+ validates_format_of :email, :with =&gt; /@/
181
+ end
182
+ </code></pre>
183
+
184
+ <p>To see more usage and examples of ActiveModel validations, check out the <a href="http://api.rubyonrails.org/classes/ActiveModel/Validations.html">ActiveModel validation documentation</a>.</p>
185
+
186
+ <h3>Callbacks</h3>
187
+
188
+ <p>Dynamoid also employs ActiveModel callbacks. Right now, callbacks are defined on <code>save</code>, <code>update</code>, <code>destroy</code>, which allows you to do <code>before_</code> or <code>after_</code> any of those.</p>
189
+
190
+ <pre class="code ruby"><code>class User
191
+ include Dynamoid::Document
192
+
193
+ ...
194
+
195
+ before_save :set_default_password
196
+ after_create :notify_friends
197
+ after_destroy :delete_addresses
198
+ end
199
+ </code></pre>
200
+
201
+ <h2>Usage</h2>
202
+
203
+ <p>Dynamoid's syntax is very similar to ActiveRecord's.</p>
204
+
205
+ <pre class="code ruby"><code><span class='id identifier rubyid_u'>u</span> <span class='op'>=</span> <span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='symbol'>:name</span> <span class='op'>=&gt;</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Josh</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span>
206
+ <span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_email'>email</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>josh@joshsymonds.com</span><span class='tstring_end'>'</span></span>
207
+ <span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_save'>save</span>
208
+ </code></pre>
209
+
210
+ <p>Save forces persistence to the datastore: a unique ID is also assigned, but it is a string and not an auto-incrementing number.</p>
211
+
212
+ <pre class="code ruby"><code><span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_id'>id</span> <span class='comment'># =&gt; &quot;3a9f7216-4726-4aea-9fbc-8554ae9292cb&quot;
213
+ </span></code></pre>
214
+
215
+ <p>Along with persisting the model's attributes, indexes are automatically updated on save. To use associations, you use association methods very similar to ActiveRecord's:</p>
216
+
217
+ <pre class="code ruby"><code><span class='id identifier rubyid_address'>address</span> <span class='op'>=</span> <span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_addresses'>addresses</span><span class='period'>.</span><span class='id identifier rubyid_create'>create</span>
218
+ <span class='id identifier rubyid_address'>address</span><span class='period'>.</span><span class='id identifier rubyid_city'>city</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span>
219
+ <span class='id identifier rubyid_address'>address</span><span class='period'>.</span><span class='id identifier rubyid_save'>save</span>
220
+ </code></pre>
221
+
222
+ <p>Querying can be done in one of three ways:</p>
223
+
224
+ <pre class="code ruby"><code><span class='const'>Address</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='id identifier rubyid_address'>address</span><span class='period'>.</span><span class='id identifier rubyid_id'>id</span><span class='rparen'>)</span> <span class='comment'># Find directly by ID.
225
+ </span><span class='const'>Address</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='symbol'>:city</span> <span class='op'>=&gt;</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span> <span class='comment'># Find by any number of matching criteria... though presently only &quot;where&quot; is supported.
226
+ </span><span class='const'>Address</span><span class='period'>.</span><span class='id identifier rubyid_find_by_city'>find_by_city</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span> <span class='comment'># The same as above, but using ActiveRecord's older syntax.
227
+ </span></code></pre>
228
+
229
+ <p>And you can also query on associations:</p>
230
+
231
+ <pre class="code ruby"><code><span class='id identifier rubyid_u'>u</span><span class='period'>.</span><span class='id identifier rubyid_addresses'>addresses</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='symbol'>:city</span> <span class='op'>=&gt;</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>Chicago</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
232
+ </code></pre>
233
+
234
+ <p>But keep in mind Dynamoid -- and document-based storage systems in general -- are not drop-in replacements for existing relational databases. The above query does not efficiently perform a conditional join, but instead finds all the user's addresses and naively filters them in Ruby. For large associations this is a performance hit compared to relational database engines.</p>
235
+
236
+ <p>If you have a range index, Dynamoid provides a number of additional other convenience methods to make your life a little easier:</p>
237
+
238
+ <pre class="code ruby"><code><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>created_at.gt</span><span class='tstring_end'>&quot;</span></span> <span class='op'>=&gt;</span> <span class='const'>DateTime</span><span class='period'>.</span><span class='id identifier rubyid_now'>now</span> <span class='op'>-</span> <span class='int'>1</span><span class='period'>.</span><span class='id identifier rubyid_day'>day</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
239
+ <span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_where'>where</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>created_at.lt</span><span class='tstring_end'>&quot;</span></span> <span class='op'>=&gt;</span> <span class='const'>DateTime</span><span class='period'>.</span><span class='id identifier rubyid_now'>now</span> <span class='op'>-</span> <span class='int'>1</span><span class='period'>.</span><span class='id identifier rubyid_day'>day</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
240
+ </code></pre>
241
+
242
+ <p>It also supports .gte and .lte. Turning those into symbols and allowing a Rails SQL-style string syntax is in the works. You can only have one range argument per query, because of DynamoDB's inherent limitations, so use it sensibly!</p>
243
+
244
+ <h2>Partitioning, Provisioning, and Performance</h2>
245
+
246
+ <p>DynamoDB achieves much of its speed by relying on a random pattern of writes and reads: internally, hash keys are distributed across servers, and reading from two consecutive servers is much faster than reading from the same server twice. Of course, many of our applications request one key (like a commonly used role, a superuser, or a very popular product) much more frequently than other keys. In DynamoDB, this will result in lowered throughput and slower response times, and is a design pattern we should try to avoid.</p>
247
+
248
+ <p>Dynamoid attempts to obviate this problem transparently by employing a partitioning strategy to divide up keys randomly across DynamoDB's servers. Each ID is assigned an additional number (by default 0 to 199, but you can increase the partition size in Dynamoid's configuration) upon save; when read, all 200 hashes are retrieved simultaneously and the most recently updated one is returned to the application. This results in a significant net performance increase, and is usually invisible to the application itself. It does, however, bring up the important issue of provisioning your DynamoDB tables correctly.</p>
249
+
250
+ <p>When your read or write provisioning exceed your table's allowed throughput, DynamoDB will wait on connections until throughput is available again. This will appear as very, very slow requests and can be somewhat frustrating. Partitioning significantly increases the amount of throughput tables will experience; though DynamoDB will ignore keys that don't exist, if you have 20 partitioned keys representing one object, all will be retrieved every time the object is requested. Ensure that your tables are set up for this kind of throughput, or turn provisioning off, to make sure that DynamoDB doesn't throttle your requests.</p>
251
+
252
+ <h2>Credits</h2>
253
+
254
+ <p>Dynamoid borrows code, structure, and even its name very liberally from the truly amazing <a href="https://github.com/mongoid/mongoid">Mongoid</a>. Without Mongoid to crib from none of this would have been possible, and I hope they don't mind me reusing their very awesome ideas to make DynamoDB just as accessible to the Ruby world as MongoDB.</p>
255
+
256
+ <p>Also, without contributors the project wouldn't be nearly as awesome. So many thanks to:</p>
257
+
258
+ <ul>
259
+ <li><a href="https://github.com/ananthakumaran">Anantha Kumaran</a></li>
260
+ <li><a href="https://github.com/jasondew">Jason Dew</a></li>
261
+ </ul>
262
+
263
+ <h2>Running the tests</h2>
264
+
265
+ <p>The tests can be run in the simple predictable way with <code>rake</code>. However, if you provide environment variables for ACCESS_KEY and SECRET_KEY, the tests will use the aws_sdk adapter rather than the local adapter: <code>ACCESS_KEY=&lt;accesskey&gt; SECRET_KEY=&lt;secretkey&gt; rake</code>. Keep in mind this takes much, much longer than the local tests.</p>
266
+
267
+ <h2>Copyright</h2>
268
+
269
+ <p>Copyright (c) 2012 Josh Symonds. See LICENSE.txt for further details.</p>
270
+ </div></div>
271
+
272
+ <div id="footer">
273
+ Generated on Tue Mar 27 17:53:05 2012 by
274
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
275
+ 0.7.5 (ruby-1.9.3).
276
+ </div>
277
+
278
+ </body>
279
+ </html>