Rails4+Paperclip+ActiveAdminのメモ


# Paperclip
- https://github.com/thoughtbot/paperclip

## installation

brew install imagemagick

gem 'paperclip', '~> 4.1'


:Bundle

## migration
:Rails generate migration AddFileUpload 

:Rmigration

```
class AddFileUpload < ActiveRecord::Migration
  def up
    add_attachment :users, :avatar
  end

  def down
    add_attachment :users, :avatar
  end
end
```

:Rake db:migrate
:Rserver!


## Model

```
  has_attached_file :avatar,
    styles: { medium: "300x300>", thumb: "100x100>" },
    default_url: "/images/:style/missing.png"

  validates_attachment_content_type :avatar,
    content_type: /\Aimage\/.*\Z/
```

~~ちなみ画像サイズの後に >はリサイズ、 #を指定するとトリミングしてくれるらしい。~~
　#はその大きさに拡大された
https://github.com/thoughtbot/paperclip/wiki/Thumbnail-Generation


## View

```
<%= form_for @user, :html => { multipart: true } do |f| %>
  <%= f.text_field :title %>
  <%= f.file_field :avatar %>
  <%= f.submit %>
<% end %>
```

## Controller
```
def create
@user = User.create(user_params)
end

private

private
def user_params
params.require(:user).permit(:title, :avatar)
end
```


## 表示
```
<%= image_tag user.avatar.url %>
<%= image_tag user.avatar.url(:medium) %>
<%= image_tag user.avatar.url(:thumb) %>
```

## DBに追加されたカラムは？

:Rschema users

```
create_table "users", force: true do |t|
  t.string   "title"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "avatar_file_name"
  t.string   "avatar_content_type"
  t.integer  "avatar_file_size"
  t.datetime "avatar_updated_at"
end
```

## ファイルの実態はどこ？
Modelに定義したsytylesディレクトリにはいってた

```
▾ system/
  ▾ users/
    ▾ avatars/
      ▾ 000/
        ▾ 000/
          ▾ 007/
            ▾ medium/
                くにお.gif
            ▾ original/
                くにお.gif
            ▾ thumb/
                くにお.gif

```


## 削除
@user.avatar = nil
@user.save

もしくは
@user.destroy

ファイルの実態も勝手に消えた


## S3へアップロード

### インストール
gem 'aws-sdk'

:Bundle


### 設定ファイルを作る
もしくはモデルに書くんだけど、設定ファイルの方が楽そうなのでそうした

config/s3.yml

```
production:
  bucket: xxxxxxxxxx
  access_key_id: yyyyyyyyyy
  secret_access_key: zzzzzzzzzz
development:
  bucket: xxxxxxxxxx
  access_key_id: yyyyyyyyyy
  secret_access_key: zzzzzzzzzz
test:
  bucket: xxxxxxxxxx
  access_key_id: yyyyyyyyyy
  secret_access_key: zzzzzzzzzz

```

:Rserver!


### Model

```
has_attached_file :avatar,
  styles: { medium: "300x300>", thumb: "100x100>" },
  default_url: "/images/:style/missing.png",
  storage: :s3,
  s3_credentials: "#{Rails.root}/config/s3.yml",
  path: ":attachment/:id/:style/:filename"
```


## 画像を保存するパスの変更
デフォルトは
:rails_root/public/system/:attachment/:id/:style/:filename

使える変数
:attachment(画像フィールドの複数形)
:id (レコードid)
:style(指定した画像のサイズ)
:filename (アップロードした画像名)
:extension (画像の形式)
:class（Class名)



## 画像ファイルの縦横を取得
:Rails generate migration AddImageDimentions

```
class AddImageDimension < ActiveRecord::Migration
  def change
    add_column :users, :image_width, :integer
    add_column :users, :image_height, :integer
  end
end
```

:Rake db:migrate

### Model
```
before_save :extract_dimentions
def image?
  avatar_content_type =~ %r{^(image|(x-)?application)/(bmp|gif|jpeg|jpg|pjpeg|png|x-png)$}
end

def extract_dimentions
  return unless image?
  tempfile = avatar.queued_for_write[:original]
  unless tempfile.nil?
    geometry = Paperclip::Geometry.from_file(tempfile)
    self.image_width = geometry.width.to_i
    self.image_height = geometry.height.to_i
  end
end
```


