2008/12/20

LANG of scpt

shoes2 を
http://shoooes.net/tutorial
ダウンロードしてとりあえずオセロなど動かしていたら思い出したこと。

astest.rb

#!/pathto/ruby191/bin/ruby
# -*- coding: utf-8 -*-

begin
  e = Encoding.default_external.name
  abort "Lang Error #{e}\n" unless e == "UTF-8"
rescue
  abort "ruby version Error\n"
end
print e


これを、ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0] で実行すると

UTF-8

が返ってくる。この astest.rb に実行権限を与えてやり AppleScript から実行できるようにしておく。
今度は AppleScript から astest.rb を実行するためのスクリプト(myscpt.scpt)をつくり astest.rb と同じ階層に保存。

myscpt.scpt

# AppleScript's version "2.0.1". OS X 10.5.6 
# dir_base =>いまいる(このファイルの保存場所)ディレクトリの親ディレクトリ
tell application "Finder"	
  set dir_base to POSIX path of (folder of (folder of (path to me) as alias) as alias)
end tell
# dir_bin =>親ディレクトリ内 bin ディレクトリ
set dir_bin to dir_base & "bin/"
# コマンドテキスト生成
set comstr to "cd " & dir_bin & "; ./astest.rb"
# 発行
set res to do shell script comstr


これを実行するとなぜかエラーになる

AppleScript エラー
Lang Error US-ASCII


改めてターミナルで自分の今の環境変数を確認してみたけれど US-ASCII ではない。

% env |grep LANG
LANG=ja_JP.UTF-8


回避策はあるのだろうか?と考えた末...

# コマンドテキスト生成
#set comstr to "cd " & dir_bin & "; ./astest.rb"
set comstr to "export LANG='ja_JP.UTF-8';cd " & dir_bin & "; ./astest.rb"

結果

"UTF-8"

do shell script 中でLANG を設定してやればいいようだ。こんなことができるなんて初めて知った。

set comstr to "ENV"
set res to do shell script comstr

で、AppleScriptの環境変数をみることができた。どうやらここではまっていたようだ。。。

+++ memo +++
教訓:AppleScript が使う環境変数と自分の環境変数は異なる # OS X 10.5.6
ruby 1.9.x のものを AppleScript から実行してエラーが発生する時は encoding の問題かもしれないので注意が必要。と。

+++ 追記 +++
2009-01-27.
http://developer.apple.com/jp/technotes/tn2002/tn2065.html
http://developer.apple.com/qa/qa2001/qa1067.html
これらを読んでわかったこと. ENV LANG は

tell Application "Finder" ...
tell me ...

からは取得できない。

tell application "Finder"
  set lang to do shell script "ENV"
end tell
lang

これらが返す値に HOME, PATH, USER... などは含まれているものの肝心の LANG は含まれていない。

tell application "Terminal"
  set lang to do shell script "ENV"
end tell

には LANG は含まれている。
というこことから export しないで LANG 取得するにはこれが最短かなあ。

tell application "Terminal" to set lang to do shell script "ENV|grep LANG"
=>
"LANG=ja_JP.UTF-8"

でも、Terminal 起動させちゃうのはちょっと...

ちなみに
http://macscripter.net/viewtopic.php?id=24805
を読んで試したら

set SystemLanguage to do shell script "defaults read .GlobalPreferences AppleLanguages | tr -d [:space:] | cut -c2-3"
=>
"ja"

だけだった。

+++ さらに追記 +++
2009-01-29.

プログラミング Ruby
P41 を読んで
エンコーディング名を指定して ruby 1.9.1 を起動する方法がわかった

% cat as-test.rb
#!/path/to/ruby191/bin/ruby1.9 -E utf-8
# coding: utf-8
begin
  str = ARGV[0]
  if str
    puts str_e = str.encoding.name
    puts str_v = str.valid_encoding?
  end
  e = Encoding.default_external.name
  abort "Lang Error #{e}\n" unless e == "UTF-8"
rescue => err
  abort "Error.#{err}.\n"
end
print "ExtEncoding:#{e}\n"
print __ENCODING__, "\n"
% ./as-test.rb aa
=>
UTF-8
true
ExtEncoding:UTF-8
UTF-8


AppleScript からの呼び出しテスト
as-test.scpt をデスクトップに保存ー実行。

# as-test.scpt
tell application "Finder"
  display dialog "input" default answer "あいうえお"
  set str to text returned of result as text
  set path_file to POSIX path of (file "as-test.rb" of desktop as alias)
  set a to do shell script "./" & path_file & " " & str
  end tell
  a
