Wednesday, December 28, 2011

Cygwin package info

The Cygwin tool for querying the package database is cygcheck. It's like a read-only version of dpkg or rpm. For example:
$ which ssh
/usr/bin/ssh
$ cygcheck -f `which ssh`
openssh-5.9p1-1

Sunday, October 9, 2011

Erlang JSON

I've got a Erlang web service (built on Spooky) which I want to return JSON. How hard could that be?

The problem is a multitude of half-assed implementations.

What seems to work is mochijson2, which comes as part of mochiweb, but only the version from Mochi, not the Basho branch.

I have an Erlang structure that looks like this:
[{"colin",[3,2,1]},{"dave",[4,3,2]}]

That's an array with two tuples. Shouldn't be rocket science, but apparently what they really want is:
[{struct,[{<<"colin">>,[1,2,3]}]},{struct,[{<<"dave">>,[4,3,2]}]}]
or some shit. Really? If I have to munge it into that format, I might as well just encode it myself.

P.S. After going a few rounds with mochijson2, I ended up just encoding it myself.

Monday, October 3, 2011

Switching Git Repositories

I just had one of those cases where I pulled down a read-only copy of someone else's GitHub repo, thinking that it'll work just fine and I'll never have to change it, and sure enough, I'm doing something weird that breaks it. I figure out what's going on and fix it, so now I want to fork my own copy on GitHub and push my changes to that, but my local copy is cloned from this other person's repo. What do I do?

This is of course exactly the sort of thing Git was designed to deal with. When I cloned this project from GitHub originally, Git set up a local repository for me, and set the GitHub repo as a remote repository named "origin". Unlike SVN, there's nothing magic about the remote repository - you can have several, and push changes to all of them. The easiest thing to do in this case is just forget about your old origin, and set a new one. Here's a slightly edited command history:
$ git remote -v
origin https://github.com/flashingpumpkin/spooky.git (fetch)
origin https://github.com/flashingpumpkin/spooky.git (push)
$ git remote rm origin
$ git remote -v
$ git remote add origin git@github.com:bluegraybox/spooky.git
$ git remote -v
origin git@github.com:bluegraybox/spooky.git (fetch)
origin git@github.com:bluegraybox/spooky.git (push)
$ git push
Counting objects: 7, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 506 bytes, done.
Total 4 (delta 3), reused 0 (delta 0)
To git@github.com:bluegraybox/spooky.git
a34ae51..b15781e master -> master
$

Ta-daa!

Sunday, October 2, 2011

Erlang on Ubuntu

Ubuntu is still (as of Oct. 2011 on Natty) on Erlang R13B3. The release date for that is 2009-11-23. So, nearly two years out of date. I tried installing Nitrogen, but it requires at least R13B4, so it's time to install the new version from source.

The good news is that the source install doesn't stomp on the Ubuntu package. Ubuntu installs it to /usr/lib and /usr/bin, and the source install goes in /usr/local/lib and /usr/local/bin, which should be on your $PATH before /usr/bin.

Here's how to set up the latest version on a fairly vanilla Ubuntu 11.04. First, you need to download the Erlang source.

You may need to install some build tools - I had to install m4.
Then unpack and build the source:
tar xzf release.tgz
cd release
./configure
make
make install

Sunday, September 18, 2011

Weird filenames in Subversion

Had to add files with '@' in the filenames to Subversion (for iPhone - thanks, Apple). This doesn't work:
svn add Icon@2x.png

You need to do this instead:
svn add Icon\@2x.png@


Thanks to Jeff Kelley.

Friday, August 19, 2011

Ruby Gem executables

$ gem environment | grep "EXECUTABLE DIRECTORY"

- EXECUTABLE DIRECTORY: /var/lib/gems/1.8/bin
$ export PATH=$PATH:/var/lib/gems/1.8/bin/

Showoff dependencies

$ git clone git@github.com:bluegraybox/showoff.git

Cloning into showoff...
...
$ cd showoff/
$ gem build showoff.gemspec
/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- parslet (LoadError)
...
$ sudo gem install showoff
[sudo] password for colin:
Building native extensions. This could take a while...
Building native extensions. This could take a while...
ERROR: Error installing showoff:
ERROR: Failed to build gem native extension.
...
libxml2 is missing. please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
...
$ sudo apt-get install ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8
...
$ sudo apt-get install libreadline-ruby1.8 libruby1.8 libopenssl-ruby
...
$ sudo apt-get install libxslt-dev libxml2-dev
...
$ sudo gem install nokogiri
...
$ sudo gem install showoff
Building native extensions. This could take a while...
Successfully installed json-1.5.3
Successfully installed gli-1.3.2
Successfully installed showoff-0.4.2
...
$ sudo gem uninstall showoff
...
$ gem build showoff.gemspec
/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- parslet (LoadError)
...
$ sudo gem install parslet
...
$ gem build showoff.gemspec
...
$ sudo gem install -l -n . showoff
Successfully installed showoff-0.5.1
...

