Made Up Technical Terms #3
Codefix – the amount of coding needed in order to maintain mental health:
I feel much better now I’ve had my codefix.
Made Up Technical Terms #2
Cosmic Ray – A developer who exhibits a naturally deleterious effect on code.
Ruby Tuesday #7 Part 2: Tips from the Inside
Getting up to speed in any language is much easier when those who have trodden the path ahead of you share their experiences. This post by Peter Cooper contains 21 tips he’s gleaned from writing Ruby Inside. Very useful. And if you read to the end, there’s a bonus tip.
Ruby Tuesday #7 Part 1: Even Twittier
Having managed to download the public timeline and output in a friendly format, the next task was to access some other methods of the Twitter API. A couple of other timeline methods exist: friends_timeline and user_timeline. Both can take the username as part of the URL. How hard will it be to update the code to accommodate these calls?
One of the first things I noticed was that each of the URL’s for the Twitter API have the same base (http://twitter.com/statuses), so it’d be good to split this out. I updated the get_url method accordingly:
def get_url(base_address, path)
url = URI.parse(base_address + path)
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http| http.request(req)}
res.body
end
I tried to use the join method of URI, but it seemed to strip out the http:// part of the URL. The next step was to create a new class called Client (in the Twitter file.) It looks like this:
class Client
BASE_ADDRESS = 'http://twitter.com/statuses'
def download_public_timeline
download_timeline('/public_timeline.xml')
end
def download_friends_timeline(username)
download_timeline("/friends_timeline/#{username}.xml")
end
def download_user_timeline(username)
download_timeline("/user_timeline/#{username}.xml")
end
private
def download_timeline(path)
get_url(BASE_ADDRESS, path)
end
end
For those of us learning Ruby, there’s a couple of interesting things to note here. The first is the constant – it’s denoted by the use of capital letters. It’s not really a constant – I could change the value if I liked, but Ruby convention is that if it begins with a capital letter then it’s a constant. Those of us coming from a C# background may be surprised to learn that this constant could be used anywhere in the program – it is not limited to the scope of the class in which it is defined. The constant can be qualified by the class name (e.g. Client::BASE_ADDRESS) for clarity. The second point of interest is the use of the word private. Any method defined after the use of private will be have private visibility (instances of the class and subclasses) until the end of the class or the words public or protected. Methods are public by default. Once again, private is not a keyword but a method specifies that the following method definitions are private.
All that’s left is to call these methods from the main body – which looks like this:
require 'Twitter'
$KCODE = "u"
translator = Translator.new
client = Client.new
translator.xml_to_tweets(client.download_public_timeline).each do |tweet|
puts "#{tweet.user.screen_name} says #{tweet.text}"
end
translator.xml_to_tweets(client.download_friends_timeline('put_your_username_here')).each do |tweet|
puts "#{tweet.user.screen_name} says #{tweet.text}"
end
translator.xml_to_tweets(client.download_user_timeline('put_your_username_here')).each do |tweet|
puts "#{tweet.user.screen_name} says #{tweet.text}"
end
The funny looking line near the top ($KCODE…) sets the text encoding to be UTF-8. I set this because there is some character data in the timeline that would look funny otherwise. (I was using Netbeans when I put this in and it had no effect – garbled output for some character data – whereas at a terminal calling the program I got the results I expected, I haven’t tried this yet with Sapphire in Steel.)
It was fairly straightforward to add this extra functionality. I fell into the trap of using C# style names, which I corrected before writing this post, so I’m clearly still thinking in C#. And even though the Client class is fairly concise, I wonder if it could be made more concise yet. The next step is to add the ability to tweet directly from the Ruby program.
Seeing the Big Picture
K. Scott Allen’s post on the limits to visual tools is an interesting read. The main contention is that visual tools don’t scale and consequently have a limit. The examples in the post back up this point. I wonder if this is a problem with visual tools per se, a limitation of current tools or a misuse of the tools. It may be a combination of these factors. The first question to ask is: do text based tools scale well? The good news is that we can split a system up into an arbitrary number of files. This is great for understanding the contents of a file – but the system as a whole is still very difficult to understand, especially for those coming to the system cold. Putting everything into one diagram is the equivalent of putting all our code into one text file – I’m going to assume you agree with me that that’s a bad idea for all but the most trivial of system. With a well factored codebase, the best route into the system for most people, in my experience, is some sort of graphical overview – the key here being that it must be at the right level of abstraction.
In creating and reviewing architectural documents and walkthroughs, I’ve often faced the same issue of getting the right amount of information into diagrams. There is a temptation to have one diagram that shows everything – this temptation should be resisted as it leads to confusion. Having a number of views of a model (each at differing levels of abstraction and with a different perspective and focus) is the best way I’ve found of distilling the right amount of information. What we need from the tooling is a way to link the views together. When you think about it, the principle here is similar to that used in splitting up a codebase into multiple files. Diagrams and models are, of course, not text free – but in the right context one diagram can save a lot of time and effort.
So, I’m not convinced that there is an inherent quality of visual tools that limits their ability to scale – but the way in which we use them and some of the functionality provided may need to change to accommodate scale. Another thing to consider is that models can be represented with both text and diagrams – potentially allowing different people to use representations that are closest to their natural strengths.
Ruby Tuesday #6 : A little XML
Last week I wrote some code to download the public timeline from Twitter. The public timeline is in XML format, so we need a little code to translate it into something more readable. Before getting to the XML reading, I moved the code from last week into a new file called, very imaginatively, Twitter.rb. I also discovered that the name I gave the method (getUrl) isn’t very Rubylike, so I changed it (to get_url). Now, I’m not the biggest fan of underscores, but part of this learning Ruby business is to learn the culture and customs as well as the language itself.
Next, I decided that it would be good to have a class representing a tweet and another class representing a user. I put both of them in the Twitter file. Here are those classes:
class Tweet attr_reader :created, :id, :text, :source, :truncated, :user def initialize(created, id, text, source, truncated, user) @created = created @id = id @text = text @source = source @truncated = truncated @user = user endend class User attr_reader :id, :name, :screen_name, :location, :description, :image_url, :url, :protected def initialize(id, name, screen_name, location, description, image_url, url, protected, followers_count) @id = id @name = name @screen_name = screen_name @location = location @description = description @image_url = image_url @url = url @protected = protected @followers_count = followers_count endend
I’ve used the attr_reader method to create read-only attributes which are set in the initialise method of each class. For those who, like me, are new to Ruby the initialise method performs a similar purpose to a constructor. The funny @ signs denote instance variables. I could have made these classes by using attr_accessor (which creates a getter and a setter), but I like this style of object. I expect I’ll find a simpler way of doing it as I go along.
Next we need to read XML into these objects. I created a class called Translator and in it created a method called xml_to_tweets (and a method it calls called element_to_user.) Here’s the class:
class Translator def xml_to_tweets(xml) tweets = [] xml_document = Document.new(xml) root = xml_document.root root.elements.each("status") do |element| tweets << Tweet.new(element.elements["created_at"].text, element.elements["id"].text, element.elements["text"].text, element.elements["source"].text, element.elements["truncated"].text, element_to_user(element.elements["user"])) end tweets end
def element_to_user(element) User.new(element.elements["id"].text, element.elements["name"].text, element.elements["screen_name"].text, element.elements["location"].text, element.elements["description"].text, element.elements["profile_image_url"].text, element.elements["url"].text, element.elements["protected"].text, element.elements["followers_count"].text) endend
REXML is a conformant XML processor, so if you’ve done any XML programming before, it’ll be familiar. Three things to note here. Firstly, this code is using the REXML library (more info here) so I had to add 2 lines to the top of the file. Those lines are:
require 'rexml/document'include REXML
Secondly, for my fellow Ruby noobs, note that the value of the last expression in a method is returned (no return statement needed, although you can put it in if it you like.)
Thirdly, for the noobs again, note the array syntax. A new array is created with the [] syntax and new elements are added with the << operator.
So, all that’s required now is some code to see if it works. I used this code:
require 'Twitter'
translator = Translator.new
translator.xml_to_tweets(get_url('http://twitter.com/statuses/public_timeline.xml')).each do |tweet| puts "#{tweet.user.screen_name} says #{tweet.text}"end
The output of that is a lot friendlier than the XML I was outputting last week, and it was fairly easy to write. Next week I’ll need to extend the code to call some of the other methods of the Twitter API.
Made Up Technical Terms
Welcome to the first (and, who knows, maybe the last) installment of a series of technical terms that I have made up (or will make up.) Today’s made up technical term is:
Facecrook – someone who adopts false identities on social networking sites with criminal intent.
Ruby Tuesday #5 : Hello World
The obvious first thing to do with a new programming language is a Hello World exercise. (If you want to know how to do Hello World in a variety of ways, follow this link.) A simple Hello World in Ruby is trivial (I used it to test my installation of Ruby here):
puts "Hello World"
Trouble with that example is we don’t learn a whole lot about Ruby. So, I figured I’d stick with a Hello World, but instead of outputting to the screen, I’d output a tweet. Twitter has an API (documented here) that should enable me to tweet via a Ruby program.
To start down this path, I figured it made sense to start with something relatively straightforward – download the public timeline. Here’s a function that takes an address and returns the response from that address:
require 'net/http'
def getUrl(address)
url = URI.parse(address)
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http| http.request(req)}
res.body
end
The interesting thing about the first line is that require isn’t a language keyword, it’s a method (defined in kernel.) Because the syntax of Ruby doesn’t require parentheses it looks like a keyword – which means that Ruby is naturally set up for this kind of extension, meaning fluent APIs and DSLs are natural in Ruby.
To call that function and output the resulting data (which given the url I’m using will be in XML format), use the following code:
puts getUrl('http://twitter.com/statuses/public_timeline.xml')
So, downloading the public timeline turns out to be fairly straightforward – although I’m not yet handling error cases like there being no connection. But the output is a bunch of XML. So, my next task will be to process the XML into something friendlier.
Drawing Conclusions
This post by Dean Wampler caught my attention. He raises the question of why we program in text and not in diagrams. Dean’s conclusion is that, for the most part:
“code written in succinct languages with well-designed API’s and DSL’s will trump a diagram-driven approach.”
I’m not so sure. Given the current state of our art, I’m inclined to agree. But I’m not satisfied with that state. It’s so hard to understand most programs that we say that they are written in code. I don’t believe that the way we write software today is sustainable. As we progress to higher levels of abstraction, we will find different means of expressing our intent. Those means may be textual or they may be diagrammatic, but what they must do is to remove the boundaries – between roles, between the domain expert and the implementer, between the user and the programmer – that make software development the difficult art we know.
Ruby Tuesday #4 : Time to Productivity
So far I’ve concentrated on setting up an environment. Meantime, I’ve been reading about Ruby and trying to learn the syntax. It’s at this point that you wonder if you can ever be productive in the new language. The syntax is relatively easy – it’s the rest that is more daunting. The rest is the stuff you take for granted in your preferred language, stuff like how to organise and layout files, how to deploy, what’s in the standard libraries, etc. It’s like learning a new language: despite a grasp of the grammatical rules, all you can say is “Where might I find the nearest tractor repair shop?” – and you don’t quite understand the response. This wouldn’t be an issue if you weren’t already proficient in a language because you wouldn’t know how much more you could achieve.
So, the question is how long does it take to become productive and how best to go about learning the real stuff. From past experience of learning new programming languages, the answer seems to be build something – in addition to continuing to read as much as possible. What should that something be?