module Cassie::Model

This module provides a simple interface for models backed by Cassandra tables.

Cassandra is very limited in how data can be accessed efficiently so this code is intentionally not designed as a full fledged DSL with all the nifty features of ActiveRecord. Doing so will only get you into trouble when you run into the limits of Cassandra data structures.

It implements ActiveModel::Model and supports ActiveModel callbacks on :create, :update, :save, and :destroy as well as ActiveModel validations.

Example:

class Thing
  include Cassie::Model

  self.table_name = "things"
  self.keyspace = "test"
  self.primary_key = [:owner, :id]

  column :owner, :int
  column :id, :int, :as => :identifier
  column :val, :varchar, :as => :value

  ordering_key :id, :desc

  validates_presence_of :id, :value

  before_save do
    ...
  end
end

Public Class Methods

find_subscribers() click to toggle source
# File lib/cassie/model.rb, line 65
def find_subscribers
  @@find_subscribers
end
new(attributes = {}) click to toggle source
Calls superclass method
# File lib/cassie/model.rb, line 474
def initialize(attributes = {})
  super
  @persisted = false
end

Public Instance Methods

==(other) click to toggle source
# File lib/cassie/model.rb, line 552
def ==(other)
  eql?(other)
end
attributes() click to toggle source

Returns a hash of column to values. Column names will be symbols.

# File lib/cassie/model.rb, line 535
def attributes
  hash = {}
  self.class.column_names.each do |name|
    hash[name] = send(name)
  end
  hash
end
counter_table?() click to toggle source

Return true if the table is used for a counter.

# File lib/cassie/model.rb, line 485
def counter_table?
  !!self.class._counter_table
end
destroy() click to toggle source

Delete a record and call the destroy callbacks.

# File lib/cassie/model.rb, line 526
def destroy
  run_callbacks(:destroy) do
    self.class.connection.delete(self.class.full_table_name, key_hash, consistency: write_consistency)
    @persisted = false
    true
  end
end
eql?(other) click to toggle source
# File lib/cassie/model.rb, line 548
def eql?(other)
  other.is_a?(self.class) && other.key_hash == key_hash
end
key_hash() click to toggle source

Returns the primary key as a hash

# File lib/cassie/model.rb, line 557
def key_hash
  hash = {}
  self.class.primary_key.each do |key|
    hash[key] = send(key)
  end
  hash
end
persisted?() click to toggle source

Return true if the record has been persisted to Cassandra.

# File lib/cassie/model.rb, line 480
def persisted?
  @persisted
end
persistence_ttl() click to toggle source

Subclasses can override this method to provide a TTL on the persisted record.

# File lib/cassie/model.rb, line 544
def persistence_ttl
  nil
end
save(validate: true, ttl: nil) click to toggle source

Save a record. Returns true if the record was persisted and false if it was invalid. This method will run the save callbacks as well as either the update or create callbacks as necessary.

# File lib/cassie/model.rb, line 492
def save(validate: true, ttl: nil)
  raise ArgumentError.new("Cannot call save on a counter table") if counter_table?
  valid_record = (validate ? valid? : true)
  if valid_record
    run_callbacks(:save) do
      options = {consistency: write_consistency, ttl: (ttl || persistence_ttl)}
      if persisted?
        run_callbacks(:update) do
          self.class.connection.update(self.class.full_table_name, values_hash, key_hash, options)
        end
      else
        run_callbacks(:create) do
          self.class.connection.insert(self.class.full_table_name, attributes, options)
          @persisted = true
        end
      end
    end
    true
  else
    false
  end
end
save!(ttl: nil) click to toggle source

Save a record. Returns true if the record was saved and raises an ActiveRecord::RecordInvalid error if the record is invalid.

# File lib/cassie/model.rb, line 517
def save!(ttl: nil)
  if save(ttl: ttl)
    true
  else
    raise Cassie::RecordInvalid.new(self)
  end
end

Private Instance Methods

adjust_counter!(name, amount, ttl: nil) click to toggle source

Used for updating counter columns.

# File lib/cassie/model.rb, line 568
def adjust_counter!(name, amount, ttl: nil)
  amount = amount.to_i
  if amount != 0
    run_callbacks(:update) do
      adjustment = (amount < 0 ? "#{name} = #{name} - #{amount.abs}" : "#{name} = #{name} + #{amount}")
      options = {consistency: write_consistency, ttl: (ttl || persistence_ttl)}
      self.class.connection.update(self.class.full_table_name, adjustment, key_hash, options)
    end
  end
  record = self.class.find(key_hash)
  value = (record ? record.send(name) : send(name) + amount)
  send("#{name}=", value)
end
values_hash() click to toggle source

Returns a hash of value except for the ones that constitute the primary key

# File lib/cassie/model.rb, line 583
def values_hash
  pk = self.class.primary_key
  hash = {}
  self.class.column_names.each do |name|
    hash[name] = send(name) unless pk.include?(name)
  end
  hash
end