スキマハコ

わすれがちなスキマな事を詰め込んでいます。ITの他に暮らしについても書いています。

pikをインストールしたよー

rubyインストールツールとしてはあんまり使えない。あくまでバージョン切替ツールくらいの認識でよい。
Gitからダウンロードした*.msiを利用した。

環境

rubyはインストールしていなくてもいいけれど、空っぽの場合は.pik\config.ymlファイルを編集しないといけない。

順番

  1. rubyをインストールする
  2. DevKitをインストールするDownloads
  3. pikをインストールする

インストールができていたら、次の通りに動くはず。

$C:\Users\chago>pik list
=> 193: ruby 1.9.3p545 (2014-02-24) i386-mingw32

#プロキシ経由の環境なので、環境変数に一時的にあてとく
$C:\Users\chago>set http_proxy=XXX.XXX.XXX.XXX:80

#リモートからダウンロード/インストールできるrubyのリスト
$C:\Users\chago>pik list -r

The --remote parameter is deprecated and will be removed in a
future release.

use 'pik list known' instead.

# MRI Ruby
[ruby-]1.8.7-p302
[ruby-]1.8.7[-p330]
[ruby-]1.9.1[-p430]
[ruby-]1.9.2-p0
[ruby-]1.9.2[-p136]

# IronRuby
ironruby-1.0.0
ironruby-1.1.0
ironruby[-1.1.1]

# JRuby
jruby[-1.5.6]
jruby-1.6.0.RC1

NetBeansを入れたら怒られたよー

versionは既に7以降をつかっていたんだけど、どうしても6.9.1を入れる必要があって。

新しいマシンにNetBeans6.9.1をインストール後、実行したら次の警告メッセージが。

Cannot locate java installation in specified jdkhome:
C:\Program Files\Java\jdk1.6.0_24
Do you want to try to use default version?

※意訳:デフォルトだとNetBeansJDKバージョンが1.6.0_24だけど見つかんないよ!YouこれでOKなの!?

 (OK押しても結局起動は失敗する)

といわれる。

どうやら、インストール時にJDK1.8.Xを指定したのが悪かったみたい。

設定ファイルを書き換えて自マシン内の環境に合わせてあげる。

netbeans_jdkhome="C:\Program Files\Java\jdk1.6.0_24"

修正後に保存し、再度実行したらするっと起動します。

RubyをWindowsで複数バージョン管理するー

Rubyは1.8.Xや1.9.X、2.0.Xとそれぞれバージョンをもっておきたいもの。

 

Linuxではrbenvさんが一般的。

rubyバージョン管理ツール rbenv インストール手順 - Nerdstacks.net

 

Windowsではpikさんが一般的っぽい。

Rubyアソシエーション: 複数のRuby環境の構築

Windowsに複数バージョンのRubyをインストールする | GENDOSU@NET

 

BeginEditとEndEditの働き

 忘れそうなのでメモしておく。
 バインドしていると、BeginEditは呼ばなくても開始している。
 働きとして2つ。

  1. RowStateの更新
  2. Row値についてのバージョン管理切替え
メンバー名 説明
Original この行には元の値が格納されています。
Current この行には現在の値が格納されています。
Proposed この行には提示された値が格納されています。
Default DataRowState の既定バージョン。 DataRowState の値が Added、Modified、または Deleted の場合、既定バージョンは Current です。 DataRowState の値が Detached の場合、既定バージョンは Proposed です。

DataRowVersion 列挙体 (System.Data)

 EndEditを呼ばない限り、編集中なのでバインドしているコントロールの値はProposedに格納されたままでCurrent(現在の値)にはならない。

 AcceptChanges メソッドを呼び出すと、Original 値が Current 値と同じになってしまうので、変更を比較したい時には利用しない。

 あと、RejectChangesメソッドは保留中のProposedを消してしまうのはCancelEditと同じ動きっぽい。

