From apotonick at gmail.com Wed Aug 5 10:15:15 2009 From: apotonick at gmail.com (Nick Sutterer) Date: Wed, 5 Aug 2009 16:15:15 +0200 Subject: [Cells-talk] what about components? In-Reply-To: References: Message-ID: hi maurizio, i can see some attempt to define a DSL in your code. is it your intention to describe the widget tree composition in an easier language? i like the idea of simplifying the current composition API. defining local params for a branch is already covered by the DomainWidget (which needs refactoring) - maybe we can move that in the DSL as you proposed. did i get your idea? cheers, nick On Sun, Jul 26, 2009 at 1:40 PM, maurizio de magnis wrote: > Hello everybody, maybe there's something like this already built in in > apotomo but, of course, my lack of knowledge about the state of the > art of apotomo made me do the following. > > Previously I used to do this kind of things: > > (A) http://pastie.org/559314 > > That's a simple widget which purpose is to enable an ajaxly selection > of cities by countries: the view has one initial drop down select > showing the available countries, and when the user triggers the change > event on that form element, an apotomo event gets triggered too, > showing a new drop down select containing the cities of the selected > country. > > Now I've tried to abstract the widget tree creation process, keeping > in mind the "skinny controller" dogma the controller: > > (replace city/country with action/container) > > (B) http://pastie.org/559322 > > basically the idea I've tried to implement is that imho it's useful to > hide apotomo's composition logic, the user should only implement > widget's states, transition rules and the widget tree structure. > Everything else should be managed by the library. for example, it > should be better to remove these from the widget's cell definition: > > ? ?CompositionRules[:required_options].each do |required_option| > ? ? ? ?attr_accessor required_option.to_sym > ? ?end > > ? ?def root_container > ? ? ? ?yield self > ? ? ? ?CompositionRules[:required_options].each do |option| > ? ? ? ? ? ?set_local_param option.to_sym, @opts[option.to_sym] > ? ? ? ?end > ? ?end > > because every Component would always use them (by class inheritance?). > > I'd also like to implement the use of another apotomo component as a > children, maybe nick can show a simpler way to accomplish this ;) > > > Maurizio > > if you don't like pastie, here are the sources of the previous links: > > ---------------------------------------------------------------------- > (A) > > RAILS CONTROLLER > > class SelectionsController < ApplicationController > ? ?include Apotomo::ControllerMethods > ? ?def compose > ? ? ? ?@current_city = City.find(params[:id]) > ? ? ? ?use_widgets do |root| > ? ? ? ? ? ?root << selection = cell(:location, :manage, 'selection') > ? ? ? ? ? ?selection << cell(:location, :init_countries, 'countries') > ? ? ? ? ? ?selection << cell(:location, :init_cities, 'cities') > ? ? ? ? ? ?selection.watch(:selection, 'cities', :update_cities, nil) > ? ? ? ?end > ? ? ? ?@selection = render_widget 'selection' do |cell| > ? ? ? ? ? ?cell.current_city = @current_city > ? ? ? ?end > ? ?end > end > > APOTOMO CELL > > class LocationCell < Apotomo::StatefulWidget > ? ?attr_accessor :current_city > > ? ?transition :from => :display_cities, :to => :update_cities > > ? ?def manage > ? ? ? ?yield self > ? ? ? ?set_local_param :selected_city, @opts[:current_city] > ? ?end > > ? ?def init_cities > ? ? ? ?@cities = [] > ? ? ? ?jump_to_state :display_cities > ? ?end > > ? ?def init_countries > ? ? ? ?@countries = Country.all > ? ? ? ?jump_to_state :display_countries > ? ?end > > ? ?def display_cities > ? ? ? ?render > ? ?end > > ? ?def display_countries > ? ? ? ?render > ? ?end > > ? ?def update_cities > ? ? ? ?@cities = City.find_all_by_country_id(param(:country)) > ? ?end > end > > (B) > > RAILS CONTROLLER > > class CellActionsController < ResourceController::Base > ? ?include Apotomo::ControllerMethods > ? ?include ApotomoComponents > > ? ?edit.before do > ? ? ? ?@cell_selection = render_apotomo_component(:cell_selection, > {:current_object => @object}) > ? ?end > end > > /lib/apotomo_components.rb > > module ApotomoComponents > ? ?def render_apotomo_component(apotomo_component, options = {}) > ? ? ? ?klass = (apotomo_component.to_s + "_cell").classify.constantize > ? ? ? ?composition_rules = klass::CompositionRules > ? ? ? ?for required_option in composition_rules[:required_options] do > ? ? ? ? ? ?raise "not enough options" unless options.keys.include? > required_option > ? ? ? ?end > ? ? ? ?root_container_id = composition_rules[:root_container_id] > ? ? ? ?use_widgets do |root| > ? ? ? ? ? ?root << cell_container = cell(apotomo_component, > :root_container, root_container_id ) > ? ? ? ? ? ?compose_widget_tree(apotomo_component, cell_container, > composition_rules[:widget_tree]) > ? ? ? ? ? ?for event_handler in composition_rules[:event_handlers] do > ? ? ? ? ? ? ? ?cell_container.watch(event_handler[:event], > event_handler[:target_cell_id], event_handler[:invoked_state], nil) > ? ? ? ? ? ?end > ? ? ? ?end > ? ? ? ?rendered_output = render_widget(root_container_id) do |root_cell| > ? ? ? ? ? ?options.each do |key, value| > ? ? ? ? ? ? ? ?root_cell.send(key.to_s + "=", value) > ? ? ? ? ? ?end > ? ? ? ?end > ? ? ? ?return rendered_output > ? ?end > > ? ?def compose_widget_tree(apotomo_component, cell_container, children) > ? ? ? ?for child in children do > ? ? ? ? ? ?cell_container << cell(apotomo_component, child[:method], > child[:id]) > ? ? ? ? ? ?if child.has_key? :children > ? ? ? ? ? ? ? ?compose_widget_tree(apotomo_component, cell_container, > child[:children]) > ? ? ? ? ? ?end > ? ? ? ?end > ? ?end > end > > APOTOMO CELL > > class CellSelectionCell < Apotomo::StatefulWidget > =begin > - interaction layer > > * TODO learn how to do plugins -.-' > > * TODO/DISCUSS get from YAML > ? ?where? > ? ? ? ?/app/cells/my_cell_name.yml > ? ? ? ?/app/cells/my_cell_name/composition_rules.yml > > * TODO add :component parameter inside the children and render a complete > component as a child. > > * widget_tree structure allows infinte depth levels: > ? ?:widget_tree => [{ > ? ? ? ?:method => :method_0, > ? ? ? ?:id => "id_for_method_0" > ? ? ? ?:children => [{ > ? ? ? ? ? ?:method => :method_0_0, > ? ? ? ? ? ?:id => "id_for_method_0_0" > ? ? ? ?}, { > ? ? ? ? ? ?:method => :method_0_1, > ? ? ? ? ? ?:id => "id_for_method_0_1" > ? ? ? ?}]}, { > ? ? ? ?:method => :method_1 > ? ? ? ?:id => "id_for_method_1" > ? ?}] > that will map this tree structure: > ? ?method_0 > ? ? ? ?method_0_0 > ? ? ? ?method_0_1 > ? ?method_1 > > * every component will have a root_container that will > make passed params accessible to its children. > =end > > ? ?CompositionRules = { > ? ? ? ?:required_options => [:current_object], > ? ? ? ?:root_container_id => 'cell_selection', > ? ? ? ?:widget_tree => [{ > ? ? ? ? ? ?:method => :init_actions, > ? ? ? ? ? ?:id => "cell_actions" > ? ? ? ? ? ?}, { > ? ? ? ? ? ?:method => :init_containers, > ? ? ? ? ? ?:id => "cell_containers" > ? ? ? ?}], :event_handlers => [{ > ? ? ? ? ? ?:event => :selection, > ? ? ? ? ? ?:target_cell_id => "cell_actions", > ? ? ? ? ? ?:invoked_state => :update_actions > ? ? ? ?}] > ? ?} > > ? ?# code shared among apotomo components > ? ?# TODO pack these into external functions > ? ?CompositionRules[:required_options].each do |required_option| > ? ? ? ?attr_accessor required_option.to_sym > ? ?end > > ? ?def root_container > ? ? ? ?yield self > ? ? ? ?CompositionRules[:required_options].each do |option| > ? ? ? ? ? ?set_local_param option.to_sym, @opts[option.to_sym] > ? ? ? ?end > ? ?end > > CUSTOM WIDGET DEFINITION > > ? ?transition :from => :display_actions, :to => :update_actions > > ? ?def init_actions > ? ? ? ?# do something here > ? ? ? ?jump_to_state :display_actions > ? ?end > > ? ?def init_containers > ? ? ? ?# do something here > ? ? ? ?jump_to_state :display_containers > ? ?end > > ? ?def display_actions > ? ? ? ?render > ? ?end > > ? ?def display_containers > ? ? ? ?render > ? ?end > > ? ?def update_actions > ? ? ? ?# do something here > ? ? ? ?jump_to_state :display_actions > ? ?end > end > ---------------------------------------------------------------------- > _______________________________________________ > Cells-talk mailing list > Cells-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/cells-talk >