heroku+sinatra+activerecord+postgresql

ローカルでの準備

環境

  • Vigrant with CentOS6.4
  • ruby 2.0~
  • postgresql9.3

postgresql のインストール

"PostgreSQL Deep Dive: VagrantPostgreSQL 9.3の動く仮想サーバを自動構築してみる (http://pgsqldeepdive.blogspot.jp/2013/09/vagrant-postgresql93.html)"のとおりにインストール。

データベースの作成

使用する database を作ります。

% sudo -u postgres -i
$ psql
=# create database testdb;
=#  \q

次にtableを postgresql で作ります。データベースは .env で指定し、model.rb でdotenv gem を使って読み込ませます。

% vi .env
DATABASE_URL = "postgres://localhost/testdb"

% vi model.rb
require 'dotenv'
require 'active_record'

Dotenv.load
ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])

class Post < ActiveRecord::Base; end

Rakefileではそれを require します:

% vi Rakefile
require 'sinatra/activerecord'
require 'sinatra/activerecord/rake'
require 'rake/clean'

require './model'

migration ファイルを作り、migrate をかけテーブルを作成します:

% rake db:create_migration NAME=create_posts
....

% vi db/migration/xxxxx_create_posts.rb
class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :text
      t.timestamps
    end
  end
end
% rake db:migrate
...

これでローカル上のデータベース作成は完了。念のため確認します:

% psql testdb
testdb=> \dt
              List of relations
 Schema |       Name        | Type  |  Owner
--------+-------------------+-------+---------
 public | posts             | table | vagrant
 public | schema_migrations | table | vagrant
(2 rows)

=> select * from posts;
 id |   text   |         created_at         |         updated_at
----+----------+----------------------------+----------------------------
=> \q

ウェブアプリの作成

'localhost:port/' にアクセスされると text: "hoge" なレコードを一つ追加していく簡単なsinatraアプリを作ります。

まずGemfile に使う gem を羅列し、bundler install して Gemfile.lock を作ります。

% vi Gemfile
source 'https://rubygems.org'

gem 'rake'
gem 'sinatra'
gem 'sinatra-contrib'
gem 'sinatra-activerecord', :require => 'sinatra/activerecord'

gem 'dotenv'
gem 'activerecord'
gem 'pg'

% bundle install
...

アプリ本体。こちらをまず先に require 'xxx' 形式で書いてから Gemfile に構成しなおしてもかまいません。

% vi app.rb
require 'bundler'
Bundler.require

require './model'

class MyApp < Sinatra::Base
  configure :development do
    register Sinatra::Reloader
    set :bind, '0.0.0.0'
  end

  get '/' do
    Post.create(text: "hoge")
    erb :show
  end
end
__END__
@@show
count: <%=  Post.count.to_s %>
<div>
<% Post.all.each do |post| %>
  <%= post.text %><br>
<% end %>

後は普通に config.ru を書き、rackup し、ブラウザ等で確認します:

% vi config.ru
require './app'
run MyApp

% bundle exec rackup &
....
% lynx losthost:9292

foreman 起動用ファイルの作成

% vi Procfile
web: bundle exec rackup -p $PORT

% foreman start &
17:38:52 web.1  | started with pid 3530
....
% curl losthost:5000

サーバーへのデプロイ

バックアップファイルなどをキレイにするため rake clean をかけ、 また.env はサーバー上に置くと変なことになるので gitignore しときます。

% rake clean
% vi .gitignore
.env

いつもどおりに:

% git init; git add . ; git commit -m init -a
% heroku create NAME
...

heroku-postgresql の addon を使います。後は通常の手順です。

%  heroku addons:add heroku-postgresql
...
%  heroku run rake db:migrate
...
% git push heroku master
...
% heroku ps
web.1: up 2014/MM/DD xx:xx:xx (~ 1m ago)

% heroku open
....

Tips

restart

データベースをrollback, migrate したりすると停止したりするので restart をかけます。

% heroku ps:restart web
Restarting web dynos... done

ローカルからサーバーのデータベースにアクセス

% heroku config して表示される DATABASE_URL の値を .env に入れます。

% heroku config
DATABASE_URL:               postgres://xxxuser:xxxpassword@@ecxxx.compute-x.amazonaws.com:5432/xxxdatabasename
HEROKU_POSTGRESQL_XXXX_URL: postgres://xxxuser:xxxpassword@@ecxxx.compute-x.amazonaws.com:5432/xxxdatabasename
LANG:                       en_US.UTF-8
RACK_ENV:                   production

% vi .env
#DATABASE_URL = "postgres://localhost/testdb"
DATABASE_URL = "postgres://xxxuser:xxxpassword@@ecxxx.compute-x.amazonaws.com:5432/xxxdatabasename"

参考

ntp で時刻を合わせないと omniauth-twitter で 401食らう件

他のアプリでふつーに使ってたのになんか動かなくなった。 dev.twitter.com で callback ちゃんと入れてるしなあ、と思ってぐぐってたr:

gem 'omniauth-twitter'で認証しようとしてOAuth::Unauthorized 401 Unauthorizedなんてエラーがでたら - Qiita : http://qiita.com/hirokishirai/items/5a43977a38ecd922bfb9

確かに virtual pc 上で 20分ぐらい遅れてた。

http://qiita.com/tsu_nera/items/9be676b04b190e45b281 のとおり ntp いれて時刻合わせたらうまく動いた。

padorina で will_paginateを bootstrap と使う

will_paginate

https://github.com/mislav/will_paginate/ にあるとおりインストールその他。

% padrino g plugin will_paginate

bootstrap と合わせるには、https://gist.github.com/phildionne/5785087 を lib/will_paginate_bootstrap_renderer.rb あたりにセーブして、config/boot.rb で require する。

