<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-752094859566372098</id><updated>2012-01-31T02:42:17.310+03:00</updated><category term='Games'/><category term='Ruby and Rails'/><category term='RightJS'/><category term='Git'/><category term='LovelyIO'/><category term='Linux'/><category term='Projects'/><category term='Emacs'/><category term='NodeJS'/><category term='HTML/CSS'/><category term='JavaScript'/><category term='Ideas'/><category term='Articles'/><category term='Blog'/><title type='text'>St. on IT</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default?start-index=101&amp;max-results=100'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>126</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2028012077470461308</id><published>2011-08-07T16:07:00.001+03:00</published><updated>2011-08-07T16:09:25.436+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML/CSS'/><category scheme='http://www.blogger.com/atom/ns#' term='LovelyIO'/><title type='text'>The Lovely Future Of RightJS</title><content type='html'>Okay, let's make it official, shall we?&lt;br /&gt;&lt;br /&gt;Folks, I'm forking RightJS. And before you start screaming like "omg, we all gonna die in here!", calm down, RightJS ain't no going nowhere. The thing I do is future oriented and quite different from RightJS itself. So RightJS will stay where it is, I'm using all the same servers for the fork, so RightJS will be available as long as people use it. And I certainly will apply patches as much as I can.&lt;br /&gt;&lt;br /&gt;Just, there will be no RightJS 3.0, RightJS 3.0 will be called &lt;a href="http://lovely.io"&gt;lovely.io&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;So, What's That All Aboot?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Folks, it's not a secret, that monolithic frameworks are dead. Dead as Java. The future is in small, agile and modular libraries and this is where &lt;a href="http://lovely.io"&gt;lovely.io&lt;/a&gt; starts.&lt;br /&gt;&lt;br /&gt;In RightJS we already had kinda modular structure, you could go to the building page and switch off things that you don't need. The problem with this approach is that it is not suitable for the nova-days CDN hosting, all the permutations of a dozen options were blowing the 15k small library into a huge collection of files that weighted several hundreds megabytes and was almost uncacheable. You could host it by uself, but on CDN even if we host it, the chances that another user has the same configurations are slim and it would be pretty much the same as if you host it on your own domain.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lovely.io"&gt;Lovely.IO&lt;/a&gt; is not really a framework as you might think of a normal javascript framework. It is more like a CDN based package management system. In &lt;a href="http://lovely.io"&gt;lovely.io&lt;/a&gt; all the RightJS core got split apart on several packages.&lt;br /&gt;&lt;br /&gt; * 'core' - basically RigihtJS Class, Events (former Observer), utility functions + some new stuff&lt;br /&gt; * 'dom' - the dom manipulations from RightJS + new jQuery like collections handler&lt;br /&gt; * 'ajax' - new XHR2 based ajax module&lt;br /&gt; * 'cookie' - the cookie module&lt;br /&gt; * 'lang' - JavaScript core extensions&lt;br /&gt; * 'sugar' - syntax sugar for the 'dom' module, things like "something".something() and so on.&lt;br /&gt;&lt;br /&gt;Every package is pretty much independent and you can choose what you want. If all you want is 'dom' you just hook it up and it will be just that. If you also like some JavaScript core extensions, then you hook the 'lang' module. Need to do some ajax? Include the ajax module. It's completely up to you what load and what not.&lt;br /&gt;&lt;br /&gt;A generic example would look like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&amp;lt;html&gt;&lt;br /&gt;  &amp;lt;head&gt;&lt;br /&gt;    &amp;lt;script src="http://cdn.lovely.io/core.js"&gt;&amp;lt;/script&gt;&lt;br /&gt;    &amp;lt;script type="text/javascript"&gt;&lt;br /&gt;      Lovely(["dom", "ajax", "fx"], function($, ajax) {&lt;br /&gt;        ajax.load("/some.url", {&lt;br /&gt;          success: function() {&lt;br /&gt;            $('#some-element').html(this.text).highlight();&lt;br /&gt;          }&lt;br /&gt;        });&lt;br /&gt;      });&lt;br /&gt;    &amp;lt;/script&gt;&lt;br /&gt;  &amp;lt;/head&gt;&lt;br /&gt;&amp;lt;/html&gt;&lt;/pre&gt;&lt;br /&gt;Basically wha happening in there is that you firstly include the &lt;a href="http://lovely.io/packages/core"&gt;lovely.io core package&lt;/a&gt; which just 3k of code and provides the modules handling. After that, you can specify the modules you need and once they're loaded (happens asynchronously) your callback function will be called where you can do whatever is it you need to do.&lt;br /&gt;&lt;br /&gt;The example above is a bit simplistic. In reality it also automatically handles all internal dependencies, versions and so on. Check out &lt;a href="http://lovely.io/packages/mines-game/demo"&gt;this example&lt;/a&gt;, it's a little game, it uses 'dom' and 'cookies', but it also uses another packages like 'timer' and 'hiscore' to function.&lt;br /&gt;&lt;br /&gt;This way we are going to have a rubygems like system where anyone can create and share modules, which can relay and reuse each other. All modules including the standard ones will be more or less equal and independent.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Automatic Packages Hosting&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Another thing that I'm solving with lovely.io is automatic packages hosting. No more mangling with CDN, demos, documentation and so on. Once you create a package it will be automatically hosted at &lt;tt&gt;cdn.lovely.io&lt;/tt&gt; which sits on Amazon's CloudFront. And it's not just scripts, all your asset images also will be there, automatically and free.&lt;br /&gt;&lt;br /&gt;The basic idea is to completely eliminate the process of modules installation. All your package users will have to do is to specify the package name and version in the dependencies, all the reset will be automatically served from the super fast amazon hosting.&lt;br /&gt;&lt;br /&gt;Check the &lt;a href="http://lovely.io/packages/mines-game/demo"&gt;mines game&lt;/a&gt; I mentioned above. Take a look into the HTML code of the page. It is fairly complex widget, it has some styles and depends on some images, more of that it is a compound widget, the hiscore block is provided by a completely different package. Yet all you have to do to make it working is this&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&amp;lt;script type='text/javascript'&gt;&lt;br /&gt;  Lovely(["mines-game-1.0.1"], function(Game) {&lt;br /&gt;    new Game().insertTo('#game-container');&lt;br /&gt;  });&lt;br /&gt;&amp;lt;/script&gt;&lt;/pre&gt;&lt;br /&gt;And it's not all. The micro-modular structure also will provide a big deal of caching optimization. As all the modules are hosted at the same CDN domain, they all will be cached one by one in the user browsers. And because of that the most frequently used packages will be always in cache.&lt;br /&gt;&lt;br /&gt;Say a user opens up a page that uses packages A and B, then he goes to a page that uses A, B and C. All he really downloads on the second page is C.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;About The Packages&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The cool stuff is not over people. Concentrate, because there is more of awesome news!&lt;br /&gt;&lt;br /&gt;The other thing is how you actually make the packages. By default there is no JavaScript. Yup, you've heard me. The actual packages code is written in CoffeeScript and SASS (though you can use JavaScript + CSS if you like). Check for example &lt;a href="https://github.com/MadRabbit/lovely.io-games/tree/master/mines"&gt;the source code of the  mines game&lt;/a&gt; from the above. It is all nice and clean now.&lt;br /&gt;&lt;br /&gt;How it works? Quite simple. Lovely.IO is not just a hosting. It also has a CLI tool called `lovely` which is much like ruby-on-rails thingy. You can create a project, you can launch a development server and so on.&lt;br /&gt;&lt;br /&gt;Then you write your code in whatever you like, and once you're ready do share it with the public, the tool will compile everything in JavaScript, minify using Ugly and push to the lovely.io server. More of that it will automatically use your README and index.html files as landing and demo pages on the server.&lt;br /&gt;&lt;br /&gt;You can structure your project as you like, you can have as many files as you like. You can use CoffeeScript or JavaScript, SASS or CSS, or even Stylus. It all doesn't matter for the end result.&lt;br /&gt;&lt;br /&gt;Finally you can do the front-side development in a clean and civilized way.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Other Stuff&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;There is one thing among this awesomeness though. Lovely.IO is an HTML5 oriented thingy, there is no IE &amp;lt; 9 support, not like I couldn't, just it's thankless job to support those old browsers. I don't wanna do that, you don't wanna do that, and the guy next to you don't wanna do that either.&lt;br /&gt;&lt;br /&gt;So, in lovely.io there will be no old IE browsers support in the standard packages, plus there is a good chance that I'll be nuking any other packages that will try to provide the old browsers support explicitly.&lt;br /&gt;&lt;br /&gt;Basically, this is where I'd like to draw the line between RightJS and LovelyIO. If you need something well tested with old browsers support, use RightJS, it will be around as long as you want it. But, if you're building a modern HTML5/CSS3 application then you might want to consider LovelyIO, this is where all new stuff will go.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Current Status and Future Plans&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;At the moment Lovely.IO has all the modules from RightJS core + RubyOnRails support module + I've made a bunch of demo projects &lt;a href="https://github.com/MadRabbit/lovely.io-games"&gt;over here&lt;/a&gt;. It appears to be more or less stable (it's based on RightJS after all), but there is no UI yet.&lt;br /&gt;&lt;br /&gt;There is also not much of documentation either. I've just opened the registration for early adopters. So if you wanna play with it, please check &lt;a href="https://github.com/MadRabbit/lovely.io/tree/master/stl"&gt;the main repository&lt;/a&gt;, this is where the STL packages live. It's mostly the good old RightJS with a bit of lean towards more standard jQuery behavior for the &lt;tt&gt;$&lt;/tt&gt; function.&lt;br /&gt;&lt;br /&gt;The next step in the project will be automatic documentation hosting for the packages and then I'll start to port UI modules from RightJS, so if you want me to port something specific from RightJS UI, let me know I'll try to make it a priority.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;Okay, I think that's the whole introduction for now. I'll give you more when I get to the documentation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2028012077470461308?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2028012077470461308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2028012077470461308' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2028012077470461308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2028012077470461308'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/08/lovely-future-of-rightjs.html' title='The Lovely Future Of RightJS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3964418746272783487</id><published>2011-05-28T17:30:00.004+03:00</published><updated>2011-05-28T17:43:29.716+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Calculating Cubic Bezier Function</title><content type='html'>If you ever played with the CSS3 native css-transitions, you know that they use &lt;tt&gt;cubic-bezier&lt;/tt&gt; notation to define the transition formula. &lt;a href="http://en.wikipedia.org/wiki/Bézier_curve"&gt;Cubic Bezier&lt;/a&gt; is a parametric function that allows you to define all sorts of smooth curvatures that looks kinda like that&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/commons/f/ff/Bezier_3_big.gif" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The reason why it's used instead of sinuses and exponents, is that it allows you to define all sorts of shapes with just a few more or less intuitively understandable parameters. Plus, unlike other mathematical functions it can be computed very fast, because it's a simple parametric function, no sequences no nothing.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; we support native css3 transitions, but it also has a pure JavaScript fx-engine to handle old browsers. And the thing is that native CSS3 transitions use the cubic-bezier functions, while the pure JavaScript engine uses a bit of mumbo-jumbo with &lt;tt&gt;Math.sin()&lt;/tt&gt; and &lt;tt&gt;Math.log()&lt;/tt&gt;, and what I wanted is to allow the developers to use cubic-bezier notation with the old engine.&lt;br /&gt;&lt;br /&gt;The problem is that it's not that simple. The cubic-bezier function itself is pretty much straightforward and you can calculate a set of X and Y coordinates in no time, but it won't make you any good because both x-s and y-s will be related to the third &lt;tt&gt;t&lt;/tt&gt; parameter and therefore unevenly distributed agains the &lt;tt&gt;t&lt;/tt&gt; parameter. What you really need is a straight &lt;tt&gt;Y = B(x)&lt;/tt&gt; function.&lt;br /&gt;&lt;br /&gt;And here it gets tricky. &lt;a href="http://gamedev.stackexchange.com/questions/5373/moving-ships-between-two-planets-along-a-bezier-missing-some-equations-for-accel"&gt;That much tricky&lt;/a&gt;. But, luckily for us, there is always a better way to do it. All you need is to recall a bit of kindergarden math.&lt;br /&gt;&lt;br /&gt;Originally, the cubic bezier function is used in a parametric form, but it also can be converted into a polynomial form, in which case it will look like that (you can google it and find say &lt;a href="http://www.algorithmist.net/bezier3.html"&gt;this document&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:"&gt;a = 3 P0&lt;br /&gt;b = 3 P1&lt;br /&gt;c = 3 P2&lt;br /&gt;c0 = P0&lt;br /&gt;c1 = b - a&lt;br /&gt;c2 = a - 2b + c&lt;br /&gt;c3 = P3 - P0 + b - c&lt;br /&gt;&lt;br /&gt;B(t) = c0 + t(c1 + t(c2 + tc3))&lt;/pre&gt;&lt;br /&gt;In case of CSS3 cubic-bezier definitions, &lt;tt&gt;P0&lt;/tt&gt; is always &lt;tt&gt;0&lt;/tt&gt; and &lt;tt&gt;P3&lt;/tt&gt; is always &lt;tt&gt;1&lt;/tt&gt;, in which case the formula gets even simpler&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:"&gt;c3 = 3 * P1&lt;br /&gt;c2 = 3 * (P2 - P1) - c3&lt;br /&gt;c1 = 1 - c3 - c2&lt;br /&gt;&lt;br /&gt;B(t) = t * (c1 + t * (c2 + t * c3))&lt;/pre&gt;&lt;br /&gt;Which, in case of of CSS3 &lt;tt&gt;x1,y1,x2,y2&lt;/tt&gt; parameters, can be written in javascript with two functions, for example this way&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var Cx = 3 * x1;&lt;br /&gt;var Bx = 3 * (x2 - x1) - Cx;&lt;br /&gt;var Ax = 1 - Cx - Bx;&lt;br /&gt;&lt;br /&gt;var Cy = 3 * y1;&lt;br /&gt;var By = 3 * (y2 - y1) - Cy;&lt;br /&gt;var Ay = 1 - Cy - By;&lt;br /&gt;&lt;br /&gt;function bezier_x(t) {&lt;br /&gt;  return t * (Cx + t * (Bx + t * Ax));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function bezier_y(t) {&lt;br /&gt;  return t * (Cy + t * (By + t * Ay));&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;There are two reasons why we use the polynomial form of the equation. Firstly, it has less operations and therefore will work faster. Secondly, we are going to use Newton's method to approximate the values and will need a first derivative of one of the functions, which in case of polynomial functions is pretty simple.&lt;br /&gt;&lt;br /&gt;Now, to the fun part. We converted the function from one form to another, but the question remains the same, how are we going to get the &lt;tt&gt;y = B(x)&lt;/tt&gt; from &lt;tt&gt;x = B(t) and y = B(t)&lt;/tt&gt;? Quite simple my friends, we will use the kindergarden math, or more exactly the Newton's method to approximate a parametric &lt;tt&gt;x&lt;/tt&gt; value for every &lt;tt&gt;t&lt;/tt&gt; value so that we could get an evenly distributed set of &lt;tt&gt;x&lt;/tt&gt; values and then get a set of &lt;tt&gt;y&lt;/tt&gt; for them using the same &lt;tt&gt;bezier_y(t)&lt;/tt&gt; function.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Newton's_method"&gt;Newton's method&lt;/a&gt; might look a bit scary for those who are not familiar with it, but it is actually very simple. &lt;a href="http://www.youtube.com/watch?v=1uN8cBGVpfs"&gt;Check this video&lt;/a&gt; the guy explains it nicely.&lt;br /&gt;&lt;br /&gt;All you need to make it work is to have a derivative to your function, which in our case will look like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;function bezier_x_der(x) {&lt;br /&gt;  return Cx + t * (2*Bx + t * 3*Ax);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;And after that, we just define a little function that will make several iterations trying to find an &lt;tt&gt;x&lt;/tt&gt; value which came from the current &lt;tt&gt;t&lt;/tt&gt; value. We don't have to be very precise in our case, we just make 5 iterations tops and limit the precision by the 3 digits after the pointer.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;function find_x_for(t) {&lt;br /&gt;  var x = t, i = 0, z;&lt;br /&gt;&lt;br /&gt;  while (i &lt; 5) {&lt;br /&gt;    z = bezier_x(x) - t;&lt;br /&gt;    if (Math.abs(z) &lt; 1e-3) break;&lt;br /&gt;&lt;br /&gt;    x = x - z / bezier_x_der(x);&lt;br /&gt;    i++;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  return x;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now you can create a loop and calculate a set of evenly distributed values, just like that:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var result = [];&lt;br /&gt;for (var i=0; i &lt; 1.001; i+=0.1) {&lt;br /&gt;  result.push(bezier_y(find_x_for(i)));&lt;br /&gt;}&lt;br /&gt;console.log(result);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;That's basically all of it. You can find the full version of this script &lt;a href="https://gist.github.com/996893"&gt;in this gist&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3964418746272783487?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3964418746272783487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3964418746272783487' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3964418746272783487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3964418746272783487'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/05/calculating-cubic-bezier-function.html' title='Calculating Cubic Bezier Function'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8698912577403155961</id><published>2011-05-08T18:29:00.002+03:00</published><updated>2011-05-08T18:58:03.231+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NodeJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>How To Read User Input With NodeJS</title><content type='html'>I think it's time for me to join the NodeJS hysteria. So, I'd like to share a bit of things I was tackling today because there is not really much of docs on that matter right now.&lt;br /&gt;&lt;br /&gt;Short things short. If you're writing a little console script in NodeJS that supposed to ask the user this and that, here's how you can get it done.&lt;br /&gt;&lt;br /&gt;Node's global object &lt;tt&gt;process&lt;/tt&gt; has two properties called &lt;tt&gt;.stdin&lt;/tt&gt; and &lt;tt&gt;.stdout&lt;/tt&gt;, which are essentially streams. You can write things into the &lt;tt&gt;stdout&lt;/tt&gt; and listen to the &lt;tt&gt;'data'&lt;/tt&gt; event in the &lt;tt&gt;stdin&lt;/tt&gt; stream. A simple example from the api-docs looks like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;process.stdin.resume();&lt;br /&gt;process.stdin.setEncoding('utf8');&lt;br /&gt;&lt;br /&gt;process.stdin.on('data', function (chunk) {&lt;br /&gt; process.stdout.write('data: ' + chunk);&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;You need to call that &lt;tt&gt;.resume()&lt;/tt&gt; method first, it initializes the STDIN reading process.&lt;br /&gt;&lt;br /&gt;It is pretty straightforward, but it's almost useless when you need to ask the user several questions, more of that validate the input data and re-ask questions when something was entered wrong.&lt;br /&gt;&lt;br /&gt;The basic trouble here is that &lt;tt&gt;.on('data'&lt;/tt&gt; handler that will fire every time the user hits the &lt;tt&gt;Enter&lt;/tt&gt;, so you'll have to figure out which question is currently asked an where to put the data. Given the fact that everything is happening asynchronously, it pretty quickly gets really messy.&lt;br /&gt;&lt;br /&gt;Fortunately for us, there is another events handler in NodeJS called &lt;tt&gt;.once(..&lt;/tt&gt; it's basically same as &lt;tt&gt;.on(&lt;/tt&gt; but it detaches the listener after the first event received. Knowing that, we can write a little helper function like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;function ask(question, format, callback) {&lt;br /&gt; var stdin = process.stdin, stdout = process.stdout;&lt;br /&gt;&lt;br /&gt; stdin.resume();&lt;br /&gt; stdout.write(question + ": ");&lt;br /&gt;&lt;br /&gt; stdin.once('data', function(data) {&lt;br /&gt;   data = data.toString().trim();&lt;br /&gt;&lt;br /&gt;   if (format.test(data)) {&lt;br /&gt;     callback(data);&lt;br /&gt;   } else {&lt;br /&gt;     stdout.write("It should match: "+ format +"\n");&lt;br /&gt;     ask(question, format, callback);&lt;br /&gt;   }&lt;br /&gt; });&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;There are two important moments. First of all, don't use &lt;tt&gt;console.log&lt;/tt&gt; to ask the user the question, otherwise it will print a new line at the end, which is kinda sucks. Secondly note that you should call &lt;tt&gt;.toString()&lt;/tt&gt; and &lt;tt&gt;.trim()&lt;/tt&gt; on your data, the first one to convert the data from a stream into an actual string, the second one is needed because the string will have a trailing new line symbol at the end after the user hits the &lt;tt&gt;Enter&lt;/tt&gt; key.&lt;br /&gt;&lt;br /&gt;After that, you can ask the user all sorts of questions. For example like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;ask("Name", /.+/, function(name) {&lt;br /&gt;  ask("Email", /^.+@.+$/, function(email) {&lt;br /&gt;    console.log("Your name is: ", name);&lt;br /&gt;    console.log("Your email is:", email);&lt;br /&gt;&lt;br /&gt;    process.exit();&lt;br /&gt;  });&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;And don't forget to call the &lt;tt&gt;process.exit()&lt;/tt&gt; when you done, otherwise the script will just hung in there.&lt;br /&gt;&lt;br /&gt;That's basically all of it. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8698912577403155961?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8698912577403155961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8698912577403155961' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8698912577403155961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8698912577403155961'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/05/how-to-read-user-input-with-nodejs.html' title='How To Read User Input With NodeJS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-6482564755592256060</id><published>2011-04-28T17:20:00.003+03:00</published><updated>2011-04-28T17:55:24.166+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Why Make A Run For It</title><content type='html'>It has been almost two years since I decided to go public with RightJS and on this occasion I'd like to post some reflections on it.&lt;br /&gt;&lt;br /&gt;Those days, thanks to github, it's pretty simple to start an open-source project. You just make a repo, write some awesome stuff and push it under the "I don't owe you shit" license. And I like it, in most cases that's all you really need. But, as fun as it is, there is actually another level to open-source projects. The level, where you decide to get serious about it and go public with your project. With promotions, users, official site, docs, tutorials and so on.&lt;br /&gt;&lt;br /&gt;It is not completely necessary to make your project kinda "officially public", in many cases it might be even a bad idea, because that will distract you from the actual programming, but there are several important things in this process, which can be pretty valuable as well. And that's what I'd like to write about today.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;It Will Make You A Better Programmer&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The trouble with programmers is that we work in this area where most people don't really understand anything. And because of that, at some level, programmers tend to start thinking that they are pretty darn smart and kick ass.&lt;br /&gt;&lt;br /&gt;A healthy portion of self-esteem is not a bad thing, it might help you to overcome some small obstacles on your way, but for a programmer as a knowledge worker it is crucial to stay hungry for new information.&lt;br /&gt;&lt;br /&gt;And running your project for real, will give you exactly that. If you do something interesting enough, people will respond, they will throw all sorts of ideas at you, they will criticize and they will like (hopefully :)) some parts of your work, which will give you all sorts of new information.&lt;br /&gt;&lt;br /&gt;With new information you'll face new problems and discover new ways to solve them, and that will make you better at what you do.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;It Will Make You More Organized&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;It is not a secret that many developers don't like TDD and Tickets. Even the ones that do, often cheat and don't cover everything, don't document bugs and so on.&lt;br /&gt;&lt;br /&gt;But, when you start an open project for real, you will have to deal with those things. You will have to handle tickets, document bugs and cover everything you can with tests so that folks who use your code had a hight quality experience.&lt;br /&gt;&lt;br /&gt;More of that, most open-source projects, especially individual ones, are just about "scratching your own itch". It is highly doubtful that you will make any real money directly from your project, and as much as you might enjoy the creative process of writing code, at some point, you will have to get pragmatic about what you do.&lt;br /&gt;&lt;br /&gt;You will need to start thinking strategically, where is it worth to invest your time and where it's not. And that is a highly valuable skill, not just for developing a software project, but genuinely in life.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;It Will Make You Better With People&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Well, you know what they say "all programmers are bad with people" :). The thing is, that people are irrational, even the smart ones. And there is only one way to learn how to deal with irrational - by accumulating experience.&lt;br /&gt;&lt;br /&gt;One of the hazards of the trade is that programmers spend most of their time with computers, and that's certainly not a good thing for your social skills.&lt;br /&gt;&lt;br /&gt;When you run an open-source project you will have to handle contributors and do that nicely. Because contributors are privilege. But as all people have different coding styles, different ways to approach issues and just different personalities, you will learn how to find common grounds with different folks, how to work together and how to coordinate the changes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I don't want to sound like an open-source project is the most awesome thing that might happen to you. By the end of the day, if you're vicious bastard then all that will help you is an old-fashioned kick in the ass.&lt;br /&gt;&lt;br /&gt;But, if you're interested in software-development, if you want to learn why and how the things done, running your own project for real might be very insightful and teach you quite a few things.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-6482564755592256060?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/6482564755592256060/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=6482564755592256060' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6482564755592256060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6482564755592256060'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/04/why-make-run-for-it.html' title='Why Make A Run For It'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7648824041042326943</id><published>2011-04-04T16:12:00.010+03:00</published><updated>2011-05-06T17:31:59.726+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Making Tags With Rails And RightJS</title><content type='html'>As the &lt;a href="http://rightjs.org/ui/tags"&gt;tags widget&lt;/a&gt; appeared recently on the &lt;a href="http://rightjs.org/ui"&gt;RightJS UI&lt;/a&gt; list, I'd like to write a simple how-to about tags in case of using RubyOnRails.&lt;br /&gt;&lt;br /&gt;You can see the tags widget in action on &lt;a href="http://rightjs.org/ui/tags/demo"&gt;this demo page&lt;/a&gt;, and &lt;a href="https://github.com/MadRabbit/right-rails-demo"&gt;over here&lt;/a&gt; you can find a demo rails application that has the tags feature implemented, along with some other &lt;a href="https://github.com/MadRabbit/right-rails"&gt;right-rails&lt;/a&gt; features.&lt;br /&gt;&lt;br /&gt;Let's start&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Initial Model&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When you need to add a tags feature in a ruby-on-rails project, a standard model would look kinda like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;class Article &lt; ActiveRecord::Base&lt;br /&gt;  has_and_belongs_to_many :tags, :uniq =&gt; true&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Tag &lt; ActiveRecord::Base&lt;br /&gt;  has_and_belongs_to_many :articles, :uniq =&gt; true&lt;br /&gt;&lt;br /&gt;  validates_presence_of :name&lt;br /&gt;  validates_uniqueness_of :name&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;As you can see, it's a pretty much straightforward m2m association, articles have many tags and tags have many articles.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tags To Strings Conversion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The next standard step is usually to add a couple of methods to convert the list of tags into a coma separated string back and forth. The basic reason is that tags are data-base records and it's not much of user-friendly to use integer ids&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;class Article &lt; ActiveRecord::Base&lt;br /&gt;  has_and_belong_to_many :tags, :uniq =&gt; true&lt;br /&gt;&lt;br /&gt;  def tags_string&lt;br /&gt;    tags.map(&amp;:name).join(', ')&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def tags_string=(string)&lt;br /&gt;    self.tags = string.split(',').map do |tag|&lt;br /&gt;      unless tag.blank?&lt;br /&gt;        Tag.find_or_create_by_name(tag.strip)&lt;br /&gt;      end&lt;br /&gt;    end.compact&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;After that you can use this proxy property in forms directly, like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&amp;lt;%= form_for @article do |f| %&gt;&lt;br /&gt;  &amp;lt;p&gt;&lt;br /&gt;    &amp;lt;%= f.label :tags_string %&gt;&lt;br /&gt;    &amp;lt;%= f.text_field :tags_string %&gt;&lt;br /&gt;  &amp;lt;/p&gt;&lt;br /&gt;&amp;lt;% end %&gt;&lt;/pre&gt;&lt;br /&gt;This way the user will always manipulate the tags list as a simple string and your app will automatically create the tag records and associations on fly&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Hooking Up RightJS&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;At this point your rails app will present a kind of a standard and simple case of tags handling. Time to make it look nice and friendly with a bit of javascript.&lt;br /&gt;&lt;br /&gt;To hook up RightJS into your Rails application all you need is just add the &lt;tt&gt;right-rails&lt;/tt&gt; gem into your Gemfile&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;gem 'right-rails'&lt;/pre&gt;&lt;br /&gt;And run the following code generator&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;rails g right_rails&lt;/pre&gt;&lt;br /&gt;Don't worry when you see a tall list of scripts it copies into your &lt;tt&gt;public/javascripts&lt;/tt&gt; directory, right-rails handles most of them automatically, so you won't really need to look inside of those.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Adding The Tags Widget&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Ironically, the actual RightJS part of this enterprise is the shortest one. Once you added &lt;tt&gt;right-rails&lt;/tt&gt; into your application, all you need to do is replace &lt;tt&gt;'f.text_field'&lt;/tt&gt; with &lt;tt&gt;'f.tags_field'&lt;/tt&gt; in your form&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&amp;lt;%= form_for @article do |f| %&gt;&lt;br /&gt;  &amp;lt;p&gt;&lt;br /&gt;    &amp;lt;%= f.label :tags_string %&gt;&lt;br /&gt;    &amp;lt;%= f.tags_field :tags_string %&gt;&lt;br /&gt;  &amp;lt;/p&gt;&lt;br /&gt;&amp;lt;% end %&gt;&lt;/pre&gt;&lt;br /&gt;RightRails handles everything else automatically, it generates all necessary html, includes javascript/css modules, it even switches between minified and source builds of javascript libraries depending on the working environment.&lt;br /&gt;&lt;br /&gt;More of that, it can automatically use the google hosted &lt;a href="http://cdn.rightjs.org"&gt;rightjs cdn-server&lt;/a&gt; for a superfast, shared scripts delivery.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Adding Autocompletion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;By default the &lt;tt&gt;f.tags_field&lt;/tt&gt; will make you a pretty looking tags-field, if you want to add the tags autocompletion feature to it, all you need is to specify a list of known tags in it&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;....&lt;br /&gt;  &amp;lt;%= f.tags_field :tags_string, :tags =&gt; Tag.all.map(&amp;:name) %&gt;&lt;br /&gt;....&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There is also a few additional options you can use with tags widgets, you can find them at the &lt;a href="http://rightjs.org/ui/tags#options"&gt;rightjs.org documentation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This is a good example to show how &lt;tt&gt;RightJS&lt;/tt&gt; is build with server-side developers in mind. As you have noticed we didn't write a single line of javascript in our little exercise, not like we couldn't, just we don't have to bother with those routine things every time we need a simple form with a bunch of standard widgets.&lt;br /&gt;&lt;br /&gt;More of that, if you take a look into your HTML code, you'll see that &lt;tt&gt;RightRails&lt;/tt&gt; didn't write a single line of script either, all it did is added the &lt;tt&gt;data-tags&lt;/tt&gt; attribute to your input field.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&amp;lt;input data-tags="{tags: ['one','two','three'}" value="one, two"&lt;br /&gt;  id="article_tags_string" name="article[tags_string]" /&gt;&lt;/pre&gt;&lt;br /&gt;This way, even if something will go wrong, the user will still see the standard input field with coma separated tags. All your styles and content will remain were it was.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;Well, this is pretty much the whole story. If you're interested, go check the &lt;a href="http://rightjs.org/ui"&gt;RightJS UI&lt;/a&gt; collection, it has many more useful widgets that you can use in your applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7648824041042326943?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7648824041042326943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7648824041042326943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7648824041042326943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7648824041042326943'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/04/making-tags-with-rails-and-rightjs.html' title='Making Tags With Rails And RightJS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1936329656927518492</id><published>2011-03-24T15:38:00.016+02:00</published><updated>2011-04-05T09:47:34.237+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Why ==null, Not ===null</title><content type='html'>Sometimes in &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; source code you might see a thing like this&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;if (value == null) {&lt;br /&gt;  // ...&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;And as every JavaScript optimization tutorial tells you to use &lt;tt&gt;'===null'&lt;/tt&gt; instead of &lt;tt&gt;'==null'&lt;/tt&gt; and JSLint yells at you when do otherwise, people immediately try to change it to the "right" way.&lt;br /&gt;&lt;br /&gt;Meanwhile there is a pretty good case  when it is much better to use &lt;tt&gt;'==null'&lt;/tt&gt; than &lt;tt&gt;'===null'&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;The thing is, that in JavaScript we have this ugly thing called &lt;tt&gt;'undefined'&lt;/tt&gt;, which is quite clunky and often weird in use. Like say you might have a piece of code like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var hash = {a: undefined};&lt;br /&gt;console.log(hash.a); // -&gt; undefined&lt;br /&gt;console.log(hash.b); // -&gt; undefined&lt;br /&gt;&lt;br /&gt;console.log('a' in hash); // -&gt; true&lt;br /&gt;console.log('b' in hash); // -&gt; false&lt;/pre&gt;&lt;br /&gt;Which is a bit strange. So, it is quite often, people use &lt;tt&gt;'null'&lt;/tt&gt; to predefine keys in hashes and then need to check whether a certain value is not &lt;tt&gt;'null'&lt;/tt&gt; or &lt;tt&gt;'undefined'&lt;/tt&gt;, kinda like this&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;if (value !== null &amp;&amp; value !== undefined) {&lt;br /&gt;  // ...&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Another case is that the &lt;tt&gt;'null'&lt;/tt&gt; thing is an object in JavaScript and sometimes you might need to check if a variable is a plain type value, you go with things like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;if (&lt;br /&gt;  typeof(value) !== 'object' &amp;&amp;&lt;br /&gt;  value !== null &amp;&amp; value !== undefined&lt;br /&gt;) {&lt;br /&gt;  // ..&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;There are lots of cases where you need this double check. You also should keep in mind the fact that &lt;tt&gt;'undefined'&lt;/tt&gt; is a variable in JavaScript and someone actually can define a variable named &lt;tt&gt;'undefined'&lt;/tt&gt;, and in general case you'll need to handle that too. Well, you get the ugly picture.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, what about that &lt;tt&gt;'==null'&lt;/tt&gt; business anyways?&lt;br /&gt;&lt;br /&gt;The trick is that &lt;tt&gt;'==null'&lt;/tt&gt;, unlike other boolean comparisons (for example &lt;tt&gt;'==true'&lt;/tt&gt;), returns &lt;tt&gt;'true'&lt;/tt&gt; only for &lt;tt&gt;'null'&lt;/tt&gt; and &lt;tt&gt;'undefined'&lt;/tt&gt; values.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;null == null;      // -&gt; true&lt;br /&gt;null == undefined; // -&gt; true&lt;br /&gt;&lt;br /&gt;null == false;     // -&gt; false&lt;br /&gt;null == '';        // -&gt; false&lt;br /&gt;null == 0;         // -&gt; false&lt;/pre&gt;&lt;br /&gt;Which basically means that you can use &lt;tt&gt;value == null&lt;/tt&gt; instead of&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;value === null || value === unefined&lt;/pre&gt;&lt;br /&gt;More of that, except being more compact, &lt;tt&gt;'value == null'&lt;/tt&gt; also will be protected from &lt;tt&gt;'undefined'&lt;/tt&gt; replacements, and the browser also won't perform a variable name lookup, because &lt;tt&gt;null&lt;/tt&gt; is a keyword and there is nothing besides it. Which will make the things faster.&lt;br /&gt;&lt;br /&gt;How much faster? Let's see. I cranked this little script and run it in FF4&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;function test1() {&lt;br /&gt;  var i=0, t = new Date(), v1 = null, v2 = undefined, r;&lt;br /&gt;  for (; i &lt; 100000; i++) {&lt;br /&gt;    r = v1 === null &amp;&amp; v1 === undefined;&lt;br /&gt;    r = v2 === null &amp;&amp; v2 === undefined;&lt;br /&gt;  }&lt;br /&gt;  console.log(new Date() - t);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function test2() {&lt;br /&gt;  var i=0, t = new Date(), v1 = null, v2 = undefined, r;&lt;br /&gt;  for (; i &lt; 100000; i++) {&lt;br /&gt;    r = v1 == null;&lt;br /&gt;    r = v2 == null;&lt;br /&gt;  }&lt;br /&gt;  console.log(new Date() - t);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;console.log('--');&lt;br /&gt;10..times(test1);&lt;br /&gt;console.log('--');&lt;br /&gt;10..times(test2);&lt;/pre&gt;&lt;br /&gt;And here is the result&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:"&gt;--&lt;br /&gt;22&lt;br /&gt;24&lt;br /&gt;23&lt;br /&gt;23&lt;br /&gt;23&lt;br /&gt;23&lt;br /&gt;23&lt;br /&gt;23&lt;br /&gt;24&lt;br /&gt;23&lt;br /&gt;--&lt;br /&gt;2&lt;br /&gt;1&lt;br /&gt;1&lt;br /&gt;1&lt;br /&gt;1&lt;br /&gt;1&lt;br /&gt;1&lt;br /&gt;1&lt;br /&gt;1&lt;br /&gt;1&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Well, that's the whole story. Think you can put it in a good use :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1936329656927518492?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1936329656927518492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1936329656927518492' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1936329656927518492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1936329656927518492'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/03/why-null-not-null.html' title='Why ==null, Not ===null'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4067243972102317496</id><published>2011-03-15T18:36:00.009+02:00</published><updated>2011-03-15T19:06:21.629+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Ruby + Gosu = Fun</title><content type='html'>&lt;a href="https://github.com/jlnr/gosu"&gt;Gosu&lt;/a&gt; is a nice simple 2D games engine for Ruby, which I used to work on this little game called &lt;a href="https://github.com/MadRabbit/pentix.rb"&gt;pentix.rb&lt;/a&gt;. And I'd like to put in some good words for the folks who maintain the project.&lt;br /&gt;&lt;br /&gt;What's particularly cool about Gosu is that it doesn't try to jump over its head and do everything by themselves. Gosu is a very small framework specifically made for 2D games, it will help you make things like windows, handle graphics, fonts, sounds and some basic animations. But it also easily integrates with &lt;a href="http://code.google.com/p/chipmunk-physics/"&gt;Chipmunk&lt;/a&gt; to handle physics and with ImageMagic/OpenGL modules if you need some seriously looking graphics.&lt;br /&gt;&lt;br /&gt;If you think about it, Gosu is a very cool thing, you leave all the rendering and heavy stuff to the native modules and use Ruby for what it's good for, for describing logic, units, interactions and so on. And Gosu does it right, it doesn't force you to inherit it's classes all the time, quite on contrary actually. Gosu itself has just a few basic classes to handle media things, like fonts and images, and for the actual units you can write plain Ruby classes, which certainly makes it much easier to implement, test and maintain.&lt;br /&gt;&lt;br /&gt;And one more thing. Gosu is not just a Ruby toy. It also has all the C++ bindings, which kinda makes it a pretty interesting choice. You can use the power of Ruby to prototype and do all the R&amp;D things. And later, if you actually make it to the investors, it will be relatively simple for you to port everything to C++ and make it running anywhere. Yup, iPhone is on the list :)&lt;br /&gt;&lt;br /&gt;This is basically it. If you ever wanted to write some simple 2D games, but didn't want to deal with C++ check out Gosu, it's pretty awesome!&lt;br /&gt;&lt;br /&gt;UPD: Check out also &lt;a href="http://code.google.com/p/gosu/wiki/RubyTutorial"&gt;this simple ruby tutorial&lt;/a&gt; they have&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4067243972102317496?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4067243972102317496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4067243972102317496' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4067243972102317496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4067243972102317496'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/03/ruby-gosu-fun.html' title='Ruby + Gosu = Fun'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4045823853867869516</id><published>2011-03-03T14:02:00.004+02:00</published><updated>2011-03-03T14:41:27.816+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTML/CSS'/><title type='text'>Super Simple Layout</title><content type='html'>I'd like to show you a little css-trickery thing I use on pages where you have a super simple content, like a documentation or a demo page, and all you want is just to make it look kinda decent without bothering with additional HTML scaffolding.&lt;br /&gt;&lt;br /&gt;Say you have the following HTML page&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:html"&gt;&amp;lt;html&gt;&lt;br /&gt;  &amp;lt;body&gt;&lt;br /&gt;    &amp;lt;h1&gt;The Main Title&amp;lt;/h1&gt;&lt;br /&gt;    &lt;br /&gt;    &amp;lt;h2&gt;Chapter Title&amp;lt;/h2&gt;&lt;br /&gt;    &amp;lt;p&gt;&lt;br /&gt;      bla bla bla&lt;br /&gt;    &amp;lt;/p&gt;&lt;br /&gt;    .......&lt;br /&gt;    &amp;lt;div id="footer"&gt;Copyright ...&amp;lt;/div&gt;&lt;br /&gt;  &amp;lt;/body&gt;&lt;br /&gt;&amp;lt;/html&gt;&lt;/pre&gt;&lt;br /&gt;It is super-simple, you've got an &lt;tt&gt;H1&lt;/tt&gt; for the page head title, you've got a bunch of &lt;tt&gt;H2&lt;/tt&gt; and &lt;tt&gt;P&lt;/tt&gt; tags with the content and kind of a footer at the end.&lt;br /&gt;&lt;br /&gt;And say, you want your page to have some sort of header/footer areas and a bit of paddings on the left and on there right.&lt;br /&gt;&lt;br /&gt;Here is how you can do that with pure CSS, without bothering with additional tags&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:css"&gt;html, body {&lt;br /&gt;  margin: 0;&lt;br /&gt;  padding: 0;&lt;br /&gt;}&lt;br /&gt;html {&lt;br /&gt;  padding-top: 8em;&lt;br /&gt;  padding-bottom: 4em;&lt;br /&gt;  background: gray; /* header and footer background */&lt;br /&gt;}&lt;br /&gt;body {&lt;br /&gt;  padding: 1em 10em;&lt;br /&gt;  padding-bottom: 5em;&lt;br /&gt;  background: white; /* the main area background */&lt;br /&gt;}&lt;br /&gt;h1 {&lt;br /&gt;  position: absolute;&lt;br /&gt;  top: -.6em;&lt;br /&gt;  font-size: 4em;&lt;br /&gt;  color: white;&lt;br /&gt;  margin: 0;&lt;br /&gt;  padding: 0;&lt;br /&gt;}&lt;br /&gt;div#footer {&lt;br /&gt;  position: absolute;&lt;br /&gt;  bottom: 1.5em;&lt;br /&gt;  color: white;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;As you can see the trick is very simple, you use top and bottom paddings on the &lt;tt&gt;HTML&lt;/tt&gt; tag to create the header and the footer, and left/right paddings on the &lt;tt&gt;BODY&lt;/tt&gt; tag to create, well left and right paddings. After that just move the header and footer elements in their place with absolute positioning and you're done!&lt;br /&gt;&lt;br /&gt;That's the whole trick. An example is &lt;a href="http://cdn.rightjs.org"&gt;over here&lt;/a&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4045823853867869516?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4045823853867869516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4045823853867869516' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4045823853867869516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4045823853867869516'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/03/super-simple-layout.html' title='Super Simple Layout'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5414272503211927975</id><published>2011-02-18T12:41:00.007+02:00</published><updated>2011-02-18T12:59:38.088+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ideas'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>If I Would Design A Simple Format</title><content type='html'>There is an idea I wanted to spit out for quite a while now. It is about simple formats and to keep the short story short, here is  a simple spec I cooked up.&lt;br /&gt;&lt;br /&gt;Basically it is a mix of RDoc, Markdown and Maruku, plus some stuff I miss in all of them. The main goal in this one is readability mixed with abilities to handle everyday things, like images, tables, API references and stuff. HTML might be allowed as a fallback.&lt;br /&gt;&lt;br /&gt;That's all, let me know what you think about it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;= Head 1&lt;br /&gt;== Head 2&lt;br /&gt;=== Head 3&lt;br /&gt;==== Head 4&lt;br /&gt;&lt;br /&gt;# Head 1&lt;br /&gt;## Head 2&lt;br /&gt;### Head 3&lt;br /&gt;#### Head 4&lt;br /&gt;&lt;br /&gt;= Head 1  #anchor1&lt;br /&gt;## Head 2 #anchor2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;*bold*&lt;br /&gt;~italic~&lt;br /&gt;_underline_&lt;br /&gt;-strike through-&lt;br /&gt;`type text`&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;http://some.url&lt;br /&gt;&amp;lt;/some.url&amp;gt;&lt;br /&gt;&amp;lt;http://some.url&amp;gt;&lt;br /&gt;&amp;lt;Some text:/some.url&amp;gt;&lt;br /&gt;&lt;br /&gt;some@email.com&lt;br /&gt;&amp;lt;some@email.com&amp;gt;&lt;br /&gt;&amp;lt;Some email:some@email.com&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;API Links&lt;br /&gt;&lt;br /&gt;{ClassName#instanceMethod}&lt;br /&gt;{ClassName.classMethod}&lt;br /&gt;&lt;br /&gt;Defining a default scope&lt;br /&gt;{-ClassName} after that&lt;br /&gt;{#instanceMethod}&lt;br /&gt;{.classMethod}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;img:/some.url&amp;gt;&lt;br /&gt;&amp;lt;swf:/some.url&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;img:123x234:/some.url&amp;gt;&lt;br /&gt;&amp;lt;swf:123x234:/some.url&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;img:Some title text:/some.url&amp;gt;&lt;br /&gt;&amp;lt;swf:Some title text:/some.url&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;img:Some title text:123x234:/some.url&amp;gt;&lt;br /&gt;&amp;lt;swf:Some title text:123x234:/some.url&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Code formatting&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    Just a genuine piece of code&lt;br /&gt;    That is pre formatted&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ruby piece of code&lt;br /&gt;&lt;br /&gt;    :ruby&lt;br /&gt;    this.is_a?(Ruby)&lt;br /&gt;    code.which_we do&lt;br /&gt;      paint in some do&lt;br /&gt;        nice(colors)&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;Javascript code&lt;br /&gt;&lt;br /&gt;    :javascript&lt;br /&gt;    this.isA(JavaScript).code();&lt;br /&gt;    that.we(Paint).accordingly();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&gt; This is a quote text&lt;br /&gt;&gt; It will be converted to a block-quote&lt;br /&gt;&gt; &gt; This is and internal quote&lt;br /&gt;&gt; &gt; it will be nested into the original one&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; * List item 1&lt;br /&gt; * List item 2&lt;br /&gt;&lt;br /&gt; - List item 1&lt;br /&gt; - List item 2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1. Num list item 1&lt;br /&gt;2. Num list item 2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;a. Letter list item 1&lt;br /&gt;b. Letter list item 2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I.   Roman number list item 1&lt;br /&gt;II.  Roman number list item 2&lt;br /&gt;III. Roman number list item 3&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; * Mixed list 1&lt;br /&gt; * Mixed list 2&lt;br /&gt;&lt;br /&gt;   - Mixed List 3&lt;br /&gt;   - Mixed List 4&lt;br /&gt;&lt;br /&gt;    1. Mixed List 5&lt;br /&gt;    2. Mixed List 6&lt;br /&gt;&lt;br /&gt;      a. Mixed List 7&lt;br /&gt;      b. Mixed List 8&lt;br /&gt;&lt;br /&gt;        I. Mixed List 9&lt;br /&gt;        II. Mixed List 10&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Head | Head | Head&lt;br /&gt;-----|------|-------------------&lt;br /&gt;Cell | Cell | Cell&lt;br /&gt;Cell | Cell | Cell&lt;br /&gt;-----|------|-------------------&lt;br /&gt;Foot | Foot | Foot&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Head |     Head    | Head&lt;br /&gt;     | Head | Head |&lt;br /&gt;-----|------|------|------------&lt;br /&gt;Cell | Cell | Cell | Cell&lt;br /&gt;Cell |     Cell    | Cell&lt;br /&gt;Cell | Cell | Cell | Cell&lt;br /&gt;     | Cell | Cell |&lt;br /&gt;-----|-------------|------------&lt;br /&gt;Foot |     Foot    | Foot&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Right | Left       | Center      |&lt;br /&gt;-----&gt;|&lt;-----------|------x------|&lt;br /&gt;    1 | 1          |      1      |&lt;br /&gt;   11 | 11         |     111     |&lt;br /&gt;  111 | 111        |    11111    |&lt;br /&gt; 1111 | 1111       |   1111111   |&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5414272503211927975?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5414272503211927975/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5414272503211927975' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5414272503211927975'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5414272503211927975'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/02/if-i-would-design-simple-format.html' title='If I Would Design A Simple Format'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1741312657601160271</id><published>2011-01-09T16:52:00.008+02:00</published><updated>2011-01-09T17:50:46.142+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS 2.2 RC Public Test</title><content type='html'>My dear fellas rightjslings (or whatever the hell we are) I've got some good news for our species! I took a bit of a break from RRTE those holidays and packed ourselves the next major update in the RightJS core, which will go by the number &lt;tt&gt;2.2.0&lt;/tt&gt;. And I would really like you to play with the thing. Bring some joy and magic in the routine, and start the year with the new shiny toys!&lt;br /&gt;&lt;br /&gt;Awrighty, now to the business. You can &lt;a href="http://stcamp.net/share/rightjs-220-rc.zip"&gt;download the thing from over here&lt;/a&gt; (NOTE: there are updates for all the plugins as well).&lt;br /&gt;&lt;br /&gt;And as for the changes, there are quite a few of them actually, so let's get through them one by one&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Native Fx Support&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The first thing that RightJS 2.2 brings is the support of native css-transitions in the visual effects engine. There is not much of changes in the actual API, RightJS will automatically figure everything out and use native features when available.&lt;br /&gt;&lt;br /&gt;Currently it runs on Safari, Chrome and FF4. Opera also supports them but I have disabled it for Opera by default, because first of all their thing is buggy (doesn't support say font-size and background-position) and secondly our own, javascript based engine works faster than their native feature :)&lt;br /&gt;&lt;br /&gt;Anyhow, if you insist to switch it on in Opera or disable in Webkit/FF4, use the new &lt;tt&gt;engine&lt;/tt&gt; option with your effects, it can be either &lt;tt&gt;'javascript'&lt;/tt&gt; or &lt;tt&gt;'native'&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Class and Wrapper merge&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The second biggest change is that the &lt;tt&gt;Class&lt;/tt&gt; and &lt;tt&gt;Wrapper&lt;/tt&gt; units were merged to make an uniformed classes structure where the &lt;tt&gt;Class&lt;/tt&gt; unit is the top parent of everything.&lt;br /&gt;&lt;br /&gt;The &lt;tt&gt;new Wrapper&lt;/tt&gt; construction won't work anymore, use the standard &lt;tt&gt;new Class&lt;/tt&gt; calls as you do when you define any other class.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Supercalls On Modules&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The other outcome of the &lt;tt&gt;Class&lt;/tt&gt; unit update is that now you can call the &lt;tt&gt;$super&lt;/tt&gt; method not only on inherited classes but with injected modules as well. It kinda looks like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;var Klass = new Class({&lt;br /&gt;  method: function() {&lt;br /&gt;    return "original";&lt;br /&gt;  }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;Klass.include({&lt;br /&gt;  method: function() {&lt;br /&gt;    return this.$super() + "+module1";&lt;br /&gt;  }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;Klass.include({&lt;br /&gt;  method: function() {&lt;br /&gt;    return this.$super() + "+module2";&lt;br /&gt;  }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;new Klass().method(); // -&gt; "original+module1+module2"&lt;/pre&gt;&lt;br /&gt;This comes very handy when you need to overload some certain methods without using the actual inheritance. Before, we used to use all sorts of &lt;tt&gt;old_method&lt;/tt&gt; hacks, now it's all nice and civilized.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Builtin Mouseenter/Mouseleave Handling&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;RightJS 2.2 will come with built in mouseenter/mouseleave events handler. As usual it all handled automagically, just use the &lt;tt&gt;'mouseenter'&lt;/tt&gt; and &lt;tt&gt;'mouseleave'&lt;/tt&gt; event names and be happy. Works both ways, directly with elements and via UJS.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;$('element').onMouseenter('addClass', 'hovered');&lt;br /&gt;$('element').on('mouseleave', 'removeClass', 'hovered');&lt;br /&gt;&lt;br /&gt;"div.something".onMouseenter('addClass', 'hovered');&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Better UJS&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Another major update is that now UJS handlers got triggered by nested elements as well, just like with the normal dom-events. Basically it means that if you say have a structure like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&lt;br /&gt;&amp;lt;div class="something"&amp;gt;&lt;br /&gt;   Some text in here&lt;br /&gt;  &amp;lt;div&amp;gt;some inner element&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;br /&gt;If you attach your UJS listener like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;"div.something".onClick('toggleClass', 'clicked');&lt;/pre&gt;&lt;br /&gt;Then it will get triggered no matter if the user has clicked the &lt;tt&gt;div.something&lt;/tt&gt; element itself or any of its internal elements. No more hacking this stuff around.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Pretty Collections Handling&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;And one more sweet thing the RightJS 2.2 release will bring. I know you all love the &lt;tt&gt;String#on&lt;/tt&gt; method to handle the UJS features, so I had extended it a bit further. Now you can run basically any &lt;tt&gt;Element&lt;/tt&gt; methods from strings, just like that.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;"div.something".addClass('red');&lt;br /&gt;"div.something".removeClass('red');&lt;br /&gt;"div.important".highlight();&lt;br /&gt;"div#some-crap".remove('fade');&lt;br /&gt;....&lt;/pre&gt;&lt;br /&gt;Those basically shortcuts for &lt;tt&gt;$$(css_rule).each('method', args);&lt;/tt&gt; it finds all the elements matching the rule and calls the method on them.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;----&lt;br /&gt;&lt;br /&gt;There are more fancy things in there, like various performance optimizations, IE8 was kicked out in the olds module, all CSS3 selectors now work everywhere, and so one  and so one. I'll describe everything with the actual release.&lt;br /&gt;&lt;br /&gt;For now you just have fun with the new sweet features and come back if you have any troubles.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1741312657601160271?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1741312657601160271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1741312657601160271' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1741312657601160271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1741312657601160271'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2011/01/rightjs-22-rc-public-test.html' title='RightJS 2.2 RC Public Test'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3119625887412185538</id><published>2010-12-22T19:47:00.003+02:00</published><updated>2010-12-22T20:11:18.124+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><title type='text'>RRTE Public Alpha</title><content type='html'>Hey people, sorry for being quiet recently, been busy with work and stuff. I don't have a pretty picture for you today, but Santa didn't come up giftless today, not at all :)&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/rightjs/rightjs-ui/blob/master/test/demos/rte.html"&gt;I've got a cute little link for you!&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I just pushed RRTE alpha to github, so you could have a sneak pick on what's going on.&lt;br /&gt;&lt;br /&gt;It's still an alpha, it doesn't work under IE yet and you might stumble upon some bugs here and there, but generally, generally it all shapes up and should give you some basic overview of the thing.&lt;br /&gt;&lt;br /&gt;Anyhow, you know the drill. Checkout, enjoy and come back with feedback! :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3119625887412185538?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3119625887412185538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3119625887412185538' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3119625887412185538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3119625887412185538'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/12/rrte-public-alpha.html' title='RRTE Public Alpha'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8485365233176561354</id><published>2010-12-01T18:38:00.005+02:00</published><updated>2010-12-01T19:27:35.886+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RRTE Status Update</title><content type='html'>Hey folks! It's been couple of weeks and some of you started to wonder, where's Nik with his RRTE and the world domination, so here is the status update.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://stcamp.net/images/rightjs/rrte-2.png" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;As you can see, we are having some serious progress. Most of the things you see on the picture are already working, but I'm trying to explore some possibilities and features.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What's Wrong With the World?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;It seems that the design-mode support in browsers is utterly broken. It's ugly, limited, uncomfortable to use and behaves differently in different browsers. Well, just like the whole JavaScript business :)&lt;br /&gt;&lt;br /&gt;There is basically two modes how the design-mode works in browsers, the tags mode and the css-mode, in the first one the browser tries to use tags to style the content, in the other one it tries to use styles. The trouble is that IE supports the tags-mode only and FF supports both, but in any case both of them do that wrong.&lt;br /&gt;&lt;br /&gt;You see, in the tag mode say they use obsolete &lt;tt&gt;FONT&lt;/tt&gt; tag to style fonts, but in this case FF doesn't support for example proper selections highlighting, they also for example use &lt;tt&gt;STRIKE&lt;/tt&gt; instead of &lt;tt&gt;S&lt;/tt&gt; tag and there is no way specify it. In the css-mode, the browsers try to use things like &lt;tt&gt;style="font-weight:bold"&lt;/tt&gt; instead of the &lt;tt&gt;&amp;lt;B&amp;gt;&lt;/tt&gt; tag pair, which is quite inflexible if not to say idiotic in terms of CSS. It also incorrectly reacts on all sorts of things, like say it will show the &lt;tt&gt;U&lt;/tt&gt; tool "on" when you put your cursor on a link, and you won't be able to switch it off because it's styled with css.&lt;br /&gt;&lt;br /&gt;And so one and so one. There is are lots of small and big fuckups like that, and honestly it is quite a mess.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Time To Fix It!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Being an arrogant idiot, I couldn't just pass by this mess, so now I'm building my own engine, which will do precisely what we need on any browser. Basically there are two sets of tools, one of them allows you to work with the &lt;tt&gt;style&lt;/tt&gt; attribute, the other one allows you to format anything with any tag you want, and, what's more important, with any attribute you need, which means that you can use ids and classes as well.&lt;br /&gt;&lt;br /&gt;The formatting setting is not limited to the H1-H6, PRE and ADDRESS tags anymore. You can easily specify say things like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Rte.Formats = {&lt;br /&gt;  'h2': "Header 2",&lt;br /&gt;  'tt': "Typetext",&lt;br /&gt;  'blockquote': "Block quote",&lt;br /&gt;  'div.blue':   "Blue block",&lt;br /&gt;  'div.green':  "Green block"&lt;br /&gt;};&lt;/pre&gt;&lt;br /&gt;Etc, etc. The basic plan is to create some sort of a mixed, truly css-oriented mode. We will use &lt;tt&gt;SPAN&lt;/tt&gt; + &lt;tt&gt;style&lt;/tt&gt; attribute for things like custom colors and fonts, but for everything else use normal tags and classes, so that the editor produced a nice and easy to process and support content.&lt;br /&gt;&lt;br /&gt;It's a bit tricky, but we will get through it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Advanced Tools&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The other thing which I'm trying to figure out is how small an RTE can be, or how much features can we squeeze in 10k? Advanced OOP features of RightJS are really helpful in this case, because all the inheritance and functionality injection features allow us to create really compact and flexible code. As you can see I already added the color tools, font name and size tools, formatting options and some other things. At the moment all of that weights about 8k of gzipped code (and that includes all the css).&lt;br /&gt;&lt;br /&gt;So I'm trying to add more features in the standard package than I originally planed and see where it will lead us. We might drop some of them into the extensions pack later on, or, if everything will go smoothly, we might not need one and all the standard functionality will be available in the basic build, so that we will have just a core and plugins. Or something like that. Will see.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;Well, this is basically it for the moment.&lt;br /&gt;To be continued...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8485365233176561354?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8485365233176561354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8485365233176561354' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8485365233176561354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8485365233176561354'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/12/rrte-status-update.html' title='RRTE Status Update'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-9064957108758997587</id><published>2010-11-12T18:45:00.009+02:00</published><updated>2010-11-14T12:29:09.798+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS RTE Concept</title><content type='html'>In case you were wondering why it was quiet around here for last couple of weeks, I'd like to share some stuff I've been secretly working on and what are the future plans.&lt;br /&gt;&lt;br /&gt;Basically we are taking a little pause until Mozilla guys sort their stuff out and release FF4 or at least 3.7, which we need to execute our &lt;a href="http://st-on-it.blogspot.com/2010/10/rightjs-22-notes.html"&gt;evil plan for RightJS 2.2&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Meanwhile I've been working on this puppy&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://stcamp.net/images/rightjs/rrte.png" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Yup, it's a RightJS based RTE that will most definitely take over the world, and so you started to want it for xmas, I'd like to put some words in about what's what and stuff.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Meet RRTE The Frameless&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;One of the major differences from usual RTEs is that our RTE (which goes under the codename &lt;tt&gt;RRTE&lt;/tt&gt;) does not use any frakking frames. Instead, it uses an inlined &lt;tt&gt;DIV&lt;/tt&gt; with &lt;tt&gt;contenteditable="true"&lt;/tt&gt; property.&lt;br /&gt;&lt;br /&gt;If now you're wondering what it gives you, then this is what:&lt;br /&gt;&lt;br /&gt;Because it's an inlined div, all the content inside of it will follow all the styles on the page, which means no more headache with transferring and applying your styles to some iframe. If you take a look at the picture, you'll see that the header text inside of the editor has exactly the same font and size as the header on the main page. The &lt;tt&gt;blockquote&lt;/tt&gt; and &lt;tt&gt;pre&lt;/tt&gt; tags are also painted through usual CSS right on the page.&lt;br /&gt;&lt;br /&gt;Secondly, because it's just a small inlined piece of page, RRTE works and feels much faster than any IFRAME based editors. It is super fast, practically it feels almost like a plain textarea element.&lt;br /&gt;&lt;br /&gt;Thirdly, because all widgets in RightJS UI are plain elements, you will be allowed to easily access and manipulate any content right inside of the editor via standard API.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Modular&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The second most important feature of RRTE is that it will be modular. Unlike all the other heavy duty RTEs, our RTE, by default will be mainly social networks oriented. It's main purpose will be to work as a lightweight embeddable RTE for all sorts of things like comments and blog posts where you already have all styles in place and just need some nice and shiny thing instead of textareas.&lt;br /&gt;&lt;br /&gt;For this reason the default package will contain the main core/engine and provide basic editing tools which you can see on the picture above. Currently it weights just &lt;b&gt;4k&lt;/b&gt; of gzipped code (including stylesheets) and my intention is to keep it under 10k size.&lt;br /&gt;&lt;br /&gt;And for all the advanced functionality, like file-pickers, fonts, tables, etc. we will have a separated package, which will turn the editor in a normal heavy duty RTE.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Extendable&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As you can see, my plan for RRTE is to extend it via plugins. I don't have any intention to implement everything in it, instead of that RRTE will have a proper OOP design in best RightJS traditions, so that you could make your own tools and extensions, using classes and inheritance. In case if you're wondering how it's gonna look like, here is an example how the &lt;tt&gt;Save&lt;/tt&gt; button is implemented&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;Rte.Tool.Save = new Class(Rte.Tool, {&lt;br /&gt;  shortcut: 'S',&lt;br /&gt;&lt;br /&gt;  initialize: function(rte) {&lt;br /&gt;    this.$super(rte);&lt;br /&gt;    if (!rte.textarea.form()) {&lt;br /&gt;      this.disable();&lt;br /&gt;    }&lt;br /&gt;  },&lt;br /&gt;&lt;br /&gt;  exec: function() {&lt;br /&gt;    if (!this.disabled) {&lt;br /&gt;      this.rte.textarea.form().submit();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;RRTE will provide you with basic tools and open API, so I expect it to be pretty civilized and easy to extend.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What and When&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Currently I keep all the fun for myself. This thing is not on github yet, basically because I hack and refactor it alot. Some parts are still quite flimsy and as I don't want you to have a wrong impression about it I'm going to keep it locally for the next week or two.&lt;br /&gt;&lt;br /&gt;At the moment it appears to me that I'm done on about 40-50%, but as I'm a bit busy those days it is hard to estimate the exact schedule. Judging by my guts I should push something worth of seeing on github in about couple of weeks and probably will have the first release by the end of November/beginning of December.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This is basically it. Enjoy the news!&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;Oh, and one more thing. I could use some help with a default iconset for this widget. Currently I use some free stuff that I found over the internet, but if you can and willing to draw some particularly cool uber-iconset, that would be much appreciated!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-9064957108758997587?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/9064957108758997587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=9064957108758997587' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/9064957108758997587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/9064957108758997587'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/11/rightjs-rte-conception.html' title='RightJS RTE Concept'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-461537330851870119</id><published>2010-10-25T18:32:00.009+03:00</published><updated>2010-10-29T13:48:58.447+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS 2.2 Notes</title><content type='html'>I'm cooking a new evil plan for RightJS 2.2 release and would like to share some thoughts and notes, so you knew what's going on and where are we heading.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Native Fx&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;We are living in an interesting moment. Native CSS transitions are finally becoming reality. Safari does it, Chrome does it, Opera does it, FF4 does it (they promised they will include the feature into the 3.7 release). IE9b does not. Rumors has it, the feature will be in the final release, but I have my doubts it will happen.&lt;br /&gt;&lt;br /&gt;Anyways, now, when the majority of sane browsers support the feature, I think it's the right time to start using native CSS transitions as a default option for our dom-morph effects. It is faster, simpler and more reliable.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Better UJS&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The current implementation of UJS in RightJS works with an event &lt;tt&gt;target&lt;/tt&gt; property only. The trouble with this solution is that when you have something like&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&amp;lt;div id="one"&gt;first&lt;br /&gt;  &amp;lt;div id="two"&gt;second&lt;br /&gt;    &amp;lt;div id="three"&gt;third&amp;lt;/div&gt;&lt;br /&gt;  &amp;lt;/div&gt;&lt;br /&gt;&amp;lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;and you attach an event listener to say the top one&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;"div#one".onClick(function() {&lt;br /&gt;  ...&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;it will reacts only if the user clicks on the &lt;tt&gt;div#one&lt;/tt&gt; itself and will successfully ignoring clicks on the inner element. Sometimes that's what you need, but in majority of the cases it creates more confusion than helping.&lt;br /&gt;&lt;br /&gt;I'm still thinking about this one, but there is a good chance that the logic will be changed and our UJS will mimic the normal event listeners behavior and react on inner elements as well.&lt;br /&gt;&lt;br /&gt;Hey! You will be able to make UJS stuff within UJS stuff! :)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Cookie plugin&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;At the beginning it seemed like a good idea to have a basic Cookie wrapper out of the box, but later I realized that on majority of my projects I don't use it. Well I assume you have pretty much the same experience.&lt;br /&gt;&lt;br /&gt;So I think the cookie module will be kicked out of the core into an officially supported plugin. it's not much, just half a KB of gzipped code, but I guess we can fill this half a K with something more useful in everyday life.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IE8 And Pals&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;IE8, or as it known in some circles "IE7 on steroids" is kinda of troubling browser. The problem is that on a good half of the pages this darn things automatically falls into IE7 mode and therefore downloads the "olds" module, on the other half, it still has some troubles with native css selectors because some of CSS3 constructions are not supported, which consequentially makes some wonder-people to wonder how "right" right.js exactly is?&lt;br /&gt;&lt;br /&gt;Furthermore the &lt;tt&gt;change&lt;/tt&gt;/&lt;tt&gt;submit&lt;/tt&gt; events still don't bubble and we carry quite large chunk of code in the core, to work this thing around.&lt;br /&gt;&lt;br /&gt;So I was having that wild thought in my head, that we could ditch IE8 and pals support into the olds module so we could fix those things in there and not bother the core.&lt;br /&gt;&lt;br /&gt;Currently, I'm considering the idea of making the line between the old and new browsers right where the CSS transitions support starts, which will effectively drop the whole IE support into the olds module.&lt;br /&gt;&lt;br /&gt;That probably will make some jquery folks to moan that we are unfair towards them,  but I'm looking into my glass ball and it says that after all the cutting and stitching, RightJS core size could drop below 12k.&lt;br /&gt;&lt;br /&gt;Well, at least that the target. Besides there is still a tiny chance that IE9 folks will hear the voice of sanity and add css-transitions into it, then this distinction will be perfectly justified.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;---&lt;br /&gt;&lt;br /&gt;Anyhow, those are the notes and thoughts. Let me know if you have something on your mind about that&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-461537330851870119?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/461537330851870119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=461537330851870119' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/461537330851870119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/461537330851870119'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/10/rightjs-22-notes.html' title='RightJS 2.2 Notes'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4595983521655934191</id><published>2010-10-14T17:42:00.008+03:00</published><updated>2010-10-14T18:57:31.471+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Cloned SELECT elements in IE</title><content type='html'>What I love about IE browsers is that genuine feeling of moral superiority that you unavoidable accumulate inside yourself when you're dealing with its features. Found one more "treasure" that killed half of my day.&lt;br /&gt;&lt;br /&gt;The story goes like that. Say I have a &lt;tt&gt;SELECT&lt;/tt&gt; element and then you clone it a bunch of times and populate with new content, kinda like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var original = document.createElement('select');&lt;br /&gt;clone = original.cloneNode(false);&lt;br /&gt;document.body.appendChild(clone);&lt;br /&gt;&lt;br /&gt;$(clone).html('&amp;lt;option&gt;boo hoo!&amp;lt;/option&gt;');&lt;/pre&gt;&lt;br /&gt;And that all works fine, but now say you update the content in another function, say an ajax callback or something&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var original = document.createElement('select');&lt;br /&gt;clone = original.cloneNode(false);&lt;br /&gt;document.body.appendChild(clone);&lt;br /&gt;&lt;br /&gt;setTimeout(function() {&lt;br /&gt;  $(clone).html('&amp;lt;option&gt;boo hoo!&amp;lt;/option&gt;');&lt;br /&gt;}, 0);&lt;/pre&gt;&lt;br /&gt;You would expect that this is no different from the first example and here is where they will get ya. Not only it does not update the options list (it will be empty), but it also somehow manages to show you that the &lt;tt&gt;innerHTML&lt;/tt&gt; property was actually updated and contains all the new data.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;// .....&lt;br /&gt;&lt;br /&gt;setTimeout(function() {&lt;br /&gt;  $(clone).html('&amp;lt;option&gt;boo hoo!&amp;lt;/option&gt;');&lt;br /&gt;  alert(clone.innerHTML); // -&gt; '&amp;lt;option&gt;boo hoo!&amp;lt;/option&gt;'&lt;br /&gt;}, 0);&lt;/pre&gt;&lt;br /&gt;All versions affected 6,7 and 8 (didn't test in 9 though)&lt;br /&gt;&lt;br /&gt;In case you really really need to update a cloned SELECT element in an async callback, here is a hack how you can do that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;// ....&lt;br /&gt;setTimeout(function() {&lt;br /&gt;  $(clone).html('&amp;lt;option&gt;boo hoo!&amp;lt;/option&gt;');&lt;br /&gt;&lt;br /&gt;  var dummy = document.createElement('option');&lt;br /&gt;  dummy.text = 'dummy'; // DON'T use innerHTML in here!&lt;br /&gt;  clone.add(dummy);&lt;br /&gt;  clone.removeChild(dummy);&lt;br /&gt;}, 0);&lt;/pre&gt;&lt;br /&gt;It will fix the problem, but you'll need to add some checks in there to ensure it's actually IE you're dealing with.&lt;br /&gt;&lt;br /&gt;That's the whole gotcha. Hope will save someone's day&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4595983521655934191?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4595983521655934191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4595983521655934191' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4595983521655934191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4595983521655934191'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/10/cloned-select-elements-in-ie.html' title='Cloned SELECT elements in IE'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2855985946213219035</id><published>2010-10-02T18:46:00.007+03:00</published><updated>2010-10-02T20:19:48.728+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Making Pretty CLI Applications In Ruby</title><content type='html'>Sometimes we need to process large amount of data in Ruby, convert things from one format to another, process a large database, etc. And when you start implementing those things as rake tasks or something like that you need some feedback, say to see the progress and so one. Normally people would use &lt;tt&gt;puts&lt;/tt&gt; &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;index = 0&lt;br /&gt;count = things.size&lt;br /&gt;things.each do |thing|&lt;br /&gt;  do_something_about thing&lt;br /&gt;&lt;br /&gt;  puts "#{index +=1} of #{count}"&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;But when you have several thousands or millions things to process this approach doesn't work, because it will just blow into the console, which is ugly and well... there are ways to do it much better, prettier and more professionally looking.&lt;br /&gt;&lt;br /&gt;There are gems and libs that will help you to do it properly, but in this article I'd like to show how it actually works internally. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Strings Rewriting&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;One of the first things you might want to learn in order to make seriously looking CLI app is how to rewrite strings all over. It is a bit tricky in Ruby so here how it looks like. First of all you need to learn the "\r" symbol, which is called "caret return" and well it returns the caret. A simple example will demonstrate. Say you have a line of code like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;puts "one\ranother"&lt;/pre&gt;&lt;br /&gt;when you run it, you will see in the console a string like &lt;tt&gt;"another"&lt;/tt&gt; and what's happening is that ruby will print &lt;tt&gt;"one"&lt;/tt&gt; then return the caret and print &lt;tt&gt;"another"&lt;/tt&gt; over it, so that you see the last one only. But the trouble is that if you'll write something like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;puts "one"&lt;br /&gt;puts "\ranother"&lt;/pre&gt;&lt;br /&gt;It won't work and you'll see two strings in the console &lt;tt&gt;"one"&lt;/tt&gt; and &lt;tt&gt;"another"&lt;/tt&gt;, and because of that if you'll put into your loop something like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;puts "\r#{index += 1} of #{count}"&lt;/pre&gt;&lt;br /&gt;it won't work either and you will see the same ugly roll of strings. To make it working you have to use the a combination of the &lt;tt&gt;print&lt;/tt&gt; and &lt;tt&gt;STDOUT.flush&lt;/tt&gt; calls, kida like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;8.times do |i|&lt;br /&gt;  print "\r#{i}"&lt;br /&gt;  STDOUT.flush&lt;br /&gt;  sleep 0.1&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;In this case it will print a string and stay on it. &lt;tt&gt;STDOUT.flush&lt;/tt&gt; dumps the current stdout into the console, and on the next iteration, it will normally go to the beginning of the string and write it over as you needed.&lt;br /&gt;&lt;br /&gt;But it is still not everything. If you run a piece of code like this one&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;%w{looooong short}.each do |str|&lt;br /&gt;  print "\r#{str}"&lt;br /&gt;  STDOUT.flush&lt;br /&gt;  sleep 0.5&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;You will see that on the second iteration, the previous line won't be entirely overwritten and instead of &lt;tt&gt;"short"&lt;/tt&gt; you will see &lt;tt&gt;"shortong"&lt;/tt&gt;, to make it work properly you need to write a long enough string that contains spaces at the end, for example&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;%w{looooong short}.each do |str|&lt;br /&gt;  print "\r#{str.ljust(80)}"&lt;br /&gt;  STDOUT.flush&lt;br /&gt;  sleep 0.5&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;tt&gt;String#ljust&lt;/tt&gt; makes a string of the given length by filling the remaining places with spaces. This way you will always overwrite 80 symbols of the line in the console.&lt;br /&gt;&lt;br /&gt;To wrap it up nicely you might create a simple function and your loop will look like that &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;def print_r(text, size=80)&lt;br /&gt;  print "\r#{text.ljust(size)}"&lt;br /&gt;  STDOUT.flush&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;index = 0&lt;br /&gt;count = things.size&lt;br /&gt;things.each do |thing|&lt;br /&gt;  do_something_about thing&lt;br /&gt;&lt;br /&gt;  print_r "#{index +=1} of #{count}"&lt;br /&gt;end&lt;br /&gt;puts "\n" # &lt;- a final new line&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Displaying the progress&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;With the trick above you will be able to show a constantly updating status line, but there are still some meat on this bone. Showing the user things like &lt;tt&gt;"345 of 87654"&lt;/tt&gt; is not particularly user friendly, because it might be a bit annoying to calculate the actual progress in your head all the time. Would be nice to show the progress in percents as well. Happily it is very simple to do using placeholders&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;print_r(&lt;br /&gt;  "%d of %d (%d%%)" %&lt;br /&gt;  [index+=1, count, (index.to_f/count * 100)]&lt;br /&gt;)&lt;/pre&gt;&lt;br /&gt;The other usual problem with status reports is that you might have particularly large set of things, say several millions of them and your script might process several thousands of them per second. In this case hitting your console several thousands times per second will seriously slow the process down, so you might need a way to skip some steps and print reports in some periods of time. You can do that the following way&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;index = 0&lt;br /&gt;count = things.size&lt;br /&gt;step   = count / 1000 # 1/10th of a percent&lt;br /&gt;&lt;br /&gt;things.each do |thing|&lt;br /&gt;  if (index += 1) % step == 0&lt;br /&gt;    print_r "....."&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;As you can see we defined the &lt;tt&gt;step&lt;/tt&gt; variable and then skip all the non-round iterations. In this particular case it will make the script to update the report every &lt;tt&gt;1/10th&lt;/tt&gt; of a percent of the job done. Which is in most cases is not a big drawback and still provides the user with progress updates.&lt;br /&gt;&lt;br /&gt;You also might think of &lt;tt&gt;ETA&lt;/tt&gt; calculations, but you probably can figure it out on your own now, it's very simple.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Add Some Colors&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;And the last thing I'd like to show is how to make colors in the console, which might make your application look even cooler. Some developers already know how to do that, but some don't. So here it is.&lt;br /&gt;&lt;br /&gt;Basically it is very simple and in some ways similar to HTML tags. You use things called &lt;tt&gt;escape sequences&lt;/tt&gt; which are just some markers like tags, you have an opening one, and you have a closing one, like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;puts "\e[32mGREEN TEXT\e[0m"&lt;br /&gt;puts "\e[31mRED TEXT\e[0m"&lt;br /&gt;puts "\e[36mBLUE TEXT\e[0m"&lt;/pre&gt;&lt;br /&gt;As you can see, the closing sequence is always the same and the opening one differs only by a number, and this number is basically describes the properties of the following text. It might be a color, or a blinking effect, you can nest them just like normal HTML tags and so one. You can find full list of options &lt;a href="http://en.wikipedia.org/wiki/ANSI_escape_code"&gt;on wikipedia&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The only trouble with those things is that the format of escape sequences differs from a platform to platform. The example above is for OSX terminal. How to make those things working in DOS and Linux you can find that on the same wikipedia page.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This is about it. Now go and make the world prettier!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2855985946213219035?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2855985946213219035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2855985946213219035' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2855985946213219035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2855985946213219035'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/10/making-pretty-cli-applications-in-ruby.html' title='Making Pretty CLI Applications In Ruby'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-939358749874109850</id><published>2010-09-23T15:47:00.008+03:00</published><updated>2010-09-23T16:18:10.067+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Easy Way Out of The STI Hell</title><content type='html'>I like the idea of STI (single table inheritance), it is cheap, dodgy but it works and allows you to play with all sorts of sub-types in a civilized way. For example you can redefine things in subclasses, use strategies and so one.&lt;br /&gt;&lt;br /&gt;But when you try STI with Rails, you immediately fall into the polymorphic routes hell. Because you have new types, Rails tries to find routes for them, and it doesn't want to use the base model as a fallback. And here, people start to do all kinds of things (I did), define controller level helpers, generators, modules and so one. But then, someone adds a new custom method or a new subtype and it all falls down like a house of cards.&lt;br /&gt;&lt;br /&gt;Well, you know what they say, the exit from a hell lays at the very bottom of it. And in Rails it will be routes. Say I want to handle all sorts of &lt;tt&gt;User&lt;/tt&gt; model subclasses, like &lt;tt&gt;User::Admin&lt;/tt&gt;, &lt;tt&gt;User::Manager&lt;/tt&gt;, &lt;tt&gt;User::Blocked&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: ruby"&gt;Trololo::Application.routes.draw do&lt;br /&gt;  # here I collect all the User subtypes&lt;br /&gt;  user_types = Dir["#{Rails.root}/app/models/user/*.rb"].map do |name|&lt;br /&gt;    "user_" + File.basename(name).gsub('.rb', '').pluralize&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # and then I put all of them one by one&lt;br /&gt;  (['users'] + user_types).each do |name|&lt;br /&gt;    resources name, :controller =&gt; 'users', :path =&gt; '/users' do&lt;br /&gt;      member do&lt;br /&gt;        get 'stats'&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      resources :comments # we can define nested resources too&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;I use the &lt;tt&gt;Rails 3&lt;/tt&gt; notation in this case, but I suppose you can figure how to make it work under Rails 2 as well.&lt;br /&gt;&lt;br /&gt;It is still a bit dodgy, but it is better and more stable than defining those type-specific routes manually in controllers, you also can automatically handle all the nested routes as well.&lt;br /&gt;&lt;br /&gt;That's it, hope that will help&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-939358749874109850?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/939358749874109850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=939358749874109850' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/939358749874109850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/939358749874109850'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/09/easy-way-out-of-sti-hell.html' title='Easy Way Out of The STI Hell'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8542483012900997504</id><published>2010-09-09T15:44:00.005+03:00</published><updated>2010-09-09T16:00:29.119+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS 2 jQuerysh Plugin</title><content type='html'>Hey folks. I had couple of minutes of free time and cooked a fancy plugin for RightJS 2.&lt;br /&gt;&lt;br /&gt;It's called 'jQuerysh' and provides the jquery-like behavior for the &lt;tt&gt;$()&lt;/tt&gt; function. You can copypast or build it from over here &lt;a href="http://github.com/rightjs/rightjs-plugins/blob/2.0.0/src/jquerysh/jquerysh.js"&gt;jquerysh on github&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Basically it does three things with the &lt;tt&gt;$()&lt;/tt&gt; function. You can navigate in jquery-style&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$('#element-id').onClick('addClass', 'marked');&lt;br /&gt;&lt;br /&gt;$('div.class').each('addClass', 'marked');&lt;/pre&gt;&lt;br /&gt;And you can call the &lt;tt&gt;onReady&lt;/tt&gt; via it, same way as in jQuery&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$(function() {&lt;br /&gt;  // will be executed when on ready&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;This is pretty much it, might be useful for people who need to work with both jQuery and RightJS.&lt;br /&gt;&lt;br /&gt;And yes, it works with RightJS 2 only. Not like I want to make you switch to RJS2 (which I actually want), RJS1 is simply not built for that.&lt;br /&gt;&lt;br /&gt;Cheers&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8542483012900997504?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8542483012900997504/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8542483012900997504' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8542483012900997504'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8542483012900997504'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/09/rightjs-2-jquerysh-plugin.html' title='RightJS 2 jQuerysh Plugin'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-761698759515921912</id><published>2010-09-09T08:30:00.006+03:00</published><updated>2010-09-09T09:56:43.608+03:00</updated><title type='text'>RightJS 1 Chrome Issues</title><content type='html'>As much as I like to think about webkit as the world's savior, it constantly produces all sorts of weird stuff in Google Chrome, the last update of which had killed some important feature in RightJS 1 (do those folks even think before they make such changes? because I guess it won't be a problem of RightJS only)&lt;br /&gt;&lt;br /&gt;In any case, the shorts and longs of the story is that they screwed the &lt;tt&gt;Element#hidden()&lt;/tt&gt; method, which broke down several UI units, Autocompleter, Calendar, Lightbox, Selectable and Tooltips.&lt;br /&gt;&lt;br /&gt;I've published a package for you over here &lt;a href="http://rightjs.lighthouseapp.com/projects/31989/tickets/343/a/665444/rui-fixes.zip"&gt;RightJS 1 UI Fixes&lt;/a&gt;. It has patched builds of all those widgets, plus a fresh RightJS 1 build with backports from RightJS 2.&lt;br /&gt;&lt;br /&gt;If this core-build will cause you troubles, you can patch your own by a simple line of code like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Element.insert({&lt;br /&gt;  visible: function() {&lt;br /&gt;    return this.getStyle('display') !== 'none';&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;And don't use the &lt;tt&gt;Element#hidden()&lt;/tt&gt; method in your code, use &lt;tt&gt;Element#visible()&lt;/tt&gt; instead.&lt;br /&gt;&lt;br /&gt;Also think about moving to RightJS2, it works with wrappers and therefore doesn't have such issues by design.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-761698759515921912?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/761698759515921912/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=761698759515921912' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/761698759515921912'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/761698759515921912'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/09/rightjs-1-chrome-issues.html' title='RightJS 1 Chrome Issues'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8517911210139930114</id><published>2010-08-27T18:27:00.003+03:00</published><updated>2010-08-27T18:56:05.955+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='RightJS'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS 2 Release Plan Updates</title><content type='html'>My dear fellas, this lovely Friday's evening I'd like to share with you some updates on the RightJS 2.0 release plan. And yes, by "updates" I mean delays.&lt;br /&gt;&lt;br /&gt;No need for dirty swears, because there are two good reasons for that. 1) Rails 3 which should come out any moment now, and 2) upcoming IE 9 beta which is scheduled to September 15th. And as we all know well those funny MS folks, I think you'll agree that it's better to wait couple of weeks rather than pull your hair off later.&lt;br /&gt;&lt;br /&gt;And it's not all that bad, there are good news too. I'm almost finished porting the UI modules to RJS 2, which means there well be the RC2 release in about couple of days I think. RC2 will include the core with all the latest updates and all the plugins and widgets ported to the new system. Generally this will be the final version and unless someone find a bug it will go to the actual release as is.&lt;br /&gt;&lt;br /&gt;You'll have couple of weeks to play with it until the IE 9 release, and I'll have couple of weeks to brush up the docs, update rightjs.org and rework right-rails. Thinks that should give as a good runaway for a nice and smooth release.&lt;br /&gt;&lt;br /&gt;This is about it. Take care!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8517911210139930114?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8517911210139930114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8517911210139930114' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8517911210139930114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8517911210139930114'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/08/rightjs-2-release-plan-updates.html' title='RightJS 2 Release Plan Updates'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1417407024887295293</id><published>2010-08-17T10:20:00.003+03:00</published><updated>2010-08-17T11:06:31.840+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS UI 2 The Battle Plan</title><content type='html'>Hello people. As I'm currently elbow deep in porting rightjs-ui modules to RightJS 2, I'd like to share some ideas and thoughts behind the changes so you knew what to expect.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;RightJS 2 With New UI System&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First of all the RightJS 2 release will be not just the core release, there are lots of cool new features and some dramatic changes, so I'm porting all the plugins and ui widgets to the new development approach, and with the RightJS 2 release I'll release updates for all the plugins as well.&lt;br /&gt;&lt;br /&gt;This way we will have some sort of a clean slate, and you'll be able to start with all new tools right away.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Widgets === Elements&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;One of the biggest change in RightJS 2 is those &lt;a href="http://st-on-it.blogspot.com/2010/08/dom-wrappers-in-rightjs-2.html"&gt;really cool object-oriented dom-wrappers feature&lt;/a&gt;, and in the RightJS UI 2 we will take full advantage of it. If in the current system all the widgets are classes with some limited interface, in RightJS UI 2, all the widgets will be inherited from the dom-wrappers, so you will be able to manipulate with them as if they were normal elements.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;// say you have a calendar unit&lt;br /&gt;var calendar = new Calendar({&lt;br /&gt;  // some options&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;// now you can do anything you want as if it was a normal Element&lt;br /&gt;calendar.insertTo('my-element', 'top');&lt;br /&gt;calendar.addClass('my-calendar');&lt;br /&gt;calendar.set('id', 'my-calendar-id');&lt;br /&gt;calendar.onChange(function() {&lt;br /&gt;  // do something about it&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;// you also will be able to access it through the standard navigation methods&lt;br /&gt;$('my-calendar-id'); // -&gt; the 'calendar'&lt;br /&gt;$('my-element').first('div.my-calendar'); // -&gt; the same calendar&lt;br /&gt;&lt;br /&gt;// it will be also follow the general inheritance structure&lt;br /&gt;calendar instanceof Calendar; // -&gt; true&lt;br /&gt;calendar instanceof Element;  // -&gt; true&lt;/pre&gt;&lt;br /&gt;You've got the picture, everything you can do with any element on the page you will be able to do with the widgets and do that directly.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Unified Initialization&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;RightJS UI were growing a bit chaotically during the last year and we have some diverse ways to initialize units. With the new release all of them will work via the same initialization process with the same principles. The &lt;tt&gt;rel&lt;/tt&gt; hacks are gone, instead of that we will use the HTML5 &lt;tt&gt;data-widget-name&lt;/tt&gt; attributes.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&amp;lt;input data-autocompleter="{url: '/some.url'}" /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;input data-calendar="{format: 'US', hideOnClick: true}" /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Meta-framework and Modularity&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;In current incarnation all the RightJS UI widgets are pretty much independent, which means that we have some duplications here and there, same things work slightly different, not really clean styles and so one.&lt;br /&gt;&lt;br /&gt;It's time to refactor the mess, with RightJS UI 2 there will be a meta framework which will provide common bits of elements and styles, like say buttons, panels, spinners, various common functionality and so one. This way we will have a consistent elements behavior over the widgets, plus it will be easier to create new widgets with the common blocks of code already existing.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Unified Stylesheets&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The stylesheets also will be cleaned up and unified. There will be common classes for buttons, panels, spinners and so one. One of the reasons is that now we will be able to create skins in a civilized way.&lt;br /&gt;&lt;br /&gt;There will be no actual skins with the release, we simply do the preparation work, but the skins constructor is one of the first things to do right after the release.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Summary&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As you can see with RightJS 2 release we will bring to the next level not just the core, but the ui modules as well.&lt;br /&gt;&lt;br /&gt;I've already ported the &lt;tt&gt;Autocompleter&lt;/tt&gt; and &lt;tt&gt;Calendar&lt;/tt&gt; widgets, you can check it out at the &lt;a href="http://github.com/rightjs/rightjs-ui/tree/2.0.0"&gt;2.0.0 branch&lt;/a&gt; on github. I estimate it will take about a week to port the rest of them to RightJS 2.&lt;br /&gt;&lt;br /&gt;After that there will be RC2 release where I'll publish RightJS core and all the modules for tests. And in about a week after that (by the end of August) we shall release.&lt;br /&gt;&lt;br /&gt;This is about it.&lt;br /&gt;&lt;br /&gt;And btw, if you have thoughts about new features for some of the existing rightjs-ui widgets, that's a really good moment to let me know.&lt;br /&gt;&lt;br /&gt;Now that's it.&lt;br /&gt;Take care!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1417407024887295293?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1417407024887295293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1417407024887295293' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1417407024887295293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1417407024887295293'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/08/rightjs-ui-2-battle-plan.html' title='RightJS UI 2 The Battle Plan'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-74704520630130561</id><published>2010-08-07T14:21:00.003+03:00</published><updated>2010-08-07T15:31:32.723+03:00</updated><title type='text'>DOM-Wrappers In RightJS 2</title><content type='html'>Put your tin-foil hats on people, because it's your next brainwashing session about upcoming RightJS 2.&lt;br /&gt;&lt;br /&gt;When you heard that RightJS 2 is going to have dom-wrappers instead of direct dom-units access you might think "oh, that's not a big deal, jQuery has dom-wrappers from the very beginning!". And you will be dead wrong! I already touched it a bit in one of my previous articles, now let me show this beauty in details.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;DOM-Wrappers == Classes&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First of all, dom-wrappers in RightJS are not just a bunch of functions behind a namespace, dom-wrappers in RightJS essentially are normal classes, with inheritance and functionality-injection support. For example you have the basic class &lt;tt&gt;Element&lt;/tt&gt; and two subclasses &lt;tt&gt;Form&lt;/tt&gt; and &lt;tt&gt;Input&lt;/tt&gt;, which means you can say normally call &lt;tt&gt;instanceof&lt;/tt&gt; on them.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$('div') instanceof Element;   // true&lt;br /&gt;$('div') instanceof Input;     // false&lt;br /&gt;$('div') instanceof Form;      // false&lt;br /&gt;&lt;br /&gt;$('form') instanceof Element;  // true&lt;br /&gt;$('form') instanceof Input;    // false&lt;br /&gt;$('form') instanceof Form;     // true&lt;br /&gt;&lt;br /&gt;$('input') instanceof Element; // true&lt;br /&gt;$('input') instanceof Input;   // true&lt;br /&gt;$('input') instanceof Form;    // false&lt;/pre&gt;&lt;br /&gt;You also can extend them separately and all the methods will follow the inheritance structure principles&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Element.include({&lt;br /&gt;  global_method: function() {&lt;br /&gt;    // this method will appear on all elements&lt;br /&gt;  }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;Input.include({&lt;br /&gt;  inputs_only: function() {&lt;br /&gt;    // this method will appear on Input elements only&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;You can use polymorphism, redefine the meaning of methods, etc. For example the &lt;tt&gt;Element#select&lt;/tt&gt; will search for matching elements by a css-rule, but &lt;tt&gt;Input#select&lt;/tt&gt; will put the focus on the input element and select its content. No problem, you can do that easily.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Define Your Own Wrappers&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The usual extending methods like &lt;tt&gt;Element#include&lt;/tt&gt;, &lt;tt&gt;Form#include&lt;/tt&gt; are fine, but in RightJS you are also allowed to define your own wrappers for certain types of elements. For example you can define a tables-sepecific wrapper&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Element.Wrappers.TABLE = new Wrapper(Element, {&lt;br /&gt;  sortBy: function(column) {&lt;br /&gt;    // sort the table body&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;Or you could create a textareas wrapper the same way&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Element.Wrappers.TEXTAREA = new Wrapper(Input, {&lt;br /&gt;  spellcheck: function() {&lt;br /&gt;    // spellcheck the content&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;NOTE: we inherited the &lt;tt&gt;Element&lt;/tt&gt; and &lt;tt&gt;Input&lt;/tt&gt; classes as you would normally do with any other classes in RigthJS.&lt;br /&gt;&lt;br /&gt;Once you've done that, anytime you access a table or a textarea you'll have those new methods available&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$('my-table').sortBy('name');&lt;br /&gt;&lt;br /&gt;$('my-textarea').spellcheck();&lt;/pre&gt;&lt;br /&gt;This feature will let you to adjust and completely redefine the framework itself for the needs of your application.&lt;br /&gt;&lt;br /&gt;But this is not the most kick-ass thing yet. Because we have much more!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Private DOM-Wrappers&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;With RightJS 2 you are not limited to the actual dom-nodes and tags. You can define your very own private dom-wrappers that won't be tied to the framework structure in any way. Why would you need that? The answer is simple - widgets.&lt;br /&gt;&lt;br /&gt;How do you normally write a widget? You create some class which aggregates some main element and then you have all the headaches with different contexts and bindings, because half of your logic is in your class and half is in your elements&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var MyWidget = new Class(Observer, {&lt;br /&gt;  initialize: function() {&lt;br /&gt;    this.element = $E('div')&lt;br /&gt;      .onClick(this.clicked.bind(this));&lt;br /&gt;  },&lt;br /&gt;&lt;br /&gt;  clicked: function() {&lt;br /&gt;     // do something about it.&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;Then more, you'll have to create several proxy methods to delegate the methods to actually insert the widget on the page and do other normal dom-manipulations and so one and so one. In more or less complicated case those things tend to get quite messy.&lt;br /&gt;&lt;br /&gt;Well my friends, you don't have to suffer anymore, because now you can define your widget as a private wrapper and inherit any existing dom-wrapper directly just like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var MyWidget = new Wrapper(Element, {&lt;br /&gt;  initialize: function(widget_id) {&lt;br /&gt;    this.$super('div', {&lt;br /&gt;      'id': widget_id,&lt;br /&gt;      'class': 'my-widget'&lt;br /&gt;    });&lt;br /&gt;    this.onClick('doSomething');&lt;br /&gt;&lt;br /&gt;    // build your entire widget over here&lt;br /&gt;  },&lt;br /&gt;&lt;br /&gt;  doSomething: function() {&lt;br /&gt;    // do something about the click&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;NOTE: now you never leave the actual element context, don't need to worry about bindings, and then, can manipulate with your widget as a usual element on your page.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var widget = new MyWidget('widget-id');&lt;br /&gt;widget.insertTo('some-element', 'after');&lt;br /&gt;widget.addClass('custom-class');&lt;br /&gt;&lt;br /&gt;// more of that!&lt;br /&gt;$('widget-id') instanceof MyWidget; // true!&lt;/pre&gt;&lt;br /&gt;And there is more! You can inherit your private wrappers as any other and create different versions of your widget!&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var MySuperWidget = new Wrapper(MyWidget, {&lt;br /&gt;  initialize: function(widget_id) {&lt;br /&gt;    this.$super(widget_id);&lt;br /&gt;&lt;br /&gt;    this.onClick('doSuperThing');&lt;br /&gt;  },&lt;br /&gt;&lt;br /&gt;  doSuperThing: function() {&lt;br /&gt;    // do the super thing over here!&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;You can create abstract widgets, share modules between them, do any sorts of crazy things, the possibilities are endless! And at the end, you still have an &lt;tt&gt;Element&lt;/tt&gt;, which you can toss around the page in the usual way.&lt;br /&gt;&lt;br /&gt;---&lt;br /&gt;&lt;br /&gt;Oh yes my dear fellas, RightJS 2 will help you to kick some serious ass! The right way!&lt;br /&gt;&lt;br /&gt;And this is about it. Come over for the next session, it will be fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-74704520630130561?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/74704520630130561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=74704520630130561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/74704520630130561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/74704520630130561'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/08/dom-wrappers-in-rightjs-2.html' title='DOM-Wrappers In RightJS 2'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-117041280888406347</id><published>2010-08-05T15:55:00.005+03:00</published><updated>2010-08-05T16:18:13.971+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>Prepare Your Project For RightJS 2</title><content type='html'>Some notes for those lucky of you who already is hooked to RightJS and the ones who think about giving it a try, but afraid that when RightJS 2 will start saving the world, the ones code will go south.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Rule #1&lt;/b&gt; &lt;tt&gt;don't panic&lt;/tt&gt;, despite of all the massive changes in the dom-stack and all the new finest kickassery, the actual API almost didn't change, just instead of real dom-nodes/events/etc you will receive some proxy objects, which have the same exact API. The difference is that you will have no direct access to the dom-unit attributes, so if you want your code to be working both in RightJS 1 and RightJS 2, use the &lt;tt&gt;Element#get&lt;/tt&gt;, &lt;tt&gt;#set&lt;/tt&gt; and &lt;tt&gt;#has&lt;/tt&gt; methods instead.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;// instead of this&lt;br /&gt;$(element).id = 'boo-hoo';&lt;br /&gt;var id = $(element).id ? $(element).id : 'default';&lt;br /&gt;  &lt;br /&gt;// write it like that&lt;br /&gt;$(element).set('id', 'boo-hoo');&lt;br /&gt;var id = $(element).has('id') ? $(element).get('id') : 'default';&lt;/pre&gt;&lt;br /&gt;If you need to access an element &lt;tt&gt;innerHTML&lt;/tt&gt; use a simple patch like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Element.include({&lt;br /&gt;  html: function() {&lt;br /&gt;    return this.innerHTML;&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;Later, when you will migrate to the actual RightJS 2, just remove it, RightJS 2 will have this method out of box.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Rule #2&lt;/b&gt; &lt;tt&gt;don't access `window` and `document` directly&lt;/tt&gt;, when you need to access some of the RightJS methods, put them through the same &lt;tt&gt;`$()`&lt;/tt&gt; function first as you do with the dom elements&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;// instead of those&lt;br /&gt;document.onReady(function() { ... });&lt;br /&gt;window.sizes();&lt;br /&gt;&lt;br /&gt;// call it like this&lt;br /&gt;$(document).onReady(function() { ... });&lt;br /&gt;$(window).sizes();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Rule #3&lt;/b&gt; &lt;tt&gt;don't use `Form.Element`&lt;/tt&gt; this unit will be renamed to &lt;tt&gt;Input&lt;/tt&gt; in RightJS 2, so don't touch it or create a simple reference like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var Input = Form.Element;&lt;/pre&gt;&lt;br /&gt;and work with the &lt;tt&gt;Input&lt;/tt&gt; object instead.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This is about it. If you follow those three simple rules, all your code should be in working condition when you switch to RightJS 2.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-117041280888406347?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/117041280888406347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=117041280888406347' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/117041280888406347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/117041280888406347'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/08/prepare-your-project-for-rightjs-2.html' title='Prepare Your Project For RightJS 2'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3335249277742866050</id><published>2010-08-01T09:09:00.002+03:00</published><updated>2010-08-01T09:27:04.102+03:00</updated><title type='text'>The Circus of Web Development</title><content type='html'>You might think of the web-development area (or sphere if you will) as some sort of a circus. For example.&lt;br /&gt;&lt;br /&gt;Say there is an elephants show of Java developers, they big, slow, but precise. Then there is a exotic pet show of Python developers. Fearless camel riders with the perl guys. JavaScript people will be magicians, weird fellas who make things appear and disappear by some flimsy tricks.&lt;br /&gt;&lt;br /&gt;Then there are a bunch of trained monkeys of Ruby kids (yes my agile friends we are :)). ASP.NET folks will be gipsy fortune-tellers, barely connected with the reality and no-one listen to them.  And there are bears on monocycles in silly turkish hats, those are the PHP people (you are comrades, you are).&lt;br /&gt;&lt;br /&gt;Clowns! Those will be the web-architects. Just like the real clowns, they barely can do a thing properly, and yet, they think they own the circus.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3335249277742866050?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3335249277742866050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3335249277742866050' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3335249277742866050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3335249277742866050'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/08/circus-of-web-development.html' title='The Circus of Web Development'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8830832481689882727</id><published>2010-07-31T11:18:00.010+03:00</published><updated>2010-08-07T18:15:23.779+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS 2.0.0-rc Is Out</title><content type='html'>Okay folks, RightJS 2 came to the point where it has a proper number and the &lt;tt&gt;'-rc'&lt;/tt&gt; suffix in it's name. And here are some notes on the new sweet features we have since the beta release.&lt;br /&gt;&lt;br /&gt;You can download the build &lt;a href="http://groups.google.com/group/rightjs/attach/d8abe13595f03a89/RightJS-2.0.0-rc.zip?hl=en&amp;part=2"&gt;over here&lt;/a&gt;, the archive contains both normal and safe-mode builds.&lt;br /&gt;&lt;br /&gt;Generally this is almost the final line, I've finished packing it with new features and this is practically all we will have in the actual 2.0.0 release. There might be some tweaks here and there during the plugins and UI modules porting, but nothing dramatic. So lets check what we've got.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Dom Wrappers&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;In RightJS 2.0 we have a new kick-ass object oriented dom-wrappers system. Our dom-wrappers are essentially classes, with types, inheritance and all the nice functionality injection features. Currently we have the following wrappers &lt;tt&gt;Window&lt;/tt&gt;, &lt;tt&gt;Document&lt;/tt&gt;, &lt;tt&gt;Event&lt;/tt&gt;, &lt;tt&gt;Element&lt;/tt&gt;, &lt;tt&gt;Form&lt;/tt&gt; and &lt;tt&gt;Input&lt;/tt&gt;, which formerly was known as the &lt;tt&gt;Form.Element&lt;/tt&gt; unit.&lt;br /&gt;&lt;br /&gt;Dom-wrappers work transparently and have all the same nice API we had in RightJS 1, the only difference is that instead of the actual dom-element all the methods now operate with dom-wrappers.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$('element-id')  // -&gt; dom wrapper&lt;br /&gt;$$('#css .rule') // -&gt; a list of dom-wrappers&lt;/pre&gt;&lt;br /&gt;All the dom wrappers are inherited from the same unit called &lt;tt&gt;`Wrapper`&lt;/tt&gt; and have a common property called &lt;tt&gt;`_`&lt;/tt&gt; which refers to the raw dom element, for example&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$('element-id')   // -&gt; Element wrapper&lt;br /&gt;$('element-id')._ // -&gt; the actual dom-node&lt;br /&gt;&lt;br /&gt;$(window)         // -&gt; Window wrapper&lt;br /&gt;$(window)._       // -&gt; the window object&lt;br /&gt;&lt;br /&gt;$(document)       // -&gt; Document wrapper&lt;br /&gt;$(document)._     // -&gt; the document object&lt;/pre&gt;&lt;br /&gt;So every time when you fill like it, you can always quickly access to the raw unit the same way across all the wrappers.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Dom-Wrappers Typecasting&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As you might noticed we have three types of wrappers for dom-elements, the &lt;tt&gt;Form&lt;/tt&gt; and &lt;tt&gt;Input&lt;/tt&gt; classes are subclasses of the &lt;tt&gt;Element&lt;/tt&gt; class and when you access your dom-elements they dynamically typecasted&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var element = $('my-form');&lt;br /&gt;element instanceof Element; // -&gt; true&lt;br /&gt;element instanceof Form;    // -&gt; true&lt;br /&gt;element instanceof Input;   // -&gt; false&lt;br /&gt;&lt;br /&gt;var element = $('my-input');&lt;br /&gt;element instanceof Element; // -&gt; true&lt;br /&gt;element instanceof Input;   // -&gt; true&lt;br /&gt;element instanceof Form;    // -&gt; false&lt;/pre&gt;&lt;br /&gt;This way you can check which type of element do you have and you can extend all of them separately&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;&lt;br /&gt;Element.include({&lt;br /&gt;  global_method: function() {}&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;Form.include({&lt;br /&gt;  forms_only_method: function() {}&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;Input.include({&lt;br /&gt;  inputs_only_method: function() {}&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;You even can use polymorphism and use the same method names&lt;br /&gt;&lt;br /&gt;And you also can define your own types, for example&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Element.Wrappers.TABLE = new Wrapper(Element, {&lt;br /&gt;  orderBy: function() {}&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;Later on there will be also private dom-wrappers which you can use in your app without extending the RightJS core and interfere with other applications.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bubbling Dom-Events&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;RightJS also received several fixes for the traditionally problematic dom-events like &lt;tt&gt;focus&lt;/tt&gt;, &lt;tt&gt;blur&lt;/tt&gt;, &lt;tt&gt;change&lt;/tt&gt; and &lt;tt&gt;submit&lt;/tt&gt;, now all of them properly bubble on all the supported browsers.&lt;br /&gt;&lt;br /&gt;There is nothing kinky about it I didn't use any special event names, just use the usual ones like nothing happened.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$(element).on({&lt;br /&gt;  focus: ...,&lt;br /&gt;  blur: ...,&lt;br /&gt;  change: ...,&lt;br /&gt;  submit: ...&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;I also added the custom events bubbling. If in RightJS the custom events didn't go anywhere from the node where you fired it, now they properly bubble up to the &lt;tt&gt;document&lt;/tt&gt; object, so you can take advantage of them using the events-delegation technique.&lt;br /&gt;&lt;br /&gt;Speaking of which.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;New Events Delegation API&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;In the later releases of RightJS 1.5 we already had some elements of the events-delegation feature support, now it's time to take it to the next level.&lt;br /&gt;&lt;br /&gt;First of all the &lt;tt&gt;String&lt;/tt&gt; unit now has all the same API as &lt;tt&gt;Element&lt;/tt&gt;, so you don't need to learn nothing new, just use what you already know.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;"#div .span".on('click', function() {...});&lt;br /&gt;"#div .span".onClick(function() {});&lt;br /&gt;"#div .span".onClick('addClass', 'marked');&lt;br /&gt;&lt;br /&gt;// you also can make the normal checks&lt;br /&gt;"#div .span".observes('click'); -&gt; true&lt;br /&gt;"#div .span".observes('mouseover'); -&gt; false&lt;br /&gt;&lt;br /&gt;// and you can unbind the listeners the usual way&lt;br /&gt;"#div .span".stopObserving('click', function() {});&lt;/pre&gt;&lt;br /&gt;Then the &lt;tt&gt;Event.delegate&lt;/tt&gt; and &lt;tt&gt;Event.behave&lt;/tt&gt; methods are gone and instead of them we have a proper events delegation API on dom-wrappers which look like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$(document).delegate('click', '#css.rule', function() {});&lt;br /&gt;$(document).delegate('click', {&lt;br /&gt;  '#css.rule1': function() {},&lt;br /&gt;  '#css.rule2': function() {}&lt;br /&gt;  ....&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;// delegation checks&lt;br /&gt;$(document).delegates('click');&lt;br /&gt;$(document).delegates('click', '#css.rule');&lt;br /&gt;&lt;br /&gt;// disabling delegation&lt;br /&gt;$(document).undelegate('click');&lt;br /&gt;$(document).undelegate('click', '#css.rule');&lt;/pre&gt;&lt;br /&gt;More of that the same API was adde to the &lt;tt&gt;Element&lt;/tt&gt; wrapper so you can attach your delegating listeners at any level of your web-page&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$('my-list').delegate('click', {&lt;br /&gt;  'li.one-thing': function() {}&lt;br /&gt;  'li.another':    function() {}&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;This way you can scope the delegation to some block of your document and handle the things locally.&lt;br /&gt;&lt;br /&gt;You also can delegate custom events too&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$(document).delegate('bingo', {&lt;br /&gt;  '#one':   function() {}&lt;br /&gt;  '#another': function() {}&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;$('one').fire('bingo');&lt;br /&gt;$('another').fire('bingo');&lt;/pre&gt;&lt;br /&gt;Generally there are no limitations and you handle any sort of events simultaneously via the same api.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;$$ Is Back!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As you might noticed I moved the &lt;tt&gt;$$&lt;/tt&gt; function back. I tried to use the jQuery-like unified &lt;tt&gt;$&lt;/tt&gt; method but didn't like it and brought the &lt;tt&gt;$$&lt;/tt&gt; back.&lt;br /&gt;&lt;br /&gt;Amen.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Semi-Safe Mode&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As you might know RightJS 2.0 will have the safe-mode build, which will hide everything behind a single &lt;tt&gt;RightJS&lt;/tt&gt; object and won't extend a thing on the user's page.&lt;br /&gt;&lt;br /&gt;A typical use case would look like that&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;RightJS.$('element-id').onClick(....);&lt;br /&gt;&lt;br /&gt;var MyClass = new RightJS.Class({&lt;br /&gt;  ...&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;There is also a way to access the language unit extensions by passing variables through the &lt;tt&gt;RightJS&lt;/tt&gt; object&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;RightJS(['boo', 'hoo', 'doh']).filter('includes', 'oo');&lt;br /&gt;RightJS(4).times(function() { .... });&lt;br /&gt;RightJS('boo hoo').endsWith('hoo');&lt;/pre&gt;&lt;br /&gt;In the RC release I added a semi-safe API for the non-safe builds, meaning you can put your variables through and access the RightJS units via the &lt;tt&gt;RightJS&lt;/tt&gt; object. This way if you need say to write a plugin that supposed to be working in both safe and non-safe modes, you can simply write it for the safe-mode and it will be working in the non-safe mode too.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JSONP Support&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;In RightJS 2.0 there is the &lt;tt&gt;JSONP&lt;/tt&gt; format support for the &lt;tt&gt;Xhr&lt;/tt&gt; unit. It all works transparently via the same &lt;tt&gt;Xhr&lt;/tt&gt; API with all the same principles, the only thing is that now you have the &lt;tt&gt;jsonp&lt;/tt&gt; option where you can specify your callback name variable, or simply use &lt;tt&gt;true&lt;/tt&gt; for the default &lt;tt&gt;'callback'&lt;/tt&gt; value.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;new Xhr('/some/url', {&lt;br /&gt;  jsonp: 'myCallback',&lt;br /&gt;  onSuccess: function() {&lt;br /&gt;    // ....&lt;br /&gt;  }&lt;br /&gt;}).send();&lt;br /&gt;&lt;br /&gt;Xhr.load('/some/url', {jsonp: true});&lt;/pre&gt;&lt;br /&gt;It even will assign the &lt;tt&gt;responseJSON&lt;/tt&gt; and &lt;tt&gt;json&lt;/tt&gt; varaibles&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;Xhr.load('/some/url', {&lt;br /&gt;  jsonp: true,&lt;br /&gt;  onSuccess: function() {&lt;br /&gt;    console.log(this.json);&lt;br /&gt;  }&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;New Language Level Methods&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;There are also several new javascript level extensions were added. &lt;tt&gt;Object.each&lt;/tt&gt;, &lt;tt&gt;Array#reject&lt;/tt&gt; (which is useful with calls by name), &lt;tt&gt;Array#min&lt;/tt&gt;, &lt;tt&gt;Array#max&lt;/tt&gt;, &lt;tt&gt;Array#sum&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Probably something else that I have forgotten.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Next Steps&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Now when the core is finished, there are quite a lot of work with porting the plugins to the new version and reworking the documentation. The site also needs to be reworked to support the new building system.&lt;br /&gt;&lt;br /&gt;At the moment I estimate it like 2-3 weeks of work. So the delivery-day is somewhere by the end of summer.&lt;br /&gt;&lt;br /&gt;Okay, think that's all I have for know.&lt;br /&gt;Take care!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8830832481689882727?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8830832481689882727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8830832481689882727' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8830832481689882727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8830832481689882727'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/07/rightjs-200-rc-is-out.html' title='RightJS 2.0.0-rc Is Out'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1600197111181042968</id><published>2010-07-30T11:15:00.011+03:00</published><updated>2010-08-05T14:54:29.430+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Why Albeit Packing Is A Bad Idea</title><content type='html'>Okay I did it quite a lot on &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt;, but I've learned my lesson. A quick and self-explanatory example would be like that.&lt;br /&gt;&lt;br /&gt;Say you have the following piece of javascript&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;a1.looooongMethod();&lt;br /&gt;a2.looooongMethod();&lt;br /&gt;a3.looooongMethod();&lt;br /&gt;a4.looooongMethod();&lt;br /&gt;a5.looooongMethod();&lt;/pre&gt;&lt;br /&gt;Being an optimization junky, you might think, "Oh! I can make it smaller!"&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;var b = 'looooongMethod';&lt;br /&gt;a1[b]();&lt;br /&gt;a2[b]();&lt;br /&gt;a3[b]();&lt;br /&gt;a4[b]();&lt;br /&gt;a5[b]();&lt;/pre&gt;&lt;br /&gt;Then you look at the size, the first one weights &lt;tt&gt;104&lt;/tt&gt; bytes and the second one &lt;tt&gt;70&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;"Woohoo!", you say, "30% optimization!". And will be wrong.&lt;br /&gt;&lt;br /&gt;Because when a web-server gzips your code, the first one will weight &lt;tt&gt;61&lt;/tt&gt; bytes and your optimized version will be... TADA! &lt;tt&gt;72&lt;/tt&gt; bytes. Yup, bigger than it was before 8)&lt;br /&gt;&lt;br /&gt;That's the whole story. Gonna go and unoptimize things back now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1600197111181042968?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1600197111181042968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1600197111181042968' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1600197111181042968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1600197111181042968'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/07/why-albeit-packing-is-bad-idea.html' title='Why Albeit Packing Is A Bad Idea'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-6428297090227193098</id><published>2010-07-19T10:57:00.003+03:00</published><updated>2010-07-19T11:06:44.596+03:00</updated><title type='text'>Freezing Terminal.app in OS X</title><content type='html'>I ran in a strange bug yesterday on my macbook. Suddenly the Terminal.app started to freeze when you lunch it or open another window.&lt;br /&gt;&lt;br /&gt;I killed half a day trying to figure out what's wrong with my HDD, and trying to restore parts of the system from a backup, but then found out that the problem was in another place.&lt;br /&gt;&lt;br /&gt;So if you run into the same issue the cure is simple.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo rm -rf /private/var/log/asl/*&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Apparently there is some problem with the log files that cause circular read/write calls, so when you nuke the old logs, it will get just fine&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-6428297090227193098?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/6428297090227193098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=6428297090227193098' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6428297090227193098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6428297090227193098'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/07/freezing-terminalapp-in-os-x.html' title='Freezing Terminal.app in OS X'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4695433922897589334</id><published>2010-06-29T18:42:00.004+03:00</published><updated>2010-06-29T19:02:26.736+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>The RightJS DOM-Wrappers Speed</title><content type='html'>Okay folks, there is your next brainwashing session to keep you hooked to the right stuff.&lt;br /&gt;&lt;br /&gt;When I &lt;a href="http://st-on-it.blogspot.com/2010/06/rightjs-development-status.html" target="_blank"&gt;announced&lt;/a&gt; that RightJS is switching to dom-wrappers, some of you seems started to panic. "Oh, no!", you said, "RightJS is going down, because native extensions murk dom-wrappers any day at any time!"&lt;br /&gt;&lt;br /&gt;Well people, you just haven't seen the right wrappers yet 8). Here is the shot of the new engine in its raw, not fully optimized state.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/warp2/warp2-ff.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/warp2/warp2-ff-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;b&gt;FF 3.6.6&lt;/b&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;And so you didn't think that it's just FF, here is a shot in Opera.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/warp2/warp2-opera.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/warp2/warp2-opera-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Opera 10.54&lt;/b&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Don't have a short for IE though, because didn't quite make it there yet, but you can imagine that it will go sky rocket, because there will be no elements extending anymore.&lt;br /&gt;&lt;br /&gt;Some of you, young and sharp, might notice that, the ID access went down for a notch. That's all because of you guys, that's because in RightJS 2, you'll have the same &lt;tt&gt;&lt;b&gt;$&lt;/b&gt;&lt;/tt&gt; method behavior as you have in your beloved jQuery, meaning you'll navigate it like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;$('#boo-hoo'); // -&gt; an element by id&lt;br /&gt;$('div.boo, div.hoo'); // -&gt; elements by the css-rule&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;There also will be things like &lt;tt&gt;$(window)&lt;/tt&gt; and &lt;tt&gt;$(document)&lt;/tt&gt;, so you'll feel yourself like home.&lt;br /&gt;&lt;br /&gt;That's about it. Hold on to the next session!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4695433922897589334?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4695433922897589334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4695433922897589334' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4695433922897589334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4695433922897589334'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/06/rightjs-dom-wrappers-speed.html' title='The RightJS DOM-Wrappers Speed'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7943432889902154089</id><published>2010-06-26T10:34:00.004+03:00</published><updated>2010-06-26T11:50:38.079+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS Development Status</title><content type='html'>Hey folks, to keep in touch, some notes on the &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; development status.&lt;br /&gt;&lt;br /&gt;As you probably already know the next step in the RightJS evolution is coming. Currently I do all the fixes and main feature updates in the 'master' branch and all the dramatic changes go into the 'two.o.o' branch. So if you'd like to see what's going on, go and check it out from github.&lt;br /&gt;&lt;br /&gt;So what's actually going on?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;New building system&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First of all I move the source code building system from the amaturish concatenation of some pieces of code to more serious layouts based builds.&lt;br /&gt;&lt;br /&gt;What does it mean?&lt;br /&gt;&lt;br /&gt;It's fairly simple. Instead of having all sorts of small isolated pieces of code, now we have one global function which isolates the RightJS initialization process inside. It doesn't mean that RightJS now closes its insides like jQuery does. No, all the crazy monkey patching abilities remain in place and it is still an open architecture, but now we don't need to worry about accidentally polluting the global scope with some temporary variables, plus it gives us a great deal of optimization abilities in speed and size.&lt;br /&gt;&lt;br /&gt;Secondly, now we use the google's closure compiler to compact the javascript code. It works a bit faster and provides slightly better results than my FrontCompiler.&lt;br /&gt;&lt;br /&gt;I haven't finished yet with all the size optimizations yet, but the layout based builds + google-compiler are already giving us some benefits. Despite that I already added quite a few new features and methods, the size of the builds went down at about 1K and goes like 40K of the minified (not packed) code, which is about 15K in gzip.&lt;br /&gt;&lt;br /&gt;Then it seems like I'm going to get rid of the albeit-packed builds. The reason is that most of the web-servers those days use gzip compression and in this case there is no significant difference between the packed and minified versions. And as the packed version initializes slower than the minified one I think there is not much of reasons to continue to support it.&lt;br /&gt;&lt;br /&gt;In any case, you'll be able to albeit-pack RightJS with FrontCompiler at any moment by yourself.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Safe Mode&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The are another reason for the layouts based builds - the safe-mode development. And currently there are several directions which the safe-mode development goes. &lt;br /&gt;&lt;br /&gt;First of all the name spaced mode. Now as RigthJS initializes inside of a layout, it extends the global scope only at the very end. So you can easily have a name spaced build by removing one line of code from the end of the file and all the RightJS global objects will be available in the scope of the RightJS object.&lt;br /&gt;&lt;br /&gt;You still will have all the native and dom object extensions with all the benefits and troubles and your code would look like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;with (RightJS) {&lt;br /&gt;  var MyClass = new Class({&lt;br /&gt;    include: Options,&lt;br /&gt;    &lt;br /&gt;    initialize: function(id) {&lt;br /&gt;      this.element = $(id);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Or, if you're against the &lt;tt&gt;with&lt;/tt&gt; calls, like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var MyClass = new RightJS.Class({&lt;br /&gt;  include: RightJS.Options,&lt;br /&gt;    &lt;br /&gt;  initialize: function(id) {&lt;br /&gt;    this.element = RightJS.$(id);&lt;br /&gt;  }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The second direction is so called condom-mode. The idea is that we initialize RightJS in a separated IFrame and then hook up the main window to the functionality via the &lt;tt&gt;RightJS&lt;/tt&gt; object. In this case you have almost complete isolation of the contexts, RightJS won't touch the main window units and you also will have access to the RightJS fancy native extensions like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var R = RightJS;&lt;br /&gt;&lt;br /&gt;R('boo.hoo').endsWith('hoo');&lt;br /&gt;R(4).times(...);&lt;br /&gt;R(function() { }).bind(...);&lt;br /&gt;R([1,2,3,4]).without(2,3);&lt;br /&gt;R.$('element-id').onClick(....);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;RightJS will extend only those dom-elements which you actually work with. So you'll still have the RightJS speed and easy goingness, but you also have the ability to safely implement say widgets on someones page.&lt;br /&gt;&lt;br /&gt;The condom mode is mostly ready, it still has some unfinished parts with the window and document access, but all the other things are already working. you can build and play with it in the `two.o.o` branch like so&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;rake build OPTIONS=safe&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The DOM-Wrappers&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The condom-mode is just a step to the next level, it is an interesting thing but it still does extend dom-elements you access. So I'm currently working on the next step which is the dom-wrappers.&lt;br /&gt;&lt;br /&gt;The idea is to replace the direct dom-elements access with some artificial proxy which will have all the same interface as a normal dom-element but without actually extending the dom-elements themselves. The reasons are obvious, it's a peaceful coexistence with another scripts on the page and better cross-frame scripting abilities/performance.&lt;br /&gt;&lt;br /&gt;This feature is still in progress and you'll see the first results in about couple of weeks.&lt;br /&gt;&lt;br /&gt;After that, that's it, we release.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Summary&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;One way or another, with the 2.0.0 release we should fix the only problem that wrong with the right javascript framework, the safety. We will have normal quick and naughty builds as we have now, but we also will have options with the namespaced and condom modes.&lt;br /&gt;&lt;br /&gt;Which means we will have a rock-solid safety against the anxious MSIE or any other browser that will think its smarter than others, and you also will have the ability to use RightJS to develop widgets in the safe-mode.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;That's about it.&lt;br /&gt;Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7943432889902154089?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7943432889902154089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7943432889902154089' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7943432889902154089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7943432889902154089'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/06/rightjs-development-status.html' title='RightJS Development Status'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4504422062473167662</id><published>2010-06-02T18:50:00.007+03:00</published><updated>2010-08-24T11:57:34.428+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTML/CSS'/><title type='text'>Postion fixed in IE6</title><content type='html'>If you old enough you might remember that wonder of technology we used to admire something like ten years ago, which also known those days as the zombi-browser aka IE6. And if you old enough and unlucky enough to have a need to make something with "position:fixed" under that browser, you might think about the usual approach with hooking up the window.scroll event with some piece of javascript and move the thing manually.&lt;br /&gt;&lt;br /&gt;My dear friend, there is a better way to hack the shit out of it. Presenting you the CSS hack which will keep your element fixed at the bottom of the window&lt;br /&gt;&lt;pre class="brush: css"&gt;div.sweet-div {&lt;br /&gt;  position: fixed;&lt;br /&gt;  bottom: 0;&lt;br /&gt;&lt;br /&gt;  /* IE6 position:fixed hack */&lt;br /&gt;  _position: absolute;&lt;br /&gt;  _bottom: none;&lt;br /&gt;  _top:expression(eval(document.compatMode &amp;&amp; document.compatMode=='CSS1Compat') ? documentElement.scrollTop +(documentElement.clientHeight-this.clientHeight) : document.body.scrollTop+(document.body.clientHeight-this.clientHeight));&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4504422062473167662?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4504422062473167662/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4504422062473167662' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4504422062473167662'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4504422062473167662'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/06/postion-fixed-in-ie6.html' title='Postion fixed in IE6'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2776425647458712902</id><published>2010-05-21T10:22:00.004+03:00</published><updated>2010-05-21T11:59:15.106+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Making a Colorpicker</title><content type='html'>The other day I've been implementing &lt;a href="http://rightjs.org/ui/colorpicker/demo"&gt;this colorpicker widget&lt;/a&gt; for &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; and now I'd like to share some mathematics and mechanics behind the widget.&lt;br /&gt;&lt;br /&gt;I won't bore you to death describing the actual layout, just some necessary theory and math. And I also will use RightJS semantics. Well, just because I love it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Color Theory&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Lets start with a bit of a theory. Every giggling school girl those days knows that any color can be created by a proper mix of three basic colors, red, green and blue. That's a brilliant idea, but the problem is that not many people actually can use it. Personally me, I don't know anyone who can instantly say how much of every color you need to take to create say a color of coffee and milk.&lt;br /&gt;&lt;br /&gt;And because of this problem the humanity created another, more natural way of color picking which is based on another three dimensions: tone, saturation and brightness. And the main idea of the colorpicker widget is to convert one system into another.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Tone Parameter&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The tone picker usually looks like a vertical bar and represents the following system of colors.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://stcamp.net/images/articles/colorpicker/pic1.png" style="border: 1px solid #ccc; background-color: #EEE; padding: 4pt" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The principle is that with this tone scale you can have any tints available with full saturation and brightness. Note also that in every point of the scale, one of the colors is always has a zero value, and at least one of them has the full value. &lt;br /&gt;&lt;br /&gt;Keep also in mind that this scale is some sort of closed circle, the end on the right is actually joined with the beginning on the left.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Saturation And Brightness&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The saturation and brightness field usually looks like that.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://stcamp.net/images/articles/colorpicker/pic2.png" style="border: 1px solid #ccc; background-color: #EEE; padding: 4pt" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The saturation parameter is zero at the left side and have the full value on the right. The brightness is full at the top and zero at the bottom. This way, you will always have the white color at the top left corner, your current tint color at the top-right corner and a black line at the bottom side.&lt;br /&gt;&lt;br /&gt;With those three handlers you also can chose any visible color but in more natural way.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Converting TSB into RGB&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The first task of the colorpicker is to convert the tint, saturation and brightness parameters which the user picks into the actual  RGB value.&lt;br /&gt;&lt;br /&gt;It was proven that the best way of keeping your values in this case is if you keep your TSB parameters in float values of range 0..1 and RGB values in integers of range 0..255. This way you won't have systematic round up errors and it will be working faster.&lt;br /&gt;&lt;br /&gt;So your TSB -&gt; RGB script will look like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;var tint = [1, 0.5, 0];&lt;br /&gt;var saturation = 0.6;&lt;br /&gt;var brightness = 0.4;&lt;br /&gt;&lt;br /&gt;var color = [0, 0, 0];&lt;br /&gt;&lt;br /&gt;for (var i=0; i &lt; 3; i++) {&lt;br /&gt;  color[i] = 1 + saturation * (tint[i] - 1);&lt;br /&gt;  color[i] = (color[i] * brightness * 255).round();&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;The principle is simple, first we combine the tint and saturation which will give as a raw color in range 0..1, then we apply the brightness parameter and convert the float value into a 0..255 integer.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Converting RGB into STB&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The backward conversion is a bit trickier. I've drown a simple picture to help you understand the principle&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://stcamp.net/images/articles/colorpicker/pic3.png" style="border: 1px solid #ccc; background-color: #EEE; padding: 4pt" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Say you have some RGB color with values in range of 0..255. Remember our tint picture, where one value is always 1 and another is always 0. So this smaller area between the green and red values is the same thing, if you take it and normalize to the 0..1 range you'll have your clear tint values. The brightness parameter will be the proportion between the brightest color and the 255 value, and your saturation is relation between the minimal color to the maximum.&lt;br /&gt;&lt;br /&gt;The script looks like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;// the initial RGB value&lt;br /&gt;var color = [200, 100, 50];&lt;br /&gt;&lt;br /&gt;// finding the minimal and maximal values&lt;br /&gt;var color_sorted = color.clone().sort(function(a,b) { return a-b; });&lt;br /&gt;var max = color_sorted[2];&lt;br /&gt;var min = color_sorted[0];&lt;br /&gt;&lt;br /&gt;// calculating the brightness and saturation&lt;br /&gt;var brightness = max / 255;&lt;br /&gt;var saturation = 1 - min / (max || 1);&lt;br /&gt;&lt;br /&gt;// calculating the tint&lt;br /&gt;var tint = [0, 0, 0];&lt;br /&gt;for (var i=0; i &lt; 3; i++) {&lt;br /&gt;  tint[i] = ((!min &amp;&amp; !max) || min == max) ? i == 0 ? 1 : 0 :&lt;br /&gt;    (color[i] - min) / (max - min);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;It is pretty much straight forward, but we also have some failsafe conditions like &lt;tt&gt;(max || 1)&lt;/tt&gt; and &lt;tt&gt;(!min &amp;&amp; !max)&lt;/tt&gt; in case if all the RGB values are zeros.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;HEX to RGB Conversions&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;And couple more things which you also will need. Converting your 0..255 colors array into HEX and RGB formatted strings back and forth.&lt;br /&gt;&lt;br /&gt;Converting an array to a HEX value is simple&lt;br /&gt;&lt;pre&gt;&lt;code&gt;'#'+ color.map(function(c) {&lt;br /&gt;  return (c &lt; 16 ? '0' : '') + c.toString(16);&lt;br /&gt;}).join('');&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Converting RGB string into an array of values looks like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;if (match = /rgb\((\d+),(\d+),(\d+)\)/.exec(value)) {&lt;br /&gt;  return [match[1], match[2], match[3]].map('toInt');&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And converting any HEX color into an array can be done like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;// converting the shortified hex in to the full-length version&lt;br /&gt;if (match = /^#([\da-f])([\da-f])([\da-f])$/.exec(value))&lt;br /&gt;  value = '#'+match[1]+match[1]+match[2]+match[2]+match[3]+match[3];&lt;br /&gt;&lt;br /&gt;if (match = /#([\da-f]{2})([\da-f]{2})([\da-f]{2})/.exec(value)) {&lt;br /&gt;  return [match[1], match[2], match[3]].map('toInt', 16);&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Well, that all you need to know to make one cool colorpicker of yours.&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2776425647458712902?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2776425647458712902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2776425647458712902' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2776425647458712902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2776425647458712902'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/05/making-colorpicker.html' title='Making a Colorpicker'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-935851821708807729</id><published>2010-05-18T08:54:00.007+03:00</published><updated>2010-07-30T13:53:27.506+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Automatic Records Search in Rails</title><content type='html'>There is one neat trick I'd like to share. In many cases, especially when you work on intranet applications, you need say to check some security settings against the data-records &lt;i&gt;before&lt;/i&gt; you let the request to access the actual method in your controller. Normally, most folks do that by adding before filters to find the records, like that&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;before_filter :find_this&lt;br /&gt;before_filter :find_that&lt;br /&gt;&lt;br /&gt;def find_this&lt;br /&gt;def find_that&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;When your application grows and starts having something like a dozen models, plus you might have nested routes where you need to check the security against the nested elements too, those things tend to get really messy.&lt;br /&gt;&lt;br /&gt;So here is a simple method which I use on my projects&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;before_filter :find_restful_units&lt;br /&gt;def find_restful_units&lt;br /&gt;  params.each do |key, value|&lt;br /&gt;    if key == 'id' || key.ends_with?('_id')&lt;br /&gt;      unit_name = key == 'id' ? controller_name.singularize : key.gsub('_id', '')&lt;br /&gt;      unit_class = Object.const_get(unit_name.camelize)&lt;br /&gt;      instance_variable_set("@#{unit_name}", unit_class.find(params[key]))&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;rescue NameError  # just hung up if there is no such unit&lt;br /&gt;  nil&lt;br /&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;You put this in your &lt;tt&gt;ApplicationController&lt;/tt&gt; and it will automatically find and assign all your models, the actual and the nested ones.&lt;br /&gt;&lt;br /&gt;This makes controllers much cleaner and you always have your models extracted and ready to use in the standard way.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-935851821708807729?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/935851821708807729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=935851821708807729' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/935851821708807729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/935851821708807729'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/05/automatic-records-search-in-rails.html' title='Automatic Records Search in Rails'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4973555165694885143</id><published>2010-04-28T17:04:00.004+03:00</published><updated>2010-04-28T17:31:18.055+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS vs. MooTools In The Game of Mines</title><content type='html'>This weekend I had some fun implementing the good old game of mines using &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt;. The result is pretty cute, you should &lt;a href="http://stcamp.net/examples/r_mines/"&gt;check it out&lt;/a&gt;. No, really, you will like it.&lt;br /&gt;&lt;br /&gt;The thing is that about year and a half ago I implemented the same exact game using &lt;a href="http://mootools.net/"&gt;MooTools&lt;/a&gt;, you can check it out &lt;a href="http://stcamp.net/jsminer/demo.html"&gt;over here&lt;/a&gt;, and well, I have some words to say on this regard.&lt;br /&gt;&lt;br /&gt;Okay, I certainly did get better during this last year and you might say that I know RightJS better than MooTools, but here are some numbers. (For the sake of a clean experiment I specifically didn't look into the old code before or during the development and eventually gone in a different approach in the implementation.)&lt;br /&gt;&lt;br /&gt;With MooTools I was working on the game almost the whole week, I didn't work full time and as I recall it you could say it were good three, three and a half days of work. The result is 14k build of JavaScript + about 5k of CSS, which is about 19k in total.&lt;br /&gt;&lt;br /&gt;In case of RightJS it took me about 6 hours to implement the whole thing from top to bottom, and the result built of JavaScript + CSS is just 9K of code.&lt;br /&gt;&lt;br /&gt;Yup, a twice as compact result and about four times less time spent. More of that I believe that RightJS implementation has much cleaner and easy to understand code.&lt;br /&gt;&lt;br /&gt;The really sweet things in case of RightJS were the number extensions, simplified collections processing and built in Observer unit. It feels so good and natural to implement game logic with them! I really enjoyed those things!&lt;br /&gt;&lt;br /&gt;Well, that's about it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4973555165694885143?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4973555165694885143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4973555165694885143' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4973555165694885143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4973555165694885143'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/04/rightjs-vs-mootools-in-game-of-mines.html' title='RightJS vs. MooTools In The Game of Mines'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5562262808761347968</id><published>2010-03-29T16:38:00.015+03:00</published><updated>2010-08-11T16:51:35.585+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>RightJS + Rails = Love</title><content type='html'>As the &lt;a href="http://github.com/MadRabbit/right-rails"&gt;right-rails&lt;/a&gt; (the Ruby On Rails plugin for &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt;) was updated recently, lets have another brainwashing session. This time for Rails people.&lt;br /&gt;&lt;br /&gt;If you're a kind of a pragmatic developer, you're probably asking yourself "why would I fall in love with this new thing? I already know Prototype and jQuery why would I need something else?", and believe me, I know precisely what you mean.&lt;br /&gt;&lt;br /&gt;But ask yourself, being a Rails developer, were you ever happy of doing AJAX features in Rails? No, I mean, not like first two weeks on rails, or things like "it's working". I mean really happy, did you feel funny about Prototype or jQuery? Did they make the butterflies in your stomach fly? Literally.&lt;br /&gt;&lt;br /&gt;Think about it. The Prototype support on Rails was always a kind of a flimsy hack and most serious developers don't really use those RJS stuff. And with jQuery you were always left alone to deal with all those kinky JavaScript stuff. That's good if you are a JavaScript guru and know your magic. But what if you are not, and what about the actual Rails business anyway? Did any of the frameworks really helped you in any way to be an actual Ruby/Rails developer?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How Is RightJS Different?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First and most important thing you should know about RightJS is that it was written by a Ruby/Rails developer for Ruby/Rails developers. And despite the fact that from time to time we have all those arrogant promotions and we mock jQuery folks constantly. RightJS is not really about JavaScript itself. It is something quite opposite.&lt;br /&gt;&lt;br /&gt;RightJS is all about getting JavaScript off your hair. RightJS is made to help Ruby developers to deal with JavaScript tasks, it reflects Ruby way of handling things, has mostly Ruby syntax and many of its key features are copies of Rails features.&lt;br /&gt;&lt;br /&gt;RightJS civilizes JavaScript and brings it closer to Ruby developers.&lt;br /&gt;&lt;br /&gt;But we didn't came here to talk about JavaScript, right? What about Rails itself?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lets Make The Butter Fly&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;How much time does it take to make a date-time field with a calendar in your framework?&lt;br /&gt;&lt;br /&gt;Let me spare you the calculations time and show how it looks like in RightJS + RightRails.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&amp;lt;% form_for(@zing) do |f| %&gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;%= f.calendar_field :dead_line %&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;% end %&gt;&lt;/pre&gt;&lt;br /&gt;Nope. I didn't forget nothing. &lt;tt&gt;`calendar_field`&lt;/tt&gt; is all what it takes. RightRails automatically includes all necessary JavaScript, CSS and I18n modules on your page and initializes the fields, so you don't need to worry about a thing.&lt;br /&gt;&lt;br /&gt;More of that, it will automatically swap between builds and source code for JavaScript in production and development modes.&lt;br /&gt;&lt;br /&gt;So, would you like some rater or slider on your form?&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&amp;lt;%= f.rater_field :rating %&gt;&lt;br /&gt;&amp;lt;%= f.slider_field :completeness %&gt;&lt;/pre&gt;&lt;br /&gt;Oh, I know! You'd like some autocompleter, right?&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&amp;lt;%= f.autocomplete_field :category, :url =&gt; categories_path %&gt;&lt;/pre&gt;&lt;br /&gt;Would you like the autocompleter to be RESTful design and hard-caching friendly? No problem!&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&amp;lt;%= f.autocomplete_field :category, :url =&gt; "/categories/%{search}.js" %&gt;&lt;/pre&gt;&lt;br /&gt;Here you go.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;And That's Not All&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The fun is not over, not by a long shot. The next feature is that RightRails comes with a brand new RJS generator, and that's not just a collection of dummy functions like in the case of the Rails original RJS templates.&lt;br /&gt;&lt;br /&gt;The new generator lets you literally write JavaScript in Ruby and more of that it allows you to mix Ruby and JavaScript calls in one flow escaping and transforming variables on fly.&lt;br /&gt;&lt;br /&gt;For example this is how the annoying nested forms look like when you make them with RightRails.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&amp;lt;% form_for(@zing) do |f| &gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;div id="categories"&gt;&lt;br /&gt;    &amp;lt;%= render @zing.categories %&gt;&lt;br /&gt;  &amp;lt;/div&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= link_to_function 'add' {|page|&lt;br /&gt;     page[:categories].insert(render(Category.new))&lt;br /&gt;  } %&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;% end %&gt;&lt;/pre&gt;&lt;br /&gt;See, the `insert` method is a JavaScript function, the `render` is the ActiveView method, `Category` is your model. All in one flow with automatic types conversion. And it is not just a list of predefined functions, you can literally do whatever you want.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;update_page do |page|&lt;br /&gt;  page[:todos].update render(@todos)&lt;br /&gt;&lt;br /&gt;  page[:todos_count].innerHTML = @todos.size + ' Items'&lt;br /&gt;&lt;br /&gt;  page[:todos].select('li').each do |item|&lt;br /&gt;    item.onClick('toggleClass', 'marked')&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  page.json_variable = @todos.to_json&lt;br /&gt;  page.items_count   = @todos.size&lt;br /&gt;  page.list_updated  = true&lt;br /&gt;&lt;br /&gt;  page.alert "There are #{@todos.size} items left on the list"&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;There are no limiatations.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mommy Look, More Fancy Stuff!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Oh yes mommy, there are! On top of all RightRails has a conventional interface to cover the most common AJAX CRUD operations. Let me show you.&lt;br /&gt;&lt;br /&gt;Say here's your remote form&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&amp;lt;% remote_form_for(@zing) do |f| %&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&gt;&lt;br /&gt;  &amp;lt;%= f.file_field :picture %&gt;&lt;br /&gt;   &lt;br /&gt;  &amp;lt;%= f.submit 'Create' %&gt;&lt;br /&gt;  &amp;lt;%= image_tag 'spinner.gif', :class =&gt; :spinner %&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;% end %&gt;&lt;/pre&gt;&lt;br /&gt;And here is your controller method&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;dev create&lt;br /&gt;  @zing = Zing.new(params[:zing])&lt;br /&gt;  if @zing.save&lt;br /&gt;    render rjs.insert(@zing)&lt;br /&gt;  else&lt;br /&gt;    render rjs.replace_form_for(@zing)&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;The `rjs` method provides access to the new RJS generator from the controllers, and `insert` and `replace_form_for` are the AJAX operations interface.&lt;br /&gt;&lt;br /&gt;It all works over the Rails conventions, the `insert` method looks for the class of the `@zing` model, finds related partial, renders it and generates a piece of JavaScript that inserts the HTML into an element with the `zings` ID.&lt;br /&gt;&lt;br /&gt;The `replace_form_for` method will render a new form with all the error messages for the @zing model and generate JavaScript that will update the form on the page.&lt;br /&gt;&lt;br /&gt;RightRails has a collection of such methods for all CRUD operations.&lt;br /&gt;&lt;br /&gt;Note also that I've put a file field and a spinner image on the form. RightRails automatically and transparently processes files uploading too, basically there is no difference whether your form has files or not, it all has the same API. And the RightJS when submits a form via AJAX, automatically searches for images with the `spinner` class and uses them as spinners, you don't need to hook them up with callbacks manually.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Summary&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I'm not going to indulge you with obvious conclusions,  I'll just tell you that I've already used RightJS + RightRails on two production projects and it saved me hell of a lot of time and nerves on JavaScript and AJAX development.&lt;br /&gt;&lt;br /&gt;Well, it was designed to.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5562262808761347968?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5562262808761347968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5562262808761347968' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5562262808761347968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5562262808761347968'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/03/because-this-is-all-about-love.html' title='RightJS + Rails = Love'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1202665583515133467</id><published>2010-03-27T15:58:00.004+02:00</published><updated>2010-07-30T13:56:03.434+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Supervisor Mode With Authlogic</title><content type='html'>Supervisor mode is a feature that allows site admins to quickly switch to any other user (say customers) and take a look at the site the same way the customers see it, may be perform some actions from this user, etc.&lt;br /&gt;&lt;br /&gt;So here is a simple tip how you do that using the &lt;a href="http://github.com/binarylogic/authlogic"&gt;Authlogic&lt;/a&gt; plugin for Rails.&lt;br /&gt;&lt;br /&gt;First of all you'll need couple new routes, one to switch to some user, another to get back to the admin mode.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;map.super_login '/login/:id', :controller =&gt; 'user_sessions', :action =&gt; 'super'&lt;br /&gt;map.back_login '/login/back', :controller =&gt; 'user_sessions', :action =&gt; 'back'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I used two new methods named 'super' and 'back', which are not exactly RESTful, but you can use 'edit' and 'update' if feel particularly evil about it. They are normally not used in the 'user_sessions' controller.&lt;br /&gt;&lt;br /&gt;After that on the list of customers you add links to switch under this user, for example like that&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;....&lt;br /&gt;   %td= link_to 'Supervision', super_login(user)&lt;br /&gt;....&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And finally you'll need those two methods in your `UserSessions` controller&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;class UserSessions&lt;br /&gt;  .....&lt;br /&gt;  # switches under this user&lt;br /&gt;  def super&lt;br /&gt;    raise AccessDenied if !admin?&lt;br /&gt;    session[:original_admin_id] = @current_user_session.user.id&lt;br /&gt;    @current_user_session = UserSession.create(User.find(params[:id]))&lt;br /&gt;    redirect_to '/'&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # switches back to the admin -mode&lt;br /&gt;  def back&lt;br /&gt;    raise AccessDenied unless @current_user_session &amp;&amp; session[:original_admin_id]&lt;br /&gt;    @current_user_session = UserSession.create(User.find(session[:original_admin_id])&lt;br /&gt;    session[:original_admin_id] = nil&lt;br /&gt;    redirect_to '/'&lt;br /&gt;  end&lt;br /&gt;  .....&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The idea is simple, in the first method you stash the original user_id in a session variable, then create new user session with that specific user. And in the second one we restore the original user in the session.&lt;br /&gt;&lt;br /&gt;You also might consider to add two methods like `current_user` and `current_user=` in your application controller they are pretty useful and will make your code cleaner.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;class ApplicationController&lt;br /&gt;  ...&lt;br /&gt;  # returns the currently logged in user&lt;br /&gt;  def current_user&lt;br /&gt;    @current_user_session.user if @current_user_session&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # assigns the currently logged in user&lt;br /&gt;  def current_user=(user)&lt;br /&gt;    @current_user_session = UserSession.create(user)&lt;br /&gt;  end&lt;br /&gt;  ...&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all. Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1202665583515133467?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1202665583515133467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1202665583515133467' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1202665583515133467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1202665583515133467'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/03/supervisor-mode-with-authlogic.html' title='Supervisor Mode With Authlogic'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-798074271916160710</id><published>2010-03-05T09:53:00.003+02:00</published><updated>2010-03-05T10:06:02.050+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Mechanize and MultiSelect fields</title><content type='html'>&lt;a href="http://github.com/tenderlove/mechanize"&gt;Mechanize&lt;/a&gt; is a little handy tool that allows you to perform all sorts of HTTP requests in Ruby. It automatically emulates all sorts of browsers, handles cookies, headers and stuff, which is really useful when you need to harvest some data from sites that don't have any computer friendly feeds.&lt;br /&gt;&lt;br /&gt;I'm using it on my current project and run into a small problem. It doesn't work nicely with array data, like for example multiselect fields emulation, so when you do something like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;agent = Mechanize.new&lt;br /&gt;&lt;br /&gt;agent.post('http://boo.boo/boo', {&lt;br /&gt;  'param[]' =&gt; ['one', 'two', 'three']&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;In the reality it sends the &lt;tt&gt;'one'&lt;/tt&gt; value only. I've sent a little patch to the devs, but it seems like it will take time before they do something about it. So here is how you fix it in your rails app.&lt;br /&gt;&lt;br /&gt;In any initializers in your &lt;tt&gt;`config/initializers/`&lt;/tt&gt; directory add the following lines.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class Mechanize::Form::Field&lt;br /&gt;  def query_value&lt;br /&gt;    if @value.is_a?(Array)&lt;br /&gt;      @value.collect{ |v| [@name, v || '']}&lt;br /&gt;    else&lt;br /&gt;      [[@name, @value || '']]&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After that it will be just fine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-798074271916160710?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/798074271916160710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=798074271916160710' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/798074271916160710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/798074271916160710'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/03/mechanize-and-multiselect-fields.html' title='Mechanize and MultiSelect fields'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4767935935809011701</id><published>2010-03-02T11:55:00.006+02:00</published><updated>2010-03-02T20:02:40.623+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>My Top 5 on that RightJS vs. jQuery Question</title><content type='html'>During the last several months of RightJS promotion, the absolute hit among the questions of 'what?' and 'why?' was understandable the question of what is the difference between RightJS and jQuery.&lt;br /&gt;&lt;br /&gt;There were lots of talks about &lt;a href="http://rightjs.org/benchmarks"&gt;RightJS speed&lt;/a&gt; and then &lt;a href="http://st-on-it.blogspot.com/2009/12/meet-shakker-mano-mano-javascript.html"&gt;more about speed&lt;/a&gt;, and then we had some abstract talks about &lt;a href="http://rightjs.org/philosophy"&gt;its philosophy&lt;/a&gt; and &lt;a href="http://st-on-it.blogspot.com/2010/01/terminal-fun-or-why-rightjs.html"&gt;its way among the others&lt;/a&gt; and we also have that &lt;a http="http://rightjs.org/comparison"&gt;simple comparison page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And what I find important in a brainwashing process is systematicness and methodicalness. We did some talks on an abstract matter, we did some talks on the opposite side of scale with performance tests, now lets touch something real. Something in the middle. And probably summarize some previous talks.&lt;br /&gt;&lt;br /&gt;So here it is, your next session; my top 5 as I see it at this morning.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Syntax&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When I'm saying "jQuery is like PHP", first of all I mean the nature of its syntax. When you boil it down to the essence, both of them are just collections of helper functions. And as PHP defines a new kinda language upon C, almost the same way jQuery defines a new language upon JavaScript.&lt;br /&gt;&lt;br /&gt;RightJS on the other hand is built right into JavaScript itself. In 90% it is the good old JavaScript, where we bring features from later JavaScript specifications and spice it with some standard features from more serious dynamic languages like Ruby and Python.&lt;br /&gt;&lt;br /&gt;As the result, when you learn jQuery, you mostly learn jQuery, I've seen many nice fellas around, who know jQuery quite well, but stuck when they needed to do simplest things in pure JavaScript.&lt;br /&gt;&lt;br /&gt;When you learn RightJS, you learn the actual JavaScript, yes we have some funny shortcuts and features that make mature web-developers happily giggling like school girls, but all of them are built upon JavaScript standards.&lt;br /&gt;&lt;br /&gt;RightJS makes you do the things in JavaScript and do them right. And because of that, whatever the future brings, your knowledge and skills will remain with you.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. The Actual STL&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When I read someone saying "jQuery is the JavaScript STL", it usually makes me laughing so much, that my neighbors probably think that I've finally went nuts and going to blow off the Kremlin.&lt;br /&gt;&lt;br /&gt;Now, don't get me wrong, I have no problems with jQuery's popularity and I respect that. Well, at least, as long as it keeps me away from people who doesn't know the difference between popularity and standards.&lt;br /&gt;&lt;br /&gt;jQuery is a &lt;i&gt;DOM&lt;/i&gt; DSL at best. First of all it doesn't have anything to do with JavaScript itself, because it's a &lt;i&gt;DOM&lt;/i&gt; wrapper, secondly there is nothing standard about jQuery in terms of JavaScript. Take for example a look at the &lt;tt&gt;.each()&lt;/tt&gt; function where the arguments are in just the opposite order than they are in the JavaScript own &lt;tt&gt;Array#forEach&lt;/tt&gt; method.&lt;br /&gt;&lt;br /&gt;If you need something that actually follows the standards, you should take a look at RightJS. It brings many standard features from later JavaScript specifications, plus many other none the less standard features from server-side programming languages, for example methods like &lt;tt&gt;String#startsWith&lt;/tt&gt; or &lt;tt&gt;Array#random&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;The DOM interfaces are just one part of RightJS and it has much more. We even have a build of RightJS without DOM features which you can use on the server-side with Node.js or say writing scripts in Qt.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Multiple Paradigms&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As jQuery is simply a wrapper over DOM interface, it doesn't give you any alternatives but use its own way of dealing with the things. Yes, you might imagine that you're practicing Lisp in JavaScript, but the reality is that 99% of jQuery code (including jQuery itself) is just a spaghettish pieces of crap.&lt;br /&gt;&lt;br /&gt;RightJS on the other hand is not just a DOM wrapper, it is a multi-paradigm framework. We have an advanced OOP stack that allows you to write clean and properly designed applications that are easy to support and refactor. We also have most of the standard FP features, like binding, curring, chains and stuff. We also have some standard native units extensions that are most helpful in procedural programming.&lt;br /&gt;&lt;br /&gt;RightJS gives you options and flexibility, so that you could deal with your work in the most effective way.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Open Sandbox&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Maybe jQuery forces you to write a spaghetti code, but there is a bigger problem. Most of jQuery and its plugins code are closed behind anonymous scopes that makes it impossible to change them part by part.&lt;br /&gt;&lt;br /&gt;You know, people are actually already invented OOP, inheritance, functionality injection and other fancy stuff to deal with those sort of things. And that's why in RightJS we have radically opposite architecture.&lt;br /&gt;&lt;br /&gt;RightJS is built using proper OOP structures and modules and it represents an open sandbox where virtually everything is accessible and customizable. You can easily bend and tune RightJS for your needs, changing and replacing its parts, adding your own features, methods and so one.&lt;br /&gt;&lt;br /&gt;And because of that it is super easy to write any sort of plugins and extensions for RightJS. Take for example a look at the right-rails plugin; couple of dozens of lines of code and RightJS API was completely transformed to have the naming system of Ruby, with underscored syntax and other features.&lt;br /&gt;&lt;br /&gt;RightJS gives you the same flexibility and hacker's joy as Ruby or Python.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. Official Plugins&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I'm sure you know what they say "jQuery has a gazillion of plugins that will do everything for you". It kinda reminds me other folks who was saying "Everything in Java has already been done".&lt;br /&gt;&lt;br /&gt;Normal people usually add on that "badly".&lt;br /&gt;&lt;br /&gt;That gazillion of plugins has actually a pretty nasty back side. The problem is in the closed architecture and inflexibility. For every case there are usually half a dozen of implementations, and quite often the choice is like "bad, bad or bad?". At the end you might spend more time looking for a proper plugin than you could spend writing it yourself, and at the finish your app will look like a Frankenstein, all stitched of different parts. &lt;br /&gt;&lt;br /&gt;Yes I know, I know, Frankenstein is a romantic figure in gothic culture, but there is so few romantic in supporting such an app.&lt;br /&gt;&lt;br /&gt;With RightJS we don't have the luxury of too many plugins, but we have several good points on that matter too. First of all, many common and frequently used features that you have as plugins for jQuery, are already baked into the RightJS core, like for example, additional form features, Ajax via iframes functionality, cookies and stuff.&lt;br /&gt;&lt;br /&gt;Then we have all the standard plugins, like D'n'D, JSON, Behaviors, plus most of the standard UI elements like Calendar, Autocompleter, Tabs, Lightbox, etc. already implemented and served in the official plugins repository.&lt;br /&gt;&lt;br /&gt;And what's different about RightJS plugins is that all of them are implemented in the same way, using the same set of rules. All events handling, options, usage principles all are the same. So that if you had experience with one plugin, you will be familiar with the rest of them too.&lt;br /&gt;&lt;br /&gt;Then all the plugins follow the open-sandbox principles of the RightJS Core. All of them are implemented using proper OOP approach and can be easily extended using inheritance, functionality injection or even monkey patching if you will.&lt;br /&gt;&lt;br /&gt;This way you don't need a dozen of variations of the same plugin, you can easily tweak any standard one to do exactly what you need.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4767935935809011701?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4767935935809011701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4767935935809011701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4767935935809011701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4767935935809011701'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/03/my-top-5-on-that-rightjs-vs-jquery.html' title='My Top 5 on that RightJS vs. jQuery Question'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3633090359900584074</id><published>2010-02-26T20:09:00.009+02:00</published><updated>2010-02-27T12:37:30.538+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Native Element#classList in FF 3.6 Against RightJS</title><content type='html'>As many of you probably aware, FF 3.6 has the &lt;a href="http://hacks.mozilla.org/2010/01/classlist-in-firefox-3-6/"&gt;HTML5 Element#classList feature&lt;/a&gt; implemented natively. And then @&lt;a href="http://twitter.com/DouweM"&gt;DouweM&lt;/a&gt; asked if we could add it to &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Well, I tried, but the thing is that those guys at Mozilla could do it better. They have the test, the picture and yet another 2x times performance improvement, awright. &lt;br /&gt;&lt;br /&gt;But lets check it out by ourselves, shall we?&lt;br /&gt;&lt;br /&gt;I've created &lt;a href="http://stcamp.net/examples/class_list_test.html"&gt;this little script&lt;/a&gt; which has two implementations, one uses FF 3.6 native feature, another one uses the same pure JavaScript algorithm we use at &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; to handle css-classes. And here's a screenshot of my test-run.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://stcamp.net/images/rightjs/class_name_test.png" /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Seems like &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; implementation murks a native feature again. It's more than three times faster on classes adding :)&lt;br /&gt;&lt;br /&gt;So how did those guys at Mozilla get their results?&lt;br /&gt;&lt;br /&gt;They simply used lousy RegExp based implementation of the feature in their test. Surely it was slower.&lt;br /&gt;&lt;br /&gt;This is pretty much it. Hope they will fix it in the next releases of FF.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3633090359900584074?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3633090359900584074/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3633090359900584074' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3633090359900584074'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3633090359900584074'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/02/native-elementclassname-in-ff-36.html' title='Native Element#classList in FF 3.6 Against RightJS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2887966753439353771</id><published>2010-02-20T23:36:00.005+02:00</published><updated>2010-02-21T13:32:36.768+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Recent jQuery Performance Updates</title><content type='html'>Now folks, you know, I was always saying "there is nothing like a smell of good statistics in the morning!". Now lets take a look at that 2x speed up claimed &lt;a href="http://blog.jquery.com/2010/02/19/jquery-142-released/"&gt;over here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm using my &lt;a href="http://github.com/MadRabbit/shakker"&gt;Shakker&lt;/a&gt; util to perform the tests. You can also read &lt;a href="http://st-on-it.blogspot.com/2009/12/meet-shakker-mano-mano-javascript.html"&gt;why I don't use Taskspeed&lt;/a&gt; anymore.&lt;br /&gt;&lt;br /&gt;So to the tests. Lets take a look at the Firefox test&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/jq-142/ff.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/jq-142/ff-thmb.png" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Yea, kind a bit of speed up at claimed `bind`, `html` and `remove` methods, certainly not 2x, and note how it actually got slower at 1.4.1 and then gets back to almost the same values as 1.4.0.&lt;br /&gt;&lt;br /&gt;Now lets take a look at the same thing at Safari&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/jq-142/safari.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/jq-142/safari-thmb.png" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;A different scale, the same picture, it's getting slower from 1.4.0 to 1.4.1 and then gets back to almost the same values at 1.4.2. No 2x speed up between 1.4.2 and 1.4.1, more of that almost no speed up at all and certainly no 3x speed up agains 1.3.2.&lt;br /&gt;&lt;br /&gt;Well, lets get further and take a look at the test in another fine browser, Chrome.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/jq-142/chrome.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/jq-142/chrome-thmb.png" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The same exact picture with the `bind`, `html` and `remove` methods, got slower on 1.4.1 and to the same value at 1.4.2.&lt;br /&gt;&lt;br /&gt;Now you should get back to that &lt;a href="http://blog.jquery.com/2010/02/19/jquery-142-released/"&gt;release notes page&lt;/a&gt; and ask yourself "why there are no results for version 1.4.0?".&lt;br /&gt;&lt;br /&gt;Yup, because there is no speed up, they merely fixed their own fuckups in 1.4.1 and now present it as 2x speed boost. &lt;br /&gt;Awesome! :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2887966753439353771?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2887966753439353771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2887966753439353771' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2887966753439353771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2887966753439353771'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/02/recent-jquery-performance-updates.html' title='Recent jQuery Performance Updates'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8180110760342993380</id><published>2010-02-20T22:38:00.003+02:00</published><updated>2010-02-21T12:08:33.526+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Slowing Things Down Under OS X</title><content type='html'>You know what they say "you need to slow down to see some certain things". In case of web-development, sometimes you need to emulate a realistically slow internet connection to see some problems with your project. See how quickly it loads up, how it receives the images, especially it useful to test file-uploads and ajax operations. So here is a little tip how can slow down requests to your local server to test the things properly.&lt;br /&gt;&lt;br /&gt;Under OS X you have access to the FreeBSD traffic shaping util &lt;tt&gt;ipfw&lt;/tt&gt;. First of all you need to define some rule which will work on local interactions.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo ipfw add 1 pipe 1 src-ip 127.0.0.1&lt;br /&gt;sudo ipfw add 1 pipe 2 dst-ip 127.0.0.1&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After that we can set up the connection speed, and also I like to add some delay to emulate a request to a remote server&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo ipfw pipe 1 config bw 40Byte/s delay 100ms&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After that all your local requests will slow down to 40Byte/s speed with a realistic 100ms response delay.&lt;br /&gt;&lt;br /&gt;When you have done with your tests, you can remove the rule with the following command.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo ipfw delete 1&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now it is all interesting but lets wrap it up in a simple shell script so we didn't need to memorize all the fancy calls.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo touch /usr/bin/slowdown&lt;br /&gt;sudo chmod +x /usr/bin/slowdown&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After that open the file with your favorite editor and write the following little script&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;if [ $1 = "clear" ] ; then&lt;br /&gt;&lt;br /&gt;  ipfw delete 1&lt;br /&gt;  &lt;br /&gt;else&lt;br /&gt;  &lt;br /&gt;  ipfw add 1 pipe 1 src-ip 127.0.0.1&lt;br /&gt;  ipfw add 1 pipe 2 dst-ip 127.0.0.1&lt;br /&gt;  ipfw pipe 1 config bw $1KByte/s delay 100ms&lt;br /&gt;  &lt;br /&gt;fi&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now you can create and remove pipes just like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo slowdown 40&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And to remove the rule call it like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;sudo slowdown clear&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;That's all about it. Happy testing!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8180110760342993380?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8180110760342993380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8180110760342993380' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8180110760342993380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8180110760342993380'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/02/slowing-things-down-under-os-x.html' title='Slowing Things Down Under OS X'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8807575616405549845</id><published>2010-01-31T12:26:00.005+02:00</published><updated>2010-01-31T13:26:12.234+02:00</updated><title type='text'>Algorithm Complexity Analysis On An Interview</title><content type='html'>I was digging around trying to find what kind of questions Google interviewers ask potential candidates and found some guy story who was asked the following question:&lt;br /&gt;&lt;br /&gt;&lt;cite&gt;"What is the complexity of searching for existence of all characters from one string into another?"&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;That guy answered "n*n", and after being rejected was wondering if it was "n*lg(n)". Poor little thing...&lt;br /&gt;&lt;br /&gt;IMHO: Rule #1 when you're having an interview (I guess, especially at Google): "never trust the interviewer", it is their job and trade to mislead you and check how you actually think, rather if you know the correct answer.&lt;br /&gt;&lt;br /&gt;So, here is how I would answer to that question. (yeah, I'm showing off, but please consider that as me simply exercising)&lt;br /&gt;&lt;br /&gt;Okay. First of all the complexity vastly depends on an implementation. I can see at least three of them.&lt;br /&gt;&lt;br /&gt;1. A blunt force solution, you just go through every character from the pattern string (&lt;i&gt;p&lt;/i&gt;) and check if each of them exist in the text string (&lt;i&gt;t&lt;/i&gt;). Strings are essentially arrays and search of any character in them have &lt;u&gt;linear&lt;/u&gt; complexity. So that if we have &lt;i&gt;n = |p|&lt;/i&gt; and &lt;i&gt;m = |t|&lt;/i&gt;, then the overall complexity of the algorithm will be&lt;br /&gt;&lt;pre&gt;&lt;code&gt;O(n * m)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;2. There might be another solution. If we pre sort the &lt;i&gt;t&lt;/i&gt; string with say quicksort algorithm, then we can use the binary search, which has &lt;u&gt;logarithmic&lt;/u&gt; complexity and faster than a blunt cell-by-cell search. In this case, considering that the quick-sort complexity is super-linear, the overall complexity will look about like that:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;O(m * lg(m) + n * lg(m))&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;3. One more version. Considering that both strings consist of the same limited number of symbols, we can create two hash-tables, one will contain unique characters from the &lt;i&gt;p&lt;/i&gt; string and another one will have characters from string &lt;i&gt;t&lt;/i&gt;. As the alphabet is a limited list, finding of the intersection of those two tables will have a &lt;u&gt;constant&lt;/u&gt; complexity. Plus we should add the complexity of building the hash-tables, which will be equal to the lengths of the strings. And the overall complexity will be&lt;br /&gt;&lt;pre&gt;&lt;code&gt;O(n + m + C) = O(n + m)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Roughly speaking, we can take a look at the case when &lt;i&gt;n = m&lt;/i&gt;, then we can say the following about those three algorithms:&lt;br /&gt;&lt;br /&gt;1. &lt;i&gt;O(n*n)&lt;/i&gt; - quadratic complexity&lt;br /&gt;2. &lt;i&gt;O(n*lg(n))&lt;/i&gt; - super-linear complexity&lt;br /&gt;3. &lt;i&gt;O(n)&lt;/i&gt; - linear complexity&lt;br /&gt;&lt;br /&gt;I suppose the last one is the winnar.&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;This is about it.&lt;br /&gt;&lt;br /&gt;I still do have that feeling that I'm gonna get my ass kicked in the upcoming interview, but at least I won't give up that easily and will throw couple of punches back.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8807575616405549845?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8807575616405549845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8807575616405549845' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8807575616405549845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8807575616405549845'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/01/algorithm-complexity-analysis-on.html' title='Algorithm Complexity Analysis On An Interview'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-9035098694133448895</id><published>2010-01-25T17:02:00.007+02:00</published><updated>2010-03-01T23:23:28.651+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Git'/><title type='text'>How to Move Folders Between Git Repositories</title><content type='html'>Today I needed to move some folders from one git repository to another preserving the history. Evidently it's a tricky business, so here is how do that.&lt;br /&gt;&lt;br /&gt;First of all you need to have a clean clone of the &lt;i&gt;source&lt;/i&gt; repository so we didn't screw the things up.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;git clone git://server.com/my-repo1.git&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After that you need to do some preparations on the source repository, nuking all the entries except the folder you need to move. Use the following command&lt;br /&gt;&lt;pre&gt;&lt;code&gt;git filter-branch --subdirectory-filter your_dir -- -- all&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This will nuke all the other entries and their history, creating a clean git repository that contains only data and history from the directory you need. If you need to move several folders, you have to collect them in a single directory using the &lt;code&gt;git mv&lt;/code&gt; command.&lt;br /&gt;&lt;br /&gt;You also might need to move all your content into some directory so it didn't conflict with the new repository when you merge it. Use commands like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;mkdir new_directory/&lt;br /&gt;&lt;br /&gt;git mv my_stuff new_directory/&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Once you've done commit your changes, but &lt;u&gt;don't push!&lt;/u&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;git commit -m "Collected the data I need to move"&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is all about the source repository preparations.&lt;br /&gt;&lt;br /&gt;Now go to your &lt;i&gt;destination&lt;/i&gt; repository&lt;br /&gt;&lt;pre&gt;&lt;code&gt;cd ../my-repo2/&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And here is the trick. You need to connect your &lt;i&gt;source&lt;/i&gt; repository as a remote using a local reference.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;git remote add repo1 ../my-repo1/&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After that simply fetch the remote source, create a branch and merge it with the destination repository in usual way&lt;br /&gt;&lt;pre&gt;&lt;code&gt;git fetch repo1&lt;br /&gt;git branch repo1 remotes/repo1/master&lt;br /&gt;&lt;br /&gt;git merge repo1 &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is pretty much it, all your code and history were moved from one repository to another. All you need is to clean up a bit and push the changes to the server&lt;br /&gt;&lt;pre&gt;&lt;code&gt;git remote rm repo1&lt;br /&gt;git branch -d repo1&lt;br /&gt;&lt;br /&gt;git push origin master&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;That's all. After that you can nuke the temporary source repository.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-9035098694133448895?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/9035098694133448895/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=9035098694133448895' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/9035098694133448895'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/9035098694133448895'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/01/how-to-move-folders-between-git.html' title='How to Move Folders Between Git Repositories'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4252145709816109994</id><published>2010-01-25T15:03:00.004+02:00</published><updated>2010-01-25T15:25:32.158+02:00</updated><title type='text'>I'm looking for a job</title><content type='html'>I'm a seasoned web-developer with almost 9 years of &lt;i&gt;professional&lt;/i&gt; experience, looking for a position of Ruby developer/software designer.&lt;br /&gt;&lt;br /&gt;I'm an early Rails adopter, started to work with it at the very first versions more than three years ago. Since then worked with Rails on a wide range of applications, blogging, social networks, e-commerce, real estate. I also have experience working with Merb and Sinatra, have several rails plugins and a non-rails application of my own. Before Rails I worked with Python, Groovy, did some Java and quite a lot of PHP (object oriented, no crap)&lt;br /&gt;&lt;br /&gt;My specialty is multi-lingual, hi-load and ajax applications. I also pretty good in models, databases and workflow design. And certainly I can do some serious JavaScript work.&lt;br /&gt;&lt;br /&gt;I will prefer a permanent position and ready to relocate (have experience working overseas), but also can work as an independent  contractor/consultant, including telecommuting (have freelance experience).&lt;br /&gt;&lt;br /&gt;If you're interested, please check out my &lt;a href="http://stcamp.net/Nikolay_Nemshilov.pdf"&gt;CV&lt;/a&gt;, it has my contacts in it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4252145709816109994?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4252145709816109994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4252145709816109994'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/01/im-looking-for-job.html' title='I&apos;m looking for a job'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7538310276893360262</id><published>2010-01-24T15:46:00.005+02:00</published><updated>2010-01-24T16:49:42.904+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>Terminal Fun, or Why RightJS?</title><content type='html'>In this article I'd like to share some backstories and ideas that lead me to the development of &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt;. There are some generic texts on the official site, but in this article I'm going to get into some architectural details, thoughts and philosophy that were used as the basis in the development.&lt;br /&gt;&lt;br /&gt;It might start to sound a bit philosophical and like RightJS saves the world, so please, be patient, I'm sure you can find some rational ideas behind.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Disclaimer&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Despite the fact that I did quite a lot of JavaScript work lately, I'm not really any sort of JavaScript guru. I mostly work as a software developer/designer on the server side and my weapon of choice is Rails.&lt;br /&gt;&lt;br /&gt;What I do with RightJS is a mere attempt to build a tool that will fit Ruby/Python developer habits and will be fun to work with.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Philosophy&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;That might be an arguable point of view, but to me the world is always trying to find a balance. The third low of mechanics says: "every action causes an equal and opposite reaction", and for this reason opposite forces always find a moment of equilibrium, moment where things are balanced.&lt;br /&gt;&lt;br /&gt;For example when a body falls though air, the forces of gravity and air friction eventually gets equal and the body stops gaining speed; reaching its terminal velocity. A simple fact, but those two opposite forces combined make things like flight possible.&lt;br /&gt;&lt;br /&gt;You might extrapolate and abstract this principle a bit further and take another two opposite things: "fun" and "safety". Abstractly speaking, I guess it is obvious that the more there is safety the less fun it is, and the more fun it is the more it gets dangerous.&lt;br /&gt;&lt;br /&gt;Those two follow the same exact principles. And as much it might be honorable and cool to be at one side, with rebels or with the enterprise; the truth and all the magic things always lay somewhere in the middle. And that's what we are trying to do in here.&lt;br /&gt;&lt;br /&gt;With RightJS I'm trying to reach that moment of terminal fun.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Back to The Reality&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When it comes to JavaScript and safety I'm sure most of developers will think jQuery. On the other side of the scale probably will be Prototype. And I'm sure everyone who ever worked with JavaScript recently, heard lots of talks about the jQuery safe-mode, and how non conflicting it is, and how it lets you easily write plugins and so one.&lt;br /&gt;&lt;br /&gt;But the truth is, that wast majority of the people who does those talks, never did write any plugins, never got in really non-solvable conflicting situations, and as the matter of fact they don't know anything but jQuery. They just heard that prototype extensions are dirty, and probably think that hating them makes them look tougher :).&lt;br /&gt;&lt;br /&gt;Meanwhile, people were using Prototype for years, there are hundreds of plugins. And yes, big corporations did and still do use it. The reason why people started to switch from Prototype is because it got too fat and slow, not because it is unsafe.&lt;br /&gt;&lt;br /&gt;It kinda reminds me the situation with airports. Someone, somewhere did created that shoe-bomb, and now we all have to stand in queues at security checks, take off our shoes and have a walk on a dirty floor.&lt;br /&gt;&lt;br /&gt;Is it safer this way? Indeed it is. But if you are not a bad guy, would you like to skip that procedure?&lt;br /&gt;&lt;br /&gt;I bet you do.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Between Safety and Fun&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;So, what about RightJS and that terminal fun thing?&lt;br /&gt;&lt;br /&gt;Yes, RightJS does extend prototypes and we do have several global scope units and functions. That's true. But we don't do that blindly creating a crazy mess.&lt;br /&gt;&lt;br /&gt;Most of the native unit extensions simply bring methods that already exist in later specifications. Things like &lt;tt&gt;String#trim&lt;/tt&gt;, &lt;tt&gt;Array#map&lt;/tt&gt;, &lt;tt&gt;Object.keys&lt;/tt&gt;, &lt;tt&gt;Function#bind&lt;/tt&gt;. Yes we do add them to the prototype level. But they all behave strictly in the standard way and they _will_ eventually appear in browsers.&lt;br /&gt;&lt;br /&gt;I agreed that there might be some desperate people who had a complicated childhood and might implement a non-standard &lt;tt&gt;Array#each&lt;/tt&gt; method and screw the things up. But IMHO, we should let the people learn on their mistakes and that's certainly not the reason to get scared and hide behind some "safe" interfaces. If someone wants he can screw the safe interface just the same way.&lt;br /&gt;&lt;br /&gt;On the other side, adding those methods, will give you several advantages, like first of all you can use all those methods right now, without waiting while obsolete browsers will vanish from the face of the Internet. Secondly it will actually prolong your application life, because those methods only going to get into common use, which means your code will be mostly working in future even when RightJS disappear.&lt;br /&gt;&lt;br /&gt;Then we do have several other top-level functions and objects. Functions like &lt;tt&gt;isString&lt;/tt&gt;, &lt;tt&gt;isArray&lt;/tt&gt;, &lt;tt&gt;isObject&lt;/tt&gt;, &lt;tt&gt;$A&lt;/tt&gt; they are extremely handy in everyday work and they are real time savers. Those methods all have clean, standard names and behavior, and you need to be a crazy person who don't know what he does, to re-implement them for some radically different purpose.&lt;br /&gt;&lt;br /&gt;Yes we do unsafe things with RightJS, but all of them are strictly managed, organized and helpful. This way we balance between total safety and fun/comfort in work.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Deeper Down The Rabbit Hole&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Any security comes with its cost, which is usually freedom and flexibility. Yes, I do agreed that having a non-conflicting interface is safe, but on the other side, it hooks your whole application on that interface. And I'm not talking about ridiculous situations, when you use the safe interface only and there are no potential threads for your application. Yes you are safe, but you're hooked to that interface.&lt;br /&gt;&lt;br /&gt;We are living in a pretty dynamic environment, and things do get faster and faster. New superior solutions appear every year. So consider this.&lt;br /&gt;&lt;br /&gt;Most of Prototype/Mootools code will work on RightJS after trivial and easily programmatically processable fixes. Same for RightJS, because it sticks to the standards in method names and behaviors, it will be really easy to transform RightJS code into Prototype/Mootools, pure DOM or even jQuery if you will.&lt;br /&gt;&lt;br /&gt;In case of a safe interface on the other hand, you don't have to change anything and you won't. 99.9% of applications will stuck with it, continuing to drag it around creating a mess of frameworks and interfaces inside of a single application. Take for example Dojo. An excellent framework, but most of applications are trapped with it, because it is too expensive to migrate.&lt;br /&gt;&lt;br /&gt;Okay, tomorrow might be a long shot and many applications don't live that long. What about today? There are also things to think about.&lt;br /&gt;&lt;br /&gt;RightJS unlike jQuery is not just a DOM-wrapper, it is an actual framework. RightJS does not leaves you alone with pure DOM/JavaScript when you disagree with what's going on. It supports multiple paradigms of development, OOP, FP, procedural style and at any moment you're free to choose how to do your job.&lt;br /&gt;&lt;br /&gt;Then, in similarity with languages like Ruby or Python, the whole structure of the framework is open. Unlike solutions with a safe interface RightJS doesn't hide anything inside of closed functions with spaghetti code. Most of the methods are organized in proper OOP structures and can be easily transformed and adjusted one by one.&lt;br /&gt;&lt;br /&gt;Another point is that RightJS helps you utilize things like options, events, dom-manipulations, etc. in a single uniformed way. And that's too the part of the terminal fun idea. There are might be dozens of ways of dealing with things outside of the framework and if there are no regulations those things might cause collisions. RightJS provides you a conventional way of dealing with those things, which at one side reduces the likelihood of troubles, and on the other brings consistency across all the applications, making it easier to work with.&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;There are lots of things that RightJS takes care of, but I think it's all out of this article theme and then you can find all of them on the &lt;a href="http://rightjs.org"&gt;official site&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So for now I guess that's all. Hope I gave you some sense of perspective and food for thoughts on the topic.&lt;br /&gt;&lt;br /&gt;Take care,&lt;br /&gt;Nikolay&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7538310276893360262?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7538310276893360262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7538310276893360262' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7538310276893360262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7538310276893360262'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/01/terminal-fun-or-why-rightjs.html' title='Terminal Fun, or Why RightJS?'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8096574687255442218</id><published>2010-01-15T16:11:00.007+02:00</published><updated>2010-01-18T14:03:31.252+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>So, is really jQuery 1.4 30'000 times faster?</title><content type='html'>Okay, I admit that, when it comes to RightJS promotion, sometimes I do behave like a bastard and have no mercy over women and foreigners. But this jQuery 1.4 celebration is well out of my league! :)&lt;br /&gt;&lt;br /&gt;I probably should just shut up about them publishing the release saying it weights 24k, and I've seen people today screaming "oh, it like the good old times!", and then they realize that it says in breakers "(gzipped)" and find out that the actual script weight is 70k. For a comparison, minified the same way Prototype weights 72k.&lt;br /&gt;&lt;br /&gt;But why I will never get tired of mocking jQuery users is that performance optimization graph John posted over there &lt;a href="http://jquery14.com/day-01/jquery-14"&gt;http://jquery14.com/day-01/jquery-14&lt;/a&gt;. In case they will fix it, I've saved a copy &lt;a href="http://stcamp.net/images/jq-14/jresig.jpg"&gt;over here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Basically what it says is that jQuery 1.4 makes &lt;b&gt;30'000&lt;/b&gt; (oh yeah baby, thirty thousands) times less calls than 1.3.2. Well to be honest in just a few operations. Seeing people making things like that I couldn't resist it and fired up my trusty &lt;a href="http://github.com/MadRabbit/shakker"&gt;Shakker&lt;/a&gt;, which bluntly compares frameworks performance feature by feature.&lt;br /&gt;&lt;br /&gt;So what do we see? They did optimized it awright. Certainly not 30'000 times, more like 30% in average. At least it looks okay in FF&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/jq-14/ff.png"&gt;&lt;img src="http://stcamp.net/images/jq-14/ff-thmb.png" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;But when you run the test in Safari and Opera, you can see that it actually getting &lt;u&gt;slower&lt;/u&gt; in some operations&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/jq-14/safari.png"&gt;&lt;img src="http://stcamp.net/images/jq-14/safari-thmb.png" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/jq-14/opera.png"&gt;&lt;img src="http://stcamp.net/images/jq-14/opera-thmb.png" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;And that's not the end. This test performs comparison on collections handling. And if you take a look inside of the new "fast" jQuery methods, you'll find out that they simply put collections processing in plain spaghetti JavaScript code. That means that it will and is working fast on collections, but when you process single elements (which is the majority of the cases), those methods will work &lt;u&gt;slower&lt;/u&gt;.&lt;br /&gt;&lt;br /&gt;This is about it. You can find all my tests code over here &lt;a href="http://github.com/MadRabbit/shakker"&gt;Shakker&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;But good luck with the 2 weeks orgy though 8)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8096574687255442218?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8096574687255442218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8096574687255442218' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8096574687255442218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8096574687255442218'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2010/01/so-is-really-jquery-14-30000-times.html' title='So, is really jQuery 1.4 30&apos;000 times faster?'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-6454841401648682349</id><published>2009-12-27T16:05:00.009+02:00</published><updated>2009-12-27T16:59:55.329+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Making a gem out of your ruby-on-rails plugin</title><content type='html'>Generally, now, when we have the &lt;a href="http://gemcutter.org"&gt;gemcutter&lt;/a&gt; service, making a gem is not a brainer. Just find a similar project on github, copypaste its gemspec, then say &lt;tt&gt;gem build my.gemspec&lt;/tt&gt; and &lt;tt&gt;gem push my.gem&lt;/tt&gt; and you're basically done.&lt;br /&gt;&lt;br /&gt;But there are some options you might want to know about.&lt;br /&gt;&lt;br /&gt;First of all to that gemspec business, as I said, if you never did that before, you'd better just find a famous plugin on github that is more or less close to your project, copy its spec in a file named &lt;tt&gt;my-plugin.gemspec&lt;/tt&gt; in the root of your plugin and fill it up with descriptions of your project.&lt;br /&gt;&lt;br /&gt;You might find the gemspec option descriptions &lt;a href="http://docs.rubygems.org/read/chapter/20"&gt;here&lt;/a&gt; and &lt;a href="http://rubygems.rubyforge.org/rubygems-update/Gem/Specification.html"&gt;here&lt;/a&gt;. For example if you need to show the user some post install message, use the &lt;tt&gt;post_install_message&lt;/tt&gt; option like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;spec.post_install_message = %Q{&lt;br /&gt;  Warning! Warning! Annoying message!&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And one more, some gemspecs on github use plain lists of files that should go into the spec, sometimes with several dozens of entries. Don't do that. First of all, it will be pain in the ass for you, because you might just forget to update the list when you create a new file, second of all it will be pain for another people who might be interested in evolving your project. Ruby is a nice language and has everything you need to process those stuff automatically. For example like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;spec.files = Dir['lib/**/*'] + Dir['spec/**/*'] + %w{&lt;br /&gt;  README&lt;br /&gt;  LICENSE&lt;br /&gt;  CHANGELOG&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;For the second thing, you need to understand how rails hooks up plugins and gems.&lt;br /&gt;&lt;br /&gt;When you create a ruby-on-rails plugin there is a file called &lt;tt&gt;init.rb&lt;/tt&gt; in the plugin directory. When rails fires up, it adds your plugin &lt;tt&gt;lib/&lt;/tt&gt; directory to the load-path and then includes this &lt;tt&gt;init.rb&lt;/tt&gt; file. Basically that's a good idea, you can keep the actual code clean in one place, and the dirty monkey patching script in another. This way you can test them separately and use separately, like say you want your module be available as is, outside of the rails stack, say with Rack apps or something.&lt;br /&gt;&lt;br /&gt;But when it comes to a gem, it's a bit different story. Gem supposed to be just a standalone piece of code which you require from your application. It doesn't know anything about initialization, and when ruby-on-rails hooks it up, it just requires a file with the same name as the rubygem.&lt;br /&gt;&lt;br /&gt;Say you have a plugin named &lt;tt&gt;super_duper&lt;/tt&gt; then you will have a file &lt;tt&gt;lib/super_duper.rb&lt;/tt&gt;, which rails will include when the gem is specified in the config. So, if you want to convert your plugin into a gem, you basically have two options. Include your init script into the super_duper.rb file, probably with some additional dirty conditions, or create another file like &lt;tt&gt;lib/super_duper_lib.rb&lt;/tt&gt; and make your user define the gem in rails config like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;conf.gem 'super_duper', :lib =&gt; 'super_duper_lib'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Both are quite dirty and yet pretty common.&lt;br /&gt;&lt;br /&gt;But, there is another, nicer option that lets you avoid disadvantages of those two approaches.&lt;br /&gt;&lt;br /&gt;The trick is simple. All you need is to name your ruby-gem in a dashed style. Like say you have a plugin &lt;tt&gt;super_duper&lt;/tt&gt;, so you create a ruby-gem named &lt;tt&gt;super-duper&lt;/tt&gt;, after that you just create a file named &lt;tt&gt;lib/super-duper.rb&lt;/tt&gt; with one simple line of code in it&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;require File.dirname(__FILE__) + '/../init.rb'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;When ruby-on-rails hooks up the gem it will load the dashed file, which will load your init script. This way you can share your initialization script between plugin and gem, and you still keep your actual code clean and easily available outside of the rails stack with the standard &lt;tt&gt;require 'super_duper'&lt;/tt&gt; call.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-6454841401648682349?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/6454841401648682349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=6454841401648682349' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6454841401648682349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6454841401648682349'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/12/making-gem-out-of-your-ruby-on-rails.html' title='Making a gem out of your ruby-on-rails plugin'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3547884445640037838</id><published>2009-12-18T17:07:00.005+02:00</published><updated>2009-12-18T17:56:02.767+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>Meet Shakker The Mano-A-Mano JavaScript Performance Testing Suite</title><content type='html'>I've started this project after several months of using &lt;a href="http://higginsforpresident.net/" target="_blank"&gt;Peter Higgins&lt;/a&gt;'s &lt;a href="http://github.com/phiggins42/taskspeed" target="_blank"&gt;TaskSpeed&lt;/a&gt; library as the main regression testing utility during the &lt;a href="http://rightjs.org" target="_blank"&gt;RightJS&lt;/a&gt; framework development.&lt;br /&gt;&lt;br /&gt;TaskSpeed is a pretty well done library, but many people (including me) were complaining about its tendency to mix different tasks in one, mainly the css-selectors and stuff, so sometimes it wasn't clear what's going on. Another problem was that it provides just a total time as the summary, which is not an exactly clean way of judging about frameworks relative speed.&lt;br /&gt;&lt;br /&gt;So I started my own little script, which become a bit bigger script and that one become even bigger one, and bigger and bigger. And now we have for ourselves practically an alternative solution for the TaskSpeed library.&lt;br /&gt;&lt;br /&gt;Meet &lt;a href="http://github.com/MadRabbit/shakker" target="_blank"&gt;The Shakker&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;The first difference between Shakker and TaskSpeed is that, Shakker has a two-level tests structure, the first one drives the test and keeps the data, the second one keeps the actual framework test. This way we have real-live like data separation on one side, and a blunt feature-to-feature comparison on the other. Shakker counts only the actual feature run-time for every framework, skipping utility operations like elements search, preparing collections, and stuff.&lt;br /&gt;&lt;br /&gt;Another difference is that, Shakker not just collects the time summary, it collects differences between framework results for every test and then calculates average values between all the tests. It also let you choose one framework and evaluate it against others. This way you can have an approximated relative frameworks speed comparison.&lt;br /&gt;&lt;br /&gt;It also keeps the original by-summary results, so that if your love sucks, you always can choose another result.&lt;br /&gt;&lt;br /&gt;So what all this gives to you? See for your self. I'm posting results for all the modern browsers below, browsing through them you can actually see now what's going on. For example, you can clearly see, that say RightJS slows down in the building and finding operations under IE7,6 because it has to extend all the nodes manually on those browsers. You also can see where optimizations for jQuery 1.4 go, and you can see how Dojo fastly unbinds event listeners, because it doesn't have an internal events registry and makes the developer keep all the pointers manually. You also can clearly see how RightJS rips off everything under Chrome and Safari, because it doesn't uses any special proxy for dom operations, and uses common Arrays, which those browsers heavily optimize.&lt;br /&gt;&lt;br /&gt;Lots of interesting stuff.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/chrome.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/chrome-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Google Chrome&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/ff.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/ff-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Firefox 3.5.6&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/safari.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/safari-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Safari 4.0.4&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/opera.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/opera-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Opera 10.10&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/konq.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/konq-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Konqueror 4.3.2 (Ubuntu on VMWare)&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/ie8.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/ie8-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Internet Explorer 8 (WinXP on VMWare)&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/ie7.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/ie7-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Internet Explorer 7(WinXP on VMWare)&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/ie6.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/ie6-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Internet Explorer 6(WinXP on VMWare)&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;All the tests were performed in OS X 1.6.2, on 2Hz macbook with 4GB of memory.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3547884445640037838?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3547884445640037838/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3547884445640037838' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3547884445640037838'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3547884445640037838'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/12/meet-shakker-mano-mano-javascript.html' title='Meet Shakker The Mano-A-Mano JavaScript Performance Testing Suite'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-6010661542212033778</id><published>2009-12-13T22:32:00.004+02:00</published><updated>2009-12-13T22:46:15.688+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>FrontCompiler With Rubygem And Console Tool</title><content type='html'>&lt;a href="http://github.com/MadRabbit/frontcompiler" target="_blank"&gt;FrontCompiler&lt;/a&gt; is a Ruby based JavaScript/CSS/HTML compressing tool of mine. It can create albeit packed JavaScript, works with DRYed CSS, can inline css in javascript, works as a Rails plugin, etc.&lt;br /&gt;&lt;br /&gt;And as I use it quite a lot on my projects, finally, I've created a rubygem for it. It's available at the gemcutter service now.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gemcutter.org/gems/front-compiler"&gt;http://gemcutter.org/gems/front-compiler&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So if you use the project, you might just install it from there.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;gem sources -a http://gemcutter.org&lt;br /&gt;gem install front-compiler&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Along with the rubygem I've added a simple console tool to work with the compressor&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;$ frontcc file1.js file2.js ....&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;It will automatically recognize the file type by its extension, so you can feed it with javascript or css or html. It will read them all, compress in a single string and spit it in the stdout. You also can mix javascript and css in one command to make the css be inlined in javascript.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-6010661542212033778?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/6010661542212033778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=6010661542212033778' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6010661542212033778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6010661542212033778'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/12/frontcompiler-with-rubygem-and-console.html' title='FrontCompiler With Rubygem And Console Tool'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3854673835017343878</id><published>2009-12-12T01:33:00.012+02:00</published><updated>2009-12-16T18:10:16.306+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS vs. jQuery, Mano A Mano</title><content type='html'>After the RightJS 1.5 release, there was people hyping about RightJS being 6-8x times faster than jQuery. After that there was quite a fight about that and then funny boy Felix had posted &lt;a href="http://debuggable.com/posts/rightjs-1-5-6-8-times-faster-than-jquery:4b1fc009-1940-4d26-bdc6-0af2cbdd56cb"&gt;an article&lt;/a&gt; which kinda says it's a bull.&lt;br /&gt;&lt;br /&gt;Some people got it seriously, some people were complaining about the &lt;a href="http://github.com/MadRabbit/taskspeed"&gt;original test&lt;/a&gt; saying that the TaskSpeed util is not really representative case affected by css-selecters and stuff.&lt;br /&gt;&lt;br /&gt;So I sat down and had written my own simple test, you can get it &lt;a href="http://github.com/MadRabbit/shakker"&gt;over here&lt;/a&gt;. This test eliminates any unnecessary disturbances, like elements selection, gets the questionable elements building process to the minimum and creates clean, pure functionality one-on-one performance test results.&lt;br /&gt;&lt;br /&gt;The result is predictable and actually correlates pretty closely with the original TaskSpeed based benchmarks.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/chrome.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/chrome-thmb.png" /&gt;&lt;/a&gt;&lt;br /&gt;Google Chrome&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/ff.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/ff-thmb.png"/&gt;&lt;/a&gt;&lt;br /&gt;Firefox 3.5.6&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs/shakker/opera.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs/shakker/opera-thmb.png"/&gt;&lt;/a&gt;&lt;br /&gt;Opera 10.10&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;As you can see, in a blunt, face to face performance test, RightJS does murk jQuery several times.&lt;br /&gt;&lt;br /&gt;Get use to it.&lt;br /&gt;&lt;br /&gt;UPD: As I'm working on the test suite, I've added the pure-dom test and also publishing results for Opera.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3854673835017343878?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3854673835017343878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3854673835017343878' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3854673835017343878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3854673835017343878'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/12/rightjs-vs-jquery-mano-o-mano.html' title='RightJS vs. jQuery, Mano A Mano'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5856072414361815628</id><published>2009-11-20T12:29:00.003+02:00</published><updated>2009-11-20T12:40:44.648+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>RightRails The RubyOnRails Plugin For RightJS</title><content type='html'>In case you don't know, some time ago I started a ruby-on-rails plugin for RightJS called &lt;a href="http://github.com/MadRabbit/right-rails"&gt;right-rails&lt;/a&gt;, which was just updated to the next version 0.3.0&lt;br /&gt;&lt;br /&gt;There are actually quite a lot of interesting and fancy stuff going on and it tries to solve many of usual headache of ajax applications development.&lt;br /&gt;&lt;br /&gt;Like say it transparently handling files uploading for remote forms, so that there is no difference if you have or have no files on your form. Plus there is a simple javascript interface for the most common ajax operations which is super easy in use. &lt;br /&gt;&lt;br /&gt;Then there is a new much more powerful RJS scripting replacement, that allows you write javascript in ruby and more of that mix in one flow the serverside and browserside contexts, it has automatic types conversion and lots of other things.&lt;br /&gt;&lt;br /&gt;Then small things like transparent Prototype/Scriptaculous helpers replacement for RightJS, plus various helpers for the RightJS own features and modules, plus automatic scripts including handler, etc. etc.&lt;br /&gt;&lt;br /&gt;If you like RightJS and do ajax stuff in rails, check it out, you'll find many helpful stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5856072414361815628?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5856072414361815628/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5856072414361815628' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5856072414361815628'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5856072414361815628'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/11/rightrails-rubyonrails-plugin-for.html' title='RightRails The RubyOnRails Plugin For RightJS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-682407555653506603</id><published>2009-09-19T09:41:00.002+03:00</published><updated>2009-09-19T09:45:54.718+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Ajax Files Uploading With RightJS And Rails</title><content type='html'>I kinda stuck with the &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; project for now and mostly write articles for the site. &lt;br /&gt;&lt;br /&gt;But there is another interesting articles that lights up some details over the remote forms with files handling using Ruby on Rails and RightJS. There is also a link to a complete ajax photo gallery application that uses the featues.&lt;br /&gt;&lt;br /&gt;Please take a look &lt;a href="http://rightjs.org/tutorials/ajaxed-photo-gallery"&gt;it is over here&lt;/a&gt;. I'm sure you might find some interesting stuff over there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-682407555653506603?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/682407555653506603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=682407555653506603' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/682407555653506603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/682407555653506603'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/09/ajax-files-uploading-with-rightjs-and.html' title='Ajax Files Uploading With RightJS And Rails'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-406069859144041264</id><published>2009-08-15T16:10:00.002+03:00</published><updated>2009-08-15T16:12:16.279+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS 1.4.0 Is Out</title><content type='html'>And by the way, forgot to mention it in here yesterday. But &lt;a href="http://rightjs.org" target="_blank"&gt;RightJS&lt;/a&gt; was released yesterday with the brand new 1.4.0 version.&lt;br /&gt;&lt;br /&gt;Mainly performance optimizations, some bug fixes and clean ups too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-406069859144041264?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/406069859144041264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=406069859144041264' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/406069859144041264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/406069859144041264'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/08/rightjs-140-is-out.html' title='RightJS 1.4.0 Is Out'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-694481929326933573</id><published>2009-08-15T16:04:00.002+03:00</published><updated>2009-08-15T16:10:03.026+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>Japanese Alphabet Training Machine</title><content type='html'>I'm trying to teach myself some Japanese. Well, sort of.&lt;br /&gt;&lt;br /&gt;So I've written the application &lt;a href="http://stcamp.net/games/nippo/" target="_blank"&gt;Happy-Nippo&lt;/a&gt; that might help you to memorize the first two, hiragana and katakana Japanese alphabets.&lt;br /&gt;&lt;br /&gt;Looks quite nice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-694481929326933573?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/694481929326933573/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=694481929326933573' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/694481929326933573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/694481929326933573'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/08/japanese-alphabet-training-machine.html' title='Japanese Alphabet Training Machine'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7833156546841233738</id><published>2009-08-13T18:26:00.002+03:00</published><updated>2009-08-13T18:33:16.681+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>FrontCompiler Updates</title><content type='html'>&lt;a href="http://github.com/MadRabbit/frontcompiler" target="_blank"&gt;FrontCompiler&lt;/a&gt; is a Ruby based JavaScript compression tool of mine. And I've just updated the self-builds feature of it.&lt;br /&gt;&lt;br /&gt;Formerly it used a literal based hashes and special markers, and I've reworked it the way it used numbers and arrays, similarly how the Base62 encryption algorithm works.&lt;br /&gt;&lt;br /&gt;Anyway. Now both, compression and decompression processes work faster, and the result builds are slightly (3-8%) smaller.&lt;br /&gt;&lt;br /&gt;This is pretty much it. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7833156546841233738?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7833156546841233738/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7833156546841233738' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7833156546841233738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7833156546841233738'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/08/frontcompiler-updates.html' title='FrontCompiler Updates'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5303551686446656368</id><published>2009-08-09T19:34:00.003+03:00</published><updated>2009-08-09T19:40:47.647+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS Now Rocks In IE8 Too</title><content type='html'>As you probably know, the new version of &lt;a href="http://rightjs.org" target="_blank"&gt;RightJS&lt;/a&gt; is coming. I've done some optimizations in the native css-selectors support in this version  and now RightJS is the fastest one in IE8 too. The only condition, it has to have a correct document definition or to be switched to the XA-Compatible mode manually.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://rightjs.org/images/benchmarks/ie8-xa.png" target="_blank"&gt;&lt;img src="http://rightjs.org/images/benchmarks/ie8-xa-thmb.png"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5303551686446656368?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5303551686446656368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5303551686446656368' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5303551686446656368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5303551686446656368'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/08/rightjs-rocks-in-ie8-xa-mode-too.html' title='RightJS Now Rocks In IE8 Too'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3740688459805657889</id><published>2009-08-06T13:20:00.003+03:00</published><updated>2009-08-06T13:30:33.700+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS Rocks In Firefox 3.5</title><content type='html'>A newer version of &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; is coming, it will come with some further native method optimizations and now it beats everyone in the new upcoming Firefox 3.5&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://rightjs.org/images/benchmarks/ff3.5.png" target="_blank"&gt;&lt;img src="http://rightjs.org/images/benchmarks/ff3.5-thmb.png"/&gt;&lt;/a&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3740688459805657889?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3740688459805657889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3740688459805657889' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3740688459805657889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3740688459805657889'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/08/rightjs-rocks-on-firefox-35.html' title='RightJS Rocks In Firefox 3.5'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2148966930890786641</id><published>2009-08-01T10:29:00.003+03:00</published><updated>2009-08-01T10:52:16.301+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTML/CSS'/><title type='text'>HTML Code For A Close Button Icon</title><content type='html'>Some people like use chars and html-codes as a default content for buttons and links. The idea is to put some simple character or code which will look like an icon, and later, if necessary, replace it with a real icon at the css-level. A "close" button is a pretty common case and people usually use the "X" character in there. The problem with it is that it has wrong dimensions and doesn't look like a normal cross on a close button. Luckily there is a better way. There is a standard html code for the mathematical multiplication symbol &lt;tt&gt;&amp;amp;times;&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;It looks like that&lt;br /&gt;&lt;br /&gt;&lt;center style="font-size: 800%"&gt;&amp;times;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;As you see it has the correct shape of the close symbol, and it still a normal character which you can paint with css just as you like, use hover effects, etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2148966930890786641?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2148966930890786641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2148966930890786641' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2148966930890786641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2148966930890786641'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/08/html-code-for-close-button-icon.html' title='HTML Code For A Close Button Icon'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7298417330989832920</id><published>2009-07-25T18:49:00.003+03:00</published><updated>2009-07-25T19:01:29.971+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS Is Faster Than Pure Dom</title><content type='html'>Well, I'm cooking a new version of &lt;a href="http://rightjs.org" target="_blank"&gt;RightJS&lt;/a&gt;. I've optimized this and that, tuned here and there. And then I've run the TaskSpeed test and it looks like RightJS works faster than pure dom.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs-rocks.png" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs-rocks-thmb.png"/&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;That's not a fake, seriously, I've got this result from Safari 4. jQuery has in this test 883 points.&lt;br /&gt;&lt;br /&gt;Sure, that's because of the way the original test was written, but the result is just hilarious 8)&lt;br /&gt;&lt;br /&gt;PS: Goin' to publish the new version in the next couple of days&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7298417330989832920?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7298417330989832920/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7298417330989832920' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7298417330989832920'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7298417330989832920'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/rightjs-is-faster-than-pure-dom.html' title='RightJS Is Faster Than Pure Dom'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-606528250210396251</id><published>2009-07-23T08:43:00.008+03:00</published><updated>2009-07-23T09:12:08.475+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Transparent Dates and Times Internationalization in Rails</title><content type='html'>The basic internationalization approach in Ruby on Rails supposes that you translate the dates and times like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Created at: &amp;lt;%= I18n.localize @model.created_at, :format =&gt; :short %&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;But most of us use the "to_s" method to format times, like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Created at: &amp;lt;%= @model.created_at.to_s :short %&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Mostly because you usually don't think much about i18n when a project is just starting up, and then it's kinda sweet, nice and short way of doing that.&lt;br /&gt;&lt;br /&gt;So, usually when it comes to the internationalization, the project is already pretty much covered with the to_s methods. If you were lucky you might had created a single helper method which processes all the dates and times in your project and then it's simple, but if you didn't you might think about highjacking the "to_s" method of the Time class. Like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class Time&lt;br /&gt;  def to_s(format=:default)&lt;br /&gt;    I18n.localize self, :format =&amp;gt; format&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;But if you do so, you will probably broke the things. Because first of all the "to_s" method supposed to work like an alias for the "strftime" method, secondly this method is used to convert times and dates in the :db format and if it was not translated properly it will break your models, thirdly it's naughty when you completely replace such a method.&lt;br /&gt;&lt;br /&gt;Okay here is a better way of doing that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;[Date, Time, ActiveSupport::TimeWithZone].each do |klass|&lt;br /&gt;  klass.instance_eval do&lt;br /&gt;    define_method :to_s_with_i18n do |*args|&lt;br /&gt;      format = args.first || :default&lt;br /&gt;      if format.is_a?(Symbol) &amp;&amp; format != :db&lt;br /&gt;        I18n.localize(self, :format =&amp;gt; format)&lt;br /&gt;      else&lt;br /&gt;        to_s_without_i18n(*args)&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    alias_method_chain :to_s, :i18n&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;It keeps the original method and doesn't touch the custom and database formats. The ActiveSupport::TimeWithZone class is the Rails class for the UTC times you need to process it too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-606528250210396251?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/606528250210396251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=606528250210396251' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/606528250210396251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/606528250210396251'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/transparent-dates-and-times.html' title='Transparent Dates and Times Internationalization in Rails'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2815589386268181636</id><published>2009-07-20T14:04:00.003+03:00</published><updated>2009-07-20T14:14:28.047+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTML/CSS'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Inline IE hacks with SASS</title><content type='html'>SASS is a nested css engine written in ruby. It's a pretty powerful and handy tool with lots of features inside, &lt;a href="http://sass-lang.com/" target="_blank"&gt;http://sass-lang.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'm using it on my current project and find it quite useful. The only thing which was bugging me is that I tend to use inline IE hacks in my css, like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;div.box {&lt;br /&gt;  cursor: pointer;&lt;br /&gt;  * cursor: hand;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And it seems like sass doesn't support such a feature, at least I haven't found it in their documentation.&lt;br /&gt;&lt;br /&gt;But in reality it does actually has support of the feature, just it is not documented. It looks like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#my-block&lt;br /&gt;  :cursor pointer&lt;br /&gt;  *cursor: pointer&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Note, there should be no space betwen '*' sign and the property name&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2815589386268181636?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2815589386268181636/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2815589386268181636' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2815589386268181636'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2815589386268181636'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/inline-ie-hacks-with-sass.html' title='Inline IE hacks with SASS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1532124667659776003</id><published>2009-07-17T16:53:00.006+03:00</published><updated>2009-07-17T17:55:04.910+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Customizing The Rails Forms Builder</title><content type='html'>The word "customer", usually means someone who come and say "I wanna custom stuff". And because it is their nature they do it all the time. Sometimes several times a day.&lt;br /&gt;&lt;br /&gt;I'm skipping here the obvious question "why would I need to customize a forms builder?" and get straight to the point.&lt;br /&gt;&lt;br /&gt;So you need to customize your forms. Big time. And if the first what comes on your mind are helper methods and partial templates, this article is for you.&lt;br /&gt;&lt;br /&gt;And please don't think about monkey patching rails form-builder. Don't behave like a php developer thinking he can use rails. Lord save their poor souls.&lt;br /&gt;&lt;br /&gt;There are better way of doing that.&lt;br /&gt;&lt;br /&gt;First of all you should realize that in 99.99% of cases you should not change the default rails templates. Meaningly that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;% form_for(@model) do |f| -%&amp;gt;&lt;br /&gt;  &amp;lt;%= f.error_messages %&amp;gt;&lt;br /&gt;  &amp;lt;p&amp;gt;&lt;br /&gt;    &amp;lt;%= f.label :name %&amp;gt;&lt;br /&gt;    &amp;lt;%= f.text_field :name %&amp;gt;&lt;br /&gt;  &amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;% end -%&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Despite that it is a simple structure, with a little bit of imagination and css magic you can make it look like anything you need.&lt;br /&gt;&lt;br /&gt;Secondly you really should pay a dollar of penalty every time you think about monkey patching. Ruby is not just a monkey patching language it is object oriented too, and rails form builder is a quite well organized structure.&lt;br /&gt;&lt;br /&gt;Instead of patching, we will create our own form-builder using advantages of the inheritance and then swap the default builder from the built-in to our own one. This way we can change the logic of the forms building transparently for the templates, you even can have several form-builders which behave differently and swap them depend on the context.&lt;br /&gt;&lt;br /&gt;The basic example would look like this. You create another helper module called FormsHelper and save it along with the other helpers in your application&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;module FormsHelper&lt;br /&gt;  def self.included(base)&lt;br /&gt;    ActionView::Base.default_form_builder = CustomFormBuilder&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  class CustomFormBuilder &amp;lt; ActionView::Helpers::FormBuilder&lt;br /&gt;    # your custom methods go here&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Inside the class you will have access to the form builder context with all its built in methods and variables. For the beginning you should know about the following ones&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;@template - the template context, you call this variable if you need to call the basic helpers&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;@object - the current model of the form&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;@object_name - the string name under which the form knows the model&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Okay now lets play with the thing a little. Say the customer says &lt;cite&gt;"don't like the &amp;lt;h2&amp;gt; header on the error reports, the one with the number of errors on the form"&lt;/cite&gt;. No problem, we just override the built in method&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;  def error_messages(options={})&lt;br /&gt;    super options.reverse_merge(:header_message =&gt; nil)&lt;br /&gt;  end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then say you as many other developers usually put your forms in a partial and then depends on the model state change the submit button caption. something like this&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;form_for(@model) do |f|&lt;br /&gt;  f.submit @model.new_record? ? "Create" : "Save"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is a little bit annoying. Say I'd like just call it &lt;tt&gt;&amp;lt;:%= f.submit %&amp;gt;&lt;/tt&gt; and want the form automatically set the caption. Here is the code&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;def submit(caption=nil, options={})&lt;br /&gt;  super(caption || @object.new_record? ? "Create" : "Save"), options&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then I got completely lazy and started to want my form builder to build a block of label + text-field in a single call, like this&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;form_for(@model) do |f|&lt;br /&gt;  f.labeled_text_field :name&lt;br /&gt;&lt;br /&gt;# this should build the thing like that&lt;br /&gt;&amp;lt;p&amp;gt;&lt;br /&gt;  &amp;lt;%= f.label :name %&amp;gt;&lt;br /&gt;  &amp;lt;%= f.text_field :name %&amp;gt;&lt;br /&gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;# you could do it like this&lt;br /&gt;def labeled_text_field(name, options={})&lt;br /&gt;  @template.content_tag(:p, label(name) + text_field(name, options))&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then, all the sudden, your customer comes back and says a scary thing &lt;cite&gt;"I like this, but I want for this particular controller, there actually was the h2 header saying 'Oh noooo!'. Yes, just for this special case".&lt;/cite&gt;&lt;br /&gt;&lt;br /&gt;No, my friend we are not going to the monkey patchers hell! We just define another form-builder over our own builder, and automatically swap it in the particular controller&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class SpecialFormBuilder &lt; CustomFormBuilder&lt;br /&gt;  def error_messages(options={})&lt;br /&gt;    super options.reverse_merge(:header_message =&gt; "Oh noooo!")&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class ParticularController &lt; ApplicationController&lt;br /&gt;  before_filter :swap_form_builder&lt;br /&gt;&lt;br /&gt;  def swap_form_builder&lt;br /&gt;    ActionView::Base.default_form_builder = SpecialFormBuilder&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Think you understand the idea. If you do the things in a serious way, lord will love you and grand you a big deal of flexibility and rapidness.&lt;br /&gt;&lt;br /&gt;This is pretty much it. Have a good one!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1532124667659776003?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1532124667659776003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1532124667659776003' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1532124667659776003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1532124667659776003'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/fixing-rails-forms-builder.html' title='Customizing The Rails Forms Builder'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-839185544035617307</id><published>2009-07-09T22:05:00.002+03:00</published><updated>2009-07-09T22:11:15.670+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Decimal to Hex and Hex to Decimal in JavaScript</title><content type='html'>Generally JavaScript doesn't have a particular data type or class for hex values but it still provides couple of features which will let you work with hex data.&lt;br /&gt;&lt;br /&gt;In similar to Ruby you can keep your hex values in strings and the convert them into decimal values back and forth.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var hex = 'FF';&lt;br /&gt;&lt;br /&gt;var dec = parseInt(hex, 16); // -&gt; 255&lt;br /&gt;&lt;br /&gt;dec = dec + 255;&lt;br /&gt;&lt;br /&gt;hex = dec.toString(16); // -&gt; '1ff'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-839185544035617307?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/839185544035617307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=839185544035617307' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/839185544035617307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/839185544035617307'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/decimal-to-hex-and-hex-to-decimal-in.html' title='Decimal to Hex and Hex to Decimal in JavaScript'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1876547952199401550</id><published>2009-07-07T17:34:00.008+03:00</published><updated>2009-07-07T18:38:39.893+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Psychic Abilities Test</title><content type='html'>I've created another toy, &lt;a href="http://stcamp.net/games/psych/" target="_blank"&gt;Psych Test&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The thing is fairly simple, you've seen it lots of times in movies. You have a standard 36 playing cards deck, and one by one you are trying to guess the next card suit color. Red or black.&lt;br /&gt;&lt;br /&gt;Well... I'm not a psychic but I know what are you thinking. &lt;br /&gt;&lt;br /&gt;"...50%...", right?&lt;br /&gt;&lt;br /&gt;I bet, if you ask any of your friends or colleagues who is occupied in CS, mathematics or physics you'll have the same exact answer. But the thing is not actually that simple, and here is where the fun begins.&lt;br /&gt;&lt;br /&gt;If you open up the link above and will click on the same exact prediction button during the whole test, the result will be exactly those 50%, well +/- 1% in some unlucky situations. You might repeat the test several times, it's pretty consistent almost without any deviations.&lt;br /&gt;&lt;br /&gt;But if you will have the same test, but will actually try to guess the next card's color, you'll have a different result, usually in range between 40 and 60%. And you almost never will have 50% again.&lt;br /&gt;&lt;br /&gt;You should try it to believe, you can check the source code too, there is no back door. Everything is fair.&lt;br /&gt;&lt;br /&gt;So what do you think, how is that possible? Do you think it is you actual ability to predict the future affects the result?&lt;br /&gt;&lt;br /&gt;I could tell a spooky story about underground CIA laboratories where they study those things since 60s, and that there is a standard measurement for such tests and an exact percentage which defines you as a proven psychic.&lt;br /&gt;&lt;br /&gt;Or, if you prefer, we could think about the problem rationally. There are just four practically independent cases and two of them correct, so the result should be close to 50% every time, no matter which button you choose. But yet it's ain't like that.&lt;br /&gt;&lt;br /&gt;As you understand this is not the test for a psychic abilities, this is a test for rational thinking.&lt;br /&gt;&lt;br /&gt;Okay, I'm not going to marinade you any further, as you come here and read this far I owe you an answer.&lt;br /&gt;&lt;br /&gt;The thing is simple but not that obvious. There's no mathematical or logical problem, every time you click any of those buttons, you've got an exact 50% probability to score. But in case when you click the same button all the time, you put your guess against an almost ideal random sequence generated by the computer. And that's why you have this pretty close 50% result every time.&lt;br /&gt;&lt;br /&gt;But when you do the "guessing", your brain follows some patterns and tries to predict the next card. Yes, generally that's a random process and every guess as good as any other, but the distribution of those guesses in time is not such good as a random distribution generated by the computer. There are fluctuations and attempts to guess, which affect the quite short 36 cards selection and gives you the non 50% result.&lt;br /&gt;&lt;br /&gt;The first time you test the computer's random numbers generator, the second time you test your own.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1876547952199401550?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1876547952199401550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1876547952199401550' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1876547952199401550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1876547952199401550'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/psychic-abilities-tester.html' title='Psychic Abilities Test'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5807923489801581513</id><published>2009-07-05T18:09:00.005+03:00</published><updated>2009-07-06T16:10:35.906+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Call vs. Apply</title><content type='html'>Today I'm going to tell you an educational story.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var f = function() {};&lt;br /&gt;&lt;br /&gt;var test = function(callback) {&lt;br /&gt;  var time = new Date();&lt;br /&gt;  (10000).times(callback);&lt;br /&gt;  console.log(new Date() - time);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;var test_with_apply = function() {&lt;br /&gt;  test(function() {&lt;br /&gt;    f.apply(this, [0,1,2,3,4,5,6,7,8,9]);&lt;br /&gt;  });&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;var test_with_call = function() {&lt;br /&gt;  test(function() {&lt;br /&gt;    f.call(this, 0,1,2,3,4,5,6,7,8,9);&lt;br /&gt;  });&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;(10).times(test_with_apply);&lt;br /&gt;console.log('--');&lt;br /&gt;(10).times(test_with_call);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The moral&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;119&lt;br /&gt;122&lt;br /&gt;118&lt;br /&gt;118&lt;br /&gt;128&lt;br /&gt;119&lt;br /&gt;120&lt;br /&gt;119&lt;br /&gt;119&lt;br /&gt;120&lt;br /&gt;--&lt;br /&gt;55&lt;br /&gt;56&lt;br /&gt;55&lt;br /&gt;55&lt;br /&gt;55&lt;br /&gt;55&lt;br /&gt;55&lt;br /&gt;56&lt;br /&gt;56&lt;br /&gt;55&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5807923489801581513?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5807923489801581513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5807923489801581513' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5807923489801581513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5807923489801581513'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/call-vs-apply.html' title='Call vs. Apply'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8229223766567930654</id><published>2009-07-02T10:01:00.004+03:00</published><updated>2009-07-02T10:09:01.681+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>Sudoku Game In JavaScript and RightJS</title><content type='html'>In order to have some show off cases for my &lt;a href="http://rightjs.org"&gt;RightJS&lt;/a&gt; project I've written basic &lt;a href="http://stcamp.net/games/sudoku/index.html"&gt;Sudoku Game&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Nothing really special but a quite nice implementation though. Check it out you will like it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8229223766567930654?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8229223766567930654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8229223766567930654' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8229223766567930654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8229223766567930654'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/sudoku-in-javascript-and-rightjs.html' title='Sudoku Game In JavaScript and RightJS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1244524326447162981</id><published>2009-07-01T22:06:00.003+03:00</published><updated>2009-07-01T22:38:27.499+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Sudoku Puzzle Generating, Pragmatic Way</title><content type='html'>Couple of days ago I described &lt;a href="http://st-on-it.blogspot.com/2009/06/sudoku-random-field-generator.html"&gt;how you could generate a good random sudoku field&lt;/a&gt;, today I'm going to advocate for just opposite thing. There's no controversy in really, just there are different cases, when you should and when you should not do that.&lt;br /&gt;&lt;br /&gt;You should or could write your own sudoku puzzles generator if you are particularly interested in the field and ready to spend lots and lots of time, getting deep in all the various rules which actually make a good sudoku. Well at least it will keep your self-esteem up.&lt;br /&gt;&lt;br /&gt;But if you are just writing some implementation of the sudoku game, you actually better not to do that. And here is why.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Sudoku generating is a random process with generally unpredictable duration. There is a good chance that your generator will get unlucky and stuck. And a hanging over application is the last thing you want your user to see.&lt;br /&gt;  &lt;li&gt;The process of generating of a really good, hard sudoku usually takes time, sometimes a few seconds&lt;br /&gt;  &lt;li&gt;There are lots of rules you need to get into, implement test over, and then if you are not an expert in sudoku you wont be able to check it out if you have done everything properly&lt;br /&gt;  &lt;li&gt;You will probably forget and never use again all the knowledge you gain during the implementation. I don't want to call it a waste of time, but I'm pretty sure there are better ways to actually waste your time.&lt;br /&gt;  &lt;li&gt;A good sudoku generator might be quite big and complicated piece of software, which will be hard to test and support.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Luckily there are better ways to do that. Use other people's work to be precise. There are lots and lots of online sudoku generators, or you could use any desktop applications, most of them cache boards in files. I used the gnome-sudoku application, it lets you generate any number of sudoku and saves them in the files in the gnome configuration directory, or you can grab my archive right here &lt;a href="http://stcamp.net/share/puzzles.zip"&gt;puzzles.zip&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;What about the number of puzzles ask you? You need some storage to keep them and have big enough collection to keep the user happy.&lt;br /&gt;&lt;br /&gt;Actually not, and here is the trick of the article. You actually can keep just a few puzzles for each level and then generate new puzzles out of the originals. You can safely do the following things with your puzzles&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Rotate them&lt;br /&gt;  &lt;li&gt;Flip in any way&lt;br /&gt;  &lt;li&gt;Randomly shuffle rows and columns inside the 3x3 boxes&lt;br /&gt;  &lt;li&gt;Shuffle rows and columns of the 3x3 boxes&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;As the result, if you say have initially a collection out of 16 puzzles, then you can rotate and flip it in 5 ways, then you can shuffle rows and columns in 9 ways in each of the nine 3x3 boxes, and eventually you can have 9 combinations of the 3x3 blocks.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;16 * 5 * 9 * 9 * 9 = 58320&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That's more than enough for your user never see the same puzzle twice in a row. More of that, all of those puzzles will be really consistent by the difficulty level because initially it is just the same puzzle.&lt;br /&gt;&lt;br /&gt;And for the end you can see a possible JavaScript implementation right here &lt;a href="http://github.com/MadRabbit/right-sudoku/blob/02fa7411da57c68f97f6b8b2f6a73a5a4cff4646/src/sudoku/boards.js"&gt;boards.js&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is pretty much it. Have a good one!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1244524326447162981?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1244524326447162981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1244524326447162981' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1244524326447162981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1244524326447162981'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/07/sudoku-puzzle-generating-pragmatic-way.html' title='Sudoku Puzzle Generating, Pragmatic Way'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3607910526672733349</id><published>2009-06-29T14:16:00.003+03:00</published><updated>2009-06-29T15:17:32.828+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Sudoku Random Field Generator</title><content type='html'>The task is simple, to fill up a 9x9 field by the sudoku game rules.&lt;br /&gt;&lt;br /&gt;Generally it's not a rocket science and there are lots of simple solutions. For example filling up the field with shifted sequences. Say you have a sequence of numbers from 1 to 9, then you might generate a valid field like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;1 2 3  4 5 6  7 8 9&lt;br /&gt;4 5 6  7 8 9  1 2 3&lt;br /&gt;7 8 9  1 2 3  4 5 6&lt;br /&gt;&lt;br /&gt;2 3 4  5 6 7  8 9 1&lt;br /&gt;5 6 7  8 9 1  2 3 4&lt;br /&gt;8 9 1  2 3 4  5 6 7&lt;br /&gt;&lt;br /&gt;3 4 5  6 7 8  9 1 2&lt;br /&gt;6 7 8  9 1 2  3 4 5&lt;br /&gt;9 1 2  3 4 5  6 7 8&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;As you see, the principle is simple, you start with your basic sequence, and then shift it by 3 positions inside 3x3 box, and by one position between the levels. The principle will still work if you start with any randomly sorted sequence of unique numbers.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;3 5 6  2 7 4  1 9 8&lt;br /&gt;2 7 4  1 9 8  3 5 6&lt;br /&gt;1 9 8  3 5 6  2 7 4&lt;br /&gt;&lt;br /&gt;5 6 2  7 4 1  9 8 3&lt;br /&gt;7 4 1  9 8 3  5 6 2&lt;br /&gt;9 8 3  5 6 2  7 4 1&lt;br /&gt;&lt;br /&gt;6 2 7  4 1 9  8 3 5&lt;br /&gt;4 1 9  8 3 5  6 2 7&lt;br /&gt;8 3 5  6 2 7  4 1 9&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After that you might shuffle columns and rows in scope of boxes to make the field more random, you even might shuffle the 3x3 rows and columns as well to have even better results.&lt;br /&gt;&lt;br /&gt;But this thing is not really serious and kinda boring. The problem is that all your numbers are grouped by three numbers and those groups in different orders will appear all over the field. And this might be used for cheating. Say you open one of the sequences &lt;tt&gt;4 1 9&lt;/tt&gt; and then wherever you open any of those three numbers you immediately know the numbers next to it.&lt;br /&gt;&lt;br /&gt;Well for smarty pants like you and me, we need something different, something bigger, better and more bad ass.&lt;br /&gt;&lt;br /&gt;After several coups of coffee I've come to the following algorithm.&lt;br /&gt;&lt;br /&gt;1. We generate our field row by row&lt;br /&gt;2. For each cell in a row, depends on the other existing numbers in the 3x3 box or the column, we generate a list of options&lt;br /&gt;3. Out of the list of options for each cell we randomly select a value&lt;br /&gt;3.1 When we convert a list of the options into a row we watch for intersections with other options and choose the most optimal collection&lt;br /&gt;&lt;br /&gt;For example we have the following thing&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;1 5 6  9 8 7  4 3 2&lt;br /&gt;9 2 3  4 6 5  8 7 1&lt;br /&gt;8 7 4  3 1 2  6 9 5&lt;br /&gt;&lt;br /&gt;4 9 7  6 3 1  5 2 8&lt;br /&gt;5 3 2  7 4 8  1 6 9&lt;br /&gt;? ? ?  ? ? ?  ? ? ?&lt;br /&gt;----------------------&lt;br /&gt;The options will be following&lt;br /&gt;&lt;br /&gt;6 1 1  2 2 9  3 4 3&lt;br /&gt;  6 8  5 5    7   4&lt;br /&gt;  8      9        7&lt;br /&gt;&lt;br /&gt;----------------------&lt;br /&gt;Out of the options we might create sequences&lt;br /&gt;&lt;br /&gt;6 1 8  2 5 9  7 4 3 &lt;br /&gt;6 8 1  5 2 9  4 7 3&lt;br /&gt;......&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This way we will have no patterns and truly random valid sudoku field.&lt;br /&gt;&lt;br /&gt;The only problem is that it is not exactly working.&lt;br /&gt;&lt;br /&gt;As it's a random process, for each valid field there will be about 5-10 cases when there will be no ability to create a correct sequence. But despite the "not exactly" prefix, the algorithm is actually working and creates beautiful truly random fields.&lt;br /&gt;&lt;br /&gt;And then you have a choice. Think about the algorithm by yourself and try to make it work every time, or you can make your computer think through the algorithm several times until a fully covered field will pop up.&lt;br /&gt;&lt;br /&gt;Yes, I know, for a piece of software it sounds cheesy, but in reality there are not much calculations in the algorithm and make it running 5-10 times is not a big deal, it takes just a few milliseconds in javascript. Besides, if you need a speed, you can always cache the fields and shuffle them later as you shuffle the fields generated by patterns.&lt;br /&gt;&lt;br /&gt;And for the grand finale here is a possible solution in JavaScript + RightJS&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;....&lt;br /&gt;  // generates randomly distributed field&lt;br /&gt;  generateGrid: function() {&lt;br /&gt;    var numbers = '123456789'.split('').map('toInt');&lt;br /&gt;    var matrix  = numbers.map(function() { return []; });&lt;br /&gt;    &lt;br /&gt;    for (var x=0; x &amp;lt; 9; x++) {&lt;br /&gt;      var options = [];&lt;br /&gt;      &lt;br /&gt;      for (var y=0; y &amp;lt; 9; y++) {&lt;br /&gt;        var col_numbers = numbers.map(function(i) { return matrix[i-1][y]; });&lt;br /&gt;        var box_numbers = numbers.map(function(i) {&lt;br /&gt;          var box_x = (x/3).floor() * 3 + ((i-1) % 3);&lt;br /&gt;          var box_y = (y/3).floor() * 3 + ((i-1)/3).floor();&lt;br /&gt;          &lt;br /&gt;          return matrix[box_x][box_y];&lt;br /&gt;        });&lt;br /&gt;        &lt;br /&gt;        options.push(numbers.without.apply(numbers, col_numbers.concat(box_numbers)));&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      matrix[x] = this.sequenceFromOptions(options);&lt;br /&gt;      &lt;br /&gt;      // drop and start over if there sequence was damaged&lt;br /&gt;      if (matrix[x].compact().length &amp;lt; 9)&lt;br /&gt;        return this.generateGrid();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    return matrix;&lt;br /&gt;  },&lt;br /&gt;  &lt;br /&gt;  sequenceFromOptions: function(options) {&lt;br /&gt;    for (var i=0; i &amp;lt; 9; i++) {&lt;br /&gt;      this.cleanOptions(options);&lt;br /&gt;      &lt;br /&gt;      options[i] = [options[i].random()];&lt;br /&gt;      for (var j=i+1; j &amp;lt; 9; j++) {&lt;br /&gt;        options[j] = options[j].without(options[i][0]);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    return options.map('first');&lt;br /&gt;  },&lt;br /&gt;  &lt;br /&gt;  cleanOptions: function(options) {&lt;br /&gt;    for (var i=0; i &amp;lt; 9; i++) {&lt;br /&gt;      if (options[i].length == 1) {&lt;br /&gt;        for (var j=0; j &amp;lt; 9; j++) {&lt;br /&gt;          options[j] = options[j].length == 1 ? options[j] : options[j].without(options[i][0]);&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;....&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3607910526672733349?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3607910526672733349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3607910526672733349' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3607910526672733349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3607910526672733349'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/06/sudoku-random-field-generator.html' title='Sudoku Random Field Generator'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4637934185154275002</id><published>2009-06-20T19:22:00.003+03:00</published><updated>2009-06-20T20:09:50.680+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>How Not To Create Rails Plugin</title><content type='html'>Another day I needed to handle some task which included communication with a SPARQL service. Quick google search gave me a reference to the &lt;a href="http://activerdf.org"&gt;ActiveRDF&lt;/a&gt; project, which claims to be the thing for ruby and rails.&lt;br /&gt;&lt;br /&gt;But the thing is that the darn thing ain't work. As the matter of fact, you won't be able even run the example from the official site. I'm not complying, used the python sparql-wrapper to accomplish the task, but this project is a good example how you should not create a Rails plugin.&lt;br /&gt;&lt;br /&gt;And here some tips and lessons you can learn out of it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Location and Versioning Control System&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The project was started at the &lt;a href="https://launchpad.net"&gt;https://launchpad.net&lt;/a&gt; service, under the bazaar versioning control system. That's probably a progressive thinking, but still a big mistake.&lt;br /&gt;&lt;br /&gt;All the Rails crowd currently, more or less lives on git and github. The reason is obvious, all the people in one place, it is easy to participate in the projects using quick forking and merging. No need to register and set up your account. You do it once and  have them all.&lt;br /&gt;&lt;br /&gt;I had some experience with launchpad, it's a pretty good system, and if I would start some specific project for Ubuntu, I would probably thought about launchpad, but for Rails related project, if you want the people to participate you should go to github.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Implementation&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The implementation as well a good example how not to write ruby code.&lt;br /&gt;&lt;br /&gt;First of all when you creating an actual plugin or gem, global classes is the last thing which you should do. There should be the only one global name and this is the name of your project. All the rest of the things should be under the namespace.&lt;br /&gt;&lt;br /&gt;The second is the API and what the users need to write in order to make it working in their application. With such a beautiful and powerful language as Ruby is, it is quite a shame to make people call the factory method directly. None of the actual rails part do that. There so much opportunities for all the meta-programming magic and you end up calling classes creation manually.&lt;br /&gt;&lt;br /&gt;They should do it simultaneously to the existing things, like ActiveRecord or ActiveResource. As the matter of fact they probably should not start a standalone project in the first place and create some extension for the ActiveRecource project, they might make it to the rails codebase and that's huge. Much better than having another rails plugin.&lt;br /&gt;&lt;br /&gt;But anyway, with a badly structured project, even if someone would like to participate and help it, that would be really hard and usually almost impossible to change anything, because there are already people who use the project as is, and people don't like to update their application code.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Support&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I don't think I should mention such basic things, that when you have year and a half old bugs in your bug-tracker, even bugs where people already posted solutions and patches and you still didn't fix the problem, that's quite suck. As well as I should not mention such simple thing like keeping your code actually working, at least with the examples you have got on the front page.&lt;br /&gt;&lt;br /&gt;The thing is that despite the fact that you are creating an open-source project and by the license you are not responsible for anything, you actually creating a project for people. Yes I know 90% of OOS just show-off projects by rookies just out of the university. But if you want the people to take you seriously you should support your projects.&lt;br /&gt;&lt;br /&gt;Even if you don't want to work on the project anymore and there are no one else, you probably should say something, like "sorry guys the thing didn't fly and I have better things to do". Yes that suck, but that's understandable, and responsible.&lt;br /&gt;&lt;br /&gt;If you didn't do that, people will still come and kill their time with your buggy code and will be left pretty much alone. You should warn your customers about such things, because everyone has different situations, someone probably would be happy to spend some hours to make a dead thing walking, but someone might be on a deadline and would expect your project actually working and probably would switch to something else if was warned.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The End&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;So the thing is simple. Bad location, bad implementation and bad support. This is more than enough to kill almost any project in any area. Think about it when you start your own.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4637934185154275002?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4637934185154275002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4637934185154275002' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4637934185154275002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4637934185154275002'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/06/how-not-to-create-rails-plugin.html' title='How Not To Create Rails Plugin'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8688081678617755583</id><published>2009-06-18T10:13:00.003+03:00</published><updated>2009-06-18T10:19:56.158+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS The First Public Version</title><content type='html'>After months and months of hesitations and struggling against my own perfectionistic habits I've eventually released the first version of my javascript library called RightJS.&lt;br /&gt;&lt;br /&gt;Basically it's the fastest and compactest JavaScript framework at the day. And the coolest one too. Sure.&lt;br /&gt;&lt;br /&gt;Well... Whatever. I wish the thing a long happy live and lots of ass-kicking experience.&lt;br /&gt;&lt;br /&gt;Amen.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://rightjs.org" target="_blank"&gt;http://rightjs.org&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8688081678617755583?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8688081678617755583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8688081678617755583' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8688081678617755583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8688081678617755583'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/06/rightjs-first-public-version.html' title='RightJS The First Public Version'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4411182684901712264</id><published>2009-06-12T16:37:00.007+03:00</published><updated>2009-06-12T16:58:38.599+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>Dynamic IFrames As Form Targets</title><content type='html'>Sometimes you need to submit a form through some hidden iframe, usually when you're trying to monkey an ajax request for a file uploading.&lt;br /&gt;&lt;br /&gt;Usually you just render an iframe element along with the form in the html code and assign the target attribute of the form. Something like this&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;form target="my-iframe"&amp;gt;.....&amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;iframe name="my-iframe"&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;It works, but in cases when you try do the same thing dynamically with javascript. Say to wire some additional fancy callbacks or something. Say like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var iframe = document.createElement('iframe');&lt;br /&gt;document.body.appendChild(iframe);&lt;br /&gt;iframe.id = iframe.name = 'my-iframe';&lt;br /&gt;window['my-iframe'].onload = function() {...};&lt;br /&gt;&lt;br /&gt;var form = document.getElementById('my-form');&lt;br /&gt;form.target = iframe.id;&lt;br /&gt;form.submit();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This thing looks generally correct, but unfortunately won't work.&lt;br /&gt;&lt;br /&gt;I'm not sure why, but I do know the way how to deal with it.&lt;br /&gt;&lt;br /&gt;The trick is simple, dirty and cross-browsing. Just how we like it.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;var div = document.createElement('div');&lt;br /&gt;document.body.appendChild(div);&lt;br /&gt;div.innerHTML = '&amp;lt;iframe id="my-iframe" name="my-iframe"&amp;gt;&amp;lt;/iframe&amp;gt;';&lt;br /&gt;var iframe = document.getElementById('my-iframe');&lt;br /&gt;iframe.addEventListener('load', function() {....}, false);&lt;br /&gt;// or for IE&lt;br /&gt;iframe.attachEvent('onload', function() {....});&lt;br /&gt;&lt;br /&gt;var form = document.getElementById('my-form');&lt;br /&gt;form.target = iframe.id;&lt;br /&gt;form.submit();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This thing is perfectly works in all the modern browsers and old IEs.&lt;br /&gt;&lt;br /&gt;And for the end, there is even better way. Try the new &lt;a href="http://rightjs.org" target="_blank"&gt;RightJS&lt;/a&gt; framework. Then the same thing will look like this.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;$('my-form').send();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is pretty much it. Have a good one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4411182684901712264?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4411182684901712264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4411182684901712264' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4411182684901712264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4411182684901712264'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/06/dynamic-iframes-as-form-targets.html' title='Dynamic IFrames As Form Targets'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7115540609840706543</id><published>2009-06-09T12:21:00.002+03:00</published><updated>2009-06-09T12:24:44.574+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Filenames Encoding Conversion Under Linux</title><content type='html'>If you need to mass-convert filenames from one encoding to another, say you are migrating from one distro to another and need to move the legacy data, you can do that with the &lt;tt&gt;convmv&lt;/tt&gt; util.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# apt-get install convmv&lt;br /&gt;# convmv -f cp1251 -t utf8 -r --notest /dir/name&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7115540609840706543?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7115540609840706543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7115540609840706543' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7115540609840706543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7115540609840706543'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/06/filenames-encoding-conversion-under.html' title='Filenames Encoding Conversion Under Linux'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4498169650710809660</id><published>2009-06-09T12:12:00.004+03:00</published><updated>2009-06-09T12:18:15.183+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Integers To Binary Or Hex Conversion In Ruby</title><content type='html'>There are some tips on how you convert integers to binary or hex numbers forth and backwards.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;irb(main):019:0&gt; 30.to_s(2)&lt;br /&gt;=&gt; "11110"&lt;br /&gt;irb(main):020:0&gt; 30.to_s(10)&lt;br /&gt;=&gt; "30"&lt;br /&gt;irb(main):021:0&gt; 30.to_s(16)&lt;br /&gt;=&gt; "1e"&lt;br /&gt;&lt;br /&gt;irb(main):027:0&gt; '11110'.to_i(2)&lt;br /&gt;=&gt; 30&lt;br /&gt;irb(main):028:0&gt; '30'.to_i(10)&lt;br /&gt;=&gt; 30&lt;br /&gt;irb(main):029:0&gt; '1e'.to_i(16)&lt;br /&gt;=&gt; 30&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4498169650710809660?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4498169650710809660/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4498169650710809660' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4498169650710809660'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4498169650710809660'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/06/integers-to-binary-and-hex-conversion.html' title='Integers To Binary Or Hex Conversion In Ruby'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5540088479987021037</id><published>2009-06-02T16:52:00.002+03:00</published><updated>2009-06-02T16:54:53.858+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS Visual Effects</title><content type='html'>Continuing having fun with RightJS. I've just published some basic visual effects library on the official site and created some simple playground where you can try this and that. Check it out, there are some fancy stuff.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://rightjs.org/fx-demo" target="_blank"&gt;http://rightjs.org/fx-demo&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5540088479987021037?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5540088479987021037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5540088479987021037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5540088479987021037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5540088479987021037'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/06/rightjs-visual-effects.html' title='RightJS Visual Effects'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2081786348851896221</id><published>2009-05-31T21:11:00.002+03:00</published><updated>2009-05-31T21:15:37.291+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS Updates</title><content type='html'>There was some updates on the &lt;a href="http://rightjs.org" target="_blank"&gt;RightJS&lt;/a&gt; project. It moved to it's own github account, IE6 benchmarks were added and I'm working on some basic visual effects library for the framework. It does lots of fancy stuff and weight just 5k extra. Kinda sweet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2081786348851896221?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2081786348851896221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2081786348851896221' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2081786348851896221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2081786348851896221'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/05/rightjs-updates.html' title='RightJS Updates'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4527523230048702417</id><published>2009-05-26T08:22:00.002+03:00</published><updated>2009-05-26T08:29:21.877+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>RightJS Kicks The Butts</title><content type='html'>RightJS, on the start was real fast on the WebKit browsers and just above average on other browsers. The difference is that on WebKit browsers it was using the native css-selectors. But after an optimization session on the RightJS manual css-selectors, now we can proudly say, that RightJS kicks all the other frameworks on almost any browser and any conditions.&lt;br /&gt;&lt;br /&gt;Check it out on the page &lt;a href="http://rightjs.org/benchmarks" target="_blank"&gt;RightJS Benchmarks&lt;/a&gt;, there are tests for all the modern browsers, including IE and Opera. There's no test results for IE6 yet, because it has some memory leaking issues which were not fixed yet and slows the browser down. But rest of the results looks cool&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4527523230048702417?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4527523230048702417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4527523230048702417' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4527523230048702417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4527523230048702417'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/05/rightjs-kicks-butts.html' title='RightJS Kicks The Butts'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-1358950869367923604</id><published>2009-05-20T10:18:00.002+03:00</published><updated>2009-05-20T10:20:28.324+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>Meet RightJS</title><content type='html'>I would not consider it like an official start, but say we are rolling out and so I started the official site for my RightJS prject. Right here.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://rightjs.org" target="_blank"&gt;http://rightjs.org&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-1358950869367923604?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/1358950869367923604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=1358950869367923604' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1358950869367923604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/1358950869367923604'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/05/meet-rightjs.html' title='Meet RightJS'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5574405951010360121</id><published>2009-05-14T04:27:00.003+03:00</published><updated>2009-05-14T04:29:16.794+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Being Pragmatic about Agility</title><content type='html'>There is a book appeared recently called "Pragmatic Career" and there was a sentence in its annotation saying "when I started to look at my life throw the glasses of agility I started to see agile patterns just everywhere". Ok, that probably not the exact quote but the point is that some people getting the agility too far. They start to worship it almost as a religion and sure see its deeds just everywhere.&lt;br /&gt;&lt;br /&gt;Don't get me wrong I'm not against agility and this is not another crapy article a la "agility sucks, I hate it". Nope, agile approach is a wonderful tool and I use it every day and pretty much happy with that. My point is that the agile approach is just a tool, you should use it wisely and be critical about it.&lt;br /&gt;&lt;br /&gt;Agile approach works pretty well if you are an experienced developer, a ninja who knows how to give a good punch from almost any position. You don't plan too much, be flexible and if the things go a wrong way, you just adjust. It reduces the entry level, gives you ability to try this and that and see how is it going, etc., etc. You take all the advantages of the technique.&lt;br /&gt;&lt;br /&gt;But when you are young, have a lack of experience and good habits, it might lead you to a wrong path. Because of it nature, people quite often start practicing the "shoot first think later" approach. They write crap and hope they will fix it later, don't write tests and documentation. They think that they are agile because they produce a lot of code and kinda buggy features which they gonna make better later. But in reality they go in circles over and over, and eventually spent an ugly amount of time.&lt;br /&gt;&lt;br /&gt;Agile approach is not about creating crap now and make it better later, it's not about skip all the things you are too lazy to do or just don't know how to do. As the matter of fact, the original "Pragmatic Programmers" book in one of its tenants says directly opposite "Don't live with broken windows". More of that, any normal professional in any area will tell you the main rule of being effective "Do the things once and only once". If you need to get back and fix your solutions, you are just killing time.&lt;br /&gt;&lt;br /&gt;Agility is all about flexibility and rapidness. It is a contradiction to the old fashioned heavy planning approach, a tool which lets avoid painful situations when you at the finish line and realize that you have not what you needed. With agile approach you do small steps, but it doesn't cancel the idea that you should do your steps right. You do small steps but each step should be done in a good way. If you rush to the end target, skipping steps and taking all the possible shortcuts, move by an unpredictable trajectory, at the end you will have something as much painful as if you was sticking to some wrong plan.&lt;br /&gt;&lt;br /&gt;And the problem actually, the problem is in ourselves, it is in our brains, in the way it works. The mother nature designs the things the way they were working in the most effective way, consuming as less energy as possible, that's the matter of surviving, and our brains are not an exception. Instead of careful thinking, we tend to jump to conclusions based on our experience, on the things we believe. That's kind of solutions caching mechanism. And the agile approach fits it like a glove, you just make rapid decisions based on current situation and your guts and move forward.&lt;br /&gt;&lt;br /&gt;The problem is that if you have right solutions in your cache/experience, you are good, but if you don't you'll go through all the problems and become one big pain in the butt, for yourself, for your colleagues and eventually for your customer.&lt;br /&gt;&lt;br /&gt;And here we come to the sake of "evil" old planning.&lt;br /&gt;&lt;br /&gt;Planning is getting evil almost the same way as anything else getting evil, including agile approach. When you overuse it. The sake of planning is that it lets you make the reality check, think critically, review your experience cache. You write some simple roadmap and ask yourself a question "How do I know what I know?". It lets you filter out problematic cases, make you aware about problems before you meet them. That's the way to make your brain actually work, not just return some first thing out of the cache.&lt;br /&gt;&lt;br /&gt;The bottom line.&lt;br /&gt;&lt;br /&gt;I think you've got the idea. Sometimes you should ask yourself what are you actually practicing when you are practicing an agile development? Don't you feel like going circles, getting back and fix the things all the time? How do you know about the decisions you made? Don't you call something useless just because you too lazy or just don't really know how to use it? If you have any hesitations, it's okay to stop, sit down and think about the things for 20 minutes, do some reality check, maybe even take a good old pencil and piece of paper and write down some short plan.&lt;br /&gt;&lt;br /&gt;This will make you actually think about the things, which will make you better, and better you will produce a better work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5574405951010360121?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5574405951010360121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5574405951010360121' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5574405951010360121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5574405951010360121'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/05/being-pragmatic-about-agility.html' title='Being Pragmatic about Agility'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3446590323807267533</id><published>2009-05-11T11:47:00.002+03:00</published><updated>2009-05-11T12:05:51.574+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>Who's Your Daddy?</title><content type='html'>There's a pet project of mine called "RightJS". Yup as you can see it from the name it's yet another javascript framework. Well kinda. At the moment it provides most of the useful features of Prototype, salted with some features from dojo and spiced with features from jQuery and MooTools. It weights just 28k and wether you know passes the taskspeed test just like that.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href="http://stcamp.net/images/rightjs-benchmark.gif" target="_blank"&gt;&lt;img src="http://stcamp.net/images/rightjs-benchmark-thmb.gif"/&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;There's the test source http://github.com/MadRabbit/taskspeed .&lt;br /&gt;&lt;br /&gt;Okay, okay. Don't hold your breath. It's just in WebKit. In FF it's faster than Prototype at about 20% and still slightly faster than jQuery. In Opera it about same speed as jQuery and a little bit slower than Prototype. In IE... Well... Who uses that obsolete browser anyway?&lt;br /&gt;&lt;br /&gt;PS: I would not believe tests result for jQuery on the benchmark, case when Prototype and RightJS were honestly loaded with elements building, updating etc. But as jQuery does not have this functionality, tests for it just feed the results with strings of already created html. Kinda cheating, you know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3446590323807267533?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3446590323807267533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3446590323807267533' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3446590323807267533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3446590323807267533'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/05/whos-your-daddy.html' title='Who&apos;s Your Daddy?'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3858791202024147108</id><published>2009-04-27T16:59:00.005+03:00</published><updated>2009-04-30T03:21:57.182+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTML/CSS'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>One Image Round Corners</title><content type='html'>I'd like to share with you some fancy idea how you make easily maintainable css-based round corners on your pages. It's quite a common feature those days and if you don't know how to do that it might be quite a headache.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Idea&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The idea is pretty simple. You create an image which contains all four of your box corners. Usually it's just a small circle or a ring. Something like that.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://stcamp.net/examples/round_corners/images/explanations.gif"/&gt;&lt;br /&gt;&lt;br /&gt;Then you create an html-tags structure, like this.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;div class="box"&gt;&lt;br /&gt;  &amp;lt;div class="corners top"&gt;&amp;lt;div class="left"&gt;&amp;lt;/div&gt;&amp;lt;div class="right"&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&lt;br /&gt;  &lt;br /&gt;  &amp;lt;div class="data"&gt;&lt;br /&gt;    Your text goes here&lt;br /&gt;  &amp;lt;/div&gt;&lt;br /&gt;  &lt;br /&gt;  &amp;lt;div class="corners bottom"&gt;&amp;lt;div class="left"&gt;&amp;lt;/div&gt;&amp;lt;div class="right"&gt;&amp;lt;/div&gt;&amp;lt;/div&gt;&lt;br /&gt;&amp;lt;/div&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;It is not necessary to have the middle div.data, but it will provide you move flexibility for css-alterations in future, so you'd better have it in there.&lt;br /&gt;&lt;br /&gt;And eventually you write some piece of css-code.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;div.box {&lt;br /&gt;  padding: 8px;&lt;br /&gt;}&lt;br /&gt;div.box div.corners div {&lt;br /&gt;  height: 8px;&lt;br /&gt;  width: 8px;&lt;br /&gt;  position: relative;&lt;br /&gt;  float: left;&lt;br /&gt;  margin-top: -8px;&lt;br /&gt;  margin-left: -8px;&lt;br /&gt;  margin-right: -8px;&lt;br /&gt;  background: url('corners_image.gif') no-repeat 0 0;&lt;br /&gt;}&lt;br /&gt;div.box div.bottom div { margin-top: 0; }&lt;br /&gt;div.box div.corners div.right {&lt;br /&gt;  float: right;&lt;br /&gt;  background-position: 100% 100%;&lt;br /&gt;}&lt;br /&gt;div.box div.top div.right     { background-position: 100% 0; }&lt;br /&gt;div.box div.bottom div.left   { background-position: 0 100%; }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The stylesheet uses the images shading technique and displays a quoter of the image for each corner, so the pixel sizes in style definition should be equal to a quoter of your corners image. And the box by itself has some nice padding of the same size, so the box was looking visually correct and fit the corners.&lt;br /&gt;&lt;br /&gt;This is pretty much it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Why Do It Like That?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Well. There are several reasons.&lt;br /&gt;&lt;br /&gt;a) It gets down the number of server requests to the server. For such tiny images, the actual loading time is the time of request and response to the server. So making it one image instead of four you make your page loading faster and don't bother your server.&lt;br /&gt;&lt;br /&gt;b) When you have one image, you don't need to deal with each corner separately, you can unify and compact the styles, and then if you need, you can replace all the corners really easy but replacing just one image. It provides you really good flexibility in terms of css-based design. Take a look in here &lt;a href="http://stcamp.net/examples/round_corners/" target="_blank"&gt;One Image Corners Example&lt;/a&gt;, I've created some demo for you. See how it's easy alterable for various cases. You just change the background color and image url and have another box ready to go.&lt;br /&gt;&lt;br /&gt;c) With such approach in most cases your boxes will be still looking consistent if there is some problem with the image loading. User doesn't see unprofessionally looking cases when you have four separated images and one of them was lost or just when they are getting loading one by one. With one image the user will have all the corners at once, and if the image was lost, he will still see appropriate looking box on the page.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;PS:&lt;/b&gt; If you need transparent backgrounds at the outer angles of the corners, you can have them on the same exact tags structure, but you will need some additional mambo jumbo in your stylesheet, which I didn't intend to cover in the article. But in 99.9% of good modern designs you won't need that anyway.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;PPS:&lt;/b&gt; This certain css example does not work correctly on IE6, although it is entirely possible to make it working there, just I don't want to spend my free time on that zombie. If you need to support it you just put some additional adjustments into you css file with IE patches.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3858791202024147108?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3858791202024147108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3858791202024147108' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3858791202024147108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3858791202024147108'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/04/one-image-round-corners.html' title='One Image Round Corners'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5287956995088515923</id><published>2009-04-05T06:47:00.002+03:00</published><updated>2009-04-05T07:00:12.298+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>FrontCompiler Updates</title><content type='html'>Finally I've found some time and implemented a new feature for the &lt;a href="http://github.com/MadRabbit/frontcompiler" target="_blank"&gt;FrontCompiler&lt;/a&gt; project.&lt;br /&gt;&lt;br /&gt;This feature compresses your javascript code by using the tokens dictionary mechanism and then create another javascript which will recompose your original javascript code on the users browser.&lt;br /&gt;&lt;br /&gt;Reconstruction happens pretty quick, almost unnoticeable. And this gives you extra 20-40% of compression over the usual front-compiler compression, which as we all know is already better than the YUI compressor's. &lt;br /&gt;&lt;br /&gt;Say the latest Prototype v1.6.0.3 was compressed from 138 kB to 59 kB. MooTools was compressed from 98k to 50k, etc.&lt;br /&gt;&lt;br /&gt;You can grab the latest, already compressed files here &lt;a href="http://stcamp.net/share/prototype/" target="_blank"&gt;http://stcamp.net/share/prototype/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Try it, it's just awesome!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5287956995088515923?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5287956995088515923/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5287956995088515923' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5287956995088515923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5287956995088515923'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/04/frontcompiler-updates.html' title='FrontCompiler Updates'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2725962458828047035</id><published>2009-03-06T04:06:00.003+02:00</published><updated>2009-03-06T04:12:09.950+02:00</updated><title type='text'>MySql CaseSensitive Select Trick</title><content type='html'>By the default, mysql compares the strings case insensitively&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;INSERT INTO users (email) VALUES ('some@email.com');&lt;br /&gt;INSERT INTO users (email) VALUES ('SoMe@EmAiL.cOm');&lt;br /&gt;&lt;br /&gt;--- The following two selects both will return two records&lt;br /&gt;&lt;br /&gt;SELECT * FROM users WHERE email = 'some@email.com';&lt;br /&gt;SELECT * FROM users WHERE email = 'SoMe@EmAiL.cOm';&lt;br /&gt;&lt;br /&gt;--- Even LIKE select will return the same two records&lt;br /&gt;&lt;br /&gt;SELECT * FROM users WHERE email LIKE 'SoMe@EmAiL.cOm';&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But there's a trick how you can run a case sensitive select on mysql, you should use the binary comparison for the strings like this.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;SELECT * FROM users WHERE email LIKE BINARY 'some@email.com';&lt;br /&gt;SELECT * FROM users WHERE email LIKE BINARY 'SoMe@EmAiL.cOm';&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The first select will return the first record only, the second will return the second one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2725962458828047035?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2725962458828047035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2725962458828047035' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2725962458828047035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2725962458828047035'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2009/03/mysql-casesensitive-select-trick.html' title='MySql CaseSensitive Select Trick'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8015561636298008163</id><published>2008-11-22T15:45:00.003+02:00</published><updated>2008-11-22T15:56:22.501+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Mommy Look, new Plugin!</title><content type='html'>I've launched new simple plugin for Rails/ActiveRecord.&lt;br /&gt;&lt;br /&gt;This one extends the belongs_to associations between models and allows you to assign related units by one (or several) fields for the model. Say you have got a message which belongs to a user, and you would like to assign the user to the message by his login name. That's our case. Use the plugin and everything will be fine! 8)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://github.com/MadRabbit/assign_by/tree/master" target="_blank"&gt;http://github.com/MadRabbit/assign_by/tree/master&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8015561636298008163?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8015561636298008163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8015561636298008163' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8015561636298008163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8015561636298008163'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/11/mommy-look-new-plugin.html' title='Mommy Look, new Plugin!'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8683785579997569392</id><published>2008-11-06T00:50:00.002+02:00</published><updated>2008-11-06T00:53:43.351+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>SearchableBy, the ActiveRecord plugin</title><content type='html'>I've created some simple plugin for Rails/ActiveRecord, which you can use for search methods definitions.&lt;br /&gt;&lt;br /&gt;It is a simple stuff which just creates some named_scopes which you case use as various search methods.&lt;br /&gt;&lt;br /&gt;Take a look at the README file at the repository &lt;a href="http://github.com/MadRabbit/searchable_by" target="_blank"&gt;http://github.com/MadRabbit/searchable_by&lt;/a&gt; there are some examples.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8683785579997569392?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8683785579997569392/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8683785579997569392' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8683785579997569392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8683785579997569392'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/11/searchableby-activerecord-plugin.html' title='SearchableBy, the ActiveRecord plugin'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-6425596992733752533</id><published>2008-10-10T17:21:00.012+03:00</published><updated>2010-08-12T07:30:35.894+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Writting own Form Helper for Rails</title><content type='html'>The plot of the story is following. Say I've got some simple models, like&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&lt;br /&gt;Country&lt;br /&gt; - name&lt;br /&gt;&lt;br /&gt;City&lt;br /&gt; - name&lt;br /&gt; - country&lt;br /&gt;&lt;br /&gt;Customer&lt;br /&gt; - city&lt;/pre&gt;&lt;br /&gt;A city belongs to a country and a customer belongs to a city. And then on the customer form I would like to have a select box of cities with selection groups by country.&lt;br /&gt;&lt;br /&gt;Well, that's not a problem, we can do that.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&lt;br /&gt;&amp;lt;%= select_tag "user[city_id]",&lt;br /&gt;      option_groups_from_collection_for_select(&lt;br /&gt;        Country.all, :cities, :name, :id, :name, @user.city_id&lt;br /&gt;      ), :id =&amp;gt; "user_city_id" %&amp;gt;&lt;/pre&gt;&lt;br /&gt;Not pretty but works. But then we add another unit&lt;br /&gt;&lt;pre class="brush: rails"&gt;&lt;br /&gt;Office&lt;br /&gt; - name&lt;br /&gt; - city&lt;br /&gt;&lt;br /&gt;Customer&lt;br /&gt; - office&lt;/pre&gt;&lt;br /&gt;Now each customer belongs to an office, and we would like to write another drop-down list with groups on the customer's form.&lt;br /&gt;&lt;br /&gt;We have already got some not-pretty stuff on the form. So, what you gonna do about that?&lt;br /&gt;&lt;br /&gt;Obviously we should wrap this puppy up.&lt;br /&gt;How?&lt;br /&gt;Well, that's the question.&lt;br /&gt;&lt;br /&gt;Certainly we can write yet another method in our helpers, and be happy, but I would like to be not just happy, but feel myself kinda sexy as well. So, I would like to write it in such a way that it was look like a standard form-element. Like that.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&lt;br /&gt;&amp;lt;%= f.grouped_collection_select :city_id, Country.all, :cities %&amp;gt;&lt;br /&gt;&amp;lt;%= f.grouped_collection_select :office_id, Countries.all, :offices %&amp;gt;&lt;/pre&gt;&lt;br /&gt;How can we achieve such a beauty?&lt;br /&gt;Simple. &lt;br /&gt;You just need to know some simple things.&lt;br /&gt;&lt;br /&gt;First of all, you should know about the existence of the class ActionView::Helpers::FormBuilder. This class is the class which instance you use in the forms building with rails. And that's the class we are going to extend.&lt;br /&gt;&lt;br /&gt;The second thing you should know is the existence of the instance variables of the FormBuilder class, which you can use when you are building your own methods&lt;br /&gt;&lt;br /&gt;&lt;code&gt;@template&lt;/code&gt; - this is a reference to the current template processing object. Each helper method you call in your templates can be called as the variable method inside the FormBuilder class.&lt;br /&gt;&lt;code&gt;@object&lt;/code&gt; - a reference to the current model instance which the form covers.&lt;br /&gt;&lt;code&gt;@object_name&lt;/code&gt; - the name of the model variable you use to build your form.&lt;br /&gt;&lt;br /&gt;The third thing you should know is how to extend classes and modules on fly. Say we create a special separated helper for our new form elements.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: rails"&gt;&lt;br /&gt;module FormHelper&lt;br /&gt;  def self.included(base)&lt;br /&gt;    ActionView::Helpers::FormBuilder.instance_eval do &lt;br /&gt;      include FormBuilderMethods&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  module FormBuilderMethods&lt;br /&gt;    def grouped_collection_select(method, collection, sub_name,&lt;br /&gt;            group_method="name", unit_id="id", unit_name="name")&lt;br /&gt;      @template.select_tag "#{@object_name}[#{method}]",&lt;br /&gt;         @template.option_groups_from_collection_for_select(&lt;br /&gt;            collection, sub_name, group_method, unit_id,&lt;br /&gt;            unit_name, @object.send(method)&lt;br /&gt;         ), :id =&amp;gt; "#{@object_name}_#{method}"&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;We define some module of ours, where new form-methods will be defined and then we include the module inside the FormBuilder class when our helper will get included to the system. And I've added some default values to the method, case my models all have uniform "name" method, so I didn't need to repeat that things over and over.&lt;br /&gt;&lt;br /&gt;And voila, this is it. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-6425596992733752533?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/6425596992733752533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=6425596992733752533' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6425596992733752533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/6425596992733752533'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/10/writting-own-form-helper-for-rails.html' title='Writting own Form Helper for Rails'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7984525475490743850</id><published>2008-09-21T21:08:00.005+03:00</published><updated>2008-09-21T21:26:52.981+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='HTML/CSS'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Nested Styles with Ruby and FrontCompiler</title><content type='html'>Nested styles is a feature which some people dream about for quite a long time. The idea is pretty simple and beautiful.&lt;br /&gt;&lt;br /&gt;Say you have got some css like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;div.article div.title {&lt;br /&gt;  font-weight: bold;&lt;br /&gt;}&lt;br /&gt;div.article div.title a.author {&lt;br /&gt;  padding-left: 12px;&lt;br /&gt;  background: url('user.png') no-repeat left;&lt;br /&gt;}&lt;br /&gt;div.article div.text {&lt;br /&gt;  border: 1px solid #EEE;&lt;br /&gt;  background: #FEF;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And now imagine that you could write it with nested constructions like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;div.article {&lt;br /&gt;  div.title {&lt;br /&gt;    font-weight: bold;&lt;br /&gt;    &lt;br /&gt;    a.author {&lt;br /&gt;      padding-left: 12px;&lt;br /&gt;      background: url('user.png') no-repeat left;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  div.text {&lt;br /&gt;    border: 1px solid #EEE;&lt;br /&gt;    background: #FEF;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;See the difference? How much more simple, clean and DRYish your css could be! Unfortunately current browsers don't support such things. At lest not yet.&lt;br /&gt;&lt;br /&gt;So, does anybody want to sit down and wait for the future coming?&lt;br /&gt;&lt;br /&gt;No, say 'no', you don't need tell such terrible things, because we have added the feature emulation in the &lt;a href="http://github.com/MadRabbit/frontcompiler/tree/master" target="_blank"&gt;FrontCompiler&lt;/a&gt; project. Which is a JavaScript/CSS/HTML processor written in Ruby and which can be used as a Rails plugin.&lt;br /&gt;&lt;br /&gt;From now one, each time when you process a css file with FrontCompiler, it will automatically convert your nested styles in the way browsers can understand. So you can easily and safe use the nested styles advantages.&lt;br /&gt;&lt;br /&gt;Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7984525475490743850?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7984525475490743850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7984525475490743850' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7984525475490743850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7984525475490743850'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/09/nested-styles-with-ruby-and.html' title='Nested Styles with Ruby and FrontCompiler'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3903587739198451871</id><published>2008-09-15T20:51:00.002+03:00</published><updated>2008-09-15T21:02:15.019+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>FrontCompiler Update</title><content type='html'>I have updated the JavaScript part of the the presented recently &lt;a href="http://github.com/MadRabbit/frontcompiler/tree/master" target="_blank"&gt;FrontCompiler&lt;/a&gt;. I've improved the compression algorithm, got rid of some old bugs, rewritten the code in a more clear way and made it work several times faster.&lt;br /&gt;&lt;br /&gt;So now, it's better, faster, cooler, nicer, whatever. &lt;br /&gt;&lt;br /&gt;And I have compressed the Prototype / ScriptAculo stuff, with the new compressor and shared them &lt;a href="http://stcamp.net/share/prototype/" target="_blank"&gt;at here&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Try it, that's nice and free! 8)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3903587739198451871?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3903587739198451871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3903587739198451871' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3903587739198451871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3903587739198451871'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/09/frontcompiler-update.html' title='FrontCompiler Update'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7854202669440871198</id><published>2008-08-21T19:34:00.001+03:00</published><updated>2008-08-21T19:40:37.347+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><title type='text'>Javascript/CSS/HTML Compiler in Ruby</title><content type='html'>I have backed some pretty tasteful cookie in here!&lt;br /&gt;&lt;br /&gt;Met &lt;a href="http://github.com/MadRabbit/frontcompiler/tree/master" target="_blank"&gt;FrontCompiler&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;It's a collection of scripts which allows you to compact your JavasScript, CSS and HTML code to reduce the loading time, and it can be used with Rails/Merb/Whatever.&lt;br /&gt;&lt;br /&gt;NOTE: in test of compacting the Prototype library the script beats famous YUI compiler on about 1 KByte.&lt;br /&gt;&lt;br /&gt;Enjoy! 8)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7854202669440871198?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7854202669440871198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7854202669440871198' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7854202669440871198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7854202669440871198'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/08/javascriptcsshtml-compiler-in-ruby.html' title='Javascript/CSS/HTML Compiler in Ruby'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2839701961529399243</id><published>2008-08-06T10:08:00.003+03:00</published><updated>2008-08-06T10:17:15.483+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>The Mines Game in JavaScript</title><content type='html'>I have been playing a cool javascript hacker this weekend and as the result have implemented the well known game of mines in pure JavaScript/CSS/HTML. Take a look it's pretty nice.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://stcamp.net/jsminer/demo.html" target="_blank"&gt;JSMiner Game&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2839701961529399243?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2839701961529399243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2839701961529399243' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2839701961529399243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2839701961529399243'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/08/mines-game-in-javascript.html' title='The Mines Game in JavaScript'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3765861623419338353</id><published>2008-07-26T21:04:00.003+03:00</published><updated>2008-07-26T21:13:23.798+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Ubuntu HDTV fix</title><content type='html'>I had a problem with Ubuntu on my old notebook with Intel Mobile 915GM video-card. It didn't want to play hdtv files, rips and some .mkv files. It had been crashing with an insufficient resources error.&lt;br /&gt;&lt;br /&gt;The issue is simple, the video card uses the system memory for its own purposes, and ubuntu has wrong default xorg configuration for the videocard. Add the following three lines to your &lt;code&gt;/etc/X11/xorg.conf&lt;/code&gt; file into the &lt;code&gt;Section "Device"&lt;/code&gt; section (search for the &lt;code&gt;Driver "i810"&lt;/code&gt; line).&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Option  "VideoRam" "65536"&lt;br /&gt;Option  "CacheLines" "1980"&lt;br /&gt;Option  "LinearAlloc" "32768"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This will fix the problem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3765861623419338353?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3765861623419338353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3765861623419338353' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3765861623419338353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3765861623419338353'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/07/ubuntu-hdtv-fix.html' title='Ubuntu HDTV fix'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-2734065602845106494</id><published>2008-07-22T13:08:00.004+03:00</published><updated>2008-07-22T14:44:06.043+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>TestCase 2.0.2 is out</title><content type='html'>TestCase - the kickass javascript testing framework has got new release with version 2.0.2&lt;br /&gt;&lt;br /&gt;And I've launched &lt;a href="http://testcase.rubyforge.org" target="_blank"&gt;the official support resource for the project&lt;/a&gt;, take a look, there is lot of interesting and useful stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-2734065602845106494?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/2734065602845106494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=2734065602845106494' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2734065602845106494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/2734065602845106494'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/07/testcase-202-is-out.html' title='TestCase 2.0.2 is out'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5753516624045308581</id><published>2008-07-20T10:26:00.009+03:00</published><updated>2008-07-20T12:24:19.151+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Rails + Gettext Crash Course, Part 2</title><content type='html'>This is the second part of &lt;a href="http://st-on-it.blogspot.com/2008/07/rails-gettext-crashcourse.html" target="_blank"&gt;the first rails+gettext article&lt;/a&gt;. In this part we will take a look at some advanced points of using gettext and its bind to ruby/rails. Generally there is nothing "advanced", just say we move a little bit beyond elementary &lt;code&gt;_('some text')&lt;/code&gt; calls.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Interface&lt;/h4&gt;Except the &lt;code&gt;_(String)&lt;/code&gt; method, there are several more standard methods to define messages for gettext. Generally there are two parts of them, for singular and plural forms. The first ones take as the main argument your usual message string, the second ones two more arguments, singular form of the message, plural form of the message and an optional number which helps to determinate when it is a singular form and when it is a plural form.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Singular methods&lt;/h5&gt;&lt;ul style="margin:0;padding:0"&gt;&lt;li&gt;&lt;b&gt;&lt;code&gt;String _(String)&lt;/code&gt;&lt;/b&gt; - the standard method as we now it. The method takes a string and returns its translated version.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;&lt;code&gt;String s_(String message[, String div="|"])&lt;/code&gt;&lt;/b&gt; - this one for the cases when you need to define a scope for the message, say you have just the same message in two different context which might have different translation. So you write a string like &lt;code&gt;s_('context|phrase')&lt;/code&gt;, your message will be splat by the divider ('|' by default) and if there is no translation the last part of it will be returned. Simple example &lt;code&gt;s_('gender|sex')&lt;/code&gt; and &lt;code&gt;s_('activity|sex')&lt;/code&gt;, by default both of them will return 'sex', but you may have two different translations of the word in different languages depend on the context.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;&lt;code&gt;String N_(String)&lt;/code&gt;&lt;/b&gt; - this one might look a little bit strange. It tells gettext to define in its base the given string as a message, but does not translate anything and returns the given string as is. Generally it does just nothing, it is a dummy method and just a marker for the gettext parser. You will be given some examples of its usage below.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h5&gt;Plural methods&lt;/h5&gt;&lt;ul style="margin:0;padding:0"&gt;&lt;li&gt;&lt;b&gt;&lt;code&gt;n_(String msg, String msg_plural, Integer number)&lt;/code&gt;&lt;/b&gt; - this one is same as &lt;code&gt;_(String)&lt;/code&gt; but for the plural cases. The first argument is a singular version of the message the second is the plural version of the message. And the last argument the current lets call it plurality marker by which gettext will define which message use. &lt;b&gt;NOTE:&lt;/b&gt; despite that you pass here only two messages, when you translate your .po files you may define several (more than two) translations of the messages which will depend on the plurality marker.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;&lt;code&gt;ns_(String msg, String msg_plural, Integer number[, String div='|'])&lt;/code&gt;&lt;/b&gt; - same as &lt;code&gt;s_(String[, div])&lt;/code&gt; but for plural forms&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;b&gt;&lt;code&gt;Nn_(String msg, String msg_plural)&lt;/code&gt;&lt;/b&gt; - same as the &lt;code&gt;N_(String)&lt;/code&gt; method but for plural forms&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;That's all about the message definition methods.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Models Internationalization&lt;/h4&gt;GetText by default has all the standard error messages translations for most of the languages inside the package. And the parser will automatically define translations for your model fields which are in the database (meant field-names translation). So you don't need to worry about that.&lt;br /&gt;&lt;br /&gt;But sure you have got your special cases and you can follow the next instructions. Say the usual case, a user model where you have got some virtual fields&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class User &lt; ActiveRecord::Base&lt;br /&gt;  has_one :profile&lt;br /&gt;  has_many :comments&lt;br /&gt;&lt;br /&gt;  attr_accessor :password, :password_confirmation&lt;br /&gt;  &lt;br /&gt;  # we can define the virtual fields translation like that&lt;br /&gt;  N_("User|Profile")&lt;br /&gt;  N_("User|Comments")&lt;br /&gt;  N_("User|Password")&lt;br /&gt;  N_("User|Password confirmation")&lt;br /&gt;&lt;br /&gt;  # we can create custom error-messages in this way&lt;br /&gt;  validates_uniqueness_of :login, :message =&gt; N_('The login-name is already taken')&lt;br /&gt;&lt;br /&gt;  def validate&lt;br /&gt;    errors.add("password", _("%{fn} is wrong!")) unless password == password_confirmation&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;As you see we have used the method &lt;code&gt;N_(String)&lt;/code&gt; for class initialisation calls and the method &lt;code&gt;_(String)&lt;/code&gt; inside the custom validation method. The point is simple, in the first case you don't really need the translation result, you just need to define some messages and the second case works in runtime so you need an already translated version of the message.&lt;br /&gt;&lt;br /&gt;And another moment, we have used the 'User|' prefix in the field name definitions, this is to determinate in the translation which model the field name belongs, so you could have different translations of the field names for different models. By default it will be splat by '|' and the last token will be taken.&lt;br /&gt;&lt;br /&gt;If you don't want to translate some particular field names, you may define to skip them by calling the &lt;code&gt;untranslate(*names)&lt;/code&gt; method like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class User &lt; ActiveRecord::Base&lt;br /&gt;  untranslate :login, :email&lt;br /&gt;  # or&lt;br /&gt;  untranslate_all&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Templates Internationalization&lt;/h4&gt;In most cases of templates internationalization you just use the standard gettext methods, like &lt;code&gt;&amp;lt;%= _('Some label text') %&gt;&lt;/code&gt;, but there is another way.&lt;br /&gt;&lt;br /&gt;Sometime templates contain lots of text with big descriptions, so it is more comfortable to have another template file special for a particular language rather than put big pieces of text in the &lt;code&gt;_(String)&lt;/code&gt; function. It is simple, just create another file next to existing one but with a name which have a suffix named after the locale you want. For example&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;app/views/pages/&lt;br /&gt;  about.html.erb&lt;br /&gt;  about_de_DE.html.erb&lt;br /&gt;  about_ru.html.erb&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;If the current locale is 'ru', then the &lt;code&gt;about_ru.html.erb&lt;/code&gt; file will be used, if there is no matching file, the default &lt;code&gt;about.html.erb&lt;/code&gt; file will be used.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Language Switching Tactics&lt;/h4&gt;And at the end some points about the language switching tactic. If you are experienced developer you probably do not need this part, but I would like to spell some words about it.&lt;br /&gt;&lt;br /&gt;The best way here I think is to follow the general ideology, separate parameter variables and the urls formatting, just as you do with say same ID parameter. A good solution would be define a parameter name which will point to the desired language, say &lt;code&gt;params[:lang]&lt;/code&gt;, then as I showed in the previous article, you can use it in your application controller to switch the locale by a before filter.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class ApplicationController &lt; ActionController::Base&lt;br /&gt;  init_gettext 'my_site'&lt;br /&gt;&lt;br /&gt;  before_filter :define_language&lt;br /&gt;&lt;br /&gt;  def define_language&lt;br /&gt;    case params[:lang]&lt;br /&gt;      when 'fr': set_locale 'fr_FR'&lt;br /&gt;      when 'ru': set_locale 'ru_RU'&lt;br /&gt;      else       set_locale 'en_EN'&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;and then you left some options for youself how present it at your urls. You may use a simplest way like and left the gings as is&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/articles/show/1             &lt;- English&lt;br /&gt;/articles/show/1?lang=fr     &lt;- French&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;br /&gt;or you can handle the language by routes so your pages were more caching friendly&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;map.connect ':controller/:action/:id'&lt;br /&gt;  &lt;br /&gt;map.connect ':lang/:controller/:action/:id',&lt;br /&gt;  :requirements =&gt; {&lt;br /&gt;    :lang =&gt; /(ru|de|fr)/&lt;br /&gt;  }, :lang =&gt; nil&lt;br /&gt;&lt;br /&gt;----&lt;br /&gt;/articles/show/1     &lt;- English&lt;br /&gt;/fr/articles/show/1  &lt;- French&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;or later you may handle them by subdomains or something like that, whatever you choose your application code will not need changes.&lt;br /&gt;&lt;br /&gt;--&lt;br /&gt;&lt;br /&gt;Okay, think that is all what I had to say about using gettext with rails. Good luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5753516624045308581?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5753516624045308581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5753516624045308581' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5753516624045308581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5753516624045308581'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/07/rails-gettext-crash-course-part-2.html' title='Rails + Gettext Crash Course, Part 2'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-5515375417383195579</id><published>2008-07-18T18:16:00.011+03:00</published><updated>2008-07-20T12:26:58.986+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Rails + Gettext Crash Course, Part 1</title><content type='html'>This article is a short guide to how use gettext with ruby-on-rails. If you do not know gettext is the standard internationalisation (i18n) library of the GNU project &lt;a href="http://en.wikipedia.org/wiki/Gettext" target="_blank"&gt;http://en.wikipedia.org/wiki/Gettext&lt;/a&gt;. And this is the first part of the two articles. The second one &lt;a href="http://st-on-it.blogspot.com/2008/07/rails-gettext-crash-course-part-2.html" target="_blank"&gt;might be found here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Since there are some of i18n plugins for rails why should you chose gettext?&lt;br /&gt;&lt;br /&gt;For some reasons.&lt;br /&gt;&lt;br /&gt;First as I said it is the standard for GNU. Standard means wide support several tools to work with it, optimisation, documentation, etc. Most of Linux programmes use gettext.&lt;br /&gt;&lt;br /&gt;Second, one of the key features of gettext is ability to use real, correct strings. You don't write strange keys in your code, you just put usual English phrases instead, like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;p&gt;&amp;lt;%= _('Some text here') %&gt;&amp;lt;/p&gt;&lt;br /&gt;&amp;lt;p&gt;&amp;lt;%= _('Length: &amp;lt;i&gt;(%{min} min)&amp;lt/i&gt;') % { :min =&gt; @min_chars } %&gt;&amp;lt;/p&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;As you see this allows you easily use any placeholders or html formatting tags in here if you need so. Another point for gettext is that you don't have to keep in mind the translation issues during the development time. You just put your text as nothing happened and always have a default translation with normal usual looking strings.&lt;br /&gt;&lt;br /&gt;After you have done with the development you can parse out all the strings which need to be translated with special util. You will have standard formed .po file which you can then translate with some special programs, where you can use spellchecking versioning and all the civilize life advantages.&lt;br /&gt;&lt;br /&gt;Third, this stuff can do lots of things, localize numbers, dates, currencies, work with pluralization on native languages and so one. Take a look at the official site for getting more information &lt;a href="http://www.gnu.org/software/gettext/gettext.html" target="_blank"&gt;http://www.gnu.org/software/gettext/gettext.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So how do we use the stuff with rails?&lt;br /&gt;&lt;br /&gt;Quite simple.&lt;br /&gt;&lt;br /&gt;First of all you need to install gettext by itself and rubysupport aka gem. Installation of gettext depends on your platform. With linux it's pretty simple. Say something like&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# emerge gettext&lt;br /&gt;or&lt;br /&gt;# apt-get install gettext&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Once you have installed gettext support, you need to install the gettext gem.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# gem install gettext&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After it is done, you need to include it into &lt;code&gt;config/environment.rb&lt;/code&gt; file of your rails project.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;require 'gettext/rails'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After this you can start develop your project and use the &lt;code&gt;_('text')&lt;/code&gt; constructions where you need to put some text.&lt;br /&gt;&lt;br /&gt;If you are using Rails 2.1, there's a bug which have not fixed yet. But you can easily fix it by saving the following code into a file &lt;code&gt;config/initializers/gettext.rb&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;# the gettext missed method fix&lt;br /&gt;module ActionView&lt;br /&gt;  class Base&lt;br /&gt;    delegate :file_exists?, :to =&gt; :finder unless respond_to?(:file_exists?)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Once you have done with the development and want to create some translation you need to do some preparations.&lt;br /&gt;&lt;br /&gt;First you need to add two simple tasks to your Rakefile.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;require 'gettext/utils'&lt;br /&gt;desc "Update pot/po files to match new version."&lt;br /&gt;task :updatepo do&lt;br /&gt;  GetText.update_pofiles(&lt;br /&gt;    "my_site",&lt;br /&gt;    Dir.glob("{app,lib}/**/*.{rb,rhtml,erb,rjs}"),&lt;br /&gt;    "my_site version"&lt;br /&gt;  )&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;desc "Create mo-files for L10n"&lt;br /&gt;task :makemo do&lt;br /&gt;  GetText.create_mofiles(true, "po", "locale")&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The first one will scan though your source code and parse out all the strings which need to be translated and put them all into a .po file. The second one will compile all your translations into a standard .mo files which gettext can use.&lt;br /&gt;&lt;br /&gt;So, you say 'rake updatepo' in your console and this will create for you the file named 'po/my_site.pot', this is template of your internationalisation. You copy the file in translation directories like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;po/&lt;br /&gt;  de_DE/my_site.po&lt;br /&gt;  fr_FR/my_site.po&lt;br /&gt;  ru_RU/my_site.po&lt;br /&gt;  my_site.pot&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then you translate the .po files with one of the translation programmes, say this one &lt;a href="http://www.poedit.net"&gt;http://www.poedit.net&lt;/a&gt;. Under Linux and KDE you can use standard KBabel programme, or under Gnome you can use the gtrasnlator project. Certainly you can edit those files just as plain text if you like so. Many texteditors support them.&lt;br /&gt;&lt;br /&gt;Once you have done with the translation, run the 'rake makemo' task in your console and it will create the 'locale' directory in your rails project folder with a standard structure like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;locale/&lt;br /&gt;  de_DE/LC_MESSAGES/my_site.mo&lt;br /&gt;  fr_FR/LC_MESSAGES/my_site.mo&lt;br /&gt;  ru_RU/LC_MESSAGES/my_site.mo&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is almost it. You only need to initiate it in your application controller now.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class ApplicationController &lt; ActionController::Base&lt;br /&gt;  GetText.locale = 'en_EN' # &lt;- default locale&lt;br /&gt;  init_gettext 'my_site'&lt;br /&gt;&lt;br /&gt;  before_filter :define_language&lt;br /&gt;&lt;br /&gt;  def define_language&lt;br /&gt;    # here you can switch the locale on fly&lt;br /&gt;    # say like that&lt;br /&gt;&lt;br /&gt;    case params[:lang]&lt;br /&gt;      when 'de': set_locale 'de_DE'&lt;br /&gt;      when 'fr': set_locale 'fr_FR'&lt;br /&gt;      when 'ru': set_locale 'ru_RU'&lt;br /&gt;      else       set_locale 'en_EN'&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;As you see we have used non existing locale 'en_EN', if there is no some locale your default strings from the source code will be used. So you always have a fallback language.&lt;br /&gt;&lt;br /&gt;This is pretty much it for the start.&lt;br /&gt;&lt;br /&gt;But remember with gettext you not only put some translated text-labels in your application, you easily can create translations of whole template files, you can translate models, values, format numbers, dates and do lot of another useful things.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-5515375417383195579?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/5515375417383195579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=5515375417383195579' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5515375417383195579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/5515375417383195579'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/07/rails-gettext-crashcourse.html' title='Rails + Gettext Crash Course, Part 1'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-4009369903865610958</id><published>2008-07-03T09:38:00.002+03:00</published><updated>2008-07-03T09:43:10.665+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><title type='text'>TestCase 2.0 is out</title><content type='html'>After several month of development, finally I have released the tool version 2.0&lt;br /&gt;&lt;br /&gt;There is &lt;a href="http://rubyforge.org/frs/shownotes.php?release_id=23530"&gt;the release notes and short changelog&lt;/a&gt;&lt;br /&gt; &lt;br /&gt;&lt;a href="http://rubyforge.org/frs/?group_id=4826&amp;release_id=23530"&gt;Download page&lt;/a&gt;&lt;br /&gt; &lt;br /&gt;And now you can &lt;a href="http://testcase.rubyforge.org/demo/index.html"&gt;watch the project's demo&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-4009369903865610958?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/4009369903865610958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=4009369903865610958' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4009369903865610958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/4009369903865610958'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/07/testcase-20-is-out.html' title='TestCase 2.0 is out'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-269174438778491634</id><published>2008-06-28T10:43:00.004+03:00</published><updated>2008-07-18T18:16:27.222+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Making Quicktime work in Ubuntu</title><content type='html'>There is a common issue with quicktime videostreams under linux. So this is a simple trick how you can make it working under the Ubuntu linux.&lt;br /&gt;&lt;br /&gt;1) Uninstall all the video plugins you have got with your firefox installation (for example the vlc plugin)&lt;br /&gt;&lt;br /&gt;2) Install the mplayer package and the quicktime extentions for it.&lt;br /&gt;&lt;br /&gt;3) Install the mozilla-mplayer plugin&lt;br /&gt;&lt;br /&gt;4) Reinstall firefox.&lt;br /&gt;&lt;br /&gt;That's it. Go to apple.com/trailers, it should get working now&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-269174438778491634?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/269174438778491634/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=269174438778491634' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/269174438778491634'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/269174438778491634'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/06/making-quicktime-works-in-ubuntu.html' title='Making Quicktime work in Ubuntu'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8380015314114263884</id><published>2008-06-16T15:55:00.004+03:00</published><updated>2008-06-16T16:07:47.672+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><title type='text'>JavaScript Diff Function Implementation</title><content type='html'>Since I have not ever done it before, I have found it was not obvious to me how to create a lists diff function in JavaScript, so I feel like should public the solution.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;function diff(first, second) {&lt;br /&gt;  var diff = [], start_k = 0;&lt;br /&gt;    &lt;br /&gt;  // calculating the diff&lt;br /&gt;  for (var i=0; i &lt; first.length; i++) {&lt;br /&gt;    var same_found = false;&lt;br /&gt;    var pluses = [];&lt;br /&gt;    for (var k=start_k; k &lt; second.length; k++) {&lt;br /&gt;      if (first[i] == second[k]) {&lt;br /&gt;        same_found = second[k];&lt;br /&gt;        start_k = k+1;&lt;br /&gt;        break;&lt;br /&gt;      } else {&lt;br /&gt;        pluses.push(second[k]);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    if (same_found) {&lt;br /&gt;      for (var j=0; j &lt; pluses.length; j++) {&lt;br /&gt;        diff.push({plus: pluses[j]});&lt;br /&gt;      }&lt;br /&gt;      diff.push({same: same_found});&lt;br /&gt;    } else {&lt;br /&gt;      diff.push({minus: first[i]});&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // pushing down the trailing elements&lt;br /&gt;  for (var k=start_k; k &lt; second.length; k++) {&lt;br /&gt;    diff.push({plus: second[k]});&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return diff;&lt;br /&gt;};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;this function will return a sequention of hashes with keys 'minus', 'plus', 'same' which represents the differences and similarities between the two incomming arrays&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8380015314114263884?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8380015314114263884/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8380015314114263884' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8380015314114263884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8380015314114263884'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/06/javascript-diff-function-implementation.html' title='JavaScript Diff Function Implementation'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8828409140014782469</id><published>2008-05-14T10:55:00.005+03:00</published><updated>2008-05-14T11:20:06.419+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Blog'/><title type='text'>Tried KDE4 on Kubuntu</title><content type='html'>I've been testing Kubuntu with KDE4 today and have learnt two lessons.&lt;br /&gt;&lt;br /&gt;1) There's nothing to be eager about KDE4. I mean, seriously, the guys do a good job, but this is still the same good old kde3. Those plasma and dolphin stuffs, doesn't change much, that's like a good version of the superkaramba project, it looks nice but practically doesn't change the workflow at all. And as 99% of applications still from KDE3, if don't mention internal changes, in really you've got a new nice theme for kde.&lt;br /&gt;&lt;br /&gt;I'm little bit disappointed, the fourth version was anounced for good couple of years ago and then been delayed several times and as the result we've got something pretty raw and almost nothing new.&lt;br /&gt;&lt;br /&gt;Yup, that's it, there's nothing in really why you should migrate to kde4 so far.&lt;br /&gt;&lt;br /&gt;2) The quality of kubuntu is lower then the quality of ubuntu. Sorry gus, but that's true. There are several things which works out of box in ubuntu, but doesn't work in kubuntu. Several things are left behind in order to keep the kde-purity, that left a bad feeling.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8828409140014782469?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8828409140014782469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8828409140014782469' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8828409140014782469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8828409140014782469'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/05/tried-kde4-on-kubuntu.html' title='Tried KDE4 on Kubuntu'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-7343043002322350478</id><published>2008-05-05T10:53:00.007+03:00</published><updated>2008-05-05T11:44:17.958+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Articles'/><title type='text'>Order By Aggregations With ActiveRecord</title><content type='html'>The issue is following, say you have got a classical structure in two models&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class Order &amp;lt; ActiveRecord::Base&lt;br /&gt;  has_many order_items&lt;br /&gt;&lt;br /&gt;  def total_due&lt;br /&gt;    order_items.to_a.sum(&amp;:total_price)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class OrderItem &amp;lt; ActiveRecord::Base&lt;br /&gt;  belongs_to :order&lt;br /&gt;&lt;br /&gt;  # and it has two principal fields&lt;br /&gt;  # decimal :price&lt;br /&gt;  # integer :quantity&lt;br /&gt;&lt;br /&gt;  def total_price&lt;br /&gt;    price * quantity&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;As you see the orders don't have own total_due fields, instead of this each order can calculate the value depend on the aggregated items. So, that's cool, we follow all the good design habits and keep the things clean.&lt;br /&gt;&lt;br /&gt;But when you retrieve/display the orders list there might be a situation when a customer would like to order the list by the total_due value. Quite a natural desire, but there's the problem, we don't have the total_due field in really. It's virtual and depend on the aggregated objects.&lt;br /&gt;&lt;br /&gt;Yes, you may reorder the items after retrieving, manually, but if you have quite a lot of orders and use a pagination you'll get back to the problem anyway.&lt;br /&gt;&lt;br /&gt;The example is pretty simple, but you see, there might be lots of similar situations when you have some calculations on an aggregated data and would like to order your lists depends on the virtual fields.&lt;br /&gt;&lt;br /&gt;So, how do we handle that?&lt;br /&gt;&lt;br /&gt;We use the :select option for the ActiveRecord find method. Usually with the option you specify a number of fields you would like to extract, to not suck up heavy ones when you don't want them. But with the same option you may specify additional fields to extract and use all the power of SQL expressions. Those new fields will be added to the instanced object temporary, you can read them but you can't save them.&lt;br /&gt;&lt;br /&gt;In our case this might be something like that&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;:select =&gt; "SUM(order_items.price * order_items.quantity) as total_due"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;br /&gt;In the case we are using the SQL function SUM, calculate the total-due for each order, and then each selected order will obtain the total_due attribute after extraction and you may use it for your sorting options.&lt;br /&gt;&lt;br /&gt;Then, as you see we mentioned the 'order_items' column in the select option, to make it working we should add a tables join and the items grouping options. The overall instruction might look like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Order.find(:all,&lt;br /&gt;  :select =&gt; "SUM(order_items.price * order_items.quantity) as total_due, orders.*",&lt;br /&gt;  :joins =&gt; "INNER JOIN order_items ON order.id = order_items.order_id",&lt;br /&gt;  :group =&gt; "order.id",&lt;br /&gt;  :order =&gt; "total_due"&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;After this you will need to fix your Order model a little, to make it use the temporary field when it appears&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class Order &amp;lt; ActiveRecord::Base&lt;br /&gt;  def total_due&lt;br /&gt;    self[:total_due] || order_items.to_a.sub(&amp;:total_price)&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Yes, that looks a little bit sqlish dirty for a rails application, but then it works and does what you need.&lt;br /&gt;&lt;br /&gt;And there's another issue with the solution. You actually cannot use the :include option with the :select option at the same time. That's usually not a big issue, just note, if you specify the :include option, your :select option won't work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-7343043002322350478?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/7343043002322350478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=7343043002322350478' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7343043002322350478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/7343043002322350478'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/05/order-by-aggregations-with-activerecord.html' title='Order By Aggregations With ActiveRecord'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-8606131298890797643</id><published>2008-04-18T18:13:00.004+03:00</published><updated>2008-04-18T18:30:53.268+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby and Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Blog'/><title type='text'>I think stories need chapters</title><content type='html'>This &lt;a href="http://rspec.info" target="_blank"&gt;rspec&lt;/a&gt;'s stories stuff is quite nice, I like it. But when I'm trying to write more or less complex story it turns out to a nightmare. It's quite hard to stay on the DRY way with the stories, you always need to repeat the stuffs again and again. Even if you organize your code in good and handy steps, you'll have to repeat them in the scenarios anyway.&lt;br /&gt;&lt;br /&gt;So, I think it would be cool to have in the stories another level aka Chapter. Think even nested chapters. I could look like that.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Story "an item live-story" do &lt;br /&gt;  Chapter "items index" do &lt;br /&gt;    Given "two items"&lt;br /&gt;&lt;br /&gt;    Scenario "anonymous gets the index" do &lt;br /&gt;      Given "an anonymous user"&lt;br /&gt;      When "he GET /items"&lt;br /&gt;      Then "he should be redirected to the login_path"&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    Scenario "registered user gets the index" do &lt;br /&gt;      Given "a logged in user"&lt;br /&gt;      When "he GET /items"&lt;br /&gt;      Then "he should see the 'items/index' page"&lt;br /&gt;      And "both items should be on the page"&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    Then "something in common happens"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The idea is that the chapters were working like scopes for the inside subchapters and scenarios. You may specify there common givens and thens which will be applied to the internal parts. May be it could be useful to be able to attach additional steps for particular chapters.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-8606131298890797643?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/8606131298890797643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=8606131298890797643' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8606131298890797643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/8606131298890797643'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/04/i-think-stories-need-chapters.html' title='I think stories need chapters'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-965308584053418018</id><published>2008-04-16T08:59:00.002+03:00</published><updated>2008-04-16T09:03:19.672+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Blog'/><title type='text'>Ajaxload - The Spinners Generator</title><content type='html'>Found today the resource &lt;a href="http://www.ajaxload.info" target="_blank"&gt;ajaxload.info&lt;/a&gt;, you can generate your custom ajaxload spinners there.&lt;br /&gt;&lt;br /&gt;The guys did pretty well I should say. There are lots of patterns, you can choose a transparent background and custom colors.&lt;br /&gt;&lt;br /&gt;Nice stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-965308584053418018?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/965308584053418018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=965308584053418018' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/965308584053418018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/965308584053418018'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/04/ajaxload-spinners-generator.html' title='Ajaxload - The Spinners Generator'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-752094859566372098.post-3207345444930341268</id><published>2008-04-15T14:16:00.003+03:00</published><updated>2008-04-15T14:26:06.521+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Blog'/><title type='text'>Displaying GIT Branch Name In Your Console</title><content type='html'>So, if you had been taken the gateway drug of GIT recently, then you probably feel the sake of branches and there's a little trick for you dear fellow.&lt;br /&gt;&lt;br /&gt;Put (or change) the following stings in your ~/.bashrc file and you'll get a nice enhancement on your console prompt which will show your current git-branch when you're in a directory which is under git.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;function git_branch {&lt;br /&gt;  git branch --no-color 2&gt; /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1) /'&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PS1='\[\033[0;37m\][\[\033[01;36m\]\W\[\033[0;37m\]] \[\033[0;32m\]$(git_branch)\[\033[00m\]\$ '&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is quite handy when you use the branches feature actively.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/752094859566372098-3207345444930341268?l=st-on-it.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://st-on-it.blogspot.com/feeds/3207345444930341268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=752094859566372098&amp;postID=3207345444930341268' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3207345444930341268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/752094859566372098/posts/default/3207345444930341268'/><link rel='alternate' type='text/html' href='http://st-on-it.blogspot.com/2008/04/displaying-git-branch-name-in-your.html' title='Displaying GIT Branch Name In Your Console'/><author><name>Nikolay V. Nemshilov</name><uri>http://www.blogger.com/profile/11826995023912878683</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_78XLCpblQNY/SiLJoEpv5mI/AAAAAAAAABU/r69o7fwQ1zc/s1600-R/9097085'/></author><thr:total>1</thr:total></entry></feed>
