While researching a way to create fullscreen apps in Cocoa, I came across an article by Matt Gallagher written about a year ago that described the process in detail. I’ve been experimenting a lot lately with MacRuby, Apple’s Ruby implementation built on top of Objective-C and LLVM, and I decided to port Matt’s code to use Ruby instead of Objective-C.

The result is Fullscreen, a small sample app that demonstrates how to create a fullscreen window in MacRuby. To run the app, you need to have the latest MacRuby (0.6) installed.

Enjoy!

Since writing about Ruby configuration objects a few months ago, I’ve done a bit of hacking on my original Config class. My main issue with it is that it’s basically serving as a wrapper for a Hash, but it’s a foreign interface. In other words, if you hand a Config object to another developer, he’s going to have to look up the API before he can use it. But that defeats the purpose for writing it in the first place.

The whole idea was to be as flexible as possible, so that configuration objects would be instantly usable and familiar. I don’t want anyone (including myself) to have to think about using the object. It should be easy to use and follow the principle of least surprise as closely as possible.

Also, configuration objects should have powerful methods that allow users to iterate over them, inspect them easily, and merge and compare them with other objects. Obviously Hash provides lots of built-in methods for doing this sort of thing, but it’s not as flexible as I’d like it to be.

The solution? SymbolTable.

SymbolTable is a much more generalized version of my original Config object that actually subclasses Hash. With all of the nice methods of Hash already built-in, a SymbolTable object becomes much more powerful than its Config predecessor and immediately usable.

require 'symboltable'

t = SymbolTable.new

# Lots of convenient accessor methods
t[:a] = 1
t[:a]       # => 1
t['a']      # => 1
t.a         # => 1
t.a = 5
t[:a]       # => 5

# Functions just like a Hash
t.sort.each do |key, value|
  puts "Key #{key} has value #{value}"
end

# You can even merge with another SymbolTable
b = SymbolTable.new
b[:b] = 'hi'
t.merge!(b)

Configuration objects are just one excellent use case for such an object. To install the code, use [RubyGems][rubygems].

$ sudo gem install symboltable

Check out the project on GitHub for more information.

Last week I pushed out some major updates to my most well-known piece of work to date, Shadowbox. This update finally brought version 3 of the code out of beta and into a much more robust and ready state. Overall I’m very happy with the release. It took much longer than I expected (for a lot of different reasons), but the time it took just helped to produce a better product in the end.

In this post I hope to reveal some of the steps I took to improve the code base and some of the gotchas that developers who are experienced with the library might encounter when upgrading to the latest version of the code. If you run into any snags that are not outlined in this post, please let me know so I can consider making further adjustments to this post and/or the documentation to clear up any confusion.

Initialization

The 3.0 beta expected the developer to specify some dependencies in Shadowbox.init if they were different from the defaults. These included the players, language, and adapter to use. The script would then check these settings and load any needed files by appending <script> tags to the head of the document. However, many users had trouble understanding these settings and couldn’t get the script to work properly as a result. This often happened when a user desired to play a certain type of content and neglected to load the appropriate player classes during initialization.

This behavior has been modified in the final version of 3.0. All player, language, and adapter dependencies are now specified when you create your own custom build of the code. Instead of being requested on every page load, all necessary files are compiled into the code up front. This change should make it much easier for users who are not familiar with the code to get it up and running quickly.

An added benefit of compiling everything into one file up front is that no additional HTTP requests need to be made to the server for dependency files. This should significantly speed up your page load times and decrease the load on your server.

Setup

The ability to pass in options via markup has been removed from the script entirely. There are several reasons for this. The first is that putting a JSON object into HTML markup is just not good practice. When I first invented that feature, I thought it would be neat if someone were able to use Shadowbox using only HTML markup, and without touching JavaScript. So I added the ability to put a JSON hash in the page markup and eval it at runtime. However, in retrospect it seems a bit silly. After all, someone who is not comfortable using JavaScript certainly wouldn’t be comfortable putting JSON objects into their HTML!

The second reason is related to the first, and it is simply the use of eval on text that is included as part of the page. With the growing popularity of XSS and similar malicious attacks on website markup, using eval presents a major security vulnerability. I know that probably could have constructed some elaborate workaround using Crockford’s json.js, but I didn’t consider the extra weight worth it for a feature that was clumsy in the first place.

Now you will still be able to use custom options for your links, but you need to do it via Shadowbox.setup (several examples are available in demo.js, the same file that is used to run the examples on the Shadowbox website.) Setting up your links via HTML markup still works, just not with custom options.

Hacks

Another major step I took in this latest release was to eliminate the number of hacks in the code. The web by nature is one big hack, and client-side programming can get especially hacky especially with a product as complex as Shadowbox. However, I believe that it’s only good to resort to hacks when you need them and only after exhausting all possible (and reasonable) alternatives.

One hack that I wanted to eliminate completely was the use of a “dynamic property” in the CSS code. This code was used to dynamically calculate the height of the viewport in browsers that do not have support for position: fixed like IE6 (there’s a bit more to it, but that’s the basic gist of it.) However, it never sat particularly well with me. I’m happy to say that I was able to modify the code to not use any proprietary CSS properties in version 3.0 final. It now feels much cleaner, and it’s a lot more future-proof.

