# This module makes sure that all Widgets that can be found in Rtml.configuration.widget_paths have been loaded, and # then registers Widget functionality with whatever class included Rtml::Widgets. Note that while merely including this # module does allow your class to _support_ Widgets, the Widget itself must declare that it is meant to affect your # object. # # You can interface with existing Widgets (assuming they support your class at a more fundamental level) by defining # a self.name method which returns the name of one of the elements the Widget in question affects. This definition must # appear _before_ the inclusion of Rtml::Widgets. # module Rtml::Widgets class << self # A list of all objects that have included Rtml::Widgets, marking them as compatible with Widgets. attr_reader :bases # The Widget mapping is a hash whose keys are symbols and whose values are instances of Module. The symbols # represent either RTML models or TML tag names, while the Modules represent the proxy from the representation's # class into the Widget itself. This way, the Module can be mixed-in with the target, and Widgets added after # the fact can simply add methods to the mixed-in Module. These methods are automatically added to the target # class by Ruby. It's a really fancy version of doing something like this: # mixin = Module.new # mixin.send(:define_method, :say_hi) { puts 'hello' } # class Target; end # Target.send(:include, mixin) # mixin.send(:define_method, :say_bye) { puts 'goodbye' } # notice this happens AFTER it's mixed in! # # target_instance = Target.new # target_instance.say_hi # => hello # target_instance.say_bye # => goodbye def mapping(key) name = (key.respond_to?(:name) ? key.name : key.to_s).underscore @widget_mapping ||= {} @widget_mapping[name] ||= Rtml::WidgetCore::WidgetProxy.new end # Returns all Widgets which affect the specified target. def affecting(target) target = (target.respond_to?(:name) ? target.name : target.to_s).underscore mapping(target).proxied_widgets end def included(base) @bases ||= [] @bases << base create_widgets_array(base) base.send(:include, Rtml::WidgetCore::WidgetAccessorInstanceMethods) base.send(:extend, Rtml::WidgetCore::WidgetAccessorClassMethods) load_widgets add_proxies_to_base(base) base.class_eval do if defined?(initialize) def initialize_with_widgets(*args, &block) initialize_without_widgets(*args, &block) Rtml::Widgets.add_proxies_to_base(self) end alias_method_chain :initialize, :widgets else def initialize Rtml::Widgets.add_proxies_to_base(self) end end end end # Mixes the various proxy methods into the specified base. def add_proxies_to_base(base) target_name = base.respond_to?(:name) ? (base.name || base.class.name).underscore.sub(/^.*\//, '') : nil (@widget_mapping ||= {}).each do |key, mixin| klass = base if base.kind_of?(ActiveRecord::Base) || base.kind_of?(Rtml::DocumentModelObject) klass = class << base; self; end end if klass.respond_to?(:name) && key == target_name klass.send(:include, mixin) end end end def load_widgets(load_path = nil) if load_path.nil? load_widgets(File.join(Rtml.root, 'builtin/widgets')) Rtml.configuration.widget_paths.each do |lp| if defined?(Rails) && defined?(Rails.root) lp = File.join(Rails.root, lp) unless lp =~ /^#{Regexp::escape Rails.root}/ || lp =~ /^C\:/ || lp =~ /\// load_widgets(lp) else load_widgets(lp) end end else if File.file?(load_path) load_widget!(load_path) else if Rtml.configuration.widget_logging_enabled Rails.logger.debug "Searching for Widgets in '#{load_path}/**/*.rb'..." end Dir.glob(File.join(load_path, "**/*.rb")).each do |fi| next if fi =~ /\.svn/ or File.directory?(fi) load_widget!(fi) end end end end def load_widget!(file) if Rtml.configuration.widget_logging_enabled begin result = require_dependency(file) Rails.logger.debug "Loading Widget: #{file} (#{result})" rescue Rails.logger.debug "Loading Widget: #{file} (#{$!.message})" raise $! end else require_dependency file end end def create_widgets_array(base) base.instance_eval do class_inheritable_array :class_widgets read_inheritable_attribute(:class_widgets) || write_inheritable_attribute(:class_widgets, []) end end end end