2012年12月18日火曜日

Asset Pipeline(Precompilling Assets)

特定のcssをデプロイ時にコンパイルしたい時のメモ

layoutが分かれていて以下の様になっている。
<%= stylesheet_link_tag    "top", :media => "all" %>

application.cssが以下の様に指定してある
*= require_tree ./application

このような場合トップのtop.cssをコンパイルしたいが、コンパイルされない
毎回デプロイしていては面倒なのでローカルで実験をする
  1. database.ymlにproduction環境を作成する
  2. dbを作成する(rake db:setup)
  3. コンパイルする(rake assets:precompile)
  4. public/assetsにtop.cssがない
  5. config/environments/production.rbを変更する
# config.assets.precompile += %w( search.js)
以上を以下の様に変更する
config.assets.precompile += %w( *.css *.js )

以上の内容はrailsガイドのAsset Pipelineに詳しく書いてあります。

2012年10月31日水曜日

バックアップサーバーを構築

バックアップサーバーの概要

  • 本体のサーバーからレプリケーションをする(mysql)
  • 毎日1回ダンプする
  • ダンプファイルはzipに変換し一週間分貯め古いモノから削除
  • autosshで切れても自動で接続する
  • サーバーが止まっても起動時にトンネルを自動で掘る
  • サーバーはubuntu1(マスター)とubuntu2(スレーブ)とする(両方VPSで違うサーバー)
1、sshの設定

スレーブ側からマスターへログイン出来るようにする
http://www.oiax.jp/rails3book/public_key.html(ssh公開鍵の設置方法)
スレーブ側からマスターへトンネルを貼る
autossh -M 10002 -f -N -L 23306:マスターIP:3306 ログインユーザー@マスターIP
(autosshは接続が切れた場合自動でつなげてくれるもの)
mysql -h 0.0.0.0 -P 23306 -u root(マスターのmysqlにログイン)
エラー発生
Lost connection to MySQL server at 'reading initial communication packet', system error: 0
my.confに以下を記入
bind-address            = 0.0.0.0

2,レプリケーションの設定(マスター)

my.confの設定
vi /etc/mysql/my.cnf
server-id = 1
log-bin = mysql-bin
replユーザーの作成
GRANT REPLICATION SLAVE ON *.* TO repl@% IDENTIFIED BY '';
テーブルをロックして実行
>FLUSH TABLES WITH READ LOCK;
>SHOW MASTER STATUS;
+-----------------------+----------+------------------+----------------------+
 | File                             | Position| Binlog_Do_DB| Binlog_Ignore_DB |
+-----------------------+----------+------------------+----------------------+
 | mysql-bin.000003 |      256   |                             |                                   |
+-----------------------+----------+------------------+----------------------+
1 row in set (0.00 sec)
テーブルロック解除
UNLOCK TABLES;

3,レプリケーションの設定(スレーブ)

my.confの設定
vi /etc/mysql/my.cnf
server-id = 2(マスターと違う数字)
log-bin = mysql-bin
レプリケーション先指定
CHANGE MASTER TO
MASTER_HOST='127.0.0.1',
MASTER_PORT=23306,
MASTER_USER='repl',
MASTER_PASSWORD='',
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=256;
先ほどのステータスで確認したファイルとポジションを記入
レプリケーションのステータス確認
SHOW SLAVE STATUS\G
ここが
Slave_IO_RunningがYes
Slave_SQL_RunningがYes
となればOK
レプリケーションの開始
START SLAVE;
何か一つ増やして中身をお互い確認し変更が同じであればOK

4,システム起動時にトンネルを掘る

/etc/rc.localに追記すればシステムが起動した際に実行してくれる
先ほどのautosshのコマンドをフルパスで、他ユーザーを指定するならば
sudo -u ユーザー名を追加

5,cronスクリプトでダンプを行う設定


SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
#MAILTO=root
MAILTO=""
HOME=/

# run-parts
0 4 * * * root/シェルファイル名.sh

上から5行を追加することでメールを送る必要がなくなる。
(最初にpostfixがインストールされていない為に実行されなかったので追記)
この内容であれば毎日午前4時にシェルが実行される

6、実行シェルの記述
以下を参考に。
http://d.hatena.ne.jp/alexam/20120609/1339228291

以上。結構こういうの頻繁に使いそうな予感・・・

2012年10月24日水曜日

画像認証(simple_capcha)を使ってメール送信

今回は問い合わせフォームを作成する際に画像認証が必要だったので勉強しました。
またメールを送る際にデータベースと関わりがない「Active model」を使用してみました。

