FrontPage  Index  Search  Changes  RSS  wikifarm  Login

SymHash.rb

概要

Phi::Control に「color」という数値型の属性があり その値が /^CL_/ にマッチする名前の定数で定義されているときに、 「attr_sym :color」で color_sym という属性を追加します。

別名なのに同じ値の定数がある場合には、第5引数で除外する。

module Phi
  class Control
    attr_sym :color, Phi , /^CL_/ ,[], [:dk_gray, :lt_gray] # , :debug
  end
end

* :debug を引数に加えると、その場でデータハッシュを表示します。

上のように定義した color_sym は次のように使うことができる。

  • form.color は「-2147483633」を返す。これでは何色か分からない。
  • form.color_sym は「:btn_face」を返す。これなら分かる。
form = Phi::Form.new
p [form.color,form.color_sym]
#=> [-2147483633, :btn_face]
  • form.color_sym にシンボルを入れると、color の値も変わる。
form.color_sym = :red
p [form.color,form.color_sym]
#=> [255, :red]
  • form.color_sym に数値を入れてもOK。
form.color_sym = Phi::CL_BLUE
p [form.color,form.color_sym]
#=> [16711680, :blue]
  • form.color に数値を入れてもOK。color_sym も変わる。
form.color = Phi::CL_YELLOW
p [form.color,form.color_sym]
#=> [65535, :yellow]

依存性

  1. Apollo 専用です。
  2. Hash_tree.rb
  3. Enumerable_to_hash.rb

利用者

  1. Phi_sym.rb

実行見本:


説明


コメントがありましたらどうぞ

(comment plugin is disabled).

ソース:

SymHash.rb

#! ruby -Ks
#-- SymHash.rb
#-- SymHash.pi

require "Enumerable_to_hash"
require "Hash_tree"
require "phi" # uses only Phi.upcase & Phi.downcase

class SymHash < Hash

def self.decode_src(mod,str)
  ret = self.new
  sym_num = {}
  str = str.delete("\n").strip
  if /(.*)\;/ =~ str
    str = $1.strip
  end
  case str
  when /(\w+)\s*\=\s*\((.*)\)/
    vals = $2.split(",")
    vals.each_with_index{|name,index|
      name = name.strip
      name_const = Phi.upcase(name)
      name_sym = Phi.downcase(name)
      sym = (/^\w+?\_(.*)$/ =~ name_sym) ? $1.intern : name_sym.intern
      val = mod.module_eval(name_const) rescue index
      sym_num[sym] = val
    }
  else
    raise :undecodable
  end
  ret[:sym_num] = sym_num
  ret[:num_sym] = sym_num.invert
  return ret
end

