lib/facet/superstruct.rb in facets-0.7.2 vs lib/facet/superstruct.rb in facets-0.9.0

- old
+ new

@@ -1,241 +2 @@ -#:title: SuperStruct -#-- -# SuperStruct -# v 1.0 -# -# Copyright (c) 2005 Hal Fulton -# -# Ruby License -# -# This module is free software. You may use, modify, and/or redistribute this -# software under the same terms as Ruby. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. -# -# -# $Id: superstruct.rb,v 1.0 2005/04/28 03:10:10 transami Exp $ -# -# ========================================================================== -# Revision History :: -# YYYY.MM.DD Ver. Dev. Description -# -------------------------------------------------------------------------- -# 2005.04.28 1.0 Trans * Minor modifications to documentation. -# ========================================================================== -#++ - -# = Description -# -# This is an easy way to create Struct-like classes; it converts easily -# between hashes and arrays, and it allows OpenStruct-like dynamic naming -# of members. -# -# Unlike Struct, it creates a "real" class, and it has real instance variables -# with predictable names. -# -# A basic limitation is that the hash keys must be legal method names (unless -# used with send()). -# -# Basically, ss["alpha"], ss[:alpha], ss[0], and ss.alpha all mean the same. -# -# == Usage -# -# To do. -# -# == Author(s) -# -# * Hal Fulton -# - -class SuperStruct - - def SuperStruct.new(*args) - @table = [] - @setsyms = [] # Setter symbols - klass = Class.new - if (args.size == 1) && (args[0].is_a? Array) - args = args[0] - end - strs = args.map {|x| x.to_s } - args.each_with_index do |k,i| - case - when (! [String,Symbol].include? k.class) - raise ArgumentError, "Need a String or Symbol" - when (strs[i] !~ /[_a-zA-Z][_a-zA-Z0-9]*/) - raise ArgumentError, "Illegal character" - end - k = k.intern if k.is_a? String - @table << k - @setsyms << (k.to_s + "=").intern - klass.instance_eval { attr_accessor k } - end - - setsyms = @setsyms - table = @table - vals = @vals - klass.class_eval do - attr_reader :singleton - define_method(:initialize) do |*vals| - n = vals.size - m = table.size - case - when n < m - # raise ArgumentError, "Too few arguments (#{n} for #{m})" - # Never mind... extra variables will just be nil - when n > m - raise ArgumentError, "Too many arguments (#{n} for #{m})" - end - setsyms.each_with_index do |var,i| - self.send(var,vals[i]) - end - end - define_method(:pretty_print) do |q| # pp.rb support - q.object_group(self) do - q.seplist(self.members, proc { q.text "," }) do |member| -# self.members.each do |member| -# q.text "," # unless q.first? - q.breakable - q.text member.to_s - q.text '=' - q.group(1) do - q.breakable '' - q.pp self[member] - end - end - end - end - define_method(:inspect) do - str = "#<#{self.class}:" - table.each {|item| str << " #{item}=#{self.send(item)}" } - str + ">" - end - define_method(:[]) do |*index| - case index.map {|x| x.class } - when [Fixnum] - self.send(table[*index]) - when [Fixnum,Fixnum], [Range] - table[*index].map {|x| self.send(x)} - when [String] - self.send(index[0].intern) - when [Symbol] - self.send(index[0]) - else - raise ArgumentError,"Illegal index" - end - end - define_method(:[]=) do |*index| - value = index[-1] - index = index[0..-2] - case index.map {|x| x.class } - when [Fixnum] - self.send(table[*index]) - when [Fixnum,Fixnum], [Range] - setsyms[*index].map {|x| self.send(x,value) } - when [String] - self.send(index[0].intern,value) - when [Symbol] - self.send(index[0],value) - else - raise ArgumentError,"Illegal index" - end - end - define_method(:to_a) { table.map {|x| eval("@"+x.to_s) } } - define_method(:to_ary) { to_a } - define_method(:members) { table.map {|x| x.to_s } } - define_method(:to_struct) do - mems = table - Struct.new("TEMP",*mems) - # Struct::TEMP.new(*vals) # Why doesn't this work?? - data = mems.map {|x| self.send(x) } - Struct::TEMP.new(*data) - end - define_method(:to_hash) do - hash = {} - table.each do |mem| - mem = mem.to_s - hash.update(mem => self.send(mem)) - end - hash - end - define_method(:set) {|h| h.each_pair {|k,v| send(k.to_s+"=",v) } } - - # Class methods... - - @singleton = class << self - self - end - - @singleton.instance_eval do - define_method(:members) do - table.map {|x| x.to_s } - end - me = self - define_method(:attr_tester) do |*syms| - syms.each {|sym| alias_method(sym.to_s+"?",sym) } - end - end - - end - klass - end - - - def SuperStruct.open(*args) - klass = SuperStruct.new(*args) - table = @table - setsyms = @setsyms - table = @table - klass.class_eval do - define_method(:method_missing) do |meth, *args| - mname = meth.id2name - if mname =~ /=$/ - getter = mname.chop - setter = mname - elsif mname =~ /\?$/ - raise NoMethodError # ?-methods are not created automatically - else - getter = mname - setter = mname + "=" - end - gsym = getter.intern - ssym = setter.intern - ivar = "@" + getter - setsyms << setter - table << getter - len = args.length - if mname == getter - klass.class_eval do # getter - define_method(getter) do - instance_variable_get(ivar) - end - end - else - klass.class_eval do # setter - define_method(setter) do |*args| - if len != 1 - raise ArgumentError, "Wrong # of arguments (#{len} for 1)", - caller(1) - end - instance_variable_set(ivar,args[0]) - instance_variable_get(ivar) - end - end - end - if mname == setter - self.send(setter,*args) - else - if len == 0 - self.send(getter) - else - raise NoMethodError, "Undefined method '#{mname}' for #{self}", - caller(1) - end - end - end - end - klass - end - -end - +require 'mega/superstruct.rb' \ No newline at end of file