ohm 0.1.0.rc6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +212 -26
- data/Rakefile +15 -0
- data/lib/ohm/key.rb +19 -1
- data/lib/ohm/validations.rb +116 -0
- data/lib/ohm/version.rb +1 -1
- data/lib/ohm.rb +1145 -86
- data/test/model_test.rb +8 -0
- metadata +6 -9
data/README.markdown
CHANGED
@@ -6,10 +6,9 @@ Object-hash mapping library for Redis.
|
|
6
6
|
Description
|
7
7
|
-----------
|
8
8
|
|
9
|
-
Ohm is a library for storing objects in
|
10
|
-
|
11
|
-
|
12
|
-
good performance.
|
9
|
+
Ohm is a library for storing objects in [Redis][redis], a persistent key-value
|
10
|
+
database. It includes an extensible list of validations and has very good
|
11
|
+
performance.
|
13
12
|
|
14
13
|
Community
|
15
14
|
---------
|
@@ -21,9 +20,8 @@ Meet us on IRC: [#ohm](irc://chat.freenode.net/#ohm) on [freenode.net](http://fr
|
|
21
20
|
Getting started
|
22
21
|
---------------
|
23
22
|
|
24
|
-
Install [Redis]
|
25
|
-
|
26
|
-
`redis-server` binary in the PATH.
|
23
|
+
Install [Redis][redis]. On most platforms it's as easy as grabbing the sources,
|
24
|
+
running make and then putting the `redis-server` binary in the PATH.
|
27
25
|
|
28
26
|
Once you have it installed, you can execute `redis-server` and it will
|
29
27
|
run on `localhost:6379` by default. Check the `redis.conf` file that comes
|
@@ -31,9 +29,9 @@ with the sources if you want to change some settings.
|
|
31
29
|
|
32
30
|
If you don't have Ohm, try this:
|
33
31
|
|
34
|
-
$ sudo gem install ohm
|
32
|
+
$ [sudo] gem install ohm
|
35
33
|
|
36
|
-
Or you can grab the code from [http://github.com/soveran/ohm]
|
34
|
+
Or you can grab the code from [http://github.com/soveran/ohm][ohm].
|
37
35
|
|
38
36
|
Now, in an irb session you can test the Redis adapter directly:
|
39
37
|
|
@@ -46,6 +44,48 @@ Now, in an irb session you can test the Redis adapter directly:
|
|
46
44
|
>> Ohm.redis.get "Foo"
|
47
45
|
=> "Bar"
|
48
46
|
|
47
|
+
## Connecting to the Redis database {: #connecting }
|
48
|
+
|
49
|
+
There are a couple of different strategies for connecting to your Redis
|
50
|
+
database. The first is to explicitly set the `:host`, `:port`, `:db` and
|
51
|
+
`:timeout` options. You can also set only a few of them, and let the other
|
52
|
+
options fall back to the default.
|
53
|
+
|
54
|
+
The other noteworthy style of connecting is by just doing `Ohm.connect` and
|
55
|
+
set the environment variable `REDIS_URL`.
|
56
|
+
|
57
|
+
Here are the options for {Ohm.connect} in detail:
|
58
|
+
|
59
|
+
**:url**
|
60
|
+
: A Redis URL of the form `redis://:<passwd>@<host>:<port>/<db>`.
|
61
|
+
Note that if you specify a URL and one of the other options at
|
62
|
+
the same time, the other options will take precedence. Also, if
|
63
|
+
you try and do `Ohm.connect` without any arguments, it will check
|
64
|
+
if `ENV["REDIS_URL"]` is set, and will use it as the argument for
|
65
|
+
`:url`.
|
66
|
+
|
67
|
+
**:host**
|
68
|
+
: Host where the Redis server is running, defaults to `"127.0.0.1"`.
|
69
|
+
|
70
|
+
**:port**
|
71
|
+
: Port number, defaults to `6379`.
|
72
|
+
|
73
|
+
**:db**
|
74
|
+
: Database number, defaults to `0`.
|
75
|
+
|
76
|
+
**:password**
|
77
|
+
: It is the secret that will be sent to the Redis server. Use it if the server
|
78
|
+
configuration requires it. Defaults to `nil`.
|
79
|
+
|
80
|
+
**:timeout**
|
81
|
+
: Database timeout in seconds, defaults to `0`.
|
82
|
+
|
83
|
+
**:thread_safe**
|
84
|
+
: Initializes the client with a monitor. It has a small performance penalty, and
|
85
|
+
it's off by default. For thread safety, it is recommended to use a different
|
86
|
+
instance per thread. I you have no choice, then pass `:thread_safe => true`
|
87
|
+
when connecting.
|
88
|
+
|
49
89
|
Models
|
50
90
|
------
|
51
91
|
|
@@ -99,10 +139,10 @@ validations. Keep reading to find out what you can do with models.
|
|
99
139
|
Attribute types
|
100
140
|
---------------
|
101
141
|
|
102
|
-
Ohm::Model provides four attribute types: {Ohm::Model
|
103
|
-
attribute}, {Ohm::Model
|
104
|
-
and {Ohm::Model
|
105
|
-
{Ohm::Model
|
142
|
+
Ohm::Model provides four attribute types: {Ohm::Model.attribute
|
143
|
+
attribute}, {Ohm::Model.set set}, {Ohm::Model.list list}
|
144
|
+
and {Ohm::Model.counter counter}; and two meta types:
|
145
|
+
{Ohm::Model.reference reference} and {Ohm::Model.collection
|
106
146
|
collection}.
|
107
147
|
|
108
148
|
### attribute
|
@@ -162,13 +202,13 @@ If you need to check for validity before operating on lists, sets or
|
|
162
202
|
counters, you can use this pattern:
|
163
203
|
|
164
204
|
if event.valid?
|
165
|
-
event.comments << "Great event!"
|
205
|
+
event.comments << Comment.create(:body => "Great event!")
|
166
206
|
end
|
167
207
|
|
168
208
|
If you are saving the object, this will suffice:
|
169
209
|
|
170
210
|
if event.save
|
171
|
-
event.comments << "Wonderful event!"
|
211
|
+
event.comments << Comment.create(:body => "Wonderful event!")
|
172
212
|
end
|
173
213
|
|
174
214
|
Working with Sets
|
@@ -184,22 +224,84 @@ Given the following model declaration:
|
|
184
224
|
You can add instances of `Person` to the set of attendees with the
|
185
225
|
`<<` method:
|
186
226
|
|
187
|
-
|
227
|
+
event.attendees << Person.create(:name => "Albert")
|
188
228
|
|
189
229
|
# And now...
|
190
|
-
|
230
|
+
event.attendees.each do |person|
|
191
231
|
# ...do what you want with this person.
|
192
232
|
end
|
193
233
|
|
194
|
-
Sorting
|
195
|
-
-------
|
234
|
+
## Sorting {: #sorting}
|
196
235
|
|
197
236
|
Since `attendees` is a {Ohm::Model::Set Set}, it exposes two sorting
|
198
237
|
methods: {Ohm::Model::Collection#sort sort} returns the elements
|
199
238
|
ordered by `id`, and {Ohm::Model::Collection#sort_by sort_by} receives
|
200
239
|
a parameter with an attribute name, which will determine the sorting
|
201
|
-
order. Both methods receive an options hash which is explained
|
202
|
-
|
240
|
+
order. Both methods receive an options hash which is explained below:
|
241
|
+
|
242
|
+
**:order**
|
243
|
+
: Order direction and strategy. You can pass in any of
|
244
|
+
the following:
|
245
|
+
|
246
|
+
1. ASC
|
247
|
+
2. ASC ALPHA (or ALPHA ASC)
|
248
|
+
3. DESC
|
249
|
+
4. DESC ALPHA (or ALPHA DESC)
|
250
|
+
|
251
|
+
It defaults to `ASC`.
|
252
|
+
|
253
|
+
**:start**
|
254
|
+
: The offset from which we should start with. Note that
|
255
|
+
this is 0-indexed. It defaults to `0`.
|
256
|
+
|
257
|
+
**:limit**
|
258
|
+
: The number of entries to get. If you don't pass in anything, it will
|
259
|
+
get all the results from the LIST or SET that you are sorting.
|
260
|
+
|
261
|
+
**:by**
|
262
|
+
: Key or Hash key with which to sort by. An important distinction with
|
263
|
+
using {Ohm::Model::Collection#sort sort} and
|
264
|
+
{Ohm::Model::Collection#sort_by sort_by} is that `sort_by` automatically
|
265
|
+
converts the passed argument with the assumption that it is a hash key
|
266
|
+
and it's within the current model you are sorting.
|
267
|
+
|
268
|
+
Post.all.sort_by(:title) # SORT Post:all BY Post:*->title
|
269
|
+
Post.all.sort(:by => :title) # SORT Post:all BY title
|
270
|
+
|
271
|
+
**:get**
|
272
|
+
: A key pattern to return, e.g. `Post:*->title`. As is the case with
|
273
|
+
the `:by` option, using {Ohm::Model::Collection#sort sort} and
|
274
|
+
{Ohm::Model::Collection#sort_by sort_by} has distinct differences in
|
275
|
+
that `sort_by` does much of the hand-coding for you.
|
276
|
+
|
277
|
+
Post.all.sort_by(:title, :get => :title)
|
278
|
+
# SORT Post:all BY Post:*->title GET Post:*->title
|
279
|
+
|
280
|
+
Post.all.sort(:by => :title, :get => :title)
|
281
|
+
# SORT Post:all BY title GET title
|
282
|
+
|
283
|
+
|
284
|
+
**:store**
|
285
|
+
: An optional key which you may use to cache the sorted result. The key
|
286
|
+
may or may not exist.
|
287
|
+
|
288
|
+
This option can only be used together with `:get`.
|
289
|
+
|
290
|
+
The type that is used for the STORE key is a LIST.
|
291
|
+
|
292
|
+
Post.all.sort_by(:title, :store => "FOO")
|
293
|
+
|
294
|
+
# Get all the results stored in FOO.
|
295
|
+
Post.db.lrange("FOO", 0, -1)
|
296
|
+
|
297
|
+
When using temporary values, it might be a good idea to use a `volatile`
|
298
|
+
key. In Ohm, a volatile key means it just starts with a `~` character.
|
299
|
+
|
300
|
+
Post.all.sort_by(:title, :get => :title,
|
301
|
+
:store => Post.key.volatile["FOO"])
|
302
|
+
|
303
|
+
Post.key.volatile["FOO"].lrange 0, -1
|
304
|
+
|
203
305
|
|
204
306
|
Associations
|
205
307
|
------------
|
@@ -219,19 +321,85 @@ Ohm lets you declare `references` and `collections` to represent associations.
|
|
219
321
|
|
220
322
|
After this, every time you refer to `post.comments` you will be talking
|
221
323
|
about instances of the model `Comment`. If you want to get a list of IDs
|
222
|
-
you can use `post.comments.
|
324
|
+
you can use `post.comments.key.smembers`.
|
325
|
+
|
326
|
+
### References explained {: #references }
|
327
|
+
|
328
|
+
Doing a {Ohm::Model.reference reference} is actually just a shortcut for
|
329
|
+
the following:
|
330
|
+
|
331
|
+
# Redefining our model above
|
332
|
+
class Comment < Ohm::Model
|
333
|
+
attribute :body
|
334
|
+
attribute :post_id
|
335
|
+
index :post_id
|
336
|
+
|
337
|
+
def post=(post)
|
338
|
+
self.post_id = post.id
|
339
|
+
end
|
340
|
+
|
341
|
+
def post
|
342
|
+
Post[post_id]
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
_(The only difference with the actual implementation is that the model
|
347
|
+
is memoized.)_
|
348
|
+
|
349
|
+
The net effect here is we can conveniently set and retrieve `Post` objects,
|
350
|
+
and also search comments using the `post_id` index.
|
351
|
+
|
352
|
+
Comment.find(:post_id => 1)
|
353
|
+
|
354
|
+
|
355
|
+
### Collections explained {: #collections }
|
356
|
+
|
357
|
+
The reason a {Ohm::Model.reference reference} and a
|
358
|
+
{Ohm::Model.collection collection} go hand in hand, is that a collection is
|
359
|
+
just a macro that defines a finder for you, and we know that to find a model
|
360
|
+
by a field requires an {Ohm::Model.index index} to be defined for the field
|
361
|
+
you want to search.
|
362
|
+
|
363
|
+
# Redefining our post above
|
364
|
+
class Post < Ohm::Model
|
365
|
+
attribute :title
|
366
|
+
attribute :body
|
367
|
+
|
368
|
+
def comments
|
369
|
+
Comment.find(:post_id => self.id)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
The only "magic" happening is with the inference of the `index` that was used
|
374
|
+
in the other model. The following all produce the same effect:
|
375
|
+
|
376
|
+
# easiest, with the basic assumption that the index is `:post_id`
|
377
|
+
collection :comments, Comment
|
378
|
+
|
379
|
+
# we can explicitly declare this as follows too:
|
380
|
+
collection :comments, Comment, :post
|
381
|
+
|
382
|
+
# finally, we can use the default argument for the third parameter which
|
383
|
+
# is `to_reference`.
|
384
|
+
collection :comments, Comment, to_reference
|
385
|
+
|
386
|
+
# exploring `to_reference` reveals a very interesting and simple concept:
|
387
|
+
Post.to_reference == :post
|
388
|
+
# => true
|
223
389
|
|
224
390
|
Indexes
|
225
391
|
-------
|
226
392
|
|
227
|
-
An index is a set that's handled automatically by Ohm. For
|
228
|
-
Ohm maintains different sets of objects IDs for quick
|
393
|
+
An {Ohm::Model.index index} is a set that's handled automatically by Ohm. For
|
394
|
+
any index declared, Ohm maintains different sets of objects IDs for quick
|
395
|
+
lookups.
|
229
396
|
|
230
397
|
In the `Event` example, the index on the name attribute will
|
231
398
|
allow for searches like `Event.find(:name => "some value")`.
|
232
399
|
|
233
|
-
Note that the
|
234
|
-
|
400
|
+
Note that the {Ohm::Model::Validations#assert_unique assert_unique}
|
401
|
+
validation and the methods {Ohm::Model::Set#find find} and
|
402
|
+
{Ohm::Model::Set#except except} need a corresponding index in order to work.
|
235
403
|
|
236
404
|
### Finding records
|
237
405
|
|
@@ -372,6 +540,20 @@ Ohm is rather small and can be extended in many ways.
|
|
372
540
|
|
373
541
|
A lot of amazing contributions are available at [Ohm Contrib](http://labs.sinefunc.com/ohm-contrib/doc/), make sure to check them if you need to extend Ohm's functionality.
|
374
542
|
|
543
|
+
Tutorials
|
544
|
+
=========
|
545
|
+
|
546
|
+
Check the examples to get a feeling of the design patterns for Redis.
|
547
|
+
|
548
|
+
1. [Activity Feed](examples/activity-feed.html)
|
549
|
+
2. [Chaining finds](examples/chaining.html)
|
550
|
+
3. [Serialization to JSON](examples/json-hash.html)
|
551
|
+
4. [One to many associations](examples/one-to-many.html)
|
552
|
+
5. [Philosophy behind Ohm](examples/philosophy.html)
|
553
|
+
6. [Learning Ohm internals](examples/redis-logging.html)
|
554
|
+
7. [Slugs and permalinks](examples/slug.html)
|
555
|
+
8. [Tagging](examples/tagging.html)
|
556
|
+
|
375
557
|
Versions
|
376
558
|
========
|
377
559
|
|
@@ -396,3 +578,7 @@ don't have to load your application environment. Since we assume it's
|
|
396
578
|
very likely that you have a bunch of data, the script uses
|
397
579
|
[Batch](http://github.com/djanowski/batch) to show you some progress
|
398
580
|
while the process runs.
|
581
|
+
|
582
|
+
|
583
|
+
[redis]: http://redis.io
|
584
|
+
[ohm]: http://github.com/soveran/ohm
|
data/Rakefile
CHANGED
@@ -20,6 +20,7 @@ desc "Stop the Redis server"
|
|
20
20
|
task :stop do
|
21
21
|
if File.exists?(REDIS_PID)
|
22
22
|
system "kill #{File.read(REDIS_PID)}"
|
23
|
+
File.delete(REDIS_PID)
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
@@ -28,3 +29,17 @@ task :test do
|
|
28
29
|
|
29
30
|
Cutest.run(Dir["test/*_test.rb"])
|
30
31
|
end
|
32
|
+
|
33
|
+
namespace :examples do
|
34
|
+
desc "Run all the examples"
|
35
|
+
task :run do
|
36
|
+
begin
|
37
|
+
require "cutest"
|
38
|
+
rescue LoadError
|
39
|
+
raise "!! Missing gem `cutest`. Try `gem install cutest`."
|
40
|
+
end
|
41
|
+
|
42
|
+
Cutest.run(Dir["examples/*.rb"])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
data/lib/ohm/key.rb
CHANGED
@@ -1,17 +1,35 @@
|
|
1
1
|
module Ohm
|
2
2
|
|
3
|
-
# Represents a key in Redis.
|
3
|
+
# Represents a key in Redis. Much of the heavylifting is done using the
|
4
|
+
# Nest library.
|
5
|
+
#
|
6
|
+
# The most important change added by {Ohm::Key} to Nest is the concept of
|
7
|
+
# volatile keys, which are used heavily when doing {Ohm::Model::Set#find} or
|
8
|
+
# {Ohm::Model::Set#except} operations.
|
9
|
+
#
|
10
|
+
# A volatile key is simply a key prefixed with `~`. This gives you the
|
11
|
+
# benefit if quickly seeing which keys are temporary keys by doing something
|
12
|
+
# like:
|
13
|
+
#
|
14
|
+
# $ redis-cli keys "~*"
|
15
|
+
#
|
16
|
+
# @see http://github.com/soveran/nest
|
4
17
|
class Key < Nest
|
5
18
|
def volatile
|
6
19
|
self.index("~") == 0 ? self : self.class.new("~", redis)[self]
|
7
20
|
end
|
8
21
|
|
22
|
+
# Produces a key with `other` suffixed with itself. This is primarily
|
23
|
+
# used for storing SINTERSTORE results.
|
9
24
|
def +(other)
|
10
25
|
self.class.new("#{self}+#{other}", redis)
|
11
26
|
end
|
12
27
|
|
28
|
+
# Produces a key with `other` suffixed with itself. This is primarily
|
29
|
+
# used for storing SDIFFSTORE results.
|
13
30
|
def -(other)
|
14
31
|
self.class.new("#{self}-#{other}", redis)
|
15
32
|
end
|
16
33
|
end
|
17
34
|
end
|
35
|
+
|
data/lib/ohm/validations.rb
CHANGED
@@ -1,7 +1,60 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
module Ohm
|
4
|
+
# Provides a base implementation for extensible validation routines.
|
5
|
+
# {Ohm::Validations} currently only provides the following assertions:
|
6
|
+
#
|
7
|
+
# * assert
|
8
|
+
# * assert_present
|
9
|
+
# * assert_format
|
10
|
+
# * assert_numeric
|
11
|
+
#
|
12
|
+
# The core tenets that Ohm::Validations advocates can be summed up in a
|
13
|
+
# few bullet points:
|
14
|
+
#
|
15
|
+
# 1. Validations are much simpler and better done using composition rather
|
16
|
+
# than macros.
|
17
|
+
# 2. Error messages should be kept separate and possibly in the view or
|
18
|
+
# presenter layer.
|
19
|
+
# 3. It should be easy to write your own validation routine.
|
20
|
+
#
|
21
|
+
# Since Ohm's philosophy is to keep the core code small, other validations
|
22
|
+
# are simply added on a per-model or per-project basis.
|
23
|
+
#
|
24
|
+
# If you want other validations you may want to take a peek at Ohm::Contrib
|
25
|
+
# and all of the validation modules it provides.
|
26
|
+
#
|
27
|
+
# @see http://cyx.github.com/ohm-contrib/doc/Ohm/WebValidations.html
|
28
|
+
# @see http://cyx.github.com/ohm-contrib/doc/Ohm/NumberValidations.html
|
29
|
+
# @see http://cyx.github.com/ohm-contrib/doc/Ohm/ExtraValidations.html
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
#
|
33
|
+
# class Product < Ohm::Model
|
34
|
+
# attribute :title
|
35
|
+
# attribute :price
|
36
|
+
# attribute :date
|
37
|
+
#
|
38
|
+
# def validate
|
39
|
+
# assert_present :title
|
40
|
+
# assert_numeric :price
|
41
|
+
# assert_format :date, /\A[\d]{4}-[\d]{1,2}-[\d]{1,2}\z
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# product = Product.new
|
46
|
+
# product.valid? == false
|
47
|
+
# # => true
|
48
|
+
#
|
49
|
+
# product.errors == [[:title, :not_present], [:price, :not_numeric],
|
50
|
+
# [:date, :format]]
|
51
|
+
# # => true
|
52
|
+
#
|
4
53
|
module Validations
|
54
|
+
# Provides a simple implementation using the Presenter Pattern. When
|
55
|
+
# presenting errors, you have to properly catch all errors generated, or
|
56
|
+
# else you'll get an {Ohm::Validations::Presenter::UnhandledErrors}
|
57
|
+
# exception.
|
5
58
|
class Presenter
|
6
59
|
class UnhandledErrors < StandardError
|
7
60
|
attr :errors
|
@@ -47,6 +100,8 @@ module Ohm
|
|
47
100
|
end
|
48
101
|
end
|
49
102
|
|
103
|
+
# A simple class for storing all errors. Since {Ohm::Validations::Errors}
|
104
|
+
# extends Array, you can expect all array methods to work on it.
|
50
105
|
class Errors < Array
|
51
106
|
attr_accessor :model
|
52
107
|
|
@@ -59,39 +114,100 @@ module Ohm
|
|
59
114
|
end
|
60
115
|
end
|
61
116
|
|
117
|
+
# Check if the current model state is valid. Each call to {#valid?} will
|
118
|
+
# reset the {#errors} array.
|
119
|
+
#
|
120
|
+
# All model validations should be declared in a `validate` method.
|
121
|
+
#
|
122
|
+
# @example
|
123
|
+
#
|
124
|
+
# class Post < Ohm::Model
|
125
|
+
# attribute :title
|
126
|
+
#
|
127
|
+
# def validate
|
128
|
+
# assert_present :title
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
#
|
62
132
|
def valid?
|
63
133
|
errors.clear
|
64
134
|
validate
|
65
135
|
errors.empty?
|
66
136
|
end
|
67
137
|
|
138
|
+
# Base validate implementation.
|
68
139
|
def validate
|
69
140
|
end
|
70
141
|
|
142
|
+
# All errors for this model.
|
71
143
|
def errors
|
72
144
|
@errors ||= Errors.new(self)
|
73
145
|
end
|
74
146
|
|
75
147
|
protected
|
76
148
|
|
149
|
+
# Allows you to do a validation check against a regular expression.
|
150
|
+
# It's important to note that this internally calls {#assert_present},
|
151
|
+
# therefore you need not structure your regular expression to check
|
152
|
+
# for a non-empty value.
|
153
|
+
#
|
154
|
+
# @param [Symbol] att The attribute you want to verify the format of.
|
155
|
+
# @param [Regexp] format The regular expression with which to compare
|
156
|
+
# the value of att with.
|
157
|
+
# @param [Array<Symbol, Symbol>] error The error that should be returned
|
158
|
+
# when the validation fails.
|
77
159
|
def assert_format(att, format, error = [att, :format])
|
78
160
|
if assert_present(att, error)
|
79
161
|
assert(send(att).to_s.match(format), error)
|
80
162
|
end
|
81
163
|
end
|
82
164
|
|
165
|
+
# The most basic and highly useful assertion. Simply checks if the
|
166
|
+
# value of the attribute is empty.
|
167
|
+
#
|
168
|
+
# @param [Symbol] att The attribute you wish to verify the presence of.
|
169
|
+
# @param [Array<Symbol, Symbol>] error The error that should be returned
|
170
|
+
# when the validation fails.
|
83
171
|
def assert_present(att, error = [att, :not_present])
|
84
172
|
assert(!send(att).to_s.empty?, error)
|
85
173
|
end
|
86
174
|
|
175
|
+
# Checks if all the characters of an attribute is a digit. If you want to
|
176
|
+
# verify that a value is a decimal, try looking at Ohm::Contrib's
|
177
|
+
# assert_decimal assertion.
|
178
|
+
#
|
179
|
+
# @param [Symbol] att The attribute you wish to verify the numeric format.
|
180
|
+
# @param [Array<Symbol, Symbol>] error The error that should be returned
|
181
|
+
# when the validation fails.
|
182
|
+
# @see http://cyx.github.com/ohm-contrib/doc/Ohm/NumberValidations.html
|
87
183
|
def assert_numeric(att, error = [att, :not_numeric])
|
88
184
|
if assert_present(att, error)
|
89
185
|
assert_format(att, /^\d+$/, error)
|
90
186
|
end
|
91
187
|
end
|
92
188
|
|
189
|
+
# The grand daddy of all assertions. If you want to build custom
|
190
|
+
# assertions, or even quick and dirty ones, you can simply use this method.
|
191
|
+
#
|
192
|
+
# @example
|
193
|
+
#
|
194
|
+
# class Post < Ohm::Model
|
195
|
+
# attribute :slug
|
196
|
+
# attribute :votes
|
197
|
+
#
|
198
|
+
# def validate
|
199
|
+
# assert_slug :slug
|
200
|
+
# assert votes.to_i > 0, [:votes, :not_valid]
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
# protected
|
204
|
+
# def assert_slug(att, error = [att, :not_slug])
|
205
|
+
# assert send(att).to_s =~ /\A[a-z\-0-9]+\z/, error
|
206
|
+
# end
|
207
|
+
# end
|
93
208
|
def assert(value, error)
|
94
209
|
value or errors.push(error) && false
|
95
210
|
end
|
96
211
|
end
|
97
212
|
end
|
213
|
+
|
data/lib/ohm/version.rb
CHANGED