dari88's diary

これから趣味にするプログラミング/PHP/javascript/kohana/CMS/web design/

WindowsでRuby, Rails, PostgreSQLをインストールしてherokuにデプロイする方法

 Ruby on RailsRoR)に興味をもったので、「Ruby on Rails チュートリアル」(Rails 3.2版Rails 4.0版)を読んでみました。で、実際にやってみるといろんな所で泥沼にはまります。これはもろもろの検討結果のメモです。

 

Ruby

・環境は Windows7 x64 です。

Ruby 1.9.3 と Ruby 2.0.0(32bit版) をRuby Installerでインストールしてあります。

Ruby DevKit もRuby Installerからダウンロードしてインストールしてあります。

 

Git

・以前から入っているので、コマンドプロンプトから使える状態です。後述する Heroku Toolbelt にも入っているので問題無いと思います。

Ruby on Rails

Railsインストーラーを使う方法はダメでした! Gemでインストールします。

コマンドプロンプトRuby 1.9.3 、 Ruby 2.0.0それぞれ専用のものをスタートのメニューからRubyのインストールフォルダにコピーして使っています。プロパティーの作業フォルダーはカレントディレクトリに書き換えておきます。このコマンドプロンプトは起動時に環境変数を設定しているので、Ruby 1.9.3 とRuby 2.0.0を間違いなく使い分けることができます。

・作業手順:

> gem update --system
> gem update rake
> RubyDevKit\devkitvars     ←DevKitの環境変数を設定しています
> gem install json --no-ri --no-rdoc

> gem install rails --version="~> 3.2.13" --no-ri --no-rdoc
又は
> gem install rails --version="~> 4.0.0" --no-ri --no-rdoc

jsonをインストールしておかないと、あとでコケます!

Rails 3.2 は heroku にデプロイする前に、後述するアセットのプリコンパイルをしておかないとコケます!

 

Railsアプリの作成(sqlite3を使う場合)

 ・まずプロジェクトを作るフォルダーを決めて、そこにRuby専用のコマンドプロンプトをコピーして、作業フォルダーを変更しておきます。コマンドプロンプトRubyのバージョンが分かるように名前を変えておきます。

・作業手順:

> rails new myapp
> cd myapp
> bundle install

・次に Gemfile を heroku を使う前提で変更します。

source 'https://rubygems.org'
ruby "1.9.3" 

gem 'rails', '4.0.2'

group :development do
  gem 'sqlite3'
end

gem 'sass-rails', '~> 4.0.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.0.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 1.2'

group :doc do
  gem 'sdoc', require: false
end

group :production do
  gem 'pg', '0.15.1'
  gem 'rails_12factor', '0.0.2'
end

Rubyのバージョンを明記します。

・development と production の区別をします。

rails_12factor を入れないとデプロイでコケます!

・次に、バンドルアップデートしてから、データベースを使うページを作り、サーバーを起動します。

> bundle update
> bundle install --without production
> rails g scaffold user name:string email:string
> rake db:migrate
> rails s

・ブラウザで http://localhost:3000/users を開いて動作を確認します。

・Ctrl + cでサーバーを停止します。

 

heroku にデプロイする

heroku のアカウントを取得します。

Heroku Toolbelt をダウンロードしてインストールします。

SSH接続のための公開、非公開キーが必要です。既存のものがあればそれを使います。無ければ heroku が自動的に作ってくれます。

・heroku にログインして新しいアプリを初期登録します。

> cd myapp
> heroku login
> heroku create

Ruby 2.0 のコマンドプロンプトを使った場合、heroku にログインできないケースがあります! 'DL is deprecated, please use Fiddle' が表示される場合はダメです!
 この場合は、Ruby 1.9.3 のコマンドプロンプトを使うしかありません! この作業は Ruby 1.9.3 で実行しても問題ありません。

・公開キーに日本語が含まれると heroku ではコケます! コンピュータ名が日本語だと自動作成のものには日本語が含まれてしまいます! (日本語の部分を英語に書き換えても問題ないようです・・・キーなのに?)

・キーを手動で作り、heroku に登録する方法:

> ssh-keygen -t rsa -C "root@yourmail.com"
> heroku keys:add 

