Class: CouchPillow::Document

Inherits:
Object
  • Object
show all
Extended by:
AttributeDirective, MigrateDirective, MultiDBDirective, RenameDirective, TTLDirective, TypeDirective, TypePrefixDirective
Defined in:
lib/couchpillow/document.rb

Constant Summary collapse

EVENTS =
[ :cas_conflict ].freeze
CAS_CONFLICT_RETRY_COUNT =
5

Constants included from RenameDirective

RenameDirective::RESERVED_KEYS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TypeDirective

_doc_type, _sanitize_id, type

Methods included from TypePrefixDirective

_is_type_prefixed?, type_prefix

Methods included from AttributeDirective

attribute, attributes

Methods included from MultiDBDirective

_default_db, _secondary_dbs, _threads, _write_to_secondary_dbs, db, wait

Methods included from RenameDirective

rename, rename_keys

Methods included from MigrateDirective

migrate, migrate_keys

Methods included from TTLDirective

has_ttl?, ttl, ttl_value

Constructor Details

#initialize(hash = {}, id = SecureRandom.hex, cas = nil) ⇒ Document

Constructor.

Parameters:

  • hash (defaults to: {})

    The document

  • id (defaults to: SecureRandom.hex)

    The id of the document

  • cas (defaults to: nil)

    CAS value of the document, from the CB client. Optional.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/couchpillow/document.rb', line 44

def initialize hash = {},
               id = SecureRandom.hex,
               cas = nil

  @data = CouchPillow.symbolize(hash)
  @original = Marshal.load(Marshal.dump(@data))

  @id = self.class._is_type_prefixed? ? self.class._sanitize_id(id) : id

  time = CouchPillow::Iso8601Time.now.utc
  @data[:_created_at] ||= time
  @data[:_updated_at] ||= time

  @cas = cas

  rename!
  whitelist!
  assign_defaults!
  migrate!
  auto_convert!
end

Instance Attribute Details

#idObject

Returns the value of attribute id.



13
14
15
# File 'lib/couchpillow/document.rb', line 13

def id
  @id
end

Class Method Details

.get(*ids) ⇒ Object

Get a Document given an id or multiple ids.

If multiple ids are passed, returns an array.

Returns:

  • nil if not found or Document is of a different type.



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/couchpillow/document.rb', line 262

def self.get *ids
  results = []
  if ids.length == 1
    id = _sanitize_id(ids.first)
    tid = _is_type_prefixed? ? "#{_doc_type}::#{id}" : id
    result, _, cas = _default_db.get(tid, extended: true)

    results << create(result, id, cas)

  elsif ids.length > 1
    # Sanitize ids first
    sanitized_ids = ids.map do |id|
      id = _sanitize_id(id)
      _is_type_prefixed? ? "#{_doc_type}::#{id}" : id
    end

    # Query the db
    db_result = _default_db.get(*sanitized_ids, extended: true)
    results = sanitized_ids.map do |k|
      result, _, cas = db_result[k]
      create(result, k, cas)
    end
  end

  results.size == 1 ? results.first : results
end

.inherited(subclass) ⇒ Object

Inherit directives



299
300
301
302
303
304
305
# File 'lib/couchpillow/document.rb', line 299

def self.inherited subclass
  attributes.each do |k, v|
    subclass.attributes[k] = v
  end
  subclass.type_prefix(_is_type_prefixed?)
  subclass.type(_doc_type)
end

.on(event, &block) ⇒ Object

Registers a listener on a specific event. See EVENTS constant for a list of accepted events.



293
294
295
# File 'lib/couchpillow/document.rb', line 293

def self.on event, &block
  event_listeners[event] = block if EVENTS.include?(event)
end

Instance Method Details

#[](key) ⇒ Object



67
68
69
# File 'lib/couchpillow/document.rb', line 67

def [] key
  @data[key.to_s.to_sym]
end

#[]=(key, value) ⇒ Object



72
73
74
# File 'lib/couchpillow/document.rb', line 72

def []= key, value
  @data[key.to_s.to_sym] = value
end

#assign_defaults!Object

Assign default values.



219
220
221
222
223
# File 'lib/couchpillow/document.rb', line 219

def assign_defaults!
  self.class.attributes.each do |k, attr|
    @data[k] = attr.trigger_default_directive if !has?(k) && attr.has_default?
  end
end

#auto_convert!Object

Auto convert.



228
229
230
231
232
# File 'lib/couchpillow/document.rb', line 228

def auto_convert!
  self.class.attributes.each do |k, attr|
    @data[k] = attr.trigger_auto_convert_directive(@data[k]) if has?(k)
  end
