Skip to main content
Posts by:

Tim Booher

April 2011 Server Setup with Rails, Ubuntu, Nginx, RVM, Capistrano, Git

There are other blogs out there telling you how to set up a ruby
server install with ubuntu, nginx, passenger, linode ubuntu 10.10, and
rvm, but I couldn’t find one that put it all together, so I decided to
share my setup from beginning to end. I hope it helps you, but I’m also
hoping you point out any risks that I should clean up.

In general I used “this blog”:http://thekindofme.wordpress.com/2010/10/24/rails-3-on-ubuntu-10-10-with-rvm-passenger-and-nginx/, but it didn’t have everything I needed, as you’ll see below.

First I set up the server in linode, then connected to the server via a simple ssh command:

ssh root@tacitus

Before anything else, I had to install basic applications such as git and emacs.

sudo apt-get install git emacs

I decided that I wanted to set up everything from the perspective of
the deployment user, passenger, so I created the user *passenger*:

root@li289-94:~# useradd -d /home/passenger -m passenger
root@li289-94:~# usermod -s /bin/bash passenger
root@li289-94:~# passwd passenger

Now set-up ssh with public keys to enable password free auto-connection. Then I logged in as root, added passenger
to sudoers (using visudo and adding passenger to the sudo group) and started getting ready to install the ruby
version manager, _rvm_.

bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

Now, we need to load RVM into a shell session to .bashrc to make the rvm executables available.

