Herokuに簡単なSinatraアプリをデプロイしてみた

参考にしたのはこのページ。

 cf. herokuでsinatraアプリをデプロイしてみた

順を追ってやってみる。

ログインと公開鍵の登録

Heroku Toolbelt のインストールは終わっているので、キーの登録から。

^o^ > heroku login
Enter your Heroku credentials.
Email: [email protected]
Password (typing will be hidden):
Could not find an existing public key.
Would you like to generate one? [Yn] Y
Generating new SSH public key.
Uploading SSH public key C:/Users/hiro/.ssh/id_rsa.pub... done
Authentication successful.

ログインしようとすると public key がない、作るか?といわれるので、Y と答える。これでキーができたはず。
もう一回ログイン。

^o^ > heroku login
Enter your Heroku credentials.
Email: [email protected]
Password (typing will be hidden):
Authentication successful.

今度はログインできた。じゃ次、public key(公開鍵)の登録。

^o^ > heroku keys:add
Found existing public key: C:/Users/takatoh/.ssh/id_rsa.pub
Uploading SSH public key C:/Users/takatoh/.ssh/id_rsa.pub... done

よし、OK。

簡単なSinatraアプリを作る

チョー簡単な、Hello, Heroku と表示するだけのアプリ。

Gemfile:

source "https://rubygems.org"

gem "sinatra"

Procfile:

web: bundle exec rackup config.ru -p $PORT

sample.rb:

require 'bundler/setup'
require 'sinatra/base'

class Sample < Sinatra::Base
  get "/" do
    "Hello, Heroku."
  end
end

config.ru:

$:.unshift(File.dirname(__FILE__))
require 'sample'

run Sample 

.gitignore:

.bundle

まずはこれだけを Git のリポジトリに commit する。

^o^ > git init
Initialized empty Git repository in C:/Users/takatoh/Documents/w/takatoh-sample/
.git/

^o^ > git add .

^o^ > git commit -m "First commit."
[master (root-commit) fa02bb8] First commit.
 5 files changed, 19 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Gemfile
 create mode 100644 Procfile
 create mode 100644 config.ru
 create mode 100644 sample.rb

ローカルで試してみる

^o^ > foreman start
'foreman' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

あれ、ダメじゃん。foreman とやらはインストールされてないのかな。まあいいや。

デプロイしてみる

^o^ > heroku create
Creating limitless-sea-3233... done, stack is cedar
http://limitless-sea-3233.herokuapp.com/ | [email protected]:limitless-sea-3233.git

Git remote heroku added

^o^ > git remote -v
heroku  [email protected]:limitless-sea-3233.git (fetch)
heroku  [email protected]:limitless-sea-3233.git (push)

heroku create にアプリ名を与えないと適当な名前がつくらしい。上では limitless-sea-3233 ってのがそう。git のリモートリポジトリに heroku が追加されてるのがわかる。
それじゃ、Heroku にデプロイしてみよう。

^o^ > git push -u heroku master
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

あれ、だめだ。なんでだ。
しばらく考えて、ログを見て気がついた。さっき登録したキーは C:/Users/takatoh/.ssh/ に保存されている。けど、普段 git でつかっているキーは C:\Users\takatoh\Documents\.ssh にある。これじゃないかと思って、(バックアップを作ったうえで)上書きしてみた。今度はどうかな。

^o^ > git push -u heroku master
Initializing repository, done.
Counting objects: 7, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (7/7), 629 bytes | 0 bytes/s, done.
Total 7 (delta 0), reused 0 (delta 0)

-----> Ruby app detected
-----> Compiling Ruby/NoLockfile
 !
 !     Gemfile.lock required. Please check it in.
 !

 !     Push rejected, failed to compile Ruby app

To [email protected]:limitless-sea-3233.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to '[email protected]:limitless-sea-3233.git'

まだだめだ。今度はなんだ。どうやら Gemfile.lock が必要で、チェックインしろといっている。

