Jekyll2020-07-01T03:38:28+00:00http://subhojit777.in/feed.xmlSubhojit PaulMy experiments with computerSubhojit Paulsubhojitpaul21@gmail.comImplementing WebSockets in Rust2020-07-01T00:00:00+00:002020-07-01T00:00:00+00:00http://subhojit777.in/implementing-websockets-in-rust<p>Recently, I needed WebSockets in my side project. I will share how I implemented it, what I learned.</p>
<h2 id="a-little-about-my-side-project">A little about my side project</h2>
<p>The <a href="https://github.com/subhojit777/questionnaire-backend">Questionnaire</a> improves the experience of the audience during a session. Consider, there is a session going on about “Everything about Demographics”; the presenter asked a question to the audience that “In which continent do you live?” Now, the people will raise hands based on the name mentioned by the presenter one-by-one. A person will not be able to know how many people in the audience belong to continent Asia. The Questionnaire will show the answers in real-time on the presenter screen. It helps to make the session more engaging.</p>
<p>In the Questionnaire, there are two screens - Presenter and Participant. The Presenter screen is where the presenter is showing the slides, and the presenter will show the question there. And, it can be answered from the Participant screen. The audience can access the Participant Screen from their smartphones, basically in a Browser.</p>
<p>The backend of the Questionnaire is in Rust. It exposes web services. The frontend - a React application consumes those services.</p>
<h2 id="where-websocket-comes-into-the-picture">Where WebSocket comes into the picture</h2>
<p>I needed a mechanism so that:</p>
<ol>
<li>The audience sees the options of the same question that the presenter has asked - they are in sync</li>
<li>Show answers real-time on the presenter screen (this feature is still in progress)</li>
</ol>
<p>Please note that I will be using the “presenter + audience sync” feature to demonstrate the implementation of WebSocket in this article.</p>
<h2 id="implementing-the-websocket">Implementing the WebSocket</h2>
<p>The application uses <a href="https://crates.io/crates/actix-web"><code class="language-plaintext highlighter-rouge">actix-web</code></a> for the WebServices. I have used <a href="https://crates.io/crates/actix-broker"><code class="language-plaintext highlighter-rouge">actix-broker</code></a> for the implementation of the WebSocket.</p>
<p><code class="language-plaintext highlighter-rouge">actix-broker</code> facilitates running of the WebSocket in the same thread as the HttpServer. Spawning a new thread for every Presenter Screen, and keeping them in sync was difficult. That is why I chose to run the WebSocket in the same thread. The handling of the WebSocket request and response is asynchronous.</p>
<h3 id="the-websocket-endpoint">The WebSocket endpoint</h3>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">actix_web</span><span class="p">::</span><span class="n">HttpRequest</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">actix_web</span><span class="p">::</span><span class="nn">web</span><span class="p">::{</span><span class="n">Data</span><span class="p">,</span> <span class="n">Payload</span><span class="p">};</span>
<span class="k">use</span> <span class="nn">actix_web</span><span class="p">::{</span><span class="n">get</span><span class="p">,</span> <span class="n">HttpResponse</span><span class="p">};</span>
<span class="k">use</span> <span class="nn">actix_web_actors</span><span class="p">::</span><span class="n">ws</span><span class="p">;</span>
<span class="nd">#[get(</span><span class="s">"/ws/"</span><span class="nd">)]</span>
<span class="k">pub</span> <span class="k">async</span> <span class="k">fn</span> <span class="nf">index</span><span class="p">(</span>
<span class="n">request</span><span class="p">:</span> <span class="n">HttpRequest</span><span class="p">,</span>
<span class="n">stream</span><span class="p">:</span> <span class="n">Payload</span><span class="p">,</span>
<span class="n">pool</span><span class="p">:</span> <span class="n">Data</span><span class="o"><</span><span class="n">Pool</span><span class="o"><</span><span class="n">ConnectionManager</span><span class="o"><</span><span class="n">MysqlConnection</span><span class="o">>>></span><span class="p">,</span>
<span class="p">)</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="n">HttpResponse</span><span class="p">,</span> <span class="nn">actix_web</span><span class="p">::</span><span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="k">let</span> <span class="n">connection</span> <span class="o">=</span> <span class="n">pool</span><span class="nf">.get</span><span class="p">()</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"unable to get database connection"</span><span class="p">);</span>
<span class="k">let</span> <span class="n">response</span> <span class="o">=</span> <span class="nn">ws</span><span class="p">::</span><span class="nf">start</span><span class="p">(</span><span class="nn">WebSocketSession</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">connection</span><span class="p">),</span> <span class="o">&</span><span class="n">request</span><span class="p">,</span> <span class="n">stream</span><span class="p">);</span>
<span class="n">response</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The above is the implementation of the WebSocket endpoint. The client (the React app) tries to connect with the WebSocket via <code class="language-plaintext highlighter-rouge">/ws/</code> endpoint.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">w3cwebsocket</span><span class="p">(</span><span class="dl">'</span><span class="s1">ws://127.0.0.1:8088/ws/</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// 127.0.0.1:8088 is the URL of the Rust HTTPServer.</span>
<span class="nx">client</span><span class="p">.</span><span class="nx">onopen</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">websocket client connected.</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div></div>
<p>During the connection, the application creates a new WebSocket session. There will be multiple sessions for everyone in the audience, including the presenter. It also requires data to be fetched from a database, and therefore, I am passing the database connection as well.</p>
<p>Behind the scenes, the WebSocket session is an <a href="https://actix.rs/docs/whatis"><code class="language-plaintext highlighter-rouge">actor</code></a>. Next, I will show you how it is handled.</p>
<h3 id="handling-of-sessions">Handling of sessions</h3>
<p>The following code snippet is taken from the side-project.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">actix</span><span class="p">::</span><span class="nn">prelude</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">actix_web_actors</span><span class="p">::</span><span class="nn">ws</span><span class="p">::</span><span class="n">WebsocketContext</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">rand</span><span class="p">::</span><span class="nn">prelude</span><span class="p">::</span><span class="n">ThreadRng</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">actix_broker</span><span class="p">::</span><span class="n">BrokerSubscribe</span><span class="p">;</span>
<span class="k">impl</span> <span class="n">Actor</span> <span class="k">for</span> <span class="n">WebSocketSession</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Context</span> <span class="o">=</span> <span class="n">WebsocketContext</span><span class="o"><</span><span class="n">Self</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">started</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">join_session</span> <span class="o">=</span> <span class="nf">JoinSession</span><span class="p">(</span><span class="n">ctx</span><span class="nf">.address</span><span class="p">()</span><span class="nf">.recipient</span><span class="p">());</span>
<span class="nn">WebSocketServer</span><span class="p">::</span><span class="nf">from_registry</span><span class="p">()</span>
<span class="nf">.send</span><span class="p">(</span><span class="n">join_session</span><span class="p">)</span>
<span class="nf">.into_actor</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="nf">.then</span><span class="p">(|</span><span class="n">id</span><span class="p">,</span> <span class="n">act</span><span class="p">,</span> <span class="mi">_</span><span class="n">ctx</span><span class="p">|</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nf">Ok</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="n">id</span> <span class="p">{</span>
<span class="n">act</span><span class="py">.id</span> <span class="o">=</span> <span class="n">id</span><span class="p">;</span>
<span class="p">}</span>
<span class="nn">fut</span><span class="p">::</span><span class="nf">ready</span><span class="p">(())</span>
<span class="p">})</span>
<span class="nf">.wait</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nd">#[derive(Clone,</span> <span class="nd">Message)]</span>
<span class="nd">#[rtype(result</span> <span class="nd">=</span> <span class="s">"usize"</span><span class="nd">)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="nf">JoinSession</span><span class="p">(</span><span class="k">pub</span> <span class="n">Recipient</span><span class="o"><</span><span class="n">Message</span><span class="o">></span><span class="p">);</span>
<span class="nd">#[derive(Message)]</span>
<span class="nd">#[rtype(result</span> <span class="nd">=</span> <span class="s">"()"</span><span class="nd">)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="nf">Message</span><span class="p">(</span><span class="k">pub</span> <span class="nb">String</span><span class="p">);</span>
<span class="nd">#[derive(Default)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="n">sessions</span><span class="p">:</span> <span class="n">HashMap</span><span class="o"><</span><span class="nb">usize</span><span class="p">,</span> <span class="n">Recipient</span><span class="o"><</span><span class="n">Message</span><span class="o">>></span><span class="p">,</span>
<span class="n">rng</span><span class="p">:</span> <span class="n">ThreadRng</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">add_session</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">client</span><span class="p">:</span> <span class="n">Recipient</span><span class="o"><</span><span class="n">Message</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nb">usize</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">id</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="k">self</span><span class="py">.rng</span><span class="nf">.gen</span><span class="p">();</span>
<span class="k">self</span><span class="py">.sessions</span><span class="nf">.insert</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">client</span><span class="p">);</span>
<span class="n">id</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Actor</span> <span class="k">for</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Context</span> <span class="o">=</span> <span class="n">Context</span><span class="o"><</span><span class="n">Self</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">started</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.subscribe_system_async</span><span class="p">::</span><span class="o"><</span><span class="n">SendMessage</span><span class="o">></span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Handler</span><span class="o"><</span><span class="n">JoinSession</span><span class="o">></span> <span class="k">for</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Result</span> <span class="o">=</span> <span class="n">MessageResult</span><span class="o"><</span><span class="n">JoinSession</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">handle</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">JoinSession</span><span class="p">,</span> <span class="mi">_</span><span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">let</span> <span class="nf">JoinSession</span><span class="p">(</span><span class="n">client</span><span class="p">)</span> <span class="o">=</span> <span class="n">msg</span><span class="p">;</span>
<span class="k">let</span> <span class="n">id</span> <span class="o">=</span> <span class="k">self</span><span class="nf">.add_session</span><span class="p">(</span><span class="n">client</span><span class="p">);</span>
<span class="nf">MessageResult</span><span class="p">(</span><span class="n">id</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">impl Actor for WebSocketSession {}</code> - This defines the behaviour of WebSocket session. When the session starts, it triggers the <code class="language-plaintext highlighter-rouge">JoinSession</code>. <code class="language-plaintext highlighter-rouge">WebSocketServer</code> is another Actor which keeps track of the sessions. You will notice <code class="language-plaintext highlighter-rouge">self.subscribe_system_async::<SendMessage>(ctx);</code> inside the implementation of the server - I will come to it later.</p>
<p>The new session is pushed to a <code class="language-plaintext highlighter-rouge">HashMap</code> when <code class="language-plaintext highlighter-rouge">JoinSession</code> is triggered.</p>
<h3 id="making-sure-presenter-and-audience-are-in-sync">Making sure presenter and audience are in sync</h3>
<p>The following code demonstrates how I am making sure that the audience sees the options of the same question that the presenter has asked.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">actix_broker</span><span class="p">::</span><span class="n">BrokerIssue</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">actix_broker</span><span class="p">::</span><span class="n">BrokerSubscribe</span><span class="p">;</span>
<span class="k">impl</span> <span class="n">Actor</span> <span class="k">for</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Context</span> <span class="o">=</span> <span class="n">Context</span><span class="o"><</span><span class="n">Self</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">started</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.subscribe_system_async</span><span class="p">::</span><span class="o"><</span><span class="n">SendMessage</span><span class="o">></span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">send_message</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">message</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="mi">_</span><span class="n">id</span><span class="p">,</span> <span class="n">recipient</span><span class="p">)</span> <span class="n">in</span> <span class="o">&</span><span class="k">self</span><span class="py">.sessions</span> <span class="p">{</span>
<span class="n">recipient</span>
<span class="nf">.do_send</span><span class="p">(</span><span class="nf">Message</span><span class="p">(</span><span class="n">message</span><span class="nf">.to_owned</span><span class="p">()))</span>
<span class="nf">.expect</span><span class="p">(</span><span class="s">"Could not send message to the client."</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Handler</span><span class="o"><</span><span class="n">SendMessage</span><span class="o">></span> <span class="k">for</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Result</span> <span class="o">=</span> <span class="p">();</span>
<span class="k">fn</span> <span class="nf">handle</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">SendMessage</span><span class="p">,</span> <span class="mi">_</span><span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="nf">.send_message</span><span class="p">(</span><span class="n">msg</span><span class="py">.content</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">WebSocketSession</span> <span class="p">{</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">send_msg</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">SendMessage</span> <span class="p">{</span>
<span class="n">name</span><span class="p">:</span> <span class="k">self</span><span class="py">.name</span><span class="nf">.clone</span><span class="p">(),</span>
<span class="n">id</span><span class="p">:</span> <span class="k">self</span><span class="py">.id</span><span class="p">,</span>
<span class="n">content</span><span class="p">:</span> <span class="n">msg</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">self</span><span class="nf">.issue_system_async</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">StreamHandler</span><span class="o"><</span><span class="n">Result</span><span class="o"><</span><span class="nn">ws</span><span class="p">::</span><span class="n">Message</span><span class="p">,</span> <span class="n">ProtocolError</span><span class="o">>></span> <span class="k">for</span> <span class="n">WebSocketSession</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">handle</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">item</span><span class="p">:</span> <span class="n">Result</span><span class="o"><</span><span class="nn">ws</span><span class="p">::</span><span class="n">Message</span><span class="p">,</span> <span class="n">ProtocolError</span><span class="o">></span><span class="p">,</span> <span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">match</span> <span class="n">item</span> <span class="p">{</span>
<span class="nf">Ok</span><span class="p">(</span><span class="nf">Ping</span><span class="p">(</span><span class="mi">_</span><span class="n">msg</span><span class="p">))</span> <span class="k">=></span> <span class="p">{</span> <span class="cm">/* Skipped for simplifcation. */</span> <span class="p">}</span>
<span class="nf">Ok</span><span class="p">(</span><span class="nf">Pong</span><span class="p">(</span><span class="mi">_</span><span class="n">msg</span><span class="p">))</span> <span class="k">=></span> <span class="p">{</span> <span class="cm">/* Skipped for simplifcation. */</span> <span class="p">}</span>
<span class="nf">Ok</span><span class="p">(</span><span class="nf">Text</span><span class="p">(</span><span class="n">text</span><span class="p">))</span> <span class="k">=></span> <span class="p">{</span>
<span class="c">// Presenter (the client) wants to show the next question.</span>
<span class="c">// Client has sent the request to navigate.</span>
<span class="c">// Retrieve the next question's data from database, and notify the audience sessions</span>
<span class="c">// to shift to the next question's options.</span>
<span class="k">let</span> <span class="n">response</span> <span class="o">=</span> <span class="n">WebSocketResponse</span> <span class="p">{</span> <span class="n">new_question_index</span> <span class="p">};</span>
<span class="k">self</span><span class="nf">.send_msg</span><span class="p">(</span><span class="nn">serde_json</span><span class="p">::</span><span class="nf">to_string</span><span class="p">(</span><span class="o">&</span><span class="n">response</span><span class="p">)</span><span class="nf">.expect</span><span class="p">(</span><span class="s">"Could not parse to JSON."</span><span class="p">));</span>
<span class="p">}</span>
<span class="mi">_</span> <span class="k">=></span> <span class="n">ctx</span><span class="nf">.stop</span><span class="p">(),</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The application is listening to any messages from a session - in this case, the presenter’s session. For simplification, I have skipped the gory details about how the next question’s data is retrieved. Notice that, it is just calling <code class="language-plaintext highlighter-rouge">WebSocketSession</code>’s <code class="language-plaintext highlighter-rouge">send_msg</code>.</p>
<p>Using <code class="language-plaintext highlighter-rouge">actix-broker</code>, the <code class="language-plaintext highlighter-rouge">WebSocketServer</code> has subscribed to the <code class="language-plaintext highlighter-rouge">SendMessage</code> - which is responsible for making sure that every Participant Screens show the next question’s options.</p>
<p>Inside <code class="language-plaintext highlighter-rouge">send_message</code>, I am looping through every active session, and sending the response to update the screens.</p>
<h3 id="handle-cleanups">Handle cleanups</h3>
<p>A person from the audience may wish to no longer participate in the session and closes one’s screen. The application needs to make sure that it does not sends the response to that closed session.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Clone,</span> <span class="nd">Message)]</span>
<span class="nd">#[rtype(result</span> <span class="nd">=</span> <span class="s">"()"</span><span class="nd">)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="nf">RemoveSession</span><span class="p">(</span><span class="k">pub</span> <span class="nb">usize</span><span class="p">);</span>
<span class="k">impl</span> <span class="n">Actor</span> <span class="k">for</span> <span class="n">WebSocketSession</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Context</span> <span class="o">=</span> <span class="n">WebsocketContext</span><span class="o"><</span><span class="n">Self</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">stopped</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="mi">_</span><span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">remove_session</span> <span class="o">=</span> <span class="nf">RemoveSession</span><span class="p">(</span><span class="k">self</span><span class="py">.id</span><span class="p">);</span>
<span class="k">self</span><span class="nf">.issue_system_async</span><span class="p">(</span><span class="n">remove_session</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Actor</span> <span class="k">for</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Context</span> <span class="o">=</span> <span class="n">Context</span><span class="o"><</span><span class="n">Self</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">started</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.subscribe_system_async</span><span class="p">::</span><span class="o"><</span><span class="n">RemoveSession</span><span class="o">></span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">remove_session</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">session_id</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.sessions</span><span class="nf">.remove</span><span class="p">(</span><span class="o">&</span><span class="n">session_id</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="n">Handler</span><span class="o"><</span><span class="n">RemoveSession</span><span class="o">></span> <span class="k">for</span> <span class="n">WebSocketServer</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Result</span> <span class="o">=</span> <span class="n">MessageResult</span><span class="o"><</span><span class="n">RemoveSession</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="nf">handle</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">RemoveSession</span><span class="p">,</span> <span class="mi">_</span><span class="n">ctx</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">Self</span><span class="p">::</span><span class="n">Context</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">let</span> <span class="nf">RemoveSession</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="n">msg</span><span class="p">;</span>
<span class="k">self</span><span class="nf">.remove_session</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
<span class="nf">MessageResult</span><span class="p">(())</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The application is aware of any session being closed. Same as <code class="language-plaintext highlighter-rouge">started</code>, the <code class="language-plaintext highlighter-rouge">stopped</code> handles the closing of a <code class="language-plaintext highlighter-rouge">WebSocketSession</code>. There, I am issuing a <code class="language-plaintext highlighter-rouge">RemoveSession</code> event, which in turn removes the session <code class="language-plaintext highlighter-rouge">id</code> from the <code class="language-plaintext highlighter-rouge">HashMap</code>.</p>
<h2 id="closing-note">Closing Note</h2>
<p>I got a lot of help from the <a href="https://github.com/actix/examples">actix examples</a> - <a href="https://github.com/actix/examples/tree/master/websocket-chat">websocket-chat</a> and <a href="https://github.com/actix/examples/tree/master/websocket-chat-broker">websocket-chat-broker</a>. Initially, I was following <code class="language-plaintext highlighter-rouge">websocket-chat</code>, and I realised that it runs the WebSocket in a separate thread. Keeping the sessions in sync was difficult. That is when I referred <code class="language-plaintext highlighter-rouge">websocket-chat-broker</code>.</p>
<p>The <a href="https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb">Rust Debugger</a> in VSCode was super useful. I was not cleaning up the sessions initially, and it resulted in WebSocket crashes. I was able to find out that the WebSocket server is trying to send data to an invalid WebSocket session, and that is why it is crashing.</p>
<h2 id="credits">Credits</h2>
<p>Thanks to <a href="https://www.linkedin.com/in/shanlalit">Lalit Shandilya</a> for his idea about this project.</p>
<p>Thanks to <a href="https://www.linkedin.com/in/skippednote">Bassam Ismail</a> for his suggestion about using WebSockets for keeping the presenter and audience screens in sync.</p>Subhojit PaulRecently, I needed WebSockets in my side project. I will share how I implemented it, what I learned.Tribute to Mark Hollis2019-05-31T00:00:00+00:002019-05-31T00:00:00+00:00http://subhojit777.in/mark-hollis-dead<p><a href="../images/mark_hollis/Mark_Hollis_1988.jpg"><img src="../images/mark_hollis/Mark_Hollis_1988.jpg" alt="Mark Hollis" /></a></p>
<p><em>Source: <a href="https://en.wikipedia.org/wiki/Mark_Hollis_(musician)">Wikipedia</a></em></p>
<p>Just today I learned that Mark Hollis is dead. I was just browsing on <a href="https://www.imdb.com/">IMDb</a>, and I decided to see his profile where I learned that he died on 25th February 2019. I was so feeling so low after reading about his death in <a href="https://en.wikipedia.org/wiki/Mark_Hollis_(musician)">Wikipedia</a>.</p>
<p>Only a few days ago I was listening to his album <a href="https://en.wikipedia.org/wiki/Laughing_Stock">Laughing Stock</a>, and I was telling my wife that this album is so good, that I make sure I am focussed to its music - not treating it as background music. I feel that, if I use it as background music I am disrespecting the music, the album and Mark Hollis himself.</p>
<p>He was an inspiration to me. Besides his music, the most fascinating thing I liked about him is that he didn’t pursue money and fame. I liked how he slowly changed the music style in later years of <a href="https://en.wikipedia.org/wiki/Talk_Talk">Talk Talk</a>, how he chose to make artful music - rather than making music for the masses.</p>
<p>I was so much hoping that someday he would make a comeback, but then he died…</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/gdAB4ZD98zM" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen=""></iframe>Subhojit PaulMy experience with Rust2019-02-20T00:00:00+00:002019-02-20T00:00:00+00:00http://subhojit777.in/rust-language-experience<p>I started learning <a href="https://www.rust-lang.org">Rust</a> in 2018. I completed my work in an <a href="https://github.com/subhojit777/dc_ajax_add_cart">open source project</a> and was thinking about learning a new programming language. My motive was to learn a language that allows you to control the lower level of a <a href="https://en.wikipedia.org/wiki/High-level_programming_language">high-level programming language</a>. I considered learning Golang, but, in most of the online articles I learned that Rust (being a system programming language) gives you more control than Go, however, the learning curve is far steeper than Go. I had no worries about deadlines or time, therefore I chose Rust.</p>
<h2 id="learning-rust">Learning Rust</h2>
<p>I started learning Rust by following <a href="https://doc.rust-lang.org/book">“The Book”</a>. I found that there are certain concepts, (like, lifetime), which are explained later in the book but should have been explained before. I was working on an exercise and spent around a month battling with the compiler errors. I referred <a href="http://shop.oreilly.com/product/0636920040385.do">Programming Rust</a> and found that it has focussed more on the concepts, and explained them very well. I soon got unblocked in the exercise. However, recently, I was referring to the 2018 version of the book and found that it has much improved. It is up to date with the recent developments in Rust. I finished the books, and the outcome was <a href="https://github.com/subhojit777/minigrep">minigrep</a>.</p>
<h2 id="post-learning">Post-learning</h2>
<p>I always wanted to write a PHP parser. After completing the books, I started looking into writing parsers in Rust. I came across <a href="https://github.com/tagua-vm/parser">tagua-vm parser</a> and <a href="https://github.com/Geal/nom">nom</a>. I tried to parse a simple PHP script. But, it was too difficult. Parsers are completely new to me. I faced difficulty in installing tagua-vm parser, and it was not updated as per the recent development in Rust compiler and nom. I tried to fix them, but quickly got overwhelmed.</p>
<p>Instead of writing something which I don’t know about, I thought about writing something in Rust which I am already good at - the web! I worked on a <a href="https://github.com/subhojit777/questionnaire">hobby project</a> which is written in JavaScript. I decided to write the backend in Rust. I am still working on it. The progress is slow, but I am seeing improvements. I am getting better at analyzing and fixing the compiler errors. <a href="http://diesel.rs">diesel.rs</a> and <a href="https://actix.rs">actix.rs</a> are very well documented, and the community is active and helpful - that is helping me a lot.</p>
<h2 id="choosing-ides">Choosing IDEs</h2>
<p>I started writing Rust in Vim. I used the plugin <a href="https://github.com/rust-lang/rust.vim">rust.vim</a>. However, I found that looking up functions and <code class="language-plaintext highlighter-rouge">struct</code>s were slow. I tried VSCode with <a href="https://marketplace.visualstudio.com/items?itemName=rust-lang.rust">rls</a>, it was better than rust.vim, but the autocompletion was not working well. I tried IntelliJ IDEA with <a href="https://intellij-rust.github.io">IntelliJ Rust</a>. It worked better than the other two. The lookups were fine, autocompletion was smoother than rls. I liked how it shows the inferred types, I found it quite helpful. I decided to stick with IntelliJ IDEA.</p>
<h2 id="challenges-and-difficulties">Challenges and difficulties</h2>
<h3 id="lifetimes-traits-generic-types">Lifetimes, traits, generic types</h3>
<p>I am originally a PHP developer, working on a framework called <a href="https://drupal.org">Drupal</a> full time. Lifetimes, generic types, traits, etc. (I can go on an on) are completely new to me. The Rust syntax looked intimidating at first, with all those lifetimes and generic types, but I am getting better at reading and understanding Rust code.</p>
<h3 id="battle-with-compiler">Battle with compiler</h3>
<p>Analyzing the compiler errors, and try to understand the core problem - I think this is an important skill you need if you are learning Rust.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">authorize_get</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="o">&</span><span class="n">HttpRequest</span><span class="o"><</span><span class="n">AppState</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nb">Box</span><span class="o"><</span><span class="n">Future</span><span class="o"><</span><span class="n">Item</span> <span class="o">=</span> <span class="n">HttpResponse</span><span class="p">,</span> <span class="n">Error</span> <span class="o">=</span> <span class="n">Error</span><span class="o">>></span> <span class="p">{</span>
<span class="k">let</span> <span class="n">state</span> <span class="o">=</span> <span class="n">req</span><span class="nf">.state</span><span class="p">()</span><span class="nf">.clone</span><span class="p">();</span>
<span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">req</span><span class="nf">.oauth2</span><span class="p">()</span>
<span class="nf">.authorization_code</span><span class="p">(</span><span class="n">handle_get</span><span class="p">)</span>
<span class="nf">.and_then</span><span class="p">(</span><span class="k">move</span> <span class="p">|</span><span class="n">request</span><span class="p">|</span> <span class="n">state</span>
<span class="py">.endpoint</span>
<span class="nf">.send</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="nf">.map_err</span><span class="p">(|</span><span class="mi">_</span><span class="p">|</span> <span class="nn">OAuthError</span><span class="p">::</span><span class="n">InvalidRequest</span><span class="p">)</span>
<span class="nf">.and_then</span><span class="p">(|</span><span class="n">result</span><span class="p">|</span> <span class="n">result</span><span class="nf">.map</span><span class="p">(</span><span class="nn">Into</span><span class="p">::</span><span class="n">into</span><span class="p">))</span>
<span class="p">)</span>
<span class="nf">.or_else</span><span class="p">(|</span><span class="n">err</span><span class="p">|</span> <span class="nf">Ok</span><span class="p">(</span><span class="nn">ResolvedResponse</span><span class="p">::</span><span class="nf">response_or_error</span><span class="p">(</span><span class="n">err</span><span class="p">)</span><span class="nf">.actix_response</span><span class="p">()))</span>
<span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The above code gave a compiler error, that it is unable to analyze the lifetime of the response and it is incorrect. I think I spent around a month chasing the error and trying to fix it. Finally, I managed to fix it with:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">authorize_get</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="o">&</span><span class="n">HttpRequest</span><span class="o"><</span><span class="n">AppState</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nb">Box</span><span class="o"><</span><span class="n">Future</span><span class="o"><</span><span class="n">Item</span> <span class="o">=</span> <span class="n">HttpResponse</span><span class="p">,</span> <span class="n">Error</span> <span class="o">=</span> <span class="n">Error</span><span class="o">>></span> <span class="p">{</span>
<span class="k">let</span> <span class="n">state</span><span class="p">:</span> <span class="n">AppState</span> <span class="o">=</span> <span class="n">req</span><span class="nf">.state</span><span class="p">()</span><span class="nf">.clone</span><span class="p">();</span>
<span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">req</span><span class="nf">.oauth2</span><span class="p">()</span>
<span class="nf">.authorization_code</span><span class="p">(</span><span class="n">handle_get</span><span class="p">)</span>
<span class="nf">.and_then</span><span class="p">(</span><span class="k">move</span> <span class="p">|</span><span class="n">request</span><span class="p">|</span> <span class="n">state</span>
<span class="py">.endpoint</span>
<span class="nf">.send</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
<span class="nf">.map_err</span><span class="p">(|</span><span class="mi">_</span><span class="p">|</span> <span class="nn">OAuthError</span><span class="p">::</span><span class="n">InvalidRequest</span><span class="p">)</span>
<span class="nf">.and_then</span><span class="p">(|</span><span class="n">result</span><span class="p">|</span> <span class="n">result</span><span class="nf">.map</span><span class="p">(</span><span class="nn">Into</span><span class="p">::</span><span class="n">into</span><span class="p">))</span>
<span class="p">)</span>
<span class="nf">.or_else</span><span class="p">(|</span><span class="n">err</span><span class="p">|</span> <span class="nf">Ok</span><span class="p">(</span><span class="nn">ResolvedResponse</span><span class="p">::</span><span class="nf">response_or_error</span><span class="p">(</span><span class="n">err</span><span class="p">)</span><span class="nf">.actix_response</span><span class="p">()))</span>
<span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The only difference is:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">state</span><span class="p">:</span> <span class="n">AppState</span> <span class="o">=</span> <span class="n">req</span><span class="nf">.state</span><span class="p">()</span><span class="nf">.clone</span><span class="p">();</span>
</code></pre></div></div>
<p>The compiler was incorrectly inferring the type of <code class="language-plaintext highlighter-rouge">state</code>, and explicitly mentioning the type fixed the problem. I believe the compiler was telling the high-level problem, and I was unable to understand the core issue. Rust is getting better at compiler error messages. Hopefully, I will get better at understanding compiler errors with time. The fully documented <a href="https://doc.rust-lang.org/error-index.html">error index</a> also helped me many times.</p>
<h3 id="wrong-decision">Wrong decision</h3>
<p>When I started writing a parser in Rust, the concept about parsers was completely new to me, plus the whole new Rust syntax and things got complex very quickly. I had to abandon it. Instead, I have started writing the backend of one of my past <a href="https://github.com/subhojit777/questionnaire">hobby project</a> in Rust. Earlier it was written in JavaScript, the project is web-based which I am already good at, and I can only focus on Rust.</p>
<h2 id="things-i-liked-loved-in-rust">Things I <del>liked</del> loved in Rust</h2>
<h3 id="concept-of-lifetime">Concept of lifetime</h3>
<p>Rust being a low-level language, it does not supports <a href="https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)">GC</a>, and you have to take care of the scope of variables. I have so far worked with high-level programming languages, which are GC enabled. I found this change difficult to understand at first, but slowly realized its usefulness. Taking care of the scope, might not be relevant in high-level languages, but it makes you understand how programming languages handle acquiring and releasing memory.</p>
<h3 id="strict-typing">Strict typing</h3>
<p>This might not sound new to everyone. I am full time working with PHP, and in the major part of my career, I worked with PHP 5.x. PHP 5.x does not support strict typing, but things are improving in PHP 7.x. Rust follows strict typing discipline and I found it very useful. It makes sure that the data always has the correct type, and you don’t have to worry about incorrect type coercions.</p>
<h3 id="raise-problems-beforehand-rather-than-fixing-it-later">Raise problems beforehand, rather than fixing it later</h3>
<p>In brief, “Rust does not allow you to execute bad code”.</p>
<p>The necessary checks like - lifetimes, <code class="language-plaintext highlighter-rouge">impl</code>-mentation of <code class="language-plaintext highlighter-rouge">trait</code>s if you have used any, strict typing, etc. - all of these prevent you to write bad code. In the example, I mentioned before, although the compiler error was cryptic, it pointed out that you have to explicitly mention the type. However, in most high-level languages, it automatically does the inference (for the sake of simplicity) and that’s when shit happens.</p>
<p>TBH I am still fighting with such strict-ness of Rust, and sometimes find it frustrating - but I have found it interesting at the same time. Also, this has inspired me to write code in a similar way in my job.</p>
<h3 id="helpful-community">Helpful community</h3>
<p>The community is full of smart and helpful people. The language is growing, it has a wide scope - from embedded to the web, and slowly getting accepted in <a href="https://www.rust-lang.org/production">major organizations</a>, there are many actively maintained libraries and I have gotten fast and helpful response for my queries.</p>
<h2 id="whats-next">What’s next</h2>
<p>Due to its benefits, I would like to continue learning and writing code in Rust. It has a steep learning curve, battling with compiler sometimes becomes frustrating, but I believe that it will help me to become a better programmer. Currently, I am writing a <a href="https://github.com/subhojit777/questionnaire-rs">hobby project</a> in Rust, but I would very much like to write Rust as part of my job.</p>Subhojit PaulI started learning Rust in 2018. I completed my work in an open source project and was thinking about learning a new programming language. My motive was to learn a language that allows you to control the lower level of a high-level programming language. I considered learning Golang, but, in most of the online articles I learned that Rust (being a system programming language) gives you more control than Go, however, the learning curve is far steeper than Go. I had no worries about deadlines or time, therefore I chose Rust.Commerce Ajax Add to Cart module porting takeaways2018-04-07T00:00:00+00:002018-04-07T00:00:00+00:00http://subhojit777.in/dc_ajax_add_cart-porting-takeaways<p>Recently I completed Drupal 8 porting of Commerce Ajax Add to Cart module -
<a href="https://www.drupal.org/project/dc_ajax_add_cart/releases/8.x-1.0-beta1"><code class="language-plaintext highlighter-rouge">8.x-1.0-beta1</code></a>.
See my <a href="https://twitter.com/_subhojit_paul/status/978158611913498624">tweet</a>. I have
been working on this for around a year.</p>
<h2 id="objective">Objective</h2>
<p>The main objective of doing the port is to learn the concepts of Drupal 8. Make this
module available for Drupal 8. And also, open sourcing - giving back to the
community.</p>
<h2 id="takeaways">Takeaways</h2>
<p>I got the chance to do things hands-on which I only knew in theory.</p>
<p>Here I am talking about concepts
<a href="https://en.wikipedia.org/wiki/Dependency_injection">Dependency Injection</a>,
<a href="https://en.wikipedia.org/wiki/Test-driven_development">Test Driven Development</a>
which I only knew theoretically.</p>
<p>Being more of a hands-on guy, I am not satisfied by just reading the theory, I
get an itch to do that practically as well.</p>
<h3 id="object-oriented-programming-in-php">Object Oriented Programming in PHP</h3>
<p>I knew the basics of OOP in PHP. But while I was working, I frequently referred
the code written in the <a href="https://github.com/drupalcommerce/commerce">Commerce core</a>,
and it heavily inspired me. The core uses the
<a href="http://www.phptherightway.com/pages/Design-Patterns.html">strategy pattern</a>.
And it felt very clean to me - You use an <code class="language-plaintext highlighter-rouge">interface</code> to define the behavior of
a <code class="language-plaintext highlighter-rouge">class</code>. And you can utilize the benefits of
<a href="http://php.net/language.oop5.typehinting">type hinting</a> using the <code class="language-plaintext highlighter-rouge">interface</code>.</p>
<h3 id="dependency-injection-in-php">Dependency Injection in PHP</h3>
<p>I read about the Dependency Injection concept but never got the chance to
implement it. While porting the module, I used
<a href="https://www.drupal.org/docs/8/api/services-and-dependency-injection/services-and-dependency-injection-in-drupal-8">services</a> (also see <a href="https://symfony.com/doc/current/service_container.html">Symfony services</a>)
to make commonly used methods available across the module, and they will be
loaded at the time of object instantiation.</p>
<p>I am a fan of <a href="https://en.wikipedia.org/wiki/Loose_coupling">loosely coupled architecture</a>.
Dependency Injection helps you to make your code more loosely coupled and clean.
It helps you maintain the state of the execution, and is more OOP aligned.</p>
<p>Acquia’s article <a href="https://docs.acquia.com/articles/drupal-8-dependency-injection">Dependency Injection</a>
mentions the benefits of using services and loading them via Dependency
Injection.</p>
<h3 id="test-driven-development-tdd">Test Driven Development (TDD)</h3>
<p>I have so much read about TDD, and this time I got the chance to follow the
process.</p>
<p>At the initial stages of the porting, since because Drupal 8 and its test
framework was not very much clear to me, thus it wasn’t possible to follow the
TDD process.</p>
<p>Once the base of the module (with tests) was complete, I started following
TDD. I followed the <a href="https://en.wikipedia.org/wiki/Test-driven_development#Test-driven_development_cycle">TDD cycle</a>
while adding the features/integrations in the module.</p>
<p>I now feel more confident following the TDD process :smile:</p>
<hr />
<p>Although there are still many bugs in the module for a stable release, still I
learned a lot.</p>Subhojit PaulRecently I completed Drupal 8 porting of Commerce Ajax Add to Cart module - 8.x-1.0-beta1. See my tweet. I have been working on this for around a year.Run Nightwatch.js tests parallelly in Docker2017-05-23T00:00:00+00:002017-05-23T00:00:00+00:00http://subhojit777.in/parallel-nightwatch-tests-docker<p><a href="http://nightwatchjs.org">Nightwatch.js</a> is a tool used for automated browser,
it is built in Node.js. Recently I had setup GitLab CI which will run
Nightwatch.js tests on every push to <code class="language-plaintext highlighter-rouge">master</code> branch.</p>
<p>It is very common that the acceptance tests are slow. Therefore, after setting
up the CI, my next requirement was to run the tests parallelly. This was
challenging, given that I am very much new to Docker. Finally, I figured it
out.</p>
<h3 id="how-to-run-nightwatchjs-in-parallel-mode">How to run Nightwatch.js in parallel mode</h3>
<p>You can either execute the tests in parallel by <code class="language-plaintext highlighter-rouge">test_workers</code> setting. Or you
can specify multiple environments while running the tests. Read more about
running tests in the parallel mode <a href="https://github.com/nightwatchjs/nightwatch-docs/blob/master/guide/running-tests/run-parallel.md">here</a>.</p>
<h3 id="how-parallel-mode-in-nightwatchjs-works">How parallel mode in Nightwatch.js works</h3>
<p>The basic idea is - it will create a child process for every test. Every child
process is assigned a browser instance. The parent process is the process that
executes the main <code class="language-plaintext highlighter-rouge">nightwatch</code> command. The parent process will wait for the
child processes (or tests) to complete, and in the end it will show you the
complete output of all the child processes.</p>
<hr />
<p>Earlier I was using
<a href="https://github.com/subhojit777/nightwatch"><code class="language-plaintext highlighter-rouge">subhojit777/nightwatch</code></a> setup to
run the tests. It was working alright until the requirement of parallel
running came. The setup was not able to spawn multiple instances of the browser,
hence the tests were completing within a few seconds without showing any output.</p>
<p>I wrote a <a href="https://docs.docker.com/compose/overview">Docker Compose</a> script that
uses official Selenium images to configure a Selenium Grid and connect it to
Chrome browser that is going to run the tests.</p>
<h3 id="the-docker-composeyml">The <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>:</h3>
<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>
<span class="na">services</span><span class="pi">:</span>
<span class="na">hub</span><span class="pi">:</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">selenium/hub:latest</span>
<span class="na">ports</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">4444:4444</span>
<span class="na">chromedriver</span><span class="pi">:</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">selenium/standalone-chrome:latest</span>
<span class="na">environment</span><span class="pi">:</span>
<span class="na">HUB_PORT_4444_TCP_ADDR</span><span class="pi">:</span> <span class="s">hub</span>
<span class="na">HUB_PORT_4444_TCP_PORT</span><span class="pi">:</span> <span class="m">4444</span>
<span class="na">depends_on</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">hub</span>
<span class="na">nightwatch</span><span class="pi">:</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">subhojit777/nightwatch:latest</span>
<span class="na">environment</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">WAIT_FOR_HOSTS=chromedriver:4444</span>
<span class="na">depends_on</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">hub</span>
<span class="pi">-</span> <span class="s">chromedriver</span>
<span class="na">links</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">chromedriver</span>
<span class="na">volumes</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">./tests:/home/node</span>
</code></pre></div></div>
<h3 id="sample-nightwatchjson">Sample <code class="language-plaintext highlighter-rouge">nightwatch.json</code>:</h3>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"src_folders"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"tests"</span><span class="p">],</span><span class="w">
</span><span class="nl">"output_folder"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"reports"</span><span class="p">,</span><span class="w">
</span><span class="nl">"test_settings"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"default"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"launch_url"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"http://site.local"</span><span class="p">,</span><span class="w">
</span><span class="nl">"selenium_port"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mi">4444</span><span class="p">,</span><span class="w">
</span><span class="nl">"selenium_host"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"chromedriver"</span><span class="p">,</span><span class="w">
</span><span class="nl">"screenshots"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"enabled"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"on_failure"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"on_error"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"path"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"screenshots"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"desiredCapabilities"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"browserName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"chrome"</span><span class="p">,</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"prod"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"launch_url"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.myprodsite.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"filter"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/*-prod.js"</span><span class="p">,</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"stg"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"launch_url"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.mystgsite.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"filter"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/*-stg.js"</span><span class="p">,</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"dev"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"launch_url"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.mydevsite.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"filter"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"tests/*-dev.js"</span><span class="p">,</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>I am assuming that the Nightwatch.js tests of your application are inside
<code class="language-plaintext highlighter-rouge">tests</code> directory.</p>
<p>Just place the <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file inside the docroot of your application.
Make sure the tests are inside <code class="language-plaintext highlighter-rouge">tests</code> directory, and the tests directory should
be in docroot. Otherwise, you can change the <code class="language-plaintext highlighter-rouge">volumes</code> entry as per your setup.</p>
<p>Let’s assume that you have placed <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> in the correct location
and the tests are in the right place, execute this command from your application
docroot:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose run <span class="nt">--rm</span> nightwatch <span class="nt">--env</span> prod,stg,dev
</code></pre></div></div>
<p>And the setup will execute production, staging and dev tests parallelly. Say,
dev takes the most time 10 minutes to complete the test, so the process will run
for 10 minutes, and after that it will show you the result of the tests as
output.</p>
<p>And don’t forget to clean up.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose down <span class="nt">-v</span>
</code></pre></div></div>Subhojit PaulNightwatch.js is a tool used for automated browser, it is built in Node.js. Recently I had setup GitLab CI which will run Nightwatch.js tests on every push to master branch.Open source contribution along with your job2017-04-14T00:00:00+00:002017-04-14T00:00:00+00:00http://subhojit777.in/opensource-with-work<p>Doing open source contribution can be, writing a Node.js module, a PHP library, help fix bugs in another library, or even helping in writing documentation. Suppose, you have written a new module, and it is working and you release it. But then you have to reply to the issues, fix bugs, add new features, do code refactoring, etc. When it comes to maintenance, it becomes difficult. You have to do your work for which you are getting paid for (regular job), spend time with family, spend time for yourself, <em>and</em> maintain the module. Carrying on like this, you don’t get enough time to maintain it, and finally, you lose interest in it.</p>
<p>I too had this problem. And, I found a way.</p>
<blockquote>
<p>Use the first hour of every weekday.</p>
</blockquote>
<p>I get up around 8:30, so by 10 I am at my working desk. Starting from 10 till 11, that one hour is completely dedicated to open source. I am doing this for around 3 months, and it is helping. I have been working on a <a href="https://github.com/subhojit777/listjs">Drupal module</a> for these 3 months, giving the first hour every weekday. And, I am seeing the change, the module is in much better condition now. A few days ago, I released the 8.x version of the module.</p>
<blockquote>
<p>Why the first hour?</p>
</blockquote>
<p><strong>Because that is the hour when your brain is the freshest.</strong> You can easily concentrate. For best results, do not open personal or company mail, Slack, etc. Just sit down with a cup of coffee, boot your computer, <code class="language-plaintext highlighter-rouge">cd</code> into the directory containing the code, open the editor, and start working on the next feature.</p>
<p>By doing this you will give 5 hours every week to the project. 5 hours is not bad, it is enough to add a new feature over a week, fix bugs and replying to issues.</p>
<p>I got this idea from some of my friends who are also into technology, and they love it.</p>
<hr />
<p>If your open source project is a library in a language that you know very well, then this <em>first-hour</em> trick will work. But, if your project is more of learning a new language, then the <em>first-hour</em> may not work because you will have to invest more hours in it. In that case, try to take some time during the weekend and learn the language, once you get a good grasp, you can then turn to the <em>first-hour</em> rule.</p>
<p>You can also extend the <em>first-hour</em> to “first two hours”, but that depends entirely on you.</p>
<hr />
<h3 id="past-experiments">Past experiments</h3>
<p>Before, I used to do the exact opposite of what I am doing now. I used to spend the last hour of my day doing open source. It was not possible for me to give the first hour of the day, because, I had to travel to my workplace and attend stand up calls.</p>
<p>At the end of the day, my brain used to be like numb, I can hardly do any logical thinking. The <em>last hour</em> trick never worked for me.</p>
<hr />
<p>Once you build a project and get it done, the real effort is to maintain it. Normally, the tendency of a developer is always to go for “new and shiny things”, and leave what he is good at. But believe me, you have to resist that temptation :smiley:</p>
<p>If you are lucky then you will get paid for your open source contribution. In my case, <a href="https://www.acromediainc.com">Acro Media</a> funded the 8.x release of the Drupal module. But at some point, you have to do it on your own, and that is when the <em>first-hour</em> trick works.</p>
<h3 id="final-words">Final words</h3>
<p>I am sure, I will be a good maintainer if I carry on like this. The thing is - I am a single man now. And this trick will be put to the real test when I will have a family to look after :smile:</p>
<h3 id="credits">Credits</h3>
<p>Thanks to <a href="https://twitter.com/vinitkme">Vinit Kumar</a> and <a href="https://twitter.com/Ashish_Thakur">Ashish Thakur</a> for their ideas :+1:</p>
<p>Got inspiration from <a href="https://twitter.com/geerlingguy">Jeff Geerling</a>. I am amazed the effort he puts to maintain <a href="https://github.com/geerlingguy/drupal-vm">Drupal VM</a>, you create a new issue, and within a day he will reply you. I got the idea of giving 5 hours every week from his blog <a href="https://www.jeffgeerling.com/blog/2016/why-i-close-prs-oss-project-maintainer-notes">Why I close PRs (OSS project maintainer notes)</a>, although the blog says he invests 5-10 hours every week.</p>Subhojit PaulDoing open source contribution can be, writing a Node.js module, a PHP library, help fix bugs in another library, or even helping in writing documentation. Suppose, you have written a new module, and it is working and you release it. But then you have to reply to the issues, fix bugs, add new features, do code refactoring, etc. When it comes to maintenance, it becomes difficult. You have to do your work for which you are getting paid for (regular job), spend time with family, spend time for yourself, and maintain the module. Carrying on like this, you don’t get enough time to maintain it, and finally, you lose interest in it.Bypass Git merge conflicts in compiled CSS2016-08-27T00:00:00+00:002016-08-27T00:00:00+00:00http://subhojit777.in/bypass-git-merge-conflict-compiled-css<p>After reading this blog you will learn, how you can automate the resolving of merge conflicts for certain files during <code class="language-plaintext highlighter-rouge">git rebase</code>.</p>
<h2 id="the-problem">The Problem</h2>
<p>I was working on a project where we were using Sass for styling. We were maintaining multiple branches for multiple features. I often had to rebase branches. And obviously, there were merge conflicts. I use <a href="http://meldmerge.org">Meld</a> to resolve conflicts. There were conflicts in the compiled CSS as well. For every merge conflicts Meld, opens up and asks you to resolve them. Similarly, it used to open up for the compiled CSS as well. You may think :confused: - well.. ok.. you can just close the Meld without doing anything and recompile the CSS. Well, that is okay unless you don’t want to automate that. My <strong>problem</strong> was - the CSS was also minified, and since it is minified, Meld used to take ~10 seconds, to open up the file in three panes. And there’s more - we had the compiled CSS <code class="language-plaintext highlighter-rouge">styles.css</code> <em>and</em> the map file as well. So, ~10 * 2 = ~20 seconds, I had to wait for ~20 seconds just to close the Meld and recompile the CSS.</p>
<p>Earlier in the project, there were fewer conflicts while rebasing, so I usually waited for that 20 seconds. But things became more painful when we used to rebase two branches with longer history. It used to take me around half hour to completely rebase one branch to another, while the “actual” conflict fix took me hardly 2 minutes.</p>
<p>So, I decided - why should I waste my time, let the Computer do this thing for me.</p>
<h2 id="the-solution">The Solution</h2>
<p>People in IRC <code class="language-plaintext highlighter-rouge">#git</code> channel helped me out with the problem. They said <a href="https://git-scm.com/docs/gitattributes"><strong><code class="language-plaintext highlighter-rouge">.gitattributes</code></strong></a> is the solution. I googled and found many blogs and StackOverflow links. I liked this <a href="http://stackoverflow.com/questions/928646/how-do-i-tell-git-to-always-select-my-local-version-for-conflicted-merges-on-a-s">StackOverflow question</a>. The answer states the solution in detail. It tells how you can make use of drivers to resolve merge conflicts. We will be creating a custom merge driver.</p>
<p><strong>The definition of the driver:</strong></p>
<p>Inside <code class="language-plaintext highlighter-rouge">.git/config</code> file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[merge "ours"]
name = Keep ours merge
driver = true
</code></pre></div></div>
<p>Or you can just execute the following commands:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config merge.ours.name <span class="s2">"Keep ours merge"</span>
git config merge.ours.driver <span class="nb">true</span>
</code></pre></div></div>
<p>Create <code class="language-plaintext highlighter-rouge">.gitattributes</code> file with the following content:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*.css merge=ours
*.css.map merge=ours
</code></pre></div></div>
<p>If you already have the <code class="language-plaintext highlighter-rouge">.gitattributes</code> file, you just append the above content in the file.</p>
<p><strong>Explanation:</strong></p>
<p>From this <a href="http://stackoverflow.com/a/3052118/1233922">StackOverflow answer</a>:</p>
<blockquote>
<p>A rebase switches <code class="language-plaintext highlighter-rouge">ours</code> (current branch before rebase starts) and <code class="language-plaintext highlighter-rouge">theirs</code> (the upstream branch on top you want to rebase).</p>
</blockquote>
<p>Since, I am using GUI mergetool Meld, therefore, in a GUI mergetool context:</p>
<blockquote>
<ul>
<li>local references the partially rebased commits: “ours” (the upstream branch)</li>
<li>remote refers to the incoming changes: “theirs” - the current branch before the rebase</li>
</ul>
</blockquote>
<p>So what we are doing is, we are keeping the local version (<code class="language-plaintext highlighter-rouge">ours</code>) of the conflicting CSS and map files. That’s it. You can do more complex stuff, you just have mention that in the <code class="language-plaintext highlighter-rouge">merge.*.driver</code>.</p>
<p>The next time there is a merge conflict in the CSS or in the map file, it won’t open Meld, it will keep the local version of the file. After the successful rebase, if you still don’t trust the merged CSS, you can recompile it again and <a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits"><code class="language-plaintext highlighter-rouge">squash</code></a> the new commit with any desired commit.</p>Subhojit PaulAfter reading this blog you will learn, how you can automate the resolving of merge conflicts for certain files during git rebase.How to create views programmatically in Drupal 82015-08-09T00:00:00+00:002015-08-09T00:00:00+00:00http://subhojit777.in/create-views-programatically-drupal8<p>Creating views programmatically is quite different than we did in Drupal 7. In Drupal 7 we have <a href="https://api.drupal.org/api/views/views.api.php/function/hook_views_default_views/7" title="hook_views_default_views()"><code class="language-plaintext highlighter-rouge">hook_views_default_views()</code></a>, we don’t have this hook in Drupal 8. We will use configuration manager in Drupal 8 for creating views programmatically. You can read more about Drupal 8 configuration manager <a href="https://www.drupal.org/documentation/administer/config" title="Configuration Manager in Drupal 8">here</a>.</p>
<h4 id="before-you-continue">Before you continue…</h4>
<ul>
<li>You know how to create a basic module in Drupal 7, right?</li>
<li>You have a Drupal 8 instance running</li>
</ul>
<p>We will create a small module. After you enable the module in any Drupal 8 instance, the view will be created there.</p>
<h4 id="the-my_moduleinfoyml-file">The <code class="language-plaintext highlighter-rouge">my_module.info.yml</code> file</h4>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">My</span><span class="nv"> </span><span class="s">module'</span>
<span class="na">description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">This</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">my</span><span class="nv"> </span><span class="s">module.</span><span class="nv"> </span><span class="s">I</span><span class="nv"> </span><span class="s">will</span><span class="nv"> </span><span class="s">create</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">view</span><span class="nv"> </span><span class="s">here.'</span>
<span class="na">core</span><span class="pi">:</span> <span class="s">8.x</span>
<span class="na">type</span><span class="pi">:</span> <span class="s">module</span>
</code></pre></div></div>
<p>This is the basic <code class="language-plaintext highlighter-rouge">info</code> file. Obviously you can add other properties as well.</p>
<h4 id="the-my_modulemodule-file">The <code class="language-plaintext highlighter-rouge">my_module.module</code> file</h4>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="cd">/**
* @file
* This is my module. I will create a view here, and it will be empty.
*/</span>
</code></pre></div></div>
<p>Yes it will be an empty file :) Here comes the tricky part.</p>
<p>First you have to create the view (that you want to create programatically) from the administrator UI.
<img src="/images/post_7/views-programmatically-1.jpg" alt="Views configuration" title="Views configuration" /></p>
<p>Go to Configuration Synchronize interface. You can find this interface here <code class="language-plaintext highlighter-rouge">admin/config/development/configuration</code>
<img src="/images/post_7/views-programmatically-2.jpg" alt="Configuration synchronize interface" title="Configuration synchronize interface" /></p>
<p>Click on <strong>Single Import/Export</strong> tab, and then click on <strong>Export</strong> tab. Finally you will reach this path <code class="language-plaintext highlighter-rouge">admin/config/development/configuration/single/export</code>
<img src="/images/post_7/views-programmatically-3.jpg" alt="Configuration export interface" title="Configuration export interface" /></p>
<p>Select <strong>View</strong> in <strong>Configuration type</strong>. All views currently present in the system will be loaded in the next setting <strong>Configuration name</strong>. Select the <em>name</em> of view in <strong>Configuration name</strong>.
<img src="/images/post_7/views-programmatically-4.jpg" alt="Configuration selection" title="Configuration selection" /></p>
<p>After you select the view name, a yaml text will be generated in the textarea below. Note the filename you see below the text area, and copy it somewhere.
<img src="/images/post_7/views-programmatically-5.jpg" alt="Views configuration name" title="Views configuration name" /></p>
<p>Create directory called <code class="language-plaintext highlighter-rouge">config</code> inside your module’s root directory, and inside that create another directory called <code class="language-plaintext highlighter-rouge">install</code>. Inside <code class="language-plaintext highlighter-rouge">install</code> create a file with filename that you copied earlier. Open that empty file. You will require the yaml text which we generated earlier, copy it all. Paste it in the empty file. At the beginning of the file you will see a line starting with <code class="language-plaintext highlighter-rouge">uuid</code>, remove it. We are removing this because its instance specific data. You would have required this data if you were exporting this view to any other instance, say dev/stage instance of the system.</p>
<h4 id="the-viewsviewviews_machine_nameyml-file">The <code class="language-plaintext highlighter-rouge">views.view.VIEWS_MACHINE_NAME.yml</code> file</h4>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># views.view.my_module_myview.yml file</span>
<span class="c1"># ....</span>
<span class="c1"># system generated yml code without uuid data</span>
<span class="c1"># ...</span>
</code></pre></div></div>
<h4 id="my_module-directory-structure">my_module directory structure</h4>
<ul>
<li>my_module
<ul>
<li><code class="language-plaintext highlighter-rouge">my_module.info.yml</code></li>
<li><code class="language-plaintext highlighter-rouge">my_module.module</code></li>
<li>config
<ul>
<li>install
<ul>
<li><code class="language-plaintext highlighter-rouge">views.view.VIEWS_MACHINE_NAME.yml</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>P.S. The above suggested method for creating views programmatically is tested on latest codebase of Drupal 8 (as of 9th August 2015). This is just an approach for creating views programmatically, there can be other <em>better</em> approaches, suggestions are welcome :)</p>Subhojit PaulCreating views programmatically is quite different than we did in Drupal 7. In Drupal 7 we have hook_views_default_views(), we don’t have this hook in Drupal 8. We will use configuration manager in Drupal 8 for creating views programmatically. You can read more about Drupal 8 configuration manager here.Share content by mail using service links module2015-05-07T00:00:00+00:002015-05-07T00:00:00+00:00http://subhojit777.in/share-content-by-mail-using-service-links-drupal<p>In this blog post you will learn how to create mail share using <a href="https://www.drupal.org/project/service_links">service links</a> module in Drupal. We are going to create a custom module for this.</p>
<p><code class="language-plaintext highlighter-rouge">sl_mail.info</code> file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>name = Service links mail
description = Service for sharing by mail
dependencies[] = service_links
core = 7.x
package = Service Links - Services
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">sl_mail.module</code> file:</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="cd">/**
* @file
* Service for sharing through mail.
*/</span>
<span class="cd">/**
* Implements hook_service_links().
*/</span>
<span class="k">function</span> <span class="nf">sl_mail_service_links</span><span class="p">()</span> <span class="p">{</span>
<span class="nv">$links</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
<span class="nv">$links</span><span class="p">[</span><span class="s1">'mail'</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'name'</span> <span class="o">=></span> <span class="s1">'Mail'</span><span class="p">,</span>
<span class="s1">'description'</span> <span class="o">=></span> <span class="nx">t</span><span class="p">(</span><span class="s1">'Share this post by mail'</span><span class="p">),</span>
<span class="s1">'link'</span> <span class="o">=></span> <span class="s1">'mailto:?subject=<encoded-title>&body=<encoded-url>'</span><span class="p">,</span>
<span class="s1">'icon'</span> <span class="o">=></span> <span class="nx">drupal_get_path</span><span class="p">(</span><span class="s1">'module'</span><span class="p">,</span> <span class="s1">'sl_mail'</span><span class="p">)</span> <span class="o">.</span><span class="s1">'/mail.png'</span><span class="p">,</span>
<span class="s1">'attributes'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'class'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'service-links-mail'</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="k">return</span> <span class="nv">$links</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You will need a mail icon. You can download any mail icon from <a href="http://www.iconfinder.com">iconfinder</a>, and save it as <code class="language-plaintext highlighter-rouge">mail.png</code> under the module directory, like this <code class="language-plaintext highlighter-rouge">sites/all/modules/sl_mail/mail.png</code>.</p>
<p>Enable the module. You have to enable the service from service links settings page here <code class="language-plaintext highlighter-rouge">admin/config/services/service-links/services</code>. The new service will look like this <img src="../images/post_1/yP47kOg.png" alt="service links mail" /></p>
<p><strong>References</strong>: See this <a href="http://servicelinks.altervista.org/?q=about/customizations/extend-the-number-of-services.html">documentation</a> of service links module to create custom services.</p>Subhojit PaulIn this blog post you will learn how to create mail share using service links module in Drupal. We are going to create a custom module for this.Use Vim as IDE for Drupal development2014-10-05T11:44:00+00:002014-10-05T11:44:00+00:00http://subhojit777.in/use-vim-as-ide-for-drupal-development<p>In my previous blog <a href="http://subhojitpaul.blogspot.com/2013/03/how-to-use-vim-for-drupal-development.html">How to use vim for Drupal development</a> I have mentioned how to use vim for Drupal development. The blog shows some plugins for code completion, drupal code snippets, and other ways for easily writing code in Drupal. I was using <a href="https://www.drupal.org/project/vimrc">vimrc</a>, <a href="https://www.drupal.org/node/29325">some settings</a> for configuring vim. Vimrc provides a collection of plugins and settings so that you can quickly setup vim for Drupal.</p>
<p>In this blog I will show you my collection of plugins and settings, and to be honest I found my collection better than vimrc ;)</p>
<p>You can find the collection here in my <a href="https://github.com/subhojit777/drupal-vim">GitHub</a> repository, instructions for installing are written there.</p>
<p>The rest of the blog is about the purpose of the plugins I have used and how you can use them.</p>
<h2 id="searchparty"><a href="https://github.com/dahu/SearchParty">SearchParty</a></h2>
<p>This plugin highlights multiple occurrences of a searched word in file.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">*</code> Searches for the next occurrence of the currently selected visual text.</li>
<li><code class="language-plaintext highlighter-rouge">#</code> Searches for the prior occurrence of the currently selected visual text.</li>
<li><code class="language-plaintext highlighter-rouge"><C-L></code> Temporarily clears search highlight.</li>
</ul>
<p>These are the features I mostly used in this plugin.</p>
<h2 id="vundlevim"><a href="https://github.com/gmarik/Vundle.vim">Vundle.vim</a></h2>
<p>This is just another plugin manager for Vim. Earlier I was using <a href="https://github.com/tpope/vim-pathogen">pathogen</a>, but I found Vundle much cleaner than pathogen. You just specify the plugins in one place, and rest is handled by Vundle.</p>
<h2 id="auto-pairs"><a href="https://github.com/jiangmiao/auto-pairs">auto-pairs</a></h2>
<p>Another vim plugin for auto closing brackets, quotes, etc. There is also <a href="https://github.com/tpope/vim-surround">surround</a> plugin that does the same job, but you can specify custom auto pair characters in auto-pairs plugin.</p>
<h2 id="ctrlpvim"><a href="https://github.com/kien/ctrlp.vim">ctrlp.vim</a></h2>
<p>It is the ultimate plugin for fuzzy finder in vim. If you have used sublime text then you will find this feature familiar, the only difference is that you can customize it to best match your needs. Note that, after you select a file to open, you can open it in new tab by pressing <code class="language-plaintext highlighter-rouge"><ctrl>-t</code></p>
<p><a href="../images/post_2/vim-ctrlp.gif"><img src="../images/post_2/vim-ctrlp.gif" alt="vim-ctrlp" /></a></p>
<h2 id="neocompletevim"><a href="https://github.com/Shougo/neocomplete.vim">neocomplete.vim</a></h2>
<p>An autocomplete plugin for vim. Earlier I have used <a href="https://github.com/Valloric/YouCompleteMe">YCM</a> for autocomplete. From usage perspective, they are very much similar. I recommend using neocomplete, because it is much lighter and consumes less resource than YCM. neocomplete is completely written in VimScript, while YCM requires python. Some python script will run alongside YCM and it provides the autocompletion feature for vim, and this is the reason your vim will consume ~500MB or may be more. In neocomplete your vim will be satisfied under ~30MB.</p>
<p><a href="../images/post_2/vim-neocomplete.gif"><img src="../images/post_2/vim-neocomplete.gif" alt="vim-neocomplete" /></a></p>
<h2 id="nerdcommenter"><a href="https://github.com/scrooloose/nerdcommenter">nerdcommenter</a></h2>
<p>This plugin eases the comment toggle in Vim. Mostly I use <code class="language-plaintext highlighter-rouge"><leader>ci</code> for inverting comment for a particular block of code. Other than that, you can use <code class="language-plaintext highlighter-rouge"><leader>cm</code> to enclose block of code within comments.</p>
<p><a href="../images/post_2/vim-nerdcommenter.gif"><img src="../images/post_2/vim-nerdcommenter.gif" alt="vim-nerdcommenter" /></a></p>
<h2 id="nerdtree"><a href="https://github.com/scrooloose/nerdtree">nerdtree</a></h2>
<p>I rarely use this plugin :) But I will tell you the unique feature of this plugin. It allows you to browse files within the current working directory. I always use ctrlp for opening files, and rarely use nerdtree. I guess in future I will remove this plugin from my collection. If you are using my vimrc settings, then you can toggle nerdtree by <code class="language-plaintext highlighter-rouge"><F7></code> key</p>
<p><a href="../images/post_2/vim-nerdtree.png"><img src="../images/post_2/vim-nerdtree.png" alt="vim-nerdtree" /></a></p>
<h2 id="syntastic"><a href="https://github.com/scrooloose/syntastic">syntastic</a></h2>
<p>It is the syntax checking plugin, and that is all it does. And like other plugins you can customize it too to best suit your needs. I no longer use this plugin, as it freezes the editor when you save a file. It runs the syntax check in the background, and sometimes that process is slow.</p>
<h2 id="vim-airline"><a href="https://github.com/bling/vim-airline">vim-airline</a></h2>
<p>One of the best plugins I have seen. It makes the vim status bar more useful and informative. You can see file format, current branch, file name, etc. in vim status bar. And again, customize it as you want.</p>
<p><a href="../images/post_2/vim-vimairline.png"><img src="../images/post_2/vim-vimairline.png" alt="vim-airline" /></a></p>
<h2 id="vim-better-whitespace"><a href="https://github.com/ntpeters/vim-better-whitespace">vim-better-whitespace</a></h2>
<p>I helps you identify whitespaces. You can even strip whitespace on save by this command <code class="language-plaintext highlighter-rouge">:ToggleStripWhitespaceOnSave</code> You can identify whitespace by writing some code in .vimrc, but I prefer using plugins, it makes the vim configuration more modular.</p>
<p><a href="../images/post_2/vim-vimwhitespace.gif"><img src="../images/post_2/vim-vimwhitespace.gif" alt="vim-vimwhitespace" /></a></p>
<h2 id="vim-colorschemes"><a href="https://github.com/flazz/vim-colorschemes">vim-colorschemes</a></h2>
<p>A nice collection of vim colorschemes. Choose between dark, light colorschemes and make your vim more sexy :p I am using solarized-dark, my other choices are badwolf and molokai.</p>
<h2 id="vim-fugitive"><a href="https://github.com/tpope/vim-fugitive">vim-fugitive</a></h2>
<p>It is said to be the ultimate git wrapper for vim, but alas I am not using it much. If used with vim-airline it will show current branch in status bar.</p>
<h2 id="vim-session"><a href="https://github.com/xolox/vim-session">vim-session</a></h2>
<p>A nice plugin that helps you to maintain multiple session in vim. Suppose, you are working on a project, you are writing some custom module and want to finish your work tomorrow, so you will want vim to open the same module file for you the next day, vim-session will do that for you. It also allows you to jump between sessions. Earlier I used to manually open files in vim, and I find it cumbersome.</p>
<p>Commands that I use frequently:</p>
<p><code class="language-plaintext highlighter-rouge">:SaveSession mysession</code> - will save the current session</p>
<p><code class="language-plaintext highlighter-rouge">:OpenSession mysession</code> - open your session</p>
<p><a href="../images/post_2/vim-session.gif"><img src="../images/post_2/vim-session.gif" alt="vim-session" /></a></p>
<h2 id="vim-snipmate"><a href="https://github.com/garbas/vim-snipmate">vim-snipmate</a></h2>
<p>Expand if, for, foreach statements with the press of <code class="language-plaintext highlighter-rouge"><tab></code> key.</p>
<p><a href="../images/post_2/vim-snipmate.gif"><img src="../images/post_2/vim-snipmate.gif" alt="vin-snipmate" /></a></p>
<h2 id="vim-snippets"><a href="https://github.com/honza/vim-snippets">vim-snippets</a></h2>
<p>A collection of PHP snippets, it also includes snippets for other languages say javascript. It works with vim-snipmate.</p>
<h2 id="drupal-snippets"><a href="https://github.com/technosophos/drupal-snippets">drupal-snippets</a></h2>
<p>Thanks to <a href="https://github.com/technosophos">technosophos</a> for taking the effort of writing drupal snippets. Now you can expand <code class="language-plaintext highlighter-rouge">hook_menu</code>, <code class="language-plaintext highlighter-rouge">hook_form_alter</code>, <code class="language-plaintext highlighter-rouge">hook_block_info</code>, <code class="language-plaintext highlighter-rouge">hook_permissions</code>, etc. in the blink of an eye (press <code class="language-plaintext highlighter-rouge"><tab></code> key). If you are not finding snippets for some hook, I recommend fork <a href="https://github.com/technosophos">technosophos</a>’s repository and add your snippets there. Or you can add pull requests to his repository.</p>
<p><a href="../images/post_2/vim-drupalsnippets.gif"><img src="../images/post_2/vim-drupalsnippets.gif" alt="vim-drupalsnippets" /></a></p>
<p>You can find the plugins and settings in this repository <a href="https://github.com/subhojit777/drupal-vim">drupal-vim</a>. Please star it, if you like it. Fork it, if you want to add more features.</p>
<p><strong>Thanks for flying Vim :)</strong></p>Subhojit PaulIn my previous blog How to use vim for Drupal development I have mentioned how to use vim for Drupal development. The blog shows some plugins for code completion, drupal code snippets, and other ways for easily writing code in Drupal. I was using vimrc, some settings for configuring vim. Vimrc provides a collection of plugins and settings so that you can quickly setup vim for Drupal.