added [[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"

To prevent restarting the shell, you can run source ~/.rvm/scripts/rvm to get everything up and running immediately.
In order to test the installation, I tried ‘type rvm | head -n1’ to be sure rvm was a function available to the user.

Now, it’s time to get everything ready for the heavy lifting a ruby install requires.

sudo aptitude install build-essential bison openssl libreadline5
libreadline5-dev curl git-core zlib1g zlib1g-dev libssl-dev
libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libmysqlclient-dev

And I forgot to run (needed later):

sudo apt-get install libxslt-dev libxml2-dev libcurl4-openssl-dev

And the big moment, the chance to let rvm handle the latest ruby. (This will take a good bit of time.)

rvm install 1.9.2
rvm use 1.9.2 --default

With 1.9.2 installed, we can now configure passenger and create a gemset for our project

rvm 1.9.2 --passenger
rvm gemset create polco
rvm 1.9.2@polco
gem install passenger
rvmsudo passenger-install-nginx-module

Stick with the passenger defaults. The only problem was on rvm 1.9.2 –passenger, I get:

NOTE: If you are using Passenger 3 you no longer need the passenger_ruby,
use the wrapper script for your ruby instead (see 'rvm wrapper')

This was a new one to me, and made me deviate from the script I was following. I am using passenger-3.0.6, so I am definitely more up to date than the server. Due to this error, I broke with the blog I was following and did not change passenger_ruby to:

passenger_ruby /home/wayne/.rvm/bin/passenger_ruby;

This might still be something I need to do since I am currently getting a 404. I am changing back to this — which for me is the following line:

passenger_ruby /home/passenger/.rvm/bin/passenger_ruby;

h3. Passenger Configuration

gem install passenger

I chose, “compile and install nginx for me” and chose default install
locations. This takes a good while too. Now it was time to set up
nginx.

The installer told me that my configuration was already modified and
that:

This installer has already modified the configuration file for you! The
following configuration snippet was inserted:

  http {
      ...
      passenger_root /home/passenger/.rvm/gems/ruby-1.9.2-p180/gems/passenger-3.0.6;
      passenger_ruby /home/passenger/.rvm/wrappers/ruby-1.9.2-p180/ruby;
      ...
  }

To prepare for the deployment, I made the deployment directory:

mkdir -p /home/passenger/polco/public/;

So now I set up a basic server with the following nginx.conf file:

worker_processes  1;
 error_log /var/log/nginx/error.log debug;

 events {
    worker_connections  1024;
 }

 http {
    passenger_root /home/passenger/.rvm/gems/ruby-1.9.2-p180/gems/passenger-3.0.6;
#    passenger_ruby /home/passenger/.rvm/wrappers/ruby-1.9.2-p180/ruby;
    passenger_ruby /home/passenger/.rvm/bin/passenger_ruby;

    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    server {
      listen 80;
      server_name localhost;
      root /home/deployer/polco/public;
      passenger_enabled on;
   }

 }

And I installed an nginx start script:

cd
git clone git://github.com/jnstq/rails-nginx-passenger-ubuntu.git
sudo mv rails-nginx-passenger-ubuntu/nginx/nginx /etc/init.d/nginx
sudo chown root:root /etc/init.d/nginx

Now I am making a single-user git repo:

mkdir ~/polco.bare.git/
cd polco.bare.git/
git init --bare

So I could now run from my desktop:

git push ssh://passenger@tacitus/~/polco.bare.git/ master

Everything worked . . . so far, now for the hard part . . .

h3. Deployment Setup

On the server, installed the bundler gem (under the current rvm: ruby 1.9.2@polco) and put together the following deploy.rb with Capistrano.

 $:.unshift(File.expand_path("/home/tim/.rvm/lib"))
 require 'rvm/capistrano'
 set :rvm_ruby_string, '1.9.2'
 set :rvm_type, :user
 require 'bundler/capistrano'
 set :application, "polco"
 role :web, "66.228.39.94"
 role :app, "66.228.39.94"
 role :db,  "66.228.39.94", :primary => true
 default_run_options[:pty] = true
 ssh_options[:forward_agent] = true
 set :deploy_to, "/home/passenger/polco"
 set :deploy_via, :remote_cache
 set :user, "passenger"
 set :use_sudo, false
 set :scm, :git
 set :scm_username, "passenger"
 set :repository, "ssh://tim@tacitus/~/polco.git/"
 set :branch, "master"
 set :git_enable_submodules, 1
 namespace :deploy do
  task :start, :roles => :app do
    run "touch #{current_path}/tmp/restart.txt"
  end

  task :stop, :roles => :app do

  end

  desc "Restart Application"
  task :restart, :roles => :app do
    run "touch #{current_path}/tmp/restart.txt"
  end

  desc "Symlink shared resources on each release - not used"
  task :symlink_shared, :roles => :app do

  end
 end

p. after ‘deploy:update_code’, ‘deploy:symlink_shared’

Then, from my desktop, I built the necessary directories and tested my deployment.

cap deploy:setup
cap deploy

The deployment works fine, and I know I still need to get my databases setup on the server for everything to run correctly. But I am surprised that nginx gives me a 404.

2011/04/11 22:52:00 [notice] 20057#0: signal 17 (SIGCHLD) received
 2011/04/11 22:52:00 [notice] 20057#0: worker process 20059 exited with code 0
 2011/04/11 22:52:00 [notice] 20057#0: exit
 2011/04/11 22:52:00 [notice] 20712#0: using the "epoll" event method
 2011/04/11 22:52:00 [notice] 20712#0: nginx/0.8.54
 2011/04/11 22:52:00 [notice] 20712#0: built by gcc 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
 2011/04/11 22:52:00 [notice] 20712#0: OS: Linux 2.6.38-linode31
 2011/04/11 22:52:00 [notice] 20712#0: getrlimit(RLIMIT_NOFILE): 1024:1024
 2011/04/11 22:52:00 [notice] 20752#0: start worker processes
 2011/04/11 22:52:00 [notice] 20752#0: start worker process 20753
 2011/04/11 22:52:16 [error] 20753#0: *1 "/home/deployer/polco/public/index.html" is not found (2: No  such file or directory), client: 71.191.237.168, server: localhost, request: "GET / HTTP/1.1", host:  "tacitus"

Please let me know of any comments or ways I can improve my setup. Also, I would love to hear if any of this was helpful for you.

By 0 Comments

reserve questions outstanding

The mechanics are difficult enough to figure out along with the big picture understanding of how an IMA is different than an traditional reservist. Here are some of the confusions I had to come to terms with:

* What is the difference between the reserve and the guard? Why do both exist?
* Do you get paid or not?
* Can I be deployed?
* How do I get an assignment?
* How do I transfer an assignment?
* How do I see my different categories in the vMPF?
* What are points? How does the system ‘work’?

By 0 Comments

Building Plots in Visio

I like to automate Microsoft Visio to draw plots. They look so much more professional and I don’t have to fuss with Excel to figure out how to make plots look like I want. Again, this is me practicing my theory of being in control, not letting software control us. Please let me know if this code is useful.

This is my source code for the stacked bar plot I’m working on. Built as an excel Macro, it automates Visio to build the plot.


Sub BuildPlot()

Dim oApp As Excel.Application
Dim oWB As Excel.Workbook

Dim oDoc As Visio.Document
Dim oPage As Visio.Page
Dim oShape As Visio.Shape

Set oApp = New Excel.Application
Set oWB = oApp.Workbooks.Open("c:usersTimothy.BooherDesktopbar_plot_macro.xlsx")

Set oDoc = Application.ActiveDocument
Set oPage = Application.ActivePage

Dim iRow As Integer
Dim iAircraft As Integer
Dim x As Double
Dim x_prime As Double
Dim y As Double
Dim y_prime As Double
Dim xWidth As Double
Dim xGap As Double
Dim iStart As Integer

xWidth = 0.5
xGap = 2
x = 0
iStart = 2

For iAircraft = iStart To (iStart + 4)
    Debug.Print oWB.sheets(1).Cells(iAircraft, 2).Value
    x = x + xGap
    x_prime = x + xWidth
    y_prime = 0
    For iRow = 7 To 4 Step -1
        Debug.Print oWB.sheets(1).Cells(iAircraft, iRow).Value
        If oWB.sheets(1).Cells(iAircraft, iRow).Value > 0 Then
            y = y_prime
            y_prime = y + oWB.sheets(1).Cells(iAircraft, iRow).Value * 0.8
            Set oShape = oPage.DrawRectangle(x, y, x_prime, y_prime)
            oShape.Cells("FillForegnd") = 8 - iRow
        End If
    Next iRow
Next iAircraft

oWB.Close

Set oApp = Nothing

End Sub

Sub draw_axes()

Dim oDoc As Visio.Document
Dim oPage As Visio.Page
Dim oShape As Visio.Shape

Set oDoc = Application.ActiveDocument
Set oPage = Application.ActivePage

Dim i As Integer ' y
Dim j As Integer ' x

Dim numX  As Integer
Dim numY As Integer
Dim max_val As Double
Dim y_new As Double

numX = 10
numY = 10

max_val = 15 ' Ceiling(14.65)

' draw y axis
oPage.DrawLine 0, 0, 0, max_val

y_new = 0

For i = 1 To numY + 1
 'DrawLine
 oPage.DrawLine 0, y_new, 0.1, y_new
 y_new = y_new + max_val / numY
Next i

' draw x
'For j = 1 To numX
'
'Next j


End Sub

Sub build_conf_intervals()

Dim oDoc As Visio.Document
Dim oPage As Visio.Page
Dim oShape As Visio.Shape

Set oDoc = Application.ActiveDocument
Set oPage = Application.ActivePage

Dim iAircraft As Integer
Dim iBar As Integer

Dim y(1) As Double
delta_x = 3
to_wit = 0.1

x = 0

For iAircraft = 16 To 20
    x = x + delta_x
    For iBar = 2 To 3
        y(iBar - 2) = oWB.sheets(1).Cells(iAircraft, iBar).Value
    Next iBar
    oPage.DrawLine x, y(0), x, y(1)
    oPage.DrawLine x - to_wit, y(1), x, y(1)
    oPage.DrawLine x - to_wit, y(0), x, y(0)
Next iAircraft

oWB.sheets(1).Cells(iAircraft, 2).Value

End Sub

By 0 Comments

MacBook Local Link Address — can it be fixed?

I have a bad, bad networking problem with my wife’s MacBook. She can’t connect to the internet anywhere, but I am working with my Netgear WNDR3300 for this problem.

Our wireless router is dd-wrt based (open source router firmware), and works fine for about 20 other nodes (other laptops, guests, mobile devices, etc). A couple of details were helpful to start with. Her MacBook has three interfaces: en0, en1, and fw0.

* en0 Ethernet Network Interface Controller (NIC)
* en1 WiFi Network Interface Controller (NIC)
* fw0 Firewire Networking

The second interface (en1) got an ip of 196.254.153.219 which is a link local address (anything in 169.254.0.0/16) is supposed to mean I didn’t get an ip from the DHCP server. However, I could see 192.168.1.1, which is the local ip for the router. This confused me, because it told me that the radio was working and I was getting some type of ip, ah I just don’t understand how I could get a partial connection to the router.

So I went into the Airport Utility, and got the message that AirPort Utility was unable to find any wireless devices. What! With a connection to the router? I was still confused. How was this possible. I was thinking about trying to reseat the wireless card, looking at buying a new wireless card, but that seemed like a lot of work that might not even solve the problem.

Edit, the Airport Utility is not for wireless interfaces, it is for Airport Express/AppleTV like items

I tried to ping google’s DNS server 8.8.8.8, with no host.

The first thing I did was try to manually set the IP to avoid a DHCP error, but the Airport utility said I had no devices.

I was able to ping the wan port (172.27.35.xxx), but I couldn’t ping the public ip (74.96.53.xxx) from verizon dsl. So I thought the issue was my modem, but the laptop was having connection problems, so I figured I had to reseat the wireless card. OSX said I had an AirPort extreme, Broadcom BCM43xx 1.0 (5.10.91.21). Hmm . . . looks connected, but maybe it’s loose? I’m out of options.

From the software, I found my wife has a MacBook5,2, which from Wikipedia is the MB881*/A with the Integrated 802.11a/b/g and draft-n (n enabled). I followed the guide here to confirm the card is in correctly: http://www.ifixit.com/Guide/Repair/Installing-MacBook-Core-2-Duo-AirPort-Card/519/ and everything checked out just fine. That took a lot of time, but confirmed that the card is not loose.

Then I went into network settings and set a manual ip. My subnet mask is 255.255.255.0 with a dhcp server serving from 192.168.1.100 with 50 maximum dhcp users. I am also using opendns for content filtering and speed advantages. So I went into network settings and assigned an IPv4 address of 192.168.1.80 with the correct subnet and router address. I next went to terminal and used ifconfig to see my ip on en1 was 192.168.1.80 and broadcast of 192.168.1.255, media was listed as autoselect status: active.

And . . . I got ping returns from 8.8.8.8 and 4.2.2.2 — name servers I used for testing. However, my default test mit.edu returned nothing (the same with www.google.com). I was able to load http://74.125.225.18 (the site for google).

O.K. so then i knew it was a DNS problem. I hit the terminal with dscacheutil -flushcache to flush the cache, and then restarted. I was worried in that I couldn’t find my NAS on my network, but I hoped a restart would cure all ills. It didn’t: still no www.mit.edu or www.yahoo.com. Hmm . . . Branes from #mac on irc recommended that I manually add the DN servers from OpenDNS. I added the standard 208.67.222.222 and 208.67.220.220 in the advanced mac en1 settings . . . and . . . working great.

Still don’t know why I was getting a local link address. Oh well, it is working just fine now. Any thoughts appreciated.

By 0 Comments

Use Ruby to Clean out bad text files

How I deal with these will be the subject of a future post. Please let me know any pointers on any of this.

By 0 Comments

Recovering Rails Data Using Production.log

So you didn’t set up automatic backups and need to recover your data? Don’t try digging it out of mysql, look no further than your rails production log. OK, try a bit with mysql, because the rails production log is a terrible way to recover your data. Only use as a last resort, but when data don’t exist anywhere else, this is what you have to do. While simple in concept (scrape the log, updated the database using ActiveRecord) this was very trick in implementation. I know my code could use some refactoring, but it is working pretty well right now.

So here is my code if anyone wants to do something similar.

I had a production log in the general format of:


Processing AttendanceController#update_exercises (for 76.105.103.226 at 2010-07-07 20:33:26) [POST]
  Session ID: 06af5da80fc41be243e42254cb776212
  Parameters: {"commit"=>"Record User Scores", "meeting_id"=>"3388", "exertions"=>{"exercise_note_437"=>"7:00 AM", "exercise_score_884"=>"6:52", "exercise_score_1120"=>"9:29", "exercise_score_1175"=>"13:21", "exercise_score_268"=>"8:07", "exercise_score_433"=>"6:29", "exercise_note_1200"=>"", "exercise_note_1190"=>"", "exercise_score_741"=>"11:02", "exercise_note_1191"=>"", "exercise_score_544"=>"8:37", "exercise_score_918"=>"***", "exercise_note_1124"=>"", "exercise_note_1069"=>"", "exercise_score_479"=>"9:18", "exercise_score_908"=>"8:04", "exercise_score_1012"=>"7:34", "exercise_score_105"=>"8:43", "exercise_note_417"=>"", "exercise_id"=>"25", "exercise_note_968"=>"", "exercise_note_913"=>"", "exercise_score_1200"=>"8:50", "exercise_note_1115"=>"", "exercise_score_437"=>"7:16", "exercise_score_1124"=>"9:32", "exercise_note_705"=>"", "exercise_score_1190"=>"12:26", "exercise_note_342"=>"", "exercise_score_1069"=>"9:34", "exercise_score_1191"=>"10:45", "exercise_note_1095"=>"", "exercise_note_872"=>"", "exercise_score_1115"=>"8:12", "exercise_score_417"=>"9:00", "exercise_note_884"=>"", "exercise_score_968"=>"9:02", "exercise_score_913"=>"7:59", "exercise_note_268"=>"", "exercise_note_433"=>"", "exercise_note_1130"=>"", "exercise_note_1031"=>"", "exercise_score_342"=>"9:26", "exercise_note_741"=>"", "exercise_note_1120"=>"", "exercise_note_544"=>"", "exercise_note_1175"=>"", "exercise_note_918"=>"", "exercise_score_1095"=>"6:59", "exercise_score_705"=>"7:29", "exercise_note_479"=>"", "exercise_note_908"=>"", "exercise_score_872"=>"8:25", "exercise_note_105"=>"", "exercise_score_1130"=>"8:14", "exercise_score_1031"=>"7:24", "exercise_note_1012"=>""}}
Redirected to actionindexid3388
Completed in 536ms (DB: 999) | 302 Found [http://www.fitwit.com/attendance/update_exercises]


Processing AttendanceController#index (for 76.105.103.226 at 2010-07-07 20:33:27) [GET]
  Session ID: 06af5da80fc41be243e42254cb776212
  Parameters: {"id"=>"3388"}
Rendering template within layouts/application
Rendering attendance/index
Completed in 195ms (View: 172, DB: 6) | 200 OK [http://www.fitwit.com/attendance/index/3388]

So I wrote this code to fix it. Basically, well, it’s complicated, if you have any questions, email me.


#!/usr/bin/env /Users/Tim/Sites/fitwit/script/runner

class CommandString
  attr_accessor :date, :method, :command

  def initialize(date, method, command)
    @date = date
    @method = method
    @command = command
  end
end

class ExerciseArray
  attr_accessor :exercise_id, :time_slot_id, :meeting_id, :values_hash

  def initialize(exercise_id, time_slot_id, meeting_id, values_hash)
    @exercise_id = exercise_id
    @time_slot_id = time_slot_id
    @meeting_id = meeting_id
    @values_hash = values_hash
  end
end

class RecoverData "Update", ("user_ids"=>[.*], "meeting_id"=>".*")/
        params = gen_hash("{#{$1}}")
        j+=1
        c = CommandString.new(@the_date,'take_attendance', params)
        commands <"(d+)", "commit"=>"Apply to ([ws]*)", "meeting_times"=>[(.*)], "id"=>"(d+)"}/
        boot_camp_id = $1.to_i
        if boot_camp_id = 40
          commit = "Apply to #{$2}"
          meeting_times = eval("[#{$3}]")
          time_slot_id = $4.to_i
          k+=1
          c = CommandString.new(@the_date,'manage_meetings', [time_slot_id, commit, meeting_times, boot_camp_id]) if boot_camp_id == 40
          commands <"Record User Scores", "meeting_id"=>"([0-9]+)", "exertions"=>({.*})}/
        meeting_id = $1.to_i
        #puts meeting_id
        attendees = gen_hash($2)
        i+=1
        c = CommandString.new(@the_date,'update_exercises', [meeting_id, attendees])
        commands < mt)
            ts.meetings < mt)
          @time_slot.meetings < user_id, :meeting_id => meeting_id) if submitted_user_new?(user_id, existing_attendee_ids)
          end
          # now deletes (for each existing user, see if they are excluded)
          existing_attendee_ids.each do |existing_id|
            if should_delete?(existing_id, post_user_ids)
              @meeting_user = MeetingUser.find_by_user_id_and_meeting_id(existing_id, meeting_id)
              @meeting_user.destroy
            end
          end
          puts "successful! attendance taken"
        else
          puts "bootcamp 41 or something, so no attendance taken"
        end
      else
        puts "attendance: #{meeting_id}"
      end
    else
      # need to raise exception
      puts "meeting id is nil for #{params.inspect}"
    end
  end

  def update_exercises(meeting_id, attendees)
    puts "updating exercises"
    # this is where we update our exercises which is the exertions table
    # the meeting _might_ not exist
    #begin
      if Meeting.exists?(meeting_id)
        meeting = Meeting.find(meeting_id)
        if meeting.time_slot.boot_camp.id == 40
          puts "!! found #{meeting_id}"
          users =  attendees.keys.map{|m| $1 if m.match(/exercise_score_(d+)$/) }.delete_if{|n| n.nil?} #  meeting.meeting_users
          exercise = Exercise.find(attendees["exercise_id"])
          users.each do |u_id|
            u = User.find(u_id)
            unless attendees["exercise_score_#{u.id}"].blank?
              ex = Exertion.new
              mus = MeetingUser.all(:conditions => ["meeting_id = ? and user_id = ?", meeting.id, u.id])
              if mus.empty? # then, we need to create an attendence
                mu = MeetingUser.create(:user_id => u.id, :meeting_id => meeting.id)
                puts "created attendance event for #{u.full_name}"
              else
                mu = mus.first
              end
              ex.meeting_user_id = mu.id
              ex.exercise_id = exercise.id
              ex.score = attendees["exercise_score_#{mu.user.id}"]
              raise "user: #{mu.user.id} mu: #{mu.id} ex: #{exercise.id}" if attendees["exercise_score_#{mu.user.id}"] == ''
              ex.notes = attendees["exercise_note_#{mu.user.id}"]
              ex.save
            end
          end
          puts "Recorded scores for #{exercise.name}"
          puts meeting.meeting_date
        else
          puts "wrong boot-camp #{meeting.time_slot.boot_camp.id}"
        end
      else
        puts "exercises:  #{meeting_id}"
      end
    #rescue

     # raise "bad: can't pull it off"
    #end
  end

  def submitted_user_new?(submitted_id,existing_ids)
    # if submitted_id is not in existing id's it is new
    !existing_ids.include?(submitted_id)
  end

  def should_delete?(existing_id,submitted_ids)
    #if existing id is not in submitted id it should be deleted
    !submitted_ids.include?(existing_id)
  end

