2012年12月16日日曜日

Rails 投票機能を作る その1

デザインやバリデーションは全くないものの、アプリ基本機能が大分実装されてきた。最後の追い込みとして、「良い」「悪い」の投票機能を実装する。一般的なpostに対する、良い、悪いの投票を想定する。

(仕様)
1.作品1つに対して、複数の投票がある(1:Nの関係)。
2.投票「良い」,「悪い」があり、1つのエンティティで保持する。
3.「良い」は、名前のみで投票することが出来る。登録ユーザならば入力も不要、ゲストは名前、E-Mailが必須とする。
4.「悪い」は、「良い」の条件に追加して、理由が必須、特に公序良俗違反か否か判定する項目を準備する。
5.夜間処理などで集計はせず、作品エンティティに「良い」「悪い」サマリ項目を追加し、ボタンが押される都度、+1カウントしていく
6.一度投票したユーザは、2回投票できない、判定はE-Mailで重複チェックする。


まず、作品側のmodel,controllerを作成する。
今日は準備で完了。

2012年12月12日水曜日

rails:comment機能を実装:削除機能

comment機能の実装の続き。私のビジネスは残業必須で、rails開発は睡眠時間を削ることに。でも楽しい。


要件
1.入力者の名称が表示されるようにする。
2.入力者が、新規入力と、削除ができるようにする。
3.コメントの入力、削除に、画面遷移・再読込を不要にする。
4.コメントの入力者のアイコンが表示されるようにする。

今日はAjaxを織り交ぜ、削除機能の実装を行う。

1.controller:commentにdestroyメソッドを追加

  def destroy
    @comment = Comment.find(params[:id])
    @comment.destroy
    render json: { comment: @comment }
  end

renderがポイント。viewでどのコメント行を削除するかのkeyとなる。

2.view:photo.showを改修1
<h2>コメント</h2>
<ul>
<% if @photo.comments.any? %>
<% @bsc.comments.each do |comment| %>
<li id="comment_<%= comment.id %>"> (注意)
<%= image_tag comment.user.avatar_url(:thumb).to_s if comment.user.avatar? %>
<%= comment.body %> (by <%= comment.user.disp_name %>)
<%= link_to image_tag("ico_xmark3c15.gif"), [comment.photo, comment], :confirm => 'Sure?',
                                               :method => :delete,
                                               :remote => true %>
</li>
<% end %>
<% else %>
<li>no comment yet.</li>
<% end %>
</ul>


こう書いていて大いに疑問を感じたことがあって(BRAHMAN聞きながらだから余計)、世の中のSampleを有りがたく参考にしながら(ほんとうに有難い)書いていると、どれがモデル名なのか、変数なのか、何なのかマヨことが良くある。もしかするとこれがrubyのrailsのStandardなのかもしれない。まだ、どうも引っかかる。あ、開発時の命名規則とか決めていないからか、そうだそうだ(反省)

愚痴はどうでもよく、viewの開発ポイントは、画像をlink_to_image_tagで入れたところ。バッテン画像。

3.view:photo.showを改修2
viewの下部に以下を追加

<script>
$(function() {
  $('a[data-method="delete"]').live('ajax:success', function(e, data, status, xhr) {
    $('#comment_'+data.comment.id).fadeOut("slow");
  });
  
});
</script>
酔いどれロートルの私はここでハマった。上記(注意)がすっぽ抜け、削除してもエンティティからレコードは消えるが、一向に画面は微動たりしない。たまにブラウザのソース表示で確認していくしかないなぁと感じた一瞬。1時間浪費。

さて検証。まず2つのコメントを準備。バッテンアイコンも表示されている。













2つ目のバッテンを押すと、Dialogが表示。OKを押す。

すーっとフェードアウトして消えていく。やった。




2012年12月11日火曜日

rails:comment機能を実装:ユーザ名の表示

せっかく出来た作品ページに、comment機能を追加する。

要件
1.入力者の名称が表示されるようにする。
2.入力者が、新規入力と、削除ができるようにする。
3.コメントの入力、削除に、画面遷移・再読込を不要にする。
4.コメントの入力者のアイコンが表示されるようにする。

2が半分できているだけで、ちょっと道半ば。
今日は1の完了を目標にする。

Photo:Commentが1:多で関連しているが、Userが今の機能では検索できていない。
まずこれを何とかする。

1.modelsフォルダのcomment.rbを改修

class Comment < ActiveRecord::Base
  belongs_to :photo
  belongs_to :user #これを追加

