Ramblings on programming Cocoa with Ruby
This post started with a point. It didn’t end with one. So, I’m going to start it again and just get to the point. What makes a good Ruby Cocoa development environment. I’ll get into my rambling thoughts below, but quickly; straight up XCode/Interface Builder, HotCocoa, Interface Builder and command line building, some combination of all three or something else.
If you were going to do it, how, or what would you do?
I’ve been playing with the above three options as I’ve trying to figure this out myself. I’ve done a few straight HotCocoa applications (SilverLining, Rife and Postie) and, while they worked out well, I found the need to do all the layout by hand tedious and boring. I really like Interface Builder for ease of UI creation.
Taking that, I tried the XCode/Interface Builder solution and went to the bundled MacRuby XCode templates. When programming Objective-C I really enjoy XCode, it makes things easy when doing purely native Mac applications. That said, XCode irks me when doing Ruby development. Maybe it’s muscle memory, not sure, but I like using TextMate to do my Ruby work.
The approach I’ve been using in my latest fiddlings (Touchstone) started with the XCode templates but I broke out, stole some of the rake tasks from HotCocoa, and switched to TextMate and the command line.
The biggest issue I’ve run into is the Interface Builder integration kinda sucks. It does work, but you have to reload the class files manually all the time. A bit frustrating.
On the flip side, I can organize my code however I want. The tendency for Objective-C programs to have all their code in the root directory drives me up the wall. Sure, XCode hides this, you can change it, but, by default, everything ends up in the root folder. Maddening.
With Touchstone, I’ve put all the application code, similar to Rails, under App/Controllers, App/Models, App/Helpers and App/Views. The same structure is copied into the .app file when it’s generated. Everything feels much cleaning. The .xib and .xcdatamodel files get compiled and moved as needed.
I know I could have used the XCode build stuff but having to launch XCode and getting the debugging output in an XCode window didn’t feel right for me. I wanted something a bit more contained.
Coming back to the original question, I think what I want to see, is something similar to Touchstone but with the convenience of HotCocoa. The HotCocoa mappings make a lot of things feel more Rubyish, feel more natural. Those bindings, combined with Interface Builder, I think, would make a kick-ass Cocoa programming environment.
Again, how would you do it?
Introducing QueryStringParser
Over at PostRank we serve a lot of data through our API servers. All of those servers parse query strings. Along with, obviously, the query string, we also encode the post data in query string format. This means we’re doing a lot of parsing of query strings. Millions of query strings a day.
Parsing time for those query strings starts to add up. We started with the query string parser from Camping and that served us well for over a year. Eventually, parsing the query data was over 80% of the processing time and something needed to be done. From there we briefly dabbled with the Rack::Utils query string parser. While faster, this still wasn’t fast enough.
From Rack, in a fit of desperation, we wrote our own. With the new version, written in C using Inline Ruby, we were finally fast enough. The parser has served us well but was starting to show some age with the Inline Ruby. We’ve started using Ruby 1.9 for our new API servers while the old servers were on 1.8. RVM doesn’t deal with Ruby Inline very well and we’d end up having to erase the Inline cache when switching between server versions. To that end, we converted the Inline Ruby version into a Ruby extension and, to help deal with Bundler, decided to push it out the door as a gem.
You can check it out on Github or install the gem from gemcutter. At this point, the gem does what we need it too. It doesn’t handle every case, but you should be able to update it, if needed, to handle what you need.
require 'query_string_parser'
p QueryStringParser.qs_parse("my=query&string=parser")
=> {"my"=>"query", "string"=>"parser"}
include QueryStringParser
qs_parse("does[]=array&does[]=data&does[3]=values&does=more")
=> {"does"=>["array", "data", "values", "more"]}
As you can see from the example above individual params will be set their values. If a parameter exists multiple times it will be turned into an array of values. You can set a number into the brackets but it will be ignored by the parser.
If you’re parsing a lot of query strings give the parser a try. Let us know what you think.
Final Fantasy XIII — Skip It
Picked up Final Fantasy XIII last week. I’m wishing now I had skipped it. I got sucked in by memories of older Final Fantasy games, I want Final Fantasy games to be good. Childhood memories and all that. Turns out, Final Fantasy XIII wants to turn that shit into lies.
I have to say, about the only thing the game had going for it was that it was pretty. The problem with that, I’m pretty sure the developers didn’t want people to play the game, they wanted to make a movie. Half the time it feels like you’re just killing time between cut scenes. You’ll have a section where one cut scene will end, you’ll move your character up a set of stairs to another cut scene. I get it, you can make pretty hair and pretty water. Yes, the clothing moves very nicely. Guess what, I want to play a game. I don’t want to watch a movie right now.
The game play is rigid as all hell. You can only go where the game wants. Your path is drawn out on the mini map and you have no-say in where you’re going. You can always tell where a hidden orb is, it’s in the small branch that appears in the map and then dead-ends. It’s laughable when the character says “We can just follow the lights”. Yea, no shit, I can’t go anywhere other then following the lights.
The story is convoluted and didn’t feel very solid to me (at least to the point in the game where I got.) The conflicts between the characters was more grating then interesting. The magnitude of backstory is almost staggering, but, a great time to put in more cut scenes to fill-in that backstory.
One of the things I really liked about the old Final Fantasy games was the turn based combat. You could plan out your battles, pick the moves you want, refresh your memory on spells. It gave you a lot more control. The new time based combat quickly turns into a hit the auto-generated sequence instead of putting thought into what’s going on.
But, the straw, the straw that broke the camels back and then kicked him in the face. The timed battles. You have a max amount of time to earn the respect of a boss. I played one of the timed battles about twenty times. I read the walkthroughs on the internet. I played a few more times. Congrats Square Enix, you created a point in the game that I can’t fucking get past. It’s a dead end. The game is now more frustration then fun. At that point, it’s going back to the store to get traded in. I’m going to go play Bioshock 2.
To sum it up, if you see Final Fantasy XIII on the shelves, skip it. It isn’t worth the money. It isn’t worth the time. Sorry Square Enix, you’ve destroyed a once loved franchise.
Thanks for that.
Download and XML parsing with HotCocoa
I’ve been working on Rife, a Google Reader client, over the last few days and have been digging my way through some more HotCocoa mappings. I figured the best way to remember some of this stuff is to write it down so, the following will look at synchronous and asynchronous downloads and writing a XML parser in HotCocoa/MacRuby.
So, what are we creating you ask? Well, as I said, I’ve been playing with Google Reader APIs and we’re going to do a synchronous request to Google for an identifier token. Once we’ve successfully authenticated we’re going to make an asynchronous request for our unread items. We’ll then parse the resulting XML document and spit the titles out to the console.
As with any HotCocoa application the easiest way to get started is to have system setup the shell of our application. We’ll use the hotcocoa command to create our application which I’m calling titles.
titania:example dj2$ hotcocoa titles
In order to authenticate to Google we’re going to need your username and password. Since I’m going to do the output to the console for demonstration purposes I’ll use the main application window to show the fields for username and password and a save button.
require ‘hotcocoa’
class Application
include HotCocoa
def start
application(:name => "Titles") do |app|
app.delegate = self
window(:frame => [100, 100, 200, 200], :title => "Titles") do |win|
win.center
win.will_close { exit }
win << label(:text => "Username", :layout => {:start => false})
win << @username_field = text_field(:layout => {:start => false, :expand => [:width]})
win << label(:text => "Password", :layout => {:start => false})
win << @password_field = secure_text_field(:layout => {:start => false, :expand => [:width]})
win << save = button(:title => "save", :layout => {:start => true}) do |button|
button.on_action { authenticate }
end
@username_field.setNextKeyView(@password_field)
@password_field.setNextKeyView(save)
save.setNextKeyView(@username_field)
end
end
end
def authenticate
puts "DO AUTH #{@username_field.to_s} #{@password_field.to_s}"
end
end
Application.new.start
If you run the application by executing macrake in the titles directory you should see the main application window. Typing something into the username and password fields and pressing save you should see something similar to the following in your terminal.
DO AUTH test test
With our framework setup let’s get to the interesting stuff. First up, authenticating so we can retrieve our identifier from Google. We’re going to make a synchronous request to retrieve the identifier and, if successful, call a method to start retrieving our reading list.
username = @username_field.stringValue
password = @password_field.stringValue
query = "https://www.google.com/accounts/ClientLogin?" +
"Email=#{CGI.escape(username)}&Passwd=#{CGI.escape(password.to_s)}" +
"&source=HotCocoaExample&service=reader"
url = NSURL.URLWithString(query)
request = NSMutableURLRequest.requestWithURL(url)
request.addValue("HotCocoaExample", forHTTPHeaderField:"source")
request.addValue("2", forHTTPHeaderField:"GData-Version")
response = Pointer.new("@")
data = NSURLConnection.sendSynchronousRequest(request, returningResponse:response, error:nil)
data = NSString.alloc.initWithData(data, encoding:NSUTF8StringEncoding)
if data =~ /^SID=(.*)\n/
@sid = $1
retrieve_reading_list
else
raise Exception.new("Authentication failed with: #{data}")
end
end
def retrieve_reading_list
puts @sid
end
If you add the above to your application, and add require 'cgi', you should be able to run the program, put in your username and password and get a long line of characters spit out on the terminal. Those characters are your Google SID.
Let’s look a bit closer at what we’re doing in the authenticate method. We start by grabbing the stringValue for the username and password fields. Then, using these values, we build the query string needed for authentication. This query string is used to build a URL object by calling NSURL.URLWithString(query). With the URL in hand we can start building our request object. This is done by calling NSMutableURLRequest.requestWithURL(url). I’m using the mutable version of the request as I want to add a few extra header values. These are both added with addValue(value, forHTTPHeaderField:field).
When we execute our request the system is going to want to put our response object somewhere. In the Cocoa version the method accepts a NSURLResponse **response parameter. In order to handle the response we need to create a Pointer object which is a MacRuby object for handling these pointers to objects. We want our pointer to point to an object so we use Pointer.new("@").
With the response setup we call NSURLConnection.sendSynchronousRequest and provide our request and response objects. I don’t care about the error, but if you do, you’d want to pass in something similar to our response pointer. The request will return a NSData object which we convert to a string using the initWithData initialization method of NSString.
With the string in hand we try to extract our SID and, if successful, execute the retrieve_reading_list method which just spits out the SID.
OK, cool, we’ve now got our authentication token and are ready to move onto the asynchronous request to get our reading list.
query = "https://www.google.com/reader/atom/user/-/state/com.google/reading-list?" +
"xt=user/-/state/com.google/read&ck=#{Time.now.to_i * 1000}&n=2"
url = NSURL.URLWithString(query)
request = NSMutableURLRequest.requestWithURL(url)
request.addValue("HotCocoaExample", forHTTPHeaderField:"source")
request.addValue("2", forHTTPHeaderField:"GData-Version")
request.addValue("SID=#{@sid}", forHTTPHeaderField:"Cookie")
NSURLConnection.connectionWithRequest(request, delegate:self)
end
def connectionDidFinishLoading(conn)
puts NSString.alloc.initWithData(@receivedData, encoding:NSUTF8StringEncoding)
end
def connection(conn, didReceiveResponse:response)
if response.statusCode != 200
puts "BAD STATUS: #{response.statusCode}"
p response.allHeaderFields
end
end
def connection(conn, didReceiveData:data)
@receivedData ||= NSMutableData.new
@receivedData.appendData(data)
end
Similar to the synchronous method we start by building our query string, NSURL and NSMutableURLRequest. We’ve added a cookie to our request object to hold the SID retrieved earlier from Google.
We fire the request by calling NSURLConnection.connectionWithRequest(request, delegate:self). We specific ourselves as the delegate for the connection. There are a few delegate methods we can implement to receive data and get notified of request states. These are:
connectionDidFinishLoading(connection)connection(connection, didReceiveResponse:response)connection(connection, didReceiveData:data)
We’ll look at our implementation of each of these callbacks in turn. First, in connectionDidFinishLoading(conn) we’re just printing out the data retrieved. We need to convert the data, similar to what we did in the synchronous request, from a NSData object to a NSString object.
In connection(conn, didReceiveResponse:response) we’re just checking to see if we got a 200 response code from the server. In all other cases we print an error.
The main work is done in connection(conn, didReceiveData:data) where we create a NSMutableData object if needed and append any data received into the mutable data object.
Running the code at this point should dump the first two items in your reading list to the console. The data will be a big mess of XML but we’ll look at parsing that in the next step.
xml = HotCocoa.xml_parser(:data => @receivedData)
@receivedData = nil
xml.on_start_document { puts "Starting Parse" }
xml.on_end_document do
HotCocoa.notification(:post => true, :name => "all_entries_loaded", :object => nil, :info => nil)
end
xml.on_parse_error { |err| puts "Parse error #{err.inspect}" }
xml.on_cdata { |cdata| @elem_text += cdata.to_s }
xml.on_characters { |chars| @elem_text += chars.to_s }
xml.on_start_element do |element, namespace, qualified_name, attributes|
@elem_text = ”
end
xml.on_end_element do |element, namespace, qualified_name|
puts @elem_text if element == ‘title’
end
xml.parse
end
We’re finally getting into some HotCocoa specific code with our XML parser. HotCocoa defines a mapping wrapper around NSXMLParser and provides a set of delegate methods. These delegates mean we don’t have to set our class as the delegate and create a bunch of methods. They mean we can attach our code as blocks on our XML object. All the better if you want to define a few parsers in one class.
We start off by creating a HotCocoa.xml_parser. The parser accepts NSData objects so we don’t need to convert our response data to a string. We then setup eight callbacks. There are actually a bunch more callbacks that can be hooked up and you should look at the xml_parser mapping code to see if you need any of them. For our purposes, we only really care about eight.
The on_start_document, on_end_document and on_parse_error callbacks, as you can probably guess, get called when we start parsing, when we finish parsing and when we receive a parse error, respectively. We don’t really care about start in this example, but I put it in anyway. When we’ve completed parsing we send a notification and other application code can then listen for this notification and do anything it needs. If we wanted we could store the entries as they’re parsed and provide them to the :object key. This would make those entries available to anyone that receives the notification.
If we receive either CDATA, with on_cdata, or text, with on_characters, we append the content to our current elements text. When we receive the open tag of a new element, on_start_element, we dump our current element text as we’ve started a new element. We can also take a look at the elements name, attributes, namespace and qualified name, if desired.
Finally, in on_end_element we print out the current element text if the element we’re finishing has a name of title.
With all the callbacks configured we use xml.parse to start the parser. You should, if you run this example, see the titles and authors of the first two posts in your reading list. (The author name is also called title and I’m not bothering to check that the parent element is entry before spitting it out.)
That’s it. You can now make synchronous and asynchronous requests for content and parse any resulting XML.
One last thing before you go. Both of the requests we did above were GET requests. You can do other types of requests using the same methods as above you just need a slightly different setup for the request. You can see a POST request below.
request.addValue(SOURCE, forHTTPHeaderField:"source")
request.addValue("2", forHTTPHeaderField:"GData-Version")
request.addValue("SID=#{@sid}", forHTTPHeaderField:"Cookie")
body="first=1&second=2&third=3"
request.setHTTPMethod(‘POST’)
request.setValue(‘application/x-www-form-urlencoded’, forHTTPHeaderField:‘Content-Type’)
request.setValue(body.length.to_s, forHTTPHeaderField:‘Content-Length’)
request.setHTTPBody(body.dataUsingEncoding(NSASCIIStringEncoding))
The first few lines should look familiar from creating our asynchronous request above. Since we’re going to be posting the data we use setHTTPMethod('POST') to setup the request method. We’ve form encoded the data so we set the appropriate Content-Type and set the Content-Length. Note, we convert the length to a string before sending to setValue. Finally, we set the body of the post with setHTTPBody. You need to convert the body string into a NSData object which we do with the dataUsingEncoding method. If you don’t convert the body to NSData you’ll end up sending a nil body with your post request.
On Gaming
I’m a gamer. My friends and I have spent a lot of time playing various table top RPG games such as Dungeons and Dragons and Mage. The amount we play has tapered off of the last few years as the group spread out, got married, had kids, became adults. We do still game, and some of those games have been running for years, other games have fallen by the wayside for various reasons.
Recently I’ve found myself thinking about gaming. I’ve never been good at the histrionics part of the game. I can find interesting ways to combine rules, feats and spells but describing the outcome is not a strong point. I’ve always felt awkward describing scenes and actions. I think this is the basis of my not running games. Much to the disappointment of my friends. In the last, 15 I think, years I’ve been gaming I’ve run twice. Once for a group and once as a solo. The group game was too long ago to remember how it went. I was told the solo was good, but it didn’t feel solid to me. Maybe I didn’t have a good enough grasp on where I wanted it to go, maybe it was something else.
The lack of description confidence is part of the reason I hang in the background in games. I’ll let the other players take the lead on quests, satisfy their personal agendas. I’ll tag along and do my bit, and sometimes come up with ideas that cause the GM to think, but don’t typically look to take the lead in games. This can, obviously, have a detrimental effect on character development. Especially after a character, played for 2-3 years, dies and you start anew. Developing the skeleton for the new character to hang off, while hanging in the shadows, is difficult.
Maybe this is an experience thing. Practice makes perfect and all that. I guess the question becomes, how to you get better at the descriptive aspects, the creative aspects, the design parts of the game?
How do you transition from a player in the background to a GM? Or, with smaller increments, a player that steps in the fore more often.
I know our main GM would love for more of the descriptive elements in the game. Hell, he’ll give XP rewards for descriptive write-ups of game content. Die modifiers if you give good descriptions of actions.
So, I ask you, gentle reader, how do you work on your descriptions, your histrionics, your character and world development? How do you make your game better?