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 + 右クリック!!

2012年7月5日木曜日

文字列の最大値を繰り上げ

数字なら分かるのですが、文字列の場合繰り上げするのは・・・
と思っていたらありました便利なモノが!それは「succ」

before_save do
  last_number#before_saveで登録してます
end

def last_number
    if new_record?#新規登録時?
      if number.blank?#numberカラムは空?
        last_column = User.find(:all).max#ユーザーテーブル全ての最大値だけを探します。
        if last_column
          self.number = last_column.number.succ#numberカラムの最大値を1つ繰り上げします。
(AAAAならAAAB,1なら2というように)
        else
          self.number = "1"#一番最初の登録で記入がないなら1を入れます。
        end
      end
    end
  end

CSVのアップロード

業務系のシステム開発であれば必要となることが多いCSVのアップロード。
それを作ってみました!
環境は
Ruby 1.9.3
Rails 3.2.5
DB mysql2

さぁ作り始めます!
まずはCSVを作成します。
できればテストでも使いたいので"spec"下に"data"フォルダでも作って置いておきましょう!

モデルから作ります。
cattr_accessor :rejected_rows, instance_reader: false
@@rejected_rows = []#エラーメッセージを表示します。

 class << self
    def create_from_csv_data(data)
      self.rejected_rows.clear#エラーメッセージをクリアにします。
      data = NKF::nkf('-S -w', data)
      csv = CSV.new(data)
      inserted = 0#新規登録件数
      updated = 0#更新件数
      transaction do
        csv.each_with_index do |arr, idx|
          next if arr[0] == "登録日時" #CSVの一行目の一つ目が登録日時であってはなりません
          next if arr.all? { |value| value.empty? } #valueが空は飛ばします。
          user = find_by_number(arr[1]) #userのnumberカラムを探します。
          already_number = ( arr[1] != User.find_by_number(arr[1]))#既に登録されているかどうか調べます。
          #登録済みのユーザー番号の場合
          if user#userのユーザー番号が存在した場合
            user.assign_attributes(values_for(arr), without_protection: true)#更新します。
            if user.changed?#変更されている場合はsaveします。
              if user.save
                updated += 1#更新件数に1追加します。
              else
                self.rejected_rows << [ idx, user ]#エラーの場合は行数とエラーメッセージを挿入します。
              end
            end
            #ユーザー番号が空もしくは登録したいユーザー番号が入っている場合
          elsif already_number || arr[1].blank?
            user = self.new(values_for(arr), without_protection: true)#新規登録します。
            if user.save
              inserted += 1#新規登録件数に追加します。
            else
              self.rejected_rows << [ idx, user ]#エラーメッセージを入れます。
            end
          else
          end
        end

        raise ActiveRecord::Rollback if rejected_rows.present?#エラーの場合ロールバックしてエラーメッセージを返します。
      end
      return inserted, updated#無事登録出来た場合は件数を返します。
    end
 
    private
    def values_for(arr)#ユーザーの内容をここに追加します。(privateにしないと長いので)
      {
        number: arr[1],#ユーザー番号
        family_name: arr[2],#名字
        kana_family_name: arr[3],#名字フリガナ
        given_name: arr[4],#名前
        kana_given_name: arr[5],#名前フリガナ
        sex: arr[6],#性別
        birth: arr[7],#誕生日
        email: arr[8],#メール
        zip_code: arr[9],#郵便番号
        prefecture: arr[10],#都道府県
        address1: arr[11],#住所
        address2: arr[12],#住所2
        phone: arr[13]#電話番号
      }
    end
  end
次にルーティングを指定します。
get 'users/import', controller: 'users', action: 'import'
post 'users/csv_import', controller: 'users', action: "csv_import"
次にコントローラーです。
def import
   User.rejected_rows.clear#エラーをクリアします。
end

def csv_import
  if params[:file].present?
    begin#トランザクション開始です。
      inserted, updated = User.create_from_csv_data(params[:file].read)#先ほど作りましたメソッドです。
      if User.rejected_rows.empty?#エラーがないなら?
        flash[:notice] = I18n.t(:notice, scope: [ :users, :csv_import], inserted: inserted, updated: updated)#i18nを使って更新と新規登録の文章をflashで出しています。
        redirect_to action: :import
      else
        render 'import'
      end
    rescue => e
      flash.now.alert = e.message#エラーメッセージを出しています。
      render 'import'
    end
  else
    flash.now.alert = "ファイルを選択して下さい。"
    render 'import'
  end
end
やっとビューにいけました
#フォームです。ここは簡単ですね。
<%= form_tag([:users, :csv_import], :multipart => true) do %>
  <table>
    <th>
      <%= label_tag :file, t('.file') %>
    </th>
    <td>
      <%= file_field_tag(:file) %>
    </td>
  </table>
  <div class="submit">
    <%= submit_tag t('.upload') %>
  </div>
<% end %>
#以下はエラー文
<% if User.rejected_rows.present? %>
<p class="alert">CSVファイルに以下の問題が見つかりました。修正して再びアップロードしてください。</p>

<table class="errors">
    <tr>
      <th>行番号</th>
      <th>エラーの説明</th>
    </tr>
    <% User.rejected_rows.each do |row| %>
      <% idx = row[0]; user = row[1] %>
      <tr>
        <td><%= idx + 1 %></td>
        <td class="error_content">
          <ul>
            <% user.errors.full_messages.each do |msg| %>
              <li><%= msg %></li>
            <% end %>
          </ul>
        </td>
      </tr>
    <% end %>
  </table>
<% end %>

なるべくコントローラーを軽くして動きが分かりやすいようにします。
モデルで書けるものはモデルに書くことをオススメします。

Ubuntu-ATOK3の枠を消す方法

ATOKを入れて目障りな左下の枠を消す方法!
# cd /etc/X11/xinit/xinput.d
# vi iiimf
その中の一番下に
/opt/atokx3/sample/iiimf_status_hide
を記入する よって以下のようになる
XIM=iiimx
XIM_PROGRAM=/usr/bin/iiimx
XIM_ARGS=-iiimd
GTK_IM_MODULE=iiim
QT_IM_MODULE=xim

if [ "$lang_region" = "ja_JP" ] ; then
  export HTT_DISABLE_STATUS_WINDOW=t
  export HTT_GENERATES_KANAKEY=t
  export HTT_USES_LINUX_XKEYSYM=t
  export HTT_IGNORES_LOCK_MASK=t
  export JS_FEEDBACK_CONVERT=t
fi

/opt/atokx3/sample/iiimf_status_hide
これで僕は解決されました!