20200215のRailsに関する記事は23件です。

ルーティングのshallowオプション

ルーティングのネストを調整するときに、shalowオプションというのを使いました。
書き方は以下です。

routes.rb
Rails.application.routes.draw do
  resources :boards do
    resources :comments, shallow: true
  end

shallowオプション有りとshallowオプション無しの比較

  • shallowオプションなし

Image from Gyazo

  • shallow: trueオプション

Image from Gyazo

edit,show,destroy,updateに、親の/boards/board_idが付かず、ネスト(入れ子)じゃ無くなっています。
逆に、shallow: trueをつけないと、/boards/board_idが全部につくURLになります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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つのコマンドを実行してくれる
  1. $ rails db:create
  2. $ rails db:schema:load
  3. $ rails db:seed

余談(現場での運用)

現場では開発者が複数人いるので、テーブルに変更を加える際は、既存のマイグレーションファイルに変更を加えないようにするのが鉄則です。

【理由】
既存のマイグレーションファイルに変更を加えると、共同開発などをしている際に、共同開発しているメンバーも$ rails db:migrate:reset しないといけなくなる

【ベストプラクティス】
新しいマイグレーションファイルを作成し、テーブルに変更を加える

このような対応をすると、共同開発しているメンバーは$ rails db:migrate するだけで良くなります!
ただ、開発環境なら共同開発してるメンバーに$ rails db:migrate:reset してくださいと周知すれば済むので、その対応でも良いですが、本番環境で$ rails db:migrate:reset$ rails db:resetをすると、DBの値が全部吹き飛び、大事故になるため、ご注意を。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Active Jobについてゼロから理解する

概要