Thursday, June 23, 2011

Unwedging Ubuntu

Upgraded my Linux desktop to Ubuntu 11.04. Kicked it off before I went to bed. Woke up sometime in the middle of the night and wandered down to check on it. It had gotten wedged at some point. The only thing to do was reboot.

It came back up unable to mount the / partition (the only non-swap partition). It dropped me to a command prompt with / mounted read-only. e2fsck reported everything ok. So, what up?

Spent a while last night poking at it and Googling around, to no avail. Got a sysadmin buddy of mine to drop by. He was also largely mystified. Eventually, we stumbled across this post, which sounded pretty close to my symptoms. Tried it; it worked.

So, for posterity, reference, and backup, here's the fix:
mount -o remount,rw /
dpkg --configure -a
sync
reboot

The instructions also say to remount the drive read-only, but that failed on "drive busy", and didn't seem to make a difference.

Thursday, April 28, 2011

LaTeX

I'm avoiding the near-obligatory puns.

LaTeX is a document formatting language. Conceptually, it's kinda in the same ballpark as HTML; you have text with special bits of text mixed in that explain how to display it. LaTeX is geared for print media, though, so it's got a bunch of extra stuff thrown in. It's specifically designed for formatting books (or even more specifically, one book in particular). I'm just starting to play with it, but it's got features for managing footnotes, displaying mathematical formulas, etc.

I'm tinkering with it because I want to print something out on paper, and I care about how it's laid out. Also just because I want to learn it. I'm designing a sort of weekly reminder sheet. I want it to be two columns so I can fold it in half lengthwise. One column is tasks, the other is events. It'll look something like this:


Errands
  • groceries
  • deposit check

Household
  • laundry
Monday
Tuesday
  • 5:00pm happy hour

Wednesday
  • 7:00pm erlang meetup

Thursday
  • 7:30pm dinner w/ Will

Friday
Saturday
  • 9:00am plant sale

Sunday

The LaTeX code to do this is below. To create a PDF of it, run the pdflatex utility on it.
\documentclass{article}

\usepackage{multicol} % need this for manual column break
\usepackage[top=0.5in, bottom=0.5in, left=0.5in, right=0.5in]{geometry} % narrow margins
\usepackage{mdwlist} % for itemize*

\renewcommand{\labelitemi}{} % no bullet for top-level list items
\renewcommand{\labelitemii}{$\cdotp$} % bullet for next level of list items

\begin{document}
\begin{multicols}{2}
\begin{itemize*}

\item Errands
\begin{itemize*}
\item groceries
\item deposit check
\end{itemize*}

\item Household
\begin{itemize*}
\item laundry
\end{itemize*}

% need \vfill at the bottom of each column; otherwise one will be stretched to be as long as the other.
\vfill
\columnbreak

\item Monday

\item Tuesday
\begin{itemize*}
\item 5:00pm happy hour
\end{itemize*}

\item Wednesday
\begin{itemize*}
\item 7:00pm erlang meetup
\end{itemize*}

\item Thursday
\begin{itemize*}
\item 7:30pm dinner w/ Will
\end{itemize*}

\item Friday

\item Saturday
\begin{itemize*}
\item 9:00am plant sale
\end{itemize*}

\item Sunday

\end{itemize*}
\vfill
\end{multicols}
\end{document}

Tuesday, April 19, 2011

Io language installation

Update: Ran across someone else who's working through the installation issues, including addons.

Reading Seven Languages in Seven Weeks. Io looks interesting, so I thought I'd install it. Turns out there's no Debian/Ubuntu package for it, so you just have to download the source and compile it.

The instructions in README.txt are pretty good, but they don't tell you that you need to have cmake and g++ installed. You'll also get a whole mess of warnings if you don't have a bunch of development libraries installed.

Here's my complete installation process. (I installed to /opt/io):
# Install all the packages you're going to need
sudo apt-get install cmake g++ libssl-dev libgmp3-dev libffi-dev libncurses-dev libfreetype6-dev libglfw-dev libpng12-dev libtiff-dev libjpeg-dev libpcre3-dev libxml2 libxml2-dev
# Create the directory you're installing it to
sudo mkdir -p /opt/io
unzip stevedekorte-io-2010.06.06-329-gf641230.zip
cd stevedekorte-io-f641230/
mkdir build && cd build
time cmake -DCMAKE_INSTALL_PREFIX=/opt/io ..
time sudo make install
# Add the lib directory to your linker (ld) config
echo "/opt/io/lib/" | sudo tee /etc/ld.so.conf.d/Io.conf
sudo ldconfig

Then you can add /opt/io/bin to you path, or just invoke /opt/io/bin/io directly.

Monday, April 11, 2011

Groovy script v class

Apparently, a groovy file is either a script or a class file, not both (contrary to this example). If you try to do something like:
class Foo {
def bar
}

def baz = new Foo()