=>
"UTF-8
true
ExtEncoding:UTF-8
UTF-8"


+++ memo +++

AppleScript ダイアログで受け取る文字列のエンコーディングは Ruby を通じて UTF-8 になっているのが確認できた。それでも、受け取る側の Ruby File ではその文字列をなにか処理する時には本当にUTF-8 か?とか valid か?をみるようにしておいたほうがいいのかも。

OS X 10.5.6
AppleScript's version "2.0.1"
Finder's version "10.5.8"

--imported_from
http://www.midore.net/daybook/2008/12/1229704901.html

2008/12/09

command-line-with-ruby

コマンドラインで対話するためのクラスを作ってみた。
http://doc.loveruby.net/refm/api/view/class/Timeout
を参考にした。
リファレンスに書かれているように何秒かするとタイムアウトし終了するようにした。$stdin で受けとる文字列が yes or no の場合と、数字の場合と、アルファベットなど文字を1文字の場合とを用意してみた。timeout() は、require 'timeout'しないと使えないけれど、もしもほかの用事で require 'net/http' していたら require 'timeout' を書かなくても timeout() を使うことができた。

CommandLine.new().yesorno
y or yes のとき true をそれ以外の場合 false をかえす。

% ruby19 -rmyttymess.rb -ve "Myttymess::CommandLine.new().yesorno"
ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
Please Select [y/n]:
y
true
% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().yesorno"
Please Select [y/n]:
n
false
% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().yesorno"
Please Select [y/n]:
a
false
% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().yesorno"
Please Select [y/n]:
Timeout...bye.
false


CommandLine.new().character
1つの文字であればその文字を返し、そうでない場合 false が返る。数字の場合も1桁の数字ならok。
ruby 1.9 の場合 日本語の「あ」も1文字と解釈してくれた。
ruby 1.8.6 の場合は false になった。

% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().character"
Please input one char:
a
"a"
% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().character"
Please input one char:
abc
false
% ruby19  -rmyttymess.rb -ve "Myttymess::CommandLine.new().character"
ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
Please input one char:
あ
"あ"
% ruby -rmyttymess.rb -ve "Myttymess::CommandLine.new().character"
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
Please input one char:
あ
false


CommandLine.new().number(max)
max に指定した数字以下なら入力された数字を それ以外の場合 falseを アルファベットなど文字の場合 false を

% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().number(10)"
Please Select Number:
9
"9"
% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().number(10)"
Please Select Number:
10
"10"
% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().number(10)"
Please Select Number:
11
false
% ruby19 -rmyttymess.rb -e "Myttymess::CommandLine.new().number(10)"
Please Select Number:
a
false


myttymess.rb

1 #!/pathto/ruby19/bin/ruby
2 # -*- coding: utf-8 -*-
3 
4 # myttymess.rb
5 # ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
6 # 2008-12-06.
7 
8 ######################################################
9 # reference:
10 # http://doc.loveruby.net/refm/api/view/class/Timeout
11 ######################################################
12 
13 # if require 'net/http', unnecessarily require 'timeout'.
14 # 'net/http.rb' require 'net/protocol.rb'. 'net/protocol.rb' require 'timeout'.
15 
16 require 'timeout'
17 
18 module Myttymess
19 
20   class MyError < Exception; end
21 
22   class StdinGets
23 
24     def interactive(mess, opt)
25       return false unless $stdin.tty? == true
26       print "#{mess}:\n"
27       ans = $stdin.gets.chop
28       m = /^y$|^yes$/.match(ans)
29       return false if /n/.match(ans) # n or no == stop
30       return false if ans.empty?     # ans is String class
31       case opt
32       when true   # yes or no
33         return false if m.nil?
34         return true
35       when false  # return one character
36         return false if ans.size > 1
37         return ans
38       else        # return number =< Max Number
39         return false if ans == "0"
40         return false if ans =~ /\D/
41         return false if ans.to_i > opt.to_i
42         return ans
43       end
44     end
45 
46     def message(str,opt)
47       ans = ''
48       begin
49         timeout(5, MyError){ans = interactive(str, opt)}
50       rescue MyError => err
51         puts "Timeout...bye.\n"
52         return false
53       end
54       return ans
55     end
56 
57   end
58 
59   class CommandLine
60 
61     def yesorno
62       str =  "Please Select [y/n]"
63       p ans = StdinGets.new().message(str, true)
64       return ans
65     end
66 
67     def number(max)
68       str =  "Please Select Number"
69       p ans = StdinGets.new().message(str, max)
70     end
71 
72     def character
73       str =  "Please input one char"
74       p ans = StdinGets.new().message(str, false)
75     end
76 
77   end
78 
79 end
80 
81 #Myttymess::CommandLine.new().yesorno
82 #Myttymess::CommandLine.new().number(max)
83 #Myttymess::CommandLine.new().character


