Posts tagged ruby

Assorted MacRuby Snippets #2

This post references MacRuby 0.5, used with Xcode 3.2 on Snopard (10.6.2); the general technique will likely work on other OS/Xcode version, tho. Just saying.

Apps with more than just one framework

The standard MR app template massages $LOAD_PATH a bit in order to have apps which embed the MR framework use said embedded framework in Release builds. The piece of code in question looks like this:

if Dir.exist?(NSBundle.mainBundle.privateFrameworksPath)
  $:.map! { |x| x.sub(/^\/Library\/Frameworks/, NSBundle.mainBundle.privateFrameworksPath) }
  $:.unshift(NSBundle.mainBundle.resourcePath.fileSystemRepresentation)
end

Which is quite alright if all you embed is the MacRuby framework and are building a release. But as soon as you add another one (Sparkle, for example), the test will always be true, whether you’re debugging without embedding MR or not, and your console will show Ruby load errors. The fix is easy, but it took me a few minutes to find the issue, so here we go.

if Dir.exist?( File.expand_path("MacRuby.framework", NSBundle.mainBundle.privateFrameworksPath) )
  $:.map! { |x| x.sub(/^\/Library\/Frameworks/, NSBundle.mainBundle.privateFrameworksPath) }
  $:.unshift(NSBundle.mainBundle.resourcePath.fileSystemRepresentation)
end

Just a heads-up: the rb_main.rb template in the current MR nightlies doesn’t contain the code above anymore — it appears the “magic” was moved into macruby_deploy (changeset), so it’s likely my fix will be unnecessary in MR 0.6.

Ephemera for Mac

I’ve built my first Mac app: Ephemera, which offers two-way Instapaper.com sync for your ebook reader.

Ephemera main window

Here’s its story, my motivation, and what it does.


Preface

Ephemera is neither endorsed by nor affiliated with Instapaper.com or Marco Arment. I’m just a huge fan.

How it came to be

A few months ago, in Oct or Nov 2009, about an hour after getting my Kindle 21 I just knew that it would be the perfect device to read my news on.

I was happy with my Cybook gen3 so far, but once I got the Kindle, it became clear that it could’ve been quite a bit more. The whole K2 experience is superior in every imaginable way; the hardware feels more solid, the UI is much better laid out and consistent (a big plus), its font is lovely… I could go on and on. And for whatever reason, I’ve never developed the wish to read my news on the Cybook.

So chalk up another one for the Kindle for inspiring me.

Anyways, back then I usually caught up on my news using iPod touch/iPhone, using the wonderful Instapaper app, which (unsurprisingly) is a frontend for the even awesomefuller Instapaper.com.

(If you haven’t heard of Instapaper yet, go on, check it out real quick, I’ll wait. — Back? Cool.)

Well, how to get my Instapaper content onto my reader, then?

Application development on the Kindle was out of the question, and still is. (Yes, I know about the upcoming SDK, but what I’ve read so far doesn’t appeal to me in any way; bandwidth and hardware limitations and somesuch.) The next best thing would be a tool running on the Mac, then. Unfortunately, I knew squat about OSX development.

Hence, I’ve decided to keep it simple, and wrote a little Ruby script I would manually run whenever I wanted to sync the device. Worked well, aside from the manual part. It’s just too much work! Still, it was nice to see my Instapaper news on my reader with its “super-easy on the eyes” e-ink screen. Quite early in the process I’ve added 2-way synchronization: when I would delete an article on the device, it was automatically archived at Instapaper.com during the next sync. Even nicer!

Still, too much manual work.

Motivation

By end of 2009 I’ve decided I’d go into OSX programming for a change. I had a clearly defined need, there wasn’t a tool for it, plus I’d never built an OSX app before — that’s enough motivation. So the first three weeks of the year I dove into MacRuby and Cocoa, just grazing Objective-C2, all while building —drumroll!Ephemera.

Oh yeah, my first-ever Mac app! :D And within the time limit, too — my goal was three weeks from start to finish (“finish” being “have something you can show to other people without completely embarassing yourself”), and I’ve made it. (BTW, meeting tight goals is awesome, don’t underestimate the effect.)

Enough of the inane banter, what is it?

Ephemera will comfortably synchronize your ebook reader with Instapaper.com via USB. It works with the Amazon Kindle, Sony readers and pretty much any device capable of reading HTML, Mobipocket or EPUB files, which should cover quite a bit of ground.

It will fetch your news as single articles or in Instapaper’s premade bundles (Mobipocket & EPUB). Personally, I prefer the former. I can read an article, delete it on my reader, and during the next sync it’ll be archived on Instapaper.com.

Being the lazy guy, I’ve also implemented a feature I call “Plug, Sync & Go”: If you want it to, Ephemera will automatically start up, sync, and then unmount your reader when it detects the USB connection. My idea was that you’d configure the app once, and then just have it work — by simply connecting your reader.

I’ve tried to make it as simple and comfortable as possible to get your news onto your reader. For example, the main window has only two buttons, one of them is for opening the preferences, and the important other one you don’t even have to press most of the time. So I think I’ve succeeded.

It’s not perfect yet, of course, as there’s still a lot of work to do, and I do have a number of ideas I’d like to implement. But I mean, look: I’ve set up a support site and a Twitter account, so you know I mean business, right? ;)

At the moment Ephemera runs on 10.5 and up 10.63, 64 bit Intel only. I don’t think I’ll add 10.4 support, but I’m working on the 32 bit version (MacRuby is giving me quite a bit of flak on that front).

UPDATE 2010-02-16: I’ve released v1.1 today, which runs on all Snow Leopard machines.

Dev notes

Ephemera was made possible by MacRuby. MacRuby sits on top of Objective-C, can be embedded & compiled, which means you can build self-contained applications.

I’ve started using MacRuby v0.5b2; last weekend v0.5final was released. They say it’s not ready for production yet, but I laugh in the face of danger, quite manly actually, because that’s how I roll. ;) But yes, it’s still a bit rough around the edges (for example, as mentioned before I couldn’t get the compiled 32bit versions of the app to work, so I’ve filed a ticket and hope someone will look into it).

Also, due to the lack of void pointer support there were issues with accessing the OSX Keychain (i.e. it didn’t work), so I had to work around that. I’ve managed to do that; on the upside I know now how to bundle Obj-C libraries in .dylib files which you can then use from MacRuby. (A seperate article on this topic is coming up soon has been written, keep an eye on the MR section of my site if you’re interested.)

That being said, if you’re feeling a bit adventurous, take a look. It’s quite a remarkable and exciting project. Writing OSX apps using Ruby is fun and enjoyable once you get the hang of it. Also, there are brave men working on getting MacRuby going on the iPhone.

UPDATE: Forgot two links to good non-web resources — you might want to check out Peepcode’s “Meet MacRuby” screencast as well as PragProg’s “Programming Cocoa with Ruby” book. The latter is a RubyCocoa book, but the overall principles of app development apply to MacRuby as well.

That’s all, folks

Anyways, that’s the story. I know it’s not an app for everyone, but it’s been a while since I wrote something I myself would use on a daily basis, and that counts for something. I’ve showed it to a few people, and some of them actually replied and helped me testing (thanks Mike, Norm, Bernhard & Mat). The overall feedback was very positive, which is encouraging, to say the least.

Yes, and I’m proud of myself. In the eternal words of Chuck Noland4: “Look what I have created… I have made fire!”

Ephemera for Mac.


  1. Insert link here to glowing Kindle 2 review I’ve yet to write. 

  2. The place to be for bracket fetishists! 

  3. Snow Leopard only at the moment, as I’ve ran into some finer points of 10.5/10.6 compatibility settings in Xcode. Le sigh

  4. From the movie “Castaway”, which I’ve never watched. Nonetheless, iconic scene. 

Xcode & MacRuby: Embed, Compile, Fix

This post references MacRuby 0.5b2, used with Xcode 3.2 on Snopard (10.6.2).

At some point you’ll probably want to create a release build for your app. You’d like to compile it and embed the MacRuby framework so the app is self-contained. Cool beans.

