.. | ||
app | ||
config | ||
lib | ||
script | ||
spec | ||
test | ||
.gitignore | ||
.rspec | ||
carto_gears_api.gemspec | ||
Gemfile | ||
Gemfile.lock | ||
LICENSE | ||
Rakefile | ||
README.md |
CARTO Gears API
DISCLAIMER: CARTO Gears API is still in development, it won't be considered stable until first major 1 version
An API for building CARTO Gears
CARTO Gear: Rails engine built on top of CARTO, using CARTO Gears API.
This is meant to be a documented way to add new features to CARTO, in a non-intrusive manner, taking advantage of a domain API maintained by CARTO team. Our goal with this API and conventions is making CARTO easily extensible.
References:
Value models
In order to make development as simple as possible, models returned by API services are immutable POROs. They're instantiated with the sleek values gem.
What about Rails?
We don't want you to be coupled to CARTO internal models code, that's why Gears API returns just POROs. Nevertheless, if you want to use some Rails magic you can decorate your classes with the minimum that you need to make a class behave like a model without persistence (enabling form integration, for example):
class MyUser < CartoGearsApi::Users::User
extend ActiveModel::Naming
include ActiveRecord::AttributeMethods::PrimaryKey
def id
# This is needed to make ActiveRecord::AttributeMethods::PrimaryKey work. Otherwise it
# won't find the id accessible thanks to Value. Magic is not always compatible.
@id
end
end
Then, you can instantiate yours based on the API one:
MyUser.with(CartoGearsApi::Users::UsersService.new.logged_user(request).to_h)
PS: you could also "open" CartoGearsApi::Users::User
and add there what's needed, but you DON'T want to do that ;-)
HOWTO
Private vs public gears
CARTO currently supports two directories for Gears:
/gears
Public engines. For example, for new features developed with a component-based approach. It's part of the main repo and gears inside it will be automatically loaded. Alternatively, they could be extracted to a separate repository and loaded via Gemfile.
/private_gears
Private engines. For example, for in-house developments that can't be shipped with the Open Source version.
It's skipped at .gitignore
, and it's dynamically loaded, so it won't appear in Gemfile
or Gemfile.lock
.
You can have the code wherever you want, and add symbolic links there.
Gears limitations
Due to the custom handling of this in order to avoid polluting Gemfile and Gemfile.lock files, gears loaded from a directory have several limitations:
- If you specify a runtime dependency of a gem already existing at Gemfile, it must have the exact version.
- Although the private gem itself doesn't appear in
Gemfile
orGemfile.lock
, dependencies do, because they need to be installed. - You should avoid adding paths to the
$LOAD_PATH
from the Gemfile. An alternative is to add them in the initialization code.
Generating a clean Gemfile.lock
As said, Gemfile.lock
won't mention private gears, but it contains private gears dependencies.
In order to generate a clean Gemfile.lock
, you should:
mv private_gears private_gears.bak
bundle update
git commit Gemfile.lock -m "Clean Gemfile.lock" && git push
mv private_gears.bak private_gears
Create a Rails engine
Assuming that you pick /gears
, create a Rails engine by
bundle exec rails plugin new gears/my_component --full --mountable
It will be mounted at root (/
) and automatically loaded.
To enable automatic reload for development, you should add the lib
path to the autoload_path
in your Engine
subclass:
module MyComponent
class Engine < ::Rails::Engine
isolate_namespace MyComponent
config.autoload_paths << config.root.join('lib').to_s if Rails.env.development?
end
end
You must use only classes under CartoGearsApi
namespace. It's currently under /gears/carto_gears_api/lib
,
but it will be documented before first public release.
Tests
CartoDB runs the tests with bundle exec rspec
at engine directory. If you want to use this, you should create
your tests with rspec.
In order to enable rspec:
rspec --init
- Copy the contents of
test_helper.rb
intospec_helper
. - Run
rspec
and fix path errors that you might get.
Documentation
CartoGearsAPI::
(Ruby API)
Generate the documentation with the following command:
yardoc --files app/views/shared/form/_input_text.html.erb
Documented ERBs are listed in the "Files" top left section at the docs.
Note: YARD support for ERB files is quite limited, and ERBs documentation is still ongoing.
Queue system
CARTO provides a queuing system. In order to send a job to the queue, do this:
require 'carto_gears_api/queue/jobs_service'
CartoGearsApi::Queue::JobsService.new.send_job('MyModule::MyClass', :class_method, 'param1', 2)
See more details at {CartoGearsApi::Queue::JobsService}.
Sending emails
You can use the queue system and whatever you need for sending emails. The most straightforward way is using Rails mailers.
You can send a test email to check that Rails is properly configured:
CartoGearsApi::Mailers::TestMail.test_mail('juanignaciosl@carto.com', 'juanignaciosl@carto.com', 'show!')
Using the queue:
CartoGearsApi::Queue::JobsService.new.send_job('CartoGearsApi::Mailers::TestMail', :test_mail, from, to, subject)
Sending your own email:
class MyMail < ActionMailer::Base
def a_mail(to)
mail(to: to, from: 'contact@carto.com', subject: 'the subject').deliver_now
end
end
CartoGearsApi::Queue::JobsService.new.send_job('MyMail', :a_mail, 'support@carto.com')
CARTO Gears API also provides a CartoGearsApi::Mailers::BaseMail
mailer with CARTO standard layout and from.
Usage example:
class MyMail < CartoGearsApi::Mailers::BaseMail
def a_mail(to)
mail(to: to, subject: 'the subject').deliver_now
end
end
Extension points
Most extension points require a registration during intialization. Although you can use initializers for your gear,
everything that depends upon CARTO should be run in an after_initialize
hook, to ensure it is loaded after CARTO. e.g:
module MyComponent
class Engine < ::Rails::Engine
isolate_namespace MyComponent
config.after_initialize do
# Your initialization code
end
end
end
Adding links to profile page
See creation at {CartoGearsApi::Pages::SubheaderLink}. Example:
CartoGearsApi::Pages::Subheader.instance.links_generators << lambda do |context|
user = CartoGearsApi::Users::UsersService.new.logged_user(context.request)
if user.has_feature_flag?('carto_experimental_gear')
include CartoGearsApi::UrlHelper
[
CartoGearsApi::Pages::SubheaderLink.with(
path: carto_gear_path(:my_gear, context, 'something'),
text: 'The Text',
controller: 'my_gear/something')
]
end
end
Events
See a list of events at {CartoGearsApi::Events::BaseEvent} and how to subscribe to them with {CartoGearsApi::Events::EventManager}. Example:
CartoGearsApi::Events::EventManager.instance.subscribe(CartoGearsApi::Events::UserCreationEvent) do |event|
puts "Welcome #{event.user.username}"
end