lib/bowline/binders.rb in bowline-0.5.3 vs lib/bowline/binders.rb in bowline-0.5.4

- old
+ new

@@ -1,25 +1,89 @@ module Bowline module Binders + # 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. 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 - def js_invoke(window, method, *args) + def js_invoke(window, method, *args) #:nodoc: if method == :setup setup(window) else send(method, *args) end @@ -27,107 +91,131 @@ 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: - # expose Post + # 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 exposed class is created/updated/deleted + # 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 - def expose(klass) + # 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)) @klass.after_update(method(:updated)) @klass.after_destroy(method(:removed)) end - # Returns class set by the 'expose' method + # Returns class set by the 'bind' method def klass - @klass || raise("klass not set - see expose method") + @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: - # jquery.getJSON("http://example.com") + # Javascript proxy to jQuery. + # See Bowline::Desktop::Proxy for more information. + # Example: + # jquery.getJSON("http://example.com").call def jquery page.jQuery end # See Bowline::logger def logger Bowline::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( name, format_event(event), @@ -150,27 +238,27 @@ name.is_a?(Array) ? name.join('.') : name.to_s end end - + + # jQuery element object attr_reader :element - attr_reader :item - # Instance of an element on the view. - # - # item.destroy - # element.highlight.call + # Instance of the bound class' record + attr_reader :item + def initialize(id, *args) #:nodoc: @element = self.class.bowline.element( self.class.name, id ) @item = self.class.find(id) end protected - # Trigger jQuery events on this element: + # Trigger jQuery events on this element. + # Example: # trigger(:highlight) def trigger(event, data = nil) element.trigger( self.class.format_event(event), data @@ -182,10 +270,10 @@ self.class.removed(item) end # Shortcut methods - # See self.class.js + # See self.class.page def page self.class.page end # See self.class.jquery \ No newline at end of file