One more hack that I tried to eliminate is the use of browser sniffing via the user-agent string. While I wasn’t able to completely eliminate this (yet), I’m down to only a few instances of it now. The code relies a lot more on object/feature detection instead. This is also much more future-proof and should go a long way in helping to support as many browsers as possible.

Speed

The entire codebase has been streamlined to run faster and more efficiently in every browser without sacrificing any of the existing functionality. I ran quite a few tests to determine the most efficient way to structure the CSS and conduct the animations while keeping memory usage down. In the process I squashed several memory leaks in IE6 that caused that particular browser to slow down after reloading the page several times. In short, the whole experience should be smoother and more consistent for users, no matter what platform they are on.

Source

The new home for the Shadowbox code is on GitHub. Although the code is not released under an open source license, I have always made the source available for debugging and learning purposes and I intend to keep it that way. Please report any bugs that you may find on the new issue tracker.

I hope you enjoy the latest release of Shadowbox.

In many Ruby applications there exists a global “configuration” object that is used to store application-wide data. For example, Capistrano uses an instance of Capistrano::Configuration to load recipe tasks and keep track of recipe variables. Rails also uses a configuration object in the ActiveSupport module that takes the form of a glorified Hash.

Using a configuration object such as these helps to keep application settings globally accessible without polluting the global namespace. They can also provide some nice syntactic sugar to mask the sometimes clumsy or verbose accessor methods of native Hash and/or Array objects.

In my work, there is a pattern that I end up using a lot for these kinds of objects. This won’t be anything new to the experienced Ruby programmer, but it should provide some insight for a beginner. The pattern looks like this:

class Config

  def initialize(data={})
    @data = {}
    update!(data)
  end

  def update!(data)
    data.each do |key, value|
      self[key] = value
    end
  end

  def [](key)
    @data[key.to_sym]
  end

  def []=(key, value)
    if value.class == Hash
      @data[key.to_sym] = Config.new(value)
    else
      @data[key.to_sym] = value
    end
  end

  def method_missing(sym, *args)
    if sym.to_s =~ /(.+)=$/
      self[$1] = args.first
    else
      self[sym]
    end
  end

end

This class is basically a wrapper for a Hash object that is kept internally as the instance variable @data. Below is some code to demonstrate how it can be used.

config = Config.new
config.database = 'database_name'
config.username = 'user'
config.db_hosts = {
  'sj'  => 'sanjose.example.com',
  'ny'  => 'newyork.example.com'
}

config.username         # "user"
config.db_hosts.ny      # "newyork.example.com"

When you use an object similar to the one above, you can completely forget about how you stored the data (was that a symbol or a string that you used for the key in your Hash?) and simply access all properties via the dot syntax. The advantage to using this class over something like OpenStruct is that you can access nested hashes the same as you would use any other variable. This makes the class especially well suited for accessing nested structures like the kinds you might find in a YAML configuration file.

yaml_data = "
---
database: mydb
auth:
  user: myuser
  pass: mypass
"

require 'yaml'
config = Config.new(YAML.load(yaml_data))

config.auth.user        # "myuser"

CentOS is a great Linux distro with possibly the worst selection of up-to-date packages. I love the ease of use of CentOS and the close ties to RedHat, but using it often means that I need to compile the libraries I need from source. When setting up a new VPS recently, these are the steps I took to install the latest version of Ruby.

$ sudo yum groupinstall 'Development Tools'
$ sudo yum install readline-devel
$ cd /usr/local/src
$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p376.tar.gz
$ tar xzvf ruby-1.9.1-p376.tar.gz
$ cd ruby-1.9.1-p376
$ ./configure && make
$ sudo make install

Everything you need is in the development tools, except for readline-devel.

This article is a bit dated by now, but I only just got the chance to read it today thanks to a friend of mine who pointed it out to me.

Tim O'Reilly:

It’s a matter of balance. Every business needs to pay attention to its bottom line; every individual needs to put a roof over his or her head and provide food for loved ones. But take a look inside: how much are you thinking about yourself and what you might gain, versus what you might create?

Read the entire article for an interesting perspective on exactly why we should all be doing what we do.

JUMP TO THIS URL

Jim Coudal, of Coudal Partners:

I don’t know what’s next! It’s kind of a joke, but we’re proudly “without business plan” in our 13th year. We’ve had a lot of things not work, and that’s OK too. If it’s a good idea and it gets you excited, try it, and if it bursts into flames, that’s going to be exciting too. People always ask, “What is your greatest failure?” I always have the same answer – We’re working on it right now, it’s gonna be awesome!

An interesting and inspiring read for anyone interested in doing business for themselves.

JUMP TO THIS URL

Hans Kullin:

So instead of doing the right thing and using the input from readers to improve a poor article, TechCrunch choses to leave incorrect statements up on their site. Instead of subscribing to Dan Gillmor’s view that “my readers know more than I do”, TechCrunch apparently thinks that their readers are idiots (and that Swedes can’t read).