まずはGemを使います。(Gemfile)
gem "galetahub-simple_captcha", :require => "simple_captcha"
Gemの情報はこちらhttps://github.com/galetahub/simple-captcha
次にsimple_captchaのdbを作成します。
$ rails g simple_captcha
$ rake db:migrate
インストールした後サーバーを再起動しこっからじゃんじゃん作ります。
ルーティング〜(config/routes.rb)
resource :message, only: [ :new, :create ] do
  get :thanks
end
次はモデル〜(models/message.rb)
class Message
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming
  extend SimpleCaptcha::ModelHelpers::SingletonMethods

  apply_simple_captcha
  attr_accessor :body, :name, :email, :subject

  validates :body, :subject, :name, presence: true
  validates :email, format: { with: /^[0-9a-zA-Z_\.\-]+?@[0-9A-Za-z_\.\-]+\.[0-9A-Za-z_\.\-]+$/, allow_blank: true }, presence: true

  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end
 
  def persisted?
    false
  end
end
「extend SimpleCaptcha::ModelHelpers::SingletonMethods」と「apply_simple_captcha」が重要です!
Active Modelの場合はいろいろと書く必要があります。
  • include ActiveModel::Validation
  • include ActiveModel::Conversion
  • extend ActiveModel::Naming
この3つはデフォルトで付けてもいいかもしれません。
あとはメールのバリデーションを行い、下に記述した
  • initialize
  • persisted?
もデフォルトで良いかと思います。

次はコントローラーを作成します。(controllers/messages_controller.rb)
class MessagesController < ApplicationController
  def new
  end
 
  def create
    @message = Message.new(params[:message])
    if @message.valid_with_captcha?
      MessageMailer.standard(@message).deliver
      redirect_to action: :thanks
    else
      flash.now.alert = "記入内容に不備があります。"
      render :new
    end
  end
 
  def thanks
  end
end
アプリケーションコントローラーにも追加(controllers/application_controller.rb)
include SimpleCaptcha::ControllerHelpers
ここはメール送るコントローラーとあまり変わりませんが、
「valid_with_captcha?」で画像認証の判断をしています。
やっとviewです。newを作成します。(messages/new.html.slim)
div class="message_views"
  h2 お問い合わせ
  div class="messages"
    div class="form"
      = form_for @message, url: message_path do |f|
        div class="form_column
          = f.label :name, "名前"
          = f.text_field :name
        div class="form_column
          = f.label :email, "メールアドレス"
          = f.text_field :email
        div class="form_column
          = f.label :subject, "件名"
          = f.text_field :subject
        div class="form_column"
          = f.label :body, "本文"
          = f.text_area :body
        div class="form_column_captcha"
          = f.simple_captcha label: "Enter ..."
        div class="submit"
          = f.submit "送信"
ここで重要なのは「f.simple_captcha label: "Enter ..."」ですね。Enterの所に好きな文言を入れればそれっぽくなります。
次はメールの文章内容です。(message_mailer/standard.text.erb)
○ wataruからのメッセージ受信のお知らせ
--------------------------------------------------------
<%= @message.name %>さんからメッセージが届いています。
送信元:<%= @message.email %>
--メッセージ--

<%= (@admin_message.body) %>

+………………………………………………………………………+
  メモってくれちゃってホントね!
+………………………………………………………………………+
最後はメーラーを作って終わりです。(mailers/message_mailer.rb)
class MessageMailer < ActionMailer::Base
  default charset: 'ISO-2022-JP', from: "info@example.com"

  def standard(message)
    @message = message
    mail(to: "wataru@example.jp", subject: message.subject)
  end
end

Ajaxでチェックボックスの値を保存

よくあるチェックボックスにチェックをon/offするだけで保存される仕組みをAjaxで作成しました。

@advertisements.each do |ad|
 = form_for ad, url: update_display_path(id: ad.id), remote: true do |f|
  = f.label :display, "on/off"
  = f.radio_button :display, true, { checked: ad.display }
的なフォームを作って
$(function(){
  $('#advertisement_display_true').click(function(){
    var val = $(this).val();
    var target = $(this.form).attr("action");
    $.ajax({
      type: "PUT",
      url: target,
      data: val,
      success: function(){
        alert("OK")
      }
    });
  });
});
とすればOK
流れは・・・
radio_buttonをクリックしたら、

そのvalueと

そのフォームのアクションを取ってきて

ajax内のtypeをput,

urlをアクション、

送るdataをvalueで、

成功時にOKっというメッセージを出す
という流れです。
Ajaxは利用者ががんがんスムーズに作れるので嬉しいですよね〜

