jeremyweiland.com http://jeremyweiland.com The home page of Jeremy Weiland Sat, 28 Mar 2009 17:44:42 +0000 http://wordpress.org/?v=2.7 en hourly 1 The New House http://jeremyweiland.com/archives/76 http://jeremyweiland.com/archives/76#comments Sat, 28 Mar 2009 17:44:42 +0000 jeremy http://blog.6thdensity.net/?p=1435 Another view of Schloss McWeilandYes, the rumors you’ve been hearing are all true: we closed on our first home Thursday evening! It’s been a long road, and we almost didn’t get there (Bank of America, you were awful). But we got a great interest rate and a deal on a home that should have cost twice as much.

And you know whom we have to thank? My late Grandma Chappell, who left us just enough money for a modest down payment in her will. Thanks, Grandma, for your wonderful gift and being a great grandparent for so long!

Here’s a breakdown of the positive points about the house:

  • Built in 1910, but completely stripped down and replumbed, rewired, with all new appliances, room, and heat pump (need to replace the refrigerator, though). Original moldings and panel doors, though!
  • A block from James River Park, with lots of water and rocks for Tela and beautiful Belle Isle accessible by paw.
  • A five minute drive from my office. It’s actually quicker to walk than to take the bus!
  • Two bedrooms and an office
  • Huge living room and dining room, with a great kitchen.
  • Utility room with room for Tasha’s kiln.
  • A garage with a separate shed, all cleaned out and ready for Tasha to wire up and turn into a studio.

Our new house in the March SnowHere’s what we need to do:

  • Fence in the front yard first, then the backyard, for Tela.
  • Put a gutter on the roof.
  • Buy a new refrigerator.
  • Put a new roof on the shed and generally turn the garage into a studio with wiring.
  • Paint the downstairs rooms.
  • Move in!

So our lease at our current place ends April 30, giving us plenty of time to get the place ready and move in. We’ll let you know when the housewarming party is, but until then just wanted to share the next phase of our lives with ya!

]]>
http://jeremyweiland.com/archives/76/feed
Quote of the Day http://jeremyweiland.com/archives/75 http://jeremyweiland.com/archives/75#comments Tue, 24 Feb 2009 16:25:44 +0000 jeremy http://blog.6thdensity.net/?p=1380 I know it’s been a while. Sorry. Anyway, this is great:

Jesse Helms and his ilk inject a bit of fresh blood into art which, for a moment, can imagine itself an insurrectionary force. The sad irony is that Helms really believes art can change the world. The NEA liberals think that all art should be permitted because, after all, it’s only art.

- from the TAZ show, Feb 6 1993

Watch the whole thing here, it’s a hoot.

]]>
http://jeremyweiland.com/archives/75/feed
Tela in heaven http://jeremyweiland.com/archives/64 http://jeremyweiland.com/archives/64#comments Fri, 06 Feb 2009 14:28:51 +0000 jeremy http://blog.6thdensity.net/?p=1360 My first real YouTube upload with my new iFlip camcorder:

]]>
http://jeremyweiland.com/archives/64/feed
Reprocessing attachment_fu images with RMagick http://jeremyweiland.com/archives/65 http://jeremyweiland.com/archives/65#comments Thu, 05 Feb 2009 21:57:43 +0000 jeremy http://blog.6thdensity.net/?p=1347 Attachment_fu is the shit, no doubt. But sometimes you want to do more than upload, resize, and thumbnail. Designers often have a specific vision that dictates a more complex workflow for incoming images. For these tasks, it may be necessary to reprocess the saved images - something you don’t necessarily need to hack attachment_fu to accomplish.

For example, my latest Rails project included a gallery page with a pretty standard layout: a series of thumbnails and an area to display the full size version of the selected thumbnail. However, the static mockup delivered by the designer had thumbnails that were black and white with a blueish tint, only turning color when you moused over them. On top of that, the thumbnails were often made from a manually defined cropping of the image. This meant that in addition to an administrative backend to allow uploading and management, I needed to provide a tool for selecting an area within the image for a custom thumbnail, not to mention figuring out where and how to do the tinting.

So here’s how I approached it: I created two STI models deriving from a common GalleryImage model, all of which are related to the GalleryItem that encapsulates the item name, description, etc:

class GalleryItem < ActiveRecord::Base
  belongs_to :full_image, :class_name => 'GalleryMainImage', 
                          :foreign_key => 'full_image_id', 
                          :dependent => :destroy
 
  belongs_to :custom_thumbnail, 
                    :class_name => 'GalleryThumbnail',
                    :dependent => :destroy
end
 
class GalleryImage < ActiveRecord::Base
end
 