## 画像のURLの難読化

### /config/initializers/paperclip_defaults.rb
イニシャライザに設定しておく。:hash_sercretはSecureRandom.base64(128)をつかってる。
```
Paperclip::Attachment.default_options.update({
  :path => ":rails_root/public/system/:attachment/:hash/:id.:extension",
  :hash_secret => "適当な文字列"
})
```

:Rserver!

### Model
```
has_attached_file :avatar,
  url: "#{ActionController::Base.relative_url_root}/system/:attachment/:hash/:id.:extension"
```


## ファイルのパスやURLを取得したい
save後、モデル.:attachment.pathで取得できた

```
@user.save
@user.avatar.path
@user.avatar.url
```


## activeadminで使いたい


### installation

Gemfile

```
gem 'formtastic', github: 'justinfrench/formtastic'
gem 'ransack', github: 'activerecord-hackery/ransack', branch: 'rails-4.1'
gem 'polyamorous', github: 'activerecord-hackery/polyamorous'
gem 'activeadmin', github: 'gregbell/active_admin'
```

:Bundle
:Rails generate active_admin:install


このへんをやる、予定

```
1. Ensure you have defined default url options in your environments files. Here
   is an example of default_url_options appropriate for a development environment
   in config/environments/development.rb:

     config.action_mailer.default_url_options = { :host => 'localhost:3000' }

   In production, :host should be set to the actual host of your application.

2. Ensure you have defined root_url to *something* in your config/routes.rb.
   For example:

     root :to => "home#index"

3. Ensure you have flash messages in app/views/layouts/application.html.erb.
   For example:

     <p class="notice"><%= notice %></p>
     <p class="alert"><%= alert %></p>

4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:

     config.assets.initialize_on_precompile = false

   On config/application.rb forcing your application to not access the DB
   or load models when precompiling your assets.

5. You can copy Devise views (for customization) to your app by running:

     rails g devise:views

```

:Rake db:migrate

:Rserver!
:Rails generate active_admin:resource user


### ActiveAdminでPaperclipを動かす
admin/user.rb

```
ActiveAdmin.register user do
  form multipart: true do |f|
    f.inputs 'user' do
      f.input :title
      #f.input :avatar, as: :file
      f.input :avatar, required: false
    end

    f.actions
  end
  permit_params :title, :avatar
end
```

ArgumentError in Admin::users#new になったけど、
現状required: falseにすれば回避できた
http://stackoverflow.com/questions/22002259/activeadmin-and-paperclip-argumenterror


## まとめ：S3(Tokyo region)を使ってpathにhashを使って、保存パスと画像サイズをDB保存

Photoモデルにresourceをattach
app/models/photo.rb

```
class Photo < ActiveRecord::Base
  before_save :append_resource_info

  has_attached_file :resource,
    storage: :s3,
    s3_credentials: "#{Rails.root}/config/s3.yml",
    path: "/photos/:resource_user_id/:file_updated_hash.:extension",
    url: ":s3_path_url",
    s3_host_name: "s3-ap-northeast-1.amazonaws.com"


  validates_attachment_content_type :resource,
    content_type: /\Aimage\/.*\Z/

  private

    Paperclip.interpolates :file_updated_hash  do |attachment, style|
      Digest::SHA256.hexdigest(format("%s/%d", attachment.instance.resource_file_name, attachment.instance.resource_updated_at.to_i))
    end

    def image?
      resource_content_type =~ %r{^(image|(x-)?application)/(bmp|gif|jpeg|jpg|pjpeg|png|x-png)$}
    end

    def append_resource_info
      return unless image?
      tempfile = resource.queued_for_write[:original]
      unless tempfile.nil?
        geometry = Paperclip::Geometry.from_file(tempfile)
        self.resource_file_width = geometry.width.to_i
        self.resource_file_height = geometry.height.to_i
        self.resource_path = self.resource.path
      end
    end

end


```