2012年10月10日水曜日

RailsとMemocached

今回の案件で、
「同じIPアドレスから10分以内にアクセスが来たら、アクセスカウンターに加算しない」
という内容があったので、これをmemcachedを使って再現してみました。

まずはインストールその後Gemを入れます。
$ apt-get install memcached

https://github.com/mperham/dalli
gem "dalli" # memcached client

$ bundle install
次は設定です。キャッシュは10分で消すように設定します。
environments/development
MEMCACHED = Dalli::Client.new("localhost:11211", :expires_in => 600 )
あとはコントローラで以下の様に設定するだけだけ。
以下はコントローラーのプライベートで作りました。
「User」というテーブルに「dl_count」があってこれがアクセスカウンターです。
def counter
  key = "#{@user.id}-#{request.remote_ip}"
  unless MEMCACHED.get(key)
    @user.update_attribute(:dl_count, @user.dl_count + 1)
    MEMCACHED.set(key, true)
  end
end
あとは必要な所に上のメソッドを追加するだけ。
簡単でしたね〜

キャッシュの消し方も必要ですね。
コンソールで以下を実行します。
$ rails c
> dc = MEMCACHED
> dc.flush_all
以上でごあす!

postfixとrails

初めてこのサーバーでメールを送ろうと思った際に簡単なセットアップだけメモ

まずはサーバーでインストールをします。

# apt-get install -y postfix

よく分からないからとりあえず再スタート(動いているかの確認)
# /etc/init.d/postfix restart
Stopping Postfix Mail Transport Agent: postfix.
Starting Postfix Mail Transport Agent: postfix.
次にテスト〜
$ mailx memo@example.com
subject: test
test
cc:
シフトDで終了
とりあえずサーバーではこれだけにして、
 Railsで設定を以下のようにする。

enviroments/production
config.action_mailer.default_url_options = { :host => "example.com" }
config.action_mailer.smtp_settings = { :enable_starttls_auto => false }

developementでもテストしたい場合は同じようにdevelopmentにも設定。
ログで確認をする。

以上でとりあえずメールが届きました〜

2012年7月30日月曜日

メールを送る

今回はメールを送る方法を紹介します。

Gemを追加します。
gem 'delayed_job'
gem 'delayed_job_active_record'
gem 'daemons'
選び方などは省略するとして、モデルでdeliverを作成します。
has_many :destinations, through: :parcels, class_name: "User"

def destination_id_map=(hash)
  hash.each do |user_id, value|
    c = User.find_by_id(user_id)
    self.destinations << c
  end
end

LIMIT = 100

def deliver
  return unless status == "wating"

  log_dir = Rails.root.join('log', 'messages')
  Dir.mkdir(log_dir) unless File.exist?(log_dir)
  filename = sprintf('%06d.log', id)
  logger = Logger.new(Rails.root.join('log', 'messages', filename))

  logger.info('started. ' + Time.current.to_s(:db))
  current_parcels_id = nil
  loop do
    parcels = self.parcels.order('parcels.destination_id ASC').limit(LIMIT).includes(:destination)
    parcels = parcels.where('parcels.id > ?', current_parcels_id) if current_parcel_id
    parcels.each do |parcel|
      next if parcel.destination.deleted?

      begin
        UserMailer.notice(parcel).deliver
        parcel.update_attribute(:status, "sent")
        logger.info(sprintf("06d ok %s %s", parcel.destination_id, parcel.destination_mail))
      rescue Exception => e
        raise e if Rails.env.test?
        parcel.update_attribute(:status "error")
        logger.info(sprintf("%06d NG %s %s", parcel.destination_id, parcel.destination_mail e.to_s))
      end
    end
    break if parcels.size < LIMIT
    current_parcel_id = parcels.last.id
  end
  update_attribute(:status, "sent")
  logger.info('Ended. ' + Time.current.to_s(:db))
end
mailer/user_mailer.rbを作成します
class UserMailer < ActionMailer::Base
  default from: "from@example.com"

  def notice(parcel)
    mail(
      :subject => parcel.message.subject,
      :from => parcel.message.from,
      :to => parcel.destination.mail
    )
  end
end
controllerのcreateで作成して完了です。
def create
  @message = Message.new(params[:message])
  if params[:destinations].kind_of?(Hash)
    @message.destination_id_map = params[:destinations]
    @message.save!
    @message.delay.deliver
    redirect_to action: :index
  end
end
delayed_jobは使い方を学ぶ必要があります。Google先生に聞いて下さい。

ログイン時にユーザー名を保存

