Posts from ...

Sterling-code.com For Your HTML Slicing Needs!

Sterling Code can help you get from design to finished HTML quickly.

We specialize in creating semantic, SEO optimized XHTML and HTML for rich emails. Offering competitive rates and quick turn-around, check us out!

Tags: software

Animating NSViews In RubyCocoa

Yesterday we talked about how to make nifty selectable toolbars like this:

selectable-toolbar.png

Now let's look at the finishing touch for our Preferences window; Animating the panel changes. We'll be flying through this at a pretty good clip, but don't worry. I'll provide the full source for your inspection.

First off, let's add some new outlets to our window controller:

  ib_outlets :generalPrefsView,
             :advancedPrefsView

CustomView.png

Now in Interface Builder, we'll create the views for each preference pane by dragging Custom Views from the Library onto our Preferences.nib.

Note: Be sure to drop the Custom Views on the main nib window in IB, not on the Preferences NSWindow. Your project (in IB) should look something like this:

Prefs-IB.png

Hook up the outlets to the new views, and edit your preference panels to your heart's desire. From here, we go back to the code.

Tip: Be sure to set the auto-sizing on your preference panels (the NSViews) so that it matches the NSWindow's contentView.

Picture 5.png

Next up are some helper methods for our window controller. I won't spend too much time explaining these, but they're pretty straight forward.

  def viewForTag(tag)
    case tag
      when 0: [@generalPrefsView,  "General"]
      when 1: [@advancedPrefsView, "Advanced"]
    end
  end

#viewForTag actually returns our NSView and a title string.

  def newFrameForNewContentView(view)
    newFrameRect = window.frameRectForContentRect(view.frame)
    oldFrameRect = window.frame
    newSize = newFrameRect.size
    oldSize = oldFrameRect.size
    frame = window.frame
    frame.size = newSize
    frame.origin.y = frame.origin.y - (newSize.height - oldSize.height)
    frame
  end

#newFrameForNewContentView calculates the new frame rectangle for the window based on the new view (preference pane).

Now we're ready to fill out our selectPrefPanel action:

  ib_action :selectPrefPanel do |sender|
    tag =  sender.tag
    view, title = self.viewForTag(tag)
    previousView, prevTitle = self.viewForTag(@currentViewTag)
    @currentViewTag = tag
    newFrame = self.newFrameForNewContentView(view)
    window.title = "#{title} Preferences"
    # Using an animation grouping because we may be changing the duration
    NSAnimationContext.beginGrouping
      # Call the animator instead of the view / window directly
      window.contentView.animator.replaceSubview_with(previousView, view)
      window.animator.setFrame_display newFrame, true
    NSAnimationContext.endGrouping
  end

Right on! Now we setup the initial pane when the window loads:

  def awakeFromNib
    window.setContentSize @generalPrefsView.frame.size 
    window.contentView.addSubview @generalPrefsView
    window.title = "General Preferences"
    @currentViewTag = 0
    # Will use CoreAnimation for the panel changes:
    window.contentView.wantsLayer = true
  end

That pretty much does it. Now you have a professional looking preferences window. So enough of those dang blasted NSTabViews!

Here's the completed PreferencesController.rb. Or, you can download the full Xcode project. (Requires Leopard, Xcode 3, and Interface Builder 3)

Happy coding!

Tags: cocoa,_software

Selectable Toolbar Icons in RubyCocoa

So you'd like to have some nifty selectable toolbar items to make your preferences window really polished? Or maybe you'd like to use the toolbar as a tab-set like Coda does. No problem, here's how to do it.

Note: I'm using Leopard & Interface Builder 3. You can create selectable toolbars in Tiger, but the process is different and not within the scope of this article.

To start, in the window controller, add an ib_action:

ib_action :selectPrefPanel do |sender|
  # We'll do stuff here later...
end

Then in Interface Builder, create the toolbar and the toolbar items. For each toolbar item:

Before you save the Nib, be sure and set the toolbar's delegate to the window controller.

Now back in the window controller code, implement a toolbarSelectableItemIdentifiers method in your controller:

def toolbarSelectableItemIdentifiers(toolbar)
  @toolbaridents ||= begin
    window.toolbar.toolbaritems.collect {|i| i.itemIdentifier }
  end
end

Lastly, when the window loads, select the first toolbar item:

