class Module # Patching delegate to take default value as option, which means that instead of # returning nil in any situation it will return the default value. So if the # object you delegate to does not exist or the method on that object does not exist # or the method returns nil then the default value is returned. # Note that if you use a default value then the allow nil is automatically set to false, # since you will always return the default if there is nil value # # class Invoice < Struct.new(:client) # delegate :name, :to => :client, :default => 'Dano' # end # # Example 1: # invoice = Invoice.new(nil) # no client # invoice.client_name # => "Dano" # # Example 2: # Client = Struct.new(:address) # client does not have attribute called name # john_doe = Client.new("1 park avenue") # invoice = Invoice.new(john_doe) # invoice.client_name # => "Dano" # # Example 3: # Client = Struct.new(:name, :address) # john_doe = Client.new(nil, "1 park avenue") # client name is nil # invoice = Invoice.new(john_doe) # invoice.client_name # => "Dano" # # def delegate(*methods) options = methods.pop unless options.is_a?(Hash) && to = options[:to] raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)." end if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method." end prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_" default = options[:default] allow_nil = (options[:allow_nil] || default) && "#{to} && " methods.each do |method| writer(method, to, prefix) if options[:writer] module_eval(<<-EOS, "(__DELEGATION__)", 1) def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) val = #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # val = client && client.__send__(:name, *args, &block) return "#{default}" if val.nil? and #{!default.blank?} # return default if val.nil? and !default.blank? val # val end # end EOS end end ##################################################################### # writer :tail_number, :aircraft # # creates a method like this: # # def tail_number=(tail_number) # self.aircraft = Aircraft.find_by_tail_number(tail_number) # end # # in this case the setter_attribute is the same as the attribute name # but in a case like this: # # setter :pic_xojet_id, :pilot, :pic_pilot # # you are saying set the attribue pic_pilot to a pilot # def writer(setter_attribute, model, prefix) define_method "#{prefix}#{setter_attribute.to_s}=" do |value| found_model = model.to_s.capitalize.constantize.find(:first, :conditions=> {setter_attribute => value}) self.send("#{model.to_s}=", found_model) end end end