New movie rental service

Archive for the 'Code' Category

I saw Archive Flagged Items from NetNewsWire into Yojimbo (via) and found it a really neat tool. I've started using it to keep a more permanent record of interesting new articles.

I have lots of news articles flagged and I've had the import crash on me before for various reasons which has resulted in some duplicate web archives being created. The reason for the duplicates is usually because there was a redirect to the actual article that Yojimbo followed, so the URL in NetNewsWire doesn't match the Yojimbo URL and checking for existing items on load doesn't work.

This was a great opportunity for me to explore RubyOSA and continue learning Ruby so I wrote a script to detect duplicate web archives based on name and mark them with a "Duplicate" label.

RUBY:
  1. #!/usr/local/bin/ruby
  2.  
  3. require 'rubygems'
  4. require 'rbosa'
  5.  
  6. yojimbo = OSA.app('Yojimbo')
  7.  
  8. seen = Hash.new(false)
  9.  
  10. # Duplicate label details
  11. duplicateLabelName = 'Duplicate'
  12. # An almost painful red
  13. duplicateLabelColor = [65535, 1536, 4628]
  14. duplicateLabel = nil
  15.  
  16. # See if we already have a label named 'Duplicate' and save it
  17. yojimbo.labels.map  { |l| l.name == duplicateLabelName && duplicateLabel = l }
  18.  
  19. # Make a label if we don't have it already
  20. if (duplicateLabel.nil?)
  21.   duplicateLabel = yojimbo.make(OSA::Yojimbo::Label,
  22.                                 nil,
  23.                                 :color => duplicateLabelColor,
  24.                                 :name => duplicateLabelName)
  25. end
  26.  
  27. yojimbo.web_archive_items.each do |f|
  28.   if (seen.include?(f.name))
  29.     puts "Found a duplicate: #{f.name}"
  30.     f.label= duplicateLabel
  31.     seen[f.name].label= duplicateLabel
  32.   else
  33.     seen[f.name] = f
  34.   end
  35. end

I'd like to start posting code on my blog so I'm testing out a syntax highlighter plugin. This post will likely go through several iterations as I see what it can do and how to do it.

Here's some ruby:

RUBY:
  1. def addPages(urls)
  2.     return false unless urls
  3.     urls.each{ |u| @queue <<PageFactory.newPage(u) }
  4.     @queue.uniq! # Get rid of dupes.
  5. end

Here's some Perl:

PERL:
  1. my @text = ( 'here', 'are', 'some', 'words' );
  2. my $offset = 0;
  3. print join(' ', map { choppedCase($_) } @text );
  4.  
  5. # Convert a string to "ChOpPeD cAsE"
  6. sub choppedCase
  7. {
  8.     my $word = shift;
  9.     my @chars = split('', $word);
  10.     my $char_count = scalar(@chars);
  11.  
  12.  
  13.     for (my $i = $offset; $i <$char_count; $i+=2)
  14.     {
  15.         $chars[$i] = uc($chars[$i]);
  16.     }
  17.  
  18.     # Track odd length words so we constantly alternate in a string
  19.     if ($char_count % 2)
  20.     {
  21.         $offset = $offset == 0 ? 1 : 0;
  22.     }
  23.     return join('', @chars);
  24. }

Here's some PHP

PHP:
  1. // Pattern to match the directory path that a file is in
  2. $dir_pattern = "@(/.*)/[^/]*$@";
  3.  
  4. // Take a path that's (possibly) relative to the directory that the script is executing in
  5. // and convert it to an absolute path.
  6. function absolutize($path)
  7. {
  8.     global $dir_pattern;
  9.  
  10.     // Only operate on paths that are relative
  11.     if (substr($path, 0, 1) == '.' && preg_match($dir_pattern, $_SERVER['PHP_SELF'], $matches))
  12.         $path = implode('', array($matches[1], '/', $path));
  13.  
  14.     $path_bits = explode('/', $path);
  15.     $elms = array();
  16.     foreach ($path_bits as $bit)
  17.     {
  18.         if ($bit == '' || $bit == '.')
  19.             continue;
  20.         if ($bit == '..') {
  21.             array_pop($elms);
  22.             continue;
  23.         }
  24.         $elms[]= $bit;
  25.     }
  26.     // Push an empty string on top so that we get a leading / when we implode
  27.     array_unshift($elms, '');
  28.     return implode('/', $elms);
  29. }

I just realized that I've coded in all three of these languages this week - plus JavaScript (can't find a good sample to post).

Update
JavaScript

JAVASCRIPT:
  1. // Add commas to a long number to make it more readable.
  2. // Currently only supports whole numbers.
  3. var addCommas = function(aNumber)
  4. {
  5.   // Convert our number to an array and reverse it so that
  6.   // we can easily work from most significant digits down
  7.   // to least significant
  8.   var bitsR = (aNumber.toString().split('')).reverse();
  9.   for (var i = bitsR.length - 1; i>2; i--)
  10.     if (i % 3 == 0)
  11.       bitsR.splice(i, 0, ',');
  12.   // Return our array with commas added back to the proper order
  13.   // for display on the screen.
  14.   return bitsR.reverse().join('');
  15. };

I generally write common CSS that applies to all pages and handle special overrides for a particular page or section by setting a class on the body tag


<body class="pressRelease">

Then if you usually style h2's on the site with a border-bottom, you can easily make press release h2's not have a border-bottom