^o^ > git status
On branch master
Untracked files:
  (use "git add ..." to include in what will be committed)

        Gemfile.lock

nothing added to commit but untracked files present (use "git add" to track)

^o^ > git add Gemfile.lock
warning: LF will be replaced by CRLF in Gemfile.lock.
The file will have its original line endings in your working directory.

^o^ > git commit -m "Gemfile.lock: Add."
[master f84a54a] Gemfile.lock: Add.
warning: LF will be replaced by CRLF in Gemfile.lock.
The file will have its original line endings in your working directory.
 1 file changed, 17 insertions(+)
 create mode 100644 Gemfile.lock

さあ、今度はどうだ。

^o^ > git push -u heroku master
Initializing repository, done.
Counting objects: 10, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (10/10), 1009 bytes | 0 bytes/s, done.
Total 10 (delta 1), reused 0 (delta 0)

-----> Ruby app detected
-----> Compiling Ruby/Rack
-----> Using Ruby version: ruby-2.0.0
-----> Warning:
       Removing `Gemfile.lock` because it was generated on Windows.
       Bundler will do a full resolve so native gems are handled properly.
       This may result in unexpected gem versions being used in your app.
       In rare occasions Bundler may not be able to resolve your dependencies at
 all.
       https://devcenter.heroku.com/articles/bundler-windows-gemfile
-----> Installing dependencies using 1.5.2
       Running: bundle install --without development:test --path vendor/bundle -
-binstubs vendor/bundle/bin -j4
       Fetching gem metadata from https://rubygems.org/...........
       Fetching additional metadata from https://rubygems.org/..
       Resolving dependencies...
       Using bundler (1.5.2)
       Installing rack (1.5.2)
       Installing tilt (1.4.1)
       Installing rack-protection (1.5.3)
       Installing sinatra (1.4.5)
       Your bundle is complete!
       Gems in the groups development and test were not installed.
       It was installed into ./vendor/bundle
       Bundle completed (8.62s)
       Cleaning up the bundler cache.
-----> WARNINGS:
       You have not declared a Ruby version in your Gemfile.
       To set your Ruby version add this line to your Gemfile:
       ruby '2.0.0'
       # See https://devcenter.heroku.com/articles/ruby-versions for more inform
ation.

       Removing `Gemfile.lock` because it was generated on Windows.
       Bundler will do a full resolve so native gems are handled properly.
       This may result in unexpected gem versions being used in your app.
       In rare occasions Bundler may not be able to resolve your dependencies at
 all.
       https://devcenter.heroku.com/articles/bundler-windows-gemfile
-----> Discovering process types
       Procfile declares types -> web
       Default types for Ruby  -> console, rake

-----> Compressing... done, 12.1MB
-----> Launching... done, v4
       http://limitless-sea-3233.herokuapp.com/ deployed to Heroku

To [email protected]:limitless-sea-3233.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from heroku.

うまくいったかな。

^o^ > heroku open
Opening limitless-sea-3233... done
hello-heruku

うまくいった!

Herokuに登録してみた

とりあえずはサインアップしただけ。

 https://www.heroku.com/

右上の Sign up をクリックするとメールアドレスを入力する画面になるので、入力して Sign up ボタンをクリック。すると、確認のメールを送ったとなるので、メールにあるリンクを開くと今度はメールアドレスとパスワードを入力する画面になる。ここで再度メールアドレスと、パスワードを入力すれば、無事にサインアップは完了。
下がサインアップが完了したあとの画面。
heroku-dashboad

Download Heroku Toolbelt for Windows とあるので、ダウンロードしてインストールした。
これで Heroku を使う準備はできたかな。

WordPressのデータをAmazon S3にバックアップする

このブログもバックアップをとっておきたいと思っていたところ。S3 を使えるようにしたことだし、S3にバックアップできるツールがないかと咲かしてみたら、Automatic WordPress Backup というプラグインを見つけた。
参考にしたのはこのページ。
 cf. Automatic WordPress Backupを使って、Amazon S3にWordPressのバックアップを取る – non-standard world, Inc.

