module Fattr

Constants

Version

Public Class Methods

description() click to toggle source
# File lib/fattr.rb, line 5
def Fattr.description
  'a "fatter attr" for ruby'
end
version() click to toggle source
# File lib/fattr.rb, line 3
def Fattr.version() Fattr::Version end

Public Instance Methods

fattrs(*args, &block) click to toggle source
# File lib/fattr.rb, line 64
  def fattrs(*args, &block)
    unless args.empty?
      returned = Fattr::Result.new

      args.flatten!
      args.compact!

      all_hashes = args.all?{|arg| Hash===arg}

      names_and_configs = {}

      if all_hashes
        args.each do |hash|
          hash.each do |key, val|
            name = key.to_s
            config = Hash===val ? val : {:default => val}
            names_and_configs[name] = config
          end
        end
      else
        config = Hash===args.last ? args.pop : {}
        names = args.select{|arg| Symbol===arg or String===arg}.map{|arg| arg.to_s}
        names.each do |name|
          names_and_configs[name] = config
        end
      end

      initializers = __fattrs__.initializers

      names_and_configs.each do |name, config|
        raise(NameError, "bad instance variable name '@#{ name }'") if("@#{ name }" =~ /[!?=]$/o)

        name = name.to_s

        default = nil
        default = config[:default] if config.has_key?(:default)
        default = config['default'] if config.has_key?('default')

        inheritable = false
        if Module===self
          inheritable = config[:inheritable] if config.has_key?(:inheritable)
          inheritable = config['inheritable'] if config.has_key?('inheritable')
        end

        initialize = (
          if inheritable
            lambda do |*ignored|
              parents = ancestors[1..-1]
              catch(:value) do
                parents.each do |parent|
                  throw(:value, parent.send(name)) if parent.respond_to?(name)
                end
                block ? block.call : default
              end
            end
          else
            block || lambda{|*ignored| default }
          end
        )

        initializer = lambda do |this|
          Object.instance_method('instance_eval').bind(this).call(&initialize)
        end

        initializer_id = initializer.object_id

        __fattrs__.initializers[name] = initializer

        compile = lambda do |code|
          begin
            module_eval(code)
          rescue SyntaxError
            raise(SyntaxError, "\n#{ code }\n")
          end
        end

      # setter, block invocation caches block
        code = <<-code
          def #{ name }=(*value, &block)
            value.unshift block if block
            @#{ name } = value.first
          end
        code
        compile[code]

      # getter, providing a value or block causes it to acts as setter
        code = <<-code
          def #{ name }(*value, &block)
            value.unshift block if block
            return self.send('#{ name }=', value.first) unless value.empty?
            #{ name }! unless defined? @#{ name }
            @#{ name }
          end
        code
        compile[code]

      # bang method re-calls any initializer given at declaration time
        code = <<-code
          def #{ name }!
            initializer = ObjectSpace._id2ref #{ initializer_id }
            self.#{ name } = initializer.call(self)
            @#{ name }
          end
        code
        compile[code]

        module_eval do
          define_method :"#{ name }!" do
            self.send :"#{ name }=", initializer.call(self)
            self.instance_variable_get :"@#{name}"
          end
        end

      # query simply defers to getter - cast to bool
        code = <<-code
          def #{ name }?
            self.#{ name }
          end
        code
        compile[code]

        fattrs << name
        returned[name] = initializer
      end

      returned
    else
      begin
        __fattr_list__
      rescue NameError, TypeError
        singleton_class =
          class << self
            self
          end
        klass = self
        singleton_class.module_eval do
          fattr_list = List.new
          define_method('fattr_list'){ klass == self ? fattr_list : raise(NameError) }
          alias_method '__fattr_list__', 'fattr_list'
        end
        __fattr_list__
      end
    end
  end