import java.awt.Dimension module SwingSupport module Extensions # Module allows including classes to receive attribute values in an *opts* Hash # and sets those attributes after object initialization module Attributes def self.included host host.send :extend, ClassMethods host.instance_eval do # attr_setter :font, :tool_tip_text, :enabled alias :new_without_attributes :new alias :new :new_with_attributes end end # Post-processing (non-setter) options given to initialize def post_process opts attach_to opts[:parent] end # Proper way to add generic component to its parent def attach_to parent parent.add self if parent end module ClassMethods def attributes @attributes ||= (superclass.attributes.dup rescue {}) end # Adds settable attributes for a given class, possibly with defaults # If defaults are given for attributes, they should be put at the end (as opts) def attr_setter *new_attributes if new_attributes.last.is_a? Hash # Some attributes are given with defaults new_attributes_with_defaults = new_attributes.pop new_attributes_with_defaults.each { |name, default| attributes[name] = default } end new_attributes.each { |name| attributes[name] = nil } end # Sets attributes after calling original new def new_with_attributes(*args, &block) opts = args.last.is_a?(Hash) ? args.pop : {} component = self.new_without_attributes(*args, &block) # Extract known attributes given in opts, or known defaults attributes = attributes().map do |name, default| value = opts.delete name result = if default.nil? # No default, return whatever value value elsif default.respond_to? :call # Default is callable, call it with whatever value default.call *value else # Return either non-nil value or default value.nil? ? default : value end [name, result] unless result.nil? end.compact # yield opts if block_given? attributes.each { |(name, value)| component.send "#{name}=", *value } # Post-process non-setter opts (setter opts are already consumed by now) component.post_process opts component end end end end end