Recently I discovered Octopress and instead of writing another
article on how to migrate to it from Wordpress, I decided to do something else. And,
fortunately enough, the topic presented itself. :)
So, Octopress is not your conventional blog engine. You create new posts by running
a command in the terminal. I’m quite comfortable with creating new posts with a rake task.
What I didn’t like is that I needed to run another command to actually open the post in my
editor. Here’s my fix to that. It patches the new_post command, by adding new optional
parameter to it, :open_in_editor. If you pass true (or other truthy value),
then the post will be opened in
my default $EDITOR (which is TextMate at this moment).
Now the creation of new post can look like this:
1
rake new_post['How to automatically open new post in editor?',:open]
I remember reading my first book on Javascript. It was covering Javascript 1.0 and included
changes in Javascript 1.1 I thought then: “Wow, that’s an advanced stuff!” If I was told
that things like jQuery or Node.js will someday exist, I wouldn’t believe that. And if
tomorrow I fall into a time vortex and travel to 90s, I will probably kill myself. It’s like
the stone age of technology. It’s better to get to a real stone age. At least, the
air is still clean. :)
Back when I was just starting to program, we used to joke about Delphi coders.
A Delphi coder wants to build a tool for cheating in games (read/write memory of another process). So, before writing a single line of code he goes to a forum and asks: “Are there ready-made components for building game cheating tools?”
I guess, this happens to every widely adopted technology that has plugins/libraries. An example.
You know how everyone is obsessed these days with scalability? Well, I am too. There’s a project of mine where I need to connect to multiple MySQL servers, depending on the current client id. The simplest implementation looks like this:
This works, but it creates a new connection on every call. Let’s suppose, we’re processing a queue and we’re handling events from different apps (customers). The cost of setting up a connection can easily outweigh the actual work. We need to cache that somehow.
Now, ActiveRecord::Base has a method called connection_config that returns, well, configuration of current connection. We can compare that to what we have on hands and, if they match, do not reconnect. Here’s how our code looks like now:
123456789101112131415161718192021
defself.use_table_for_appaidconfig=get_connection_configaidunlesscan_use_connection?(config)&&ActiveRecord::Base.connection.active?ActiveRecord::Base.establish_connectionconfigendenddefcan_use_connection?configcurrent_config=ActiveRecord::Base.connection_config# current config can have more keys than our config (or vice versa), so simple hash comparison doesn't work.config.eachdo|k,v|# if even a single parameter is different - can't reuse this connection.ifconfig[k]!=current_config[k]# TODO: log warningreturnfalseendendtrueend
Looks almost good. Now let’s extract this to a module for ease of re-using. Here’s the final code.
moduleSqlModelHelpersdefself.includedklassklass.send:extend,ClassMethodsendmoduleClassMethodsdefsetup_connectionaid# TODO: do not reestablish good connections.# 1. get config to connect# 2. compare with current config (ActiveRecord::Base.connection_config)# 3. if configs match and connection is open - do not establish_connectionconfig=get_connection_configaidunlesscan_use_connection?(config)&&ActiveRecord::Base.connection.active?ActiveRecord::Base.establish_connectionconfigendenddefcan_use_connection?configcurrent_config=ActiveRecord::Base.connection_configconfig.eachdo|k,v|ifconfig[k]!=current_config[k]# TODO: log warningreturnfalseendendtrueendendendclassMyModel<ActiveRecord::BaseincludeSqlModelHelpersdefself.use_table_for_appaidsetup_connectionaidtable_nameendend
Now if we want to use this functionality in some other models, we just include that module. This code can certainly be improved further, post your suggestions :)
While this certainly works and gets job done, it has some flaws. First, it’s a string, so some editors and IDEs will get confused and lose coloring/completion. Second, there’s no validation on module name. In best case, you’ll get compiler error. In worst case, you’ll get hard to track bugs.
You live to learn every day. Today I discovered for..in loop in ruby. When I saw it in a question on stackoverflow, I was like “Hey, dude, this is ruby, not javascript!” in my head. But, apparently, it’s legal ruby :)
123456789
arr=[1,2,3]forainarrputs"element #{a}"end#=> element 1#=> element 2#=> element 3
You can put ranges in there also.
123
forain(1..10)putsaend
I usually write such loops with .each, but good to know there’s another way.
This operator returns a boolean value (as one would expect), so you can write code like this:
1
puts"B inherits from A"ifB<A
Note how it resembles the class definition syntax. I think that this is simply brilliant (not very intuitive, though. I wouldn’t think of writing this code).
Now that we know this fact, let’s try to sort the classes :)
123456789101112131415161718192021222324252627
classAendclassB<AendclassC<AendclassD<BendclassE<AendclassClass# we need to define the spaceship operator for classes, since it's not defined yet.def<=>otherreturn0ifself==otherreturn-1ifself<other1endendklasses=[A,B,C,D,E]klasses.sort# [E, C, D, B, A]
Now this class hierarchy is sorted, with children first and parents last. Homework: come up with a practical application for this :)
Let’s say you want to dump your MongoDB database. There’s a handy tool that does just that, mongodump.
1
mongodump
If executed without any arguments it will try to connect to localhost:27017 and dump all databases. You can specify a single database that you’re interested in and it will dump just this database.
1
mongodump -d mydb
But in some cases you don’t want to dump the whole database. In my case, it’s an analytics application and 99% of data is in raw event collections (events20120320, events20120321, …). I am interested only in a small number of “important” collections. But mongodump doesn’t provide us with an option to specify several collections. You can only dump one collection at a time. If you don’t mind some typing, that’s easy.
But we’re all programmers, so let’s automate this stuff. I, personally, never used bash loops before, and it seems like a good use case for them. Let’s do it.
123456
colls=( mycoll1 mycoll2 mycoll5 )for c in ${colls[@]}domongodump -d mydb -c $cdone
First line defines a bash array literal. Don’t use commas to delimit array elements, they’ll become part of the element. The ${colls[@]} string means “all array elements” and it can be used anywhere where a variable is expected. The rest is pretty straightforward, I think.
Today we’ll be deploying a simple Sinatra app with Capistrano, using Unicorn as our web server. First things first: let’s think of a stupid name for this project. What about “sincapun”? Any objections? Good, let’s proceed.
$ bundle exec ruby sincapun.rb
[2012-03-09 16:04:17] INFO WEBrick 1.3.1
[2012-03-09 16:04:17] INFO ruby 1.9.3 (2012-02-16)[x86_64-darwin11.3.0]== Sinatra/1.3.2 has taken the stage on 4567 for development with backup from WEBrick
[2012-03-09 16:04:17] INFO WEBrick::HTTPServer#start: pid=14871 port=4567
So far, so good. Now let’s convert it to modular app and create a proper rackup file.
# config/deploy.rb# We're using RVM on a server, need this.$:.unshift(File.expand_path('./lib',ENV['rvm_path']))require'rvm/capistrano'set:rvm_ruby_string,'1.9.3'set:rvm_type,:user# Bundler tasksrequire'bundler/capistrano'set:application,"sincapun"set:repository,"git@github.com:stulentsev/sincapun.git"set:scm,:git# do not use sudoset:use_sudo,falseset(:run_method){use_sudo?:sudo::run}# This is needed to correctly handle sudo password promptdefault_run_options[:pty]=trueset:user,"myname"set:group,userset:runner,userset:host,"#{user}@myhost"# We need to be able to SSH to that box as this user.role:web,hostrole:app,hostset:rails_env,:production# Where will it be located on a server?set:deploy_to,"/srv/#{application}"set:unicorn_conf,"#{deploy_to}/current/config/unicorn.rb"set:unicorn_pid,"#{deploy_to}/shared/pids/unicorn.pid"# Unicorn control tasksnamespace:deploydotask:restartdorun"if [ -f #{unicorn_pid} ]; then kill -USR2 `cat #{unicorn_pid}`; else cd #{deploy_to}/current && bundle exec unicorn -c #{unicorn_conf} -E #{rails_env} -D; fi"endtask:startdorun"cd #{deploy_to}/current && bundle exec unicorn -c #{unicorn_conf} -E #{rails_env} -D"endtask:stopdorun"if [ -f #{unicorn_pid} ]; then kill -QUIT `cat #{unicorn_pid}`; fi"endend
We need a config file for Unicorn. Here is what it may look like:
# define paths and filenamesdeploy_to="/srv/sincapun"rails_root="#{deploy_to}/current"pid_file="#{deploy_to}/shared/pids/unicorn.pid"socket_file="#{deploy_to}/shared/unicorn.sock"log_file="#{rails_root}/log/unicorn.log"err_log="#{rails_root}/log/unicorn_error.log"old_pid=pid_file+'.oldbin'timeout30worker_processes2# increase or decreaselistensocket_file,:backlog=>1024pidpid_filestderr_patherr_logstdout_pathlog_file# make forks fasterpreload_apptrue# make sure that Bundler finds the Gemfilebefore_execdo|server|ENV['BUNDLE_GEMFILE']=File.expand_path('../Gemfile',File.dirname(__FILE__))endbefore_forkdo|server,worker|defined?(ActiveRecord::Base)andActiveRecord::Base.connection.disconnect!# zero downtime deploy magic:# if unicorn is already running, ask it to start a new process and quit.ifFile.exists?(old_pid)&&server.pid!=old_pidbeginProcess.kill("QUIT",File.read(old_pid).to_i)rescueErrno::ENOENT,Errno::ESRCH# someone else did our job for usendendendafter_forkdo|server,worker|# re-establish activerecord connections.defined?(ActiveRecord::Base)andActiveRecord::Base.establish_connectionend
That should do it. Now you can deploy your app, assuming that you have RVM on the server, you can SSH into it and write to /srv directory.
12
cap deploy:setup
cap deploy
Deploy should spit a lot of text into the console, and there should be no errors. Verify that our unicorns are launched correctly by logging into the server and running this:
Your server’s hardware clock isn’t perfectly accurate. It may run faster or slower (in my experience it was always slower). So it is important to synchronize it every so often, or else you might encounter some unexpected bugs. There’s a command in Ubuntu that synchronizes hardware clock against atomic clock servers. It’s called ntpdate
12
$ sudo ntpdate ntp.ubuntu.com
9 Mar 03:59:12 ntpdate[7225]: step time server 91.189.94.4 offset 179.440440 sec
This one was three minutes behind. Could be worse, though. So, now clock is more or less accurate. To keep it this way, let’s add an hourly cron job. Create a file called ‘ntpdate’ (for example) at ‘/etc/cron.hourly’ with this content:
123
#! /bin/shntpdate ntp.ubuntu.com
We don’t need sudo here, because these jobs are run with root privileges. Now make that file executable.
1
sudo chmod +x /etc/cron.hourly/ntpdate
We’re all set now. Come back a few days later and verify that clock doesn’t deviate as much anymore.