eddorre

Rolled Out Gravatars

Wednesday September 03, 2008 21:46 | comment icon 1 Comment

One of the things that I’ve had in mind when I started coding this blog in Rails was to eventually support Gravatars otherwise known as Globally Recognized Avatars.

Before rolling out this feature, comments had a little astronaut next to the speech bubble for each and every individual comment. If you have registered your email and uploaded a photo with the Gravatar service, it will no longer display the astronaut icon and it will load the registered image. If you haven’t registered, your comment gets a default kanji image.

Making this change in Rails was actually pretty trivial especially since Michael Mayo posted a very nice tutorial not long ago on his blog.

I did make some modifications from the code that he posted.

One of the more prominent changes that I made was to split out the image creation from the just generating the URL.

For example, I have this first method in my application_helper.rb file:


def gravatar_url_for(email, options = {})
      url_for({ :gravatar_id => Digest.MD5.hexdigest(email.downcase), :host => 'www.gravatar.com',
                   :protocol => 'http://', :only_path => false, :controller => 'avatar.php',
                   :default => 'http://eddorre.com/images/visitor.png',
                   :rating => 'G'}.merge(options))
end

I convert the email address to lowercase letters with the downcase method because an MD5 of noemail@eddorre.com and NOEMAIL@eddorre.com are actually two different things. This way, there can be no error. One of the other settings that I’m passing in is a “default” flag. If you haven’t registered with the Gravatar service, I’ll just use the astronaut image that I have and finally I don’t want goatse photos or worse showing up on my blog so I make the rating a mandatory “G”.

Notice that the above method doesn’t actually generate an image tag, it will only generate a URL.

For that, I have a second helper method defined simply as:


def gravatar_image(email, options = {})
      image_tag(gravatar_url_for(email, options), :alt => "Author Image")
end

This is the code that actually creates the Gravatar image.

Now in my view code, I can do the following (where @comment.email represents a variable with the user’s email address):


<%= gravatar_image(@comment.email, { :size => 40 }) %>

And it will display the image correctly. I should note that Michael’s example lists a size in height and width and you’ll notice that I’m passing the size as just an integer. I believe that the height and width options have been deprecated in favor of the more succinct size option.

Did I Really Just See a Positive HFCS Ad?

Sunday September 07, 2008 13:11 | comment icon 4 Comments

In the past week or so Dear GF and I have seen two TV commercials that are pro HCFS; more commonly known as High Fructose Corn Syrup. You can watch the first one and the second.

In both ads there is a product (the first one uses some generic fruit punch and the second one a popsicle) that is being discussed between two individuals. They go something like this:

  • Person 1: You know that [product] has high fructose corn syrup in it.
  • Person 2: Yeah, and?
  • Person 1: Well, you know what they say about it.
  • Person 2: No, what do they say about it?
  • Person 1: Um….ah…um…
  • Person 2: Maybe that it’s made from corn and is perfectly fine in moderation?

Before fading to black there is an overlay that reads Corn Refiners Association followed by a prompt to “get the facts” by going to their website.

Anyone with half a brain that hasn’t been completely programmed and brainwashed by corporations can tell that this ad is horse shit.

First of all, the “in moderation” line is deceptive considering the increasing number of products (including breads, cereals, sodas, condiments and others) that include it. It’s hard to consume something in moderation when it’s practically in everything that you pick up. In order to get away from consuming it you’ll have to move to pricier “natural” brands which is out of the grasp of many American families (especially in these economic times).

Second, and most importantly, both ads use “Person 1’s” lack of in depth knowledge of HFCS as crutch. So let me get this straight. If I can’t explain why something is bad for me then it’s good? This is a play right out of the tobacco lobbyists playbook. They denied and probably still do deny that their products are harmful. Many people can’t explain why diabetes is bad and we all know that we don’t want it. Heck, many people can’t explain off the top of our heads why ingesting Liquid Drano is bad for you (other than knowing that it’s poison) but we know better than taking a swig of it.

It’s not a surprise that Corn Refiners Association would produce these ads. Word of mouth advertising is against them and so is popular opinion (even if the average person can’t explain why). They must protect their brand even if it isn’t good for people.

I just hope that people see these ads for what they are.

Extending XMPP and Ruby Part II

Sunday September 21, 2008 02:41 | comment icon 0 Comments

Last month I wrote the a post titled Extending XMPP and Ruby. The post dealt with incorporating a plugin system to be used with my XMPP script that I wrote about in the post XMPP and Ruby.