・次に、Git を初期化して、コミットしておきます。

・heroku にアプリをデプロイして DB のマイグレーションを行います。

> git push heroku master
> heroku run rake db:migrate 

・heroku は自動的にトップの index ページを削除しますので、最初にアクセスすると焦ります!

http://*********.herokuapp.com/users/ にアクセスして下さい。

Rails 3.2 の場合は heroku にプッシュする前にプリコンパイルする必要があります。プリコンパイル後にGitのコミットも必要です。

> rake assets:precompile

 

deveropにPostgreSQLを使う場合

Download PostgreSQL からダウンロードしてインストールします。c:\ の直下にインストールするのが推奨です。

・64bit版でも問題ありません。また、スタックビルダから追加ソフトをインストールする必要はありません。

環境変数の Path に 'C:\PostgreSQL\9.3\bin' を追加します。

・新しいアプリを作ります:

> rails new myapp1 -d postgresql
> cd myapp1
> bundle install

PostgreSQL を使う場合は -d postgresql がポイントです!

・myapp1にて、新規のデータベースとそのユーザーを作ります:

> createuser -U postgres -P new_username
> createdb -U postgres -O new_username -E utf8 mydb1 

・Gemfile を heroku 用に書き換えます。

source 'https://rubygems.org'
ruby "2.0.0" 

gem 'rails', '4.0.2'

gem 'pg', '0.17.1'

gem 'sass-rails', '~> 4.0.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.0.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 1.2'

group :doc do
  gem 'sdoc', require: false
end

group :production do
  gem 'rails_12factor', '0.0.2'
end

・myapp1\config\database.yml を書き換えます。

development:
  adapter: postgresql
  encoding: utf8
  database: mydb1
  pool: 5
  username: new_username
  password: your_password
  host: localhost
  port: 5432

test:
  adapter: postgresql
  encoding: utf8
  database: mydb1
  pool: 5
  username: new_username
  password: your_password
  host: localhost
  port: 5432

production:
  adapter: postgresql
  encoding: utf8
  database: mydb1
  pool: 5
  username: new_username
  password: your_password
  host: localhost
  port: 5432

 ・次に、バンドルアップデートしてから、データベースを使うページを作り、サーバーを起動します。

> bundle update
> bundle install --without production
> rails g scaffold user name:string email:string
> rake db:migrate
> rails s

・ブラウザで http://localhost:3000/users を開いて動作を確認します。

・Ctrl + cでサーバーを停止します。

・heroku にデプロイする手順は上記と同じです。

 

補足事項

 以上で heroku にデプロイするまでの手順は終わりですが、補足事項をメモしておきます。

・heroku でURLを変更した場合、URLが書いてあるファイルを書き換える必要があります。 → .git/config

> git config -e

RoR チュートリアルに書いてあるルーティングの方法は古いみたいです。また、ページを追加するたびにコントローラとルートに追記が必要なの? と疑問に思っていたら違いました。

  root 'static_pages#home'
  get 'about' => 'static_pages#about'
  get 'samples/:action' => 'my_pages'
  resources :users

・3行目の書き方をしておけば、コントローラにアクションを追記するだけで済みます。

・コントローラーごとにスタイルシートを使い分けたい場合は app/assets/stylesheets にそれぞれのシートを準備します。ビューのリンク部分は次のように記述します。javascript についても同様です。

<%= stylesheet_link_tag params[:controller], media: "all", "data-turbolinks-track" => true %>
<%= javascript_include_tag params[:controller], "data-turbolinks-track" => true %>

・application.css の '*= require_tree . ' の '=' は消した方がよいでしょう。

・しかし、このままでは heroku にデプロイしても個別のスタイルシートをプリコンパイルしてくれません! 従って、CSSが全く反映されないページが表示されます!

・対策として、config/apprication.rb に以下のように追記します。

config.assets.precompile += %w( users.css static_pages.css my_pages.css)

 

地理院地図(電子国土Web)に色別標高図をオーバーレイして透過率調整を加える方法

 22日の日記の続きです。地理院地図(電子国土Web)の地図選択画面に色別標高図のオーバーレイを追加し、さらに透過率調整のスライダーを加えてみました。

 