VB.NETからExcelファイルのデータを読み込むには

 やりたいこと。
 ★一覧表の領域と、その他情報領域が存在する。役割ごとに名前付きセルとして定義してあるので、そこから情報を取得する。
 ※名前付きセルはファイルに一意に存在するよう定義する。

開発環境

 ※但し、2007以降でも動くもの

方法案

  1. マクロで読込できる形式で吐き出し(CSVとか)、そのファイルを分析して、取得する。
  2. VB.NETからExcelに対してDB接続し、情報を取得する
  3. COM相互運用を利用し、VB.NETからExcelオブジェクトに対して情報を取得する
  4. VSTOを使う

 メリット・デメリット
 1.一番原始的な気がする。
  メリット: 確実。
  デメリット:ファイル監視が絡む。
 2.インポートと言えばこれ。
  メリット: 無駄なく取れそう(したことない)
  デメリット:帳票形式のデータインポートには向いていない。
 
 4はクライアントにもVSTOをインストールする必要があるようなので、今回は見送り。
 3については経験がなかったので、実験してみた。

やってみる

 初めに書いたソース

' ファイルパスはOpenFileDialogを利用し、確実に存在する前提。
        Private Sub Read(ByVal ustrFilePath As String)

            ' Excelを操作クラス
            Dim excelApp As Excel.Application = Nothing
            Dim wkbk As Excel.Workbook = Nothing
            Dim sheets As Excel.Sheets = Nothing
            Dim wksheet As Excel.Worksheet = Nothing

            ' データ取得クラス
            Dim rangeDataList As Excel.Range = Nothing
            Dim rangeYear As Excel.Range = Nothing
            Dim nmsNames As Excel.Names = Nothing

            ' 結果格納
            Dim data(,) As Object
            Dim strYear As String

            Try
                ' Excelアプリケーションの開始
                excelApp = New Excel.Application
                ' ファイルオープン
                wkbk = excelApp.Workbooks.Open(ustrFilePath)
                sheets = wkbk.Worksheets
                wksheet = sheets(1)

                ' 取得
                nmsNames = wkbk.Names
                For i = 0 To nmsNames.Count - 1
                    ' 一致する名前定義クラスから、参照するRangeを取得
                    If nmsNames(i).Name = "DataList" Then
                        rangeDataList = nmName.RefersToRange

                    ElseIf nmsNames(i).Name = "Year" Then
                        rangeYear = nmName.RefersToRange

                    End If
                Next

                If rangeDataList Is Nothing OrElse rangeYear Is Nothing Then
                    exit sub
                End If

                ' 格納
                data = DirectCast(rangeDataList.Value, Object(,))
                strYear = rangeYear.Value

            Catch
                Throw
            Finally

                ' 解放
                nmsNames = Nothing
                rangeYear = Nothing
                rangeDataList = Nothing
                wksheet = Nothing
                sheets = Nothing
                wkbk = Nothing
                wkbks = Nothing

                ' Close Excel.
                excelApp.Quit()
                excelApp = Nothing
            End Try
        End Sub

 わあ!ひどいありさまだ!
 このソースだと、このメソッドを呼ぶたびにExcelプロセスが残っていき、元プロセスが終了しない限り(または終了しても残るかも)、見た目的にデーモンなプロセスがどんどん増えていきます。
 上手く解放が行われていないため、Excel終了を読んでもプロセスが死なないのですね。

