
In my
first post about Grape, I quickly skipped through its main capabilities, for which I was deservedly filled up with reproaches of excessive compression of the material. According to the results of the publication of the last post, I promised a practical example of using the framework, as well as comparative benchmarks.
Today we will start creating an example - we will develop an API that can:
- register user
- activate it by email
- update the data of the authorized user
- return the profile of an authorized user
')
To be honest, I was a little impudent - we will not write anything.
After that post, I thought for a while - what should I do? It’s just that I didn’t really want to spend time writing a spherical example in a vacuum - I wanted, as they say, “to eat fish and not choke on a bone”.
Therefore, I spent some time and assembled the gem generator of a full-featured API application on grape, calling it plainly -
grape-gen .
"Skeleton" applications, batteries included
I will not torment you with long stories about what a modern web application needs, except for the framework itself.
The generator out of the box offers:
- ORM (currently only mongoid)
- authorization (Warden + CanCanCan)
- background tasks (Sidekiq)
- messages in real time (Faye)
- integration with ElasticSearch (Tire)
- download files (Carrierwave)
- send email via Mandrill (MandrillMailer)
- a customized test environment with guard + spork and a gem for documenting the API using tests.
All this is glued together and ready to go.
Redis is highly recommended, at least for faye and sidekiq.
After gem install grape-gen in the shell, the command of the same name will be available. To generate the application execute:
$ grape-gen app your_app_name
The generator provides the ability to disable certain components, here is a help to the team
$ grape-gen help app Usage: grape-gen app APP_NAME Options: [--path=PATH] [--orm=ORM]
The default grape-rackbuilder is my crutch for autoloading files and reloading code in a dev environment. Everything is not smooth in it, and the code is terrible there, but it copes with the majority of the functions assigned to it, I will patch it in the near future.
About EventMachine
In the comments to the first post, the user
stalkerg expressed the idea that "without asynchronous programming, such a framework has little meaning."
I believe that there is some truth in this, so the application “out of the box” is intended to be launched under an EventMachine-based server - Thin or Goliath.
This means that all the “batteries” used in the application are prepared for replacing the blocking IO with asynchronous counterparts from EventMachine, and for Carrierwave, image processing is transferred to the thread-pool via EventMachine.defer (yes, we have GIL, but even in this In the case of the same amount of time, we process twice as many images while giving the event-loop “sigh” twice as often - I checked it with tests).
Well, em-synchrony, of course. Each request is executed in our own fiber through Rack :: FiberPool, so there is no callback-hell.
API provided by the application
POST /api/auth/register email password display_name POST /api/auth/approve_email email email_approvement_code PUT /api/profile display_name avatar remove_avatar GET /api/profile
The API gives us JSON, which is generated using JBuilder and serialized via MultiJson + oj.
Configuring the application
Connections to redis, elasticsearch, third-party API keys are configured in the config / application.yml file
Database connections are configured in config / database.yml
Logging settings in config / logging.yml
Sidekiq settings in config / sidekiq.yml
Trial run
After we ascertained the correctness of our configs, it is time to launch our newly created application:
$ RACK_ENV=production thin start -p 9292
After the processes start successfully, a page with a primitive faye client subscribed to the
/ user / registered and
/ time channels will be available at
http: // localhost: 9292 / fayeMessages to the channel
/ time are sent by the Sidekiq task, scheduled to run every 5 seconds.
Thus, every 5 seconds a line with server time will be added to the page.
After the user is registered and his email is confirmed, a message with his display_name is added to the
/ user / registered channel, having received that the browser add a line with a proposal to greet the new user.
Test environment
The test environment is based on the RSpec 3 gems, rspec_api_documentation. Included loved by many FactoryGirl, DatabaseCleaner and Faker
All this starts under Guard + Spork, plus in the test environment, as in development, a reload of the code is used, which allows you to run the tests quite quickly.
Separately, it should be said about the
rspec_api_documentation gem - it allows you to combine the process of writing tests and the formation of API documentation.
Before that I used Swagger, but unfortunately it is more suitable for canonical REST API. If you have more JSON RPC-style APIs, it’s not easy to fit your API into a Swagger descriptive structure, while documenting the API response structure is only available for the grape-entity. The above-mentioned gems using their DSL over RSpec allow you to document the API using examples: you describe a test case (for example, a valid user registration), when you run this example, it remembers the request sent to the server, the response received, the request url, and generates documentation from this information. It is also possible to set the description of the parameters and. etc.
Here is an example:
resource "Account" do get "/accounts" do parameter :page, "Page to view"
What's next?
I think that I provided the basic information needed to quickly create my API application in this post.
The gems used in the project are well documented. If you think that I missed something important - write in the comments or drugs, I will definitely add.
For the next post I will try to prepare more or less objective benchmarks of this application.
For now, I can say that on the i5 2500K one application instance (one thread) processes ~ 700 requests per second to POST / api / auth / register with the data of an existing user.
There are also plans to add support for JRuby on the Goliath server (the JIT is very good there) and http and in-app caching.