主な追加点

 cahngeMap2 では色別標高図のデータをもらってきて、オーバーレイヤーを作成し、透過率の設定に応じて色別標高図の透過率を設定しています。opachange はスライダーを動かすと呼ばれ、色別標高図の透過率を変更して表示数値も変更しています。

var layer_relief;
function changeMap2(mapid,checked){
    if(checked === true) {
        var data = libraryMapData[1];
        var layers = [];
        mapHandler.addMapLayer(data,layers);
        layer_relief = layers["relief"];
        layer_relief.setOpacity(opavalue/100);
        setTimeout('layer_relief.redraw(true);', 3000);
    }else{
        map.removeLayer(layer_relief);
    }

}

function opachange(value){
    opavalue = value;
    document.getElementById("slider2-value").textContent = "透過率調節: "+opavalue;
    if (layer_relief){
        layer_relief.setOpacity(opavalue/100);
    }
}

 

主な追加点の修正(14/01/19 追記)

 国土地理院は libraryMapData[] を書き換えることが判明しました。対策として色別標高図の定義オブジェクトを直接記述する仕様に変更しました。

var layer_relief;
function changeMap2(mapid,checked){
    if(checked === true) {
//        var data = libraryMapData[2];
        var data = {
          dataset : "dataSet_Relief",
          icon : false,
          key : "relief",
          legendFunc : "getLibraryLegend_relief",
          title : "色別標高図",
          type : "map",
          zindex : 215
        };
        var layers = [];
        mapHandler.addMapLayer(data,layers);
        layer_relief = layers["relief"];
        layer_relief.setOpacity(opavalue/100);
        setTimeout('layer_relief.redraw(true);', 3000);
    }else{
        map.removeLayer(layer_relief);
    }

}

 

サンプル

 詳細はサンプルページのソースを見てください。「// ヤマレコ用」とコメントが付いている付近を追えば大体分かると思います。

 

f:id:dari88:20131225220228j:plain

 淡色地図に色別標高図をオーバーレイしたところです

 

f:id:dari88:20131225220513j:plain

 Googleの地形図に色別標高図をオーバーレイしたものです。いつものGoogleマップに色気が出ていますね

 

電子国土WebのページにGoogleマップを追加する方法

 電子国土WebのVer.4は基本的OpenLayersに準拠しているので、電子国土WebのページGoogleマップのレイヤーを追加してみました。これが結構難しいんですね。で、難儀した点をメモしておきます。これは12月6日の日記の続きです。

 

難点1

 電子国土Webでは地理院地図を切り替えるたびに地理院地図のレイヤーを削除して新規にレイヤーを定義しています。このためGoogleマップのレイヤーを単純に追加しただけでは表示に混乱を来します。

 対策として、function changeMap()をhtmlに記述して再定義します。初期にmapに追加したgoogleマップを一旦削除して、地理院地図をmapに追加した後に再度mapに追加するように書き換えました。