2.modelsフォルダのuser.rbを改修

class Muserdtil < ActiveRecord::Base
 has_meny :comments

3.Photoのcontroller:showを改修

@photo = Photo.find(params[:id],:include=>:user,
      :conditions=>["photos.user_id = users.id"])

4.Photoのview:showを改修

<% if @photo.comments.any? %>
<% @photo.comments.each do |comment| %>
<li>
<%= image_tag comment.user.avatar_url(:thumb).to_s if comment.user.avatar? %>
<%= comment.body %> (by <%= comment.user.DISP_NAME %>)
</li>
<% end %>
<% else %>
<li>no comment yet.</li>
<% end %>
アイコンが大きすぎるのが気になる(サイズを小さくしておこう)ものの、
1.入力者の名称が表示されるようにする。
4.コメントの入力者のアイコンが表示されるようにする。
が達成。一気に4まで達成出来たのは良かった。



2012年12月10日月曜日

rails:comment機能を実装

せっかく出来た作品ページに、comment機能を追加する。

要件
1.入力者の名称が表示されるようにする。
2.入力者が、新規入力と、削除ができるようにする。
3.コメントの入力、削除に、画面遷移・再読込を不要にする。
4.コメントの入力者のアイコンが表示されるようにする。

・まずは、子model(コメント)基本機能を作成する。
$ rails g model comment muserdtil_id:integer body:text photo:references

・routes.rbで親modelに対して以下の定義をする。
resources :photo do
  resources :comments
end

・子modelはreferrences設定をすると、勝手にbelongs_to :photo設定してくれるので、設定は不要。

・親modelの方は、has_many設定が必要。
has_many :photos

・親modelのview:showに以下を追加
<h2>Comments</h2>
<ul>
<% if @bsc.photocomments.any? %>
<% @bsc.photocomments.each do |comment| %>
<li>
<%= comment.body %> (by <%= comment.muserdtil_id %>)
</li>
<% end %>
<% else %>
<li>no comment yet.</li>
<% end %>
</ul>
追加完了。

・子のモデルのControllerを作る。
$ rails g controller Comments
def create
    @photo = Photo.find(params[:photo_id])
    @comment = Photo.find(params[:photo_id]).comments.create(params[:comment])
    redirect_to photo_path(@photo)
  end


・親modelのview:showに投稿フォームを追加。
<h2>Add a comment</h2>
<%= form_for([@post, @comment]) do |f| %>
<div class="field">
  <%= f.label :body %>
  <%= f.text_area :body, :rows => 5 %>
</div>
<div class="actions">
  <%= f.submit %>
</div>
<% end %>
私の場合、投稿者は、LoginSessionから取得してくるので、項目はなし。

・親controllerのshowにコメント枠を追加する。
# コメントの枠を作成する。
@photo = Photo.find(params[:id]).comments.build

お、ユーザ名が表示できていない。commentをreateする際に盛り込んでいないからですね。次回はコメント欄にユーザ名を表示する開発をします。








2012年12月6日木曜日

ImageMagickのインストールに戦々恐々

Webアプリで画像を扱う時、最近でこそcloudinaryの様な、簡単な画像編集までやってくれるサービスが出てきているものの、王道はImageMagick。

RubyではImageMagickを使うためにRMagickをgemで入れる必要があるが、ImageMagickのインストールの後ろには累々たる若葉マーク開発者の屍が多数、とまで行かないものの、多くの開発者の抜け毛を誘っている事は間違いないでしょう。

さて、私も導入します。

1.ImageMagickのインストール。

brew install imagemagick
==> Installing imagemagick dependency: pkg-config ==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/pkg-config-0.27.1.lion.bottle.tar.gz ######################################################################## 100.0% ==> Pouring pkg-config-0.27.1.lion.bottle.tar.gz /usr/local/Cellar/pkg-config/0.27.1: 9 files, 624K ==> Installing imagemagick dependency: jpeg ==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/jpeg-8d.lion.bottle.tar.gz ######################################################################## 100.0% ==> Pouring jpeg-8d.lion.bottle.tar.gz /usr/local/Cellar/jpeg/8d: 18 files, 1.3M ==> Installing imagemagick ==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/imagemagick-6.7.7-6.lion.bottle.3.tar.gz ######################################################################## 100.0% ==> Pouring imagemagick-6.7.7-6.lion.bottle.3.tar.gz ==> Caveats Some tools will complain unless the ghostscript fonts are installed to: /usr/local/share/ghostscript/fonts ==> Summary /usr/local/Cellar/imagemagick/6.7.7-6: 1405 files, 32M

