My .vimrc

(_vimrc)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
set backspace=indent,eol,start
set nocompatible

set autoindent          " always set autoindenting on
set history=50          " keep 50 lines of command line history
set ruler               " show the cursor position all the time
set showcmd             " display incomplete commands
set incsearch           " do incremental searching
syntax on
set hlsearch

set statusline=%<%f%h%m%r\ %y%=%{&ff}\ %l,%c%V\ %P

highlight StatusLineNC ctermfg=yellow ctermbg=green
highlight StatusLine ctermfg=white ctermbg=red

set scrolloff=1

set ignorecase smartcase  " Do a smart case search
set whichwrap+=<,>,[,]  " Left takes you back across lines
set laststatus=2        " Always have a statusbar
set hidden              " Keep files in the buffer list
set showmatch           " Show matching braces
set autowrite           " Autowrite: "on" saves a lot of trouble
set lazyredraw  " lazyredraw:  do not update screen while executing macros

"set textwidth=120



imap <C-Left> <C-O>b
imap <C-Right> <C-O>w

"set cinkeys=0{,0},:,0#,!<TAB>,!^F
set cindent
set comments=sl:/*,mb:*,elx:*/


set term=xterm-color

colorscheme koehler

set guifont=-*-fixed-*-*-normal-*-18-*-*-*-*-*-*-*

set guioptions=lmrbrt
map <F3> ggVGg?

"python stuff
set tabstop=4    
set shiftwidth=4 
set smarttab   
set expandtab
set softtabstop=4    
set autoindent
"filetype indent on