class GalleryMainImage < GalleryImage
  has_one :gallery_item, :dependent => :destroy
  has_attachment :content_type => :image, 
                 :storage => :file_system, 
                 :max_size => 50.megabytes,
                 :resize_to => '457>',
                 :thumbnails => { :default_thumbnail => '68x68!',
                                  :gray_thumbnail => '68x68!' },
                 :path_prefix => 'public/gallery',
                 :thumbnail_class => "GalleryThumbnail"
 
  validates_as_attachment
end
 
class GalleryThumbnail < GalleryImage
  has_one :gallery_item, :dependent => :destroy
  has_attachment :content_type => :image, 
                 :storage => :file_system, 
                 :max_size => 1.megabyte,
                 :resize_to => '68x68',
                 :thumbnails => { :gray_thumbnail => '68x68' },
                 :path_prefix => 'public/gallery'
 
  validates_as_attachment
end

So, the important takeaways here are the following:

  • All thumbnails would be of class “GalleryThumbnail”.
  • An item has one GalleryMainImage with it’s own attachment_fu-generated thumbnails. These thumbnails are the “uncropped” thumbnails.
  • An item can have two more custom cropped thumbnails associated with it - a color one and a gray one.

Is it clunky the way attachment_fu generates thumbnails given how we’re setting this up (why does GalleryThumbnail have thumbnails?)? Yes. But note especially that the specific settings in the “has_attachment” line (such as resizing and thumbnail generation) are only applied to a full attachment model. Therefore, only the objects associated with “item.full_image” and “item.custom_thumbnail” go through the full swath of attachment_fu processing. In other words, just because a full image generates thumbnails of class GalleryThumbnail does not mean each of those GalleryThumbnail objects gets their own thumbnails - obviously, that would be stupid. It’s better to not think about the generated images attachment_fu delivers as “thumbnails” so much as different versions of the attachment.

The next step is doing the grayscaling. I determined the path forward on this first by consulting the designer who delivered the mockup. He went back into Photoshop and gave me an idea of what filters and processes were applied to the thumbnails he had created. Basically, he created a grayscaled version of the thumbnail and then applied a gradient map that mapped black to a shade of blue. It gave it a very interesting effect:

141744150_e76e7e0aed_default_thumbnailbandw141744150_e76e7e0aed_gray_thumbnail

It took a lot of digging, but I was able to find two RMagick methods that could reproduce this effect: quantize and level_colors. Now it was just a matter of finding out how to integrate them into the thumbnailing process. I was surprised that simple callbacks basically did the trick. I put a method in the base class for the images:

class PortfolioImage < ActiveRecord::Base
protected
  def update_gray_thumbnail!  
    return unless thumbnail.blank? # only perform this on a custom thumbnail or an original image
 
    # the code below looks dumb, but I think the trick is that RMagick
    # can only perform one operation per file load.  I don't get it.
    thumb = thumbnails.find_by_thumbnail("gray_thumbnail")
    Magick::Image.read(thumb.full_filename).first.quantize(256,Magick::GRAYColorspace).write(thumb.full_filename)
    Magick::Image.read(thumb.full_filename).first.level_colors("#201000", "#f7f7f7", false).write(thumb.full_filename)
  end
end

Remember: in our modeling, everything is a GalleryImage. What makes a given GalleryImage a thumbnail according to attachment_fu is that it has a non-nil response to the “thumbnail” message (if this were our gray_thumbnail, it would return “gray_thumbnail” in response to the “thumbnail” message). So by proceeding only if thumbnail returns a blank response, we guarantee that we work with a main attachment like a MainImage or custom thumbnail, and that we don’t work on any of their associated images.

So look at how we generate thumbnails for a GalleryThumbnail and GalleryMainImage: there’s a default_thumbnail and a gray_thumbnail. If you’re using attachment_fu with “:storage => :file_system”, then you have physical files in the project that you can modify to your heart’s content. The above method changes the actual file associated with the “gray_thumbnail”, which is initially saved as a color thumbnail. So if you put a hook in your GalleryMainImage and GalleryThumbnail models to make this alteration on saving the model, you should be money:

after_save :update_gray_thumbnail!

Attachment_fu regenerates thumbnails on every model save, so it’s important we reapply the RMagick processing each time.

So what about the custom cropping? Well, you’ll need a controller that can generate a new GalleryThumbnail to be associated with the GalleryItem. All I’ll say on that count is that you should look at some javascript cropping utilities; I’m using jquery so I used imgAreaSelect. Following this example I was able to create a tool letting the user drag a box over with previewing of the resulting thumbnail, passing the coordinates for cropping to the controller via a form submission. Then it was just a matter of cropping the image, which once again is merely a matter of manipulating the actual full-size image file saved in the public directory after the fact:

 def create
    item = GalleryItem.find(params[:portfolio_item_id])
    crop = item.full_image.crop(params[:x1], params[:y1], params[:w], params[:h])
    thumb = GalleryThumbnail.new
    thumb.uploaded_data = crop
    thumb.save    
    item.portfolio_thumbnail = thumb
    if item.save
      flash[:notice] = "Cropped custom thumbnail saved."
      redirect_to admin_gallery_item_path(item)
    else
      flash[:error] = "Error resizing"
      render :action => 'new'
    end

