Jekyll2019-02-19T07:24:28+00:00https://blog.imanel.org/feed.xmlImanel’s BlogBernard PotockiMoving forward with WebSockets2012-12-20T00:00:00+00:002012-12-20T00:00:00+00:00https://blog.imanel.org/2012/12/20/moving-forward-with-websockets<p>Two years ago I created <a href="https://rubygems.org/gems/libwebsocket">LibWebSocket</a> - gem designed to abstract complicated WebSocket API and make it easy to use. During this time a lot things happened - several big gems started using LibWebSocket (like Selenium-Webdriver and Pusher), and it was downloaded nearly 1,5 million times. A lot changed in WebSocket world itself - couple drafts passed, specification was <a href="https://datatracker.ietf.org/doc/rfc6455/?include_text=1">standardized</a> and most browsers implemented <a href="https://caniuse.com/websockets">native support</a> for WS protocol.</p>
<p>But time passes and old solutions needs updates. Unfortunately, because big changes in specification it’s impossible to keep LibWebSocket up to date - mostly because of new features that are incompatible with some design decisions made during creation of LibWebSocket. Because of that I decided that best option is to rewrite it from scratch, with new standards in mind. As a result I would like to announce new gem: <a href="https://github.com/imanel/websocket-ruby">WebSocket-Ruby</a>.</p>
<p>Most of you will ask “why we need another WebSocket implementation?”. That is great question, and let me answer it before moving forward. Currently we have multiple servers and maybe one or two clients for WebSocket. Each of them implements standard by itself, which results in multiple bugs and quirks - even most widely used EM-WebSocket have its own collection. In addition to that focusing on both server part and WebSocket API part make development a lot slower - I strongly believe that splitting responsibility to separate gems is best approach(for proof please see Rack and Thin separation).</p>
<p>In order to make sure that this implementation is reliable I created two additional gems - WebSocket <a href="https://github.com/imanel/websocket-eventmachine-client">Client</a> and <a href="https://github.com/imanel/websocket-eventmachine-server">Server</a>. At the time of writing this article both of them have full <a href="https://crossbar.io/autobahn/">Autobahn</a> compatibility and best performance from all other Ruby implementations.</p>
<p>My next step is to start making pull requests for other major WebSocket clients and servers implementations - the more of them will use common implementation, the better it will be, and whole community will benefit. So if you are maintainer of such implementation, or you are willing to simply help transferring your favorite one to WebSocket-Ruby then I will gladly help.</p>Bernard PotockiTwo years ago I created LibWebSocket - gem designed to abstract complicated WebSocket API and make it easy to use. During this time a lot things happened - several big gems started using LibWebSocket (like Selenium-Webdriver and Pusher), and it was downloaded nearly 1,5 million times. A lot changed in WebSocket world itself - couple drafts passed, specification was standardized and most browsers implemented native support for WS protocol.How to change default search engine in Safari 6?2012-06-26T00:00:00+00:002012-06-26T00:00:00+00:00https://blog.imanel.org/2012/06/26/how-to-change-default-search-engine-in-safari-6<p>When new Safari will be released plugins like <a href="http://hackemist.com/SafariOmnibar">Safari Omnibar</a> will be obsolete - new Safari has functionality similar to Chrome Omnibox. Yet - one function is missing: option to change search provider. I’m pretty sure that a lot of us use plugins like SafariOmnibar not just to integrate Google Search with URL bar, but also to be able to use different search providers than Google, Bing and Yahoo.</p>
<p>Good news: it is still be possible to use custom search. Bad news: it’s little different that earlier.</p>
<p>If you want to integrate multiple search engines like Wiki, Youtube, Quora and many more, then I would suggest to use to <a href="https://duckduckgo.com">DuckDuckGo</a> and it’s awesome <a href="https://duckduckgo.com/bang.html">!Bang syntax</a> - not only it will allow accessing multiple search engines, but will provide much more functionality than standard Google.</p>
<h2 id="how-to-change-provider-to-duckduckgo">How to change provider to DuckDuckGo?</h2>
<p>I know that there’s a lot of scary methods around net that will show you methods like manualy editing binary file or messing with configuration. I’m not a big fan of those - mostly because after every Safari update you will be forced to do it again. What I prefer is much cleaner way of <a href="https://help.duckduckgo.com/customer/portal/articles/255650">editing single text file in system</a>.</p>
<p>What you will need to do is editing file /etc/hosts, which contain DNS entries, and change one of currently used search providers to DuckDuckGo server.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">184.72.115.86 search.yahoo.com</code></pre></figure>
<p>This will change all searches via “Search using Yahoo!” option to search via DuckDuckGo. It’s possible to change Google or Bing search to work like that, but it’s not recommended because it will prevent to access all Google/Bing functionality, when Yahoo have it’s search engine in separate subdomain so it’s non-obtrusive.</p>
<h2 id="but-i-want-to-still-use-google-as-main-engine">But I want to still use Google as main engine!</h2>
<p>For some of you fully changing to DuckDuckGo is no-go. Because of that there’s another method - redirecting all Google Searches with !Bang syntax to DuckDuckGo(which will redirect to according search engine or page).</p>
<p>In order to do so you will need to support for Greasemonkey UserScripts. There was obsolete method with SIMBL plugin for that, but currently Safari extension build on standard APIs exists - it’s called <a href="https://mac.softpedia.com/get/Internet-Utilities/NinjaKit-for-Safari.shtml">NinjaKit</a>. Please install it to gain access to all user scripts available at <a href="https://userscripts.org">https://userscripts.org</a>.</p>
<p>After installing NinjaKit all you need is to add simple UserScript that will detect and redirect queries staring with bang - you can find it <a href="https://gist.github.com/1817223">here</a>. From now on you can use normal Google Search, but when i.e. want to search YouTube instead just type in search bar:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">!</span>yt funny cats</code></pre></figure>Bernard PotockiWhen new Safari will be released plugins like Safari Omnibar will be obsolete - new Safari has functionality similar to Chrome Omnibox. Yet - one function is missing: option to change search provider. I’m pretty sure that a lot of us use plugins like SafariOmnibar not just to integrate Google Search with URL bar, but also to be able to use different search providers than Google, Bing and Yahoo.How to install MySQL Ruby gem in OS X Snow Leopard2011-04-04T00:00:00+00:002011-04-04T00:00:00+00:00https://blog.imanel.org/2011/04/04/how-to-install-mysql-ruby-gem-in-os-x-snow-leopard<p>I know this was posted multiple times by other people, but this information is scattered and hard to find so I’m posting quick summary.</p>
<p>First of all you must download <a href="https://dev.mysql.com/downloads/mysql/5.1.html">MySQL binary</a>. Be aware that ruby gem will not work with version newer than 5.1, and in Snow Leopard you should use 64-bit version.</p>
<p>After installing it (it should land in /usr/local) you can start compiling mysql gem. In order to do so you need to specify arch flags and path to your mysql dir:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">env <span class="nv">ARCHFLAGS</span><span class="o">=</span><span class="s2">"-arch i386"</span> gem install mysql <span class="nt">--</span> <span class="se">\</span>
<span class="nt">--with-mysql-dir</span><span class="o">=</span>/usr/local/mysql <span class="se">\</span>
<span class="nt">--with-mysql-lib</span><span class="o">=</span>/usr/local/mysql/lib <span class="se">\</span>
<span class="nt">--with-mysql-include</span><span class="o">=</span>/usr/local/mysql/include</code></pre></figure>
<p>Note that if you are using rvm then you shouldn’t install it in @global gemset - bundler will not see that and will try to recompile it again. So you will need to run this command for every gemset you will be using.</p>Bernard PotockiI know this was posted multiple times by other people, but this information is scattered and hard to find so I’m posting quick summary.Building WebSocket server in Ruby2010-12-05T00:00:00+00:002010-12-05T00:00:00+00:00https://blog.imanel.org/2010/12/05/building-websocket-server-in-ruby<p><a href="http://showmetheco.de/articles/2010/11/timtow-to-build-a-websocket-server-in-perl.html">Vti’s post</a> about building WebSocket server inspired me to write another one, but this time about both server and client in Ruby.</p>
<p>Currently we have couple of great implementations of WebSocket in Ruby - <a href="https://github.com/igrigorik/em-websocket">em-websocket</a>, <a href="https://github.com/gimite/web-socket-ruby">web-socket-ruby</a> or <a href="https://rainbows.rubyforge.org/sunshowers">sunshowers</a> to name a few. And looking at each one of them I can see two things - they all are great, and they all are alone. What I mean?</p>
<p><a href="https://github.com">Github</a> alone says that there are 40 projects in Ruby connected to WebSocket. Some of them are basing one on another, but there are more than 15 server implementations from scratch. Only two support both Draft 75 and Draft 76. And only one support newer drafts. Am I only one who see the problem?</p>
<p>That’s why I built <a href="https://github.com/imanel/libwebsocket">LibWebSocket</a> library whose main purpose is to eliminate the problem of aging server implementations - wrapper around multiple drafts of WebSocket with common API that any server implementation can build against. Thank’s to that whole community will be able to gain instead of working independently. So how to get started?</p>
<h2 id="building-server">Building server</h2>
<p>Currently most popular Ruby WebSocket server is em-websocket (I’m also using it in my <a href="https://github.com/socky">Socky</a> project) and I believe that currently it’s the best implementation. So why don’t start from building similar server which just couple of lines instead of huge library?</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#!/usr/bin/env ruby</span>
<span class="nb">require</span> <span class="s1">'rubygems'</span>
<span class="nb">require</span> <span class="s1">'eventmachine'</span>
<span class="nb">require</span> <span class="s1">'libwebsocket'</span>
<span class="k">module</span> <span class="nn">EchoServer</span>
<span class="k">def</span> <span class="nf">receive_data</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="vi">@hs</span> <span class="o">||=</span> <span class="no">LibWebSocket</span><span class="o">::</span><span class="no">OpeningHandshake</span><span class="o">::</span><span class="no">Server</span><span class="p">.</span><span class="nf">new</span>
<span class="vi">@frame</span> <span class="o">||=</span> <span class="no">LibWebSocket</span><span class="o">::</span><span class="no">Frame</span><span class="p">.</span><span class="nf">new</span>
<span class="k">if</span> <span class="o">!</span><span class="vi">@hs</span><span class="p">.</span><span class="nf">done?</span>
<span class="vi">@hs</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">if</span> <span class="vi">@hs</span><span class="p">.</span><span class="nf">done?</span>
<span class="n">send_data</span><span class="p">(</span><span class="vi">@hs</span><span class="p">.</span><span class="nf">to_s</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">return</span>
<span class="k">end</span>
<span class="vi">@frame</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">while</span> <span class="n">message</span> <span class="o">=</span> <span class="vi">@frame</span><span class="p">.</span><span class="nf">next</span>
<span class="n">send_data</span> <span class="vi">@frame</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">message</span><span class="p">).</span><span class="nf">to_s</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">EventMachine</span><span class="o">::</span><span class="n">run</span> <span class="k">do</span>
<span class="no">EventMachine</span><span class="o">::</span><span class="n">start_server</span> <span class="s1">'0.0.0.0'</span><span class="p">,</span> <span class="mi">8080</span><span class="p">,</span> <span class="no">EchoServer</span>
<span class="nb">puts</span> <span class="s2">"Started EchoServer on </span><span class="si">#{</span><span class="n">host</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">port</span><span class="si">}</span><span class="s2">..."</span>
<span class="k">end</span></code></pre></figure>
<p>And that’s all! Of course it’s just simplified implementation, but I think that shows what I’m talking about. No more handling events, checking version and implementing multiple drafts - it’s all inside! And together with that we can also build client:</p>
<h2 id="building-client">Building client</h2>
<p>This one is more tricky but still most work is handled by LibWebsocket:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s2">"socket"</span>
<span class="nb">require</span> <span class="s1">'libwebsocket'</span>
<span class="k">class</span> <span class="nc">WebSocket</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">params</span> <span class="o">=</span> <span class="p">{})</span>
<span class="vi">@hs</span> <span class="o">||=</span> <span class="no">LibWebSocket</span><span class="o">::</span><span class="no">OpeningHandshake</span><span class="o">::</span><span class="no">Client</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:url</span> <span class="o">=></span> <span class="n">url</span><span class="p">,</span>
<span class="ss">:version</span> <span class="o">=></span> <span class="n">params</span><span class="p">[</span><span class="ss">:version</span><span class="p">])</span>
<span class="vi">@frame</span> <span class="o">||=</span> <span class="no">LibWebSocket</span><span class="o">::</span><span class="no">Frame</span><span class="p">.</span><span class="nf">new</span>
<span class="vi">@socket</span> <span class="o">=</span> <span class="no">TCPSocket</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@hs</span><span class="p">.</span><span class="nf">url</span><span class="p">.</span><span class="nf">host</span><span class="p">,</span> <span class="vi">@hs</span><span class="p">.</span><span class="nf">url</span><span class="p">.</span><span class="nf">port</span> <span class="o">||</span> <span class="mi">80</span><span class="p">)</span>
<span class="vi">@socket</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="vi">@hs</span><span class="p">.</span><span class="nf">to_s</span><span class="p">)</span>
<span class="vi">@socket</span><span class="p">.</span><span class="nf">flush</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">data</span> <span class="o">=</span> <span class="vi">@socket</span><span class="p">.</span><span class="nf">getc</span>
<span class="k">next</span> <span class="k">if</span> <span class="n">data</span><span class="p">.</span><span class="nf">nil?</span>
<span class="n">result</span> <span class="o">=</span> <span class="vi">@hs</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="nf">chr</span><span class="p">)</span>
<span class="k">raise</span> <span class="vi">@hs</span><span class="p">.</span><span class="nf">error</span> <span class="k">unless</span> <span class="n">result</span>
<span class="k">if</span> <span class="vi">@hs</span><span class="p">.</span><span class="nf">done?</span>
<span class="vi">@handshaked</span> <span class="o">=</span> <span class="kp">true</span>
<span class="k">break</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">raise</span> <span class="s2">"no handshake!"</span> <span class="k">unless</span> <span class="vi">@handshaked</span>
<span class="n">data</span> <span class="o">=</span> <span class="vi">@frame</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">data</span><span class="p">).</span><span class="nf">to_s</span>
<span class="vi">@socket</span><span class="p">.</span><span class="nf">write</span> <span class="n">data</span>
<span class="vi">@socket</span><span class="p">.</span><span class="nf">flush</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">receive</span>
<span class="k">raise</span> <span class="s2">"no handshake!"</span> <span class="k">unless</span> <span class="vi">@handshaked</span>
<span class="n">data</span> <span class="o">=</span> <span class="vi">@socket</span><span class="p">.</span><span class="nf">gets</span><span class="p">(</span><span class="s2">"</span><span class="se">\xff</span><span class="s2">"</span><span class="p">)</span>
<span class="vi">@frame</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">messages</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">while</span> <span class="n">message</span> <span class="o">=</span> <span class="vi">@frame</span><span class="p">.</span><span class="nf">next</span>
<span class="n">messages</span> <span class="o"><<</span> <span class="n">message</span>
<span class="k">end</span>
<span class="n">messages</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>This will allow to connecting, sending and receiving messages from WebSocket server in drafts #75 and #76(more vesions comming)</p>
<h2 id="state-and-future">State and future</h2>
<p><a href="https://github.com/imanel/libwebsocket">LibWebSocket</a> is currently in very early state of development but I hope that it will move fast to stable version. You can now install it as gem and start using:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">gem</span> <span class="n">install</span> <span class="n">libwebsocket</span></code></pre></figure>
<p>See project at github: <a href="https://github.com/imanel/libwebsocket">https://github.com/imanel/libwebsocket</a>.</p>Bernard PotockiVti’s post about building WebSocket server inspired me to write another one, but this time about both server and client in Ruby.Socky Announcement2010-10-09T00:00:00+00:002010-10-09T00:00:00+00:00https://blog.imanel.org/2010/10/09/socky-announcement<p>Meet Socky - the ultimate open-source, cross-platform, secure push server solution for your web applications! Socky uses standard WebSockets where available, with graceful fallback to Flash-based component making it usable even on mobile devices running iOS, Android and other Flash-enabled browsers.</p>
<p>Socky project consists of two independent tools - the Ruby-based push server and client-side library. Socky was developed with performance in mind - the server scales well among any number of hosts and can handle thousands of users on a single host making it perfect for things like chat, broadcasting and other real-time communication features.</p>
<p>What makes Socky different from the competing products? It’s is free of charge, doesn’t require any external servers, uses web standards where possible and has few dependencies making it the easiest, cheapest and most universal solution. Over 700 downloads during the first 4 months of development speak for themselves!</p>
<p>Key features:</p>
<ul>
<li>SSL-enabled</li>
<li>compatible with Ruby 1.8 and 1.9, jruby and rubinius</li>
<li>plugin for rails 2.2+ available</li>
<li>no 3rd party servers involved</li>
<li>high scalability</li>
</ul>
<p>Supported platforms:</p>
<ul>
<li>Google Chrome or any other browser supporting WebSockets or Flash</li>
<li>iOS 4.2+</li>
<li>Android with Flash/Flash Lite</li>
<li>Any other device with WebSockets or Flash/Flash Lite support</li>
</ul>
<p>Find out more about Socky at <a href="https://github.com/socky">https://github.com/socky</a>.</p>Bernard PotockiMeet Socky - the ultimate open-source, cross-platform, secure push server solution for your web applications! Socky uses standard WebSockets where available, with graceful fallback to Flash-based component making it usable even on mobile devices running iOS, Android and other Flash-enabled browsers.Web standards in mobile browsers2010-06-28T00:00:00+00:002010-06-28T00:00:00+00:00https://blog.imanel.org/2010/06/28/web-standards-in-mobile-browsers<p>For the past few weeks I focused on developing <a href="https://github.com/socky/socky-server-ruby">Socky</a> - new WebSocket based push server for Ruby on Rails. Its main advantage was to be working well in places where there is no flash - like iPhone or iPad. It is therefore obvious that I became very sensitive to standard implementation in so called “modern browsers” - also mobile ones. So, taking the opportunity of just released iOS4, I decided to write a few words about standard support in mobile browsers.</p>
<p>First tested was the latest iOS4 - loudly advertised as a strong HTML5 supporter. A set of rapid tests showed that the most important elements actually works flawlessly - audio and video, geolocation or SVG. Also, elements such as CSS 3 border-radius, CSS animations and reflections are working properly. But the most important feature for me, WebSockets, is missing. I don’t understand why this protocol isn’t there, when in lately released Safari 5 it wasn’t issue. So how are users of the system be able to use chat, or HTML multiplayer games? There is also no support for @font-face or Web Workers. Maybe we see in the near future?</p>
<p>The second largest mobile system is Android - I tested version 2.2 (not yet available everywhere). It’s not disappointing - most of the tests passed without error. As in the iOS audio and video tags work properly, geolocation and CSS 3 too, and, moreover, does @font-face. Unfortunately, there also is no support for WebSocket (which Google Chrome has for a long time) - but fortunately, working flash eliminates this problem (thanks to <a href="https://github.com/gimite/web-socket-js">web-socket-js</a>) I was surprised by the lack of SVG support - I understand that this is a very processor aggravating technology, but lack of it is very arduous.</p>
<p>Last discussed system is a WebOS version 1.4.1 (there should be new version 1.4.5 in the next few days) - system is based entirely on HTML and CSS, so you would expect the best support for standards. Unfortunately, there is some disappointment - most of the tests pass, but from presented systems this one have highest number of unsupported technology - @font-face, WebSockets, SVG, and geolocation. Hopefully the next update will improve browser a little.</p>
<p>All tests were performed using a <a href="https://www.modernizr.com/">Modernizr</a>, and some of them tested manually (audio, video, WebSockets and geolocation).</p>Bernard PotockiFor the past few weeks I focused on developing Socky - new WebSocket based push server for Ruby on Rails. Its main advantage was to be working well in places where there is no flash - like iPhone or iPad. It is therefore obvious that I became very sensitive to standard implementation in so called “modern browsers” - also mobile ones. So, taking the opportunity of just released iOS4, I decided to write a few words about standard support in mobile browsers.CSS3 in every browser with SASS and PIE2010-06-18T00:00:00+00:002010-06-18T00:00:00+00:00https://blog.imanel.org/2010/06/18/css3-in-every-browser-with-sass-and-pie<p>Recently, more and more popular is the use of the HTML5 and CSS3. Everyone rushed to them and this is perfectly understandable - after all, developers will be able to bet on semantic instead of doing work arounds for some browsers. But the problem is that at the moment even the “modern” browsers have problems handling some common attributes. The most common example is the border-radius, which has up to three versions - so if we want all browsers display the same rounding, we need to write this as:</p>
<figure class="highlight"><pre><code class="language-scss" data-lang="scss"><span class="nn">#round_me</span> <span class="p">{</span>
<span class="na">-moz-border-radius</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="na">-webkit-border-radius</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>And this isn’t “uniformity”, but hopefully that for some time only one of them will be needed. At the moment we need to put the whole set every time. And we often forget - as the example <a href="https://github.com/">GitHub</a> who sometimes remember and sometimes not to use “border-radius.”</p>
<p>Fortunately, if you use the help such as <a href="https://sass-lang.com/">SASS</a> (available for many programming languages) then the problem disappears. This framework allows us to define the “mixins” which then can be attached anywhere without the need to rewrite the same code several times (according to the <a href="https://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a> mantra). The easiest way would be to show how:</p>
<figure class="highlight"><pre><code class="language-scss" data-lang="scss"><span class="k">@mixin</span> <span class="nf">rounded-corners</span><span class="p">(</span><span class="nv">$size</span><span class="p">)</span> <span class="p">{</span>
<span class="na">-moz-border-radius</span><span class="p">:</span> <span class="nv">$size</span><span class="p">;</span>
<span class="na">-webkit-border-radius</span><span class="p">:</span> <span class="nv">$size</span><span class="p">;</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="nv">$size</span><span class="p">;</span>
<span class="p">}</span>
<span class="nn">#round_me</span> <span class="p">{</span>
<span class="k">@include</span> <span class="nd">rounded-corners</span><span class="p">(</span><span class="m">1em</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>Mixins themselves are not rendered, but only attached to the other classes so the end user code will be as in the first example.</p>
<p>Some ask why not use the class instead of id - the answer is simple: the separation of HTML and CSS. Because if one day you’ll want to get rid of the rounded corners then with one is easier: to remove the entry from the appropriate id or search through all the HTML pages and remove the class?</p>
<p>There remains the problem of one browser - namely Internet Explorer. It does not support the CSS3 specification and at the moment is still very popular. Thats why there are libraries like the <a href="http://css3pie.com/">PIE</a> - to facilitate this. For the above border-radius example (and many others) it require only to attach a .htc file and attribute will work well in IE6, IE7 and IE8. The specification requires, however, to attach it to every class in which CSS is used - so our definition this time will look like this:</p>
<figure class="highlight"><pre><code class="language-scss" data-lang="scss"><span class="nn">#round_me</span> <span class="p">{</span>
<span class="na">-moz-border-radius</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="na">-webkit-border-radius</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="m">1em</span><span class="p">;</span>
<span class="na">behavior</span><span class="p">:</span> <span class="sx">url(PIE.htc)</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>This is another line of which you must remember. And this is the best place where you can see the beauty of SASS - just add this line in one place and will work everywhere. Thus, the final version of the code will look like this:</p>
<figure class="highlight"><pre><code class="language-scss" data-lang="scss"><span class="k">@mixin</span> <span class="nf">rounded-corners</span><span class="p">(</span><span class="nv">$size</span><span class="p">)</span> <span class="p">{</span>
<span class="na">-moz-border-radius</span><span class="p">:</span> <span class="nv">$size</span><span class="p">;</span>
<span class="na">-webkit-border-radius</span><span class="p">:</span> <span class="nv">$size</span><span class="p">;</span>
<span class="nl">border-radius</span><span class="p">:</span> <span class="nv">$size</span><span class="p">;</span>
<span class="na">behavior</span><span class="p">:</span> <span class="sx">url(PIE.htc)</span><span class="p">;</span>
<span class="p">}</span>
<span class="nn">#round_me</span> <span class="p">{</span>
<span class="k">@include</span> <span class="nd">rounded-corners</span><span class="p">(</span><span class="m">1em</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>I hope that this solution will help you to create beautiful and semantic pages a lot easier.</p>Bernard PotockiRecently, more and more popular is the use of the HTML5 and CSS3. Everyone rushed to them and this is perfectly understandable - after all, developers will be able to bet on semantic instead of doing work arounds for some browsers. But the problem is that at the moment even the “modern” browsers have problems handling some common attributes. The most common example is the border-radius, which has up to three versions - so if we want all browsers display the same rounding, we need to write this as:Cucumber testing for multiple users - continuation2010-03-03T00:00:00+00:002010-03-03T00:00:00+00:00https://blog.imanel.org/2010/03/03/cucumber-testing-for-multiple-users-continuation<p>A little over a month ago you could read my article about real-time applications testing using Cucumber. In the meantime a new version of Cucumber emerged with <a href="https://github.com/jnicklas/capybara">Capybara</a> support added. Since the previous method posed several problems during system to system migration (especially Snow Leopard hacks occasionally did not work in other systems) we’ll try a new approach - this time working OOTB in every system.</p>
<p>As always, I will not describe the installation nor configuration process (you can find this in Capybara’s documentation) so let’s get straight to the practice. By default Capybara does not allow you to open multiple browser instances. According to the main developer - “it’s impossible by design”. As they say - It’s impossible. But doable ;) After taking a glance at Capybara’s code (I must admit that it is written quite nice) I found a couple of points where you could bypass this annoying limitation.</p>
<p>It is time to get to work. Let’s create a new set of steps in the features/step_definitions - call it e.g “selenium_steps.rb”. For now let’s assume that we want to open two browser instances (you can change that to different value if you want to). Create a method examining whether any browser instance exists (and creating new one if there’s none):</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">check_selenium_browsers</span>
<span class="no">Capybara</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="s1">'@current_driver'</span><span class="p">,</span> <span class="ss">:selenium</span><span class="p">)</span>
<span class="k">if</span> <span class="vg">$browsers</span><span class="p">.</span><span class="nf">nil?</span>
<span class="vg">$browsers</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">current_url</span>
<span class="vg">$browsers</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">get_selenium_browser</span>
<span class="n">set_selenium_browser</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>
<span class="n">current_url</span>
<span class="vg">$browsers</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">get_selenium_browser</span>
<span class="n">set_selenium_browser</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>The second line verifies that you use a correct test engine (in this case selenium). “current_url” method call guarantees that browser current state won’t be lost during process. Next save to a variable state of the browser and then reset it so Capybara can open a new instance. Why not do that by ourselves? The reason is simple - Capybara uses quite a lot of tricks so you could run into potential problems after some code refactoring done by developers. For now this method is sufficient and guarantees stable (or at least predictable) behavior. Next call to “current_url” will open a new brower. Finally restore browser to its initial state. Look at the save/restore browser state functions code below:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">get_selenium_browser</span>
<span class="p">{</span>
<span class="ss">:session</span> <span class="o">=></span> <span class="no">Capybara</span><span class="p">.</span><span class="nf">current_session</span><span class="p">,</span>
<span class="ss">:driver</span> <span class="o">=></span> <span class="no">Capybara</span><span class="o">::</span><span class="no">Driver</span><span class="o">::</span><span class="no">Selenium</span><span class="p">.</span><span class="nf">instance_variable_get</span><span class="p">(</span><span class="s1">'@driver'</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">end</span></code></pre></figure>
<p>As you can see, this method is very simple - it only acquires the current session (for Selenium) and “driver” (which is actually a reference to the Selenium server instance).</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">set_selenium_browser</span><span class="p">(</span><span class="n">browser_id</span><span class="p">)</span>
<span class="n">browser</span> <span class="o">=</span> <span class="vg">$browsers</span><span class="p">[</span><span class="n">browser_id</span><span class="p">]</span> <span class="o">||</span> <span class="p">{}</span>
<span class="k">if</span> <span class="n">browser</span><span class="p">[</span><span class="ss">:session</span><span class="p">].</span><span class="nf">nil?</span>
<span class="no">Capybara</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="s1">'@session_pool'</span><span class="p">,</span> <span class="p">{})</span>
<span class="no">Capybara</span><span class="o">::</span><span class="no">Driver</span><span class="o">::</span><span class="no">Selenium</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="s1">'@driver'</span><span class="p">,</span> <span class="kp">nil</span><span class="p">)</span>
<span class="k">else</span>
<span class="no">Capybara</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="s1">'@session_pool'</span><span class="p">,</span> <span class="p">{</span><span class="s2">"selenium</span><span class="si">#{</span><span class="no">Capybara</span><span class="p">.</span><span class="nf">app</span><span class="p">.</span><span class="nf">object_id</span><span class="si">}</span><span class="s2">"</span> <span class="o">=></span> <span class="n">browser</span><span class="p">[</span><span class="ss">:session</span><span class="p">]})</span>
<span class="no">Capybara</span><span class="o">::</span><span class="no">Driver</span><span class="o">::</span><span class="no">Selenium</span><span class="p">.</span><span class="nf">instance_variable_set</span><span class="p">(</span><span class="s1">'@driver'</span><span class="p">,</span> <span class="n">browser</span><span class="p">[</span><span class="ss">:driver</span><span class="p">])</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>There’s no magic here too - you just save the session state and override reference to the Selenium Server. Now we can write browser change functions with ease:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Given</span> <span class="sr">/^I am using first browser$/</span> <span class="k">do</span>
<span class="n">check_selenium_browsers</span>
<span class="n">set_selenium_browser</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">end</span>
<span class="no">Given</span> <span class="sr">/^I am using second browser$/</span> <span class="k">do</span>
<span class="n">check_selenium_browsers</span>
<span class="n">set_selenium_browser</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="k">end</span></code></pre></figure>
<p>This way we may use any number of browsers to test Juggernaut, WebSockets and different functionalities (even a simple, ajax communication between users, thus saving time needed to reauth in subsequent requests) in parallel.
Notice that during Cucumber shutdown phase not all browser instances will be closed (only the active one). To fix this add this code to the one of initialization (features/support) files:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">at_exit</span> <span class="k">do</span>
<span class="vg">$browsers</span> <span class="o">&&</span> <span class="vg">$browsers</span><span class="p">.</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="nb">id</span><span class="p">,</span> <span class="n">browser</span><span class="o">|</span> <span class="n">browser</span><span class="p">[</span><span class="ss">:driver</span><span class="p">].</span><span class="nf">quit</span> <span class="k">rescue</span> <span class="kp">nil</span> <span class="p">}</span>
<span class="k">end</span></code></pre></figure>Bernard PotockiA little over a month ago you could read my article about real-time applications testing using Cucumber. In the meantime a new version of Cucumber emerged with Capybara support added. Since the previous method posed several problems during system to system migration (especially Snow Leopard hacks occasionally did not work in other systems) we’ll try a new approach - this time working OOTB in every system.Testing Juggernaut using Cucumber and Selenium2010-01-24T00:00:00+00:002010-01-24T00:00:00+00:00https://blog.imanel.org/2010/01/24/testing-juggernaut-using-cucumber-and-selenium<p>During tests development you usually use well-known and tested solutions like <a href="https://rspec.info">RSpec</a> or <a href="https://cukes.info">Cucumber</a> + <a href="https://github.com/brynary/webrat">Webrat</a>. They let you develop in a fast and convinient way. Unfortunately there are times when you need to get you hands dirty. If your application use only javascript then <a href="https://celerity.rubyforge.org">Celerity</a> may help. But as soon as you incorporate Flash or Java into your application things get much harder. For such situations the <a href="http://seleniumhq.org">Selenium</a> framework should be well-suited. It lets you test your app by simulating user actions through any (supported) web browser. Connection of Selenium and Cucumber seem to be very interesting option too - for more details look at Cucumber wiki.</p>
<p>By default Cucumber uses one browser to open your application and perform selected tests. But from time to time you may need more instances to run in parallel mode - e.g to test real-time user communication. Luckily there is an easy way to override Selenium scripts in Cucumber letting you open another browser instance and switch between them. You just need to add following lines into “env.rb” (or selenium dedicated configuration file).</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vg">$browser2</span> <span class="o">=</span> <span class="no">Webrat</span><span class="o">::</span><span class="no">SeleniumSession</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">selenium</span>
<span class="vg">$browser</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="vg">$browser1</span> <span class="o">=</span> <span class="no">Webrat</span><span class="o">::</span><span class="no">SeleniumSession</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">selenium</span></code></pre></figure>
<p>The second line is necessary to proper initialization of the second browser by Cucumber. After that, you just need to write appropriate steps for Selenium, say:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Given</span> <span class="sr">/^I am using first browser$/</span> <span class="k">do</span>
<span class="k">if</span> <span class="no">Webrat</span><span class="p">.</span><span class="nf">configuration</span><span class="p">.</span><span class="nf">mode</span> <span class="o">==</span> <span class="ss">:selenium</span>
<span class="vg">$browser</span> <span class="o">=</span> <span class="vg">$browser1</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Given</span> <span class="sr">/^I am using second browser$/</span> <span class="k">do</span>
<span class="k">if</span> <span class="no">Webrat</span><span class="p">.</span><span class="nf">configuration</span><span class="p">.</span><span class="nf">mode</span> <span class="o">==</span> <span class="ss">:selenium</span>
<span class="vg">$browser</span> <span class="o">=</span> <span class="vg">$browser2</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>By default the first browser will be active one. But you should not forget that lastly used browser is not restarted between two consecutive tests. You should always switch to the first browser at the end of your test. In addition, there are two useful steps:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">When</span> <span class="sr">/^I wait for page to load$/</span> <span class="k">do</span>
<span class="k">if</span> <span class="no">Webrat</span><span class="p">.</span><span class="nf">configuration</span><span class="p">.</span><span class="nf">mode</span> <span class="o">==</span> <span class="ss">:selenium</span>
<span class="n">selenium</span><span class="p">.</span><span class="nf">wait_for_page_to_load</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Given</span> <span class="sr">/^I wait for juggernaut$/</span> <span class="k">do</span>
<span class="k">if</span> <span class="no">Webrat</span><span class="p">.</span><span class="nf">configuration</span><span class="p">.</span><span class="nf">mode</span> <span class="o">==</span> <span class="ss">:selenium</span>
<span class="nb">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></figure>
<p>First one ensures that all scripts are loaded before moving on to the next steps. Second one gives Juggernaut time to deliver messages. With this configuration you can use all delivered by Cucumber steps without worrying about compatibility problems.</p>Bernard PotockiDuring tests development you usually use well-known and tested solutions like RSpec or Cucumber + Webrat. They let you develop in a fast and convinient way. Unfortunately there are times when you need to get you hands dirty. If your application use only javascript then Celerity may help. But as soon as you incorporate Flash or Java into your application things get much harder. For such situations the Selenium framework should be well-suited. It lets you test your app by simulating user actions through any (supported) web browser. Connection of Selenium and Cucumber seem to be very interesting option too - for more details look at Cucumber wiki.Accents in Sphinx2010-01-14T00:00:00+00:002010-01-14T00:00:00+00:00https://blog.imanel.org/2010/01/14/accents-in-sphinx<p>When writing web applications you need to use full-text search from time to time. <a href="http://www.sphinxsearch.com/">Sphinx</a> or <a href="http://ferret.davebalmain.com/trac">Ferret</a> may be of interest to you in such situation. I do prefer the Sphinx because of its speed and ease of use. But it can pose a problem for you from time to time - especially when searching in languages with accents. By default it does not convert UTF-8 properly - Sphinx treats characters outside the ASCII default set as the separation marks. You can find many pages with a simple solution to this issue - just add appropriate conversion rules to the configuration file:</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">charset_table</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0..9,</span><span class="nv"> </span><span class="s">a..z,</span><span class="nv"> </span><span class="s">_,</span><span class="nv"> </span><span class="s">A..Z->a..z,</span><span class="nv"> </span><span class="s">U+00C0->a,</span><span class="nv"> </span><span class="s">U+00C1->a,</span><span class="nv"> </span><span class="s">U+00C2->a,</span><span class="nv"> </span><span class="s">U+00C3->a,</span><span class="nv"> </span><span class="s">U+00C4->a,</span><span class="nv"> </span><span class="s">U+00C5->a,</span><span class="nv"> </span><span class="s">U+00C7->c,</span><span class="nv"> </span><span class="s">U+00C8->e,</span><span class="nv"> </span><span class="s">U+00C9->e,</span><span class="nv"> </span><span class="s">U+00CA->e,</span><span class="nv"> </span><span class="s">U+00CB->e,</span><span class="nv"> </span><span class="s">U+00CC->i,</span><span class="nv"> </span><span class="s">U+00CD->i,</span><span class="nv"> </span><span class="s">U+00CE->i,</span><span class="nv"> </span><span class="s">U+00CF->i,</span><span class="nv"> </span><span class="s">U+00D1->n,</span><span class="nv"> </span><span class="s">U+00D2->o,</span><span class="nv"> </span><span class="s">U+00D3->o,</span><span class="nv"> </span><span class="s">U+00D4->o,</span><span class="nv"> </span><span class="s">U+00D5->o,</span><span class="nv"> </span><span class="s">U+00D6->o,</span><span class="nv"> </span><span class="s">U+00D9->u,</span><span class="nv"> </span><span class="s">U+00DA->u,</span><span class="nv"> </span><span class="s">U+00DB->u,</span><span class="nv"> </span><span class="s">U+00DC->u,</span><span class="nv"> </span><span class="s">U+00DD->y,</span><span class="nv"> </span><span class="s">U+00E0->a,</span><span class="nv"> </span><span class="s">U+00E1->a,</span><span class="nv"> </span><span class="s">U+00E2->a,</span><span class="nv"> </span><span class="s">U+00E3->a,</span><span class="nv"> </span><span class="s">U+00E4->a,</span><span class="nv"> </span><span class="s">U+00E5->a,</span><span class="nv"> </span><span class="s">U+00E7->c,</span><span class="nv"> </span><span class="s">U+00E8->e,</span><span class="nv"> </span><span class="s">U+00E9->e,</span><span class="nv"> </span><span class="s">U+00EA->e,</span><span class="nv"> </span><span class="s">U+00EB->e,</span><span class="nv"> </span><span class="s">U+00EC->i,</span><span class="nv"> </span><span class="s">U+00ED->i,</span><span class="nv"> </span><span class="s">U+00EE->i,</span><span class="nv"> </span><span class="s">U+00EF->i,</span><span class="nv"> </span><span class="s">U+00F1->n,</span><span class="nv"> </span><span class="s">U+00F2->o,</span><span class="nv"> </span><span class="s">U+00F3->o,</span><span class="nv"> </span><span class="s">U+00F4->o,</span><span class="nv"> </span><span class="s">U+00F5->o,</span><span class="nv"> </span><span class="s">U+00F6->o,</span><span class="nv"> </span><span class="s">U+00F9->u,</span><span class="nv"> </span><span class="s">U+00FA->u,</span><span class="nv"> </span><span class="s">U+00FB->u,</span><span class="nv"> </span><span class="s">U+00FC->u,</span><span class="nv"> </span><span class="s">U+00FD->y,</span><span class="nv"> </span><span class="s">U+00FF->y,</span><span class="nv"> </span><span class="s">U+0100->a,</span><span class="nv"> </span><span class="s">U+0101->a,</span><span class="nv"> </span><span class="s">U+0102->a,</span><span class="nv"> </span><span class="s">U+0103->a,</span><span class="nv"> </span><span class="s">U+0104->a,</span><span class="nv"> </span><span class="s">U+0105->a,</span><span class="nv"> </span><span class="s">U+0106->c,</span><span class="nv"> </span><span class="s">U+0107->c,</span><span class="nv"> </span><span class="s">U+0108->c,</span><span class="nv"> </span><span class="s">U+0109->c,</span><span class="nv"> </span><span class="s">U+010A->c,</span><span class="nv"> </span><span class="s">U+010B->c,</span><span class="nv"> </span><span class="s">U+010C->c,</span><span class="nv"> </span><span class="s">U+010D->c,</span><span class="nv"> </span><span class="s">U+010E->d,</span><span class="nv"> </span><span class="s">U+010F->d,</span><span class="nv"> </span><span class="s">U+0112->e,</span><span class="nv"> </span><span class="s">U+0113->e,</span><span class="nv"> </span><span class="s">U+0114->e,</span><span class="nv"> </span><span class="s">U+0115->e,</span><span class="nv"> </span><span class="s">U+0116->e,</span><span class="nv"> </span><span class="s">U+0117->e,</span><span class="nv"> </span><span class="s">U+0118->e,</span><span class="nv"> </span><span class="s">U+0119->e,</span><span class="nv"> </span><span class="s">U+011A->e,</span><span class="nv"> </span><span class="s">U+011B->e,</span><span class="nv"> </span><span class="s">U+011C->g,</span><span class="nv"> </span><span class="s">U+011D->g,</span><span class="nv"> </span><span class="s">U+011E->g,</span><span class="nv"> </span><span class="s">U+011F->g,</span><span class="nv"> </span><span class="s">U+0120->g,</span><span class="nv"> </span><span class="s">U+0121->g,</span><span class="nv"> </span><span class="s">U+0122->g,</span><span class="nv"> </span><span class="s">U+0123->g,</span><span class="nv"> </span><span class="s">U+0124->h,</span><span class="nv"> </span><span class="s">U+0125->h,</span><span class="nv"> </span><span class="s">U+0128->i,</span><span class="nv"> </span><span class="s">U+0129->i,</span><span class="nv"> </span><span class="s">U+012A->i,</span><span class="nv"> </span><span class="s">U+012B->i,</span><span class="nv"> </span><span class="s">U+012C->i,</span><span class="nv"> </span><span class="s">U+012D->i,</span><span class="nv"> </span><span class="s">U+012E->i,</span><span class="nv"> </span><span class="s">U+012F->i,</span><span class="nv"> </span><span class="s">U+0130->i,</span><span class="nv"> </span><span class="s">U+0134->j,</span><span class="nv"> </span><span class="s">U+0135->j,</span><span class="nv"> </span><span class="s">U+0136->k,</span><span class="nv"> </span><span class="s">U+0137->k,</span><span class="nv"> </span><span class="s">U+0139->l,</span><span class="nv"> </span><span class="s">U+013A->l,</span><span class="nv"> </span><span class="s">U+013B->l,</span><span class="nv"> </span><span class="s">U+013C->l,</span><span class="nv"> </span><span class="s">U+013D->l,</span><span class="nv"> </span><span class="s">U+013E->l,</span><span class="nv"> </span><span class="s">U+0143->n,</span><span class="nv"> </span><span class="s">U+0144->n,</span><span class="nv"> </span><span class="s">U+0145->n,</span><span class="nv"> </span><span class="s">U+0146->n,</span><span class="nv"> </span><span class="s">U+0147->n,</span><span class="nv"> </span><span class="s">U+0148->n,</span><span class="nv"> </span><span class="s">U+014C->o,</span><span class="nv"> </span><span class="s">U+014D->o,</span><span class="nv"> </span><span class="s">U+014E->o,</span><span class="nv"> </span><span class="s">U+014F->o,</span><span class="nv"> </span><span class="s">U+0150->o,</span><span class="nv"> </span><span class="s">U+0151->o,</span><span class="nv"> </span><span class="s">U+0154->r,</span><span class="nv"> </span><span class="s">U+0155->r,</span><span class="nv"> </span><span class="s">U+0156->r,</span><span class="nv"> </span><span class="s">U+0157->r,</span><span class="nv"> </span><span class="s">U+0158->r,</span><span class="nv"> </span><span class="s">U+0159->r,</span><span class="nv"> </span><span class="s">U+015A->s,</span><span class="nv"> </span><span class="s">U+015B->s,</span><span class="nv"> </span><span class="s">U+015C->s,</span><span class="nv"> </span><span class="s">U+015D->s,</span><span class="nv"> </span><span class="s">U+015E->s,</span><span class="nv"> </span><span class="s">U+015F->s,</span><span class="nv"> </span><span class="s">U+0160->s,</span><span class="nv"> </span><span class="s">U+0161->s,</span><span class="nv"> </span><span class="s">U+0162->t,</span><span class="nv"> </span><span class="s">U+0163->t,</span><span class="nv"> </span><span class="s">U+0164->t,</span><span class="nv"> </span><span class="s">U+0165->t,</span><span class="nv"> </span><span class="s">U+0168->u,</span><span class="nv"> </span><span class="s">U+0169->u,</span><span class="nv"> </span><span class="s">U+016A->u,</span><span class="nv"> </span><span class="s">U+016B->u,</span><span class="nv"> </span><span class="s">U+016C->u,</span><span class="nv"> </span><span class="s">U+016D->u,</span><span class="nv"> </span><span class="s">U+016E->u,</span><span class="nv"> </span><span class="s">U+016F->u,</span><span class="nv"> </span><span class="s">U+0170->u,</span><span class="nv"> </span><span class="s">U+0171->u,</span><span class="nv"> </span><span class="s">U+0172->u,</span><span class="nv"> </span><span class="s">U+0173->u,</span><span class="nv"> </span><span class="s">U+0174->w,</span><span class="nv"> </span><span class="s">U+0175->w,</span><span class="nv"> </span><span class="s">U+0176->y,</span><span class="nv"> </span><span class="s">U+0177->y,</span><span class="nv"> </span><span class="s">U+0178->y,</span><span class="nv"> </span><span class="s">U+0179->z,</span><span class="nv"> </span><span class="s">U+017A->z,</span><span class="nv"> </span><span class="s">U+017B->z,</span><span class="nv"> </span><span class="s">U+017C->z,</span><span class="nv"> </span><span class="s">U+017D->z,</span><span class="nv"> </span><span class="s">U+017E->z,</span><span class="nv"> </span><span class="s">U+01A0->o,</span><span class="nv"> </span><span class="s">U+01A1->o,</span><span class="nv"> </span><span class="s">U+01AF->u,</span><span class="nv"> </span><span class="s">U+01B0->u,</span><span class="nv"> </span><span class="s">U+01CD->a,</span><span class="nv"> </span><span class="s">U+01CE->a,</span><span class="nv"> </span><span class="s">U+01CF->i,</span><span class="nv"> </span><span class="s">U+01D0->i,</span><span class="nv"> </span><span class="s">U+01D1->o,</span><span class="nv"> </span><span class="s">U+01D2->o,</span><span class="nv"> </span><span class="s">U+01D3->u,</span><span class="nv"> </span><span class="s">U+01D4->u,</span><span class="nv"> </span><span class="s">U+01D5->u,</span><span class="nv"> </span><span class="s">U+01D6->u,</span><span class="nv"> </span><span class="s">U+01D7->u,</span><span class="nv"> </span><span class="s">U+01D8->u,</span><span class="nv"> </span><span class="s">U+01D9->u,</span><span class="nv"> </span><span class="s">U+01DA->u,</span><span class="nv"> </span><span class="s">U+01DB->u,</span><span class="nv"> </span><span class="s">U+01DC->u,</span><span class="nv"> </span><span class="s">U+01DE->a,</span><span class="nv"> </span><span class="s">U+01DF->a,</span><span class="nv"> </span><span class="s">U+01E0->a,</span><span class="nv"> </span><span class="s">U+01E1->a,</span><span class="nv"> </span><span class="s">U+01E6->g,</span><span class="nv"> </span><span class="s">U+01E7->g,</span><span class="nv"> </span><span class="s">U+01E8->k,</span><span class="nv"> </span><span class="s">U+01E9->k,</span><span class="nv"> </span><span class="s">U+01EA->o,</span><span class="nv"> </span><span class="s">U+01EB->o,</span><span class="nv"> </span><span class="s">U+01EC->o,</span><span class="nv"> </span><span class="s">U+01ED->o,</span><span class="nv"> </span><span class="s">U+01F0->j,</span><span class="nv"> </span><span class="s">U+01F4->g,</span><span class="nv"> </span><span class="s">U+01F5->g,</span><span class="nv"> </span><span class="s">U+01F8->n,</span><span class="nv"> </span><span class="s">U+01F9->n,</span><span class="nv"> </span><span class="s">U+01FA->a,</span><span class="nv"> </span><span class="s">U+01FB->a,</span><span class="nv"> </span><span class="s">U+0200->a,</span><span class="nv"> </span><span class="s">U+0201->a,</span><span class="nv"> </span><span class="s">U+0202->a,</span><span class="nv"> </span><span class="s">U+0203->a,</span><span class="nv"> </span><span class="s">U+0204->e,</span><span class="nv"> </span><span class="s">U+0205->e,</span><span class="nv"> </span><span class="s">U+0206->e,</span><span class="nv"> </span><span class="s">U+0207->e,</span><span class="nv"> </span><span class="s">U+0208->i,</span><span class="nv"> </span><span class="s">U+0209->i,</span><span class="nv"> </span><span class="s">U+020A->i,</span><span class="nv"> </span><span class="s">U+020B->i,</span><span class="nv"> </span><span class="s">U+020C->o,</span><span class="nv"> </span><span class="s">U+020D->o,</span><span class="nv"> </span><span class="s">U+020E->o,</span><span class="nv"> </span><span class="s">U+020F->o,</span><span class="nv"> </span><span class="s">U+0210->r,</span><span class="nv"> </span><span class="s">U+0211->r,</span><span class="nv"> </span><span class="s">U+0212->r,</span><span class="nv"> </span><span class="s">U+0213->r,</span><span class="nv"> </span><span class="s">U+0214->u,</span><span class="nv"> </span><span class="s">U+0215->u,</span><span class="nv"> </span><span class="s">U+0216->u,</span><span class="nv"> </span><span class="s">U+0217->u,</span><span class="nv"> </span><span class="s">U+0218->s,</span><span class="nv"> </span><span class="s">U+0219->s,</span><span class="nv"> </span><span class="s">U+021A->t,</span><span class="nv"> </span><span class="s">U+021B->t,</span><span class="nv"> </span><span class="s">U+021E->h,</span><span class="nv"> </span><span class="s">U+021F->h,</span><span class="nv"> </span><span class="s">U+0226->a,</span><span class="nv"> </span><span class="s">U+0227->a,</span><span class="nv"> </span><span class="s">U+0228->e,</span><span class="nv"> </span><span class="s">U+0229->e,</span><span class="nv"> </span><span class="s">U+022A->o,</span><span class="nv"> </span><span class="s">U+022B->o,</span><span class="nv"> </span><span class="s">U+022C->o,</span><span class="nv"> </span><span class="s">U+022D->o,</span><span class="nv"> </span><span class="s">U+022E->o,</span><span class="nv"> </span><span class="s">U+022F->o,</span><span class="nv"> </span><span class="s">U+0230->o,</span><span class="nv"> </span><span class="s">U+0231->o,</span><span class="nv"> </span><span class="s">U+0232->y,</span><span class="nv"> </span><span class="s">U+0233->y,</span><span class="nv"> </span><span class="s">U+1E00->a,</span><span class="nv"> </span><span class="s">U+1E01->a,</span><span class="nv"> </span><span class="s">U+1E02->b,</span><span class="nv"> </span><span class="s">U+1E03->b,</span><span class="nv"> </span><span class="s">U+1E04->b,</span><span class="nv"> </span><span class="s">U+1E05->b,</span><span class="nv"> </span><span class="s">U+1E06->b,</span><span class="nv"> </span><span class="s">U+1E07->b,</span><span class="nv"> </span><span class="s">U+1E08->c,</span><span class="nv"> </span><span class="s">U+1E09->c,</span><span class="nv"> </span><span class="s">U+1E0A->d,</span><span class="nv"> </span><span class="s">U+1E0B->d,</span><span class="nv"> </span><span class="s">U+1E0C->d,</span><span class="nv"> </span><span class="s">U+1E0D->d,</span><span class="nv"> </span><span class="s">U+1E0E->d,</span><span class="nv"> </span><span class="s">U+1E0F->d,</span><span class="nv"> </span><span class="s">U+1E10->d,</span><span class="nv"> </span><span class="s">U+1E11->d,</span><span class="nv"> </span><span class="s">U+1E12->d,</span><span class="nv"> </span><span class="s">U+1E13->d,</span><span class="nv"> </span><span class="s">U+1E14->e,</span><span class="nv"> </span><span class="s">U+1E15->e,</span><span class="nv"> </span><span class="s">U+1E16->e,</span><span class="nv"> </span><span class="s">U+1E17->e,</span><span class="nv"> </span><span class="s">U+1E18->e,</span><span class="nv"> </span><span class="s">U+1E19->e,</span><span class="nv"> </span><span class="s">U+1E1A->e,</span><span class="nv"> </span><span class="s">U+1E1B->e,</span><span class="nv"> </span><span class="s">U+1E1C->e,</span><span class="nv"> </span><span class="s">U+1E1D->e,</span><span class="nv"> </span><span class="s">U+1E1E->f,</span><span class="nv"> </span><span class="s">U+1E1F->f,</span><span class="nv"> </span><span class="s">U+1E20->g,</span><span class="nv"> </span><span class="s">U+1E21->g,</span><span class="nv"> </span><span class="s">U+1E22->h,</span><span class="nv"> </span><span class="s">U+1E23->h,</span><span class="nv"> </span><span class="s">U+1E24->h,</span><span class="nv"> </span><span class="s">U+1E25->h,</span><span class="nv"> </span><span class="s">U+1E26->h,</span><span class="nv"> </span><span class="s">U+1E27->h,</span><span class="nv"> </span><span class="s">U+1E28->h,</span><span class="nv"> </span><span class="s">U+1E29->h,</span><span class="nv"> </span><span class="s">U+1E2A->h,</span><span class="nv"> </span><span class="s">U+1E2B->h,</span><span class="nv"> </span><span class="s">U+1E2C->i,</span><span class="nv"> </span><span class="s">U+1E2D->i,</span><span class="nv"> </span><span class="s">U+1E2E->i,</span><span class="nv"> </span><span class="s">U+1E2F->i,</span><span class="nv"> </span><span class="s">U+1E30->k,</span><span class="nv"> </span><span class="s">U+1E31->k,</span><span class="nv"> </span><span class="s">U+1E32->k,</span><span class="nv"> </span><span class="s">U+1E33->k,</span><span class="nv"> </span><span class="s">U+1E34->k,</span><span class="nv"> </span><span class="s">U+1E35->k,</span><span class="nv"> </span><span class="s">U+1E36->l,</span><span class="nv"> </span><span class="s">U+1E37->l,</span><span class="nv"> </span><span class="s">U+1E38->l,</span><span class="nv"> </span><span class="s">U+1E39->l,</span><span class="nv"> </span><span class="s">U+1E3A->l,</span><span class="nv"> </span><span class="s">U+1E3B->l,</span><span class="nv"> </span><span class="s">U+1E3C->l,</span><span class="nv"> </span><span class="s">U+1E3D->l,</span><span class="nv"> </span><span class="s">U+1E3E->m,</span><span class="nv"> </span><span class="s">U+1E3F->m,</span><span class="nv"> </span><span class="s">U+1E40->m,</span><span class="nv"> </span><span class="s">U+1E41->m,</span><span class="nv"> </span><span class="s">U+1E42->m,</span><span class="nv"> </span><span class="s">U+1E43->m,</span><span class="nv"> </span><span class="s">U+1E44->n,</span><span class="nv"> </span><span class="s">U+1E45->n,</span><span class="nv"> </span><span class="s">U+1E46->n,</span><span class="nv"> </span><span class="s">U+1E47->n,</span><span class="nv"> </span><span class="s">U+1E48->n,</span><span class="nv"> </span><span class="s">U+1E49->n,</span><span class="nv"> </span><span class="s">U+1E4A->n,</span><span class="nv"> </span><span class="s">U+1E4B->n,</span><span class="nv"> </span><span class="s">U+1E4C->o,</span><span class="nv"> </span><span class="s">U+1E4D->o,</span><span class="nv"> </span><span class="s">U+1E4E->o,</span><span class="nv"> </span><span class="s">U+1E4F->o,</span><span class="nv"> </span><span class="s">U+1E50->o,</span><span class="nv"> </span><span class="s">U+1E51->o,</span><span class="nv"> </span><span class="s">U+1E52->o,</span><span class="nv"> </span><span class="s">U+1E53->o,</span><span class="nv"> </span><span class="s">U+1E54->p,</span><span class="nv"> </span><span class="s">U+1E55->p,</span><span class="nv"> </span><span class="s">U+1E56->p,</span><span class="nv"> </span><span class="s">U+1E57->p,</span><span class="nv"> </span><span class="s">U+1E58->r,</span><span class="nv"> </span><span class="s">U+1E59->r,</span><span class="nv"> </span><span class="s">U+1E5A->r,</span><span class="nv"> </span><span class="s">U+1E5B->r,</span><span class="nv"> </span><span class="s">U+1E5C->r,</span><span class="nv"> </span><span class="s">U+1E5D->r,</span><span class="nv"> </span><span class="s">U+1E5E->r,</span><span class="nv"> </span><span class="s">U+1E5F->r,</span><span class="nv"> </span><span class="s">U+1E60->s,</span><span class="nv"> </span><span class="s">U+1E61->s,</span><span class="nv"> </span><span class="s">U+1E62->s,</span><span class="nv"> </span><span class="s">U+1E63->s,</span><span class="nv"> </span><span class="s">U+1E64->s,</span><span class="nv"> </span><span class="s">U+1E65->s,</span><span class="nv"> </span><span class="s">U+1E66->s,</span><span class="nv"> </span><span class="s">U+1E67->s,</span><span class="nv"> </span><span class="s">U+1E68->s,</span><span class="nv"> </span><span class="s">U+1E69->s,</span><span class="nv"> </span><span class="s">U+1E6A->t,</span><span class="nv"> </span><span class="s">U+1E6B->t,</span><span class="nv"> </span><span class="s">U+1E6C->t,</span><span class="nv"> </span><span class="s">U+1E6D->t,</span><span class="nv"> </span><span class="s">U+1E6E->t,</span><span class="nv"> </span><span class="s">U+1E6F->t,</span><span class="nv"> </span><span class="s">U+1E70->t,</span><span class="nv"> </span><span class="s">U+1E71->t,</span><span class="nv"> </span><span class="s">U+1E72->u,</span><span class="nv"> </span><span class="s">U+1E73->u,</span><span class="nv"> </span><span class="s">U+1E74->u,</span><span class="nv"> </span><span class="s">U+1E75->u,</span><span class="nv"> </span><span class="s">U+1E76->u,</span><span class="nv"> </span><span class="s">U+1E77->u,</span><span class="nv"> </span><span class="s">U+1E78->u,</span><span class="nv"> </span><span class="s">U+1E79->u,</span><span class="nv"> </span><span class="s">U+1E7A->u,</span><span class="nv"> </span><span class="s">U+1E7B->u,</span><span class="nv"> </span><span class="s">U+1E7C->v,</span><span class="nv"> </span><span class="s">U+1E7D->v,</span><span class="nv"> </span><span class="s">U+1E7E->v,</span><span class="nv"> </span><span class="s">U+1E7F->v,</span><span class="nv"> </span><span class="s">U+1E80->w,</span><span class="nv"> </span><span class="s">U+1E81->w,</span><span class="nv"> </span><span class="s">U+1E82->w,</span><span class="nv"> </span><span class="s">U+1E83->w,</span><span class="nv"> </span><span class="s">U+1E84->w,</span><span class="nv"> </span><span class="s">U+1E85->w,</span><span class="nv"> </span><span class="s">U+1E86->w,</span><span class="nv"> </span><span class="s">U+1E87->w,</span><span class="nv"> </span><span class="s">U+1E88->w,</span><span class="nv"> </span><span class="s">U+1E89->w,</span><span class="nv"> </span><span class="s">U+1E8A->x,</span><span class="nv"> </span><span class="s">U+1E8B->x,</span><span class="nv"> </span><span class="s">U+1E8C->x,</span><span class="nv"> </span><span class="s">U+1E8D->x,</span><span class="nv"> </span><span class="s">U+1E8E->y,</span><span class="nv"> </span><span class="s">U+1E8F->y,</span><span class="nv"> </span><span class="s">U+1E96->h,</span><span class="nv"> </span><span class="s">U+1E97->t,</span><span class="nv"> </span><span class="s">U+1E98->w,</span><span class="nv"> </span><span class="s">U+1E99->y,</span><span class="nv"> </span><span class="s">U+1EA0->a,</span><span class="nv"> </span><span class="s">U+1EA1->a,</span><span class="nv"> </span><span class="s">U+1EA2->a,</span><span class="nv"> </span><span class="s">U+1EA3->a,</span><span class="nv"> </span><span class="s">U+1EA4->a,</span><span class="nv"> </span><span class="s">U+1EA5->a,</span><span class="nv"> </span><span class="s">U+1EA6->a,</span><span class="nv"> </span><span class="s">U+1EA7->a,</span><span class="nv"> </span><span class="s">U+1EA8->a,</span><span class="nv"> </span><span class="s">U+1EA9->a,</span><span class="nv"> </span><span class="s">U+1EAA->a,</span><span class="nv"> </span><span class="s">U+1EAB->a,</span><span class="nv"> </span><span class="s">U+1EAC->a,</span><span class="nv"> </span><span class="s">U+1EAD->a,</span><span class="nv"> </span><span class="s">U+1EAE->a,</span><span class="nv"> </span><span class="s">U+1EAF->a,</span><span class="nv"> </span><span class="s">U+1EB0->a,</span><span class="nv"> </span><span class="s">U+1EB1->a,</span><span class="nv"> </span><span class="s">U+1EB2->a,</span><span class="nv"> </span><span class="s">U+1EB3->a,</span><span class="nv"> </span><span class="s">U+1EB4->a,</span><span class="nv"> </span><span class="s">U+1EB5->a,</span><span class="nv"> </span><span class="s">U+1EB6->a,</span><span class="nv"> </span><span class="s">U+1EB7->a,</span><span class="nv"> </span><span class="s">U+1EB8->e,</span><span class="nv"> </span><span class="s">U+1EB9->e,</span><span class="nv"> </span><span class="s">U+1EBA->e,</span><span class="nv"> </span><span class="s">U+1EBB->e,</span><span class="nv"> </span><span class="s">U+1EBC->e,</span><span class="nv"> </span><span class="s">U+1EBD->e,</span><span class="nv"> </span><span class="s">U+1EBE->e,</span><span class="nv"> </span><span class="s">U+1EBF->e,</span><span class="nv"> </span><span class="s">U+1EC0->e,</span><span class="nv"> </span><span class="s">U+1EC1->e,</span><span class="nv"> </span><span class="s">U+1EC2->e,</span><span class="nv"> </span><span class="s">U+1EC3->e,</span><span class="nv"> </span><span class="s">U+1EC4->e,</span><span class="nv"> </span><span class="s">U+1EC5->e,</span><span class="nv"> </span><span class="s">U+1EC6->e,</span><span class="nv"> </span><span class="s">U+1EC7->e,</span><span class="nv"> </span><span class="s">U+1EC8->i,</span><span class="nv"> </span><span class="s">U+1EC9->i,</span><span class="nv"> </span><span class="s">U+1ECA->i,</span><span class="nv"> </span><span class="s">U+1ECB->i,</span><span class="nv"> </span><span class="s">U+1ECC->o,</span><span class="nv"> </span><span class="s">U+1ECD->o,</span><span class="nv"> </span><span class="s">U+1ECE->o,</span><span class="nv"> </span><span class="s">U+1ECF->o,</span><span class="nv"> </span><span class="s">U+1ED0->o,</span><span class="nv"> </span><span class="s">U+1ED1->o,</span><span class="nv"> </span><span class="s">U+1ED2->o,</span><span class="nv"> </span><span class="s">U+1ED3->o,</span><span class="nv"> </span><span class="s">U+1ED4->o,</span><span class="nv"> </span><span class="s">U+1ED5->o,</span><span class="nv"> </span><span class="s">U+1ED6->o,</span><span class="nv"> </span><span class="s">U+1ED7->o,</span><span class="nv"> </span><span class="s">U+1ED8->o,</span><span class="nv"> </span><span class="s">U+1ED9->o,</span><span class="nv"> </span><span class="s">U+1EDA->o,</span><span class="nv"> </span><span class="s">U+1EDB->o,</span><span class="nv"> </span><span class="s">U+1EDC->o,</span><span class="nv"> </span><span class="s">U+1EDD->o,</span><span class="nv"> </span><span class="s">U+1EDE->o,</span><span class="nv"> </span><span class="s">U+1EDF->o,</span><span class="nv"> </span><span class="s">U+1EE0->o,</span><span class="nv"> </span><span class="s">U+1EE1->o,</span><span class="nv"> </span><span class="s">U+1EE2->o,</span><span class="nv"> </span><span class="s">U+1EE3->o,</span><span class="nv"> </span><span class="s">U+1EE4->u,</span><span class="nv"> </span><span class="s">U+1EE5->u,</span><span class="nv"> </span><span class="s">U+1EE6->u,</span><span class="nv"> </span><span class="s">U+1EE7->u,</span><span class="nv"> </span><span class="s">U+1EE8->u,</span><span class="nv"> </span><span class="s">U+1EE9->u,</span><span class="nv"> </span><span class="s">U+1EEA->u,</span><span class="nv"> </span><span class="s">U+1EEB->u,</span><span class="nv"> </span><span class="s">U+1EEC->u,</span><span class="nv"> </span><span class="s">U+1EED->u,</span><span class="nv"> </span><span class="s">U+1EEE->u,</span><span class="nv"> </span><span class="s">U+1EEF->u,</span><span class="nv"> </span><span class="s">U+1EF0->u,</span><span class="nv"> </span><span class="s">U+1EF1->u,</span><span class="nv"> </span><span class="s">U+1EF2->y,</span><span class="nv"> </span><span class="s">U+1EF3->y,</span><span class="nv"> </span><span class="s">U+1EF4->y,</span><span class="nv"> </span><span class="s">U+1EF5->y,</span><span class="nv"> </span><span class="s">U+1EF6->y,</span><span class="nv"> </span><span class="s">U+1EF7->y,</span><span class="nv"> </span><span class="s">U+1EF8->y,</span><span class="nv"> </span><span class="s">U+1EF9->y"</span></code></pre></figure>
<p>It turns out that it is not enough - even after adding this piece of code some of the polish (that’s my native language) signs were not recognized properly. After some searching I found a reason - polish “tails” were written not as characters in UTF-8 but as an html entities. There’s a simple and clean solution - add following line to your configuration file:</p>
<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">html_strip</span><span class="pi">:</span> <span class="s">1</span></code></pre></figure>
<p>And now everything should work great - the only drawback is that the Sphinx ignores accents and converts them into ASCII characters. But that’s a low price in exchange for functionality that Sphinx gives to you.</p>Bernard PotockiWhen writing web applications you need to use full-text search from time to time. Sphinx or Ferret may be of interest to you in such situation. I do prefer the Sphinx because of its speed and ease of use. But it can pose a problem for you from time to time - especially when searching in languages with accents. By default it does not convert UTF-8 properly - Sphinx treats characters outside the ASCII default set as the separation marks. You can find many pages with a simple solution to this issue - just add appropriate conversion rules to the configuration file: