deviseをカスタマイズする

Rails と devise の話はまだ続くよ。今日は devise のビューとコントローラをカスタマイズする。

やっぱりユーザー名がほしい

ユーザーアカウントに Email だけじゃなくて、やっぱりユーザー名がほしい。というわけで、migration スクリプトに2行追加。

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :name, null: false, default: ""
      t.string :email, null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string :current_sign_in_ip
      t.string :last_sign_in_ip

      ## Confirmable
      t.string :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string :unconfirmed_email      # Only if using reconfirmable

      ## Lockable
      # t.integer :failed_attempts, default: 0, null: false
      # Only if lock strategy is :failed_attempts
      # t.string :unlock_token
      # Only if unlock strategy is :email or :both
      # t.datetime :locked_at t.timestamps
    end

    add_index :users, :name, unique: true
    add_index :users, :email, unique: true
    add_index :users, :reset_password_token, unique: true
    add_index :users, :confirmation_token, unique: true
    # add_index :users, :unlock_token, unique: true
  end
end

そしてデータベースの作り直し。

takatoh@nightschool:~/w/lathercraft$ bundle exec rake db:drop
takatoh@nightschool:~/w/lathercraft$ bundle exec rake db:migrate

devise のビューをコピーしてカスタマイズ

デフォルトでは gem のビューが使われているけど、カスタマイズするためにビューをコピーする。

takatoh@nightschool:~/w/lathercraft$ bundle exec rails generate devise:views users
      invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/users/shared
      create    app/views/users/shared/_links.erb
      invoke  form_for
      create    app/views/users/confirmations
      create    app/views/users/confirmations/new.html.erb
      create    app/views/users/passwords
      create    app/views/users/passwords/edit.html.erb
      create    app/views/users/passwords/new.html.erb
      create    app/views/users/registrations
      create    app/views/users/registrations/edit.html.erb
      create    app/views/users/registrations/new.html.erb
      create    app/views/users/sessions
      create    app/views/users/sessions/new.html.erb
      create    app/views/users/unlocks
      create    app/views/users/unlocks/new.html.erb
      invoke  erb
      create    app/views/users/mailer
      create    app/views/users/mailer/confirmation_instructions.html.erb
      create    app/views/users/mailer/reset_password_instructions.html.erb
      create    app/views/users/mailer/unlock_instructions.html.erb

とりあえず、sign up するときの app/views/users/registrations.new.html.erb と sign in するときの app/views/users/sessions/new.html.erb にユーザー名の欄を付け加えた。

<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>
  <div>
    <%= f.label :name %>
    <%= f.email_field :name, autofocus: true %>
  </div>
  <div>
    <%= f.label :email %>
    <%= f.email_field :email %>
  </div>
  <div>
    <%= f.label :password %>
    <%= f.password_field :password, autocomplete: "off" %>
  </div>
  <div>
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation, autocomplete: "off" %>
  </div>
  <div>
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "users/shared/links" %>
<h2>Sign in</h2>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
  <div>
    <%= f.label :name %>
    <%= f.email_field :name, autofocus: true %>
  </div>
  <div>
    <%= f.label :email %>
    <%= f.email_field :email %>
  </div>
  <div>
    <%= f.label :password %>
    <%= f.password_field :password, autocomplete: "off" %>
  </div>
  <% if devise_mapping.rememberable? -%>
  <div>
    <%= f.check_box :remember_me %>
    <%= f.label :remember_me %>
  </div>
  <% end -%>
  <div>
    <%= f.submit "Sign in" %>
  </div>
<% end %>

<%= render "users/shared/links" %>

コントローラーのカスタマイズ

コントローラーをカスタマイズするには、Devise::SessionsController, Devise::RegistrationsController, Devise::PasswordsController を継承したクラスを作成して、各メソッドをオーバーライドすればいいようだ。どのメソッドをオーバーライドすればいいかを、bundle exec rake routes で確認する。

