require 'opal' require 'opal-jquery' unless RUBY_ENGINE == 'opal' require 'tilt' require 'awesome_print' end require "base64" require 'roda/component/faye' require 'roda/component/instance' require 'roda/component/dom' require 'roda/component/events' if RUBY_ENGINE == 'opal' $component_opts ||= { events: {}, comp: {}, cache: {} } end class Roda class Component attr_accessor :scope def initialize(scope = false) @scope = scope if client? $faye.subscribe "/components/#{self.class._name}" do |msg| `window.console.log(#{msg})` end end end class << self attr_accessor :_name if RUBY_ENGINE == 'ruby' def inherited(subclass) super # We want to set the app for all sub classes subclass.set_app app end end def on_server &block if server? yield else m = Module.new(&block) m.public_instance_methods(false).each do |meth| define_method "#{meth}" do |*args, &blk| name = self.class._name event_id = "comp-event-#{$faye.generate_id}" Element['body'].on event_id do |event, local, data| blk.call local, event, data end $faye.publish("/components/outgoing/#{$faye.private_id}/#{$faye.public_id}", { name: name, type: 'event', event_type: 'call', event_method: meth, event_id: event_id, local: args.first || nil }) true end end include m end end # The name of the component def name _name @_name = _name.to_s if server? component_opts[:class_name][@_name] = self.to_s end end # The html source def html _html, &block if server? if _html.is_a? String cache[:html] = File.read _html else cache[:html] = yield end cache[:dom] = Nokogiri::HTML cache[:html] end end # setup your dom def setup &block block.call cache[:dom] if server? end alias :clean :setup def events @_events ||= Events.new self, component_opts, false end def on *args, &block events.on(*args, &block) end # cache for class def cache unless @_cache @_cache ||= Roda::RodaCache.new @_cache[:tmpl] = {} @_cache[:server_methods] = [] end @_cache end # set the current roda app def set_app app @_app = app.respond_to?(:new) ? app.new : app end # roda app method def app @_app ||= {} end # We need to save the nokogiri dom and the raw html. # the reason we ave the raw html is so that we can use it client side. def tmpl name, dom, remove = true cache[:tmpl][name] = { dom: remove ? dom.remove : dom } cache[:tmpl][name][:html] = cache[:tmpl][name][:dom].to_html cache[:tmpl][name] end alias :add_tmpl :tmpl alias :set_tmpl :tmpl # shortcut to comp opts def component_opts if server? app.component_opts else $component_opts end end def method_missing method, *args, &block if server && app.respond_to?(method, true) app.send method, *args, &block else super end end private def server? RUBY_ENGINE == 'ruby' end def client? RUBY_ENGINE == 'opal' end end def cache @_cache ||= self.class.cache.dup end def cache= new_cache @_cache = new_cache end def events @_events ||= Events.new self.class, component_opts, scope end def dom if server? @_dom ||= DOM.new cache[:dom].dup || begin Nokogiri::HTML cache[:html] end else @_dom ||= DOM.new(Element) end end # Grab the template from the cache, use the nokogiri dom or create a # jquery element for server side def tmpl name if t = cache[:tmpl][name] if server? DOM.new t[:dom].dup else DOM.new Element[t[:html].dup] end else false end end def component_opts self.class.component_opts end def trigger *args events.trigger(*args) end def method_missing method, *args, &block if server && scope.respond_to?(method, true) scope.send method, *args, &block else super end end private def server? RUBY_ENGINE == 'ruby' end alias :server :server? def client? RUBY_ENGINE == 'opal' end alias :client :client? end # This is just here to make things more cross compatible if RUBY_ENGINE == 'opal' RodaCache = Hash end end