📜 ⬆️ ⬇️

Asynchronous Cramped ruby ​​framework: architecture and usage

Cramp is a fully asynchronous real-time framework written by Pratik Naik, a developer at 37signals and a member of the Rails core-team. This framework is primarily intended for organizing bidirectional communication between the client and the server and has built-in support for WebSockets and Server-Sent Events. In this article we will discuss the main things related to the use of this tool, as well as try to understand its architecture and understand how it works.

Perhaps the main drawback of the framework is the small number of articles and examples, there is only a small page of documentation on the official website and the actual code of the project on the github . From there, basically everything written below was taken.

Architecture


Cramp is based on EventMachine, ActiveSupport and Thor and runs on a Thin or Rainbows server.

Thor is used to generate an application — Cramp :: Generators :: Application.start is called in the binary, which sequentially executes all the public methods of the Application class, whose names: create_root, create_config, create_models, speak for themselves.
')
Further, when the server starts, the EventMachine cycle starts, which catches all the events, launching the callbacks attached to them. That is why Cramp only works on Rainbows or Thin (by default), which are built on EM.

The key concept in the Cramp philosophy is Action (classes Action and Abstract). Action is somewhere between the controller and the actual action in Rails (if it is at all appropriate to compare asynchronous and synchronous frameworks). Each action processes one request or, it would be more correct to say, one entry point, because for example a connection through a web socket cannot be called a single request.

A typical query is conditionally divided into 4 stages:

The killer feature of this framework is WebSockets. They are supported out of the box and do not require a separate server. To use web sockets it is enough to create an on_data handler method in your action. All other questions will be decided by the server implementing the receive_data EM method, which is triggered when a message is received from the connection and calls the user handler:

Thin
 callback = @request.env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK] callback.call(data) if callback 


Rainbows
 callback = @env[WEBSOCKET_RECEIVE_CALLBACK] callback.call(data) if callback 


On it we will finish with architecture and we will pass to use.

Using


To work with Cramp, it is recommended to use Ruby branches 1.9 to use faybayra that with the presence of tools such as rvm or rbenv should not be a problem even if you are still sitting on 1.8.7.

Installing Cramp should not cause any difficulties:
 gem install cramp 


Next, run
 cramp new project_name 

and the aforementioned Thor generates the skeleton of your application.
Then, being in the project folder, we execute bundle install, which tightens all dependencies.

We have already created one action - app / actions / home_action.rb, with the following content:
 class HomeAction < Cramp::Action def start render "Hello World!" finish end end 


As it is easy to understand from this code, the start handler forms the answer in the form of the string “Hello World!” And finishes the processing of the request. Check this assumption by running the server:
 bundle exec thin start 

and indeed, everything works as expected. As far as I understand, Thin itself picks up the file config.ru, which lies in the project folder, but for fidelity you can run this command in the following form:
 bundle exec thin --max-persistent-conns 1024 --timeout 0 -R config.ru start 

or
 bundle exec thin --max-persistent-conns 1024 --timeout 0 -V -R config.ru start 

for debugging in verbose mode.

Naturally, we do not need to render static text and html markup directly, for this we must use the views (views) that are in separate files. Create an app / views folder and in it an index.erb file with the following simple content:
 <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Hello World!</title> <link rel="stylesheet" type="text/css" href="stylesheets/styles.css" /> </head> <body> <h1>Hello World!</h1> </body> </html> 


And in the public / stylesheets folder we create the styles.css file with just one line:
 h1{color:red} 


As you can see from the extension, in this example the Erb engine is used, but of course you can use Haml, Slim or whatever your heart desires, after having included the corresponding entry in the Gemfile.

In order for action to render the contents from the erb-file, first we will connect the engine itself:
 require 'erb' 


You can add this line to the action itself, but in order to use Erb in the whole project, it is more convenient to connect it to application.rb.

Next, change home_action.rb:
 class HomeAction < Cramp::Action def start page = ERB.new(File.read(ExampleProject::Application.root('app/views/index.erb'))) render page.result(binding) finish end end 


Here ExampleProject is the name of the project itself. But where is our css? In order for css-files to be available, you need to allow distribution of static content from the folder containing them in the config.ru file:
 use Rack::Static, :urls => ["/javascripts", "/stylesheets"], :root => CrampArticle::Application.root(:public) 


Well, the headline turned red, as expected.
Now let's go ahead and create a new action WsAction, which will communicate with the client via web sockets. First, let's establish which server the web sockets will work on:
 Cramp::Websocket.backend = :thin 


Again, instead of Thin, you can use Rainbows, I personally prefer Thin just because it lies on the Github and the docks on it are more.
Next, in the app / actions folder, create the ws_action.rb file and add the following code to it:
 class WsAction < Cramp::Action self.transport = :websocket on_data :process_data def process_data(data) render "Hello" if /^hello/i =~ data end end 


It is enough to add the line self.transport =: websocket to the class text so that it receives and sends messages via WebSockets. Then we set up a message handler and send a greeting if a message was received starting with “hello”.

To try this in action, first you need to add a route (route) for the WsAction class (config / routes.rb file):
 get('socket/').to(WsAction) 


And secondly, we add to client index.erb a code like this:
 <script type="text/javascript"> var ws = new WebSocket("ws://localhost:3000/socket/"); ws.onopen = function() { ws.send("Hello!"); }; ws.onmessage = function(e) { alert(e.data); }; ws.onclose = function() { alert("closed"); }; </script> 


For Firefox, the latest versions of WebSocket are replaced by MozWebSocket. The client code is very simple and is presented solely for demonstration (like the server one itself), therefore the condition for the web socket class or background on the flash is not included in it, do it yourself.

That's all, if you want to explore Cramp in more depth, there are a few more topics not covered in this article: Server-Side Events, filers, working with ActiveRecord. And of course you can help the development of the project with the help of bug reports and pull requests on the githaba.

Additional links


Project code
github.com/lifo/cramp

Project site (documentation, links to examples)
cramp.in

What is EventMachine and how to use it
github.com/eventmachine/eventmachine/wiki

Websockets with Cramp
vinsol.com/blog/2011/05/09/websockets-with-cramp

Another small article about Cramp and WebSockets
boldr.net/html5-websockets-cramp

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


All Articles