- 投稿日:2019-08-28T23:07:31+09:00
Railsでメール送信機能を実装する手順を結構丁寧に(development環境編)
次からもっとスムースにできるように、ここに執筆します。
前提
- バージョンは
Rails 5.2.3です。- テンプレートエンジンはHamlを使っていますが、ERBでもSlimでもそんなに変わらないはずです。
$ rails -v Rails 5.2.3要件(仕様、やりたいこと)
- 問い合わせフォームで必要事項を入力して送信すると、その内容をDBに保存するとともに、管理者にテキストメールで通知する。
letter_opener を導入する
- letter_opener というgemを導入すると、開発中にテストで送信したメールが実際にアドレス先に飛んでいく代わりに、ブラウザのポップアップで確認できるようになります。
- 導入することで、テスト用のメールが誤って実際に使われているアドレス先に送信されるのを防ぐことができます。
- 実際にやってみればどうなるかわかると思いますので、とりあえず以下の手順で導入だけ済ませておきます。
Gemfilegroup :development do gem 'letter_opener' end$ bundle installconfig/environments/development.rbconfig.action_mailer.delivery_method = :letter_opener実装
では実装していきます。
config/environments/development.rbを編集する
config.action_mailer.raise_delivery_errors = falseをコメントアウトすることで、development環境からもメールを送信する設定に切り替えることができます。config.action_mailer.delivery_methodは先ほど追加した部分です。config.action_mailer.default_url_optionsは、後述のメールテンプレート内でURLヘルパーを使用できるようにする設定です。config/environments/development.rbRails.application.configure do #...(中略)... # コメントアウトすることでdevelopment環境からもメールを送信する設定になる # config.action_mailer.raise_delivery_errors = false # smtpで実際にメール送信させるのではなく、letter_openerというgemを用いてテストする # config.action_mailer.delivery_method = :smtp config.action_mailer.delivery_method = :letter_opener # メールテンプレートはviewと違ってURLヘルパーを使ってもドメインが取得できず、メール本文にURLを載せられないので、その対策をする config.action_mailer.default_url_options = {:host => 'localhost:3000'} endメーラークラスを作成する
rails generateコマンドが使用できます。- 今回はHTMLメールではなくテキストメールを実装したいため、
app/views/notice_mailer/sendmail_contact.html.hamlは削除します(HTMLテンプレートの方が優先して呼ばれるみたいなので、そちらを残しておくとエラーになったり意図しない内容のメールになってしまいます)。$ rails g mailer Notice sendmail_contact Running via Spring preloader in process 1180 create app/mailers/notice_mailer.rb invoke haml create app/views/notice_mailer create app/views/layouts/mailer.text.haml create app/views/layouts/mailer.html.haml create app/views/notice_mailer/sendmail_contact.text.haml create app/views/notice_mailer/sendmail_contact.html.haml ->削除する
- メーラークラスは以下の内容にします。
app/mailers/notice_mailer.rbclass NoticeMailer < ApplicationMailer # メール送信元アドレスを設定する default from: "noreply@example.com" def sendmail_contact(contact) @contact = contact # メール送信先アドレスを設定する mail to: "admin@example.com", subject: "お問い合わせが届きました" # メール件名 end endメールテンプレートを作成する
- ここに記述した内容がメール本文になります。
- メーラークラスのメソッド内で使用したインスタンス変数は、ここで参照できます。
- バックスラッシュを使うことで、改行できます。
- 先述した通り、
config/environments/development.rbに必要な設定をしていれば、URLヘルパーを使うこともできます。app/views/notice_mailer/sendmail_contact.text.hamlお問い合わせが届きました。 \ = "会社名: #{@contact.company}" = "お名前: #{@contact.name}" = "メールアドレス: #{@contact.email}" = "電話番号: #{@contact.telephone}" = "お問い合わせ内容:" = @contact.messageメーラークラスのメソッドを呼び出す
- 追加しているのは、
NoticeMailer.sendmail_contact(@contact).deliverの1行です。app/controllers/contacts_controller.rbclass ContactsController < ApplicationController def new @contact = Contact.new end def create @contact = Contact.new(contact_params) if @contact.save NoticeMailer.sendmail_contact(@contact).deliver # ここで呼び出している redirect_to new_contact_url, notice: '問い合わせの送信に成功しました。' else render :new end end private def contact_params params.require(:contact).permit(:company, :name, :email, :telephone, :message) end endこれで一通りの実装が完了しました。実際に動かしてみましょう!
(option)development環境から実際にメール送信する
- letter_opener を使わず、実際にメールを送信してみる場合は、
config/environments/development.rbを以下のように記述します。- 今回は、ドメインはGmailのものを設定します。
- Gmailのアプリパスワードというのは、普段Googleのアカウントにログインするために使っているパスワードとは異なりますので、ご注意ください。
config/environments/development.rbRails.application.configure do #...(中略)... # コメントアウトすることでdevelop環境からもメールを送信する設定になる # config.action_mailer.raise_delivery_errors = false # smtpで実際にメール送信させる config.action_mailer.delivery_method = :smtp # config.action_mailer.delivery_method = :letter_opener # メールテンプレートはviewと違ってURLヘルパーを使ってもドメインが取得できず、メール本文にURLを載せられないので、その対策をする config.action_mailer.default_url_options = {:host => 'localhost:3000'} config.action_mailer.smtp_settings = { :enable_starttls_auto => true, :address => 'smtp.gmail.com', :port => 587, :domain => 'gmail.com', :authentication => :plain, # 本当に飛ばす場合(letter_openerを使わない場合)は、Gmailのメールアドレスが必要 :user_name => 'aaaaa@gmail.com', # 本当に飛ばす場合(letter_openerを使わない場合)は、Gmailのアプリパスワードが必要 :password => 'aaaabbbbccccdddd' } end(WIP)production環境から実際にメール送信する
- 本番環境については別記事にまとめる予定です。まだ私も実装できていないので…。
参考
- 投稿日:2019-08-28T23:04:32+09:00
【配送手数料1000円なんて高すぎる!!】UberEATSで安く注文する〜全てはタピオカのために〜
UberEATS CHEAPER
(間違って記事削除してしまったので再掲してます...)
TL; DR
UberEatsでタピオカミルクティーを頼みたいのに配送料が高すぎる
配送料が安くなったタイミングを通知できるようにした背景
私はタピオカミルクティーが好きです。正しくは、タピオカ抹茶ティーが大好きです。
口が寂しい時にタピオカミルクティーは丁度良いです。
糖分は取れるし、腹持ちいいし。
オフィスから歩いてタピオカミルクティーを買えるお店はありません。
なのでいつも UberEATSを利用させて頂いております。しかし、いつも思うのです。
頼みたい時ってだいたい昼下がりの時間なんですけど、結構な頻度で
配送料高っっっっっっっっっっっっっ!!!!!!!!!!と。頼むにしてはギリギリを超える手数料。
なので、配送料が安くなった時に通知してくれるプログラム作りました。
概要
Rubyツールです。
アクセス負荷がかからないくらいで5分おきにデータを取得し、安くなったかどうかを判断しています。
通知方法はSlackとMacの通知センターの2つに対応しています
(他のサービス、Windowsなどは反響があれば作ろうかなと思います。)技術的内容
seleniumで操作しながら情報を取得しています。
UberEatsでは、まずブラウザにお届け先の住所のデータをCookieで持つ必要があります。
その上で、お店の配送手数料や配送時間、そもそも配送可能かどうかが分かるようになります。
そのため、seleniumを利用しました。
一度お届け先の郵便番号を入力し、住所候補が出てきます。
出てきた住所候補をクリックしてCookieに住所情報を保持します。
その状態でお店のページに遷移します。
そうすると、住所を元に配送手数料が表示されます。
(色々調べたら緯度・経度を元にしていました。詳しくは別記事で書こうと思います。)html要素はcssセレクター、xpathで取得していますが、html構造が変わったらこの
ツールは使えなくなるかもしれません。ご了承くださいませ。使い方
$ bundle exec ruby ubereats_cheaper.rb [通知方法] [郵便番号] [希望配送手数料] [UberEATSのお店のURL] [Slack webhook url オプション引数]
- 引数1 通知方法
mac(デフォルト)・・・macの通知センターから通知を送ります。画面右上からぴょこっと出てきます。
slack・・・Slackの特定のチャンネルに通知を送ります。引数5にてslack webhook urlが必要になります。
- 引数2 郵便番号
[0-9]{6}のフォーマットで入力してください。
- 引数3 希望配送手数料
数字を入力してください。ここで入力した数字よりも配送手数料が低くなったら通知がきます。
- 引数4
UberEATSのお店のURLここにはお店の個別ページのURLを入力してください。
例:https://www.ubereats.com/ja-JP/tokyo/food-delivery/%E3%82%B7-%E3%82%A2%E3%83%AC%E3%82%A4-%E6%B8%8B%E8%B0%B7246%E5%BA%97-the-alley-shibuya-246/C7X1V9lWQ5KIoPq7YYLc4A/
- 引数5
Slackのwebhook urlhttps://www.sejuku.net/blog/74471
こちらを参考に、webhook urlを取得してください。一応このwebhook urlは後々削除されるとのことなので、反響があれば後々新しい方に切り替えようと思います。コマンド例
bundle exec ruby ubereats_cheaper.rb 'slack' 1070062 1000 'https://www.ubereats.com/ja-JP/tokyo/food-delivery/%E3%82%B7-%E3%82%A2%E3%83%AC%E3%82%A4-%E6%B8%8B%E8%B0%B7246%E5%BA%97-the-alley-shibuya-246/C7X1V9lWQ5KIoPq7YYLc4A/' 'https://hooks.slack.com/services/hoge1/hoge2/hoge3'なお、
bundle exec ruby ubereats_cheaper.rbだけで実行すると、macで、自分が最近よくいる南青山へ、大好きな渋谷のジ アレイ(有名なタピオカミルクティーのお店)の配送手数料が1000円(1000円より安くなることが割と少ない)よりも安くなった時に通知が来るようになっています。インストール
- このリポジトリをcloneしてください
$ git clone git@github.com:nochiraaa/ubereats_cheaper.git
$ bundle install --path vendor/bundleを実行Chromeドライバをインストール。以下のURLより取得できます。
https://sites.google.com/a/chromium.org/chromedriver/downloads
もしくはHomebrewを使っている方は下記でもインストールができます。
$ brew install chromedriver
Homebrewが入っていない方はこちらから
http://brew.sh/index_ja.html
Chromeドライバのバージョンは最新のものであれば問題なく動くと思います。
動作確認はChromeDriver 76.0.3809.68 (420c9498db8ce8fcd190a954d51297672c1515d5-refs/branch-heads/3809@{#864})で行っています。今後の展開
シンプルな内容で作りましたが、下記の改善も検討中です!
- 配送時間にも対応する
- オススメのお店をレコメンド
- 他に安いお店をレコメンド
- 他に早く届くお店をレコメンド
- 他のプラットフォームにも対応する
- LINE@
- API的に動くようにする
- firefox, IE, Edge, Safariにも対応する
- アメリカでも使えるようにする
- seleniumを使わず処理する
- 投稿日:2019-08-28T23:04:32+09:00
【配送手数料1000円なんて高すぎる!!】UberEATSで安く注文する〜全てはタピオカのために〜[再掲]
UberEATS CHEAPER
(間違って記事削除してしまったので再掲してます...)
TL; DR
UberEatsでタピオカミルクティーを頼みたいのに配送料が高すぎる
配送料が安くなったタイミングを通知できるようにした背景
私はタピオカミルクティーが好きです。正しくは、タピオカ抹茶ティーが大好きです。
口が寂しい時にタピオカミルクティーは丁度良いです。
糖分は取れるし、腹持ちいいし。
オフィスから歩いてタピオカミルクティーを買えるお店はありません。
なのでいつも UberEATSを利用させて頂いております。しかし、いつも思うのです。
頼みたい時ってだいたい昼下がりの時間なんですけど、結構な頻度で
配送料高っっっっっっっっっっっっっ!!!!!!!!!!と。頼むにしてはギリギリを超える手数料。
なので、配送料が安くなった時に通知してくれるプログラム作りました。
概要
Rubyツールです。
アクセス負荷がかからないくらいで5分おきにデータを取得し、安くなったかどうかを判断しています。
通知方法はSlackとMacの通知センターの2つに対応しています
(他のサービス、Windowsなどは反響があれば作ろうかなと思います。)技術的内容
seleniumで操作しながら情報を取得しています。
UberEatsでは、まずブラウザにお届け先の住所のデータをCookieで持つ必要があります。
その上で、お店の配送手数料や配送時間、そもそも配送可能かどうかが分かるようになります。
そのため、seleniumを利用しました。
一度お届け先の郵便番号を入力し、住所候補が出てきます。
出てきた住所候補をクリックしてCookieに住所情報を保持します。
その状態でお店のページに遷移します。
そうすると、住所を元に配送手数料が表示されます。
(色々調べたら緯度・経度を元にしていました。詳しくは別記事で書こうと思います。)html要素はcssセレクター、xpathで取得していますが、html構造が変わったらこの
ツールは使えなくなるかもしれません。ご了承くださいませ。使い方
$ bundle exec ruby ubereats_cheaper.rb [通知方法] [郵便番号] [希望配送手数料] [UberEATSのお店のURL] [Slack webhook url オプション引数]
- 引数1 通知方法
mac(デフォルト)・・・macの通知センターから通知を送ります。画面右上からぴょこっと出てきます。
slack・・・Slackの特定のチャンネルに通知を送ります。引数5にてslack webhook urlが必要になります。
- 引数2 郵便番号
[0-9]{6}のフォーマットで入力してください。
- 引数3 希望配送手数料
数字を入力してください。ここで入力した数字よりも配送手数料が低くなったら通知がきます。
- 引数4
UberEATSのお店のURLここにはお店の個別ページのURLを入力してください。
例:https://www.ubereats.com/ja-JP/tokyo/food-delivery/%E3%82%B7-%E3%82%A2%E3%83%AC%E3%82%A4-%E6%B8%8B%E8%B0%B7246%E5%BA%97-the-alley-shibuya-246/C7X1V9lWQ5KIoPq7YYLc4A/
- 引数5
Slackのwebhook urlhttps://www.sejuku.net/blog/74471
こちらを参考に、webhook urlを取得してください。一応このwebhook urlは後々削除されるとのことなので、反響があれば後々新しい方に切り替えようと思います。コマンド例
bundle exec ruby ubereats_cheaper.rb 'slack' 1070062 1000 'https://www.ubereats.com/ja-JP/tokyo/food-delivery/%E3%82%B7-%E3%82%A2%E3%83%AC%E3%82%A4-%E6%B8%8B%E8%B0%B7246%E5%BA%97-the-alley-shibuya-246/C7X1V9lWQ5KIoPq7YYLc4A/' 'https://hooks.slack.com/services/hoge1/hoge2/hoge3'なお、
bundle exec ruby ubereats_cheaper.rbだけで実行すると、macで、自分が最近よくいる南青山へ、大好きな渋谷のジ アレイ(有名なタピオカミルクティーのお店)の配送手数料が1000円(1000円より安くなることが割と少ない)よりも安くなった時に通知が来るようになっています。インストール
- このリポジトリをcloneしてください
$ git clone git@github.com:nochiraaa/ubereats_cheaper.git
$ bundle install --path vendor/bundleを実行Chromeドライバをインストール。以下のURLより取得できます。
https://sites.google.com/a/chromium.org/chromedriver/downloads
もしくはHomebrewを使っている方は下記でもインストールができます。
$ brew install chromedriver
Homebrewが入っていない方はこちらから
http://brew.sh/index_ja.html
Chromeドライバのバージョンは最新のものであれば問題なく動くと思います。
動作確認はChromeDriver 76.0.3809.68 (420c9498db8ce8fcd190a954d51297672c1515d5-refs/branch-heads/3809@{#864})で行っています。今後の展開
シンプルな内容で作りましたが、下記の改善も検討中です!
- 配送時間にも対応する
- オススメのお店をレコメンド
- 他に安いお店をレコメンド
- 他に早く届くお店をレコメンド
- 他のプラットフォームにも対応する
- LINE@
- API的に動くようにする
- firefox, IE, Edge, Safariにも対応する
- アメリカでも使えるようにする
- seleniumを使わず処理する
- 投稿日:2019-08-28T22:28:31+09:00
Rails & Nuxt.jsのアプリケーションにGraphQLを導入する
前提
Rails & Nuxt.jsのDocker環境をalpineイメージで構築する
こちらのポストの環境をもとに進めるので、Dockerのサービス名等は適宜読み替えていただくようにお願いします。ディレクトリ構成
後述のコマンドでは、Railsはbackend、NuxtはfrontendがDockerのサービス名になっています。. ├── backend <- Ruby on Rails │ ├── Dockerfile │ ├── Gemfile │ ├── Gemfile.lock │ (中略) │ ├── frontend <- Nuxt.js │ ├── Dockerfile │ ├── README.md │ ├── nuxt.config.js │ ├── package-lock.json │ ├── package.json │ (中略) │ ├── docker-compose.yml └── .envライブラリ追加
Rails
- graphql-ruby ... RubyでGraphQLを導入するならコレ
- graphiql-rails ... GraphQL IDE のRails版。ブラウザから
./backend/Gemfileを修正し、bundle installします。## (中略) ## gem 'graphql' #added group :development do gem 'listen', '>= 3.0.5', '< 3.2' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' gem 'graphiql-rails' #added end ## (中略) ##$ docker-compose exec backend bundle installNuxt
こちらのライブラリをインストールします。
本記事では graphql-tag は使いません。$ docker-compose exec frontend yarn add @nuxtjs/apollo実装
Rails
generator で雛形を作成します。
$ docker-compose exec backend rails g graphql:installgraphiql-railsの Readme にしたがって、Railsのconfigファイルを修正します。
./backend/config/routes.rbGraphiQLエンジンをマウントし、ブラウザからアクセスできるようにします。
Rails.application.routes.draw do post "/graphql", to: "graphql#execute" #generatorでinsertされる # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html #added if Rails.env.development? mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql' end end
./backend/config/application.rbAPIモードの場合に必要な修正です。
## (中略) ## - # require "sprockets/railtie" + require "sprockets/railtie" ## (中略) ##GraphiQLで動作確認
dockerコンテナを再起動後、ブラウザで http://localhost:3000/graphiql にアクセスし、GraphiQLを開きます。
./backend/app/graphql/types/query_type.rbのサンプルを利用して、下記のようにqueryの結果が返ってくればOKです。$ docker-compose restart backendNuxt
Nuxtアプリのルートに、下記のディレクトリ、ファイルを追加、編集します。
今回はmutationは使いませんが、あわせて作成しておきます。. └── frontend ├── nuxt.config.js │ ├── pages │ └── index.vue │ └── apollo ├── client-configs │ └── default.js └── gqls ├── mutations └── queries └── getTestField.gql
./frontend/nuxt.config.jsNuxtでapolloクライアントを使用するための設定を追加します。
default.jsを読み込まず、nuxt.config.jsに直書きしてもOKです。export default { /* (中略) */ modules: [ '@nuxtjs/apollo', //added ], /* (中略) */ apollo: { clientConfigs: { default: '~/apollo/client-configs/default.js' } } }
./frontend/apollo/client-configs/default.jsapolloには様々なオプションありますがが、今回はqueryの実行が確認できればよいので最低限です。
uriのホスト名は、DockerのRailsアプリのサービス名backendになります。import { HttpLink } from 'apollo-link-http' export default () => { const httpLink = new HttpLink({ uri: 'http://backend:3000/graphql' }) return { link: httpLink } }
./frontend/apollo/gqls/queries/testField.gqlGraphiQLで実行したものです。
query { testField }
./frontend/pages/index.vuequeryを実行した結果を表示します。
<template> <p>{{ testField }}</p> </template> <script> import testField from '~/apollo/gqls/queries/testField'; export default { data() { return { testField: {} } }, apollo: { testField: { query: testField } } } </script>お疲れさまでした。
(簡略化シすぎた感)
- 投稿日:2019-08-28T20:40:16+09:00
Railsチュートリアルメモ 第3章
個人的なメモ。
Railsチュートリアルでは2章ではScaffoldを使ってあらましを、
3章から本格的にアプリ制作に入る。
3章では「静的なページの制作」、「自動テストのあらまし」の学習をするメモでは気になった部分、忘れそうな部分を記述。
3章 ほぼ静的なページの作成
3.2 静的ページ
3.2.1 静的なページの生成
コントローラ生成(rails generate)時にアクションは複数指定できる
$ rails generate controller StaticPages home helphome helpのようにcontroller名のあとに複数指定可能
Rubyの命名規則の特徴
クラス名にはキャメルケース(例:StaticPages)が使われる
ファイル名にはスネークケース(例:static_pages)が使われる
※あくまで慣習だが、同様に習うべきRailsコマンドの短縮
完全なコマンド 短縮形 $ rails server$ rails s$ rails console$ rails c$ rails generate$ rails g$ rails test$ rails t$ bundle install$ bundleRailsでの手戻し
コントローラ, モデル
$ rails destroy controller HogeHoges
generateに対してのdestroy※ destroy時、
/config/routes.rb内にルーティングが残っているかもしれない。手作業で削除DB
DBも逐一マイグレーションが必要
$ rails db:migrateだが、1手手戻しもできる
$ rails db:rollbackまたdbのマイグレーションは逐一バージョンが付与されているので、最初に戻す場合、以下
$ rails db:migrate VERSION=03.2.2 静的なページの調整
viewsの場所
app/views/コントローラ名/アクション名.html.erb
erb (Embedded Ruby)
htmlにrubyを埋め込んである。3.3 テストから始める
テスト駆動開発 (test-driven development; TDD)
RailsチュートリアルではガチガチのTDDでは進みが悪いので、必要に応じてTDDチュートリアル内で行うテストは3つ
- コントローラテスト (3章〜)
- モデルテスト (6章〜)
- 統合テスト (7章〜)
3.3.1
コントローラテスト
$ rails generate controller hogehogeの時点でテストが既に作成されている。
/test/controllers/3.3.2
テスト駆動のサイクル
- 失敗するテストを最初に書く
- アプリケーションのコードを書いて成功させる (パスさせる)
- 必要ならリファクタリングする
それぞれステータスから
- RED
- GREEN
- REFACTOR
とも。
このサイクルを繰り返す。以下テストを元に、
$ rails testを確認しながら、通るようにしていく。
テストを書き、通るように開発。簡単なTDD。require 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest # 省略 test "should get about" do get static_pages_about_url assert_response :success end endリファクタリング
「1匹いたら30匹いると思え」
金言。BUGでなくとも腐敗臭漂うだけでリファクタリングもの。
ネズミもGもBUGもスパゲッティコードも。早いうちからのリファクタリングが大事。3.4 少しだけ動的なページ
HTML内でheadのtitleタグを動的に表示し、TDDでつなぐ。
3.4.2
リファクタリング。
2,3回出てくる文字列を変数化
(インスタンス変数、文字列の式展開について、触れられている)3.4.3
DRY (Donot Repeat Yourself)
Rubyの原則。
共通部分をRailsの機能でまとめる。
/app/views/layouts/application.html.erb
上記に大枠のレイアウトを記述。
その他アクションのerb内の内容は、application.html.erb内に
<%= yield %>
と記述し、挿入する。3.5 最後に
コミットとデプロイ
masterブランチに変更をmerge
herokuにデプロイ$ git checkout master $ git merge 作業ブランチ名 # プッシュとデプロイ $ git push # github側 push $ rails t # herokuデプロイ前テスト $ git push heroku # heroku側 push $ heroku open # 確認デプロイ前に
rails testを実行。癖をつけるようにする。3.6 高度なセットアップ
3.6.1 minitest reporters
minitest-reporters (gem) でRailsのテストを見やすくする。
RED、GREENで色がつくようになる。3.6.2 Guardによるテストの自動化
Guardというgemでファイルを監視、自動テスト
$ bundle exec guard init # 初期化 $ bundle exec guard # 監視スタートguardのプロンプトが開く。
フルスキャンはそのままEnter。
終了は<C-d>でデタッチ。GuardはSpringサーバ(Railsの機能)を使うが、バグが有りプロセスが残ることが有るので、重いときには余計なものをkillする。
Linux killコマンド
プロセスのkill
$ ps aux | grep springパイプでgrepに渡し、springのみ抽出
pid(数字5桁)を指定してkill$ kill -15 12345 #pidはあくまで例ここのoptionで指定するシグナルは15がterminationで一緒か?
どうやらCPUごとに一部変わるそうで、x86なら-15でSIGTERM(termination)で終了できるらしい。
我が家のintelmacさんはx86かと思うので-15Springなら
1. 一括終了を最初に試す、
2. ダメならpkill$ spring stop $ pkill -15 -f プロセス名 # ダメなら所感
テスト駆動で書きながら、コントローラからビューに至るまでの流れをトレースできたように思う。
簡単なテストだが、まだ苦に思わない程度で済んでいる。
演習で2,3度同じような作業が繰り返されるので、少しずつ馴染めそう。
- 投稿日:2019-08-28T20:40:16+09:00
Railsチュートリアル(第4版)メモ 第3章
Railsチュートリアル(第4版)の個人メモ
気になった部分、忘れそうな部分を記述。
- Ruby 2.6.1
- Rails 5.1.6
2章ではScaffoldを使ってあらましを、
3章から本格的にアプリ制作に入る。
3章では「静的なページの制作」、「自動テストのあらまし」の学習をする3章 ほぼ静的なページの作成
3.2 静的ページ
3.2.1 静的なページの生成
コントローラ生成(rails generate)時にアクションは複数指定できる
$ rails generate controller StaticPages home helphome helpのようにcontroller名のあとに複数指定可能
Rubyの命名規則の特徴
クラス名にはキャメルケース(例:StaticPages)が使われる
ファイル名にはスネークケース(例:static_pages)が使われる
※あくまで慣習だが、同様に習うべきRailsコマンドの短縮
完全なコマンド 短縮形 $ rails server$ rails s$ rails console$ rails c$ rails generate$ rails g$ rails test$ rails t$ bundle install$ bundleRailsでの手戻し
コントローラ, モデル
$ rails destroy controller HogeHoges
generateに対してのdestroy※ destroy時、
/config/routes.rb内にルーティングが残っているかもしれない。手作業で削除DB
DBも逐一マイグレーションが必要
$ rails db:migrateだが、1手手戻しもできる
$ rails db:rollbackまたdbのマイグレーションは逐一バージョンが付与されているので、最初に戻す場合、以下
$ rails db:migrate VERSION=03.2.2 静的なページの調整
viewsの場所
app/views/コントローラ名/アクション名.html.erb
erb (Embedded Ruby)
htmlにrubyを埋め込んである。3.3 テストから始める
テスト駆動開発 (test-driven development; TDD)
RailsチュートリアルではガチガチのTDDでは進みが悪いので、必要に応じてTDDチュートリアル内で行うテストは3つ
- コントローラテスト (3章〜)
- モデルテスト (6章〜)
- 統合テスト (7章〜)
3.3.1
コントローラテスト
$ rails generate controller hogehogeの時点でテストが既に作成されている。
/test/controllers/3.3.2
テスト駆動のサイクル
- 失敗するテストを最初に書く
- アプリケーションのコードを書いて成功させる (パスさせる)
- 必要ならリファクタリングする
それぞれステータスから
- RED
- GREEN
- REFACTOR
とも。
このサイクルを繰り返す。以下テストを元に、
$ rails testを確認しながら、通るようにしていく。
テストを書き、通るように開発。簡単なTDD。require 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest # 省略 test "should get about" do get static_pages_about_url assert_response :success end endリファクタリング
「1匹いたら30匹いると思え」
金言。BUGでなくとも腐敗臭漂うだけでリファクタリングもの。
ネズミもGもBUGもスパゲッティコードも。早いうちからのリファクタリングが大事。3.4 少しだけ動的なページ
HTML内でheadのtitleタグを動的に表示し、TDDでつなぐ。
3.4.2
リファクタリング。
2,3回出てくる文字列を変数化
(インスタンス変数、文字列の式展開について、触れられている)3.4.3
DRY (Donot Repeat Yourself)
Rubyの原則。
共通部分をRailsの機能でまとめる。
/app/views/layouts/application.html.erb
上記に大枠のレイアウトを記述。
その他アクションのerb内の内容は、application.html.erb内に
<%= yield %>
と記述し、挿入する。3.5 最後に
コミットとデプロイ
masterブランチに変更をmerge
herokuにデプロイ$ git checkout master $ git merge 作業ブランチ名 # プッシュとデプロイ $ git push # github側 push $ rails t # herokuデプロイ前テスト $ git push heroku # heroku側 push $ heroku open # 確認デプロイ前に
rails testを実行。癖をつけるようにする。3.6 高度なセットアップ
3.6.1 minitest reporters
minitest-reporters (gem) でRailsのテストを見やすくする。
RED、GREENで色がつくようになる。3.6.2 Guardによるテストの自動化
Guardというgemでファイルを監視、自動テスト
$ bundle exec guard init # 初期化 $ bundle exec guard # 監視スタートguardのプロンプトが開く。
フルスキャンはそのままEnter。
終了は<C-d>でデタッチ。GuardはSpringサーバ(Railsの機能)を使うが、バグが有りプロセスが残ることが有るので、重いときには余計なものをkillする。
Linux killコマンド
プロセスのkill
$ ps aux | grep springパイプでgrepに渡し、springのみ抽出
pid(数字5桁)を指定してkill$ kill -15 12345 #pidはあくまで例ここのoptionで指定するシグナルは15がterminationで一緒か?
どうやらCPUごとに一部変わるそうで、x86なら-15でSIGTERM(termination)で終了できるらしい。
我が家のintelmacさんはx86かと思うので-15Springなら
1. 一括終了を最初に試す、
2. ダメならpkill$ spring stop $ pkill -15 -f プロセス名 # ダメなら所感
テスト駆動で書きながら、コントローラからビューに至るまでの流れをトレースできたように思う。
簡単なテストだが、まだ苦に思わない程度で済んでいる。
演習で2,3度同じような作業が繰り返されるので、少しずつ馴染めそう。
- 投稿日:2019-08-28T18:48:00+09:00
rails specで特定のファイルを指定してテストする
ググると
rails spec spec/models/user_spec.rbのようなのが出てきますが、実際は全部テスト実行されます。どこかのバージョンで変わったみたい。
うちのRails 5.2環境ではこれでいけた。
rails spec SPEC=spec/models/user_spec.rb
- 投稿日:2019-08-28T18:39:56+09:00
UFO演算子をメソッドとして定義する
概要
最近、Rubyについてもっと理解を深めたくて、Ruby技術者認定試験合格教本を読んでいます。
以下のようなUFO演算子(<=>)をメソッドとして定義している部分が分からず、まとめてみました。
(宇宙船演算子ともいうらしいですが、UFO演算子と呼ばせていただきます)def <=> other return self.id <=> other.id endざっくり結論
sortメソッド等に利用する為に、<=> を
再定義している記述だった。↑ @scivola さんより以下ご指摘いただき、「再」を削除しました。
Object には <=> は定義されていないので,Employee クラスで 再定義 しているわけではないんですよね。
ですので、単純に「定義」している記述という表現が正しいです!
どうしてこの結論に至ったか、以下経緯をまとめました。
経緯
下記のような記述が登場。
パッとみて、上記のメソッド部分が全然分からなかったので調べました。class Employee attr_reader :id attr_accessor :name def initialize id, name @id = id @name = name end def to_s return "#{@id}: #{@name}" end def <=> other return self.id <=> other.id end end employees = [] employees << Employee.new("3","Tanaka") employees << Employee.new("1","Suzuki") employees << Employee.new("2","Sato") employees.sort! employees.each do |employee| puts employee end実行結果
1: Suzuki 2: Sato 3: Tanaka分からなかったこと
employees.sort! の所で呼ばれてるメソッドっぽいけど、
どうやって呼ばれているの? 役割は?調べてみたこと
<=> ってなんだっけ?
UFO演算子と呼ばれる比較演算子で、これは比較の結果を数値で返します。左辺の値が右辺の値より大きければ、1、等しければ0、左辺の値が小さければ-1を返します。ソート処理などで順番の決定などに使用します。
100 <=> 10 #=> 1 100 <=> 100 #=> 0 10 <=> 100 #=> -1Ruby技術者認定試験合格教本より
ふむふむ。
sortメソッドについても改めて調べてみる
sortメソッドは、配列の要素をソートした新しい配列を返します。要素の順序の比較には<=>演算子が使われ、「要素1 <=> 要素2」の結果が-1なら要素1が先、0なら同じ、1なら要素2が先となります。
https://ref.xaio.jp/ruby/classes/array/sortほうほう。sort って、<=> を使っているか!
じゃあ、そのタイミングで呼ばれているっぽいな・・・挙動確認してみた
ダメ元で、UFO演算子メソッド部分をコメントアウトして、
employees.sort!してみる。23: employees = [] 24: employees << Employee.new("3","Tanaka") 25: employees << Employee.new("1","Suzuki") 26: employees << Employee.new("2","Sato") 27: binding.pry => 28: employees.sort! 29: employees.each do |employee| puts employee end [1] pry(main)> employees => [#<Employee:0x00007ff24eb8d9c0 @id="3", @name="Tanaka">, #<Employee:0x00007ff24eb8d948 @id="1", @name="Suzuki">, #<Employee:0x00007ff24eb8d8d0 @id="2", @name="Sato">] [2] pry(main)> employees.sort! ArgumentError: comparison of Employee with Employee failed from (pry):3:in `sort!'やっぱり出来ない!
比較する要素が複数あるからかな。
コメントアウトを外して、実行してみると上手くいく↓22: employees = [] 23: employees << Employee.new("3","Tanaka") 24: employees << Employee.new("1","Suzuki") 25: employees << Employee.new("2","Sato") 26: binding.pry => 27: employees.sort! 28: employees.each do |employee| puts employee end [1] pry(main)> employees.sort! => [#<Employee:0x00007fdb013d13d0 @id="1", @name="Suzuki">, #<Employee:0x00007fdb013d1358 @id="2", @name="Sato">, #<Employee:0x00007fdb013d1448 @id="3", @name="Tanaka">]次に、UFO演算子のメソッド部分で
binding.pryして、
self.idとother.idの値をチェック、どんな値が返されるかを考えてみる。16: def <=> other 17: binding.pry => 18: return self.id <=> other.id 19: end [1] pry(#<Employee>)> self.id => "3" [2] pry(#<Employee>)> other.id => "1"3 <=> 1 だから、1が返る。
結果、 1 => 3 => 2 の順番になるかな。exit してみると止まるので、次の
self.idとother.idをチェック。16: def <=> other 17: binding.pry => 18: return self.id <=> other.id 19: end [1] pry(#<Employee>)> self.id => "3" [2] pry(#<Employee>)> other.id => "2"3 <=> 2 だから、1が返る。
結果、 1 => 2 => 3 の順番かな。exit して、
self.idとother.idをチェック。16: def <=> other 17: binding.pry => 18: return self.id <=> other.id 19: end [1] pry(#<Employee>)> self.id => "1" [2] pry(#<Employee>)> other.id => "2"1 <=> 2 だから、-1が返る。
結果変わらず、 1 => 2 => 3 の順番かな。ここでexitしたら、最後まで実行された!
id 同士だけを比較しているので、エラーにならなかった!!まとめ
やはり、sort! のタイミングで、UFO演算子のメソッドが呼ばれていた。
UFO演算子のメソッド内で、<=> の定義を、id同士を比較するものに
再定義していた為、
エラーにならずsortメソッドが最後まで実行された!sortメソッドは、1が返ると右辺と左辺の順序が入れ替わり、
-1が返ると順序が入れ替わらないので、1=>2=>3 の順に並び替わる。最後に
分かってしまえばそんなに難しくないですが、最初に
def <=> otherを見た時は、正直混乱しました。
「演算子を再定義することが出来る」という概念がなかったので、勉強になりました!
sort の挙動についてもよく分かってよかった。Qiita 初投稿です。見辛い&レベル低いかもですが、お許しください・・
「違うよ!」「ここの説明の方が分かりやすいよ!」などのアドバイスありましたら、
コメントいただけると喜びます。補足
@scivola さんより、 <=> を定義せず idの値を sort_by を使って比較する方法も教えていただきました!
コメント欄にて詳しく解説してくださいましたので、チェックしてみてください!
@scivola さん、ありがとうございます!!参考にさせていただいたサイト様
https://www.uosansatox.biz/entry/2018/01/25/112048
https://codeday.me/jp/qa/20190301/344992.html
https://www.buildinsider.net/language/rubytips/0018最後まで読んでくださり、ありがとうございました!
- 投稿日:2019-08-28T17:01:57+09:00
他言語経験者がRailsの案件にジョインしたときに、何を足掛かりにすべきか
はじめに
この記事はOmotesando.rb #50で、参加者から質問を募集して、LTを行うという企画の中で、未経験者・初学者からのRailsプロジェクトに参画する際の質問が多かったことから、「実際に案件に参画するときにどのようなキャッチアップをしていけばよいか」という観点で話してみようと思い立ち、作成しました。
対象読者・想定プロジェクト
Ruby/Railsのプロジェクトに参画したことのない、Ruby/Railsプロジェクト未経験なWeb技術者を対象としています。また、Ruby/Railsの知識については、Railsチュートリアルは一通り目を通しているという前提で書いています。また、想定しているプロジェクトとしては、既に案件はスタートしており、gitリポジトリの整備、環境等の整備が一通り終わっており、人の受け入れ態勢が整っているプロジェクトであることを想定しています。
最初の関門である、環境構築の部分から書こうと思ったのですが、ここはプロジェクトによって様々なケースがあり、ボリュームが大きくなりそうだったので、今回の記事の中ではスキップして、環境構築は完了したという前提で話を勧めます。全体を把握する
プロジェクトに初めて入った時に、最初にやるべきことは全体の把握です。というのも、現実のRailsプロジェクトはRailsチュートリアルのようにシンプルではありません。むしろ基本に忠実に作られていることの方が稀です。いくつかのRuby/Railsプロジェクトに参画した経験があれば、どの程度しっかり作られているのかの勘が利くのですが、Rails未経験者・初学者の場合、そのあたりの感覚がよく分からないため、まず関わるプロジェクトがどの程度基本を押さえて作られているのかを把握しておく必要があります。
全体を把握するのに、まず以下を見ておくとよいでしょう。
- Gemfile / Gemfile.lock
- routes.rb
- app以下、lib以下
- config/initializers以下
- spec、もしくはtest以下
Gemfile / Gemfile.lock
Railsでは多数のGem(ライブラリ)が使われていて、中規模ぐらいのRailsプロジェクトになってくると、依存するGemも含めて100以上のGemが使われていることも珍しくはありません。Railsチュートリアルでは、極力Gemを使わない構成になっているので、あまり触れることはありませんが、現実のプロジェクトではGemが大量に導入されています。
ライブラリによってはRailsの挙動を大きく変更するものがあったり、プロジェクトのルールとしてGem特有の記法を強制するものもあるので、まずどのようなGemが使われているのかを把握しておく必要があります。Gemfileの中のgemを調べるときはrubygems.orgから、調べたいGem名を入力して、詳細画面からgithubやHomepage等のサイトに飛んで、README.md等のドキュメントを参照します。初学者のうちは分からないGemが多いと思いますので、調べるのも大変でしょうが、経験が蓄積されてくると全体の作りやプロジェクトのルールなども見えてくるので、ざっくりと概要をつかんでおくとよいと思います。
以下は私がプロジェクトにジョインしたときにあったら注意すべきGemと対応例の一例です。
gem 対応例 devise 認証処理がある。ユーザ名・パスワードの管理のテーブルにUsers等が使われていると考えられるので、app/models以下からdeviseの文字列を検索する omniauth-xxxx 外部認証連携が存在する。config/以下に認証連携用の設定が書き込まれていると考えておく。 acts_as_paranoid フラグを立ててデータを消したことにする論理削除と呼ばれる概念がある。対象のモデルは強制的にdeleted_at is not nullのクエリが付加されるため、app/models以下からacts_as_paranoidの文字列を検索し、対象となるモデルを把握しておく。 paranoia 同上 ruby-grape ActionControllerではないAPIエンドポイントを持っている。app/以下からGrape::APIを検索して、場所を把握しておく。また、エンドポイントはroutes.rbに書かれていることが多いため、合わせて参照しておく。 sidekiq 実際のURLリクエストとは別タスクとして実行される非同期処理がある。perform / perform_laterなどの文字列を検索し、非同期処理の内容自体やトリガー部分を確認しておく。 resque 同上 sequel DB検索にActiveRecordを使っていない可能性がある。プロジェクトのルールを確認する。 composite_primary_keys データベースの主キーがidではない可能性がある。app/models以下でprimary_keyで検索し、各テーブルの主キーを確認する。 sassc-rails CSSの代わりにassets/stylesheetsにSASSが使用されている可能性がある。 haml-rails ERBの代わりにhamlが採用されている。hamlの記法を用いてViewを書く必要がある。 slim-rails ERBの代わりにslimが採用されている。slimの記法を用いてViewを書く必要がある。 simple_form Form Objectと呼ばれる概念が導入されている可能性がある。 一つ一つ詳細に見ている時間がないという場合には、そのGemがどういう用途で導入されているかぐらいは確認しておきましょう。
The ruby toolboxというサイトで、Gem名で検索するとGemのカテゴリが分かるので、大まかに使用用途を推測することができます。routes.rb
RailsではRESTful URLでリソースベースのアクセスになるようにURL設計をしていくのが一般的ですが、現実のプロジェクトがしがらみなくRailsの推奨される構成でURL設計されているかというと、そういうわけではありません。例えば他のプロジェクトから移行するようなリプレース案件では、以前のURLと整合性がとれるようにURLをマッピングしなおさなければいけませんし、Railsを良く知らない設計者の趣味ですべてのURLをPOSTで記述しなければならないというトンデモルールが強制されている可能性すらあります。routes.rbを読んで、まずRails一般的なURL設計になっているかどうかを理解しておく必要があります。
Rails GuidesのRailsルーティングを熟読し、URL定義のパターンを理解しておきましょう。普通にget/postなどのメソッドで個別に定義する表現、matchメソッドでパターンマッチさせる表現、resourcesを使った表現や、複数のresourcesメソッドをネスト構造にしたnested resourcesやnamespaceと組み合わせた表現など、色々あります。
routes.rbを読む前に
rake routesを叩いて、エンドポイントをざっくりと俯瞰しておきましょう。app以下、lib以下
app以下
app以下にはassets, controllers, models, views, helpers, mailersがRailsの標準のディレクトリとして作成されています。これらのディレクトリの中身については、後で詳しく確認するとして、その他にディレクトリが存在しないかを確認しましょう。例えばservicesというディレクトリが存在していたら、そのプロジェクトではServiceクラスという独自のレイヤが存在する可能性がありますし、formsというディレクトリが存在していたら、そのプロジェクトではForm Objectという独自のレイヤが存在する可能性があります。
いずれもmodelクラスやcontrollerクラスが混沌としないように、プロジェクトの誰かが導入したものですので、これらの独自のレイヤをどのようなケースで使うべきなのかについては、導入した人物がまだ在籍しているのであれば直接質問する。そうでなければ、既存のコードから、どのようなメソッドが定義されていて、どのようなケースで、どこから呼ばれるのか、については把握しておく必要があります。
ServiceクラスもForm Objectも賛否両論あるものですが、プロジェクトの中できちんとルールが統一されているのであれば、導入自体はあまり問題ではありません。問題となるケースとしては責務が曖昧である、もしくは使用用途を逸脱した神クラスになっている場合や、導入が中途半端であるものは使っていて、あるものは使っていないなどの混在が存在する場合です。そのような可能性も含めて、プロジェクトとして使うべきなのか・使わないべきなのかの意思統一をしておく必要があります。
参考文献
Railsで重要なパターンpart1:Sercice Object(翻訳)
Rails: Form Objectと#to_modelを使ってバリデーションをモデルから分離する(翻訳)lib以下
lib以下は古くからGemにするまでもないようなプロジェクトの内部だけで完結するプライベートなファイルの置き場として利用されています。よく見る例としてはomniauthの独自strategyのファイルの置き場や、隠れオレオレライブラリ、オープンクラスで既存の振る舞いを書き換えたモジュールなどが配置されていることがあります。プロジェクトのファイルはapp以下に並ぶので、見落としがちなのですがlib以下にファイルがある場合は、そのファイルがどこでrequireされていて、何をしているのかは簡単に把握しておくとよいでしょう。コードが分からなくても、git log / git blameや、チケットから目的が理解できる場合もあります。
config/initializers以下
config/initializers以下はRailsが起動するプロセスの中で呼び出されます。起動プロセスの詳細についてはRailsガイドのRailsの初期化プロセスに詳しく載っているので、そちらを参照してください。ここもlib以下と同様に、app以外でプロジェクトのファイルが配置される可能性があるため見落としがちなのですが、Gemの初期化処理や、lib以下に置いたファイルのrequire処理など、アプリケーションの重要な手掛かりとなるコードが置かれていることがあります。
spec(もしくはtest以下)
参画しているプロジェクトにテストコードが十分に書かれていれば、キャッチアップを早める足がかりとなります。テストコードがどの程度書かれているかについては、
rake statsで、本体コードとテストコードのコード行数の比率を見てみましょう。あくまで個人的な目安ですが、本体コードとテストコードが同程度書かれていれば、正常系程度の最低限のテストコードが用意されていると見込める。本体コードの2~3倍程度テストコードがあれば、十分な分量のテストコードが用意されていると判断しても良いでしょう。
注意点としては過去にはテストコードが用意されていたが、メンテナンスする文化がなくなり、現在はテストコードはFailするものばかりで役に立っていないようなケースもあるため、まずはテストコードを手元で実行して、全部Passするかの確認はしておきましょう。大抵のRailsプロジェクトはbundle exec rake specやbundle exec rspec spec/**/*_spec.rbで動くようになっていますが、特殊な初期化が必要なために起動は別のコマンドで行っていたり、巨大なプロジェクトではRSpecが多すぎるため、分割して実行しているような場合もあります。プロジェクトでどのようにテストコードを実行するかの手順は確認しておきましょう。app以下を詳細に確認していく
全体を軽く見通した後は、app以下の個別のファイルについて目を通していきます。
個別のファイルについて詳細に目を通すのは実作業に取り掛かる段にして、先に確認しておくのは以下のようなことです。app/controllers以下
- application_controller.rbに定義されている共通のコールバック
- application_controller.rbにMix-inされている各種モジュールの意味
- app/controllers/concernsディレクトリ以下にあるモジュール群
- controllers以下の各コントローラに定義されているコールバック
- controllers以下の各コントローラにMix-inされているモジュール
app/models以下
- application_record.rbに定義されている共通のコールバック
- application_record.rbにMix-inされている各種モジュールの意味
- app/models/concernsディレクトリ以下にあるモジュール群
- models以下の各モデルに定義されているコールバック
- models以下の各モデルにMix-inされているモジュール
- models以下の各のhas_one / has_many / belongs_toの対応関係
- 各種バリデーションの充足度
application_controller.rbやapplication_record.rbに定義されているコールバックはコントローラ・モデルに影響するため、個別のモデルだけを見ていても分からないような振る舞いをすることがあります。またRubyではMix-inと呼ばれる方法でinclude / extend / prepend等でモジュールが差し込まれることがあり、これがapplication_controller.rbやapplication_record.rbに入っていると、すべてのクラスで影響を受けるようになっています。
モジュールはGemによって利用可能なものだったり、concerns以下のディレクトリに配置されたものを呼び出したりするものが一般的なので、concernsディレクトリを見ておけば振る舞いを変えるようなモジュールを先に見つけておくことができるでしょう。
application_record.rbについては、Rails5以降で導入された概念なので、もしかすると
ActiveRecord::Base.include AwesomeModuleのような形でモジュールがincludeされているケースもあります。このような記述をしているときは、大抵イレギュラーな処理が挟み込まれているので、includeされているモジュールが何者なのかは把握しておけると良いです。コールバックについてはRailsチュートリアルにもあるので、細かい説明は不要かと思いますが、より詳細に知りたい場合はRailsガイドのActiveRecordコールバックに目を通しておきましょう。
バリデーションについては、プロジェクトによっては全く書かれていなかったり、とても厳密に記述していたりと記述がまちまちなので、参画するプロジェクトがどの程度しっかりと書かれているのかを見ておきましょう。ベースとなる設計書や、バリデーションのガイドラインがプロジェクトとして用意されているのであれば、当然それに従うのが良いと思います。
app/views以下
views以下については、コントローラとメソッドの対応が、ディレクトリとファイル名の関係になっていることが多いので、コントローラから追っていけば対応関係は大体把握できるので、特に留意点もないのですが、layoutsファイルにapplication.html.erb以外のファイルがある場合は、controller側からlayoutメソッドを呼び出している箇所を検索しておくとよいです。views以下で使われているテンプレートエンジンはプロジェクトによってerbだったり、slimだったり、hamlだったり、jbuilderだったり、rablだったりしますが、記述の仕方が違うだけで、最終的にはhtmlやjsonファイルに変換されるという点では本質的には同じことなので、記述方法は公式ドキュメントで学びましょう。
app/helpers以下
ActionView内でレシーバなしで呼び出されているメソッドがあれば、Gemのメソッド、もしくはapp/helpers以下に定義されている可能性が高いです。application_helper.rbにまとめて書かれていて、他のモジュールは空という場合もあります。Railsでは
config.action_controller.include_all_helpersを明示的にfalseにしない限り、どこのhelperに記述しても結果は同じということもあり、どのhelperに書くかはプロジェクト次第なところがあります。まずはhelpers以下のファイルをざっと読んでみて、どういう指針で定義されているかは見ておきましょう。app/assets以下、もしくはapp/javascript以下
まだ多くのRailsプロジェクトはapp/assets以下で管理されていると思いますが、最近はWebpackerなどを導入するプロジェクトもあり、app/javascript以下にJavascriptが配置されていることもあります。ビルド時に何か問題が発生したときに、sprocketsなのかwebpackなのかは知っておく必要があります。
まとめ
以上、Railsのプロジェクトに参画しておくときに見ておくとよいものを、簡単にまとめてみました。
ここまで一通り読んでおくと、エンドポイントから機能をたどる時は、routes.rb → app/controllers/ → app/models → app/viewsと辿って、該当の機能を追うことができますし、新しい機能を追加するときも、周りの記述方針と合わせることができます。Railsに慣れないうちは、残念ながら目の前にあるコードが全てだと思うので、良し悪しを判断することは難しいと思いますが、まずは全体的に一貫性のある、統一感のあるコードが書けるようになることから始めましょう。
- 投稿日:2019-08-28T16:29:34+09:00
Rails | Devise: DoubleRenderError
Deviseをいじいじしたあと、サインアップを試みるとこんなエラーが...
AbstractController::DoubleRenderError in Users::SessionsController#create
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".解決方法
Devise: コントローラのオーバライドでredirect_toを使わない
- 原因となった箇所
def after_sign_up_path_for redirect_to users_path end
- 修正
def after_sign_up_path_for users_path end理由
redirect_to users_path # deviseの仕様上、上記の記述は以下と同義となってしまうから redirect_to(redirect_to(users_path))助けられた記事
- 投稿日:2019-08-28T16:00:11+09:00
Ruby on RailsのアプリをGAEでデプロイする方法
1.まずはじめに
ポートフォリオを作成する上で少しオリジナル性を出したくてherokuではなくGCPを利用しようとしたのがきっかけになります。
その中で一番時間を取らなさそうなGAEでアプリをデプロイすることにしましたのでその際にやったことを記述しております。2.準備したもの
2-1.rubyのバージョン
サーバーのrubyにバージョンとアプリのバージョンを揃えました。
※こちらでバージョンアップについて書いてます2-2.GitHubのレポジトリ
GitHubのレポジトリをクローンしますので必須になります。
2-3.app.yaml
ルートディレクトリーにapp.yamlファイルを作成しておきます。
app.yamlentrypoint: bundle exec rackup --port $PORT env: flex runtime: ruby3.GCP操作方法
3-1.GAE内での操作
クリックすると下記の画面になるかと思います。
その中の赤枠でカッコってあるものをクリックします。(Cloud Shellが起動されます)3-2.GitHubのクローン
Cloud Shellが起動したらまず初めに自身のレポジトリをクローンします。
$ git clone https://github.com/Nash-BETA/test.git Cloning into 'test'... remote: Enumerating objects: 8394, done. remote: Counting objects: 100% (8394/8394), done. remote: Compressing objects: 100% (6611/6611), done. remote: Total 8394 (delta 1502), reused 7935 (delta 1047), pack-reused 0 Receiving objects: 100% (8394/8394), 34.13 MiB | 9.60 MiB/s, done. Resolving deltas: 100% (1502/1502), done.3-4.GAEのgemのインストールおよびDBの作成
クローンが完了したらディレクトリを移動してアプリケーションコードを表示させます。
$ cd test $ cat app.yaml次にbundle installを行います。
$ bundle installDBの作成をします。
$ rake db:migrate3-5.サーバーの選択
GCPコマンドでアプリの作成およびサーバーの選択
$ gcloud app create You are creating an app for project [First project]. WARNING: Creating an App Engine application for a project is irreversible and the region cannot be changed. More information about regions is at <https://cloud.google.com/appengine/docs/locations>. Please choose the region where you want your App Engine application located: [1] asia-east2 (supports standard and flexible) [2] asia-northeast1 (supports standard and flexible) [3] asia-northeast2 (supports standard and flexible) [4] asia-south1 (supports standard and flexible) [5] australia-southeast1 (supports standard and flexible) [6] europe-west (supports standard and flexible) [7] europe-west2 (supports standard and flexible) [8] europe-west3 (supports standard and flexible) [9] europe-west6 (supports standard and flexible) [10] northamerica-northeast1 (supports standard and flexible) [11] southamerica-east1 (supports standard and flexible) [12] us-central (supports standard and flexible) [13] us-east1 (supports standard and flexible) [14] us-east4 (supports standard and flexible) [15] us-west2 (supports standard and flexible) [16] cancel Please enter your numeric choice:14を選択。(特に理由がないのですがus-east4を選択しました)
3-5.app.yamlファイルにシークレットコードの追加
$ bundle exec rails secret [シークレットキーが表示されます] $ vim app.yaml entrypoint: bundle exec rackup --port $PORT env: flex runtime: rubyvim上でoをタイピングして挿入モードにして、下記の様に追加する。
app.yamlentrypoint: bundle exec rackup --port $PORT env: flex runtime: ruby env_variables: SECRET_KEY_BASE: [シークレットキー]escキーを押して:wqを入力すれば、変更内容の保存完了
3-6.デプロイして完了
$ gcloud app deploy Updating service [default] (this may take several minutes)...done. Setting traffic split for service [default]...done. Deployed service [default] to [https://Firstproject.appspot.com]これで完了
- 投稿日:2019-08-28T15:10:15+09:00
Rails 画像アップロード機能の実装方法 メモ
初めに
railsで画像をアップロードする機能を実装するには、carrierwaveというgemを使う方法があります。
今回、画像を投稿する機能について学習したので、自分なりにまとめておこうと思います。使用するgem
・Carrierwave
画像をアップロードするために必要な機能を追加するためのgem
※Carrierwaveの導入には「ImageMagick」がインストールされている必要があるので、
実装前に以下のコマンドを入力して置くといいかもです。$ brew install imagemagick・Minimagick
画像に対して、画像同士を合成したり、リサイズしたりと編集することができるようになるためのgem
実装
では実装に入っていきます。
1.まずは上記の2つのgemを扱えるようにするためGemfileに以下のように追記します。Gemfilegem 'carrierwave' gem 'mini_magick' #画像に対して処理を行う場合2.gemをGemfileに追記したのでターミナルで以下のコマンドを実行します。
$ bundle install3. 次にcarrierwaveを利用するためのアップローダーを作成します。carrierwaveを導入したことで以下のコマンドが利用できるようになったので、ターミナルで実行しましょう。
$ rails g uploader image ※imageの箇所は任意の名前でOKです。例:pictureなどするとapp/uploadersディレクトリ以下にimage_uploader.rbが作成されます。
このファイルに別途記述を行うことで、アップロードの仕方を設定できます。
※設定方法は下記5.以降を参照4.アップローダーが作成できたので、画像アップロード機能を実装したいモデルに対して編集を行っていきます。
モデル名.rb# app/models/モデル名.rb mount_uploader :image, ImageUploader上記を追記することで、アップローダーを任意のモデルに対して
マウントすることができました。
※マウント
取り付ける、搭載するなどの意味↑↑↑画像をアップロードする機能のみの場合はここまで↑↑↑
5.アップロードする画像に対して別途処理を行いたい場合は
以下の設定を行います。
3.で行ったrails g uploader imageで作成された
image_uploader.rbを確認し、
include CarrierWave::MiniMagickがコメントアウトされているので、これを以下のように解除しましょう。
コメントアウトを解除することでgem 'mini_magick'を使用することができるようになります。app/uploaders/image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagickこれでmini_magickの機能が使用できるようになったので
例えば、image_uploader.rbの任意の箇所に以下のコードを追記すると、縦横比を維持したまま、width, heightを800pxにリサイズしアップロードすることができるようになります。app/uploaders/image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick # ~省略~ process resize_to_fit: [800, 800] endおわりに
以上がgemのCarrierwaveとmini_magickを使用した画像のアップロード機能の実装になります。
画像の投稿はwebアプリケーションにおいて基本的な機能ですので抑えて置きたいと思います。使用したgemのGithub
・Carrierwave
https://github.com/carrierwaveuploader/carrierwave
・mini_magick
https://github.com/minimagick/minimagick
- 投稿日:2019-08-28T12:48:17+09:00
Rails6 のちょい足しな新機能を試す71(implict_order_column 編)
はじめに
(多分)Rails 6 に追加された新機能を試す第71段。 今回は、
implicit_order_column編です。
Rails 6 では、firstやlastで使われるソートのカラムをimplicit_order_columnで指定できるようになっています。
これは、id を UUID にしていた場合に、firstやlastが予測できる結果となるようにするためのようです。
(Rails 6.0.0 がリリースされましたが、確認当時は、 Rails 6.0.0.rc2 が最新でした。悪しからず)
Ruby 2.6.3, Rails 6.0.0.rc2, PostgreSQL 10.7 で確認しました。Rails 6.0.0.rc2 は
gem install rails -v 6.0.0rc2 --prereleaseでインストールできます。$ rails --version Rails 6.0.0.rc2今回は、 User モデルを作成して
rails consoleを使って確認します。プロジェクトを作る
rails new rails_sandbox --database postgresql cd rails_sandboxUser モデルを作る
bin/rails g model User nameid を UUID にする
id を UUID にするために、マイグレーションのファイルを変更します。
db/migrate/20190803221758_create_users.rbclass CreateUsers < ActiveRecord::Migration[6.0] def change enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto') #この行を追加 create_table :users, id: :uuid do |t| # id: :uuid オプションを追加 t.string :name t.timestamps end end endseed データを作成する
seed データを作成します。 name の昇順にデータを登録します。
db/seeds.rbUser.create(name: 'Andy') User.create(name: 'Bob') User.create(name: 'Cindy')マイグレーションを実行し seed データを登録する
bin/rails db:create db:migrate db:seedrails console で確認する
rails consoleで確認します。
User.firstを実行してみます。irb(main):001:0> User.first User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]] => #<User id: "5da612ca-f055-4678-9c87-927b0a1a28d2", name: "Cindy", created_at: "2019-08-03 22:24:51", updated_at: "2019-08-03 22:24:51">結果が Cindy になってしまいました。 (結果は必ず Cindy になるとは限りません。)
SQL を確認するとidでソートされていることもわかります。
User.lastを実行してみます。irb(main):002:0> User.last User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]] => #<User id: "fe31d9a5-8de0-4d35-8a6f-183902f2884b", name: "Bob", created_at: "2019-08-03 22:24:51", updated_at: "2019-08-03 22:24:51">結果が Bob になりました。
SQLを確認するとidの降順にソートした最初のレコードが検索されているのがわかります。implicit_order_column を使う
User モデルを変更して implicit_order_column で
:nameを指定してみます。app/models/user.rbclass User < ApplicationRecord self.implicit_order_column = :name endrails console で確認する
修正を反映させるため
reload!します。irb(main):003:0> reload! Reloading... => true
User.firstの結果は、 Andy になります。nameの昇順でソートされていることがわかります。irb(main):004:0> User.first User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."name" ASC LIMIT $1 [["LIMIT", 1]] => #<User id: "cc7ff0b1-4dc3-47f4-a5cd-259a804c9690", name: "Andy", created_at: "2019-08-03 22:24:51", updated_at: "2019-08-03 22:24:51">
User.lastの結果は、 Cindy になります。nameの降順でソートされていることがわかります。irb(main):005:0> User.last User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."name" DESC LIMIT $1 [["LIMIT", 1]] => #<User id: "5da612ca-f055-4678-9c87-927b0a1a28d2", name: "Cindy", created_at: "2019-08-03 22:24:51", updated_at: "2019-08-03 22:24:51">複数カラムの指定はできない
app/models/user.rbself.implicit_order_column = %i[name created_at]とした場合は、 ActiveRecord::StatementInvalid エラーになりました。
まあ、そりゃそうですよね。 複数形 (implicit_order_columns) じゃないですもんね。irb(main):011:0> User.first Traceback (most recent call last): 2: from (irb):11 1: from (irb):11:in `rescue in irb_binding' ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column users.[:name, :created_at] does not exist) LINE 1: SELECT "users".* FROM "users" ORDER BY "users"."[:name, :cre... ^試したソース
試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try071_implicit_order_column参考情報
- 投稿日:2019-08-28T00:59:12+09:00
bundle install時にnokogiriエラー(nokogiriのバージョン1.10.4)
Railsで新しいプロジェクトを作ろうと、
bundle install —path vendor/bundle
を実行した際に、nokogiriのインストールでエラーが発生しました。環境
$ brew --version
Homebrew 2.1.10
Homebrew/homebrew-core (git revision ea340; last commit 2019-08-20)
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18]
$ gem --version
3.0.3エラーメッセージ
エラーメッセージの中から原因と対処法が記された部分を抜粋します。
IMPORTANT NOTICE: Building Nokogiri with a packaged version of libxslt-1.1.33 with the following patches applied: - 0001-Fix-security-framework-bypass.patch Team Nokogiri will keep on doing their best to provide security updates in a timely manner, but if this is a concern for you and want to use the system library instead; abort this installation process and reinstall nokogiri as follows: gem install nokogiri -- --use-system-libraries [--with-xml2-config=/path/to/xml2-config] [--with-xslt-config=/path/to/xslt-config] If you are using Bundler, tell it to use the option: bundle config build.nokogiri --use-system-libraries bundle install対処法
Nokogiriのインストールに必要なlibxml2ライブラリというものがNokogiriの中に同梱されているのですが、これをsystem内にあるlibmxl2を利用するように設定するとうまくいくとのことです。
今回はbundleを使ってインストールするので、下記のコマンドでうまくいきました。bundle config build.nokogiri --use-system-libraries bundle install —path vendor/bundleまとめ
どうやら古より伝わるエラーらしく、様々な対処法がネットで報告されています。
再発率の高い厄介なエラーですね…今回はエラーメッセージに対処法がそのまま載っていました。ググることも大事ですが、その前にエラーメッセージや公式リファレンスに目を通すことが大事ですね。
- 投稿日:2019-08-28T00:38:22+09:00
大学2年生のオリジナルWebアプリケーション開発7日目
今日の流れ
- Eventモデル
- Event一覧の表示
- dateの短縮日付表示
改めて僕の開発しているアプリをご紹介しますと、出欠管理アプリを開発しています。
そのきっかけはサークルの出欠管理が非効率だったので、せっかくだから自分で作ってみようというところから始まりました。管理する出欠は何かしらのイベント(Event)なので、Eventモデルを構築することを考えました。
1. Eventモデル
Eventモデルには何が必要なのか考えた結果、次のようなカラムとそのデータ型を選択しました。
カラム名 データ型 user_id references event_name string date datetime memo text カラム以外は基本的にRailsチュートリアルのmicropostをeventに置き換えただけとなっています。
次にイベントモデルのバリデーションを考えました。
user_id, event_name, dateには存在の、event_nameとmemoには文字数制限のバリデーションを施しました。そして、userとeventを関連付けるためにbelong_to/has_manyを利用しました。
models/event.rbbelongs_to :usermodels/user.rbhas_many :events, dependent: :destroy2. Event一覧の表示
次にユーザーのページにそのユーザーの企画したEvent(フォームから作成予定)を一覧表示させました。
この部分はRailsチュートリアル第13章の13.2にあたります。
micropostの部分をeventに変更させるだけでしたが、後述するdateの表示で手間取りました。3. dateの短縮日付表示
今日最も時間がかかったのはこの部分です。Railsチュートリアルにはdatetimeや日付表示に関して記述はなかったので自分で模索しながら進めました。
Event一覧中のdateの表示は短縮日付(正確な呼び名は知りません)、yyyy/mm/dd(曜日)という形で表示したかったのです。
調べているとすぐにstrftimeというメソッドを用いればよいことがわかりました。
しかし、以下の多くの要因で混乱してなかなかうまく表示されませんでした。具体的には、yyyy/mm/dd(曜日)としたいのに、2019-08-026 17:52:54 +0900みたいな感じで表示されていました。
要因:
- strftimeで作られるオブジェクトはStringクラスであること
- ブロック付きメソッドを用いていた
- 時間を表すクラスには、Timeクラス, Dateクラス, DateTimeクラスなど複数存在すること
一つ目の要因
まず、一つ目の要因への策として、一回dateカラムのデータ型をstringに変更して解決はできたのですが(その方法が気になる方はこちら)、後のフォームのことを考えてやはりdatetimeでいくことにしました。
最終的な結論は、viewで表示される2019-08-026 17:52:54 +0900をview側でyyyy/mm/dd(曜日)表示にしてしまうということです。これに気づいたことが大きかったです。これらのサイトを参考にさせていただきました。
https://www.javadrive.jp/ruby/date_class/index5.html
https://techracho.bpsinc.jp/hachi8833/2016_10_06/25960二つ目の要因
db/seeds.rb40.times do |n| endの "n" が混乱させてきたのですが、まあこれはdb/seeds.rbの中でRailsチュートリアルと同様にしていれば平気でした。
とりあえず、rails cを多用して実験しまくりました!三つ目の要因
こちらのサイトなどを拝見したところ、どうも時間を扱うクラスは奥が深くここではすべてを理解することはやめました。
試行錯誤していると、dateのデータ型はdatetimeですが、timeとかでもうまくRailsがやっていてくれている感じはしていました。
まあ、面倒くさいという理由からすべての時間はto_datetimeでdatetimeに無理やり直しました。
最終的に
最終的には次のようにした結果、うまくいきました。
db/seeds.rbusers = User.order(:created_at).take(2) 40.times do |n| event_name = "練習#{n}" date = n.days.ago.to_datetime # datetimeに変更 memo = "楽しみましょう" users.each { |user| user.events.create!(event_name: event_name, date: date, #seed.rbでstrftimeを使うとstringとなるが、viewで(date)time?に変更されてしまう(2019-08-026 17:52:54 +0900の表示になる) memo: memo) } endviews/events/_event.html.erb<% d = event.date %> <%# この時点ではdatetimeとして受け取っている %> <% wd = ["日", "月", "火", "水", "木", "金", "土"] %> <span class="date">日時:<%= d.strftime("%Y/%m/%d(#{wd[d.wday]})") %></span> <%# datetimeのdをここでstrftimeを用いてstringに変更している(yyyy/mm/dd(曜日)の表示になる) %>終わりに
今日から本格的にオリジナルの部分を作り始めました。まぁRailsチュートリアルの第13章を参考にしているのですが。とにかく大変で時間がかかるのですが、その大変さを乗り越えるために必要なものとして今回気づいたのは
- ググる力
- rails c
が大事だということ。
ググる力はもちろんですが、Railsチュートリアルでなんとなく使っていたrails cがこんなに大事だとは思いませんでした。コンソールでの実験がいかに強力かわかりました。
明日はフォームの作成をしようと思います。個人的にはまだモデルや、モデルのカラムが足りないかなと思っていて、今後はEventにパスワード?やShareLinkモデルやAnswerモデルなんかが必要になるのかと思っていますので、モデルを考える時間がさらに必要だと考えています。とりあえず今日はこの辺で。