RailsのActive Jobについてゼロから理解する為に調べたことをまとめます。

  • 本格的なアプリでは往々にして時間のかかる処理が発生する
    • 大量のデータ集計、外部サービスとの連携、メール送信など
  • そしてこれらの処理はリアルタイムに完了してなくても良い場合もある
  • アプリから実行すべき処理(ジョブ)待ち行列(キュー)に登録して後から実行(非同期実行)することで、アプリのレスポンスを改善できる!

  • Active Jobとは、そのようなジョブの管理から実行までの管理する為のモジュール

    • Active Jobそのものは、基本的に、ジョブ操作の為のインタフェース(メソッド名などの決まりごと)を提供しているに過ぎない
    • 実際にジョブを実行するのはジョブ管理ライブラリの役割(Delayed JobSidekiq
  • アダプターを切り替えれば、アプリケーションをほとんど改修することなくバックエンドのジョブ管理ライブラリを自由に切り替えられる

やってみた

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()

コールバック

ジョブを登録/実行するタイミングで実行されるメソッド、またはその仕組のこと

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】simple_calendarを日本語表記にする方法

Railsでsample_calendarを導入したのですが、日本語表記にするのに少し手間取りました。

しかし、無事解決することができたので共有したいと思います。

Railsのバージョン 5.2.3
Rubyのバージョン 2.5.1

手順1

config/application.rb
config.i18n.default_locale = :ja

まずはこいつを追加します。

手順2

config/locales/ja.yml
ja:
  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-15 20.15.27.png

完璧ですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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

完成形イメージ

チャート描画.png

Gemに追記

Gemfile.
  gem 'gon'
$ bundle install

Chart.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.rb
    def show
        @taist = Taist.find(params[:id])
        #railsの変数をchart.jsに渡す
        gon.taist = @taist
    end

taists/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.js
var 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.js
scale:{
    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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby / Rails資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】リロードしないとJavaScriptが動かない!【簡単に解決】

はじめに

以前jQueryを使ってタブメニューの切り替えを実装したときに、
ビューを開いた初回は非同期で切り替わるのに、
別のページに移動したり
「戻る」ボタンを押したあと再びタブメニュー画面に戻ると、、、、

動かない、、、!なぜだ、、!

なんて経験をして1日無駄にしたことがあります。
jQueryやビュー画面のコードは間違ってないはず!なぜ!って人向けの記事です。

解決方法①

link_toごとにturbolinksとやらを無効にすればOKです。簡単です。

<%= link_to "ホーム", root_path, data: {"turbolinks" => false} %>

data: {“turbolinks" => false}は遷移先ページのみturbolinksを切ることが出来ます。

turbolinksとは、ページ遷移をAjaxに置き換え、JavaScriptCSSのパース(解析・変換)を省略することで高速化するgemで、Rails4からはデフォルトで使用されています。↓

gem 'turbolinks', '~> 5'

解決方法②

jsファイルに"turbolinks:load"を記述すればOKです。簡単です。

document.addEventListener("turbolinks:load", function() {
  // ...
})

まとめ

turbolinksを無効化する方法として、gemファイルから直接削除する方法もあるようですが、
元々turbolinksはページの読み込みを高速化するgemです。

ボタンを押すたびに画面が初期化され、
一旦真っさらになった後、再度jsやらcssを読み直す。。。待ってられません。

その辺の処理をTurbolinks5が全部やってくれているんです。

なるべく
①個別のページ(link_toごと)に無効にするか、
②jsファイルにコードを加えるだけにしましょう。

参考
https://qiita.com/suzy1031/items/d3a8e96fe4d31111d22e

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新規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

前提

  1. rbenv,Rubyのインストール
  2. MySQLのダウンロード
  3. rbenvへのbundleインストール(下記)
$ rbenv exec gem install bundler
$ rbenv rehash

手順

  1. プロジェクトを作成したいディレクトリに移動
  2. Railsインストール
  3. 上記のRailsを元にrails new
  4. Railsプロジェクトのセットアップ

1. プロジェクトを作成したいディレクトリに移動

$ mkdir projects
$ cd projects

2. Railsインストール

  1. Gemfile作成
  2. Gemfile書き換え(もちろんvimをはじめとしたエディタで編集してもおk)
  3. 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-test

rails newの際にオプションをつけることでMySQLおよびRSpecのためのプロジェクトであることを示しています.
-B--skip-bundleと同義であり,先のbundle installにおいてvendor/bundleへのパス指定を行わなかった場合は必要ないと思われます.

4. Railsプロジェクトのセットアップ

残念ながらこれだとRails6で標準のwebpackerがインストールされていないため,サーバーを起動してもエラーが起こります.なのでこれを解決するために以下.

$ brew install yarn
$ be rails webpacker:install

yarnは一度インストールしておけば問題ないですが,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系が多いというお話もお聞きしますし.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]ransackを使わずに検索機能の実装

実装すること

投稿(Item)のタイトル(title)の検索

モデルの作成

$ rails g model Item title:string
$ rails db:migrate

ルーティングの作成

config/routes.rb
Rails.application.routes.draw do

    get "search" => "items#search", as: 'search'

end

Itemコントローラーの作成・編集

$ rails g controller items
app/controllers/items_controller.rb
class 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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=3

コーヒーマップ.png

ER図

Taistテーブルは、投稿(コーヒー)のテイスト(味)データを格納します。
Itemテーブルは、投稿(コーヒー)のデータを格納します。
関係は、Taist:Item = 1:Nになります。

おすすめ機能ER図.png

アソシエーションの確認

Taistモデル

accepts_nested_attributes_forは、Railsが標準で提供している、ActiveRecordのメソッドの一つです。モデル同士が関連付けられている時に、ネストさせることで一度にまとめてレコードの更新ができるようになります。
今回はTaistレコードが作成・更新されるとそれに紐づいたItemレコードも作成・更新されるようにしています。
allow_destroy: trueをattributesメソッドに追加することで削除可能になります。削除するには対象となるレコードに_destroy: 1のようなパラメーターを渡します。

app/models/taist.rb
class Taist < ApplicationRecord
    #アソシエーション
    has_many :items
    #親子同時保存(親:taist/子:item)
    accepts_nested_attributes_for :items, allow_destroy: true
end

Itemモデル

app/models/item.rb
class 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.rb
class 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
end

taists/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.rb
class 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


end

users/show.html.erb

app/views/users/show.html
<h1>あなたへのおすすめ</h1>
<h3>タイトル</h3>
    <%= @like_recommend.title %>
<h3>投稿</h3>
    <%= attachment_image_tag @like_recommend, :image %>

最後に

最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。

GitHub
https://github.com/yuto1014/find_coffee.git

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Devise ログインボタン押せない

問題

ログインボタン押せない
リロードするとログインボタンを押せるようになる

解決方法

Gemfile
gem 'turbolinks', '~> 5'

Gemfile
# gem 'turbolinks', '~> 5'

コメントアウトするとうまく行きます。

bundle updateでコメントアウトを反映させましょう

ターミナル
$ bundle update
app/assets/javascripts/application.js
//= require turbolinks
# ↑これも消しましょう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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無しでもカラム追加を反映できていたのです。
何が違うとこうなるんでしょうか?(そこまで調べ切れていません...)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails環境構築その3【terraform】

はじめに

terraformをつかってAWS(EC2,RDS)を作成します。

全体の流れ

  1. terraformインストール
  2. AWSでIAMユーザー作成
  3. AWS CLI導入
    1. AWS CLIにIAMユーザーの登録
  4. terraformとは
  5. terraform導入
    1. プロバイダーの設定
    2. VPC作成
    3. サブネット作成
    4. インターネットゲートウェイ作成
    5. ルートテーブル作成
    6. EC2作成
    7. Security Groupの作成
    8. RDS作成

参考
* 10分で理解するTerraform

terraformインストール

$ brew update
$ brew install terraform

AWSでIAMユーザー作成

参考
* Identity and Access Management へようこそ
* Terraformで構築するAWS

AdministratorAccessポリシー(管理者権限)を選択してください。

権限が足りないと
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 --version

AWS CLIにIAMユーザーの登録

ターミナル(ホームディレクトリ)
aws configure
または、
aws configure --profile ユーザー名

順番に記入します

ターミナル
AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [None]: 
Default output format [None]: json
# Asia Pacific (Tokyo) ap-northeast-1

terraformとは

インフラのコード化であります。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.tf
provider "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.tf
resource "aws_internet_gateway" "aws-tf-igw" {
  vpc_id = aws_vpc.aws-tf-vpc.id
  tags = {
    Name = "aws-tf-igw"
  }
}

ルートテーブルの作成

使うリソースaws_route_table

main.tf
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"
  }
}