autocmd BufRead *.py set smartindent cinwords=if,elif,else,for,while,try,except,finally,def,class
im :<CR> :<CR><TAB>
autocmd BufWritePre *.py normal m`:%s/\s\+$//e ``

autocmd BufRead *.thtml set ft=php
autocmd BufWrite *.thtml set ft=php

autocmd FileType css set smartindent

set noendofline

Tips for node.js Deployment on SmartOS

At the lovely company I work, we’re doing quite some work with node.js. Best environment for node.js deployment is a SmartOS machine, of course. One of the nice features of SmartOS is the deep level of dtrace integration. Check Max’s presentation from Node Dublin ‘12.

On the other side, SmartOS is SunOS’s younger nephew and not Linux’, which means that all your init.d/upstart scripts your already have will not be 100% portable to this new OS.

I’ll show here a couple of tips, to make the first deployment of your node.js service or site easier.

First, give your server a nicer name, so when you ssh in, it’s easier on your eyes and memory then the default md5/guid name.

1
echo "massive-lannister.local" > /etc/nodename

Second prepare your server to communicate with your Github account. Generate a private key…

1
2
ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub

…and copy the content of ~/.ssh/id_rsa.pub to GitHub > Account Settings > SSH Keys > Add SSH key.

Now let’s set up a home for your project.

1
2
mkdir /home/massive/
cd /home/massive/

Clone the repository

1
git clone git@github.com:bamse/massive-lannister.git

Install the npm dependencies

1
2
cd massive-lannister/
npm install

To set up the node.js service, you’ll be using Service Management Facility.

In your git project, create a directory srvconf where the you’ll store the non-critical server configuration files like nginx.conf or SMF manifest.

Now create a SMF xml manifest based on this template.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="node-massive-lannister-service">
  <service name="site/node-massive-lannister-service" type="service" version="1">
  <create_default_instance enabled="true"/>
  <single_instance/>
  <dependency name="network" grouping="require_all" restart_on="refresh" type="service">
    <service_fmri value="svc:/milestone/network:default"/>
  </dependency>
  <dependency name="filesystem" grouping="require_all" restart_on="refresh" type="service">
    <service_fmri value="svc:/system/filesystem/local"/>
  </dependency>
  
  <method_context working_directory="/home/massive/massive-lannister">
    <method_credential user="admin" group="staff" privileges='basic,net_privaddr'  />
    <method_environment>
      <envvar name="PATH" value="/home/massive/massive-lannister/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin"/>
      <envvar name="HOME" value="/home/massive/massive-lannister"/>
      <envvar name="NODE_ENV" value="production"/>
    </method_environment>
  </method_context>
  
  <exec_method
    type="method"
    name="start"
    exec="/opt/local/bin/node /home/massive/massive-lannister/app.js"
    timeout_seconds="60"/>
  
  <exec_method
    type="method"
    name="stop"
    exec=":kill"
    timeout_seconds="60"/>

  <property_group name="startd" type="framework">
    <propval name="duration" type="astring" value="child"/>
    <propval name="ignore_error" type="astring" value="core,signal"/>
  </property_group>

  <property_group name="application" type="application">
  </property_group>

  <stability value="Evolving"/>

  <template>
    <common_name>
      <loctext xml:lang="C">node.js massive lannister service</loctext>
    </common_name>
  </template>
  </service>
</service_bundle>

Import the service and start it.

1
2
svccfg import srvconf/node-massive-lannister-service-manifest.xml
svcadm enable node-massive-lannister-service

And verify that it works:

1
tail -f -n 50 "/var/svc/log/site-node-massive-lannister-service:default.log"

If you wish to stop the service:

1
svcadm disable node-massive-lannister-service

If you’re using the node.js service behind a nginx server, set this up too. Some companies (like Joyent) are exposing node.js directly, not behind a proxy.

Install nginx:

1
pkgin in nginx -y

Copy the nginx config:

1
2
mkdir -p /opt/local/etc/nginx/sites-enabled
cp srvconf/nginx-site-api.conf /opt/local/etc/nginx/sites-enabled

Edit nginx.conf and add the include line after http {

vim /opt/local/etc/nginx/nginx.conf
#add this line after http {
include /opt/local/etc/nginx/sites-enabled/*.conf;

Enable nginx:

1
svcadm enable nginx

Sample of nginx-massive-lannister.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server {
  listen   80; ## listen for ipv4; this line is default and implied
  #listen   [::]:80 default ipv6only=on; ## listen for ipv6

  root /home/massive/massive-lannister/public;
  index index.html index.htm;

  # Make site accessible from http://localhost/
  server_name api.massive-lannister.com;

  location /api {
      proxy_pass http://127.0.0.1:5648;
      proxy_buffering off;
      proxy_connect_timeout 66;
      proxy_read_timeout 66;
      proxy_send_timeout 66;
      proxy_hide_header X-Powered-By;
      proxy_hide_header X-Response-Time;
      proxy_set_header Host $host;
  }
}

To make a new deployment, at a later point:

1
2
3
4
ssh root@massive-lannister.com
cd /home/massive/massive-lannister/
git pull
svcadm restart node-massive-lannister-service

Another useful tip, with this setup is the separation of production and development environment. If you noticed, in the manifest xml file, we set a variable called NODE_ENV to production.

In app.js, include the reference to your config file:

1
var conf = require('./lib/config.js')

And later:

1
seneca.use('mongo-store', conf.mongo)

The content of lib/config.js is pretty small:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var env = process.env.NODE_ENV || 'development'
console.log('Config in ' + env + ' mode')

var config = {}
config.port = 5648; //TYRI
if (env === 'development') {
  config.mongo = {
  name:'db-dev',
  host:'',
  username:'massive-test',
  password:''
  }
} else {
  config.mongo = {
  name:'db-prod',
  host:'',
  username:'massive-prod',
  password:''
  }
}

module.exports = config

Inspired by Isaac Schlueter’s Github project.

Happy coding!

P.S. In case you’re wandering who’s massive-lannister, that’s the name of the project suggested by Github, when I went on to create a new repository. Some of their suggestions were north-american-hipster and mustached-sansa. If Tyrion Lannister and Sansa are present, I guess that someone from Github is a big fan of George RR Martin’s A Song of Ice and Fire. Or HBO’s Game of Thrones, I can’t tell until I see if they used Khaleesi or Daenerys for the Mother of Dragons’ name.

On IPv6

IPv6 is here, however its support is not ubiquitous. Although some hosting providers support it, some of the big names don’t offer native support (cought Amazon/Azure).

The first step in setting your IPv6 ip is to check if your hosting provider supports it. The previous hosting company I used (easyspeedy.com) and the current companies (prgmr.com, burst.net) do however, some of the biggest players like Azure (Azure FAQ) and Amazon (Amazon FAQ) don’t. Unfortunately, this means that a lot of other companies built on top of Amazon’s services don’t. The following service providers, although not based on Amazon or Azure, still doesn’t offer IPv6 internet routing (Squarespace, WPEngine, Joyent, Nodejitsu). Some companies offer a tutorial on how to set up IPv6 using he.net tunnels.

The second step is to get an ip. Check if you already have an IPv6 address assigned. Login via ssh and run

sudo ifconfig|grep inet6 

If you’re output has some record like the one from below, you’re in luck.

inet6 addr: 2001:470:1:41:891d:4105:8c8d:2942/64 Scope:Global

If not, contact your provider support team and they may assign you ip addresses. Some providers will give you a /64 class (several billions) others will give you just 10 or 20. Since there are so many IPv6 addresses available (several billions for each person on the planet), these should be free of charge (compared with $10-S20/year for any additional IPv4 address).

Setup the address you received. Tutorials for Ubuntu/CentOS are available online. They are really to find them using your favourite search engine.

Note on setting your IPv6 address: at the moment, cPanel does not support IPv6. According to their site, support for it is coming soon (soon as in 2013-2014). This is somewhat sad, since you won-t be able to set up Apache if you’re also using cPanel.

Verify that your IP is correctly, set up. Use one of the many online sites

Step three is to let others know that you are using IPv6, meaning that all your hosts which have an IPv4 IP should have an IPv6 one. This is done from your hosting provider DNS server (or from your own DNS server). Start by quering your domain, cli whois or online whois. Then log in to your domain provider or DNS server and set up an AAAA record, for each A record. CNAME or MX point to hosts, so nothing to do here. Depending on your DNS TTL (time to live), your new IPs will be available sooner or later.

Step four is to setup the rest of your infrastructure, web server, ssh, and other.

Verify which service is already listening on ipv6 using netstat.

bash$ netstat -nalvt
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN

In the previous example, ssh listes on IPv4 (address 0.0.0.0:22) and IPv6 and (address :::22), however mysql listens only on IPv4 (127.0.0.1:3306).

From experience, ssh and mail already listen on IPv6.

The web server, has to be setup, though. Nginx is easier to setup, because you only need to configure it to listen on IPv6, however Apache needs to be then configured for each virtual host (for Apache only).

Once you’ve completed these steps, you can setup an IPv6 only address for your site (e.g. ipv6.site.com or www.ipv6.site.com) and see that you can open it. If this works too, congratulations, you’re ready for the future.

On Coding Guidelines

A good quote to start this rant is one that I just read a couple of days ago:

“Programs must be written for people to read, and only incidentally for machines to execute.” – Abelson and Sussman

I see guidelines as a Strunk and White for code. If you pick any book, you will notice it has a flow. Each paragraph starts with a tab, each sentence ends with a punctuation sign, there is no space between parentheses and their content, etc. What coding guidelines are suggesting is the same thing. If you have been online since the early 2000 you have probably noticed the l33t (IRC) speak. You can read l33t speak, but is easier to use proper words and formatting.

When you are working on project (more then 100 lines), you are making a plan, draw an architecture. The coding guidelines are one of the small, overlooked details of that project. Most of the times, they are your particular ones, implicit ones, even if you did not declare them as such. Did you use two different styles in projects using the same programming language? I guess not. We exclude the projects that were written years apart, as styles may evolve. When you are part of a community, it is nice to follow their rules (implied or written). In that community some have their quirks (eg not using coffee script), why adding another one?

Coding guidelines do not cover your ass. If you need this, then add a couple of processes. Start with code reviews (which are a good idea), add QA sign-off and then formal sign-off. Coding guidelines are good to easy in project switching and project collaboration. I was reading somewhere that Google enforces the same C++/Python/etc coding standard.

I will not go as far, but I would like to use the community coding guideline. I have seen colleagues sending code back, after code review, because of wrong curly braces placement. I believe that is a bit too much.

I agree that some people are too attached to rules and processes (after they become a manager) but that is like the bicycle shed problem, everyone knows how to paint it. I am arguing for easy collaboration, and if possible easy automation of finding your own mistakes (JSLint or FxCop in C#).

Guidelines are not about not trusting your team. Trust is earned by shipping great software, making your customer happy, or helping your colleagues (when in distress), not by using proper format, commas et all. Guidelines are not a process or a method. I doubt that there are consultants that your manager can hire to sell them to you (cough agile).

In the end, I believe that following coding guidelines (but not enforcing them) will help the person that uses them because the code will be easier to read three months later and peers will find that code easier to understand.

Objective-C Bits

NSObject equality

In your NSObject, if you implement isEqual, make sure you also implement hash.

For a container object like:

1
2
3
4
5
6
@interface Member : NSObject

@property (assign, nonatomic) int memberId;
@property (copy, nonatomic) NSString *name;

@end

Implement it like this:

1
2
3
4
5
6
7
8
9
- (BOOL)isEqual: (id)other {
  return ([other isKindOfClass:[Member class]]
      && [other memberId] == memberId);
}

// Providing that memberId is unique
- (NSUInteger)hash {
  return memberId;
}

FMDatabase and LIKE

FMDatabase using LIKE with a query like

1
SELECT * FROM member WHERE name LIKE '%member%'

translates to this:

1
2
3
4
5
6
NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:
                    [NSString stringWithFormat:@"%%%@%%", searchTerm], @"name",
                    nil];

NSString *q = @"SELECT * FROM member WHERE name LIKE :name";
FMResultSet *s = [database executeQuery:q withParameterDictionary:argsDict];

And not to this:

1
2
3
4
5
6
NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:
                    [NSString stringWithFormat:@"%@", searchTerm], @"name",
                    nil];
                  
NSString *q = @"SELECT * FROM member WHERE name LIKE '%:name%'";
FMResultSet *s = [database executeQuery:q withParameterDictionary:argsDict];

If you’re using named parameters (:name), then you can’t put quotes around them.

MKMapView

You need to set the region before the view is loaded.

E.g.

1
2
3
4
5
MKMapView *mapView;
MKCoordinateRegion region;
//...
[mapView regionThatFits:region];
[mapView setRegion:region animated:TRUE];

Should be called before viewDidLoad, for example in viewWillLayoutSubviews.

Spotlight vs Mdfind

Or how to debug why your Spotlight plugin is not finding your files (on Lion).

Let’s say that you’re writing a Core Data, and you’re having a store where you’re saving all your entities (e.g. books). There are a couple of tutorials on how to integrate this Core Data store with Spotlight, for example Marcus Zarra’s presentation “Spotlight and QuickLook vs. Core Data” (NSConference ‘09) or his excellent book Core Data: Apple’s API for Persisting Data on Mac OS X.

Basically, each time you’re adding/updating/deleting a row, you are creating/updating a file containing some data you want to index in Spotlight (e.g. book title, description, ISBN, etc).

However, things might not work from the first run. If it happens, I suggest to first try all the steps from Troubleshooting Spotlight Importers.

If the above steps doesn’t help, here are a couple of steps you can also try. Test if your import works with your file:

bash$ mdimport -d2 -g ~/path/to/TestSpotlight.mdimporter ~/path/to/book-isbn-123456.book

There are two important parts in the output of this command. The first one is this:

loading plugin at path '/Users/.../TestSpotlight.mdimporter' for UTIs: com.coralmagnetic.book.

This part says what UTI does your importer know to read (in this case com.coralmagnetic.book).

Import: magic_file returned "XML  document text" for path "/Users/.../book-isbn-123456.book" of type "dyn.ah62d4rv4ge81s7psqvw1e2xtr7zxe"

This part says what type is the file you’re trying to import. In this particular case, it says ‘XML document text’. Because there is a missmatch between the importer plugin and the importer file and this file will be scanned and imported as ‘generic’ file. You will be able to find it using the file name (or extension name), but you won’t find it by looking for content. However if this line says:

Importing type 'com.coralmagnetic.book' using ...

then it should import your content. In case there is a missmatch between these UTIs, you need to check both your Info.plist files (from the App and from the Spotlight importer) for the array key ‘CFBundleDocumentTypes’ to contain key ‘LSItemContentTypes’ with the value ‘com.coralmagnetic.wunderbook’.

Let’s say you made it so far, and the correct importer is reading all the info from your meta file, and somewhere in the output you’re seeing it as:

kMDItemDisplayName = "Alice's Adventures in Wonderland - Lewis Caroll";
kMDItemKind = {
  "" = "Book";
};

kMDItemTextContent = "It tells of a girl named Alice...";
kMDItemTitle = "Alice's Adventures in Wonderland";
kObjectId = "x-coredata://63CB1FB7-9F56-465B-8104-1A0DC8AA383E/Books/p112";

Now, you should try to look for them using Spotlight search (and his command line equivalent: mdfind). If both commands return something, you’re done.

But let’s assume that at an earlier point, as a good MacOSX developer, you’ve decided to save the the files somewhere in the caches directory: \~/Library/Caches/Books/. Running:

mdimport -d2 -g ~/Downloads/Spotlight/TestSpotlight.mdimporter ~/Library/Caches/Books/book-isbn-123456.book 

will return the correct, matching UTIs, but it wouldn’t list the content of the meta file (e.g. the kMDItemTitle key). However, if you move the same “book-isbn–123456.book” file to a different place (e.g. \~/Desktop), the content is properly read. And now is when ‘mdls’ it comes to the rescue, to list the metadata for each specified file.

If you run mdls for the same file, both in ~/Library/Caches/Books and ~/Desktop you’ll notice a couple of things.

First, in the ~/Library/Caches directory you have this part:

kMDItemSupportFileType = (
  MDSystemFile
)

And in ~/Desktop directory you have this part:

kIsbn        = 12345
kMDItemTitle = "Alice's Adventures in Wonderland"
kObjectId    = "x-coredata://63CB1FB7-9F56-465B-8104-1A0DC8AA383E/Books/p112"

The first part, it tells Spotlight that this is a system file. This means that you won’t be able to find it via the right top Spotlight search (command + space). To find these files, use Finder search, click on the + and add “System files” “are included”.

I don’t know the exact reason that it doesn’t index the files from ~/Library/Caches, but it indexes them from other ~/Library directories. I suppose it has a list of ‘Exclusions’ saved somewhere (it’s not in the regular Spotlight preferences panel). I found this accidentally, and it could be that it wasn’t present before Mac OSX Lion.

Fixing UIAnimator removeAnimationsForTarget Crash

You tested your app on the Simulator and everything is fine. You’ve install it on the iPhone, and when you reach a certain TableViewController your app crashes with the following message:

[UIAnimator removeAnimationsForTarget:]. 

How would you approach this issue?

Using common sense, try to set a breakpoint in each function from your TableViewController, then step through them to see in/after which function does the crash happens. In my case, the responsible code was found in the method:

1
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

It entered this function, once for each cell, but it crashed after exiting. As a new iOS developer, I was trying to set a couple of different UITableViewCell in one TableViewController. Here is a sample of the code I was using in the first version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  // Setting a generic cell
  static NSString *CellIdentifier = @"Cell";
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  
  if (cell == nil) {
      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
              autorelease];
  }

  // ... Other code

  // Replace generic cell with custom cell
  if(objectType == CMMUItemViewSectionSummary){
      NSArray *topLevelObjects = [[NSBundle mainBundle]
          loadNibNamed:@"SummaryCell"
          owner:nil
          options:nil];

      for(id currentObject in topLevelObjects){
          if([currentObject isKindOfClass:[SummaryCell class]]){
              cell = (SummaryCell *) currentObject;
              break;
          }
      }

      cell.summaryData = summaryData;
  } else if(objectType == CMMUItemViewSectionStatuses){
      NSArray *topLevelObjects = [[NSBundle mainBundle]
                                  loadNibNamed:@"ItemStatusCell"
                                  owner:nil
                                  options:nil];
                                  
      for(id currentObject in topLevelObjects){
          if([currentObject isKindOfClass:[ItemStatusCell class]]){
              cell = (ItemStatusCell *) currentObject;
              break;
          }
      }
          
      cell.statusesData = statusesData;
  } else {
      cell.textLabel.text = someDefaultData;
  }

  // Return the cell
  return cell;
}

As you can see in the above code, after I was getting a generic cell, I would try to replace it with a custom cell. I guess that somewhere after

tableView:cellForRowAtIndexPath: 

some other piece of code was trying to cleanup the unused cells. Figuring this, it was pretty easy to get it fixed. Using the same sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  NSString *CellIdentifier = @"Cell"; //Generic identifier
  UITableViewCell *cell;
  // Other code ...

  CellIdentifier = [NSString stringWithFormat:@"Cell%d", objectType]; //cell of my object type
  cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  if(objectType == CMMUItemViewSectionSummary){
      if (cell == nil) {
          NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"SummaryCell"
              owner:nil
              options:nil];
              
          for(id currentObject in topLevelObjects){
              if([currentObject isKindOfClass:[SummaryCell class]]){
                  cell = (SummaryCell *) currentObject;
                  break;
              }
          }
      }

      cell.summaryData = summaryData;
  } else if(objectType == CMMUItemViewSectionStatuses){
      if (cell == nil) {
          NSArray *topLevelObjects = [[NSBundle mainBundle]
              loadNibNamed:@"ItemStatusCell"
              owner:nil
              options:nil];

          for(id currentObject in topLevelObjects){
              if([currentObject isKindOfClass:[ItemStatusCell class]]){
                  cell = (ItemStatusCell *) currentObject;
                  break;
              }
          }
      }

      cell.statusesData = statusesData;
  }

  if(cell == nil){
      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                         reuseIdentifier:CellIdentifier]
                  autorelease];
  }

  return cell;
}

Even if is not the optimal way to use custom TableViewCells, it solves the crash problem.

Improvements and suggestions are welcomed.

GTD and MacOSX Apps

 

I’ve found out about GTD around 2008 and tempted to try it out, I’ve checked what Macs apps are available for that. Technically not all these apps are 100% GTD but more on the ‘task management’ side.

After a short research I picked OmniFocus at the beginning of 2009. Since I don’t follow all GTD rules, I used is mostly as a “glorified to do list”, installed on the laptop only. I added a couple of tasks and projects and started to tick them off. It had recurrent tasks (which were used to keep track of my haircut schedule), folders/projects (to store lists of appliances/gadgets to buy or the debts like the car loan).

When Andy from Potion Factory launched the first beta of The Hit List, I gave that a try too. I preferred the UI design to OmniFocus’ one. It also had folders/projects and recurrent tasks. It even came up with a setup project meant to showcase the best of it. On the preferences pane it has a tab called iPhone Sync – coming soon. One of my reasons was the hope that the iPhone client would come soon enough (2-4 months) and it would cost less then OmniFocus for iPhone (was hoping for $10). As of today, there is no iPhone app (and no news about it).

In this period of time there was another application available: Things (from Cultured Code). The couple of times when I checked it, it lacked recurrent task and a way to make tasks available only from a certain date on. It didn’t manage to convince me to switch away from THL then. But because THL for iPhone is still in development, about a week ago I decided to give Things another try. Recurrent tasks look nice enough and there is a way to add tasks that are visible only from a certain date on. The prices of the iPhone/iPad apps are in the ranges I’m willing to pay. I got used to the way Things handles tasks (vs THL) and the future looks promising. I’ll give Things a try until the end of August.

I don’t want to complain about Andy’s silence period. He’s a one man indie shop and that makes it easy to throw his work schedule of the track. After all, on my screensaver project things are not moving as fast as I’d like to.

Since it was somewhat clear for me that I should replace THL, I tried to get back to OmniFocus, but I can’t get used to its user interface and work flow.

What I’ve learned from this story is that I should not buy a product based on the promises of “coming soon”. Probably I’d be much happier if I have had paid $20 for OmniFocus for iPhone and use it on a second device for about one year instead of being cheap and switching to The Hit List and now to Things. On the other hand, I really enjoyed these later two programs.

For other interested persons, I’ve attached a comparison screenshot with my pet peeve, recurrent tasks.

Snow Leopard Wakes From Sleep Immediately

 

I’ve noticed that starting with a couple of days ago, the iMac doesn’t go to sleep anymore. If I go to Apple menu then Sleep, it tries to go to sleep, but after the hard disk stops spinning, it wakes up again.

After carefully inspecting the Console logs I’ve found the following messages:

Feb 8 20:32:17 … kernel[0]: hibernate_write_image done (0)
Feb 8 20:32:17 … kernel[0]: sleep
Feb 8 20:32:17 … kernel[0]: Wake reason = 
Feb 8 20:32:17 … kernel[0]: System Wake
Feb 8 20:32:17 … kernel[0]: Previous Sleep Cause: 5

What’s particularly strange is the empty “Wake reason =” message after sleep. Usually, there you can see different messages like “Wake reason = OHC1”, when you wake the Mac by pressing a key on the USB keyboard.

After removing some innocent Extensions (Remote Buddy and CoolBook2 — this one ended up here when I’ve migrated the data from the laptop), I’ve found out the root cause.

In my case, it was the wireless network’s fault. I already have a regular network cable between the Time Capsule and the iMac, but I also have a wireless network for the mobile devices. For some reason, I’ve activated the wireless network on the iMac. This caused the computer to wake up from sleep immediately, but no “Wake reason” was given.

In case your MacOSX doesn’t want to go to sleep too, try this.

Attention to Details in Professional IDEs

About 3 weeks ago I had to switch from Mac OS X (Leopard) to Windows (7 beta build 7100) at the company I work for. One of the reasons of the change was starting of a new project, that will be developed on Microsoft stack (C#, ASP.NET MVC and Microsoft SQL Server 2008). The project will have to be deployed on some Windows Servers, so no point in using C# Mono or PHP.

The first week, was spent getting used to the new platform and all the tools available on it. Installing pasteboard history (ClipX), an Orthodox File Manager (Nomad.NET), a proper editor (GVim for Windows) was something you had to do if you wanted to have a proper work environment.

The second week, I started to take a closer look at the IDE itself, as I already received the sources I was going to work from now on. For a change, it is nice to have code completion for about every namespace, class, method or function, but is annoying not to have word completion (for strange words you have to write inside string declarations like SQL keywords).

Beside the different change in layout, one of the other things that I noticed was the lack of attention to details in user interface and the general lack of polish. For example, in the above image you can see the subtle blue break point (enabled or disabled) in XCode versus the red break point from Visual Studio. I’d rather have 10 more pixels to fit one or two characters then to have 10% unused space in a window. Also see the subtle indicator for folding area, shades of gray in XCode and square with +/– sign in Visual Studio.

A much more visible differences in Visual Studio are the items from the menu item and the layout of the sub-windows that change when you go from Edit mode into Run/Debug mode and back. It is distracting to see shapes changing in background when you should concentrate on the front window. Better to keep everything fix and in place, and let you focus on the things you’re doing.

These are the touches that makes most of Mac OS X software much more enjoyable then most of the Windows software.

Productivity on Mac

 

As many office workers, I got infected with the ‘productivity bug’. I found myself using several types of software daily (or at least weekly) on Mac (that I didn’t use while I was using Linux).

One of these types is task management, represented by OmniFocus, which helps me not to forget different task/errands. For what I use OmniFocus?

  • List of monthly rates I have to pay (car loan, assurance fees)
  • List of important things (utilities) or less important (having a haircut) I have to pay from time to time
  • List of things I’d like to buy in the new future (inline skates, shoes)
  • List of things to do (home errands)

For what I don’t use it?

  • Managing projects, although some projects are written down there, but I don’t work on them much.
  • Grocery store lists. For that I use a small post it on the fridge, where I write what I should not forget to buy on the next trip to grocery store.

Also not to forget dates/meetings/events I use iCal, the build in calendaring software. I prefer this to OmniFocus because it syncs with my iPods and with my mobile phone. OmniFocus can sync some of it’s entries to iCal too. I’ve tried this feature once, but I think I put all my to-dos in one calendar and that it become littered with entries from OmniFocus. On a screencast I’ve seen that you should sync only one of your contexts (e.g. errands) with one of the calendars, not all the contexts with one calendar (as I did).

San Francisco

 

Going from Santa Clara to San Francisco, by public transport, gives you a lot of time to think, since you have to change two light trains (VTA, BART) and one regional train (Caltrain) you’ll need roughly two and half hours from one place to another. On the way to airport, I had some time to note a few things about my experience there and about ZendCon 2008.

Coming from a small country (population of 22 millions) and an even smaller city (population of 400 000) I was impressed by the big distances Americans in these area have to travel daily. If you’re not living 5 or 10 km closer to your office, and in a big city, your daily possibilities are limited and is not practical to use the public transport for everything, because you’ll have to spend a lot of time commuting (for example this more then two hours trip by public transport could have been done in 40-50 minutes by car, there are 70km, after all). We (me and my Ciao colleagues) have lost this time when we had to go from Santa Clara to San Francisco, so we didn’t manage to see much. We’ve seen some classic tourist stuff, Golden Gate, the Cable Train, Fisherman’s Wharf, and Mission Street, a Spanish neighborhood we’ve landed in by mistake. Still, it was surreal to see all the things you see in the movies, the exterior fire stairs, the American Police cars, the Transamerica Pyramid building and other landmarks from San Francisco.

These tourist stuff happened on Saturday and Sunday, the first two days I spent here. For the rest of the week, I’ve attended ZendConf in Santa Clara.

More about the conference, in another post.

OSX vs Linux, Two Months Report

 

OSX vs Linux – 2 Months report

  • The first excuse (aka Disclaimer), for sure there are lots of things I have yet to discover or get used to them. Don’t consider this a professional opinion.
  • The second excuse, I bought the system from a company that pre-installed some software (Mac Office, Adobe Suite full), maybe they did something wrong.

I used Linux about 8 hours a day for the past 5 years. The companies that employed me allowed me to use any OS I want as long as I get my stuff done. This means I have a pretty good idea about what’s under the hood and how to tune it (read break it).

What I like in Linux:

  • moving windows with click + Alt. Don’t make me look for the title bar to move a window. Once, in OS X, one of the task windows of EasyTag got stuck under the top menu bar. I had to delete the program’s preferences file to make it move away, when using Alt + click would have been much easier.
  • lots of free software, I had almost everything I needed, especially a great software for tagging mp3s called EasyTag. Luckily, I can install this program from MacPorts (a FreeBSD ports like system), and works almost like in Linux (for whatever reason, mimetypes are not really well integrated into the OS X version).
  • installing software is easy (if you have a decent package manager system, like portage in Gentoo). Still some software might not work properly (I had some issues with Skype, KDE and sound/microphone settings).

What I don’t like in Linux:

  • there are lots of stuff, I won’t write them here explicitly, but I’ll mention some of them between the lines.

Looking from the opposite corner, I used OS X more then 8 hours a day for the past 2 and a half months. I still have to pass some exams to be considered a ‘user’.

What I like in OS X:

  • integration – Addressbook – iSync – iTunes – iPod – phone. I can have access to all my contacts from my Nokia phone, iPod or laptop.
  • integration 2 – Open a text editor, write a paragraph or two, select the text, then go to AppName –> Services –> Mail –> Send selection. There is something similar in Finder, when you select a file. Under Services Menu there are a lot of actions you can do with the selected text/file.
  • consistency (sort of), command+n, creates a new ‘thing’, command+o, opens a ‘thing’, command+shift+{} moves between tabs (except in Firefox).
  • starting a program is really easy. Press command+space, start typing and press enter when you’ve found it.
  • MarcoPolo – a great free third party software that changes different settings following certain rules. For example, mounts my network shares, sets up the screensaver to 3 minutes and turns bluetooth on when I get to work. Disables the screensaver, turns bluetooth off when I get home.
  • installing software is easy, if that software was packaged according to the rules.
  • I don’t have much options to configure. Defaults are enough for most of the cases. This allows me to concentrate on getting things done instead of playing with the settings.
  • software update is integrate in most of the apps (ala Firefox update). I don’t have to ‘emerge —sync; emerge -pv world’ weekly. Works pretty good, except when the updates break other things (for example QuickTime upgrade broke Perian subtitles).
  • the big companies actually consider making software for it. (Google Sketch, Adobe Full Suite – I don’t use it though, Y!Messenger – that works with the integrated webcam, but without sound, Skype with video).
  • Spelling is integrated in almost everything. I hear it will be integrated in KDE4 also.

What I don’t like in OS X:

  • keyboard layout – I’m using the Terminal pretty much (working remote, editing with vim), so I’m using the control (ctrl) modifier quite often (ctrl+d, ctrl+a, ctrl+e, ctrl+r, ctrl+w in vim) and I can say it’s not in the best position. A nice solution to this is to switch Caps and Ctrl (ala old Emacs gurus) and then get used to it again. It is a bit harder on the first days, but then it pays off.
  • keyboard layout 2 – I got used to Romanian Programmer Layout (alt_gr + t,s,a,i,q) on Linux and Windows. There is a similar layout on OSX, just that alt is placed on the other side on the laptop keyboard, and instead of using right hand for alt and left hand for a,s,q now I have to use the left hand for alt which is not so easy.
  • Others complained about the above too, and Apple took some steps to fix this. On latest MacBookPro models, they replace the small Enter key with Alt/Option. On the bright side, the US laptop layout looks much better then the European one, for the same laptop model.
  • keyboard layout 3 – it might look like a hardware issue, but I can’t use a normal (external) keyboard, because it lacks command key, which is heavily used by OS X.
  • clipboard history – you can have it by default (or a small setting) in KDE and Gnome. You have to pay for a program to do this in OSX (or find a free alternative that works in Leopard, like jumpcut)
  • crashes – It happened a couple of times for the laptop not to wake up from sleep, I had to power it off and power it on again. After the update from 10.5.1 to 10.5.2 it happens less often. This could be caused by a third party software (perhaps Firefox).
  • crashes 2 – Some apps refused to start when I made something wrong. For example, Mail didn’t started (crashed on start) when I created an empty ToDo item, or System Preferences’ Language Settings didn’t started (crashed on start) when I changed something in the language input list. All these changes were made from the user interface, I didn’t fiddle with configuration files or other Linux-like things. (This happened in 10.5.1, I didn’t test it again in 10.5.2).
  • command tab not switching to the first window, but to the application. This happens for Firefox, if I switch to TextMate or Finder, I get the first/last window used (assuming that I have more then one window open), when I switch back to Firefox, I get the Application, not the first/last Firefox window. This means that I need an extra command + ` (backtick) to switch to the first/last window. (I talked with a guy that had Tiger installed and Firefox worked as expected there, so it might be specific to Leopard, or particular to my system). Later note:it looks like it’s because of del.icio.us’ extension. I had an older version that appended the ‘del.icio.us’ menu item after the help menu item. I updated that extension to latest version and the problem went away.
  • installing free software from MacPorts/Fink might be a pain. When I’ve tried to install EasyTag from Fink, I noticed that one of the depending package was Tetex. This isn’t EasyTag’s fault, because it depends only on GTK, libmp3 and a couple of X11 libraries. Tetex was a level 2 or 3 dependency. Why can’t I pick what I need and what I don’t (ala emerge -pv easytag and then add or subtract dependecies) MacPorts was smarter though (minus the mimetype issue).
  • applications have a habit of stealing focus. If I click on Firefox to start and then click on Terminal to do something else, when the systems maps Firefox, it will still my focus, and if the application is set to start forced on a different space, it will switch to that space also. Don’t do that! Why doesn’t starts in background and let me continue with what I’m doing? Next to Firefox I can name Adium, Y!Messenger, and lots of other applications with this annoying habit.
  • the passion for closed formats (iTunes XML Library, plist files instead of .programrc files) and changing of Unix standards (the stuff normally started from /etc/rc.d or /etc/init.d where moved to launchd + plists, same for crontabs). This point was influenced by Mark Pilgrim’s rants about Apple closed formats.

