Module: NoBrainer::Document::AtomicOps

Extended by:
ActiveSupport::Concern
Defined in:
lib/no_brainer/document/atomic_ops.rb

Defined Under Namespace

Modules: ClassMethods Classes: PendingAtomic, PendingAtomicArray, PendingAtomicContainer, PendingAtomicSet, PendingAtomicUnset

Instance Method Summary collapse

Instance Method Details

#_is_attribute_touched?(name) ⇒ Boolean

Returns:



158
159
160
# File 'lib/no_brainer/document/atomic_ops.rb', line 158

def _is_attribute_touched?(name)
  @_touched_attributes.include?(name.to_s)
end

#_read_attribute(name) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/no_brainer/document/atomic_ops.rb', line 186

def _read_attribute(name)
  return super if name == self.class.pk_name

  ensure_exclusive_atomic!
  return super unless in_atomic?

  # If we are missing fields, it's okay, we'll assume nil.
  value = missing_field?(name) ? nil : super

  case value
  when PendingAtomicContainer then value
  when PendingAtomicUnset     then raise "Attribute `#{name}' is unset"
  when PendingAtomic          then value.dup
  else PendingAtomic._new(self, name, value, _is_attribute_touched?(name))
  end
end

#_touch_attribute(name) ⇒ Object



151
152
153
154
155
156
# File 'lib/no_brainer/document/atomic_ops.rb', line 151

def _touch_attribute(name)
  # The difference with dirty tracking and this is that dirty tracking does
  # not take into account fields that are set with their old value, whereas the
  # touched attribute does.
  @_touched_attributes << name.to_s
end

#_write_attribute(name, value) ⇒ Object



203
204
205
206
207
208
209
210
211
212
# File 'lib/no_brainer/document/atomic_ops.rb', line 203

def _write_attribute(name, value)
  ensure_exclusive_atomic!

  case [in_atomic?, value.is_a?(PendingAtomic)]
  when [true, false]  then raise NoBrainer::Error::AtomicBlock.new('Avoid the use of atomic blocks for non atomic operations')
  when [false, true]  then raise NoBrainer::Error::AtomicBlock.new('Use atomic blocks for atomic operations')
  when [true, true]   then super.tap { _touch_attribute(name) }
  when [false, false] then super.tap { _touch_attribute(name) }
  end
end

#assign_attributes(attrs, options = {}) ⇒ Object



214
215
216
217
# File 'lib/no_brainer/document/atomic_ops.rb', line 214

def assign_attributes(attrs, options={})
  ensure_exclusive_atomic!
  super
end

#clear_dirtiness(options = {}) ⇒ Object



146
147
148
149
# File 'lib/no_brainer/document/atomic_ops.rb', line 146

def clear_dirtiness(options={})
  super
  @_touched_attributes = Set.new
end

#ensure_exclusive_atomic!Object



171
172
173
# File 'lib/no_brainer/document/atomic_ops.rb', line 171

def ensure_exclusive_atomic!
  raise NoBrainer::Error::AtomicBlock.new('You may not access other documents within an atomic block') if in_other_atomic?
end

#in_atomic?Boolean

Returns:



162
163
164
# File 'lib/no_brainer/document/atomic_ops.rb', line 162

def in_atomic?
  !!Thread.current[:nobrainer_atomic]
end

#in_other_atomic?Boolean

Returns:



166
167
168
169
# File 'lib/no_brainer/document/atomic_ops.rb', line 166

def in_other_atomic?
  v = Thread.current[:nobrainer_atomic]
  !v.nil? && !v.equal?(self)
end

#queue_atomic(&block) ⇒ Object



175
176
177
178
179
180
181
182
183
184
# File 'lib/no_brainer/document/atomic_ops.rb', line 175

def queue_atomic(&block)
  ensure_exclusive_atomic!

  begin
    old_atomic, Thread.current[:nobrainer_atomic] = Thread.current[:nobrainer_atomic], self
    block.call(RethinkDB::RQL.new)
  ensure
    Thread.current[:nobrainer_atomic] = old_atomic
  end
end

#reloadObject



237
238
239
240
# File 'lib/no_brainer/document/atomic_ops.rb', line 237

def reload(*)
  raise NoBrainer::Error::AtomicBlock.new('You may not reload fields within an atomic block') if in_atomic?
  super
end

#save?(options = {}) ⇒ Boolean

Returns:

Raises:



224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/no_brainer/document/atomic_ops.rb', line 224

def save?(options={})
  # TODO allow reload => true as an option to save+reload in a single op.
  raise NoBrainer::Error::AtomicBlock.new('You may persist documents only outside of queue_atomic blocks') if in_atomic?
  super.tap do |saved|
    if saved
      @_attributes.each do |attr, value|
        next unless value.is_a?(PendingAtomic)
        @_attributes[attr] = value.class.new(self, attr, nil, false, value.type)
      end
    end
  end
end

#unset(attr) ⇒ Object



219
220
221
222
# File 'lib/no_brainer/document/atomic_ops.rb', line 219

def unset(attr)
  return queue_atomic { unset(attr) } unless in_atomic?
  _write_attribute(attr, PendingAtomicUnset.new(self, attr, nil, true, nil))
end