- 投稿日:2020-09-10T23:07:13+09:00
Webpackerとは (Rails6で rails s してエラーが出た時の話)
Rails6で新規アプリを作成しようとし、データベース作成後に
rails sでサーバを起動しようとしたところ、以下のようなエラーが出ました。$ rails s => Booting Puma => Rails 6.0.3.3 application starting in development => Run `rails server --help` for more startup options Exiting Traceback (most recent call last): 77: from bin/rails:3:in `<main>' 76: from bin/rails:3:in `load' ・ ・ ・ Webpacker configuration file not found /Users/<ユーザー名>/<アプリ名>/config/webpacker.yml. Please run rails webpacker:install Error: No such file or directory @ rb_sysopen - /Users/<ユーザー名>/<アプリ名>/config/webpacker.yml (RuntimeError)「Webpackerの設定ファイルが見つかりません」とのこと。
/config/webpacker.ymlがあるべきだそう(なかった)。Webpackerとは
JavaScriptのビルドツール「Webpack」のラッパーで、RailsアプリケーションでWebpackを使ってJavaScriptを管理することを簡単にしてくれるGem
現場で使えるRuby on Rails5 速習実践ガイド私は「RailsでJavaScriptを使えるようにする為に色々よしなにしてくれるGem」と理解しました・・・。
(この辺りの理解は今後深めていけたら。)またRailsガイドv6.0によると、「WebpackerがRails 6のデフォルトJavaScriptコンパイラになる」とあり、
rails newした時点で「gem 'webpacker'」がGemfileに書かれている理由がわかります。
以前はSprockets (スプロケッツ)というものがJavaScriptの標準ビルドツールとなっていたようです。rails webpacker:installしてみる
どうしてGemfileに書いてあったのにWebpackerがインストールされてないんだっ、と思うところですが、
大人しく「rails webpacker:install」してみると$ rails webpacker:install Yarn not installed. Please download and install Yarn from https://yarnpkg.com/lang/en/docs/install/「Yarnがインストールされていません」と言われてしまいました。
この為にrails newした時にWebpackerがインストールされなかったのでしょう。Yarnとは
JavaScriptのパッケージマネージャのこと。
下記の記事がわかりやすかったです。
JavaScriptのパッケージマネージャーnpmとYarnについて解説します!Yarnのインストール
$ brew install yarnもう一度 rails webpacker:install をしてみる
$ rails webpacker:install create config/webpacker.yml Copying webpack core config create config/webpack create config/webpack/development.js create config/webpack/environment.js create config/webpack/production.js create config/webpack/test.js ・ ・ ・ Webpacker successfully installed ? ?無事にWebpackerをインストールできました。
あらためて rails s してみる
参考にさせていただきました
- 投稿日:2020-09-10T22:28:37+09:00
Postgresエラー「PG::ConnectionBad」
何度もつまづいたので一旦簡単にメモ
PG::ConnectionBad
could not connect to server: Connection refused Is the server running on host "localhost" (::1) and accepting TCP/IP connections on port 5432? could not connect to server: Connection refused Is the server running on host "localhost" (127.0.0.1) and accepting TCP/IP connections on port 5432?
% postgres -D /usr/local/var/postgres 2020-09-10 22:13:34.167 JST [8631] FATAL: lock file "postmaster.pid" already exists 2020-09-10 22:13:34.167 JST [8631] HINT: Is another postmaster (PID 494) running in data directory "/usr/local/var/postgres"?% rm /usr/local/var/postgres/postmaster.pidrmでpostmaster.pidを削除
% brew services restart postgresqlリスタートしてあげる。
% rails s起動して確認
ポイント
postmaster.pidが残っていること
正常にpostgresqlが終わらせられていないと
postmaster.pidというファイルが残ってしまった結果
接続できない起動できないエラーが発生するみたいです。もう少し詳しく調べて再度更新予定。
postgresqlの起動と停止についての仕組みをしる必要がありそう
今回はメモまで。参考記事
- 投稿日:2020-09-10T19:45:10+09:00
Railsでmechanizeを使ってスクレイピング
PythonでWebスクレイピングをやったことありましたが、
Railsもできるんだよねと思い立って、アプリ作る際に導入してみたい!と思いやってみたの記録になります。今回は、その日の平均株価とかをスクレイピングします。
参考にさせていただいた記事
https://qiita.com/soehina/items/948f7f158a3a2d5be1dc下準備
Railsアプリの作成
gem 'mechanize'のインストールView
index.html.erb<%= @values.join %>Controller
_controller.rbrequire 'mechanize' def index @scrps = Scrp.all agent = Mechanize.new @values = [] page = agent.get("https://www.nikkei.com/markets/worldidx/chart/nk225/") @elements= page.search('li.m-trend_economic_table_list') @elements.each do |element| @values << element.inner_text end endgetメソッドとsearchメソッドで、情報を取得し
inner_textで文字を引っこ抜いてくれます。
それをvaluesに入れてあげて、配列にしています。とても簡単に、スクレイピングができました。
お疲れ様です。おわり!
- 投稿日:2020-09-10T16:52:05+09:00
form.labelで文字をエスケープしたときの実装メモ
<%= form.label :title, "件名", for: "title" %> <%= form.text_field :title, id: "title", placeholder: "件名を入力してください" %>上記のlabelの中の件名を
<span>件名<span>
としたい。(bootstrapをあてる関係で)
しかし<%= form.label :title,"<span>件名</span>", for: "title" %>としても意図した挙動にはならない。
これはhtmlタグが文字列として認識されてしまうから。
なので"件名"の部分だけエスケープさせたい。
エスケープで検索すると、html_safe、raw、sanitize等いくつかメソッドがあるとのこと。(参考)
https://madogiwa0124.hatenablog.com/entry/2017/09/16/105843
https://railsguides.jp/action_view_overview.html#sanitizehelperRailsガイドより、
html_safeやrawではなく、後述の#sanitizeメソッドを使うことが推奨されています。セキュリティ上の問題が生じるため、ユーザー入力に対して#html_safeや#rawを使ってはいけません。
とあることから
ユーザー情報を入力するためのinputタグなどにはsanitizeメソッドを、ユーザー情報以外であればhtml_safeやrawを使うっぽい。ユーザー情報入力フォーム → sanitize ユーザー情報以外の入力フォーム → raw 入力フォーム以外 → html_safeこんな感じ??
実際のオプションの指定の仕方は
https://api.rubyonrails.org/
を参考に、以下のように実装したら期待した表示に。<%= form.label :title, '<span>件名</span>'.html_safe, for: "title" %> <%= form.text_field :title, id: "title", placeholder: "件名を入力してください" %>ちなみに
https://api.rubyonrails.org/
でlabelを検索すると
ActionView::Helpers::FormBuilderのlabelとActionView::Helpers::FormHelperのlabelが引っかかった。
この違いって何???と悩んだのでメモFormHelperはform_tag
FormBuilderはform_fom、form_with今回はレシーバがフォームヘルパオブジェクトじゃなくてフォームビルダオブジェクトになるので、FormBuilderのlabelのオプション指定を調べて実装する必要があった。
レシーバとは
obj.methodこういう記載があるところの
obj(レシーバ).method(メソッド)この前の部分。
aaa.label_tag("hoge".html_safe)
上記だとこのaaaの部分。今回だとform_withで実装しているため、form.labelのレシーバはFormBuilder
- 投稿日:2020-09-10T14:40:54+09:00
プロフィール編集機能の実装
備忘録です!!
新たなカラムの追加
userのアバター画像のカラムを生成します。
rails g migration add_avatar_to_users avatar:stringviewの生成
プロフィール詳細画面として
profiles/show.html.erb<% content_for(:title, 'プロフィール') %> <div class="container pt-3"> <div class="row"> <div class="col-md-10 offset-md-1 mb-5"> <h1 class="float-left"><%= t('.title') %></h1> <%=link_to "編集", edit_profile_path, class: 'btn btn-success float-right' %> </div> <div class="col-md-10 offset-md-1"> <table class="table"> <tbody> <tr> <th> <span>メールアドレス</span> </th> <td><%= current_user.email %></td> </tr> <tr> <th> <span>氏名</span> </th> <td><%= current_user.decorate.full_name %></td> </tr> <tr> <th> <span>アバター</span> </th> <td><%= image_tag current_user.avatar.url, size: '50x50', class: 'rounded-circle mr15' %></td> </tr> </tbody> </table> </div> </div> </div>プロフィール変更画面
edit.html.erb<% content_for(:title, 'プロフィール編集') %> <div class="container pt-3"> <div class="row"> <div class="col-md-10 offset-md-1 mb-5"> <h1 class="float-left"><%= t('.title') %></h1> </div> <div class="col-md-10 offset-md-1"> <%= form_with model: @user, url: profile_path, local: true do |f| %> <%= render "shared/error_messages", object: f.object %> <div class="form-group"> <%= f.label :email %> <%= f.email_field :email, class: "form-control" %> </div> <div class="form-group"> <%= f.label :last_name %> <%= f.text_field :last_name, class: "form-control" %> </div> <div class="form-group"> <%= f.label :first_name %> <%= f.text_field :first_name, class: "form-control" %> </div> <div class="form-group"> <%= f.label :avatar %> <%= f.file_field :avatar, class: "form-control", accept: 'image/*', onchange: 'previewFileWithId(preview)' %> <%= f.hidden_field :avatar_cache %> <div class='mt-3 mb-3'> <%= image_tag @user.avatar.url , size: '100x100', class: 'rounded-circle' %> </div> </div> <%= f.submit (t 'defaults.update'), class: "btn btn-primary"%> <% end %> </div> </div> </div>ルーティングの設定
今回は、resourcesではなく、resourceを使っていきます。
本来、/users/:id/edit、のようになるはずですが、もしidを変えれば、他のユーザーのプロフィール編集ページに入ることができてしまう可能性があります。(もちろん、入れないようにバリデーションはかけますが)
プロフィール変更ページは自分自身に対してしか使わないので、idを用いず、pofile/editとなるようにルーティングを設定していきます。
resource resource :profile, only: %i[show edit update]profileコントローラーの生成
rails g controller profilesuserの情報は、userモデルに紐づいたデータベースに保存されていますが、今回は新たにモデルに紐づかないprofileコントローラーを生成し、profileコントローラーでプロフィールの編集ページを実装していきます。
profile_controller.rbdef edit @user = User.find(current_user.id) end def update @user = User.find(current_user.id) if @user.update(user_params) redirect_to profile_path, success: t('defaults.message.edited', item: User.model_name.human) else flash.now['danger'] = t('defaults.message.not_edited', item: User.model_name.human) render :edit end end private def user_params params.require(:user).permit(:email, :first_name, :last_name, :avatar) end実装完了です!
- 投稿日:2020-09-10T14:34:06+09:00
renderのcollectionオプションを使った繰り返し処理の省略
動作環境
Ruby 2.6.5
Rails 6.0.3.2collectionを使うことでrenderで呼び出した部分テンプレートを繰り返し処理する場合は、省略して記述できることを学んだので、投稿してみました。
collectionを使わない場合
index.html.erb<% @hoges.each do |hoge| %> <%= render partial: "huga", locals: {hoge: hoge} %> <% end %>上記のコードは、部分テンプレートhugaを呼び出し、その中でhogeという変数を渡して繰り返し処理を行うというコードです。
これをcollectionを使うとどうなるのかを見てみましょう。collectionを使う場合
index.html.erb<%= render partial: "huga", collection: @hoges %>こちらのコードは先ほどのコードと全く同じ意味を持ちます。3行のコードが1行で済むので、非常に楽に記述できることがわかると思います。
注意していただきたいのが、部分テンプレートhugaで@hogeや@hogesと記述してしまうとエラーが発生してしまうことです。collectionの後の@hogesはコントローラーから受け取っているインスタンス変数です。実際に、部分テンプレートに渡している変数は先ほどのコードと同じhogeですので気をつけてください。急にrenderのオプションでcollectionが出てきて、私は混乱してしまったことがあるので、この記事が少しでも役に立てればと思います。
- 投稿日:2020-09-10T13:07:17+09:00
【Appleサブスクリプションオファー】プロモーションオファー署名の作成方法
tl;dr
Appleのサブスクリプションオファーのプロモーションオファーに関しての情報がなかった。
これが、日本初の資料。プロモーションオファーとは
WWDC 2019動画
https://developer.apple.com/videos/play/wwdc2019/305
Apple公式ドキュメント
https://developer.apple.com/jp/app-store/subscriptions/#subscription-offers
対象となるのは、そのサブスクリプションを現在利用している、または過去に利用したことがあるお客様です。
これらのオファーによって、ユーザー数の拡大や維持のため、
独自のプロモーションを柔軟に行うことができるようになります。
キャンペーンを通じて、サブスクリプションをキャンセルした利用者に再サブスクリプションを促したり、
別のサブスクリプションへのアップグレードを特別価格で提供したりすることができます。準備
Apple公式ドキュメント
サブスクリプションオファーの設定
実装
※今回は、サーバーサイドでの署名作成に関してのみ記載する
Apple公式ドキュメント
プロモーションオファー用の署名の生成
https://developer.apple.com/jp/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers/署名の生成に必要なもの
■appBundleID
環境変数で持つ
■keyIdentifier
環境変数で持つ
■productIdentifier
アプリ側からパラメータでもらう
■offerIdentifier
アプリ側からパラメータでもらう
■applicationUsername
アプリ側からパラメータでもらう
■nonce
サーバー側で生成する
■timestamp
サーバー側で生成する
署名
署名の生成
サンプルコード
本来はメソッドで分割するが、わかりやすさを重視するrequire 'openssl' require 'base64' require 'securerandom' require 'json' # 環境変数として読み込むが、あえて記載する private_key = '-----BEGIN PRIVATE KEY-----xxxxxxxxxxxxxxxxxxx-----END PRIVATE KEY-----' # 環境変数から読み込んだ秘密鍵の改行コードがエスケープされてしまうのを防ぐ private_key = OpenSSL::PKey::EC.new(private_key.gsub(/\\n/, "\n"))) app_bundle_id = 'xxxx' key_identifier = 'xxxx' product_identifier = 'xxxx' offer_identifier = 'xxxx' application_username = 'xxxx' nonce = SecureRandom.uuid timestamp = (Time.current.to_f * 1000).to_i.to_s # 不可視の分離文字('\u2063')をパラメータの間にはさみ、結合する payload = app_bundle_id + "\u{2063}" + key_identifier + "\u{2063}" + product_identifier + "\u{2063}" + offer_identifier + "\u{2063}" + application_username + "\u{2063}" + nonce + "\u{2063}" + timestamp # 署名 # Ruby2.4.0以降であれば # signature = private_key.sign(digest, data) # SHA-256ハッシュを使って署名 signature = private_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(payload)) # base64にエンコード # strict_encode64を使い、改行コードを消す signature_base64 = Base64.strict_encode64(signature) # 検証 # OpenSSL::PKey::ECオブジェクトを生成 ec = OpenSSL::PKey::EC.new(private_key.group) ec.public_key = private_key.public_key # SHA-256ハッシュで検証 digest = OpenSSL::Digest::SHA256.new # payloadを秘密鍵で署名したその署名文字列がsignatureであることを公開鍵を使って検証 ec.verify(digest, signature, payload) result = { key_identifier: key_identifier, nonce: nonce, timestamp: timestamp, signature: signature_base64 }.to_json楕円曲線デジタル署名アルゴリズム(ECDSA)について
最初はruby_ecdsaというgemを使おうかと検討していた
https://github.com/DavidEGrayson/ruby_ecdsarequire 'ecdsa' require 'securerandom' require 'digest/sha2' group = ECDSA::Group::Secp256k1 private_key = 1 + SecureRandom.random_number(group.order - 1) public_key = group.generator.multiply_by_scalar(private_key) message = 'ECDSA is cool.' digest = Digest::SHA2.digest(message) temp_key = 1 + SecureRandom.random_number(group.order - 1) signature = ECDSA.sign(group, private_key, digest, temp_key) valid = ECDSA.valid_signature?(public_key, digest, signature) puts "valid: #{valid}"が、秘密鍵が数値を想定したつくりになっていたので使用を見送った
Apple公式の署名作成サンプル
JavaScriptとNode.jsを使ったサンプル
サーバを起動して、アクセスするとレスポンスが返る
- 投稿日:2020-09-10T13:07:17+09:00
Appleのサブスクリプションオファーのプロモーションオファー署名作成
tl;dr
Appleのサブスクリプションオファーのプロモーションオファーに関しての情報がなかった。
これが、日本初の資料。プロモーションオファーとは
WWDC 2019動画
https://developer.apple.com/videos/play/wwdc2019/305
Apple公式ドキュメント
https://developer.apple.com/jp/app-store/subscriptions/#subscription-offers
対象となるのは、そのサブスクリプションを現在利用している、または過去に利用したことがあるお客様です。
これらのオファーによって、ユーザー数の拡大や維持のため、
独自のプロモーションを柔軟に行うことができるようになります。
キャンペーンを通じて、サブスクリプションをキャンセルした利用者に再サブスクリプションを促したり、
別のサブスクリプションへのアップグレードを特別価格で提供したりすることができます。準備
Apple公式ドキュメント
サブスクリプションオファーの設定
実装
※今回は、サーバーサイドでの署名作成に関してのみ記載します
Apple公式ドキュメント
プロモーションオファー用の署名の生成
https://developer.apple.com/jp/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers/署名の生成に必要なもの
■appBundleID
環境変数で持つ
■keyIdentifier
環境変数で持つ
■productIdentifier
アプリ側からパラメータでもらう
■offerIdentifier
アプリ側からパラメータでもらう
■applicationUsername
アプリ側からパラメータでもらう
■nonce
サーバー側で生成する
■timestamp
サーバー側で生成する
署名
署名の生成
サンプルコード
本来はメソッドで分割するが、わかりやすさを重視するrequire 'openssl' require 'base64' require 'securerandom' require 'json' # 環境変数として読み込むが、あえて記載する private_key = '-----BEGIN PRIVATE KEY-----xxxxxxxxxxxxxxxxxxx-----END PRIVATE KEY-----' # 環境変数から読み込んだ秘密鍵の改行コードがエスケープされてしまうのを防ぐ private_key = OpenSSL::PKey::EC.new(private_key.gsub(/\\n/, "\n"))) app_bundle_id = 'xxxx' key_identifier = 'xxxx' product_identifier = 'xxxx' offer_identifier = 'xxxx' application_username = 'xxxx' nonce = SecureRandom.uuid timestamp = (Time.current.to_f * 1000).to_i.to_s # 不可視の分離文字('\u2063')をパラメータの間にはさみ、結合する payload = app_bundle_id + "\u{2063}" + key_identifier + "\u{2063}" + product_identifier + "\u{2063}" + offer_identifier + "\u{2063}" + application_username + "\u{2063}" + nonce + "\u{2063}" + timestamp # 署名 # Ruby2.4.0以降であれば # signature = private_key.sign(digest, data) # SHA-256ハッシュを使って署名 signature = private_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(payload)) # base64にエンコード # strict_encode64を使い、改行コードを消す signature_base64 = Base64.strict_encode64(signature) # 検証 # OpenSSL::PKey::ECオブジェクトを生成 ec = OpenSSL::PKey::EC.new(private_key.group) ec.public_key = private_key.public_key # SHA-256ハッシュで検証 digest = OpenSSL::Digest::SHA256.new # payloadを秘密鍵で署名したその署名文字列がsignatureであることを公開鍵を使って検証 ec.verify(digest, signature, payload) result = { key_identifier: key_identifier, nonce: nonce, timestamp: timestamp, signature: signature_base64 }.to_json楕円曲線デジタル署名アルゴリズム(ECDSA)について
最初はruby_ecdsaというgemを使おうかと検討していた
https://github.com/DavidEGrayson/ruby_ecdsarequire 'ecdsa' require 'securerandom' require 'digest/sha2' group = ECDSA::Group::Secp256k1 private_key = 1 + SecureRandom.random_number(group.order - 1) public_key = group.generator.multiply_by_scalar(private_key) message = 'ECDSA is cool.' digest = Digest::SHA2.digest(message) temp_key = 1 + SecureRandom.random_number(group.order - 1) signature = ECDSA.sign(group, private_key, digest, temp_key) valid = ECDSA.valid_signature?(public_key, digest, signature) puts "valid: #{valid}"が、秘密鍵が数値を想定したつくりになっていたので使用を見送った
Apple公式の署名作成サンプル
JavaScriptとNode.jsを使ったサンプル
サーバを起動して、アクセスするとレスポンスが返ります
- 投稿日:2020-09-10T13:07:17+09:00
Appleのサブスクリプションオファーのプロモーションオファー署名の作成方法
tl;dr
Appleのサブスクリプションオファーのプロモーションオファーに関しての情報がなかった。
これが、日本初の資料。プロモーションオファーとは
WWDC 2019動画
https://developer.apple.com/videos/play/wwdc2019/305
Apple公式ドキュメント
https://developer.apple.com/jp/app-store/subscriptions/#subscription-offers
対象となるのは、そのサブスクリプションを現在利用している、または過去に利用したことがあるお客様です。
これらのオファーによって、ユーザー数の拡大や維持のため、
独自のプロモーションを柔軟に行うことができるようになります。
キャンペーンを通じて、サブスクリプションをキャンセルした利用者に再サブスクリプションを促したり、
別のサブスクリプションへのアップグレードを特別価格で提供したりすることができます。準備
Apple公式ドキュメント
サブスクリプションオファーの設定
実装
※今回は、サーバーサイドでの署名作成に関してのみ記載する
Apple公式ドキュメント
プロモーションオファー用の署名の生成
https://developer.apple.com/jp/documentation/storekit/in-app_purchase/generating_a_signature_for_subscription_offers/署名の生成に必要なもの
■appBundleID
環境変数で持つ
■keyIdentifier
環境変数で持つ
■productIdentifier
アプリ側からパラメータでもらう
■offerIdentifier
アプリ側からパラメータでもらう
■applicationUsername
アプリ側からパラメータでもらう
■nonce
サーバー側で生成する
■timestamp
サーバー側で生成する
署名
署名の生成
サンプルコード
本来はメソッドで分割するが、わかりやすさを重視するrequire 'openssl' require 'base64' require 'securerandom' require 'json' # 環境変数として読み込むが、あえて記載する private_key = '-----BEGIN PRIVATE KEY-----xxxxxxxxxxxxxxxxxxx-----END PRIVATE KEY-----' # 環境変数から読み込んだ秘密鍵の改行コードがエスケープされてしまうのを防ぐ private_key = OpenSSL::PKey::EC.new(private_key.gsub(/\\n/, "\n"))) app_bundle_id = 'xxxx' key_identifier = 'xxxx' product_identifier = 'xxxx' offer_identifier = 'xxxx' application_username = 'xxxx' nonce = SecureRandom.uuid timestamp = (Time.current.to_f * 1000).to_i.to_s # 不可視の分離文字('\u2063')をパラメータの間にはさみ、結合する payload = app_bundle_id + "\u{2063}" + key_identifier + "\u{2063}" + product_identifier + "\u{2063}" + offer_identifier + "\u{2063}" + application_username + "\u{2063}" + nonce + "\u{2063}" + timestamp # 署名 # Ruby2.4.0以降であれば # signature = private_key.sign(digest, data) # SHA-256ハッシュを使って署名 signature = private_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(payload)) # base64にエンコード # strict_encode64を使い、改行コードを消す signature_base64 = Base64.strict_encode64(signature) # 検証 # OpenSSL::PKey::ECオブジェクトを生成 ec = OpenSSL::PKey::EC.new(private_key.group) ec.public_key = private_key.public_key # SHA-256ハッシュで検証 digest = OpenSSL::Digest::SHA256.new # payloadを秘密鍵で署名したその署名文字列がsignatureであることを公開鍵を使って検証 ec.verify(digest, signature, payload) result = { key_identifier: key_identifier, nonce: nonce, timestamp: timestamp, signature: signature_base64 }.to_json楕円曲線デジタル署名アルゴリズム(ECDSA)について
最初はruby_ecdsaというgemを使おうかと検討していた
https://github.com/DavidEGrayson/ruby_ecdsarequire 'ecdsa' require 'securerandom' require 'digest/sha2' group = ECDSA::Group::Secp256k1 private_key = 1 + SecureRandom.random_number(group.order - 1) public_key = group.generator.multiply_by_scalar(private_key) message = 'ECDSA is cool.' digest = Digest::SHA2.digest(message) temp_key = 1 + SecureRandom.random_number(group.order - 1) signature = ECDSA.sign(group, private_key, digest, temp_key) valid = ECDSA.valid_signature?(public_key, digest, signature) puts "valid: #{valid}"が、秘密鍵が数値を想定したつくりになっていたので使用を見送った
Apple公式の署名作成サンプル
JavaScriptとNode.jsを使ったサンプル
サーバを起動して、アクセスするとレスポンスが返る
- 投稿日:2020-09-10T12:49:11+09:00
Ruby on Railsの開発環境でPostgreSQLを利用してPJを作成する
Rails new
$ rails new app-name -d postgresql $ cd app-nameapp-nameは任意の名前をつける
vs codeを開く
$ code .この時点で
-bash: code: command not foundと怒られた方はvscodeの設定が必要
コマンドパレットを開き「shell command」
すると『Shell Command: Install 'code' command in PATH command.』
が表示されるので適用するとできるようになるデータベースの作成
$ rails db:createlocalhost:3000にアクセスするとRailsの初期画面が表示されたのでできているのではないかと思います。
- 投稿日:2020-09-10T12:38:23+09:00
(ギリ)20代の地方公務員がRailsチュートリアルに取り組みます【第6章】
前提
・Railsチュートリアルは第4版
・今回の学習は3周目(9章以降は2周目)
・著者はProgate一通りやったぐらいの初学者基本方針
・読んだら分かることは端折る。
・意味がわからない用語は調べてまとめる(記事最下段・用語集)。
・理解できない内容を掘り下げる。
・演習はすべて取り組む。
・コードコピペは極力しない。続いて第6章入りまーす。こっから第12章まで、ログインと認証システムの開発に取り掛かるそうな。長丁場ですがやりきってやりましょう。
本日の一曲はこちら。
17歳とベルリンの壁 "プリズム"
ここ数年あんまり音楽開拓してなかったけど、良い国産シューゲバンドが出てきるな〜。
【6.1.1 データベースの移行 演習】
1. Railsはdb/ディレクトリの中にあるschema.rbというファイルを使っています。これはデータベースの構造 (スキーマ (schema) と呼びます) を追跡するために使われます。さて、あなたの環境にあるdb/schema.rbの内容を調べ、その内容とマイグレーションファイル (リスト 6.2) の内容を比べてみてください。
→ 何が正解か分かんないけど、マイグレーションファイルの中身は反映されてますね。
2. ほぼすべてのマイグレーションは、元に戻すことが可能です (少なくとも本チュートリアルにおいてはすべてのマイグレーションを元に戻すことができます)。元に戻すことを「ロールバック (rollback)と呼び、Railsではdb:rollbackというコマンドで実現できます。$ rails db:rollback上のコマンドを実行後、db/schema.rbの内容を調べてみて、ロールバックが成功したかどうか確認してみてください (コラム 3.1ではマイグレーションに関する他のテクニックもまとめているので、参考にしてみてください)。上のコマンドでは、データベースからusersテーブルを削除するためにdrop_tableコマンドを内部で呼び出しています。これがうまくいくのは、drop_tableとcreate_tableがそれぞれ対応していることをchangeメソッドが知っているからです。この対応関係を知っているため、ロールバック用の逆方向のマイグレーションを簡単に実現することができるのです。なお、あるカラムを削除するような不可逆なマイグレーションの場合は、changeメソッドの代わりに、upとdownのメソッドを別々に定義する必要があります。詳細については、Railsガイドの「Active Record マイグレーション」を参照してください。
→ rails db:migrateを実行。スキーマの中身が消えてます。schema.rbActiveRecord::Schema.define(version: 0) do end3. もう一度rails db:migrateコマンドを実行し、db/schema.rbの内容が元に戻ったことを確認してください。
→ 戻った!
【6.1.2 modelファイル 演習】
1. Railsコンソールを開き、User.newでUserクラスのオブジェクトが生成されること、そしてそのオブジェクトがApplicationRecordを継承していることを確認してみてください (ヒント: 4.4.4で紹介したテクニックを使ってみてください)。
2. 同様にして、ApplicationRecordがActiveRecord::Baseを継承していることについて確認してみてください。
→ コンソール上で下記実行。>> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> >> user.class => User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime) >> user.class.superclass => ApplicationRecord(abstract) >> user.class.superclass.superclass => ActiveRecord::Base
【6.1.3 ユーザーオブジェクトを作成する 演習】
1. user.nameとuser.emailが、どちらもStringクラスのインスタンスであることを確認してみてください。
→ あいよ。>> user.name.class => String >> user.email.class => String
2. created_atとupdated_atは、どのクラスのインスタンスでしょうか?
→ 両方ともActiveSupport::TimeWithZoneクラス(この記事が参考になりそう)>> user.created_at.class => ActiveSupport::TimeWithZone >> user.updated_at.class => ActiveSupport::TimeWithZone
【6.1.4 ユーザーオブジェクトを検索する 演習】
1. nameを使ってユーザーオブジェクトを検索してみてください。また、 find_by_nameメソッドが使えることも確認してみてください (古いRailsアプリケーションでは、古いタイプのfind_byをよく見かけることでしょう)
→ 下記>> User.find_by(name: "shoji") User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "shoji"], ["LIMIT", 1]] => #<User id: 2, name: "shoji", email: "shoji@mail.com", created_at: "2020-09-08 22:54:09", updated_at: "2020-09-08 22:54:09"> >> User.find_by_name("shoji") User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "shoji"], ["LIMIT", 1]] => #<User id: 2, name: "shoji", email: "shoji@mail.com", created_at: "2020-09-08 22:54:09", updated_at: "2020-09-08 22:54:09">
2. 実用的な目的のため、User.allはまるで配列のように扱うことができますが、実際には配列ではありません。User.allで生成されるオブジェクトを調べ、ArrayクラスではなくUser::ActiveRecord_Relationクラスであることを確認してみてください。
→ 下記(ついでに上位クラスまで調べています)>> users = User.all User Load (0.2ms) SELECT "users".* FROM "users" LIMIT ? [["LIMIT", 11]] => #<ActiveRecord::Relation [#<User id: 1, name: "miura", email: "miura@mail.com", created_at: "2020-09-08 22:53:59", updated_at: "2020-09-08 22:53:59">, #<User id: 2, name: "shoji", email: "shoji@mail.com", created_at: "2020-09-08 22:54:09", updated_at: "2020-09-08 22:54:09">]> >> users.class => User::ActiveRecord_Relation >> users.class.superclass => ActiveRecord::Relation >> users.class.superclass.superclass => Object >> users.class.superclass.superclass.superclass => BasicObject >> users.class.superclass.superclass.superclass.superclass => nil
3. User.allに対してlengthメソッドを呼び出すと、その長さを求められることを確認してみてください (4.2.3)。Rubyの性質として、そのクラスを詳しく知らなくてもなんとなくオブジェクトをどう扱えば良いかわかる、という性質があります。これをダックタイピング (duck typing) と呼び、よく次のような格言で言い表されています「もしアヒルのような容姿で、アヒルのように鳴くのであれば、それはもうアヒルだろう」。(訳注: そういえばRubyKaigi 2016の基調講演で、Ruby作者のMatzがダックタイピングについて説明していました。2〜3分の短くて分かりやすい説明なので、ぜひ視聴してみてください!)
→ データ数が表示されました。>> User.all.length User Load (0.2ms) SELECT "users".* FROM "users" => 2
【6.1.5 ユーザーオブジェクトを更新する 演習】
1. userオブジェクトへの代入を使ってname属性を使って更新し、saveで保存してみてください。
→ 下記>> user1.name = "yongon" => "yongon" >> user1.save (0.1ms) SAVEPOINT active_record_1 SQL (0.6ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "yongon"], ["updated_at", "2020-09-08 23:12:52.428275"], ["id", 1]] (0.1ms) RELEASE SAVEPOINT active_record_1 => true
2. 今度はupdate_attributesを使って、email属性を更新および保存してみてください。
→ 下記(ミスっていろいろやり直してるのidがずれてます)>> user1.update_attributes(name: "yongon", email: "yongon@mail.com") (0.1ms) SAVEPOINT active_record_1 SQL (0.1ms) UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ? [["email", "yongon@mail.com"], ["updated_at", "2020-09-09 03:12:33.687572"], ["id", 4]] (0.1ms) RELEASE SAVEPOINT active_record_1 => true
3. 同様にして、マジックカラムであるcreated_atも直接更新できることを確認してみてください。ヒント: 更新するときは「1.year.ago」を使うと便利です。これはRails流の時間指定の1つで、現在の時刻から1年前の時間を算出してくれます。
→ 下記>> user1.update_attribute(:created_at, 1.year.ago) (0.1ms) SAVEPOINT active_record_1 SQL (1.1ms) UPDATE "users" SET "created_at" = ?, "updated_at" = ? WHERE "users"."id" = ? [["created_at", "2019-09-09 03:18:24.829284"], ["updated_at", "2020-09-09 03:18:24.830017"], ["id", 4]] (0.1ms) RELEASE SAVEPOINT active_record_1 => true
【6.2.1 有効性を検証する メモと演習】
setupメソッドを使うと、メソッド内に書かれた処理がテスト直前に実行される。この中でインスタンス変数を定義しておけば、すべてのテスト内で使えるようになる。
1. コンソールから、新しく生成したuserオブジェクトが有効 (valid) であることを確認してみましょう。
→ 下記>> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> >> user.valid? => true2. 6.1.3で生成したuserオブジェクトも有効であるかどうか、確認してみましょう。
→ 一回コンソール閉じてるから消えてるよ〜。どうせ有効なので割愛。
【6.2.2 存在性を検証する メモと演習】
assert_not @user.valid? がRED
→ 「@userは有効ちゃうよな?」と主張してるのに、「有効やんけ!」とツッコミが入ってる状態 と考えると分かりやすい。
validatesにも出てくるが、メソッドの最後の引数としてハッシュを渡す場合、{ }は省略可能1. 新しいユーザーuを作成し、作成した時点では有効ではない (invalid) ことを確認してください。なぜ有効ではないのでしょうか? エラーメッセージを確認してみましょう。
→ nameもemailも入力してないからヴァリデーションが働いてます。>> u = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> >> u.valid? => false >> u.errors.full_messages => ["Name can't be blank", "Email can't be blank"]
2. u.errors.messagesを実行すると、ハッシュ形式でエラーが取得できることを確認してください。emailに関するエラー情報だけを取得したい場合、どうやって取得すれば良いでしょうか?
→ このページに書いてました。.messagesつけてもつけなくても一緒ですね。>> u.errors.messages => {:name=>["can't be blank"], :email=>["can't be blank"]} >> u.errors[:email] => ["can't be blank"] >> u.errors.messages[:email] => ["can't be blank"]
【6.2.3 長さを検証する 演習】
1. 長すぎるnameとemail属性を持ったuserオブジェクトを生成し、有効でないことを確認してみましょう。
2. 長さに関するバリデーションが失敗した時、どんなエラーメッセージが生成されるでしょうか? 確認してみてください。
→ まとめて下記>> user = User.new(name: "a"*55, email: "e"*244 + "@example.com") => #<User id: nil, name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", email: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee...", created_at: nil, updated_at: nil> >> user.valid?=> false >> user.errors.full_messages=> ["Name is too long (maximum is 50 characters)", "Email is too long (maximum is 255 characters)"]
【6.2.4 フォーマットを検証する 演習】
正規表現は覚える必要あるでしょうか?ややこしいので、都度調べて実装する方がいいような。
1. リスト 6.18にある有効なメールアドレスのリストと、リスト 6.19にある無効なメールアドレスのリストをRubularのYour test string:に転記してみてください。その後、リスト 6.21の正規表現をYour regular expression:に転記して、有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。
→ たしかめるだけ。
2. 先ほど触れたように、リスト 6.21のメールアドレスチェックする正規表現は、foo@bar..comのようにドットが連続した無効なメールアドレスを許容してしまいます。まずは、このメールアドレスをリスト 6.19の無効なメールアドレスリストに追加し、これによってテストが失敗することを確認してください。次に、リスト 6.23で示した、少し複雑な正規表現を使ってこのテストがパスすることを確認してください。
→ これもやるだけー。
3. foo@bar..comをRubularのメールアドレスのリストに追加し、リスト 6.23の正規表現をRubularで使ってみてください。有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。
→ 確認しました。
【6.2.5 一意性を検証する メモと演習】
ここの内容ちょっとややこしいけど、要はメールアドレスに関して大文字・小文字を区別しないように設定してると。テストは大文字ユーザーが有効ででない(大文字でも同じアドレス)ことを確かめてるわけか。
そして、メールアドレスがデータベースに保存される前に、すべてを小文字にするためにコールバックメソッドが登場しました。軽く調べたところ、コールバックの利用は慎重にしないといけないようです。コールバックの中で条件分岐とか避けた方がいいみたい。1. リスト 6.33のように、メールアドレスを小文字にするテストをリスト 6.26に追加してみましょう。ちなみに追加するテストコードでは、データベースの値に合わせて更新するreloadメソッドと、値が一致しているかどうか確認するassert_equalメソッドを使っています。リスト 6.33のテストがうまく動いているか確認するためにも、before_saveの行をコメントアウトして redになることを、また、コメントアウトを解除すると greenになることを確認してみましょう。
→ 指示通り実行。before_saveで保存前に小文字に変換しているので、コメントアウトするとREDに、解除するとGREENになります。2. テストスイートの実行結果を確認しながら、before_saveコールバックをemail.downcase!に書き換えてみましょう。ヒント: メソッドの末尾に!を付け足すことにより、email属性を直接変更できるようになります (リスト 6.34)。
→ ここで出てくる「!」は、破壊的な処理を表します。つまり、メールアドレスが小文字変換されたままで維持されるということ。書き換えてもテストはGREENです。
【6.3.2 ユーザーがセキュアなパスワードを持つ 演習】
1. この時点では、userオブジェクトに有効な名前とメールアドレスを与えても、valid?で失敗してしまうことを確認してみてください。
2. なぜ失敗してしまうのでしょうか? エラーメッセージを確認してみてください。
→ まとめていくよ!パスワードが空白はダメだってよ。>> user = User.new(name: "kote", email: "kote@mail.com") => #<User id: nil, name: "kote", email: "kote@mail.com", created_at: nil, updated_at: nil, password_digest: nil> >> user.valid? User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "kote@mail.com"], ["LIMIT", 1]] => false >> user.errors.messages => {:password=>["can't be blank"]}
【6.3.3 パスワードの最小文字数 演習】
1. 有効な名前とメールアドレスでも、パスワードが短すぎるとuserオブジェクトが有効にならないことを確認してみましょう。
2. 上で失敗した時、どんなエラーメッセージになるでしょうか? 確認してみましょう。
→ 今回もまとめていくよ!もちろんパスワードが短すぎると怒られます。ここでhas_secure_passwordが働いて、パスワードがハッシュ化されているのが分かりますね。>> user = User.new(name: "kote", email: "kote@mail.com", password: "kotte") => #<User id: nil, name: "kote", email: "kote@mail.com", created_at: nil, updated_at: nil, password_digest: "$2a$10$7Svz/KnRoF7zab0PnhKFL.n/OsSltRvvREHECcmuq.D..."> >> user.valid? User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "kote@mail.com"], ["LIMIT", 1]] => false >> user.errors.messages=> {:password=>["is too short (minimum is 6 characters)"]}
【6.3.4 ユーザーの作成と認証 演習】
1. コンソールを一度再起動して (userオブジェクトを消去して)、このセクションで作ったuserオブジェクトを検索してみてください。
→ 下記>> user = User.find(1) User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-09-10 02:37:56", updated_at: "2020-09-10 02:37:56", password_digest: "$2a$10$A5n.HFBigQfwnWVJZw2N0e4M9sxPaR8ndLZwqtZWYS7...">
2. オブジェクトが検索できたら、名前を新しい文字列に置き換え、saveメソッドで更新してみてください。うまくいきませんね...、なぜうまくいかなかったのでしょうか?
→ 下記。saveメソッドだと全ての属性を更新しようとするから、パスワードの更新でエラー吐いてるみたい。>> user.name = "meshino" => "meshino" >> user.save (0.1ms) begin transaction User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ? [["email", "mhartl@example.com"], ["id", 1], ["LIMIT", 1]] (0.0ms) rollback transaction => false >> user.errors.messages => {:password=>["can't be blank", "is too short (minimum is 6 characters)"]}
3. 今度は6.1.5で紹介したテクニックを使って、userの名前を更新してみてください。
→ ということで、update_attributeを使って更新します。(ただし、さっきエラー吐いたuserを再利用しているので、name更新後も有効ではありません。パスワードの再設定が必要と思われます)>> user.update_attribute(:name, "nakamura") (0.1ms) begin transaction SQL (4.2ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "nakamura"], ["updated_at", "2020-09-10 03:07:11.190666"], ["id", 1]] (6.4ms) commit transaction => true >> user.valid? User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ? [["email", "mhartl@example.com"], ["id", 1], ["LIMIT", 1]] => false >> user.errors.messages=> {:password=>["can't be blank", "is too short (minimum is 6 characters)"]}
第6章まとめ
・データベースを更新する時はつどつどマイグレーションを作成してマイグレートしよう。
・Active Recordは便利。Railsは下火になってきてるけどActive Recordは利点とどっかで書かれてた。
・バリデーションで無効な入力内容を設定して弾こう。
・正規表現は多分そういうもんなんだと認識する程度でよいと思う。
・データベースにインデックス追加で検索効率向上。一意性も保証。
・has_secure_passwordはgemで利用しているわけだから、他にも便利なgemでセキュアな処理が実装できるんかな。と思って検索したらやっぱり出てきました。今後学んでいこう。
いろいろ気になることを寄り道して調べているので時間がかかります。でも絶対チュートリアルの内容だけでは通用しないと思うので、他の様々なことにも興味を持って吸収していきます。
さて次!第7章はユーザー登録の実装です!sign up!
⇦ 第5章はこちら
学習にあたっての前提・著者ステータスはこちら
なんとなくイメージを掴む用語集
・assert_not
notで否定してるので、まんま逆の意味。対象が真なら失敗、偽なら成功。・Active Record コールバック
何かの処理の前/後に呼び出すメソッドを設定できる。詳しくはRailsガイドへ。・スタブ
テスト時に用意する代用品。テスト対象の処理から呼び出される代用品がスタブ、テスト対象の処理を呼び出す代用品がドライバ。
- 投稿日:2020-09-10T11:17:16+09:00
Mysqlにデータベースを変更する
database.ymlの中身を以下のように書き換えてください。
config/database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: socket: /tmp/mysql.sock development: <<: *default database: amazon_app_mysql_development test: <<: *default database: amazon_app_mysql_test production: <<: *default database: amazon_app_mysql_production username: amazon_app_mysql password: <%= ENV['AMAZON_APP_MYSQL_DATABASE_PASSWORD'] %>sqliteをコメントアウトする
Gemfile#gem 'sqlite3', '~> 1.4'mysqlを追加する
Gemfilegem 'mysql2', '>= 0.4.4'mysql2を更新させ、反映させる
ターミナル$ bundle updateDBを初期化
$ rails db:migrate:reset
- 投稿日:2020-09-10T11:12:11+09:00
Rails Tutorial 第13章 演習13.4.1.2 paramsハッシュがなくなる
演習13.4.1.2 paramsハッシュがなくなる
ネットの他の方を見ると、params:ハッシュがなくなっている方が多かったです。一方テキストではなくなっていません。最近テキストが書き換えられたのかもしれません。
ネットの方の例
test/integration/microposts_interface_test.rbpost microposts_path, micropost: { content: content, picture: picture }テキスト
test/integration/microposts_interface_test.rbpost microposts_path, params: { micropost: { content: content, picture: FILL_IN } }type="input"とtype=inputのどちらでも動きました。
ページのソースを見たところ、"input"だったので""を入れました。ネットの他の方は"”ありとなしのどちらもありました。
test/integration/microposts_interface_test.rbtest "micropost interface" do assert_select 'input[type="file"]'演習13.3.5.1 「有効な送信」でエラーを起こさせたところ他のところでエラーになる
# if @micropost.save if true flash[:succsess] = "Micropost created!" redirect_to root_urlsaveをしないようにコメントアウトしたところ、その前の「無効な送信」のテストが
エラーになってしまいました。saveをしないので、error_explanationが出ないようです。FAIL["test_micropost_interface", MicropostsInterfaceTest, 0.9583898989999398] test_micropost_interface#MicropostsInterfaceTest (0.96s) Expected at least 1 element matching "div#error_explanation", found 0.. Expected 0 to be >= 1. test/integration/microposts_interface_test.rb:17:in `block in <class:MicropostsInterfaceTest>'
- 投稿日:2020-09-10T10:55:15+09:00
[Rails]エラーStandardError: An error has occurred, all later migrations canceled: Column `外部キー名` on table `テーブル名` does not match column `id` on `テーブル名`の対処方法
エラー内容
$ rails db:migrate上記コマンドを実行すると発生するエラー。ターミナルでのエラー表記は以下の通り。
エラー文一部抜粋.Column `user_id` on table `items` does not match column `id` on `users`, which has type `bigint(20)`. To resolve this issue, change the type of the `user_id` column on `items` to be :bigint. (For example `t.bigint :user_id`).エラー文の一部を翻訳してみると...。
エラー文翻訳.テーブル `items` のカラム `user_id` が `users` のカラム `id` と一致しません。この問題を解決するには、`items` の `user_id` カラムの型を :bigint に変更します。(例えば `t.bigint :user_id`)。今回はitemsテーブルが外部キーとして指定しているカラムが参照元と一致しませんよ!というエラーですね。
対処法(仮説)
結論、Railsでは外部キーを使用する際は
references型
を推奨しているので、bigint型
を使用する必要はありません。このエラーのポイントは参照できませんということなので、マイグレーションファイルの作成順に問題があると仮説できます。
マイグレーションファイルの作成順とは?
外部キーを使用するテーブル(references型を記述するテーブル)と参照されるテーブルには作成順によって参照できなくなる場合があり、今回のエラーは作成順序の誤りで発生しました。
作成順は、①参照される側のテーブル→②外部キーを使用するテーブル(references型を記述するテーブル)です。
対処法はマイグレーションファイルの作成日時を修正してあげれば解決できます。対処法(仮設検証)
添付画像の数字部分を、参照される側のテーブルよりも外部キーを使用するテーブルの数字を大きくすれば解決します。エラー分からは少し推測しづらいエラーですね。
今回の場合だと、
create_items
が20200909000000
なら20200909100000
でいいです。
ちなみに、最初の4桁は西暦、次の4桁は月日です。最後に
今回のエラーはテーブル数が増えると発生しやすいエラーなのかなと思います。
ただ、対象法を知っていれば問題なく解決できると思いますので、参考にしてみてください!
- 投稿日:2020-09-10T09:40:58+09:00
[Ransack] ransackable_scopesには気を付けろ
結論
ransackable_scopes
で実行するscope
に渡す引数が以下の値だったら
ArgumentError wrong number of arguments (given 0, expected 1)
が発生するから気を付けろ!!!
"true"
,"TRUE"
,"t"
,"T"
,1
,"1"
"false"
,"FALSE"
,"f"
,"F"
,0
,"0"
これらの値はそれぞれTrue, Falseに暗黙的に変換されるため、引数に渡せない。
解決策
config/initializers/ransack.rbRansack.configure do |config| config.sanitize_custom_scope_booleans = false endこれを追記するだけで、上述の値全てを渡せるようになります。1
渡せる値をカスタムしたいなら
諦めてください。
暗黙的に変換される値はRansack::Constants::BOOLEAN_VALUES
2 で定義されています。
この定数はfreeze
されています。
公式によるとfreeze
したオブジェクトをunfreezeする方法は無いそうです。3There is no way to unfreeze a frozen object.
変換される値のカスタマイズついてはかなり調べましたが、無理でした。。。
一応、「暗黙的に変換する値をカスタマイズできるようにしたよ!」って内容のPRがマージされていました。
これによると、以下のように変換する値をカスタマイズできるらしいです。Ransack.configure do |config| config.truthy_values_to_convert_in_custom_scopes = ['TRUE', 'true', '1'] config.falsey_values_to_convert_in_custom_scopes = ['FALSE', 'no way no how'] endしかし、それ通りに記述しても以下のようなエラーが発生しました。
=> NoMethodError: undefined method `truthy_values_to_convert_in_custom_scopes' for Ransack:Moduleファッ!?!?!?!?!?!?!?!?!?!?!?!?
PRの実装内容をを確認しましたが、概要で書かれているような内容は実装されていませんでした。。。
誰かここ分かる方がいれば教えてください。。。また、@t_oginoginさんがRansackを使わない解決策を提案してくれているので、皆さんの実装状況に合わせてご参考下さい。
https://qiita.com/t_oginogin/items/b45636d64c271ebc409c参考文献
sanitize_custom_scope_booleansについて(Ransack公式)
https://github.com/activerecord-hackery/ransack#using-scopesclass-methods ↩BOOLEAN_VALUESの中身(Ransack公式)
https://github.com/activerecord-hackery/ransack/blob/c9cc20de9e0f7bab92e0579c85bed64d614d23de/lib/ransack/constants.rb#L26 ↩unfreezeできない(Ruby公式)
https://ruby-doc.org/core-2.6.6/Object.html#method-i-freeze ↩
- 投稿日:2020-09-10T09:24:41+09:00
rails tutorial 第8章
はじめに
独学でrails tutorialを進めていく過程を投稿していきます。
進めていく上でわからなかった単語、詰まったエラーなどに触れています。
個人の学習のアウトプットなので間違いなどあればご指摘ください。
初めての投稿なので読みにくいところも多々あるかと思いますがご容赦ください。
第8章 基本的なログイン機構
8.1.2 ログインフォーム
ログインフォームを作成時のscopeの働きについてわからなかったので調べました。
form_with(url: login_path, scope: :session, local: true)参考
https://qiita.com/akilax/items/f36b13f377f7e442bc73あまり深く考え過ぎずにパラメーターを渡す際に必要なname値のプレフィックスと考えるのが良さそうです。
ざっくりと自分なりにまとめると、Active Recodeを継承しているオブジェクトのフォームでは
<%= form_with(model: @user, local: true) do |f| %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> . . <% end %>とし、入力された値(生成されたinputタグのname値) へのアクセスは
params[:user][:name]となり、入力結果をuserハッシュに保存していました。
今回のsessionフォームにおいても同様で、パラメーターに入力結果の値を渡すために、scopeで指定したハッシュに入力結果を保存しているということだと思います。
8.1.4 フラッシュメッセージを表示する
ログイン失敗時にエラーメッセージを表示します。
app/controllers/sessions_controller.rb#リスト 8.8: ログイン失敗時の処理を扱う(誤りあり) class SessionsController < ApplicationController def new end def create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) # ユーザーログイン後にユーザー情報のページにリダイレクトする else flash[:danger] = 'Invalid email/password combination' # 本当は正しくない render 'new' end end def destroy end end実は上のコードのままでは、リクエストのフラッシュメッセージが一度表示されると消えずに残ってしまいます。リスト 7.27でリダイレクトを使ったときとは異なり、表示したテンプレートをrenderメソッドで強制的に再レンダリングしてもリクエストと見なされないため、リクエストのメッセージが消えません。例えばわざと無効な情報を入力して送信してエラーメッセージを表示してから、Homeページをクリックして移動すると、そこでもフラッシュメッセージが表示されたままになっています。(rails tutorial 8章より引用)
ここで、なぜリクエストと見なされなければメッセージが消えないのか、また、エラーメッセージ表示後にhomeページをクリックして移動する際にGETリクエストをしているから消えてもよいのでは?という疑問が生まれました。
この疑問を解消するため、まずは改めてrenderとredirect_toの違いについて調べてみた。
参考
https://qiita.com/january108/items/54143581ab1f03deefa1
この記事を読んでもやはりhomeページをクリックする時にリクエストしてるよな、、、という疑問が残りました。
その後も色々調べてようやく回答にたどり着きました。どうやらflashについての理解が足りなかったようです。参考
https://pikawaka.com/rails/flashつまりflashメッセージが表示されてから、リクエストを受け、アクションが実行されてからフラッシュメッセージが消去されるようです。
だからrenderメソッドでviewが表示された後、homeリクエストをしてもメッセージが残っているようでした。
試しに
1.無効な情報を入力して送信
2.エラーメッセージを確認
3.homeページに移動
4.homeページでもエラーメッセージ消えていないことを確認
5.再度homeページをクリック
とするとエラーメッセージは消えていました。凄いすっきりしました!!
8.2 ログイン
Railsのセッション用ヘルパーはビューにも自動的に読み込まれます。Railsの全コントローラの親クラスであるApplicationコントローラにこのモジュールを読み込ませれば、どのコントローラでも使えるようになります 。(rails tutorial 8章より引用)
注意
viewには自動で読み込まれますが、コントローラーなどで、ヘルパーに設定したメソッドなどを利用する場合はincludeで読み込む必要があります。8.2.2 現在のユーザー
このトピックではわかりにくいところがあり、初めてQiitaで質問を利用させていただきました。
内容を深堀したとき、なぜfindメソッドではなくfind_byメソッドを利用すべきだったのか、わかりませんでしたので質問をしました。
質問内容
https://qiita.com/shun_study_p/questions/da3de50fe7826dc151ed
回答をしていただいた方、ありがとうございます。
@current_user ||= User.find_by(id: session[:user_id])Userオブジェクトそのものの論理値は常にtrueになることです。そのおかげで、@current_userに何も代入されていないときだけfind_by呼び出しが実行され、無駄なデータベースへの読み出しが行われなくなります。(rails tutorial 8章より引用)
ここの文についても自分なりに補足
@current_user ||= User.find_by(id: session[:user_id])この一文は@current_userがnilの時はfind_byメソッドを実行しUserオブジェクトを作成するというもの。(@current_userにUserオブジェクトが代入される)
そうなった後は@current_userはUserオブジェクトとなり、またUserオブジェクトはtrueを返すので、以降は左辺がtrueとなり、無駄なfind_byメソッドは実行されず(@current_userがnilではないから)無駄なデータの呼び出しも行われなくなるということ。8.2.3 レイアウトリンクを変更する
<%= link_to "Profile", current_user %>復習も兼ねてこちらの一文に置いて何が行われているかというと
<%= link_to "Profile", "/users/#{current_user.id}" %> <%= link_to "Profile", user_path(current_user.id) %> <%= link_to "Profile", user_path(current_user) %> <%= link_to "Profile", current_user %>と省略されて行っています。
ここでまた一つの疑問が、
link_toの引数でモデルオブジェクトを渡すのはわかります。でも今回渡してるのってメソッドじゃないの、、、?
こちらに関しては以下の記事を参考にしました。
参考記事1(7章でも参考にしました)
https://qiita.com/Kawanji01/items/96fff507ed2f75403ecb参考記事2
https://teratail.com/questions/198096
どうやら戻り値にモデルのインスタンスを返すならそのメソッドはモデルオブジェクトとみなせるようですね。8.2.4 レイアウトの変更をテストする
def User.digest(string) cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost BCrypt::Password.create(string, cost: cost) endこのdigestメソッドは、今後様々な場面で活用します。例えば9.1.1でもdigestを再利用するので、このdigestメソッドはUserモデル(user.rb)に置いておきましょう。この計算はユーザーごとに行う必要はないので、fixtureファイルなどでわざわざユーザーオブジェクトにアクセスする必然性はありません(つまり、インスタンスメソッドで定義する必要はありません)。(rails tutorial 8章より引用)
インスタンスメソッドとクラスメソッドの違いをあまり理解出来ていなかったのか、上の文の意味がよくわからない、、、
上の本文は
1.インスタンスメソッドで定義するとわざわざユーザーオブジェクトにアクセスする必要がある。
2.今回の"渡された文字列のハッシュを返すというdigestメソッド"はユーザーごとのインスタンスで行う必要はない。(クラスオブジェクトから直接実行すればよいもの)と言っている。なんとなくだがこの2つが言いたいことはわかるような、、、
まだふんわりとした理解ですが、記事を参考に自分でまとめてみました。
参考
https://qiita.com/tbpgr/items/56eb65c0ea5882abbb07つまり
クラスメソッドは○○クラス自身に関する情報の変更や参照の役割をもっている。なので今回のようなパスワードのハッシュ化、記事の例にあるように男女といった性別の属性、これらはクラスメソッドであらかじめ定義しておくことができる。
インスタンスメソッドは、個別のインスタンスに関する情報の変更や参照の役割りを持っている。
なので特定のデータのパスワードや名前と言った情報の参照をしたいなどといったときはインスタンスメソッドを使う。(ユーザーオブジェクトにアクセスする必要があることもイメージしやすい)
こんなところだろうか、、、難しい、、
終わりに
今回の章も難しく、7割程度の理解で進んでしまった部分もあるので、また復習が必要だと思いました。
- 投稿日:2020-09-10T08:36:46+09:00
[Rails]1対1対多の場合のdelegateとhas_many-throughの挙動の違い
下記のように1対1対多の関係のモデルがあるとします。
- UserとExamineeは1対1
- ExamineeとTestは1対多
class User has_one :examinee end class Examinee belongs_to :user has_many :tests end class Test belongs_to :examinee endでは、Userモデルから関連するTestモデルを取得したいときはどのように実装しますか?
様々なやり方がありますが、ActiveRecordの便利機能
delegate
を使うか、has_many-through
を使うことが多いのではないでしょうか?
どちらもやりたいことは達成できますが、発行されるクエリが少し違うので紹介します。delegate
delegateを使うとメソッドを別クラスに委譲することが出来ます。
詳細はRailsガイドを参照してください。
3.4.1 delegate今回の場合、下記のように実装します。
app/models/user.rbdelegate :tests, to: :examinee実行すると下記の通り2つのクエリーが発行されます。
まず委譲先のexamineeを取得(1つ目のクエリー)して、その後、examinee.testsを実行(2つ目のクエリー)する挙動になっています。irb> user.tests Examinee Load SELECT `examinees`.* FROM `examinees` WHERE `examinees`.`user_id` = 1 LIMIT 1 Test Load SELECT `tests`.* FROM `tests` WHERE `tests`.`examinee_id` = 1has_many-through
has_many-throughは多対多の時に使われることが多いですが、今回のように1対多の場合も利用できます。
詳細はRailsガイドを参照してください。
2.4 has_many :through 関連付け今回の場合、下記のように実装します。
app/models/user.rbhas_many :tests, through: :examinee実行すると下記の通り1つのクエリーが発行されます。
こちらの場合はjoinしたクエリーが1つだけ発行されます。
この機能が多対多に対応するように実装されていると考えると、deletgateのように2段階では効率よく取得できないのでjoinで取得しているんだなと理解できると思います。irb> user.tests Test Load SELECT `tests`.* FROM `tests` INNER JOIN `examinees` ON `tests`.`examinee_id` = `examinees`.`id` WHERE `examinees`.`user_id` = 1最後に
2クエリーで取得するほうが良いのか、joinされた1クエリーで取得するほうが良いのかは実行環境によるので一概に良し悪しは判断出来ません。
というか大抵の場合はどちらで書いても問題なく動作するのでぶっちゃけどちらでもよいと思います。ただ、ブラックボックス的に見ると同じことをしているように見えても、今回のように内部で発行されるクエリーが違ったりします。
たまにはこういう細かな違いを機能の成り立ちや目的などを考えならが確認してみると面白いと思います。
- 投稿日:2020-09-10T08:16:55+09:00
Rails 6で認証認可入り掲示板APIを構築する #5 controller, routes実装
←Rails 6で認証認可入り掲示板APIを構築する #4 postのバリデーション、テスト実装
controllerを作る
前回はmodelを作ったので、今回はcontrollerを実装していきます。
$ rails g controller v1/posts実行するとcontrollerとrequest specファイルが生成されます。
とりあえずcontrollerを以下まで実装します。
app/controllers/v1/posts_controller.rb# frozen_string_literal: true module V1 # # post controller # class PostsController < ApplicationController before_action :set_post, only: %i[show update destroy] def index # TODO end def show # TODO end def create # TODO end def update # TODO end def destroy # TODO end private def set_post @post = Post.find(params[:id]) end def post_params params.permit(:subject, :body) end end end
- index: post一覧を取得する
- show: post1レコードの情報を取得する(R)
- create: post1レコードを作成する(C)
- update:post1レコードを更新する(U)
- destroy: post1レコードを削除する(D)
CRUDに沿ったcontrollerを、一旦ロジック無しで作ります。
なお、V1というnamespaceを切っているのはAPI開発ではよくやる手法です。
これにより後方互換の無いversion2を作る際、分離して開発がしやすくなります。続いてroutesを設定します。
config/routes.rb# frozen_string_literal: true Rails.application.routes.draw do + namespace "v1" do + resources :posts + end end
これでCRUDのroutesが設定されます。確認してみましょう。
$ rails routes ... Prefix Verb URI Pattern Controller#Action v1_posts GET /v1/posts(.:format) v1/posts#index POST /v1/posts(.:format) v1/posts#create v1_post GET /v1/posts/:id(.:format) v1/posts#show PATCH /v1/posts/:id(.:format) v1/posts#update PUT /v1/posts/:id(.:format) v1/posts#update DELETE /v1/posts/:id(.:format) v1/posts#destroy ...indexテストの実装
例によってテストを先に実装します。
挙動としては
- 登録されたpostを返す
- created_at降順でソート
- 20件でlimit
でいきます。
簡易的なテストアプリケーションのチュートリアルのためpagerは組み込みませんが、もしかしたら今後記事を書くかもしれません。spec/requests/v1/posts_controller.rb# frozen_string_literal: true require "rails_helper" RSpec.describe "V1::Posts", type: :request do describe "GET /v1/posts#index" do before do create_list(:post, 3) end it "正常レスポンスコードが返ってくる" do get v1_posts_url expect(response.status).to eq 200 end it "件数が正しく返ってくる" do get v1_posts_url json = JSON.parse(response.body) expect(json["posts"].length).to eq(3) end it "id降順にレスポンスが返ってくる" do get v1_posts_url json = JSON.parse(response.body) first_id = json["posts"][0]["id"] expect(json["posts"][1]["id"]).to eq(first_id - 1) expect(json["posts"][2]["id"]).to eq(first_id - 2) end end end
- beforeは、同ブロック以下itで毎回事前に実行されます。
create_list(:post, 3)
はpostを3レコード生成しDBに保存する処理です。- また、itブロック終了時にテストDBはrollbackされます。
つまり挙動をまとめると、
LINE 10:it "正常レスポンスコードが返ってくる"ブロック開始
LINE 8:3件分のpostが保存される
LINE 11:v1_posts_url(v1/posts/index)にgetリクエストを行う
LINE 12:レスポンスコードが:ok(200 正常)
LINE 13:it "正常レスポンスコードが返ってくる"ブロック終了。rollbackされpostレコードは0件に
LINE 14:it "件数が正しく返ってくる"ブロック開始
LINE 8:3件分のpostが保存される
LINE 15:v1_posts_url(v1/posts/index)にgetリクエストを行う
LINE 16:response.bodyをJSON.parseしてRubyの配列に変換
LINE 17:レスポンスのpostが3レコード
LINE 18:it "件数が正しく返ってくる"ブロック終了。rollbackされpostレコードは0件に
↓
...となります。
この時点ではcontroller未実装なので、当然テストはコケます。
なお、最後のテストは厳密にはcreated_atの比較が必要ですが、簡易的にidで比較をしています。
本来はlimitのテストもすべきですが省略します。興味があれば、create_listで21件作って20件しか返ってこないことを確認するテストを実装してみてください。Tips.
ついでによく使うfactoryBotのメソッドを紹介しておきます。
build(:post)
postを1レコード、メモリ上に生成。saveしない限りDBには反映されない。Post.newに相当create(:post)
postを1レコード生成しDBに保存。Post.create!に相当build_list(:post, 5)
postを5レコード生成。buildの複数版create_list(:post, 5)
postを5レコード生成。createの複数版example.comをhostsに追加
なお、requestsテストは以下対応が必要です。
config/application.rb... config.hosts << ".amazonaws.com" + config.hosts << "www.example.com" ...なぜならrspecのテストはwww.example.comからのリクエストとして認識されるためです。
indexの実装
app/controllers/v1/posts_controller.rb... def index - # TODO + posts = Post.order(created_at: :desc).limit(20) + render json: { posts: posts } end ...これで一覧取得ができます。
試しにcurlでAPIを叩いてみます。$ curl localhost:8080/v1/posts {"posts":[{"id":2,"subject":"","body":"hoge","created_at":"2020-09-06T01:07:52.628Z","updated_at":"2020-09-06T01:07:52.628Z"},{"id":1,"subject":"hoge","body":"fuga","created_at":"2020-09-05T13:50:01.797Z","updated_at":"2020-09-05T13:50:01.797Z"}]}もし空のdataが返ってきた場合は、
rails c
からpostのレコードを生成してみてください。ここまでできたら、rubocopやrspec実行を忘れずに行った後、git commitしましょう。
続き
→Rails 6で認証認可入り掲示板APIを構築する #6 show, create実装
【連載目次へ】
- 投稿日:2020-09-10T07:24:01+09:00
Railsのcsvダウンロードで直面する数々の問題を解決したらgemができた 〜csb gemの紹介〜
はじめに: csvダウンロードの処理をちゃんと書くのって意外と面倒じゃないですか?
例えば「rails csv ダウンロード」で検索すると以下のようなサンプルコードがよく出てきます。
posts_controller.rbclass PostsController < ApplicationController def index @posts = Post.all end endindex.csv.rubyrequire 'csv' CSV.generate do |csv| column_names = %w[投稿日 カテゴリ タイトル 本文] csv << column_names @posts.each do |post| column_values = [ l(post.created_at.to_date), post.category.name, post.title, post.content, ] csv << column_values end endしかしこのサンプルコードでは次のような問題が解決出来ていません。
- Excelで開くと文字化けする
- レコード件数が大量にあった場合にメモリエラーやタイムアウトエラーが発生する可能性がある
- 列名と値の定義が離れているために、カラム数が増えてくると可読性が悪く保守性が低くなりがち
- CSV出力の条件が複雑化した場合にテストが書きにくい(system testで頑張るしかない)
この問題を解決するために、csbというgemを作りました。
このgemは弊社ソニックガーデンの複数のプロジェクトで1年以上、本番利用されてきた実績があります。今回の記事ではこのgemの概要と使い方を紹介します。
何が出来るの?
- BOM付きUTF-8で出力することで、Excelでも文字化けせずに開ける
- 数十万件以上といった大量データの場合でも、ストリーミングダウンロードにすることでメモリエラーやタイムアウトエラーといったよく起こりがちなトラブルを防げる
- 可読性高くメンテナブルにエクスポート用の処理を書ける
- エクスポート用の処理を切り出しやすくなりテスタビリティが上がる
使い方
インストール
Gemfilegem 'csb'$ bundle install基本
基本的には以下のようにコントローラでレコードをロードして、ビューにcsvの定義を書くといった流れになります。
これだけで自動的にストリーミングダウンロードとなります。ダウンロード元のビュー
index.html.haml= link_to 'CSVダウンロード', posts_path(format: :csv)CSV出力用コントローラ
posts_controller.rbdef index @posts = Post.preload(:category) endCSV出力用コビュー
index.csv.csb# csv.itemsにレコードを入れます(each可能であればActiveRecord::Relation以外のオブジェクトを入れることも出来ます) csv.items = @posts # 以下で各カラムを定義しています csv.cols.add('投稿日') { |post| l(post.created_at.to_date) } # ブロックの場合引数にはレコードが渡ってきます csv.cols.add('カテゴリー') { |post| post.category.name } csv.cols.add('タイトル', :title) # 単純なメソッド呼び出しだけの場合はシンボルで書けます csv.cols.add('本文', :content)応用
大量データの場合
ただし大量データの場合、上記の書き方だと全レコードのロードが完了してからストリーミング開始となってしまうため、メモリエラーやタイムアウトエラーが発生しやすくなりストリーミングのメリットを活かせません。
大量データの場合は以下のようにcsv.items = @posts.find_each
と書いて、csv.items
にEnumerator
を渡すことでストリーミングのメリットを最大限に活かすことが可能となります。index.csv.csbcsv.items = @posts.find_each csv.cols.add('投稿日') { |post| l(post.created_at.to_date) } csv.cols.add('カテゴリー') { |post| post.category.name } csv.cols.add('タイトル', :title) csv.cols.add('本文', :content)大量データかつ
Decorator
と組み合わせる場合大量データかつ、CSV出力用にデータを加工したいけど繰り返し同じ処理を書きたくないといったケースでは、専用の
Decoratorクラス
を用意することで以下のように書けます。(draperの例)index.csv.csbcsv.items = @posts.find_each.lazy.map(&:decorate) csv.cols.add('投稿日') { |post| l(post.created_at.to_date) } csv.cols.add('カテゴリー') { |post| post.category.name } csv.cols.add('タイトル', :decorated_title) csv.cols.add('本文', :content)特殊な出力
また外部システムとのcsv連携で時々発生する、以下のようなケースにも対応しています。
- 固定文字列を出力したい
- 中身は空でいいけど列は必須
- 同名のカラムが複数必要
index.csv.csbcsv.items = @posts csv.cols.add('固定文字列', 'Dummy') # 第二引数をシンボルではなく文字列にするとそのまま文字列として出力されます。 csv.cols.add('空文字列') # 第二引数を省略すると空文字となります。`csv.cols.add('空文字列', '')` と書くのと同じ。 csv.cols.add('複数カラム', :col1) # 同名カラムを複数定義することも可能です。(定義順に出力されます) csv.cols.add('複数カラム', :col2)Excelで文字化けせずに開きたい
以下のように出力文字コードをBOM付きUTF-8に設定することで、最近のExcelであればWindowsでもMacでも文字化けせずに開けるようになります。
config/initializers/csb.rbCsb.configure do |config| config.utf8_bom = true # default: false endダウンロードするcsvファイル名を設定したい
古いブラウザの考慮が不要で、必ずリンク経由でのダウンロードであればaタグのdownload属性を利用するのがお手軽ですが(参考リンク)、view側でも以下のように指定可能となっています。(Rails6以降であれば日本語も問題ないはずです)
index.csv.csbcsv.items = @posts csv.filename = "posts_#{Time.current.to_i}.csv" # ...テスト
複雑な条件によるcsv出力の場合は当然テストも必要となりますが、system testで条件別に検証というのは面倒ですよね。
そんな場合はcsv出力の定義だけをモデル等に書くことで、テスタビリティを上げることが出来ます。post.rbdef self.csb_cols Csb::Cols.new do |cols| cols.add('タイトル', :title) cols.add('本文', :content) cols.add('画像') { |post| post.image&.url } end endindex.csv.csbcsv.items = @posts csv.cols = Post.csb_colspost_spec.rb# requireすることでcol_pairsメソッドが追加で定義されます require 'csb/testing' context '画像が添付されている場合' do let(:image) { build(:image, url: 'https://example.test/example.jpg') } let(:post) { build(:post, title: 'Testing', content: 'hogehoge', image: image) } it '画像カラムにURLが出力されること' do expect(Post.csb_cols.col_pairs(post)).to eq [ ['タイトル', 'Testing'], ['本文', 'hogehoge'], ['画像', 'https://example.test/example.jpg'], ] end end context '画像が添付されていない場合' do let(:post) { build(:post, title: 'Testing', content: 'hogehoge', image: nil) } it '画像カラムが空となること' do expect(Post.csb_cols.col_pairs(post)).to eq [ ['タイトル', 'Testing'], ['本文', 'hogehoge'], ['画像', ''], ] end end※今後のバージョンアップでcsv全体での検証も書きやすくする予定です。
その他
オプション例
config/initializers/csb.rbCsb.configure do |config| # デフォルトはfalseとなっているので注意してください。 config.utf8_bom = true # default: false # 基本はデフォルトのままtrueでいいかと思いますが、大量データ配信が一切不要の場合はfalseとするほうがviewで発生するエラーに気付きやすいです。 config.streaming = false # default: true # 何も設定しない場合、Bugsnag等のエラー検知ツールを入れていたとしてもストリーミング配信中のエラーは通知されないので注意が必要です。 config.after_streaming_error = ->(error) do # default: nil Rails.logger.error(error) Bugsnag.notify(error) end endgemの名前の由来
csv builderの略です。
gemの名前とviewの拡張子を揃えようとすると短い名前の方が嬉しいので、語呂が良くて(日本語での)発音も似ているcsb
となりました。リポジトリ
https://github.com/aki77/csb
良かったら使ってみてください。
- 投稿日:2020-09-10T01:35:30+09:00
rails newする際のオプションとrails newした後に行う設定
個人的に「
rails new
をする際によく使うな」と感じるオプションと、新規プロジェクトを作成したときによく行う設定を備忘録を兼ねてまとめます。rails newをする際のオプション
まずは
rails new
をする際によく付けるオプションについてです。
一つ一つのオプションについては後述します。rails _6.0.3.2_ new appname --database=mysql --skip-testrailsのバージョン指定
rails _6.0.3.2_ newこうするとrailsのバージョンを指定することができます。
_6.0.3.2_
の部分はその都度値を変えてください。使用するデータベースの指定
--database=mysqlこのようにDBを指定しないと、デフォルトのDBは
sqlite
というものになります。
今回はmysql
を使用する設定です。Minitestを生成しない
--skip-testデフォルトでプロジェクトを生成すると、Minitestというものが作られます。
私自身、テストにはRSpecを使用することが多いため、上記のようにMinitestを生成しないようにしています。rails newした後に行う設定
ここからは実際に
rails new
をした後の設定となります。
rails g
コマンド使用時に、不要ファイルを生成しないように設定config/application.rbmodule appname class Application < Rails::Application # 以下を追加 config.generators do |g| g.stylesheets false g.javascripts false g.helper false g.test_framework false end config.time_zone = "Tokyo" config.i18n.default_locale = :ja end end今回主に追加したのは下記の部分です。
config.generators do |g| g.stylesheets false g.javascripts false g.helper false g.test_framework false end
rails g
コマンドでコントローラを作成すると、ファイルが自動的に生成されてしまいますが、不要なもの(coffeeやcss)を生成しないようにするのがこの記述です。
rails g
コマンドは開発の過程で頻繁に使用するものなので、この設定を最初に済ませておくと非常に楽になります。config.time_zone = "Tokyo" config.i18n.default_locale = :jaまた、こちらでデフォルトの言語を日本語にし、タイムゾーンのデフォルトを東京に設定します。