end

BootCamp.find(40).time_slots.map{|ts| ts.meetings.destroy_all}

r = RecoverData.new()
commands = r.get_data
puts "about to run commands"
attendance_cursor = {98 => 0, 99 => 0, 100 => 0, 101 => 0}
exercise_cursor = {98 => 0, 99 => 0, 100 => 0, 101 => 0}
ea = []
commands.each do |c|
  case c.method
  when "take_attendance"
    user_id = c.command["user_ids"].first
    ts_all = User.find(user_id).registrations.map{|reg| reg.time_slot}
    ts = ts_all.select{|ts| ts if ts.boot_camp_id == 40}.first
    unless ts.nil?
      ts_id = ts.id
      bc = ts.boot_camp.id
      meeting_id = ts.meetings.map{|m| m.id}[[attendance_cursor[ts_id],23].min]
      #puts "meeting id is #{meeting_id}"
      attendance_cursor[ts_id]+=1
      puts "#{ts_id}, #{bc}, #{c.command["meeting_id"]}, #{c.date}, #{c.method}, #{c.command["user_ids"].to_sentence}"
      r.do_attendance(c.command, meeting_id)
    else
      puts "no timeslots for #{user_id}, but they are in bootcamps #{ts_all.map{|ts| ts.boot_camp.id}.to_sentence}"
    end
  when "manage_meetings"
    #  [time_slot_id, commit, meeting_times, boot_camp_id]
    if c.command.last == 40
      #puts "#{c.date}, #{c.method}, #{c.command.to_sentence}"
      r.manage_meetings(c.command[0], c.command[1], c.command[2], c.command[3])
    end
  when "update_exercises"
    # [meeting_id, attendees]
    params = c.command[1]
    params.first.first.match(/(d+)$/)
    user_id = $1 || 399 # this is to account for a wierd record that has an exercise if
    u = User.find(user_id)
    ts = u.registrations.map{|reg| reg.time_slot}.select{|ts| ts.boot_camp.id == 40}.first
    ex_id = params["exercise_id"]
    puts "!! #{ex_id}"
    unless ts.nil?
      ts_id = ts.id
      puts "time slot: #{ts_id}"
      m_index = [exercise_cursor[ts_id],23].min