I’ll close this short list by quoting a FAQ from alt.sysadmin.recovery:

Q: Is there any OS that doesn’t suck.
A: No.

Internet Explorer PNG Transparency Bug

Caveats to Internet Explorer PNG transparency bug

.png_image {
  background: url('/img/some_image.png') no-repeat;
  z-index:10;
}

* html .png_image {
  background: url('');
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=image, src='/img/some_image.png');
}

If you’re using the above hack to handle Internet Explorer 6 PNG fix, and you’re still seeing some light blue parts, check the following things:

  • The name of the class is the same in all the places you’re using it (in the div or img tag where you need the image, the “* html .png_image” class line and the “.png_image” class line)
  • The above CSS must be included in the HTML body/head using <style> tags. Do NOT include from an external file.
  • The path of the image is correct in both CSS definitions.
  • If you are trying to apply this hack to an <img> element, try to use a transparent pixel for <img> and set the png image from css. The image from img source is rendered above the image from css source.

A Clean Keyboard

 

While dreaming at a new Microsoft Natural Ergonomic Keyboard 4000 for my home computer, I’ve taken the time to clean up the keyboard I use at my current workplace. Since I’ve used the first MS Natural Keyboards, a couple of years back, I preffered them to normal format keyboards.

It looked so dirty because I didn’t clean it since I have bought it 4 years ago.