修正しよう

  • 解放を適切に行おう。

 COM相互運用というのは.NET ⇔ ランタイム呼び出し可能ラッパー (RCW: Runtime Callable Wrapper) ⇔ COMの関係で動きます。
 RCWが適切に解放されないと、Excelプロセスは終了出来ないのです。
 解放には ReleaseComObjectメソッド、あるいはFinalReleaseComObjectを利用します。
 解放の記述を修正です。

    ' 解放を修正
            Finally
                ' 解放(全ての解放を行うため、FinalReleaseComObjectを利用します)
                If Not nmsNames Is Nothing Then
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(nmsNames)
                    nmsNames = Nothing
                End If
                If Not rangeYear Is Nothing Then
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(rangeYear)
                    rangeYear = Nothing
                End If
                If Not rangeDataList Is Nothing Then
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(rangeDataList)
                    rangeDataList = Nothing
                End If

                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wksheet)
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheets)
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wkbk)
                wksheet = Nothing
                sheets = Nothing
                wkbk = Nothing

                ' Close Excel.
                excelApp.Quit()
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(excelApp)
                excelApp = Nothing
            End Try

 Nothingの代入は不要かもしれません。むだかなーと思いつつもやってます。
 解放の順番も重要です。
 COMは次のような階層構造になっています。

Application
Workbooks
Workbook
Worksheets
Worksheet
Range

 ですので、子供から先に解放すべきです。

 ここまで来ても、まだ解放されません…。

  • 暗黙的にキャストされている箇所はRCWが生まれている。

 なんと恐ろしいことでしょう。
 次のような箇所に該当します。

                ' ファイルオープン
                wkbk = excelApp.Workbooks.Open(ustrFilePath) ' ← Workbooksを暗黙的に変換している!
                sheets = wkbk.Worksheets
                wksheet = sheets(1)

 このように.が2つ以上ある場合は暗黙的にキャストが働いていると考えて構いません。
 こうして発生したRCWの参照は解放されず、どうしようもなくなります。
 全て、一度変数で参照させて最後に必ず解放するようにしましょう。

            ' Excelを操作クラス
            Dim excelApp As Excel.Application = Nothing
            Dim wkbks As Excel.Workbooks = Nothing
            Dim wkbk As Excel.Workbook = Nothing
            Dim sheets As Excel.Sheets = Nothing
            Dim wksheet As Excel.Worksheet = Nothing

            ' Excelアプリケーションの開始
            excelApp = New Excel.Application
            wkbks = excelApp.Workbooks			' ← 暗黙的変換を回避
            ' ファイルオープン
            wkbk = wkbks.Open(ustrFilePath)
            sheets = wkbk.Worksheets
            wksheet = sheets(1)
  • 繰り返しの中で代入した場合は毎回解放する

 ループ文で回している場合は、利用後に毎回解放しましょう。
 ※なんか怖いので。

                ' 取得
                nmsNames = wkbk.Names
                For i = 0 To nmsNames.Count - 1
                    nmName = nmsNames(i)
                    ' 一致する名前定義クラスから、参照するRangeを取得
                    If nmName.Name = "DataList" Then
                        rangeDataList = nmName.RefersToRange

                    ElseIf nmName.Name = "Year" Then
                        rangeYear = nmName.RefersToRange

                    End If

                    ' 解放(参照が切り替わるので、毎回解放しないとゴミとして残るのでは…)
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(nmName)
                    nmName = Nothing
                Next
  • DirectCastはつかっちゃだめ

 キャストが走るので、解放できないRCWが出来てしまうのです。
 ふつーに代入しちゃいましょう。

                data = rangeDataList.Value

まとめ

  • RCWは必ず解放する
  • 暗黙的なキャストはしないで、必ず変数に格納する
  • 解放するときはCOMの階層構造を意識した順番で行う。

VirtualBox+GestOS(CentOS)でローカル開発環境を作るよー

 前回は外部のネットワークにつなげるように設定したのだけれど、あくまでローカルに開発するよっていう時。

  • ホストOSと通信出来る
    • Virtual Host Only アダプタ
  1. 外部ネットワークから受信(yumなどが出来るように)のみをしたい
    • NAT

 というわけで、それぞれのネットワークアダプタをGestOSの設定より指定する。(起動前)

プロキシで外につなぐ

 NATは「DHCP による IPアドレスの自動取得」でアドレスを設定する。このため、ゲートウェイとDNSサーバの設定が必須になるみたい。
 (DHCP による IPアドレスの自動取得設定は有効の場合)

 プロキシを利用している場合はhttpサーバにプロキシを設定する必要がある。

