lib/bowline/binders.rb in bowline-0.4.6 vs lib/bowline/binders.rb in bowline-0.5.0

- old
+ new

@@ -1,166 +1,203 @@ module Bowline module Binders class Base + extend Bowline::Watcher::Base + extend Bowline::Desktop::Bridge::ClassMethods + js_expose + class << self - # See Bowline::js - def js - Bowline::js + def windows + @windows ||= [] end - - # Equivalent of the 'jQuery' function - def jquery - @@jquery ||= JQuery.new + + # Called by JS when first bound + def setup(window) + self.windows << window + self.items = all + true end - - # See the Observer class - def observer - @observer ||= Observer.new - end - # See Bowline::logger - def logger - Bowline::logger + def js_invoke(window, method, *args) + if method == :setup + setup(window) + else + send(method, *args) + end end - - # See Bowline::show_view - def show_view(*args) - Bowline::show_view(*args) + + def instance_invoke(id, meth, *args) #:nodoc: + self.new(id).send(meth, *args) end - def params - @params + def find(id) + klass.find(id) end - - def params=(p) #:nodoc: - case p - when String - # Params comes in a string (since it's a - # serialized form) - we need to make it into - # a nestled hash. Stolen from Ramaze - m = proc {|_,o,n|o.merge(n,&m)} - @params = params.inject({}) do |hash, (key, value)| - parts = key.split(/[\]\[]+/) - hash.merge(parts.reverse.inject(value) { |x, i| {i => x} }, &m) - end - else - @params = p - end + + def all + klass.all end - def elements - @elements + def items=(items) #:nodoc: + bowline.populate(name, items.to_js).call end - def setup(d) #:nodoc: - @elements ||= [] - @elements << d - self.item_sync! + def created(item) #:nodoc: + bowline.created( + name, + item.id, + item.to_js + ).call end - def trigger(event, data = nil) - @elements ||= [] - @elements.map {|e| - e.trigger(format_event(event), data) - } + def updated(item) #:nodoc: + bowline.updated( + name, + item.id, + item.to_js + ).call end - def loading(&block) - trigger(:loading, true) - yield - trigger(:loading, false) + def removed(item) #:nodoc: + 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 + # + # When the exposed 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) + # + # klass instance needs to respond to: + # * id + # + # You can override .to_js on the model instance + # in order to return specific attributes for the view + def expose(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 + def klass + @klass || raise("klass not set - see expose method") + end + + # JavaScript proxy to the page: + # 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: + # bowline.log("msg").call + def bowline + page.Bowline + end - def instance(el) #:nodoc: - self.new(el).method(:send) - end - - def inherited(child) #:nodoc: - return if self == Bowline::Binders::Base - return if child == Bowline::Binders::Singleton - return if child == Bowline::Binders::Collection - name = child.name.underscore - name = name.split('/').last - js.send("bowline_#{name}_setup=", child.method(:setup)) - js.send("bowline_#{name}_instance=", child.method(:instance)) - js.send("bowline_#{name}=", child.method(:send)) - end + # Javascript proxy to jQuery: + # jquery.getJSON("http://example.com") + def jquery + page.jQuery + end - def format_event(name) #:nodoc: - name.is_a?(Array) ? - name.join('.') : - name.to_s - end + # See Bowline::logger + def logger + Bowline::logger + end + + # Trigger events on all elements + # bound to this binder: + # trigger(:reload, {:key => :value}) + def trigger(event, data = nil) + bowline.trigger( + name, + format_event(event), + data + ).call + end + + # Helper method to trigger a loading + # event either side of a block: + # loading { + # # Slow code, e.g. http call + # } + def loading(&block) + trigger(:loading, true) + yield + trigger(:loading, false) + end + + def format_event(name) #:nodoc: + name.is_a?(Array) ? + name.join('.') : + name.to_s + end end attr_reader :element attr_reader :item - - def initialize(element, *args) #:nodoc: - # jQuery element - @element = element - # Calling chain.js 'item' function - @item = element.item() - if @item - # If possible, find Ruby object - @item = self.class.find(@item._id.to_i) - end + + # Instance of an element on the view. + # + # item.destroy + # element.highlight.call + def initialize(id, *args) #:nodoc: + @element = self.class.bowline.element( + self.class.name, id + ) + @item = self.class.find(id) end - - # Trigger jQuery events on this element - def trigger(event, data = nil) - self.element.trigger( - self.class.format_event(event), - data - ) - end + + protected + # Trigger jQuery events on this element: + # trigger(:highlight) + def trigger(event, data = nil) + element.trigger( + self.class.format_event(event), + data + ).call + end + + # Remove element from the view + def remove! + self.class.removed(item) + end - # Raw DOM element - def dom - self.element[0] - end + # Shortcut methods - # Shortcut methods + # See self.class.js + def page + self.class.page + end - # See self.class.show_view - def show_view(*args) - self.class.show_view(*args) - end - - # See self.class.js - def js - self.class.js - end - alias :page :js - - # See self.class.jquery - def jquery - self.class.jquery - end - - # See self.class.observer - def observer - self.class.observer - end + # See self.class.jquery + def jquery + self.class.jquery + end - # See self.class.logger - def logger - self.class.logger - end - - private - # This is just a unique identifier - # for the item - and isn't - # used in the dom - def item_id - if item.respond_to?(:dom_id) - item.dom_id - else - [ - item.id, - self.class.name.underscore - ].join("_") - end + # See self.class.logger + def logger + self.class.logger end end end end \ No newline at end of file