Tag Archives: ruby

Your own dyndns with Linode

Problem: you want a fixed host name to connect to your home network on some residential ISP service. You don’t have a static ip, so it could change at any time, and you want it to stay up to date.

There are a lot of services out there like dyndns and no-ip that do that for you for free, but it’s on their domain names. If you are hosting some of your own domains on linode, you can use their DNS API to do the same thing, but with your own host names. This is a little script I banged out 6 months ago to do that. The ruby can be used as is, the config.yml will need to be tweaked to your own needs below.

#!/usr/bin/ruby

require "yaml"
require "rubygems"
require "linode"
require "pp"

config = YAML.load_file('config.yml')

domain = config["domain"]
host = config["host"]

@d_id = 0
@r_id = 0

l = Linode.new(:api_key => config["API Key"])

# Get the domain id
l.domain.list.each do |d|
    if d.domain == domain
        @d_id = d.domainid
    end
end

# Get the actual host resource id
l.domain.resource.list("DomainID" => @d_id).each do |h|
    if h.name == host
        @r_id = h.resourceid
    end
end

# Update that to the current remote record
l.domain.resource.update(
                         "DomainID" => @d_id,
                         "ResourceID" => @r_id,
                         "Target" => "[remote_addr]"
                         )

This assumes a config.yml that looks something like this:

API Key: from linode account
domain: adomainthatIown.com
host: myhostname

Why Mixins matter

The Ruby language has this great feature called Mixins.  It lets you add methods to existing classes. Take this example from Ruby on Rails (which uses mixins heavily):

time = 2.days.ago

Yes, that’s valid code and does what you think. The result is a DateTime object. It was accomplished by adding methods the Numeric and to DateTime. If you come from a strongly locked down language background, this sort of thing horrifies you. If you don’t, you think it’s awesome. Remember, this isn’t inheritance, it’s actually changing the fundamental classes.  This makes it much easier to retrofit on existing code. The python folks call it Monkey Patching, which makes it sounds kind of dirty. I like Mixins better.

While Ruby has been my language of choice for personal time hacking, I’m doing a lot of Java recently due to working on Android things. This past weekend I remembered one of the things I dislike about strongly typed languages: type gymnastics.

If you’ve got a time as milliseconds in Java and you want it in a string you’ve got to first convert it to a Calendar, then to a Date, then give that Date to a DateFormat. You’d think that Calendar.toString() might actually give you something useful, but it doesn’t. Oh, and also realize that every one of these objects has it’s own internal notion of TimeZone, quietly gotten from the system, so if you are trying to do something in UTC, you have to manually force that on them all. This is the crux of type gymnatics, long changes of type conversion operations because to get from A to B (in my case A:long, B:string) you’ve got to go through C, D, and E in the right order. These conversions are error prone, and clumsy, and largely produce some part of your namespace called “util” that is largely static methods to convert between one thing and another.

This is because the authors of A never thought you’d have to get to B. What’s so interesting about B? C is really nice this time of year, wouldn’t you just like to stay there instead?

The real problem is that original authors of software rarely really understand what their software will be used for, and the more immutable languages don’t let you fix things after the fact. In Java this means that any project ends up with a “utils” package which is used to do conversions between types that can’t natively be converted. All that code is just boring boiler plate, which is where you are most often going to make a hard to find mistake. Nothing generates errors like boredom.

Monitoring and Controlling your Proliphix thermostat with Ruby

It’s been 2 years since I got my Proliphix thermostat, and while I did some early hacking on it, largely the whole effort just sat around for the last 2 years.  However, with the fun of connecting up my weather sensors, I went back in this weekend and beat the code into a much more sane interface.

Thermostat.rb 1.1.1 was released yesterday.  It provides a concise interface to the Proliphix web services API.  An example of the usage is something like:

  thermostat = Thermostat.new("hostname", "admin", "password")

  # get the current temperature
  current_temp = thermostat.temp

  # get the current setback heat value
  current_target_temp = thermostat.heat_to

  # set the thermostat to 69F (units are set in the thermostat)
  thermostat.heat_to = 69

