Recently I was tasked with leveling up on a not so popular gem, or let me say a meta-gem called
Pragma. Calling Pragma a gem would be an understatement as it does more than just a gem, and depends on several other gems. Pragma can be used to build a complete API within a very short time, it’s described on its documentation as:
An expressive, opinionated ecosystem for building beautiful RESTful APIs with Ruby. It creates a solid foundation for your API so that you can focus on the stuff that matters.
In this tutorial, I’m going to show you how you can get started with Pragma. I believe this tutorial will save you a lot of headache which I had while trying to get things done with Pragma, as I couldn’t get enough resources on this gem apart from its simple and basic documentation.
We are going to be building a very simple application called
Mini-Mart.This is going to be a simple e-commerce API that will have product, user, order and review resources. Kindly note that we may not complete the app in this post, as I always try to avoid my posts from getting too long, therefore this may get split into parts.
To create an app with pragma, we just need to clone pragma’s starter repository.
git clone https://github.com/pragmarb/pragma-rails-starter.git mini-mart
This would create a Pragma app ready to go into the
cd mini-mart to enter the app’s directory and take a look at the files generated. If you go through the README file, you would see that we have to run the following commands to setup our app and the database.
cp config/database.example.yml config/database.yml
cp config/application.example.yml config/application.yml
These would automatically copy the contents from the examples configuration to our application and database configuration files. Note that you might have to tweak parts of these files so as to fit your development setting, especially the
database.yml file. I changed the
database for both the development and test environments in my
database.yml file to look exactly like this:
url: <%= ENV['DATABASE_URL'] %>
As you can see, my postgres has empty as both username and password, kindly enter yours accordingly. I have also decided to rename the databases to
mini_mart_test instead of
Now we can safely run
rake db:setup to have our database created. Let’s first create the
products table, we need to generate a
Product model for that.
rails g model Product name:string description:text number_in_store:integerrails db:migrate
Running these two commands would create the
products table in our database, having the columns
updated_at. We should not worry about having any associated tables for now, as we can easily create a migration for that when the need arises.
If you’ve gone through the tutorial I mentioned earlier, you should now know that
Pragma pulls in the following pieces:
To reiterate what you should have read in the tutorial; Decorator handles your json presentation, Contract handles validation, Policy deals with authorization while Operation handles the main business logic. It’s also worth mentioning that Pragma was built on Trailblazer, and you should have also seen the difference between Pragma and Trailblazer in the first tutorial. So if you want to dig deeper on any of the parts mentioned above, it would make sense to check it out on Trailblazer.
Let’s generate the resource for the
Product model we created.
rails g pragma:resource product
This command can be seen as the
rails g scaffold for Pragma. It will automatically generate a
product folder containing all the necessary files, and place it inside the
resources folder. It also creates the necessary routes and spec files as well. Your folder structure should now look similar to this:
Right now we already have all endpoints for product in place, we just need to do some tweaking to make it work effectively. Run
rails routes to see all your present available routes, and try and access some of them with postman, you would get some funny responses.
Let’s start by creating some real products, to do that, we need to put some validation in place. Open the
base.rb file inside the
contract folder and let’s put some validation in there.
# app/resources/api/product/contract/base.rbmodule API
class Base < Pragma::Contract::Base
property :number_in_store validation do
property method allows us to specify different columns we would like to manipulate, and the
validation takes in a block which allows us to specify the kind of validation we want to put on each attribute. Since Pragma::Contract was built on
Reform, you are strongly advised to check its documentation for all the available methods. This basic validation we’ve written will make sure that we can’t create a product without specifying all the three attributes.
Now let’s move to the
decorator, there are two files inside this folder;
instance.rb. The instance.rb file handles presentation for a single record while collection.rb handles for a collection of records. Let’s put the following in the instance.rb.
# app/resources/api/product/decorator/instance.rbmodule API
class Instance < Pragma::Decorator::Base
feature Pragma::Decorator::Timestamp property :id
property :number timestamp :created_at
Here we are featuring the
timestamp helper from Pragma::Decorator, which helps us to convert datetime object of
updated_at to UNIX timestamp which is the preferred way of presenting times in an API. We also use
property to list the attributes we would like to display in the json response. You can dig deeper on this through the Pragma::Decorator’s README.
Don’t forget that I said instance.rb is for presenting a single record, but it is referenced by default from the collection.rb, so you may not need to edit the collection.rb. Though you can open the file to see what’s going on there.
At this point we should be able to create a new product. Go to postman and send a POST request to this route http://localhost:3000/api/v1/products/ with the necessary request body. You should obviously get a response message that says
You are not authorized to access the requested resource.
This is because we’ve not authorized any user to be able to create product. So open the
policy.rb file and change the
create? method to the following:
# app/resources/api/product/policy.rbdef create?
This means we have given access to any user to create product. Test with postman again and everything should work fine. Send more requests so that we can have more products in our database.
Let’s assume that we only want to have products of unique names on our products table. That is. we do not want to have multiple products having the same name. In ActiveModel, simply specifying
unique: true on the attribute
name would solve this problem for us. But here in Pragma::Contract, it requires more codes and I have not been able to get that to work. My work around for now, is to specify a
step in the
create operation that checks the records against the new one, and write a response message into the response object.
create.rb file of operation, let’s put the following:
# app/resources/api/product/operation/create.rbmodule API
class Create < Pragma::Operation::Create
step :unique?, before: 'persist.save'
failure :log_error! def unique?(options, params:, **)
end def log_error!(options)
Pragma::Operation::Response.new( status: 409,
message: "Record already exists"
We have basically two methods inside this class. The
unique? method that checks our products table against the new record and then return true or false. The
log_error! method that adds our error message into the result.response object, instantiating the Response class from Pragma::Operation makes that possible. Kindly note that every method in operation files takes
options as parameter, and maybe with some other optional parameters. The
options is so important as it contains some keys as related to every request being made.
Pragma::Operation provides us with some methods such as
success that we can use to control the execution of the methods written. This is another beautiful part of Pragma that allows us to control codes’ execution without using
If-condition. We specified the
unique? step, and also specified a before-hook that this should be executed before saving the record into the database. What the
log_error! failure means is that once the unique? step fails, the execution should come to log_error!. If
step had been used for this, the execution won’t bother coming to log_error! if unique? fails.
Now try sending a request to create a new product using an already existing name, and you should get an appropriate error message. I will also strongly advise that you check documentation on Pragma::Operation and that of Trailblazer to have a better understanding, as this is the heart of Pragma, and you must know your way around it.
Let’s now grant access to get all the products in our database, open
policy.rb and change the
resolve method inside the
Scope class to the following:
This gives us access to all the product records in our application. If you send a GET request to http://localhost:3000/api/v1/products, you should have a response containing all your products.
Also if you want to get a particular product, sending a GET request to http://localhost:3000/api/v1/products/id_of_the_product will return the product, but we will also have to grant access for this by specifying
true in the
show? method inside
You should have gotten the flow by now, if you want to be able to update and delete a product, you will also have to tweak both
destroy methods to return
Looking at what we have right now, the way we have exposed our app by specifying
true as return values of all our authorization methods is not actually ideal. We will need to protect some parts by the time we have the user resource and put authentication in place, which we are going to be doing in the next part of this tutorial.
If you have any suggestion/comment /question as regards this tutorial, kindly post as comment below.