takatoh@nightschool:~/w/lathercraft$ bundle exec rake routes
                  Prefix Verb   URI Pattern                       Controller#Action
        new_user_session GET    /users/sign_in(.:format)          devise/sessions#new
            user_session POST   /users/sign_in(.:format)          devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)         devise/sessions#destroy
           user_password POST   /users/password(.:format)         devise/passwords#create
       new_user_password GET    /users/password/new(.:format)     devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format)    devise/passwords#edit
                         PATCH  /users/password(.:format)         devise/passwords#update
                         PUT    /users/password(.:format)         devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)           devise/registrations#cancel
       user_registration POST   /users(.:format)                  devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)          devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)             devise/registrations#edit
                         PATCH  /users(.:format)                  devise/registrations#update
                         PUT    /users(.:format)                  devise/registrations#update
                         DELETE /users(.:format)                  devise/registrations#destroy
       user_confirmation POST   /users/confirmation(.:format)     devise/confirmations#create
   new_user_confirmation GET    /users/confirmation/new(.:format) devise/confirmations#new
                         GET    /users/confirmation(.:format)     devise/confirmations#show
(後略)

これを見ると、とりあえずは registration#new と sessions#new かな。それぞれこうした。

class Users::RegistrationsController < Devise::RegistrationsController
end
Devise::SessionsController
  def new
    super
  end

  def create
    super
  end

  def destroy
    super
  end
end

ついでに PasswordsController も。

class Users::PasswordsController < Devise::PasswordsController
end

ルーティングの設定

作ったコントローラーを使うように、ルーティングを変更する。

Rails.application.routes.draw do
  devise_for :users, :controllers => {
    :registrations => "users/registrations",
    :sessions      => "users/sessions",
    :passwords     => "users/passwords"
  }
(後略)

ルーティングを確認してみよう。

takatoh@nightschool:~/w/lathercraft$ bundle exec rake routes
                  Prefix Verb   URI Pattern                       Controller#Action
        new_user_session GET    /users/sign_in(.:format)          users/sessions#new
            user_session POST   /users/sign_in(.:format)          users/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)         users/sessions#destroy
           user_password POST   /users/password(.:format)         users/passwords#create
       new_user_password GET    /users/password/new(.:format)     users/passwords#new
      edit_user_password GET    /users/password/edit(.:format)    users/passwords#edit
                         PATCH  /users/password(.:format)         users/passwords#update
                         PUT    /users/password(.:format)         users/passwords#update
cancel_user_registration GET    /users/cancel(.:format)           users/registrations#cancel
       user_registration POST   /users(.:format)                  users/registrations#create
   new_user_registration GET    /users/sign_up(.:format)          users/registrations#new
  edit_user_registration GET    /users/edit(.:format)             users/registrations#edit
                         PATCH  /users(.:format)                  users/registrations#update
                         PUT    /users(.:format)                  users/registrations#update
                         DELETE /users(.:format)                  users/registrations#destroy
       user_confirmation POST   /users/confirmation(.:format)     devise/confirmations#create
   new_user_confirmation GET    /users/confirmation/new(.:format) devise/confirmations#new
                         GET    /users/confirmation(.:format)     devise/confirmations#show
(後略)

confirmation のルーティングが devise のままだけど大丈夫かな。

試しにサインアップしてみると、無事うまく行った。

sign-in-with-name-and-email

deviseでサインアップした時にメールを送れるようにする

引き続き devise の話。confirmation 用のメールを送る設定。
config/environments/development.rb に次の設定を追加する。

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  :address        => 'smtp.gmail.com',
  :port           => 587,
  :authentication => :plain,
  :user_name      => 'メールアドレス',
  :password       => 'パスワード'
}

とはいえ、メールアドレスやパスワードをハードコーディングするのは良くないよなあ。どうやるのがいいんだろ。

追記

メールアドレスやパスワードの設定場所は、以下のページによると config フォルダ以下に .yml ファイルを作って、config/initializers フォルダにおいたスクリプトで読み込むのがいいらしい。

 cf. Railsの設定情報 – Build INSIDER
 cf. Rails でアプリ固有の設定情報や定数を定義する – EasyRamble

そこで、次のように config/app_config.yaml と config/initializers/app_config.rb を作った。

---
development:
  email: メールアドレス
  email_password: パスワード
test:
  email: メールアドレス
  email_password: パスワード