サブネットの関連付けでルートテーブルをパブリックサブネットに紐付け

使うリソースaws_route_table_association

main.tf
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側に作る)

使うリソースaws_instanceaws_key_pair

main.tf
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"
  }
}

# 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_groupaws_security_group_rule

main.tf
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
}

Security Group詳細

ElasticIP作成

使うリソースaws_eip

main.tf
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}"
}

outputについて

ElasticIPなどはGUIから確認できますが、outputブロックで記述するとterraform apply実行時に下記のように出力されます。

Apply complete! 

Outputs:

example-public-ip = ElasticIP

RDSの作成

RDSは2つのサブネットが必要なのであと一つ違うアベイラビリティゾーンから作成します。
使うリソースaws_subnetaws_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_groupaws_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.tf

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.tfvars
public_key_path = "~/.ssh/terraform-aws.pub"
aws-td-db-username = ※※※※※※※※※※
aws-td-db-password = ※※※※※※※※※
それぞれ指定してください。

実行

terraform plan
terraform apply
# 確認
terrafom show

RDSインスタンス作成時にハマったエラーについて

その1
DBName must begin with a letter and contain only alphanumeric characters

これはRDSインスタンスのnameについてのエラーです。私の場合は`name = "db11"`と書きましたが、文字で初めて英数字両方を書かないといけないらしいです。

参考
* AWS aws_db_instance DBName issue

その2
Error creating DB Instance: InvalidParameterValue: Invalid DB engine

これはRDSインスタンスの`engine`の名前の書き方でエラーが出ました。今回は`engine = "postgres"`と書きました。

参考
* "Invalid DB engine" when creating AWS/RDS Postgresql instance
* Engineのセクションを見てください

内容一式

main.tf
provider "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.tfvars
public_key_path = "~/.ssh/terraform-aws.pub"
aws-td-db-username = ※※※※※※※※※※
aws-td-db-password = ※※※※※※※※※

おしまい。
何かありましたらコメント欄で

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 キーボード ショートカット

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.5

rbenvでインストールした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に成功した。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#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:NilClass


Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2986

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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 :thinking:

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をお使いください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

そもそもコマンドって?

我々が普段使っているcdlsなどは、魔法でもなんでもなく、全てプログラム
このプログラムを呼び出すための略称がコマンドという認識で良いと思います。