To summarize the goal of the XMPP Ruby script; it’s a lightweight XMPP (Jabber/Instant Message) agent that resides on servers. Using this agent, I can immediately tell if a server is up and running as well as pass it commands through my IM client and have it reply back to me with the output. To enforce security, the script had a set number of commands that it could execute.

This had the problem of not really being scalable or extensible. For example, in order to add allowed commands one would have to modify the actual XMPP script itself. Not good.

In order to rectify that, I authored the plugin system that dynamically loaded classes at start time. This made it possible for me to be able to instantiate any object from those classes and run the methods of those classes.

In the test script it worked fine, but when incorporated with the actual XMPP agent script it was still hobbled by the fact that if you wanted to instantiate a new object you’d have to modify the original script. Again, not scalable or extensible.

With this latest iteration, I have added the ability to dynamically instantiate objects based on plugins as well as the ability to call the methods of the object all from the IM interface.

figure1

Before we explore the code, let’s take a look at our file structure (pictured in Figure 1). Everything is contained in a top level directory titled xmpp_agent. There is an init.rb file which loads all of the plugins in the plugins directory which are: command.rb, iis.rb, network.rb, system.rb. Below the plugins, are the files test.rb and xmpp_agent.rb.

The test.rb file is a “scratchpad” file that I use to test functionality. It is not a unit test file. As the figure mentions, the command.rb plugin file is new to this iteration. This file is responsible for creating objects dynamically at runtime as well as dynamically executing methods from those objects.

Since running commands have been abstracted out to its own class, there is no need to have that resident in the xmpp_agent.rb file. That code has been removed from the previous incarnation (see XMPP and Ruby). Everything else remains the same in the xmpp_agent.rb file with the exception of a new require declaration at the top of the script; require ‘init’ – as this loads our plugins (see Extending XMPP and Ruby).

When you send the agent a message that is phrased like this (items in brackets are variables):

command: [plugin] [method name] [arguments]

It will create a new Command object and calls the run_command method.

The first thing that the run_command method does is it calls a command parser method shown below:


  def parse_command(command)
    #Strip the command part out of the string - we don't need it any more.
    command.slice!("command: ")
        
    #Create an array for the arguments
    arguments = command.split(" ")
    return arguments
  end

This removes the extraneous “command:” part of the IM message and returns the rest of the string to the run_command method which is defined below:


  def run_command(command)
    vars = self.parse_command(command)
    
    #Convert the first item in the array to a class
    begin
      class_name = Object.const_get(vars[0].capitalize)
    rescue Exception => e
      return "ERROR: " << e.message << ". Are you sure that the #{vars[0]} plugin is installed?"
    end
    
    #Remove the first item (class name) since it's not needed anymore
    vars.delete_at(0)
    
    #Get the method name and remove it from the array
    if vars[0]
      method_name = vars.delete_at(0)
    else
      return "ERROR: Are you missing the method name?"
    end
    
    #If there are more arguments left call remote method passing in args, else just call the remote method
    if vars[0]
      run_remote_method(class_name, method_name, vars.each {|x| "#{x}" })
    else
      run_remote_method(class_name, method_name)
    end
  end

Now that we have everything parsed like we need it, we call run_remote_method.


  def run_remote_method(class_name, method_name, *args)
    begin
      o = class_name.new
    rescue Exception => e
      return "ERROR: " << e.message << ". Can't create the object, is it in the plugin folder?"
    end
    
    begin
      o.send(method_name, *args)
    rescue Exception => e
      return "ERROR: " << e.message << ". Did you include this method in the plugin file?"
    end
  end

As you can see, we dynamically instantiate a new object based on the class name that we’re passing in. After the exception handling, we call the method of the new object followed by any arguments.

For example: Let’s say that I want my server to ping an address. Instead of logging into the server and running the ping command, I could use the IM client to tell the server to ping the address and give me the results. All I have to type into the IM chat window is the following: command: network ping -c2 eddorre.com. This will ping eddorre.com twice on *nix machines.

The script will dynamically instantiate a Network object (provided it’s in the plugins directory) and try to run the method ping followed by any arguments.

In order for this to work, the network plugin/class has to have a method for ping in it. Let’s take a look at the code for that:


  def ping(*args)
    command = 'ping ' << args.join(" ")
    #Execute the command and return the result
    `#{command}`
  end

From the code above, I have defined a method called ping which takes the arguments at the command line, appends them to “ping”, and then executes the command (the backtick symbols mean execute the command and return the result).