#      puts "m index #{m_index}"
#      puts "meetings #{ts.meetings.inspect}"
      meeting_id = ts.meetings.map{|m| m.id}[m_index]
#      puts "meeting id: #{meeting_id}"
      exercise_cursor[ts_id]+=1
      # :exercise_id, :time_slot_id, :meeting_id, :values_hash
      ea << ExerciseArray.new(ex_id, ts_id, meeting_id, params)
      #puts "#{c.date}, #{c.method}, #{meeting_id}, #{c.command[1].inspect}"
      #r.update_exercises(meeting_id, c.command[1])
    else
      puts "all time slots are empty for bc 40 for user #{u.id} (they are in another boot_camp)"
    end
  end
end
puts "doing it"
ea.group_by{|e| [e.exercise_id, e.time_slot_id] }.each do |e|
  values_hash = {}
  meeting_id = e[1].first.meeting_id
  puts e.inspect
  e[1].each do |poopa|
    values_hash = values_hash.merge(poopa.values_hash)
  end
  puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
  puts "#{meeting_id}, #{values_hash.inspect}"
  puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
  r.update_exercises(meeting_id,values_hash)
end

By 0 Comments

Summer Training Plan

My summer/fall 20 week training plan which is all focused around the Marine Corps Marathon. All long runs will be done with the DC Road Runners. Details on the long runs are at DC Road Runners’ page. CF stands for crossfit workouts found at CrossFit Adaptation. Track workouts done with DC Road Runners Track Workouts