I have a “crop” method on GalleryMainImage defined thusly:

def crop(x, y, width,height)
    blob = StringIO.new(Magick::Image.read(full_filename).first.crop(x.to_i, y.to_i, width.to_i, height.to_i).to_blob)
    {'tempfile' => blob,
     'content_type' => "image/#{filename.split('.').last}",
     'filename' => "custom_#{filename}"}
  end

The use of StringIO and returning a hash is just tricks to get attachment_fu to accept our cropped image as a parameter for “uploaded_data=”. And once the cropped image is passed into “uploaded_data=” and object is saved, the thumbnail will be generated using the cropped image - and grayscaled appropriately!

That’s pretty much it - I know this is really complicated, but I hope it helps somebody out there. Feel free to ask questions, and be advised that I may revisit this article to word things differently.

]]>
http://jeremyweiland.com/archives/65/feed
Duckpin bowling on Wednesday http://jeremyweiland.com/archives/66 http://jeremyweiland.com/archives/66#comments Mon, 02 Feb 2009 16:17:04 +0000 jeremy http://blog.6thdensity.net/?p=1345 In case you don’t get the Facebook invite, feel free to join Tasha and me for duckpin bowling at Plaza Bowl (523 E Southside Plz) on Wednesday at 6:30pm to celebrate my 30th birthday!

]]>
http://jeremyweiland.com/archives/66/feed
An RJS Redirect Matcher for rspec http://jeremyweiland.com/archives/67 http://jeremyweiland.com/archives/67#comments Thu, 29 Jan 2009 16:10:46 +0000 jeremy http://blog.6thdensity.net/?p=1333 You know what’s stupid? Clumsily checking for a javascript redirect in your RJS with this kind of shit:

it "should redirect to the collaborative quote screen" do
  xhr :post, 'attach', :attachment_id => '4023'
  response.body.should =~ /window\.location\.href = \"/collabquote\";"
end

Not only is this ugly, but it ties your test to a particular route, rather than allowing you to use your named route. So I whipped up a custom RJS redirect matcher in about 10 minutes following the guidelines in this post, and I was surprised how easy it was. It should be pretty self explanatory.

module RedirectViaRjsToMatcher  
  class RedirectViaRjsTo  
    def initialize(expected)  
      @expected = expected  
    end  
 
    def matches?(target)  
      @target = target
      @url = target.body.split('"')[1]
      @target.body == "window.location.href = \"#{@expected}\";"
    end  
 
    def failure_message  
      "expected redirect via rjs to #{@expected}, redirected instead to #{@url}"
    end  
 
    def negative_failure_message  
      "unexpected redirect via rjs to #{@expected}"  
    end
  end
 
  # Actual matcher that is exposed 
  def redirect_via_rjs_to(expected)  
    RedirectViaRjsTo.new(expected)  
  end
end

All you need to do is save this file in /spec/custom/redirect_via_rjs_to.rb and include it in /spec/spec_helper.rb like so:

require 'spec/be_the_same_as'
 
Spec::Runner.configure do |config|  
  config.include(RedirectViaRjsToMatcher)  
end

Voilla! It really is that easy, and turns that first spec into something a bit more readable and reusable:

it "should redirect to the collaborative quote screen" do
  xhr :post, 'attach', :attachment_id => '4023'
  response.should redirect_via_rjs_to quote_path(@quote)
end

UPDATE: Jim clued me into simple_matcher, which appears to be yet another very easy way to create custom matchers.

]]>
http://jeremyweiland.com/archives/67/feed
Behind the scenes at Richmond’s unbowling destination http://jeremyweiland.com/archives/68 http://jeremyweiland.com/archives/68#comments Sat, 17 Jan 2009 18:59:43 +0000 jeremy http://blog.6thdensity.net/?p=1295 plazabowl004So Matt O., I, and our wives rolled over to Plaza Bowl and Duckpins this past Thursday to duckpin bowl and drink PBR pitchers. We had a great time and ended up talking to the owner (it was a slow night). He asked us about any ideas we had for better marketing the place and we had a great conversation that really explored our love for the place.

Plaza Bowl is really just an incredibly fun destination - it’s laid back, and you don’t feel pressure to bowl 300. There’s now a stage where some of the lanes used to be, and bands play regularly. It’s got a great retro feel since none of the equipment has been manufactured for decades, and the owner maintains it himself.

