Main > Diary > Development
November 5, 2007
Python: Using Lambda to Delay Argument Bindings
At the University of Minnesota the first Computer Science class is still taught in Scheme. A lot of students complain the learning an archaic functional language is a waste of time, I could not disagree more. I think it is a great introduction for many reasons but it is especially useful to programmers nowadays who are using modern procedural languages which have an increasing tendency to mix in paradigms from the functional domain.
I am working on an RPC interface for a project at the office and I found myself writing yet another function to dispatch an event callback into a sequence of calls. I wanted to have a Python library routine callback call one of my functions with the first argument statically assigned to be a value of my choosing. In other words: I needed to bind some of the argument variables immediately but allow the remainder of the arguments be left unbound. Here a bit of functional flair fit the solution: lambda function composition.
First we will create our stub dispatch function:
def dispatch(variant, *args): print "dispatch(%s, %s)" % (variant, args) # a real implementation may now act differently based on the value of variant
We want our Python library call back to call our function like so...
dispatch("type1", (0, 1, 2)) dispatch("type2", (0, 1, 2)) # etc
Using a simple lambda composition we can create a new function that has the variant argument bound while still allowing the *args array to be set at a later time. This may be done simply:
mk_cb = lambda v: lambda *a: dispatch(v, *a)
Then we can create a series of custom functions which may be registered as callbacks with whatever variant type we wish to set:
cb1 = mk_cb("type1") cb2 = mk_cb("type2")
We can call one of the callback functions just to test it:
>>> cb1((0,1,2)) dispatch(type1, ((0,1,2),))
And finally we can use it with some Python library callback registration function:
some_py_obj.register_function(cb) # cb is the dispatch() function with 'type1' as the first argument
So who says that we are, "never going to use this stuff?"
Posted by jordanh at 6:14 AM | Comments (2) | TrackBack
Main > Diary > Development
February 7, 2007
Photoblog Archive Changed
The photoblog archive used to be this paginated lump of goo.
Now, it's a semi-slick dynamic HTML wonderland of thumbnails! Go give your eyes an eyeful.
Posted by jordanh at 12:47 AM | Comments (0) | TrackBack
Main > Diary > Development
February 5, 2007
Enter AJAX
I AJAXified the comment system for the blog. It's about time to join the rest of 2007, I say.
Wrangling MT, my legacy templates, and all the spam prevention stuff I've got integrated proved me to completely mis-scope how long this would take. I figured I could have the whole thing done in an evening, but it turned into about three nights of work on and off the past couple of weeks. Bleh.
I pulled the basic ideas from BrettDeWoody's MT AJAX Comments, but modified the scripts and such to play nicely with the DOM structure I had already defined. A couple of AJAX spinners later and I was ready to roll. Even after the rough spots, working with prototype.js directly (outside the safety of Rails, per se) certainly does leave one with a sense of awe of the authors of http://script.aculo.us/.
Go ahead, comment away. I'll no doubt make some improvements to the system over the next little while but for now, my new primary drive will be to add some more content. It is time to pick up the camera again!
Posted by jordanh at 1:49 AM | Comments (7) | TrackBack
Main > Diary > Development
October 30, 2006
mtcolor butt cheese
I decided to check into my referrer logs tonight just to see how much bandwidth the MySpace kidz are using hot-linking to my photoblog images (just wait until I come up with a fun image to replace them all with! Suggestions -> me) when I noticed that syntax highlighting stopped working again. This happened because I updated perl and the Syntax::Highlight::Universal module went AWOL again. Getting mtcolorer working continuously could have it's own highly paid crack support staff working 24x7. It's a terrible foundation this thing is built upon.
Long story short, I needed to rebuild the module by hand. A few modifications have to be made to the module source in order to get it to compile on a recent Linux distribution:
- Add -fpermissive to the CCFLAGS parameter of Makefile.PL.
- Use perl Makefile.PL to build a Makefile.
- Edit this makefile and change whatever *-gcc entries to *-g++; otherwise gcc will not generate the shared C++ symbols correctly.
- Edit ./colorer/common/Hashtable.h and change references to capacity and bucket on lines 22-23, 64-65 to this->capacity and this->bucket (scoping people!).
- make ; make install Voila!
After that it was smooth sailing; and time for bed!
{ printf("And to all, a %s.\n", "good night"); exit(-1); }
Posted by jordanh at 1:11 AM | Comments (1) | TrackBack
Main > Diary > Development
May 13, 2006
Recollating MySQL, How I Broke the latin_swedish_ci Blues...
I got mad. When I get mad sometimes I write ruby. Tonight I wrote ruby.
A couple of weeks ago the server that hosts this site melted down. I replaced the box and migrated all of the data into a freshly compiled copy of MySQL. Everything seemed to work peachy until I noticed that all of my UTF-8 characters were coming out as questions marks. It's very annoying when you want to say 世界が愛している and you get ????????. 나뻤어요!
What was worse was seeing exacly how much work it was going to take to change the collation in all of the columns in all of the tables within all of the databases on my system. Bleh.
I wrote a script in Ruby to change all of the collations en masse for a particular database. This is useful for me because I really don't use any other collation other than utf8_bin, save for the odd column set to utf8_general_ci.
If you're interested in such progmatic solution, read on...
The below 100 lines took me about an hour to write, so be careful if you're running it on a production system. Take care to know if you need any other collations other than the TARGET_COLLATION defined in the top of the class.
#!/usr/bin/ruby18 require 'mysql' class RecollateMysqlDatabase HOST = "localhost" USERNAME = "username" PASSWORD = "password" DATABASE = "mt_ablog" TARGET_CHARSET = "utf8" TARGET_COLLATION = "utf8_bin" def initialize @dbh = nil begin @dbh = Mysql.real_connect(HOST, USERNAME, PASSWORD, DATABASE) puts "Connected to MySQL Server Version" + @dbh.get_server_info rescue MysqlError => e mysql_exception_handler(e) end end def mysql_exception_handler(e) print "Error code: ", e.errno, "\n" print "Error message: ", e.error, "\n" begin @dbh.close rescue nil end exit 255 end def recollate_tables(preview = true) puts "Fetching tables..." if preview # get a list of tables tables = @dbh.list_tables # A place to store our operations table_column_hash = Hash.new alter_table_query_list = [ ] # for each table get a list of columns: tables.each do |table| puts "Have table: #{table}" if preview res = @dbh.query("SHOW FULL COLUMNS FROM `#{table}`") while row_hash = res.fetch_hash do if row_hash["Collation"] != "NULL" and row_hash["Collation"] != TARGET_COLLATION column_hash = { "Field" => row_hash["Field"], "Type" => row_hash["Type"] } table_column_hash[table] = [ ] if table_column_hash[table].nil? table_column_hash[table].push(column_hash) end end table_column_hash.each_key do |table| puts "\nIn #{table}, these columns need re-collating:\n" if preview table_column_hash[table].inject(false) do |comma,column_hash| printf "#{((comma) ? ", " : "\t")}" + column_hash["Field"] if preview comma = true alter_table_query_list.push <<-EOQ ALTER TABLE `#{table}` CHANGE `#{column_hash["Field"]}` `#{column_hash["Field"]}` #{column_hash["Type"]} CHARACTER SET #{TARGET_CHARSET} COLLATE `#{TARGET_COLLATION}` EOQ end printf ".\n" if preview end printf "\n" if preview end puts "-= PREVIEW OF SQL, NO CHANGES HAVE BEEN MADE =-" if preview alter_table_query_list.inject(0) do |i,query| puts query if preview if not preview printf "Executing query ##{i}...\r" STDOUT.flush begin @dbh.query(query) rescue MysqlError => e mysql_exception_handler(e) end end i += 1 end printf "\n" puts "-= PREVIEW OF SQL, NO CHANGES HAVE BEEN MADE =-" if preview end end rmd = RecollateMysqlDatabase.new rmd.recollate_tables(preview = true) printf "Okay to proceed? (YES) -> " response = STDIN.gets.chomp if response != "YES" puts 'I need the answer "YES" in all caps to proceed! Exiting...' exit 1 end rmd.recollate_tables(preview = false) puts "Done!" exit 0
Instructions: modify the top of the class connect with the username and password for the host and database you are interested in recollating. Run the script and if you are satisfied that the ALTER TABLE queries look sane, answer YES to let the script do the work.
Good luck!
Posted by jordanh at 1:32 AM | Comments (0) | TrackBack
Main > Diary > Development
March 10, 2006
mtcolorer-0.2 plugin source code cleanup
Here is a patch for mtcolorer-0.2 to clean up the source a bit. I was initially having trouble getting the plugin to work and I made some mostly cosmetic improvements to the source in order to facilitate debugging. Happy coding!
Here's the diff (link), using mtcolorer:
--- mtcolorer.pl.orig 2006-03-10 18:48:32.000000000 -0600 +++ mtcolorer.pl 2006-03-10 18:46:19.000000000 -0600 @@ -1,5 +1,5 @@ # --------------------------------------------------------------------------- -# MT-Colorer +# MT-Colorer # A plugin for Movable Type # # Release 1.0.0 @@ -11,66 +11,70 @@ # You may use it for commercial or personal use. # If you distribute it, please keep this notice intact. # -# Copyright (c) 2006 -# --------------------------------------------------------------------------- +# Copyright (c) 2006 +# --------------------------------------------------------------------------- +# +# Additional Changes: +# +# 10-Mar-2006 - minor fixes/clean-up work Jordan Husney <jordan@husney.com> +# package MT::Plugin::Colorer; use Syntax::Highlight::Universal; use strict; use MT; -use MT::Entry; -use MT::Plugin; +use MT::Plugin; use MT::Template::Context; -use MT::WeblogPublisher; -my $highlighter = Syntax::Highlight::Universal->new(); -my $plugin = new MT::Plugin(); -$plugin->name("MT Colorer"); -$plugin->description('Source code hightlighter based on Colorer (http://colorer.sf.net)'); -$plugin->doc_link('http://klim.doslash.org/projects/mtcolorer'); -$plugin->author_name('Vladimir Klimontovich'); +my $highlighter = Syntax::Highlight::Universal->new(); +my $plugin = new MT::Plugin(); +$plugin->name("MT Colorer"); +$plugin->description('Source code hightlighter based on Colorer (http://colorer.sf.net)'); +$plugin->doc_link('http://klim.doslash.org/projects/mtcolorer'); +$plugin->author_name('Vladimir Klimontovich'); $plugin->author_link('http://klim.doslash.org/eng_index.shtml'); $plugin->plugin_link('http://klim.doslash.org/projects/mtcolorer'); -$plugin->version("0.2"); +$plugin->version("0.2-jrh"); +MT->add_plugin($plugin); MT->add_callback("BuildPage", 1, $plugin, \&hightlight_text); -MT->add_plugin($plugin); - -sub trim -{ - my($string)=@_; - for ($string) - { - s/^\s+//; - s/\s+$//; - } - return $string; +sub trim +{ + my($string)=@_; + for ($string) + { + s/^\s+//; + s/\s+$//; + } + return $string; } sub hightlight_block -{ - my ($lang, $source) = @_; - my $htext = $highlighter->highlight($lang, $source); +{ + my ($lang, $source) = @_; + my $htext = $highlighter->highlight($lang, $source); $htext = trim($htext); return "<pre class=\"mtc_block\">".$htext."</pre>"; -} +} sub hightlight_inline -{ - my ($lang, $source) = @_; - my $htext = $highlighter->highlight($lang, $source); +{ + my ($lang, $source) = @_; + my $htext = $highlighter->highlight($lang, $source); $htext = trim($htext); return "<span class=\"mtc_inline\">".$htext."</span>"; -} - -sub hightlight_text -{ +} + +sub hightlight_text +{ my ($cb, %args) = @_; - use Data::Dumper; my $html = ${$args{'Content'}}; + $html =~ s/<mtc:block([^>]*?)lang="([^"]+?)"([^>]*?)>(.*?)<\/mtc:block>/hightlight_block($2,$4)/ges; $html =~ s/<mtc:inline([^>]*?)lang="([^"]+?)"([^>]*?)>(.*?)<\/mtc:inline>/hightlight_inline($2,$4)/ges; - ${$args{'Content'}} = $html; - return 1; + ${$args{'Content'}} = $html; + + return 1; } +1;
Posted by jordanh at 6:49 PM | Comments (0) | TrackBack
Main > Diary > Development
mtcolorer test
This is a test of the mt-colorer plugin, test #18:
int main(int argc, char *argv[]) { printf("hello, %s world!\n", "big"); return 0; }
Success!
If you are having trouble getting the plugin working, see my comments over at http://klim.doslash.org/projects/mtcolorer/index.shtml, the mtcolorer home page.
Posted by jordanh at 4:34 PM | Comments (0) | TrackBack
Main > Diary > Development
September 25, 2005
The Love Affair and the Hate Campaign
My love affair with Ruby continues. I wrote a small piece of software to perform numerical integration estimates. I learned that the development process in Ruby generally goes something like this:
- Have a clear idea of what you want to do.
- Have things not go as planned.
- Suspect something in Ruby to have an underlying wart or bit of complexity.
- Slap forehead due to own stupid mistake.
- Repeat.
I'm digging it.
I'm also grooving on a simple Vim plugin I went searching for. I had always wanted the ability to yank lines directly out of a shell buffer in order to be able to manipulate them within Vim. Well, I found exactly what I was looking for here.
It seems every time I wish to change something simple here on this blog of mine that I run into horrible cross-browser compatibility problems. It is silly the shear amount of infrastructure I have to put in to divide the universe into "standards-compliant" and "IE" realms. Today's problem dealt with wanting to finally solve my problem with displaying text within <pre> tags.
When I had originally published this site, I knew that pre data was getting clipped. I left it as a problem to solve on a rainy day. Well, today was rainy and I had just published and entry that I had wished to use a lot of preformatted text within.
The easy fix for Firefox is allow content in the content div to overflow. Sadly, this causes the sidebar on the right side to drop. Although I found this article on dropped div tags to be extremely useful, the JavaScript solutions presented were too much dinging around for me to bear.
I ended up allowing overflow in the standards-based stylesheet and then overriding this later in the IE-specific one. Also in the IE stylesheet pre elements are set to overflow:auto so they will get ugly little scrollbars if they overflow.
One wonders how much of this will be "fixed" in the new version of IE.
Posted by jordanh at 1:34 PM | Comments (0) | TrackBack
Main > Diary > Development
July 5, 2005
A Day Off, Photoblog Improvements
Today, I added a number of improvements to the photoblog.
First of which is I am now paginating the photoblog archive using MTPaginate. I found this article very useful in setting up the template. I may tweak the formatting or the pagination division a bit in the future, but for now it is a vast improvement over having all of the entries (and images!) load onto a single page.
Second, I added a strip of photos to the bottom of every photoblog individual entry. This should hopefully encourage people to want to browse the collection a bit more.
There is a propensity in MT to overload the Body, Extended Body, & Excerpt fields in order to create web log entries that are not simple articles such as entries for a photoblog. Having only three fields, or "columns" if one were to view this as a loosely typed database, can put you in a sticky situation when it comes to marking up and presenting your data to the user in different presentation contexts.
As a result, I've found more than once that I've had to resort to using Javascript to reach into the DOM and tweak the markup that is stored in one of those fields to change a link, etc. when that field is presented to the user. What makes this approach even more kludgey and is that then you introduce a whole slew of compatibility problems that must be solved between IE and Firefox. Only the Apple heads know if my site looks even halfway decent on Safari.
Browser compatibility feedback would be much appreciated.
This is the last day of my four-day holiday weekend. No work, just recreation. Life is good when life is on vacation.
Posted by jordanh at 2:07 PM | Comments (0) | TrackBack
Main > Diary > Development
April 30, 2005
Intelligent Image Resizing in JavaScript
update 11-Mar-2006: added syntax highlighting to source-code.
It's been a little while since I've played around much with doing anything slightly less than the ordinary with JavaScript and the DOM. After seeing some colleagues of mine bring up my photoblog I noticed that it is somewhat annoying not to be able to view the entire image on your screen at once at lower screen resolutions.
I started playing around—inadvertently on the live site—with developing a solution that would scale the main image using the window.onresize event. I was quickly reminded by how much I hate the differences in implementation between IE and Firefox. I found the following articles useful:
Obtaining the browser window size Quirksmode JavaScript - Browser detect
The whole thing is implemented as a JavaScript class that manipulates two ids in the DOM, "podImage" the image and "podImageViewOriginalLink" which creates the link test to return the image back to its original size.
Here's the code:
function podImageClass() { if ( !(this instanceof podImageClass) ) return new podImageClass(); var browser; var podImage; var podImageHeight; var podImageWidth; var podImageAspect; var debounceResize = 0; var haveResized = 0; function __setViewOriginalLink(displayBool) { var voLink = document.getElementById("podImageViewOriginalLink"); if (displayBool) { voLink.innerHTML = '<a href="javascript:podImage.setOriginalSize()">' + "Scale to Original Size" + "</a>"; } else { voLink.innerHTML = ' '; } } function _setText () { var podImageText = '"<$MTEntryTitle$>"' + " / " + "Click For Previous"; podImage.alt = podImageText; podImage.title = podImageText; } function _setSize() { var myWidth = 0, myHeight = 0; if (debounceResize) { debounceResize = 0; return; } if( typeof( window.innerWidth ) == 'number' ) { //Non-IE myWidth = window.innerWidth; myHeight = window.innerHeight; } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { //IE 6+ in 'standards compliant mode' myWidth = document.documentElement.clientWidth; myHeight = document.documentElement.clientHeight; } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { //IE 4 compatible myWidth = document.body.clientWidth; myHeight = document.body.clientHeight; } // Only do the resize if we've done this before or if our // image is greater than their window size 64px of top margin. if ( !( haveResized || ((podImageHeight + 64) > myHeight) ) ) { return; } // allow 128px margins on top and bottom: var newImageHeight = myHeight - ( 2 * 128 ); if ( (podImageHeight + 64) < myHeight ) { // Just use original width and height; _setOriginalSize(); } else { // Downscale: if (newImageHeight > 0) { var newImageWidth = newImageHeight * podImageAspect; podImage.width = newImageWidth; podImage.height = newImageHeight; } __setViewOriginalLink(true); } haveResized = 1; } function _setOriginalSize() { // We have to play with the onresize handler to work // around IE sending spurious events. window.onresize = null; podImage.width = podImageWidth; podImage.height = podImageHeight; __setViewOriginalLink(false); if (browser == "MSIE") { debounceResize = 1; } window.onresize = _setSize; } function _checkIt(string) { var detect = navigator.userAgent.toLowerCase(); place = detect.indexOf(string) + 1; thestring = string; return place; } function _init() { if (_checkIt('msie')) browser = "MSIE" else browser = "Other"; podImage = document.getElementById("podImage"); podImageWidth = podImage.width; podImageHeight = podImage.height; podImageAspect = (podImageHeight > 0) ? (podImageWidth / podImageHeight) : 0; } this.init = _init; this.setText = _setText; this.setSize = _setSize; this.setOriginalSize = _setOriginalSize; _init(); } <!-- setup podImage class: --> var podImage; addLoadEvent(function() { podImage = podImageClass(); podImage.setText(); podImage.setSize(); window.onresize = podImage.setSize; });
It's live on the site now; let me know if you run into any bugs or if you just find the whole thing annoying.
I'm sure one day this entire exercise will be reduced to two lines of CSS.
Posted by jordanh at 1:47 PM | Comments (1) | TrackBack
