ohm 0.1.0.rc6 → 0.1.0
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/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