In fact, we were thrilled that he invited us behind the scenes to observe the 1950s era machinery that runs the lanes, and I snapped some pictures on my iPhone. As programmers Matt and I were amazed at this complex mechanical state machine, and there’s an aesthetic to the vintage gears, chains, and conveyor belts that adds to the appeal. A steampunk’s dream!

I highly recommend that you check Plaza Bowl and Duckpins out. It’s difficult to communicate how awesome this place is, but I guarantee you’ll have a good time and be back. We want to keep this place in town, so show your love!

plazabowl002plazabowl005plazabowl001plazabowl003
]]>
http://jeremyweiland.com/archives/68/feed
Happy Holidays http://jeremyweiland.com/archives/69 http://jeremyweiland.com/archives/69#comments Thu, 25 Dec 2008 16:34:57 +0000 jeremy http://blog.6thdensity.net/?p=1239 Here’s a photo montage Tasha put together to express our wishes for the season. Enjoy your days of mirth!

Happy Holidays
]]>
http://jeremyweiland.com/archives/69/feed
All hail the Amen Break! http://jeremyweiland.com/archives/70 http://jeremyweiland.com/archives/70#comments Wed, 24 Dec 2008 14:11:11 +0000 jeremy http://blog.6thdensity.net/?p=1235 Since I used to produce amateur electronic music, mostly drum and bass / jungle (you can sample some of my work here) I’m very familiar with the prevalence and importance of the Amen break. So it’s cool to see a short documentary that can chronicle its adventures and tie it into free culture and the tyranny of intellectual property law.

]]>
http://jeremyweiland.com/archives/70/feed
Migrating your contacts from Windows Mobile to the iPhone without touching a PC http://jeremyweiland.com/archives/71 http://jeremyweiland.com/archives/71#comments Tue, 23 Dec 2008 22:34:09 +0000 jeremy http://blog.6thdensity.net/?p=1226 Well, I just got an iPhone, and I gotta say it’s one cool gizmo. Time will tell if the sexy creature will hold up or if the network will be sufficient (nobody has good things to say about AT&T’s coverage, but so far so good). But it’s definitely the most usable phone I’ve ever beheld, and I think it’ll end up making my life easier.

So my old phone (which I detailed here) had to go - Windows Mobile was a complete bitch, and within the past week I couldn’t even get buttons to work. The touch screen is a disaster, Windows Mobile takes years to respond to your input, and the battery life was abysmal. While the iPhone didn’t have a high bar to clear, I did encounter some frustration: trying to get my contacts off my old phone and onto my new one.

If you’re using a Windows PC with iTunes and your iPhone, importing your contacts should be a cinch: just use ActiveSync to bring them into Microsoft Outlook, then import those contacts into iTunes directly. But if you don’t have a PC, it’s complicated. Early on I tried using Mail2Web’s free exchange server to pull stuff off the old phone. That worked well, and I simply installed a “Exchange ActiveSync Profile” on the iPhone to bring in the contacts. Trouble was, these contacts were permanently connected to the exchange account. I didn’t want an exchange account on my phone indefinitely, so when I removed the profile, my contacts went with it!

Since I do have a Macbook, I was able to figure out a way to get contacts from the exchange server into Apple’s Address Book application. The iPhone works seamlessly with Address Book, so this is definitely the way to go. After much sweat and cursing, here’s finally the tried and true way to retrieve those contacts off your Windows Mobile device - without unnecessarily dirtying your fingers with Windows:

  1. Sign up for a Mail2Web Live account
  2. Install the Exchange ActiveSync program on your Windows Mobile device - you can download it from the Mail2Web Control Panel under “ActiveSync Settings”. Be sure to log into that and not the “Outlook Web Access” site. Check the sidebar for the link.
  3. Sync the device with your exchange account - this should happen automatically when the package installs, but if not initiate an ActiveSync synchronization manually.
  4. Open Address Book on your Mac. Select “Preferences”, check the box next to “Synchronize with Exchange” and enter the following credentials:
    • Username: your Mail2Web user name@mail2web.com
    • Password: your Mail2Web password
    • Outlook Web Access Server: https://exchange.mail2web.com/exchange/what you put in the username field above
  5. Open the iSync application on your Mac, enter “Preferences”, and select the “Show status in menu bar” checkbox. You’ll then see an icon in the menu bar at the top of the screen. Right click it and select “Sync Now”.
  6. Wait for the sync to finish up - you may need to iterate through conflicting contacts to clean them up, but it’ll walk you through it.
  7. Connect your iPhone to your Mac, go into iTunes, select your iPhone on the left-hand sidebar, select the “Info” tab, and check the box that says “Sync Address Book contacts”, and click the sync button at the bottom.
  8. When the sync is finished, check your iPhone to ensure the contacts came through.

Enjoy your iPhone and your freedom from MicroSerfdom!

]]>
http://jeremyweiland.com/archives/71/feed