自分は、元々windowsユーザなのですが、Windowsユーザだと
exeファイルがイメージし易いかと思います。
C言語とかで、コマンドプロンプトにHelloWorldと表示するhellow.exeファイルを作成して、
hellow.exeをダブルクリックするとコマンドプロンプトが開きHelloWorldと表示されます。
lsコマンドだったらls.exeをダブルクリックしたら、ファイルのリストがコマンドプロンプト上に表示されるそんなイメージで自分はいます。
間違ってたらすみません。。。。

パスって?

コマンドが、プログラムということがわかりましたが、
パソコンは、そのプログラムが何処にあるか知りません。
hellow.exeがどのファイルに存在するのかパソコンは理解できないわけです。
そこで、PATHという環境変数に、コマンドは、このパスの配下にあるプログラムだよ
と教えてあげることで、パソコンが、どのファイルの配下にhellow.exeがあるか理解できるという仕組みです。
これが俗にいうパスを通すというやつです。

小学生の時に友人から、インストールって本質的には、パスを通すことなんだよって
言われて、その時は、何が何だかって感じでしたが、今となっては、なるほどって感じです。

環境変数って?

お使いのMacやWindowsに設定されている、変数です。
パソコンに設定されているもや、ログインしているユーザごとに設定されている変数もあります。

後書き

何故急にコマンドが呼び出せなくなったのか不明ですが、
コマンドってなんだっけパスとかなんだっけというLinuxの基礎知識を得るいい機会でした。

何か、間違ってることありましたら、優しくご指摘いただけますと幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[font-awsomeのエラー]NoMethodError in Creditcard#indexの一例

1.エラーの内容

スクリーンショット 2020-02-15 4.16.35.png

fontawsome利用の記述をしたところそのようなメソッドないというエラーが出ています

2.エラーの原因

公式の記載方法に則っていましたが、gemfileにgem "font-awesome-sass"が抜けているからno methodエラーとなっていました

3.エラーの解決方法

gemfileにgem "font-awesome-sass"を記載し、bundle installをすることで正しく読み込まれます

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActionController::UnknownFormat in CreditcardController#indexの解決方法

1.エラーの内容

<エラー文の例>
スクリーンショット 2020-02-15 3.04.40.png

題名部分の英語を読むと"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コマンドを打つ

これで題名のエラーについて大抵のものは解決できるかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]ActionController::UnknownFormat in UsersController#indexの解決方法

1.エラーの内容

<エラー文の例>
スクリーンショット 2020-02-15 4.03.58.png

題名部分の英語を読むと"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コマンドを打つ

これで題名のエラーについて大抵のものは解決できるかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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をかけて引っかからなかったらインスタンスを作成する便利なメソッド。データべースに保存はしない。
  • input[type="text"]:focusはinput要素のtype="text"がユーザーの操作でフォーカスされた時のスタイルを指定する。疑似要素=:hoverと同類。

感想

  • 単純にとても楽しかった。最近取り掛かっているスクール課題と違って答えのある安心感もあった。
  • また最初にアプリケーションのデータベース設計について解説されていたのが良かった。
  • Deviseをはじめて使ったけど簡単すぎてびっくり。
    • ただその分魔法のようなところが多くて、英語のリファレンスを読み込まないと自分の動かしたいように動かせないなとも感じた。
  • CSSをきちんと書いてくださってるので、見た目が本当に綺麗なアプリが出来ていくのでテンションが上がる。
    • 学習初日はCSS手打ちせずコピペしていたが、それでは勉強にならないと思い、手打ちして1行か2行追加するごとにページを読み込み直してCSSの挙動を確認した。
  • 少し複雑そうなコードに関しては説明があるので、基本的な部分で詰まるところは少なかった。
  • ただし、JavaScript(jTinder関連のコード)や、Action Cableの部分は分からないところがまだまだ多く、説明もそこまで詳しくされていなかったのでもう一度見直す必要がある。
  • そんなAction Cableを使ったリアルタイムチャット機能が出来たときは今まで作ったことのないような機能だったので感動した。

終わりに

2周目はerbではなくSlimで書きながら、JavaScriptとAction Cableの理解に重点を置きつつ復習出来たらいいなと思います。
重めのスクール課題が待ち構えているので、それが終わって余裕がある時にやります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む