Unfortunately, it’s not as easy as it seems. Yes, the MacRuby Xcode template contains both an “Embed” and a “Compile” target, and they seem to work fine. ;) The problem I ran into, though, was that whenever you would require anything from the Ruby standard library (say, yaml or stringio), the app would forget about the embedded version of MR and look for /Library/Frameworks/MacRuby.framework/….

I didn’t notice this at first since this folder exists on my dev machine (big surprise), but when I tested it on a clean machine (i.e. one where MacRuby isn’t installed), nothing would happen. Well, nothing except some lines in the system.log, that is:

Being a newb to the ancient arts of compiling shit self-written software I was scratching my head rather furiously. It took me several hours of digging around the Googles and macruby-devel to learn about install_name_tool, and how it’s used to adjust the path of a shared library inside a file.

And that’s what I did, then. I’ve created a new build target, “Embed and Compile”, which has two sub-targets:

  1. the reference to the default app build phase (which means “MyNewApp” is a direct dependency of the “Embed and Compile” target)
  2. a “Run Script” build phase named “Embed, Compile, Fix”

The script runs macruby_deploy and afterwards tells install_name_tool to do its dirty, dirty work. I found it’s not enough to have it adjust the executable only; for me it was necessary to fix all the *.rbo files as well.

So, that’s it. The above works for me, it might work for you as well. If you have comments, suggestions or recommendations, please sound off below. I’m still learning all of this, and any input is appreciated. :)

Oh, and in case I sound ungrateful or anything: I am not. MacRuby 0.5b2 is exactly what is says it is: the second beta of a software’s pre-1.0 version. It’s already pretty damn impressive but not really done yet. I had a hunch what I was getting into, so it’s cool. ;)

Assorted MacRuby Snippets

Some things I’ve learned or discovered during the last few days. Nothing special, but taking notes is usually a good idea, so there.

Get values fromInfo.plist

For example, app name and version:

info = NSBundle.mainBundle.infoDictionary
info.objectForKey("CFBundleName")
info.objectForKey("CFBundleVersion")

Open an URL in the default browser:

url = NSURL.URLWithString("http://municode.de/")
NSWorkspace.sharedWorkspace.openURL(url)

Run an AppleScript

Sometimes you want to execute a short AppleScript snippet to save yourself some time by using the higher-level functionality AS offers instead of writing a huge block of MacRuby. (For example, to eject a FS volume.) Here’s how you do it (the AS is deliberately simple):

script = "display dialog (\"omg\")"
pnt = Pointer.new_with_type("@")
as = NSAppleScript.alloc.initWithSource(script)
as.executeAndReturnError(pnt)

More info at developer.apple.com.

Delete nodes/tags from an XML document

Let’s say you have a variable doc which represents a NSXMLDocument, and you want to remove all em and cite tags:

error = Pointer.new_with_type("@")
selectors = [ "//em", "//cite" ].join("|")
doc.nodesForXPath( selectors, error: error ).each do |n|
  n.detach
end

More notes might follow at a later date. :)

MacRuby compilation step fixes

Just a note, mostly to myself: The default XCode MacRuby rb_main.rb template will contain these lines:

Dir.entries(dir_path).each do |path|
  if path != File.basename(__FILE__) && path.match(/\.rb$/)
    require(path)
  end
end

Works fine for uncompiled files, but when you want to compile your app, there’ll be no *.rb files — just *.rbo files. So rb_main.rb needs to be adjusted.

Dir.entries(dir_path).each do |path|
  if path != File.basename(__FILE__) && path.match(/\.rbo?$/)
    require(path)
  end
end

I just spent 15 minutes wondering about these Unknown class 'Controller', using 'NSObject' instead. Encountered in Interface Builder file at path … in my system log, and I tend to forget this kind of detail so I wanted to jot it down as a reminder to future Carlo.

So, listen up, future Carlo. This is important.

Dev Env acting up when trying to do bulk operations

(What a title…) I was pulling my hair out over the last two days when I was implementing bulk operations in a project of mine.

In this case, the list of operations to do in bulk is compiled in the browser, and then the single requests are sent one by one to the server as single AJAX requests. (Think “mark this as read” functionality.)