production:
  email: メールアドレス
  email_password: パスワード
APP_CONFIG = YAML.load_file("#{Rails.root}/config/app_config.yml")[Rails.env]

ところが、これで Rails コンソールを立ち上げようとするとエラーになる。

takatoh@nightschool:~/w/lathercraft$ rails console
/home/takatoh/w/lathercraft/config/environments/development.rb:44:in block in ': uninitialized constant APP_CONFIG (NameError)
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/railtie.rb:210:in instance_eval'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/railtie.rb:210:in configure'
	from /home/takatoh/w/lathercraft/config/environments/development.rb:1:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/engine.rb:594:in block (2 levels) in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/engine.rb:593:in each'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/engine.rb:593:in block in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/initializable.rb:30:in instance_exec'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/initializable.rb:30:in run'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/initializable.rb:55:in block in run_initializers'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:226:in block in tsort_each'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:348:in block (2 levels) in each_strongly_connected_component'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:418:in block (2 levels) in each_strongly_connected_component_from'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:427:in each_strongly_connected_component_from'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:417:in block in each_strongly_connected_component_from'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/initializable.rb:44:in each'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/initializable.rb:44:in tsort_each_child'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:411:in call'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:411:in each_strongly_connected_component_from'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:347:in block in each_strongly_connected_component'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:345:in each'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:345:in call'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:345:in each_strongly_connected_component'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:224:in tsort_each'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/tsort.rb:205:in tsort_each'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/initializable.rb:54:in run_initializers'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/application.rb:300:in initialize!'
	from /home/takatoh/w/lathercraft/config/environment.rb:5:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/application.rb:92:in preload'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/application.rb:140:in serve'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/application.rb:128:in block in run'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/application.rb:122:in loop'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/application.rb:122:in run'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/application/boot.rb:18:in '
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in require'
	from /home/takatoh/.rvm/rubies/ruby-2.1.1/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in require'
	from -e:1:in main'

最初の方に書いてあるけど、APP_CONFIG 定数が初期化されていない、と出てる。推測するに、config/initializers/app_config.rb よりも config/environments/development.rb (ここで APP_CONFIG を参照している)のほうが先に読み込まれるんではなかろうか。
結局、他に方法が思いつかなかったので、config/app_config.yml を config/envronments/development.rb の中で読み込むように変更した。

APP_CONFIG = YAML.load_file("#{Rails.root}/config/app_config.yml")[Rails.env]

config.action_mailer.default_url_options = { :host =&gt; 'localhost:3000' }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  :address        => 'smtp.gmail.com',
  :port           => 587,
  :authentication => :plain,
  :user_name      => APP_CONFIG["email"],
  :password       => APP_CONFIG["email_password"]
}

これでうまく動いた。

deviseでsign in/sign up/sign outへのリンク

昨日に続いて、devise の話。sign in/sign up/sign out それぞれへのリンクをつけた。こんな感じ。

<ul>
  <li style="list-style-type: none;">
    <ul>
<% if user_signed_in? %>
    </ul>
  </li>
</ul>
<ul>
  <li style="list-style-type: none;">
    <ul>
      <li><%= link_to "Sign out", destroy_user_session_path, :method => :delete %></li>
    </ul>
  </li>
</ul>
<% else %>
      <ul>
        <li style="list-style-type: none;">
      <ul>
      <li><%= link_to "Sign in", new_user_session_path %></li>
      <li><%= link_to "Sign up", new_user_registration_path %></li>
    </ul>
  </li>
</ul>
<% end %>

サインインしているときは、Sign out だけ、していないときは Sign in と Sign up へのリンクを表示する。

Railsアプリにdeviseでユーザー認証を

先日から作り始めた Web アプリに、ユーザー認証を付けたい。ググってみると、devise っていうのがいいみたいなので、それを使うことにした。参考にしたのは以下のページ。

 cf. Railsでログイン、ユーザー登録機能を簡単に実装できる devise がスゴすぎると話題 – lough-raku^^
 cf. Rails4 にて Devise でユーザー登録・ログイン認証・認可の機能を追加 – EasyRamble
 cf. DeviseでSign up時にメール確認する – 悪あがきプログラマー

まずはインストール