--imported_from
http://www.midore.net/daybook/2008/12/1228833000.html

2008/12/08

amazon-aws-with-ruby1.9.1

コマンドで ean を指定すると、amazon にアクセスしその本の情報を返してくれるものをつくってみた。
せっかくなので ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0] で作ってみた。
useaws.rb xxx を実行すると、xxxの番号が本の場合なら Book class のto_sを、CD の場合は Music classのto_sをかえす。
その他のメディアは作ってない。

%./useaws.rb 9784763198426
# =>
[Book][9784763198426][-][-]| アホは神の望み | 村上 和雄 | 2008 | ¥ 1,680


とか
% pathto/ruby/191 useaws.rb 9784763198426 > aho.txt
% open aho.txt
などして使う。

+++ memo +++

アマゾンからデータ(xml)を取得したデータは、utf-8 か?をチェックしてそうじゃなかったらutf-8にしないといけないんだけど、force じゃなくてもいいのかもしれない。

BaseItem#setup でハッシュのデータをもとにインスタンス変数を生成する際 Proc を使ってみた。使ってみたかったので使っているにすぎなくて適切かどうかはよくわかっていない。

カンマを付与するメソッド comma(n) は「Ruby Way」[第二版] P147に掲載されていた素晴らしいやつにちょっと挑戦してみた。「Ruby Way」ではわずか3行なのに自分のは長くてわかりにくい。本当は、もとのデータの price format をどこかに保存しておいてそれを to_s が使えるようにしてやればいいんだけど、ゆくゆくは marshal data にしたい思惑があったので、数字なら数字を、文字列ならば文字列を Book class or Music class に持っていてほしいと考えてるうちにこのように...

おかしな点などいろいろあるかと思われます