One of the conditions for the new company I’ve wanted to work was to let me bring in my keyboard. The other one was to let me use my OS of choice (Slackware at that time, Gentoo now).

On Writing

 

On Writing: A Memoir of the Craft by Stephen King

A non fiction book about writing. Not as grammar or style oriented as Strunk and White’s The Elements of Style (which I got for my birthday, BTW :) ), it talks about general stuff rather then details.

A couple of notes from it:

  • about his life (how he got to the first published book, Carrie, who inspired him, about his not so easy life before becoming successful)
  • about writing (he wrote short stories that sent them to various magazines during his high school and university period)
  • about his accident (happened in the same period when he was writing this book)
  • a second draft example (2nd draft = 1st draft – 10%)
  • a list of good books to read (Bill Bryson’s A Walk in the Woods, one of my favorites is there too)
  • a good writer must read a lot and write a lot

I recommend this book to anyone who’s interested in writing, as a career or hobby.

Mplayer on Windows

 

Here is how you can setup mplayer to use Central European encoding for subtitles (instead of default UTF-8):

Go to mplayer directory and edit the config file, located in mplayer subdirectory of mplayer (that will be \mplayer\mplayer\config), After the “font” line add:

subfont-encoding=iso-8859-2

While you are still there, you can add

fs=yes