def awakeFromNib
  window.toolbar.selectedItemIdentifier = window.toolbar.toolbarItems[0].itemIdentifier
end

Viola! Now you have selectable toolbar items.

Here's the full source for the window controller.

It's worth mentioning that this isn't specific to RubyCocoa. You can do the same thing in Objective-C, Python, or Nu (example).

Next, I'll show you how to create the views that will go within your preferences window, and how to animate them to really finish it off.

Update: Find the next article here.

Tags: cocoa,_software

Nuapp

Nuapp is a simple script (written in Nu, of course) that generates a skeleton Nu application. Check it out here: http://pastie.textmate.org/138976

You'll probably want to customize it. For instance, I doubt you'll want to use my "Requisite Beta Disclaimer". :-)

Tags: nu,_software

Snippet: Compiling XIBs into NIBs in your Nukefile

Here's a function I use in my Nukefile to compile XIBs into NIBs:

(function compile-xibs-from-to (xibs to_path)
     (if (and (!= target "clobber") (!= target "clean"))
         (then
              (SH "mkdir -p #{to_path}")
              ((filelist xibs) each:(do (xib)
                                        (set nib (xib stringByReplacingPathExtensionWith:"nib"))
                                        (SH "ibtool #{xib} --compile #{to_path}/#{( nib fileName )}")))
              (filelist "^#{to_path}/[^/]*.nib$"))
         (else nil)))

(set @nib_files (compile-xibs-from-to "^resources/views/[^/]*.xib$" "build/nibs"))

It uses ibtool to compile the .xib into a .nib:

ibtool source.xib --compile target.nib

Tags: cocoa,_nu,_software

AJAX Is X-TREME!

Author’s Note: I actually wrote this back in 2006, but it kind of fell through the cracks. So I present it now, for your amusement/pleasure/whatever…

The term “Web 2.0” has become synonymous with AJAX. With the hype and buzz that’s surrounded “Web 2.0”, people seem to invent reasons for using it. Even when it makes no sense. Sometimes especially when it makes no sense. Just because it’s “cool!”


ZooDotCom Presents - AJAX is X-TREME!!!


Oh yes, AJAX is cool. It can really enhance the user experience of your application. And yes, you can do some interesting single page applications that don’t require a server. But hopefully you’re not quite as gung-ho about it as Charlie here (from the comic above).

Rails makes it easy to use AJAX. Really easy. Too easy? Perhaps. And occasionally even I’ve been sucked into the trap of throwing out a quick, AJAXified, solution rather than taking the time to build it right, to build it compatible with the “Web 1.0” (or non-JavaScript enabled) folks. But I shouldn’t have. And neither should you.


ZooDotCom Presents - Fine, then how do I it?!


That, Charlie, is a good question.

In my next post I’ll talk a bit about Comatose, and how I went about ensuring that it’s backward compatible with non-JavaScript enabled browsers.

Author’s Other Note: I will post the second part to this soon… Probably next week. Cheers.

Tags: comatose,_comics,_rails,_software

R.I.P. Mr. Wieringo

R.I.P. Mr. Wieringo

Tags: comics

Comatose 0.8.1

It’s been a long time coming, but it’s finally here: Comatose 0.8.1!

Over the next few days, I’ll be transitioning the project to Google Code and Google Groups please start using them for reporting bugs and such. Here are the important URLs:

I’ve also updated the docs to reflect the new 0.8+ way of doing things. They’ll wind up on the wiki so that they’ll be easily maintained by all.

