ohm 0.0.32 → 0.0.33

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 CHANGED
@@ -3,7 +3,6 @@ Ohm ॐ
3
3
 
4
4
  Object-hash mapping library for Redis.
5
5
 
6
-
7
6
  Description
8
7
  -----------
9
8
 
@@ -47,7 +46,6 @@ Now, in an irb session you can test the Redis adapter directly:
47
46
  >> Ohm.redis.get "Foo"
48
47
  => "Bar"
49
48
 
50
-
51
49
  Models
52
50
  ------
53
51
 
@@ -59,8 +57,8 @@ the example below:
59
57
 
60
58
  class Event < Ohm::Model
61
59
  attribute :name
62
- set :participants
63
- list :comments
60
+ reference :venue, Venue
61
+ set :participants, Person
64
62
  counter :votes
65
63
 
66
64
  index :name
@@ -70,9 +68,18 @@ the example below:
70
68
  end
71
69
  end
72
70
 
71
+ class Venue < Ohm::Model
72
+ attribute :name
73
+ collection :events, Event
74
+ end
75
+
76
+ class Person < Ohm::Model
77
+ attribute :name
78
+ end
79
+
73
80
  All models have the `id` attribute built in, you don't need to declare it.
74
81
 
75
- This is how you interact with ids:
82
+ This is how you interact with IDs:
76
83
 
77
84
  event = Event.create :name => "Ohm Worldwide Conference 2031"
78
85
  event.id
@@ -92,13 +99,16 @@ validations. Keep reading to find out what you can do with models.
92
99
  Attribute types
93
100
  ---------------
94
101
 
95
- Ohm::Model provides four attribute types: `attribute`, `set`, `list` and
96
- `counter`.
102
+ Ohm::Model provides four attribute types: {Ohm::Model::attribute
103
+ attribute}, {Ohm::Model::set set}, {Ohm::Model::list list}
104
+ and {Ohm::Model::counter counter}; and two meta types:
105
+ {Ohm::Model::reference reference} and {Ohm::Model::collection
106
+ collection}.
97
107
 
98
108
  ### attribute
99
109
 
100
110
  An `attribute` is just any value that can be stored as a string. In the
101
- example above, we used this field to store the Event's `name`. You can
111
+ example above, we used this field to store the event's `name`. You can
102
112
  use it to store numbers, but be aware that Redis will return a string
103
113
  when you retrieve the value.
104
114
 
@@ -122,6 +132,17 @@ the value, but you can not assign it. In the example above, we used a
122
132
  counter attribute for tracking votes. As the incr and decr operations
123
133
  are atomic, you can rest assured a vote won't be counted twice.
124
134
 
135
+ ### reference
136
+
137
+ It's a special kind of attribute that references another model.
138
+ Internally, Ohm will keep a pointer to the model (its ID), but you get
139
+ accessors that give you real instances. You can think of it as the model
140
+ containing the foreign key to another model.
141
+
142
+ ### collection
143
+
144
+ Provides an accessor to search for all models that `reference` the current model.
145
+
125
146
  Persistence strategy
126
147
  --------------------
127
148
 
@@ -150,54 +171,69 @@ If you are saving the object, this will suffice:
150
171
  event.comments << "Wonderful event!"
151
172
  end
152
173
 
174
+ Working with Sets
175
+ -----------------
153
176
 
154
- Associations
155
- ------------
156
-
157
- Ohm lets you use collections (lists and sets) to represent associations.
158
- For this, you only need to provide a second parameter when declaring a
159
- list or a set:
160
-
161
- set :attendees, Person
162
-
163
- After this, every time you refer to `event.attendees` you will be talking
164
- about instances of the model `Person`. If you want to get the raw values
165
- of the set, you can use `event.attendees.raw`.
177
+ Given the following model declaration:
166
178
 
