Checking for one key among several in a Ruby hash

I've been hard at work on More Things Need To this weekend and have learned lots of things in the process. One of these things was a handy bit of Ruby that seemed worth sharing for other newcomers.

What I wanted to do was find out if any key from a list of keys existed in a hash. For example, given a hash animals, I'd like to know if it contains at least one key among the keys horse, frog, and cow. Checking for a single key is easy:

animals.key?('horse')

But what about when we want to check for all three of those animals? Well, we could just do one after another:

animals.key?('horse') || animals.key?('frog') || animals.key?('cow')

But that can get cumbersome very quickly. There are a few cleaner ways to do this for multiple keys, but the method I ended up using takes advantage of the fact that the Array class in Ruby includes the Enumerable module, which has a handy method called any?. This method takes a block and will return true if any invocations of the block return a "truthy" value. So, here is the solution:

['horse', 'frog', 'cow'].any? do |key|
  animals.key?(key)
end

So what is happening here? First, we're defining an array that contains all the keys we want to check for. Then we call the any? method on that array and pass it a block. Inside the block, we check if the key in the current invocation exists in animals. If an existing key is ever found, the entire call will return true. This removes some repetition and makes the code easier to read.

Note that you can also find out if the hash contains all of the keys rather than just one by using the all? method instead of any?.

Do websites need to look the same in every browser?

There are numerous awesome new features introduced in HTML5 and CSS 3, but neither specification is finalized and browser support for them is still pretty unreliable. After getting a taste of what these new technologies can do, it can be hard to resist using them and to wait for better browser support. My position for a long time has been that, although it's frustrating as a developer, it's best to create things for the lowest common denominator and not use features that don't have reliable support across browsers. But my thoughts on the subject have been changing recently.

One of the catalysts for this change was a spectacular quote from Douglas Crockford that I came across:

If a web browser is defective, causing errors in the display or performance of the page, should the page developer struggle to hide the browser's defects, or should the defects be revealed in hope of creating market pressure to force the browser maker to make good? By which approach is humanity better served?

The debate as to whether or not a developer should support bad, buggy browsers (henceforth referred to as IE) is not a new one. Most often it is simply not a choice for the developer. Most businesses will require support for IE due to its sheer market share and the monetary consequences of not doing so. Even on a project without requirements dictated by an employer, choosing not to support IE puts you at a disadvantage, because chances are there are other developers out there making similar products that do support IE. And when faced with a choice, which are clients going to choose?

Because of these reasons, I've always erred on the side of caution and supported IE. But reading that insightful quote from Douglas Crockford reinvigorated the debate in my mind. I started to rethink how important supporting IE really is and how to find a better compromise in regard to feature support in browsers. There's an emerging school of thought that we should rid ourselves of the preconception that a website should look exactly the same in every browser. I've seen this new attitude come up in several different places recently.

So, does a website need to look the same in every browser? The short answer: no.

My new attitude is to think of browser support with a progressive enhancement approach. I can dramatically improve the look of a site with new CSS 3 effects such as box-shadow. But IE doesn't support it. Should I forego its use? Or use some JavaScript to recreate the effect? Neither! I should use it. Users with browsers that support it will see the fancy version, and IE users will see something more basic, but still fully functional. Since they have not seen it any other way, they're not disappointed, and nothing looks wrong or broken. They're none the wiser.

I feel that this approach is a good compromise on what Douglas Crockford's quote suggests. Instead of crying, "IE be damned!" and allowing it to break, it inverts the sentiment to use a positive context. Instead of punishing IE users for using an outdated browser, it continues to present the baseline for them, and rewards users of modern browsers with extra sparkle. This encourages the use of modern browsers just like the "let IE break" attitude, but keeps everything perfectly useable and presentable to the least common denominator.

Understanding jQuery 1.4's $.proxy() method

No sooner than I wrote about how to control a JavaScript function's context with the Prototype Function.prototype.bind, jQuery 1.4 is released and one of its new tricks is jQuery.proxy, which we can use for exactly the same purpose.

To refresh you, the problem that needed solving was how to preserve a reference to the calling object when the value of this is changed. Consider the following:

MyModule = function() {
  this.$div = $('#testdiv');
  this.myString = "Hi! I'm an object property!";

  this.$div.click(this.handleClick);
};

MyModule.prototype.handleClick = function() {
  console.log(this.myString); // undefined
};

var m = new MyModule();

When you click on the div, we are given undefined, because this now refers to the DOM element that triggered the event and not the instance of MyModule we've stored in the variable m. The element does not have a myString property, and hence, it is undefined. So how do we access the myString we want? The solution I wrote about previously was to use Function.prototype.bind from the Prototype library, which allows us to control what this will refer to inside the function we're calling. But now in version 1.4 of jQuery, we can handle this situation with the new jQuery.proxy method. The method has two signatures:

jQuery.proxy( function, scope )
jQuery.proxy( scope, name )

In the first form, the function argument is the function we're calling, and the scope argument sets the context the function should be called in. In the second form, the scope comes first, and the name argument is a string that gives the name of the function we're calling. Note that the function provided in name should be a property of whatever object we're using as the scope. Let's look at an example to make this more clear.

MyModule = function() {
  this.$div = $('#testdiv');
  this.myString = "Hi! I'm an object property!";

  this.$div.click($.proxy(this.handleClick, this));
};

MyModule.prototype.handleClick = function() {
  console.log(this.myString); // Hi! I'm an object property!
};

var m = new MyModule();

Now when we click on the div, we see the result we want, because this is now bound to our m object, which is an instance of MyModule. This uses jQuery.proxy's first signature. We could achieve exactly the same effect using the second signature by using this line instead:

this.$div.click($.proxy(this, "handleClick"));

The small "gotcha" that remains is how to access the DOM element that triggered the event, now that we've changed the value of this within our function. The good news is that, like before, all event handling functions are passed an event object with details about the event that occurred. Within that event object is a property called currentTarget, which holds exactly what we're looking for: the DOM element that triggered the event. Here is an example of how to access it:

MyModule.prototype.handleClick = function(event) {
  console.log(this.myString); // Hi! I'm an object property!
  console.log(event.currentTarget); // <div id="testdiv">
};

Thanks to jQuery.proxy, we now have the ability to control function scope with jQuery alone. To read more about all the new features in jQuery 1.4, check out John Resig's overview on The 14 Days of jQuery.

1 10 12 14 18