Now, I’ll admit, using IM as an interactive shell is sort of limiting, but it can be much more powerful. For example, let’s take a look at the IIS class. This class, would in theory, handle everything related to Microsoft’s IIS web server. Let’s say that we want to find out how many current anonymous users are on a specific web site on our web server. We can define a method to return that information via the IM interface.

For example, let’s say I call this from the IM interface: command: iis current_anon_users [website]

The code for the current_anon_users method is below:


require 'win32ole'

class Iis < Plugin
  def current_anon_users(site)
    wmi = WIN32OLE.connect("winmgmts:root\\cimv2:Win32_PerfRawData_W3SVC_WebService.Name='_#{site}'")
    wmi.CurrentAnonymousUsers
  end
end

The code is simple, use a WMI object to return the data using the built in performance monitor on the server.

Using this new extensibility, you would be able to send a message to an agent such as “record this show” or “turn on the lights” depending on what you wanted it to do. All you have to do is build the plugin for it.

Canadian Adventures - Part I - The Capilano Bridge

Sunday September 21, 2008 14:11 | comment icon 11 Comments

I’ve been meaning to post about this but I never seem to get around to it. In perusing my Flickr Photostream, I rediscovered my old photos from a recent trip to Canada.

This is the first part of that post.

Last year, my mom, sister, and niece came to visit our new home Vancouver, WA. After a couple of days chilling at home we drove up to the other, more well known Vancouver.

My sister, the architect of the trip, wanted to hit a couple of spots that included Stanley Park, The Capilano Suspension Bridge, and Grouse Mountain. Now, for those that don’t know me personally, I’ll get this right out; I’m terrified of heights. Well, that’s a misnomer really. I’m terrified of what happens when you fall from a great height. If I could fly under my own power then heights probably wouldn’t be as scary.

The Capilano Bridge was up first. We drove up to the parking area and headed toward the bridge area. As we walked up to the entrance, I started getting nervous. They made it seem so official and so final. As if “if you get past this point you have to cross the bridge”.

Capilano Bridge Entrance

I asked the gal that was herding lines of people, “Has anyone ever gotten halfway and then couldn’t make it the rest of the way?” I just imagined myself getting halfway out there, suddenly being paralyzed with fear, collapsing into a fetal position where I would wet myself, suck my thumb and whine incoherently until being rescued by park rangers. She looked at me incredulously as if the thought was inconceivable and simply answered, “No.”

Goaded by my family, we all paid and entered the park area. It seems that my paranoia about entering the park was largely unfounded. You could pay the money to enter and actually not go across the bridge at all. I was somewhat relieved. This could be my spot to chicken out and not cross the bridge. Before I made that decision though, I wanted to take a look at the beast. The first time I viewed it, it didn’t look that bad.

Capilano Bridge View 1

In reality, this is an optical illusion. The bridge actually looks like something out of Indiana Jones and the Temple of Doom. Witness a far away shot of the bridge!

Capilano Bridge View 2

Feeling somewhat encouraged by the first view of the bridge, I decided to cross and confront my fears. Upon setting foot on the bridge, the first thing that I noticed was that it swayed, a lot. Of course as a suspension bridge, this is a good thing. If the bridge was rigid, it would certainly snap in the face of mother nature but that didn’t help to allay my fears.

I steeled myself and continued. As I made my way forward the enormity of the situation (the bridge is 230 ft/80 m above the Capilano Canyon floor and has a 450 ft/150 m span) started taking hold and fear started to rush in. I could feel the blood rushing away from my face as I stood paralyzed on the span grasping both of the hand rails. I felt the bridge sway underneath my feet and imagined kids horse playing behind me and accidentally knocking me off to my death.

I considered my options. Turn around and head back leaving my family to wonder where I had gone, collapse in a pool of my own fear and urine, or keep going. I closed my eyes, took a deep breath, refocused and miraculously continued forward.

I made my way slowly all the while holding both hand rails with an eagle like grip until someone wanted to pass me in which case I gripped the right hand railing with both hands and stopped moving until they passed. Although it felt like a lifetime of anguish while crossing the bridge, I reality it probably took 60 seconds to move across the entire span.

Once I was over, I felt like kissing the ground but I was exhilarated by the fact that I had faced my fear, set it aside and accomplished the task at hand. It was definitely worth it for the resulting “treetop adventure” on the other side as well as a personal sense of satisfaction.

end kanji