あら、あっさり。

2.Sampleアプリ作成。
せっかくだから動かしましょう。
篳篥日記さんのこの記事を参考に。
$ rails new carrierwave_solo


3.Gemfileを編集して、Carrierwave導入

Gemfileに以下を追加。
gem 'rmagick'
gem 'carrierwave'


$ bundle install
Using carrierwave (0.7.1) 
Installing rmagick (2.13.1) with native extensions 

4.ScaffoldでUserを作成する。
$ rails g scaffold User name:string avatar:string
carrierwaveでは、画像保存カラムは、stringという決まりがあるらしい。

5.画像Uploadクラスimageを作成。
$ rails g upload image
Could not find generator upload.あれ?
$ rails g uploader image
      create  app/uploaders/image_uploader.rb
うまくいった。

6.app/uploader/image_uploader.rbを編集。

# encoding: utf-8
class ImageUploader < CarrierWave::Uploader::Base
  #
  include CarrierWave::RMagick
  include Sprockets::Helpers::RailsHelper
  include Sprockets::Helpers::IsolatedHelper

  # Choose what kind of storage to use for this uploader:
  storage :file

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # Process files as they are uploaded:
  process :resize_to_limit => [200, 200]

  # Create different versions of your uploaded files:
  version :thumb do
   process :resize_to_fill => [50, 50]
  end

  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  def extension_white_list
     %w(jpg jpeg gif png)
  end
end

7.app/uploader/avatar_uploader.rbを新規作成。
ImageUploaderクラスを継承する形で作成。

# coding: utf-8
class AvatarUploader < ImageUploader
  process :resize_to_fill => [100, 100]

  version :thumb do
    process :resize_to_fill => [50, 50]
  end
end



8.ファイルサイズのvalidationを行う為の設定。
https://gist.github.com/1009861#file_file_size_validator.rb
ここを参考に、
8-1.lib/file_size_validator.rbファイルを作り、上記アドレスから内容を引っ張ってくる。
8-2.en.ymlにエラーメッセージの定義を追加する。(いずれja.ymlに移設しなければ)
8-3.application.rbに以下を追記。

config.autoload_paths += %W(#{config.root}/lib)


9.app/model/user.rbを編集。

class User < ActiveRecord::Base
  attr_accessible :avatar, :name
  mount_uploader :avatar, AvatarUploader
  validates :name, presence: true
  validates :avatar, file_size: { maximum: 50.kilobytes.to_i }
end
10.Viewを編集する。
10-1.app/views/shared/_errors.html.erbを作成。

<% if model.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(model.errors.count, "error") %> prohibited this user from being saved:</h2>

    <ul>
      <% model.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>
10-2.app/views/users/_form.html.erbを編集。

<%= form_for(@user) do |f| %>
  <%= render partial: '/shared/errors', locals: { model: @user } %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>

  <div class="field">
    <%= f.label :avatar %><br />
    <%- if @user.avatar? -%>
      <label><%= f.check_box :remove_avatar %>Remove</label>
      <%= image_tag @user.avatar_url(:thumb).to_s %>
      <%= f.hidden_field :avatar_cache if @user.avatar_cache %>
    <%- end -%>
    <%= f.file_field :avatar %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
10-3.app/views/users/show.html.erbを編集。

<p id="notice"><%= notice %></p>

<p>
  <b>Name:</b>
  <%= @user.name %>
</p>

<p>
  <b>Avatar:</b>
  <%= image_tag @user.avatar_url.to_s if @user.avatar? %>
</p>

<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>

10-4.app/views/users/index.html.erbを編集。

<h1>Listing users</h1>

<table>
  <tr>
    <th>Name</th>
    <th>Avatar</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= image_tag user.avatar_url(:thumb).to_s if user.avatar? %></td>
    <td><%= link_to 'Show', user %></td>
    <td><%= link_to 'Edit', edit_user_path(user) %></td>
    <td><%= link_to 'Destroy', user, confirm: 'Are you sure?', method: :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New User', new_user_path %>

11.migrate忘れとった
$ rake db:migrate
ひとまずUploadの動作確認は完了。

感想

・どうやらremove_avatarとavatar_cacheをエンティティに追加しないとダメっぽい。viewから該当項目を削除すると、問題なくuploadできるようになった。
・画像の比率を考えたリサイズ方法を確立しなければならない。Flickrとかどうやってんだろう。