% cat -n aws.rb

     1	# -*- coding: utf-8 -*-
     2	# aws.rb
     3	# ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
     4	
     5	require 'rexml/document'
     6	require 'uri'
     7	require 'net/http'
     8	
     9	module AWS
    10	
    11	  class Amazon
    12	
    13	    def initialize(str=nil)
    14	      unless str.nil?
    15	        case str.encoding.name # String
    16	        when "UTF-8"
    17	          @xml = REXML::Document.new(str)
    18	        else
    19	          ustr = str.force_encoding("UTF-8")
    20	          @xml = REXML::Document.new(ustr)
    21	        end
    22	      end
    23	      @h = Hash.new
    24	      @i = nil
    25	    end
    26	
    27	    def item
    28	      dh = data_to_h
    29	      unless dh.nil?
    30	        @i = selectitem
    31	        return nil if @i.nil?
    32	        @i.setup.call(@h)
    33	        return @i
    34	      end
    35	    end
    36	
    37	    private
    38	
    39	    def selectitem
    40	      g = @h["ProductGroup"]
    41	      return Book.new() if g == "Book"
    42	      return Music.new() if g == "Music"
    43	    end
    44	
    45	    def data_to_h
    46	      return nil if @xml.nil?
    47	      return nil if @xml.to_s =~ /Error/ #or @xml.to_s.empty?
    48	      return nil unless @xml.encoding == "UTF-8"
    49	      getelement
    50	      set_data
    51	      return @h
    52	    end
    53	
    54	    def getelement
    55	      ei = @xml.root.elements["Items/Item"]
    56	      @attrib = get(ei,"ItemAttributes")
    57	      @img = get(ei,"MediumImage")
    58	      @url = get(ei,"DetailPageURL")
    59	    end
    60	
    61	    def set_data
    62	      @attrib.each{|x| @h[x.name] = plural(@attrib,x.name)}
    63	      @h.delete_if{|k,v| v.nil?}
    64	      setprice
    65	      setimg
    66	      seturl
    67	      @h["EAN"] = @h["EAN"].to_i
    68	      @h["ISBN"] = @h["ISBN"].to_i unless @h["ISBN"].nil?
    69	      @h["price"] = @h["price"].to_i
    70	    end
    71	
    72	    def get(ele,str)
    73	      ele.elements[str]
    74	    end
    75	
    76	    def plural(ele,str)
    77	      e = ele.get_elements(str)
    78	      case e.size
    79	      when 0
    80	      when 1
    81	        ele.elements[str].text
    82	      else
    83	        @h[str] = e.map{|i| i.text}.join(" / ")
    84	      end
    85	    end
    86	
    87	    def setprice
    88	      @h["price"] = @attrib.elements["ListPrice/FormattedPrice"].text.gsub(/\D/,'')
    89	    end
    90	
    91	    def setimg
    92	      @h["mediumimage"] = @img.elements["URL"].text unless @img.nil?
    93	    end
    94	
    95	    def seturl
    96	      url = exurl(@url.text) #url.encoding => <Encoding:UTF-8>
    97	      return nil unless url.encoding.name == "UTF-8"
    98	      eurl = url.gsub(/\?SubscriptionId=.*/u,'')
    99	      m = /amazon.co.jp\/(.*?\/)/u.match(eurl)
   100	      @h["url"] = eurl.gsub(m[1],'') # => "http://www.amazon.co.jp/dp/4274065979"
   101	    end
   102	
   103	    def exurl(string)
   104	      # reference: lib/ruby/1.9.0/cgi/util.rb #line.15
   105	      enc = string.encoding
   106	      string.gsub(/%([0-9a-fA-F]{2})/){[$1.delete('%')].pack("H*")}.force_encoding(enc)
   107	    end
   108	
   109	  end
   110	
   111	  class Seturi
   112	
   113	    def initialize(aws_accessKeyId=nil, associate_ID=nil)
   114	      @aws_key = aws_accessKeyId
   115	      @aws_id = associate_ID
   116	      @jp_ecs = 'http://ecs.amazonaws.jp'
   117	      #http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService
   118	    end
   119	
   120	    def seturi(ean)
   121	      return nil if @aws_key.nil?
   122	      return nil if @aws_id.nil?
   123	      uri = URI.parse(@jp_ecs)
   124	      uri.path='/onca/xml'
   125	      q = [
   126	        "Service=AWSECommerceService",
   127	        "AWSAccessKeyId=#{@aws_key}",
   128	        "Operation=ItemLookup",
   129	        "ItemId=#{ean}",
   130	        "AssociateTag=#{@aws_id}",
   131	        "ResponseGroup=Medium"
   132	      ]
   133	      ean = ean.to_s
   134	      case ean.size
   135	      when 10
   136	        q << ["SearchIndex=Books" ,"IdType=ISBN"]  # Book isbn 10
   137	      when 12
   138	        q << ["SearchIndex=Music", "IdType=EAN"]   # Not japanese CD
   139	      when 13
   140	        if ean.to_s =~ /^978|^491/                 # Book isbn 13 (EAN)
   141	          q << ["SearchIndex=Books" ,"IdType=EAN"]
   142	        elsif ean.to_s =~ /^458/                   # japanese DVD
   143	          q << ["SearchIndex=DVD", "IdType=EAN"]
   144	        else
   145	          q << ["SearchIndex=Music", "IdType=EAN"] # Japanese CD
   146	        end
   147	      end
   148	      uri.query = q.join("&")
   149	      return uri
   150	    end
   151	
   152	  end
   153	
   154	  class Access
   155	
   156	    def initialize(uri)
   157	      @host = uri.host
   158	      @request = uri.request_uri
   159	    end
   160	
   161	    def reponse
   162	      res = ""
   163	      begin
   164	        Net::HTTP.start(@host){|http|
   165	          response = http.get(@request)
   166	          res = response.body
   167	        }
   168	        sleep 2
   169	        return res
   170	      rescue SocketError
   171	        return 'socket'
   172	      end
   173	    end
   174	
   175	  end
   176	
   177	  class BaseItem
   178	
   179	    attr_reader :group,:ean, :title, :author, :price
   180	    attr_accessor :memo, :genru
   181	
   182	    def initialize
   183	      @adddate = Time.now.strftime("%Y-%m-%d %H:%M:%S")
   184	    end
   185	
   186	    def values
   187	      ary = []
   188	      self.instance_variables.each{|v|
   189	        val = instance_variable_get(v)
   190	        ary << val unless val.nil?
   191	      }
   192	      return ary.join("_")
   193	    end
   194	
   195	    def setup
   196	      Proc.new do |h|
   197	        h.each{|k,v|
   198	          s = "@#{k}".downcase.to_sym
   199	          set_ins(s,v)
   200	        }
   201	        set_ins(:@genru,'-')
   202	        set_ins(:@memo,'-')
   203	        nil
   204	      end
   205	    end
   206	
   207	    private
   208	
   209	    def set_ins(i,val)
   210	      val = val.encode("UTF-8") if val.kind_of?(String)
   211	      self.instance_variable_set(i, val)#unless val.nil?
   212	    end
   213	
   214	    def com_txt(ary)
   215	      str = String.new()
   216	      ary.each{|i|
   217	        val = instance_variable_get(i)
   218	        str << lines = i.to_s.gsub("@","--") + "\n" + val.to_s + "\n"
   219	      }
   220	      return str
   221	    end
   222	
   223	    def pubyear(y)
   224	      return n = /(\d...)/.match(y.to_s)
   225	    end
   226	
   227	    def comma(n)
   228	      s, ary, res, jsEn = n.size, [], [], "\u{A5} " # added space
   229	      k = (s - 4) % 3
   230	      n.to_s.each_char{|i| ary << i}
   231	      ary.each_index{|i|
   232	        res << ary[i]
   233	        k, res = k + 3, res << "," if i == k and i + 1 < s
   234	      }
   235	      return jsEn + res.join('')
   236	      ### Ruby Way P147.###
   237	      #str = n.to_s.reverse
   238	      #str.gsub!(/([0-9]{3})/,"\\1,")
   239	      #str.gsub(/,$/,"").reverse
   240	    end
   241	
   242	  end
   243	
   244	  class Book < BaseItem
   245	
   246	    def to_s
   247	      price_j = comma(@price)
   248	      per = person
   249	      n = pubyear(@publicationdate)
   250	      return printf "[%s][%013d][%s][%s]| %s | %s | %s | %s\n", @productgroup,@ean,@memo,@genru,@title,per,n,price_j
   251	    end
   252	
   253	    def person
   254	      ac = @author
   255	      ac = "#{@author}\s(#{@creator})" unless @creator.nil?
   256	      return ac
   257	    end
   258	
   259	    def to_txt
   260	      ary = [:@ean,:@title,:@author,:@creator,:@publisher,:@adddate,:@genru,:@memo]
   261	      ary.delete(:@creator) if @creator.nil?
   262	      com_txt(ary)
   263	    end
   264	
   265	  end
   266	
   267	  class Music < BaseItem
   268	
   269	    def to_s
   270	      price_j = comma(@price)
   271	      n = pubyear(@releasedate)
   272	      return printf "[%s][%013d][%s][%s]| %s | %s | %s | %s\n", @productgroup,@ean,@memo,@genru,@title,@artist,n,price_j
   273	    end
   274	
   275	    def to_txt
   276	      ary = [:@ean,:@title,:@artist,:@label,:@adddate,:@genru,:@memo]
   277	      com_txt(ary)
   278	    end
   279	
   280	  end
   281	
   282	end


