Document Store Module

Simple module to use a single shared connection, of course I have made my personal one very robust, but these are easy examples.

require 'couchbase'

module DocumentStore
  C ="http://localhost:8091/")
  C.quiet = true                                # return nil instead of Exceptions for missing keys

Object <> Document + Atomic Counter

Here we separate out the inventory into a simple add-on key as an atomic counter. The main document is still saved using Product#save command which iterates through instance variables.

class Product
  include DocumentStore
  attr_accessor :sku, :name, :price
  # iterate through attr keys and set instance vars
  def initialize(attr = {})
    unless attr.nil?
      attr.each do |name, value|
        setter = "#{name}="
        next unless respond_to?(setter)
        send(setter, value)

  # iterate through instance variables and convert keypairs to hash
  def to_hash
    Hash[ { |name| [name[].to_s, instance_variable_get(name)] } ]
  # save object to Couchbase, with the email as the key, normally we use a common module 
  # for the connection as only a single connection is required for an entire app server
  def save
    C.set(@sku.downcase, to_hash)
    doc = C.get(@sku.downcase + "::inventory_count")          # get current inventory counter
    C.set(@sku.downcase + "::inventory_count", 0) unless doc  # initialize if counter doesn't exist
  # return current inventory 
  def inventory
    return nil unless @sku
    C.get(@sku.downcase + "::inventory_count")    
  # increase the current inventory
  def increase_inventory(q)
    return nil if q <= 0
    C.incr(@sku.downcase + "::inventory_count", q, :initial => 0 + q)
  # decrease the current inventory, but only if the quantity is available, also returns remaining inventory
  def decrease_inventory(q)
    return nil if q <= 0 || inventory == 0 || q > inventory
    C.decr(@sku.downcase + "::inventory_count", q, :initial => 0) 
  class << self
    include DocumentStore
    # find product by sku 
    def find_by_sku(sku)    
      C.quiet = true                # => don't throw exception for missing keys
      doc = C.get(sku.downcase)     # => try to find key
      return nil if doc.nil?        # => return nil unless key exists 
      return       # => return Product object (doc will be a hash)
    # return inventory by sku
    def inventory_by_sku(sku)
      p = Product.find_by_sku(sku)
      return p.inventory if p
  end # end of class method definitions

In this basic class, we separate inventory out into a separate counter so that inventory management is atomic. Of course we would want to probably raise exceptions and/or error codes for situations like running out of inventory, but that's simple enough to add.

Using the Product Class

Here is how this would be used, and the resulting documents and output.

require 'product'

# you could get this from an input form for instance
p1_hash = {
  "sku" => "GI101", 
  "name" => "GI Joe Standard",
  "price" => "$11.99"
p1 =                                           # => { "sku": "GI101", "name": "GI Joe Standard", "price": "$11.99"}

puts p1.inventory                             # => 0
puts p1.increase_inventory(100)               # => 100
puts p1.decrease_inventory(5)                 # => 95
puts p1.decrease_inventory(500)               # => nil

Q&A and Comments


Q&A and Comments

comments powered by Disqus