Hi. I’m Rudy Lee.

Here are some thoughts of mine.

AngularJS Free Course by Code School

I spent some time last weekend to run through the new AngularJS course by Code School. This course is sponsored by Google which means you don’t need to pay for the Code School membership to play this course. AngularJS is one of the popular Javascript frameworks to build single page app application. You might be familiar with other frameworks such as Ember.js, Backbone.js and Knockout.js.

I really enjoyed the course and definitely learned something new from it. It covers the basic concept about directives, services and dependecy injection. I didn’t understand those features when the first time I learned about AngularJS. In the beginning of learning AngularJS, I tended to copy and paste code without understanding the meaning behind it. This caused confusion when I tried to learn more about the framework.

Code School also released another screencast on how to build AngularJS app from scratch. I’ll suggest you to check that one out as well so you can apply the knowledge that you have learned from the course to build real application. However, you have to become the member to get access to the screencast. There are also some other websites that provide AngularJS videos such as http://www.egghead.io and http://www.thinkster.io


Configuring Elastic Beanstalk Environment with .ebextensions

At Captiv8, we are using Amazon AWS to host most of our PHP projects. We are heavily rely on Elastic Beanstalk to help us set up PHP environment, database and load balancer. On top of that, we are also managing our own server image based on Amazon AMI. In this image, we installed additional software and packages that we need for our application. However, this approach has a drawback as it is difficult to maintain the image and track changes. Everytime you need to update the image, you have to create new server, install the new software and export it into new image. This will leave you with bunch of different images and it is hard to tell what are the things that have changed inside each image.

Chef

In order to solve these problems, I decided to find a way to automate the process. My first attempt was trying to use Chef to provision the Elastic Beanstalk environment. I have been using Chef for a while to provision my Vagrant machines. It is powerful and more convenient in compare with bash scripts. Since I am already familiar with Chef, I started looking at tutorials on how to use Chef with Elastic Beanstalk. Most of the tutorials that I found don’t provide easy way to integrate Chef with Elastic Beanstalk. One of them mentions about using AWS OpsWorks with Chef but I think it is overkill for the time being. So, I ditched Chef and start looking for another solution.

ebextensions

ebextensions is another solution that I found after checking the official documentation of Elastic Beanstalk. With this solution, you need to to create .ebextensions folder inside your project and create a file to define what are the packages that you want to install into the environment. Elastic Beanstalk will automatically run the script every time you deploy a new version of the application. Aside from that, you can also tell ebextensions to execute shell script in the instance or changing permission of a file. You can read more details about ebextension here: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html

This is my directory structure at the moment:

Here is the example of my ebextension config file:

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
packages:
  yum:
    mlocate: []

commands:
  01updateComposer:
    command: export COMPOSER_HOME=/root && /usr/bin/composer.phar self-update
  02updateTag:
    command: ec2-create-tags $(ec2-metadata -i | cut -d ' ' -f2) --tag Project=ChangeThis
    cwd: /home/ec2-user
    env:
      EC2_HOME: /opt/aws/apitools/ec2
      EC2_URL: https://ec2.ap-southeast-2.amazonaws.com
      JAVA_HOME: /usr/lib/jvm/jre
      PATH: /bin:/usr/bin:/opt/aws/bin/

