アカハヤの技術ブログ

クズと天才は紙一重 twitter:@akahaya719

Capistranoでデプロイした時にunicornが再起動しなかった話

capistranoを使用し、ec2にデプロイした時に

Bundler::GemfileNotFound

というエラーが出てしまい、unicornの再起動がうまくいかない時の対処法。

 

Gemfileは存在しているのだが、

Gemfile読み取り時に過去のバージョンのディレクトリのGemfileを見ようとし

そのディレクトリはすでに存在しないので、エラーが起きてしまう。

 

config/unicorn/production.rbまたはstaging.rbに以下を記述

before_exec do |server|
 ENV['BUNDLE_GEMFILE'] = "ec2内のGemfileまでのパス"
end

 

すでにこの問題が起きてしまっている場合は、

再度デプロイしても失敗してしまうので、

一度、unicornを手動で停止または、再起動しなくてはならない。

railsでgooglemapを表示

Gemfileに以下を追加して、bundle install

gem 'gmaps4rails'
gem 'geocoder'

 

application.html.slimのhead内に以下を記述

script src="//maps.google.com/maps/api/js?v=3.23&key=[API_KEY]"
script src="//cdn.rawgit.com/mahnunchik/markerclustererplus/master/dist/markerclusterer.min.js"
script src='//cdn.rawgit.com/printercu/google-maps-utility-library-v3-read-only/master/infobox/src/infobox_packed.js' type='text/javascript'

 

[API_KEY]には、Google Maps APIのキーを取得して入れてください

以下urlより

Get API Key  |  Google Maps JavaScript API  |  Google Developers

 

application.jsに以下を追加

//= require gmaps/google
//= require underscore

 

underscore.jsはこちらからコピーし

http://underscorejs.org/underscore-min.js

app/assets/javascript/underscore.jsに記述

 

googlemapを表示したいview側に以下を追加

