Dilbert.com

Posted by admin, Wed Apr 30 10:41:00 UTC 2008

We are sorry for the trouble you have been having with the new launch. We hope all the work we will be doing in the coming weeks will make it easier for you to enjoy Dilbert in all his incarnations. Just so you know, we are thinning out the flash, streamlining the mashups, making the site easier to navigate as well as allowing those on non-supported operating systems to view the animations.

For those of you who are unhappy with the new site, this note from Scott Adams should answer some of your questions:

……..

Recently we redesigned the Dilbert.com web site and added a ton of features, such as animation, deeper archives, mash ups, and more. The reaction from readers has been fascinating.

www.dilbert.com

Let me get this out of the way: I realize the Beta version of the web site has lots of issues. It’s overloaded with Flash, slower than it needs to be, and the navigation is confusing. We’re fixing most of that over the next few weeks. I apologize for the inconvenience.

The fascinating thing about the responses is that it revealed three distinct types of Dilbert readers:

The first group is the ultra-techies who have an almost romantic relationship with technology. For them, the new site felt like getting dumped by a lover. Their high-end technology (generally Linux) and security settings made much of the site inconvenient. Moreover, the use of Flash offended them on some deep emotional level.

The second group objected to the new level of color and complexity, and the associated slowness. They like their Dilbert comics simple, fast, and in two colors. Anything more is like putting pants on a cat.

The third group uses technology as nothing more than a tool, and subscribes to the philosophy that more free stuff is better than less free stuff. That group has embraced the new features on the site and spiked the traffic stats.

For you first two groups, if you promise to keep it to yourselves, we created a stripped-down Dilbert page with just the comic, some text navigation, and the archive: www.dilbert.com/fast. This alternate site is a minor secret, mentioned only here and in the text footnote to the regular site as “Linux/Unix.”

The main site will be getting a Flash diet that will make it speedier soon, so check back in a few weeks. That’s where all the fun will be.

Enjoy.

……

Thanks,

Dilbert.com Customer Service

0 comments | Filed Under: | Tags:

Installing RubyonRails from source on Debian

Posted by admin, Mon Jan 21 11:06:00 UTC 2008

I'm installing a dedicated rails server. This is the documentation on getting things working on Debian stable.

Firstly, I chose to install ruby and rubygems from source because the packages in Debian Stable are a little older than I would like.

First thing to do is get a development environment installed on Debian. The quickest/simplest way is to install the build-essential pseudo package.

aptitude install build-essential

Then we need zlib libraries for ruby

aptitude install zlib1g-dev

Download the ruby source code and extract it

tar zxvf ruby-1.8.6-p111.tar.gz

cd ruby-1.8.6-p111
./configure
make
sudo make install

Download ruby gems and extract it

tar zxvf rubygems-1.0.1.tgz
cd rubygems-1.0.1
ruby ./setup.rb

Install rails

gem install rails

Install msyql

aptitude install mysql-server

and client libraries

aptitude install libmysqlclient15-dev

gem install mysql

and that's it done.

0 comments | Filed Under: Linux Rails | Tags:

Offsite Backups

Posted by admin, Wed Sep 05 14:01:00 UTC 2007

I have an external fireware enclosure that is now full of drives - 3.4TB in total. I use these drives for offsite backups. I connect to remote servers and rsync all the important stuff onto the drives on a 10 day cycle.

Thanks to some clever use of hard links and rsync I don’t need 10 times the disk space to do this though. It works like this -

Day 1 rsync remote directories to backup.1 on back drive

Day 2 Hard link backup.1 files to backup.2 rsync remote directories to backup.1

Day 3 Rename backup.2 to backup.3 Hard link backup.1 files to backup.2 rsync remote directories to backup.1

And so on. The number of backups in the cycle is configurable. The magic is in how rsync rebuilds a file that needs updating - it creates a new file and assembles it from the local file and the remote file and then removes the local file and replaces it with the new one - therefore breaking the hard link.

So if a file hasn’t changed it occupies the same space on the disk for each copy.

The script - below - takes 2 options - -b specifies the number of backups in a cycle and -s tells the script that you want a single update - this will simply refresh the newest backup - without doing any rolling back.

You must also supply a parameter to the script to tell it which backup you are running - this is used to mount the appropriate drive - achieved using drive labels.

Once the drive is mounted a file called backup.list is read - this file specifies the source server, the source path and the destination folder in the backup. i.e.


webserver /var/www/ webserver/documentroot
dnsserver /etc/bind/ dnsserver/bind 

The 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/bin/ksh
#
# Script to mount usb drive, rsync various bits, umount
#