Incidents like this one definitely make me pause and think before linking to anything from TechCrunch. (Via Hacker News.)

JUMP TO THIS URL

In the past year I’ve attended several conferences directly targeted at the web developer community. At each one, I’ve been less and less surprised at the large number of Macs in the audience. Of course, everyone at a developer conference has their laptop out during all of the talks, so it was pretty easy to take a survey of Mac vs. PC users.

The most recent conference I attended was the Mountain West Ruby Conference held in Salt Lake City back in March. It would be a conservative estimate to say that at least 90% of the audience had a Mac. Conference attendee Jim Knowlton pretty perfectly summed up the sentiment one might have when viewing the audience from the stage.

It truly is remarkable how the Mac has won over so many developers in such a short period of time. Just three years ago I clearly remember walking in on the first day at the Information Systems program at BYU and being the only one there (besides the professor, Dr. Albrecht) with a Mac. Of course, not everyone there was setting out to be a computer programmer. However, certainly everyone in that program could be considered a fairly “technical” person. Now, you can hardly spot an Inspiron or ThinkPad at a developer conference.

It all just adds a bit more credence (as if any was needed) to John Gruber’s statement yesterday about the platform of choice for computer nerds:

People who love computers overwhelmingly prefer to use a Mac today. Microsoft’s core problem is that they have lost the hearts of computer enthusiasts. Regular people don’t think about their choice of computer platform in detail and with passion like nerds do because, duh, they are not nerds. But nerds are leading indicators.

Later in the same article, Gruber addresses some remarks made by former WalMart executive and current Microsoft COO Kevin Turner at Microsoft’s Worldwide Partner Conference earlier this month. On the subject of competition with Apple, Turner said:

I pulled this out of my Sunday newspaper. I have an old habit because I came from retail looking at the Sunday tabs and circulars that are in newspapers. This is straight out of my paper last Sunday. This is a comparison out of a leading electronics retailer that you can get a 13.3-inch Macbook for US$1199 from that retailer. Guess what. That same retailer, you can get the same PC with more RAM, a bigger hard drive, and almost a three-inch bigger screen for US$649. What an incredible opportunity.

This is out of last Sunday’s paper, the Apple tax. It’s getting out. And when we put Windows 7 in there, which we’ve got coming out in October, what an incredible opportunity for us to fight back. And it feels really good to be on the offensive here. And we know we’ve got plenty of work to do. We don’t have it all figured out. But I want you to know, ladies and gentlemen, we’re doing stuff and we’re in the game and continuing to take some of these hard market share opportunities head on and compete because it’s a test of will, as I said.

This kind of WalMart thinking is not going to work for Microsoft for one simple reason: they are not in the business of turning profits on volume. If they were, you’d be able to pick up a copy of Windows Vista WalMart-style, for $10. No, Microsoft definitely cares about their margins just like most other tech companies do.

The problem is that when they try and compete in the same price range as Apple, they lose. It’s not that they’re not trying. I can definitely buy a PC over US$1000 if I wanted to. The products are already brought to market. Microsoft’s problem is that when prices get into that range, the vast majority of customers run a comparison and for whatever reason they pick Apple.

I interpret Turner’s statement above as an admission of defeat in the high margin price category. He’s basically saying that PC manufacturers make a superior product, but they cannot sell it for as much as Apple can sell one of theirs. He’s saying that PC manufacturers must incur greater costs from the better components they are using without the benefit of collecting the premium.

The question he should be asking himself is this: If Apple really is selling inferior products, why can’t I charge more than they charge? If PC’s really are superior products, why don’t we get to charge a premium?

It’s not difficult. This is Business Management 101 type stuff. This is the question that PC manufacturers need to answer if they want to compete with Apple in the high margin space. It’s a tough question to ask, because the answer requires a lot of creativity and change to the way they’re currently doing things. It will require a shift from resting on their laurels to putting some more polish on their products. Given this kind of talk from guys like Turner, I wouldn’t hold my breath.

I’m heading out to the Ruby Hoedown in Nashville at the end of August. And for some reason, I suspect I’ll see another slew of Macs in the audience.

David Pogue:

These messages are outrageous for two reasons. First, they waste your time. Good heavens: it’s 2009. WE KNOW WHAT TO DO AT THE BEEP.

Do we really need to be told to hang up when we’re finished!? Would anyone, ever, want to “send a numeric page?” Who still carries a pager, for heaven’s sake? Or what about “leave a callback number?” We can SEE the callback number right on our phones!

Second, we’re PAYING for these messages. These little 15-second waits add up–bigtime. If Verizon’s 70 million customers leave or check messages twice a weekday, Verizon rakes in about $620 million a year. That’s your money. And your time: three hours of your time a year, just sitting there listening to the same message over and over again every year.

Please keep ranting David! I have expressed my frustration countless times to those who know me on this very issue. It’s absurd that I’m still listening to such instructional messages every time I call someone in 2009. This is a very good example of the telecommunications companies following one another around like sheep instead of thinking for themselves.

All it takes is one person to stand up in a meeting and say, “Why in the world do we still have those silly messages whenever someone calls one of our customers?” (Via John Gruber.)

JUMP TO THIS URL