Convert tabs to spaces in your project
09/03/02
find . -type f -exec perl -i -pe 's/\t/ /g' \{\} \;
find . -type f -exec perl -i -pe 's/\t/ /g' \{\} \;
I've been working with the great team at Rouxbe for almost a year now. They are an online video recipe site that do an amazing job at showing you how to cook through their high quality video recipes and technique drilldowns.
Yesterday we hit another milestone with the release of the first online cooking school. The production team is still working away on more lessons that are going to be released soon, but so far the content is looking great. If you're interested in food, I would highly suggest you check it out. There is one free lesson that you don't even need to be logged in to check out.
Happy cooking!
For the past while I've been working on Apricado, with a few other friends. Apricado is a easy to use independent music store. No crazy features, just features that you need to get going. It's almost ready to launch now so we've released a little preview of how easy it is to create and sell music on the site. You can check out the full size preview on apricado.com, or watch the embedded version on the blog here. Please let us know what you think. Thanks.
I found this handy little snippet on github yesterday when I was looking through some of the rails commits.
Git won't add a folder unless there is something in it. If you want to commit an empty folder, the common practice is to add a .gitignore file in the directory.
This snippet will add .gitignore files to all empty directories recursively from your current directory.
~ > find . \( -type d -empty \) -and \( -not -regex ./\.git.* \) -exec touch {}/.gitignore \;
Thanks to Nate for sharing. You can view the commit where he added this snippet in the comments here.
This nice little script has been cruising the web lately. Here’s my results.
~ > history | awk {'print $2'} | sort | uniq -c | sort -k1 -rn | head
218 git
90 gst
26 rake
21 ls
20 cd
15 cap
12 mate
11 gca
11 gb
10 rm