to always have full screen when dropping a movie over mplayer’s icon.

Here is my mplayer config file:

vo=directx
double=yes
font=c:/windows/fonts/arial.ttf
subfont-encoding=iso-8859-2
autosync=100
framedrop=yes
quiet=yes
fs=yes

P.S. In theory, you can achieve this by replacing arial.ttf with arial-ce.ttf, just that I couldn’t manage to find arial-ce.ttf free on Internet. And it looks like arial.ttf already has the CE fonts (aka iso-8859-2) packed into it.

Python Scripts

“Python ® is a dynamic object-oriented programming language that can be used for many kinds of software development.”

Python and Nike+

It’s pretty simple to make a script to read your stats from Nike+. Use a browser like script (there is one provided above), do some query to Nike+ and retrive some XML, that you can parse using BeautifuSoup. I did this to integrate my Nike+ stats with Django. I’m planning to post what and how I did it here. If you want to see the scripts earlier, don’t hesitate to contact me via email.

Python Simple Web Browser

Ever wanted to make a script that logins to a ticket system and retrieve certain informations from there? Or browse sites that require you to subscribe? You can make this using python 2.4 very easily. Just make sure your requests are using cookielib.

Example class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class SimpleBrowser(dict):
  """ Python 2.4 simple browser. Can browse sites that require cookies too. """
  urlopen = None
  Request = None
  cj = None
  #me user agent
  headers = {'User-agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
  #no POST data
  postdata = None

  def __init__(self):
      """ Setup browser, init cookielib """
      import urllib2, cookielib
      self.cj = cookielib.LWPCookieJar()
      opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj))
      urllib2.install_opener(opener)
      self.urlopen = urllib2.urlopen
      self.Request = urllib2.Request

  def open(self,url):
      """ Open this url, return text of page. """
      req = self.Request(url, self.postdata, self.headers)
      handle = self.urlopen(req)
      return handle.read().strip()

