- 投稿日:2020-02-15T23:23:17+09:00
ルーティングのshallowオプション
ルーティングのネストを調整するときに、shalowオプションというのを使いました。
書き方は以下です。routes.rbRails.application.routes.draw do resources :boards do resources :comments, shallow: true endshallowオプション有りとshallowオプション無しの比較
- shallowオプションなし
- shallow: trueオプション
edit,show,destroy,updateに、親の/boards/board_idが付かず、ネスト(入れ子)じゃ無くなっています。
逆に、shallow: trueをつけないと、/boards/board_idが全部につくURLになります。
- 投稿日:2020-02-15T22:35:55+09:00
rails db:reset、rails db:migrate:reset、rails db:setupの違い
rails db:reset、rails db:migrate:reset、rails db:setupの違いをたまにどれがどれだったか忘れるので、まとめておきます。
rails db:reset
- rails db:reset の場合は、新しくテーブルを作り直して、さらにシードファイルも読み込んでダミーデータも作ってくれる。新しくテーブルを作り直すので、既存のデータは全て消えて新たにシードファイルを元に作り直す。
Dropped database 'hogehoge_development' Dropped database 'hogehoge_test' Created database 'hogehoge_development' Created database 'hogehoge_test'
- rails db:reset はマイグレーションファイルを編集しても、その内容は反映されない。マイグレーションを実行しない。スキーマファイル ( db/schema.rb ) だけを利用する。
rails db:migrate:reset
- 新たにテーブルを作り直すので、テーブルのデータはもちろん全部消える。※ここは、
$ rails db:reset
と同じDropped database 'hogehoge_development' Dropped database 'hogehoge_test' Created database 'hogehoge_development' Created database 'hogehoge_test'
db/migrate/hoge.rbの、マイグレーションも実行。DB をdrop した後、通常通りのマイグレート(db:migrate)が行われる。つまり、db/migrate/hoge.rb が古い順から全て実行される。
シードファイルは読み込まないので、新しいダミーデータは生成されない。($ raild db:seedは自分で実行する必要がある)
rails db:setup
- アプリケーションのDB周りの初期設定をしてくれる。以下3つのコマンドを実行してくれる
- $ rails db:create
- $ rails db:schema:load
- $ rails db:seed
余談(現場での運用)
現場では開発者が複数人いるので、テーブルに変更を加える際は、既存のマイグレーションファイルに変更を加えないようにするのが鉄則です。
【理由】
既存のマイグレーションファイルに変更を加えると、共同開発などをしている際に、共同開発しているメンバーも$ rails db:migrate:reset
しないといけなくなる【ベストプラクティス】
新しいマイグレーションファイルを作成し、テーブルに変更を加えるこのような対応をすると、共同開発しているメンバーは
$ rails db:migrate
するだけで良くなります!
ただ、開発環境なら共同開発してるメンバーに$ rails db:migrate:reset
してくださいと周知すれば済むので、その対応でも良いですが、本番環境で$ rails db:migrate:reset
や$ rails db:reset
をすると、DBの値が全部吹き飛び、大事故になるため、ご注意を。
- 投稿日:2020-02-15T21:52:33+09:00
【Rails】Active Jobについてゼロから理解する
概要
RailsのActive Jobについてゼロから理解する為に調べたことをまとめます。
- 本格的なアプリでは往々にして時間のかかる処理が発生する
- 大量のデータ集計、外部サービスとの連携、メール送信など
- そしてこれらの処理はリアルタイムに完了してなくても良い場合もある
アプリから実行すべき処理(ジョブ)を待ち行列(キュー)に登録して後から実行(非同期実行)することで、アプリのレスポンスを改善できる!
Active Jobとは、そのようなジョブの管理から実行までの管理する為のモジュール
- Active Jobそのものは、基本的に、ジョブ操作の為のインタフェース(メソッド名などの決まりごと)を提供しているに過ぎない
- 実際にジョブを実行するのはジョブ管理ライブラリの役割(Delayed JobやSidekiq)
アダプターを切り替えれば、アプリケーションをほとんど改修することなくバックエンドのジョブ管理ライブラリを自由に切り替えられる
やってみた
delayed_jobを利用する
delayed_jobをインストールする
- Gemfileに追記
gem 'delayed_job_active_record'
bundle install
$ bundle install
delayed_job
び必要なファイルを生成$ bundle exec rails generate delayed_job:active_record create bin/delayed_job chmod bin/delayed_job create db/migrate/20200121063912_create_delayed_jobs.rb
- マイグレを打って
delayed_jobs
テーブルを追加
- 非同期で実行する処理を一時的に管理するテーブル
$ bundle exec rails db:migrate == 20200121063912 CreateDelayedJobs: migrating ================================ -- create_table(:delayed_jobs, {:force=>true}) -> 0.0292s -- add_index(:delayed_jobs, [:priority, :run_at], {:name=>"delayed_jobs_priority"}) -> 0.0226s == 20200121063912 CreateDelayedJobs: migrated (0.0519s) =======================
- Active Jobでdelayed_jobを有効にする(
config/application.yaml
)# using delayed_job config.active_job.queue_adapter = :delayed_joジョブを動かしてみる
- ジョブを作成する
$ bundle exec rails g job Sleep create app/jobs/sleep_job.rb
- 生成されたジョブを編集する
performメソッド
がジョブの実処理を表す- メソッド配下に非同期実行する処理を記述する
def perform() puts("start sleep") sleep 10 end
- ジョブを登録してみる(rails consoleから)
- ジョブを登録するには
クラス名.perform_later(...)
とする- これでジョブをキューに登録しなさい、という意味になる
- テストでジョブを即座に実行したい場合は
perform_now()
を利用するirb(main):002:0> SleepJob.perform_later() (0.4ms) SET NAMES utf8mb4, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 (0.2ms) BEGIN Delayed::Backend::ActiveRecord::Job Create (0.9ms) INSERT INTO `delayed_jobs` (`handler`, `run_at`, `queue`, `created_at`, `updated_at`) VALUES ('--- !ruby/object:ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper\njob_data:\n job_class: SleepJob\n job_id: 6643be62-59fd-4ea3-81a1-c14fcd13aafb\n provider_job_id: \n queue_name: default\n priority: \n arguments: []\n executions: 0\n exception_executions: {}\n locale: en\n timezone: UTC\n enqueued_at: \'2020-01-21T06:53:15Z\'\n', '2020-01-21 06:53:15', 'default', '2020-01-21 06:53:15.343196', '2020-01-21 06:53:15.343196') (0.4ms) COMMIT Enqueued SleepJob (Job ID: 6643be62-59fd-4ea3-81a1-c14fcd13aafb) to DelayedJob(default) => #<SleepJob:0x00007f9cf4c456d8 @arguments=[], @job_id="6643be62-59fd-4ea3-81a1-c14fcd13aafb", @queue_name="default", @priority=nil, @executions=0, @exception_executions={}, @provider_job_id=1>
- delayed_jobを起動してキューに登録されたジョブを実行する
$ bundle exec bin/delayed_job startジョブ実行のカスタマイズ
delayed_jobの動作パラメータ
config/initializer
配下にdelayed_job.rb
のような初期化ファイルを作成
- delay_jobs:ジョブの遅延実行を有効化するか
- max_attemps:最大リトライ回数
- max_run_time:最大実行回数
- destroy_failed_jobs:失敗したジョブを破棄するか
- read_ahead:一度に読み込むジョブの個数
- sleep_delay:実行ジョブがない場合のsleep期間
キューの名前を設定する
- キューを分けることで、特定のキューだけを優先して実行するなどの仕分けが可能になる
queue_as
メソッドを利用するqueue_name_prefix
パラメータでprefixを宣言するset
メソッドを利用するirb(main):006:0> SleepJob.set(queue: :sleep).perform_later()コールバック
ジョブを登録/実行するタイミングで実行されるメソッド、またはその仕組のこと
- 投稿日:2020-02-15T20:16:07+09:00
【Rails】simple_calendarを日本語表記にする方法
Railsでsample_calendarを導入したのですが、日本語表記にするのに少し手間取りました。
しかし、無事解決することができたので共有したいと思います。
Railsのバージョン 5.2.3 Rubyのバージョン 2.5.1 手順1
config/application.rbconfig.i18n.default_locale = :jaまずはこいつを追加します。
手順2
config/locales/ja.ymlja: date: abbr_day_names: - 日 - 月 - 火 - 水 - 木 - 金 - 土 abbr_month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 day_names: - 日曜日 - 月曜日 - 火曜日 - 水曜日 - 木曜日 - 金曜日 - 土曜日 formats: default: "%Y/%m/%d" long: "%Y年%m月%d日(%a)" short: "%m/%d" month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 time: am: 午前 formats: default: "%Y年%m月%d日(%a) %H時%M分%S秒 %z" long: "%Y/%m/%d %H:%M" short: "%m/%d %H:%M" pm: 午後次に上記記述を追加してください。ネストには気をつけてくださいね。
すると
完璧ですね。
- 投稿日:2020-02-15T18:42:16+09:00
[Rails]Chart.jsでコーヒーのテイストを視覚化
実装すること
コーヒーの味データをTaistテーブルに格納します。
Taistテーブルの「refresh,bitter,body,fruity」の4つのカラムデータをChart.jsを使用して描画します。
railsの変数は「gon」というGemを使用してjavascript内でも使用します。Taistテーブル定義
カラム名 カラム説明 データ型 デフォルト ID テイストID integer title タイトル string refresh スッキリ integer bitter ビター integer body コク integer fruity フルーティ integer created_at 登録日 datetime NOW updated_at 更新日 boolean NOW 完成形イメージ
Gemに追記
Gemfile.gem 'gon'$ bundle installChart.jsを読み込む
Chart.jsを使用するために、CDNに公開されているjsファイルをapplication.html.erbのhead内で読み込みます。
app/views/layouts/application.html<head> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script> </head>Chart.jsを表示する
taists.controlloer.rb
Taistテーブルの「refresh,bitter,body,fruity」の4つのカラムデータを持ったレコードをgonを使用した変数に代入します。
前提として、コーヒーのテイストによって、「refresh,bitter,body,fruity」に入るデータは異なることとします。app/controllers/taists_controller.rbdef show @taist = Taist.find(params[:id]) #railsの変数をchart.jsに渡す gon.taist = @taist endtaists/show.html.erb
グラフ描画エリアの指定に、canvas要素を使用します。
app/views/taists/show.html<canvas id="showRaderChart"></canvas>javascripts/application.js
・先ほど設定した、canvas要素(ID:showRaderChart)を取得し、変数ctxに代入します。
・var taist = gon.taist
でrailsでの変数をgonを使用してjavascript内でも使用します。
・type: 'radar'
で、描画するグラフの種類をレーダーチャートにしています。app/assets/javascripts/application.jsvar ctx = document.getElementById("showRaderChart"); var taist = gon.taist var showRadarChart = new Chart(ctx, { type: 'radar', data: { labels: ["スッキリ", "ビター", "コク", "フルーティ"], datasets: [{ label: taist.title, data: [taist.refresh, taist.bitter, taist.body, taist.fruity], backgroundColor: 'RGBA(135, 206, 235, 0.5)', borderColor: 'RGBA(65, 105, 225, 1)', borderWidth: 1, pointBackgroundColor: 'RGB(46,106,177)' }] }, options: { title: { display: true, text: '' }, scale:{ ticks:{ suggestedMin: 0, suggestedMax: 3, stepSize: 1, callback: function(value, index, values){ if(value == 1){ return "やや感じる" }else if(value == 2){ return "感じる" }else{ return "強く感じる" } } } } } });チャートに表示される項目ですが、
suggestedMin: 0
で、最小を0,suggestedMax: 3
で最大を3とし、
この値がvalue == 1
なら、"やや感じる"と表示、2なら"感じる"、3なら"強く感じる"と表示するようにしています。application.jsscale:{ ticks:{ suggestedMin: 0, suggestedMax: 3, stepSize: 1, callback: function(value, index, values){ if(value == 1){ return "やや感じる" }else if(value == 2){ return "感じる" }else{ return "強く感じる" } } } }最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
Twitter:https://twitter.com/yto_oct
note:https://note.com/yto_oty参考
公式ドキュメント
http://www.chartjs.org/
Chart.jsでグラフを描画してみた
https://qiita.com/Haruka-Ogawa/items/59facd24f2a8bdb6d369
gonを使ったRailsとJavascriptの連携について
https://qiita.com/s_nakamura/items/5d153f7d9db1b1190296
- 投稿日:2020-02-15T18:07:15+09:00
【Rails】リロードしないとJavaScriptが動かない!【簡単に解決】
はじめに
以前
jQuery
を使ってタブメニューの切り替えを実装したときに、
ビューを開いた初回は非同期で切り替わるのに、
別のページに移動したり
「戻る」ボタンを押したあと再びタブメニュー画面に戻ると、、、、動かない、、、!なぜだ、、!
なんて経験をして1日無駄にしたことがあります。
jQueryやビュー画面のコードは間違ってないはず!なぜ!って人向けの記事です。解決方法①
各
link_to
ごとにturbolinks
とやらを無効にすればOKです。簡単です。<%= link_to "ホーム", root_path, data: {"turbolinks" => false} %>
data: {“turbolinks" => false}
は遷移先ページのみturbolinks
を切ることが出来ます。※
turbolinks
とは、ページ遷移をAjax
に置き換え、JavaScript
やCSS
のパース(解析・変換)を省略することで高速化するgem
で、Rails4
からはデフォルトで使用されています。↓gem 'turbolinks', '~> 5'解決方法②
js
ファイルに"turbolinks:load"
を記述すればOKです。簡単です。document.addEventListener("turbolinks:load", function() { // ... })まとめ
turbolinks
を無効化する方法として、gem
ファイルから直接削除する方法もあるようですが、
元々turbolinks
はページの読み込みを高速化するgemです。ボタンを押すたびに画面が初期化され、
一旦真っさらになった後、再度js
やらcss
を読み直す。。。待ってられません。その辺の処理を
Turbolinks5
が全部やってくれているんです。なるべく
①個別のページ(link_toごと)に無効にするか、
②jsファイルにコードを加えるだけにしましょう。
- 投稿日:2020-02-15T17:35:30+09:00
新規Railsプロジェクト作成手順(Rails 6)
目的
Ruby on Rails 6,MySQL,RSpecを用いた環境を構築し,サーバーを起動してエラーなく最初のページが表示されるようにする.
開発環境
MacOS High Sierra 10.13.6
Ruby 2.7.0
Ruby on Rails 6.0.2.1前提
- rbenv,Rubyのインストール
- MySQLのダウンロード
- rbenvへのbundleインストール(下記)
$ rbenv exec gem install bundler $ rbenv rehash手順
- プロジェクトを作成したいディレクトリに移動
- Railsインストール
- 上記のRailsを元にrails new
- Railsプロジェクトのセットアップ
1. プロジェクトを作成したいディレクトリに移動
$ mkdir projects $ cd projects2. Railsインストール
- Gemfile作成
- Gemfile書き換え(もちろんvimをはじめとしたエディタで編集してもおk)
- Railsインストール
$ bundle init $ cat << EOS > Gemfile source "http://rubygems.org" gem "rails", "6.0.2.1" EOS $ bundle install #指定したバージョンのRailsがインストールされていることを確認 $ bundle list
bundle install
に--path vendor/bundle
をつけるかどうか論議が発生しますが,基本的には付けなくて大丈夫とのこと.
またBundler2.1においては--path
オプションをつけると警告が出ます.それを回避するためには以下のようにしてbundlerの設定を変更する方がベターですが,この設定を行わなくても基本的にはおkです.$ bundle config set --local path 'vendor/bundle'ちなみに自分は以前pathを指定しておりその名残でvendor/bundleにbundle_pathが通っているため,以下ではbundle execを頭につけてrailsコマンドを実行しますが,そうでない方は単純にrailsコマンドを入力していただければいいかと.
3. 上記のRailsを元にrails new
# alias設定 $ alias be='bundle exec' $ be rails new app_name -B -d mysql --skip-testrails newの際にオプションをつけることでMySQLおよびRSpecのためのプロジェクトであることを示しています.
-B
は--skip-bundle
と同義であり,先のbundle installにおいてvendor/bundle
へのパス指定を行わなかった場合は必要ないと思われます.4. Railsプロジェクトのセットアップ
残念ながらこれだとRails6で標準のwebpackerがインストールされていないため,サーバーを起動してもエラーが起こります.なのでこれを解決するために以下.
$ brew install yarn $ be rails webpacker:installyarnは一度インストールしておけば問題ないですが,webpackerは
(be) rails new
してプロジェクトを新規作成するたびにする必要があります.次にRSpecをgemに追加.もちろんプロジェクト内のGemfileに対して.
group :development, :test do (省略) gem 'rspec-rails' end毎度おなじみbundle install.
$ bundle install
bundle install
が作成したプロジェクト内のvendor/bundle
に対して行われた場合には,そのディレクトリをgitで扱わないようにするために以下を追加します.$ echo '/vendor/bundle' >> .gitignoreそして最後にデータベースを生成し,サーバーを起動します.
$ be rails db:create $ be rails sこれでエラーなくサーバーが起動されるはず.
※ ローカルにRailsをインストールした場合
2でローカルのvendor/bundleにbundle installした場合,Gemfileやvendor/bundleを消すのがいいかと.
新しいプロジェクト作成するたびに溜まっていってしまうので.$ rm -f Gemfile $ rm -f Gemfile.lock $ rm -rf .bundle $ rm -rf vendor/bundle最後に
ようやく環境が整ったのでゴリゴリポートフォリオ作っていきたいと思います.
追記
Rails 6で環境整えたはいいものの,やはりまだ情報が出回ってないということで初心者のうちはRails5でポートフォリオ作ることにします.
現場の環境としてもまだまだ5系が多いというお話もお聞きしますし.
- 投稿日:2020-02-15T17:23:40+09:00
[Rails]ransackを使わずに検索機能の実装
実装すること
投稿(Item)のタイトル(title)の検索
モデルの作成
$ rails g model Item title:string $ rails db:migrateルーティングの作成
config/routes.rbRails.application.routes.draw do get "search" => "items#search", as: 'search' endItemコントローラーの作成・編集
$ rails g controller itemsapp/controllers/items_controller.rbclass ItemsController < ApplicationController def search #itemのtitleを曖昧検索 @items = Item.where('items.title LIKE(?)', "%#{params[:search]}%").order(created_at: :desc) #フォームに入力した内容を取ってくる @search_result = "#{params[:search]}" end endビューの編集
投稿一覧
検索フォームはパーシャルにします。
app/views/items/index.html<div class="search"> <%= render 'items/search_form' %> </div>検索フォーム
app/views/items/_search_form.html<%= form_tag(users_search_path, :method => 'get') do %> <%= text_field_tag :search, params[:search], class: "form-control", id: "search_area", placeholder: "タイトルを入力"%> <%= submit_tag '検索' %> <% end %>検索結果
app/views/items/search.html<h2>タイトル"<%= @search_result %>"の検索結果(<%= @items.count %>件)</h2> <% @items.each do |item| %> <%= item.title %> <% end %>最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
Twitter:https://twitter.com/yto_oct
note:https://note.com/yto_oty参考
gemを使わずに検索機能を作ろう!複数テーブルの検索にも対応可能!
https://qiita.com/chain_saw_man/items/07faf8fed3224c1dccfb
Ruby on Rails 検索機能拡張 (railsチュートリアル)
https://qiita.com/mochikichi321/items/5c9630c5d87b47130942
- 投稿日:2020-02-15T16:52:22+09:00
[Rails]親子同時保存(cocoon)とリコメンド機能(おすすめ機能)の実装
実装すること
今回は私が作成した、「お気に入りのコーヒーを投稿するポートフォリオ」で実装した「あなたへのおすすめ機能」のコードを書いていきます。
簡単にポートフォリオについて説明させて頂くと、
・ユーザーは飲んだコーヒーを投稿します。(Itemテーブル)
・投稿する際に飲んだコーヒーのテイスト(Taistテーブル)を16個のブロックから選びます。
・その投稿に対してユーザーはいいねをすることができます。(Likeテーブル)
・ログインユーザーはマイページに「あなたへのおすすめ」の投稿(Item)が表示されます。
「あなたへのおすすめ」を表示する流れは下記となります。
①ログインユーザーが一番最近いいねした投稿と同じテイストデータの投稿を取ってくる
②①の投稿の中でまだログインユーザーがいいねしていない投稿を取ってくる
③②の投稿の中で最新の投稿を1つ取ってくる(②が無い場合はランダムに1つ取ってくる)※いいね機能に関しては、下記リンク先で説明しております。
https://qiita.com/yuto_1014/items/78d8b52d33a12ec33448イメージ
新規投稿をする際に、飲んだコーヒーのテイスト(Taistテーブル)を16個のブロックから選びます。
16個のブロックには、それぞれTaistテーブルの「refresh,bitter,body,fruity」の4つのカラムに入る数字が割り振られています。
(例)左上なら、refresh=3, bitter=0, body=0, fruity=3ER図
Taistテーブルは、投稿(コーヒー)のテイスト(味)データを格納します。
Itemテーブルは、投稿(コーヒー)のデータを格納します。
関係は、Taist:Item = 1:Nになります。アソシエーションの確認
Taistモデル
accepts_nested_attributes_for
は、Railsが標準で提供している、ActiveRecordのメソッドの一つです。モデル同士が関連付けられている時に、ネストさせることで一度にまとめてレコードの更新ができるようになります。
今回はTaistレコードが作成・更新されるとそれに紐づいたItemレコードも作成・更新されるようにしています。
allow_destroy: true
をattributesメソッドに追加することで削除可能になります。削除するには対象となるレコードに_destroy: 1
のようなパラメーターを渡します。app/models/taist.rbclass Taist < ApplicationRecord #アソシエーション has_many :items #親子同時保存(親:taist/子:item) accepts_nested_attributes_for :items, allow_destroy: true endItemモデル
app/models/item.rbclass Item < ApplicationRecord belongs_to :taist belongs_to :user has_many :likes, dependent: :destroy has_many :liked_users, through: :likes, source: :user #refile attachment :image end投稿機能の作成(コントローラー・ビュー)
taists_controller.rb
親子同時保存(Taistテーブル・Itemテーブル)にはcocoonを使用しています。
画像投稿はrefileを使用しています。app/controllers/taists_controller.rbclass TaistsController < ApplicationController def new #cocoonで親子同時保存 @taist = Taist.new @taist.items.build end def create @taist = Taist.new(taist_params) if @taist.save render :index else render :new end end private def taist_params params.require(:taist).permit(:fruity, :refresh, :body, :bitter, items_attributes: [:id, :title, :taist_id, :image, :user_id]) end endtaists/new.html.erb
・Taistモデルの子要素であるItemモデルは、
fields_for
を使用してフォームを作成します。
・f.label '✔︎', for: 'refreshA'
は、id: 'refreshA'のラジオボタンに紐づいています。
・refresh以外のbiiter,body,fruityはhidden_field
で隠し、disabled: "disabled"
で初期状態では保存されないようにしています。javascriptを使用して、id=refreshAがチェックされていた時は、class=refreshAのhidden_fieldを持つdisabled: "disabled"
を解除することで、特定のデータのみ保存するようにします。app/views/taists/new.html<h1>新規投稿</h1> <h5>コーヒーマップ</h5> <%= form_for(@taist, url: users_taists_path, remote: true) do |f| %> <%= f.fields_for :items do |item| %> <%= render 'item_fields', f: item %> <% end %> <div class="refresh_contentA"> <%= f.radio_button :refresh, 3, class: "radio_btn", id: 'refreshA' %> <%= f.label '✔︎', for: 'refreshA' %> <%= f.hidden_field :bitter, :value => 0, disabled: "disabled", class: 'hiddenA' %> <%= f.hidden_field :body, :value => 0, disabled: "disabled", class: 'hiddenA' %> <%= f.hidden_field :fruity, :value => 3, disabled: "disabled", class: 'hiddenA' %> </div> ... <!-- refresh_contentがA-Qの16個あります。 --> <%= f.submit "保存", class:"form-control" %> <% end %>taists/_item_fields.html.erb
・
hidden_field
でuser_idにcurrent_user.idを代入しています。app/views/taists/_item_fields.html<div class="nested-fields form-inline"> <p>画像投稿</p> <%= f.attachment_field :image %> <p>タイトル</p> <%= f.text_field :title, class: "form-control" %> <%= f.hidden_field :user_id, :value => current_user.id %> </div>application.js
チェックされたrefreshIDに紐づいた'disabled'を解除します。
app/assets/javascripts/application.js$(document).on("turbolinks:load", function() { $('#refreshA').click(function() { $(".hiddenA").attr('disabled', false); }); // #refreshA-Qまで記載しています。 });おすすめを表示する(コントローラ・ビュー)
大変お待たせしました。おすすめ機能を表示するロジックを記載致します。
users_controller.rb
app/controllers/users_controller.rbclass UsersController < ApplicationController def show @user = User.find(params[:id]) #@userがいいねした投稿 @likes = Like.where(user_id: @user.id).order(created_at: :desc) if @likes.exists? #like = @userがいいねしている投稿の最新1つ like = @likes.last #item = likeのitem_idが含まれるitem item = like.item #item_recommend = itemと同じtaist taist_recommend = Taist.find_by(refresh: item.taist.refresh, bitter: item.taist.bitter, body: item.taist.body, fruity: item.taist.fruity) #item_recommend = taist_recommendと同じitemを全て item_recommend = taist_recommend.items #いいねしたitem全てのitem.id x = [] #xに配列を代入できるようにする @likes.each do |like| x << like.item_id #xにlike.item_idを全て代入して配列にする end #like_recommend = item_recommendの中で@userがいいねしていないitemの最新1つ @like_recommend = item_recommend.where.not(id: x).last else @like_recommend = Item.order("RAND()").last end end endusers/show.html.erb
app/views/users/show.html<h1>あなたへのおすすめ</h1> <h3>タイトル</h3> <%= @like_recommend.title %> <h3>投稿</h3> <%= attachment_image_tag @like_recommend, :image %>最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
- 投稿日:2020-02-15T16:51:20+09:00
Devise ログインボタン押せない
- 投稿日:2020-02-15T16:08:18+09:00
herokuでdb:migrateしたのにunknown attributeが発生
heroku run rails db:migrate
は正常に済んでいるように見えるのだけれども、
Railsアプリからデータを登録しようとするとWe're sorry, but something went wrong.
のエラー画面に。herokuのログ([more]からView logs)で確認したら、追加したはずのカラムが
unknown attribute
になっている。結果
今回は
heroku restart --app <アプリケーション名>
で解決。これまではrestart無しでもカラム追加を反映できていたのです。
何が違うとこうなるんでしょうか?(そこまで調べ切れていません...)
- 投稿日:2020-02-15T15:42:13+09:00
Rails: 継承とMix-inは使わないこと〜コードの共通化について
※この記事は、まとめ記事「多人数によるRailsアプリケーション開発」の1項目にする予定です。
要旨
Railsアプリケーションでの可読性、引き継ぎや改修のしやすさについてあれこれ考えているのですが、1つには「コードの共通化」に問題があると感じています。
これから作るRailsアプリケーションでは、次のガイドラインを設けたい。
- コードの共通化のために親クラスやモジュールを書くのは禁止。
- コードを共通化するときは、サービスクラスを呼び出す形にする。
- いきなりコードを共通化せずに、立ち止まってよく考える。重複を放置することも選択肢に入れる。
コードの共通化の害
継承の害についてはいろんな人がよい記事を書いていますので、「継承 アンチパターン」とか「inheritance antipattern」でググってください。
次の動画もどうぞ。
私は、次の記事からヒントを得ています。「下手な抽象化よりも重複のほうがまし」というものです。
私が体験した範囲では次のようなことがありました(犯人は私であったりします)。
- 先人がよかれと思って作った共通コードを誰も再利用しない。
- 先人がよかれと思って作った共通コードの出来の悪さに苦しめられる。
- 共通コードに次々にifやオプションが生えていき、奇怪なコードになる(上記の動画やThe Wrong Abstractionを参照)。
コードを共通化したいときは
サービスクラスを使う
次のように別のクラスに似たような機能を見つけて、コードを共通化したくなったとします。
class Dog def save # 長大な似たようなコード end end class Cat def save # 長大な似たようなコード end end親クラスやモジュールを作るよりも、まずサービスクラスを呼び出す形にすることを考えます。
class Dog def save serive = AnimalSaver.new(self) service.perform end end class Cat def save serive = AnimalSaver.new(self) service.perform end endクラスメソッドを呼び出す形にしてもよいです。
class Dog def save AnimalSaver.perform(self) end endコピペして修正する
その次にChickenクラスを加えるとします。DogやCatでやることとは少しでも違うことがあれば、AnimalSaverにifやオプションを生やすよりも、コピペして新しいクラスを修正することを検討します。
class Chicken def save serive = ChickenSaver.new(self) service.perform end end放置する
重複を放置することで可読性や引き継ぎ、改修の容易さが上回るなら、共通化より優先するべきです。
次の例でNamableモジュールが作りたくなったりしたら、こだわり過ぎです(というか、DRY原則について勘違いがある気がします)。
class Client def full_name "#{family_name} #{given_name}" end end class Customer def full_name "#{family_name} #{given_name}" end end継承やMix-inを使ってもいい場合
次の場合は、継承やMix-inを使ってもよいです。
- 継承やMix-inを前提としたライブラリを使うとき。Railsのコントローラやモデル、ActiveModel::Modelなど。
- app/helpers 下でのテンプレート用のメソッド。
- どうしても継承やMix-inでないとうまく書けないもの。
- クラスのコードが長大化したのでモジュールに分けるのは、まあ、ありかなあと思うけどどうでしょう。
実際にあった例
ここは思い付いたら追加します。
不要なインターフェイス
このシステムを引き継いだときにはAnimalはCatしかいませんでしたし、これから増える見込みもありません。実装の予定がない設計は混乱の元です。
module AnimalInterface def say raise NotImplementedError end end class Cat include AnimalInterface def say puts "meow" end endクラス内モジュールの使い回し
これは自分で何度かやってしまいました、すいません。わかりにく過ぎるので、こういうの書いてはいけません。たぶんクラスの組み立てからやり直すべき。
class CatUploader # かぶってるからモジュールにしてしまえ! module Common def s3_key "#{Rails.env}/cats/#{cat.id}" end end include Common end class CatDownloader include CatUploader::Common end思い付いたこと
継承は、言語の入門書には必ず載っているので「使わなければならない機能」のようなイメージがあります。
現実の継承が前提にしているのは、まず設計をして(ドキュメントを作って)、それから開発に取り掛かる、というプロジェクトなんじゃないだろうか。サクッと作って後からガンガン直す、というプロジェクトには不向きなのではと思います。
参考文献:
『Effective Java 第2版』 ジョシュア・ブロック、2001年、P80-90
- 投稿日:2020-02-15T13:24:46+09:00
rails環境構築その3【terraform】
はじめに
terraformをつかってAWS(EC2,RDS)を作成します。
全体の流れ
- terraformインストール
- AWSでIAMユーザー作成
- AWS CLI導入
- AWS CLIにIAMユーザーの登録
- terraformとは
- terraform導入
- プロバイダーの設定
- VPC作成
- サブネット作成
- インターネットゲートウェイ作成
- ルートテーブル作成
- EC2作成
- Security Groupの作成
- RDS作成
参考
* 10分で理解するTerraformterraformインストール
$ brew update $ brew install terraformAWSでIAMユーザー作成
参考
* Identity and Access Management へようこそ
* Terraformで構築するAWSAdministratorAccessポリシー(管理者権限)を選択してください。 権限が足りないと Error creating VPC: UnauthorizedOperation: You are not authorized to perform this operation. とエラーが出ます。AWS CLI インストール
参考
* AWS CLI のインストールと設定
順番にやればいいですが、私の場合はpythonのパスや環境でハマりました
エラー解決↓
* MacOSとHomebrewとpyenvで快適python環境を。
* Python・Python3のインストール先、パス等確認
* Python2.7からPython3.6をデフォルトにする話
* Python3インストール(Mac編)上記のリンクを参考にすればイケルと思います。
ターミナルcurl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" sudo python get-pip.py sudo pip install awscliエラーなくインストルされバージョンが出れば成功です
ターミナルpip --version aws --versionAWS CLIにIAMユーザーの登録
ターミナル(ホームディレクトリ)aws configure または、 aws configure --profile ユーザー名順番に記入します
- AWS CLI の設定
- AWS-CLIの初期設定のメモ
それぞれ先ほど作成した、IAMユーザーをコピペします。
リージョンと出力形式はリンク先から選んでください。ターミナルAWS Access Key ID [None]: AWS Secret Access Key [None]: Default region name [None]: Default output format [None]: json # Asia Pacific (Tokyo) ap-northeast-1terraformとは
インフラのコード化であります。GUIを通さずにインフラに変更を加えられコードの共有と再利用ができます。
参考
* インフラの構成管理を自動化するTerraform入門
* Terraformを使ってAWSのVPCを作成してEC2を起動した
ファイル形式は主に.tf
と.tfvars
を使います。
ファイル分割はあとにして全てmain.tf
に書きます。主なコマンド
terraformがあるディレクトリに移動して実行する# 初期化 Terraformで新しく設定を記述した場合、初期化を行う必要があります。 terraform init # 確認(所謂dry-run) terraform plan # 適用 コードの状態をAWS上へ適用 terraform apply # すべて消去するコマンド terraform destroy # リソースの閲覧 terraform show準備
mkdir terraform cd terraform touch main.tf touch terraform.tfvarsプロバイダーの設定
最初にproviderという指定をする必要がある。
複数の環境に対応しているため、「どのプロバイダーを使うのか?」を宣言するproviderとは
- その名と通りプロバイダでAWSの他にherokuやGCPもできるらしい
- profileで
aws config
でprofileを指定した場合はその名前、していない場合は"default"
region = "ap-northeast-1"
で指定したリージョン内にVPCなどを作っていくmain.tfprovider "aws" { profile = ユーザー名 region = "ap-northeast-1" }VPCを作成
使うリソース
aws_vpc
main.tf# VPC resource "aws_vpc" "aws-tf-vpc" { cidr_block = "10.1.0.0/16" instance_tenancy = "default" tags = { Name = "aws-tf-vpc" } }実行
terraformがあるディレクトリに移動して実行するterraform init terraform plan terraform apply # リソースの閲覧 terraform show実際に確かめてみる
aws-tf-vpc
と書いてあるVPC
があれば成功ですresourceとは
resourceはVPCやEC2のような起動したいリソースを定義 リソースの種類は、プロバイダーがAWSの場合はaws_*という名前でTerraformで予め定義されています。VPCであればaws_vpc、EC2であればaws_instanceです。resourceの構文
リソースはresourceブロックで設定します。resource "<リースの種類>" "<リソース名>" {}という構文です。resourceの定義と命名
resource "aws_vpc" "this" { ここでは 「"aws_vpc"というリソースを"this"という名前」 で作成しています。 resource "aws_vpc" まではAWSのVPCを作成するという意味で、 "this" はTerraformで定義する他のリソースから参照する際に使用します。他リソースの属性の参照
Terraformにはテンプレート内の他リソースの属性を参照する方法があります。 具体的には、<リソースの種類>.<リソース名>.<属性名>で他リソースの属性を参照することができます。その他の知識は下のリンクで
* AWSでTerraformに入門vscodeをterraform v0.12対応させる
下記のようなメッセージが出た人向け For Terraform 0.12 support try enabling the experimental language server with the 'Terraform: Enable/Disable Language Server' command コマンドパレットを開く(ctl/cmd+shift+p) terraform: install/update language server -> 現在最新の v0.0.9 を選択 terraform: Enable/Disable Language Server を実行 一度vscodeを閉じたらHCL2記法の.tfファイルでもエラーが出なくなりました。.gitignoreに上げて行けないファイルの追加
下記のファイルはgithubに上げてはいけないとので追加します。
* Terraform.gitignore.gitignore# Local .terraform directories **/.terraform/* # .tfstate files *.tfstate *.tfstate.* # .tfvars files *.tfvarsサブネット作成
使うリソース
aws_subnet
main.tf# サブネット2つ作成(publicとprivate) resource "aws_subnet" "aws-tf-public-subnet-1a" { vpc_id = aws_vpc.aws-tf-vpc.id cidr_block = "10.1.1.0/24" availability_zone = "ap-northeast-1a" tags = { Name = "aws-tf-public-subnet-1a" } } resource "aws_subnet" "aws-tf-private-subnet-1a" { vpc_id = aws_vpc.aws-tf-vpc.id cidr_block = "10.1.20.0/24" availability_zone = "ap-northeast-1a" tags = { Name = "aws-tf-private-subnet-1a" } }インターネットゲートウェイの作成
使うリソース
aws_internet_gateway
main.tfresource "aws_internet_gateway" "aws-tf-igw" { vpc_id = aws_vpc.aws-tf-vpc.id tags = { Name = "aws-tf-igw" } }ルートテーブルの作成
使うリソース
aws_route_table
main.tfresource "aws_route_table" "aws-tf-public-route" { vpc_id = aws_vpc.aws-tf-vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.aws-tf-igw.id } tags = { Name = "aws-tf-public-route" } }サブネットの関連付けでルートテーブルをパブリックサブネットに紐付け
使うリソース
aws_route_table_association
main.tfresource "aws_route_table_association" "aws-tf-public-subnet-association" { subnet_id = aws_subnet.aws-tf-public-subnet-1a.id route_table_id = aws_route_table.aws-tf-public-route.id }EC2作成(public側に作る)
使うリソース
aws_instance
、aws_key_pair
main.tfresource "aws_instance" "aws-tf-web" { ami = "ami-011facbea5ec0363b" instance_type = "t2.micro" disable_api_termination = false key_name = aws_key_pair.auth.key_name vpc_security_group_ids = [aws_security_group.aws-tf-web.id] subnet_id = aws_subnet.aws-tf-public-subnet-1a.id tags = { Name = "aws-tf-web" } } # amiとはAmazon Linux 2 AMIです。今回は実際のamiの値を直接入れてますが、いつも最新版にできます。 variable "public_key_path" {} resource "aws_key_pair" "auth" { key_name = "terraform-aws" public_key = file(var.public_key_path) }terraform.tfvars# ローカルに鍵がある場所を指定 public_key_path = "~/.ssh/terraform-aws.pub"amiを常に最新版にする
インスタンスのkey_nameについて
GUIで作るときはインスタンス作成時にsshキーを新規作成や既存のキーを使いAWSにアクセスしますが、
terraformで作成する場合は.tfvars
ファイルから参照して公開鍵をインスタンスのkey_name
に貼り付けます。
私はterraform-aws
という名前で鍵を作成しました。ターミナル$ cd .ssh $ ssh-keygen -t rsa terraform-aws 今回の場合の名前 ここでエンターキーを2連打 $ ls ここでterraform-aws terraform-aws.pubができていることを確認参考
* Resource: aws_key_pair
* Terraform でキーペア登録し起動した EC2 に SSH接続
Terraform 変数について
Terraform の変数は
variable
ブロックで定義
var.<変数名>
という書式で参照
外部ファイルに値を定義した場合、terraform.tfvars
なら自動的に読み込まれて変数に代入されます。
また.tfvars
はgithubに挙げないとこが大事です。
参考
* 【Terraform 再入門】EC2 + RDS によるミニマム構成な AWS 環境をコマンドライン一発で構築してみようSecurity Groupの作成
使うリソース
aws_security_group
、aws_security_group_rule
main.tfresource "aws_security_group" "aws-tf-web" { name = "aws-tf-web" description = "aws-tf-web_sg" vpc_id = aws_vpc.aws-tf-vpc.id tags = { Name = "aws-tf-web" } } # 80番ポート許可のインバウンドルール resource "aws_security_group_rule" "inbound_http" { type = "ingress" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0", ] # ここでweb_serverセキュリティグループに紐付け security_group_id = aws_security_group.aws-tf-web.id } # 22番ポート許可のインバウンドルール resource "aws_security_group_rule" "inbound_ssh" { type = "ingress" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0", ] # ここでweb_serverセキュリティグループに紐付け security_group_id = aws_security_group.aws-tf-web.id } # アウトバウンドルール resource "aws_security_group_rule" "outbound_all" { type = "egress" from_port = 0 to_port = 0 protocol = -1 cidr_blocks = [ "0.0.0.0/0", ] # ここでweb_serverセキュリティグループに紐付け security_group_id = aws_security_group.aws-tf-web.id }Security Group詳細
ElasticIP作成
使うリソース
aws_eip
main.tfresource "aws_eip" "aws-tf-eip" { instance = aws_instance.aws-tf-web.id vpc = true } output "example-public-ip" { value = "${aws_eip.aws-tf-eip.public_ip}" }outputについて
ElasticIPなどはGUIから確認できますが、
output
ブロックで記述するとterraform apply
実行時に下記のように出力されます。Apply complete! Outputs: example-public-ip = ElasticIPRDSの作成
RDSは2つのサブネットが必要なのであと一つ違うアベイラビリティゾーンから作成します。
使うリソースaws_subnet
、aws_db_subnet_group
main.tf# RDS用のサブネットを作成 resource "aws_subnet" "aws-tf-private-subnet-1c" { vpc_id = aws_vpc.aws-tf-vpc.id cidr_block = "10.1.3.0/24" availability_zone = "ap-northeast-1c" tags = { Name = "aws-tf-private-subnet-1c" } } # 使用する2つを指定します。 resource "aws_db_subnet_group" "rdb-tf-db" { name = "rdb-tf-db-subnet" description = "It is a DB subnet group on tf_vpc." subnet_ids = [aws_subnet.aws-tf-private-subnet-1a.id,aws_subnet.aws-tf-private-subnet-1c.id] tags = { Name = "rdb-tf-db" } }DB用のセキュリティーを作成
WebサーバーからのみDBサーバーにアクセスできるようにするためにセキュリティを作ります。
使うリソースaws_security_group
、aws_security_group_rule
main.tf# Security Group resource "aws_security_group" "aws-tf-db" { name = "aws-tf-db" description = "aws-tf-db-sg" vpc_id = aws_vpc.aws-tf-vpc.id tags = { Name = "aws-tf-db" } } resource "aws_security_group_rule" "db" { type = "ingress" from_port = 5432 to_port = 5432 protocol = "tcp" source_security_group_id = aws_security_group.aws-tf-web.id security_group_id = aws_security_group.aws-tf-db.id } # source_security_group_idとはアクセスを許可するセキュリティグループIDつまりWeb側のセキュリティグループを指します。RDSインスタンスの作成
使うリソース
aws_db_instance
main.tfvariable "aws-td-db-username" {} variable "aws-td-db-password" {} resource "aws_db_instance" "aws-td-db" { identifier = "aws-td-db" allocated_storage = 20 name = "db11" engine = "postgres" engine_version = "11.5" instance_class = "db.t2.micro" storage_type = "gp2" username = var.aws-td-db-username password = var.aws-td-db-password vpc_security_group_ids = [aws_security_group.aws-tf-db.id] db_subnet_group_name = aws_db_subnet_group.rdb-tf-db.name }terraform.tfvarspublic_key_path = "~/.ssh/terraform-aws.pub" aws-td-db-username = ※※※※※※※※※※ aws-td-db-password = ※※※※※※※※※ それぞれ指定してください。実行
terraform plan terraform apply # 確認 terrafom showRDSインスタンス作成時にハマったエラーについて
その1DBName must begin with a letter and contain only alphanumeric characters これはRDSインスタンスのnameについてのエラーです。私の場合は`name = "db11"`と書きましたが、文字で初めて英数字両方を書かないといけないらしいです。参考
* AWS aws_db_instance DBName issueその2Error creating DB Instance: InvalidParameterValue: Invalid DB engine これはRDSインスタンスの`engine`の名前の書き方でエラーが出ました。今回は`engine = "postgres"`と書きました。参考
* "Invalid DB engine" when creating AWS/RDS Postgresql instance
* Engineのセクションを見てください内容一式
main.tfprovider "aws" { profile = プロフィール名 region = "ap-northeast-1" } # VPC作成 resource "aws_vpc" "aws-tf-vpc" { cidr_block = "10.1.0.0/16" instance_tenancy = "default" enable_dns_support = "true" enable_dns_hostnames = "true" tags = { Name = "aws-tf-vpc" } } # サブネット2つ作成(publicとprivate) resource "aws_subnet" "aws-tf-public-subnet-1a" { vpc_id = aws_vpc.aws-tf-vpc.id cidr_block = "10.1.1.0/24" availability_zone = "ap-northeast-1a" tags = { Name = "aws-tf-public-subnet-1a" } } resource "aws_subnet" "aws-tf-private-subnet-1a" { vpc_id = aws_vpc.aws-tf-vpc.id cidr_block = "10.1.20.0/24" availability_zone = "ap-northeast-1a" tags = { Name = "aws-tf-private-subnet-1a" } } # インターネットゲートウェイの作成 resource "aws_internet_gateway" "aws-tf-igw" { vpc_id = aws_vpc.aws-tf-vpc.id tags = { Name = "aws-tf-igw" } } # ルートテーブルの作成 resource "aws_route_table" "aws-tf-public-route" { vpc_id = aws_vpc.aws-tf-vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.aws-tf-igw.id } tags = { Name = "aws-tf-public-route" } } # サブネットの関連付けでルートテーブルをパブリックサブネットに紐付け resource "aws_route_table_association" "aws-tf-public-subnet-association" { subnet_id = aws_subnet.aws-tf-public-subnet-1a.id route_table_id = aws_route_table.aws-tf-public-route.id } # EC2作成(public側) resource "aws_instance" "aws-tf-web" { ami = "ami-011facbea5ec0363b" instance_type = "t2.micro" disable_api_termination = false key_name = aws_key_pair.auth.key_name vpc_security_group_ids = [aws_security_group.aws-tf-web.id] subnet_id = aws_subnet.aws-tf-public-subnet-1a.id tags = { Name = "aws-tf-web" } } variable "public_key_path" {} resource "aws_key_pair" "auth" { key_name = "terraform-aws" public_key = file(var.public_key_path) } # Security Group resource "aws_security_group" "aws-tf-web" { name = "aws-tf-web" description = "aws-tf-web_sg" vpc_id = aws_vpc.aws-tf-vpc.id tags = { Name = "aws-tf-web" } } # 80番ポート許可のインバウンドルール resource "aws_security_group_rule" "inbound_http" { type = "ingress" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0", ] # ここでweb_serverセキュリティグループに紐付け security_group_id = aws_security_group.aws-tf-web.id } # 22番ポート許可のインバウンドルール resource "aws_security_group_rule" "inbound_ssh" { type = "ingress" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0", ] # ここでweb_serverセキュリティグループに紐付け security_group_id = aws_security_group.aws-tf-web.id } # アウトバウンドルール resource "aws_security_group_rule" "outbound_all" { type = "egress" from_port = 0 to_port = 0 protocol = -1 cidr_blocks = [ "0.0.0.0/0", ] # ここでweb_serverセキュリティグループに紐付け security_group_id = aws_security_group.aws-tf-web.id } # ElasticIP resource "aws_eip" "aws-tf-eip" { instance = aws_instance.aws-tf-web.id vpc = true } output "example-public-ip" { value = aws_eip.aws-tf-eip.public_ip } ####RDSの作成 # RDS用のサブネットを作成 resource "aws_subnet" "aws-tf-private-subnet-1c" { vpc_id = aws_vpc.aws-tf-vpc.id cidr_block = "10.1.3.0/24" availability_zone = "ap-northeast-1c" tags = { Name = "aws-tf-private-subnet-1c" } } # DB用のセキュリティーを作成 # Security Group resource "aws_security_group" "aws-tf-db" { name = "aws-tf-db" description = "aws-tf-db-sg" vpc_id = aws_vpc.aws-tf-vpc.id tags = { Name = "aws-tf-db" } } resource "aws_security_group_rule" "db" { type = "ingress" from_port = 5432 to_port = 5432 protocol = "tcp" source_security_group_id = aws_security_group.aws-tf-web.id security_group_id = aws_security_group.aws-tf-db.id } resource "aws_db_subnet_group" "rdb-tf-db" { name = "rdb-tf-db-subnet" description = "It is a DB subnet group on tf_vpc." subnet_ids = [aws_subnet.aws-tf-private-subnet-1a.id,aws_subnet.aws-tf-private-subnet-1c.id] tags = { Name = "rdb-tf-db" } } variable "aws-td-db-username" {} variable "aws-td-db-password" {} resource "aws_db_instance" "aws-td-db" { identifier = "aws-td-db" allocated_storage = 20 name = "db11" engine = "postgres" engine_version = "11.5" instance_class = "db.t2.micro" storage_type = "gp2" username = var.aws-td-db-username password = var.aws-td-db-password vpc_security_group_ids = [aws_security_group.aws-tf-db.id] db_subnet_group_name = aws_db_subnet_group.rdb-tf-db.name }terraform.tfvarspublic_key_path = "~/.ssh/terraform-aws.pub" aws-td-db-username = ※※※※※※※※※※ aws-td-db-password = ※※※※※※※※※おしまい。
何かありましたらコメント欄で
- 投稿日:2020-02-15T11:59:29+09:00
Visual Studio Codeのショートカットが便利だと思った話
みなさん開発する際テキストエディタは何を使用されていますか?
僕はVisual Studio Codeを使用してRailsの勉強をしています。railsを勉強するにあたって初心者あるあるだと思ってるんですが、(僕だけかな。。。?)
rails new でアプリケーションの雛形作って
controller作って、model作ってview作って編集して。。。開発を進めて行くとファイル数が多くなっていき、
修正したいファイルを探すのに時間を使っちゃうことが多々ありました。
例)index.html.erbを修正したい!
えーと確かappフォルダのえーとviewフォルダのホゲホゲフォルダのなかで。。。。あれ今どこみてるんだ。。。?などなど疲れているとよく指定のファイルを探す時間が多くなってしまってどうにかならないかなーって思ってました。
考えたこと
rails newで作成したら機械的に決まったフォルダを作成しており
基本的に触るのってviewファイルとcontrollerファイルとmodelファイルなんですよね。
そしてrailsアプリケーションって
veiwだったら
app/views/hogehoge/fugafuga.html.erb
modelだったら
app/models/hogehoge/fugafuga.rb
controllerだったら
app/controllers/hogehoge/fugafuga_controller.rb
とだいたい置き場所って決まっているのでターミナルから起動できれば便利だなーって考えました。実はターミナルから開ける
MACだと下記コマンドを打てば、Visual Studio Code上の一部画面にターミナルを開くことができました。
Cntrol + Shift + `
でターミナル上で
code フォルダ名/ファイル名
※codeに関しては別途インストールが必要と入力すれば任意のファイルを開くことができます。
これに気付いた時本当にテンション上がりました。
タイピングする際のホームポジションを崩しませんからね。またtabでの入力補完を使用すればタイピングミスが少なく爆速でファイル作成&修正ができちゃいます。
他にも便利なショートカットがたくさんありますので、余裕がある時にでも一つづつ覚えていこうと思っております。
参考情報
コマンドラインからVS Codeでファイルやフォルダを開く
Visual Studio Code キーボード ショートカット
- 投稿日:2020-02-15T11:35:52+09:00
Could not find nokogiri-1.10.7 in any of the sources (Bundler::GemNotFound) が出た時の対処法
rails generate
を実行したら以下のエラーが出た。$ rails generate controller Welcome index Traceback (most recent call last): 22: from /Library/Ruby/Gems/2.6.0/gems/spring-2.1.0/bin/spring:49:in `<main>' 21: from /Library/Ruby/Gems/2.6.0/gems/spring-2.1.0/lib/spring/client.rb:30:in `run' 20: from /Library/Ruby/Gems/2.6.0/gems/spring-2.1.0/lib/spring/client/command.rb:7:in `call' 19: from /Library/Ruby/Gems/2.6.0/gems/spring-2.1.0/lib/spring/client/server.rb:9:in `call' 18: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 17: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 16: from /Library/Ruby/Gems/2.6.0/gems/spring-2.1.0/lib/spring/server.rb:9:in `<top (required)>' 15: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 14: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 13: from /Library/Ruby/Gems/2.6.0/gems/spring-2.1.0/lib/spring/commands.rb:4:in `<top (required)>' 12: from /Library/Ruby/Gems/2.6.0/gems/spring-2.1.0/lib/spring/commands.rb:33:in `<module:Spring>' 11: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 10: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require' 9: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/setup.rb:20:in `<top (required)>' 8: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler.rb:107:in `setup' 7: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/runtime.rb:20:in `setup' 6: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/runtime.rb:108:in `block in definition_method' 5: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/definition.rb:226:in `requested_specs' 4: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/definition.rb:237:in `specs_for' 3: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/definition.rb:170:in `specs' 2: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/spec_set.rb:85:in `materialize' 1: from /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/spec_set.rb:85:in `map!' /Users/maiamea/.rbenv/versions/2.6.3/lib/ruby/2.6.0/bundler/spec_set.rb:91:in `block in materialize': Could not find nokogiri-1.10.7 in any of the sources (Bundler::GemNotFound)現在使っているRubyのバージョンを確認
$ ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin19] $ rbenv versions system 2.4.1 * 2.6.3 (set by /Users/maiamea/Documents/prog/rails/todo/.ruby-version) 2.6.5rbenvでインストールしたRubyと、もともとMacにデフォルトで入っていたRubyのライブラリ(Gem)が混在していたためエラーになっていた。
以下のコマンドで直った。
# Rubyのライブラリ(Gem)のインストール $ bundle install # インストールしたRubyライブラリのコマンドをrbenvで使えるようにする $ rbenv rehash対処後
$ rails generate controller Welcome index Running via Spring preloader in process 80856 create app/controllers/welcome_controller.rb route get 'welcome/index' invoke erb create app/views/welcome create app/views/welcome/index.html.erb invoke test_unit create test/controllers/welcome_controller_test.rb invoke helper create app/helpers/welcome_helper.rb invoke test_unit invoke assets invoke scss create app/assets/stylesheets/welcome.scss無事
rails generate
に成功した。
- 投稿日:2020-02-15T10:14:35+09:00
#Rails の ActiveRecord create の返り値で レコードが作成保存されたかどうかを確認する ( persisted? )
- create! と違い create は例外を起こさない
- 返り値が model class の instance になるので、 persisted? ( 永続化されたかどうか? ) を聞くと良さそうだ。
- SQLレベルで失敗した場合は例外扱いになり、返り値は得られないようだ。
# ------------------------- # 作成成功ケース # ------------------------- succeeded_created = User.create(name: "yy") # (1.0ms) BEGIN # User Exists (1.4ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` = BINARY 'yy' LIMIT 1 # User Create (1.4ms) INSERT INTO `users` (`name`, `created_at`) VALUES ('yy', '2020-02-13 23:42:17') # (4.9ms) COMMIT succeeded_created.persisted? # => true succeeded_created.valid? # User Exists (1.0ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` = BINARY 'yy' AND `users`.`id` != 83 LIMIT 1 # => true succeeded_created.invalid? # User Exists (2.2ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` = BINARY 'yy' AND `users`.`id` != 83 LIMIT 1 # => false succeeded_created.errors #=> #<ActiveModel::Errors:0x000056396e61bc70 # @base=#<User:0x000056396e816480 id: 83, name: "yy", created_at: Thu, 13 Feb 2020 23:45:53 UTC +00:00>, # @details={}, # @messages={}> succeeded_created.errors.present? #=> false # ------------------------- # バリデーションによる作成失敗ケース # ------------------------- failed_created = User.create(name: "yy") # (0.6ms) BEGIN # User Exists (1.2ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` = BINARY 'yy' LIMIT 1 # (1.0ms) ROLLBACK failed_created.persisted? # => false failed_created.errors # => #<ActiveModel::Errors:0x0000563973bd4888 # @base=#<User:0x0000563973bd9d10 id: nil, name: "yy", created_at: nil>, # @details={:name=>[{:error=>:taken, :value=>"yy"}]}, # @messages={:name=>["はすでに存在します"]}> failed_created.errors.present? # => true failed_created.valid? # User Exists (1.2ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` = BINARY 'yy' LIMIT 1 => false failed_created.invalid? # User Exists (1.7ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` = BINARY 'yy' LIMIT 1 => true # ----------------------------- # SQL エラーが発生したケース # ----------------------------- sql_failed_created = User.create(name: "yy") # (0.8ms) BEGIN # User Create (6.1ms) INSERT INTO `users` (`name`, `created_at`) VALUES ('yy', '2020-02-13 23:55:11') # (5.7ms) ROLLBACK #ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry 'yy' for key 'index_users_on_name': INSERT INTO `users` (`name`, `created_at`) VALUES ('yy', '2020-02-13 23:55:11') # # Caused by Mysql2::Error: Duplicate entry 'yy' for key 'index_users_on_name' sql_failed_created.persisted? # NoMethodError: undefined method `persisted?' for nil:NilClassOriginal by Github issue
- 投稿日:2020-02-15T09:11:04+09:00
【Rails6】DEPRECATION WARNING: Rails 6.1 will return Content-Type header without modification. If you want just the MIME type, please use `#media_type` instead.
環境
- Ruby: 2.6.3
- Rails: 6.0.2.1
なんだこのWarning
DEPRECATION WARNING: Rails 6.1 will return Content-Type header without modification. If you want just the MIME type, please use `#media_type` instead.
ActionDispatch::Response#content_type
を呼ぶとこんなWarningが出ました。
- Rails6.1からContent-Typeヘッダの値をそのまま返すよう変更するよ(これまではcharsetを除外してたけど含まれるようになるよ)
- MIME typeだけが欲しい場合は
ActionDispatch::Response#media_type
を使ってねと言われています。
欲しいのは
text/html
とかtext/plain
みたいなMIME typeでしたので、
ActionDispatch::Response#media_type
へ修正してWarning解消しました。参考
従来は、ActionDispatch::Response#content_typeの戻り値にcharsetパートが含まれていませんでした。 この振る舞いは変更され、従来省略されていたcharsetパートも含まれるようになりました。
MIMEタイプだけが欲しい場合は、代わりにActionDispatch::Response#media_typeをお使いください。
- 投稿日:2020-02-15T08:33:29+09:00
Docker コンテナ内のrailsコマンドが呼び出せなくなったのでメモ
概要
docker環境で、
railsチュートリアルを進めていたら、
急に下記のようにrailsコマンドが呼び出せなくなったので、
対処方法をメモbash: rails: command not found環境
こちらの記事を参考にrailsのdocker環境を構築
https://qiita.com/reflet/items/f73cac406760ee4ecc13解決策
railsのDockerファイルに下記のようにパス通しを記載しrailsコマンドを呼び出せるようにした。
ENV PATH $PATH:/usr/local/src/binそもそもコマンドって?
我々が普段使っている
cd
やls
などは、魔法でもなんでもなく、全てプログラム
このプログラムを呼び出すための略称がコマンドという認識で良いと思います。自分は、元々windowsユーザなのですが、Windowsユーザだと
exeファイルがイメージし易いかと思います。
C言語とかで、コマンドプロンプトにHelloWorld
と表示するhellow.exe
ファイルを作成して、
hellow.exe
をダブルクリックするとコマンドプロンプトが開きHelloWorld
と表示されます。
ls
コマンドだったらls.exe
をダブルクリックしたら、ファイルのリストがコマンドプロンプト上に表示されるそんなイメージで自分はいます。
間違ってたらすみません。。。。パスって?
コマンドが、プログラムということがわかりましたが、
パソコンは、そのプログラムが何処にあるか知りません。
hellow.exe
がどのファイルに存在するのかパソコンは理解できないわけです。
そこで、PATH
という環境変数に、コマンドは、このパスの配下にあるプログラムだよ
と教えてあげることで、パソコンが、どのファイルの配下にhellow.exe
があるか理解できるという仕組みです。
これが俗にいうパスを通すというやつです。小学生の時に友人から、インストールって本質的には、パスを通すことなんだよって
言われて、その時は、何が何だかって感じでしたが、今となっては、なるほどって感じです。環境変数って?
お使いのMacやWindowsに設定されている、変数です。
パソコンに設定されているもや、ログインしているユーザごとに設定されている変数もあります。後書き
何故急にコマンドが呼び出せなくなったのか不明ですが、
コマンドってなんだっけパスとかなんだっけというLinuxの基礎知識を得るいい機会でした。何か、間違ってることありましたら、優しくご指摘いただけますと幸いです。
- 投稿日:2020-02-15T04:28:07+09:00
[font-awsomeのエラー]NoMethodError in Creditcard#indexの一例
- 投稿日:2020-02-15T03:48:44+09:00
ActionController::UnknownFormat in CreditcardController#indexの解決方法
1.エラーの内容
題名部分の英語を読むと"creditcard_controllerに定義したindexアクションはありません"というエラー文となっています
2.エラーの原因
1.viewファイルを作っていない
rails g controller xxx
によってファイルを生成した直後はapp/view/xxxフォルダまでしかできておらず、viewファイルは手動で作成する必要があります2.app/controllers/xxx_controller.rbの中の
def 〇〇
に連動したファイル名としていない
railsの機能によりcontroller.rbファイルのdef 〇〇
をrequire './view/xxx'という機能により見えないところで呼び出しを行なっています。(xxxフォルダを読み込む(実行されるのは〇〇ファイル)コマンド機能となっています)なので、その命名規則に則っていないとこのエラーとなります。3.連動したファイル名としているがファイル(フォルダ)名を間違っている
2.と同様の理屈により、スペルミスがあったり、複数形のsをつけ忘れたりすると、呼び出し時にエラーを吐いてしまいます。4.erbファイルではなく、hamlファイルで記載しようとした時に、gemfileの中に
gem 'haml-rails'
の記述を忘れている
hamlファイルというのは独自の追加機能で、読み込むにはgemファイルが必要であり、その記載を忘れているとファイル名があっていてもファイルそのものを読み込むことができず、エラーを吐いてしまいます3.エラーの解決法
1.app/view/xxx/〇〇.html.erb この位置にファイルをつくる
2.app/view の中のフォルダ名(xxx)とファイル名(〇〇)がコントローラ名と対応しているか確認する
3.対象ファイルが2.の命名規則に従いつつ誤字脱字や複数形のミスをしていないか確認する
4.(hamlファイルを使用する場合)gemfileの中にgem 'haml-rails'
の記述を行い、bundle install
コマンドを打つこれで題名のエラーについて大抵のものは解決できるかと思います。
- 投稿日:2020-02-15T03:48:44+09:00
[Rails]ActionController::UnknownFormat in UsersController#indexの解決方法
1.エラーの内容
題名部分の英語を読むと"creditcard_controllerに定義したindexアクションはありません"というエラー文となっています
2.エラーの原因
1.viewファイルを作っていない
rails g controller xxx
によってファイルを生成した直後はapp/view/xxxフォルダまでしかできておらず、viewファイルは手動で作成する必要があります2.app/controllers/xxx_controller.rbの中の
def 〇〇
に連動したファイル名としていない
railsの機能によりcontroller.rbファイルのdef 〇〇
をrequire './view/xxx'という機能により見えないところで呼び出しを行なっています。(xxxフォルダを読み込む(実行されるのは〇〇ファイル)コマンド機能となっています)なので、その命名規則に則っていないとこのエラーとなります。3.連動したファイル名としているがファイル(フォルダ)名を間違っている
2.と同様の理屈により、スペルミスがあったり、複数形のsをつけ忘れたりすると、呼び出し時にエラーを吐いてしまいます。4.erbファイルではなく、hamlファイルで記載しようとした時に、gemfileの中に
gem 'haml-rails'
の記述を忘れている
hamlファイルというのは独自の追加機能で、読み込むにはgemファイルが必要であり、その記載を忘れているとファイル名があっていてもファイルそのものを読み込むことができず、エラーを吐いてしまいます3.エラーの解決法
1.app/view/xxx/〇〇.html.erb この位置にファイルをつくる
2.app/view の中のフォルダ名(xxx)とファイル名(〇〇)がコントローラ名と対応しているか確認する
3.対象ファイルが2.の命名規則に従いつつ誤字脱字や複数形のミスをしていないか確認する
4.(hamlファイルを使用する場合)gemfileの中にgem 'haml-rails'
の記述を行い、bundle install
コマンドを打つこれで題名のエラーについて大抵のものは解決できるかと思います。
- 投稿日:2020-02-15T01:32:45+09:00
TechCommitのお年玉企画で当たったTechpitの教材「Tinder風マッチングアプリを作ってみよう!」を終えて
TechpitのTinder風マッチングアプリを作ってみよう!を完了しました
Qiita初投稿です。
TechCommitのお年玉企画で当選したこちらの教材を無事終わらせることが出来たので、感想と学んだことのメモを残しておきます。学んだこと
- content_tagは動的にHTMLを生成出来る便利なコード。erbとhtmlが混ざってしまう時にすっきり書ける。
content_tag(:i, "", class: "fas fa-cog fa-2x")
→<i class="fas fa-cog fa-2x></i>
が生成される。- これは自分がInstacloneを作ってた時、font-awesomeを使う際にどのようにslimを書いていいか混乱したので、これからは迷わなくてすみそう。
& > a
とかはScssじゃなくCSSの書き方で、子供セレクタのみ指定する(孫はダメ)- transitionは変化するまでの時間や変化の度合いを指定するCSSのプロパティで、JavaScriptを使わなくても動きのある見た目の変化をつけることが出来る。
- border-radiusは角(ボックスで言うところのborder)を丸めるときに使うやつ。そのまま。
border-color: transparent transparent #fff #fff
- この書き方は、順に上辺|右辺|下辺|左辺と、それぞれの辺の色を指定している(paddingとかと同じ仕組み)
@users = User.where.not(id: current_user.id)
- where.notは条件に当てはまらないものだけ取得するという意味。この場合はidがcurrent_user.id以外のものだけ取得する。NOTクエリにあたる。
find_or_initialize_by
メソッドは、引数で渡したカラムでfindをかけて引っかからなかったらインスタンスを作成する便利なメソッド。データべースに保存はしない。
- TechPitのTinder風マッチングアプリ作成教材では、update_attributesでデータベースの更新をしていた。
- saveとupdate_attributesの違い - Qiita
input[type="text"]:focus
はinput要素のtype="text"がユーザーの操作でフォーカスされた時のスタイルを指定する。疑似要素=:hoverと同類。感想
- 単純にとても楽しかった。最近取り掛かっているスクール課題と違って答えのある安心感もあった。
- また最初にアプリケーションのデータベース設計について解説されていたのが良かった。
- Deviseをはじめて使ったけど簡単すぎてびっくり。
- ただその分魔法のようなところが多くて、英語のリファレンスを読み込まないと自分の動かしたいように動かせないなとも感じた。
- CSSをきちんと書いてくださってるので、見た目が本当に綺麗なアプリが出来ていくのでテンションが上がる。
- 学習初日はCSS手打ちせずコピペしていたが、それでは勉強にならないと思い、手打ちして1行か2行追加するごとにページを読み込み直してCSSの挙動を確認した。
- 少し複雑そうなコードに関しては説明があるので、基本的な部分で詰まるところは少なかった。
- ただし、JavaScript(jTinder関連のコード)や、Action Cableの部分は分からないところがまだまだ多く、説明もそこまで詳しくされていなかったのでもう一度見直す必要がある。
- Action Cable の概要 - Railsガイド
- ↑書いてることが今のレベルじゃちょっと分からない
- そんなAction Cableを使ったリアルタイムチャット機能が出来たときは今まで作ったことのないような機能だったので感動した。
終わりに
2周目はerbではなくSlimで書きながら、JavaScriptとAction Cableの理解に重点を置きつつ復習出来たらいいなと思います。
重めのスクール課題が待ち構えているので、それが終わって余裕がある時にやります。