Monday, December 5, 2011
Inkwellian Theatre: Minicomic Anthology: Jumblaya! #1
Friday, October 15, 2010
Simple Todo App using Backbone
Here's a quick example I threw together of a simple Todo app using Backbone.js. It overrides the Backbone.sync function to persist the tasks to localStorage. I tried to keep it simple.
I'd really like any Backbone gurus to have a look and make suggestions (or make their own fork) for better collection/view handling. Or to just rework it to show "best practices."
Some questions I have are:
- What's the best way to handle views when the associated model has been destroyed?
- Is there a better way to handle collections? (A collection controller perhaps?)
- When a collection is refreshed, is it better to blitz the DOM (as this example does) or loop through the models to see if they already have Views and attach/create as needed?
It seems most of my questions are related to Views, huh?
Well, I'll continue to experiment and if I come to any conclusions I'll be sure and post them. But if you are, or know, anyone well versed in Backbone (can't be too many people, at this point) I'd really like some feedback.
You can see it below, or here.
Enjoy!
Technorati Tags: javascript, backbone.js, jquery
Using Backbone.js in CoffeeScript
If you don't know about CoffeeScript yet, you need to check it out. Now. -- I love it. It feels as fun and clean as writing in Ruby, only it compiles to JavaScript. That's what you would call "all good." If you were one to use such expressions.
Here's some coffee I created so I could use Backbone classes as native CoffeeScript classes:
Thursday, October 14, 2010
Apps With Backbone
I've been working with a lot of JavaScript (single page) apps lately. I've found that the primary challenge of JS apps is keeping your data and UI sync'ed up.
There are those who say that attaching the data directly to the DOM is fine... But I'm leery of that. Some browsers (notably IE, of course) tend to leak memory when attaching extraneous data to the DOM. Plus, it just feels wrong. There's a reason "Separation of Concerns" (SoC) is a bit of a mantra.
Ideally you'd want to use something like Cocoa's KVC bindings for your UI. But the two main projects that have that as a goal, SproutCore and Cappuccino, are actually trying to implement the entire Cocoa stack on the web. Cappuccino even goes so far as to create a new language (sort of) to do so. Which is great, I guess, if you want to invest the time to learn their huge frameworks. (Don't get me started on their lack of decent docs.)
A new option appeared yesterday. DocumentCloud released an interesting so-called MVC framework named Backbone.js.
I should mention that it's not actually an MVC framework in the truest sense. It provides Models, Views, and Collections... MVC actually stands for Models, Views, and Controllers. For Controllers, I think Sammy.js is probably the best Controller framework you'll use.
Why is Backbone interesting? Because it's basically a small framework (~2Kb) that provides the core pieces of KVO bindings.
It's also built on top of jQuery and underscore.js. Bonus!
I'm definitely gonna be digging into Backbone and I'll write up my findings and some examples as I go along.
Hark! He Posts.
What's this? Has hell frozen over?
Not exactly. But I am resurrecting my blog. I've included all the old content here to keep some kind of continuity.
I'll be posting my more technical oriented material here. For illustrations, sketchs and other fun stuff, I'll be posting that to zoodotcom.com.
Lately I've been moving more to the JavaScript world, an already familiar place for me. So expect to see some posts about Node.js, Backbone.js, CoffeeScript and other things that catch my fancy.
Cheers!
Wednesday, September 2, 2009
Coming Soon: TaskTHIS² (A Titanium Application)
In the next week or so, I'll be releasing the first beta of TaskTHIS2.
Unlike the original, this version is a cross-platform desktop application. It's being distributed on the Titanium platform.
Over the next few weeks I'll discuss the technology behind it (all WebKit based: HTML/JS/CSS). And some of the libraries I built to make development easier (including my build script and a JavaScript class library called Klass.js). So be on the lookout for that!
Tuesday, May 12, 2009
Plausible Deniability
I’m a fan of the NBA… Most of the time. But I swear, the officiating has been horrible this year. Not just for my team, but all over. It’s enough to make you wonder.
Check out my speculation of what goes on in the NBA league offices.
Thursday, December 11, 2008
Liquid.js, A Non-Evaling Template Engine in JavaScript
Of late, I’ve needed a robust JavaScript template engine that doesn’t use eval (and preferably not with). Also, I’ve always liked Liquid.
So, two plus two equals… JavaScript! Hah, you thought it was four, didn’t you? Well, that’s what you get for thinking.
Yeah, long story short, I ported Liquid to JavaScript. You can grab it from github (where else?) here: http://github.com/darthapo/liquid.js
I’m calling this version 0.1. It’s not battle tested yet, but I know it’ll work using Firefox 3+, Safari 3+, and Adobe Air 1.1.
So what are the differences between running the Ruby version and the JavaScript version? Well, as far as the templates themselves, nothing. It’s a full port, so all of Liquid’s default tags and filters are supported in Liquid.js. I even added a placeholder function that you can implement yourself (based on your own needs) to support the ‘include’ tag.
Liquid.readTemplateFile = function(path) {
var elem = $(path);
if(elem) {
return elem.innerHTML;
} else {
return path +" can't be found.";
// Or throw and error, or whatever you want...
}
}
var src = "{% include 'myOtherTemplate' with current_user %}";
var tmpl = Liquid.parse( src );
alert( tmpl.render({ current_user:'M@' }));
An easy way to include template in a page is to use script elements, like this:
<!-- Browsers ignore script blocks with an unrecognized type. -->
<!-- Makes for pretty good inline template storage. -->
<script type="text/liquid" id="myOtherTemplate">
Hello, {{ current_user }}!
</script>
Currently, Liquid.js requires MooTools. Some of the things I’d like to polish up:
- Remove MooTools requirement, run entirely independent of any other js library
- Add Rhino for console-based testing
- Test, test, test on Internet
ExploderExplorer
Sunday, November 30, 2008
TaskTHIS is Back!
TaskTHIS is up and running again. Sorry for the downtime, but since I host it for you guys for free, I don't think you can complain too much. :-)
In the process, it's moved URLs as well. You can now find it at:
http://taskthis.elucidata-apps.com
The old URL (taskthis.darthapo.com) should redirect you there automatically.
I had to convert TaskTHIS from Rails 1.1 to Rails 2.1, which was not too fun. Especially since it had already been converted from pre-1.0 to 1.1.
Anyway, it's probably still a little rough around the edges, so if you find any bugs please add a post in the support forum.
Wednesday, November 19, 2008
Shuffling Servers
I'm in the process of shuffling servers around, so a lot of the apps/sites I've created will be down whilst I make the move.
Some things are already back up: ZooDotCom and Lil Monstas. Others, like Maelstrom and TaskTHIS are not.
In fact, TaskTHIS may take a little while longer to bring back up. I'm going to update it to Rails 2. That having been said, I did make a backup of the database before bringing it down. So when it returns all of your data will still be there.
Also, I've noticed that I post a lot fewer blog posts these days since I generally vent on my Twitter account, I wind up having not too much left to say. Funny that.
Wednesday, January 23, 2008
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!
Friday, January 18, 2008
Animating NSViews In RubyCocoa
Yesterday we talked about how to make nifty selectable toolbars like this:
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
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:
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.
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!
Wednesday, January 16, 2008
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 :selectPrefPaneldo|sender|# We'll do stuff here later...
endThen in Interface Builder, create the toolbar and the toolbar items. For each toolbar item:
- Turn off the 'autovalidates' option
- Set the action to target the
selectPrefPanel:action on your window controller (probably the File's Owner)
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:
deftoolbarSelectableItemIdentifiers(toolbar)@toolbaridents||=begin
window.toolbar.toolbaritems.collect {|i| i.itemIdentifier }endendLastly, when the window loads, select the first toolbar item:
defawakeFromNib
window.toolbar.selectedItemIdentifier = window.toolbar.toolbarItems[0].itemIdentifier
endViola! 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.
Tuesday, January 15, 2008
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". :-)
Monday, January 14, 2008
Snippet: Compiling XIBs into NIBs in your Nukefile
Here's a function I use in my Nukefile to compile XIBs into NIBs:
(functioncompile-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$"))
(elsenil))) (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
Thursday, October 25, 2007
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…
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.
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.
Friday, August 17, 2007
Friday, June 29, 2007
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
ComatoseControllerandComatoseAdminControllerclasses - Fixed the
ComatoseController#showaction 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_pathto ” instead of null - Formalized
ComatoseDrops. UseComatose.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_setupblock that gets called after Comatose is setup (in theDispatcher#to_prepareevent) - Added HTML comment output for calls that result in a
method_missingon theProcessingContext - 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
Monday, February 19, 2007
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
<html>
<head>
<title>Layout Example</title>
</head>
<body>
<div id="header"><!-- shared header code --></div>
<%= render :partial=>"layouts/#{controller.sub_layout}" %>
<div id="footer><!-- shared footer code --></div>
</body>
</html>
Note: I’m getting the partial name from a controller method named sub_layout. I’ll explain that bit in just minute—In the meantime, knowing that it’ll return either ‘public’ or ‘admin’ is enough.
You’ll notice I don’t have a call to <%= yield %> in the layout itself…
So then, your partials (or sub layouts) will look something like this:
app/views/layouts/_public.rhtml
<div id="tabs"><!-- public tabset --></div> <div id="public-content"> <div id="sidebar"> <!-- sidebar content here --> </div> <%= yield %> </div>
app/views/layouts/_admin.rhtml
<div id="tabs"><!-- admin tabset --></div> <div id="admin-content"> <%= yield %> </div>
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…
Tuesday, January 30, 2007
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.includesis an array of module names (as symbols) that will be included in theComatoseController.comatose.helpersis 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
- DEFERCOMATOSELOAD 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 << :authentiationsystem; Comatose.config.authorization = :requirelogin
- The automatic loading of RAILS_ROOT/lib/comatose/*.rb files is NO longer supported.
- In addition to mapping comatoseroot in your routes.rb, you’ll want to map.comatoseadmin as well
Obviously, if you have any issues with this version, please let me know.