Art of Programming
Can Technical Excellence be a Reality? (Dmytrii Nagirniak ~ Dmitriy Nagirnyak)
Thursday, 22 December 2011
KnockoutJS Validations Screencast
Tuesday, 29 November 2011
Issues switching to JRuby from MRI 1.9
This is just a quick recap of what issues I have had trying to switch existing app from MRI Ruby (1.9.3) to JRuby (1.6.4).
Unfortunately I stepped back and didn't want to spend more time as it felt an unbreakable chain of issues. Resolving one, you think "huh, awesome". But very soon you hit the next.
And I couldn't see it stopping.
But PLEASE bear in mind that I only spent a couple of hours on that. With a little bit more persistence, you may be able to the end of the tunnel.
The overall impression is that it is not a big deal really. The problems arise when trying to use other libraries. And unfortunately, not all of the gems care deep enough to support JRuby. And this is the biggest issue in my opinion, there are very little issues with JRuby itself. The transition (from the syntax perspective) was extremely easy.
The application is pretty standard Rails 3.1 app that uses gems like Dragonfly, inherited_resources, cancan, paper_trail, Authlogic, CoffeeScript, Haml, Sass, Formtastic etc. For testing - things like rspec-rails, cucumber-rails, capybara-webkit, guard (rspec and cucumber), database_cleaner etc.
So here is a quick list of the things that I faced on my way.
Before everything else I enabled support for 1.9 syntax in JRuby (export JRUBY_OPTS=--1.9).
It all first started with the weird issue where the wrong arguments were passed to RSpec shared example.
Unfortunately I did not figure out why it happened and just patched it as you can see in the comments there.
After fixing this issue I had another one related to database_cleaner. So I ended up with a pull request that worked for me.
The other issue was a huge stack trace. I scrolled 3 pages in the terminal to get to the top. Most of that stack trace is deep inside Java. Additionally I got wrong line number on exception. This "features/step_definitions/company_steps.rb:234" makes me thing that the exception is at line 234. But that file only had 49 lines of code. Now, go figure.
Going on... SimpleCov gem didn't work out of the box and gave the warning that I need to enable debugging. That's fine and understandable. But after half an hour of debugging of unrelated exception, it turned out that SimpleCove threw an exception because of that. So had to go and explicitly disable it.
Then I managed to run all of my specs. But I was pretty surprised how slow it was:
# rvm use 1.9.3
# time rspec # real=14s, rspec time ~ 12s
# rvm use jruby
# time rspec # real=52s, rspec time ~ 30s
But this is expected as JRuby doesn't have enough time to optimise here. It should be much better in production. But it will be also shit slow after deployment.Ok. That's fine. I can trust it's fast.
Then I tried running the cucumber specs and had to file the issue to support new Ruby 1.9.3 syntax (a = b = f 1). Not a big deal, easily fixed with parens: a = b = f(1).
Next and the final issue - I could not run the headless cucumber specs (capybara-webkit). It just got stuck (no CPU used) and feels like there is a dead-lock or something similar.
I couldn't see way of fixing it easily and decided that for now I am done with it. So going back to Ruby 1.9.3.
To summarise, I did not feel like it's a problem to switch to JRuby. But it's really a big deal to actually use it due to the number of C extensions that JRuby doesn't really support.
Again, to emphasise, there is nothing horribly wrong with JRuby and it seems to be pretty good. But the real showstoppers are the C extensions.
Saturday, 19 November 2011
Rails Plugin with Tested Assets Screencast
How do they do all those small things? What tricks do they have? How would they approach that problem?
Unfortunately, I can't remember seeing people sharing the way they actually work (unless they pair). I want to see what mistakes they make, how they fix those, how they try to come up with a word erasing a line 10 times.
I believe all this is of a great value for a lot of us.
What I described is partially done by PeepCode's Play By Play series and Tekpub. Those are amazing (usually). But that's not exactly what I am after. Those screencasts are high quality, polished and prepared ones. YOU are NOT featuring there. I want to see YOU there. All of you, guys.
So I decided to make a first step (or maybe not so) and screencast myself while working on a Rails Plugin.
This is mostly real-time work. You'll see how I fail, you'll spot the mistakes I've done and did not even notice, you'll see how I write README. And, most importantly, you will hopefully see some of the things that will help yourself.
This video features the following tools/technologies:
- Ruby, Ruby On Rails
- CoffeeScript
- SASS
- Git, Github
- Vim (MacVim and Terminal)
- Jasmine and basic testing of JavaScript and even CSS assets
- etc
Tuesday, 4 October 2011
Use your Terminal like a Pro
~/dotfiles:
--+ bashrc, bash_profile
--+ ssh/config
-->--bash -> (env, config, aliases). bash has source bach/env, bash/config etc.
Then:
ln -s ~/.bashrc ~/bin/dotfiles
echo ". ~/.bashrc" > ~/.bash_profile
Put everything into .bashrc except any long running tasks.
.bash_profile - for interactive shells. But you can just usually source .bashrc.
Now some keyboard shortcuts:
- Cntrl-a - go to the beginning of line
- Cntrl-e - go to the end of line
- Cntrl-k - delete everything to the right
- Cntrl-w - delete previous word
- ESC->-b - go back one word
- ESC->-f - go forward one word
If you often connect via SSH like so:
ssh -p 2323 username@my-server.example.com
Then you can shorten it to ssh my-server if you will add entry to ~/.ssh/config:
Host my-server
HostName my-server.example.com
Port 2323
User username
Then you can use it as:
# Tunnelling
ssh -L7997:127.0.0.1:7979 my-server
# Copy folder to server
scp my_folder my-server:my_other_folder/
# Or even use it with git
git clone my-server:repo-name.git
Now some useful things you can do in the terminal:
!!- run previously executed command (you can do `sudo !!` for example)!$- last argument of previous command!60- run the command #60. Number comes fromhistorycommand
(for example: run !echo- rerun last command starting with echo- Ctrl-r - interactive search on history of commands; Use Ctrl-j to abandon.
!?file.log?!- run the last matching command (not interactive)^file.log^another-file.log- run the last matching command replacing file.log with another-file.log!?file.log?!- run the last matching command (not interactive)mv README.{txt,markdown}- same asmv README.txt README.markdown.
tail file.log and then cat !$ is same as cat file.log
You will also appreciate power of the shell when using aliases and functions:
alias ss="./script/server"
alias s="git status"
alias gca="git commit -am"
alias zipr="zip -r"
function take {
mkdir $1
cd $1
}
# take create-and-go-here
function gg() {
git commit -v -a -m "$*"
}
# gg Commit message with no quotes
But when functions get more complicated, you might want to use a scripting language (Ruby, Python etc).
Put those scripts into your ~/bin directory and add it to your PATH variable.
There are also number of Mac specific tricks.
One is is
open anything command that will open the file/directory in the default application.
You can also force it to use a specific app:
open -a /Applications/Firefox.app index.html.
This one becomes a good candiate for an alias.
You can also pipe output of any command into
pbcopy command to copy it to the clipboard.
For example:
cat index.html | pbcopy will copy the content of the file into clipboard.
You can also do it the other way around:
pbpaste > index.html or
pbpaste | grep "jquery".
Another useful thing OpenTerminal (drag the app to finder toolbar). It allows you to open terminal with the current directory set to the Finder window.
You can also use the keyboard to open terminal using LaunchBar. It does much more though. It acts like a global terminal for the system.
You can also try to use
zsh shell. It will work with the existing bash aliases just fine.
Most of the tips here come from the PeepCode's Advanced Command Line screencast.
Hope this helps you on your way to becoming a Terminal Guru.
Friday, 19 August 2011
Templating done easy - JavaScript HAML with Rails
HAML templating with no worries: native, precompiled HAML. Blazing fast and as easy to use as it can be.
UPDATE: this functionality moved into ruby-haml-js gem, please use it instead.
Recently I have been working on a Ruby gem that packs a set of common JavaScript libraries in one place so you can just reference those easily from Rails 3.1 app. But now I want to write a bit about a very handy tool that the gem includes. I needed to work on a Backbone app and, unfortunately, there was no very simple and easy way of using HAML markup for my client side templates. Main problems:- I don't want to embed the template into the document.
- The Underscore.js templating is ok, but it is too verbose for me (as most of others).
- The haml-js is great, but I do not want to bother precompiling the templates.
- I do not want to think about templating as another layer or component. I want it to just work.
Thursday, 19 May 2011
Easy Rails deployment with Capistrano and custom Nginx configs
In my previous post I provided some info about setting up the server for rails app.
Now I am going to describe the Nginx configuration and example of Capistrano deployment file.
One thing that I don't want to do for every new app I deploy is to go to the web server and change the settings (as a root user) so that the web server picks the Rails application.
In order to avoid that I decided to use nginx's include command. It allows to add include multiple nginx config files using UNIX wildcards.
So by simply adding this line to the nginx.config I make it possible to configure Nginx from Rails applicatoins themselves:
include /home/*/apps/*/current/nginx.conf;.
I decided to include all configs within http tag so that a Rails app can configure multiple servers for itself.
There are obvious drawbacks for this:
- It is less "safe" as on application might affect others if you will start adding Nginx settings outside of the
servertag. But I am ready to sacrifice this for the sake of having more flexibility by assuming all server citizens are good guys. - We still need to have root access to restart the Nginx. We shouldn't need to do it often, so that's ok with me.
- To deploy a rails app (and any Rack based app), user should put the app under his home directory in
apps/[name]/current. - To "enable" an app, user should put
nginx.conffile inapps/[name]/current.
But for now, all this cool structure isn't very helpful unless we deploy the app.
I went with the Ruby de-facto deployment tool - Capistrano. There are enough resources on the net on how to "Capify" your application, so I won't go into details. I assume you have just done capify . and understand what is going in there.
Briefly, what I need to do is following:
- Deploy the app explicitly to a particular domain (staging, production, local server etc).
- On every deployment - backup SQLite database (I know, I'll use something better when I'll have 1 million users).
- Prepare Nginx config file for the server (it depends on the domain we deploy at).
- Pre-Compile assets (SCSS in my case).
So grab the gist (embedded below) with all the details and let me know what can be improved there. The usage is pretty simple:
cap deploy user=dnagir domain=connecty.approache.com
You can deploy the same app multiple times to the same server with different subdomains and they will not conflict:
cap deploy user=dnagir domain=staging.connecty.approache.com
cap deploy user=dnagir domain=demo.connecty.approache.com
The most interesting I think is that the nginx.conf file is created during deployment using default Ruby templating engine and transfered over to the right location on the server. (But still remember root-ed Nginx restart is still required).
You can look at the complete source in the Connecty project at Github.
Please feel free to tell me what you think about this process.
Setting up Ubuntu 11.04 server for Rails with RVM
I had to set up the server (Ubuntu 11.04) for running couple of Rails apps and thought I could share the notes I've written while doing that. Any feedback and improvements are welcome.
apt-get update ; apt-get upgrade
# set timezone
dpkg-reconfigure tzdata
# RVM
bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
# update /etc/bash.bashrc
# -y to answer YES for questions
aptitude -y install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev
rvm install 1.9.2; rvm use 1.9.2 --default
# while ruby is being installed prepare nginx
cd /usr/src
wget http://nginx.org/download/nginx-1.0.0.tar.gz
tar -xf nginx-1.0.0.tar.gz
cd nginx-1.0.0
# Use one place for installation
rm -r /opt/ && ln -fs /usr/local /opt
gem update --system ; gem update
gem install bundler
#install imagemagic
apt-get -y install imagemagick
# proceed with passenger
apt-get -y install libcurl4-openssl-dev
gem install passenger
passenger-install-nginx-module
# additional otions:
# --prefix=/opt/nginx
# --with-http_gzip_static_module --with-http_stub_status_module --with-http_sub_module
# add nginx autostart script: http://wiki.nginx.org/Nginx-init-ubuntu
# verify the correct paths
cd /opt && \
wget -O init-deb.sh http://library.linode.com/assets/602-init-deb.sh && \
mv /opt/init-deb.sh /etc/init.d/nginx && \
chmod +x /etc/init.d/nginx && \
/usr/sbin/update-rc.d -f nginx defaults
# add to /opt/nginx/conf/nginx.conf
include /home/*/apps/*/current/nginx.conf;
/etc/init.d/nginx restart
# copy ssh keys (from local computer)
cat ~/.ssh/id_rsa.pub | ssh root@my.server.com "mkdir ~/.ssh ; cat - >> ~/.ssh/authorized_keys"
# change default ssh port from 22 to something to get rid of nasty attacks
vim /etc/ssh/sshd_config # change the port number
service ssh reload
#install firewall?? No need for now as only openning 80,443 ports
# apt-get install ufw
That should be enough for you to start using the server. In the future post I plan to describe my setup to make deploying new applications easier.