function changeMap(mapid){
    // ヤマレコ用
    map.removeLayer(Google_Physical);
    map.removeLayer(Google_Streets);
    map.removeLayer(Google_Hybrid);
    map.removeLayer(Google_Satellite);

        if (!mapid) {
                return;
        }
        
        var tmpDataSet = dataSetObj.dataSet_Default;
        var overLayerZindex = 110;
        
        if(overLayer){
                map.removeLayer(overLayer);
                overLayer = null;
        }

        var overlayDataSet = null;
        var overlayName;

        switch (mapid){
                case 'Spring':  tmpDataSet = dataSetObj.dataSet_Spring; overlayName = "Color"; overlayDataSet = dataSetObj.dataSetOverlay_Color; break;
                case 'Summer':  tmpDataSet = dataSetObj.dataSet_Summer; overlayName = "Color"; overlayDataSet = dataSetObj.dataSetOverlay_Color; break;
                case 'Summer_n':        tmpDataSet = dataSetObj.dataSet_Summer_n; overlayName = "Color"; overlayDataSet = dataSetObj.dataSetOverlay_Summer_n; break;
                case 'Autumn':  tmpDataSet = dataSetObj.dataSet_Autumn; overlayName = "Color"; overlayDataSet = dataSetObj.dataSetOverlay_Color; break;
                case 'Autumn_n':        tmpDataSet = dataSetObj.dataSet_Autumn_n; overlayName = "Color"; overlayDataSet = dataSetObj.dataSetOverlay_Autumn_n; break;
                case 'Winter':  tmpDataSet = dataSetObj.dataSet_Winter; overlayName = "Color"; overlayDataSet = dataSetObj.dataSetOverlay_Color; break;
                case 'Grey':    tmpDataSet = dataSetObj.dataSet_Mono;   overlayName = "Grey";  overlayDataSet = dataSetObj.dataSetOverlay_Mono;  break;
                case 'Grey_n':  tmpDataSet = dataSetObj.dataSet_Mono_n;   overlayName = "Grey";  overlayDataSet = dataSetObj.dataSetOverlay_Mono_n;  break;

                case 'Season':  tmpDataSet = dataSetObj.dataSet_Season; break;
                case 'Blank':   tmpDataSet = dataSetObj.dataSet_Blank;          break;
                case 'Relief':  tmpDataSet = dataSetObj.dataSet_Relief; break;
                case 'Jaise':   tmpDataSet = dataSetObj.dataSet_Jaise;          break;
                case 'DJBMO':   tmpDataSet = dataSetObj.dataSet_Y2K7; overlayName = "IndexY2K7"; overlayDataSet = dataSetObj.dataSetOverlay_IndexY2K7; break;
                case 'NLII4':   tmpDataSet = dataSetObj.dataSet_YK88;           break;
                case 'NLII3':   tmpDataSet = dataSetObj.dataSet_YK84;           break;
                case 'NLII2':   tmpDataSet = dataSetObj.dataSet_YK79;           break;
                case 'NLII1':   tmpDataSet = dataSetObj.dataSet_YK74;           break;
                case 'DJBMM_n': tmpDataSet = dataSetObj.dataSet_Default_n;      break;
                case 'pale':    tmpDataSet = dataSetObj.dataSet_Pale;           break;
                case 'Default':
                default:                tmpDataSet = dataSetObj.dataSet_Default;        break;
        }

        var command = "";
        
        var baseZIndex = 100;
        if (webtisMap) {
                baseZIndex = webtisMap.getZIndex();
                map.removeLayer(webtisMap);
        }

        webtisMap = new webtis.Layer.BaseMap("地理院地図", {
                wrapDateLine: true,
                dataSet: tmpDataSet,
                errorImagePath: webtis.TILE_URL.NODATA
        });

        map.addLayer(webtisMap);
        webtisMap.setZIndex(baseZIndex);
        
        map.addLayers([Google_Physical, Google_Streets, Google_Hybrid, Google_Satellite]);  // ヤマレコ用
        
        setTimeout('webtisMap.redraw(true);', 3000);

        if (overlayDataSet != null) {
                overLayer = new webtis.Layer.BaseMap(overlayName, {
                        isBaseLayer: false,
                        dataSet: overlayDataSet,
                        wrapDateLine: true
                });
                map.addLayer(overLayer);
                overLayer.setZIndex(overLayerZindex);
                
                setTimeout('overLayer.redraw(true);', 3000);
        }
}

 

 難点2

 LayerSwitcherのアイコンの表示位置を変更するのが極めて困難でした。普通はstyle.cssのクラス.olControlLayerSwitcherに対して定義しているLayerSwitcherの配置をhtml内で再定義すれば良さそうなものです。しかし、電子国土Webではstyle.cssjavascript内から読み込んでいるので、手に負えません。ネットで調べても対策は得られす、試行錯誤した結果次の結論に至りました。

    LayerSwitcher = new OpenLayers.Control.LayerSwitcher();
    map.addControl(LayerSwitcher);
    document.getElementById(LayerSwitcher.id).style.top="50px";   

これはベスト解でしょうね。

 

サンプルサイト

 詳しいソースコードこちらのデモページのソースを見てください。ヤマレコ用とコメントのある場所を見れば大概分かると思います。

 

f:id:dari88:20131223044511j:plain

Ruby 1.8.7 & 1.9.3 の開発環境(IDE)について

 Ruby でのプログラミングにおいてもやはり統合開発環境(IDE)は非常に役に立ちます。で、メモです。

