Skip to content

Commit

Permalink
Add extensive documention to binders and bowline.js
Browse files Browse the repository at this point in the history
  • Loading branch information
maccman committed Dec 22, 2009
1 parent b5617fd commit 3b5712b
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 33 deletions.
115 changes: 112 additions & 3 deletions assets/bowline.js
@@ -1,3 +1,112 @@
/*
Bowline JavaScript API
This library lets you call Ruby methods, and bind up elements.
It requires jQuery and Chain.js:
http://jquery.com
http://github.com/raid-ox/chain.js
= Functions
invoke(klass, method, *args)
Invoke a class method on a particular class. Usually
used to invoke methods on a binder. The class needs to
be exposed to JS (using the Bowline::Desktop::Bridge#js_expose).
Usage:
Bowline.invoke('MyClass', 'my_method');
instanceInvoke(klass, id, method, *args)
Invoke an instance method an a binder.
Usually called via the jQuery helper functions.
Usage:
Bowline.instanceInvoke('UsersBinder', 1, 'charge!');
windowInvoke(method, *args)
Invoke class method on this window's class.
Usage:
Bowline.windowInvoke('close');
helper(method, *args)
Invoke a method defined in any helper.
bindto(element, klass, options = {})
Bind a element to a Bowline binder.
Usually called via the jQuery helper functions.
Usage:
Bowline.bindto('#users', 'UsersBinder');
The options can either be a template hash:
{
'.name .first': {
style: 'color: blue;',
content: 'First Name: {first}'
},
'.name .last': 'Family Name: {last}',
'.address': function(data, el){
if(!data.address)
el.hide();
return data.address;
},
builder: function(){
var data = this.item();
this.find('.name').click(function(){alert(data.name)});
}
}
Or the options can be a builder function:
(function(){
this.bind('click', function(){
var data = this.item();
alert(data);
})
For more documentation, look at the Chain.js library:
http://wiki.github.com/raid-ox/chain.js/elementchain
= Filtering items
$('#users').items('filter', 'value');
= Sorting items
$('#users').items('sort', 'first_name');
= Update events
$('#users').update(function(){
//...
});
= JQuery functions
These are how you usually bind elements, or invoke a binders class/instance methods.
$.fn.bindto(klass, options)
Associate an an element with a Bowline binder.
Example:
$("#users").bindto('UsersBinder');
$.fn.invoke(method, *args)
Invoke a class/instance method on a Bowline binder.
If called on the bound element, in this example the #users div, then a class method
will be called on the binder.
Example:
$("#users").invoke("my_class_method", "arg1");
If called on a item inside a bound element, an instance method will be called.
Example:
$("#users").items(10).invoke("my_instance_method");
= Using other libraries (e.g. Prototype)
Although this library requires jQuery, its API is not jQuery
specific. It's perfectly feasible to rewrite to use Prototype instead.
Additionally, jQuery plays nicely with other libraries using it's noConflict() method.
So you're still free to use other JavaScript libraries without fear of conflicts.
*/