167
- The `attendees` collection also exposes two sorting methods: `sort`
168
- returns the elements ordered by `id`, and `sort_by` receives a parameter
169
- with an attribute name, which will determine the sorting order. Both
170
- methods receive an options hash which is explained in the documentation
171
- for {Ohm::Attributes::Collection#sort}.
179
+ class Event < Ohm::Model
180
+ attribute :name
181
+ set :attendees, Person
182
+ end
172
183
 
173
- Adding instances of `Person` to the attendees hash is done with the
174
- `add` method:
184
+ You can add instances of `Person` to the set of attendees with the
185
+ `<<` method:
175
186
 
176
- @event.attendees.add(Person.create(name: "Albert"))
187
+ @event.attendees << Person.create(name: "Albert")
177
188
 
178
189
  # And now...
179
190
  @event.attendees.each do |person|
180
191
  # ...do what you want with this person.
181
192
  end
182
193
 
183
- Just to clarify: when you add items to a set with `<<`, Ohm inserts
184
- whatever you send without checking it. When you use `add`, it assumes
185
- it's an instance of some `Ohm::Model` and stores its id.
194
+ Sorting
195
+ -------
196
+
197
+ Since `attendees` is a {Ohm::Model::Set Set}, it exposes two sorting
198
+ methods: {Ohm::Model::Collection#sort sort} returns the elements
199
+ ordered by `id`, and {Ohm::Model::Collection#sort_by sort_by} receives
200
+ a parameter with an attribute name, which will determine the sorting
201
+ order. Both methods receive an options hash which is explained in the
202
+ documentation for {Ohm::Model::Collection#sort sort}.
203
+
204
+ Associations
205
+ ------------
206
+
207
+ Ohm lets you declare `references` and `collections` to represent associations.
208
+
209
+ class Post < Ohm::Model
210
+ attribute :title
211
+ attribute :body
212
+ collection :comments, Comment
213
+ end
214
+
215
+ class Comment < Ohm::Model
216
+ attribute :body
217
+ reference :post, Post
218
+ end
186
219
 
220
+ After this, every time you refer to `post.comments` you will be talking
221
+ about instances of the model `Comment`. If you want to get a list of IDs
222
+ you can use `post.comments.raw`.
187
223
 
188
224
  Indexes
189
225
  -------
190
226
 
191
227
  An index is a set that's handled automatically by Ohm. For any index declared,
192
- Ohm maintains different sets of objects ids for quick lookups.
228
+ Ohm maintains different sets of objects IDs for quick lookups.
193
229
 
194
- For example, in the example above, the index on the name attribute will
195
- allow for searches like Event.find(name: "some value").
230
+ In the `Event` example, the index on the name attribute will
231
+ allow for searches like `Event.find(name: "some value")`.
196
232
 
197
233
  Note that the `assert_unique` validation and the methods `find` and `except` need a
198
234
  corresponding index in order to work.
199
235
 
200
- ### Finding
236
+ ### Finding records
201
237
 
202
238
  You can find a collection of records with the `find` method:
203
239
 
@@ -230,7 +266,6 @@ are valid. Nesting assertions is a good practice, and you are also
230
266
  encouraged to create your own assertions. You can trigger validations at
231
267
  any point by calling `valid?` on a model instance.
232
268
 
233
-
234
269
  Assertions
235
270
  -----------
236
271
 
@@ -0,0 +1,186 @@
1
+ module Ohm
2
+ class Collection
3
+ include Enumerable
4
+
5
+ attr_accessor :key, :db
6
+
7
+ def initialize(key, db = Ohm.redis)
8
+ self.key = key
9
+ self.db = db
10
+ end
11
+
12
+ def each(&block)
13
+ all.each(&block)
14
+ end
15
+
16
+ # Return the values as model instances, ordered by the options supplied.
17
+ # Check redis documentation to see what values you can provide to each option.
18
+ #
19
+ # @param options [Hash] options to sort the collection.
20
+ # @option options [#to_s] :by Model attribute to sort the instances by.
21
+ # @option options [#to_s] :order (ASC) Sorting order, which can be ASC or DESC.
22
+ # @option options [Integer] :limit (all) Number of items to return.
23
+ # @option options [Integer] :start (0) An offset from where the limit will be applied.
24
+ #
25
+ # @example Get the first ten users sorted alphabetically by name:
26
+ #
27
+ # @event.attendees.sort(:by => :name, :order => "ALPHA", :limit => 10)
28
+ #
29
+ # @example Get five posts sorted by number of votes and starting from the number 5 (zero based):
30
+ #
31
+ # @blog.posts.sort(:by => :votes, :start => 5, :limit => 10")
32
+ def sort(options = {})
33
+ return [] if empty?
34
+ options[:start] ||= 0
35
+ options[:limit] = [options[:start], options[:limit]] if options[:limit]
36
+ db.sort(key, options)
37
+ end
38
+
39
+ # Sort the model instances by id and return the first instance
40
+ # found. If a :by option is provided with a valid attribute name, the
41
+ # method sort_by is used instead and the option provided is passed as the
42
+ # first parameter.
43
+ #
44
+ # @see #sort
45
+ # @return [Ohm::Model, nil] Returns the first instance found or nil.
46
+ def first(options = {})
47
+ options = options.merge(:limit => 1)
48
+ sort(options).first
49
+ end
50
+
51
+ def [](index)
52
+ first(:start => index)
53
+ end
54
+
55
+ def to_ary
56
+ all
57
+ end
58
+
59
+ def ==(other)
60
+ to_ary == other
61
+ end
62
+
63
+ # @return [true, false] Returns whether or not the collection is empty.
64
+ def empty?
65
+ size.zero?
66
+ end
67
+
68
+ # Clears the values in the collection.
69
+ def clear
70
+ db.del(key)
71
+ self
72
+ end
73
+
74
+ # Appends the given values to the collection.
75
+ def concat(values)
76
+ values.each { |value| self << value }
77
+ self
78
+ end
79
+
80
+ # Replaces the collection with the passed values.
81
+ def replace(values)
82
+ clear
83
+ concat(values)
84
+ end
85
+ end
86
+
87
+ # Represents a Redis list.
88
+ #
89
+ # @example Use a list attribute.
90
+ #
91
+ # class Event < Ohm::Model
92
+ # attribute :name
93
+ # list :participants
94
+ # end
95
+ #
96
+ # event = Event.create :name => "Redis Meeting"
97
+ # event.participants << "Albert"
98
+ # event.participants << "Benoit"
99
+ # event.participants.all
100
+ # # => ["Albert", "Benoit"]
101
+ class List < Collection
102
+
103
+ # @param value [#to_s] Pushes value to the tail of the list.
104
+ def << value
105
+ db.rpush(key, value)
106
+ end
107
+
108
+ alias push <<
109
+
110
+ # @return [String] Return and remove the last element of the list.
111
+ def pop
112
+ db.rpop(key)
113
+ end
114
+
115
+ # @return [String] Return and remove the first element of the list.
116
+ def shift
117
+ db.lpop(key)
118
+ end
119
+
120
+ # @param value [#to_s] Pushes value to the head of the list.
121
+ def unshift(value)
122
+ db.lpush(key, value)
123
+ end
124
+
125
+ # @return [Array] Elements of the list.
126
+ def all
127
+ db.list(key)
128
+ end
129
+
130
+ # @return [Integer] Returns the number of elements in the list.
131
+ def size
132
+ db.llen(key)
133
+ end
134
+
135
+ def include?(value)
136
+ all.include?(value)
137
+ end
138
+
139
+ def inspect
140
+ "#<List: #{all.inspect}>"
141
+ end
142
+ end
143
+
144
+ # Represents a Redis set.
145
+ #
146
+ # @example Use a set attribute.
147
+ #
148
+ # class Company < Ohm::Model
149
+ # attribute :name
150
+ # set :employees
151
+ # end
152
+ #
153
+ # company = Company.create :name => "Redis Co."
154
+ # company.employees << "Albert"
155
+ # company.employees << "Benoit"
156
+ # company.employees.all #=> ["Albert", "Benoit"]
157
+ # company.employees.include?("Albert") #=> true
158
+ class Set < Collection
159
+
160
+ # @param value [#to_s] Adds value to the list.
161
+ def << value
162
+ db.sadd(key, value)
163
+ end
164
+
165
+ def delete(value)
166
+ db.srem(key, value)
167
+ end
168
+
169
+ def include?(value)
170
+ db.sismember(key, value)
171
+ end
172
+
173
+ def all
174
+ db.smembers(key)
175
+ end
176
+
177
+ # @return [Integer] Returns the number of elements in the set.
178
+ def size
179
+ db.scard(key)
180
+ end
181
+
182
+ def inspect
183
+ "#<Set: #{all.inspect}>"
184
+ end
185
+ end
186
+ end
@@ -13,3 +13,12 @@ unless "".respond_to?(:lines)
13
13
  end
14
14
  end
15
15
  end
16
+
17
+ unless Object.new.respond_to?(:tap)
18
+ class Object
19
+ def tap
20
+ yield(self)
21
+ self
22
+ end
23
+ end
24
+ end
data/lib/ohm/key.rb ADDED
@@ -0,0 +1,46 @@
1
+ module Ohm
2
+
3
+ # Represents a key in Redis.
4
+ class Key
5
+ attr :parts
6
+ attr :glue
7
+ attr :namespace
8
+
9
+ def self.[](*parts)
10
+ Key.new(parts)
11
+ end
12
+
13
+ def initialize(parts, glue = ":", namespace = [])
14
+ @parts = parts
15
+ @glue = glue
16
+ @namespace = namespace
17
+ end
18
+
19
+ def append(*parts)
20
+ @parts += parts
21
+ self
22
+ end
23
+
24
+ def eql?(other)
25
+ to_s == other.to_s
26
+ end
27
+
28
+ alias == eql?
29
+
30
+ def to_s
31
+ (namespace + [@parts.join(glue)]).join(":")
32
+ end
33
+
34
+ alias inspect to_s
35
+ alias to_str to_s
36
+
37
+ def volatile
38
+ @namespace = [:~]
39
+ self
40
+ end
41
+
42
+ def group(glue = self.glue)
43
+ Key.new([self], glue, namespace.slice!(0, namespace.size))
44
+ end
45
+ end
46
+ end
data/lib/ohm/redis.rb CHANGED
@@ -33,7 +33,6 @@ module Ohm
33
33
  :lpush => true,
34
34
  :lrem => true,
35
35
  :lset => true,
36
- :rpoplpush => true,
37
36
  :rpush => true,
38
37
  :sadd => true,
39
38
  :set => true,