Gemfile に以下の行を追加。

gem 'devise'

そいでもってインストール。

takatoh@nightschool:~/w/lathercraft$ bundle install

ジェネレータでモデルを作成

takatoh@nightschool:~/w/lathercraft$ bundle exec rails generate devise:install
takatoh@nightschool:~/w/lathercraft$ bundle exec rails generate devise User

モデル名は User にした。この時点で次のようなマイグレーションファイルができる。今回はユーザー名を入れたかったので name フィールドを追加、加えて Email での確認をしたかったので、confirmable のところがコメントアウトしてあったのを外した。

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :name, null: false, default: ""
      t.string :email, null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable t.integer :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string :current_sign_in_ip
      t.string :last_sign_in_ip

      ## Confirmable
      t.string :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string :unconfirmed_email     # Only if using reconfirmable

      ## Lockable
      # t.integer :failed_attempts, default: 0, null: false
      # Only if lock strategy is :failed_attempts
      # t.string :unlock_token
      # Only if unlock strategy is :email or :both
      # t.datetime :locked_at
      t.timestamps
    end

    add_index :users, :email, unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token, unique: true
    # add_index :users, :unlock_token, unique: true
  end
end

これで rake db:migration、サーバを起動して localhost:3000/users/sign_up にアクセスすると、下の画面のようにサインアップできるようになる。

user-sign-up

Sign up ボタンをクリックすれば OK だ。といってもまだメールを送る設定とかしていないので、サインアップは完了できない。でも、ログには出てくるはず……って、あれぇ?エラーになった。

devise-registration-error

なんでだ?
参考にしたページによれば、ログにメールが出力されて confirm 用のページへのリンクが乗っているはずなんだけど。うーん、勝手に name カラムを追加したのが悪いんだろうか。

いろいろ調べて試した結果

↓このページで答えを見つけた。

 cf. Ruby on Rails + Deviseの組み合わせでMissing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true – もっとクールにプログラミング

それと他にも間違いと思しきところが。結局直したのは次の3ヶ所。

1つ目は、マイグレーションファイルの下の方で次の行がコメントアウトされていたのを有効にした。

add_index :users, :confirmation_token, unique: true

2つ目は、app/models/user.rb に :confirmable オプションを追加した。

最後、3つ目が上のページにも書いてあるように、config/environments/development.rb の最後に一行追加。

config.action_mailer.default_url_options = { :host =&gt; 'localhost:3000' }

これでやっと、メールがログに出るようになった。

confirmation

href=”…” に示されているアドレスにアクセスすると、無事サインインの画面が出てサインインできた。

めでたしめでたし。というところで、今日はここまで。

UbuntuでもRailsがうまく動いてくれない→解決

このあいだ Windows7 64bit で Rails が動いてくれない話を書いた。Ubuntu ではうまくいくかと思ったら話はそう簡単じゃなかった。

このあいだと同じように、書籍を管理するアプリを作ることにする。

takatoh@nightschool:~/w/$ rails new Librarian

本来ならこれだけで Rails サーバを起動できるようになるはず。ところが、

