rails 4.2, paperclip, Jcropを使ってユーザが写真切り取りし保存する機能をつける

#はじめに
大体探しつくのは、以下のものである

[#182 Cropping Images ](http://railscasts.com/episodes/182-cropping-images)
[#182 Cropping Images (revised)](http://railscasts.com/episodes/182-cropping-images-revised)

問題は、これらはとても使えるがすでに5年前のために、古くてRails4でそのまま使うことができない。

作者もこのままで挑戦したが、Infinite loopが起こった。

以下の様なStack Overflowにあたることになる。
[paperclip jcrop and rails 4 infinite loop fix](http://stackoverflow.com/questions/20033053/paperclip-jcrop-and-rails-4-infinite-loop-fix)

今回さ作者が、いろいろ調べ、最終的にできるようになるまで時間がかかったので、ここに書いて他の人の参考になればと思い、記す。

基本的に、上記のコードをそのまま使うが、一部変更がある。流れを理解するために、上のビデオを見ることを奨励する。

#前提

すでにPaperclipで画像を保存できる状態だとする。

今回作者の条件は、Userモデルに、imageという画像ファイルを保存するようになっている。


#本題

##1. Jcropをダウンロードし、Railsで読み込む

[Jcrop » the jQuery Image Cropping Plugin](http://deepliquid.com/content/Jcrop.html)

からダウンロードし、jquery.Jcrop.jsとjquery.Jcrop.min.cssをそれぞれ、vendor/assets/javascripts/と vendor/assets/stylesheets/に置く

これをapp/assets/javascripts/application.jsで以下の様に読み込む

```rb
//= require jquery.Jcrop
```

app/assets/javascripts/application.css

```rb
*= require jquery.Jcrop.min

```
これでjcropを読み込むことができた！

##2. cropするテンプレートを作り、そこに飛ばすようにする。

app/views/users/crop.haml

```haml
:javascript
  $(function(){
    $("#cropbox").Jcrop({//サイズ変更があったら、アップデートする
      onChange: update_crop,
      onSelect: update_crop,
      setSelect: [0,0,300,300],
      aspectRatio: 1
    });
  });
  function update_crop(coords){//アップデートする関数で、
    var rx = 100/coords.w;
    var ry = 100/coords.h;
    $("#preview").css({//プレビューを更新する。
      width: Math.round(rx * #{ @user.image_geometry(:medium).width }) + 'px',
      height: Math.round(ry * #{ @user.image_geometry(:medium).height }) + 'px',
      marginLeft: '-' + Math.round(rx * coords.x) + 'px',
      marginTop: '-' + Math.round(ry * coords.y) + 'px'
    });
    var ratio = #{@user.image_geometry(:original).width}/#{@user.image_geometry(:medium).width};
    $("#crop_x").val(coords.x * ratio);//クロップするための4つの変数をformの中で更新する。
    $("#crop_y").val(coords.y * ratio);//
    $("#crop_w").val(coords.w * ratio);//
    $("#crop_h").val(coords.h * ratio);//
  }
= image_tag @user.image.url(:medium), id: "cropbox"

%h4 プレビュー
%div{style: "width:100px; height:100px; overflow:hidden"}
  = image_tag @user.image.url(:medium), id: "preview"


= form_for @user do |f|
  .form-group
    - for attribute in [:crop_x, :crop_y, :crop_w, :crop_h]
      = f.hidden_field attribute, id: attribute

  %p= f.submit '送信', class: 'btn btn-primary'

```

users_controller.rbのupdate（createでも）で、画像が更新されるときに、cropに飛ぶようにする。

```rb
  def update
    @user = User.find_by(account: params[:id])
    if @user.update_attributes(user_params)
      if params[:user][:image].blank?#画像がないときは、今までどおり更新
        flash[:success] = "更新されました"
        redirect_to @user
      else#画像があるときは、Cropに飛ばす。
        render :crop
      end
    else
      render :edit
    end
  end
```

## 3. userモデルで、更新した時に、画像を変更するようにコードを足す

app/models/user.rb

```rb
attr_accessor .... , :crop_x, :crop_y, :crop_w, :crop_h
after_update :reprocess_image, if: :cropping?#クロップするための位置情報が送られてきたら、updateの後に、reprocess_imageというPrivateメソッドを呼ぶ

...

has_attached_file :image, styles: {medium: "300x300>", thumb: "100x100#", icon: "40x40#"}, processors: [:cropper]
# mediumを使ってCropするので、”>”を使う。"#"を使うと直接真ん中部分を正方形に切り取って、保存されてしまうため、切り取り作業を行うときに、すでに正方形のものからしか切り取れなくなる！
# processors: [:cropper]は4に書く、paperclipをoverrideするClass名である。（この名前は任意）

def cropping?# クロップするための位置情報が送られてきたかチェック
  crop_x && crop_y && crop_w && crop_h
end

def image_geometry(style=:original)
  @geometry ||= {}
  @geometry[style] ||= Paperclip::Geometry.from_file(image.path(style))
end

...

private

def reprocess_image # ここが、RailsCastと違う！！
  image.assign(image)
  image.save
end
```



## 4. 最後に、PaperclipをOverrideする(railscastと異なる部分)

config/initializers/cropper.rbに以下を書く(railscastでは、lib/processors/cropper.rbとしていた。)

```rb
module Paperclip
  class Cropper < Thumbnail
    def transformation_command
      if crop_command
        crop_command + super.join(' ').sub(/ -crop \S+/, '').split(" ")
      else
        super
      end
    end

    def crop_command
      target = @attachment.instance
      if target.cropping?
        [" -crop", "#{target.crop_w}x#{target.crop_h}+#{target.crop_x}+#{target.crop_y}"]
      end
    end
  end
end
```

、paperclipは、以下のようにimagemagickのコマンドを実行するので、上のコードはインスタンス変数@userから形を整えていると思えばいい。

```bash
convert input.png -crop 100x100+0+10 output.png
```



完成！

## うまくいかない場合のチェック点。

###切り出しのJcropが動いているか？
 hidden_fieldとなっている、crop_x, crop_y, crop_w, crop_hをtext_fieldに変えることで、変数がちゃんとうごいているかをチェックできる

###overrideしたtransformation_commandが呼ばれているか。
puts "transformation_command"などを入れ、確認。
railscastの指定する場所だと呼ばれないので、これが結構重要


#参考
[how to allow users to crop images in rails 4](http://andowebsit.es/blog/noteslog.com/post/how-to-allow-users-to-crop-images-in-rails-4/)