Posted by Vincent Woo
Tue, 02 Jun 2009 00:10:00 GMT

In my current project, I needed to come up with a way to visually differentiate subdomain accounts. Adding rich theming support didn’t make sense since:
- End users are not responsible for the site design and hiring a graphic designer is low priority
- The web application is for automating a workflow as much as possible. Less options and configuration means less maintenance and manual intervention
In order to meet these guidelines, I wondered about the possibility of asking end users to choose just one site color. Could the web application do something meaningful and seamlessly with that single choice?
Game Plan
Every time one of the stylesheets is linked to in the view:
- open up the stylesheet
- find all RGB color values and convert to match the requested color
- save stylesheet to a cache directory
We’ll be using the HSL color model. It breaks down a color into three components: hue, saturation, and lightness. Hue is what we’re interested in. By shifting only a color’s hue, we can, for instance, turn a red color to yellow without breaking the design legibility or contrast.
Conveniently, CSS3 allows us to adjust the hue by specifying an angle in degrees on a color wheel where red is both 0 and 360. Using this method, we only need to do basic substitution without any math conversions.
The bad news is HSL color values are not supported by older browsers or Internet Explorer 8. It is reassuring that modern versions of the other major web browsers (Safari, Firefox, Opera, and Chrome) do support the HSL model but we’ll have to play the waiting game for legacy browsers to go out of use. To be safe, our color values will start off and end up as RGB values.
Finally, once we convert the stylesheets, we’ll need to cache them somewhere. I’m storing them in #{Rails.root}/public/stylesheets/hue#{hue_value} because:
- reuse: it is possible for several subdomain accounts to share the same hue value
- it’ll be served as a static file by Apache
- free cache sweeping: upon deployment via Capistrano, old cached files are not carried over
Implementation Limitations
The colors in my stylesheets are RGB hex strings such as #1234AA and #123. There is no support for color names like ‘red’ in my implementation since I don’t use them.
Another consideration are the graphic design constraints. Since only a dominate hue is allowed, image usage should be minimized or you’ll have to worry about image conversions (not covered in this tutorial). Shoot for a minimalist design with blocks of solid color.
Implementation
install the Color gem. It is used to handle our color model and hue conversions.
# config/environment.rb
config.gem 'color', :version => '1.4.0'
rake gems:install
We’re going to add several methods into ActionView::Helpers::AssetTagHelper. First, use alias_method_chain() to append our manipulation logic to run just before the original stylesheet_link_tag().
def stylesheet_link_tag_with_hue(*sources)
options = sources.extract_options!
sources.map! do |stylesheet_path|
if stylesheet_path.starts_with?('/') || stylesheet_path.starts_with?('http')
stylesheet_path
else
stylesheet_path = stylesheet_path + '.css' if File.extname(stylesheet_path) == ''
ensure_stylesheet_for_hue(HUE, stylesheet_path)
"hue/#{HUE}/" + stylesheet_path
end
end
sources << options
stylesheet_link_tag_without_hue *sources
end
alias_method_chain :stylesheet_link_tag, :hue
It’ll run through the list of stylesheet sources. If any source is a candidate for hue conversion, ensure_stylesheet_for_hue() is called and the source is prepended with the hue directory path.
Here is where the file operations and hue conversions take place:
def ensure_stylesheet_for_hue(hue, path_suffix)
source_path = Rails.root.join('public', 'stylesheets', path_suffix)
destination_path = Rails.root.join('public', 'stylesheets', 'hue', hue.to_s, path_suffix)
return nil unless File.exist?(source_path)
return nil if File.exist?(destination_path) && Rails.env != 'development'
File.open(source_path) do |file|
output = file.read.gsub(/#([0-9a-f]{6}|[0-9a-f]{3})/i) do
color = hsl_color_from_rgb_hex_string($1)
color.hue = hue
color.html
end
FileUtils.mkdir_p(File.dirname(destination_path))
File.open(destination_path, 'w') {|output_file| output_file.write(output)}
end
end
The color library doesn’t have a constructor for our RGB color hex strings so we’ll add one here.
def hsl_color_from_rgb_hex_string(hex)
args = case hex.length
when 3: [hex[0,1]*2, hex[1,1]*2, hex[2,1]*2]
when 6: [hex[0,2], hex[2,2], hex[4,2]]
end
args.map! {|arg| arg.to_i(16)}
Color::RGB.new(*args).to_hsl
end
Results
Overall, I’m satisfied with the functionality. The biggest issue is the connotations we give to various colors and their shadings. For my site, it is hard to select an orange hue since dark orange doesn’t look like orange. Also, it’s not pink, it’s lightish red. Some minor tweaking of saturation and lightness helps to make it work well with all hues though.
Posted in Ruby on Rails, Design | Tags color, css3, hsl, hue, rgb, stylesheets | no comments
Posted by Vincent Woo
Tue, 24 Mar 2009 09:45:00 GMT
For those of us who are still using the Exception Notifier Plugin, be careful when upgrading to a recent version of Rails. Doing so with Rails 2.2.2+ and the configuration is in an environment file (#{RAILS_ROOT}/config/environments/production.rb for instance) will not send notification emails. Instead, it’ll fail with:
Net::SMTPSyntaxError (501 5.1.3 Bad recipient address syntax
):
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:679:in `check_response'
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:652:in `getok'
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:634:in `rcptto'
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:545:in `send0'
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:544:in `each'
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:544:in `send0'
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:471:in `sendmail'
/opt/ruby-enterprise-1.8.6-20090113/lib/ruby/1.8/net/smtp.rb:378:in `start'
Several ways to work around this problem. You could wrap the configuration inside an after_initialize block:
config.after_initialize do
ExceptionNotifier.exception_recipients = %w(joe@schmoe.com bill@schmoe.com)
ExceptionNotifier.sender_address = %("App Error" )
ExceptionNotifier.email_prefix = "[APP] "
end
The other solution is to put the configuration in an initializer file.
Of course, there is the option to instead use Hoptoad or Exceptional.
There is a lighthouse ticket if you want to follow the discussion.
Posted in Ruby on Rails | Tags after_initialize, exception, notifier, plugin, rails | no comments
Posted by Vincent Woo
Fri, 13 Mar 2009 11:38:00 GMT
In one of my Rails projects, I have this one class that strips HTML from some user submitted text. Instead of reinventing the wheel, I reused the Rails view helper strip_tags() by including the relevant module:
class Presenter
include ActionView::Helpers::SanitizeHelper
end
This worked great until I upgraded the Rails framework to v2.2.2. The following error began appearing:
undefined method `full_sanitizer' for Presenter:Class
Turned out it was easy to fix. Looking at the strip_tags method in actionpack/lib/action_view/helpers/sanitize_helper.rb:
def strip_tags(html)
self.class.full_sanitizer.sanitize(html)
end
It’s trying to access a class method that doesn’t exist in Presenter. While I included the instance methods, now I also have to extend the necessary class methods.
class Presenter
include ActionView::Helpers::SanitizeHelper
extend ActionView::Helpers::SanitizeHelper::ClassMethods
end
Now all is good.
Posted in Ruby on Rails | Tags extend, helper, html, rails, strip_tag, view | no comments
Posted by Vincent Woo
Fri, 05 Dec 2008 01:01:00 GMT
Upgrading a Rails project to version 2.2 broke controller specs and I couldn’t figure out why. The problem i was facing was that the default Rails exception rescuing would not work in the test environment.
I’m seeing alot of something like the following with ‘rake spec’:
ActiveRecord::RecordNotFound in 'PagesController responding to GET show should not find the record'
ActiveRecord::RecordNotFound
An exception is raised during the page request as expected but it prematurely ends the spec instead of going through the rescue handlers I’ve setup through rescue_from(). I had no idea if this new behavior is expected or a regression. Sure I could do…
lambda{do_get}.should raise_error(ActiveRecord::RecordNotFound)
...but I wasn’t looking forward to updating the spec suite to conform. Also, it wouldn’t be testing the rescue handler behaviors. Initially, searching via the almighty Google yield no answers. Stepping through code execution using rdebug revealed that RSpec modifies Rails to not rescue exceptions as it normally would. However, the modification also includes a simple way to revert to the default behavior. Score! So now I’m doing:
before(:each) do
controller.use_rails_error_handling!
end
With that solved, I now know which search terms to use and, lo and behold, Google reveals that somebody had the same problem and already figured it out. Google, why had you forsaken me earlier?
Also, there is more context if you’re interested.
Posted in Ruby on Rails | Tags exception, rails, rescue, rspec | no comments | no trackbacks
Posted by Vincent Woo
Wed, 26 Nov 2008 20:36:00 GMT
Still got a laugh out of this classic comic years since it came out. Check out the rest of his site though unfortunately there isn’t a full archive of his work.
Posted in Misc. | Tags comic, iron_chef, sharkey | no comments | no trackbacks
Posted by Vincent Woo
Mon, 03 Nov 2008 04:38:00 GMT
Take a look at the Firefox extension Pixel Perfect if you implement webpages based off Photoshop mockups. It overlays a transparent image of your choice over the webpage so that out of place elements are easy to spot.
It’s currently at version 0.6.1 but it has plenty of potential. I could see how it could be integral to the workflow but at the moment it’s great for quick checks.
Posted in Web | Tags extension, firefox, mockups, photoshop, webpages, workflow | no comments | no trackbacks
Posted by Vincent Woo
Sun, 12 Oct 2008 02:27:00 GMT
alias for teh win! Didn’t get into the habit until now because I thought saving a few keystrokes wasn’t that beneficial. But damn it helps with a few edge cases and now I’m addicted.
In my ~/.profile:
# For managing encrypted FileVault containing my Rails products.
# '-owners on' so the owner and groups can be set on files.
alias mount_rails="hdiutil attach /Users/vwoo/Documents/Knox/rails.sparseimage -owners on"
alias umount_rails="hdiutil detach /Volumes/rails"
# alias now takes the place of bash scripts I made for several
# server software installed through MacPorts.
# Arguments work like bash scripts. $1 is the first argument etc.
# so I can run 'apache start' and 'apache stop'
alias apache="sudo /opt/local/etc/LaunchDaemons/org.macports.apache2/apache2.wrapper $1"
alias postgresql="sudo /opt/local/etc/LaunchDaemons/org.macports.postgresql82-server/postgresql82-server.wrapper $1"
Posted in Misc. | Tags alias, filevault, macports, shell | no comments | no trackbacks
Posted by Vincent Woo
Sat, 11 Oct 2008 22:01:00 GMT
Clean, completness, and clarity. There is something refreshing about an empty email inbox. There is satisfaction from accomplishing all to-do tasks. There is liberation from paying off debts.
Something I’ve been striving for in the past couple of weeks is to reduce the mass of junk thats been accumulating in my laptop. Lots of “I’ll do that later” tasks spread out through email, iCal, and text files have been deleted. If I can put a task off for a long time, then it isn’t important. The same with bookmarks, ebooks, media, and applications.
Like archiving a massive iTunes library and only restoring select music only with a need, clean out the dock of all icons and restore only the ones that are used regularly. Mine are Firefox, mail.app, iCal, iTunes, iTerm, and TextMate. Any other apps can be called up through Spotlight or Quicksilver.
I use instant messenging only a bit at random times of the day. The IM client I used previously would add a dock icon and perpetually open window that would take up unnecessary space the rest of the time. So I switched to iChat where it can be hidden away as a menu button when not in use. If iChat isn’t working that way by default, enable it at iChat -> Preferences… -> General. Check off “Show status in menu bar”.
Switching to iChat means a move to a different IM network. It offers a new opportunity to restart with an empty contact list. I added back only the contacts when I have a need to contact them. The end result in a much slimmer quality contact list. Old contacts who wish to can find me with the Jabber account info in my nick.
Posted in Misc. | Tags clarity, clutter, debt, dock, email, ichat, im, jabber, menubar, osx, tasks | no comments | no trackbacks
Posted by Vincent Woo
Wed, 08 Oct 2008 05:35:00 GMT
Recently, our team spent a great amount of time tutoring a remote client. In this case, the client needed help understanding the basics for an off the shelf e-commerce package. Our usual channels of communication were not adequate since the client was not web-savvy and was easily confused with our instructions. We had to adapt, re-evaluate our assumptions, and find ways to get the client up to speed. How could we teach the essentials without face to face conferences and without a shared lingua franca? We rejected our usual suspects, tried a few new strategies, and found another one that, in hindsight, could have been very useful in tutoring remote clients.
Screen Sharing
The best solution is to use realtime chat involving a shared desktop. This is as close to a face to face conference as possible.
My preference is iChat screen sharing but not everybody uses Apple computers. a VNC derivative or Windows remote desktop would have been fine too.
Screen sharing is very close to a face to face conference but it does have its problems. The biggest is that the setup could be difficult for non web-savvy clients. Troubleshooting remotely is falling back to square one. Only recommended for clients with a reasonable chance of setting it up or who have lots of patience.
Existing Documentation
Usually, there is documentation for users to study. Have a look around the software website and you’ll probably find official manuals and screencasts. Chances are good that a web search will find some great user created tutorials as well. This is a good first step and many times it’ll work well to point clients to these resources instead of creating the content yourself.
In our case example, there are plenty of documentation for the software chosen by our client. Sadly, in response to some of the client’s questions, references to selected manual pages and 7 minute video tutorials from the original developers were too confusing so we had to look at other solutions.
Phone Conferences, Email, Instant Messenging
Phone calls, email, and instant messenging are lousy for demoing or tutoring purposes. It’s not meant for walking through a website or explaining multi-step tasks. However, they’re great for answering questions that don’t require great detail and if the team and remote client have a shared jargon.
Services such as Skype and Vonage could help keep long distance call costs down while still acting like a regular phone line.
Campfire is a web based instant messaging tool. No worries about setup and it retains chat histories accessible by all members of the team.
Highlighted Screenshots
Our aft-mentioned client was paralyzed and frustrated with the complexity of the website admin panel. The interface was too cluttered and the client was unable to navigate the menu to even begin performing administration tasks. Clarity is the key goal to strive for.
Try highlighting certain page elements to clarify what exactly the client should be concerned with. Another way of looking at it is the opposite: to de-emphasize as much of the non-essentials of a busy screen as much as possible.
Any additional text has immediate context. Consequently, text stays concise by only explaining the significance of the highlight elements. No need to describe page elements beforehand.
I tried a couple of highlighting techniques and the one I like best involves darkening, and thus lowering the contrast, of the entire screenshot. The background still preserves enough prominence such that the client can still figure out detail if desired. Since the page is dark, it is then easy to accentuate the important elements: make it bright and full of contrast.
Here is a screenshot to illustrate how easy it is to recreate the effect in Photoshop. Of course, many variations are possible so experiment until you find something to your liking.
While these screenshots can be made fairly quickly, a whole series of them sucks up precious time. Still, they are effective in getting the message across and can be worth the effort. Because I took into account what the client knowledge level was and customized it to answer questions directly and concisely, the client was able to learn at an accelerated rate.
Desktop Recording
The other day I came across this software called Jing that captures the video of the computer screen while recording from the computer’s sound input. It isn’t realtime like screen sharing, but is valuable for constructing quick and dirty tutorials that require little or no post processing.
The premise of a streamlined workflow is what sold me on the idea. First, record a demonstration as you would normally present it. Jing will then automatically upload the movie so that it is accessible through the web.
Jing can be configured to automatically upload the movie through FTP to a web accessible directory. If you don’t have a web host, they do offer complimentary 2GB webspace accounts at screencasts.com. You can signup for this account during the Jing program installation.
So far, I’ve created a few test movies and the quality is great. movies are about a megabyte for every 20 seconds but your mileage will vary.
I can’t say Jing is perfect though. It doesn’t integrate well with the OSX environment with its custom yellow and black theme. Worse, switching to and from some of Jing’s dialog boxes is a hassle since they’ll disappear and are unrecoverable through the dock or expose. Sometimes application windows are unmovable and are set to appear above all others. Jing needs an interface overhaul.
Another downer is that files are encoded in only .swf format. That’s fine for quick throw away movies but is not post processing friendly. Finally, the greatest sin is that Jing crashed too many times to ignore. I wasn’t keeping track but it felt like there was a 50% chance it’ll crash every time I record. I will need to find a more robust desktop recording software for more involved work.
Despite the negatives, Jing has a clear and productive purpose. I’ll keep Jing installed.
Posted in Design, Web | Tags campfire, conferences, desktoprecording, documentation, email, highlight, ichat, instantmessenging, jargon, jing, phone, photoshop, remoteclients, screencasts, screensharing, screenshots, skype, tutoring, vnc, vonage | 1 comment | no trackbacks
Posted by Vincent Woo
Mon, 29 Sep 2008 20:56:00 GMT
Laser-printed logo onto a bean seed. Watch it grow with you. Genius method to embed specific values into a brand.
Yiying Lu’s portfolio site has lots of other creative designs as well.
Posted in Design | Tags brand, design, lasers, portfolio | no comments | no trackbacks