h2 { border-bottom: 1px solid #666; }

body.pressRelease h2 { border-bottom: none; }

This lets you easily group page or section specific overrides together and identify them in a contextual way.

I've been doing some HTML/CSS/JS work for various clients at work recently and I've come across some very useful articles/tips for doing stuff. I'm sure none of this is new but if I only encountered it recently, maybe others haven't yet.

Selfclearing Elements - Don't you hate it when you float an element and the container doesn't get the height correctly. Then, maybe you float the container, but that causes problems with other things. Then you try adding an element just to hang a clear on to fix it and now you're cluttering up your pristine markup with garbage... There is a solution and it's even elegant.

z-index/stacking - So, if you want something to appear above something else on a page, you just give it a higher z-index, right? That's what I've always thought but I was never really satisfied with that because it didn't always work the way I expected it to. I didn't totally understand it so, when I had to fix something with z-indexes, I tended to just try different values in different places until I found something that works. No more. I found Overlapping and Z index and now it all makes sense.

JavaScript the JSON way - It's pretty amazing how far the JavaScript language has come in the last few years. One of the coolest things I've seen recently is the technique for organizing your code with JSON to prevent function name collision and make things generally cleaner. Dustin Diaz has a great introduction to the method that will get you rolling with the JSON goodness.

I've been doing JavaScript programming off and on for a number of years and I feel pretty competent. I know I don't have a super deep grasp of the fundamentals but I understand how AJAX works, the benefits of sandboxing your code with JSON and some of how libraries such as jQuery and prototype work. I knew that there were gaps and that I didn't have the real grounding necessary to totally understand how everything works but I never thought much about how to go about filling in the blanks.

Then I came across this blog post yesterday. I've watched parts 1 and 2 and I'm really impressed by how Douglas Crockford clearly and succinctly presents very basic info on the language and is laying the groundwork for a much better understanding of it. I know feel like I've got a really good grasp on concepts that before I sort of understood.

The lectures are very watchable and I really encourage anyone who's interested in learning JavaScript or getting a better understanding of the fundamentals to watch them.

So, while I was doing some work with my plugin, I realized that there were a couple things that could be improved.

  1. You had to specify parameters as an array, even if there was only one parameter.
  2. You had to specify the body as a string - making it cumbersome to create multi-statement functions.

Well, both of those issues are in the past. You can still use an array for parameters and string for body, but you can also use an array for body and a string for a (single) parameter - or any combination of such.

So, grab version 0.2 and have at it.

I'm working on a Rails app using RJS and Prototype Window Class. I needed to be able to return an anonymous JavaScript function in my RJS template but when passing it in a string to page.call it kept getting quoted and didn't work right (as you would expect).

I solved it by creating a JavaScriptFunction class and helpers so that it's easily usable in an RJS template. Browse the plugin or install it:

# With subversion
./script/plugin install -x http://svn.vanderbrew.com/svn/repos/plugins/javascript_function/

# Without subversion
./script/plugin install http://svn.vanderbrew.com/svn/repos/plugins/javascript_function/

To use it, just do:

page.call 'Dialog.confirm', "New Thing - do you want to add it?",
            { :windowParameters => { :width => 300 },
              :sokLabel => 'Yes',
              :cancelLabel => 'No',
              :buttonClass => 'myButtonClass',
              :id => 'myDialogId',
              :cancel => anonymous_javascript_function(:parameters => ["win"],
                         :body => "$('#{@element_id}').innerHTML = '#{escape_javascript @thing.name}'"),
              :sok => anonymous_javascript_function(:parameters => ["win"],
                         :body => "return true;")
            }

It's my first plugin so I'm open to any feedback you have about it. Please use the Contact form to reach me.

Note - the keys :sokLabel and :sok shouldn't have an 's' in the front for real implementation with the JS library - I did that to prevent an emoticon from appearing thanks to WordPress.

So, as you probably noticed, my creative writing on the site has fallen off quite a bit lately. It seems that I can focus on creative writing or I can focus on coding, but I'm not so good at doing both at the same time.

I'm going to be focusing on coding for the foreseeable future because that's where I want to allocate my time right now. I've got a big unfinished project in that arena and it's time to make some progress on it, hopefully contributing back to the community as I go.

I hope to return to more consistent writing in the future and I'm sure that I'll have the occasional line here and there but I don't think that there will be very many for a few months. I've created a Creativity feed and if you're just visiting for the writing I encourage you to simply track that with your feed aggregator.

I purchased a copy of Chad Fowler's excellent Rails Recipes recently. He's got a few recipes dealing with edit in place and one about building local autocomplete. I pulled these recipes together into a single solution that gives me an edit in place with local autocomplete functionality.

Rails Helper
I created a couple Rails helper functions to encapsulate the functionality so I can call it with one function in my view. View the source, with syntax highlighting.

JavaScript
I had to extend the existing Scriptaculous code to support my specific functionality. View the source, with syntax highlighting.

Implementation
Now, you just include the JS in your page and use the new helper to build the UI component.

<%= in_place_editor_field_with_local_autocomplete :recipe,
                                                  :cookbook_title,
                                                  {},
                                                  {},
                                                  {
                                                    :catalog => 'cookbooks',
                                                    :fullSearch => true,
                                                    :frequency => 0,
                                                    :minChars => 2
                                                  }
%>

Update
Ola Bini has developed an InPlaceEditor with Autocomplete plugin for Rails. Definitely worth a look.

So, I was on Amazon yesterday, looking for a false bottom for making a lauter tun. I was awfully surprised to see that result. I'm guessing that it's a perfect example of what can happen when suggestion code goes wrong, probably due to the combination of specific and uncommon search terms.

Even more interesting is the fact that I get this result when searching in the Outdoor Living area of Amazon's site, but not when searching from the "homepage". I guess that it's just a testament to the complexity of the site and how segragated the functionality of different stores is.