Goal is to qualify for Boston Marathon and to be doing all CF workouts as Rx’d by October 31.

Week Mon Tue Wed Thur Fri Sat Sun
1 CF ? CF ? ? LSD: 12|UP Upton Hill 2 m run
2 CF 20 mi bike 3 x 1600M @ C/I    400M recovery swim CF LSD: 14|Southeast w Key for 14-ish mi. 2 m run + swim
3 5 m run CF+3 m run 6-8 x 500M @ I/P   300M recovery CF CF+5 m pace LSD: 10|Fort Scott Hill 3 m run+ swim
4 5 m run CF+3 m run Ladder:  800M – 400M – 200M – 1000M; Half the distance in recovery.  2 sets with 4:00 break. swim CF+5 m run LSD: 13.5|Piney Branch w Memorial Bridge Return 3 m run+ swim
5 6 m run CF+3 m run RACE NIGHT.  Track:  6-8 x 600M @ I/P.  400M recovery. CF CF+6 m pace LSD: 15|Iron Triangle 3 m run+ swim
6 6 m run CF+3 m run RACE NIGHT.   Track:  12-14 x 400M @ C/I  100M recovery. CF CF+6 m pace LSD: 16|Battery Kemble Loops 3 m run+ swim
7 7 m run CF+3 m run TRACK RACES [TENTATIVE].  If so, no workout.  CF CF+7 m run LSD: 13.5|Glover/Mass/Capital Crescent 3 m run+ swim
8 7 m run CF+3 m run 3-4 x 1600M @ T/I  400M recovery.  (DCRRC race on 8/3) CF CF+7 m pace LSD: 14|Marymount Chain Bridge 3 m run+ swim
9 8 m run CF+4 m run 6-8 x 500M @ I/P   300M recovery CF CF+8 m pace LSD: 17|Arlington Triangle 3 m run+ swim
10 8 m run CF+4 m run 12-16 x 400M @ C/I  100M recovery CF CF+8 m run LSD: 13|Cathedral/Clintons 3 m run+ swim
11 9 m run CF+4 m run 4-5 x 1000M @ I/P  400-600M recovery.  CF 9 m pace LSD: 16|16 Mile Sidewalks MCM 3 m run+ swim
12 9 m run CF+4 m run Track Workout CF 9 m pace LSD: 18|Battery Kemble Loops 3 m run+ swim
13 10 m run CF+5 m run Track Workout CF 10 m run LSD: 20|C&O Out/Back 4 m run+ swim
14 6 m run CF+5 m run Track Workout CF 6 m pace LSD: 14|Ross Drive 4 m run+ swim
15 10 m run CF+5 m run Track Workout CF CF+10 m pace LSD: 22|Capital Crescent/Rock Creek 4 m run+ swim
16 6 m run CF+5 m run Track Workout CF 6 m run LSD: 14|Marymount Chain Bridge 5 m run+ swim
17 10 m run CF+5 m run Track Workout CF CF+10 m pace LSD: 20|W & OD Out-n-Back 5 m run+ swim
18 8 m run CF+5 m run Track Workout CF CF+4 m pace LSD: 14|Ross Drive 5 m run+ swim
19 6 m run CF+4 m run Track Workout CF CF+4 m run LSD: 10|MCM Taper Route 4 m run+ swim
20 4 x 400 2 m run rest rest 2 m run 1 mi run 26.2 race pace

