- 投稿日:2019-02-18T22:18:11+09:00
【Rails】Active Storage環境下でGoogle Cloud Visionのセーフサーチを実装
個人開発のWebアプリ「まちかどルート」v5.42への実装メモです。
まえがき
Rails 5.2のファイルアップローダーであるActive Storage環境下だとGoogle Cloud Visionのセーフサーチがうまく動かなかったので下記のように工夫しました。
具体的には画像ファイルを /public にいったん配置して、そのパスを指定して画像分析にかけ、終わったら /public から削除するという流れです。
posts_controller.rb# 画像の選択の有無を判断 if post_params[:image] != nil # 画像を uploaded_file に格納 uploaded_file = post_params[:image] # /public へのパスを指定 output_path = Rails.root.join('public', uploaded_file.original_filename) # ファイルを開いて image_file に格納 image_file = File.open(output_path) # Google Cloud VisionのAPIを使う image_annotator = Google::Cloud::Vision::ImageAnnotator.new response = image_annotator.safe_search_detection image: image_file response.responses.each do |res| safe_search = res.safe_search_annotation # if文では公式ドキュメントと違って .to_s を付ける必要がありました if safe_search.adult.to_s == "VERY_LIKELY" || safe_search.adult.to_s == "LIKELY" flash[:error] = "不適切な画像と判断されました powered by Google Cloud Vision" redirect_to root_path return elsif safe_search.violence.to_s == 'VERY_LIKELY' || safe_search.violence.to_s == 'LIKELY' flash[:error] = "不適切な画像と判断されました powered by Google Cloud Vision" redirect_to root_path return elsif safe_search.medical.to_s == 'VERY_LIKELY' || safe_search.medical.to_s == 'LIKELY' flash[:error] = "不適切な画像と判断されました powered by Google Cloud Vision" redirect_to root_path return end end # 最後に /public の画像ファイルを削除します File.delete(output_path) end上記のコードを適所に書けばGoogle Cloud Visionのセーフサーチが機能します。そこで
VERY_LIKELY
またはLIKELY
と判断されればroot_path
にリダイレクトされるようになります。あとがき
Active Storage環境下だと公式ドキュメントどおりにやってもうまくいきませんでした。また、事前に
gem 'google-cloud-vision', '~> 0.32.2'
をbundle install
したあとに「protobufが見つかりません」という内容のエラーが出ました。そこでGemfile
にgem 'google-protobuf', '~> 3.7.0.rc.2'
を追記のうえgem update google-protobuf
を実行することで回避できました。
- 投稿日:2019-02-18T21:17:08+09:00
Rails vs PostgreSQL カラムをstringからintegerに変更できない
バージョン情報
- Ruby: 2.5.0
- Rails: 5.1.6
- Postgres: 11.1
- macOS: 10.14.1
事象
usersテーブルのgenderカラムをstringからintegerに変えるため、以下のコマンドでマイグレーションファイルを作成し、
bin/rails g migration change_gender_datatype_to_integer_in_users作成されたマイグレーションファイルに以下のように記載した後、
2019mmddhhmmss_change_gender_datatype_to_integer_in_users.rbclass ChangeGendersDatatypeInUsers < ActiveRecord::Migration[5.1] def change change_column :users, :gender, :integer end endマイグレーションを実行すると、
bin/rails db:migrate以下のエラーが表示されました。
rails aborted! StandardError: An error has occurred, this and all later migrations canceled: PG::DatatypeMismatch: ERROR: default for column "gender" cannot be cast automatically to type integerググってみると、同様のエラーに遭遇している先人たちがおりましたので参考にしましたが、いずれの方法でも回避することはできず。。。
https://qiita.com/gam0022/items/a73910de5b8eb44e5b13
https://stackoverflow.com/questions/12603498/rails-migration-error-w-postgres-when-pushing-to-heroku
https://dzone.com/articles/rails-5-change-database-column解決方法
一度該当カラムをremoveして、再度追加することで対応しました。
一度ドロップして、
remove_column :users, :genderドロップできたら新しいマイグレーションファイルを作成して、add_columnしてあげます。
add_column :users, :gender, :integer, after: :phone, default: 0すると追加できました!が、なぜかafterが機能しておらず、末尾に追加されてしまいました。
schema.rbt.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "gender", default: 0解決したら更新します。
- 投稿日:2019-02-18T19:29:17+09:00
herokuのpush先をサクッと変更してアプリを複製
DBのエラー続きでどうしようもなくなったとき、
『新しいアプリとして公開できたらな〜』
と思っていた時、成功して感動したのでここに記す。Goal
公開しているアプリを別URLで複製して公開し直す。
*すでにherokuにアプリを公開しているコト前提
How to
まずは、push先がどこになっているのか確認
$ git remote -v新しい名前でリモートリポジトリを作成
$ heroku create ********さっき作ったリモートリポジトリにpush先を変更
$ $ git remote set-url heroku ********さっき見たく、push先を確認。
しっかり変更されていればOK$ git remote -vここからはおなじみ
$ git add . $ git commit -m"first commit" $ git push heroku master $ heroku open
- 投稿日:2019-02-18T18:29:29+09:00
AWS Cloud9でRailsプロジェクト作成直後にぶつかる問題の対応方法
はじめに
Ruby on Rails初心者がUdemyで入門しようとしています。環境構築で詰まるという初心者が陥りがちな罠を避けるため、AWS Cloud9を利用することにしました。すると・・・早速罠にハマったので備忘録として対応方法をまとめます。
The Complete Ruby on Rails Developer Course
https://www.udemy.com/the-complete-ruby-on-rails-developer-course/環境
- AWS Cloud9
- ruby 2.6.0p0
- Rails 5.0.7.1
Railsプロジェクト作成
Cloud9を開きます。2019/02/18現在、Cloud9は東京リージョンでは提供されていないので適当なリージョンを選択します。「Create environment」から環境を作成します。Configuration Settingsはデフォルト値で構いません。
IDEが開いたら下部のターミナルで以下のコマンドを実行します。
~/environment $ rails new sample ~/environment $ cd sample/ ~/environment/sample $ rails s -b $IP -p $PORTPreviewを実行します。
罠1 Gem::LoadError
Preview画面に何やらエラーが出ています。
Specified 'sqlite3' for database adapter, but the gem is not loaded. Add
gem 'sqlite3'
to your Gemfile (and ensure its version is at the minimum required by ActiveRecord).sqlite3がロードされていないようですが、インストールはされています。
~/environment/sample $ gem list | grep sqlite3 sqlite3 (1.4.0)対応方法
検索してもドンピシャなものが見当たりませんでしたが、どうやらsqlite3のバージョンを変えてみるとよいようです。以下のようにGemfileを修正します。
Gemfilediff --git a/Gemfile b/Gemfile index 0447f70..03752d1 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.0' # Use sqlite3 as the database for Active Record -gem 'sqlite3' +gem 'sqlite3', '~> 1.3.6' # Use Puma as the app server gem 'puma', '~> 3.0' # Use SCSS for stylesheets変更を反映して再度試してみます。
~/environment/sample $ bundle update ~/environment/sample $ rails s -b $IP -p $PORT => Booting Puma => Rails 5.0.7.1 application starting in development on http://127.0.0.1:8080 => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.12.0 (ruby 2.6.0-p0), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://127.0.0.1:8080 Use Ctrl-C to stop Started GET "/" for 126.209.207.2 at 2019-02-18 09:16:09 +0000 Cannot render console from 126.209.207.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Rails::WelcomeController#index as HTML Parameters: {"internal"=>true} Rendering /home/ec2-user/.rvm/gems/ruby-2.6.0/gems/railties-5.0.7.1/lib/rails/templates/rails/welcome/index.html.erb Rendered /home/ec2-user/.rvm/gems/ruby-2.6.0/gems/railties-5.0.7.1/lib/rails/templates/rails/welcome/index.html.erb (3.1ms) Completed 200 OK in 17ms (Views: 7.1ms | ActiveRecord: 0.0ms)Gem::LoadErrorは消えました。
罠2 Cannot render console from 126.209.207.2!
Previewを開くと上記のようなログが出力されます(以下、抜粋)。
Cannot render console from 126.209.207.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
このときの画面は以下のようになっています。接続が拒否されています。
対応方法
下図の赤枠で示した部分をクリックすると別タブで正常に開くことができます。
めでたしめでたし。
まとめ
環境構築の罠を避けようとして別の罠にハマる辛さがありましたが、無事Rails入門の準備ができました。
- 投稿日:2019-02-18T18:23:03+09:00
【rspec】APIモードで404 Not Foundをjsonで返す時のテストについて
APIモードで404 Not Foundをjsonで返す時のテストについて
存在しないプロジェクトのページにアクセスしたら、404 not foundのページを返すテストを書いていた。
しかし、今回はAPI仕様なのでhtmlで404を返してはいけない。
そのため、「APIモードでは、存在しないprojectにアクセスしてきたら、status404を返すだけで良い」という結論。
そもそも論だった。
expect(response).to have_http_status :not_foundもしくは、
expect(response.status).to be 404というテストを書くだけで良い!(この2つは同義)
have_http_statusは、rspecのマッチャー。(eq等と同じ)
https://www.masalog.site/entry/2018/07/26/214211このhave_http_statusにHTTPステータスを引数(シンボル)で渡せば、引数のステータスが返せる。
HTTPステータスコード一覧
https://blog.toshimaru.net/rails-http-status-symbols/success
:okは200番
client error
:not_foundは404番
server error
:internal_server_errorは500番
そしてこの2つは同義。
expect(response).to have_http_status :ok:expect(response.status).to be 200
- 投稿日:2019-02-18T18:13:13+09:00
git clone から rails s までの5ステップ
(ほぼ自分用メモです)
新規ではなく、動くものがすでGitHubにあがっているRailsプロジェクトをcloneしてローカルで動かすための5ステップ。
ローカルにRails自体が動く環境ができている=Yay! You're on Rails!が表示できる状態であることが前提。
gitコマンドが使える状態なのも前提。$ git clone https://github.com/<user_name>/<project_name>.git $ cd <project_dir> $ bundle install $ bin/rails db:migrate $ bin/rails s
- 投稿日:2019-02-18T18:01:04+09:00
【Rails】自作WebアプリからMastodonのプロフィール画像を変更
個人開発のWebアプリ「まちかどルート」v5.41への実装メモです。
プログラミングに入門して8ヶ月。
今回も SNS「Mastodon」のAPI に挑戦してみました。まえがき
・まちかどルートではMastodonのアカウントでログイン認証するようにしています。
・今回の実装をするまでは、まちかどルートのプロフィール画像を変えるためにわざわざMastodonで変更し、まちかどルートで再ログインする必要がありました。
・画像アップローダーとしてRails 5.2の新機能 Active Storage を使い、ファイルをAmazon S3に保管しているのですが
gem 'mastodon-api'
(v2.0)を通してアップロードするとき、その環境のせいでとても苦労しました。model
user.rbhas_one_attached :imageActive Storageを使うのでマニュアルどおりの作法でmodelにこう書きます。バリデーションについては今回、割愛します。
view
edit.html.erb<%= form_with model: @user, multipart: true, local: true do |f| %> <%= f.file_field :image %> <%= button_tag :type => "submit" do %>変更を保存<% end %>プロフィール画像のファイルを選択するためのフォームです。
controller
users_controller.rbdef update # 画像の選択の有無を確認 if params[:image] != nil # 画像を uploaded_file に格納 uploaded_file = params[:image] # 画像を /public にいったん配置するため output_path にパスを設定 output_path = Rails.root.join('public', uploaded_file.original_filename) # Mastodonには2MB制限があるので画像を縮小 ## MiniMagickを使います。まずは画像を入力 img = MiniMagick::Image.read(uploaded_file) ## 縮小します img.resize "300x300" ## 縮小したら /public に書き出します img.write output_path # MastodonのAPIを通してアップロードします ## 配列を用意します。Mastodon指定のパラメーターは avatar です user_array = { "avatar": output_path } ## APIを叩くためのクライアントを生成します domain = '[対象のMastodonインスタンスのドメイン]' access_token = '[ユーザーのアクセストークン]' client = Mastodon::REST::Client.new(base_url: "https://#{domain}", bearer_token: access_token) ## MastodonのAPIを叩きます client.update_credentials(user_array) # 以上でMastodon側のプロフィール画像が変更されます # 続いて、まちかどルートにも同じ画像を反映させます ## MastodonのAPIを叩いて変更後のavatarを取得します @user.avatar = client.verify_credentials.avatar ## ユーザー情報を保存します @user.save # 最後に、不要となった/publicの画像を削除します File.delete(output_path) end flash[:notice] = "アイコンを変更しました" redirect_to @user endcontrollerが一番苦労しました。
解説はコメントアウトにある通りです。あとがき
Active Storageはとても簡単に導入できるアップローダーなのですが
user_array = { "avatar": @user.image }と書ければ、わざわざ/publicに画像を配置する手間がなくて楽なのに
url_for(@user.image)
とやっても「そんな画像はありません」というエラーが返ってきてしまうんです。というわけで、いろいろ試行錯誤して上記のようになりました。とくに/publicにいったん配置する方法がわかったので、これから他の画像系APIを使うのに役立ちそうです。今後も学んでいきたいと思います。
- 投稿日:2019-02-18T17:14:59+09:00
Shrineのimage_dataを含むモデルのFactoryの作り方
(編集しようとしたら誤って削除してしまったので再投稿しました)
railsの画像アップロードライブラリshrineを用いた場合の、FactoryBotのFactoryの書き方について悩んだポイントがあったので書いておきます
前提
登場するModel
- Area
column type memo name string require
- Shop
column type memo area bigint require logo bigint require name string require
- Logo
column type memo area bigint require name string require image_data text require Area has_many Shop
Area has_many Logo
Shop belongs_to Logo各Shopはエリアに登録されているロゴを使用できるイメージです。
悩んだこと
logoのファクトリーのimage_data カラムをどう書くべきなのか悩みました。
spec/factories/logos.rbFactoryBot.define do factory :logo do area sequence(:name) { |i| "logo#{i}" } image { #この部分!! } end endもともとは↓のように書いていたのですが、これだとlogoのテストデータが作成されるたびにファイルへアクセスするためRSpec全体の実行時間に大きな影響を与えていました。
image { File.open("#{Rails.root}/spec/fixtures/img/example.png") }RSpec実行時間
Factory追加前→ 27s
追加後 →1min 3sshopが作られるたびにFactoryBotによってlogoも自動で作られるのだから遅くなるのは当たり前ですね
試したこと
適当な文字列をつっこむ
image_data { 'sample' }結果
imageを実際に使うテストにてJSON::ParserError:765: unexpected token at 'sample'
それはそう。。
fixture_file_upload使ってみる
この記事ではfixture_file_upload使ったらFile.openより相当早い結果になっているので期待大!
image { fixture_file_upload("#{Rails.root}/spec/fixtures/img/example.png", 'img/png') }結果
テストはすべてpass!
ただしFinished in 1 minute 9.24 seconds (files took 15.67 seconds to load)
File.openのときと変わらないくらい時間かかってる。。
使い方がおかしい?最終的に
ファイルアクセスではなく架空のcacheを作成してみる
Shrineのuploaded_fileメソッドを用いてキャッシュの状態でファイルが上がっていることにする作戦
factory :logo do area sequence(:name) { |i| "logo#{i}" } image_data { Shrine.uploaded_file( 'id' => SecureRandom.hex(8), 'storage' => 'cache', 'metadata' => { 'mime_type' => 'image/jpeg', 'size' => 1.megabyte }).to_json } end結果
Finished in 31.04 seconds
いいじゃん!!
補足
最終型において
image
ではなくimage_data
を使っている理由について書きます。はじめはFile.openと同じように下記のように書いていたんですが、
image { Shrine.uploaded_file( 'id' => SecureRandom.hex(8), 'storage' => 'cache', 'metadata' => { 'mime_type' => 'image/jpeg', 'size' => 1.megabyte }).to_json}結果
imageを実際に使うテストにてエラーが起きるNo such file or directory @ rb_sysopen - /usr/src/app/public/uploads/cache/675ce0d849e8a5f8
ファイルをあげようとしてるんだけど、そんなファイルはないよと言われているように見えます。
shrineを用いた実装において、
image_data → 添付ファイルの全情報がjson形式で保存されるためのDBカラム image → 添付ファイルをハンドリングするためのvirtualなattributeということは
image_data → upload済みのファイル情報を入れる。 image → uploadしたいファイル情報を入れる。最終形では、
Shrine.uploaded_file
でファイルが上がっていることにしていたのでimage_dataに入れるのが正解ということでした
- 投稿日:2019-02-18T16:55:24+09:00
rails mysql2のインストール時のエラー
gemのmysql2をインストールする際にハマったのでメモ
エラーログ
Fetching mysql2 0.3.21 Installing mysql2 0.3.21 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: /path/to/vendor/bundle/ruby/2.4.0/gems/mysql2-0.3.21/ext/mysql2 /path/to/.rbenv/versions/2.4.1/bin/ruby -I /path/to/.rbenv/versions/2.4.1/lib/ruby/site_ruby/2.4.0 -r ./siteconf20190218-87873-15epx9f.rb extconf.rb --with-cppflags\=-I/usr/local/opt/openssl/include checking for ruby/thread.h... yes checking for rb_thread_call_without_gvl() in ruby/thread.h... yes checking for rb_thread_blocking_region()... no checking for rb_wait_for_single_fd()... yes checking for rb_hash_dup()... yes checking for rb_intern3()... yes ----- Using mysql_config at /usr/local/opt/mysql@5.7/bin/mysql_config ----- checking for mysql.h... yes checking for errmsg.h... yes checking for mysqld_error.h... yes ----- Don't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load ----- ----- Setting libpath to /usr/local/opt/mysql@5.7/lib ----- creating Makefile current directory: /path/to/vendor/bundle/ruby/2.4.0/gems/mysql2-0.3.21/ext/mysql2 make "DESTDIR=" clean current directory: /path/to/vendor/bundle/ruby/2.4.0/gems/mysql2-0.3.21/ext/mysql2 make "DESTDIR=" compiling client.c compiling infile.c compiling mysql2_ext.c compiling result.c linking shared-object mysql2/mysql2.bundle ld: library not found for -lssl clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [mysql2.bundle] Error 1 make failed, exit code 2 Gem files will remain installed in /path/to/vendor/bundle/ruby/2.4.0/gems/mysql2-0.3.21 for inspection. Results logged to /path/to/vendor/bundle/ruby/2.4.0/extensions/x86_64-darwin-18/2.4.0-static/mysql2-0.3.21/gem_make.out An error occurred while installing mysql2 (0.3.21), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.3.21' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: mysql2パスが通っていないのが原因
bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
- 投稿日:2019-02-18T15:30:36+09:00
Progate rails道場コースⅣ 攻略メモ中編
今回はいいね機能を実装していきます。
9/14 「いいね!」ボタンを作ろう
最初に流れを説明すると、
①likes_controller内でcreateアクションとdestroyアクションを実装する
⬇
②if文を使い、いいね済みとそうでない場合のボタンを実装する
です。likes_controllerのcreateアクションについて解説
まずは①のcreateアクションから。
likes_controller
def create @like = Like.new(user_id: @current_user.id, post_id: params[:post_id]) @like.save redirect_to("/posts/#{params[:post_id]}") endコード解説
いいね!された時に実行されるcreateアクションです。
動きとしては、いいねボタンが押された時のログインユーザーのIDと、投稿データのIDをDBのlikesテーブルに保存するものです。まずはLikeモデルに対してnewメソッドを用いて、ログイン中のユーザーID(@current_user.id)と、表示している投稿データのID(params[:post_id])の値が入ったインスタンス変数@likeを作成し、DBへ保存します。(2〜4行目)
その後投稿詳細ページへとリダイレクトして完成です。
likes_controllerのdestroyアクションについて解説
続いて、いいねを解除するためのdestroyアクションです。
def destroy @like = Like.find_by(user_id: @current_user.id, post_id: params[:post_id]) @like.destroy redirect_to("/posts/#{params[:post_id]}") endcreateアクションと似たコードで動きます。
2行目ではfind_byメソッドを用いて、Likeモデルから作成済みのデータを探してきます。
createアクションのnewメソッドで使ったものと同じ条件で検索してるだけです。
そして見つけたデータをdestroyメソッドで削除します。ビューにいいねボタンを表示する
if文を使って、
ログイン中のユーザーが表示中の投稿にいいね!済み = いいね!解除ボタンを表示
いいね!してない場合 = いいね!ボタンを表示という風に実装します。
posts/show.html.erb
<% if Like.find_by(user_id: @current_user.id, post_id: @post.id )%> <%= link_to("/likes/#{@post.id}/destroy", {method: "post"}) do %> <span class="fa fa-heart like-btn-unlike"></span> <% end %> <% else %> <%= link_to("/likes/#{@post.id}/create", {method: "post"}) do %> <span class="fa fa-heart like-btn"></span> <% end %> <% end %>ちょっと複雑そうに見えますが、上から順に解説していきます。
まず1行目。<% if Like.find_by(user_id: @current_user.id, post_id: @post.id )%>ログイン中のユーザー(@current_user.id)が、表示している投稿(@post.id)にいいね!済みかどうかを調べるコードです。
likesテーブルのデータはこの2つのIDから成るので、この検索条件にひっかかればいいね!済みということになります。<%= link_to("/likes/#{@post.id}/destroy", {method: "post"}) do %> <span class="fa fa-heart like-btn-unlike"></span> <% end %>続いて2〜4行目。link_toメソッドでいいねボタンとdestroyアクションを紐づけます。
もしif文の結果が真の場合(一致するいいね!データが見つかった場合)、いいね解除ボタンが表示されるようにします。ただ、これまでlink_toメソッドの第一引数には表示したい文字列を渡してきました。(”編集” ”削除”など)しかし、今回のようなfant-awesomeなどのHTMLをそのまま記述しても、文字列として認識されてしまうため上手く表示できません。
これを回避するために、少し記述法が変わります。<%= link_to("表示する文字列", "URL")%>通常はこのように記述するものを、
<%= link_to("URL") do %> <!-- ここにHTMLのコードを記述 --> <% end %>このように変えてあげます。
doが入ることでendも必要になるので、忘れないように!これに基づき、createアクションに紐付いたfant-awesomeのアイコンを表示しましょう。
<%= link_to("/likes/#{@post.id}/destroy", {method: "post"}) do %> <span class="fa fa-heart like-btn-unlike"></span> <% end %>これで表示されるはずです。メソッドのpost指定も忘れずに。
else以降も同じように記述しましょう。10/14 いいね数を表示しよう
まずはビューに渡すためのいいね!の数を数えるインスタンス変数、@likes_countを用意します。
def show @post = Post.find_by(id: params[:id]) @user = @post.user @likes_count = Like.where(post_id: @post).count end4行目のコード解説。
whereメソッドを使い、likesテーブルのpost_idが投稿のID(@post)と一致するデータを全て集めます。
わかりやすく言うと、全いいね!の中から特定の投稿のものだけ集めてくる感じですね。
そして集めてきたデータに対してcountメソッドを用いることで、要素の数をインスタンス変数に代入します。これで無事に、特定の投稿にいいね!された数を取得することができました。
「いいね!」した投稿を表示しよう
ユーザーがいいね!した投稿を一覧表示するページを作ります。
指定のURLをペーストした後、新しいページのためのルーティングを作成してあげます。routes.rb
get "users/:id/likes" => "users#likes"次はuser_controllerのlikesアクションです。
def likes @user = User.find_by(id: params[:id]) @likes = Like.where(user_id: params[:id]) end2行目でユーザーデータ表示のためのインスタンス変数@userを用意。
3行目で現在表示中のユーザーをURLのparamsから取得、whereメソッドを用いてlikesテーブルからuser_idが一致するデータ(表示中のユーザーがいいね!した投稿)を全て拾い、@likesに代入します。これで必要なデータは準備できたので、ビューに表示していきましょう。
ちょっと長くなります。likes.html.erb
<% @likes.each do |like| %> <% post = Post.find_by(id: like.post_id) %> <div class="posts-index-item"> <div class="post-left"> <img src="<%= "/user_images/#{post.user.image_name}" %>"> </div> <div class="post-right"> <div class="post-user-name"> <%= link_to( "#{post.user.name}", "/users/#{post.user.id}" ) %> </div> <%= link_to(post.content, "/posts/#{post.id}") %> </div> </div> <% end %>まずは1行目。ブロックを使って取得した全いいねのデータを渡す準備です。
<% @likes.each do |like| %>これでlikeを使うことにより、@likesに入っている、ユーザーがいいね!したデータを一つずつ用いることができます。
しかし今のままだと、@likesから渡されるデータはlikesテーブルから取得したものであるため、中身はuser_idとpost_idしか入っておらず、このままでは投稿内容を表示することができません。
そこで、@likes内のpost_id属性を利用して、紐づくpostsテーブルから投稿内容を取得します!<% post = Post.find_by(id: like.post_id) %>Postモデルにfind_byメソッドを用いて、(Post.find_by)
引数にlikesテーブル内のpost_idを、PostモデルのIDと紐付けて検索します(id: like.post_id)
これでいいね!データから投稿データを引っ張ってくることができました!後は変数postに入れてあげましょう!これでブロックの処理が始まる際に、変数postに投稿データが入るようになったので、後はこれまでと同じように表示してあげます。
<img src="<%= "/user_images/#{post.user.image_name}" %>"> <%= link_to( "#{post.user.name}", "/users/#{post.user.id}" ) %> <%= link_to(post.content, "/posts/#{post.id}") %>これでいいね関連の実装は終了です。
次回で本当のラスト。がんばりましょう!
- 投稿日:2019-02-18T14:36:08+09:00
[Rails]複合主キーを持つテーブルのレコードを更新する(*非常用)
環境
- Ruby 2.5.1
- Rails 5.2.1
- Mysql2 0.5.1
前提
そもそも、Rails において複合主キーの Model を扱うことはサポート外になります。複合主キーを使うと下記のような警告も出力されます。
WARNING: Active Record does not support composite primary key. Composite primary key is ignored.また、
find
,update
,save
,delete
メソッドの使用が不可能になります。これらのメソッドを呼び出すと下記のようなエラーを出力します。Model
- MultiPkeyModel という複合主キーの Model があったとします
- pkey_1, pkey_2 カラムが主キーとします
find
MultiPkeyModel.find(1) # 数値は適当 # ActiveRecord::UnknownPrimaryKey: # Unknown primary key for table multi_pkey_models in model MultiPkeyModel.そもそも id カラムを持たないので find の引数に何も指定しようがないですが、引数有無問わず上記のエラーが発生することを確認しました。
update, save, delete
record = MultiPkeyModel.find_by(pkey_1: somevalue1, pkey_2: somevalue2) # update record.update(foo: 'bar') # ActiveRecord::StatementInvalid: Mysql2::Error: # Unknown column 'multi_pkey_models.' in 'where clause': # UPDATE `multi_pkey_models` SET `foo` = 'bar' WHERE `multi_pkey_models`.`` IS NULL # save record.foo = 'bar' record.save # => update と同様のエラーのため省略 # delete record.delete # ActiveRecord::StatementInvalid: Mysql2::Error: # Unknown column 'multi_pkey_models.' in 'where clause': # DELETE FROM `multi_pkey_models` WHERE `multi_pkey_models`.`` IS NULLいづれの場合も、 SQL 文の WHERE 句が
WHERE `multi_pkey_models`.`` IS NULL
というおかしな状態になっているため失敗していると思われます。 Model クラスが id カラムを持たないため、本来 id カラムが指定されうる箇所が空になってしまっています。本題
では、どうするべきか。そもそも複合主キーによるテーブルを使った運用自体をよしとするかどうかという話もありますが、本項ではそこまで壮大な話は扱わず、 複合主キーテーブルを使わざるをえない状況 という前提で、苦肉の策を共有してみたいと思います。
find の代わり
これは
find_by
ですみます。レコードの更新を伴わない場合はすぐには困ることもなさそうです。MultiPkeyModel.find_by(pkey_1: somevalue1, pkey_2: somevalue2)update, delete の代わり
身も蓋もないですが、SQL文を直接発行したほうが手っ取り早いと思います。
下記は update を行う場合のコードの記述例です。sql = <<-SQL UPDATE `multi_pkey_models` SET `foo` = :foo WHERE `pkey_1` = :pkey_1 AND `pkey_2` = :pkey_2 SQL ActiveRecord::Base.connection.execute(ActiveRecord::Base.send( :sanitize_sql_array, [ sql, foo: 'bar', pkey_1: someValue1, pkey_2: someValue2 ] ))どうしても Active Record を使いたい場合は
update_all
という手もあります。MultiPkeyModel.where(pkey_1: someValue1, pkey_2: someValue2).update_all(foo: 'bar')ただ、update_all は本来は複数のレコードをまとめて更新するためのメソッドです。 pkey_1, pkey_2 の組み合わせを持つレコードは必ず1つであることがテーブルの制約上保証されてはいますが、そういった背景を知らない人がコードを読んだ際に、コード上は複数のレコードを更新しにいっているように見えるので、あまり読み手に優しくないと思います。
ただ、「なんでここSQL文直接書いてるんだろう?」と思う人もいるかもしれないのでSQL文生書きにしていることの理由などをコメントで補足しておくといいとおもいます。あれ、update_all 呼んでコメント書くのも一緒か とも思いましたが、そこはエンジニア各位のご判断にお任せしたいとおもいます。
まとめ
複合主キーを持つテーブルを扱う際には、各種 CRUD メソッドが想定どおり機能しないケースがあるのでご注意ください。
ちなみに
gem でなんとかしている例もけっこうあるようです。実際問題、筆者も含め複合主キーってけっこう使いますよね。。。
- composite-primary-keys/composite_primary_keys: Composite Primary Keys support for Active Record
- 「composite_primary_keys」の検索結果 - Qiita
識者からのご指摘、ご感想ありましたらぜひコメントいただけるとうれしいです。
- 投稿日:2019-02-18T11:56:40+09:00
form_with は remote (Ajax) が既定値なのに scaffold で生成されるコードは local: true オプションが付いていてどうにかしたい
やりたいこと
- Rails の scaffold をジェネレートすると
form_with
にlocal: true
のオプションが付いています。local
オプションのデフォルトはfalse
なわけで、 Ajax にしていこうという流れの中で scaffold のコードが Ajax じゃないのに違和感を覚えました。- というわけで、 scaffold で生成されたコードに対し、現実的なコード改修量で Ajax リクエストによる CRUD を実現してみます。
前提
- webpacker を使っている
- rails-ujs を使っている
- turbolinks を使っている
$ yarn add rails-ujs turbolinks
app/javascript/packs/application.jsimport Rails from 'rails-ujs' Rails.start() import Turbolinks from 'turbolinks' Turbolinks.start()
- 下記のようなユーザモデルを scaffold で作成した直後の状態を想定
name
およびage
という属性を持つ。前者はstring
で後者はinteger
name
およびage
ともに入力必須 (バリデーションエラー時の挙動確認で必要なので)$ ./bin/rails g scaffold User name age:integer
app/models/user.rbclass User < ApplicationRecord + validates :name, :age, presence: true end
- Rails のバージョンは 5.2.2
$ rails -v 5.2.2実施
ざっくり言うと以下の流れに。
- save 成功時、リダイレクトが Ajax の場合でも動くようにする
- save 失敗時、 form 要素だけを html で返し、その結果により既存 form を入れ替える
_form
パーシャルファイル、local: true
を外すこのポストの内容の前提なので。
app/views/users/_form.html.erb-<%= form_with(model: user, local: true) do |form| %> +<%= form_with(model: user) do |form| %> <% if user.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
_form
パーシャルファイルが<form>
タグだけを描画する、という前提を変えない通常、 scaffold ジェネレータを実施すると
_form
パーシャルファイルは<form>
タグに該当する html を描画するわけですが、それ以外の要素を_form
パーシャルファイル内で描画しないようにします。
たとえば<form>
タグの直下に<a>
タグでリンクを置きたいからといって_form
パーシャルファイルの中にそれを記述してはいけません。同じことを実現するには_form
パーシャルファイルを呼び出す側に記述すればことが済むはずです。例えば以下のようにするのではなく。。。
app/views/users/edit.html.erb<%= render 'form', user: @user %>app/views/users/_form.html.erb<%= form_with(model: user) do |form| %> ...省略 <% end %> <%= link_to 'Other Site', 'http://example.com' %>
_form
パーシャルでは<form>
だけ描画します。app/views/users/edit.html.erb<%= render 'form', user: @user %> <%= link_to 'Other Site', 'http://example.com' %>app/views/users/_form.html.erb<%= form_with(model: user) do |form| %> ...省略 <% end %>gem turbolinks を入れる
単に turbolinks を動かすだけなら JS の npm 管理下にある turbolinks を入れるだけで良いけど、 gem の turbolinks は別の理由で必要です。
gem turbolinks は Rails コントローラのredirect_to
の挙動をさしかえ、非Ajax の場合のリダイレクトと同じような動きをするようにします。
参考: https://github.com/turbolinks/turbolinks-classic/blob/master/lib/turbolinks/redirection.rbcreate/update アクションのバリデーションエラー時に html 全体でなく
_form
パーシャルの内容だけ返す
- render で
_form
パーシャルだけを描画して返すようにします。このときにlocals
を指定するのを忘れずに- 更に、ステータスコードは 200 でなく 422 Unprocessable Entity などのクライアントエラーを返すようにします
app/controllers/users_controller.rbdef create @user = User.new(user_params) if @user.save redirect_to @user, notice: 'User was successfully created.' else - render :new + if request.xhr? + render partial: 'form', status: :unprocessable_entity, locals: { user: @user } + else + render :new + end end endapp/controllers/users_controller.rbdef update if @user.update(user_params) redirect_to @user, notice: 'User was successfully updated.' else - render :edit + if request.xhr? + render partial: 'form', status: :unprocessable_entity, locals: { user: @user } + else + render :edit + end end endフォームで
ajax:error
イベントを拾ったときに自身の内容を書き換えるapp/javascript/packs/application.jsimport Rails from 'rails-ujs' Rails.start() import Turbolinks from 'turbolinks' Turbolinks.start() // ここから document.addEventListener('turbolinks:load', (event) => { const forms = document.querySelectorAll('form[data-remote="true"]') // remote フォームについて forms.forEach((form) => { form.addEventListener('ajax:error', (event) => { // 先のコントローラの処理で 200 を返しているとここは発火しないので注意 const detail = event.detail const xhr = detail[2] const contentType = xhr.getResponseHeader('content-type') if (contentType === 'text/html; charset=utf-8') { // html が返ってきている場合 const target = event.currentTarget const tmp = document.createElement('div') tmp.innerHTML = xhr.responseText const element = tmp.firstElementChild target.innerHTML = element.innerHTML // <form> タグの innerHTML の中身を入れ替える } // TODO: form タグ自体の属性についても厳密に丸々入れ替えるべきかもしれないが、ここではそこまでしていません }) }) })ここまで実施すると、 scaffold コードをベースにした Ajax CRUD が実現できているはずです。
良し悪し
- Pros.
_form
パーシャルに<form>
を書くというルールを前提とした場合に非常にシンプル- Javascript の記述がほとんど要らない
rake app:templates:copy
でコピーした controller と view のテンプレートファイルに少し手を入れる程度で、他にほとんど気にすることがない- Cons.
- この実装は、バリデーションエラー時に
<form>
タグ以外の場所を更新できない
- 実施する場合、たとえば
<form ... data-remote-placeholder="#form-wrapper">
のような属性があれば form でなくdocument.querySelector(form.dataset.remotePlaceholder)
が示す参照先をベースに書き換えする。みたいな拡張は可能。- あるいは SJR でやるとか
まとめ
- scaffold はまだ Ajax 化されていない (今後されるの?)
- scaffold のコードをベースに Ajax 化してみた
- gem turbolinks を使うと Ajax/非Ajax を意識せずに
redirect_to
を使える- save 失敗時に form の html 要素を返し、それにより自身の form の中身を差し替える(と決めておくと簡単)
補足
https://github.com/hamajyotan/scaffold_ajaxify
この記事での内容を実施したリポジトリの共有です。
- 投稿日:2019-02-18T09:00:36+09:00
Railsでログイン機能実装
Goal
deviseでログイン機能を実装
How to
step1.Gemをインストールしてサーバーを立ち上げ直す
step2.コマンドを利用してdeviseの設定ファイルを作成する
step3.コマンドを利用してUsersモデルを作成する
step4.未ログイン時にはログインと新規登録ボタンを表示する
step5.コントローラにリダイレクトを設定するstep1.Gemをインストールしてサーバーを立ち上げ直す
gtmfileの一番最後に記述
gem 'devise'step2.コマンドを利用してdeviseの設定ファイルを作成する
先ほど記述したgemを実行
$ bundle installstep3.コマンドを利用してUsersモデルを作成する
一気に必要な機能をインストール
$ rails g devise:installモデルを生成
$ rails g devise user $ rake db:migrateuser.rbを開き、5行目の「:trackable」を削除
app/models/user.rbclass User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable endstep4.未ログイン時にはログインと新規登録ボタンを表示する
test.rb<% if user_signed_in? %> <%= link_to "ログアウト", destroy_user_session_path, method: :delete %> <% else %> <%= link_to "ログイン", new_user_session_path, class: 'post' %> <%= link_to "新規登録", new_user_registration_path, class: 'post' %> <% end %>step5.コントローラにリダイレクトを設定する
app/controllers/*****_controller.rbclass *****Controller < ApplicationController before_action :move_to_index, except: :index def index end def new end def create end private def *****_params params.permit(:*****, :*****, :*****) end def move_to_index redirect_to action: :index unless user_signed_in? end end ```
- 投稿日:2019-02-18T08:56:33+09:00
railsでLogin機能実装
Goal
deviseでログイン機能を実装
How to
step1.Gemをインストールしてサーバーを立ち上げ直す
step2.コマンドを利用してdeviseの設定ファイルを作成する
step3.コマンドを利用してUsersモデルを作成する
step4.未ログイン時にはログインと新規登録ボタンを表示する
step5.コントローラにリダイレクトを設定するstep1.Gemをインストールしてサーバーを立ち上げ直す
gtmfileの一番最後に記述
gem 'devise'step2.コマンドを利用してdeviseの設定ファイルを作成する
先ほど記述したgemを実行
$ bundle installstep3.コマンドを利用してUsersモデルを作成する
一気に必要な機能をインストール
$ rails g devise:installモデルを生成
$ rails g devise user $ rake db:migrateuser.rbを開き、5行目の「:trackable」を削除
user.rb$ rails g devise user $ rake db:migratestep4.未ログイン時にはログインと新規登録ボタンを表示する
test.rb<% if user_signed_in? %> <%= link_to "ログアウト", destroy_user_session_path, method: :delete %> <% else %> <%= link_to "ログイン", new_user_session_path, class: 'post' %> <%= link_to "新規登録", new_user_registration_path, class: 'post' %> <% end %>step5.コントローラにリダイレクトを設定する
app/controllers/*****_controller.rbclass *****Controller < ApplicationController before_action :move_to_index, except: :index def index end def new end def create end private def *****_params params.permit(:*****, :*****, :*****) end def move_to_index redirect_to action: :index unless user_signed_in? end end ```
- 投稿日:2019-02-18T05:05:55+09:00
rails gem carrierwave imageがnull
rails
画像を投稿機能を付ける段階でネットの情報を見ながらgem で carrierwaveを使う方もいると思います。
エラーがないはずなのにデータベースのimageの値がnull になるケースがある方もいるかもしれませんのでここに書いておきます。
form_tag, のなかに → multipart: :true
を書き忘れているかもしれませんので確認をしてみては、
低レベルの記事ではありますが参考になればいいと思います。
なぜ必要かと言うと
multipartオプションがないと例えば
cat1.jpeg という画像の場合、
cattt1.jpeg というファイル名だけをstringとして受け取ってしまい、画像情報を受け取れないのです。
ファイルを取り込むときは:multipart => true
をform_tagの第二引数に指定すると
StringIO(stringを拡張したもの)でクエリーがやってきて画像が取り込めるようになるそうです。
以上です
- 投稿日:2019-02-18T02:58:59+09:00
Shrineのimage_dataを含むモデルのFactoryの作り方
railsの画像アップロードライブラリshrineを用いた場合の、FactoryBotのFactoryの書き方について悩んだポイントがあったので書いておきます
前提
登場するModel
- Area
column type memo name string require
- Shop
column type memo area bigint require logo bigint require name string require
- Logo
column type memo area bigint require name string require image_data text require Area has_many Shop
Area has_many Logo
Shop belongs_to Logo各Shopはエリアに登録されているロゴを使用できるイメージです。
悩んだこと
logoのファクトリーのimage_data カラムをどう書くべきなのか悩みました。
spec/factories/logos.rbFactoryBot.define do factory :logo do area sequence(:name) { |i| "logo#{i}" } image { #この部分!! } end endもともとは↓のように書いていたのですが、これだとlogoのテストデータが作成されるたびにファイルへアクセスするためRSpec全体の実行時間に大きな影響を与えていました。
image { File.open("#{Rails.root}/spec/fixtures/img/example.png") }RSpec実行時間
Factory追加前→ 27s
追加後 →1min 3sshopが作られるたびにFactoryBotによってlogoも自動で作られるのだから遅くなるのは当たり前ですね
試したこと
適当な文字列をつっこむ
image_data { 'sample' }結果
imageを実際に使うテストにてJSON::ParserError:765: unexpected token at 'sample'
それはそう。。
fixture_file_upload使ってみる
この記事ではfixture_file_upload使ったらFile.openより相当早い結果になっているので期待大!
image { fixture_file_upload("#{Rails.root}/spec/fixtures/img/example.png", 'img/png') }結果
テストはすべてpass!
ただしFinished in 1 minute 9.24 seconds (files took 15.67 seconds to load)
File.openのときと変わらないくらい時間かかってる。。
使い方がおかしい?最終的に
ファイルアクセスではなく架空のcacheを作成してみる
Shrineのuploaded_fileメソッドを用いてキャッシュの状態でファイルが上がっていることにする作戦
factory :logo do transient do cache_image Shrine.uploaded_file( 'id' => SecureRandom.hex(8), 'storage' => 'cache', 'metadata' => { 'mime_type' => 'image/jpeg', 'size' => 1.megabyte }).to_json end area sequence(:name) { |i| "logo#{i}" } image { cache_image } image_data { cache_image } end結果
Finished in 31.04 seconds
いいじゃん!!
補足
この場合、image/image_dataともに明示的にcache_imageを入れてあげないとうまくいきませんでした。
はじめは下記のように書いていたんですが、
image { Shrine.uploaded_file( 'id' => SecureRandom.hex(8), 'storage' => 'cache', 'metadata' => { 'mime_type' => 'image/jpeg', 'size' => 1.megabyte }).to_json}結果
imageを実際に使うテストにてNo such file or directory @ rb_sysopen - /usr/src/app/public/uploads/cache/675ce0d849e8a5f8
ちゃんとimageが上がっていない?とってこれていない?
shrineの実装を見ないとわからなそうなので今回はここまでで
- 投稿日:2019-02-18T01:33:40+09:00
駆け出しrailsエンジニアがはじめの一ヶ月でくらったレビューをさらす
はじめに
早いもので、現職について1ヶ月が過ぎました。
初めてrailsを業務で扱っている自分ですが、バリバリの先輩エンジニアにメタメタのギッタギタにレビューをしていただいています。
とてもありがたい。。備忘録の意味も込めて整理しておこうと思います。
指摘事項の雰囲気ごとにまとめました。
- 開発の常識だよ系
- 知っておこう系
- かっこよく書こう系
設計だったり実装方針だったりのレビュー事項は一般化しづらいので含めていません。
そんなこと言われなくてもわかるでしょという恥ずかしい内容も多々ありますが、駆け出しなんだし恥ずかしがらずに晒していきます。いってみよう。
開発常識系
rubyやrailsに限らず、開発者としてやっておこう/意識しようという内容のもの
ファイル末尾に空行を入れよう
POSIXという偉い規格が定めているテキストファイルの定義に反するから
テキストファイルとは「1 つ以上の行」行は「0 個以上の改行以外の文字と末尾の改行」
不要なファイルはコミットしない
例えば、rails gで自動生成されたものでも不要なファイルは消そう
modelsのspecとかは絶対作るわけではないのでrails gはいろいろ作ってくれがちなのでmigrationファイルを作成するときくらいしか使わないかもとのこと。
不要なトランザクションは貼らない
当たり前なのだけれど
example_update.rbdef update @hoge = Hoge.find(params[:id]) if Hoge.transaction_with {update_with_huga} redirect_to .. else ... end end private def update_with_huga #呼び出し元でトランザクション貼ってるからこっちでは貼る必要なし end※transaction_withはswitch_pointのメソッド
カラムの追加位置を意識しよう
add_column時はbeforeオプションを使って適切な位置に追加する
知っておこう系
「知らないとやばいよ」〜「知っておくと便利だよ」まで含めて
外部キー貼るとき
外部キーを表すときは、
t.bigint
ではなくt.references
で設定する
勝手にindexを貼ってくれる.first / .last
.first
だと、 全てのデータをSELECT文で取得して最初のカラムを返す
レコードの数が増えていくであろうモデルに対しては使わないように気をつけるclients = Client.first #SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1Active Record クエリインターフェイスはどんなSQLが発行されるのかを確認した上で使うこと
関連付けの使用
不要なSQLを発行させていないか気をつける
post.user.id #postに紐づくuserを取得するselect文が発行される post.user_id #sqlは発行されないsanitize_sql_like使おう
そもそもsanitizeってなんだよと最初は思いました。
ものすごく簡単に言うとユーザーからの不正なSQL実行を防ぐための入力値のエスケープ処理というイメージであっているはず#一般的なlike検索 -> 文字列にsql入れられたら実行されて困っちゃう User.where('name LIKE ?', `%#{args[:name]}%`) #sanitize_sql_like -> 文字列にエスケープかかるから安心 User.where('name LIKE ?', "%#{sanitize_sql_like(args[:name])}%")validationはいろいろ準備されてるよ
例:入力値として整数だけを受け付けたいカラムが存在するとき
数字のみ受け付けたい# もともとの自分の実装 VALID_NUMBER_INPUT_REGEX = /\A[0-9]+\z/.freeze validates :hoge, presence: true, format: { with: VALID_NUMBER_INPUT_REGEX } # レビュー後 validates :hoge, numericality: { only_integer: true }numericality以外にもたくさんある
validationに限らずRailsGuideは一通り目を通すべきと痛感しましたかっこよく書こう系
こうするとrubyぽいよ、railsぽいよ、逆にそう書くとかっこわるいよという指摘
()いらない
java出身だと最初は違和感が抜けないです
@post = current_user.post.build() #()は不要だよ @post = current_user.post.build不要な返り値いらない
場合によりけりですが、例えばなにかのbefore_actionで認証して失敗したらリダイレクトさせるときなんかは返り値不要
return true if @account&.authenticated? #このtrueいらない redirect_to hoge_path冗長なifはださい
これはrubyだからというわけでもない気がしますが。
シンプルな分岐はifをつかわなくても書ける場合が大半booleanを返すメソッドif account.admin? true else account.shop.id == record.id end #こう書ける account.admin? || account.shop_id == record.idscopeを使おう
ItemはShopに所属している前提
もともとの自分の実装#コントローラ @items = Item.search(current_user.shop_id, params[:search_word]).page(params[:page]).per(USER_PER_PAGE) #Userモデル searchメソッド def self.search(shop_id, name) Item.where(shop: shop_id).where('name LIKE ?', "%#{name}%") endレビュー後#コントローラ @items = Item.where(shop: current_user.shop).keyword_by(params[:search_word]).page(... # keyword_byは、model側にscopeを作る #Userモデル scope scope :keyword_by, ->(search_word) do if search_word.present? where('name LIKE ?', "%#{name}%") end endscopeを用いたほうが
search
の中に処理を内包するより、Controllerでどのようなフィルタリングをするかがわかりやすくなります。関連があるときは明示的に使おう
#もともとの自分の実装 Item.where(shop: current_user.shop).find(params[:id]) #レビュー後 current_user.shop.items.find(params[:id])このように書くことで、
current_user
に紐づく何かを処理しないといけないということを明確にすることが多いpartial collection
なんらかの配列があってそれぞれになにかを表示したいというケース
haml-# もともとの自分の実装 - hoge_array.each do |hoge| = hoge.name = link_to .. ... -# レビュー後 = render partial: 'hoge', collection: hoge_array, as: 'hoge' -# この上で下のような_hoge.html.hamlを準備する = hoge.name = link_to .. ...最後に
本当は「要件と照らし合わせたときの実装方針」とか「責務の分担に関する考え方」みたいな自分が感動した部分に関して紹介したかったのですが言語化が難しかったです。。
この部分に熟練のエンジニア方の凄さがつまっているはずなので、またの機会にアウトプットできればと思います。
駆け出しエンジニアのみなさま
おれはこんなレビューされて痺れたぜという話があれば教えてください。
- 投稿日:2019-02-18T01:29:38+09:00
db:migrateでalready existだった時の話
modelが気に入らなくて作り直そうとした
そういうこと、ありません?
開発中に「あれ?この変数忘れてたわ」とmodelを作り直したいなーって思って、rails destroy => rais generate modelしたんですよ。
んでdb:migrateしようと思ったらご覧のありさま。ec2-user:~/environment/circle-list (master) $ rails db:migrate == 20190216164559 CreateCircles: migrating ==================================== -- create_table(:circles) rails aborted! StandardError: An error has occurred, this and all later migrations canceled: SQLite3::SQLException: table "circles" already exists: CREATE TABLE "circles" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "space" varchar, "memo" varchar, "url" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL) (以下略)SQLite上に作りたいテーブルは既にいるよ!と怒られました。
うん知ってる。ゴリ押しでどうにかしたいんだよこっちは。つよくてにゅーげーむ
テーブルの中身をふっとばしていいなら「db:migrate」の後ろに「reset」をつけよう
$ rails db:migrate:reset Dropped database 'db/development.sqlite3' Database 'db/test.sqlite3' does not exist Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' == 20190216164559 CreateCircles: migrating ==================================== -- create_table(:circles) -> 0.0020s == 20190216164559 CreateCircles: migrated (0.0027s) ===========================
- 投稿日:2019-02-18T00:44:50+09:00
Railsを本番環境で作動させる
Railsを本番環境で稼働するのに必要なコマンド
作ったプログラムを本番環境で落としてきたときにそのままでは動かなかったので必要だった作業を(覚えている範囲で)まとめておこうと思います。
Assetファイルのプリコンパイル
静的ファイルについては、サーバを起動する際にhtml,cssなどの必要なファイルを一つにまとめるようになっています。そのため、ある場所のファイルは自動的にコンパイルされます。具体的には、
public/assets
中にあるファイル群については自動的にコンパイルされ、そのコピーが静的なassetとしてwebサーバに保存されます。しかし、いくつかの場所にあるファイルはproduction環境においては自動的にはコンパイルされないので、手動でCSSファイルなどをコンパイルしておく必要があリます。これをプリコンパイルと呼び、次のコマンドで実行することができます。
bundle exec rake assets:precompile RAILS_ENV=production
プリコンパイルの際に実際にコンパイルする対象のファイルは、設定ファイルをいじることで指定することができます。Secret Keyの設定
重要なファイルなどを暗号化する際に用いるSecret Keyというのがあって、これは手元のPCで作業している際に自動的に生成され、それを元に暗号化・復号化を行なっているようです
このファイルは他の人に知られてはいけない情報のためGitの管理対象から外れており、従ってGit経由でサーバーに落としてきたときにはこの自動生成されたファイルをコピーすることができず、サーバを起動しようとすると自動的に新たな(間違った)Keyが生成されるため、復号化がうまくいかずエラーが発生します。 ので、手元の環境でSecret Keyをコピーして本番環境にうつしてやる必要があります。どのデータを移行する必要があるかは、エラーメッセージを読めばある程度わかるようになっている(はずです)。
具体的にSecret_key_baseが用いられる対象として、次のものがあげられます。
1. 暗号化cookie
2. 署名済みcookie
3. アプリのmessage_verifier
参考にしたWebサイト
Rails Guid
Tech Racho
- 投稿日:2019-02-18T00:44:50+09:00
Railsを本番環境で作動させるために必要な作業
Railsを本番環境で稼働するのに必要なコマンド
作ったプログラムを本番環境で落としてきたときにそのままでは動かなかったので必要だった作業を(覚えている範囲で)まとめておこうと思います。
CSSのプリコンパイル
詳しい仕様についてはよくわかってないのですが、Railsでは本番環境で稼働させる前に手動でCSSファイルをコンパイルしておく必要があるようです。これをプリコンパイルと呼び、次のコマンドで実行することができます。
bundle exec rake assets:precompile RAILS_ENV=production
プリコンパイルの際に実際にコンパイルする対象のファイルは、設定ファイルをいじることで指定することができます(いつかまとめます)Secret Keyの設定
重要なファイルなどを暗号化する際に用いるSecret Keyというのがあって、これは手元のPCで作業している際に自動的に生成され、それを元に暗号化・復号化を行なっているようです
このファイルは他の人に知られてはいけない情報のためGitの管理対象から外れており、従ってGit経由でサーバーに落としてきたときにはこの自動生成されたファイルをコピーすることができず、サーバを起動しようとすると自動的に新たな(間違った)Keyが生成されるため、復号化がうまくいかずエラーが発生します。 ので、手元の環境でSecret Keyをコピーして本番環境にうつしてやる必要があります。どのデータを移行する必要があるかは、エラーメッセージを読めばある程度わかるようになっている(はずです)。また詰まったときにでも更新していこうと思います。