Example usage:

bash$ python
>>> sb = SimpleBrowser()
>>> login_page = sb.open('http://www.jira.com/?os_username=juser&os_password=jpass&os_cookie=true')
>>> next_page = sb.open('http://www.jira.com/browse/')
>>> #or with POST data
>>> sb = SimpleBrowser()
>>> sb.postdata = 'os_username=juser&os_password=jpass&os_cookie=true'
>>> login_page = sb.open('http://www.jira.com/') # and so on ...

dir2m4b

In case you didn’t know, Apple’s iPod can play audiobooks. If you convert your mp3’s in their audiobook ‘format’ you can benefit from some nice features like bookmark and audiobook group (all your audiobooks are available on the audiobook menu, not on the usual music menu).

Audiobook ‘format’ actually is Apple Lossless encoded (aac, m4a) file with the extension changed to m4b. To convert a mp3 to m4b in linux you can use mpg123 to convert it from mp3 to wav and faac to convertit from wav to m4a (m4b).

I made this script to automate this conversion. Inspiration was given by Darren Kirby’s an dir2ogg.

Actually I removed the parts I thought I won’t use in my script and replaced the ogg converter with the a free implementation of Apple’s Lossless one (faac). To use this script you need python (duh!), pyid3lib, faac and mpg123.

