require 'cells' require 'onfire' require 'hooks' require 'apotomo/event' require 'apotomo/widget_shortcuts' require 'apotomo/rails/view_helper' require 'apotomo/rails/controller_methods' # FIXME. require 'apotomo/widget/tree_node' require 'apotomo/widget/event_methods' require 'apotomo/widget/javascript_methods' module Apotomo # == Accessing Parameters # # Apotomo tries to prevent you from having to access the global #params hash. We have the following # concepts to retrieve input data. # # 1. Configuration values are available both in render and triggered states. Pass those in #widget # when creating the widget tree. Use #options for reading. # # has_widgets do |root| # root << widget(:mouse_widget, 'mum', :favorites => ["Gouda", "Chedar"]) # # and read in your widget state # # def display # @cheese = options[:favorites].first # # 2. Request data from forms etc. is available through event.data in the triggered states. # Use the #[] shortcut to access values directly. # # def update(evt) # @cheese = Cheese.find evt[:cheese_id] class Widget < Cell::Rails DEFAULT_VIEW_PATHS = [File.join('app', 'widgets')] include Hooks # Use this for setup code you're calling in every state. Almost like a +before_filter+ except that it's # invoked after the initialization in #has_widgets. # # Example: # # class MouseWidget < Apotomo::Widget # after_initialize do # @cheese = Cheese.find options[:cheese_id] # end define_hook :after_initialize define_hook :has_widgets attr_writer :visible include TreeNode include Onfire include EventMethods include WidgetShortcuts include JavascriptMethods helper Apotomo::Rails::ViewHelper helper Apotomo::Rails::ActionViewMethods abstract! undef :display # We don't want #display to be listed in #internal_methods. attr_reader :name alias_method :widget_id, :name attr_reader :options after_initialize do run_hook :has_widgets, self end def initialize(parent, id, options={}) super(parent) # TODO: do that as long as cells do need a parent_controller. @options = options @name = id @visible = true setup_tree_node(parent) run_hook :after_initialize, self end def parent_controller # i hope we'll get rid of any parent_controller dependency, soon. root? ? @parent_controller : root.parent_controller end def visible? @visible end # Invokes +state+ and hopefully returns the rendered content. def invoke(state, *args) return render_state(state, *args) if method(state).arity != 0 # TODO: remove check and make trigger states receive the evt default. render_state(state) end # Renders and returns a view for the current state. That's why it is usually called at the end of # a state method. # # ==== Options # * See http://rdoc.info/gems/cells/Cell/Rails:render # # Example: # class MouseWidget < Apotomo::Widget # def eat # render # end # # render the view eat.haml. # # render :text => "alert('SQUEAK!');" # # issues a squeaking alert dialog on the page. def render(*args, &block) super end # Returns the widget named +widget_id+ if it's a descendent or self. def find_widget(widget_id) find {|node| node.name.to_s == widget_id.to_s} end def address_for_event(type, options={}) options.reverse_merge! :source => name, :type => type, :controller => parent_controller.controller_path # DISCUSS: dependency to parent_controller. end def url_for_event(type, options={}) apotomo_event_path address_for_event(type, options) end def self.controller_path @controller_path ||= name.sub(/Widget$/, '').underscore unless anonymous? end # Renders the +widget+ (instance or id). def render_widget(widget_id, state=:display, *args) if widget_id.kind_of?(Widget) widget = widget_id else widget = find_widget(widget_id) or raise "Couldn't render non-existent widget `#{widget_id}`" end widget.invoke(state, *args) end end end