require 'fileutils' require 'attributes' class Widget #--{{{ VERSION = '0.0.1' unless defined? Widget::VERSION def self.version() VERSION end class << self attribute('libdir'){ File.join RAILS_ROOT, 'lib', 'widgets' } def for_controller controller, name, *a, &b klass = Widget.load name returning(klass.new(*a, &b)){|widget| widget.controller = controller} end def load name lib = File.join libdir, "#{ name }.rb" RAILS_ENV == "development" ? Kernel.load(lib) : Kernel.require(lib) #RAILS_ENV == "development" ? eval(IO.read((lib))) : Kernel.require(lib) #eval(IO.read((lib))) begin const_for name rescue raise "wtf? Widget '#{ name }' was not defined in #{ lib }" end end def const_for name consts = name.camelize.split %r/::/ klass = Widget consts.each{|const| klass = klass.const_get(const)} klass end def new *a, &b returning( allocate ) do |obj| obj.instance_eval do @in_render = false initialize *a, &b end end end end FileUtils.mkdir_p libdir rescue nil attribute('name'){ self.class.name.gsub(%r/^.*Widget::/, '').underscore } attribute 'controller' attribute 'template' attribute 'show' => true attribute 'content' => '' def inspect "#{ name }=#{ super }" end def template "widgets/#{ name }" end def configure options = {} , &block options.to_options! has_attribute = attributes.inject({}){|h,k| h.update k.to_s => true, k.to_sym => true} options.each do |k,v| if has_attribute[k] send k, v else attribute k => v end end instance_eval &block if block self end def inherited_attributes ancestors = self.class.ancestors offset = ancestors.index Widget ancestors = ancestors[0, offset + 1].compact if offset ancestors.reverse.map do |ancestor| ancestor.attributes end.flatten.uniq end def to_hash inherited_attributes.inject({}){|h,a| h.update a.to_sym => send(a)} end def render options = {} raise "recursive render of #{ name }" if @in_render @in_render = true begin return '' unless show? widget = self template = widget.template locals = widget.to_hash.update options.to_options! locals[:widget] = locals['widget'] = locals[:w] = locals['w'] = widget controller.instance_eval do render_to_string :file => template, :use_full_path => true, :locals => locals end ensure @in_render = false end end alias_method "to_s", "render" def with_content content yield render end alias_method "for_content", "with_content" def [] k send k end def []= k, v send k, v end def widget name, *a, &b controller.widget name, *a, &b end def self.Class path, &block classes = path.camelize.split %r/::/ klass = Widget classes.each do |const| subklass = Class.new Widget klass.module_eval{ remove_const const if const_defined? const const_set const, subklass } klass = subklass end klass.module_eval &block klass end #--}}} end if defined?(Rails) class ActionController::Base def widget name = '', *a, &b Widget.for_controller self, name, *a, &b end helper_method 'widget' end end def Widget(*a, &b) Widget.Class(*a, &b) end Widgetz = Widget unless defined? Widgetz def Widgetz(*a, &b) Widgetz.Class(*a, &b) end