rbenv and rubygems on a osx installation

Edit:

I had tried to install on Ubuntu, I had to install some extra packages to build ruby,

$ sudo apt-get install build-essential zlib1g zlib1g-dev zlibc libxml2 libxml2-dev libxslt-dev libssl-dev

End Edit;

I really like rvm because it has lots of great features and I never had a problem with it when I used it, but I wanted to give rbenv a try. Finally, I had a fresh installation of Lion to try it on. I followed all the steps found in the rbenv wiki, followed by downloading and installing the latest version of Ruby and RubyGems.

The following are my steps that I want to use as a reminder of what I did. I opted not to install via homebrew, which would probably have been easier, but I always like doing things the hard way first to figure out how it all works. I also had to install xcode, in order to compile new gems and ruby versions.

Installing rbenv:

~ $ git clone git://github.com/sstephenson/rbenv.git .rbenv
~ $ echo 'export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"' >> ~/.zshrc
~ $ echo 'eval "$(rbenv init -)"' >> ~/.zshrc
~ $ source ~/.zshrc

That’s it for install rbenv, now I need to install ruby.

Since I opted to install rbenv the hard way, I also opted not to install ruby-build, and download the ruby source directly from http://ruby-lang.org. I got ruby-1.9.2-p290 and ruby-1.8.7-p352 tarballs.

Installing ruby:

~/Downloads/ruby-1.9.2-p290 $ ./configure --prefix=$HOME/.rbenv/versions/1.9.2-p290
~/Downloads/ruby-1.9.2-p290 $ make && make install

Refresh rbenv after installing gems and ruby versions:

~ $ rbenv rehash

I check the version available, and then set my global preference to the latest installed version:

~ $ rbenv versions
  1.9.2-p290
~ $ rbenv global 1.9.2-p290
~ $ rbenv versions
* 1.9.2-p290

Next, I wanted to install the bundler gem, but I found out that by default ‘gem’ executable is pointing to the OSX version. After confirming I was installing into the right version of ruby, I needed to download and install rubygems.

~/Downloads/rubygems-1.8.10 $ ruby --version
ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.1.0]
~/Downloads/rubygems-1.8.10 $ ruby setup.rb

Then I need to rebuild the rbenv shim binaries to get the gem executable.

~ $ rbenv rehash

That’s it. I double checked the gem version I was running:

~ $ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 1.8.10
  - RUBY VERSION: 1.9.2 (2011-07-09 patchlevel 290) [x86_64-darwin11.1.0]
  - INSTALLATION DIRECTORY: /Users/beverlyguillermo/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1
  - RUBY EXECUTABLE: /Users/beverlyguillermo/.rbenv/versions/1.9.2-p290/bin/ruby
  - EXECUTABLE DIRECTORY: /Users/beverlyguillermo/.rbenv/versions/1.9.2-p290/bin
  - RUBYGEMS PLATFORMS:
    - ruby
    - x86_64-darwin-11
  - GEM PATHS:
     - /Users/beverlyguillermo/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1
     - /Users/beverlyguillermo/.gem/ruby/1.9.1
  - GEM CONFIGURATION:
     - :update_sources => true
     - :verbose => true
     - :benchmark => false
     - :backtrace => false
     - :bulk_threshold => 1000
  - REMOTE SOURCES:
     - http://rubygems.org/

Happy with all of that, I installed any gems I wanted. Yea, rbenv!

vmware, ubuntu, and shared folders

I use VMWare Fusion a lot, simply because I like to replicate the server configurations on my local development machine and it’s easier to have multiple instances of servers readily available with it. I also like to have my code available at all times with whichever VM I am using, so the code is shared. One thing I notice is that the VMTools defaults to using Mac’s UID/GID information, rather than the equivalent on the VM. So using these comments, I found a way to make the changes.

First, I double checked /etc/mtab to find out how it’s mounted.

$ vim /etc/mtab

and it looks like this in that file on my machine:

.host:/ /mnt/hgfs vmhgfs rw,ttl=1 0 0

I copied that and edited the /etc/fstab file and added the UID/GID for my user or whatever existing user or group. I also got from the comments that it fails mounting automatically, and that I should also add nobootwait to the parameters and manually perform unmount and mount. It really sucks but that’s what you got to deal with for the flexibility of multiple development OSes.

$ vim /etc/fstab

In the file, I added:

.host:/ /mnt/hgfs vmhgfs rw,ttl=1,uid=1000,gid=1000,nobootwait 0 0

When you restart the VM, you’ll get some errors (if you’re using the GUI, you probably won’t see it), but login and then do the following:

# umount /mnt/hgfs
# mount /mnt/hgfs

Create a script per the comments if you want. It saved me a lot of time.

Multiline comments in ruby

I have noticed a lot of books or sites don’t say how to do multi-line comments in ruby. Well, you can use the ruby documentation blocks –

=begin

and

=end

Anything in between the two lines will be “commented” out. The purpose of the above lines is for documentation though, but it’s really useful to “temporarily” block on multiple lines of code…