takatoh@nightschool:~/w/$ cd Librarian
takatoh@nightschool:~/w/Librarian$ rails server
/home/takatoh/.rvm/gems/ruby-2.1.1/gems/execjs-2.2.1/lib/execjs/runtimes.rb:51:in autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/execjs-2.2.1/lib/execjs.rb:5:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/execjs-2.2.1/lib/execjs.rb:4:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/uglifier-2.5.1/lib/uglifier.rb:3:in require'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/uglifier-2.5.1/lib/uglifier.rb:3:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1@global/gems/bundler-1.6.3/lib/bundler/runtime.rb:76:in require'
	from /home/takatoh/.rvm/gems/ruby-2.1.1@global/gems/bundler-1.6.3/lib/bundler/runtime.rb:76:in block (2 levels) in require'
	from /home/takatoh/.rvm/gems/ruby-2.1.1@global/gems/bundler-1.6.3/lib/bundler/runtime.rb:72:in each'
	from /home/takatoh/.rvm/gems/ruby-2.1.1@global/gems/bundler-1.6.3/lib/bundler/runtime.rb:72:in block in require'
	from /home/takatoh/.rvm/gems/ruby-2.1.1@global/gems/bundler-1.6.3/lib/bundler/runtime.rb:61:in each'
	from /home/takatoh/.rvm/gems/ruby-2.1.1@global/gems/bundler-1.6.3/lib/bundler/runtime.rb:61:in require'
	from /home/takatoh/.rvm/gems/ruby-2.1.1@global/gems/bundler-1.6.3/lib/bundler.rb:132:in require'
	from /home/takatoh/w/Librarian/config/application.rb:7:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:79:in require'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:79:in vblock in server'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:76:in tap'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:76:in server'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands/commands_tasks.rb:40:in run_command!'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/railties-4.1.4/lib/rails/commands.rb:17:in '
	from /home/takatoh/w/Librarian/bin/rails:8:in require'
	from /home/takatoh/w/Librarian/bin/rails:8:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/client/rails.rb:27:in load'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/client/rails.rb:27:in call'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/client/command.rb:7:in call'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/client.rb:26:in run'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/bin/spring:48:in '
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/binstub.rb:11:in load'
	from /home/takatoh/.rvm/gems/ruby-2.1.1/gems/spring-1.1.3/lib/spring/binstub.rb:11:in '
	from /home/takatoh/w/Librarian/bin/spring:16:in require'
	from /home/takatoh/w/Librarian/bin/spring:16:in '
	from bin/rails:3:in load'
	from bin/rails:3:in main'

