Module: CouchSurfer::Model::ClassMethods
- Defined in:
- lib/couch_surfer/model.rb
Instance Method Summary collapse
-
#all(opts = {}, &block) ⇒ Object
Load all documents that have the “couchrest-type” field equal to the name of the current class.
- #all_design_doc_versions ⇒ Object
-
#cast(field, opts = {}) ⇒ Object
Cast a field as another class.
-
#cleanup_design_docs! ⇒ Object
Deletes any non-current design docs that were created by this class.
- #create(attrs = {}) ⇒ Object
-
#database ⇒ Object
returns the CouchRest::Database instance that this class uses.
- #default ⇒ Object
-
#first(opts = {}) ⇒ Object
Load the first document that have the “couchrest-type” field equal to the name of the current class.
-
#get(id) ⇒ Object
Load a document from the database by id.
-
#has_view?(view) ⇒ Boolean
returns stored defaults if the there is a view named this in the design doc.
-
#key_accessor(*keys) ⇒ Object
Defines methods for reading and writing from fields in the document.
-
#key_reader(*keys) ⇒ Object
For each argument key, define a method
key
that reads the corresponding field on the CouchDB document. -
#key_writer(*keys) ⇒ Object
For each argument key, define a method
key=
that sets the corresponding field on the CouchDB document. - #list(*args) ⇒ Object
- #lists(lists_hash) ⇒ Object
- #method_missing(m, *args) ⇒ Object
- #set_default(hash) ⇒ Object
-
#timestamps! ⇒ Object
Automatically set
updated_at
andcreated_at
fields on the document whenever saving occurs. -
#unique_id(method = nil, &block) ⇒ Object
Name a method that will be called before the document is first saved, which returns a string to be used for the document’s
_id
. -
#use_database(db) ⇒ Object
override the CouchSurfer::Model-wide default_database.
-
#view(name, query = {}, &block) ⇒ Object
Dispatches to any named view.
-
#view_by(*keys) ⇒ Object
Define a CouchDB view.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(m, *args) ⇒ Object
260 261 262 263 264 265 266 267 |
# File 'lib/couch_surfer/model.rb', line 260 def method_missing m, *args if has_view?(m) query = args.shift || {} view(m, query, *args) else CouchRest::Document.send(:method_missing, m, *args) end end |
Instance Method Details
#all(opts = {}, &block) ⇒ Object
Load all documents that have the “couchrest-type” field equal to the name of the current class. Take the standard set of CouchRest::Database#view options.
47 48 49 50 51 52 53 |
# File 'lib/couch_surfer/model.rb', line 47 def all opts = {}, &block self.design_doc ||= CouchRest::Design.new(default_design_doc) unless design_doc_fresh refresh_design_doc end view :all, opts, &block end |
#all_design_doc_versions ⇒ Object
285 286 287 288 |
# File 'lib/couch_surfer/model.rb', line 285 def all_design_doc_versions database.documents :startkey => "_design/#{self.to_s}-", :endkey => "_design/#{self.to_s}-\u9999" end |
#cast(field, opts = {}) ⇒ Object
Cast a field as another class. The class must be happy to have the field’s primitive type as the argument to it’s constuctur. Classes which inherit from CouchRest::Model are happy to act as sub-objects for any fields that are stored in JSON as object (and therefore are parsed from the JSON as Ruby Hashes).
Example:
class Post < CouchRest::Model
key_accessor :title, :body, :author
cast :author, :as => 'Author'
end
post..class #=> Author
Using the same example, if a Post should have many Comments, we would declare it like this:
class Post < CouchRest::Model
key_accessor :title, :body, :author, comments
cast :author, :as => 'Author'
cast :comments, :as => ['Comment']
end
post..class #=> Author
post.comments.class #=> Array
post.comments.first #=> Comment
105 106 107 108 |
# File 'lib/couch_surfer/model.rb', line 105 def cast field, opts = {} self.casts ||= {} self.casts[field.to_s] = opts end |
#cleanup_design_docs! ⇒ Object
Deletes any non-current design docs that were created by this class. Running this when you’re deployed version of your application is steadily and consistently using the latest code, is the way to clear out old design docs. Running it too early could mean that live code has to regenerate potentially large indexes.
295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/couch_surfer/model.rb', line 295 def cleanup_design_docs! ddocs = all_design_doc_versions ddocs["rows"].each do |row| if (row['id'] != design_doc_id) database.delete_doc({ "_id" => row['id'], "_rev" => row['value']['rev'] }) end end end |
#create(attrs = {}) ⇒ Object
38 39 40 41 42 |
# File 'lib/couch_surfer/model.rb', line 38 def create(attrs = {}) instance = new(attrs) instance.save instance end |
#database ⇒ Object
returns the CouchRest::Database instance that this class uses
25 26 27 |
# File 'lib/couch_surfer/model.rb', line 25 def database self.class_database || CouchSurfer::Model.default_database end |
#default ⇒ Object
143 144 145 |
# File 'lib/couch_surfer/model.rb', line 143 def default self.default_obj end |
#first(opts = {}) ⇒ Object
Load the first document that have the “couchrest-type” field equal to the name of the current class.
Returns
- Object
-
The first object instance available
or
- Nil
-
if no instances available
Parameters
- opts<Hash>
-
View options, see
CouchRest::Database#view
options for more info.
66 67 68 69 |
# File 'lib/couch_surfer/model.rb', line 66 def first opts = {} first_instance = self.all(opts.merge!(:limit => 1)) first_instance.empty? ? nil : first_instance.first end |
#get(id) ⇒ Object
Load a document from the database by id
30 31 32 33 34 35 36 |
# File 'lib/couch_surfer/model.rb', line 30 def get id doc = database.get id new(doc) rescue RestClient::ResourceNotFound raise CouchSurfer::RecordNotFound end |
#has_view?(view) ⇒ Boolean
returns stored defaults if the there is a view named this in the design doc
270 271 272 273 |
# File 'lib/couch_surfer/model.rb', line 270 def has_view?(view) view = view.to_s design_doc && design_doc['views'] && design_doc['views'][view] end |
#key_accessor(*keys) ⇒ Object
Defines methods for reading and writing from fields in the document. Uses key_writer and key_reader internally.
112 113 114 115 |
# File 'lib/couch_surfer/model.rb', line 112 def key_accessor *keys key_writer *keys key_reader *keys end |
#key_reader(*keys) ⇒ Object
For each argument key, define a method key
that reads the corresponding field on the CouchDB document.
130 131 132 133 134 135 136 137 |
# File 'lib/couch_surfer/model.rb', line 130 def key_reader *keys keys.each do |method| key = method.to_s define_method method do @attributes[method] end end end |
#key_writer(*keys) ⇒ Object
For each argument key, define a method key=
that sets the corresponding field on the CouchDB document.
119 120 121 122 123 124 125 126 |
# File 'lib/couch_surfer/model.rb', line 119 def key_writer *keys keys.each do |method| key = method.to_s define_method "#{method}=" do |value| @attributes[method] = value# ||= nil end end end |
#list(*args) ⇒ Object
311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/couch_surfer/model.rb', line 311 def list(*args) unless design_doc_fresh refresh_design_doc end list_name = args.first view_name = args.last[:using] = args.last[:options] response = design_doc.list(list_name, view_name, ) if response['rows'] response['rows'].collect{|r|new(r['doc'])} else response end end |
#lists(lists_hash) ⇒ Object
307 308 309 |
# File 'lib/couch_surfer/model.rb', line 307 def lists(lists_hash) design_doc['lists'] = lists_hash end |
#set_default(hash) ⇒ Object
139 140 141 |
# File 'lib/couch_surfer/model.rb', line 139 def set_default hash self.default_obj = hash end |
#timestamps! ⇒ Object
Automatically set updated_at
and created_at
fields on the document whenever saving occurs. Save in a format parseable by Time and including milliseconds
151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/couch_surfer/model.rb', line 151 def %w(updated_at created_at).each do |method| define_method method do Time.parse(@attributes[method]) end end before(:save) do self['updated_at'] = Time.now self['created_at'] = self['updated_at'] if new_document? end end |
#unique_id(method = nil, &block) ⇒ Object
Name a method that will be called before the document is first saved, which returns a string to be used for the document’s _id
. Because CouchDB enforces a constraint that each id must be unique, this can be used to enforce eg: uniq usernames. Note that this id must be globally unique across all document types which share a database, so if you’d like to scope uniqueness to this class, you should use the class name as part of the unique id.
170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/couch_surfer/model.rb', line 170 def unique_id method = nil, &block if method define_method :set_unique_id do self['_id'] ||= self.send(method) end elsif block define_method :set_unique_id do uniqid = block.call(self) raise ArgumentError, "unique_id block must not return nil" if uniqid.nil? self['_id'] ||= uniqid end end end |
#use_database(db) ⇒ Object
override the CouchSurfer::Model-wide default_database
20 21 22 |
# File 'lib/couch_surfer/model.rb', line 20 def use_database db self.class_database = db end |
#view(name, query = {}, &block) ⇒ Object
Dispatches to any named view.
276 277 278 279 280 281 282 283 |
# File 'lib/couch_surfer/model.rb', line 276 def view name, query={}, &block unless design_doc_fresh refresh_design_doc end query[:raw] = true if query[:reduce] raw = query.delete(:raw) fetch_view_with_docs(name, query, raw, &block) end |
#view_by(*keys) ⇒ Object
Define a CouchDB view. The name of the view will be the concatenation of by
and the keys joined by and
Example views:
class Post
# view with default options
# query with Post.by_date
view_by :date, :descending => true
# view with compound sort-keys
# query with Post.by_user_id_and_date
view_by :user_id, :date
# view with custom map/reduce functions
# query with Post.by_tags :reduce => true
view_by :tags,
:map =>
"function(doc) {
if (doc['couchrest-type'] == 'Post' && doc.tags) {
doc.tags.forEach(function(tag){
emit(doc.tag, 1);
});
}
}",
:reduce =>
"function(keys, values, rereduce) {
return sum(values);
}"
end
view_by :date
will create a view defined by this Javascript function:
function(doc) {
if (doc['couchrest-type'] == 'Post' && doc.date) {
emit(doc.date, null);
}
}
It can be queried by calling Post.by_date
which accepts all valid options for CouchRest::Database#view. In addition, calling with the :raw => true
option will return the view rows themselves. By default Post.by_date
will return the documents included in the generated view.
CouchRest::Database#view options can be applied at view definition time as defaults, and they will be curried and used at view query time. Or they can be overridden at query time.
Custom views can be queried with :reduce => true
to return reduce results. The default for custom views is to query with :reduce => false
.
Views are generated (on a per-model basis) lazily on first-access. This means that if you are deploying changes to a view, the views for that model won’t be available until generation is complete. This can take some time with large databases. Strategies are in the works.
To understand the capabilities of this view system more compeletly, it is recommended that you read the RSpec file at spec/core/model_spec.rb
.
246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/couch_surfer/model.rb', line 246 def view_by *keys self.design_doc ||= CouchRest::Design.new(default_design_doc) opts = keys.pop if keys.last.is_a?(Hash) opts ||= {} ducktype = opts.delete(:ducktype) unless ducktype || opts[:map] opts[:guards] ||= [] opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')" end keys.push opts self.design_doc.view_by(*keys) self.design_doc_fresh = false end |