README.markdown in ohm-0.1.0.rc6 vs README.markdown in ohm-0.1.0
- old
+ new
@@ -4,14 +4,13 @@
Object-hash mapping library for Redis.
Description
-----------
-Ohm is a library for storing objects in
-[Redis](http://code.google.com/p/redis/), a persistent key-value
-database. It includes an extensible list of validations and has very
-good performance.
+Ohm is a library for storing objects in [Redis][redis], a persistent key-value
+database. It includes an extensible list of validations and has very good
+performance.
Community
---------
Join the mailing list: [http://groups.google.com/group/ohm-ruby](http://groups.google.com/group/ohm-ruby)
@@ -19,23 +18,22 @@
Meet us on IRC: [#ohm](irc://chat.freenode.net/#ohm) on [freenode.net](http://freenode.net/)
Getting started
---------------
-Install [Redis](http://code.google.com/p/redis/). On most platforms
-it's as easy as grabbing the sources, running make and then putting the
-`redis-server` binary in the PATH.
+Install [Redis][redis]. On most platforms it's as easy as grabbing the sources,
+running make and then putting the `redis-server` binary in the PATH.
Once you have it installed, you can execute `redis-server` and it will
run on `localhost:6379` by default. Check the `redis.conf` file that comes
with the sources if you want to change some settings.
If you don't have Ohm, try this:
- $ sudo gem install ohm
+ $ [sudo] gem install ohm
-Or you can grab the code from [http://github.com/soveran/ohm](http://github.com/soveran/ohm).
+Or you can grab the code from [http://github.com/soveran/ohm][ohm].
Now, in an irb session you can test the Redis adapter directly:
>> require "ohm"
=> true
@@ -44,10 +42,52 @@
>> Ohm.redis.set "Foo", "Bar"
=> "OK"
>> Ohm.redis.get "Foo"
=> "Bar"
+## Connecting to the Redis database {: #connecting }
+
+There are a couple of different strategies for connecting to your Redis
+database. The first is to explicitly set the `:host`, `:port`, `:db` and
+`:timeout` options. You can also set only a few of them, and let the other
+options fall back to the default.
+
+The other noteworthy style of connecting is by just doing `Ohm.connect` and
+set the environment variable `REDIS_URL`.
+
+Here are the options for {Ohm.connect} in detail:
+
+**:url**
+: A Redis URL of the form `redis://:<passwd>@<host>:<port>/<db>`.
+ Note that if you specify a URL and one of the other options at
+ the same time, the other options will take precedence. Also, if
+ you try and do `Ohm.connect` without any arguments, it will check
+ if `ENV["REDIS_URL"]` is set, and will use it as the argument for
+ `:url`.
+
+**:host**
+: Host where the Redis server is running, defaults to `"127.0.0.1"`.
+
+**:port**
+: Port number, defaults to `6379`.
+
+**:db**
+: Database number, defaults to `0`.
+
+**:password**
+: It is the secret that will be sent to the Redis server. Use it if the server
+ configuration requires it. Defaults to `nil`.
+
+**:timeout**
+: Database timeout in seconds, defaults to `0`.
+
+**:thread_safe**
+: Initializes the client with a monitor. It has a small performance penalty, and
+ it's off by default. For thread safety, it is recommended to use a different
+ instance per thread. I you have no choice, then pass `:thread_safe => true`
+ when connecting.
+
Models
------
Ohm's purpose in life is to map objects to a key value datastore. It
doesn't need migrations or external schema definitions. Take a look at
@@ -97,14 +137,14 @@
validations. Keep reading to find out what you can do with models.
Attribute types
---------------
-Ohm::Model provides four attribute types: {Ohm::Model::attribute
-attribute}, {Ohm::Model::set set}, {Ohm::Model::list list}
-and {Ohm::Model::counter counter}; and two meta types:
-{Ohm::Model::reference reference} and {Ohm::Model::collection
+Ohm::Model provides four attribute types: {Ohm::Model.attribute
+attribute}, {Ohm::Model.set set}, {Ohm::Model.list list}
+and {Ohm::Model.counter counter}; and two meta types:
+{Ohm::Model.reference reference} and {Ohm::Model.collection
collection}.
### attribute
An `attribute` is just any value that can be stored as a string. In the
@@ -160,17 +200,17 @@
For most use cases, this pattern doesn't represent a problem.
If you need to check for validity before operating on lists, sets or
counters, you can use this pattern:
if event.valid?
- event.comments << "Great event!"
+ event.comments << Comment.create(:body => "Great event!")
end
If you are saving the object, this will suffice:
if event.save
- event.comments << "Wonderful event!"
+ event.comments << Comment.create(:body => "Wonderful event!")
end
Working with Sets
-----------------
@@ -182,27 +222,89 @@
end
You can add instances of `Person` to the set of attendees with the
`<<` method:
- @event.attendees << Person.create(name: "Albert")
+ event.attendees << Person.create(:name => "Albert")
# And now...
- @event.attendees.each do |person|
+ event.attendees.each do |person|
# ...do what you want with this person.
end
-Sorting
--------
+## Sorting {: #sorting}
Since `attendees` is a {Ohm::Model::Set Set}, it exposes two sorting
methods: {Ohm::Model::Collection#sort sort} returns the elements
ordered by `id`, and {Ohm::Model::Collection#sort_by sort_by} receives
a parameter with an attribute name, which will determine the sorting
-order. Both methods receive an options hash which is explained in the
-documentation for {Ohm::Model::Collection#sort sort}.
+order. Both methods receive an options hash which is explained below:
+**:order**
+: Order direction and strategy. You can pass in any of
+ the following:
+
+ 1. ASC
+ 2. ASC ALPHA (or ALPHA ASC)
+ 3. DESC
+ 4. DESC ALPHA (or ALPHA DESC)
+
+ It defaults to `ASC`.
+
+**:start**
+: The offset from which we should start with. Note that
+ this is 0-indexed. It defaults to `0`.
+
+**:limit**
+: The number of entries to get. If you don't pass in anything, it will
+ get all the results from the LIST or SET that you are sorting.
+
+**:by**
+: Key or Hash key with which to sort by. An important distinction with
+ using {Ohm::Model::Collection#sort sort} and
+ {Ohm::Model::Collection#sort_by sort_by} is that `sort_by` automatically
+ converts the passed argument with the assumption that it is a hash key
+ and it's within the current model you are sorting.
+
+ Post.all.sort_by(:title) # SORT Post:all BY Post:*->title
+ Post.all.sort(:by => :title) # SORT Post:all BY title
+
+**:get**
+: A key pattern to return, e.g. `Post:*->title`. As is the case with
+ the `:by` option, using {Ohm::Model::Collection#sort sort} and
+ {Ohm::Model::Collection#sort_by sort_by} has distinct differences in
+ that `sort_by` does much of the hand-coding for you.
+
+ Post.all.sort_by(:title, :get => :title)
+ # SORT Post:all BY Post:*->title GET Post:*->title
+
+ Post.all.sort(:by => :title, :get => :title)
+ # SORT Post:all BY title GET title
+
+
+**:store**
+: An optional key which you may use to cache the sorted result. The key
+ may or may not exist.
+
+ This option can only be used together with `:get`.
+
+ The type that is used for the STORE key is a LIST.
+
+ Post.all.sort_by(:title, :store => "FOO")
+
+ # Get all the results stored in FOO.
+ Post.db.lrange("FOO", 0, -1)
+
+ When using temporary values, it might be a good idea to use a `volatile`
+ key. In Ohm, a volatile key means it just starts with a `~` character.
+
+ Post.all.sort_by(:title, :get => :title,
+ :store => Post.key.volatile["FOO"])
+
+ Post.key.volatile["FOO"].lrange 0, -1
+
+
Associations
------------
Ohm lets you declare `references` and `collections` to represent associations.
@@ -217,23 +319,89 @@
reference :post, Post
end
After this, every time you refer to `post.comments` you will be talking
about instances of the model `Comment`. If you want to get a list of IDs
-you can use `post.comments.raw`.
+you can use `post.comments.key.smembers`.
+### References explained {: #references }
+
+Doing a {Ohm::Model.reference reference} is actually just a shortcut for
+the following:
+
+ # Redefining our model above
+ class Comment < Ohm::Model
+ attribute :body
+ attribute :post_id
+ index :post_id
+
+ def post=(post)
+ self.post_id = post.id
+ end
+
+ def post
+ Post[post_id]
+ end
+ end
+
+_(The only difference with the actual implementation is that the model
+is memoized.)_
+
+The net effect here is we can conveniently set and retrieve `Post` objects,
+and also search comments using the `post_id` index.
+
+ Comment.find(:post_id => 1)
+
+
+### Collections explained {: #collections }
+
+The reason a {Ohm::Model.reference reference} and a
+{Ohm::Model.collection collection} go hand in hand, is that a collection is
+just a macro that defines a finder for you, and we know that to find a model
+by a field requires an {Ohm::Model.index index} to be defined for the field
+you want to search.
+
+ # Redefining our post above
+ class Post < Ohm::Model
+ attribute :title
+ attribute :body
+
+ def comments
+ Comment.find(:post_id => self.id)
+ end
+ end
+
+The only "magic" happening is with the inference of the `index` that was used
+in the other model. The following all produce the same effect:
+
+ # easiest, with the basic assumption that the index is `:post_id`
+ collection :comments, Comment
+
+ # we can explicitly declare this as follows too:
+ collection :comments, Comment, :post
+
+ # finally, we can use the default argument for the third parameter which
+ # is `to_reference`.
+ collection :comments, Comment, to_reference
+
+ # exploring `to_reference` reveals a very interesting and simple concept:
+ Post.to_reference == :post
+ # => true
+
Indexes
-------
-An index is a set that's handled automatically by Ohm. For any index declared,
-Ohm maintains different sets of objects IDs for quick lookups.
+An {Ohm::Model.index index} is a set that's handled automatically by Ohm. For
+any index declared, Ohm maintains different sets of objects IDs for quick
+lookups.
In the `Event` example, the index on the name attribute will
allow for searches like `Event.find(:name => "some value")`.
-Note that the `assert_unique` validation and the methods `find` and `except` need a
-corresponding index in order to work.
+Note that the {Ohm::Model::Validations#assert_unique assert_unique}
+validation and the methods {Ohm::Model::Set#find find} and
+{Ohm::Model::Set#except except} need a corresponding index in order to work.
### Finding records
You can find a collection of records with the `find` method:
@@ -370,10 +538,24 @@
Ohm is rather small and can be extended in many ways.
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.
+Tutorials
+=========
+
+Check the examples to get a feeling of the design patterns for Redis.
+
+1. [Activity Feed](examples/activity-feed.html)
+2. [Chaining finds](examples/chaining.html)
+3. [Serialization to JSON](examples/json-hash.html)
+4. [One to many associations](examples/one-to-many.html)
+5. [Philosophy behind Ohm](examples/philosophy.html)
+6. [Learning Ohm internals](examples/redis-logging.html)
+7. [Slugs and permalinks](examples/slug.html)
+8. [Tagging](examples/tagging.html)
+
Versions
========
Ohm uses features from Redis > 1.3.10. If you are stuck in previous
versions, please use Ohm 0.0.35 instead.
@@ -394,5 +576,9 @@
Yes, you need to provide the model names. The good part is that you
don't have to load your application environment. Since we assume it's
very likely that you have a bunch of data, the script uses
[Batch](http://github.com/djanowski/batch) to show you some progress
while the process runs.
+
+
+[redis]: http://redis.io
+[ohm]: http://github.com/soveran/ohm