メモ.
以下のファイルを編集.
sudo vi /etc/default/keyboard
以下の箇所を変更.
... XKBOPTIONS="ctrl:nocaps" ...
再起動する.
メモ.
以下のファイルを編集.
sudo vi /etc/default/keyboard
以下の箇所を変更.
... XKBOPTIONS="ctrl:nocaps" ...
再起動する.
えらく多機能なので試しながらメモる.
とりあえず使ってみる.
認証をするからにはサンプルが欲しいので用意しよう.
メモを管理するサービスということにしよう.
コントローラは
の2つを用意する.
機能は認証とメモ管理のみ.
$ rails new notes
gem 'devise'
をGemfileへ書いて
$ bundle install
とりあえずなのでscaffoldで.
$ rails g controller StaticPages $ rails g scaffold note subject:string body:string $ rails db:migrate
StaticPagesへはconfig/routes.rbを編集してルートも追加.
Rails.application.routes.draw do root to: 'static_pages#index' #追加 resources :notes end
雑にトップページも作っておく.
<!-- app/views/static_pages/index.html.erb --> <h1>Top</h1>
ここまででメモ機能は動作する.
ここからDeviseを使っていく.
Deviseを使うにはまず以下のコマンドでセットアップする.
$ rails g devise:install
そうすると丁寧に以下のメッセージを出してくれる.
=============================================================================== Some setup you must do manually if you haven't yet: 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', port: 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. You can copy Devise views (for customization) to your app by running: rails g devise:views ===============================================================================
1はメーラの設定, Deviseは認証やパスワードリマインダーにメールを使えるのでそのため. 2はルートページURLの設定, これは認証時の遷移先で必要になるだろう. 3はエラーメッセージなどを表示するタグをviewに入れてくれということ. 4はカスタムのviewが欲しい時に使えという事.
Deviseはデフォルトでviewを用意するのでカスタムする時には4のコマンドでまずはviewのファイルを生成してやらないとならない.
(viewをカスタムしない場面もあまりなさそうなものだけど)
2のルートページの設定は上述で行ったので, 1, 3を行います.
カスタムviewは後回し.
# config/environments/development.rb # 追加 config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
app/views/layout/application.html.erb
<!-- ここから --> <div class="notice"> <%= notice %> </div> <div class="alert"> <%= alert %> </div> <!-- ここまで --> <%= yield %>
認証するためのアカウント情報を保持するためのモデルを作成します.
作成にはDeviseが用意しているコマンドを使います.
$ rails g devise User
作成されたファイルを少し覗いてみます.
# app/models/user.rb class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
deviseの利用するモジュールがわらわら書かれています(後述)
マイグレーションファイルも見てみます.
# frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[5.1] def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable t.integer :sign_in_count, default: 0, null: false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip ## Confirmable # t.string :confirmation_token # t.datetime :confirmed_at # t.datetime :confirmation_sent_at # t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true # add_index :users, :confirmation_token, unique: true # add_index :users, :unlock_token, unique: true end end
すごいわらわら出来てます...
コメントアウトされているものをアンコメントするとConfirmableなどのモジュールも利用できそうな気配.
とりあえずデフォルトの認証機能さえ使えれば良いのでマイグレーション.
$ rails db:migrate
サインインとサインアウトの画面へのリンクをトップページに作りたいので, routesを確認してみます.
imai@IMAI-PC:~/lab/projects/notes$ rails routes Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit user_registration PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy POST /users(.:format) devise/registrations#create [中略]
とりあえずusersの部分だけ見てもたくさん生成されています.
とはいえリンクに使えそうなものは以下でしょう.
これらを使ってリンクを用意します.
ユーザ登録(sign up)とログイン(sign in)は必要でしょう.
トップページ(app/views/static_pages/index.html.erb)にリンクを加えます.
<h1>Top</h1> <%= link_to 'sign in', new_user_session_path %> <%= link_to 'sign up', new_user_registration_path %>
ログインしたらログアウトのリンクも欲しいのでレイアウト(app/views/layouts/application.html.erb)へ加えます.
<body> <!-- ここから --> <div class="session-control-box"> <% if signed_in? %> <%= link_to 'sign out', destroy_user_session_path, method: :delete %> <% end %> </div> <!-- ここまで -->
signed_in?はdeviseが用意してくれるヘルパーメソッドです.
その名の通りログイン中だったらtrueを返します.
ログイン後にはメモの一覧へ移動してほしいのでヘルパーメソッドの利用を追加します.
app/controllers/application_controller.rbに
の利用を追加します.
名前の通りこのメソッドの戻り値のパスへリダイレクトされます.
class ApplicationController < ActionController::Base protect_from_forgery with: :exception def after_sign_in_path_for(resource) notes_path end def after_sign_out_path_for(resource) root_path end end
これでログイン後とログアウト後の挙動を変更できます.
リダイレクトされてもログイン前にメモ管理のエリアにアクセスできてしまっては本末転倒なので, 認証機能を追加します.
class NotesController < ApplicationController before_action :authenticate_user! #追加 before_action :set_note, only: [:show, :edit, :update, :destroy]
authenticate_[モデル名]メソッドがdeviseで用意されているのでこれを使うだけです.
未認証の場合はログイン画面へリダイレクトされます.
このままではログインさえすれば誰のメモでも見れてしまうので, メモに所有者情報を持たせてログインしているユーザのメモだけが見れるように変更します.
まずはmigrationでnoteへuserへの外部キーを追加します.
$ rails g migration AddUserToNotes user:references $ rails db:migrate
モデルへアソシエーションもつけておきます.
ユーザ側.
# app/models/user.rb class User < ApplicationRecord has_many :notes
メモ側.
# app/models/note.rb class Note < ApplicationRecord belongs_to :user end
次にscaffoldで作ったコントローラをちょっと変更.
def index #[変更] #@notes = Note.all @notes = current_user.notes end [中略] def new #[変更] #@note = Note.new @note = current_user.notes.build end [中略] def create #[変更] #@note = Note.new(note_params) @note = current_user.notes.build(note_params) [中略] private # Use callbacks to share common setup or constraints between actions. def set_note #[変更] #@note = Note.find(params[:id]) @note = current_user.notes.find_by(id: params[:id]) redirect_to notes_url if @note.nil? end
current_[モデル名]ヘルパーメソッドがdeviseによって用意されるのでログイン中のユーザを取得するのは簡単です.
ここまでやると普通にアプリケーションっぽくなります.
deviseそのままでは英語メッセージが表示されるのでI18N対応してやる.
なんとそのままの名前のgemがあった.
余談だがこの方, kaminariとか色々なgemのi18n対応をやってるみたいで地味にすごい.
Gemfileに追記して更新する.
# 追記 gem 'devise-i18n'
$ bundle install
デフォルトロケールを設定していない場合はこちらも設定.
# config/application.rb # 以下を記述 config.i18n.default_locale = :ja
これだけでメッセージが日本語化される.ありがたい.
公式にも一応色々記載があるので込み入ったことをしたい場合は読むとよいかも.
専用のwikiもある.
ちょっと試すぐらいならデフォルトでも良いけど, 流石にこのままのviewでは実用に耐えない.
モデルもちょっといじって, 登録時にユーザ名も登録するようにしてみる.
migrationでユーザ名を追加する.
$ rails g migration AddNameToUsers name:string $ rails db:migrate
deviseでviewをカスタマイズするにはまず以下のコマンドでビューを生成する.
$ rails g devise:views users(モデル名)
するとdeviseで使うビューが用意される.
これをカスタマイズしていく.
controllerもviewと同様で, 下記のコマンドで生成する.
$ rails g devise:controllers users
親切にroutes.rbを変更しろと教えてくれる.
create app/controllers/users/confirmations_controller.rb create app/controllers/users/passwords_controller.rb create app/controllers/users/registrations_controller.rb create app/controllers/users/sessions_controller.rb create app/controllers/users/unlocks_controller.rb create app/controllers/users/omniauth_callbacks_controller.rb =============================================================================== Some setup you must do manually if you haven't yet: Ensure you have overridden routes for generated controllers in your routes.rb. For example: Rails.application.routes.draw do devise_for :users, controllers: { sessions: 'users/sessions' } end ===============================================================================
生成されたコントローラがapp/controllers/users/に用意されるが, これらは全てコメントアウトされている.
なのでこれをベースに使うものをアンコメントしていかないとならない.
各コントローラと機能の対応は多分以下の感じじゃないかな.
コントローラ名 | 機能 |
---|---|
confirmations_controller.rb | 登録後のメールでの確認 |
passwords_controller.rb | パスワードリマインダー |
registrations_controller.rb | ユーザ登録 |
sessions_controller.rb | セッション(ログイン/ログアウト) |
omni_auth_callbacks_controller.rb | omni_authによるOAuth制御のコールバック |
unlocks_controller.rb | ログインに一定回数失敗したらアカウントロックするアレ |
とりあえず
だけは使いたいので以下の2つのファイルの中身をアンコメントする.
コンソールで出た指示通りに.
#devise_for :users devise_for :users, controllers: { registrations: 'users/registrations', sessions: 'users/sessions' }
ここまででコントローラとビューをカスタムする前と同じ挙動になる.
app/views/users/registrations/new.html.erbを変更して名前欄を追加する.
<!-- ここから --> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name, autofocus: true %> </div> <!-- ここまで --> <div class="field">
ついでにログイン中のユーザ名を見えるようにしておく.
<!-- app/views/layout/application.html.erbへ追加 --> <p><%= current_user.name %></p>
strong parametersを追加するだけですが.
def configure_sign_up_params #devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute]) devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) end
公式にstrong parametersに関する記載があったので, 困ったら読めば良いかな.
普通にjaのymlを用意してActiveRecordの部分に書いた.
# config/locales/ja.yml ja: activerecord: attributes: user: name: ユーザ名
これだけでうまく動作します.素敵ですね.
ControllerのテストもIntegrationテストもどちらでもIntegrationHelpersをincludeすると
の両メソッドが利用できるので, これで困ることはほぼないと思う.
# test/controllers/note_controller_test.rb class NotesControllerTest < ActionDispatch::IntegrationTest include Devise::Test::IntegrationHelpers setup do sign_in users(:yamada) end
ちょっと調べる必要があったのがFixturesの書き方で, 以下の様に書いた.
# test/fixtures/users.yml yamada: email: yamada@example.com encrypted_password: <%= Devise::Encryptor.digest(User, 'password') %>
deviseには以下の10個のモジュールがあり, それぞれ用途に応じて使い分ける事ができる.
モジュール名 | 機能 | デフォルト |
---|---|---|
database_authenticatable | DBに保存するパスワードの暗号化 | 有効 |
registerable | サインアップ処理 | 有効 |
recoverable | パスワードリセット機能 | 有効 |
rememberable | クッキーにログイン情報を保持する | 有効 |
trackable | サインイン回数・時刻・IPアドレスを保存 | 有効 |
validatable | メールアドレスとパスワードでのバリデーション | 有効 |
confirmable | メール送信による登録確認 | |
lockable | 一定回数ログインに失敗した際のアカウントロック | |
timeoutable | 一定時間でセッションを削除する | |
omniauthable | OmniAuthのサポート |
ちょっとすぐに全部は試せそうにない.心折れた.
とはいえこれだけでかなり使えそうです.
letter_opener_webはRailsで開発中のメールをブラウザで見れる便利なgem.
WSL環境やvagrant上での開発などではブラウザでメールが見られるのは非常に便利.
Gemfileを編集.
開発時のみ入れるように.
gem 'letter_opener_web', :group => :development
インストール
$ bundle install
config/environments/development.rbを編集.
# 以下2行を追加 config.action_mailer.default_url_options = { host: 'localhost:3000' } config.action_mailer.delivery_method = :letter_opener_web
config/routes.rbを編集.
# 以下3行を追加 if Rails.env.development? mount LetterOpenerWeb::Engine, at: "/letter_opener" end
これで
localhost:3000/letter_opener
へブラウザでアクセスするとブラウザ上でメールが確認できます.
mailerとcontrollerを適当に作って試してみる.
$ rails g mailer HelloMailer
挨拶メールを送るだけ.
class HelloMailer < ApplicationMailer default from: "foo@example.com" def greeting to = "bar@example.com" subject = "greeting!" mail(subject: subject, to: to) do |format| format.text end end end
$ rails g controller send_mails
indexへアクセスしたらメールを送るようにする.
class SendMailsController < ApplicationController def index HelloMailer.greeting.deliver end end
config/routesも追加.
get '/send_mails', to: 'send_mails#index'
ないと怒るので.
<!-- app/views/send_mails/index.html.erb --> sent
ここまでで
localhost:3000/send_mails
へアクセスした後に
localhost:3000/letter_opener
へアクセスするとメールが見れた.
これは便利.
以前はgemなしで実装したので今度はCarrierWaveを使ってみる.
あとはせっかくなので画像のリサイズなんかも試す.
CarrierWaveで画像のリサイズなんかをやるためにはRMagickやMiniMagickが必要になります.
で, これらはImageMagickのラッパーなのでやっぱりImageMagickが必要になります.
Ubuntu(WSL)の場合.
$ sudo apt install imagemagick $ sudo apt install libmagick++-dev
$ rails new carrier_wave_sample
今回はCarrierWave推奨のmini_magickを使います.
gem 'carrierwave' gem 'mini_magick'
bundleでインストール.
$ bundle install
前回同様, scaffoldで.
とりあえず掲示板っぽいものを作成.
メッセージ毎に画像を1つ登録できるようにする.
$ rails g scaffold message message:string image_path:string $ rails db:migrate
uploaderはcarrierwaveで画像をアップロードする際の設定を行うイメージです.
CarrierWaveではRailsのgenerateコマンドに追加が行われて,
$ rails g uploader [アップローダー名]
が利用できるようになりますのでこれで作成します.
以下のようにしました.
$ rails g uploader MessageImage
アップローダーはapp/uploadersフォルダが新しく作られてそこへまとめられます.
今回生成されたものを見てみると以下のようになっています.
class MessageImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick # include CarrierWave::MiniMagick # Choose what kind of storage to use for this uploader: storage :file # storage :fog # 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 [中略]
store_dirメソッドはファイルの保存先かな?とかある程度想像がつくのでわかりやすそうではあります.
モデルに画像のパスを保存するので, 今回作成したUploaderとモデルを関連付ける必要があります.
class Message < ApplicationRecord mount_uploader :image_path, MessageImageUploader end
scaffoldで作ったビュー(_form.html.erb)のファイルパスの部分をfile_fieldに変更.
<div class="field"> <%= form.label :image_path %> <%= form.file_field :image_path, id: :message_image_path %> </div>
表示側(show.html.erb)も変更.
<p> <strong>Image path:</strong> <%= image_tag(@message.image_path_url) %> </p>
ここまででアップロードと表示ができるようになります.
簡単.
アップロードした画像はpublic/の下にstore_dirメソッド定義通りのパスで作成されていました.
今回は画像の変換にMiniMagickを使うのでコメントを外しておきます.
app/uploader/message_image_uploader.rbを変更.
class MessageImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick process resize_to_fit: [200, 200]
resize_to_fitは縦横比を維持したままリサイズするメソッドです.
上記では200×200のサイズになるようにアップロード時に変換するようにしています.
Module: CarrierWave::MiniMagick
リファレンスを見ると他にもいくつかメソッドがありますがよくわからんなぁと思っていたところ詳しく記載してくださっている方がいらっしゃったのでリンク張っときます.
麺処 まつば - CarrierWave + RMagick 画像のリサイズをまとめてみました
アップロードした画像とは別にサムネイルを作る事もできます.
元々コメントアウトで記述されているのでアンコメントするだけで使えます.
# 以下 # Create different versions of your uploaded files: version :thumb do process resize_to_fit: [50, 50] end
表示するには以下の様にビューを変更します.
app/views/messages/show.html.erbを変更.
<!-- 以下を追加 --> <p> <%= image_tag(@message.image_path_url(:thumb)) %> </p>
[モデル名][画像パス保存の属性名]_urlのメソッドには引数にversionのシンボルが渡せます.
今回は:thumbを追加したので引数にこれを渡す事で作成したサムネイルの画像パスを取得できます.
こちらもコメントアウトをアンコメントするだけで使えます.試しにpngを禁止してみました.
バリデーションで引っかかるので便利です.
app/uploaders/message_image_uploader.rbを編集.
def extension_whitelist %w(jpg jpeg gif) end
画像ファイルの上限サイズを指定できます.
app/uploaders/message_image_uploader.rbを編集.
def size_range 1..5.megabytes end
エラーのバリデーションメッセージを日本語化したいところです.
公式に記載がありました.
これだけできれば色々できそうです.
select2というjquery-pluginがあってこれを使うと良い感じになりそう.
(jQuery捨てるとか私の様な三流には無理そうです.ほとんどこれで十分だし...)
これをRailsから利用するgemがあるのでこれを利用します.
都道府県を選ぶ画面を用意するために適当なプロジェクトとコントローラ・ビューを用意します.
$ rails new select2_sample $ rails g controller Prefectures
config/routesも編集. 以下を追加.
resources :prefectures
Gemfileに以下を追記.
gem 'jquery-rails' gem 'select2-rails'
いつも通り更新.
$ bundle install
雑に都道府県をべた張り.
<!-- app/views/prefectures/index.html.erb --> <!-- railsのヘルパーで生成 --> <%= select_tag 'searchDropdownBox', options_for_select(["都道府県", "北海道", "青森", "岩手"]), class: "searchable" %> <!-- 普通のHTMLタグ --> <select> <option>北海道</option> <option>青森</option> <option>岩手</option> <option>宮城</option> <option>秋田</option> <option>山形</option> <option>福島</option> <option>茨城</option> <option>栃木</option> <option>群馬</option> <option>埼玉</option> <option>千葉</option> <option>東京</option> <option>神奈川</option> <option>新潟</option> <option>富山</option> [中略] </select>
この時点で動作確認すると普通にセレクトボックスが表示されます.
bundleでの導入以外に以下が必要です.
具体的には以下になりました.
application.js
[中略] //= require rails-ujs //= require turbolinks //= require jquery //= require select2 //= require_tree .
application.css
[中略] *= require select2 *= require_tree . *= require_self
bundleで導入済みなのでここからはRailsで使えるようにしていきます.
導入には以下が必要です.
こちらの具体例は以下.
prefectures.coffee
$ -> $('.searchable').select2({ width: 200, })
bootstrapと共に利用するには, まずbootstrapを導入してapplication.scssへ以下のように変更.
[中略] * *= require select2 */ @import "bootstrap-sprockets"; @import "bootstrap";
application.jsに以下を追記.
//= require select2 //= require select2_locale_ja # これ //= require_tree .
jsで設定する際にパラメータへlanguageを追加.
$( ".searchable" ).select2({ language: "ja" });
便利そう.
使ってみた.
$ rails new migration_comment --database=mysql $ rails g model user name:string email:string
config/database.ymlは適宜設定.
ハッシュでcommentを渡せばよい様です.
class CreateUsers < ActiveRecord::Migration[5.1] def change create_table :users do |t| t.string :name, comment: "ユーザ名" t.string :email, comment: "メールアドレス" t.timestamps end end end
mysqlコマンドで見てみた. ちゃんとコメントが発行されている.
mysql> show create table users\G *************************** 1. row *************************** Table: users Create Table: CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL COMMENT 'ユーザ名', `email` varchar(255) DEFAULT NULL COMMENT 'メールアドレス', `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
sqlite3で確認できなかった.
(そもそもsqlite3ってコメントあったっけ?)
MySQLとpostgreSQL限定なのだろうか.
入門シリーズが続いている.
CarrierWave使うサンプル多いので, 使わないで実装してみる.
Railsは5.1.6
掲示板的に
のCRUDができるものにする.
CRUD実装自体は目的ではないのでScaffoldで.
$ rails g scaffold message message:string $ rails db:migrate
とりあえずメッセージ登録ができるようになる.
一般的にはパス保存といわれている気がするし, 迷ったけど今回はDBへ保存する実装にした.
理由は
特にアクセス制御についてはパスを保存の場合は, どうやってうまく制御するのかがわからなかった.
publicに入れてパスを保存というのが一般的なのかな?という気もするけど, エンタープライズだと画像もアクセス制御に気を付けないとならない.
UUIDみたいな当てようもないものをファイル名にしてパスを保存したりするのだろうか?
この辺よくわからない.知りたいなぁ.
まぁ, Oracleもポジショントークではあるもののこんな事言ってますし, DB保存もありではないかと.
imageモデルを作成し, 上述で作成したmessageモデルと関連させることにする.
$ rails g model image message:references filename:string data:binary $ rails db:migrate
アソシエーションも追加.
# app/models/message.rb class Message < ApplicationRecord has_one :images end
class Image < ApplicationRecord belongs_to :message end
下ごしらえが済んだので実装する.
_form.html.erbにfile_fieldを加えてやる.
1対1でアソシエーション張ったので一応fields_for使った.
<div class="field"> <%= form.label :message %> <%= form.text_field :message, id: :message_message %> </div> <!-- アップロード用に追加 --> <%= form.fields_for(message.image) do |f| %> <div class="field"> <%= f.label :image %> <%= f.file_field :data, id: :image_data %> </div> <% end %>
登録フォームを呼び出すときにイメージモデルのインスタンスを用意しておく.
# GET /messages/new def new @message = Message.new @message.build_image # 追加 end
追加時にファイルを保存するように変更.
まずbuild_imageでImageモデルのインスタンスを用意しておく.
ストロングパラメータも新しく用意した.
で, データを取得してDBへ入れたいのだけど, image_params[:data]で取得できるのはActionDispatch::Http::UploadedFileのインスタンスらしい.
アップロード画像は付加情報も含めインスタンスで表現される.(実際のファイルは仮ファイルで別の場所へ保存)
ファイルのデータはこのインスタンスからreadメソッドで読み込める.
ファイル名はoriginal_filenameメソッドで取得できる.
リファレンス読むとcontent_typeも取得できるので, このサンプルのImageモデルに拡張子の項目を追加しなかったことを若干後悔.
# POST /messages # POST /messages.json def create @message = Message.new(message_params) # ここから @image = @message.build_image @image.data = image_params[:image][:data].read @image.filename = image_params[:image][:data].original_filename # ここまで追加 respond_to do |format| if @message.save format.html { redirect_to @message, notice: 'Message was successfully created.' } format.json { render :show, status: :created, location: @message } else format.html { render :new } format.json { render json: @message.errors, status: :unprocessable_entity } end end end private # Never trust parameters from the scary internet, only allow the white list through. def message_params params.require(:message).permit(:message) end # 追加したパラメータ取得メソッド def image_params params.require(:message).permit(image: [:data]) end
ここまででアップロードは動作した.
若干ごちゃつく.
せっかくなら表示もさせたいので.
config/routes.rbへアップロードした画像を得るimageというルートを追加する.
resources :messages do member do get 'image' end end
app/views/messages/show.html.erbへ表示用のタグを追加.
<p> <strong>Message:</strong> <%= @message.message %> </p> <%= image_tag(image_message_path(@message), alt: @message.image.filename) %>
app/controllers/messages_controller.rbへ画像取得のアクションを追加する.
# scaffoldで生成したサンプルなのでbefore_actionにimageも追加してやる before_action :set_message, only: [:show, :edit, :update, :destroy, :image] [中略] # GET /messages/1/image def image send_data @message.image.data, type: 'image/jpeg' end
ついでに試した.
app/views/messages/show.html.erb
<%= image_tag(image_message_path(@message), alt: @message.image.filename) %> <!-- 追加 --> <%= link_to 'download', image_message_path(@message) %>
app/controllers/messages_controller.rbのimageアクションを変更.
ファイル名をURLエンコードしてsend_dataへ渡してやるだけ.
# GET /messages/1/image def image filename = ERB::Util.url_encode(@message.image.filename) send_data @message.image.data, type: 'image/jpeg', filename: filename end