#map style="width: 800px; height: 400px;"
javascript: handler = Gmaps.build('Google'); handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){ markers = handler.addMarkers(#{raw @hash.to_json}); handler.bounds.extendWith(markers); handler.fitMapToBounds(); });

 

controller側に以下を記述

@places = Place.all
address = 住所
lati_long = Geocoder.coordinates(address)
@hash = Gmaps4rails.build_markers(@places) do |place, marker|
 marker.lat lati_long[0]
 marker.lng lati_long[1]
end

 

Geocoder.coordinates(address)のaddress部分に住所を入れると

緯度と経度を取得してくれます。

railsで検索可能なセレクトボックスを実装

select2-railsというgemを使用し、

検索可能なセレクトボックスを作成することができます。

 

Gemfileに以下を記述してbundle installを実行。

gem 'select2-rails'

 

application.jsに以下を追加

//= require select2

 

application.cssに以下を追加

*= require select2

 

select2を使用したいところに、以下のjsを追加

$('フォームのidまたはclass').select2({
 width: 400,
 allowClear: true
});

 

allowClear: trueをつけると、

×ボタンでセレクトボックスで選択したものを削除できる。

 

allowClear: trueがうまく動かなかったので

以下jsを追加して、無理やり動かした

$('.select2-selection__clear').on('click', function(){
$('フォームのidまたはclass').val(null).trigger("change");
})

meta_tagsでrailsのSEO対策

Railsで、no_indexを使ったり、titleをページごとに変えたいというときがあると思うのですが、その時にmeta_tagsというgemが役に立ちます。

 

Gemfileに以下を記述してbundle installを実行

gem 'meta_tags'

 

helpers/application_helper.rbに以下を記述

def default_meta_tags
{
title: "サイトのタイトル",
description: "説明",
keywords: "キーワード",
icon: image_url("サイトのアイコン"),
charset: "UTF-8",
og: {
title: "タイトル",
type: "website",
url: request.original_url,
image: image_url("OGPで設定する画像"),
site_name: "サイト名",
description: "説明",
locale: "ja_JP"
}
}
end

 

OGPはサイトのURLを貼った時に表示されるやつです。

 

layouts/application.html.slimのheadに以下を追加

= display_meta_tags(default_meta_tags)

これでデフォルトで設定したmeta_tagを読み込みます。

 

特定のページをno_indexにしたいという場合は

該当ページのviewに以下を記述します

- set_meta_tags noindex: true

 

ページのタイトルを変えたい場合は

該当ページのview側に以下を記述

- set_meta_tags title: "タイトル"

 

ページタイトル | デフォルトで設定したタイトルのように

したい場合は以下のように記述

- set_meta_tags site: "タイトル"

Railsでアニメーションを使ったページネーションがしたかった話

RailsのページネーションをAjaxで処理し、

ページの切り替え時にアニメーションを使い

ページを切り替えるということがしたかったのでまとめ

 

Gemfileに以下を記述し、bundle installします。

gem 'kaminari'

 

kaminari自体でも、ページネーションは可能ですが、

今回は、一部の機能だけを使用します。

ページネーションをしたいだけであれば、普通にkaminariを使うことをオススメします。

 

controllerに、以下を記述。

def index
 @books = Book.page(params[:page]).per(5)
end

page(params[:page])で現在のページを取得し、対応したBookを取り出しています。

pre(5)は、デフォルトの表示数を表しています。何もない場合は25表示されます。

また、対応モデルに以下を記述することで、デフォルト値を変更することも可能です。

paginates_per 10

ページネーションを使いたいだけなら

controllerに記述後、ビュー側に以下を記述すれば、ページネーションは完成します。

= paginate @books

今回、= paginateは使用しません

 

ページネーションしたいところを、部分テンプレートにします

index.html.slimに以下を記述。

#books
=render partial: 'book'

 

_book.html.slimに以下を記述。

-unless @books.first_page?
 =link_to '<', books_path(page: @books.prev_page), remote: true

-@books.each do |book|
 =book.title

-unless @books.last_page?
 =link_to '>', books_path(page: @books.next_page),remote: true

first_page?で最初のページかどうか、

last_page?で最後のページかどうかを判断し

prev_pagenext_pageでそれぞれ、

前のページと次のページに飛ばす処理をしています。

部分テンプレートだけを更新したいので、

remote: trueを使用し、Ajax処理をします。

 

最後に、index.js.erbに以下を記述し、ページ切り替え時にアニメーションを使用します。

$('#books').fadeOut(300);
function openPage(){
 $('#books').html("<%= j (render 'book') %>");
 $('#books').fadeIn(500);
}
setTimeout("openPage()", 300);

railsでcrontab実装

wheneverというgemを使うことで、

定期的にcronで実行したいバッチ処理等を簡単に作成できます。

 

Gemfileに以下を記述してbundle installを実行

gem 'whenever', require: false

 

その後、次のコマンドを実行

bundle exec wheneverize .

すると、config以下にschedule.rbが作成されます。

 

次の4つをスケジューリングできます。

command: bashコマンドの実行
rake: rakeタスクの実行
runner: Rails内のメソッド実行
script: scriptの実行

 

次にschedule.rbに設定を記述します。

# ログファイルの指定
set :output, "#{path}/log/cron.log"
#実行環境の指定(デフォルトはproduction)
set :environment, :production

every 1.day, at: '6:00 am' do
  runner 'MyModel.test'
end

毎日am6:00にMymodelのtestメソッドが実行されます。

 

他にも設定方法は色々あり、少し例をあげると

#1時間ごと
:hour
#3時間ごと
3.hours
#曜日指定
:sunday, at: '6am'
#毎月25~30日まで、0:00に実行
'0 0 25-30 * * '

こんな感じです。

 

wheneverでは、Rails.rootが使えないらしいので、

Rails内のディレクトリを参照する場合は#{path}をつけてください。

 

最後に、crontabの設定です。

#crontabの設定、更新 
RAILS_ENV=development bundle exec whenever --update-crontab
#crontabの設定削除 
RAILS_ENV=development bundle exec whenever --clear-crontab

FTPサーバーからファイルを取得する(Ruby)

まず、FTPサーバーを構築します。

今回は、ローカル(Mac)とAzureに構築する二つのパターンを紹介します。

 

・ローカルでFTPサーバーを立ち上げる

起動する。

sudo launchctl load -w /System/Library/LaunchDaemons/ftp.plist

停止する。

sudo launchctl unload -w /System/Library/LaunchDaemons/ftp.plist

これだけです。

 

・AzureでFTPサーバーを立ち上げる

Azureにログイン後、新規->Web+モバイル->WebAppと選択し、

アプリ名やサブスクリプションなどを入力し作成。

サーバー構築後、設定->デプロイ資格情報でユーザ名とパスワードを設定します。

設定->プロパティでFTPホスト名が分かるのでFTPクライアントからAzureサーバーへ接続を行うことができます。

 

FTPサーバーからファイルを取得する

Net::FTPというRubyのクラスを使用します

require 'net/ftp'
host = URI(FTPホスト名).host
port = URI(FTPホスト名).port
remotefile = '習得するFTP側のファイルのフルパス'
local_file = '習得したデータの保存ファイルのフルパス'

#localの場合
Net::FTP.open('localhost') do |ftp|
ftp.login(ローカルのユーザ名, ローカルのパスワード)
ftp.passive = true
ftp.get(remotefile, local_file)
end

#Azureの場合
ftp = Net::FTP.new
ftp.connect(host, port)
ftp.login(Azureに設定したユーザ名, Azureに設定したパスワード)
ftp.passive = true
ftp.get(remotefile, local_file)