インストールは簡単、ほかのプラグインのように、検索すれば一発で出てくる。
インストールすると、ダッシュボードの左側に Volcanic というメニューが追加されている。ここから Automatic Backup を選んでクリックすると、設定画面になる。この画面で Amazon S3 の Access Key ID と Secret Access key、バックアップ先のバケット名を指定する。バケットは先に作っておいてもいいし、ここで新しいバケット名を指定すればプラグインが作ってくれる。今回は blog-panicblanket というバケットを新しく作ることにした。
さて、あとは Backup schedule の欄で Daily を選んで、Save Changes And Backup Now をクリックすれば完了。
Amazon S3 のほうでも blog-panicblanket バケットの中に、awd/blog.panicblanket.com/2014-06-14-1359.zip として保存されているのが確認できた。

aws-sdkを使ってAmazon S3を操作する

ちょっと間が開いたけど、インストールした aws-sdk を使ってみた。

created-bucket

まずはバケットを作る。aws-sdk を使ってもできるけど、ブラウザからやった。これがバケットを作ったところ。見難い(てか見えない)けど panicblanet ていうバケットができている。

次に、API にアクセスするための Access Key ID と Secret Access Key を取得する。これは右上の自分の名前が書いてあるところをクリックすると現れるプルダウンメニューから Security Credentials を選ぶと、キーを作れるページに移動するのでそこで作る。詳しくは書かないけど難しくはない。

さて、これで準備が整った。それじゃ最初はファイルをアップロードするスクリプトから行こう。

require 'aws'
require 'yaml'

config = YAML.load_file("aws-config.yaml")

file = ARGV.shift
key = ARGV.shift
content = File.open(file, "rb"){|f| f.read}

s3 = AWS::S3.new(
  :access_key_id     => config["ACCESS_KEY_ID"],
  :secret_access_key => config["SECRET_ACCESS_KEY"])

bucket = s3.buckets["panicblanket"]
obj = bucket.objects[key]
if obj.exists?
  puts "Error: Object already exists: #{key}."
  exit
end
obj.write(content, :acl => :public_read)

キーをハードコードするのはアレなので aws-config.yaml ファイルから読み込むようにした。早速試してみよう。

^o^ > ruby s3_upload_file.rb Penguins.jpg Penguins.jpg
uploaded_object

何も出力されないのはうまくいったからのはずだ。ブラウザで確認してみると、ちゃんとアップロードされているのがわかる。

penguins

アクセスコントロールに :public_read を指定しているので、誰でもブラウザで見られるはずだ。URL は http://panicblanket.s3.amazonaws.com/Penguins.png。

うん、うまくいってる。

バケットの中のオブジェクトを指定するキーはディレクトリ風にもできる。こんなふうに。

^o^ > ruby s3_upload_file.rb Penguins.jpg data/Penguins.jpg

それじゃ次はバケットに保存されているオブジェクトのリストを取得してみる。

require 'aws'
require 'yaml'

config = YAML.load_file("aws-config.yaml")

s3 = AWS::S3.new(
  :access_key_id     => config["ACCESS_KEY_ID"],
  :secret_access_key => config["SECRET_ACCESS_KEY"])

bucket = s3.buckets["panicblanket"]
bucket.objects.each do |obj|
  puts obj.key
end
^o^ > ruby s3_list_objects.rb
Penguins.jpg
data/Penguins.jpg

上でアップロードした2つのオブジェクトが表示された。

つぎ、ダウンロード。

require 'aws'
require 'fileutils'
require 'yaml'

config = YAML.load_file("aws-config.yaml")

key = ARGV.shift

s3 = AWS::S3.new(
  :access_key_id =&gt; config["ACCESS_KEY_ID"],
  :secret_access_key =&gt; config["SECRET_ACCESS_KEY"])

