Module: RelaxDB

Defined in:
lib/relaxdb.rb,
lib/more/grapher.rb,
lib/relaxdb/query.rb,
lib/relaxdb/views.rb,
lib/relaxdb/server.rb,
lib/relaxdb/relaxdb.rb,
lib/relaxdb/document.rb,
lib/relaxdb/migration.rb,
lib/relaxdb/paginator.rb,
lib/relaxdb/design_doc.rb,
lib/relaxdb/validators.rb,
lib/relaxdb/view_object.rb,
lib/relaxdb/view_result.rb,
lib/relaxdb/all_delegator.rb,
lib/relaxdb/view_uploader.rb,
lib/relaxdb/uuid_generator.rb,
lib/relaxdb/net_http_server.rb,
lib/relaxdb/paginate_params.rb,
lib/relaxdb/references_proxy.rb,
lib/relaxdb/taf2_curb_server.rb,
lib/relaxdb/view_by_delegator.rb

Defined Under Namespace

Modules: Validators Classes: AllDelegator, CouchDB, DesignDocument, Document, DocumentNotSaved, GraphCreator, HTTP_404, HTTP_409, HTTP_412, Migration, MigrationVersion, NotFound, PaginateParams, Paginator, Query, ReferencesProxy, Server, UpdateConflict, UuidGenerator, ValidationFailure, View, ViewByDelegator, ViewCreator, ViewObject, ViewResult, ViewUploader

Constant Summary collapse

@@db =
nil

Class Method Summary collapse

Class Method Details

.bulk_save(*objs) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/relaxdb/relaxdb.rb', line 103

def bulk_save(*objs)
  begin
    bulk_save!(*objs)
  rescue ValidationFailure, UpdateConflict
    false
  end
end

.bulk_save!(*objs) ⇒ Object

Raises:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/relaxdb/relaxdb.rb', line 70

def bulk_save!(*objs)
  if objs[0].equal? :all_or_nothing
    objs.shift
    all_or_nothing = true
  end
  
  pre_save_success = objs.inject(true) { |s, o| s &= o.pre_save }
  raise ValidationFailure, objs.inspect unless pre_save_success
  
  docs = {}
  objs.each { |o| docs[o._id] = o }
  
  data = { "docs" => objs }
  data[:all_or_nothing] = true if all_or_nothing
  resp = db.post("_bulk_docs", data.to_json )
  data = JSON.parse(resp.body)
  
  conflicted = []
  data.each do |new_rev|
    obj = docs[ new_rev["id"] ]
    if new_rev["rev"]
      obj._rev = new_rev["rev"]
      obj.post_save
    else
      conflicted << obj._id
      obj.conflicted
    end
  end
  
  raise UpdateConflict, conflicted.inspect unless conflicted.empty?
  objs
end

.configure(config) ⇒ Object



12
13
14
15
16
17
# File 'lib/relaxdb/relaxdb.rb', line 12

def configure(config)
  @@db = CouchDB.new config
  
  raise "A design_doc must be provided" unless config[:design_doc]
  @dd = config[:design_doc]
end

.create_from_hash(data) ⇒ Object



291
292
293
# File 'lib/relaxdb/relaxdb.rb', line 291

def create_from_hash(data)
  data["rows"].map { |row| create_object(row["value"]) }
end

.create_obj_from_doc(data) ⇒ Object



306
307
308
309
310
# File 'lib/relaxdb/relaxdb.rb', line 306

def create_obj_from_doc(data)
  klass = data["relaxdb_class"]
  k = klass.split("::").inject(Object) { |x, y| x.const_get y }
  k.new data
end

.create_object(data) ⇒ Object



295
296
297
298
299
300
301
302
303
304
# File 'lib/relaxdb/relaxdb.rb', line 295

def create_object(data)
  klass = data.is_a?(Hash) && data["relaxdb_class"]
  if klass
    k = klass.split("::").inject(Object) { |x, y| x.const_get y }
    k.new data
  else 
    # data is a scalar or not of a known class
    ViewObject.create data
  end
end

.create_views?Boolean

Set in configuration and consulted by view_docs_by, has_many, has_one, references_many and all Views will be added to CouchDB iff this is true

Returns:

  • (Boolean)


32
33
34
# File 'lib/relaxdb/relaxdb.rb', line 32

def create_views?
  @create_views
end

.dbObject



36
37
38
# File 'lib/relaxdb/relaxdb.rb', line 36

def db
  @@db
end

.db_exists?(name) ⇒ Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/relaxdb/relaxdb.rb', line 49