My problem was that the first and second call in the bulk list usually went through well, but the rest of the calls just ran against a wall since all of a sudden Rails had problems finding the either logged in user or was missing certain methods and/or attributes. Highly annoying as well as completely erratic, as I was sure my code was okay in the first place.

When trying the same operations on a one-by-one basis, all was good. No issues whatsoever.

So while trying to figure out what the fuck was going on, I’ve played around with different ways to get the current user, checking for the availability of its methods and so on, all to no avail.

At one point I’ve disabled the protect_from_forgery call, and one or two different errors started to appear:

A copy of ApplicationController has been removed from the module tree but is still active

That was new. So I’ve started digging around for an answer, and found it in an old Ruby Forum thread.

Turns out that Rails’ development mode was the culprit, as the app’s code is reloaded on every request; so when a lot of concurrent calls are made, the code might reload slower than the calls are coming in and hijinks ensues.

The overly simple solution to this problem? In /config/environments/development.rb, I just set config.cache_classes to true, meaning the code isn’t reloaded all the time — and as it turns out, my code runs just fine after all! Happy happy joy joy.

The downside is that I’ll have to restart my dev server every time I make a code change, but in this particular case, that’s not a big deal.

Excellent Localmemcache

Yesterday’s Munich on Rails meetup was the usual mix of interesting talks and geeky, delightful conversations. When I say “usual”, I of course mean it’s been the kind of evening I by now kind of expect. ;) Many thanks to Roland for organizing and the Experteers guys for the venue and the food. Nom!

Anyways: interesting talks.

Marco Otte-Witte presented his Excellent gem (gotta love the name) for static code analysis. It’s mostly aimed towards checking Rails code for smell, although —and he made that clear— it’s not targetted at people who strive for the blissful state of “zero warnings”. It’s more relaxed in that way; merely showing you unusual (or stupid or silly) parts of your code, such as missing validations in your models or having instance variables in your partials and the likes. Sadly, at the moment it’s not dealing with HAML templates yet, just ERB. (He’s looking for volunteers, by the way.;)) Here are the slides:

I can actually see myself using it.

And then Sven C. Koehler presented his somewhat irritatingly named1 yet spiffy Localmemcache. It’s a local, shared-memory-based, persistent key/value store, which looks pretty fascinating. I was a wee bit confused by it until it finally clicked — you wouldn’t be able to tell from its name, but it’s not related to memcached. Aha! It’s a C library with Ruby bindings which offers a more or less simple storage system (values are of the type String, but of course that would include Marshal‘ed data) and apparently blazingly fast — his benchmarks showed that Localmemcache is almost as fast as accessing native Ruby hashes. Its not for everyone —for example, as I understand, it requires a 64-bit Unix system— but it looks like a pretty interesting alternative to memcached for single-machine setups like, say, your single production machine or your local dev box. This should ease the issue of sharing data between different Ruby processes, for example. I’m definitely going to check that out.

Oh, and Peter Schrammel presented a concept for a truly private asset server. As I’m not entirely sure whether this is really public information yet, I’ll keep my yapper shut here. :)

Afterwards we all headed to the Park Café for conversations and drinks. All in all a very nice evening, even though I was still a bit groggy from the day before — the München Twittwoch. (Which reminds me, I should probably whip up a quick post about that as well. Eh.)

Again: my thanks go to the MoR organizers and all the people who showed up, I had a good time. :)

Update: Artikel von Marco zu Excellent auf RailsMagazin.de


  1. To me, at least. Sorry, Sven. :) 

God Gotchas

This is a reblog of a post from my new Ruby-themed tumblelog. I know it’s kind of cheap to repost your own stuff, but who cares.

I’ve spent a couple of hours today pulling my hair out while trying to get one of my background job scripts to work with god, the “easy to configure, easy to extend monitoring framework written in Ruby”.

Well, it took a while to make it work, tho. Yes, god is cool and simple and gets the job done. But there are some things that cost me hours and which I found out only by reading the source code.