I’ve got support under the covers for everything in the Proliphix API. I’ve only mapped about 1/2 of it to the user visible interface, starting with all the functions I’ve tended to need or use.  I was a good little agile developer and built unit tests for everything here.  Using the new module, I added the thermostat to my homegraph code, with some pretty reasonable results:

All this is released under the MIT license.

Building ruby projects with newgem

Over the weekend I was working on revamping a whole set of older ruby projects, some having to do with my Proliphix Thermostat.  I had this crufty Rakefile from the icalendar project that I’d been copying and modifying for new projects, and it was slowly degrading.  I thought that there had to be a better way.  There is, it’s called newgem.

Newgem is like h2xs in the Perl world, something to stub out a new module with all the right files and structures.  But, as the Ruby folks tend to do, it ups the ante in the process.  In addition to the basic build, packaging, testing, and coverage results you’d expect, you also get a targets for: creating you rubyforge website, publishing your gems to rubyforge and gemcutter, posting release announcements on rubyforge, and even posting blog posts about your release (though I haven’t configured that one yet).

The release cycle is now:

rake release
rake website
rake post_news

The Rakefile created is loading these features from base modules, so it’s only ~ 30 lines, a heck of a lot easier to maintain than the 300 line Rakefiles I had which provided about 1/2 these features inline.

If you are doing Ruby development, you should really check out newgem.

Temperature.rb released

As I’ve been working on my weather station at home at nights, I realized the code would be a lot cleaner if I wasn’t constantly keeping track of temperature units.  So I created the Temperature module for Ruby which adds methods to numbers to make them implicitly temperatures, as well as a parsing method on strings.  To get a flavor of it, here are some examples:

freezing_in_F = 32
freezing_in_C = freezing_in_F.to_C

boiling_in_C = 100
boiling_in_C.units = "C"
boiling_in_F = boiling_in_C.to_F

absolute_zero = "0K".to_degrees
absolute_zero_in_C = absolute_zero.to_C
absolute_zero_in_F = absolute_zero.to_F

The full documentation for the project is on rubyforge.  Gems, tar, and zip format have all been published, and it should be propagating out to the main gem servers tonight.  It’s not exceptionally complicated, however it is convenient, and it’s even got 236 unit tests to ensure it’s doing things right.  The code is released under the MIT license, so you are pretty much able to do anything you want with it.

Things I learned this week

In no particular order, a quick run down of some things I learned this week (no particular order):

Ruby / Ruby on Rails

  • In Ruby: don’t use f.readlines.each in a loop, as that waits for all output, then iterates.  Use f.readline instead, but be prepared to catch the EOF exception when you finish (it’s a documented part of that interface)
  • In Ruby on Rails: rss is a valid format (at least in 2.2.2), and can be used in a builder

Mono / C#

  • In Mono: File.Exists fails on directories.  Even though directories are just special files in Linux, the implementation decided that wasn’t right.  Use Directory.Exists instead.

Networking

  • IPv6 finally made sense to me, after implementing a 3 site topology for my Network Lab graduate class.

Ruby Typed Accessors

Pat Ladd started IMing me a week ago looking for a simple Ruby solution to the following problem:

  • He was building objects of a SOAP web service
  • SOAP in Ruby returned everything as strings
  • He wanted his objects to have numbers be numbers, not strings

You could do a lot of manual conversion, but it really seemed like there should be a simple way to say “this is an int, do the conversion for me”.  While ruby is dynamically typed, it is also strongly typed, so String + Int causes an exception, typically in a place you weren’t expecting.

He and I googled around for a while and found that no one seemed to have tackled this yet.  Later that day Pat sent me the first version of this, all done with some fun meta programming.  He and I both realized that this was probably useful enough that we should share, so I signed up to help package it as a gem.

