My first Go application – nowplaying

Posted by – October 21, 2014

I've now completed a Go version of my first Clojure app, called nowplaying. You can view the source code here. The Clojure version is here.

Since Go is more statically-typed, I needed more code to manage parsing JSON and XML feeds. I also had to wrestle with one non-UTF8 XML feed. The most interesting comparison is to view the two files that grab and parse feeds from the 4 radio stations. The Clojure version is 66 lines of code versus 119 lines in Go.

Using Go to parse non-UTF8 XML feeds

Posted by – October 6, 2014

For a learning exercise, I'm rewriting my Nowplaying Clojure web application into Go. In the case of Clojure, the clojure.xml package handled this non-UTF8 XML file:

<?xml version="1.0" encoding="ISO-8859-1"?>
<nexgen_audio_export>
  <audio ID="id_1667331726_30393658">
    <type>Song</type>
    <status>Playing</status>
    <played_time>09:41:18</played_time>
    <composer>Frederic Delius</composer>
    <title>Violin Sonata No.1</title>
    <artist>Tasmin Little, violin; Piers Lane, piano</artist>
  </audio>
</nexgen_audio_export>

without complaint, but in the case of Go, I got this error:

xml: encoding "ISO-8859-1" declared but Decoder.CharsetReader is nil

when I tried my first version:

type Piece struct {
  Title    string
  Composer string
}
 
type SecondInversionFeed struct {
  XMLName xml.Name             `xml:nexgen_audio_export`
  Audio   SecondInversionAudio `xml:"audio"`
}
 
type SecondInversionAudio struct {
  Title    string `xml:"title"`
  Composer string `xml:"composer"`
}
 
func translateSecondInversion(data []byte) Piece {
  var feed SecondInversionFeed
  err := xml.Unmarshal(data, &feed)
  if err != nil {
    log.Fatal("Unmarshal error:", err)
  }
  return Piece{feed.Audio.Title, feed.Audio.Composer}
}

I read this Stack Overflow thread a few times, but I still wasn't sure how to use go-charset or some other library to accomplish my task.

I first tried using go-charset to translate the file and pass it to Unmarshal, but that declaration at the top:

  <?xml version="1.0" encoding="ISO-8859-1"?>

still caused the same error. I then realized that the Unmarshal function simply creates a new Decoder, so I just had to pass a reference to the charset.NewReader function, and the xml package would use that to translate my XML data.

Here is a small program that demonstrates my approach:

package main
 
import (
  "bytes"
  "code.google.com/p/go-charset/charset"
  _ "code.google.com/p/go-charset/data"
  "encoding/xml"
  "fmt"
)
 
type Feed struct {
  XMLName xml.Name  `xml:nexgen_audio_export`
  Audio   FeedAudio `xml:"audio"`
}
 
type FeedAudio struct {
  Title    string `xml:"title"`
  Composer string `xml:"composer"`
}
 
func main() {
  xml_data := []byte(`
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <nexgen_audio_export>
    <audio ID="id_1667331726_30393658">
      <type>Song</type>
      <status>Playing</status>
      <played_time>09:41:18</played_time>
      <composer>Frederic Delius</composer>
      <title>Violin Sonata No.1</title>
      <artist>Tasmin Little, violin; Piers Lane, piano</artist>
    </audio>
  </nexgen_audio_export>
`)
  var feed Feed
 
  reader := bytes.NewReader(xml_data)
  decoder := xml.NewDecoder(reader)
  decoder.CharsetReader = charset.NewReader
  err := decoder.Decode(&feed)
  if err != nil {
    fmt.Println("decoder error:", err)
  }
  fmt.Println(feed.Audio.Title)
}

My first Clojure application – nowplaying

Posted by – September 4, 2014

Along with James, I listen to a lot of classical music via streaming stations. Many of those don't have very useful sites for looking up on my phone what is playing at the moment. To remedy that, I just created my first site in Clojure:

http://nowplaying.tristanmedia.com/

It's running on a free Heroku instance, so it may be a little slow to load.

You can view the source code here.

Update

I made a new version that uses jQuery to request the data from a JSON interface built with Liberator.

My first Django application

Posted by – July 18, 2014

I have run Filterizer, a one-page art calendar, using Rails in the past. I just relaunched it using Python and Django, running on Heroku. The source code is here.

Rails 4.1, initializers, and secrets.yml

Posted by – April 9, 2014

I'm using 4.1 on a new project. When I tried to set up an initializer using the values in secrets.yml, I got this error:


/Users/barry/.rvm/gems/ruby-2.1.1@rails4.1/gems/railties-4.1.0.rc2/lib/rails/application.rb:311:in `secrets': uninitialized constant Rails::Application::YAML (NameError)
from /Users/barry/projects/archiv8-billing/config/initializers/chargify.rb:2:in `block in '
from /Users/barry/.rvm/gems/ruby-2.1.1@rails4.1/gems/chargify_api_ares-1.0.4/lib/chargify_api_ares/config.rb:6:in `configure'
from /Users/barry/projects/archiv8-billing/config/initializers/chargify.rb:1:in `
'

I fixed it by adding require 'yaml' to the top of my initializer:


