annotator_store 1.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c6ddd11b4ff25f3477a7c2ef180080c135f192ba
4
+ data.tar.gz: 40c0857fbb2adc58ec90f65622f91ca463016d9d
5
+ SHA512:
6
+ metadata.gz: 9dde1c468bcdeb6c3a6bd7069b9bb1176e479036b3e1b4b3b4bff26b756368d4fde382ffd863596fd5dd11ed04cd6995565bb8e5191b1d23080dd5212d5b067c
7
+ data.tar.gz: 39a4e0020f14898a1d0e3b730fa4adba062baa338fdf317c10214935b357f7235637d109a067319e0c87068d4ed78b603683c4ae0a4f732d6ca6ae54d17e9470
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ CHANGELOG
2
+ =========
3
+
4
+ v1.0.0.pre
5
+ ----------
6
+
7
+ * Rename gem from `annotator-store` to `annotator_store`
8
+ * Fix issue with explicit require needed in main application
9
+
10
+
11
+ v0.4.0
12
+ ------
13
+
14
+ * Add support for MySQL
15
+
16
+
17
+ v0.3.0
18
+ ------
19
+
20
+ * Change create endpoint to respond with `201 CREATED`
21
+ * Test support for Ruby `>= 1.9.3`.
22
+ * Test support for Rails `>= 4.0`.
23
+
24
+
25
+ v0.2.0
26
+ ------
27
+
28
+ * Add required files to gemspec
29
+ * Add CRUD (create, read, update and delete) endpoints
30
+ * Add search endpoint with filter support for uri
31
+ * Included tests for routes, requests, models and controllers
32
+
33
+
34
+ v0.1.0
35
+ ------
36
+
37
+ * Create index page with description of API.
38
+ * Add annotation functionality & create necessary endpoints
39
+
40
+
41
+ v0.0.1
42
+ ------
43
+
44
+ Initial release, to lock the gem name ... by v1.0.0 we should have a version
45
+ ready for deployment in production environment.
46
+
47
+ * Initialise the project
48
+ * Create structure
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,28 @@
1
+ CONTRIBUTING
2
+ ============
3
+
4
+ Submitting a Pull Request
5
+ -------------------------
6
+
7
+ 1. [Fork the repository][fork].
8
+ 2. [Create a topic branch][branch] (`git checkout -b BRANCH_NAME`).
9
+ 3. [Install bundler][bundler].
10
+ 4. Check that tests pass with `rspec spec`.
11
+ 5. Write a failing test to capture existing bug or lack of feature.
12
+ 6. Run `rspec spec` to verify that test fails.
13
+ 7. Implement your feature or bug fix.
14
+ 8. Ensure tests pass.
15
+ 9. If it's a new feature or a bug fix, please add an entry to the CHANGELOG file.
16
+ 10. Check code style violations using [Rubocop][rubocop].
17
+ 11. Add a commit (`git commit -am 'AWESOME COMMIT MESSAGE'`).
18
+ 12. Push your changes to the branch (`git push origin BRANCH_NAME`).
19
+ 13. [Submit a pull request.][pr]
20
+ 14. You will get some feedback and may need to push additional commits
21
+ with more fixes to the same branch; this will update your pull request
22
+ automatically.
23
+
24
+ [branch]: http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches
25
+ [bundler]: http://bundler.io
26
+ [fork]: https://help.github.com/articles/fork-a-repo/
27
+ [pr]: https://help.github.com/articles/using-pull-requests
28
+ [rubocop]: https://github.com/bbatsov/rubocop
data/LICENSE.md ADDED
@@ -0,0 +1,23 @@
1
+ License
2
+ =======
3
+
4
+ Copyright (c) 2014 Job King'ori Maina
5
+
6
+ MIT License
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
9
+ this software and associated documentation files (the "Software"), to deal in
10
+ the Software without restriction, including without limitation the rights to
11
+ use, copy, modify, merge, publish, distribute, sub-license, and/or sell copies
12
+ of the Software, and to permit persons to whom the Software is furnished to do
13
+ so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,414 @@
1
+ Annotator Store
2
+ ===============
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/annotator_store.svg)][5]
5
+ [![Build Status](https://travis-ci.org/itsmrwave/annotator_store-gem.svg?branch=master)][13]
6
+
7
+ Rails engine to implement a [Ruby on Rails][18] backend store implementation for
8
+ [Annotator][annotator].
9
+
10
+ > Annotator an open-source JavaScript library to easily add annotation
11
+ > functionality to any webpage. Annotations can have comments, tags, links,
12
+ > users, and more. Annotator is designed for [easy extensibility][1] so its a
13
+ > cinch to add a new feature or behaviour. Annotator also fosters an active
14
+ > developer community with contributors from four continents, building 3rd party
15
+ > plugins allowing the annotation of PDFs, EPUBs, videos, images, sound, and
16
+ > more.
17
+
18
+ The gem should be up on [rubygems.org][5], the [CHANGELOG here][7] and [all the
19
+ releases listed here][8].
20
+
21
+
22
+ Contents
23
+ --------
24
+
25
+ 1. Dependencies & Versions
26
+ 2. Installation
27
+ 3. Annotation Format
28
+ 4. API Endpoints
29
+ 5. Development
30
+ 6. Testing & Appraisals
31
+ 7. Versioning
32
+ 8. Contributing
33
+ 9. License
34
+
35
+
36
+ Dependencies & Versions
37
+ ----------------------
38
+
39
+ This engine requires Rails `>= 4.0` and Ruby `>= 1.9.3` and supports more than
40
+ one database.
41
+
42
+ Supported Ruby versions:
43
+
44
+ * [X] 1.9.3
45
+ * [X] 2.0.0
46
+ * [X] 2.1.0
47
+ * [X] 2.1.1
48
+ * [X] 2.1.2
49
+
50
+ Supported Rails versions:
51
+
52
+ * [X] 4.0.x
53
+ * [X] 4.1.x
54
+ * [X] 4.2.x
55
+
56
+ Supported databases:
57
+
58
+ * [X] MySQL
59
+ * [X] PostgreSQL
60
+
61
+ _'Supported'_ means that the test suite is designed to cover these versions
62
+ only. If your version isn't supported [raise a ticket][19]; make sure you
63
+ include the versions.
64
+
65
+ Sometimes when the build is failing, it's probably a few of these
66
+ configurations. Have a [look at the builds here][13] and see section on testing
67
+ & appraisals for more information.
68
+
69
+
70
+ Installation
71
+ ------------
72
+
73
+ Add this line to your application's Gemfile:
74
+
75
+ gem 'annotator_store'
76
+
77
+ And then from the `APP_ROOT` execute:
78
+
79
+ $ bundle install
80
+
81
+ Configure your database credentials in `config/database.yml` and then run the
82
+ migrations to create the tables to store the annotations. Depending on the
83
+ database of choice you'll need to set the `DB` environment variable when running
84
+ the migrations. There are slight variations in the structure of the DB when
85
+ using MySQL and PostgreSQL. The default setting is PostgreSQL if not set.
86
+
87
+ # Copy migrations over from the engine
88
+ $ rake annotator_store:install:migrations
89
+
90
+ # To run the copied migration when using MySQL
91
+ $ DB=mysql rake db:migrate
92
+
93
+ # To run the copied migration when using PostgreSQL
94
+ $ DB=postgres rake db:migrate
95
+
96
+ # To run the copied migration without specifying type (defaults to postgres)
97
+ $ rake db:migrate
98
+
99
+ Then mount it in `config/routes.rb`:
100
+
101
+ # Configures store endpoint in your app
102
+ mount AnnotatorStore::Engine, at: '/annotator_store'
103
+
104
+ Now it should be ready for use. All the endpoints will be available at
105
+ `http://0.0.0.0:3000/annotator_store` in your app.
106
+
107
+
108
+ Annotation Format
109
+ -----------------
110
+
111
+ An annotation is a JSON document that contains a number of fields describing the
112
+ position and content of an annotation within a specified document:
113
+
114
+ {
115
+ "id": "39fc339cf058bd22176771b3e3187329", # unique id (added by backend)
116
+ "annotator_schema_version": "v1.0", # schema version: default v1.0
117
+ "created": "2011-05-24T18:52:08.036814", # created datetime in iso8601 format (added by backend)
118
+ "updated": "2011-05-26T12:17:05.012544", # updated datetime in iso8601 format (added by backend)
119
+ "text": "A note I wrote", # content of annotation
120
+ "quote": "the text that was annotated", # the annotated text (added by frontend)
121
+ "uri": "http://example.com", # URI of annotated document (added by frontend)
122
+ "ranges": [ # list of ranges covered by annotation (usually only one entry)
123
+ {
124
+ "start": "/p[69]/span/span", # (relative) XPath to start element
125
+ "end": "/p[70]/span/span", # (relative) XPath to end element
126
+ "startOffset": 0, # character offset within start element
127
+ "endOffset": 120 # character offset within end element
128
+ }
129
+ ]
130
+ }
131
+
132
+ For PostgreSQL the primary key of the annotation will be a UUID while for MySQL it
133
+ will be a normal integer id.
134
+
135
+
136
+ API Endpoints
137
+ -------------
138
+
139
+ ### Root
140
+
141
+ | Method | Path | Returns |
142
+ | ------ | ---- | ------------------------------------------------------------------------ |
143
+ | GET | / | `200 OK` with an object containing store metadata, including API version |
144
+
145
+ Returns (example):
146
+
147
+ $ curl http://example.com/annotator_store
148
+ {
149
+ "name": "Annotator Store API",
150
+ "version": "2.0.0",
151
+ "links": {
152
+ "annotation": {
153
+ "create": {
154
+ "url": "http://example.com/annotator_store/annotations",
155
+ "method": "POST",
156
+ "description": "Create or add new annotations."
157
+ },
158
+ "read": {
159
+ "url": "http://example.com/annotator_store/annotations/:id",
160
+ "method": "GET",
161
+ "description": "Read, retrieve or view existing annotation."
162
+ },
163
+ "update": {
164
+ "url": "http://example.com/annotator_store/annotations/:id",
165
+ "method": "PUT/PATCH",
166
+ "description": "Update or edit existing annotation."
167
+ },
168
+ "delete": {
169
+ "url": "http://example.com/annotator_store/annotations/:id",
170
+ "method": "DELETE",
171
+ "description": "Delete or deactivate existing annotation."
172
+ }
173
+ },
174
+ "search": {
175
+ "url": "http://example.com/annotator_store/search",
176
+ "method": "GET",
177
+ "description": "Search for annotations"
178
+ }
179
+ }
180
+ }
181
+
182
+
183
+ ### Create
184
+
185
+ | Method | Path | Returns |
186
+ | ------- | ------------ | -------------------------------------------------------------------------- |
187
+ | POST | /annotations | `201 CREATED` with location in header set to the appropriate read endpoint |
188
+
189
+ Receives an annotation object in the proper annotation format, sent with `Content-Type: application/json`.
190
+
191
+ Returns (example):
192
+
193
+ $ curl http://example.com/annotator_store/annotations
194
+ {
195
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
196
+ "text": "Annotation text",
197
+ ...
198
+ }
199
+
200
+
201
+ ### Read
202
+
203
+ | Method | Path | Returns |
204
+ | ------ | ---------------- | ---------------------------------- |
205
+ | GET | /annotations/:id | `200 OK` with an annotation object |
206
+
207
+ Returns (example):
208
+
209
+ $ curl http://example.com/annotator_store/annotations/d41d8cd98f00b204e9800998ecf8427e
210
+ {
211
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
212
+ "text": "Annotation text",
213
+ ...
214
+ }
215
+
216
+
217
+ ### Update
218
+
219
+ | Method | Path | Returns |
220
+ | ---------- | ---------------- | --------------------------------------------------------------------- |
221
+ | PUT/PATCH | /annotations/:id | `200 OK` with location in header set to the appropriate read endpoint |
222
+
223
+ Receives attributes in the proper annotation format, sent with `Content-Type: application/json`.
224
+
225
+ Returns (example):
226
+
227
+ $ curl http://example.com/annotator_store/annotations/d41d8cd98f00b204e9800998ecf8427e
228
+ {
229
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
230
+ "text": "Annotation text",
231
+ ...
232
+ }
233
+
234
+
235
+ ### Delete
236
+
237
+ | Method | Path | Returns |
238
+ | ---------- | ---------------- | ------------------------------------------ |
239
+ | DELETE | /annotations/:id | `204 NO CONTENT` and obviously, no content |
240
+
241
+
242
+ ### Search
243
+
244
+ | Method | Path | Returns |
245
+ | ------ | -------- | ------------------------------------------------------- |
246
+ | GET | /search | An object with total and rows fields |
247
+
248
+ _Total_ is an integer denoting the total number of annotations matched by the
249
+ search, while _rows_ is a list containing what might be a subset of these
250
+ annotations.
251
+
252
+ If implemented, this endpoint should also support the `limit` and `offset` query
253
+ parameters for paging through results.
254
+
255
+ _Ps: Pagination with limit and offset not yet implemented. See [issue #1][15]._
256
+
257
+ Returns (example):
258
+
259
+ $ curl http://example.com/annotator_store/search?text=annotation
260
+ {
261
+ "total": 43127,
262
+ "rows": [
263
+ {
264
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
265
+ "text": "Updated annotation text",
266
+ ...
267
+ },
268
+ ...
269
+ ]
270
+ }
271
+
272
+
273
+ Development
274
+ -----------
275
+
276
+ There's a dummy Rails application in the `spec/dummy` folder. This application
277
+ is used as a mounting point for the engine, to make testing the engine on a
278
+ Rails app extremely simple. This directory should be treated like a typical
279
+ Rails testing environment, allowing for unit, functional and integration tests.
280
+
281
+ The current dummy app was generated using Rails 4.1.6 and with PostgreSQL as the
282
+ default store. The app depends on the `DB` environment variable to know which
283
+ settings to use for the database. See `config/database.yml` for details.
284
+
285
+ Set the `DB` environment variable to either `mysql` or `postgres` to choose
286
+ between the two.
287
+
288
+ # To use MySQL
289
+ $ DB=mysql [commands to run]
290
+
291
+ # To use PostgreSQL
292
+ $ DB=postgres [commands to run]
293
+
294
+ You can start up the dummy app to give it a spin by running `rails server` in
295
+ `spec/dummy` and then browse to `http://0.0.0.0:3000/`. There's a README in
296
+ there with a few details on setup, make sure you check it out.
297
+
298
+
299
+ Testing & Appraisals
300
+ --------------------
301
+
302
+ You may extend the dummy application by generating controllers, models or views
303
+ from within the directory (`spec/dummy`), and then use those to test our engine
304
+ (I've done this already but feel free to add). Then use the rspec command to run
305
+ your specs.
306
+
307
+ #=> Run all specs
308
+ $ bundle exec rspec
309
+
310
+ #=> Run only model specs example ...
311
+ $ bundle exec rspec spec/models
312
+
313
+ #=> Run only specs for AnnotatorStore::AnnotationsController ...
314
+ $ bundle exec rspec spec/controllers/annotations_controller_spec.rb
315
+
316
+ These will run the tests as per your local default configuration.
317
+
318
+ The [appraisal gem][16] is used to integrate with bundler and rake to test the
319
+ engine against different versions of dependencies in repeatable scenarios called
320
+ _'appraisals'_. This makes it easy to check for regressions in the library
321
+ without interfering with day-to-day development using Bundler.
322
+
323
+ As a result, a separate test run is created for each Ruby version and every
324
+ Rails version (see `travis.yml` file for specifics).
325
+
326
+ Locally you can test for different Rails versions. For example:
327
+
328
+ # Run specs against rails 4.0.12
329
+ $ appraisal rails-4.0.12 rspec spec
330
+
331
+ # Run specs against rails 4.1.8
332
+ $ appraisal rails-4.1.8 rspec spec
333
+
334
+ # Run specs against rails 4.2.0
335
+ $ appraisal rails-4.2.0 rspec spec
336
+
337
+ Check the Appraisal file at the root for the different rails configurations.
338
+ [Learn more about appraisals here][17].
339
+
340
+ PostgreSQL is configured to be the default database configuration. Set the `DB`
341
+ environment variable to either `mysql` or `postgres` to choose between the two.
342
+
343
+ # To use MySQL
344
+ $ DB=mysql [commands to run your tests]
345
+
346
+ # To use PostgreSQL
347
+ $ DB=postgres [commands to run your tests]
348
+
349
+ Automated tests are configured and set up to [run on Travis-CI][13]. Any push or
350
+ pull request will be built. The `DB` environment variable should be set to
351
+ either `mysql` or `postgres` to create a build matrix with good coverage.
352
+
353
+
354
+ Versioning
355
+ ----------
356
+
357
+ Major version zero (0.y.z) is for initial development. Anything may change at
358
+ any time. The public API should not be considered stable (implicitly mean, not
359
+ production ready ... yet).
360
+
361
+ Version 1.0.0 defines the public API (implying that it is production ready). The
362
+ way in which the version number is incremented after this release is dependent
363
+ on this public API and how it changes as per [Semantic Versioning
364
+ 2.0.0][semver].
365
+
366
+ All the releases, with their respective changes are [listed here][8].
367
+
368
+
369
+ Contributing
370
+ ------------
371
+
372
+ Want to contribute to the code? First, have a look at the guide in the
373
+ [CONTRIBUTING.md][9] file for the workflow.
374
+
375
+ Then, here's some Annotator documentation to help you get up to speed:
376
+
377
+ * [Annotator Storage][10] API specifications.
378
+ * [Annotator Store Plugin][11] documentation.
379
+
380
+ In summary, this gem helps implement a store for the plugin to interact with.
381
+
382
+ Any code contributors should be [listed here][12].
383
+
384
+
385
+ License
386
+ -------
387
+
388
+ [King'ori J. Maina][2] © 2014. The MIT License bundled therein is a permissive
389
+ license that is short and to the point. It lets people do anything they want as
390
+ long as they provide attribution and waive liability.
391
+
392
+
393
+ [annotator]: http://annotatorjs.org/
394
+ [semver]: http://semver.org
395
+
396
+ [1]: http://docs.annotatorjs.org/en/latest/hacking/plugin-development.html
397
+ [2]: http://kingori.co/
398
+ [3]: http://bundler.io/gemfile.html
399
+ [4]: http://bundler.io
400
+ [5]: https://rubygems.org/gems/annotator_store
401
+ [6]: http://rubydoc.info/gems/annotator_store/frames/
402
+ [7]: https://github.com/itsmrwave/annotator_store-gem/blob/master/CHANGELOG.md
403
+ [9]: https://github.com/itsmrwave/annotator_store-gem/blob/master/CONTRIBUTING.md
404
+ [8]: https://github.com/itsmrwave/annotator_store-gem/releases
405
+ [10]: http://docs.annotatorjs.org/en/v1.2.x/storage.html
406
+ [11]: http://docs.annotatorjs.org/en/v1.2.x/plugins/store.html
407
+ [12]: https://github.com/itsmrwave/annotator_store-gem/graphs/contributors
408
+ [13]: https://travis-ci.org/itsmrwave/annotator_store-gem
409
+ [14]: https://github.com/itsmrwave/annotator-store-demo
410
+ [15]: https://github.com/itsmrwave/annotator_store-gem/issues/1
411
+ [16]: http://rubygems.org/gems/appraisal
412
+ [17]: http://www.rubydoc.info/gems/appraisal
413
+ [18]: http://rubyonrails.org
414
+ [19]: https://github.com/itsmrwave/annotator_store-gem/issues/new
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
8
+ load 'rails/tasks/engine.rake'
9
+
10
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,97 @@
1
+ require_dependency 'annotator_store/application_controller'
2
+
3
+ module AnnotatorStore
4
+ class AnnotationsController < ApplicationController
5
+ before_action :set_annotation, only: [:show, :update, :destroy]
6
+
7
+ # POST /annotations
8
+ def create
9
+ format_client_input_to_rails_convention_for_create
10
+ @annotation = Annotation.new(annotation_params)
11
+ respond_to do |format|
12
+ if @annotation.save
13
+ format.json { render :show, status: :created, location: annotation_url(@annotation) }
14
+ else
15
+ format.json { render json: @annotation.errors, status: :unprocessable_entity }
16
+ end
17
+ end
18
+ end
19
+
20
+ # GET /annotations/1
21
+ def show
22
+ end
23
+
24
+ # PATCH/PUT /annotations/1
25
+ def update
26
+ format_client_input_to_rails_convention_for_update
27
+ respond_to do |format|
28
+ if @annotation.update(annotation_params)
29
+ format.json { render :show, status: :ok, location: annotation_url(@annotation) }
30
+ else
31
+ format.json { render json: @annotation.errors, status: :unprocessable_entity }
32
+ end
33
+ end
34
+ end
35
+
36
+ # DELETE /annotations/1
37
+ def destroy
38
+ @annotation.destroy
39
+ respond_to do |format|
40
+ format.json { head :no_content, status: :no_content }
41
+ end
42
+ end
43
+
44
+ # OPTIONS /annotations
45
+ def options
46
+ respond_to do |format|
47
+ format.json { render :options }
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # Use callbacks to share common setup or constraints between actions.
54
+ def set_annotation
55
+ @annotation = Annotation.find(params[:id])
56
+ end
57
+
58
+ # Convert the data sent by AnnotatorJS to the format that Rails expects so
59
+ # that we are able to create a proper params object
60
+ def format_client_input_to_rails_convention_for_create
61
+ params[:annotation] = {}
62
+ params[:annotation][:version] = params[:annotator_schema_version] unless params[:annotator_schema_version].blank?
63
+ params[:annotation][:text] = params[:text] unless params[:text].blank?
64
+ params[:annotation][:quote] = params[:quote] unless params[:quote].blank?
65
+ params[:annotation][:uri] = params[:uri] unless params[:uri].blank?
66
+ params[:annotation][:ranges_attributes] = params[:ranges].map do |r|
67
+ range = {}
68
+ range[:start] = r[:start]
69
+ range[:end] = r[:end]
70
+ range[:start_offset] = r[:startOffset]
71
+ range[:end_offset] = r[:endOffset]
72
+ range
73
+ end unless params[:ranges].blank?
74
+ end
75
+
76
+ # Convert the data sent by AnnotatorJS to the format that Rails expects so
77
+ # that we are able to create a proper params object
78
+ def format_client_input_to_rails_convention_for_update
79
+ params[:annotation] = {}
80
+ params[:annotation][:version] = params[:annotator_schema_version] unless params[:annotator_schema_version].blank?
81
+ params[:annotation][:text] = params[:text] unless params[:text].blank?
82
+ params[:annotation][:quote] = params[:quote] unless params[:quote].blank?
83
+ params[:annotation][:uri] = params[:uri] unless params[:uri].blank?
84
+ end
85
+
86
+ # Only allow a trusted parameter 'white list' through.
87
+ def annotation_params
88
+ params.require(:annotation).permit(
89
+ :text,
90
+ :quote,
91
+ :uri,
92
+ :version,
93
+ ranges_attributes: [:start, :end, :start_offset, :end_offset]
94
+ )
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,15 @@
1
+ module AnnotatorStore
2
+ class ApplicationController < ActionController::Base
3
+ before_action :set_headers
4
+
5
+ private
6
+
7
+ def set_headers
8
+ headers['Access-Control-Allow-Origin'] = '*'
9
+ headers['Access-Control-Expose-Headers'] = 'ETag'
10
+ headers['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD'
11
+ headers['Access-Control-Allow-Headers'] = '*,x-requested-with,Content-Type,If-Modified-Since,If-None-Match'
12
+ headers['Access-Control-Max-Age'] = '86400'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module AnnotatorStore
2
+ class PagesController < ApplicationController
3
+ before_action :format_input, only: [:search]
4
+
5
+ def index
6
+ end
7
+
8
+ def search
9
+ @annotations = AnnotatorStore::Annotation.where(search_params)
10
+ @total = @annotations.size
11
+ end
12
+
13
+ private
14
+
15
+ def format_input
16
+ params[:search] = {}
17
+ params[:search][:uri] = params[:uri]
18
+ end
19
+
20
+ def search_params
21
+ params.require(:search).permit(:uri)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ module AnnotatorStore
2
+ module AnnotationsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module AnnotatorStore
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ module AnnotatorStore
2
+ class Annotation < ActiveRecord::Base
3
+ # Associations
4
+ has_many :ranges, dependent: :destroy, autosave: true
5
+
6
+ # Allow saving of attributes on associated records through the parent,
7
+ # :autosave option is automatically enabled on every association
8
+ accepts_nested_attributes_for :ranges
9
+
10
+ # Validations
11
+ validates :version, presence: true
12
+ validates :text, presence: true
13
+ validates :quote, presence: true
14
+ validates :uri, presence: true
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ module AnnotatorStore
2
+ class Range < ActiveRecord::Base
3
+ # Associations
4
+ belongs_to :annotation
5
+
6
+ # Validations
7
+ validates :start, presence: true
8
+ validates :end, presence: true
9
+ validates :start_offset, presence: true
10
+ validates :end_offset, presence: true
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ json.id annotation.id
2
+
3
+ # Version
4
+ json.annotator_schema_version annotation.version
5
+
6
+ # Meta
7
+ json.uri annotation.uri
8
+
9
+ # Content
10
+ json.text annotation.text
11
+ json.quote annotation.quote
12
+ json.ranges do
13
+ json.array! annotation.ranges do |range|
14
+ json.start range.start
15
+ json.end range.end
16
+ json.startOffset range.start_offset
17
+ json.endOffset range.end_offset
18
+ end
19
+ end
20
+
21
+ # Timestamps
22
+ json.created annotation.created_at
23
+ json.updated annotation.updated_at
@@ -0,0 +1 @@
1
+ json.approved true
@@ -0,0 +1 @@
1
+ json.partial! 'annotator_store/annotations/annotation', annotation: @annotation, as: :annotation
@@ -0,0 +1,31 @@
1
+ json.name 'Annotator Store API'
2
+ json.version '2.0.0'
3
+ json.links do
4
+ json.annotation do
5
+ json.create do
6
+ json.url annotator_store.annotations_url
7
+ json.method 'POST'
8
+ json.description 'Create or add new annotations.'
9
+ end
10
+ json.read do
11
+ json.url annotator_store.annotation_url(':id')
12
+ json.method 'GET'
13
+ json.description 'Read, retrieve or view existing annotation.'
14
+ end
15
+ json.update do
16
+ json.url annotator_store.annotation_url(':id')
17
+ json.method 'PUT/PATCH'
18
+ json.description 'Update or edit existing annotation.'
19
+ end
20
+ json.delete do
21
+ json.url annotator_store.annotation_url(':id')
22
+ json.method 'DELETE'
23
+ json.description 'Delete or deactivate existing annotation.'
24
+ end
25
+ end
26
+ json.search do
27
+ json.url annotator_store.search_url
28
+ json.method 'GET'
29
+ json.description 'Search for annotations'
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ json.total @total
2
+ json.rows do
3
+ json.array! @annotations, partial: 'annotator_store/annotations/annotation', as: :annotation
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>AnnotatorStore</title>
5
+ <%= stylesheet_link_tag "annotator_store/application", media: "all" %>
6
+ <%= javascript_include_tag "annotator_store/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,15 @@
1
+ AnnotatorStore::Engine.routes.draw do
2
+
3
+ # Root path
4
+ root 'pages#index', defaults: { format: :json }
5
+
6
+ # Search
7
+ match 'search', to: 'pages#search', via: [:get], defaults: { format: :json }, constraints: { format: :json }
8
+ match 'search', to: 'annotations#options', via: [:options], defaults: { format: :json }, constraints: { format: :json }
9
+
10
+ # Annotations Endpoint
11
+ resources :annotations, only: [:create, :show, :update, :destroy], defaults: { format: :json }, constraints: { format: :json } do
12
+ match '/', to: 'annotations#options', via: [:options], on: :collection
13
+ match '/', to: 'annotations#options', via: [:options], on: :member
14
+ end
15
+ end
@@ -0,0 +1,48 @@
1
+ # This migration executes differently depending on the database set via the DB
2
+ # environment variable. For PostgreSQL we use UUIDs ... for MySQL we use normal
3
+ # primary keys.
4
+ class CreateAnnotatorStore < ActiveRecord::Migration
5
+ def self.up
6
+ # If using PostgreSQL database these are extensions are necessary
7
+ database_type = ENV['DB'] || 'postgres'
8
+ if database_type == 'postgres'
9
+ enable_extension 'plpgsql' unless extension_enabled?('plpgsql')
10
+ enable_extension 'uuid-ossp' unless extension_enabled?('uuid-ossp')
11
+ options = { id: :uuid, default: 'uuid_generate_v4()' }
12
+ else
13
+ options = {}
14
+ end
15
+
16
+ # Table to store annotations
17
+ create_table :annotator_store_annotations, options do |t|
18
+ t.string :version # Schema version
19
+ t.text :text # Content of annotation
20
+ t.text :quote # The annotated text
21
+ t.string :uri # URI of annotated document
22
+ t.timestamps # Time created_at and updated_at
23
+ end
24
+
25
+ # Table to store ranges covered by an annotation since each annotation could
26
+ # have many ranges ... at least by design in annotator.js but not yet
27
+ # implemented. Since the associated annotation's primary could be a UUID, at
28
+ # least in the case of PostgreSQL, we'll have to apply some logic to cater
29
+ # for the different scenarios.
30
+ create_table :annotator_store_ranges do |t|
31
+ if database_type == 'postgres'
32
+ t.uuid :annotation_id, index: true # Associated annotation's UUID
33
+ else
34
+ t.references :annotation, index: true # Associated annotation's normal id
35
+ end
36
+ t.string :start # Relative XPath to start element
37
+ t.string :end # Relative XPath to end element
38
+ t.integer :start_offset # Character offset within start element
39
+ t.integer :end_offset # Character offset within end element
40
+ t.timestamps
41
+ end
42
+ end
43
+
44
+ def self.down
45
+ drop_table :annotator_store_annotations
46
+ drop_table :annotator_store_ranges
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ require 'annotator_store/engine'
2
+ require 'jbuilder'
3
+
4
+ module AnnotatorStore
5
+ end
@@ -0,0 +1,11 @@
1
+ module AnnotatorStore
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace AnnotatorStore
4
+
5
+ config.generators do |g|
6
+ g.integration_tool :rspec
7
+ g.test_framework :rspec, fixture: false
8
+ g.fixture_replacement :factory_girl, dir: 'spec/factories'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module AnnotatorStore
2
+ VERSION = '1.0.0.pre'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :annotator_store do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,212 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: annotator_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Job King'ori Maina
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mysql2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: appraisal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: database_cleaner
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: factory_girl_rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: faker
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: json-schema
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: jbuilder
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rails
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '4.0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '4.0'
153
+ description: Rails engine to implement a Ruby backend store implementation for Annotator,
154
+ an open-source JavaScript library to easily add annotation functionality to any
155
+ webpage.
156
+ email:
157
+ - j@kingori.co
158
+ executables: []
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - CHANGELOG.md
163
+ - CONTRIBUTING.md
164
+ - LICENSE.md
165
+ - README.md
166
+ - Rakefile
167
+ - app/assets/javascripts/annotator_store/application.js
168
+ - app/assets/stylesheets/annotator_store/application.css
169
+ - app/controllers/annotator_store/annotations_controller.rb
170
+ - app/controllers/annotator_store/application_controller.rb
171
+ - app/controllers/annotator_store/pages_controller.rb
172
+ - app/helpers/annotator_store/annotations_helper.rb
173
+ - app/helpers/annotator_store/application_helper.rb
174
+ - app/models/annotator_store/annotation.rb
175
+ - app/models/annotator_store/range.rb
176
+ - app/views/annotator_store/annotations/_annotation.json.jbuilder
177
+ - app/views/annotator_store/annotations/options.json.jbuilder
178
+ - app/views/annotator_store/annotations/show.json.jbuilder
179
+ - app/views/annotator_store/pages/index.json.jbuilder
180
+ - app/views/annotator_store/pages/search.json.jbuilder
181
+ - app/views/layouts/annotator_store/application.html.erb
182
+ - config/routes.rb
183
+ - db/migrate/20141013113654_create_annotator_store.rb
184
+ - lib/annotator_store.rb
185
+ - lib/annotator_store/engine.rb
186
+ - lib/annotator_store/version.rb
187
+ - lib/tasks/annotator_store_tasks.rake
188
+ homepage:
189
+ licenses:
190
+ - MIT
191
+ metadata: {}
192
+ post_install_message:
193
+ rdoc_options: []
194
+ require_paths:
195
+ - lib
196
+ required_ruby_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: 1.9.3
201
+ required_rubygems_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">"
204
+ - !ruby/object:Gem::Version
205
+ version: 1.3.1
206
+ requirements: []
207
+ rubyforge_project:
208
+ rubygems_version: 2.2.2
209
+ signing_key:
210
+ specification_version: 4
211
+ summary: Rails engine to implement a Ruby backend store implementation for Annotator.
212
+ test_files: []