you'll get an error like:
Invalid duplicate class definition of class Foo


Further explanation here and here.

Wednesday, April 6, 2011

Set "Thip, crinkle, spoit" to OFF

It's a Dilbert reference.

Here's something that tends to bite me on the ass if I haven't started a new Maven project recently: Maven defaults to Java 1.3. Like, even if my /usr/bin/java is 1.6, Maven still horks if my code isn't compatible with 1.3, which it usually isn't because of generics. So I end up having to track down this snippet and paste it into my pom.xml:
    <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>


On a related note, it seems that the Groovy-Eclipse plugin doesn't add JUnit to the classpath for Groovy projects. So you need to paste
    <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>

into your .classpath file and restart Eclipse.

Thursday, March 31, 2011

Ruby gotcha: Hash default

Ruby lets you specify a default value when you create a Hash. Otherwise, if a key isn't found, it returns nil:
h = {}
=> {}
h['foo']
=> nil
h = Hash.new(1)
=> {}
h['foo']
=> 1

Great! That works fine when the default value is a number, but try a String, and you may see something weird:
h = Hash.new("default")
=> {} # empty hash created
h['foo']
=> "default" # 'foo' has the default value
h['bar'] += " value"
=> "default value" # 'bar' gets the default, plus " value" appended
h['baz'].slice!(0..1) # whoa, wait! what's going on here?
=> "de"
h['foo']
=> "fault" # what happened to foo?
h['bar']
=> "default value" # bar is still ok!
h
=> {"bar"=>"default value"} # but it's the only thing in the hash!

What's going on here? In short, a bunch of stuff that makes sense in retrospect, once you know what's happening under the hood, but isn't very intuitive.

The first thing is that calling h['foo'] doesn't add key 'foo' to the hash with the default value; it just returns the default. It doesn't add a key to the hash unless we actually set a value, like we do for 'bar'.

What about the "h['baz'].slice!(0..1)" line? That's actually modifying the default string in place, not just for baz, but for everyone. It still doesn't set a value for baz. That's a little surprising. If you do slice() instead of slice!(), it doesn't modify anything.
h['baz'].slice(0..1)
=> "de"
h['baz']
=> "default"

To modify baz and only baz, you need to do something like this:
h['baz'] += ""                 # set the value in baz to the default plus an empty string
=> "default"
h['baz'].slice!(0..1) # modify baz in place
=> "de"
h['foo']
=> "default" # foo still the default
h['baz']
=> "fault" # baz modified

Ok, that's a quirky edge case, but what's the big deal? Why am I writing a post about this? Because if set the default to an empty array, it really isn't going to work the way you want:
h = Hash.new([])
=> {}
h['foo']
=> []
h['foo'].push "red"
=> ["red"]
h['bar']
=> ["red"]
h['bar'].push "green"
=> ["red", "green"]

Instead of having an empty array associated with each key, there's one array shared by all keys. (I actually ran across this problem while trying to set a default hash, and it was much less obvious what had gone wrong.)
Now, you can create a Hash that will do what you want, but you need to pass a code block to the constructor:
h = Hash.new {|hash, key| hash[key] = []}
=> {}
h['foo']
=> []
h['foo'].push "red"
=> ["red"]
h['bar'].push "green"
=> ["green"]

So it turns out that this constructor is what you need to use pretty much anytime your default is a container or complex object. That's going to me handy for me to know, at least.

Tuesday, March 29, 2011

Time Capsule + Linux

I just spent a while figuring out how to access my Apple Time Capsule from my Linux box (Ubuntu 10.10). Turns out it's really easy, but there's a bit of a trick. You can do it through the "Connect to Server..." dialog on the "Places" menu on the panel.

Service type is "Windows Share" (which is a bit unintuitive).
Server is the IP address of your Time Capsule.
Share is what tripped me up. It's the name of the name of the Time Capsule as it appears in your Mac's /Volumes directory. Note that this may not be how it shows up in the Finder. My Time Capsule shows up in the Finder as "Colin MacDonald's Time Capsule", but it's mounted as /Volumes/Time Capsule Drive. "Time Capsule Drive" (with spaces) was what I needed for the Share name.
Folder should be "data". This is an existing directory on the Time Capsule drive. (If you look at the root directory for the Time Capsule when it's mounted, you'll also see a .sparsebundle file for each Mac you have backed up.)
User Name is your username on the Time Capsule. It's probably the same as your username on your Mac. (You'll be prompted for the matching password later.)
Domain Name is "WORKGROUP".
Bookmark is optional. It just gives you a handy name for the folder in your file browser (Nautilus).

Once you've mounted the Time Capsule, it will show up in Nautilus as a share. This will be its root directory, not the "data" folder. If you created a bookmark, it will also show up in Nautilus, but it will point to the "data" folder.

Of course, since it's a Windows share, I lose my Linux file permissions when I copy things there, which limits its use as a backup drive. I expect there'll be another post when I get that figured out.