It may be a little hidden but Git actually comes with auto completion, you just have to set it up.
If you have the Git source on your computer then you are good to go. If not, you are going to have to download it. After downloading Git we are going to copy the git-completion bash file from the contrib/completion directory into our home directory, prepending it with a period so that it's hidden.
~ > git clone git://git.kernel.org/pub/scm/git/git.git git ~ > cp git/contrib/completion/git-completion.bash ~/.git-completion.bash
Depending on how you've setup your bash startup files the next couple of steps may differ. If you want a great guide on setting up your bash startup files, check out this article.
The first thing we are going to do is include git-completion.bash in our ~/.bash_profile. Look for the .bashrc source include and put git-completion right before it.
# ~/.bash_profile source ~/.git-completion.bash source ~/.bashrc
Now you just need to reload the bash_profile and auto complete is ready to go.
~ > . ~/.bash_profile
Wasn't that easy. While we're messing around in the bash startup files though, why don't we add another great little feature. Git-completion.bash includes a method (__git_ps1) to find your current branch and print out it's name. We are going to append the branch name to our command prompt so we can always tell what branch we are working in.
Open your .bashrc files and search for the 'export PS1' line and replace it with the following.
# ~/.bashrc export PS1='\w$(__git_ps1 "(%s)") > '
After reloading your ~/.bashrc you will have your branch names showing up in the command prompt whenever you are in a Git project. No need to call git-branch to see what branch you are in anymore.
~/projects/TestGit(master) > git checkout rails20 Switched to branch "rails20" ~/projects/TestGit(rails20) >
If you run into any trouble, check out the git-completion.bash file. It has some good instructions on how to use it at the top of the file.
Just under two years ago I was searching for a birthday present for my girlfriend. The bag that she used for school everyday was starting to fall apart so a new bag seemed in order. I started a search for the perfect bag and made up a list of criteria.
After a busy afternoon downtown I had narrowed the playing field down to three pretty nice bags.
The first was the Otrlieb Sling-It. I had owned this bag for the previous 3 years and it was still going strong. It had a laptop sleeve and an easy velcro closure system. The design was pretty simple and elegant, but a little too 'bicycle messenger' for what I was looking for. For $132 US, it was a great bag, but not the perfect one.
The second bag that I looked at was the Timbuk2 Messenger. It had lots of space, a sleeve for the laptop and looked really well put together. The design seemed cluttered though, with straps and pockets all over the place. The two big plastic snaps on the font looked a bit cheap, and tri-panel fabric cut, didn't really do it for me either. Prices ranged from $120 - $210 US.
The bag that I finally ended up deciding on was the Jack Spade Laptop Tech Field Bag. It blended all the elements that I was looking for into one bag. It was sleek and stylish, very functional and well made. It came with custom die cast hardware (no plastic anywhere on it) and had lots of room for papers and books. It was a bit more expensive than the other bags, running from $195 - $425 US but looked like it was worth the higher price tag.
After giving it to my girlfriend for her birthday I think I made it a couple days before I went out and bought myself the same bag but in a different color. It's been over a year and a half now and the bag is still holding up great.
While checking out the Jack Spade site recently I also found out that they've starting to sell their bags at Apple Stores.
So you have multiple branches on your local machine and now you want to share one or more with the other developers on your project.
You can let everyone know your IP address and have them connect to your computer directly or if you're using GitHub, you can push your branches up to it, and then the other developers can pull your newly shared branches from there.
Heres the a few commands that you are going to need to know.
# Push your local branch to GitHub git push origin <local branch name>:refs/heads/<remote branch name>
Just by pushing your branch to a remote server doesn't mean that you are tracking it. You'll need to add the branch to your git config or you can delete the branch and then create a new one that is linked to the remote one with the following command.
# Create a new local branch that tracks a remote branch. # If you already have a branch of the same name, delete it # first and then recreate the branch from the remote repository. git branch -f <local branch name> origin/<remote branch name>
Now others can pull your branch from GitHub.
# Create a new local branch that tracks a remote branch git branch <local branch name> origin/<remote branch name>
One thing to note is that you cannot delete a remote branch from your local machnine, and as far as I've seen, GitHub doesn't have a gui to delete a branch online.
# Delete a remote branch off the remote server git push origin :<remote branch name>
# Stop tracking a remote branch git branch -d -r origin/<remote branch name>
For some other great tips on using Git, check out this wiki.
Thanks to Dustin (in the comments) for pointing out this shortcut. If your want the remote branch to be named the same as your local branch you can use the following.
# Push your local branch to GitHub git push origin <local branch name>
Most of the time when you commit a migration, you'll want to run that migration against the code base you have when you check that migration is. To make this simpler to do, all you have to do is call use_git and gitty migrations with automatically find that revision for you.
Here's an updated example on how to use gitty migrations
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 ResetAllAccountTypes < ActiveRecord::Migration use_git # or # use_git :revision => "42ca6c6de8cbd6591dcada7437c97" # or # use_git :revision => "v2.0" def self.up add_column :user, :account, :integer, :null => false User.find(:all).each do |user| user.account = case user.account_type when "Free" : Account::Types::Free when "Paid" : Account::Types::Paid else Account::Types::Unknown end end remove_column :user, :account_type end def self.down ... end end |
Migrations make it super easy to work with your database in Rails, with only a few small problem that I've run across.
Certain migrations rely on specific changelists
Most of the time, this is because you're dealing with a legacy database that you are trying to clean up and you are moving data around. You want everyone to get the changes, but don't want to rely on people running a rake task at a certain revision to implement the changes. To get around this we sometimes redefine a class in the migration to override the newer, updated class of the same name in our application. But what if we didn't have to do this, what if we could just tell the migration what revision or changelist the migration should run against and let it run. This is where gitty migrations would come in.
Here's how it's done. (this example was just done for the purpose of showing how the plugin works)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class ResetAllAccountTypes < ActiveRecord::Migration use_git_revision "42ca6c6de8cbd6591dcada7437c97000839e8074" # or if you've used git-tag to tag your revision # use_git_revision "v2.0" def self.up add_column :user, :account, :integer, :null => false User.find(:all).each do |user| user.account = case user.account_type when "Free" : Account::Types::Free when "Paid" : Account::Types::Paid else Account::Types::Unknown end end remove_column :user, :account_type end def self.down ... end end |
Now whenever a developer runs this migration, it will check out that revision of git, run the migration against it, and then change your git repository back to where it was. If in the future you decide to use an account type table instead of an enum and change all your code, this migration will still work.
TODO
Thoughts or comments? Have a better way of dealing with this. Let me know.
I installed IIS on my computer the other day because I'm doing some work for a client who is using ASP. When I tried to start up the server, I got this error message 'Unexpected Error 0x8ffe2740 Occurred'. Having no idea what error 0x8ffe2740 was I hit up google for some answers. Turns out Error 0x8ffe2740 means Port already in use. It would have been nice if they had just supplied a simple explanation in there error message. Is that too much to ask for?
I was talking with Mat Harvard on gtalk last night and the idea of starting a Victoria Ruby Users Group came up. I had bought vicruby.com quite a while ago but hadn't had the time to develop it so I quickly whipped up a template for stikipad, changed my DNS and we now have a wiki for the newly formed 'Victoria Ruby Users Group'. We'll see how it goes. Not sure what the interest is going to be like in Victoria. It's not that big of a place. Hopefully we will have our first meeting in the new year. Talk/presentation ideas anyone?
I've been playing with liquid templates in rails now for about a month. When I first started there wasn't a great deal of nice, easy to follow, step by step tutorials. There are a few good wiki's but putting all the information together can be a bit quite a bit of work.This guide is meant to be super simple and easy to follow. It will be a start to finish guide on how to install and use liquid in you ruby on rails application. This is part one. Enjoy!
First let's create our project and add the liquid rails plugin.
server:/# cd /rails
server:/# rails liquid
server:/# cd liquid/vendor/plugins
server:/# svn export svn://home.leetsoft.com/liquid/trunk/liquid/
server:/# cd ../../
server:/# rake rails:freeze:edge
server:/# ruby script/serverserver:/# rm public/index.html
server:/# pico config/routes.rb1 2 |
map.resources :posts map.connect '', :controller => "posts" |
server:/# cd db
server:/# mkdir migrate
server:/# cd migrate
server:/# pico 001_create_posts.rb1 2 3 4 5 6 7 8 9 10 11 12 13 |
class CreatePosts < ActiveRecord::Migration def self.up create_table :posts do |t| t.column :title, :string t.column :body, :text t.column :created_at, :datetime end end def self.down drop_table :posts end end |
server:/# rake db:migrate
server:/# ruby script/generate scaffold_resource post
server:/# ruby script/serverserver:/# pico app/controllers/posts_controller.rb1 2 |
#@posts = Post.find(:all) @posts = Post.find(:all).collect(&:attributes) |
server:/# cd app/views/post
server:/# mv index.rhtml index.liquid
server:/# pico index.liquid1 2 3 4 5 6 7 8 9 10 |
<h1>Our Posts</h1> <div id="posts"> {% for post in posts %} <div class="post"> <h2>{{ post.title }}</h2> <p class="date">{{ post.created_at | date:"%b %d, %Y" }}</p> <p>{{ post.body }}</p> </div> {% endfor %} </div> |
server:/# pico app/controllers/posts_controller.rblayout 'default' |
server:/# pico app/views/layouts/default.liquid1 2 3 4 5 6 7 8 9 10 11 12 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title>My Liquid Enabled Site</title> <link rel="Stylesheet" href="/stylesheets/scaffold.css" type="text/css" media="screen" /> </head> <body> {{ content_for_layout }} </body> </html> |
After putting it off for sometime now, I just switched over from Typo to Mephisto. There are going to be a couple of glitches in the changeover but hopefully everything should be up and running smoothly in the next week or so.
About three weeks after putting my email address on the main page of my blog I started to get spam, and a lot of it. That’s when I decided to install Spamassassin. To help Spamassassin to detect spam I also installed DCC and Razor, two anti-spam filters. Here’s what I did and how I did it.
This tutorial assumes that you have postfix up and running already.
First lets walk through the steps that postfix goes through when you receive an incoming email.
When receiving an email postfix looks for .forward in your home directory and executes any commands that you have in this file. If postfix doesn’t find a .forward file then it will execute /etc/procmailrc with no options and afterwards execure ~/.procmailrc if it can find that file.
Ok, let’s get started.
server:/# apt-get install spamassassin
server:/# adduser --system --home /var/lib/spam --shell /bin/false --disabled-password --disabled-login spamd
server:/# pico /etc/default/spamassassin
Change ENABLED=0 to ENABLED=1
server:/# pico /etc/postfix/master.cf
Look for a line like this:
smtp inet n - - - - smtpdand on the following line add:
-o content_filter=spamassassin
And at the end of the file add this line
spamassassin unix - n n - - pipe flags=Rq user=spamd argv=/usr/local/bin/sa-filter.sh -f ${sender} ${recipient}
Now let’s create the file we just refered to in the previous line of code.
server:/# pico /usr/local/bin/sa-filter.sh
And let’s add this into it
#!/bin/bash
/usr/bin/spamc | /usr/sbin/sendmail -i "$@"
exit $?
Now that that’s done, make sure that spamd is the owner and then restart everything.
server:/# chown spamd:spamd /usr/local/bin/sa-filter.sh
server:/# chmod 755 /usr/local/bin/sa-filter.sh
server:/# /etc/init.d/spamassassin restart
server:/# /etc/init.d/postfixreload
server:/# cd ~/
server:/# apt-get install razor
server:/# wget http://www.dcc-servers.net/dcc/source/dcc-dccproc.tar.Z
server:/# tar xfvz dcc-dccproc.tar.Z
server:/# cd dcc-dccproc-*
server:/# ./configure
server:/# make
server:/# make install
server:/# rm dcc-dccproc.tar.Z
server:/# rm -Rf dcc-dccproc-*
First lets make sure that DCC is working.
server:/# cdcc info
You should get a big long list of servers.
Next let’s get Razor all set up.
server:/# cd /etc/mail/spamassassin
server:/# mkdir .razor
server:/# razor-admin -home=/etc/mail/spamassassin/.razor -register
server:/# razor-admin -home=/etc/mail/spamassassin/.razor -create
server:/# razor-admin -home=/etc/mail/spamassassin/.razor -discover
server:/# pico /etc/mail/spamassassin/.razor/razor-agent.conf
We have to add one line to this file
razorhome = /etc/mail/spamassassin/.razor/
It will most likly be the only line in the file.
Now let’s open up the spamassassin local config file
server:/# pico /etc/mail/spamassassin/local.cf
We want to add two lines at the end of the file
/etc/mail/spamassassin/.razor/razor-agent.conf
use_dcc 1 dcc_path /usr/local/bin/dccproc dcc_add_header 1
One last thing we need to do is add DCC to your spamassassin init file.
server:/# pico /etc/mail/spamassassin/init.pre
Add this to the end of the file
loadplugin Mail::SpamAssassin::Plugin::DCC
Now restart Spamassassin and reload Postfix.
server:/# /etc/init.d/spamassassin restart
server:/# /etc/init.d/postfix reload
server:/# pico ~/.forward
Now lets add a line. Make sure to keep the quotes.
"|exec /usr/bin/procmail || exit 75"
Before we create our procmail instructions we need to do a couple things. First we want to create a new folder in our mail dir called “Spam” which we’ll forward all our spam to. Next we have to take note to where our shell is located.
server:/# touch ~/mail/Spam
server:/# which sh
/bin/sh
Now let’s tell Procmail what to do.
server:/# pico ~/.procmailrc
Make sure bash is set to the value you got before. Also change the MAILDIR to wherever your mail directory is. I’ve asumed it’s in ~/home/mail.
#Preliminaries
SHELL=bin/sh
MAILDIR=${HOME}/mail
DEFAULT=$MAILDIR/
:0:
* ^X-Spam-Flag: YES
$MAILDIR/Spam
Now if anything is flaged as spam it will automatically be moved into the spam folder.
Let’s restart spamassassin and postfix one last time, for good luck.
server:/# /etc/init.d/spamassassin start
server:/# /etc/init.d/postfix restart
Now sit back, relax and reminisce about the days when you used to get spam.
http://blog.psuter.ch/index.php?/archives/29-installing-spamassassin-on-debian-with-postfix.html http://aaron.birenboim.com/unix/postfix+spamassassin.htm http://linux.duke.edu/~mstenner/docs/sa-docs/advanced.html http://www.cs.rutgers.edu/~watrous/procmail-spam.html
Eric Goodwin is a web developer living in Victoria BC, Canada. You can contact him at eric@ericgoodwin.com