こんな具合にエラーになってしまう。
エラートレースの最初の方に「Could not find a JavaScript runtime.」と書いてある。これが原因らしいので、ググってみるとそのものズバリのページが見つかった。
 cf. rails server 時に `autodetect’: Could not find a JavaScript runtime が出たら。
これによると、therubyracer という gem をインストールするための記述が Gemfile でコメントアウトされているのを、修正してインストールしてやればいいらしい。

gem 'therubyracer', platforms: :ruby

これで bundle install

takatoh@nightschool:~/w/Librarian$ bundle install
(略)

さあ、うまくいくかな?

takatoh@nightschool:~/w/Librarian$ rails server
=> Booting WEBrick
=> Rails 4.1.4 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
=> Ctrl-C to shutdown server
[2014-07-06 06:31:57] INFO  WEBrick 1.3.1
[2014-07-06 06:31:57] INFO  ruby 2.1.1 (2014-02-24) [x86_64-linux]
[2014-07-06 06:31:57] INFO  WEBrick::HTTPServer#start: pid=21228 port=3000
rails-welcome-aboard

うまくいった!

自作のgemをrubygems.orgに公開してみた

新しく作ろうとしているプロジェクトで使うライブラリがあって、しかも2つのプロジェクトで共通に使うつもりなので、こういう場合は gem にしてしまうのがいい。filestorage っていう一般名詞みたいな名前のライブラリだけど gem search してみたら見つからなかったので、いいや、作ってしまえ。
というわけで、ほんとにシンプルなものだけど gem を作って rubygems.org に登録してみた。以下手順のメモ。

Bundlerでプロジェクトを作る

^o^ > bundle gem filestorage
DL is deprecated, please use Fiddle
      create  filestorage/Gemfile
      create  filestorage/Rakefile
      create  filestorage/LICENSE.txt
      create  filestorage/README.md
      create  filestorage/.gitignore
      create  filestorage/filestorage.gemspec
      create  filestorage/lib/filestorage.rb
      create  filestorage/lib/filestorage/version.rb
Initializing git repo in C:/Users/hiro/Documents/tmp/filestorage
warning: LF will be replaced by CRLF in .gitignore.
The file will have its original line endings in your working directory.
warning: LF will be replaced by CRLF in Gemfile.
The file will have its original line endings in your working directory.
warning: LF will be replaced by CRLF in LICENSE.txt.
The file will have its original line endings in your working directory.
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
warning: LF will be replaced by CRLF in Rakefile.
The file will have its original line endings in your working directory.
warning: LF will be replaced by CRLF in filestorage.gemspec.
The file will have its original line endings in your working directory.
warning: LF will be replaced by CRLF in lib/filestorage.rb.
The file will have its original line endings in your working directory.
warning: LF will be replaced by CRLF in lib/filestorage/version.rb.
The file will have its original line endings in your working directory.

これで gem の雛形ができる。git リポジトリもできていて、ファイルがステージされた状態になっている。まずはここでいったん commit。ライブラリの本体(lib/filestorage/local_filestorage.rb)を書いて、filestorage.gemspec を編集してもう一度 commit。GitHub にリポジトリを作って push しておく。
これでひとまずは出来上がり(まあ、バージョン 0.0.1 だからね)。

rubygems.orgからAPI Keyをとってくる

rubygems.org に gem を登録するには、API Key が必要。サインアップしてあったので(いつやったんだっけ?)、プロファイルの編集ページで API Key を見ることができる。これを %HOME%/.gems/credentials ファイルに保存するんだけど、ワンライナーがページに載っているのでそれを真似して保存した。ただし、載っているのは Unix 向けなので、Windows ではそのまま使えるわけじゃない。とはいってもやってることは簡単で、次のようにすればいい。

^o^ > curl -u takatoh https://rubygems.org/api/v1/api_key.yaml > %HOME%/.gem/credentials

gemをつくって登録する

もう一息だ。まずは gem を作る。

^o^ > bundle exec rake build
DL is deprecated, please use Fiddle
DL is deprecated, please use Fiddle
filestorage 0.0.1 built to pkg/filestorage-0.0.1.gem.

これで pkg ディレクトリの中に filestorage-0.0.1.gem ができた。これを rubygems.org に登録する。

^o^ > bundle exec rake release
DL is deprecated, please use Fiddle
DL is deprecated, please use Fiddle
filestorage 0.0.1 built to pkg/filestorage-0.0.1.gem.
Tagged v0.0.1.
Pushed git commits and tags.
Pushed filestorage 0.0.1 to rubygems.org.

これだけで OK。なんか git のタグをうって GitHub に push までしてくれるみたいだな。

そんでもって、これが登録後のページ。おお、ちゃんと登録されてる!

filestorage

追記

たいしたもんじゃないけど、書いたコードをさらしておく。

# encoding: utf-8

require 'pathname'
require 'fileutils'

module Filestorage

  class LocalFilestorage

    def initialize(base_dir)
      @base_dir = Pathname.new(base_dir)
    end

    def store(path, file)
      fullpath = @base_dir + path
      FileUtils.mkdir_p(fullpath.parent)
      if file.instance_of?(Pathname)
        FileUtils.cp(file, fullpath)
      elsif file.instance_of?(File)
        File.open(fullpath, "wb") do |f|
          f.write(file.read)
        end
      else
        File.open(fullpath, "wb") do |f|
          f.write(file)
        end
      end
      path
    end

    def get(path)
      fullpath = @base_dir + path
      File.open(fullpath, "rb")
    end

  end # of class LocalFilestorage

end # of module LocalFilestorage

なんか、1時間くらい経ってからページをみたら 18 downloads とか書いてある。もう 18 回もダウンロードされたってこと?みんながっかりしただろうな。

さらに追記

参考にしたページを書くのを忘れてた。ここを参考にしました。

 cf. RubyGems.org へ gem を公開する。 – komiyakの通り道

Windows7(64bit)でRailsがうまく動いてくれない

今日は Rails だ。試しに簡単な Web アプリを作ってみるよ。蔵書を管理するアプリにしよう。名前は Librarian だ。
と、意気込んだところだけど、結果から言うをうまくいかなかった。以下その顛末。

まずは Rails のプロジェクトを作る。

^o^ > rails new Librarian

このコマンドでずら~っとファイル一式が生成される。とりあえずはこれで git のリポジトリを作って最初のコミット。

^o^ > cd Librarian

^o^ > git init
Initialized empty Git repository in C:/Users/hiro/Documents/w/Librarian/.git/

^o^ > git add .
warning: LF will be replaced by CRLF in .gitignore.
The file will have its original line endings in your working directory.
(以下略)

^o^ > git commit -m "First commit."
[master (root-commit) 167a985] First commit.
warning: LF will be replaced by CRLF in .gitignore.
The file will have its original line endings in your working directory.
(以下略)

で、次は scaffold。

^o^ > ruby bin/rails generate scaffold book title volume author
DL is deprecated, please use Fiddle
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/data_source.rb:1
82:in `rescue in create_default_data_source': No source of timezone data could b
e found. (TZInfo::DataSourceNotFound)
Please refer to http://tzinfo.github.io/datasourcenotfound for help resolving th
is error.
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:179:in `create_default_data_source'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:40:in `block in get'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:39:in `synchronize'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:39:in `get'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone.rb:629:in `data_source'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone.rb:92:in `get'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone_proxy.rb:67:in `real_timezone'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone_proxy.rb:30:in `period_for_utc'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone.rb:549:in `current_period'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/core_ext/object/try.rb:45:in `public_send'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/core_ext/object/try.rb:45:in `try'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:223:in `utc_offset'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:396:in `block in []'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:396:in `tap'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:396:in `[]'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/core_ext/time/zones.rb:60:in `find_zone!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/railtie.rb:20:in `block in '
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:30:in `instance_exec'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:30:in `run'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:55:in `block in run_initializers'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:150:in `block in tsort_each'

        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:183:in `block (2 levels) in