% cat -n useaws.rb

     1	# -*- coding: utf-8 -*-
     2	
     3	# useaws.rb
     4	# Created by midore on 2008-12-06.
     5	# ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
     6	
     7	require 'aws'
     8	
     9	class UseAmazon
    10	
    11	  include AWS
    12	
    13	  attr_accessor :ean, :aws_id, :aws_key
    14	
    15	  def initialize(ean=nil)
    16	    @ean = ean
    17	    @aws_key = nil
    18	    @aws_id = nil
    19	  end
    20	
    21	  def to_obj
    22	    doc = getdoc
    23	    return "socket error" if doc == "socket"
    24	    return "error doc" if doc.to_s =~ /Error/ or doc.empty?
    25	    a = Amazon.new(doc)
    26	    a.item
    27	  end
    28	
    29	  def getdoc
    30	    url = Seturi.new(@aws_key, @aws_id).seturi(@ean)
    31	    doc = Access.new(url).reponse
    32	  end
    33	
    34	end
    35	
    36	begin
    37	  ean =  ARGV[0] #ean = 9784797336610 if ean.nil? # 9784763198426
    38	  amazon = UseAmazon.new(ean)
    39	  amazon.aws_id = 'midore-22'
    40	  amazon.aws_key = 'xxxxxxxxxxxxx'
    41	  p ob = amazon.to_obj
    42	  puts ob.to_s
    43	  #puts ob.to_txt
    44	rescue
    45	  abort "Error\n"
    46	end


--imported_from
http://www.midore.net/daybook/2008/12/1228746600.html