Tagger is a flexible tagger solution for Rails application where any enitiy e.g cat, dog, etc. can be tagged.
Is Rack based.
Is a complete MVC solution based on Rails engines.
Allows you to tag a model.
Tagger is tested in rails 5.1.4. Ruby version ruby-2.3.1
Add this line to your application's Gemfile:
gem 'tagger', :git => 'git@github.com:bhanubhakta/tagger.git'
Then run bundle install
Next, you need to run generator:
$ bundle exec rake tagger:install:migrations
$ bundle exec rake db:migrate
Integrating with your rails app:
- Add
acts_as_taggable
in the model you want to tag
Example
class Dog < ActiveRecord::Base
acts_as_taggable
end
- Update all environment files with:
Tagger.set_tagged_klass('<class_ name_of_the_taggable_model>')
Example development.rb
Rails.application.configure do
...
Tagger.set_tagged_klass('Dog')
end
- Mount routes using
mount_tagger
in routes.rb
Example route.rb
Rails.application.routes.draw do
...
mount_tagger
end
- We have to manually update the tagged_klass in environment files.
- Tagger can be used to tag a single model for now. This can be extended so that it can be used by multiple models in the same rails application.
- API authentication is not being used. This layer needs to be added for security.
- Haven’t performed security and vulnerability tests.
- Have initializers instead of updating the configs manually.
- Make mounting of routes configureable.
- Have verbose error response for API.
The following rails app is an example of how to integrate 'tagger' into the app. The app has a model called 'Dog' which 'acts_as_taggable'. The rails app version is 5.1.4.
Github link:
https://github.com/bhanubhakta/unicorn
The following is the 'unicorn' app hosted in heroku for demo. All the endpoints from 'Available APIs' section can be accessed.
https://vast-stream-64886.herokuapp.com/tagger/api/v1/dogs
I have used the ‘api/:version/:resource’ url format for the API to be used as:
- It gives us plenty of room to work on future versions while still maintaining the older versions.
- We could have a normal website running in the base url, using this layout helps seem less integration of different services but using the same base url/hostname.
- Testing will be easier.
For the taggable class, we can either use the class's name or 'breed'. Example: For Dog model we can use '/tagger/api/v1/dogs' or '/tagger/api/v1/breeds'. Both will return the same results.
POST /tagger/api/v1/dogs
input:
name: The name of the breed.
tags: An array of tags to be tagged.
params = {
name: 'German Sheperd',
tags: ["Knows Kung Fu", "Climbs Trees"]
}
response:
{
"dogs": {
"id": 1,
"name": "German Sheperd",
"created_at": "2017-09-16T06:07:53.800Z",
"updated_at": "2017-09-16T06:07:53.800Z"
},
"tags": [
{
"id": 1,
"name": "Knows Kung Fu",
"created_at": "2017-09-16T06:07:53.843Z",
"updated_at": "2017-09-16T06:07:53.843Z"
},
{
"id": 2,
"name": "Climbs Trees",
"created_at": "2017-09-16T06:07:53.852Z",
"updated_at": "2017-09-16T06:07:53.852Z"
}
]
}
GET /tagger/api/v1/dogs
- returns all dogs
response:
[
{
"id": 1,
"name": "German Sheperd",
"created_at": "2017-09-16T06:07:53.800Z",
"updated_at": "2017-09-16T06:07:53.800Z"
}
]
GET /tagger/api/v1/dogs/:id
- returns the dogs and all the tags belonging to it
response: {
"dogs": {
"id": 1,
"name": "German Sheperd",
"created_at": "2017-09-16T06:07:53.800Z",
"updated_at": "2017-09-16T06:07:53.800Z"
},
"tags": [
{
"id": 1,
"name": "Knows Kung Fu",
"created_at": "2017-09-16T06:07:53.843Z",
"updated_at": "2017-09-16T06:07:53.843Z"
},
{
"id": 2,
"name": "Climbs Trees",
"created_at": "2017-09-16T06:07:53.852Z",
"updated_at": "2017-09-16T06:07:53.852Z"
}
]
}
PATCH /tagger/api/v1/dogs/:id
- Updates the dog and it's tags
input:
name: German Sheperd
tags: ["Barks hard", "Climbs Trees"]
response: {
"dogs": {
"id": 1,
"name": "German Sheperd",
"created_at": "2017-09-16T06:07:53.800Z",
"updated_at": "2017-09-16T06:30:04.083Z"
},
"tags": [
{
"id": 3,
"name": "Barks hard",
"created_at": "2017-09-16T06:30:04.101Z",
"updated_at": "2017-09-16T06:30:04.101Z"
},
{
"id": 2,
"name": "Climbs Trees",
"created_at": "2017-09-16T06:07:53.852Z",
"updated_at": "2017-09-16T06:07:53.852Z"
}
]
}
DELETE /tagger/api/v1/dogs/:id
- Removes the dog
response:
{ "message": "Deleted successfully" }
GET /tagger/api/v1/dogs/:id/tags
- Gets tags on a dog
response: {
"tags": [
{
"id": 4,
"name": "Barks hard",
"created_at": "2017-09-16T06:44:27.260Z",
"updated_at": "2017-09-16T06:44:27.260Z"
},
{
"id": 5,
"name": "Climbs Trees",
"created_at": "2017-09-16T06:44:27.318Z",
"updated_at": "2017-09-16T06:44:27.318Z"
}
]
}
POST /tagger/api/v1/dogs/:id/tags
- Replaces tags on a dog
input:
tags: ['low shedding', 'pet friendly']
response: {
"dogs": {
"name": "German Sheperd",
"id": 2,
"created_at": "2017-09-16T06:44:27.031Z",
"updated_at": "2017-09-16T06:44:27.031Z"
},
"tags": [
{
"id": 6,
"name": "low shedding",
"created_at": "2017-09-16T06:47:01.401Z",
"updated_at": "2017-09-16T06:47:01.401Z"
},
{
"id": 7,
"name": "pet friendly",
"created_at": "2017-09-16T06:47:01.469Z",
"updated_at": "2017-09-16T06:47:01.469Z"
}
]
}
GET /tagger/api/v1/tags
- returns all tags in the system
response:
[
{
"id": 1,
"name": "Knows Kung Fu",
"created_at": "2017-09-16T06:07:53.843Z",
"updated_at": "2017-09-16T06:07:53.843Z"
},
{
"id": 2,
"name": "Barks hard",
"created_at": "2017-09-16T06:44:27.260Z",
"updated_at": "2017-09-16T06:44:27.260Z"
}
]
GET /tagger/api/v1/tags/:id
- returns a tag
response:
{
"id": 1,
"name": "Knows Kung Fu",
"created_at": "2017-09-16T06:07:53.843Z",
"updated_at": "2017-09-16T06:07:53.843Z"
}
PATCH /tagger/api/v1/tags/:id
- updates a tag
input:
name: Name of the tag to be updated.
response:
{
"id": 1,
"name": "Golden Retriever",
"created_at": "2017-09-16T06:07:53.843Z",
"updated_at": "2017-09-16T06:59:18.669Z"
}
DELETE /tagger/api/v1/tags/:id
- deletes the tag and all associations to breeds
response:
{
"message": "Deleted successfully"
}
GET /tagger/api/v1/dogs/stats
- Retrieves statistics about all dogs
response:
dogs: [
{
"id": 2,
"name": "German Sheperd",
"tag_count": 2,
"tag_ids": [
6,
7
]
}
]
GET /tagger/api/v1/tags/stats
- Retrieves statistics about all tags
response:
tags:
[{
"id": 6,
"name": "low shedding",
"entity_count": 1,
"entity_ids": [
2
]
},
{
"id": 7,
"name": "pet friendly",
"entity_count": 1,
"entity_ids": [
2
]
}]
The gem is available as open source under the terms of the MIT License.