Or, in Google calendar format

[googleapps domain=”www” dir=”calendar/hosted/theboohers.org/embed” query=”src=theboohers.org_o0027jm7d81mj2ma339qk5mbhk{aaa01f1184b23bc5204459599a780c2efd1a71f819cd2b338cab4b7a2f8e97d4}40group.calendar.google.com&ctz=America/New_York” width=”800″ height=”600″ /]

By 0 Comments

Convert avi’s, mkv etc to the iphone

Handbrake is amazing with active development and a great community. When I wanted to transfer a bunch of lectures to iphone/ipod touch format, I faced the time-consuming challenge of converting a hundred files using the Handbrake gui and figuring out the complicated set of options for HandbrakeCLI — their nightly build standalone.

So, here is what I did:

I assume you have the Handbrake gui installed, but not the binary. Go to Handbrake http://build.handbrake.fr/ and get the latest CLI executable for your operating system and put it in a good place, preferably in your path.

Next, you want to make use of the excellent presets in the gui of Handbrake, use the ruby file manicure.rb to get the settings you need. (http://trac.handbrake.fr/browser/trunk/scripts/manicure.rb). Make sure you have the plist gem installed and then you can run.


bonhoffer Tim$ ./manicure.rb -rH > presets

In presets, I found the code I wanted, which I then put in ruby code of my own:


#! /usr/bin/ruby

# handbrake converts all files automatically for the ipod, nice

Dir.glob("*.avi").each do |filename|
  correct_fn = filename.sub(/s/,'-') # let's take out spaces from our filenames
  File.rename(filename,correct_fn)
  # change the output file name to an mp4
  new_file_name = correct_fn.sub(/avi$/,'mp4')
  # now it is time for business
  system("HandBrakeCLI -i #{correct_fn} -o #{new_file_name} -e x264 -q 20.0 -a 1 -E faac -B 128 -6 dpl2 -R 48 -D 0.0 -f mp4 -X 480 -m -x cabac=0:ref=2:me=umh:bframes=0:subme=6:8x8dct=0:trellis=0")
end

It is running in the background right now. No silly, clickity, click, click of the gui.

By 0 Comments

Hard drive mirroring With Unison and the DNS-323

My requirements:
– All files on my laptop will be on my DNS-323 Network attached storage device
– Any changes made to the shared drive on the DNS-323 will propagate to my Mac
– This happens securely and safely

The problem is that there are scant resources online to do this. (Isn’t this done frequently?) I am worried that there is a better implementation for this, but it is the best way forward.

I did find “this link”:http://www.edsalisbury.net/linux/how-to-set-up-a-two-way-mirror-between-mac-os-x-and-ubuntu-linux/, “this one”:http://www.macgeekery.com/gspot/2006-07/complete_bi_directional_home_sync_and_backup_with_unison and “this”:http://www.payne.org/index.php/Mac_OS_X_File_Sync. But they didn’t really answer my question on how to get everything working with a DNS-323.

Previously, I hacked my DNS-323 to give me ssh access as well as installed the ipkg manager to install files. However, it seems no-one has installed unison on the DNS-323 before.

I am currently working on this and trying to figure out if I need to install unison on the DNS-323 or not. I am also worried that no-one else seems to be doing this. I generally arrive late to the party on this stuff, what are other folks doing for this?

By 0 Comments

Where Men Win Glory: The Odyssey of Pat Tillman

Jon Krakauer uses 416 pages to make the audacious claim that he has found the Nietzschen Uebermensch in “Pat Tillman”:http://en.wikipedia.org/wiki/Pat_Tillman — and thus feeds the roaring literary fire of condemnation for the Bush administration in particular and religious conservatives in general. In a facile and sloppy argument that makes liberal use of argument by anecdote, he goes out of his way to package and sell his vision of the ideal man. This provides the opportunity for contrast against his straw-man of the modern religious conservative who is (here we go again) an unenlightened coward, motivated only by power and control of the weak. Here is Pat Tillman as the literary device — constructed and packaged to advance the consummate liberal ideal: an Emerson reading gay rights advocate who used his prodigious strength and pugnacity only to defend the weak, all while scorning any faith that claims it knows anything with certainty.

Krakauer restricts his moral influences to enlightenment skeptics or pre-christian classical writers and with his moral canon thus defined, he uses ample references to the likes of the Iliad and Nietzsche to supply a ready alternative basis of morality. Beyond presenting a system of morality unadulterated by conservative principles, he goes out of his way to denigrate the integrity of conservatives. It didn’t take long to think I was reading Christopher Hitchens or Sam Harris while I really was just interested in nothing more than the subtitle: “The legacy of Pat Tillman”. It is clear why his chose his actual title as a direct lift from the Iliad. His point? Men win glory when they transcend conservative values. Thus a story which initially appears to be about a man’s story becomes an apologetic for one of the most important agendas of modern liberalism: defining a sense of morality apart from religion and tradition.

So every page is then devoted to either exalting our postmodern hero, Pat, or casting aspersions on conservatives. Mr Tillman is presented as a modern man who combines love of life, strength, wisdom, sensitivity, morality, liberal use of the F-bomb, all surrounded by an acerbic scorn for religion and political conservatives. And thus we have the central irony of this book: while Jon Krakauer’s claim is that conservatives used Pat as a pawn to seize greater power and control, Pat is yet used once again — this time he is an the icon of liberalism. Jon Krakauer in expositing Tillman has repeated the path of prior liberals such as Adolf Harnack who tried to redefine Christ as similar to themselves. Father George Tyrell (a Catholic modernist) described this interaction well: “The Christ that what Harnack sees, looking back through nineteen centuries of Catholic darkness, is only the reflection of a Liberal Protestant face, seen at the bottom of a deep well.”

So now we have Jon Krakauer/Pat Tillman fused into the ideal of a modern man unencumbered by conservatism and belief. And with this icon, Jon Krakauer tells a captivating story. On one level it is brilliant to use someone who was the symbol of strength and sacrifice (virtues conservatives have unjustly claimed as unique to them) to pillory conservatives. That Jon Krakauer is a master storyteller is beyond dispute. He has crafted a story involving a set of elements perfectly tuned to sell to the reading masses: elite athleticism, culture wars, the Iliad, Nietzsche, heroism, all with generous potshots against George Bush and Donald Rumsfeld. So Krakauer becomes another who rides the zeitgeist and ends up defining a component of it by offering Pat Tillman as the consummate example of liberal ethics. Clearly, this book will sell, but I’ll speculate that Krakauer’s ambitions are much higher than profit here. For he has written a philosophy book, not a biography. Seen through this lens, I would recommend this book to others who wish to understand the chorus of new atheists: goodness without God is not just possible, but even better than goodness adulterated by the varieties of organized religion.

With his claim understood, how strong is his case? Like William Gladstone, Krakauer is attracted to the morality of the Iliad, which preaches that moderation in all things is the most important component of morality. Nietzsche as well presents an appeal to strength, and along with Ayn Rand — touches something deep within me, that calls me to be braver and stronger — by MYSELF. But here is where personal experience comes into the picture, and I get to present some anecdotes of my own. Trying to succeed on my own strength only leaves me more bitter and selfish. Perhaps Krakauer can claim this is because I’m internally weak, or perhaps weakened by my faith. But personal strength and courage for me have only been the direct result of my knowledge of my weakness and depending on an external source for strength. Thus I present a different ideal of the greatest man: one who lays down his life for his friends — who spends his time not making himself a better human, but emptying themselves for the sake of others — and doing so not for some vague sense of justice, but for tangible reward in heaven and the joy of thankful worship.

Beyond advancing his philosophy with Pat Tillman as a prop, Krakauer went out of his way to provide talking points for the Democratic party establishment. It almost felt like contrived product placements seen in movies. For example, John Krakauer went out of his way to spend several pages on the 2000 presidential election, with the final claim that Bush, through the interference of conservatives on the Supreme Court, was unlawfully elected president. This all despite the findings of subsequent in-depth recounts. I bring this forward, because it is one thing to criticize an administration, but another to espouse the political talking points that come from either side of the aisle.

I also don’t share Krakauer’s cynicism that US leaders engage in wars simply to increase their own power. The little I know about how national security doctrine is put together in this country is that it’s a messy business. For better or worse, one or two individuals do not yield enough power to be responsible for the entire direction of the nation. The coterie of individuals considered responsible for everything that goes wrong in Iraq and Afghanistan is basically distilled down to the usual suspects. To me, this is such a tired, simplistic, message that somehow continues to sell books to an increasingly gullible American public who thinks their cynicism about the Bush administration is a panacea for their very Naivete.

Thus I found Krakauer’s argumentation style to be anecdotal and somewhat insulting. He makes tenuous connections like the fact that helicopter was not available on a particular day to be used as an argument that Iraq received too many resources to the detriment of operations in Afghanistan. Military operations planning and allocation of assets is a very complicated process that many factors can influence. Speculating on potential correlation and then building an argument for causation is the most freshmen of errors. (Can I also note that the B-1 is not a stealth bomber. Did he have anyone with Air Force knowledge fact check his book?)

If I set aside the plot to denigrate my faith and political beliefs, there is much I found intriguing about this book. Pat Tillman and I grew up at the same time in a similar culture. We both joined the military and perhaps have felt a similar scorn for its mediocrity. There is no doubt that he experienced the associated dissonance that comes from an ideal of a warrior as “guardian of Plato’s ideal state”:http://classics.mit.edu/Plato/republic.8.vii.html contrasted with the modern reality of an all volunteer force — infected by financially motivated bottom feeders and the soul-stagnating bureaucracy of modern big government. So while Krakauer would cast me closer to the book’s arch villain, the evangelical LTC Ralph Kauzlarich, than his exalted Tillman, I think Pat and I might have had a good bit to talk about. This is perhaps why I have such strong feelings about this book: there is no doubt that Pat Tillman has once again been used.

By 0 Comments