function log {
  D=$(date '+%Y/%m/%d %H:%M:%S')
  print -- "$D $*"
}

integer BACKUPS=5

TODO=0

OPTS="$*"

while getopts b:s ARG
do
  case $ARG in
    b) BACKUPS=$OPTARG
      ;;
    s) TODO=1
      ;;
    ?) print -u2 "Invalid argument"
      exit 1
      ;;
  esac
done

((OPTIND=OPTIND-1))
shift $OPTIND

if [[ $# != 1 ]]
then
  print -u2 "No host specified"
  exit 1
fi

HOST=$1

log "Backup Starting $OPTS"

MOUNTPOINT=/backups/backup.$HOST
mkdir $MOUNTPOINT > /dev/null 2>&1

# Find the device for the called HOST
if mount -L$HOST $MOUNTPOINT
then
  log "Disk for $HOST mounted on $MOUNTPOINT"
else
  log "Cannot find host backup volume $HOST"
  exit 1
fi

df -v $MOUNTPOINT

if [[ $TODO != 1 ]]
then
#roll back and recreate backup
  integer N
  integer M

  ((N=BACKUPS-1))
  while ((N>=2))
  do
    ((M=N+1))
    if [[ ! -d $MOUNTPOINT/backup.$N ]]
    then
      log "Creating sequential backup directory backup.$N"
      mkdir $MOUNTPOINT/backup.$N
    fi
    if [[ -d $MOUNTPOINT/backup.$M ]]
    then
      log "Removing backup directory backup.$M"
      rm -rf $MOUNTPOINT/backup.$M
    fi
    log "Moving backup.$N to backup.$M"
    mv $MOUNTPOINT/backup.$N $MOUNTPOINT/backup.$M
    RES=$?
    if [[ $RES != 0 ]]
    then
      STATUS="Failed"
    else
      STATUS="Complete"
    fi
    log "Sync $STATUS"
    ((N=N-1))
  done

  log "Creating sequential backup directory backup.2"
  mkdir $MOUNTPOINT/backup.2
  log "Duplicating latest backup into $MOUNTPOINT/backup.2"
  cp -al $MOUNTPOINT/backup.1/* $MOUNTPOINT/backup.2/.
else
  log "Refreshing latest backup only"
fi

while read SERVER SOURCE DESTINATION
do
  if [[ ! -d $MOUNTPOINT/backup.1/$DESTINATION ]]
  then
    if ! mkdir -p $MOUNTPOINT/backup.1/$DESTINATION
    then
      log -u2 "Failed to create $MOUNTPOINT/backup.1/$DESTINATION"
      continue
    fi
  fi

  log "Syncing $SERVER:$SOURCE to backup.1/$DESTINATION"
  rsync -az --delete --exclude="*\.mp3" $SERVER:$SOURCE $MOUNTPOINT/backup.1/$DESTINATION/.
  RES=$?
  if [[ $RES != 0 ]]
  then
      STATUS="Failed ($RES)"
    else
      STATUS="Complete"
  fi
  log "Sync $STATUS"
done < $MOUNTPOINT/backuplist
df -v $MOUNTPOINT

umount $MOUNTPOINT
rmdir $MOUNTPOINT
log "Backup for host $HOST complete"

3 comments | Filed Under: Linux | Tags:

Absent (minded)

Posted by admin, Sun Sep 02 12:33:00 UTC 2007

I've been away (got married, had honeymoon etc..). Not had much time to spare let alone any for programming until now and now I can't remember anything :)

I've decided that from now on I will use my blog purely for useful information to remind me of things. Mainly for my benefit but as it's online it might be useful for others.

0 comments | Filed Under: | Tags:

Restful Rails

Posted by admin, Thu Jun 21 10:30:00 UTC 2007

I recently took the decision to move to using restful rails. In simple terms it removes the action from the URL and uses HTTP's built-in functionality to determine the actions being requested.

So, instead of requesting /users/show/1 you simply request /users/1 with a (normal) HTTP GET method.

No shocks there - pretty sensible and obvious.

To update a user you perform a HTTP PUT method on the same URL, and to delete you perfrom HTTP DELETE.

There are some niggles - such as coping with edits and news but rails identifies these by appending the action after the URL with a semi-colon delimiter - /users/1;edit or /users;new.

To turn on restful routes you simply add

1
map.resources :users

to your routes.rb file.

This adds a whole bunch of new helpers too that you use to construct the URLs you need to use, such as

1
2
edit_user_path(@user)
new_user_path(@user)

Theres a catch though when using AJAX calls - you need to also add :method => :GET (or POST etc..) - I see this as a bit of a hack and hopefully it will be more elegant in future. See the API for more information.

To get up to speed on using restul routes DHH's rails conf keynote of 2006 is good viewing - although some of the code examples given are out of date since it was added to core rails.

This PDF is also a worthwhile read.

0 comments | Filed Under: Rails | Tags:

I've moved to the dark side

Posted by admin, Wed Jun 06 10:58:00 UTC 2007

I’m an avid Linux user - both on the server side of things and the desktop.

But this week I got a Macbook Pro. Primarily for Rails development but also some photo work and as a general-use laptop.

I love it. Apple isn’t quite the dark side but it was always a little grey to me, but everything on a Mac looks so fresh and clean!

0 comments | Filed Under: Rails | Tags:

Using Subversion

Posted by admin, Tue Apr 10 21:49:00 UTC 2007

I've recently moved my main application to a new off-site web server. I used the move as an excuse to also use subversion and capistrano and could not have got started without the great primer on Rails and subversion here.

After some slight hicoughs I seem to have it all working and now wish I had used it from the beginning. Being able to upgrade my live server by simply typing cap deploy is pretty awesome :)

0 comments | Filed Under: Rails | Tags:

Rails Helpers (and a little bit of javascript)

Posted by admin, Mon Mar 12 10:56:00 UTC 2007

On a website I have the following simple Rails/Scriptaculous list -

  • A List Item
  • Another List Item

Click on the first item and then on the link referring to the next item.

The first problem I had with this was that the standard scriptaculous routines don't support a blind-if-not-visible-already option - so when you clicked on the referring link, if the section it referred to was already visible it would hide and be displayed.

I needed to check the display property of the DIV and only do the blind/appear if the DOM was currently hidden. I chose to do this using a combination of some javascript and a helper.

Javascript

1
2
3
4
5
6
7
8
9
10
11
12
function slideshow(dom, options) {
  Effect.toggle(dom,'blind',options);
  Effect.toggle(dom,'appear',options);
}

function highlight(dom, slideoptions, options) {
  if ($(dom).getStyle('display') == 'none') {
    Effect.toggle(dom,'blind',slideoptions);
    Effect.toggle(dom,'appear',slideoptions);
  }
  new Effect.Highlight(dom,options);
}

The javascript is pretty simple but it helps to reduce the code clutter in both the helper and the generated HTML.

Helper

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
module ApplicationHelper
    def slideshow(element_id = false, js_options = {})
      element = element_id ? element_id.to_json : "element"
      js_options[:queue] = if js_options[:queue].is_a?(Hash)
        '{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}'
      elsif js_options[:queue]
        "'#{js_options[:queue]}'"
      end if js_options[:queue]

      "slideshow(#{element},#{options_for_javascript(js_options)});"

    end

    def highlight(element_id = false, sl_options={}, js_options={})
      element = element_id ? element_id.to_json : "element"

      js_options[:queue] = if js_options[:queue].is_a?(Hash)
        '{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}'
      elsif js_options[:queue]
        "'#{js_options[:queue]}'"
      end if js_options[:queue]

      sl_options[:queue] = if sl_options[:queue].is_a?(Hash)
        '{' + sl_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}'
      elsif sl_options[:queue]
        "'#{sl_options[:queue]}'"
      end if sl_options[:queue]

      "highlight(#{element}, #{options_for_javascript(sl_options)}, #{options_for_javascript(js_options)});"
    end
end

I stole most of the code in the helper from the existing scriptaculous helper. As you can see it is just a simple wrapper to generate the javascript function call. I could have simplified the use of my new helpers still by having the helper itself generate the links also but this would have also required supportting any additional attributes etc.. - as link_to_function already does this I decided using it was a no brainer.

View

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<ul>
 <li><%= link_to_function 'A List Item', 
  slideshow(:list1, { :duration => 0.5, :queue => 'parallel' }) %>
  <div id="list1" style="display: none">
   The text in this list item refers to the list item below,
   and when you click on 
   <%= link_to_function 'this link', 
    highlight(:list2,
      { :duration => 0.5, :queue => 'parallel' },
      { :duration => 0.5, :queue => 'end' } ) %> 
    the item below should open also
  </div>
 </li>
 <li><%= link_to_function 'Another List Item', 
    slideshow( :list1, { :duration => 0.5, :queue => 'parallel' } ) %>
  <div id="list2" style="display: none">
    This is the text explaining the second item in this list.
  </div>
 </li>
</ul>

0 comments | Filed Under: Rails | Tags:

Mental Masturbation

Posted by admin, Wed Mar 07 19:22:00 UTC 2007

Take a look at this code

1
: [forums(:rails), users(:tom), topics(:pdi)].each &:reload

Discuss.

0 comments | Filed Under: Rails | Tags: