Skip to content
This repository has been archived by the owner on Jan 28, 2018. It is now read-only.

Rails engine to implement a Ruby backend store implementation for Annotator

License

Notifications You must be signed in to change notification settings

itskingori/annotator_store-gem

Repository files navigation

Annotator Store

Gem Version Build Status

Rails engine to implement a Ruby on Rails backend store implementation for Annotator.

Annotator an open-source JavaScript library to easily add annotation functionality to any webpage. Annotations can have comments, tags, links, users, and more. Annotator is designed for easy extensibility so its a cinch to add a new feature or behaviour. Annotator also fosters an active developer community with contributors from four continents, building 3rd party plugins allowing the annotation of PDFs, EPUBs, videos, images, sound, and more.

The gem should be up on rubygems.org, the CHANGELOG here and all the releases listed here.

Contents

  1. Dependencies & Versions
  2. Installation
  3. Annotation Format
  4. API Endpoints
  5. Development
  6. Testing & Appraisals
  7. Versioning
  8. Contributing
  9. License

Dependencies & Versions

This engine requires Rails >= 4.0 and Ruby >= 1.9.3 and supports more than one database.

Supported Ruby versions:

  • 1.9.3
  • 2.0.0
  • 2.1.0
  • 2.1.1
  • 2.1.2

Supported Rails versions:

  • 4.0.x
  • 4.1.x
  • 4.2.x

Supported databases:

  • MySQL
  • PostgreSQL

'Supported' means that the test suite is designed to cover these versions only. If your version isn't supported raise a ticket; make sure you include the versions.

Sometimes when the build is failing, it's probably a few of these configurations. Have a look at the builds here and see section on testing & appraisals for more information.

Installation

Add this line to your application's Gemfile:

gem 'annotator_store'

And then from the APP_ROOT execute:

$ bundle install

Configure your database credentials in your config/database.yml file and then run the migrations to create the tables to store the annotations.

# Copy migrations over from the engine
$ rake annotator_store:install:migrations

# To run the copied migration
$ rake db:migrate

Then mount it in config/routes.rb:

# Configures store endpoint in your app
mount AnnotatorStore::Engine, at: '/annotator_store'

Now it should be ready for use. All the endpoints will be available at http://0.0.0.0:3000/annotator_store in your app.

Annotation Format

An annotation is a JSON document that contains a number of fields describing the position and content of an annotation within a specified document:

{
  "id": 1,                                   # unique id (added by backend)
  "annotator_schema_version": "v1.0",        # schema version: default v1.0
  "created": "2011-05-24T18:52:08.036814",   # created datetime in iso8601 format (added by backend)
  "updated": "2011-05-26T12:17:05.012544",   # updated datetime in iso8601 format (added by backend)
  "text": "A note I wrote",                  # content of annotation
  "quote": "the text that was annotated",    # the annotated text (added by frontend)
  "uri": "http://example.com",               # URI of annotated document (added by frontend)
  "ranges": [                                # list of ranges covered by annotation (usually only one entry)
    {
      "start": "/p[69]/span/span",           # (relative) XPath to start element
      "end": "/p[70]/span/span",             # (relative) XPath to end element
      "startOffset": 0,                      # character offset within start element
      "endOffset": 120                       # character offset within end element
    }
  ]
}

API Endpoints

Root

Method Path Returns
GET / 200 OK with an object containing store metadata, including API version

Returns (example):

$ curl http://example.com/annotator_store
{
  "name": "Annotator Store API",
  "version": "2.0.0",
  "links": {
    "annotation": {
      "create": {
        "url": "http://example.com/annotator_store/annotations",
        "method": "POST",
        "description": "Create or add new annotations."
      },
      "read": {
        "url": "http://example.com/annotator_store/annotations/:id",
        "method": "GET",
        "description": "Read, retrieve or view existing annotation."
      },
      "update": {
        "url": "http://example.com/annotator_store/annotations/:id",
        "method": "PUT/PATCH",
        "description": "Update or edit existing annotation."
      },
      "delete": {
        "url": "http://example.com/annotator_store/annotations/:id",
        "method": "DELETE",
        "description": "Delete or deactivate existing annotation."
      }
  },
  "search": {
      "url": "http://example.com/annotator_store/search",
      "method": "GET",
      "description": "Search for annotations"
    }
  }
}

Create

Method Path Returns
POST /annotations 201 CREATED with location in header set to the appropriate read endpoint

Receives an annotation object in the proper annotation format, sent with Content-Type: application/json.

Returns (example):

$ curl http://example.com/annotator_store/annotations
{
  "id": 1,
  "text": "Annotation text",
  ...
}

Read

Method Path Returns
GET /annotations/:id 200 OK with an annotation object

Returns (example):

$ curl http://example.com/annotator_store/annotations/d41d8cd98f00b204e9800998ecf8427e
{
  "id": 1,
  "text": "Annotation text",
  ...
}

Update

Method Path Returns
PUT/PATCH /annotations/:id 200 OK with location in header set to the appropriate read endpoint

Receives attributes in the proper annotation format, sent with Content-Type: application/json.

Returns (example):

$ curl http://example.com/annotator_store/annotations/d41d8cd98f00b204e9800998ecf8427e
{
  "id": 1,
  "text": "Annotation text",
  ...
}

Delete

Method Path Returns
DELETE /annotations/:id 204 NO CONTENT and obviously, no content

Search

Method Path Returns
GET /search An object with total and rows fields

Total is an integer denoting the total number of annotations matched by the search, while rows is a list containing what might be a subset of these annotations.

If implemented, this endpoint should also support the limit and offset query parameters for paging through results.

Ps: Pagination with limit and offset not yet implemented. See issue #1.

Returns (example):

$ curl http://example.com/annotator_store/search?text=annotation
{
  "total": 43127,
  "rows": [
    {
      "id": 1,
      "text": "Updated annotation text",
      ...
    },
    ...
  ]
}

Development

There's a dummy Rails application in the spec/dummy folder. This application is used as a mounting point for the engine, to make testing the engine on a Rails app extremely simple. This directory should be treated like a typical Rails testing environment, allowing for unit, functional and integration tests.

The current dummy app was generated using Rails 4.1.6 and with PostgreSQL as the default store. The app depends on the DB environment variable to know which settings to use for the database. See config/database.yml for details.

Set the DB environment variable to either mysql or postgres to choose between the two.

# To use MySQL
$ DB=mysql [commands to run]

# To use PostgreSQL
$ DB=postgres [commands to run]

You can start up the dummy app to give it a spin by running rails server in spec/dummy and then browse to http://0.0.0.0:3000/. There's a README in there with a few details on setup, make sure you check it out.

Testing & Appraisals

You may extend the dummy application by generating controllers, models or views from within the directory (spec/dummy), and then use those to test our engine (I've done this already but feel free to add). Then use the rspec command to run your specs.

#=> Run all specs
$ bundle exec rspec

#=> Run only model specs example ...
$ bundle exec rspec spec/models

#=> Run only specs for AnnotatorStore::AnnotationsController ...
$ bundle exec rspec spec/controllers/annotations_controller_spec.rb

These will run the tests as per your local default configuration.

The appraisal gem is used to integrate with bundler and rake to test the engine against different versions of dependencies in repeatable scenarios called 'appraisals'. This makes it easy to check for regressions in the library without interfering with day-to-day development using Bundler.

As a result, a separate test run is created for each Ruby version and every Rails version (see travis.yml file for specifics).

Locally you can test for different Rails versions. For example:

# Run specs against rails 4.0.12
$ appraisal rails-4.0.12 rspec spec

# Run specs against rails 4.1.8
$ appraisal rails-4.1.8 rspec spec

# Run specs against rails 4.2.0
$ appraisal rails-4.2.0 rspec spec

Check the Appraisal file at the root for the different rails configurations. Learn more about appraisals here.

PostgreSQL is configured to be the default database configuration. Set the DB environment variable to either mysql or postgres to choose between the two.

# To use MySQL
$ DB=mysql [commands to run your tests]

# To use PostgreSQL
$ DB=postgres [commands to run your tests]

Automated tests are configured and set up to run on Travis-CI. Any push or pull request will be built. The DB environment variable should be set to either mysql or postgres to create a build matrix with good coverage.

Versioning

Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable (implicitly mean, not production ready ... yet).

Version 1.0.0 defines the public API (implying that it is production ready). The way in which the version number is incremented after this release is dependent on this public API and how it changes as per Semantic Versioning 2.0.0.

All the releases, with their respective changes are listed here.

Contributing

Want to contribute to the code? First, have a look at the guide in the CONTRIBUTING.md file for the workflow.

Then, here's some Annotator documentation to help you get up to speed:

In summary, this gem helps implement a store for the plugin to interact with.

Any code contributors should be listed here.

License

King'ori J. Maina © 2014. The MIT License bundled therein is a permissive license that is short and to the point. It lets people do anything they want as long as they provide attribution and waive liability.