The script tests if mpg123 exist, but unfortunately it cannot check if faac exists. I couldn’t found a combination of switches for which faac will return 0 :( and I didn’t find out how to check if a shell returned a zero status.

Usage:

bash$ dir2m4b -dr mp3director

Watermark using Python Image Library (PIL)

First, create a transparent PNG (using Gimp) that will be applied to the image, then fire up python:

bash$ python
>>> from PIL import Image
>>> fi = Image.open("filter.png") #open the filter
>>> fi.show() # have I opened the correct image? :D
>>> im = Image.open("image.jpg") #open the image
>>> im.show() # have I opened the correct image? :) 
>>> im.paste(fi,(im.size[0]-fi.size[0],im.size[1]-fi.size[1]),fi) 
>>> # paste the logo on the desired image. 
>>> # this logo will be placed on the right-down corner.
>>> im.show() # voila :)
>>> im.save("image_watermark.jpg", quality=95)

Linux Tips

 

Screenshot Blog

crontab -e 10,30,50 8-17 * * 1-5 import -window root -display :0 ~/screen-`date +%Y-%m-%d-%H-%M`.jpg

Explanation: I read somewhere, some time ago, about the idea to track of what you have done, through screenshots. For this, you can use ImageMagick’s import and a cronjob. In the above cronjob you take a screenshot every 20 minutes, from 8 to 17, Monday to Friday. At the end of the day, you can make yourself a quick review.

Note: if you are planning to publish these images, do not forget to delete the ones with the plans for world domination.

How to start 100 xeyes :).

It’s useful to make practical jokes to work fellows.

sh$ for ((i=0;i<101;i++)); do xeyes & done

Later edit: As the computers are more powerful every year, maybe 1000 xeyes would be more appropiate nowadays.

Changing keyboard layout

Use Romanian layout, with ã mapped to alt_gr + a, â mapped to alt_gr + q, …

sh$ setxkbmap -layout ro us_ro

or, on newer versions of X, try this:

sh$ setxkbmap -layout ro

Latex Hearts

1
\renewcommand{\labelitemi}{$\heartsuit$}

This is the LaTex command you use to change the bullets from default to hearts. I used it when I wrote my graduation diploma.

:)