Ruby のインストール

 Downloads - RubyInstaller からダウンロードしてインストルするのが確実です。

Ruby 1.8.7 & 1.9.3 の使い分け

 NetBeans にて切り替えて使い分ければ済みます。

NetBeans のインストール(アップデート)

 V7.3.1 を使います。次に、Ruby用のプラグインを Ruby and Rails - plugin にて NetBeans 7.3 のタブからダウンロードしてインストールします。

Fast Debugger のインストール

 最初に Downloads - RubyInstaller から DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe をダウンロードしてインストールします。C:\ の直下に RubyDevKit というフォルダーを作成してそこで実行すると良いでしょう。解凍が済んだら、コマンドプロンプトでそこに移動して,

  > ruby dk.rb init

  > ruby dk.rb install

を実行します。

 次は、Fast Debugger をインストールしますが、NetBeans や gem でインストールしようとしても時間の無駄です。RubyGems.org のページにて "ruby-debug-ide" で検索してこのページから ruby-debug-ide をクリックしてリンク先のページから gemファイルを入手します。これを適当なところに移動して、コマンドプロンプトにて gem でインストールします。

 尚、ruby-debug-ide を選択した場合は、日本語を含むプログラムの場合はコケます。ruby-debug-ide19 については試していないので分かりません。

 

Ruby 1.8.7 で認証proxiを越える: open-uri.rb にパッチをあてる方法

 Mac/Linux 用の配布用実行ファイルを作るには Ruby 1.8.7 を使わざるを得ません。で、open-uri で認証プロクシサーバーを越えようとしても動きません。調べてみると open-uri.rb が未完成状態なんですね!

 ネットで調べると、次のように書けば動きそうな感じがしたのですが・・・。

options = {:proxy => proxy_host,:http_basic_authentication => [proxy_user,proxy_passwd],
                "User-Agent" => useragent}

 そこで open-uri.rb を追ってみると、確かに動かない仕様になっています。

 

パッチの当て方

 Ruby 1.8.7 の open-uri.rb の215行目付近は次のように書いてあります。

      if proxy
        klass = Net::HTTP::Proxy(proxy.host, proxy.port)
      end

  これを次のように書き換えます。

      if proxy
        if options.include? :http_basic_authentication
          user, pass = options[:http_basic_authentication]
          klass = Net::HTTP::Proxy(proxy.host, proxy.port,user,pass)
        else
          klass = Net::HTTP::Proxy(proxy.host, proxy.port)
        end
      end

 

因みに Ruby 1.9.3 では?

 Ruby 1.9.3 では上記の options 指定では動きません。次のように書く必要があります。

options = {:proxy_http_basic_authentication => [proxy_host,proxy_user,proxy_passwd],
                "User-Agent" => useragent}

 

ヤマレコの日記を一括ダウンロードしてバックアップする ヤマレコダイアリー

 ヤマレコの日記を一括ダウンロードしてバックアップするヤマレコダイアリーのダウンロードページです。

 

Windows版の使い方

・プログラムをダウンロードします。
  YamarecoDiary.exe V1.7 2014/02/15

・ヤマレコの日記を保存するファオルダーを決めて、そこにこのプログラムを移動します。ヤマレコダウンローダーと同じフォルダーでも問題ありません。

・ヤマレコのユーザー番号を調べてください。これはユーザーIDではありません。
 ヤマレコの日記をどれか開きます。ブラウザの上部にURLが表示されているはずです。URLが http://www.yamareco.com/modules/diary/1234-detail-56789 であれば、ユーザー番号は 1234 です。尚、日記番号は 56789 です。

・YamarekoDiary.exe を実行します。案内が出るので、最初にユーザー番号を入力してください。次に実行モードを聞かれます。通常は何も入力しないで Enterキー です。

・プログラムが終了したら、フォルダーに作成された index-diary1234-2013.html みたいな名前のファイルを開いてください。

・日記のインデックスは年ごとに作成されます。これらをブラウザーのブックマーク(お気に入り)に登録して下さい。

 

Mac/Linux版の使い方

 ・プログラムをダウンロードします。
  Ydiary V1.7 2014/02/15