From the changelog:

  • All includes and helpers are loaded at the end of the ComatoseController and ComatoseAdminController classes
  • Fixed the ComatoseController#show action to correctly send an HTTP status of 404 even if it finds, and renders, a page at ‘/404’
  • Fixed the migration to default a page’s full_path to ” instead of null
  • Formalized ComatoseDrops. Use Comatose.define_drop "drop_name", do ... end. Every method within that block should return a value for use with a Liquid and/or ERB template. Usage in a comatose page: {{ drop_name.def_name }}
  • Added support for a config.after_setup block that gets called after Comatose is setup (in the Dispatcher#to_prepare event)
  • Added HTML comment output for calls that result in a method_missing on the ProcessingContext
  • Updated page tree to remember collapsed/expanded nodes between page visits
  • Fixed some errors that were caused by null revisions (usually happened after initial installation)
  • Added my javascript test_runner lib for testing slugs generated by JavaScript. More JS tests to come.
  • Bugfix #8640 (rubyforge bug)

For more see the devblog

Tags: comatose,_rails,_software

Sorta Nested Layouts

I was playing around with layouts in Rails the other day… I know, clearly I have too much time on my hands. But anyway, I found that you can fake a nested layout scheme in Rails by delegating view rendering to partials. As an added bonus, you don’t need to hack around with any of Rails’ internals to make it work.

For example, let’s say I’m working on an application that has both a public layout and an admin layout. The public layout consists of tabs and a sidebar. The admin layout has different tabs and no sidebar at all. Both layouts share a site header graphic.

With that in mind, here’s how we could structure our layouts and partials:

app/views/layouts/application.rhtml


  
    Layout Example
  
  
    

      <%= render :partial=>"layouts/#{controller.sub_layout}" %>

    
<%= yield %>

app/views/layouts/_admin.rhtml

<%= yield %>

Ah, there’s the <%= yield %>! That’s how you can delegate the view rendering. Basically, you’re using a partial to wrap HTML around the call to <%= yield %>.

OK, in the application.rhtml listing above, I get the partial that works as a sub layout from the controller. To hook that up, in your ApplicationController, you can specify a default sub layout like this:

class ApplicationController < BaseController

  # .. your actions

  def sub_layout
    "public" 
  end

end

Then in any administrative controllers, you can override it:

class UsersController < ApplicationController

  # .. your admin-like actions

  def sub_layout
    "admin" 
  end

end

I went ahead a threw together a little example application to better illustrate:

sublayout_example.zip (68 Kb)

Perhaps the example of different tabs and sidebars isn’t most compelling reason to use sub layouts—Which is fine. The key point to all of this is that you can delegate the rendering of your view from a layout to a partial. Which I’m sure you can leverage in all kinds of cool ways…

Tags: rails,_software

Comatose 0.8 Released

Tada! Comatose finally supports Rails 1.2!

In fact, it only supports Rails 1.2 now. If you are still on Rails 1.1, don’t upgrade Comatose — It won’t work. Consider yourself warned. (version 0.7.1 is tagged, so all you Rails 1.1ers can still get to it)

OK, so I made quite a few changes whilst upgrading Comatose. No functionality was removed, yet, but enough has changed that we should hit the high points here… I’ll post a more in-depth article on the dev blog later.

First up, there’s a new configuration system. It’s quite dandy, really. It single-handedly made the DEFER_COMATOSE_LOAD and controller class hacking obsolete.

You can still use all the same old settings, only you set them in a configuration block in your environment.rb now. Here’s an example from an app I’m currently working on:

Comatose.configure do |comatose|
  comatose.includes << :authenticated_system
  comatose.helpers <<  :application_helper
  # admin
  comatose.admin_title = "My App's CMS"
  comatose.admin_sub_title = "... its' fun for the whole family!"      
  comatose.admin_includes << :authenticated_system
  comatose.admin_helpers << :application_helper
  comatose.admin_authorization = :admin_required
  comatose.default_tree_level = 1
  comatose.admin_get_author do
    current_user.login
  end
  comatose.admin_get_root_page do
    roots = %w(public-root content-fragments)
    roots.collect {|path| Comatose::Page.find_by_path(path)}
  end
end

Look over that example closely, there’s a lot hidden in there.

  • comatose.includes is an array of module names (as symbols) that will be included in the ComatoseController.
  • comatose.helpers is also an array of module names, only these are loaded as helpers so you can use them in a view, most likely in your layout.

    Remember, you no longer extend ComatoseController to add functionality.

  • All that holds true for ComatoseAdminController and the comatose.admin_includes and comatose.admin_helpers as well.
  • comatose.admin_get_author and comatose.admin_get_root_page both accept a block of code that should return the author and root_page respectively.

    You can use any default controller method, as well as methods from included modules

  • comatose.admin_authorization is using a method from an included module directly, the :admin_required method from the AuthenticatedSystem module.

    The options that accept blocks will also accept symbols representing module methods.

  • Have a look at the config class for all the options

Also, there are unit tests now. Yay!

You can get an overview of the coverage, if you like. Oh, using rake test:plugins doesn’t work, to run them you will need to change into the vendor/plugins/comatose directory and run rake, after you’ve run the Comatose migrations.

From the CHANGELOG:

- Now ONLY supports Rails 1.2 (not even tested in Rails 1.1)
- New configuration system
- DEFER_COMATOSE_LOAD is gone -- Using Dispatcher.to_prepare replaces it
- You no longer extend the base classes to add functionality. You configure Comatose to include modules and send it symbols for providing said functionality. e.g. Comatose.config.includes << :authentiation_system; Comatose.config.authorization = :require_login
- The automatic loading of RAILS_ROOT/lib/comatose/*.rb files is NO longer supported. 
- In addition to mapping comatose_root in your routes.rb, you'll want to map.comatose_admin as well

Obviously, if you have any issues with this version, please let me know.

Tags: comatose,_rails,_software

Comatose, TaskTHIS, and Theme Support. Oh My!

Well, I’ve been a little remiss in my open-source development of late. Many apologies, things have been a bit hectic. You know the feeling, I’m sure.

But that’s not why I’m posting. I thought I’d outline my ideas for the future of these projects and get your feedback and/or ideas.

Comatose, The Micro CMS Plugin

The things that are definitely coming…

Edge Rails - Since Rails 1.2 is now official, it’s time to finally support ‘edge rails’. :-D

Actually, I have a version that runs on Rails 1.2 now, but I’m debating about Rails 1.1 backward compatibility. The new version changes quite a bit. The DEFER_COMATOSE_LOAD stuff goes away, replaced by better configuration support and a Dispatch::to_prepare block. Plus you no longer override the ComatoseAdminController for authentication, instead in the configuration you specify modules to include in the class. Oh yeah, and the controllers no longer extend ApplicationController.

Test Harness - The tests in the plugin itself are very spartan, at best. I have a horribly ugly test harness I use for the actual unit testing that I’m going to clean up and release.

Some possibilities

Liquid Only - I really think I’m going to drop ERb support. How many of you use ERb over Liquid for page processing?

RESTful Pages - Perhaps using the new RESTfully CRUDDY support would be useful. The idea of having an API for pages is interesting.

Mount Behaviors - I haven’t thought this through yet, it just popped into my head: map.comatose_root 'devblog', :index=>'blog', :behavior=>'blog'

This example would add ‘blog-like’ support such as all children being paged entries (showing last 10, next page, last page, etc.), automatic hAtom microformat support, and maybe some sort of archives… I don’t know. I still haven’t thought about this too much.

The behavior support would be pluggable, much like text-filters and drops… Can you think of any other behavior that would make sense?

Maybe :behavior=>'syndicated' to create RSS/ATOM feeds of the child pages? Perhaps they’d be mixable by saying :behavior=>'blog syndicated' or :behavior=>'blog,syndicated'.

The probably nots

Media Management - It just feels like too much. I have a hacked up version of Comatose that supports page attachments. But you wind up having to deal with upload directories, and file permissions and… It just feels too heavy. And too heavy != micro.

Perhaps it could be a separate plugin?

TaskTHIS

TaskTHIS is getting a bit long in the tooth. It was written right about the time some of my AJAX patches were being added to Rails. Which was pre 1.0. So, yeah. It needs some love.

It was created as a show-and-tell for the then-new-and-nifty AJAX support. Which, of course, is now old hat.

In keeping with the tradition of show-and-tell, I thought TaskTHIS would be an excellent application to show how to use the new CRUD/REST/Resources stuff in Rails 1.2.

I have a few ideas outlined here. The biggest ones are:

API - This is fairly straight-forward, we’ll get most if it from Rails. We’ll just add the appropriate authentication for the XML requests.

OpenID - I was thinking replacing the existing login system with OpenID authentication. I like the idea of just typing in my domain to login… Who needs passwords? Seriously though, this may be a bit controversial — please weigh in.

Oh, I forgot to mention, TaskTHIS.com is working again.

Theme Support Plugin

Basically, just add any outstanding patches and ensure it works on Rails 1.2. I imagine the routing stuff will need to be tweaked.

Whadda Ya Think?

What would/wouldn’t you like to see in any of these projects?

Tags: comatose,_rails,_software,_taskthis,_theme_support

Default Gravatar Image; A Greasemonkey Script

So you may have noticed that Gravatar has been going through some growing pains and isn’t working right now.

Until it’s back, pages that use gravatars aren’t loading quickly, or taking forever to load with broken images… Which is a little annoying. So I wrote a quick Greasemonkey user script to replace the image’s source with the site’s default gravatar image.

  Gravatar Default Image « Click to install

Note: This will only work on sites that use an img tag to show avatars. If a site uses a gravatar url as a css background-image, well, you’re SOL. However, with a little hacking, I’m sure you could tweak this script to do that too, if it was important to you. ;-)

I tested this with Greasemonkey on Firefox 2 and Creammonkey on Safari 2. It’ll probably work on Turnabout for Internet Exploder, but I didn’t test it.

Tags: software

Lil Monstas!!

LilMonstas.com!

Lil Monstas is live! Woot! We actually made our Halloween deadline too — barely. There was about five minutes left!

Check it out today and every Monday for new installments. I hope you enjoy it!

Update: LilMonstas.com now has an RSS feed!

Tags: comics

Comatose 'Compatibility Mode'

OK, so after mentioning it in my last post, I’ve decided to go ahead and implement it. Comatose version 0.7.1 adds a DEFER_COMATOSE_LOAD flag that you can use to, well, defer comatose’s loading. You will have to manually tell it to load in your environment.rb using Comatose.load.

See this devblog post for more.

Normally, I’d only post this on the Comatose development blog, but I wanted to make sure anybody who’s having problems using Comatose with other plugins/applications knows about this. If you have problems or questions, leave a comment here. Thanks!

Tags: comatose,_rails,_software

Comatose Version 0.7

This release finally adds support for versioning. It also adds an auto-loader for any comatose customizations you put in a RAILS_ROOT/lib/comatose folder.

See the devblog post for more.

Comatose has been having a few problems co-existing with some kinds of plugins lately. The problems seem to be caused, at least in part, because the comatose controllers extend ApplicationController and therefore requires the application.rb before the rest of the plugins load.

Plus, some plugins seem to take issue with any previous plugins defining an Active Record model before they’re loaded — very picky stuff.

To combat this, I’m thinking of adding a DEFER_LOADING flag in comatose/init.rb. When the plugin initializes, if that flag is true it won’t load the controllers or the models. You’ll then need to add a call to Comatose::Load in your environment.rb that would then load the controllers and models — after the rest of your app had loaded. By default, the flag will be set to false so that it remains backward compatible.

Look for that in the next release.

Tags: comatose,_rails,_software

But I'm Not Bitter...

Darth Lucas

Tags: comics,_movies

Pardon The Dust

I’ve updated the website, moving away from WordPress (for various reasons). So there may be a few broken links floating about.

The RSS feeds may be a bit fragile too… I updated the .htaccess file to redirect the feeds correctly—I hope. If you’re having a problem with the feeds, broken links, or something else regarding the site, please let me know.

Tags: default

Just Plain Wrong

spamtacular

Tags: default

Comatose v0.6

Comatose 0.6 is out! Here’s a quick summary of the new stuff:

  • ComatoseController has been split into ComatoseController and ComatoseAdminController
  • Both controllers extend your ApplicationController
  • The views, stylesheets, and javascripts have been updated and renamed to reflect the controller change
  • Configuration via Comatose::Options class
  • Liquid is the default text processor
  • Support for named routes
  • A Getting Started Guide
  • Pages now have a created_on field
  • Bug-fixes

For more, see the CHANGELOG.

Turns out I spoke too soon in my earlier post, there is a schema change. So if you're upgrading you'll need to run:

$ ./script/generate comatose_migration --upgrade --from=0.5

If you have questions/problems/feedback, drop by the Comatose project site and post in the forum or report a bug.

Tags: comatose,_rails,_software

Comatose Development Site

Rubyforge has been kind enough to create a project site for Comatose. I’ve enabled the following project tools for it:

Rubyforge also provides a project-specific wiki. I haven’t enabled it, at this point — If you think it’d be useful, lemme know and I can.

So from here on, if you have problems with Comatose you can add a bug ticket. Or better yet, fix it and submit a patch. ;-) If you need help with something, feel free to post on the mailing list or the help forum.

If you’ve used Comatose, I’d like to encourage you to drop by the open-discussion forum and leave a note. If you’ve done anything special with it, I’d love to hear it!

Tags: comatose,_rails,_software