each_strongly_connected_component'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:219:in `each_strongly_connec
ted_component_from'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:182:in `block in each_strong
ly_connected_component'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:180:in `each'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:180:in `each_strongly_connec
ted_component'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:148:in `tsort_each'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:54:in `run_initializers'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/ap
plication.rb:288:in `initialize!'
        from C:/Users/hiro/Documents/w/Librarian/config/environment.rb:5:in `'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/ap
plication.rb:264:in `require'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/ap
plication.rb:264:in `require_environment!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:147:in `require_application_and_environment!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:133:in `generate_or_destroy'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:51:in `generate'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:40:in `run_command!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands.rb:17:in `'
        from bin/rails:4:in `require'
        from bin/rails:4:in `
'

なんかエラーが出た。どうやら timezone のデータが見つからない、といっているらしい。ついでに(親切にも)http://tzinfo.github.io/datasourcenotfound を見ればいいよ、とも。
早速ページにアクセスしてみる。すると、あった、Gemfile にちょっと記述を追加すればいいらしい。

If you are using a 64-bit version of Ruby on Windows, then add :x64_mingw to the list of platforms as follows:

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw]

指示通り、Gemfile に :x64_mingw を追記して、buldle install を実行。

^o^ > ruby bin/bundle install
DL is deprecated, please use Fiddle
Using rake 10.3.2
Using i18n 0.6.9
Using json 1.8.1
Using minitest 5.3.5
Using thread_safe 0.3.4
Using tzinfo 1.2.1
Using activesupport 4.1.1
Using builder 3.2.2
Using erubis 2.7.0
Using actionview 4.1.1
Using rack 1.5.2
Using rack-test 0.6.2
Using actionpack 4.1.1
Using mime-types 1.25.1
Using polyglot 0.3.5
Using treetop 1.4.15
Using mail 2.5.4
Using actionmailer 4.1.1
Using activemodel 4.1.1
Using arel 5.0.1.20140414130214
Using activerecord 4.1.1
Using coffee-script-source 1.7.0
Using execjs 2.2.1
Using coffee-script 2.2.0
Using thor 0.19.1
Using railties 4.1.1
Using coffee-rails 4.0.1
Using hike 1.2.3
Using multi_json 1.10.1
Using jbuilder 2.1.1
Using jquery-rails 3.1.1
Using bundler 1.6.3
Using tilt 1.4.1
Using sprockets 2.11.0
Using sprockets-rails 2.1.3
Using rails 4.1.1
Using rdoc 4.1.1
Using sass 3.2.19
Using sass-rails 4.0.3
Using sdoc 0.4.0
Using sqlite3 1.3.9
Using turbolinks 2.2.2
Using uglifier 2.5.1
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

だけどなんだかちゃんとインストールされたような気配がない。試しにもう一度 scaffold をやってみる。