・以降の使い方は ヤマレコダウンローダー と同様です。

 

<注意事項等>

・2014年1月8日にヤマレコのデザインが一新されました。旧版を使い続けると表示がおかしくなりますので、新版を使ってください。この際、旧バックアップは恒久保存版にして、新しいフォルダーにバックアップすることをお勧めします。

・プログラムはログインしません。従って、一般公開されていない日記があるとこれを取得できません。全て一般公開してから実行してください。

・ヤマレコのサイト側で仕様変更が行われると日記の再現性が悪化する可能性があります。

・取得済の日記は再取得しません。取得済みの日記を修正した場合は日記番号を指定して再取得することが可能です。後からコメントが付いた場合などにも便利です。

・少なくとも一年に一回はバックアップを取ってください。二年以上前の年に未取得の日記があると取りにいきません。

・山友への再配布はご自由になさってください。

 

<備考>

 表示している記号の意味は以下のとおりです。

* 今回ファイルを取得した
x サーバーにファイルが無い
. 既にファイルを取得済み

_ 時間待ち

 

proxyサーバー対応

 ヤマレコダイアリーは proxyサーバーに対応しています。プログラムと同じフォルダーに proxy.txt という名前のテキストファイルを作り、次の内容を記入して下さい。

<ヤマレコダウンローダー用プロキシ設定ファイル>
'=' の前後にスペースを入れてはいけません

proxy_use=1 プロキシを使う場合は 1 proxy=http://example.proxy.co.jp:8080 プロキシのホスト名:ポート番号 userid= ユーザーID(認証が必要なら) password= パスワード(認証が必要なら)

 

<履歴>

V1.7:2014/02/15 ヤマレコの仕様変更対応
・広告の仕様変更に対応した
・js,cssのリンク形式変更に対応した
V1.6:2014/01/18 日記のデザイン変更に対応した
・プロフ写真のサイズほか各種変更に対応

V1.5:2014/01/18 ヤマレコの仕様変更対応
・js,css圧縮形式で送る仕様になったので解凍処理を追加した

V1.4:2014/01/11 ヤマレコのデザイン変更に対応した
 ・日記本文に他の日記へのリンクがあると取りに行くバグを修正した

V1.3:2013/12/15 広告対策のみ
 ・グーグル広告の仕様変更に対処した

V1.2:2013/12/15 サーバーがこけている時の対処等
 ・ヤマレコのサーバーが不調の場合やメンテ中の場合に対処した
 ・ファイルのアイコンをピンクのYdアイコンに入れ替えた
 ・proxi.txt のエンコード対応に関して改善した

V1.1:2013/12/14 新年の処理に問題があった点などを修正した
 ・新年になった時、年末に未取得の日記があると取りそこねる問題を修正した
 ・Mac/Linux版も proxyサーバー対応にした
 ・.jpeg という拡張子のファイルを取りそこねていたので修正した

V1.0:2013/12/12 初版公開

 

ソースコード

・下記URLからダウンロードして下さい。

 https://mega.co.nz/#F!Dooi1Kbb!Jl_puAvAPqWWUIEWnqK_vQ

 

Rubyプログラムを Ruby 1.9.3 と Ruby 1.8..7 両対応にする方法

 Ruby の配布用実行ファイルを作る場合、Windows用では Ruby1.9.3 対応の ocra が便利ですが、Mac/Linux用では Ruby 1.8.7 対応の rubyscript2exe を使うしか方法がありません。同じプログラムを2本メンテナンスするのは大変なので、一本化を検討してみました。

 ヤマレコダウンローダーで引っかかったのはエンコードの変換に関わる iconv と encode です。で、その回避法のメモです。

 

エンコードの変換

 プログラムの冒頭で Ruby のバージョンを確認して、iconv をインクルードするか否かを指定します。

if /^1\.8/.match(RUBY_VERSION) 
  require 'iconv'
end

 実際に文字コードを変換する部分では、

      if /^1\.8/.match(RUBY_VERSION) 
        html = Iconv.iconv('UTF-8','EUC-JP',html)[0]
      else
        html = html.encode("UTF-8","EUC-JP")
      end

のように Ruby のバージョンに応じてエンコードの変換を使い分けします。