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