def self.decode_exp( mod, exp, only=[], excepts=[], row_key=false)
  ret = self.new
  name = exp.inspect
  arr = mod.constants.select{|n| exp =~ n }
  raise "no constant matches '#{name}' in #{mod}" if arr.empty?
  if row_key
    arr2 = arr.collect{|n| [n.intern, mod.const_get(n)] }
  else
    arr2 = arr.collect{|n| exp =~ n ; [$'.downcase.intern, mod.const_get(n)] }
  end
  arr2 = arr2.select{|k,v| only.include? k } if only && only.size > 0
  ret[:sym_num] = sym_num = Hash[*arr2.flatten]
  excepts.each{ |k| sym_num.delete(k) } if excepts && excepts.size > 0
  ret[:num_sym] = num_sym = sym_num.invert
  return ret if num_sym.size == sym_num.size
    #
    # (ja)ここで例外が出た場合には第3引数で衝突シンボルを除外してください。
    # または、第4引数で必要なシンボルだけに制限してください。
    #
    # (en)if error shows this area, please restrict by the 3rd argument
    #  of designate excepts on the 4th argument.
    #
  conflict_keys = (sym_num.keys - num_sym.values)
  conflict_vals = conflict_keys.collect{|k| sym_num[k]}
  conflicts = conflict_vals.collect{|v|
      [ v, sym_num.select{|kk,vv| vv == v}.collect{|kk,vv| kk}]
    }.to_hash
  raise <<MMMM
conflict in #{name} in #{mod} module
 ---- conflict is ----
:conflict => #{conflicts.tree}
 ---- end of error info ----
MMMM
  return ret
end

  #-- instance methods

def to_num(sym)
  sym_num = self[:sym_num]
  return sym_num_to_num( sym_num , sym ) unless sym.is_a? Array
  sym.collect{|sym| sym_num_to_num( sym_num , sym )}
end
def to_sym( num )
  num_sym = self[:num_sym]
  return num_sym_to_sym( num_sym , num ) unless num.is_a? Array
  num.collect{|num| num_sym_to_sym( num_sym , num )}
end

def sym_num_to_num( sym_num , sym )
  sym_num[sym] || ( (sym.is_a? Symbol) ? (raise "unknown symbol '#{sym}'") : sym )
end
def num_sym_to_sym( num_sym , num )
  num_sym[num] || num
end

end # of class SymHash

class Module
private

  def attr_sym(name , *args)
    debug_mode = args.delete(:debug)
    a = args.collect{|i|i.inspect}.join(',')
    mod = args[0]
    case args[1]
    when String
      decoder = "decode_src"
    else
      decoder = "decode_exp"
    end
    str = <<MMMM

      @@#{name}_sym_hash = nil

      def self.#{name}_sym_hash
        @@#{name}_sym_hash ||= SymHash.#{decoder}(#{a})
      end
      def #{name}_sym
        self.class.#{name}_sym_hash.to_sym(self.#{name})
      end

      def #{name}_sym= (v)
        self.#{name} = self.class.#{name}_sym_hash.to_num(v) # rescue nil # ?????
      end
      def #{name}_sym_hash
        self.class.#{name}_sym_hash
      end
      if $DEBUG || debug_mode
        puts "#{mod}::#{name}_sym_hash ="
        puts  #{name}_sym_hash.tree
      end
MMMM
    module_eval str
  end
end # of class Module

if __FILE__ == $0

require "phi"
require "Hash_tree"

# Phi::Control に「color」という数値型の属性があり
# その値が /^CL_/ にマッチする名前の定数で定義されているときに、
# 「attr_sym :color」で color_sym という属性を追加する。

# 別名なのに同じ値の定数がある場合には、第5引数で除外する。

module Phi
  class Control
    attr_sym :color, Phi , /^CL_/ ,[], [:dk_gray, :lt_gray] # , :debug
  end
end

puts Phi::Control.new.methods.select{|name| /color/ =~ name}.sort

#=> color_sym
#=> color_sym=
#=> color_sym_hash

# Phi::Control.color_sym_hash の中身

puts Phi::Control.color_sym_hash.tree

#=> {
#=>   :num_sym     => {
#=>     -2147483647  => :background ,
#=>     -2147483646  => :active_caption ,
#=>     -2147483645  => :inactive_caption ,
#=>     -2147483644  => :menu ,
#=>     -2147483643  => :window ,
#=>     -2147483642  => :window_frame ,
#=>     -2147483641  => :menu_text ,
#=>     -2147483640  => :window_text ,
#=>     -2147483639  => :caption_text ,
#=>     -2147483638  => :active_border ,
#=>     -2147483637  => :inactive_border ,
#=>     -2147483636  => :app_work_space ,
#=>     -2147483635  => :highlight ,
#=>     -2147483634  => :highlight_text ,
#=>     -2147483633  => :btn_face ,
#=>     -2147483632  => :btn_shadow ,
#=>     -2147483631  => :gray_text ,
#=>     -2147483630  => :btn_text ,
#=>     -2147483629  => :inactive_caption_text ,
#=>     -2147483628  => :btn_highlight ,
#=>     -2147483625  => :info_text ,
#=>     -2147483624  => :info_bk ,
#=>     0            => :black ,
#=>     128          => :maroon ,
#=>     255          => :red ,
#=>     32768        => :green ,
#=>     32896        => :olive ,
#=>     65280        => :lime ,
#=>     65535        => :yellow ,
#=>     8388608      => :navy ,
#=>     8388736      => :purple ,
#=>     8421376      => :teal ,
#=>     8421504      => :gray ,
#=>     10789024     => :med_gray ,
#=>     12632256     => :silver ,
#=>     12639424     => :money_green ,
#=>     15780518     => :sky_blue ,
#=>     15793151     => :cream ,
#=>     16711680     => :blue ,
#=>     16711935     => :fuchsia ,
#=>     16776960     => :aqua ,
#=>     16777215     => :white ,
#=>   } ,
#=>   :sym_num     => {
#=>     :active_border => -2147483638 ,
#=>     :active_caption => -2147483646 ,
#=>     :app_work_space => -2147483636 ,
#=>     :aqua        => 16776960 ,
#=>     :background  => -2147483647 ,
#=>     :black       => 0 ,
#=>     :blue        => 16711680 ,
#=>     :btn_face    => -2147483633 ,
#=>     :btn_highlight => -2147483628 ,
#=>     :btn_shadow  => -2147483632 ,
#=>     :btn_text    => -2147483630 ,
#=>     :caption_text => -2147483639 ,
#=>     :cream       => 15793151 ,
#=>     :fuchsia     => 16711935 ,
#=>     :gray        => 8421504 ,
#=>     :gray_text   => -2147483631 ,
#=>     :green       => 32768 ,
#=>     :highlight   => -2147483635 ,
#=>     :highlight_text => -2147483634 ,
#=>     :inactive_border => -2147483637 ,
#=>     :inactive_caption => -2147483645 ,
#=>     :inactive_caption_text => -2147483629 ,
#=>     :info_bk     => -2147483624 ,
#=>     :info_text   => -2147483625 ,
#=>     :lime        => 65280 ,
#=>     :maroon      => 128 ,
#=>     :med_gray    => 10789024 ,
#=>     :menu        => -2147483644 ,
#=>     :menu_text   => -2147483641 ,
#=>     :money_green => 12639424 ,
#=>     :navy        => 8388608 ,
#=>     :olive       => 32896 ,
#=>     :purple      => 8388736 ,
#=>     :red         => 255 ,
#=>     :silver      => 12632256 ,
#=>     :sky_blue    => 15780518 ,
#=>     :teal        => 8421376 ,
#=>     :white       => 16777215 ,
#=>     :window      => -2147483643 ,
#=>     :window_frame => -2147483642 ,
#=>     :window_text => -2147483640 ,
#=>     :yellow      => 65535 ,
#=>   } ,
#=> }

# ここで定義した color_sym は次のように使うことができる。

# form.color は「-2147483633」を返す。これでは何色か分からない。
# form.color_sym は「:btn_face」を返す。これなら分かる。

form = Phi::Form.new
p [form.color,form.color_sym]
#=> [-2147483633, :btn_face]

# form.color_sym にシンボルを入れると、color の値も変わる。

form.color_sym = :red
p [form.color,form.color_sym]
#=> [255, :red]

# form.color_sym に数値を入れてもOK。

form.color_sym = Phi::CL_BLUE
p [form.color,form.color_sym]
#=> [16711680, :blue]

# form.color に数値を入れてもOK。color_sym も変わる。

form.color = Phi::CL_YELLOW
p [form.color,form.color_sym]
#=> [65535, :yellow]

p "---- from Delphi source ----"

module Phi
class PropGridItem
  attr_sym :style , Phi , <<MMMM
TPropGridStyle = (psEdit, psCombo,psComboList,psBooleanComboList,psYesNoComboList,
   psColorCombo,psColorComboList, psEllipsis, psColorEllipsis,psFileNameEllipsis);
MMMM
end
end

puts Phi::PropGridItem.style_sym_hash.tree # _sym_hash

#=> {
#=>   :num_sym     => {
#=>     0            => :edit ,
#=>     1            => :combo ,
#=>     2            => :combo_list ,
#=>     3            => :boolean_combo_list ,
#=>     4            => :yes_no_combo_list ,
#=>     5            => :color_combo ,
#=>     6            => :color_combo_list ,
#=>     7            => :ellipsis ,
#=>     8            => :color_ellipsis ,
#=>     9            => :file_name_ellipsis ,
#=>   } ,
#=>   :sym_num     => {
#=>     :boolean_combo_list => 3 ,
#=>     :color_combo => 5 ,
#=>     :color_combo_list => 6 ,
#=>     :color_ellipsis => 8 ,
#=>     :combo       => 1 ,
#=>     :combo_list  => 2 ,
#=>     :edit        => 0 ,
#=>     :ellipsis    => 7 ,
#=>     :file_name_ellipsis => 9 ,
#=>     :yes_no_combo_list => 4 ,
#=>   } ,
#=> }

item = Phi::PropGridItem.new
puts item.methods.select{|name| /sym/ =~ name}.sort

#=> style_sym
#=> style_sym=
#=> style_sym_hash

p :ok

end # if test

Last modified:2006/06/23 21:51:32
Keyword(s):
References:[Enumerable_to_hash.rb] [Hash_tree.rb] [Phi_sym.rb]