Last night I took a notion into my head and wound up spending a solid few hours with Ruby. I’m happy with how that went! There’s some first-time-with-a-new-language friction, but nothing out of the ordinary. Here’s what I came up with, and afterwards, why I chose that and what I think it shows that I accomplished that.
module Jekyll class MusicLink < Liquid::Tag
def initialize(tag_name, contents, tokens)
super
@contents = contents
end
def render(context)
@affiliateCode = 'secret' # Fill in yours!
page = context.environments.first['page']
if page['music-artist'] && page['music-track']
music_url, music_string = getMusic(page['music-artist'], page['music-track'])
return %(<span class='music-box'>Music: <a class='music-link' href="#{music_url}">#{music_string}</a></span>)
else
return %()
end
end
def makeItunesTarget(artist, track)
iTunesURL = URI("https://itunes.apple.com/search")
iTunesParams = {
:country => "us", :media => "music",
:limit => "5", :entity => "musicTrack",
:term => artist + " " + track,
}
iTunesURL.query = URI.encode_www_form(iTunesParams)
return iTunesURL
end
def getFromItunes(iTunesURL)
http = Net::HTTP.new(iTunesURL.host, iTunesURL.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Get.new(iTunesURL.request_uri)
response = http.request(request)
if response.code == "200"
jsonResponse = JSON.load(response.body)
if jsonResponse['resultCount'] == 0
jsonResponse = false
end
else
jsonResponse = false
end
return jsonResponse
end
def makeAnchorFromItunesData(iTunesJSON)
unless iTunesJSON
return iTunesJSON, iTunesJSON
end
primaryResult = iTunesJSON['results'].first
if @affiliateCode
urlRegex = /(https:\/\/itunes\.apple\.com\/[^?]+\?[^&]+).+/
urlReplacement = '\1\2&partnerId=30&siteID=' + @affiliateCode
affiliatedTrackUrl= primaryResult['trackViewUrl'].sub(urlRegex, urlReplacement)
primaryResult['trackViewUrl'] = affiliatedTrackUrl
end
anchorURL = primaryResult['trackViewUrl']
anchorString = CGI.escapeHTML("%s - %s" % [primaryResult['artistName'], primaryResult['trackName']])
return anchorURL, anchorString
end
def getMusic(artist_name, track_name)
music_url, music_string = makeAnchorFromItunesData(getFromItunes(makeItunesTarget(artist_name, track_name)))
return music_url, music_string
end
end end
Liquid::Template.register_tag('music', Jekyll::MusicLink)
This creates a new Liquid tag, {% music %}
, which can be inserted in page templates.
I added it to my footer.html
after the byline, timestamp, and categories.
The tag checks whether the post’s YAML front-matter has data for a musician and a track name.
If the post has that data, the plugin attempts to create a link to the iTunes Store for the given track.
With makeItunesTarget()
it puts together a URL that is a query to the iTunes Store Search API, with getFromItunes()
it loads the query URL and hands off the response to the standard library’s JSON parser, and with makeAnchorFromItunesData()
it takes the first search result and generates text to use for an <a>
tag and a URL to use for the tag’s href
attribute (if you have an affiliate code for the iTunes store, it’ll be inserted).
Finally, there’s a convenience function, getMusic()
, that just composes the previous three.
Part of why this worked well is that it’s another project with limited scope: I had a specific objective in mind, so I was able to keep moving gradually towards it. However, that limited scope was a way of making progress towards the broad goal of “learn Ruby” and also took on the medium-scope goal of “learn the iTunes Store Search API.” As a practical matter, learning to work with other people’s APIs, whether they’re libraries, services, or daemons, is an important skill for a working programmer; toy projects that include cultivating that skill are good uses of my time. Learning new languages is also a career-long thing: for all the talk of Lisp being “the hundred-year language,” no-one now working as a programmer will be programming in just one language for the rest of their days. There are shell scripts and libraries and wrappers: there is a fragmented world that despite the friction of fragmentation, would not actually be better-served by a language monoculture. In addition, there are plenty of exciting things out there whose roots are in Ruby, so I was enthusiastic about picking up a smattering of Ruby.
I’m definitely fond of Ruby so far.
Part of this is because I’m getting to the point where I’m seeing parallels with other languages and able to make good guesses about how a new language will behave.
I was able to guess from reading source “oh okay, Ruby is one of the languages where the return value of a function, if not explicit, is the value of the last statement evaluated in its body,” was pleasantly surprised that it has the same tuple-packing return-multiple-values feature as Python, and noticed “oh hey neat, there’s a Scheme-like function!()
naming convention for functions that mutate their parameters.”
So that’s all good stuff.
Part of choosing Ruby, too, is that I’m currently blogulating via Octopress, which is built on Ruby. Most of why I chose it is that Wordpress is awful (on the axes I care about), but now that I’ve chosen it, I want to have a grasp of how it works. That means learning Ruby and tinkering—which I’m looking forward to.
As a supplemental note, if this stuff sounds to you like a good attitude for a programmer to have, you should hire me.