def db_exists?(name)
  db.db_exists? name
end

.db_infoObject



53
54
55
56
# File 'lib/relaxdb/relaxdb.rb', line 53

def db_info
  data = JSON.parse db.get.body
  create_object data
end

.ddObject

This is a temporary method that helps the transition as RelaxDB moves to a single design doc per application.



21
22
23
# File 'lib/relaxdb/relaxdb.rb', line 21

def dd
  @dd
end

.delete_db(name) ⇒ Object



58
59
60
# File 'lib/relaxdb/relaxdb.rb', line 58

def delete_db(name)
  db.delete_db name
end

.doc_ids(view_name, params) ⇒ Object



162
163
164
165
166
# File 'lib/relaxdb/relaxdb.rb', line 162

def doc_ids view_name, params
  params[:raw] = true
  result = view view_name, params
  ids_from_view result
end

.docs(view_name, params) ⇒ Object

Queries a view that doesn’t emit the underlying doc as a val.



155
156
157
158
159
160
# File 'lib/relaxdb/relaxdb.rb', line 155

def docs view_name, params
  params[:raw] = true
  params[:include_docs] = true
  hash = view view_name, params
  hash["rows"].map { |row| row["doc"] ? create_obj_from_doc(row["doc"]) : nil }
end

.enable_view_creation(default = true) ⇒ Object



25
26
27
28
# File 'lib/relaxdb/relaxdb.rb', line 25

def enable_view_creation default=true
  View.reset
  @create_views = default
end

.get(uri = nil) ⇒ Object

Convenience methods - should be in a diffent module?



314
315
316
# File 'lib/relaxdb/relaxdb.rb', line 314

def get(uri=nil)
  JSON.parse(db.get(uri).body)
end

.ids_from_view(raw_result) ⇒ Object



168
169
170
# File 'lib/relaxdb/relaxdb.rb', line 168

def ids_from_view raw_result
  raw_result["rows"].map { |h| h["id"] }
end

.list_dbsObject



62
63
64
# File 'lib/relaxdb/relaxdb.rb', line 62

def list_dbs
  db.list_dbs
end

.load(ids, atts = {}) ⇒ Object

Examples:

RelaxDB.load "foo", :conflicts => true
RelaxDB.load "foo", :revs => true
RelaxDB.load ["foo", "bar"]


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/relaxdb/relaxdb.rb', line 121

def load(ids, atts={})
  # RelaxDB.logger.debug(caller.inject("#{db.name}/#{ids}\n") { |a, i| a += "#{i}\n" })
  
  if ids.is_a? Array
    return [] if ids.empty?
    
    resp = db.post("_all_docs?include_docs=true", {:keys => ids}.to_json)
    data = JSON.parse(resp.body)
    data["rows"].map { |row| row["doc"] ? create_obj_from_doc(row["doc"]) : nil }
  else
    begin
      qs = atts.map{ |k, v| "#{k}=#{v}" }.join("&")
      qs = atts.empty? ? ids : "#{ids}?#{qs}"
      resp = db.get qs
      data = JSON.parse resp.body
      create_obj_from_doc data
    rescue HTTP_404
      nil
    end
  end
end

.load!(ids) ⇒ Object

Raises:



143
144
145
146
147
148
149
150
# File 'lib/relaxdb/relaxdb.rb', line 143

def load!(ids)
  res = load(ids)
  
  raise NotFound, ids if res == nil
  raise NotFound, ids if res.respond_to?(:include?) && res.include?(nil)
  
  res
end

.loggerObject



40
41
42
# File 'lib/relaxdb/relaxdb.rb', line 40

def logger
  @@db.logger
end

.merge(data, merge_key) ⇒ Object

Should be invoked on the result of a join view Merges all rows based on merge_key and returns an array of ViewOject



202
203
204
205
206
207
208
209
210
211
# File 'lib/relaxdb/relaxdb.rb', line 202

def merge(data, merge_key)
  merged = {}
  data["rows"].each do |row|
    value = row["value"]
    merged[value[merge_key]] ||= {}
    merged[value[merge_key]].merge!(value)
  end
  
  merged.values.map { |v| ViewObject.create(v) }
end

.paginate_view(view_name, atts) ⇒ Object



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/relaxdb/relaxdb.rb', line 219