file = key
if File.dirname(file) != "."
  FileUtils.mkdir_p(File.dirname(file))
end

bucket = s3.buckets["panicblanket"]
obj = bucket.objects[key]

unless obj.exists?
  puts "Error: No such object: #{key}"
  exit
end

File.open(file, "wb") do |f|
  obj.read do |chunk|
    f.write(chunk)
  end
end
^o^ > ruby s3_download_object.rb data/Penguins.jpg

^o^ > ls data
Penguins.jpg

これもうまくいってる。よしよし。

それじゃ最後に、オブジェクトの消去。

require 'aws'
require 'yaml'

config = YAML.load_file("aws-config.yaml")

s3 = AWS::S3.new(
  :access_key_id     => config["ACCESS_KEY_ID"],
  :secret_access_key => config["SECRET_ACCESS_KEY"])
bucket = s3.buckets["panicblanket"]

key = ARGV.shift
obj = bucket.objects[key]
unless obj.exists?
  puts "No such object: #{key}"
  exit
end
obj.delete
^o^ > ruby s3_delete_object.rb data/Penguins.jpg

^o^ > ruby s3_list_objects.rb
Penguins.jpg

ちゃんと消えたようだ。

aws-sdkをgemでインストールしたらNokogiriも使えるようになった

Amazon の S3 を使ってみようと思っていろいろ調べた結果、aws-sdk という gem をインストールすればいいというのがわかったので、インストールしたら Nokogiri も一緒にインストールされた。

^o^ > gem install aws-sdk
Fetching: mini_portile-0.6.0.gem (100%)
Successfully installed mini_portile-0.6.0
Fetching: nokogiri-1.6.2.1-x64-mingw32.gem (100%)
Nokogiri is built with the packaged libraries: libxml2-2.8.0, libxslt-1.1.28, zl
ib-1.2.8, libiconv-1.14.
Successfully installed nokogiri-1.6.2.1-x64-mingw32
Fetching: aws-sdk-1.42.0.gem (100%)
Successfully installed aws-sdk-1.42.0
Parsing documentation for mini_portile-0.6.0
Installing ri documentation for mini_portile-0.6.0
Parsing documentation for nokogiri-1.6.2.1-x64-mingw32
unable to convert "\x90" from ASCII-8BIT to UTF-8 for lib/nokogiri/2.0/nokogiri.
so, skipping
unable to convert "\x90" from ASCII-8BIT to UTF-8 for lib/nokogiri/2.1/nokogiri.
so, skipping
Installing ri documentation for nokogiri-1.6.2.1-x64-mingw32
Parsing documentation for aws-sdk-1.42.0
Installing ri documentation for aws-sdk-1.42.0
3 gems installed

うーん、前に苦労してダメだったのはなんだったんだ。

Schemeでマージソート

やってみた。

(define merge-sort
  (lambda (lis)
    (if (= (length lis) 1)
      lis
      (let* ((n (div (length lis) 2))
             (left (take lis n))
             (right (drop lis n)))
        (merge (merge-sort left) (merge-sort right))))))