Proxy

以下の二つのファイルを作成。

$ cat /etc/profile.d/proxy.csh
setenv http_proxy http://プロキシサーバアドレス:ポート番号/
setenv ftp_proxy http://プロキシサーバアドレス:ポート番号/
setenv HTTP_PROXY http://プロキシサーバアドレス:ポート番号/
setenv FTP_PROXY http://プロキシサーバアドレス:ポート番号/
$ cat /etc/profile.d/proxy.sh
#!/bin/sh
export http_proxy=http://プロキシサーバアドレス:ポート番号/
export ftp_proxy=http://プロキシサーバアドレス:ポート番号/
export HTTP_PROXY=http://プロキシサーバアドレス:ポート番号/
export FTP_PROXY=http://プロキシサーバアドレス:ポート番号/

HerokuでWebサービスを公開するまでの記録をするよー

環境もろもろの設定

基本的な開発環境の設定はこちらを参考にしました。
Heroku(ヘロク)で,Ruby on Railsアプリを簡単に公開する方法の入門 (無料のRuby向けPaaS環境の使い方) - 主に言語とシステム開発に関して

IDEはNetBeans7を利用します。
設定方法の参考はこちら:
NetBeans 7.1にRuby on Railsプラグインをインストールする手順 - Rails 雑感 - Ruby on Rails with OIAX

ということでいまはこんな感じ。

# Rubyのバージョン
$ruby --version
ruby 1.9.3p125 (2012-02-16) [i386-mingw32]

# Gemのバージョン
$gem --version
1.8.16

# Railsインストール
$gem  i rails

# Gemインストール状況
$gem list

*** LOCAL GEMS ***

addressable (2.2.8)
bigdecimal (1.1.0)
bundler (1.1.4)
excon (0.14.3)
heroku (2.28.10)
heroku-api (0.2.7)
io-console (0.3)
json (1.5.4)
launchy (2.1.0)
mime-types (1.19)
minitest (2.5.1)
netrc (0.7.5)
rake (0.9.2.2)
rdoc (3.9.4)
rest-client (1.6.7)
rubyzip (0.9.9)

# ----- rails3.2で以下がインストール

actionmailer (3.2.6)
actionpack (3.2.6)
activemodel (3.2.6)
activerecord (3.2.6)
activeresource (3.2.6)
activesupport (3.2.6)
arel (3.0.2)
builder (3.0.0)
erubis (2.7.0)
hike (1.2.1)
i18n (0.6.0)
journey (1.0.4)
mail (2.4.4)
multi_json (1.3.6)
polyglot (0.3.3)
rack (1.4.1)
rack-cache (1.2)
rack-ssl (1.3.2)
rack-test (0.6.1)
rails (3.2.6)
railties (3.2.6)
sprockets (2.1.3)
thor (0.15.4)
tilt (1.3.3)
treetop (1.4.10)
tzinfo (0.3.33)

# Gitの場所。現在64bitOSで開発中。でもなんかここに設定される…。
$where git
C:\Program Files (x86)\Git\cmd\git.exe

Railsのプロジェクトを作るよ

ここはNetBeansでプロジェクトを作るための備忘録になる。

Rubyプラットフォームはマシンにインストールしているバージョンであればいずれも選択可能。
コマンドラインで入力しても可

d:\>rails new meigo
jsonのエラーが出る場合

前も出た気がするんだけど・・・。
railsアプリケーションを作ったら、こんな風に表示される。

Gem::InstallError: The 'json' native gem requires installed build tools.

Please update your PATH to include build tools or download the DevKit
from 'http://rubyinstaller.org/downloads' and follow the instructions
at 'http://github.com/oneclick/rubyinstaller/wiki/Development-Kit'
An error occured while installing json (1.7.3), and Bundler cannot continue.
Make sure that `gem install json -v '1.7.3'` succeeds before bundling.

