- 投稿日:2019-06-25T22:13:47+09:00
Railsチュートリアル完走後に、改めてリソースについてまとめてみた
背景
- Railsチュートリアル完走後に、どのようにして機能を拡張していくか考えたときになんとなく「機能をリソース」として扱えれば、設計がしやすいのでは?というのが発端。また、リソースについてもしっかり理解できていなかったので完走したあとに再度情報整理したかった。
目的
- 「Railsにおけるリソース」について、構成する要素の関係性についてまとめることで理解を深めてオペレーション時にもすぐに活かせる状態にしたい。
RESTの原則
- RailsはRESTの原則に従って設計されている。
- RESTとは、ざっくりいうとwebを設計するための考え方のひとつ。
- Railsでのリソースを理解するためには、土台であるRESTの原則を知る必要がある。
4つの原則
- ステートレスなクライアント/サーバプロトコル
- すべての情報(リソース)に適用できる「よく定義された操作」のセット
- リソースを一意に識別する「汎用的な構文」
- アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディアの使用」
引用元:Representational State Transfer - Wikipedia
1. ステートレスなクライアント/サーバプロトコル
RESTな考え方では、サーバーへのリクエストも、クライアントへのレスポンスも毎回状態を管理しない(ステートレス)HTTPを使う。
メリットとしては、複数のサーバーで複数のクライアントに対してレスポンスする際に効率がよい。
web設計する際に1つ目の原則に関しては、基本的にHTTPを使っていれば意識しなくて遵守される。
HTTP(HTTP/1.1)には、8つのメソッドが定義されており、2つ目の原則と関連してくる。
- GET
- POST
- PUT
- HEAD
- DELETE
- OPTIONS
- TRACE
- CONNECT
引用元:Hypertext Transfer Protocol - Wikipedia
2. すべての情報(リソース)に適用できる「よく定義された操作」のセット
RESTな考え方では、全てのリソースにHTTPメソッド(GET、POST、PUT、DELETEなど)を使ったアクセス可能な共通インターフェイスを持つ。
HTTPメソッドは次の役割を持っており、この役割に当てはめて設計を考える。
HTTPメソッド 役割 GET リソースの取得 POST 子リソースの作成、リソースへのデータ追加、その他の処理 PUT リソースの更新、リソースの作成 DELETE リソースの削除 HEAD リソースのヘッダ(メタデータ)の取得 OPTIONS リソースがサポートしているメソッドの取得 TRACE 自分宛にリクエストメッセージを返す(ループバック)試験 CONNECT プロキシ動作のトンネル接続への変更 3. リソースを一意に識別する「汎用的な構文」
RESTな考え方では、全てのリソースはURI(広い概念でURLとURNが含まれる)で表されるユニークなアドレス持つ
4. アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディアの使用」
今ではHTMLページに他のページへのリンクを貼ることは当たり前だが、そのこと。
これも、意識してなくても基本的に遵守される。RESTfulとは
4つの原則に従って作成されたものを指す。
RailsでRESTfulなリソースを作成するために意識すること
Railsにおいて、RESTの原則の1と4に関しては、現在であれば当たり前なのであまり意識せずともよいが、2と3に関しては意識する必要がある。
2. すべての情報(リソース)に適用できる「よく定義された操作」のセット
Railsにおいて、2に関しては、具体的にRailsチュートリアル内のコラム 2.2で言及されており、
Railsのリソースは、下記に対応する共通インターフェイスを持っているといえる。
- リレーショナルデータベースの作成/取得/更新/削除 (Create/Read/Update/Delete: CRUD) 操作
- 4つの基本的なHTTPメソッド (POST/GET/PATCH/DELETE)
Railsは、RESTの原則を取り入れいち早く対応しているフレームワークでもあり、共通インターフェイスを持たせるために、HTTPメソッドごとにデータベース上のCRUD操作と対応付けを定義している。
Railsのルーティング機能は、RESTの考え方を取り入れる上で、重要。
ルーティングでHTTPメソッドとアクションを紐づけ、webでのCRUD操作を実現する。下記は、photosリソースの例(CRUD操作の列は、HTTPメソッドとデータベースの繋がりが分かりやすいように追加)。
HTTP動詞 パス コントローラ#アクション 目的 CRUD操作 GET /photos photos#index すべての写真の一覧を表示 取得(R) GET /photos/new photos#new 写真を1つ作成するためのHTMLフォームを返す 取得(R) POST /photos photos#create 写真を1つ作成する 作成(C) GET /photos/:id photos#show 特定の写真を表示する 取得(R) GET /photos/:id/edit photos#edit 写真編集用のHTMLフォームを1つ返す 削除(D) PATCH/PUT /photos/:id photos#update 特定の写真を更新する 更新(U) DELETE /photos/:id photos#destroy 特定の写真を削除する 削除(D) 引用元:Rails のルーティング - Rails ガイドの2.2の表参照
HTTPメソッドとURL(パス)を組み合わせて、CRUD操作を実現していることがわかる。
Railsの良いところは、
resources :photos
を使えば、RESTの考え方に沿った共通インターフェイスができる点。3. リソースを一意に識別する「汎用的な構文」
RESTの原則の3に関しては、Railsチュートリアルの7章の7.1.2 Usersリソースで少し触れられています。
RESTの原則に従う場合、リソースへの参照はリソース名とユニークなIDを使うのが普通です。
他にチュートリアルで良い例が見つけられなかったが下記が参考になった。
- URLに動詞を含めず、複数形の名詞のみで構成する
リソースを2.2.2 MVCの挙動のMVCモデルの図と使って説明
これらを踏まえた上で、自分のリソースのイメージは、こんな感じです。
図でいうと、ユーザーから見たインターフェイスは/usersになる。
/usersのページのリクエスト(HTTPメソッド)を元にCRUD操作と結びつける(ルーターとコントローラの役割。モデルも密接に関わっている)。CRUD操作をするためには、データモデルが必要になり、最終的にユーザーから見える/usersに提供するためにビューが必要になる。
これらが組み合わさり、ユーザーにCRUD操作を提供するオブジェクトがリソース。これがリソースの正体。
Railsチュートリアルで出てきたリソースを比較してみる。
一番やりかったのがこれ。
下記ページのP25をみたときに全体像が分かっていれば、少しリソースの設計がわかりやすくと思ったため。
Rest ful api設計入門3章以降で出てくるリソースをRailsのCRUD操作に当てはめてみた(自分が分かりやすいように少し調整)。
1~3行目までは、すべてのリソースに共通する項目で、4行目は、途中で説明したphotosリソースを例として使っている。5行目以降がRailsチュートリアルで出てくるリソース。
すべてのアクションを使っていないリソースがあったり、用途に合わせてパスを調整しているリソースもあったりすることがわかる。
目的 一覧表示 作成フォーム 作成 個別表示 編集フォーム 個別更新 個別削除 HTTPリクエスト GET GET POST GET GET PATCH/PUT DELETE 対応アクション index new create show edit update destroy photos /photos /photos/new /photos /photos/:id /photos/:id/edit /photos/:id /photos/:id users /users /signup /signup /users/:id
/users/:id/following
/users/:id/followers/users/:id/edit /users/:id /users/:id sessions - /login /login - - - /logout account_activations - - - - account_activations#edit - - password_resets - /password_resets/new /password_resets - /password_resets/:id/edit /password_resets/:id - microposts - - /microposts - - - /microposts/:id relationships - - /relationships - - - /relationships/:id まとめ
最後は、良くわからない表になってしまったがなんとなくでしか分からなかったリソースの全体像がわかったと思う。
なにより必要に応じて、分かりやすいように名前付きルートの変更(users/newをsignup)をしている点や全てのアクションを使わなくてもリソースの考え方に則って実装することで、リソースとして扱うという実装方法の統一が図れるという点、14章で出てくるフォローの実装はさらにRESTのリソースをより活用した実装(ルーティングをネストさせている)という点に気づけた。
参考
Representational State Transfer - Wikipedia
ステートフル ステートレスとはどういうことか - Sojiro’s Blog
Hypertext Transfer Protocol - Wikipedia
Representational State Transfer (REST)
「Webを支える技術」を読みました とRESTのまとめ - 大学生からの Web 開発
URLとURIは何が違うの? どちらが正しい呼び方? | 初代編集長ブログ―安田英久 | Web担当者Forum
RESTful-APIのURL設計を考えてみる - Qiita
- 投稿日:2019-06-25T21:41:41+09:00
form_forについて
form_forについて
構文の書き方
.hamlform_for(モデルオブジェクト , オプション) do |f| フォームコントロールの設置form_forが生成するフォームコントロール
check_box :チェックボックス
color_field :色の入力欄
date_field :日付の入力欄
datetime_field :日時の入力欄(グローバルタイム)
datetime_local_field :日時の入力欄(ローカルタイム)
email_field :emailアドレスの入力欄
fields_for :form_forの中で別のモデルを指定したフォーム
file_field :画像や文章などのファイルを選択するフォーム
hidden_field :隠しフィールドの生成
label :ラベルの生成
month_field :月の入力欄
number_field :数値入力欄
password_field :パスワード入力欄
phone_field
telephone_field :電話番号入力欄
radio_button :ラジオボタンの生成
range_field :範囲選択バー
search_field :検索ボックスフォーム
text_area :文字列入力欄
text_field :文字列入力欄
time_field :時間入力欄
url_field :URL入力欄
week_field :週の入力欄
submit :送信ボタン以上のようにフォームコントロールに設置するオプションと軽い説明を作成しました。
- 投稿日:2019-06-25T21:29:40+09:00
Rails x Docker環境にテストDBを構築する
Docker上でRailsアプリを開発するにあたり、テスト用のDBと開発用DBをそれぞれ構築する必要があったのでそのときの構築手順を記載。
前提
以下のようなDockerファイルでRailsアプリケーションを構築しているとする。
DockerfileFROM ruby:2.6 RUN apt-get update -qq && apt-get install -y nodejs RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]RailsアプリケーションのDB設定を以下のようになっているとする。
database.ymldefault: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <%= ENV.fetch('DB_USERNAME', 'root') %> password: <%= ENV.fetch('DB_PASSWORD', 'pass') %> development: <<: *default host: db database: myapp_development test: <<: *default host: test-db database: myapp_test production: <<: *default database: myapp_productionDockerの設定方法
DBのコンテナ名はdatabase.ymlで設定したhostと一緒にする。
今回の場合、開発用DBのdockerコンテナは'db', テスト用DBのdockerコンテナは'test-db'とする。
MYSQL_USERとMYSQL_PASSWORDはdatabase.ymlで設定したユーザー名とパスワードと一致させる必要がある。
MYSQL_ROOT_PASSWORDはroot用パスワード。設定が必須。
MYSQL_DATABASEはdatabase.ymlで設定したdatabaseと一緒にする。
docker-compose.ymlversion: '3' services: web: build: . env_file: development.env ports: - '3001:3000' volumes: - .:/myapp depends_on: - db - test-db db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: 'pass' MYSQL_USER: 'webapp' MYSQL_PASSWORD: 'test' MYSQL_DATABASE: myapp_development ports: - '3306:3306' test-db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: 'pass' MYSQL_USER: 'webapp' MYSQL_PASSWORD: 'test' MYSQL_DATABASE: myapp_test ports: - '3307:3306' # ローカルPCから接続するために設定docker-compose.ymlに直接記述してもいいけど、環境変数は外出ししておく。
今回は、webappというユーザーがtestというパスワードでDBに接続できるような設定にする。↓development.envDB_USERNAME=webapp DB_PASSWORD=test接続確認
webのコンテナ名がdocker-rails_web_1という名前で立ち上がっている前提。
$ docker exec -it $(docker ps -f name=docker-rails_web_1 -q) /bin/bash [docker] # rails c -e test irb(main):001:0> ENV['RAILS_ENV'] => "test" [docker] # rails c irb(main):001:0> ENV['RAILS_ENV'] => "development"
- 投稿日:2019-06-25T19:53:59+09:00
progate 6/25
/login」で2つのルーティングが被っているように見えますが、「get」と「post」では異なるルーティングとして扱われるので問題ありません。(link_toメソッドではデフォルトでgetのルーティングを探し、form_tagメソッドがデフォルトでpostのルーティングを探します。)
- インスタンス変数に対するnameメソッドの意味 @user.name = params[:name]
次回 8回 8/15以降
- 投稿日:2019-06-25T18:58:58+09:00
【Ruby on Rails】ストロングパラメータって何なの?
【注:Rails初心者記事】
記載に間違いなどがありましたら、指摘いただけると幸いです。はじめに.この記事の目的
ストロングパラメータって何ぞや?
いろいろ調べた結果、頭の中が崩壊したので、一度記事としてまとめます。
これで、少しは理解が深まる・・・はず!!1.ストロングパラメータって何?
ストロングパラメータは、Web上から受けたつけたパラメータが、本当に安全なデータかどうかを検証した上で、取得するための仕組みです。Rails4から実装されています。
2.ストロングパラメータがなぜ必要なのか?
この仕組みを使うことで、意図しない(安全では無い)データの登録・更新を防いでくれるます。
具体的にどうやって防ぐかと言うと、メソッドにあらかじめ登録・更新を許可するカラム名を指定(ホワイトリスト形式)しておきます。そうすると、万が一、未許可のカラムデータが送られてきても、データの登録前に未許可であることを検出し、登録対象として無視することができます。
不特定多数に公開するWebアプリケーションだからこそ、ストロングパラメータの仕組みは必要不可欠な訳ですね。
2−1.「マスアサインメント(Mass Assignment)機能と脆弱性」という問題
ストロングパラメータ機能が実装される以前(rails3)は、実際に意図しないデータの登録・更新が発生していたようです。実例では無いですが、どう言った問題なのか具体例をあげて整理してみました。
・ 問題発生の土壌
以下のとおり、マスアサインメント機能の脆弱性を孕んだWebアプリを作成したことを前提とします。
モデル
ユーザの管理を行うUserテーブル(モデル)
最後の「admin」カラムは管理者ユーザかどうかを識別するために作っています。
(admin=1(管理者))
user_id name admin 1 はむ太郎 hamu@hamu.co.jp 1 ビュー
ユーザ情報の登録を求めるビュー
下図のとおり、Userモデルのname、emailのみの入力を意図して作成しています。
「admin」カラムは管理者ページからのみ操作したいので対象としていません。new.html.erb・・・中略・・・ <%= form_for @user do |f| %> <div class="field"> <%= f.text_field :name, placeholder: "ユーザ名を入力してね!" %> </div> <div class="field"> <%= f.text_field :email, placeholder: "e-mailアドレスを入力してね!" %> </div> <div class="actions"> <%= f.submit %> </div> <% end %> ・・・中略・・・コントローラー
ユーザ情報の登録を行うコントローラ
マスアサインメント機能を使用し、Userモデル丸ごとデータ登録をさせるよう作成されています。test_controller.rbdef create user = User.new(params[:user(モデル名)]) user.save def・ 問題の発生契機と内容
このアプリのWeb公開後、該当のViewから下図のようなパラメータを受け取りました。最後のパラメータ"admin"は、開発者側が意図していない(安全で無い)データでした。
このパラメータは悪意あるユーザによるパラメータの改ざんによって発生したようです。params.{"user"=>{"name"=>"はむ太郎","email"=>"hamu@hamu.co.jp","admin"=>1}} ^^^^^^^^^^^^^^この結果どうなるでしょうか?
「admin」と言うカラムは管理者ユーザかどうかを識別をしています。
そのため、この悪意あるユーザが管理者権限付きのユーザIDを獲得してしまったのです!
こんな事しちゃうユーザです。その後の更なるセキュリティ攻撃に発展しそうな予感大ですね。。。こうした問題が「マスアサインメント機能と脆弱性の問題」でした。多分。
ただ、Rails4以降はストロングパラメータの使用が必須になっていて、上であげたような記述は出来ないようです。以下に実際の事象に関する記事がありましたので、参考までに。。。【マスアサインメント機能の脆弱性問題に関する記事】
https://www.infoq.com/jp/news/2012/03/GitHub-Compromised/3.ストロングパラメータの適用対象
3-1.適用対象について
ユーザがフォームから入力する情報がストロングパラメータの適用対象となります。
観点としては、以下2つを覚えておくと迷わないと思います。・対象のデータ
ユーザフォーム(View)から送られてきたデータ。
コントローラ上の表記で言う”params”
※ただし、1カラムずつparams内のデータを指定する場合は不要
・対象の機能:対象のデータに対する登録・更新機能
コントローラ上に定義された”create/update"などのメソッド3-2.適用対象外について
対象となる機能・データ以外であれば、ストロングパラメータを介する必要はないようです。
・不要なデータ
ユーザフォームから入力された情報ではない(=params以外)データ。
例えば、current_user(devise利用時に使える変数)を登録する場合などです。ただし、敢えて、ストロングパラメータの対象とすることも可能です。・不要な機能
データの登録・更新が発生しない機能はもちろんですが、View/controllerを介さない機能も不要です。例えば、テストデータの一括登録、データ移行、バッチ処理などは不要。と言うより、機能自体使えないですね。きっと。4.ストロングパラメータの書き方
前置きが長くなりましたが、ストロングパラメータの書き方について整理してみましょう。
4-1. 基本構文
ストロングパラメータは以下のように記述します。
メソッド名に命名規則は無いようですが[ モデル名_params ]とするのが一般的なようです。
また、実行結果として、許可されたカラムの値だけを抽出し、ハッシュ形式で呼び出し元に値を返してくれます。user_controller.rbprivate def user_params params.require(:キー(モデル名)).permit(:カラム名1,:カラム名2,・・・).marge(カラム名: 入力データ) end4-2.requireメソッド
requireメソッドを使用する事で、params内の特定のキーに紐付く値だけを抽出する事ができます。そのため、引数には取り出したい値のキーを指定する必要があります。
例)キー値userに対するデータを抽出したい場合は、以下のように設定します
params.require(:user).permit(・・・略・・・)・ キーの設定元
View上でform_forメソッドを使用した場合のキー設定箇所を見てみましょう。下図の例ではform_forに続く”@user"がrequireメソッドで指定すべきキーです。
なお、form_forメソッドは、モデルに基づくformを作成する際に使うヘルパーメソッドで"@user"が対象のモデル名をさしています。new.html.erb・・・中略・・・ <%= form_for @user do |f| %> <%= f.text_field :name, placeholder: "ユーザ名を入力してね!" %> <%= f.submit %> <% end %> ・・・中略・・・4-3.permitメソッド
permitメソッドを使用する事で、許可された値のみを取得することができます。
そのため、permitメソッドの引数には登録を許可する全てのカラム名を指定しておく必要があります。もし、許可されいないカラムがparams内に存在した場合、そのデータは取得されず無視されます。例)Userモデルに存在するnameおよび、emailカラムのみ入力を受け付けたい場合
(他のカラム(admin)は受け付けないたくない)params.require(:user).permit(:name,:email)※ユーザの入力項目を増やした場合は、ストロングパラメータへの項目追加も忘れずに!
もし、忘れたら・・・何と明示的にエラーとなりません!(エラーキャッチとかしてるとなるのかな?)
必須項目だった場合、DB自体に保存されませんし、必須項目でない場合も、該当データのみDBに反映されない歯抜けの状態になっちゃいます。4-4.mergeメソッド
mergeメソッドを使用することでハッシュ同士を結合することができます。
例えば、paramsに含まれない値をストロングメソッドに加えたい場合などに、ストロングパラメータの後に記述することができます。params.require(:user).permit(:name,:email).merge(user_id: current_user.id)4-5.privateメソッド
privateメソッド配下に記述したメソッドは、クラス外からのアクセスができません。
基本的にストロングパラメータは、クラス外からのアクセスをさせないようにprivateメソッド配下に書くようです。5.ストロングパラメータの呼び出し方
折角定義したストロングパラメータのメソッドですが、呼び出して使わないと意味がありません。どのように呼び出すか、記述例を見てみましょう。
なお、呼び出し方には大きく2パターンあります。(もっとあるかもしれませんが・・・)5-1. 丸投げパターン
このパターンが多いと思います。ストロングパラメータに全てお任せパターンです。
user_controller.rbdef create User.create(user_params) end5-2. 部分投げパターン
params以外のデータを含む場合などに使うようです。
ただ、このパターンでは、そもそもマスアサインメント機能を使っていません。そのため、ストロングパラメータ自体不要ですね。実際にストロングパラメータを使わなくても登録が可能です。
なお、params以外のデータもストロングパラメータとして指定できるので、丸投げパターンで呼び出すことも可能です。(4-4.margeメソッド参照)post_controller.rbdef create Post.create(image: post_params[:image], text: post_params[:text],user_id: current_id) endまとめ
ストロングパラメータは、マスアサインメント機能の脆弱性問題を回避するために作られた機能。
そのため、対象はparamsを使用したデータの登録、更新のみ。
さらに言うと、paramsを使っていたとしても、1カラムずつ定義する場合は、ストロングパラメータの利用が必須ではない!これでストロングパラメータと少しは仲良くなれたかな。。。
参考
・Ruby on Rails5 アプリケーションプログラミング 山田祥寛 (参考書)
・https://kirohi.com/strong_parameters_rails
・https://diveintocode.jp/tips/strong_parameter
- 投稿日:2019-06-25T16:54:47+09:00
「Gemfile」は「gemfile」じゃだめだよ!
タイトル通り、Gemfileの「G」は大文字じゃないとデプロイの際、エラーになります。
僕は「gemfile」のファイル名のままデプロイして12時間ハマりました。
僕みたいにハマる方がこれ以上生まれないよう、念のため共有しておきます。ふと新しいwebアプリケーションを作りたいと思い、以下の記事にしたがってrails newしました。
https://qiita.com/yuitnnn/items/b45bba658d86eabdbb26ただ、以下の記事で説明されているように、gemを--path vendor/bundle配下で管理する必要は別にないかもしれないです。
https://qiita.com/jnchito/items/99b1dbea1767a5095d85原因はわかりませんがrails newして新しいrailsアプリを作成した時になぜかファイル名が「Gemfile」ではなく「gemfile」(先頭の文字が小文字のg)になっていました。
多分、自分が何かおかしいことをやったんだと思います。その状態でリモートリポジトリにpush、Herokuにデプロイして、https://×××××.herokuapp.comにアクセスするとエラーが発生しています。
heroku使っている人なら一度は見たことがあるであろうおなじみの画面ですよね!
そこで試しにheroku run rails cをしてみるとTraceback (most recent call last): 4: from /app/bin/rails:3:in `<main>' 3: from /app/bin/rails:3:in `load' 2: from /app/bin/spring:8:in `<top (required)>' 1: from /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require' /usr/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require': cannot load such file -- bundler (LoadError)bundlerがないと怒られてます。
HerokuダッシュボードのSettingsを見てみると
RailsアプリをデプロイしたのにFrameworkがNode.jsになっています。
HerokuのBuildpacksを見てみると、RailsでWebpackerを利用するために必要なNode.jsのbuildpackしか使われていないことが分かります。肝心なrubyのbuildpackがないため、rubyのbuildpackを追加してもう一度Herokuへデプロイしてみました。
remote: -----> Build succeeded! remote: ! This app may not specify any way to start a node process remote: https://devcenter.heroku.com/articles/nodejs-support#default-web-process-type remote: remote: -----> App not compatible with buildpack: https://buildpack-registry.s3.amazonaws.com/buildpacks/heroku/ruby.tgz remote: More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure remote: remote: ! Push failed remote: Verifying deploy... remote: remote: ! Push rejected to ×××××××××××××. remote: To https://git.heroku.com/×××××××××××××.git ! [remote rejected] master -> master (pre-receive hook declined) error: failed to push some refs to 'https://git.heroku.com/×××××××××××××.git'なぜかデプロイできない!!!
ここからが長かった。。。
buildpackについて調べたり、bundlerについて調べたり色々したけど、
結局、原因は「Gemfile」のファイル名が「gemfile」と先頭の「g」がなぜか小文字になっていることが原因でした。https://devcenter.heroku.com/articles/buildpacks#detection-failure
上記URL先に「アプリケーションのルートフォルダにGemfileがないといけない」と記載されていますが、まさか小文字の「gemfile」だと正しく読み込んでくれないとは気づかなかったです。ってかなんで小文字の「gemfile」になってたんだ...どこかの記事に「エラーで長時間詰まるときは大体単純なミスが多い」と書いてあったのを読んだことがありますが、今回はまさにそれでした。
今回みたいになぜかファイル名が小文字で「gemfile」となっているようなことが他の方に起こることはほぼないと思いますが、一応投稿して共有させて頂きました。
- 投稿日:2019-06-25T16:37:48+09:00
【Rails】html.erbでの記述
- 投稿日:2019-06-25T16:21:26+09:00
【初心者向け】i18nを利用して、enumのf.selectオプションを日本語化する[Rails]
こんにちは、エンジニアとして就職を目指しています、タヌキです。
前回の記事では、haml, form_withを利用して、f.selectの入力フォームを作るために色々と試行錯誤した話、
さらに、enumを利用してデータを利用しやすくした話を書きました。▼前回の記事はこちら
【初心者向け】form_with, haml, enumを使ってselectによるプルダウンリストを作った話[Rails]
https://qiita.com/tanutanu/items/1bb5f12ac8ae90e71352その中で、f.selectの入力フォームは実装できたのですが、
最後に選択肢が英語になってしまうという課題が残りました。そのため、今回はenum利用時に、f.selectの選択肢を日本語にするための方法をご紹介したいと思います。
合わせて、日本語化したデータをビューなど他の場所でも使うための方法もご紹介いたします。
どうぞよろしくお願いいたします。今回参考にした記事
まず、今回参考にさせていただいた記事はこちらです。
参考というよりも、こちらの記事がとても良すぎて、ほとんどこのままの内容で実装できましたので、
本記事の内容も下記の記事とほぼ同じです。ページ下方の、selectオプション以外への使用方法の項だけ、内容が異なります。
自分自身のまとめのために、やったことを記しているので、
selectオプションへの使用方法だけが知りたい!という方は下記の記事を参照された方が良いと思います。▼参考にした記事はこちら
https://qiita.com/tomoharutsutsumi/items/272a10f4fefb555944f2必要なファイル
enumを日本語化する上で必要だったファイルは下記の通りです。
- (gem) enum_help, rails-i18n
- model ←今回は restaurant.rb
- ja.yml
- application.rb
その他、enumを日本語化して記載したいビューファイルです。
gem ファイルのインストール
まずは、enumをI18n(国際化)対応させるgem
enum_help をインストールします。rails-i18nの方はまだしっかり言語化できていないのですが、
i18nの機能が使いやすくなるそうです。Gemfilegem 'rails-i18n' gem 'enum_help'bundle lnstall します。
model に enumを記載する
次に、modelにenumを記載します。
今回は、レストラン情報を載せるrestaurantsテーブルの、昼の予算のカラム budget_d に対して、下記のようにenumを記載しました。models/restaurant.rbenum budget_d: { default: 0, till_1000: 1, till_2000: 2, till_3000: 3, till_4000: 4, till_5000: 5, over_5000: 6 }, _prefix: true最後の prefix: true は、同じ値をもつ複数のenumが存在するときにつけるものです。
今回は、夜の予算を定義する budgetn も同じアプリ内に存在していたので、 _prefix:true をつけました。▼詳しくは、こちらをご覧ください。
https://qiita.com/emacs_hhkb/items/fce19f443e5770ad2e13ja.ymlに翻訳情報を記載する
翻訳情報を記したファイル、ja.ymlを
config/locales/ 内に作成し、下記のように記します。config/locales/ja.ymlja: enums: restaurant: budget_d: default: "--" till_1000: "~¥999" till_2000: "¥1,000~¥1,999" till_3000: "¥2,000~¥2,999" till_4000: "¥3,000~¥3,999" till_5000: "¥4,000~¥4,999" over_5000: "¥5,000~"上記は、enumのデータを翻訳したい時の記載方法ですので、その他の場所を翻訳したいときには、別の記載方法となります。
▼詳しくは、こちらの記事をご覧ください。
https://qiita.com/shi-ma-da/items/7e5c3d75c9a9f51abdd5デフォルトの言語を日本語化する
application.rb の設定を変更して、デフォルトの言語を日本語にします。
config/application.rb# 前略 module SomeApp class Application < Rails::Application # 中略 config.i18n.default_locale = :ja # デフォルトのlocaleを日本語(:ja)にする end endパスを通して、i18nのロケールファイルが読み込まれるようにする。
以下の記述も application.rb に追記して、locales フォルダ内のファイルが全て読み込まれるようにします。
config/application.rb# 前略 module SomeApp class Application < Rails::Application # 中略 config.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # 追記 end endこれで、enumを日本語化するための設定は完了です!
設定だけでかなり長かったです・・・。最終的なコード
そして、今回 f.select に使用したコードは下記のようになりました。
viewfile= f.select :budget_d, Restaurant.budget_ds_i18n.keys.map{|k| [I18n.t("enums.restaurant.budget_d.#{k}"), k]}まず、budget_d の中身を含む配列、budget_ds に対し、
keys メソッドを実施し、[["default": "--"], ["till_1000": "~¥999"], ...] などのキーだけ(["default", "till_1000"])を配列の形で取り出します。そして、その値一つ一つに対して、mapを使い処理をしています。
処理の内容は、
I18n.t ... enumの内容を翻訳するメソッド
を使って、["翻訳した内容":"value"]の配列を作る処理です。enums.restaurant.budget_d のように、
ja.ymlに書いたenumの翻訳情報の位置をきちんと記します。結果
その結果、できたドロップダウンリストがこちら。
生成されたコードがこちらです。
valueがdefaultやtill_1000などになっていますが^^;これで正しくvalueはデータベースに保存されます。
その他の場所で、翻訳したデータを使う。
最後に、ビューのその他の場所で翻訳したファイルを使う方法をご紹介します。
基本的には、「カラム名_i18n」をつけた表記にすればokです。view%p= restaurant.budget_d_i18n #これで、昼の予算が日本語で表示されるenumの日本語化といい、f.selectの表記といい、なかなか時間のかかった実装でした。
それでは、ここまで読んでくださり、ありがとうございました。
- 投稿日:2019-06-25T15:55:44+09:00
Railsでのsqliteの基本操作
超基本操作
--コンソールの起動--
$ rails dbconsole--テーブルの確認--
sqlite> .tables--スキーマの確認--
sqlite> .schemaテーブルのカラムの追加・削除
rails g migrationでファイルを作成して、rails db:migration でDBに反映させる流れ
--カラムの追加--
rails g migration[Addカラム名Toテーブル名] [カラム名:型]
※Addの後のカラム名とテーブル名の表記に注意
※カラム名にしてはいけないワード[img]$ rails g migration AddSurlToLists surl:string $ rake db:migrate---カラムの削除--
$ rails g migration RemoveAreaFromLists area:int $ rake db:migrate
- 投稿日:2019-06-25T15:22:31+09:00
ローカルでpostgreSQLに接続できなくなった時(備忘録)
エラー内容
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?
$ brew uninstall postgresql $ rm -fr /usr/local/var/postgres/ $ brew install postgresqlもちろん、ローカルのpostgreSQLデータ吹っ飛ぶので注意。
- 投稿日:2019-06-25T15:21:42+09:00
rspecの導入+カバレッジを出力する
概要
Ruby on Railsの開発で単体試験、カバレッジを測りたいという時のための手順
すでにRailsが起動してるよ!という状態からスタート環境
Ruby on Rails自体はDockerの上で起動(Dockerじゃなくても同じように動くはず)
* Ruby : 2.6
* Rails : 5.2.3
* rspec-rails : 3.8.2
* simplecov : 0.16.1やったこと
Gemfileの編集
Gemfileに
rspec-rails
とsimplecov
を追加する
test
`モードのときにのみインストールするように設定Gemfilegroup :test do gem 'rspec-rails' gem 'simplecov' endrspecをインストールする
gemをダウンロードする(今回はdocker-compose使ってます)
docker-compose buildDocker使ってないと
bundle install
かなbundle install
rspecをインストール(generate)する
rails generate rspec:installこんなファイル構成が生まれてばOK
.spec spec/ spec/spec_helper.rb spec/rails_helper.rb設定を書き換える
Railsのモード指定をする
spec_helper.rbRSpec.configure do |config| ENV['RAILS_ENV'] = 'test' (略) endカバレッジレポート作成を設定する
spec_helper.rbrequire 'simplecov' RSpec.configure do |config| (略) if ENV['CIRCLE_ARTIFACTS'] dir = File.join(ENV['CIRCLE_ARTIFACTS'], 'coverage') SimpleCov.coverage_dir(dir) end SimpleCov.start endテストクラス作成
テスト対象のクラスを作る
本来はrails generate model item ・・・
というようにrails generate
したときにテストクラス(〜_spec.rb)が作られる
けど、すでに存在するクラスに対しては作られないので主導で作成
以下のようなファイル構成で作る(例はitemというモデルがあった場合)spec/models/item_spec.rbファイルの中身はこんな感じ
item_spec.rbrequire 'rails_helper' RSpec.describe Item, type: :model do # describeはテストの大きな枠で、クラスが持つ機能(メソッド)ごとに作るといいかも # 文字列のところは任意で決められ、表すものを書けばOK describe 'aaa test' do # itはテストケース単位 # 文字列のところは任意で決められ、表すものを書けばOK it 'bbb test' do # テスト内容を記載 end end end試験データを作成するときは
factory_bot_rails
を使うと良い
(今回は説明しません)テスト実行
テスト実行は1コマンド
rspec # テストケースを指定する場合は引数でファイル名を渡す(対象ファイルが多いと時間がかかるため、開発中は限定したい) rspec spec/models/item_spec.rb
結果がベローって出て、全部緑ならOK
最後にカバレッジが出るので、試験結果とカバレッジ結果を両方確認できるFinished in 0.03117 seconds (files took 3.09 seconds to load) 1 example, 0 failures Coverage report generated for RSpec to /sample_app/coverage. 187 / 194 LOC (96.39%) covered.カバレッジ結果は
caverage/index.html
に出力されるので、通過していない箇所をブラウザで確認できる注意点
specファイルが書かれていないクラスはカバレッジ計測されません
(他のテストケースを実行したときに呼ばれれば計測される)
全テスト対象クラスを最初から用意しておくことを激しくお勧めします
テスト結果が正しくないまま「カバレッジが高いぞ!」ってはしゃぐことになります応用編
手動で実行すると忘れるものです
なので、リポジトリにpushしたときにテストが実行されるよう自動化をお勧めします
(github → circleCIとかgitlabのスクリプトで十分効果が発揮できる)
テスト結果をslackなどのコミュニケーションツールに投げ込むと失敗に気づけると思います
考えたら色々きりがないので、色々な環境で試してみてください。
- 投稿日:2019-06-25T15:10:44+09:00
MacOS(Mojave)でrailsをinstallしようとしてコケた
概要
MacにはデフォルトでRubyが入っているので早速railsを入れようと
sudo gem install rails
したらコケた。
概ねこの記事通りにやったら解決した。
Failed to build gem native extension.
実行すると以下のようなエラーが発生。
ERROR: Error installing rails: ERROR: Failed to build gem native extension. current directory: /Library/Ruby/Gems/2.3.0/gems/nokogiri-1.10.3/ext/nokogiri /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/bin/ruby -r ./siteconf20190625-38062-z2jdpr.rb extconf.rb mkmf.rb can't find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/include/ruby.h extconf failed, exit code 1 Gem files will remain installed in /Library/Ruby/Gems/2.3.0/gems/nokogiri-1.10.3 for inspection. Results logged to /Library/Ruby/Gems/2.3.0/extensions/universal-darwin-18/2.3.0/nokogiri-1.10.3/gem_make.out
xcode-select --install
で治るという情報もあったが、こちらは既にインストールしてあったので、解決策にはならなそう。
MacOSのMojaveにはmacOS SDK Headerが入っていないのがエラーの原因らしいので、インストールする。$ sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /
the package path specified was invalid
実行すると以下のようなエラーが発生。
installer: Error - the package path specified was invalid: '/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg'.仕方ないので直接ダウンロードする。
AppleDeveloperからCommand Line Tools (macOS 10.14)for Xcode 10.2.1
を検索。ダウンロードファイルを開いてそのままインストールを進めると/Library/Developer/CommandLineTools/Packages/
にインストールされる。
AppleIDで認証する必要があるため少し面倒。まとめ
無事
sudo gem install rails
でrailsをインストールできた。
Mojave以前のソフトウェアアップデートでも同じような現象が起きたらしいので、以後のアップデートで同じ現象が起きた時用に。
- 投稿日:2019-06-25T14:26:02+09:00
Rails6 のちょい足しな新機能を試す41(MailDeliveryJob 編)
はじめに
Rails 6 に追加されそうな新機能を試す第41段。 今回は、
MailDeliveryJob
編です。
Rails 6 では、ActionMailer::DeliveryJob
を使うとDEPRECATION WARNING
が表示されるようになります。
代わりにActionMailer::MailDeliveryJob
が用意されています。Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1
- parameterized mail に対応するために、
ActionMailer::Parameterized::DeliveryJob
が導入される。ActionMailer::DeliveryJob
とActionMailer::Parameterized::DeliveryJob
の2つがあるのはややこしいから、1つのクラスActionMailer::MailDeliveryJob
に統合しよう。- クラスが変わってしまうから、
ActionMailer::DeliveryJob
やActionMailer::Parameterized::DeliveryJob
を使った場合にDEPRECATION WARNING
を表示しよう。という流れだったみたいです。
今回の準備
今回は、 Rails6 のちょい足しな新機能を試す27(perform_deliveries 編) のソースに手を加えていくことにより動作確認します。
MyMailDeliveryJob を作る
メール送信のジョブを差し変えるため、
MyMailDeliveryJob
クラスを作ります。
このとき派生元のクラスをDEPRECATION WARNING
を出すために、意図的にActionMailer::DeliveryJob
にします。app/jobs/my_mail_delivery_job.rbclass MyMailDeliveryJob < ActionMailer::DeliveryJob before_perform :logger_info def logger_info Rails.logger.info('BEFORE MyMailDeliveryJob perform') end endMyMailDeliveryJob を使う
MyMailDeliveryJob を使うように
UserMailer
を修正します。app/mailers/user_mailer.rbclass UserMailer < ApplicationMailer self.delivery_job = MyMailDeliveryJob # この行を追加 ... endユーザーを登録する
ブラウザから User を登録してメールを送信します。
development.log に
DEPRECATION WARNING
が表示されます。また、MyMailDeliveryJob
を設定したにも関わらず、ActionMailer::Parameterized::DeliveryJob
が動作していることにも注意してください。log/development.log... [ActiveJob] [ActionMailer::Parameterized::DeliveryJob] [...] DEPRECATION WARNING: Sending mail with DeliveryJob and Parameterized::DeliveryJob is deprecated and will be removed in Rails 6.1. Please use MailDeliveryJob instead. (called from instance_exec at /usr/local/bundle/gems/activesupport-6.0.0.rc1/lib/active_support/callbacks.rb:429) ...MyMailDeliveryJob クラスの親クラスを変更する
MyMailDeliveryJob の親クラスを
ActionMailer::MailDeliveryJob
に変更します。app/job/my_mail_delivery_job.rbclass MyMailDeliveryJob < ActionMailer::MailDeliveryJob ... end再度ユーザーを登録する
ブラウザから User を登録してメールを送信します。
今度は、
DEPRECATION WARNING
も消えて、MyMailDeliveryJob
が動作していることがわかります。log/development.log... [ActiveJob] [MyMailDeliveryJob] [...] Performing MyMailDeliveryJob ... [ActiveJob] [MyMailDeliveryJob] [...] BEFORE MyMailDeliveryJob perform ...結論
ActionMailer::DeliveryJob
やActionMailer::Parameterized::DeliveryJob
を使っている場合や独自のメール送信ジョブのクラスを作っている場合は、ActionMailer::MailDeliveryJob
から派生させたメール送信ジョブのクラスに変更するのが良いでしょう。試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try041_mail_delivery_job参考情報
- 投稿日:2019-06-25T14:05:36+09:00
マイグレーションについて
前職で、「マイグレーションについて調べてみて。」と言われたけど、結局何を調べて答えたのか忘れてメモもしてなくて。改めて調べてみました。
↑キーワード検索から、いろんな人の記事を漁るのも良い方法かもしれません。
Migration とは何者か
【現時点での理解】
複数人で開発していて、データベースが勝手に書き換えられるような環境での開発で、
全員がおんなじ状態のDBを使えるようにする仕組み。ソースコードに対するバージョンコントローラの Git 、
データベースに対するバージョンコントローラの Migrationみたいなものでしょうか。
※筆者は主に PHP/Laravel を書きます。PHP/Laravel の場合
Laravelドキュメント:マイグレーション
bz0さんの記事Ruby on Rails の場合
Python/Django の場合
- 投稿日:2019-06-25T13:49:09+09:00
Rails production環境で Mysql2::Error: Disk full が出たときの対処
- 投稿日:2019-06-25T12:52:53+09:00
simplecovでデフォルトのタブを消す方法
結論
spec/rails_helper.rbにgroups.clearを記述して1つづつ再定義しよう
環境
Ruby 2.3.7
Rails 4.2.5
simplecov 0.16.1
rspec-rails 3.4.2やりたい事
simplecovのデフォルトグルーピングを削除したい。
(うちはMailers、Jobsとか殆ど使ってないんで無くしたい。)これを
こうしたい
やり方
以下のファイルを編集しよう
spec/rails_helper.rb
SimpleCov.start :rails do groups.clear # 一旦全定義をclear # グルーピングしたい定義を1つづつ再定義 add_group 'Controllers', 'app/controllers' add_group 'Models', 'app/models' add_group 'Helpers', 'app/helpers' add_group 'Libraries', 'lib/' # 集計除外したい場合はfilterにぶち込もう add_filter %w[app/services app/jobs] endデフォルトのタブは以下で定義されている。(これは編集しない)
.bundle/ruby/2.3.0/gems/simplecov-0.16.1/lib/simplecov/profiles/rails.rb
# frozen_string_literal: true [SimpleCov.profiles.define "rails" do load_profile "test_frameworks" add_filter %r{^/config/} add_filter %r{^/db/} add_group "Controllers", "app/controllers" add_group "Channels", "app/channels" if defined?(ActionCable) add_group "Models", "app/models" add_group "Mailers", "app/mailers" add_group "Helpers", "app/helpers" add_group "Jobs", %w[app/jobs app/workers] add_group "Libraries", "lib/" track_files "{app,lib}/**/*.rb" end](url)
- 投稿日:2019-06-25T12:28:55+09:00
マイクロサービスでは環境によって変わる変数は環境変数を直接書いた方が良いと思ったという話
About
タイトルだけを見ると何を当たり前の事をと思われると思いますが
Ruby on Railsではdevelopmentやproductionでの変数を切り替える方法がいくつかあります。プログラム内で分岐する
def switch_var case Rails.env when 'production' then 'var of production' when 'development' then 'var of development' else 'default var' end endこんなコードを書いている人もいると思います。
私も昔書いていましたが、何度か変数の変更が入ると変更すべきコードを探すのが億劫になります。dotenv-rails
https://github.com/bkeepers/dotenv
.envファイルを読み込む事で各環境での変数を定義します。
ただ、.envはgitへのコミット非推奨な割に
dotenvとrailsが密になってしまいstaging環境やproduction環境を構築する際に窮屈さを感じました。
ただ、ローカル環境においてはdocker-composeと.envを組み合わせるという方法はアリだと思います。config設定用のgemを利用する
他にも色々ありますが、yamlでconfigを管理する様なgemがあります。
それなりに便利なので一時期使っていましたが、
結局gemに依存するとメンテナンスコストもかかってしまうので
特にマイクロサービスレベルでの環境変数程度で有ればgemは不要だと私は思いました。環境変数をアプリケーションに渡す
test, development環境
Docker前提で作っているのでdocker-composeで渡します。
CI上でもdocker-compose.ymlを専用に作っておいてそちらを利用しています。version: '3' services: web: environment: - RAILS_ENV - RACK_ENV - AWS_BUCKET_NAME=xxxx - AWS_ACCESS_KEY_ID=xxxx - AWS_SECRET_ACCESS_KEY=xxxx何も書かないとhostの環境変数が使われます。
production環境
マイクロサービスをTerraformで定義しています。
ECSのタスク定義で渡します。ecs.tfresource "aws_ecs_task_definition" "task" { container_definitions = data.template_file.service_container_definition.rendered } data "template_file" "service_container_definition" { template = file("./templates/container_definitions.json.tpl") }container_definitions.json.tpl[ { "environment": [ { "name": "RAILS_ENV", "value": "production" }, { "name": "RACK_ENV", "value": "production" }, { "name": "AWS_BUCKET_NAME", "value": "xxxx" }, { "name": "AWS_ACCESS_KEY_ID", "value": "xxxx" }, { "name": "AWS_SECRET_ACCESS_KEY", "value": "xxxx" } ] } ]実際にはアプリケーションケーションの規模によって
最適なツールが有るのかも知れませんが
マイクロサービスにおいてはこういう形で渡した方が見通しも良くなっていると思いました。
- 投稿日:2019-06-25T11:46:54+09:00
なぜかActiveRecord::RecordNotUniqueで怒られる
状況
rails初心者です
railsでdeviseを使ってログイン機能を作った際に新規登録のUIを変更し実行した際に
ActiveRecord::RecordNotUniqueで怒られた。原因
主な原因はテーブルのindexにありそうでした。
schema.rb#~~省略 t.index ["username"], name: "index_users_on_username", unique: true t.index ["nil"], :name "index_users_on_name", unique: trueこのようにnilに対してのインデックスがなぜに出来たのかもどのような効果があるのかもはっきりとは分かっていませんが用意されていました。
解決策
直前に'username'というカラムを追加していました。
そこで一旦$ rails db:rollbackテーブルを1つ前の状態に戻し
$ rails db:migratemigrateし直した結果見事にnilに対してのindexは削除されていました!
終わりに
nilに対してのindexをremove_indexで消す方法もありなのかなと思いましたがあんまりやり方が分からず今回のやり方でうまくいったのでよしとしました。
どうしてnilに対してのindexが作られていたのかあんまり分かっていないのでどなたか詳しい方教えていただけたら嬉しいです。
- 投稿日:2019-06-25T07:10:35+09:00
ローカルでrails sが止まらない!
ユースケース
railsのゾンビプロセスを発生させる手法
- tmux, terminalを閉じてしまった場合
- railsをバックグラウンドで実行する方法
railsのバックグラウンド実行
$ rails server -d
- process idを消す
$ rm ./tmp/pids/server.pidサーバーを止める方法
process id(以下pid)を消す必要があります。
消し方としては以下の3つの方法があります。
- ポートからpidを探す
- tmpのserver.pidからpidを探す
- pumaのprocessからpidを探す
ポートからpidを探す
$ lsof -i:3000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME ruby 31584 _user_name_ 10u IPv4 0xac1c1f275d70c41 0t0 TCP localhost:hbci (LISTEN) ruby 31584 _user_name_ 11u IPv6 0xac1c1f26fce7e19 0t0 TCP localhost:hbci (LISTEN) $ kill -kill 31584tmpのserver.pidからpidを探す
$ ls tmp/pids/server.pid tmp/pids/server.pid $ cat tmp/pids/server.pid 31584 $ kill -kill 31584pumaのprocessからpidを探す
$ ps aux | grep puma _user_name_ 32578 0.0 0.0 xxx xxx s000 S+ xx:xxAM 0:00.00 grep puma _user_name_ 31584 0.0 0.8 xxx xxx s005 S+ xx:xxAM 0:02.52 puma 3.12.1 (tcp://localhost:3000) [project_name] $ kill -kill 31584参考文献
- 投稿日:2019-06-25T05:25:10+09:00
スクレイピング
用途
ウェブサイト上のHTMLからデータを抜き出す処理
使用例
例えばこのようなHTMLのサイトがあったとして
<ul> <li>TEST1</li> <li>TEST2</li> <li>TEST3</li> </ul>TEST1
TEST2
TEST3
の値を取り出す事ができる必要なGem
Mechanize
Mechanizeクラスが使えるようになる
Gemfileの最後の行に以下のコードを記述する
Gemfile.gem 'mechanize'Gemfileに記述されたgemをインストールする
$ bundle installMechanizeクラスのインスタンスを生成
スクレイピングするにはまず、Mechanizeクラスのインスタンスを生成する
例.agent = Mechanize.new #Mechanizeクラスのインスタンスを生成して、agentへ代入webサイトのHTML情報を取得する
getメソッド
getメソッドはMechanizeクラスのインスタンスメソッド
get(スクレイピングしたいウェブサイトのURL)例.agent = Mechanize.new page = get("https://qiita.com/") #QiitaのHTMLを取得HTMLの文字列ではなく、ウェブサイトのHTMLの情報を持ったMechanize::Pageオブジェクトを取得
※オブジェクト
関連する変数(値)とメソッド(動作)をまとめて、そのまとまりに名前を付けたもの
- 投稿日:2019-06-25T01:05:18+09:00
【Rails】gretelを使ってパンくずリストを作成
gretelの使い方
某ECサイトのコピーを作成する際に、パンくずリストの実装をしました。
いくつかgemがありましたが、コントローラを使用せずに設定ファイルとビューに記述するだけで実装できるgretelを選択しました。
あと名前が可愛い(←パンくずリストとは
下記の画像をご覧下さい。
よくサイトで見るやつですね!
今回はこれを実装していきます。① gretelの導入
GitHub
https://github.com/lassebunk/gretel公式
https://www.rubydoc.info/gems/gretel② gemのinstall
gem "gretel"$ bundle install③ ファイルの設定
設定ファイルを下記コマンドで作成
$ rails generate gretel:installすると下記のファイルが生成されます。
config/breadcrumbs.rbcrumb :root do link "Home", root_path end # crumb :projects do # link "Projects", projects_path # end # crumb :project do |project| # link project.name, project_path(project) # parent :projects # end # crumb :project_issues do |project| # link "Issues", project_issues_path(project) # parent :project, project # end # crumb :issue do |issue| # link issue.title, issue_path(issue) # parent :project_issues, issue.project # end # If you want to split your breadcrumbs configuration over multiple files, you # can create a folder named `config/breadcrumbs` and put your configuration # files there. All *.rb files (e.g. `frontend.rb` or `products.rb`) in that # folder are loaded and reloaded automatically when you change them, just like # this file (`config/breadcrumbs.rb`).
まずはマイページをパンくずリストに記載したいので、下記のように追記します。config/breadcrumbs.rb# ルート crumb :root do link "トップページ", root_path end # マイページ crumb :mypage do link "マイページ", mypage_users_path end・ :mypage ← 設定ファイルを呼び出します。
・ "マイページ" ← パンくずリストに表示される名称です。
・ mypage_users_path ← 呼び出し元のパスを指定します。④ ビューの設定
mypage.html.haml-# config/breadcrumbs.rbに定義したmypageを呼び出し - breadcrumb :mypage -# 下記を記述した箇所にパンくずリストが表示される。 = breadcrumbs pretext: "You are here:",separator: " › "・ pretext ← パンくずリストの先頭に挿入する文章を記述。いらない場合は設定しなくて大丈夫です。
・ separator ← パンくずの区切り文字を指定。「&rsaquo」は出力されると「›」になります。パンくずリストの表示は、一般的に全ページに表示できるlayouts/application.html.hamlに記載することが多いかと思いますが、今回は一部のページでのみ表示させたいのと、改修を容易にする為、下記のように部分テンプレート化しています。
layouts/_breadcrumbs.html.haml.breadcrumbs = breadcrumbs pretext: "",separator: " › ", class: "breadcrumbs-list"
実際のリスト呼び出しと表示結果はこのようになっています。mypage.html.haml- breadcrumb :mypage = render "layouts/breadcrumbs"⑤ 親の設定
config/breadcrumb.rbのcrumbとendの間にparentを設定することで親を設定することができます。
マイページを親に設定してプロフィールをリストに表示させましょう。config/breadcrumbs.rb# ルート crumb :root do link "トップページ", root_path end # マイページ crumb :mypage do link "マイページ", mypage_users_path end # プロフィール crumb :profile do link "プロフィール", edit_user_path parent :mypage endすると以下のように表示されます。
今回参考にさせていただいた記事
https://qiita.com/you8/items/d2d37a745060b79c112f
https://qiita.com/namitop/items/5bcb3b90e63af758a9b0
http://vdeep.net/rubyonrails-gretel
https://doruby.jp/users/kisuzuki/entries/gretel%E3%81%A7%E3%83%91%E3%83%B3%E3%81%8F%E3%81%9A%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90以上がgretelを使用したパンくずリストの実装となります。
- 投稿日:2019-06-25T00:18:22+09:00
自作アプリのUserモデルにテストコード(RSpec)を書いてみた
はじめに
Railsでよく見るバリデーションのテストをRSpecで書きました。まだまだ勉強中なので、問題点があればご指摘ください。
テスト内容
今回は、name, user_id, email を持つUserモデルを作成しました。
マイグレーションファイルclass CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :name t.string :user_id t.string :email t.timestamps end end endUserモデルのテストでは、主にバリデーションのテストを行いました。
app/models/user.rbclass User < ApplicationRecord before_save { email.downcase! } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, uniqueness: { case_sensitive: false }, format: { with: VALID_EMAIL_REGEX } endバリデーションの設定に関しては、Railsチュートリアルを参考にしています。バリデーションの設定や意味などは、そちらを参考に。
環境設定
Gemfile に以下のように追記します。
group :development, :test do # 省略 gem 'factory_bot_rails' gem 'rspec-rails' # 省略 endそうすると、Userモデル作成と同時にRSpecのファイルが生成されます。
$ rails g model User ... Running via Spring preloader in process 19654 invoke active_record create db/migrate/20190617xxxxxx_create_userss.rb create app/models/user.rb invoke rspec create spec/models/user_spec.rbなので、テストは以下に書き込めばいいと。
spec/models/user_spec.rb
テスト作成
以下が、Userモデルに対するすテスト(RSpec)
spec/models/user_spec.rbrequire 'rails_helper' RSpec.describe User, type: :model do before do @user = User.create( name: "Yamada Taro", user_id: "taro", email: "taro@example.com", ) end # name、user_id、emailがあれば有効な状態であること it "is valid with a name, user_id, email" do user = User.new( name: "Sato Taro", user_id: "sato", email: "sato@example.com", ) expect(user).to be_valid end # nameがなければ無効な状態であること(nameは必須項目) it "is invalid without a name" do user = User.new(name: nil) user.valid? expect(user.errors[:name]).to include("can't be blank") end # メールアドレスがなければ無効な状態であること(emailは必須項目) it "is invalid without an email address" do user = User.new(email: nil) user.valid? expect(user.errors[:email]).to include("can't be blank") end # 重複したメールアドレスなら無効な状態であること it "is invalid with a duplicate email address" do user = User.new(email: "taro@example.com") user.valid? expect(user.errors[:email]).to include("has already been taken") end # メールアドレスが小文字化されていること it "email addresses should be saved as lower-case" do mixed_case_email = "TANAKA@example.com" @user.email = mixed_case_email @user.save expect(@user[:email]).to eq("tanaka@example.com") end # メールアドレスの一意性の検証 it "email is unique" do user = User.create(name: "Test", user_id: "test", email: "taro@example.com") expect(user).to_not be_valid expect(user.errors[:email]).to include("has already been taken") end # メールアドレスのフォーマットの検証 describe "mail" do context "Correct format" do it "is OK" do @user.email = 'user@example.com' expect(@user).to be_valid end end context "Incorrect format" do it "is NG" do @user.email = 'user@example,com' expect(@user).to_not be_valid expect(@user.errors[:email]).to include("is invalid") end end end # nameの長さの検証 describe "name length" do context "length 50 txt" do it "is OK" do @user.name = 'a' * 50 expect(@user).to be_valid end end context "length 51 txt" do it "is NG" do @user.name = 'a' * 51 expect(@user).to_not be_valid expect(@user.errors[:name]).to include("is too long (maximum is 50 characters)") end end end end1ブロックづつ説明します
重複のチェックの際に使用するので、事前にインスタンスを作成しておきます。
spec/models/user_spec.rbbefore do @user = User.create( name: "Yamada Taro", user_id: "taro", email: "taro@example.com", ) endname、user_id、emailがあれば有効な状態であること
spec/models/user_spec.rb# name、user_id、emailがあれば有効な状態であること it "is valid with a name, user_id, email" do user = User.new( name: "Sato Taro", user_id: "sato", email: "sato@example.com", ) expect(user).to be_valid end
expect(user).to be_valid
は、user
が妥当(valid)なことを期待するという意味です。nameがなければ無効な状態であること(nameは必須項目)
spec/models/user_spec.rbit "is invalid without a name" do user = User.new(name: nil) user.valid? expect(user.errors[:name]).to include("can't be blank") endメールアドレスがなければ無効な状態であること(emailは必須項目)
spec/models/user_spec.rbit "is invalid without an email address" do user = User.new(email: nil) user.valid? expect(user.errors[:email]).to include("can't be blank") endvalid? が
false
なら、errors.messages
にエラー内容が格納されます。今回は、それが、"can't be blank"
であることを期待しています。重複したメールアドレスなら無効な状態であること
spec/models/user_spec.rbit "is invalid with a duplicate email address" do user = User.new(email: "taro@example.com") user.valid? expect(user.errors[:email]).to include("has already been taken") endvalid? が
false
なら、errors.messages
にエラー内容が格納されます。今回は、それが、"has already been taken"
であることを期待しています。メールアドレスが小文字化されていること
spec/models/user_spec.rbit "email addresses should be saved as lower-case" do mixed_case_email = "TANAKA@example.com" @user.email = mixed_case_email @user.save expect(@user[:email]).to eq("tanaka@example.com") end
@user[:email]
の値 ="tanaka@example.com"
がTRUE
であることを期待しています。メールアドレスの一意性の検証
spec/models/user_spec.rbit "email is unique" do user = User.create(name: "Test", user_id: "test", email: "taro@example.com") expect(user).to_not be_valid expect(user.errors[:email]).to include("has already been taken") end
taro@example.com
はすでに保存されたアドレスなので、バリデーションは通らないことと、エラーメッセージは"has already been taken"
であることを期待しています。メールアドレスのフォーマットの検証
,com
のカンマがあるのは、無効である.com
であれば、有効である(すくなくとも無効ではない)ことを
User
モデルのバリデーション部分に記載しているので、テストがパスしないことを期待しています。spec/models/user_spec.rbdescribe "mail" do context "Correct format" do it "is OK" do @user.email = 'user@example.com' expect(@user).to be_valid end end context "Incorrect format" do it "is NG" do @user.email = 'user@example,com' expect(@user).to_not be_valid expect(@user.errors[:email]).to include("is invalid") end end endnameの長さの検証
name
の中身の文字数が
- 50文字までは有効
- 51文字以上は無効
であることを
User
モデルのバリデーション部分に記載しているので、テストがパスしないことを期待しています。spec/models/user_spec.rbdescribe "name length" do context "length 50 txt" do it "is OK" do @user.name = 'a' * 50 expect(@user).to be_valid end end context "length 51 txt" do it "is NG" do @user.name = 'a' * 51 expect(@user).to_not be_valid expect(@user.errors[:name]).to include("is too long (maximum is 50 characters)") end end endテスト実行結果
bundle exec rspec spec
で実行できます。$ bundle exec rspec spec StaticPagesController GET #top returns http success GET #about returns http success UsersController GET #new returns http success User is valid with a name, user_id, email is invalid without a name is invalid without an email address is invalid with a duplicate email address email addresses should be saved as lower-case email is unique mail Correct format is OK Incorrect format is NG name length length 50 txt is OK length 51 txt is NG Finished in 0.34401 seconds (files took 1.19 seconds to load) 13 examples, 0 failures全て通りました。
まとめ
テストとは基本的に、期待する値と、実際に生成される値を比較検証なんだと体感しました。
テストの妥当性を検証するために、期待する値を変更したりして、テストをパスしないことを確かめることも重要だとわかりました。
it ... end
中にあるブロックは、1つのテストをまとめる役割をしています。これによって、テストがパスしなかった場合、どこでコケたか、原因究明がわかりやすくなることがわかりました。書いていて。参考