ログインでは次回から自動ログインはたくさん見かけますが、今回はクッキーを使った、名前を保存を作成します。

まずはチェックボックスを作成します。
<%= check_box_tag :remember_me, cookies[:user_name].present? %><%= label_tag :remember_me, 'ユーザー名を保存' %>
つぎにコントローラーの設定を変えます。
def create
  admin = Administrator.find_by_user_name(params[:user_name]
  if params[:remember_me] && params[:user_name].present?
    cookies.permanent[:user_name] = params[:user_name]
  else
    cookies.delete(:user_name)
  end
  -省略-
end
という形にします。
もしパラメーターにremember_meが来てて、user_nameが空ではない場合、
cookiesにpermanent(永遠)に保存するということです。
今回はユーザー名だけなのでこれで問題ないと思います。

最後にログインフォームに加えます。
<%= text_field_tag :user_name, cookies[:user_name] %>
これでクッキーが入ります。
こう見ると簡単でしたね〜

2012年7月26日木曜日

Ajax:保存orエラーを返す

今回はフォームの入力欄で入力が完了したら自動で(Ajax)で保存を行う。
ただしすでにあるモノを更新するというテイで!


controller
  def index    
  @users = User.order('id ASC')
  end

  # Ajax
  def update
    @user = User.find(params[:id])
    @user.update_attributes params[:user]
    @users = User.order('id ASC')
    render :index
  end

次にviewのindexです。
<div class="success"><p></p></div>
<% @users.each do |c| %>
   <tbody>
      <tr>
        <%= form_for
           (c, url: { action: :update, id: c.id }, 
            html: { id: "user_#{c.id}" }
           ) do |form| 
        %>
        <td><%= c.name %></td>
        <td>
           <%= form.text_field :display_name, class: "edit_display_name" %>
        </td>
        <td>
          <%= form.check_box :shown, { class: "shown" }, true, false  %>
        </td>
        <% end %>
      </tr>
   </tbody>
<% end %>
これで1つの箱の中にいくつものformがある設定ができました。
次にAjaxです。

assets/javascript内に自由にファイルを作成します。
$(function(){   $(".edit_display_name").change(function() {
    var val = $(this).val();
    var val2 = $(this).parent().next().children("input").val();
    var target = $(this.form).attr("action");
    if (val == "") {
      alert("表示名を入力してください。");
      $(this).css("background-color","yellow");
      return
    }
    $.ajax({
      type: "PUT",
      url: target,
      data: {
        user: {
          display_name: val,
          shown: val2
        }
      },
      success: function() {
        $("div.success").show()
        $("div.success p").html("「表示名」を" + val + "に変更しました。");
      }
    })
  })


  • val,val2でformのvalueを取ってきて、targetのactionを指定しています。
  • それをdataに入れて、urlにtargetを入れます。
  • updateなのでもちろんtypeはPUTです。
  • その前にエラーの場合、今回はvalが空の場合はalertを出し、returnしています。
  • エラー時にはcssでフォームを黄色にしています。
  • もし問題なく進めば、successが呼ばれて、メッセージが呼ばれます。
  • div.successはdisplay:noneに指定しておけば以上のsuccessでOKです。

Rails:簡単戻るボタン

簡単な戻るボタンを作成

<%= link_to '戻る', :back %>

これはhistory.back()と同じです。 これでOKなんですけど、前のURLによって変えたい時は

<% if request.referer.include?("?") %>

とすると検索されていた場合というif文が作成できる


以上です。

jQuery:全てにチェックを付ける!

今回はよくある全てにチェックを付けるという内容です

参考までに


assets/javascript内に新しいファイルを作成します。
$(function() {
  $("#全てにチェックのcheck_boxのID名").click(function() {
    if(this.checked) {
     $(".チェックボックスのクラス名").attr('checked', $(this).attr('checked'));
    }else{
     $(".チェックボックスのクラス名").removeAttr('checked')
    }
  });
});
ここで重要なのはチェックボックスのクラス名で、クラスにすること。基本IDは1つなので当たり前か・・・ これだけでできあがり!

nginxでrailsのpublic内を取得する方法

ローカルではできたのに、サーバーに上げたら見えなくなったのでメモ


root /var/www/hoge/current/public;

---省略---

 if (!-f $request_filename) {
    proxy_pass http://hoge_server;
}


重要なのは下の部分を追加するということ!

2012年7月9日月曜日

Ubuntu12.04ーランチャーの項目削除!

 クラッシックタイプで上のランチャーの項目を削除したい場合の方法は・・・

Alt + 右クリック!!