^o^ > ruby bin/rails generate scaffold book title volume author
DL is deprecated, please use Fiddle
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/data_source.rb:1
82:in `rescue in create_default_data_source': No source of timezone data could b
e found. (TZInfo::DataSourceNotFound)
Please refer to http://tzinfo.github.io/datasourcenotfound for help resolving th
is error.
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:179:in `create_default_data_source'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:40:in `block in get'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:39:in `synchronize'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/dat
a_source.rb:39:in `get'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone.rb:629:in `data_source'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone.rb:92:in `get'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone_proxy.rb:67:in `real_timezone'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone_proxy.rb:30:in `period_for_utc'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/tim
ezone.rb:549:in `current_period'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/core_ext/object/try.rb:45:in `public_send'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/core_ext/object/try.rb:45:in `try'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:223:in `utc_offset'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:396:in `block in []'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:396:in `tap'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/values/time_zone.rb:396:in `[]'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/core_ext/time/zones.rb:60:in `find_zone!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/activesupport-4.1.1/lib/act
ive_support/railtie.rb:20:in `block in '
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:30:in `instance_exec'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:30:in `run'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:55:in `block in run_initializers'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:150:in `block in tsort_each'

        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:183:in `block (2 levels) in
each_strongly_connected_component'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:219:in `each_strongly_connec
ted_component_from'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:182:in `block in each_strong
ly_connected_component'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:180:in `each'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:180:in `each_strongly_connec
ted_component'
        from C:/Ruby200-x64/lib/ruby/2.0.0/tsort.rb:148:in `tsort_each'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/in
itializable.rb:54:in `run_initializers'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/ap
plication.rb:288:in `initialize!'
        from C:/Users/hiro/Documents/w/Librarian/config/environment.rb:5:in `'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/ap
plication.rb:264:in `require'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/ap
plication.rb:264:in `require_environment!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:147:in `require_application_and_environment!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:133:in `generate_or_destroy'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:51:in `generate'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands/commands_tasks.rb:40:in `run_command!'
        from C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/railties-4.1.1/lib/rails/co
mmands.rb:17:in `'
        from bin/rails:4:in `require'
        from bin/rails:4:in `
'

やっぱり同じじゃないか。
仕方がないから、tzinfo-data っていう gem を直接インストールする。

^o^ > gem install tzinfo-data
Fetching: tzinfo-data-1.2014.5.gem (100%)
Successfully installed tzinfo-data-1.2014.5
Parsing documentation for tzinfo-data-1.2014.5
Installing ri documentation for tzinfo-data-1.2014.5
Done installing documentation for tzinfo-data after 3 seconds
1 gem installed

よし、インストールできた。これで大丈夫だろう。

^o^ > ruby bin/rails generate scaffold book title volume author
DL is deprecated, please use Fiddle
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/tzinfo-1.2.1/lib/tzinfo/data_source.rb:1
82:in `rescue in create_default_data_source': No source of timezone data could b
e found. (TZInfo::DataSourceNotFound)
Please refer to http://tzinfo.github.io/datasourcenotfound for help resolving th
is error.
(以下略)

だめだ~!ちゃんと tzinfo-data はインストールできてるのに何で見つからないんだ。

もしかして Ruby 2.0.0 と Rails 4.1.1 って相性が悪いのか?

追記(2014/07/01)

昨日ダメだった Rails だけど、今日会社の PC (Windows7 32bit、Ruby 2.0.0 32bit)でこっそりやってみたらあっさりとうまくいってしまった。64bit版がダメだってこと?

HerokuとRailsの本を読んだ

こないだ Heroku を使ってみて成功したので、今度は Rails でやってみようかと、「HerokuではじめるRailsプログラミング入門」という本を読んだ。
「Herokuではじめる」とは書いてあるけど、Heroku についてはほんのちょっとしか書いてなくて、基本的に Ruby on Rails の入門書。それも Rails3 準拠のようで、最新の Rails を使うにはもうちょっと足りない、という感じ。
とはいえ、Rails は何年も前(たしかバージョンが 2.0.2 だったかな)に使ったきりほとんど忘れていたので、どんな感じかを思い出すのにはちょうどよかった。
もう少し Rails4 の本を読んで、この2年ほど動いていない Rails アプリを作り直そう。

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 を使う準備はできたかな。