dodecaphonic/opal-pouchdb

View on GitHub
opal/pouchdb/database.rb

Summary

Maintainability
A
0 mins
Test Coverage
# coding: utf-8
module PouchDB
  # Creates a Database, either interacting with it locally or remotely. If
  # remotely, the library will act as a CouchDB client.
  #
  # Unless explicitly noted, this wrapper follows the same API names and
  # conventions of the original JavaScript code, with the exception of not
  # providing the alternative between callbacks and promises on CRUD operations,
  # opting for promises in every stance. The only situations in which that is
  # not the case are those involving EventEmitter, where only providing promises
  # would break semantics.
  #
  # That means you can safely read the original documentation and translate it
  # directly to Ruby. An `options` Object will be an options Hash, an Object
  # representing a document will be a Hash in Ruby land.
  #
  # This is not a toll-free library. It tries to follow the principle of least
  # surprise, forcing it to convert PouchDB's promises to Opal promises and
  # Objects to Hashes. It's not that great of a price to pay for convenience.
  #
  # Basic usage:
  #
  #   db = PouchDB::Database.new(name: "awesome_db")
  #   db.put(_id: "doc-id", contents: "This is important").then do
  #     db.all_docs(include_docs: true).then do |docs|
  #       puts "Yay, #{docs.size} docs"
  #     end
  #   end.fail do |error|
  #     puts "This code is perfect, but something went wrong"
  #   end
  #
  # Refer to the PouchDB API and Getting Started guides for more.
  class Database
    include Native
    include Conversion

    # Creates a Database. If passed a URL, the Database will act as a CouchDB
    # client. Otherwise, data will be stored and queried locally.
    #
    # Notice that here we don't support the alternative between passing the name
    # or `options` as the first argument. As such, name must *always* be
    # passed in as a keyword argument. A KeyError will be thrown otherwise.
    #
    # @param [Hash] options The options for database creation
    # @option options [String] :name The name or URL of the database (mandatory)
    # @option options [Boolean] :auto_compaction This turns on auto compaction, which means
    #   compact() is called after every change (default: false)
    # @option options [String] :adapter One of 'idb', 'leveldb', 'websql', or 'http'.
    #   If unspecified, PouchDB will infer this automatically, preferring IndexedDB
    #   to WebSQL in browsers that support both (i.e. Chrome, Opera and Android 4.4+).
    # @option options [Hash] :ajax For CouchDB clients only. Refer to official doc
    #   for more info.
    # @raise [KeyError] if `:name` is not passed in as a keyword argument
    def initialize(options = {})
      @name = options.fetch(:name)
      super `new PouchDB(#{options.to_n})`
    end

    attr_reader :name

    # Deletes the database. Be aware this will not affect replicas.
    #
    # @option options [Hash] :ajax Refer to the official doc for more info.
    # @return [Promise<Hash>]
    def destroy(options = {})
      as_opal_promise(`#{@native}.destroy()`)
    end

    # Deletes a document from the database. It can be called in one of two ways:
    #
    #    db.remove(doc: <full document hash>)
    #    # or, alternatively
    #    db.remove(doc_id: <id>, doc_rev: <revision>)
    #
    # @param args [Hash] The arguments for the function
    # @option args [Hash] :doc A document with _id and _rev to be removed. If defined,
    #   :doc_id and :doc_rev will be ignored.
    # @option args [String] :doc_id A document's id (requires :doc_rev)
    # @option args [String] :doc_rev A document's revision (requires :doc_id)
    # @option args [Hash] :options Extra options (refer to the official doc).
    # @return [Promise<Hash>]
    def remove(args = {})
      doc     = args[:doc]
      doc_id  = args[:doc_id]
      doc_rev = args[:doc_rev]
      options = args[:options]

      %x{
        var pouchPromise;
        if (doc) {
          pouchPromise = #{@native}.remove(#{doc.to_n}, #{options.to_n})
        } else {
          pouchPromise = #{@native}.remove(doc_id, doc_rev, #{options.to_n})
        }
      }

      as_opal_promise(`pouchPromise`)
    end

    # Creates or updates a document. When updating a document, its _id and _rev
    # can either be in `doc` or passed explicitly as :doc_id and :doc_rev.
    #
    # @param doc [Hash] The contents to create or update
    # @param args [Hash] Optional arguments
    # @option args [String] :doc_id A document's id (requires :doc_rev)
    # @option args [String] :doc_rev A document's rev (requires :id)
    # @option args [Hash] :options Extra options (refer to the official doc)
    # @return [Promise<Hash>]
    def put(doc, args = {})
      doc_id  = args[:doc_id]
      doc_rev = args[:doc_rev]
      options = args[:options]

      as_opal_promise(`#{@native}.put(#{doc.to_n}, doc_id, doc_rev, #{options.to_n})`)
    end

    # Creates a document, generating its id in the process. It's better to
    # always use `put` with an explicit id in order to avoid PouchDB's very long
    # Strings and to take advantage of its sorting of keys when using `all_docs`.
    #
    # @param doc [Hash] The contents to use when creating
    # @param options [Hash] Extra options (refer to the official doc)
    # @return [Promise<Hash>]
    def post(doc, options = {})
      as_opal_promise(`#{@native}.post(#{doc.to_n}, #{options.to_n})`)
    end

    # Retrieves a document from the database.
    #
    # @param doc_id [String] A document's id
    # @param options [Hash] Optional arguments. All default to `false`
    # @option options [String] :rev Fetch a specific revision. Defaults to
    #   winning revision
    # @option options [Boolean] :revs Include revision history with the document
    # @option options [Boolean] :revs_info Include a list of revisions, and
    #   their availability
    # @option options [String, Array<String>] :open_revs Fetch all leaf
    #   revisions if open_revs="all" or fetch all leaf revisions specified
    #   in open_revs array. Leaves will be returned in the same order as
    #   specified in input array
    # @option options [Boolean] :conflicts If specified, conflicting leaf
    #   revisions will be attached in _conflicts array
    # @option options [Boolean] :attachments Include attachment data
    # @option options [Hash] :ajax Refer to the official doc
    # @return [Promise<Hash>]
    def get(doc_id, options = {})
      as_opal_promise(`#{@native}.get(doc_id, #{options.to_n})`)
    end

    # Creates, updates or deletes multiple documents. If you omit an _id in one
    # of the documents, a new document will be created with a generated id; if
    # you pass in both an _id and _rev, it will be updated; if you add
    # `_deleted: true`, it will be removed.
    #
    # @param docs [Array<Hash>] an Array of documents
    # @param options [Hash] optional arguments
    def bulk_docs(docs, options = {})
      as_opal_promise(`#{@native}.bulkDocs(#{docs.to_n}, #{options.to_n})`,
                      &ARRAY_CONVERSION)
    end

    # Fetches multiple documents in bulk, indexing and sorting them by their
    # _ids. Deleted documents are only included if `options.keys` is specified.
    # All options are `false` unless otherwise specified.
    #
    # @param options [Hash] optional arguments
    # @option options [Boolean] include_docs include the document itself in
    #   each row in the `doc` field. Otherwise by default you only get the
    #   _id and rev properties.
    # @option options [Boolean] conflicts Include conflict information in
    #   the `_conflicts` field of a doc (only if `include_docs` is `true`)
    # @option options [Boolean] attachments Include attachment data as
    #   base64-encoded string (only if `include_docs` is `true`)
    # @option options [String] startkey Used in conjuction with `endkey`.
    #   Get documents with IDs in a certain range
    # @option options [String] endkey Used in conjuction with `startkey`.
    #   Get documents with IDs in a certain range
    # @option options [Boolean] inclusive_end Include documents having an
    #   ID equal to the given `options.endkey` (default: `true`)
    # @option options [Fixnum] limit Maximum number of documents to return
    # @option options [Fixnum] skip Number of documents to skip before
    #   returning (warning: poor performance on IndexedDB/LevelDB)
    # @option options [Boolean] descending Reverse the order of the output
    #   documents
    # @option options [String] key Only return documents with IDs matching
    #   this key
    # @option options [Array<String>] keys Keys to be fetched in a single
    #   shot. Neither `startkey` nor `endkey` can be specified with this
    #   option; the rows returned are in the same order as the supplied
    #   `keys` Array; the row for a deleted document will have the revision
    #   ID of the deletion, and an extra `deleted: true` in the `value`
    #   key; the row for a nonexistent document will just contain an
    #   `"error"` key with the value `"not_found"`. For details, see the
    #   CouchDB query options documentation.
    # @return [Promise]
    def all_docs(options = {})
      as_opal_promise(`#{@native}.allDocs(#{options.to_n})`) { |response|
        AllDocuments.new(response)
      }
    end

    # A list of changes made to documents in the database, in the order they
    # were made. It returns an object with the method `cancel()`, which you call
    # if you don't want to listen to new changes anymore.
    #
    # It is an EventEmitter and will emit a 'change' event on each document
    # change, a 'complete' event when all the changes have been processed, and
    # an 'error' event when an error occurs. In addition to the 'change' event,
    # any change will also emit a 'create', 'update', or 'delete' event.
    #
    # @param options [Hash] optional arguments
    # @option options [Boolean] include_docs Include the associated document
    #   with each change
    # @option options [Boolean] conflicts Include conflict information in
    #   the `_conflicts` field of a doc (only if `include_docs` is `true`)
    # @option options [Boolean] attachments Include attachment data as
    #   base64-encoded string (only if `include_docs` is `true`)
    # @option options [Boolean] descending Reverse the order of the output
    #   documents
    # @option options [String,Fixnum] since Start the results from the change
    #   immediately after the given sequence number. You can also pass
    #   'now' if you want only new changes (depends on `live: true` )
    # @option options [Fixnum] timeout The request timeout, in milliseconds
    # @option options [Fixnum] limit Limit the numbers of results to this
    #   number
    # @option options [Array<String>] Only shows changes for docs with these
    #   ids
    # @option options [String] filter Reference a filter function from a design
    #   document to selectively get updates. To use a view function, pass in
    #   `_view` and provide a reference to that function with the `view` key
    # @option options [Hash] query_params Properties to pass to the filter
    #   function (e.g.: { foo: "bar" }, where "bar" will be available in the
    #   filter function as `params.query.foo`. To have access to the params,
    #   define your function as receiving a second argument
    # @option options [String] view Specify a view function (e.g.
    #   "design_doc_name/view_name") to act as a filter. Documents counted
    #   as "passed" for a view filter if a map function emits at least one
    #   record of them (`options.filter` must be set to `"_view"`)
    # @option options [Boolean] returnDocs Available for non-http databases
    #   only, and defaults to `true`. Passing `false` prevents the changes
    #   feed from keeping all the documents in memory -- in other words,
    #   complete always has an empty results array, and the `change` event
    #   is the only way to get the event. Useful for large change sets
    #   where otherwise you would run out of memory
    # @option options [Fixnum] batch_size Only available for http databases,
    #   this configures how many changes to fetch at a time. Increasing this
    #   can reduce the number of requests made. Default is 25.
    # @option options [String] style Specifies how many revisions are returned
    #   in the changes array. The default, `"main_only"`, will only return
    #   the current "winning" revision; `"all_docs"` will return all leaf
    #   revisions (including conflicts and deleted former conflicts). Most
    #   likely you won't need this unless you are writing a replicator.
    # @return [EventEmitter]
    def changes(options = {})
      EventEmitter.new(`#{@native}.changes(#{options.to_n})`)
    end

    def replicate
      Replication.new(@native)
    end

    def sync(other)
      EventEmitter.new(`#{@native}.sync(#{database_as_string(other)})`)
    end

    # Get information about a database.
    #
    # @return [Promise]
    def info
      as_opal_promise(`#{@native}.info()`)
    end

    # Cleans up any stale map/reduce indexes.
    #
    # As design docs are deleted or modified, their associated index files (in
    # CouchDB) or companion databases (in local PouchDBs) continue to take up
    # space on disk. view_cleanup removes these unnecessary index files.
    #
    # @return [Promise]
    def view_cleanup
      as_opal_promise(`#{@native}.viewCleanup()`)
    end

    # Triggers a compaction operation in the local or remote database. This
    # reduces the database's size by removing unused and old data, namely
    # non-leaf revisions and attachments that are no longer referenced by those
    # revisions. Note that this is a separate operation from view_cleanup.
    #
    # For remote databases, PouchDB checks the compaction status at regular
    # intervals and fires the callback (or resolves the promise) upon
    # completion. Consult the compaction section of CouchDB's maintenance
    # documentation for more details.
    #
    # Also see auto-compaction, which runs compaction automatically (local
    # databases only).
    #
    # @param options [Hash] optional arguments
    # @option options [Fixnum] interval  Number of milliseconds to wait
    #   before asking again if compaction is already done. Defaults to 200.
    #   (Only applies to remote databases.)
    # @return [Promise]
    def compact(options = {})
      as_opal_promise(`#{@native}.compact(#{options.to_n})`)
    end

    # Given a set of document/revision IDs, returns the subset of those that do
    # not correspond to revisions stored in the database. Primarily used in
    # replication.
    #
    # @return [Promise]
    def revs_diff(diff)
      as_opal_promise(`#{@native}.revsDiff(#{diff.to_n})`)
    end
  end
end