% vi app/controllers/entry.rb
...
  entries = Entry.paginate(page: params[:page], per_page: 5).order("created_at desc")
  content_for(:pagination, will_paginate(entries, renderer: WillPaginate::ViewHelpers::BoostrapLinkRenderer, previous_label: "&laquo;", next_label: "&raquo;"))
  haml :index, locals: {entries: entries}
...
% vi app/views/index.haml
- entries.each do |entry|
  = haml :show, :layout => false, locals: {entry: entry}
= yield_content(:pagination)

とかしてあげる。

参考

Ruby - padrino+will_paginate+bootstrap向けにカスタムレンダラを実装 - Qiita : http://qiita.com/insight3110/items/d513aeaeac32552c3c9a

ただしここの最後にある変更はする必要がない。

padrino で自分の config

padrino で自分の config

YML app own config · padrino/padrino-framework Wiki GitHub : https://github.com/padrino/padrino-framework/wiki/YML-app-own-config

あたりを見ながら。

% vi config/config.yaml
:twitter:
  :consumer_key:  "xxxx"
  :consumer_secret:  "xxxx"

% vi config/apps.rb
Padrino.configure_apps do
  ...

  begin
    set :config, YAML.load(open(Padrino.root("/config/config.yaml")).read)
  rescue 
    logger.fatal("cannot open config file: /config/config.yaml")
    exit
  end
end

としとくと、どこでも settings.config[:twitter][:consumer_key] と拾える。

padrino tips

padrino tips

rspec で test

準備

g project するときに -t rspec すると rspec がすぐ使える。

database へのアクセス

test 環境で migrate しないと table がないと怒られます。

% rake ar:migrate RACK_ENV=test

post

it do の中でpost "/post" して last_response 拾おうとしても、403 が帰ってきちゃいます。 form_tag() 使うなどして token 渡さないと csrf で蹴られちゃうんですね。 テスト環境では外しちゃってもいいかも?

    configure :test do
      set :protect_from_csrf, false      
    end

textarea の行頭空白

.panel-body
  = form_for(:foo) do |f|
    %div.form-group
    = f.text_field(:name)
    %div.form-group
    = f.text_area(:content, cols: 80, rows: 20)

とかすると行頭の空白の扱いが変なので、text_area のとこの = を ~ に変えるとうまくいく。

表示させるときも、

.panel-body~ @page.formatted_content.html_safe

のように ~ を使う。

ソースを読む

g admin_map してできた models/pages.rb, views/pages/* とか読むとためになるかも。

splat

sinatra の時見たく get '/*' do だと params[:splat] に入ってくんない。 どうやら get '/*splat' としてあげないとダメなようだ。

gollum インストールメモ

gollum インストールメモ

インストール

% sudo gem install すると iculib がないと怒られるので、 % yum list | grep icu して出てくる libicu-devel を入れてから再度入れなおす。

% yum install libicu-devel
...
% sudo gem install gollum
...

起動

init して server を立ち上げます。

% mkdir wiki; cd wiki
% git init
% gollum

http://localhost:4567

gollum-lib

エンジン部は別なので、このライブラリをバックエンドに使っていじくるのもいいのかも。 gollum/gollum-lib · GitHub : https://github.com/gollum/gollum-lib

参考サイト

padrino でのウェブサービス作りを実況してみる

padrino でのウェブサービス作りを実況してみる

概要

twitter で書きなぐったのをまとめてブログとかwikiとかshortnote にあげる

まずはプロジェクトとか

プロジェクト名は、"timeline_editor"。安易ですね。

% pwd
/home/source
% padrino g project timeline_editor -d activerecord -e erb -s jquery -t rspec
....
% cd timeline_editor
% vi app/app.rb
module TimelineEditor
  class App < Padrino::Application
    ...
    get :index, map: "/" do
      erb "hello world"
    end
  end

レイアウト

bootstrap 使って見てくれを整えます。

% vi app/views/layouts/application.erb
<html>
<head>
  <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
  <script type="text/javascript" src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
  <link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="navbar navbar-default">
    <ul class="nav navbar-nav">
      <li><a href="/">top</a></li>
    </ul>
  </div>
  <div class="container">
    <div class="panel panel-primary">
      <div class="panel-heading">title</div>
      <div class="panel-body"><%= yield %></div>
    </div>
    <div class="panel panel-primary">
      <div class="panel-heading">ENV</div>
      <div class="panel-body">
    <div><h2>env</h2><%= h env.inspect %></div>
      </div>
    </div>
  </div>
</body>
</html>

bootstrap は後ほど取り上げるけど、ソレっぽく見てくれを整えてくれる。 ナビゲーションバーを上に、中身を "panel" で包むと見やすい。

timeline を表示させる

gem の twitter を使おう。consumer key/secret は取っておいてね。

% vi Gemfile
...
gem 'twitter'
% bundle
...
% vi app/app.rb
    get :user_timeline do
      twitter = Twitter::REST::Client.new do |config|
        config.consumer_key = "xxx"
        config.consumer_secret = "xxx"
        config.access_token = "xxx"
        config.access_token_secret = "xxx"
      end 
      timeline = twitter.user_timeline(count: 50)
      erb timeline.map {|status| status.text }.inspect
    end

key や token の類はとりあえず決め打ちにします。config化や認証は後で。 inspect して status が取れてるか確認。 そしたら view を使ってみる。

% vi app/app.rb
    get :user_timeline do
      ...
      timeline = twitter.user_timeline(count: 50)
      erb partial 'status', :collection => timeline
...
% vi app/views/_status.erb
<li><%= h status.text %></li>

partial template を使います。対象オブジェクトの配列をcollection で渡して view で "_" から始まるテンプレートファイルを作って partial を呼ぶと、配列回してくれるらしいです。便利ですね。