require 'yaml'
Chargify.configure do |c|
  c.api_key   = Rails.application.secrets.chargify_key
  c.subdomain = Rails.application.secrets.chargify_subdomain
end

Validating URLs for non-ActiveRecord objects

Posted by – July 9, 2010

I'm using Mongoid and MongoDB on a new project, so my models are not derived from ActiveModel. On previous projects I just used the validates_url_format_of plugin, but for this project I put the code from the module into an initializer (config/initializers/validation.rb) and used extend.

module ValidatesUrlFormatOf
  IPv4_PART = /\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]/  # 0-255
  REGEXP = %r{
    \A
    https?://                                                    # http:// or https://
    ([^\s:@]+:[^\s:@]*@)?                                        # optional username:pw@
    ( (([^\W_]+\.)*xn--)?[^\W_]+([-.][^\W_]+)*\.[a-z]{2,6}\.? |  # domain (including Punycode/IDN)...
        #{IPv4_PART}(\.#{IPv4_PART}){3} )                        # or IPv4
    (:\d{1,5})?                                                  # optional port
    ([/?]\S*)?                                                   # optional /whatever or ?whatever
    \Z
  }iux
 
  DEFAULT_MESSAGE     = 'does not appear to be a valid URL'
  DEFAULT_MESSAGE_URL = 'does not appear to be valid'
 
  def validates_url_format_of(*attr_names)
    options = { :allow_nil => false,
                :allow_blank => false,
                :with => REGEXP }
    options = options.merge(attr_names.pop) if attr_names.last.is_a?(Hash)
 
    attr_names.each do |attr_name|
      message = attr_name.to_s.match(/(_|\b)URL(_|\b)/i) ? DEFAULT_MESSAGE_URL : DEFAULT_MESSAGE
      validates_format_of(attr_name, { :message => message }.merge(options))
    end
  end
 
end

Then my model extends that module:

class Location
  include Mongoid::Document
  include Mongoid::Timestamps
  extend ValidatesUrlFormatOf
 
  validates_url_format_of :url, :allow_blank => true
...

Testing HTTP basic authentication with RSpec 2

Posted by – July 6, 2010

Here's how I test my admin controllers that use HTTP basic authentication using RSpec 2:

before(:each) do
    user = 'test'
    pw = 'test_pw'
    request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
end

Actually, that's how I did it when I first tested things, but I've since put it in its own module under spec/support/auth_helper:

module AuthHelper
  # do admin login
  def admin_login
    user = 'test'
    pw = 'test_pw'
    request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
  end  
end

and now my controller spec looks like this:

describe Admin::LocationsController do
 
  include AuthHelper
 
  before(:each) do
    admin_login
  end
 
  describe "GET index" do
    it "assigns all locations as @locations" do
      loc = Factory.create(:location)
      get :index
      assigns(:locations).should eq([loc])
    end
  end
 
  describe "GET show" do
    it "assigns the requested location as @location" do
      loc = Factory.create(:location)
      get :show, :id => loc.id
      assigns(:location).should === loc
    end
  end  
 
end

That "Factory" line comes from my use of factory_girl rather than fixtures.

Rails 3, RSpec, Mongoid and Database Cleaner

Posted by – July 5, 2010

After wrestling with various combinations of cleaning out my database between tests, this is what I'm using on a new Rails 3 application that uses Mongoid, RSpec 2, and Database Cleaner. I have one table (neighborhoods) which is populated using rake db:seed, so I'm excluding that from the cleanup.

Put this into your spec/spec_helper.rb:

require 'database_cleaner'
 
RSpec.configure do |config|
  config.mock_with :rspec
 
  config.before(:each) do
    DatabaseCleaner.orm = "mongoid" 
    DatabaseCleaner.strategy = :truncation, {:except => %w[ neighborhoods ]}
    DatabaseCleaner.clean
  end
end

UPDATE: This isn't working for me now. Apparently the config.before(:each) part isn't being called in the versions of rspec (2.0.0.beta.21), cucumber (0.8.5), and cucumber-rails (0.3.2) that I'm using now. I'm now using the approach by Kevin Faustino here.

MacPorts annoyance with PHP and MySQL

Posted by – October 27, 2009

I'm doing some local WordPress development, so I set up Apache, PHP, and MySQL using MacPorts. Apparently, the default setup does not set the location of the MySQL socket for you, so I copied /opt/local/etc/php5/php.ini-development to /opt/local/etc/php5/php.ini and changed these lines:

pdo_mysql.default_socket=/opt/local/var/run/mysql5/mysqld.sock
mysql.default_socket =/opt/local/var/run/mysql5/mysqld.sock
mysqli.default_socket =/opt/local/var/run/mysql5/mysqld.sock

Quick fix for moving to Vlad the Deployer 2.0.0 with git

Posted by – September 9, 2009

I just upgraded my dev machine to version 2.0.0 of Vlad the Deployer. I got one unexpected error -- "Please specify the deploy path via the :deploy_to variable" -- but here is how I fixed it:

git support is now a separate gem, so remember to run

sudo gem install vlad-git

I also had some problems when version 1.4.0 was also on my machine, so I uninstalled that one with:

sudo gem uninstall vlad -v "1.4.0"