For true comments, do single line commenting…

Vim and Single Line Commenting

If you wanted to do single line commenting (i.e., adding # in front of the line) in Vim, use the regex search and replace.

Visually select the lines (using Shift-v and the arrow keys to select), then going into command mode by enter ‘:’ (colon), where you get something like this

:'<,'>

Then all you have to do is enter

:'<,'>s!^!#!g

Logging all queries that goes through mysql

All I want to know is what these frameworks are doing when they load a page, and they like to do a lot of things behind the scenes. Some of them allow you to log the sql out, but some of them are don’t have it or make it difficult to output. In mysql, you can turn on logging through it’s configuration, but sometimes I like to just do it via command-line without have to restart the server.

As root, enter:

SET GLOBAL general_log="ON";
SET GLOBAL log_output="TABLE";

Run through your website, and you can grab all your queries using:

SELECT * FROM mysql.general_log;

and to disable the logging, just run:

SET GLOBAL general_log="OFF";
SET GLOBAL log_output="FILE";

Reference: http://dev.mysql.com/doc/refman/5.1/en/log-destinations.html

IE7 and HTML5 video

I use Modernizr and HTML5 innerShiv to enable using html5 tags in IE. One of those tags is the video tag that will work in the latest browsers, for example from VideoJS:

    <video class="video-js" width="640" height="264" controls preload poster="http://video-js.zencoder.com/oceans-clip.png">
      <source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
      <source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm; codecs="vp8, vorbis"' />
      <source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg; codecs="theora, vorbis"' />
      <!-- Flash Fallback. Use any flash video player here. Make sure to keep the vjs-flash-fallback class. -->
      <object class="vjs-flash-fallback" width="640" height="264" type="application/x-shockwave-flash"
        data="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf">
        <param name="movie" value="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf" />
        <param name="allowfullscreen" value="true" />
        <param name="flashvars" value='config={"playlist":["http://video-js.zencoder.com/oceans-clip.png", {"url": "http://video-js.zencoder.com/oceans-clip.mp4","autoPlay":false,"autoBuffering":true}]}' />
        <!-- Image Fallback. Typically the same as the poster image. -->
        <img src="http://video-js.zencoder.com/oceans-clip.png" width="640" height="264" alt="Poster Image"
          title="No video playback capabilities." />
      </object>
    </video>

The above video is the demo video used by VideoJS. When I debugged with IE7, this video tag, it kept throwing a javascript warning.

'null' is null or not an object.

The message would repeat several times in the IE developer tools console and the only way to remove the warnings was to remove the embedded video tag. So in order to get it to work with IE7, I had to make some changes — note, if you’re going to try this code, you need Modernizr, VideoJS, and jQuery.

$(document).ready(function() {
  if (!Modernizr.video) {
     $('body').append('<a href="http://video-js.zencoder.com/oceans-clip.mp4" id="player"><img src="http://video-js.zencoder.com/oceans-clip.png" title="Click to play" /></a>');
     flowplayer('player',  'http://releases.flowplayer.org/swf/flowplayer-3.2.7.swf');
  } else {
     $('body').append('<video class="video-js" width="640" height="264" controls preload poster="http://video-js.zencoder.com/oceans-clip.png">' +
     // The rest of the video tag contents
      '</video>');
    VideoJS.setupAllWhenReady();
  }
});

The above code would just force the flowplayer as the default for browsers unable to support the video tag properly, and any other browser, the video tag would be added to the body and the videojs javascript would handle the ui interface.

VMWare Fusion, static vm ips, and port forwarding

Edit: 2011-09-14

It looks like VMWare stores all the configuration files in /Library/Preferences/VMWare Fusion directory. I recently just found this out and anything that I had mentioned earlier works now.

Edit: 2011-07-05

I decided to set the ip address statically in my VMs. In ubuntu, my VMs’ interface is called eth0 (you can check your interface’s name via ifconfig) and then edit /etc/network/interfaces, where you’d add

auto lo
iface lo inet loopback
 
# default ip, if you didn't have an alias
auto eth0
iface eth0 inet static
address 192.168.2.2
netmask 255.255.255.0
network 192.168.2.0
broadcast 192.168.2.255
gateway 192.168.2.1
 
# alias to ip
auto eth0:0
iface eth0:0 inet static
address 192.168.1.2
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1

and restart

$ sudo /etc/init.d/networking restart

Edit: 2011-06-29

I was doing some testing with the latest June 2011 beta of VMWare, and I notice the files mentioned below are missing. I’ll update the post when I figure out what the ___what___ happened.

For now I am switching between a NAT and Bridge. Using the Bridge to allow other computers on my network to access the the VM services I enabled (i.e., nginx, mysql, and ssh..etc). NAT when I’m not on a network, and a Bridge when I’m connected to a router.

I use my VMs on my machine for development and sometimes I need my vms to talk to each other. In order to keep the IPs static, I had to do the following:

  1. Edit /Library/Application Support/VMWare Fusion/vmnet8/dhcpd.conf
  2. Add the following to the very end:
    host VirtualHost {
         hardware ethernet 00:50:56:3e:fa:ff;
         fixed-address 172.16.230.xxx;
    }
    
  3. Change VirtualHost to the name of my virtual host and the fixed-address to the IP I want.

Another thing I want is to be able to use http://localhost or http://127.0.0.1 on my machine in order to allow other computers in my network to access my dev machines web servers. In order to do this, you need to setup NAT forwarding.

  1. Edit /Library/Application Support/VMWare Fusion/vmnet8/nat.conf
  2. Add the following under the [incomingtcp] section:
    # the static ip address you had assigned earlier
    80 = 172.16.230.xxx:80
    

After all the configuration changes, reboot vmware fusion either by restarting the app or running this in the terminal:

$ sudo "/Library/Application Support/VMware Fusion/boot.sh" --restart

Displaying frame rates in Flash

Sample frame rate code.

package {
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.text.TextField;
  import flash.utils.getTimer;
 
  public class FPS_Test extends Sprite {
    private var ball:Sprite;
    private time:int;
    private last_time:int;
    private fps:TextField;
 
    public function FPS_Test() {
      time = 0;
      last_time = 0;
      fps = new TextField();
      addChild(fps);
 
      ball = new Sprite();
      addChild(ball);
 
      ball.graphics.beginFill(0xff0000);
      ball.graphics.drawCircle(0, 0, 40);
      ball.graphics.endFill();
      ball.x = 20;
      ball.y  = stage.stageHeight / 2;
 
      addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
 
  public function onEnterFrame(e:Event):void
  {
    time = getTimer();
    fps.text = "fps: " + (1000 / (time - last_time));
    ball.x++;
    last_time = getTimer();
  }
}

Using Ubuntu, Nginx, FastCGI, Python, and Django

NOTE: No security on the following, this was for development purposes just to learn python and django.

From a fresh install of Ubuntu Meerkat (10.10), I added the following packages:

  1. python
  2. python-flup
  3. python-mysqldb
  4. nginx

After making sure, python is installed and installing the django framework sdk. I check to make sure django will use fastCGI by running the following:

$ cd [path to django project root]
$ python manage.py runfcgi host=127.0.0.1 port=8080 --settings=settings

And to kill the process:

$ pkill -f "python manage.py runfcgi host=127.0.0.1 port=8080 --settings=settings"

Next, is to edit nginx configuration, adding the new django site to nginx’s sites-enabled directory:

  server {
    listen 80; 
    server_name localhost;
 
    location ~* ^.+.(js|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|mov) {
      rroot /media/;
      access_log   off;
      expires      30d;
    }   
    location / { 
          # host and port to fastcgi server
          fastcgi_pass 127.0.0.1:8080;
          fastcgi_param PATH_INFO $fastcgi_script_name;
          fastcgi_param REQUEST_METHOD $request_method;
          fastcgi_param QUERY_STRING $query_string;
          fastcgi_param CONTENT_TYPE $content_type;
          fastcgi_param CONTENT_LENGTH $content_length;
          fastcgi_pass_header Authorization;
          fastcgi_intercept_errors off;
      }   
      access_log  /var/log/nginx/localhost.access_log main;
      error_log  /var/log/nginx/localhost.error_log;
  }

Then, restarting nginx and then trying to access the site via the regular port 80 access.

Specific file extensions in cakePHP

How to do it is scattered and buried in various places for CakePHP for various versions of the framework. Some of the older methods were complex and really wouldn’t work anymore for the latest build, which is 1.3. So I figured out that 1.3 does make it easier and I finally learned how to use http://domain.com/controller/view.xml as a request or link in a few steps. First, I need to add to routes.php in the config directory:

Router::parseExtensions('xml');

and in the app_controller, add the component, so it’ll know what layout and view to use:

var $components = array('RequestHandler');

This XML layout and view is standard with CakePHP in 1.3 but if I wanted to add a .json extension with the content-type set as “application/json” — add the json extension to the router, and then add json folder to the views/layout path. In the json folder, add default.ctp and it’s contents are:

<?php
header("Pragma: no-cache");
header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
header('Content-Type: application/json');
header("X-JSON: ".$content_for_layout);
echo $content_for_layout;
?>

And to pass parameters like http://domain/controller/view/param1/param2, I need to add the extension I want to use at the very end. So the parameter in the request, or in the link, becomes http://domain/controller/view/param1/param2.json or whatever other extension.

Also, in addition to adding the component to the app_controller, if I wanted to remove the debugging output that gets generated at the bottom of the json or xml for requests that pass ‘HTTP_X_REQUESTED_WITH’ == ‘XMLHttpRequest’ in the header, I added to my beforeRender method in the app_controller.

public function beforeRender() {
if ($this->RequestHandler->isAjax())
Configure::write('debug',0);
}