Thanks for taking the time to contribute! We appreciate you!
This project and everyone participating in it is governed by the BioPortal Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to [email protected].
Please don't file issues to ask questions. You'll get the fastest response by sending a message to our support list: [email protected]
The BioPortal REST API is an open source project made up of seven repositories, which are hosted in the NCBO Organization on GitHub. This section is meant to help you understand which of the seven repositories encapsulates the functionality you'd like to modify or report bugs against.
- ontologies_api - Hypermedia API for ontologies
- ontologies_linked_data - Models and serializers for ontologies and related artifacts
- ncbo_annotator - Annotate text with relevant ontology concepts
- ncbo_ontology_recommender - Obtain recommendations for relevant ontologies based on excerpts from biomedical text or lists of keywords
- ncbo_cron - Cron jobs that run on a regular basis in the infrastructure
- goo - Graph Oriented Objects (GOO) for Ruby. An RDF/SPARQL-based ORM.
- sparql-client - SPARQL client for Ruby
Bugs are tracked using GitHub issues. Before submitting a bug report, determine which repository the problem should be reported against, and search the Issues tab to see if the problem has already been entered. When creating issues:
- Use clear, descriptive titles
- Describe the steps to reproduce the problem
- Include details about your environment
Enhancement suggestions are welcome, including new features and minor enhancements to existing functionality. Enhancements are tracked using GitHub issues with the "enhancement" label. Before creating an enhancement request, determine which repository the enhancement should be reported against, and search the Issues tab to see if it has already been entered.
We follow the fork and pull collaborative development model.
Please use the following guideline for code contributions:
- Fork the repository
- New features and bug fixes should be developed in their own branch
- Add tests for any changes
Pull requests are accepted and encouraged.
There are several ways to work with the code and run the application. The three things you will likely do most often are:
- Run the application with code reloading enabled
- Run the console
- Run tests
We use a library called Shotgun to force our entire application to reload on each request. This allows you to make a change in a file, hit refresh in a browser, and see the changes reflected. To load the application with Shotgun, use the following command:
bundle exec shotgun
Once started, the application will be available on localhost:9393 (by default, this can be changed).
If you need to insert a breakpoint, modify the code by adding binding.pry
on a line by itself. When you make a request, the application will stop at that point in the code and you can inspect objects and local variables easily. Type ls
to see a list of local variables and methods that are available to run.
You can load a pry session that's bootstrapped with the project environment:
bundle exec rackup -E console
This will put you into the application at a point where you can invoke code. For example, you could create and save new Goo models, make requests using methods from Rack::Test, or access variables, settings, etc set for the project.
Tests can be created under the top-level test
folder in the corresponding section (model, controller, etc). Tests are written using the Ruby default Test::Unit library. Many projects will have a base test class that initializes the environment as needed (e.g. test_case.rb
from ontologies_api).
To run tests, just use Ruby to call the class:
bundle exec ruby test/controllers/test_user_controller.rb
(from ontologies api)
You can insert breakpoints using binding.pry
and interact with the code directly from the test.
Another option is invoking full test suites with Rake. To see the list of available rake tasks, run rake -T
from the project folder. Generally, running rake test
will execute all tests.
Sinatra routes can be defined in controller files, found in the /controllers folder. All controller files should inherit from the ApplicationController, which makes methods defined in the ApplicationController available to all controllers. Generally you will create one controller per resource. Controllers can also use helper methods, either from the ApplicationHelper or other helpers.
Re-usable code can be included in helpers in files located in the /helpers folder. Helper methods should be created in their own module namespace, under the Sinatra::Helpers module (see MessageHelper for an example).
The /lib folder can be used for organizing complex code or Rack middleware that doesn't fit well in the /helpers or /models space. For example, a small DSL for defining relationships between resources or a data access layer.
Environment-specific settings can be placed in the appropriate /config/environments/{environment}.rb file. These will get included automatically on a per-environment basis.
Logs are created when running in production mode. In development, all logging goes to STDOUT.
A simple testing framework, based on Ruby's TestUnit framework and Rake, is available. The tests rely on a few conventions:
- Models and controllers should require and inherit from the /test/test_case.rb file (and TestCase class).
- Helpers should require and inherit from the /test/test_case_helpers.rb file (and TestCaseHelpers class).
- Libraries should preferably have self-contained tests.
The Rack::Test environment is available from all test types for doing mock requests and reading responses.
Several rake tasks are available for running tests:
bundle exec rake test
runs all testsbundle exec rake test:controllers
runs controller testsbundle exec rake test:models
runs model testsbundle exec rake test:helpers
runs helper tests
Tests can alternatively be run by invoking Ruby directly:
bundle exec ruby test/controllers/test_hello_world.rb
A global logger is provided, which unfortunately does not yet integrate with Sinatra's logger. The logger is available using the constant LOGGER
and uses Apache's common logging format.
There are multiple levels of logging available (debug
, info
, warn
, error
, and fatal
), with only logging for info
and above available in the production environment.
For more information on the logger, see Ruby's Logger class.
The application is bootstrapped from the app.rb file, which handles file load order, setting environment-wide configuration options, and makes controllers and helpers work properly in the Sinatra application without further work from the developer.
app.rb loads the /init.rb file to handle this process. Sinatra settings are included in the app.rb file.
Dependent gems can be configured in the Gemfile using Bundler.
The following is information you may find useful while working in a Ruby/Sinatra/Rack environment.
Goo is a general library for Object to RDF Mapping written by Manuel Salvadores. It doesn't have any NCBO-specific pieces in it, except to model data in the way it makes sense for us. It includes functionality for basic CRUD operations.
Using Goo, we have created a library called ontologies_linked_data. This library extends Goo to provide specific models for use with NCBO data. You can see how things work by looking at the tests included with ontologies_linked_data or Goo. We'll cover the basics here:
We can look at some tests in Goo to see how to work with objects built with Goo.
For example, here is an object Person
defined in a test: test_model_person.rb
In the method test_person
, you can see how an instance of the model is created: Person.new
There can be restrictions on the kind of data stored in an attribute for a Goo object. For example, Person
contains an attribute called contact_data
. This attribute can only be populated with an instance of the ContactData
class or it will not be considered valid. This is defined as a part of the object with this syntax:
:contact_data , :instance_of => { :with => :contact_data }
To test if an instance is valid, you can use the valid?
method. For example:
> p = Person.new
> p.valid?
=> false
If calling valid?
fails, the corresponding errors will be available by calling the errors
method, for example:
> p = Person.new
> p.valid?
=> false
> p.errors
After validating an object, you can call the save
method to store the object's triples in the triplestore backend. If the object isn't valid, calling save
will result in an exception.
The simplest way to retrieve an object is using its ID with the class method find
:
Person.find("paul")
You can also do a lookup with the full IRI:
Person.find(RDF::IRI.new("http://example.org/person/paul"))
Each object type has its own IRI prefix, so using the short form of the ID will simply result it in being appended to the IRI prefix.
You can also search for objects using attribute conditions:
Person.where(name: "paul")
Person.where(birth_date: DateTime.parse("2012-10-04T07:00:00.000Z"))
You can also retrieve all objects:
Person.all
In the future, there will be syntax to handle offsets and limits.
After retrieving an object, you can modify attributes and then save the object in order to update the data. This corresponds to an HTTP PATCH.
Another option is to delete the existing object and write a new one with the same ID as the old. This would be equivalent to an HTTP PUT.
Goo objects also contain a delete
method that will remove all of the object's triples from the store.
Rack is a framework that sits between a web server (Apache, passenger, Thin, etc) and application code:
[ web server ] → [ request ] → [ rack / middleware ] → [ application ] ↓
[ web server ] ← [ response ] ← [ rack / middleware ] ←
Rack and its associated middleware basically wraps your application code and allows you to work with and modify the HTTP request and response information. This happens in the rack / middleware
steps above.