container_commands:
  01-command:
    command: updatedb
  02-command:
    command: rm -rf /captiv8/.ebextensions
  03-command:
    command: mkdir -p /captiv8/.ebextensions
  04-command:
    command: cp -R .ebextensions/* /captiv8/.ebextensions/
  05-command:
    command: bash /captiv8/.ebextensions/scripts/app-setup.sh

option_settings:
  - namespace: aws:elasticbeanstalk:application:environment
    option_name: COMPOSER_HOME
    value: /root

And this is the example of my bash script:

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
67
68
#!/usr/bin/env bash

#
# References: 
# - http://www.hudku.com/blog/configuration-setup-customizing-aws-elastic-beanstalk/
# - http://www.hudku.com/blog/security-credentials-customizing-aws/#.elastic-beanstalk-app
#

# Main configuration, change these for each project
appName="change_this"
newrelicLicense="newreliclicense"

# Check if this is the very first time that this script is running
if ([ ! -f /root/.not-a-new-instance.txt ]) then
  newEC2Instance=true
fi

# Install applications if this is new instance
if ([ $newEC2Instance ]) then
    # Allow sudo command to be used as part of beanstalk ebextensions scripts without a terminal
    grep -q 'Defaults:root !requiretty' /etc/sudoers.d/$appName || echo -e 'Defaults:root !requirettyn' > /etc/sudoers.d/$appName
    chmod 440 /etc/sudoers.d/$appName

    # Add sudo command if not already present to .bashrc of ec2-user so that we are logged on as root when we use ssh
    grep -q "sudo -s" /home/ec2-user/.bashrc || echo -e "nsudo -sn" >> /home/ec2-user/.bashrc

  # Install phpMyAdmin
  yum -y --enablerepo=epel install phpmyadmin
  rm /etc/httpd/conf.d/phpMyAdmin.conf
  rm /etc/phpMyAdmin/config.inc.php
  mv /captiv8/.ebextensions/templates/phpMyAdmin/phpMyAdmin.conf /etc/httpd/conf.d/
  mv /captiv8/.ebextensions/templates/phpMyAdmin/config.inc.php /etc/phpMyAdmin/
  chmod 644 /etc/httpd/conf.d/phpMyAdmin.conf
  chmod 644 /etc/phpMyAdmin/config.inc.php
  service httpd restart

  # Install New Relic
  rpm -Uvh http://yum.newrelic.com/pub/newrelic/el5/x86_64/newrelic-repo-5-3.noarch.rpm
  yum -y install newrelic-php5
  echo -ne '\n\' | newrelic-install install
  rm /etc/php.d/newrelic.ini
  mv /captiv8/.ebextensions/templates/newrelic/newrelic.ini /etc/php.d/
  chmod 644 /etc/php.d/newrelic.ini
  perl -pi -e "s/PHP Application/$appName/g" /etc/php.d/newrelic.ini
  perl -pi -e "s/newrelicLicense/$newrelicLicense/g" /etc/php.d/newrelic.ini

  # Install New Relic Server Monitor  
  yum -y install newrelic-sysmond
  nrsysmond-config --set license_key=$newrelicLicense
  /etc/init.d/newrelic-sysmond start
  service httpd restart

  # Install OSSEC
  yum -y install mysql-devel postgresql-devel
  wget http://www.ossec.net/files/ossec-hids-2.7.1.tar.gz -P /captiv8
  tar xzvf /captiv8/ossec-hids-2.7.1.tar.gz -C /captiv8
  rm /captiv8/ossec-hids-2.7.1.tar.gz
  rm /captiv8/ossec-hids-2.7.1/etc/preloaded-vars.conf
  mv /captiv8/.ebextensions/templates/ossec/preloaded-vars.conf /captiv8/ossec-hids-2.7.1/etc/
  /captiv8/ossec-hids-2.7.1/install.sh
  /var/ossec/bin/ossec-control start
fi

# If new instance, now it is not new anymore
if ([ $newEC2Instance ]) then
    echo -n "" > /root/.not-a-new-instance.txt
    chmod 644 /etc/php.d/.not-a-new-instance
fi

Inside my ebextensions config file, I call the shell script which will install additional software. The benefit of using shell script is you have more options and it is much easier to customize the software. Since the .ebextensions folder is copied to the instance, you can tell the shell script to copy a template config file that you have prepared before hand. I hope you find this blog post useful.


Audio Books

Around October 2013, I decided to move from city to Gladesville which located 9 kilometres north-west of Sydney CBD. It wasn’t easy decision because I need to spend about 2 hours on commuting every day.

For the first couple months, I was fine with the long commuting time. I can distract myself with my phone or sleeping on the bus. However, I started to feel unproductive and I can feel it affected my concentration throughout the day. So, I tried to do some research on the Internet about productive use of commuting time. Most of the articles that I found suggesting to listen either podcasts or audio books.

I decided to give audio book a try and downloaded Eat That Frog by Brian Tracy. If you are interested, you can easily find the audio version of this book on YouTube. At first, I was a little bit skeptical with the result but it turned out to be really helpful. I can feel that my life is back on track again. I started to use Trello to keep my list of tasks. Once in a while, I’ll update my goals and create separate boards for each project.

Since that, I have been listening to several different audio books such as 168 hours by Laura Vanderkam and Getting things done by David Allen. Both of them shared some basic principles from Eat that frog. I prefer Getting things done because it’s more straight forward and focusing on actions you can perform to improve your productivity. Although, I can say that this book is quite to hard understand so you need to listen it more than once to get a good grasp of the concept.


Add ‘current’ class to website menu in Rails

It is common to have ‘active’ state or ‘current’ state on website navigation. This will help visitors to know which page they have selected.

This solution is based on Stackoverflow’s question which I couldn’t find. First, I’ll create a method inside Rails application_helper.rb file. I’ll call this method cp(). Here are the syntax:

application_helper.rb
1
2
3
4
5
6
module ApplicationHelper
  def cp(path)
    current_route = Rails.application.routes.recognize_path(path)
    "current" if current_page?(path) or params[:controller] == current_route[:controller]
  end
end

The method uses current_page and Rails.application.routes.recognize_path to get information about current page.

After that we can use it in our view. Here is the example:

application.html.erb
1
2
3
4
5
6
7
8
9
<nav id="menu-panel">
    <%= link_to 'SERVICES', services_path, class: cp('/services') %>
    <%= link_to 'FACILITIES', facilities_path, class: cp('/facilities') %>
    <%= link_to 'ABOUT', about_path, class: cp('/about') %>
    <%= link_to 'CAREERS', careers_path, class: cp(careers_path) %>
    <%= link_to 'BLOG', blog_index_path, class: cp(blog_index_path) %>
    <%= link_to 'CONTACT', contact_path, class: cp('/contact') %>
    <a href="#" id="close-menu-panel"><b>CLOSE</b></a>
</nav>

I hope that helps.


Install the latest tmux on Ubuntu 12.04

In this post, I will show you how to install the latest tmux on Ubuntu 12.04. First, we need to install ‘add-apt-repository’ command on our machine. This can be done by running these commands:

1
2
3
sudo apt-get install software-properties-common

sudo apt-get install python-software-properties

After that you can add third party repository ( Ubuntu official repository does not have latest tmux at the moment )

1
2
3
4
5
sudo add-apt-repository ppa:pi-rho/dev

sudo apt-get update

sudo apt-get install tmux

I will suggest you to install the latest tmux if you want to use Tmuxinator.


Rails 4 CORS

McFlyyy pointed out another solution to solve this problem. I haven’t tested it myself so follow it at your own risk Click Here

I was trying to use Rails to build REST API for my AngularJS app and came across CORS error on my Chrome developer tools.

According to Alexey Vasiliev, Cross-origin resource sharing (CORS) is a web browser technology specification which defines ways for a web server to allow its resources to be accessed by a web page from a different domain. Such access would otherwise be forbidden by the same origin policy. CORS defines a way in which the browser and the server can interact to determine whether or not to allow the cross-origin request. It is a compromise that allows greater flexibility, but is more secure than simply allowing all such requests. CORS is supported in the following browsers:

After following couple of outdated tutorials, I found the quick solution for it. Here are the steps:

Add route to handle OPTIONS method

AngularJS using OPTIONS method to check the CORS support on the API server. Thus, you need to add line in your route file to handle this. This can be done by adding code like this

1
2
match 'users', to: 'users#index', via: [:options]
resources :users

To check whether your configuration is correct you can run ‘rake routes’. It should print out something like this:

1
2
3
4
5
6
7
8
9
10
11
   Prefix Verb    URI Pattern               Controller#Action
    users OPTIONS /users(.:format)          users#index
          GET     /users(.:format)          users#index
          POST    /users(.:format)          users#create
 new_user GET     /users/new(.:format)      users#new
edit_user GET     /users/:id/edit(.:format) users#edit
     user GET     /users/:id(.:format)      users#show
          PATCH   /users/:id(.:format)      users#update
          PUT     /users/:id(.:format)      users#update
          DELETE  /users/:id(.:format)      users#destroy
     root GET     /                         users#index

You can see on the second line, we handle OPTIONS verb and redirect to index action.

Add before_filter and after_filter to allow CORS

The next step is we need to return proper header to tell AngularJS that our server allow CORS. Here is the sample controller file:

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
UsersController < ApplicationController

  skip_before_filter :verify_authenticity_token
  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers

  # For all responses in this controller, return the CORS access control headers.
  def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
    headers['Access-Control-Max-Age'] = "1728000"
  end

  # If this is a preflight OPTIONS request, then short-circuit the
  # request, return only the necessary headers and return an empty
  # text/plain.

  def cors_preflight_check
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
    headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version'
    headers['Access-Control-Max-Age'] = '1728000'
  end

  def index
    @users = User.all

    respond_to do |format|
      format.json { render :json => @users }
    end
  end
end

We need to add skip_before_filter :verify_authenticity_token because Rails will return 422 status code and error message ‘Can’t verify CSRF token authenticity’


Fix npm symlink problem in Vagrant

Today I encountered weird symlink error when trying to install npm modules on my Vagrant Ubuntu Box. The error says “pm ERR! Error: UNKNOWN, symlink ‘../which/bin/which’”.

After quick Google, it turns out the problem is caused by npm trying to create symlink which is not supported on Windows ( I am using Windows 8 as my host machine ).

Here is quick solution which allow you to install npm modules without creating any symlinks :

1
npm install --no-bin-link

Trailing Characters ^M

This morning I ran into weird errors in one of my Vagrant machine. These errors come up when I was trying to run tmux and vim:

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
Error detected while processing /home/vagrant/.vimrc:
line    1:
E488: Trailing characters: nocompatible^M
line    2:
E488: Trailing characters: nu^M
line    3:
E488: Trailing characters: nobackup^M
line    4:
E488: Trailing characters: nowritebackup^M
line    5:
E488: Trailing characters: noswapfile^M
line    6:
E488: Trailing characters: hlsearch^M
line    7:
E488: Trailing characters: ruler^M
line    8:
E474: Invalid argument: laststatus=2^M
line    9:
E474: Invalid argument: tabstop=2^M
line   10:
E474: Invalid argument: shiftwidth=2^M
line   11:
E488: Trailing characters: expandtab^M
Vim: Error reading input, exiting...ne down, b/u/k: up, q: quit
Vim: Finished.
Press ENTER or type command to continueVim: Error reading input, exiting...
Vim: Finished.

I had similar problem before when trying to edit file from Windows machine inside vim. ^M character is added by Windows machine to mark newline in their files.

The quickest solution to fix this is by installing ‘dos2unix’ software and let it fix the file for you. In Ubuntu, you can run this command to install it:

1
sudo apt-get install dos2unix

And fix the file by typing this:

1
dos2unix <file>

It will automatically remove all the trailing characters for you.


Zeus inside Vagrant

I have been using Vagrant as my development environment for the last couple weeks. It has been very helpful as I prefer to use Linux as my development environment and I can only use Windows at my workplace. With Vagrant, I can easily set up new Linux machine, install Vim plus all the plugins and completely separate development machine for each project.

Today, I was trying to use Zeus to speed up my Rspec test inside one of the Rails projects. I found this gem through a screencast by Ryan Bates about Fast Test.

Install Zeus is pretty easy, you just need to run this command on your terminal:

1
gem install zeus

After that you just need to go to the directory of your Rails app and start the Zeus server using this command:

1
zeus start

However, when I got an error when I was trying to ran that command. Here is the error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vagrant@precise32:/vagrant$ zeus start
Starting Zeus server
[ready] [crashed] [running] [connecting] [waiting]
boot
└── default_bundle
    ├── development_environment
    │   └── prerake
    └── test_environment
        ├── cucumber_environment
        └── test_helper

Available Commands: [waiting] [crashed] [ready]
zeus rake
zeus runner (alias: r)
zeus console (alias: c)
zeus server (alias: s)
zeus generate (alias: g)
zeus destroy (alias: d)
zeus dbconsole
zeus cucumber
zeus test (alias: rspec, testrb)
It looks like Zeus is already running. If not, remove .zeus.sock and try again.

You can see on the last line that it’s complaining something about .zeus.sock. The solution is pretty easy, you just need to add environment variable to your Vagrant machine. Use this command to add environment variable:

1
export ZEUSSOCK=/tmp/zeus.sock

Also make sure you use the latest version of Zeus. Add this line inside your Gemfile:

1
2
# Use Zeus
gem 'zeus', ">= 0.13.4.pre2"

Run ‘bundle’ to install the gem and update the version. You should be able to run ‘zeus start’ now and other Rails commands.


Refocus

For the past few weeks, it seems that I am gradually moving into different life style. When I was still doing my course, it is almost impossible to spend time on the weekend to catch up with friends, walking around city and trying new stuff. Everytime my friend invited me to hang out with them I always said that I need to finish my assignments.

I remember one of my friend just asked me this question “So, what you usually do during the weekend ?” and I answered “Nothing, just doing my assigments and other stuff”. He looked surprised with my answer and he added “Don’t you have any friends that invited you out ?”. At that time, I realized that he must be feel pity and I said to him “At first, you might feel that your life is unfair. You will think that why everyone can enjoy their life during the weekend and you trapped here working on your assigments. But, as the time goes you start to get used to it”.

So for the past few months, I have been doing the same routine. Working and attending the class during the weekdays, working on assignments with my group mate during the weekend. Sometimes, I felt stress and tired but believe me, that was the most meaningful moment in my life. During that time, I definitely know what I am going to do everyday and somehow I feel more motivated.

The problem with my current life style is I have more control on what I can do during my free time. I can decide that I can go to cinema, buy clothes, trying this new restaurant or just spending whole day watching YouTube. However, I started to feel that this path is leading me to different direction. The direction that I might regret in the future.

That is why I decided to start writing more blog posts about my life experience. I believe writing blog post can help you refocus on what you should do.