📜 ⬆️ ⬇️

Grape: no single rails, part 2

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


')

TL; DR
gem install grape-gen 

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] # Default: mongoid [--batteries=one two three] # Batteries to include # Default: ["sidekiq", "carrierwave", "mandrill", "es", "faye"] [--use-grape-rackbuilder], [--no-use-grape-rackbuilder] # Default: true 

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 #   API $ thin start -p 9393 -e production -R faye.ru #   faye $ sidekiq -C config/sidekiq.yml -r ./config/boot_sidekiq.rb -e production #   Sidekiq 

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 / faye
Messages 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" # default :document is :all example "Get a list of all accounts" do do_request status.should == 200 end # Don't actually document this example, purely for testing purposes example "Get a list on page 2", :document => false do do_request(:page => 2) status.should == 404 end # With example_request, you can't change the :document example_request "Get a list on page 3", :page => 3 do status.should == 404 end end post "/accounts" do parameter :email, "User email" example "Creating an account", :document => :private do do_request(:email => "eric@example.com") status.should == 201 end example "Creating an account - errors", :document => [:private, :developers] do do_request status.should == 422 end end end 

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.

Source: https://habr.com/ru/post/248291/


All Articles