end

#delete!Object

Delete this document from the server.



105
106
107
108
109
110
111
112
113
114
# File 'lib/couchpillow/document.rb', line 105

def delete!
  result = self.class._default_db.delete(db_id)

  # write to the secondary only if the primary succeeds
  self.class._write_to_secondary_dbs do |db|
    db.delete(db_id)
  end if result

  result
end

#doc_typeObject

Helper to get the type of this Document. Can’t really name this ‘type`. Need to avoid name conflict with Ruby’s own ‘type` method.



192
193
194
# File 'lib/couchpillow/document.rb', line 192

def doc_type
  self.class._doc_type
end

#has?(key) ⇒ Boolean

Check if this Document has the key

Returns:



170
171
172
# File 'lib/couchpillow/document.rb', line 170

def has? key
  @data.has_key?(key)
end

#migrate!Object

Run the migration directive



145
146
147
148
149
150
151
# File 'lib/couchpillow/document.rb', line 145

def migrate!
  self.class.migrate_keys.each do |k, pr|
    if @data.has_key? k
      @data[k] = pr.call @data[k]
    end
  end
end

#rename!Object

Rename the keys in this Document as specified by the RenameDirective#rename directive.



199
200
201
202
203
204
205
# File 'lib/couchpillow/document.rb', line 199

def rename!
  self.class.rename_keys.each do |from, to|
    @data.has_key?(from) and
      @data[to] = @data[from] and
      @data.delete(from)
  end
end

#save!(opts = {}) ⇒ Object

Save this document to the server



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/couchpillow/document.rb', line 79

def save! opts = {}
  result = nil

  # write to the primary db first
  result = _cas_handler do
    whitelist!
    sort!
    _timestamp!
    validate!
    opts[:cas] = @cas
    opts[:ttl] ||= self.class.ttl_value
    self.class._default_db.set(db_id, _to_save, opts)
  end

  # write to the secondary only if the primary succeeds
  # and ignore CAS for secondary DBs.
  self.class._write_to_secondary_dbs do |db|
    db.set(db_id, _to_save)
  end if result

  result
end

#sort!Object

Sort keys on this document.



252
253
254
# File 'lib/couchpillow/document.rb', line 252

def sort!
  @data = @data.sort.to_h
end

#to_hashObject

Convert this Document to a Hash



184
185
186
# File 'lib/couchpillow/document.rb', line 184

def to_hash
  { :_id => @id, :_type => doc_type }.merge!(@data)
end

#to_json(*a) ⇒ Object

Convert this Document to a JSON string



177
178
179
# File 'lib/couchpillow/document.rb', line 177

def to_json *a
  to_hash.to_json(*a)
end

#update(hash) ⇒ Object

Updates the attributes in the document. Existing attributes will be overwritten and new ones will be added. Any other existing attributes that are not present in the hash will be ignored.



158
159
160
161
162
163
164
165
# File 'lib/couchpillow/document.rb', line 158

def update hash
  hash.each do |k,v|
    @data[k.to_sym] = v
  end
  rename!
  whitelist!
  auto_convert!
end

#update!(opts = {}) ⇒ Object

Attempt to update this Document. Fails if this Document does not yet exist in the database.



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

def update! opts = {}

  # write to the primary db first
  result = _cas_handler do
    whitelist!
    sort!
    _timestamp!
    validate!
    opts[:cas] = @cas
    result = self.class._default_db.replace(db_id, _to_save, opts)
  end

  # write to the secondary only if the primary succeeds
  # and ignore CAS for secondary DBs.
  opts.delete :cas
  self.class._write_to_secondary_dbs do |db|
    db.replace(db_id, _to_save)
  end if result

  result
end

#validate!Object

Go through each attribute, and validate the values. Validation also perform auto-conversion if auto-conversion is enabled for that attribute.



238
239
240
241
242
243
244
245
246
247
# File 'lib/couchpillow/document.rb', line 238

def validate!
  self.class.attributes.each do |k, attr|
    if has?(k)
      @data[k] = attr.validate(@data[k])
    else
      @data[k] = attr.trigger_default_directive if attr.has_default?
      raise ValidationError, "Attribute '#{k}' is required" if attr.required? && !has?(k)
    end
  end
end

#whitelist!Object

Cleanup the @data hash so it only contains relevant fields.



210
211
212
213
214
# File 'lib/couchpillow/document.rb', line 210

def whitelist!
  @data.delete_if do |k, v|
    !self.class.attributes.has_key?(k)
  end
end