(define merge
  (lambda (lis1 lis2)
    (let loop ((l1 lis1) (l2 lis2) (l0 '()))
      (cond ((and (null? l1) (null? l2)) (reverse l0))
            ((null? l1) (loop l1 (cdr l2) (cons (car l2) l0)))
            ((null? l2) (loop (cdr l1) l2 (cons (car l1) l0)))
            ((< (car l1) (car l2)) (loop (cdr l1) l2 (cons (car l1) l0)))
            (else (loop l1 (cdr l2) (cons (car l2) l0)))))))

(print (merge-sort '(5 1 7 6 8 2 9 3 4)))

実行結果:

^o^ > gosh merge-sort.scm
(1 2 3 4 5 6 7 8 9)

自前のWebアプリにLightbox風のjQueryプラグイン、Colorboxを導入してみた

この間、といっても先月の頭のことだけど、昔作った CGI プログラムを手を入れた、という話とちょろっとだけした(そのときのエントリの話題は CGI プログラムそのものじゃなくて markdown と RDiscount)。
で、今日はその CGI プログラムに Colorbox という Lightbox 風の jQuery プラグインを導入してみたので、そのメモ。
Lightbox 風のプラグインて数え切れないほどあるけど、はっきり言ってどれがいいのやらよくわからない。なのでたまたま最初のほうに出てきた Colorbox が悪くなさそうだったので使ってみた、というわけ。
Colorbox のサイトはここ。

 cf. Colorbox – a jQuery lightbox

参考にしたのはこのページ。

 cf. Lightbox風のjQueryプラグインColorBoxの使い方 – bl6.jp

基本的には上に書いたページに沿ってやっただけで、特別なカスタマイズはしてない。

まずは、スクリプトとスタイルシートを CGI のフォルダの下に js というフォルダを作って配置する。
で、表示に使うテンプレートのほうで読み込みと設定をすればいい。読み込む部分はこう。

<script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="js/colorbox/jquery.colorbox-min.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        $("a[rel='colorbox']").colorbox();
    });
 </script>

これは <head> 要素の中に書いた。これで rel 属性が ‘colorbox’ の <a>要素が Colorbox の対象になる。なので表示したい画像のリンクに rel=”colorbox” を追加する。

<a href="<%= escape(File.join(@src_prefix, i['path'])) %>" rel="colorbox">

href 属性の中身が escape(File.join.... となってるのは動的に変化する部分だから。

これで設定は終わり。これだけで、モーダルな画像表示ができてスライドショウもできる。なんだかちょっとだけかっこよくなったかも。

最後に jQuery のサイトへのリンクも張っておこう。

cf. jQuery

Sublime Text 3のスニペットが便利

この間から、CodeEval をチョコチョコとやっていて、いつのまにやら日本国内でのランクが5になっていたよ。ま、人数が少ないようだけど。
で、たまに Ruby を使うけどたいていは Python でコードを書いている。そのコードなんだけど、プログラムの要求条件がファイルから入力して1行ずつ処理する、というのがほとんどだ。ということは毎回同じコードを書くことになるわけだけど、ここで Sublime Text のスニペットを使うと便利なので、今日はその作り方のメモ。

メニューから [ツール] → [スニペット追加] を選ぶと、次のような雛形が表示される。これを編集して、保存すればいいわけだ。

<![CDATA[
  Hello, ${1:this} is a ${2:snippet}.
]]>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<!-- <tabTrigger>hello</tabTrigger> -->
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<!-- <scope>source.python</scope> -->

で、どこをどう編集すればいいのかというと、まずは CDATA の中身(Hello,~となっているところ)。ここが実際に貼り付けられるコード片になる。${1:this} とか ${2:snippet} となっているところは一種の変数のようなもので、貼り付けたあと続けて書き換えられるようにカーソルが移動する。もちろんなくてもいい。${0} と書いておくと貼り付けた後そこにカーソルが移動する。
<tabTrigger> の中はスニペットを呼び出すショートカットのようなもので、これを入力してタブキーを押すとスニペットが貼り付けられる。
<scope> はどんな状況で有効になるか、らしい。source.python とあるのは Python のソースコードを書いているときに有効になる。

結局、こういうのを書いた。

<![CDATA[
import sys

test_cases = open(sys.argv[1], 'r')<br /]]> for test in test_cases:
${0}

test_cases.close()

]]>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
codeeval-python2
<!-- Optional: Set a scope to limit where the snippet will trigger -->
source.python

これを、C:\Users\takatoh\AppData\Roaming\Sublime Text 3\Packages\User フォルダの下に、codeeval-python2.sublime-snippet という名前で保存する。Sublime Text は .sublime-snippet という(長い)拡張子のファイルをスニペットと認識するようだ。フォルダは上記のフォルダ直下でなくてもいいみたいなので、Sunippets というフォルダを作ってその中に保存した。

さあ、準備は完了したので使ってみよう。Sublime Text を立ち上げて新規ファイルを開く。何も書く前に test.py とでも名前をつけて保存する。これで Python を編集するモードになって、上のスニペットが使えるようになったはずだ。
codeeval-python2 とタイプしてタブキーを押すとスニペットが貼り付けられるはず・・・だけど c をタイプした時点で候補に出てきた。これは便利。

Windows7でSublime Text 3を使ってみる

普段は秀丸エディタを使ってるんだけど、Sublime Text 3 を導入してみたのでメモ。といってもググればいくらでも出てくるので、要点だけ。参考にしたページは主にここ。

 cf. 『初心者向け』 Sublime Text 3 導入と日本語化について – Naverまとめ

ダウンロードとインストール

ダウンロードはここから。

Windows の 64bit 版をダウンロード。
ダウンロードしたファイルをダブルクリックしてインストール。インストール自体は簡単そのもの。

Package Control

Sublime Text 3 本体のインストールが終わったら、とにもかくにも Package Control をインストールする。Sublime Text には多くのパッケージ、プラグインが用意されていて、それを管理するもののようだ。メニューから [View] → [Show Console] を選ぶか、[Ctrl]+[`] キーを押してコンソールを表示する。次のページから、Package Control インストール用の Python スクリプトをコピペして Enter。

すると、[Tools] → [Command Palette] の中に Package Control が追加される。

日本語化

メニュー等の日本語化。上で追加された Package Control を使って Japanize というパッケじをインストールする。
インストール自体が無事に終わると、設定の仕方がエディタの画面に現れるので、そのとおりにする。以下メッセージから引用。

適用手順
1.C:\Users\ユーザー名\AppData\Roaming\Sublime Text 3\Packages\Japanizeにインストールされている*.jpファイルを、
   C:\Users\ユーザー名\AppData\Roaming\Sublime Text 3\Packages\Default
  にコピーします。※Defaultフォルダがない場合は作成してください。
2.コピーしたファイルをオリジナルのファイル(.jpが付かないファイル)と置き換えます。(念のため、オリジナルのファイルが有る場合は.orgなどを付けて保管しておきましょう。)
3.C:\Users\ユーザー名\AppData\Roaming\Sublime Text 3\Packages\Japanize\Main.sublime-menu(.jpが付かない方)を、
   C:\Users\ユーザー名\AppData\Roaming\Sublime Text 3\Packages\User
  にコピーします。すると、他のプラグインで上書きされてしまっているトップメニューも日本語化されます。
以上です。

これでメニュー等の日本語化は終了。

その他のパッケージ

とりあえずインストールしたパッケージは次の2つ。

  • ConvertToUTF8
  • IMESupport

Sublime Text は UTF-8 が規定の文字コードなので、Shift JIS のファイルを編集するときには不都合がある。ConvertToUTF8 はファイルを開くときに UTF-8 に変換し、保存するときには Shift JIS で保存してくれる。
IMESupport は日本語入力をインラインでできるようにするパッケージ。

その他の設定

設定は基本的に Json のテキストファイルでするようになっている。メニューから [基本設定] → [基本設定 – ユーザ] を選ぶと、Preferences.sublime-settings というファイルが開く。以下の設定を追加してファイルを保存すると、設定が反映される。

"tab_size": 2,
"translate_tabs_to_spaces": true,
"highlight_line": true,
"highlight_modified_tabs": true,
"draw_white_space": "all",
"show_encoding": true

言語ごとの設定

たとえば、Python のスクリプトを編集するときにだけ適用したい設定を書くには、Python ファイルを開いた状態で [基本設定] → [基本設定 – 拡張] → [シンタックス指定 – ユーザ] を選ぶと、Python.sublime-settings というファイルが開く。ここに上とおなじ要領で設定を書くと Python スクリプトだけに適用される。Python ではタブサイズを 4 にした。

さて、これでしばらく使ってみよう。