これにはこちらを参考にしました:
[仕事]WindowsでのRails3環境構築: サウスポーなSEの独り言
どうやらビルドするためのライブラリなどが足りない様子。

devkitをダウンロード:Download Archives 
展開するときは専用のディレクトリを用意したほうがいいかな。

d:\devkit>ruby dk.rb init
[INFO] found RubyInstaller v1.9.3 at C:/Ruby193

Initialization complete! Please review and modify the auto-generated
'config.yml' file to ensure it contains the root directories to all
of the installed Rubies you want enhanced by the DevKit.

d:\devkit>ruby dk.rb install
[INFO] Updating convenience notice gem override for 'C:/Ruby193'
[INFO] Installing 'C:/Ruby193/lib/ruby/site_ruby/devkit.rb'

この状態で、もう一度rails new アプリをして、エラーが出なければ終了。

なにを作る?

ここまできてから何を作るかって言うのもちょっとおばかさんなのだけれど、今回はtwitterbotを作る。

参考サイト:Herokuを使って1日1回名言をツイートするTwitter Botの作り方 - アインシュタインの電話番号☎

twitterのための設定

 botのための準備。

  1. twitterサービス用のアカウントを取得する。
  2. アカウントをサービスとしてアカウントに登録する。
twitterサービス用の設定

こちらを参考:8分21秒で分かるRubyとOAuthによるTwitterAPIの使い方(動画) - 昼メシ物語
現在(2012/7)だと記述形式はちがうけど、大体これにのっとって設定したよ。

twitter-oauthの設定

こちらを参考:Herokuを使って1日1回名言をツイートするTwitter Botの作り方 - アインシュタインの電話番号☎
すごく丁寧でわかりやすい!

この手順にほぼ従い、やっていったんだけど…。


エラーって言われるんだぜ…。

herokuのログを見る
$heroku logs
2012-07-07T16:02:27+00:00 app[web.1]: /app/config.ru:2:in `require': no such file to load -- app.rb (LoadError)
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/config.ru:2:in `block in <main>'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:51:in `instance_eval'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:51:in `initialize'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/config.ru:1:in `<main>'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/config.ru:1:in `new'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:40:in `eval'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:40:in `parse_file'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/server.rb:200:in `app'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/server.rb:301:in `wrapped_app'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/server.rb:252:in `start'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/server.rb:137:in `start'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/bin/rackup:4:in `<top (required)>'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/bin/rackup:19:in `load'
2012-07-07T16:02:27+00:00 app[web.1]: 	from /app/vendor/bundle/ruby/1.9.1/bin/rackup:19:in `<main>'
2012-07-07T16:02:29+00:00 heroku[web.1]: Process exited with status 1
2012-07-07T16:02:29+00:00 heroku[web.1]: State changed from starting to crashed

えーと設定が足りないのかしら・・・。

rubyのバージョンにあった記述になっているか?

herokuのバージョンは1.9.x。RubyでTwitterのOAuth認証に必要なトークンを取得する - アインシュタインの電話番号☎のままでは稼動できないことに気づきました。

  • ソースエンコーディングは明示的に!

マジックコメントというやつですね。

# encoding: utf-8
  • requireで相対ソースファイルを読み込むときは"."をつける。

これは1.9.xからrequireの動作が変わったのが原因。
もともとは、requireはロードパスに基づいて、ライブラリを探すのですが、1.9からカレントディレクトリが除外されたので、見に行けなくなったのでした…。
解決方法として以下のとおり。
好みがあるかも。

# 明示的カレントを見るようにする。
require './app.rb'

# 相対パスで見に行く用の記述にする
require_relative 'app.rb'

# ロードパスにカレントディレクトリを追加しちゃう
$:.unshift File.dirname(__FILE__) 
require 'app.rb'

もー!私のうっかりさんめ…
とはいえ、Railsアプリとしてnewした場合はapp.rbには最初から$:.unshift File.dirname(__FILE__)みたいな記述があるので、本当は詰まらなかったところかも。