Announcing Typed Accessors Gem

A quick example of how this works is below:

class Foo
float_accessor :float
date_accessor :date
end

>> f = Foo.new
=> #
>> f.float = “1.4”
=> “1.4”
>> f.float
=> 1.4
>> f.float = “1”
=> “1”
>> f.float
=> 1.0
>> f.date = “2009-10-30”
=> “2009-10-30”
>> f.date
=> #
>> f.date.to_s
=> “2009-10-30”

The full documentation can be found on rubyforge, and the source tree on github.  It’s even got unit tests as of this weekend (which already found and fixed one bug).  This is released under MIT license, so do with it what you will.

If you want to use typed accessors just:

gem install typed_accessors

And require it in your environment.  It works as a mixin on Class, so very seemlessly fits in your environment.

Why I love Ruby on Rails… impement a new feature in about an hour

Yes, I’m a fanboy, but with good reasons.

Tonight I decided that I’d really like to add openid support to the MHVLUG Event Calendar.  Accounts aren’t used that much in it, because you have to be in the admin role to notice any difference.  However, it’s just one less field for me to have to fill in.

So, the 6 million dollar question, what was the duration of time between deciding I’d like to do this (though having no idea how), and having a working implementation…

65 minutes.

Which included getting up to get dinner in the oven.

In blow by blow format:

  • A quick bit of googling got me to the openid-authentication railscast, which I watched all the way through.
  • I fixed a couple of his refactoring bits to work with newer restful_authentication.
  • I also installed an old open_id_authentication plugin the first time, which gave me an exception.  Make sure to get the latest on github.
  • I modified my user to have my identity_url using the rails console
  • I used the rails debugger to figure out that I needed an extra slash on the end of my identity_url, which is why my login with openid wasn’t working

Which is a pretty serious amount of bouncing around because I had no idea what I was doing when I got started.  That will turn subsequent openid enablement into a 15 minute exercise.

Ruby Snippet – Tagging mp3 files

For the npr shows that don’t podcast, I use icecream to save them off for my own time shifting. The files end up with names like “car_talk_2008_01_17.mp3”. Until recently, that was good enough, but the new Sandisk players that both my wife and I have only function on tags, not on filenames. Last night I wrote this small ruby script to fix that:

#!/usr/bin/ruby

require "date"
require "rubygems"
require "mp3info"

ARGV.each do |file|
  title, datestr = file.scan(/(\w+)_(\d+_\d+_\d+).mp3/)[0]
  if title and datestr
    date = DateTime.parse(datestr.gsub!(/_/,"-"))
    title.gsub!(/_/, " ")
    title = title.split.map {|a| a.capitalize}.join(" ")
    puts date
    puts title
    Mp3Info.open(file) do |mp3|
      if not mp3.tag.album == "#{title} #{date.strftime("%Y")}"
        mp3.tag.album = "#{title} #{date.strftime("%Y")}"
        mp3.tag.artist = "WAMC Recordings"
        mp3.tag.title = date.strftime("%Y %m %d - #{title}")
      end
    end
  end
end

What’s going on should be pretty clear, but I’ll highlight a few things.  First we are iterating over ARGV, so this takes a list of files on the command line.  DateTime has a parser, which is actually pretty good.  Anything that looks like a standard date can be converted back to one.

Ruby methods always return object instances, which let you do things like

title.split.map {|a| a.capitalize}.join(" ")

where you split on white space, capitalize the components in the array, and join it back into a string.

And to wrap it all up, we’ve got a great Mp3Info library as a gem. Wondering where the save call is? Well that’s one of the wonderful things about ruby do blocks, the save is implicit when we end the block as mp3 goes out of scope. No need to make sure you clean up those resources or sync manually, because by doing the action in the do block all the setup / teardown is handled by the system. I used to be confused about do blocks, now I love them for this very reason.