def paginate_view(view_name, atts)      
  page_params = atts.delete :page_params
  view_keys = atts.delete :attributes      
  qpaginate = atts.delete :qpaginate
  
  paginate_params = PaginateParams.new atts
  raise paginate_params.error_msg if paginate_params.invalid? 
  
  paginator = Paginator.new(paginate_params, page_params)

  atts[:reduce] = false
  query = Query.new(view_name, atts)
  query.merge(paginate_params)
  
  docs = ViewResult.new(JSON.parse(db.get(query.view_path).body))
  docs.reverse! if paginate_params.order_inverted?
  
  if qpaginate
    paginator.add_q_next_and_prev docs, view_name, view_keys
  else
    paginator.add_next_and_prev(docs, view_name, view_keys)
  end
  
  docs
end

.pp_get(uri = nil) ⇒ Object



318
319
320
321
# File 'lib/relaxdb/relaxdb.rb', line 318

def pp_get(uri=nil)
  resp = db.get(uri)
  pp(JSON.parse(resp.body))
end

.pp_post(uri = nil, json = nil) ⇒ Object



323
324
325
326
# File 'lib/relaxdb/relaxdb.rb', line 323

def pp_post(uri=nil, json=nil)
  resp = db.post(uri, json)
  pp(JSON.parse(resp.body))
end

.qpaginate_docs(view_name, atts) ⇒ Object

Paginates over views that don’t emit the underlying doc as a val using the same idiom as qpaginate_view



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/relaxdb/relaxdb.rb', line 269

def qpaginate_docs view_name, atts
  page_params = atts.delete :page_params
  view_keys = atts.delete :attributes      
  
  paginate_params = PaginateParams.new atts
  raise paginate_params.error_msg if paginate_params.invalid?       
  paginator = Paginator.new(paginate_params, page_params)

  atts[:raw] = true
  query = Query.new(view_name, atts)
  query.merge(paginate_params)
  
  result = JSON.parse(db.get(query.view_path).body)
  doc_ids = ids_from_view result
  doc_ids.reverse! if paginate_params.order_inverted?
  docs = RelaxDB.load! doc_ids
  
  paginator.add_q_next_and_prev docs, view_name, view_keys

  docs      
end

.qpaginate_view(view_name, atts) ⇒ Object

The paginate_view method only populates next and prev links if they exist. However, its implementation uses three queries to determine if the links should exist.

It may be possible to determine if the links should exist with just a single query (by using a 1 doc offset on either side of the docs to be displayed).

This method makes a small change to paginate_view, dispensing with knowledge of when to create prev and next links but only requiring a single query.

Indiscrimate display of prev links makes this method more suited to forward navigation only.



260
261
262
263
# File 'lib/relaxdb/relaxdb.rb', line 260

def qpaginate_view view_name, atts
  atts[:qpaginate] = true
  paginate_view view_name, atts
end

.reduce_result(data) ⇒ Object



213
214
215
216
217
# File 'lib/relaxdb/relaxdb.rb', line 213

def reduce_result(data)
  res = create_from_hash data
  res.size == 0 ? nil :
    res.size == 1 ? res[0] : res
end

.reload(obj) ⇒ Object



111
112
113
# File 'lib/relaxdb/relaxdb.rb', line 111

def reload(obj)
  load(obj._id)
end

.replicate_db(source, target) ⇒ Object



66
67
68
# File 'lib/relaxdb/relaxdb.rb', line 66

def replicate_db(source, target)
  db.replicate_db source, target
end

.rf_view(view_name, params) ⇒ Object

CouchDB defaults reduce to true when a reduce func is present. RelaxDB used to indiscriminately set reduce=false, allowing clients to override if desired. However, as of CouchDB 0.10, such behaviour results in

{"error":"query_parse_error","reason":"Invalid URL parameter `reduce` for map view."}

View issues.apache.org/jira/browse/COUCHDB-383#action_12722350

This method is an internal workaround for this change to CouchDB and may be removed if a future change allows for a better solution e.g. map=true or a _map endpoint



183
184
185
186
# File 'lib/relaxdb/relaxdb.rb', line 183

def rf_view view_name, params
  params[:reduce] = false
  view view_name, params
end

.use_db(name) ⇒ Object

Creates the named database if it doesn’t already exist



45
46
47
# File 'lib/relaxdb/relaxdb.rb', line 45

def use_db(name)
  db.use_db name
end

.view(view_name, params = {}) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
# File 'lib/relaxdb/relaxdb.rb', line 188

def view(view_name, params = {})
  q = Query.new(view_name, params)
  
  resp = q.keys ? db.post(q.view_path, q.keys) : db.get(q.view_path)
  hash = JSON.parse(resp.body)
  
  if q.raw then hash
  elsif q.reduce then reduce_result hash
  else ViewResult.new hash
  end      
end