Polymorphic Image Controller
I love attachment fu. I also love using a polymorphic image model. So how do you get around the problem of keeping your controllers DRY and RESTful, without littering it image functions? Here’s what I managed to come up with. It may not be the perfect solution, but it gets the job done and keeps things clean.
# images_controller.rb def create return unless request.post? image = Image.new(params[:image]) object = find_polymorphic_object if image.valid? object.images.create(params[:image]) flash[:notice] = "#{image.category.titleize} Successfully Added" redirect_to :back end end private def find_polymorphic_object sections = request.env['REQUEST_URI'].scan(%r{(\w*)/(\d*)}).reverse.reject { |x| x[1] == "" } sections.map! do |controller_name, id| [controller_name.singularize.camelize, id] end object, id = sections[0] eval("#{object}.find(#{id})") end
All the magic relies on having a RESTful URL for the find_polymorphic_object to work. It splits the request URL into an array, and from that pulls out the controller that is requesting the image upload. Eval the results with a find, and like magic the Image controller doesn’t need to know explicitly about every other model that has images.
Rails Generators: Part 1
Of the many tools that Ruby on Rails brings to the party, generators are by far one of the more useful but most often overlooked. They are a little esoteric compared to the well known plugin and their big brother cousin, the engine. While they won’t change the world, they can save you time by removing the burden of the boilerplate code that still winds its way into apps.
Generators can live in a handful of places. You’ll find them in gems, plugins, the lib folder of a rails app and in a special directory within your own home directory. Personally I find it easiest to keep my own generators within my home directory. This way they’re readily available for any rails project.
Let’s go through setting this up if it’s not there.
mkdir -p ~/.rails/generators
When you get down to it, a rails generator is really a folder full of templates and an instruction file that tells the templates where in your rails app they need to go. Think of it like a little company. You’ve got you (the CEO) who tells the boss (instruction file) what kind of work you want done. The boss then tells the workers (templates) where to go. Also included is a file called “USAGE” which handily gives out information about the generator, think of it like a receptionist.
For an example, let’s build a quick mini-scaffold. A spiffy one at that!
First we need to create the Spiffy Scaffold directory within our home generators directory. It’s important that the name of the folder be the same name as the generator itself.
cd ~/.rails/generators mkdir spiffy_scaffold
Now we need to add a few key files.
cd spiffy_scaffold touch USAGE # receptionist touch spiffy_scaffold_generator.rb # boss touch controller.rb # worker touch index.html.erb # worker
Congratulations! You’ve just made a generator, albeit one that doesn’t do much… yet. Still, take a moment to bask in your freshly generated glory. Create a dummy rails application, hop down into it via the command line and run script/generator. You’ll see the following.
Installed Generators User: spiffy_scaffold Builtin: controller, integration_test, mailer, migration, model, observer, plugin, resource, scaffold, session_migration, web_service
The “User:” before spiffy_scaffold tells us that rails found that generator within our home directory. Any others it finds there will be listed too. Neat! Now we’ve got a blank generator in place. In part 2 we’ll actually make it do something.
To Austin
I’m off to Austin for a few days. Some exciting changes are coming soon. Also, I’m working on a few new projects which should yield some spiffy new rails tricks. Stay tuned!
Scriptaculous Lavalamp Navigation
Quite a while ago I came across this post describing a really spiffy navigation technique. I really liked the organic effect and wanted to use it on a couple of projects. Unfortunately, most of my development takes place in rails and anyone who’s tried knows that mootools and prototype/scriptaculous don’t like to play nice in the same sandbox. I had given up, until just last week. I found this little gem over at “ok with failure”. The transitions were just what I needed, so I decided to try and give the original library a port to this framework. It only took a little tweaking here and there, and it works!
# menu.js
var SlideList = Class.create({ initialize: function(menu, options) { this.menu = $(menu), this.current = this.menu.select('li.current').first(); this.menu.select('li').each(function(item){ Event.observe(item, 'mouseover', function(){ this.moveBg(item); }.bind(this)); Event.observe(item, 'mouseout', function(){ this.moveBg(this.current); }.bind(this)); }.bind(this)); new Element.insert(this.menu, '<li style="display:none;" class="background"><div class="left"></div></li>') this.back = this.menu.select('li.background').first(); if(this.current) { this.setCurrent(this.current) }; }, setCurrent: function(el, effect){ this.back.setStyle({left: (el.offsetLeft)+'px', width: (el.offsetWidth)+'px'}); (effect) ? this.back.hide() : this.back.show(); this.current = el; }, clickItem: function(event, item) { if(!this.current) this.setCurrent(item, true); this.current = item; this.options.onClick(new Event(event), item); }, moveBg: function(to) { if(!this.current) return; new Effect.Parallel([ new Effect.Move(this.back, { sync: true, transition: Effect.Transitions.SwingTo, x: to.offsetLeft, y: 0, mode: 'absolute' }), new Effect.Morph(this.back, { sync: true, style: { width: to.getWidth()+'px' } }) ]) } }) Event.observe(window, 'load', function() { new SlideList('primary') });
# application.html.haml
#navigation .gutter %ul#primary %li.current= link_to "Home", root_url %li= link_to "Events", events_url %li= link_to "Forums", "http://www.totpguild.com/phpbb/" %li= link_to "Contact", new_contact_url
#navigation.sass
#navigation :background #ee2024 :border-top solid 5px #ff511d :font-family "Georgia", times, serif :font-weight bold :position relative :height 30px :padding 5px :overflow hidden ul :padding 0 :margin 0 :position absolute :z-index 12 li :float left :list-style none a /* :text-indent -500em */ :z-index 10 :display block :float left :height 30px :position relative :overflow hidden :color #fff :font-size 18pt :padding 0 14px :text-decoration none li.background :background url('../images/navigation/bg_menu_right.png') no-repeat top right !important :background url('../images/navigation/bg_menu_right.gif') no-repeat top right :z-index -1 :position absolute .left :background url('../images/navigation/bg_menu.png') no-repeat top left !important :background url('../images/navigation/bg_menu.gif') no-repeat top left :height 30px :margin-right 9px /* 7px is the width of the rounded shape */
Navigation Spacers Without the Mess
Designers love to put spacers in horizontal navigation, be it bullets, pipes or whatever character they find handy. Up to this point, I’d been shoving them in there and cursing at the intrusion into a sites usability and architecture.
No more!
Via some prototype library magic I’ve whipped up a little function to dynamically add whatever spacer I want to whatever menu I want based on it’s ID. This current implementation is actually taking a class and zipping through an array, as it’s what I needed in this instance, but the modifications to make it ID based would be slim.
menuspacer.js
var MenuSpacer = Class.create({ initialize: function(menus, spacer) { this.menus = menus; this.placeSpacers(spacer); }, placeSpacers: function(spacer) { $$(this.menus).each(function(menu) { Element.childElements(menu).each(function(s){ Element.insert(s, { after: ' <li>'+spacer+'</li> ' }) }); Element.childElements(menu).last().remove(); }); } }); document.observe("dom:loaded", function() { new MenuSpacer('ul.menu','•') });
Include Specific Stylesheets and Javascripts in Rails Templates
I can’t claim any credit for this, but came across it in this entry over at RailsCasts.
Basically you’re using helpers to call a special yield that gets inserted into the head of your document. Very handy for slipping in layout specific stylesheets or javascripts without cluttering up the main template.
# layouts/layout.html.haml %html{ :xmlns => "http://www.w3.org/1999/xhtml", :"xml:lang" => "en", :lang => "en"} %head %title Spiffy Page = stylesheet_link_tag "reset", :media => "all" = stylesheet_link_tag "public", :media => "screen" = javascript_include_tag :all, :cache => true = yield(:head)
# application_helper.rb def javascript(*files) content_for(:head) { javascript_include_tag(*files) } end def stylesheet(*files) content_for(:head) { stylesheet_link_tag(*files) } end
# controller/action.html.haml - javascript 'javascript_file'
Usability First
In a conversation today with a friend, and fellow consultant, I realized just how little usability plays in the World of Businesstm. Most meetings, he said, focused on permissions and data. Permissions… and data. Think about it for a second. It’s like building a house and focusing all your energy on hammers and nails. Imagine living in that house. It may not have a bathroom, but each nail was put perfectly into place and does exactly what that nail is supposed to do. That’s madness I tell you, madness!
I have had the good fortune to work on numerous projects that put usability first. Some of the first questions to come up are, “What should the user experience be like?” or “How can we make what they’re trying to do easier?”. It’s keeping that in mind that will make or break a website. Let’s face it, how much do you enjoy using websites that feel like work? I’d rather use one that doesn’t make me think about how to use it.
To that end, I highly recommend Don’t Make Me Think, by Steve Krug. It is one of the most influential books I read when I was a green developer, just starting out with working in HTML and CSS. It should be required reading for anyone who builds websites, or who manages web projects. In fact, anyone who winds up working for me in the long term gets a copy of that as a welcoming present.
Ramblings from Behind the Curtain
Welcome to my digital acreage. A little plot of 1’s and 0’s upon which I will scatter my thoughts, opinions and ramblings. Watered with a vain attempt to become a better writer, and tended with proper postings, it will be interesting to see what grows.
The blog title you ask? I believe it was Arthur C. Clarke who said, “Any sufficiently advanced technology is indistinguishable from magic”. Not unlike the doctor from Oz, are we geeks who hold the “magic wands” of technology. It is my philosophy that technology is best when you don’t know you’re using it. When it becomes so seamless with your task, your life, it simply evolves into a natural extension of what you’re trying to accomplish. Therein, lies the true magic, and the secrets held behind the curtain.
Geeks, however, are not all magic and technology. We’re real people too. In fact here, right next to a code example, you may find a new recipe for almond crusted pesto chicken or insights into the gym lifestyle. So belly up, have a seat, enjoy the show, and honestly… pay no attention to the geek behind the curtain.