var Bowline = {
msgs: [],
callbacks: {},
Expand Down Expand Up @@ -54,7 +163,7 @@ var Bowline = {
Bowline.invoke(args);
},

bind: function(el, klass, options){
bindto: function(el, klass, options){
el = jQuery(el);
el.chain(options);
el.data('bowline', klass);
Expand Down Expand Up @@ -182,10 +291,10 @@ var Bowline = {
}
};

$.fn.bowline = function(){
$.fn.bindto = function(){
var args = $.makeArray(arguments);
args.unshift(this);
Bowline.bind.apply(this, args);
Bowline.bindto.apply(this, args);
};
})(jQuery);

Expand Down
149 changes: 119 additions & 30 deletions lib/bowline/binders.rb
@@ -1,21 +1,83 @@
module Bowline
module Binders
# TODO
# Binders are a central part of Bowline. They perform two main functions:
# 1) Bind a model to the view, so any changes to the model get automatically
# reflected in the view.
# 2) View abstraction of the model. You can define view specific class & instance
# methods, and easily call them from bound JavaScript objects.
#
# To use a binder, you first need to bind it to a model using the bind method.
# Example:
# class UsersBinder < Bowline::Binders::Base
# bind User
# end
#
# Once a class is bound, any updates to the model automatically update any bound HTML.
# The class names in the HTML are tied to the model's attribute names.
# You can bind HTML using the bowline.js bindup function.
# Example:
# <div id="users">
# <div class="item">
# <span class="name"></span>
# </div>
# </div>
# <script>
# $("#users").bindup('UsersBinder');
# </script>
#
# =Class methods
#
# You can define class methods on your binder, and call them using JavaScript
# using the invoke function on the bound HTML element.
# Example:
# <script>
# var users = $("#users").bindup('UsersBinder');
# users.invoke("method_name", "arg1", "arg2")
# </script>
#
# =Instance methods
#
# You can call your binders instance method from JavaScript by calling the invoke
# function on the generated HTML elements. Your binder's instance methods have access
# to an 'element' variable, which is the jQuery element, and a 'item' variable, which
# is the bound model's instance record.
#
# Example:
# class UsersBinder < Bowline::Binders::Base
# bind User
# def charge!
# #...
# end
# end
#
# <script>
# $('#users').items(10).invoke('charge!');
# </script>
#
# For more documentation on Bowline's JavaScript API, see bowline.js
class Base
extend Bowline::Watcher::Base
extend Bowline::Desktop::Bridge::ClassMethods
js_expose

class << self
# An array of window currently bound
# An array of window currently bound.
def windows
@windows ||= []
end

# Called by JS when first bound
def setup(window)
# Called by a window's JavaScript whenever that window is bound to this Binder.
# This method populates the window's HTML with all bound class' records.
# Override this if you don't want to send all the class' records to the window.
# Example:
# def setup(window)
# super(window, last_10_tweets)
# end
def setup(window, items = all)
self.windows << window
self.items = all
window.bowline.populate(
name, items.to_js
).call
true
end

Expand All @@ -31,64 +93,81 @@ def instance_invoke(id, meth, *args) #:nodoc:
self.new(id).send(meth, *args)
end

# Calls .find on the klass sent to the bind method.
# This is used internally, to find records when the page
# invoke instance methods.
def find(id)
klass.find(id)
end


# Calls .all on the klass sent to the bind method.
# This method is called internally by the setup method.
def all
klass.all
end

def items=(items) #:nodoc:
# Set the binder's items. This will replace all items, and update the HTML.
def items=(items)
bowline.populate(name, items.to_js).call
end

def created(item) #:nodoc:
# Add a new item to the binder, updating the HTML.
# This method is normally only called internally by
# the bound class's after_create callback.
def created(item)
bowline.created(
name,
item.id,
item.to_js
).call
end

def updated(item) #:nodoc:
# Update an item on the binder, updating the HTML.
# This method is normally only called internally by
# the bound class's after_update callback.
def updated(item)
bowline.updated(
name,
item.id,
item.to_js
).call
end

def removed(item) #:nodoc:
# Remove an item from the binder, updating the HTML.
# This method is normally only called internally by
# the bound class's after_destroy callback.
def removed(item)
bowline.removed(
name,
item.id
).call
end

protected
# Associate the binder with a model
# to setup callbacks so changes to the
# model are automatically reflected in
# the view. Usage:
# Associate the binder with a model to setup callbacks so
# changes to the model are automatically reflected in the view.
# Example:
# bind Post
#
# When the bound class is created/updated/deleted
# the binder's callbacks are executed and the view
# updated accordingly.
#
# klass needs to respond to:
# * all
# * find(id)
# * after_create(method)
# * after_update(method)
# * after_destroy(method)
#
# Classes inheriting fromActiveRecord and Bowline::LocalModel are
# automatically compatable, but if you're using your own custom model
# you need to make sure it responds to the following methods:
# * all - return all records
# * find(id) - find record by id
# * after_create(method) - after_create callback
# * after_update(method) - after_update callback
# * after_destroy(method) - after_destroy callback
#
# klass instance needs to respond to:
# * id
# The klass' instance needs to respond to:
# * id - returns record id
# * to_js - return record's attribute hash
#
# You can override .to_js on the model instance
# in order to return specific attributes for the view
# You can override the to_js method on the model instance
# in order to return specific attributes for the view.
def bind(klass)
@klass = klass
@klass.after_create(method(:created))
Expand All @@ -101,21 +180,27 @@ def klass
@klass || raise("klass not set - see bind method")
end

# JavaScript proxy to the page:
# JavaScript proxy to the page.
# See Bowline::Desktop::Proxy for more information.
# Example:
# page.myFunc(1,2,3).call
def page
Bowline::Desktop::Proxy.new(
windows.length == 1 ? windows.first : windows
)
end

# JavaScript proxy to the Bowline object:
# JavaScript proxy to the Bowline object.
# See Bowline::Desktop::Proxy for more information.
# Example:
# bowline.log("msg").call
def bowline
page.Bowline
end

# Javascript proxy to jQuery:
# Javascript proxy to jQuery.
# See Bowline::Desktop::Proxy for more information.
# Example:
# jquery.getJSON("http://example.com").call
def jquery
page.jQuery
Expand All @@ -127,7 +212,8 @@ def logger
end

# Trigger events on all elements
# bound to this binder:
# bound to this binder.
# Example:
# trigger(:reload, {:key => :value})
def trigger(event, data = nil)
bowline.trigger(
Expand All @@ -154,8 +240,11 @@ def format_event(name) #:nodoc:
name.to_s
end
end


# jQuery element object
attr_reader :element

# Instance of the bound class' record
attr_reader :item

def initialize(id, *args) #:nodoc:
Expand Down
4 changes: 4 additions & 0 deletions lib/bowline/desktop/window_manager.rb
Expand Up @@ -83,6 +83,10 @@ def page
Proxy.new(window)
end

def bowline
page.Bowline
end

# Returns true if the both the HTML and JavaScript in
# this window have loaded. Use the on_load event to know
# when this window has loaded.
Expand Down

0 comments on commit 3b5712b

Please sign in to comment.