So, I just want to quickly jot down some gotchas before I forget them again, running the risk of falling into the same traps again in the future.

  1. My script used the constant LOG to keep my Logger instance. Logging worked fine when I ran the script by itself, yet when god took over, it didn’t anymore. Actually, the script died rather quickly. As there was no logging at all going on, and all STDOUT output was suppressed, I came rather close to losing it. Turns out god itself is declaring a LOG constant of its own, which was done before my script had the chance, so when it attempted to initialize it, it would actually try to re-declare an existing constant, and we all know how well that works. ;)

  2. Once that was done, my script was logging just fine, but it didn’t produce any output whatsoever. Raah! Teeth were gnashed… there was definitely teeth gnashing going on. Even telling “my” Logger (the one inside my script) to write to a file didn’t produce anything. That was a fun hour, really. The reason for this behaviour: god is closing all open file descriptors when it sets up monitoring a script. Which included my script. Awesome! On the upside, it meant I could get rid of the part of my code dealing with different logger behaviours. Meaning less LOC! It doesn’t get any more agile than that, folks.

  3. In case you actually want to capture anything your original script is sending to STDOUT, there’s the not-really-documented God::Watch#log. Set it inside your God.watch block to specify a log file. (See example below.)

  4. If you need to set ENV variables, God::Watch#env is your friend. Accepts a hash with arbitrary key/value pairs. For example, I declare a few God.watch blocks, one for each value of an array (think “worker 1 to 5”), and I use God::Watch#env to pass the current value to the worker script. Works well.

  5. When you do a sudo god stop <watch>, make sure to give it a few moments before running sudo god start <watch> again, or you might end up with orphaned unmonitored scripts running rampant in the background.

  6. Running sudo god log without any further arguments will tell you that “You must specify a Task or Group name”. That’s actually a lie, as it only accepts task names. (A group is a number of related tasks. A task is a single monitoring watch.)

So, yeah.

Don’t get me wrong, please: It might not look like it, but once I had figured it all out, I’ve decided I actually like god. I like the feature set, it’s really easy to set up, and it works. I’m happy it exists.

escaloop.com is go!

A few months ago I was talking to Hendrik about lifestreams, and in my ongoing struggle for his undying love (as a friend), I’ve whipped up a little somethingsomething using the wonderful Yahoo! Pipes.

“Lifestream?”, you ask. “What the deuce is a lifestream?!” A good question. A lifestream is basically a big bucket where all the updates and update notifications from your blog, your ADD-induced Twitter posts, your Flickr uploads etc come together in one concise way so it’s easier for others to ignore them. ;) Also, you only have one URL to hand out to hot women (or men) in pubs because the stream inadvertedly works as a hub page, too!

Now, while the prototype was quickly hacked together, it felt clunky. Sure, you can pass a dozen URLs or so to the pipe, but what if the URLs would change? Or if you wanted to add a new one or delete one from the list? Then it’d be a lot of tinkering with the script call in your HTML code.

So I had the idea to build a site where you could configure the list of URLs and the layout and everything, and which would give you a HTML badge for your blog or site, a snippet that wouldn’t change.

And today, I proudly open escaloop to the public.

I feel it’s good enough to test the waters, I believe. I’ve played around with it, fiddling with different implementations on different types of pages, and it looks okay. I guess I could try to think of every possibility for every site on earth, but we know how that would turn out.

It certainly looks fine on my own blog. (Considering that I don’t have that many active feeds, that is.)

So with that, I’ll release escaloop into the wild. It’s still a bit rough around the edges, and there might be bugs. That said: Please take a look, play around with it, build yourself a badge or two for your site, blog, MySpace page, whatever. If you have feedback, please let me know in the escaloop Google Group.

Have fun, Carlo

PS: You might ask what took me so long. In my defense, I am a lazy bastard. Also, I wrote the first rough draft in Python, then switched to Ruby. There I’ve wrote the first prototype using the Sinatra DSL, and finally settled to make use of nifty lightweight Ramaze framework. (By the way, Sinatra is nice, but not what I was looking for.)

During the last few months I’ve also had to put a lot of time into Mass Effect, which is a great game. You understand.