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

Railsに動画を挿入する方法

railsに動画を挿入する方法

結論

index.html.erb
<%= video_tag "earth.mov", class: "landing_video", loop: true, autoplay: true, muted: true %>

earth.movの箇所はそれぞれご自身で好きな動画をこちらに当てはめてください。

基本的にmp4でもmovでも形式は問われないと思いますが、UIを考えたときにあまり容量の大きすぎるものだと使い勝手が悪くなってしまうと思うのでその辺りはお気をつけて!

現場からは以上です!

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

お名前ドットコムで取得したドメインが急に使用出来なくなった時

デプロイで使用しているのは、RDS、EC2、route53です。
今回、ターミナルでコマンド dig 取得したドメインでstatusを確認したら
status: NXDOMAINが見つかり、これを解決するために一日かかりました。

急に使えなくなった場合の解決策は、
①IPアドレスでアクセスできるか確認する。

IPアドレスでアクセスできた場合、下記の内容確認。
①お名前ドットコムにログインし重要なお知らせが来ていないか確認する。
②メールに重要メールが届いていないか確認する。

私の場合は、メールに
20xx年xx月xx日12:00までに登録情報の修正をお願いします。と届いていたのですが
それに気づかずに指定の日付を超えてしまった為、ドメインが使用できなくなっていました。
登録情報を修正したら、3日後ぐらいにドメインが使用できるようになりました。
お名前ドットコムからのメール多い....

digコマンドの説明

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

Railsでドットが2つ並んだやつに出くわした

カリキュラムにあったっけ?

はじめに見た時は、普通に記述ミスかと思いました。
メンターさんに聞いた時も初めは見たことないですね。と仰っていました!
調べてこれはオブジェクトと言うことを教えていただきました。

下記が問題のコードです。

date: @todays_date..@todays_date + 6)

これは、2点ドットですが、3点ドットもあります。

範囲オブジェクトというそうです。
SQLに保存する時に使用するみたいで、
・コードが少なく可読性が高い
・エラーになりにくい
上記のようなメリットがあるそうです。

どんな使いかた?

rails
User.where(age: ...30)

上記のコードが

SQL
SELECT "users".* FROM "users" WHERE "users"."age" < 30

たしかに短くなる。

初めの方のコードは、範囲抽出(Beetween)といいます。
簡単に言うと左の値から、右の値までって感じですね!

下記のページ参照させていただきました。
気になる人は見てみてください!

【Rails】範囲オブジェクト(Range)を使ったActiveRecordのwhere比較、範囲検索のコードの書き方https://simple-minds-think-alike.hatenablog.com/entry/active-record-where-with-range

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

ローカルサーバー起動できない!時の対処法

Ruby on Railsでpictweetのアプリケーション作成中。
ローカルサーバーを再起動しようと思ったが "cotroll + c"を入力しても反応なし。
ターミナルを強制終了し、再度立ち上げてから "rails s"を入力。

すると、エラー発生。
”Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)”
とのことで、なにやら3000番のポートが使用中だそうだ(よくわかってない)。

「ターミナルを再起動した時にローカルサーバーが立ち上がらない」
という状況である。

サーバーを終了しないままターミナルを閉じた時に生じる現象らしい。

色々調べた結果、

1、lsofコマンドでポートで実行中のファイルを確認する
2、そのファイルをkillコマンドで削除する

という手順で解決した。

1、lsofコマンドでポートで実行中のファイルを確認する

$ lsof -i :3000

今回は3000番ポートなので、最後の数字を3000で入力。
結果が↓

COMMAND  PID      USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ruby    8376 SHIGEDEEN   13u  IPv4 0xa2239c9f9d219327      0t0  TCP localhost:hbci (LISTEN)
ruby    8376 SHIGEDEEN   14u  IPv6 0xa2239c9f85859677      0t0  TCP localhost:hbci (LISTEN)
ruby    8376 SHIGEDEEN   25u  IPv6 0xa2239c9f92497157      0t0  TCP localhost:hbci->localhost:58898 (CLOSE_WAIT)
ruby    8376 SHIGEDEEN   27u  IPv6 0xa2239c9f7cc60a37      0t0  TCP localhost:hbci->localhost:58871 (CLOSE_WAIT)
ruby    8376 SHIGEDEEN   28u  IPv6 0xa2239c9f6f09fb37      0t0  TCP localhost:hbci->localhost:58879 (CLOSE_WAIT)
ruby    8376 SHIGEDEEN   29u  IPv6 0xa2239c9f9040b777      0t0  TCP localhost:hbci->localhost:58885 (CLOSE_WAIT)

ここで、PID接続しているものを消せばよいらしい。

2、実行中ファイルをkillコマンドで削除する

というわけで 8376 を killコマンドで消します。

kill -QUIT 8376

または、

kill -QUIT <PID>

これで、サーバーを再起動することができるようになりました。

参考文献

今回参考にさせていただいた記事はこちら?
(ローカルサーバを停止せずにターミナルを閉じてしまいました。:http://song-of-life.hatenablog.com/entry/2017/02/02/204010)
(げ!ローカルサーバーを切らずにターミナルを消しちゃった!:https://qiita.com/nagao_norihiro/items/aba40bd4e0eac9f9a92d)

ちなみに、lsofコマンドは他にも有用な使い道があるようだ。
(lsofコマンド入門:https://qiita.com/hypermkt/items/905139168b0bc5c28ef2)

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

DelegationError について(Active Storage)

ポートフォリオ作成中
DelegationError
とエラーが出てその下に内容が
variant delegated to attachment, but attachment is nil
(google翻訳)添付ファイルに委譲された変形体で、添付ファイルがnilである
とエラーが出ました。

解決方法

私のアプリはInstagramのようなアプリを開発しているので写真がないためこのエラーが出たと仮説を立て
データベースをリセットすることでこの問題を解決することができました!!

対策として

このエラーの対策として、写真を保存するカラムにイメージが存在しないと保存ができないようにしてこのエラーが出ないように使用と思います

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

Rails Tutorial 第2章学習編

Rails Tutorial 第2章で(。´・ω・)ん?って思ったところ

演習
CSSを知っている読者へ: 新しいユーザーを作成し、ブラウザのHTMLインスペクター機能を使って「User was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?

Google Chrome Shift+Ctrl+I or (F12) or 右クリックした中にある検証をクリック
image.png
HTML・CSSコードが見れます、
この機能を使って「User was successfully created.」を調べろって事だと思います('ω')ノ
もう少し詳しく知りたい方は⇒こちらをどうぞ

簡単な内容のおさらい。

ユーザー登録機能を作る
魔法のコマンド 『 Rails g scaffold 』( scaffoldは日本語で足場という意味 )  

$ rails generate scaffold User name:string email:string
$ rails db:migrate

このたった2つコマンドを叩くだけでこんなページが出来上がる。
ユーザー登録が出来る・・・もはや魔法の領域。
image.png

Scaffoldの欠点

  • データ検証がない。(空欄・でたらめなアドレスでも登録できちゃう)
  • ユーザー認証がない。(ログイン・ログアウト機能はない。誰でもユーザーページにアクセスできちゃう)
  • レイアウトやスタイルが整っていない。
  • テストが書かれていない。
  • 理解が困難。

投稿機能を作る

$ rails generate scaffold Micropost content:text user_id:integer
$ rails db:migrate

先ほどと同じく2つコマンドを叩くだけとはいかない。
※ルーティングを追加する必要がある。

ruby config/routes.rb
Rails.application.routes.draw do
  resources :microposts # ←これを追加するとurl/micropostで投稿ページにアクセスできる
  resources :users
  root 'users#index'
end

models/micropost.rbにvalidatesを追加する事で検証機能を実装できる

ruby app/models/micropost.rb
class Micropost < ApplicationRecord
  validates :name, length: { maximum: 140 } #入力できる文字列は140文字まで
                   presence: true #文字列が入力されている事を確認する
end

異なるデータモデル同士の関連付ける

ruby app/models/user.rb
class User < ApplicationRecord
#ユーザーはマイクロポストを沢山持ってるのでhas many :micropostを追加
  has_many :microposts
end
ruby app/models/micropost.rb
class Micropost < ApplicationRecord
#マイクロポストはユーザーに紐づいているのでbelongs_to :userを追加
  belongs_to :user
  validates :content, length: { maximum: 140 }
end

これでRDBの関連付けが完了する。わ~い簡単( *´艸`)

Rails Console (Railsアプリを対話的に操作できる便利ツール)

# Rails コンソール
$ rails c
>> #ここにRubyコード記述をすると実行してくれる

>> first_user = User.first # Userテーブルからidが1のユーザーを変数first_userに格納

コマンドの処理内容
User.first
↓ (自動的にデータベースを操作するSQLコマンドに変換される。)
User Load (0.2ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC
  LIMIT ?  [["LIMIT", 1]]
↓ SQLの実行結果
 => #<User id: 1, name: "Michael Hartl", email: "michael@example.org",
 created_at: "2020-10-23 00:39:14", updated_at: "2020-10-23 00:41:24">
↓ 
SQLの実行結果をfirst_userに格納

>> first_user.microposts #先ほどのユーザーが投稿したmicropostを全部取り出す。
Micropost Load (3.2ms)  SELECT "microposts".* FROM "microposts" WHERE
  "microposts"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 1, content:
 "First micropost!", user_id: 1, created_at: "2020-10-23 02:04:13", updated_at:
 "2020-10-23 02:04:13">, #<Micropost id: 2, content: "Second micropost",
 user_id: 1, created_at: "2020-10-23 02:04:30", updated_at: "2020-10-23
 02:04:30">]>

>> micropost = first_user.microposts.first #first_userの1番目の投稿を変数micropostに格納

感想

この章はあまり躓かなかったです。('ω')ノ
git&herokuの便利さに惚れ惚れしてきました。

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

[Ruby] 二次元配列の取り出し

この記事ではmacOS Catalina10.15.6にインストールしたRuby 2.6.5を使っています。

二次元配列とは

  • 配列の中に配列が入っている形です。
  • items_priceのハッシュの中にそれぞれのアイテムが配列として格納されています。
  • さらに、1つ1つのアイテムの中にも名前と値段の配列が入っています。
items_price = [["pen", [100, 200, 120]], ["book", [120, 150, 220]], ["pen_case", [1000, 1500]]]

二次元配列を取り出すには

  • each文を使ってハッシュを取り出します。
  • 今回は二次元なので2回each文を使っているのですね。
items_price = [["pen", [100, 200, 120]], ["book", [120, 150, 220]], ["pen_case", [1000, 1500]]]

items_price.each do |item|
  sum = 0
  item[1].each do |price|
    sum += price
  end
  puts "#{item[0]}の値段は#{sum}円です。"
end

では、penを例に取って一つずつみていきます。

1回目のeach(2行目)

items_price.each do |item| ~ end
# => items_price配列の中の["pen", [100, 200, 120]]を取り出し、ブロック変数itemに代入

2回目のeach(4行目)

item[1].each do |price| ~ end
# => item配列の中の[100, 200, 120]を取り出し、ブロック変数priceに代入
  • ちなみに、item[1]はprice配列の要素の数、つまりここでは3回繰り返すことになります。

出力する

puts "#{item[0]}の値段は#{sum}円です。"
  • item[0]はitem配列の添字が0、["pen", [100, 200, 120]]の"pen"の部分になります。

結果

penの値段は420円です。
bookの値段は490円です。
pen_caseの値段は2500円です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails6 Vue.js】axiosを使ってCSVインポート処理を実装する

Vue.jsとRailsAPIを使って複数のCSVファイルをそれぞれのテーブルにインポートする必要があったので記録しておきます。

ご留意ください

Rails,Vue.js学習中の初学者が備忘録を兼ねて書いています。
内容に誤りを含む可能性・さらに良い手法がある可能性が多分にありますので、参考にする際はその点ご留意ください。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。

実現したいこと

inputボックスを用意し、選択したCSVファイル(2種類)をそれぞれ用意したテーブルにaxiosを使ってPOSTしたい。

csv1 ⇨ code_listsテーブル
csv2 ⇨ financialsテーブル
テーブルの定義は下記の通りです。

code_listsテーブル
  create_table "code_lists", force: :cascade do |t|
    t.string "edinet"
    t.string "securities"
    t.string "company"
    t.string "sector"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["company"], name: "index_code_lists_on_company"
    t.index ["edinet"], name: "index_code_lists_on_edinet", unique: true
    t.index ["securities"], name: "index_code_lists_on_securities"
  end
financialsテーブル
  create_table "financials", force: :cascade do |t|
    t.string "edinet"
    t.date "rec_date"
    t.string "account_name"
    t.float "value"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["account_name"], name: "index_financials_on_account_name"
    t.index ["edinet", "rec_date", "account_name"], name: "index_financials_on_edinet_and_rec_date_and_account_name", unique: true
    t.index ["edinet"], name: "index_financials_on_edinet"
  end

下準備

事前にライブラリを追加します。

config/application.rb
require 'csv' #追記

Gemのrooを追加してbundle installします

Gemfile
gem 'roo'

axios-post 時にcsrfトークン対策が必要な為、別途プラグインを設定します。
pluginsフォルダを新たに作成してください。

app/javascript/packs/plugins/vue-axios.js
const VueAxiosPlugin = {}
export default VueAxiosPlugin.install = function(Vue, { axios }) {
  const csrf_token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
  axios.defaults.headers.common = {
    "X-Requested-With": "XMLHttpRequest",
    "X-CSRF-Token": csrf_token
  }

  Vue.axios = axios
  Object.defineProperties(Vue.prototype, {
    axios: {
      get () {
        return axios
      }
    }
  })
}

エントリーファイルにプラグインをimportします

hello_vue.js
import Vue from "vue/dist/vue.esm";
import axios from "axios"; // 追加
import VueAxiosPlugin from "./plugins/vue-axios";  // 追加
import App from "./components/App.vue";

Vue.use(VueAxiosPlugin, { axios: axios }) // 追加

new Vue({
  el: "#app",
  render: h => h(App),
})

ルーティング

ルーティングは次のように設定しました。
必要な箇所のみ記載しています。
それぞれRailsAPIのルーティング設定にしています。
code_listsテーブル用のcsvファイルはapi/code_lists/importへ
financialsテーブル用のcsvファイルはapi/financials/importへPOSTします。

config/routes.rb
Rails.application.routes.draw do
  (上記省略)
  namespace :api, format: 'json' do
    resources :code_lists do
      post :import, on: :collection
    end
  end

  namespace :api, format: 'json' do
    resources :financials do
      post :import, on: :collection
    end
  end
  (以下省略)
end

View

ビューは次のようにしました。

Import.vue
<template>
  <div>
    <div class="import-form">
      <input @change="selectedFile" type="file" name="file">
    </div>
    <div class="import-form">
      <button @click="upload('/api/code_lists/import')" type="submit">コードリストのアップロード</button>
    </div>
    <div class="import-form">
      <button @click="upload('/api/financials/import')" type="submit">財務データのアップロード</button>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  data: function(){
    return {
      uploadFile: null
    };
  },
  methods: {
    selectedFile: function(e) {
      // 選択された File の情報を保存しておく
      e.preventDefault();
      let files = e.target.files;
      this.uploadFile = files[0];
    },
    upload: function(url) {
      // FormData を利用して File を POST する
      let formData = new FormData();
      formData.append('file', this.uploadFile);
      // let config = {
      //     headers: {
      //         'content-type': 'multipart/form-data'
      //     }
      // };
      axios
          .post(url, formData)
          .then(function(response) {
              // response 処理
          })
          .catch(function(error) {
              // error 処理
          })
    }
  }
}
</script>

inputボックスのファイル選択後にselectedFileメソッドでファイルをuploadFileに保存します。そして、buttonのクリックイベントでuploadメソッドが実行されます。uploadメソッドの引数のurl(先ほどroutesで設定した2つのurl)へファイルをPOSTします。POSTする際に事前に保存したuploadFileをFormDataオブジェクトに渡している事がポイントです。
今回はconfigは使用しなかった為コメントアウトしました。

サーバサイド

今回は2つのcsvファイルをそれぞれのテーブルにImportする実装となり、それぞれのモデル毎に実装が必要となりますが、ほぼ同じ内容となる為Financial Modelの実装のみ記載しておきます。
まずコントローラについて。

app/controllers/api/financials_controller.rb
class Api::FinancialsController < ApplicationController
  def import
    Financial.import(params[:file])
  end
end

コントローラはFinancial Classのクラスメソッドを呼び出すだけとなります。

続いて、financialモデルの記述は以下の通りです。

app/models/financial.rb
class Financial < ApplicationRecord
  validates :edinet, presence: true
  validates :rec_date, presence: true
  validates :account_name, presence: true
  validates :value, presence: true
  def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|
      # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成
      financial = find_by(edinet: row["edinet"], rec_date: row["rec_date"], account_name: row["account_name"]) || new
      # CSVからデータを取得し、設定する
      financial.attributes = row.to_hash.slice(*updatable_attributes)
      # 保存する
      financial.save
    end
  end
  def self.updatable_attributes
    ["edinet", "rec_date", "account_name", "value"]
  end
end

インポートしたいCSVのカラム名をupdatable_attributesに入力。
またfind_byで既に取り込み済みのレコードかどうかを確認しており、取り込み済みの場合は上書きされます。

最後に

現何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
上場企業の財務データを活用したアプリケーションを開発中です。フロントエンドをVue.js、バックエンドはRailsAPIを使って開発を進めています。引き続き、得られた知見は記事にしてまとめていきたいと思います。
ここまでお付き合いいただきありがとうございました!

参考にさせていただいた記事:
csrf対策:【Vue】axiosで、デフォルトでCSRFトークンを設定できるようにする
フロントエンド実装:Vue.js でファイルをポストしたいとき
バックエンド実装:【Ruby on Rails】CSVインポート

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

【Rails6+Vue.js】axiosを使ってCSVインポート処理を実装する

Vue.jsとRailsAPIを使って複数のCSVファイルをそれぞれのテーブルにインポートする必要があったので記録しておきます。

ご留意ください

Rails,Vue.js学習中の初学者が備忘録を兼ねて書いています。
内容に誤りを含む可能性・さらに良い手法がある可能性が多分にありますので、参考にする際はその点ご留意ください。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。

実現したいこと

inputボックスを用意し、選択したCSVファイル(2種類)をそれぞれ用意したテーブルにaxiosを使ってPOSTしたい。

csv1 ⇨ code_listsテーブル
csv2 ⇨ financialsテーブル
テーブルの定義は下記の通りです。

code_listsテーブル
  create_table "code_lists", force: :cascade do |t|
    t.string "edinet"
    t.string "securities"
    t.string "company"
    t.string "sector"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["company"], name: "index_code_lists_on_company"
    t.index ["edinet"], name: "index_code_lists_on_edinet", unique: true
    t.index ["securities"], name: "index_code_lists_on_securities"
  end
financialsテーブル
  create_table "financials", force: :cascade do |t|
    t.string "edinet"
    t.date "rec_date"
    t.string "account_name"
    t.float "value"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["account_name"], name: "index_financials_on_account_name"
    t.index ["edinet", "rec_date", "account_name"], name: "index_financials_on_edinet_and_rec_date_and_account_name", unique: true
    t.index ["edinet"], name: "index_financials_on_edinet"
  end

下準備

事前にライブラリを追加します。

config/application.rb
require 'csv' #追記

Gemのrooを追加してbundle installします

Gemfile
gem 'roo'

axios-post 時にcsrfトークン対策が必要な為、別途プラグインを設定します。
pluginsフォルダを新たに作成してください。

app/javascript/packs/plugins/vue-axios.js
const VueAxiosPlugin = {}
export default VueAxiosPlugin.install = function(Vue, { axios }) {
  const csrf_token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
  axios.defaults.headers.common = {
    "X-Requested-With": "XMLHttpRequest",
    "X-CSRF-Token": csrf_token
  }

  Vue.axios = axios
  Object.defineProperties(Vue.prototype, {
    axios: {
      get () {
        return axios
      }
    }
  })
}

エントリーファイルにプラグインをimportします

hello_vue.js
import Vue from "vue/dist/vue.esm";
import axios from "axios"; // 追加
import VueAxiosPlugin from "./plugins/vue-axios";  // 追加
import App from "./components/App.vue";

Vue.use(VueAxiosPlugin, { axios: axios }) // 追加

new Vue({
  el: "#app",
  render: h => h(App),
})

ルーティング

ルーティングは次のように設定しました。
必要な箇所のみ記載しています。
それぞれRailsAPIのルーティング設定にしています。
code_listsテーブル用のcsvファイルはapi/code_lists/importへ
financialsテーブル用のcsvファイルはapi/financials/importへPOSTします。

config/routes.rb
Rails.application.routes.draw do
  (上記省略)
  namespace :api, format: 'json' do
    resources :code_lists do
      post :import, on: :collection
    end
  end

  namespace :api, format: 'json' do
    resources :financials do
      post :import, on: :collection
    end
  end
  (以下省略)
end

View

ビューは次のようにしました。

Import.vue
<template>
  <div>
    <div class="import-form">
      <input @change="selectedFile" type="file" name="file">
    </div>
    <div class="import-form">
      <button @click="upload('/api/code_lists/import')" type="submit">コードリストのアップロード</button>
    </div>
    <div class="import-form">
      <button @click="upload('/api/financials/import')" type="submit">財務データのアップロード</button>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  data: function(){
    return {
      uploadFile: null
    };
  },
  methods: {
    selectedFile: function(e) {
      // 選択された File の情報を保存しておく
      e.preventDefault();
      let files = e.target.files;
      this.uploadFile = files[0];
    },
    upload: function(url) {
      // FormData を利用して File を POST する
      let formData = new FormData();
      formData.append('file', this.uploadFile);
      // let config = {
      //     headers: {
      //         'content-type': 'multipart/form-data'
      //     }
      // };
      axios
          .post(url, formData)
          .then(function(response) {
              // response 処理
          })
          .catch(function(error) {
              // error 処理
          })
    }
  }
}
</script>

inputボックスのファイル選択後にselectedFileメソッドでファイルをuploadFileに保存します。そして、buttonのクリックイベントでuploadメソッドが実行されます。uploadメソッドの引数のurl(先ほどroutesで設定した2つのurl)へファイルをPOSTします。POSTする際に事前に保存したuploadFileをFormDataオブジェクトに渡している事がポイントです。
今回はconfigは使用しなかった為コメントアウトしました。

コントローラーとモデル

今回は2つのcsvファイルをそれぞれのテーブルにImportする実装となり、それぞれのモデル毎に実装が必要となりますが、ほぼ同じ内容となる為Financial Modelの実装のみ記載しておきます。
まずコントローラについて。

app/controllers/api/financials_controller.rb
class Api::FinancialsController < ApplicationController
  def import
    Financial.import(params[:file])
  end
end

コントローラはFinancial Classのクラスメソッドを呼び出すだけとなります。

続いて、financialモデルの記述は以下の通りです。

app/models/financial.rb
class Financial < ApplicationRecord
  validates :edinet, presence: true
  validates :rec_date, presence: true
  validates :account_name, presence: true
  validates :value, presence: true
  def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|
      # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成
      financial = find_by(edinet: row["edinet"], rec_date: row["rec_date"], account_name: row["account_name"]) || new
      # CSVからデータを取得し、設定する
      financial.attributes = row.to_hash.slice(*updatable_attributes)
      # 保存する
      financial.save
    end
  end
  def self.updatable_attributes
    ["edinet", "rec_date", "account_name", "value"]
  end
end

インポートしたいCSVのカラム名をupdatable_attributesに入力。
またfind_byで既に取り込み済みのレコードかどうかを確認しており、取り込み済みの場合は上書きされます。

最後に

現何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
上場企業の財務データを活用したアプリケーションを開発中です。フロントエンドをVue.js、バックエンドはRailsAPIを使って開発を進めています。引き続き、得られた知見は記事にしてまとめていきたいと思います。
ここまでお付き合いいただきありがとうございました!

参考にさせていただいた記事:
csrf対策:【Vue】axiosで、デフォルトでCSRFトークンを設定できるようにする
フロントエンド実装:Vue.js でファイルをポストしたいとき
バックエンド実装:【Ruby on Rails】CSVインポート

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

RailsのAPIモードをCookie(session)認証に対応させる

Vue.js + RailsのAPIモードの認証方式をauth_tokenからsession(cookie)に変更した時の変更点をまとめる

これを始めた理由

既存のauth_token認証がWebアプリにとってセキュアでない。

代理ログインの要件がある

  • 普通のwebアプリで実装する予定の管理画面でログインしたユーザーとSPAで作られた管理画面にシームレスにログインさせるには
    • OAuthサーバーを作るか
    • Cookie(Session)の従来の認証に変える必要がある。

今回は納期優先でsession認証に変更

Cookie認証に必要だったもの

config/application.rb

    # Only loads a smaller set of middleware suitable for API only apps.
    # Middleware like session, flash, cookies can be added back manually.
    # Skip views, helpers and assets when generating a new resource.
    config.api_only = false # <- true を false に変更

session storeをcookieにする場合は config.api_only = true にした上で必要なmiddle wearを読み込む方法で行けたが、それはそれで

  • Deviseと相性が悪い
  • session storeにredisを使いたい

という理由でredis storeを使う時は、この方法以外に手段がなかった。

application_controller.rb (全ての基底のcontroller)

class ApplicationController < ActionController::API
  include ActionController::Cookies #<- これを追加

ActionController::API を継承して作る場合はこの設定も必要

axios(ajaxライブラリ)

  axios.create({
    // credential = cookie
    withCredentials: true,
    //...
  })

ajax側にCORS対応としてCookieを送る機能を利用する。

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

#Rails Tutorial 第1章学習編

Rails Tutorial 第一章で(。´・ω・)ん?って思ったところ

 Cloud9のユーザー登録

Cloud9は現在Amazon Web Services (AWS) に統合されたため、Cloud9を使うためにはAWSアカウントが必要になります。AWSアカウントを既にお持ちの場合は、AWSにログインしてください。AWSコンソールに行き、検索ボックスから “Cloud9” と入力すると、Cloud9の開発環境を作成するためのページに行けます。
Amazon Web Services (AWS) アカウントを持っていない場合は、AWS Cloud9のユーザー登録をします。悪用防止のためクレジットカード情報の入力が必須になりましたが、Railsチュートリアルのワークスペースは1年間無料なのでご安心ください。アカウントが有効になるまで最大で24時間かかりますが、著者の場合は約10分ほどで完了しました。
Cloud9の管理ページ (図 1.4) に無事たどり着いたら “Create environment” をクリックし、図 1.5のような画面になるまで進めてください。情報を適宜入力して、確認ボタンを押して進めていくと、最終的にCloud9の開発環境を手に入れることができます (図 1.6)。このとき “root” ユーザーであるという警告メッセージが表示されるときがありますが、こちらの対処方法は後ほど説明します (IAMという機能を使った対応方法について13.4.4で説明します)。
引用:https://railstutorial.jp/chapters/beginning?version=5.1#sec-development_environment

これじゃわかんねーよって感じだったのでYoutube上にあったcloud9の登録方法説明動画を参考にしました。

  コマンドでどうやってファイル開けばいいの? を解決

#Rails tutorialには便利なc9をインストール('ω')ノ
#c9 <ファイル名> でファイルを開けるよ。
npm install -g c9

  このコマンド何してるの? その1

Rubyのインストールで無駄な時間を使わないようにするために追記
$ echo "gem: --no-document" >> ~/.gemrc

UNIXコマンドの処理内容
echo "gem: --no-document" ("gem: --no-documentを)
>> (末尾に追記してね。)
~/.gemrcファイル (ホームディレクトリの下にある~/.gemrcというファイルに)

このコマンド何してるの? その2

JavaScriptソフトウェアの依存関係を管理するYarnをインストール
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)

UNIXコマンドの処理内容
source ファイルに書かれたコマンドを現在のシェルで実行するコマンド
<() : プロセス置換(コマンドの出力結果をファイルとして扱う機能)

curl -sL https://cdn.learnenough.com/yarn_install
curl [http://対象のURL]  HTTPリクエストを実施し結果を標準出力へ
-s -- 余計な出力をしない
-L -- リダイレクトがあったらリダイレクト先の情報を取る

yarn_installファイルの中身です。
#!/bin/bash

# Installs YARN on AWS Cloud9 Ubuntu Server.
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

やっと全貌が見えました! 以下のコマンドを実行してるんですね・・・!
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

これって・・・なんでしょう?
自分のプラットフォームに合ったYarnインストール手順(英語)
image.png

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

JavaScriptソフトウェアの依存関係を管理するYarnをインストールする為のコマンドって事だった。

このコマンド何してるの? その3

sudo ln -sf `which nano` /usr/bin

UNIXコマンドの処理内容
sudo 管理者で実行
ln -sf シンボリックリンク(ショートカット)を作成

/usr/binという場所に飛ぶwhich nanoという名前のショートカットを作成?

クラウドIDEを使う場合は、次にGitで使うデフォルトのエディタを設定します(編集や、“amend”でプロジェクトを変更するのに使われます)。ここでは、比較的使いやすくクラウドIDEのデフォルトエディタでもあるnanoエディタを使うことにしましょう。この設定を書き込んでもログアウト時にデフォルトエディタはログアウトしてしまいますし、パスも正しくないので、を実行してシンボリックリンク(symlinkとも呼ばれます)を作成してnanoの実行ファイルを正しく指すようにしましょう (初心者には少々難しいので、今読んで理解できなくても心配は不要です)。

(。´・ω・)ん? でどうすりゃいいの? まぁ飛ばすか( ^ω^)・・・。

Rails Tutorialで使用するデータベースソフト(SQL)について

cloud9(開発環境)ではsqliteを使用。
HerokuではPostgreSQLを使用。
開発環境と本番環境は同じSQLを使う事が推奨。

なんで違うの使っているだろう学習の為なのかな?(´-ω-`)分からない。

Amazon RDS での AWS 無料利用枠
DB インスタンスを各月で連続して実行するのに十分な、MySQL、MariaDB、PostgreSQL、Oracle BYOL、または SQL Server (SQL Server Express Edition) などを使う Amazon RDS の Single-AZ db.t2.micro インスタンスのための、750 時間という使用時間
Oracle BYOL db.t3.micro Single-AZ インスタンスの使用は、Amazon RDS の無料利用枠に含まれています。Oracle BYOL の上で、db.t2.micro Single-AZ と db.t3.micro Single-AZ のインスタンスを両方実行する場合、その使用量はインスタンスクラス全体で合計されます。
20 GB の汎用 (SSD) DB ストレージ*
自動データベースバックアップとユーザーによる任意の DB スナップショットに使用できる 20 GB のバックアップストレージ*

PostgreSQL使えるのに・・・なんでだろう。。。

Railsインストール関連

Railsをインストール RubyGemsが提供するgemコマンドを使う。
gem install rails -v 6.0.3
gem install [アプリ] -v [バージョン指定]
#Railsで空のテンプレート作成コマンド
rails _6.0.3_ new hello_app
rails _[version]_ new [アプリ名]
Gemfile.はインストールするライブラリ(部品)を記述するファイル
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.3'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3'
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster.
# Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a
  # debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console'
  # anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  # Spring speeds up development by keeping your application running in the
  # background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
#Gemfileの中に記載されてるライブラリを一括インストールするコマンド
bundle install

#インストールしたライブラリに依存関係で問題が発生した場合に使うコマンド
bundle update
#Railsサーバーの立ち上げコマンド
rails server
rails s #(省略形) ← こっちの方がよく使う
rb config/environments/development.rb
Rails.application.configure do
  .
  .
  .
  # Cloud9 への接続を許可する
  config.hosts.clear ←これ追記しないとRailsサーバーへのアクセスで弾かれる
end

Git関連

Gitはソースコードを管理する便利なアプリ
Githubはインターネット上にソースコードを保管する場所を提供してくれるサービス

# Gitで使う名前の設定とメールアドレス設定
# ※Gitに設定する名前やメールアドレスは、今後リポジトリ上で一般に公開されるので注意

$ git config --global user.name "名前"
$ git config --global user.email メールアドレス

# checkoutコマンドをcoに省略できるようにする。
$ git config --global alias.co checkout

# git pushする時にパスワード要求される時間を変える 毎回パスワード打つのめんどさを軽減
$ git config --global credential.helper "cache --timeout=86400"

# リポジトリ(保存箱)を作るコマンド
$ git init

# 一時保存箱にファイルを入れるコマンド -A は変更(新規・追加・削除)があったファイルを全て追加という意味
$ git add -A

# 一時保存箱にメモを付けて保存箱へ移すコマンド -m "メッセージ" でメモを書ける
$ git commit -m "Initialize repository"

# インターネット上にある自分のソースコード置き場をoriginという名前で登録
$ git remote add origin https://github.com/<あなたのGitHubアカウント名>/hello_app.git

# 自分のアップロード
$ git push -u origin master

# 変更があるかを確認できる
$ git status

# 過去にcommitした内容を確認
$ git log

Heroku関連

Herokuはインターネット上でアプリを公開させてくれるサービス(5個まで無料)
ソースコードのバージョン管理にGitを使っていれば、Railsアプリケーションを簡単に公開できる

開発環境と本番環境で環境が違うのでそれに合わせてインストールするライブラリを変更する必要がある。
cloud9(開発・テスト環境) = sqlite3 Heroku(本番環境) = PostgreSQL

ruby.Gemfile
source 'https://rubygems.org' 
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

#ここから上は全て共通でインストールするんだと思う('ω')ノ

#開発環境とテスト環境のみインストール
group :development, :test do 
  gem 'sqlite3', '1.4.1'
  gem 'byebug',  '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

#開発環境のみインストール
group :development do 
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

#テスト環境のみインストール
group :test do 
  gem 'capybara',           '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  gem 'webdrivers',         '4.1.2'
end

#本番環境のみインストール
group :production do 
  gem 'pg', '1.1.4' 
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
#本番用以外のgemをインストールする
$ bundle install --without production

pg gemを追加したことやRubyバージョンを指定したことをGemfile.lockに反映させないと、
本番環境へのデプロイで失敗してしまうためです。

#herokuコマンドのインストール
source <(curl -sL https://cdn.learnenough.com/heroku_install)

実行してる中身のコマンドはこれ!
curl -OL https://cli-assets.heroku.com/heroku-linux-x64.tar.gz
tar zxf heroku-linux-x64.tar.gz && rm -f heroku-linux-x64.tar.gz
sudo mv heroku /usr/local
echo 'PATH=/usr/local/heroku/bin:$PATH' >> $HOME/.profile
source $HOME/.profile > /dev/null
# herokuにログインする (--interactiveオプションを使うとブラウザをかずにログインできる)
$ heroku login --interactive

# Herokuに新しいアプリケーションを作成する (gitのソースコードを解析して適切なサーバーを用意)
$ heroku create 

# ソースコードをアップロードする これでネット上に自分の作ったサービスが公開される。 
$ git push heroku master

感想

Cloud9・Git・Herokuを使ってHello Worldだけど公開サービスができた!
アプリ開発だけで終わる本が多い中でサービスを公開まで1章で紹介されているのは本当にすごいと思う。
アウトプットする事でUNIXコマンドで躓いた部分を調べて謎が解けた。
UNIXコマンド・・・(゜-゜) 事前学習読んだんだけどな
source 、(プロセス置換) <()、curl、 In こんなん書いてあったっけ・・・。 

さぁ 2章も張り切って頑張るぞー( `ー´)ノ

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

Rails Tutorial 第1章学習編

Rails Tutorial 第一章で(。´・ω・)ん?って思ったところ

 Cloud9のユーザー登録

Cloud9は現在Amazon Web Services (AWS) に統合されたため、Cloud9を使うためにはAWSアカウントが必要になります。AWSアカウントを既にお持ちの場合は、AWSにログインしてください。AWSコンソールに行き、検索ボックスから “Cloud9” と入力すると、Cloud9の開発環境を作成するためのページに行けます。
Amazon Web Services (AWS) アカウントを持っていない場合は、AWS Cloud9のユーザー登録をします。悪用防止のためクレジットカード情報の入力が必須になりましたが、Railsチュートリアルのワークスペースは1年間無料なのでご安心ください。アカウントが有効になるまで最大で24時間かかりますが、著者の場合は約10分ほどで完了しました。
Cloud9の管理ページ (図 1.4) に無事たどり着いたら “Create environment” をクリックし、図 1.5のような画面になるまで進めてください。情報を適宜入力して、確認ボタンを押して進めていくと、最終的にCloud9の開発環境を手に入れることができます (図 1.6)。このとき “root” ユーザーであるという警告メッセージが表示されるときがありますが、こちらの対処方法は後ほど説明します (IAMという機能を使った対応方法について13.4.4で説明します)。
引用:https://railstutorial.jp/chapters/beginning?version=5.1#sec-development_environment

これじゃわかんねーよって感じだったのでYoutube上にあったcloud9の登録方法説明動画を参考にしました。

AWS Cloud9の登録にはクレジットカードが必須
あんまりいないと思うけどクレカ作れない人向け(´-ω-`)
住信SBIネット銀行で口座開設すれば貰えるデビットカードで代用できます('ω')ノ

  コマンドでどうやってファイル開けばいいの? を解決

#Rails tutorialには便利なc9をインストール('ω')ノ
#c9 <ファイル名> でファイルを開けるよ。
npm install -g c9

  このコマンド何してるの? その1

Rubyのインストールで無駄な時間を使わないようにするために追記
$ echo "gem: --no-document" >> ~/.gemrc

UNIXコマンドの処理内容
echo "gem: --no-document" ("gem: --no-documentを)
>> (末尾に追記してね。)
~/.gemrcファイル (ホームディレクトリの下にある~/.gemrcというファイルに)

このコマンド何してるの? その2

JavaScriptソフトウェアの依存関係を管理するYarnをインストール
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)

UNIXコマンドの処理内容
source ファイルに書かれたコマンドを現在のシェルで実行するコマンド
<() : プロセス置換(コマンドの出力結果をファイルとして扱う機能)

curl -sL https://cdn.learnenough.com/yarn_install
curl [http://対象のURL]  HTTPリクエストを実施し結果を標準出力へ
-s -- 余計な出力をしない
-L -- リダイレクトがあったらリダイレクト先の情報を取る

yarn_installファイルの中身です。
#!/bin/bash

# Installs YARN on AWS Cloud9 Ubuntu Server.
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

やっと全貌が見えました! 以下のコマンドを実行してるんですね・・・!
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

これって・・・なんでしょう?
自分のプラットフォームに合ったYarnインストール手順(英語)
image.png

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get -y update && sudo apt-get -y install yarn

JavaScriptソフトウェアの依存関係を管理するYarnをインストールする為のコマンドって事だった。

このコマンド何してるの? その3

sudo ln -sf `which nano` /usr/bin

UNIXコマンドの処理内容
sudo 管理者で実行
ln -sf シンボリックリンク(ショートカット)を作成

/usr/binという場所に飛ぶwhich nanoという名前のショートカットを作成?

クラウドIDEを使う場合は、次にGitで使うデフォルトのエディタを設定します(編集や、“amend”でプロジェクトを変更するのに使われます)。ここでは、比較的使いやすくクラウドIDEのデフォルトエディタでもあるnanoエディタを使うことにしましょう。この設定を書き込んでもログアウト時にデフォルトエディタはログアウトしてしまいますし、パスも正しくないので、を実行してシンボリックリンク(symlinkとも呼ばれます)を作成してnanoの実行ファイルを正しく指すようにしましょう (初心者には少々難しいので、今読んで理解できなくても心配は不要です)。

(。´・ω・)ん? でどうすりゃいいの? まぁ飛ばすか( ^ω^)・・・。

Rails Tutorialで使用するデータベースソフト(SQL)について

cloud9(開発環境)ではsqliteを使用。
HerokuではPostgreSQLを使用。
開発環境と本番環境は同じSQLを使う事が推奨。

なんで違うの使っているだろう学習の為なのかな?(´-ω-`)分からない。

Amazon RDS での AWS 無料利用枠
DB インスタンスを各月で連続して実行するのに十分な、MySQL、MariaDB、PostgreSQL、Oracle BYOL、または SQL Server (SQL Server Express Edition) などを使う Amazon RDS の Single-AZ db.t2.micro インスタンスのための、750 時間という使用時間
Oracle BYOL db.t3.micro Single-AZ インスタンスの使用は、Amazon RDS の無料利用枠に含まれています。Oracle BYOL の上で、db.t2.micro Single-AZ と db.t3.micro Single-AZ のインスタンスを両方実行する場合、その使用量はインスタンスクラス全体で合計されます。
20 GB の汎用 (SSD) DB ストレージ*
自動データベースバックアップとユーザーによる任意の DB スナップショットに使用できる 20 GB のバックアップストレージ*

PostgreSQL使えるのに・・・なんでだろう。。。

Railsインストール関連

Railsをインストール RubyGemsが提供するgemコマンドを使う。
gem install rails -v 6.0.3
gem install [アプリ] -v [バージョン指定]
#Railsで空のテンプレート作成コマンド
rails _6.0.3_ new hello_app
rails _[version]_ new [アプリ名]
Gemfile.はインストールするライブラリ(部品)を記述するファイル
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.3'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3'
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster.
# Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a
  # debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console'
  # anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  # Spring speeds up development by keeping your application running in the
  # background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
#Gemfileの中に記載されてるライブラリを一括インストールするコマンド
bundle install

#インストールしたライブラリに依存関係で問題が発生した場合に使うコマンド
bundle update
#Railsサーバーの立ち上げコマンド
rails server
rails s #(省略形) ← こっちの方がよく使う
rb config/environments/development.rb
Rails.application.configure do
  .
  .
  .
  # Cloud9 への接続を許可する
  config.hosts.clear ←これ追記しないとRailsサーバーへのアクセスで弾かれる
end

Git関連

Gitはソースコードを管理する便利なアプリ
Githubはインターネット上にソースコードを保管する場所を提供してくれるサービス

# Gitで使う名前の設定とメールアドレス設定
# ※Gitに設定する名前やメールアドレスは、今後リポジトリ上で一般に公開されるので注意

$ git config --global user.name "名前"
$ git config --global user.email メールアドレス

# checkoutコマンドをcoに省略できるようにする。
$ git config --global alias.co checkout

# git pushする時にパスワード要求される時間を変える 毎回パスワード打つのめんどさを軽減
$ git config --global credential.helper "cache --timeout=86400"

# リポジトリ(保存箱)を作るコマンド
$ git init

# 一時保存箱にファイルを入れるコマンド -A は変更(新規・追加・削除)があったファイルを全て追加という意味
$ git add -A

# 一時保存箱にメモを付けて保存箱へ移すコマンド -m "メッセージ" でメモを書ける
$ git commit -m "Initialize repository"

# インターネット上にある自分のソースコード置き場をoriginという名前で登録
$ git remote add origin https://github.com/<あなたのGitHubアカウント名>/hello_app.git

# 自分のアップロード
$ git push -u origin master

# 変更があるかを確認できる
$ git status

# 過去にcommitした内容を確認
$ git log

Heroku関連

Herokuはインターネット上でアプリを公開させてくれるサービス(5個まで無料)
ソースコードのバージョン管理にGitを使っていれば、Railsアプリケーションを簡単に公開できる

開発環境と本番環境で環境が違うのでそれに合わせてインストールするライブラリを変更する必要がある。
cloud9(開発・テスト環境) = sqlite3 Heroku(本番環境) = PostgreSQL

ruby.Gemfile
source 'https://rubygems.org' 
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

#ここから上は全て共通でインストールするんだと思う('ω')ノ

#開発環境とテスト環境のみインストール
group :development, :test do 
  gem 'sqlite3', '1.4.1'
  gem 'byebug',  '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

#開発環境のみインストール
group :development do 
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

#テスト環境のみインストール
group :test do 
  gem 'capybara',           '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  gem 'webdrivers',         '4.1.2'
end

#本番環境のみインストール
group :production do 
  gem 'pg', '1.1.4' 
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
#本番用以外のgemをインストールする
$ bundle install --without production

pg gemを追加したことやRubyバージョンを指定したことをGemfile.lockに反映させないと、
本番環境へのデプロイで失敗してしまうためです。

#herokuコマンドのインストール
source <(curl -sL https://cdn.learnenough.com/heroku_install)

実行してる中身のコマンドはこれ!
curl -OL https://cli-assets.heroku.com/heroku-linux-x64.tar.gz
tar zxf heroku-linux-x64.tar.gz && rm -f heroku-linux-x64.tar.gz
sudo mv heroku /usr/local
echo 'PATH=/usr/local/heroku/bin:$PATH' >> $HOME/.profile
source $HOME/.profile > /dev/null
# herokuにログインする (--interactiveオプションを使うとブラウザをかずにログインできる)
$ heroku login --interactive

# Herokuに新しいアプリケーションを作成する (gitのソースコードを解析して適切なサーバーを用意)
$ heroku create 

# ソースコードをアップロードする これでネット上に自分の作ったサービスが公開される。 
$ git push heroku master

感想

Cloud9・Git・Herokuを使ってHello Worldだけど公開サービスができた!
アプリ開発だけで終わる本が多い中でサービスを公開まで1章で出来るって本当にすごいと思う。
今回のアウトプットでUNIXコマンドで躓いていただなぁっと調べて少し謎が解けた。
UNIXコマンド・・・(゜-゜) 事前学習読んだんだけどな
source 、(プロセス置換) <()、curl、 In こんなん書いてあったっけ・・・。 

まぁいいや、さぁ 2章も張り切って頑張るぞー( `ー´)ノ

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

項目内容の情報を保存する 〜Active_Hash〜

情報の登録フォーマットを実装する際に避けて通れないのが、選択した項目(collectionなど)をDBに保存すること。
フリマなどのアプリでは都道府県、商品の状態、配送日数などの登録が必要です。アンケートを想定するなら性別、生年月日など様々な場面でみられますね。
項目内容を予めseed.rbなどで用意してテーブルを作るということを想定する方も多くいると思いますが、これは無駄なテーブルを作っていると言っても過言ではありません。

無駄なテーブル?

DB設計などでもよく扱われる内容が「正規化」です。ざっくり言ってしまうとデータの構造を効率的でシンプルにすることです。しかしこの正規化を徹底的に行ってしまうと、小さなテーブルが増えてしまいこれも処理の負荷の要因につながります。そのため項目専用テーブルを作ることは極力避けなければなりません。

そんな際に役立つのがActive_hashです

Active_hashとは?

専用モデルを作り、その中に予め項目内容の値を用意することでDBを介さずとも項目を用意することができます。言うなれば「モデル内に項目テーブル」を作ることになります。こうすることで小さなテーブルをDB側に用意する必要がなくなるため負荷が軽くなるのです。

導入手順

今回はcollection_selectを使った商品アンケートを想定して実装していこうと思います。
保存・管理したい内容は以下の4つです。
・年代
・性別
・商品を知った経緯
・商品を使ってみての感想

DB負荷などを考えてテーブルは1つにしたいです。
設計としては
1.年代は10代〜90代
2.性別は男性・女性
3.商品を知った経緯は広告、知人、家族、インターネット
4.商品を使ってみての感想は手入力
としていきます。

この中で項目(collection_select)として使用したいのは1~3番の内容です。
ではこれを想定してアンケートを実装していきましょう。
今回の解説はactive_hashの実装のみに留めますのでそれ以外の行程は割愛しますのでご了承ください。

Step:1 Gemfileにactive_hashを追加しインストールを実行

gem 'active_hash'

ターミナル

% bundle install

Step:2 モデル&マイグレーションファイルの用意

gコマンドでモデルを生成 ※モデル名はお好みで

% rails g model model question

・マイグレーションファイルの準備

active_hashを使う場合項目内容はintegerカラムで管理します。このintegerカラムがidとして動いてくれるのです。そのためカラム名は必ず"_id"をつけることを忘れないようにしましょう。
手入力欄についてはいつものtextカラムで実装します。

00000000000000_create_questions.rb

class CreateQuestions < ActiveRecord::Migration[6.0]
  def change
    create_table :questions do |t|
      t.integer :age_id,         null: false
      t.integer :sexuality_id,   null: false
      t.integer :find_item_id,   null: false
      t.text    :comment,        null: false
      t.timestamps
    end
  end
end

その後いつものようにテーブルを作成して、マイグレーションファイルを実行します。

% rails db:create
% rails db:migrate

・項目ごとのモデルファイルを作成する
active_hashを実装するモデルを項目の3つ分を用意します。
マイグレーションファイルは必要ないのでオプションをつけてスキップさせます。

% rails g model age --skip-migration
% rails g model sexuality --skip-migration
% rails g model find_item --skip-migration

では各項目用に準備したモデルの中身を書いていきましょう

生成したモデルの内部はおそらく初期はこのような形になっていると思います

class Age < ApplicationRecord
end

これをactive_hash専用モデルにするため、以下のように編集を加えます。

class Age < ActiveHash::Base
end

これにより、このモデル内で擬似的な項目テーブルを作れるようになりました。
項目をこの中に入れる際はクラスメソッドとハッシュを使って実装していきます。
可読性をあげるポイントは1つめのキーをidに、「もう1つのキーには項目として扱いたい名前をつけること。

class クラス名 < ActiveHash::Base
  self.data = [
  { id: 0, hoge: fuga }]
end

これに沿って以下のようになります。

class Age < ActiveHash::Base
  self.data = [
      { id: 0, name: '---' },
      { id: 1, name: '10代' },
      { id: 2, name: '20代' },
      { id: 3, name: '30代' },
      { id: 4, name: '40代' },
      { id: 5, name: '50代' },
      { id: 6, name: '60代' },
      { id: 7, name: '70代' },
      { id: 8, name: '80代' },
      { id: 9, name: '90代' }
  ]
class FindItem < ActiveHash::Base
  self.data = [
    { id: 0, example: '---' },
    { id: 1, example: '広告で知った' },
    { id: 2, example: '知人の紹介で知った' },
    { id: 3, example: '家族の紹介で知った' },
    { id: 4, example: 'インターネットで知った' }
  ]
end
class Sexuality < ActiveHash::Base
  self.data = [
    { id: 0, name: '---' },
    { id: 1, name: '男性' },
    { id: 2, name: '女性' }
  ]
end

これでactive_hashを使って擬似テーブルの作成は完了。
あとは大元となる一番最初に作ったquestionモデルに連携させる記述を施します。

class Question < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :age
  belongs_to_active_hash :sexuality
  belongs_to_active_hash :find_item
end

Step:3 ビューファイルの記述

<h1>当社の商品についてアンケートにご協力ください</h1>

<div class="wrapper">
  <div class="form">
    <%= form_with model: @question, local: true do |f| %>
      <div class="form-box">
        <h3>年代をお選びください</h3>
        <%= f.collection_select(:age_id, Age.all, :id, :name) %>
      </div>
      <div class="form-box">
        <h3>性別をお選びください</h3>
        <%= f.collection_select(:sexuality_id, Sexuality.all, :id, :name)%>
      </div>
      <div class="form-box">
        <h3>当社の商品をどこでお知りになりましたか?</h3>
        <%= f.collection_select(:find_item_id, FindItem.all, :id, :example)%>
      </div>
      <div class="form-text">
        <h3>当社の商品をお使頂いてみての感想を簡単にで構わないのでご記入ください</h3>
        <%= f.text_area :comment %>
      </div>
      <%= f.submit "送信する" %>
    <% end %>
  </div>
</div>

ポイントはcollection_selectの引数です。
第一引数はparamsに渡す値(カラム名)、 第二引数には表示させたい項目(モデル名)。

Step:4 コントローラの記述

class QuestionsController < ApplicationController
  def index
    @question = Question.new
  end

  def create
    # binding.pry
    @question = Question.new(question_params)
    @question.save
    redirect_to root_path
  end

  private
  def question_params
    params.require(:question).permit(
      :age_id, 
      :sexuality_id, 
      :find_item_id, 
      :comment)
  end
end

ここについては特に言及箇所はありません。

手短に実装を行ってみました

皆さんもぜひactive_hashに挑戦してみてください

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

画像をクリックすると詳細画面に遷移する方法

環境

この記事ではmacOS Catalina10.15.6にインストールしたRuby 2.6.5を使っています。

目的

レポート一覧画面において、投稿されたレポートの画像をクリックすると詳細が見れるようにする。

結論: ネストを使用

画像をクリックした際に、詳細画面に遷移させるには、link_toメソッドにネストを用いることで解決できます。do〜endで囲む。

index.html.erb
    <%= link_to report_path(report.id) do %>
      <div class="content_post">
        <%= image_tag report.image if report.image.attached? %>
        <p>顧客名:<%= report.name %></p>
        <span class="name">
          <%= report.date %>
        </span>
      </div>
    <% end %>

まとめ

ネストは、ルーティングの設定の際にもどのレポートにコメントを行うのかの指定を出来、非常に役に立つ機能であると思います!
同じような悩みや壁にぶつかっている人の役に立てれば幸いです!

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

【0からCircleCIに挑戦】自動テストを構築する(Rails6.0・mysql8.0・Rspec)

背景

未経験から自社開発系企業の就職を目指します。良質なポートフォリオ作成のためCircleCIを勉強することにしました。

現状の知識レベルとしては、Ruby on railsを使って簡単なアプリケーション開発、gitを使ったバージョン管理、herokuを使ってデプロイできるレベルです。自分の忘備録かつ、同じくらいのレベルでこれからCircleCIに挑戦してみようと思っている方に向けて少しでも役に立てればと思います。

最終目標

CircleCIでgit pushするたび自動テストが走るようにする。また自動テストをsuccessにする

【関連記事】
【0からCircleCIに挑戦】CircleCIの基礎を学ぶ

環境

ruby 2.6.6
rails 6.0
db: mysql 8.0
test: rspec

自動テストを構築する(Rails6.0・mysql8.0・Rspec)

githubとCircleCIの連携は完了している前提で進めます。

基本的には、circleCIの公式、または【circleCI】Railsアプリでgithubと連携してrubocopとrspecテストを走らせるなどを参考にしていけば自動テストは簡単に設定できると思います。ただ自動デプロイや今後より複雑な設定をしていくためには、ファイルの記述を分解し、理解する必要があります。まず全体を見ていきましょう。

ファイル全体

.circleci/config.yml```
version: 2.1
jobs:                          #----------------------------------------------★ジョブ
  build:
    docker:                    #----------------------------------------------★Executor
      - image: circleci/ruby:2.6.6-node-browsers
        environment:
          RAILS_ENV: 'test'
          MYSQL_HOST: 127.0.0.1
          MYSQL_USERNAME: 'root'
          MYSQL_PASSWORD: ''
          MYSQL_PORT: 3306
      - image: circleci/mysql:8.0
        command: mysqld --default-authentication-plugin=mysql_native_password
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
          MYSQL_ROOT_HOST: '%'
    working_directory: ~/repo
    steps:                      #---------------------------------------------★ステップ
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-
      - run:
          name: install dependencies
          command: |
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      - save_cache:
          paths:
          - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}
      - run: mv config/database.yml.ci config/database.yml
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load
      - run:
          name: RSpec

          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/models/users_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format progress \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES
      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

細かい記述は置いておき、前回の記事で触れた「ステップ」「ジョブ」「Executor(エグゼキュータ)」について整理しましょう。

  • ステップは18行目に「steps」と書いてある箇所です。今回はステップを宣言している箇所が1箇所しかないので、ステップは1つです。「steps」以下はすべて1つのステップに属するコマンドリストです。
  • ジョブは上から2行目に「jobs」と書かれている箇所で宣言されています。こちらも1つしか記述がないので、今回はジョブ1つです。「jobs」以下すべてがジョブの内容になります。ちなみにジョブ名は「build」です
  • 「Executor(エグゼキュータ)」は4行目に書かれています。今回は「docker」と書かれているのでDocker Executorになります。「Docker」以下にある「image」の2箇所でイメージを取得し、コンテナを作成しています。

つまりまとめると、「このファイルは「build」というジョブが1つあり、そのジョブはdocker環境で実行され、実行内容はステップ(1つ)以下だよ」という意味になります。大まかな構造をみたので、次は1つ1つ細かく見ていきましょう。

① version~Executorの箇所

.circleci/config.yml
version: 2.1
jobs:                          
  build:
    docker:                   
      - image: circleci/ruby:2.6.6-node-browsers
        environment:
          RAILS_ENV: 'test'
          MYSQL_HOST: 127.0.0.1
          MYSQL_USERNAME: 'root'
          MYSQL_PASSWORD: ''
          MYSQL_PORT: 3306
      - image: circleci/mysql:8.0
        command: mysqld --default-authentication-plugin=mysql_native_password
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
          MYSQL_ROOT_HOST: '%'
    working_directory: ~/repo
  • versionはCircleCIのバージョンです。今から設定するのであれば2か2.1で良いかと思います。
  • jobsはこれからジョブの内容を書くという宣言です。次の行のbuildがジョブの名前になります。ちなみにジョブが一つしかない場合は、ジョブ名はbuildでないといけないので気をつけましょう。
  • dockerは「Executor(エグゼキュータ)」でdocker環境を構築してジョブを実行することを宣言しています。
  • 1つめのimageはrubyのイメージを取得しています。rubyの前の「circleci/」はコンビニエンスイメージといいCircleCIが用意しているイメージのことを差します。
  • enviroment以下はrubyイメージに対しての設定です。「RAILS_ENV: 'test'」はテスト環境であることを示し、「MYSQL_HOST: 127.0.0.1」は同じローカル内のMYSQLに接続することを差します。「MYSQL_USERNAME」・「MYSQL_PASSWORD」の箇所はMYSQLの設定です。こちらはテスト用のconfigファイルにも記述する必要があります。そのファイルに関しては後ほど説明します。「MYSQL_PORT: 3306」はMYSQLのポート番号です。
  • 次にもう一つイメージがあり、こちらはMYSQLのイメージを作成しています。構成は同じでenvironment以下でイメージの環境設定をしています。ちなみに「command~」はmysqlの認証プラグインを変更しています。mysql8.o以降を使う際は必須ですのでお気をつけください。「MYSQL_ALLOW_EMPTY_PASSWORD: 'true'」はpasswordなしでもMYSQLにログインすることを許可しています。「MYSQL_ROOT_HOST: '%'」はどのホストからでもアクセス可能を示します。
  • 「working_directory:」 はステップをどこで実行するかを示します。デフォルトだと「~/project」です。

では後半部分もみていきましょう。

② steps~の箇所

.circleci/config.yml
    steps:                    
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-
      - run:
          name: install dependencies
          command: |
            bundle install --jobs=4 --retry=3 --path vendor/bundle
      - save_cache:
          paths:
          - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}
      - run: mv config/database.yml.ci config/database.yml
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load
      - run:
          name: RSpec

          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/models/users_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format progress \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES
      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results
  • 「steps」でステップのコマンドリストを記述することを宣言しています。
  • 「-checkout」はリポジトリからコードを取り出しています。今回でいうとgithubの該当プロジェクトから取り出しています。ちなみにリポジトリからコードやデータを取り出すことをチェックアウトといいます。
  • 「restore_cache」はキャッシュの読み込みです。キャッシュは前回の記事で説明しましたが、簡単にいうとジョブを素早く実行するための仕組みです。「key:〜」の2行分は,「Gemfile.lock」に変更があった場合にキャッシュの内容を更新するという意味です。
  • 次の1つめの「-run」はbundle installを実行しています。nameは任意の名前です。またbundle installの後ろに「-job=4・・・」と書いてありますが、これは並列処理に関する記述で読み込みを高速化してくれます。
  • 「save_cache」はキャッシュの保存です。ここで保存された内容(※bundle installのこと)が、次回以降「restore_chache」で読み込まれます。
  • 「- run: mv config/database.yml.ci config/database.yml」でデータベースに関する設定を入れ替えています。「config/database.yml.ci」に関しては後述しますが、CircleCI用にわかりやすく別ファイルで用意したデータベース設定です。
  • 次の2つの行でCircleCI用のデータベースを作成・反映しています
  • そして「- run:name: RSpec」の箇所でようやくテストに関する記述をします。
  • 「command: | 」で複数行にシェルコマンドがあることを宣言しています。以降の行ではテストの実行、実行結果の保存をしているという記述になります。ここは少し記述が複雑ですがCircleCI公式にも書いてあるので参照してください。テストを実行し、それを指定した場所に保存していると把握しましょう。
  • 「store~」もテスト結果に関する表示ですが、こちらはテスト結果をCircleCIのアカウント上に表示させる記述です。

③database.yml.ci(ホスト上のファイル)

たびたび出てきましたが、CircleCI上のデータベースの設定ファイルも確認しましょう。

config
test: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: <%= ENV.fetch("MYSQL_USERNAME") %>
  password: <%= ENV.fetch("MYSQL_PASSWORD") %>
  host: <%= ENV.fetch("MYSQL_HOST") %>
  port: <%= ENV.fetch("MYSQL_PORT") %>
  database: ci_test

こちらはいつものdatabase.ymlファイルと変わらないので、ほとんど説明不要だと思います。ちなみに環境変数を利用しています。

以上で設定ファイルの説明は終わりです。あとは毎回git pushするたびにテストが実行されます。
下記がテスト実行画面です。すべてsuccessになっていることを確認しましょう。(※上から2番めのデータベースに関しては、falseが出てない限り問題ありません。データベースコンテナが正常どおり起動し、終了したマークになります)

スクリーンショット 2020-10-23 10.27.33.png

githubのプルリク上にも下記のように、自動テスト完了マークが表示されるかと思います。
スクリーンショット 2020-10-23 10.31.32.png

まとめ・感想

CircleCIの公式が有能だったため、基本的には公式みればほとんどの疑問が解決できました。自動テストに関しては簡単に導入できそうです。ただ見た感じ自動デプロイに関しては少し難しそうなので、AWSやDockerの知識を復習しながら挑戦してみたいと思います。

参考

【書籍】
『CircleCI実践入門──CI/CDがもたらす開発速度と品質の両立 浦井 誠人 (著), 大竹 智也 (著), 金 洋国 (著) 』

【qiita】
いまさらだけどCircleCIに入門したので分かりやすくまとめてみた
【CircleCI】Railsアプリに導入(設定ファイルについて)

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

lengthメソッドについて

lengthメソッド

文字列が持つメソッドです。
length(レングス)メソッドは文字列の文字数を数えてくれるメソッドです。
文字間の空白も数えられます。

メソッドの使用は、
以下のように値に対してメソッド名「length」をドット.で繋ぎ実行します。

【例】irb
# 文字列の文字数を出す
irb(main):001:0> "Hello World".length
=> 11

"Hello World".lengthは、1つの式として見ることもできますね。
上の例で=>の後に表示されているのは、「Hello World」の文字数「11」です。
文字自体は、「10」文字ですが、「 」空白も入れての「11」文字になります。

irbで以下のコードを実行してみましょう

では、実際にlengthメソッドを試してみましょう。

irb
# lengthメソッドで文字数を出す
irb(main):001:0> "good morning".length

# 続けてこのように表示されれば成功
=> 12

まとめ

メソッドとは、プログラミングにおける何らかの処理をまとめたもの。
lengthメソッドとは、文字列の文字数を数えるメソッドのこと。
⚠︎空白(スペース)も数えられます。

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

アンケートの「良かった」「ふつう」「悪かった」の割合(パーセンテージ)をSQLで算出する

やりたいこと

アンケートテーブル(surveys)に含まれる、「良かった」「ふつう」「悪かった」の割合(パーセンテージ)を出したい。
どんなSQLを書けば良いか?

データの例

surveysテーブルの中身(全13件)

id user_id answer
1 251 良かった
2 113 良かった
3 46 ふつう
4 414 良かった
5 456 良かった
6 18 ふつう
7 173 ふつう
8 441 良かった
9 419 悪かった
10 157 ふつう
11 116 良かった
12 204 良かった
13 445 悪かった

算出したい値

  • 良かった = (7件なので)53.8%
  • ふつう = (4件なので)30.8%
  • 悪かった = (2件なので)15.4%

対象RDBMS

  • PostgreSQL 9.6

解答例

こんなSQLで算出できる。

SELECT
  TO_CHAR(
    100.0 * SUM(CASE WHEN answer = '良かった' THEN 1 ELSE 0 END) / COUNT(*),
    '999.9%'
  ) AS "良かった",
  TO_CHAR(
    100.0 * SUM(CASE WHEN answer = 'ふつう'   THEN 1 ELSE 0 END) / COUNT(*),
    '999.9%'
  ) AS "ふつう",
  TO_CHAR(
    100.0 * SUM(CASE WHEN answer = '悪かった' THEN 1 ELSE 0 END) / COUNT(*),
    '999.9%'
  ) AS "悪かった"
FROM surveys

出力結果

良かった ふつう 悪かった
1 53.8% 30.8% 15.4%

簡単な解説

  • SUM(CASE WHEN answer = '良かった' THEN 1 ELSE 0 END) で「良かった」が何件あったのかをカウントする。(ここでは7)
  • COUNT(*)でテーブルの全件数をカウントする(ここでは13)
  • 上の2つの数値で割り算して100を掛ければ、パーセンテージが算出できる
  • ただし、整数同士の割り算は整数値に丸められてしまうので、7÷13×100=0となってしまう。それを避けるため、100ではなく100.0を最初に掛ける。これにより小数点以下の値も算出される。(100.0×7÷13=53.8461538)
  • 最後にTO_CHAR関数で数値をフォーマットする(TO_CHAR(53.8461538, '999.9%')53.8%
  • 同じ手順で「ふつう」と「悪かった」のパーセンテージを算出する

実際に実行してみる

以下のサイトにアクセスして"Run it"ボタンをクリックすると、SQLの実行結果を確認できる。

https://rextester.com/XINWIX39774
Screen Shot 2020-10-23 at 10.01.33.png

おまけ:Railsでの実装例

Ruby on Railsで実装するならこんな感じ。

# SELECT句のSQLを書く
sql = <<~SQL
100.0 * SUM(CASE WHEN answer = '良かった' THEN 1 ELSE 0 END) / COUNT(*) AS good,
100.0 * SUM(CASE WHEN answer = 'ふつう'   THEN 1 ELSE 0 END) / COUNT(*) AS fair,
100.0 * SUM(CASE WHEN answer = '悪かった' THEN 1 ELSE 0 END) / COUNT(*) AS bad
SQL

# SQLを実行して値を取得する
survey = Survey.select(sql)[0]
survey.good #=> 53.8461538
survey.fair #=> 30.7692308
survey.bad  #=> 15.3846154

補足説明

  • 列の別名を日本語にするとプログラムとの相性が悪くなるので、ここではgood/fair/badとした
  • 値のフォーマットはViewの責務なので、ここでは算出された値を純粋に返すだけにした
  • Survey.select(sql).firstとすると、ORDER BY句が自動的に追加されてSQLエラーが発生するので、あえて[0]とした

別解

本記事のコメント欄より。

AVG関数を使う

SELECT
  TO_CHAR(
    AVG(CASE WHEN answer = '良かった' THEN 100 ELSE 0 END),
    '999.9%'
  ) AS "良かった",
  TO_CHAR(
    AVG(CASE WHEN answer = 'ふつう'   THEN 100 ELSE 0 END),
    '999.9%'
  ) AS "ふつう",
  TO_CHAR(
    AVG(CASE WHEN answer = '悪かった' THEN 100 ELSE 0 END),
    '999.9%'
  ) AS "悪かった"
FROM surveys;

(実行結果は上の本文と同じ)

列方向ではなく、行方向に平均値を出す

SELECT
  answer,
  TO_CHAR(
    100.0 * COUNT(*) / (SELECT COUNT(*) FROM surveys),
    '999.9%'
  ) AS "rate"
FROM surveys
GROUP BY answer
ORDER BY rate DESC;

実行結果

answer rate
良かった 53.8%
ふつう 30.8%
悪かった 15.4%
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NameError: uninitialized constantを解決する【ActionMailer】

概要

製作中のアプリケーションにAction Mailerを導入して実装しようとしたら序盤で沼にハマったので備忘録として記録します。

参考にした記事

上記の記事の4 メールを送信してみようでエラーが発生

NameError: uninitialized constant InquiryMailer

最初はクラス名やファイル名の単純なエラーかと思ったが一向に解決しない。

結論

以下の記事にたどり着き、どうやらspringが悪さをしているらしい。
試しに止めてみたら上手くいきました。

spring stop

参考にした記事
springについて

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

Rails 画像をスライドさせる 【Swiper】

やりたい事

よくあるホームページで、画像が自動でスライドしていくのがあると思います。
あれを今回「Swiper」を使い簡単に実装しようと思います。
SwiperはダウンロードとCDNがあるので今回はCDNの方で実装を行います。

実装

こちらからコピーして読み込む。https://cdnjs.com/libraries/Swiper

https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/js/swiper.min.js
https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/css/swiper.css

CDNで読み込む例

<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/css/swiper.min.css">
</head>
<body>
〜中略〜
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/js/swiper.min.js"></script>
</body>

SwiperのHTMLの基本の記述

<div class="swiper-container"> 
   <!-- メイン表示部分 --> 
   <div class="swiper-wrapper"> 
     <!-- 各スライド -->
     <div class="swiper-slide"><img src="/assets/01.jpg" alt style="width: 100%; height: 100%;"></div>
     <div class="swiper-slide"><img src="/assets/02.jpg" alt style="width: 100%; height: 100%;"></div>
     <div class="swiper-slide"><img src="/assets/03.jpg" alt style="width: 100%; height: 100%;"></div>
   </div>
   <!-- ナビケーション -->
   <div class="swiper-button-prev"></div>
   <div class="swiper-button-next"></div>
   <div class="swiper-pagination"></div>
</div>

CSSでサイズ調整

全体を囲う<div class="swiper-container">にCSSで調整する事で、スライダーをサイズを設定する。

.swiper-container{
   width: 1000px;
   height: 400px;
}

Swiperの基本設定のJavaScript記述

<script>
    var mySwiper = new Swiper('.swiper-container');
</script>

これで一応、最低限の機能で動くスライダーの完成です。
ただし、本当に最低限の機能「イメージを左右にマウスなどでドラックすれば動く」という状態であり、左右の矢印ナビゲーションや、自動再生などは機能しない状態。

Swiperのオプションを追加

オプションを追加していきます。オプション色々ありますが今回はこれでやります。

<script>
    var mySwiper = new Swiper ('.swiper-container', {
      loop: true,         //画像のループ
      effect: 'slide',    //切り替わるときのアニメーション
      speed: 3000,        //画像の切替スピード
      autoplay: {         //自動で動かす
        delay: 3000,
        disableOnInteraction: true
      },
      pagination: {        //ページネーション
        el: '.swiper-pagination',
      },
      navigation: {        //ナビケーション
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },
    });
</script>

実装完了

最終的に下記の様になります。

<!DOCTYPE html>
<html>
  <head>
    <title>Mailers</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application' %>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/css/swiper.min.css">
  </head>
  <body>
    <div class="swiper-container">
    <!-- メイン表示部分 -->
      <div class="swiper-wrapper">
      <!-- 各スライド -->
        <div class="swiper-slide"><img src="/assets/01.jpg" alt style="width: 100%; height: 100%;"></div>
        <div class="swiper-slide"><img src="/assets/02.jpg" alt style="width: 100%; height: 100%;"></div>
        <div class="swiper-slide"><img src="/assets/03.jpg" alt style="width: 100%; height: 100%;"></div>
      </div>
      <div class="swiper-button-prev"></div>
      <div class="swiper-button-next"></div>
      <div class="swiper-pagination"></div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/js/swiper.min.js"></script>
    <script>
        var mySwiper = new Swiper ('.swiper-container', {
          loop: true,        //画像のループ
          effect: 'slide',   //切り替わるときのアニメーション
          speed: 3000,       //画像の切替スピード
          autoplay: {        //自動で動かす
            delay: 3000,
            disableOnInteraction: true
          },
          pagination: {      //ページネーション
            el: '.swiper-pagination',
          },
          navigation: {      //ナビケーション
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
          },
        });
    </script>
  </body>
</html>

おわり

お疲れ様でした。Railsであれば実装完了をコピペすれば動くはずです。
参考リンクにオプションの細かな内容が記載されていますので参考にしてみて下さい。
https://garigaricode.com/swiper/

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

Rails 画像を自動でスライドさせる 【Swiper】

やりたい事

よくあるホームページで、画像が自動でスライドしていくのがあると思います。
あれを今回「Swiper」を使い簡単に実装しようと思います。
SwiperはダウンロードとCDNがあるので今回はCDNの方で実装を行います。

実装

こちらからコピーして読み込む。https://cdnjs.com/libraries/Swiper

https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/js/swiper.min.js
https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/css/swiper.css

CDNで読み込む例

<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/css/swiper.min.css">
</head>
<body>
〜中略〜
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/js/swiper.min.js"></script>
</body>

SwiperのHTMLの基本の記述

<div class="swiper-container"> 
   <!-- メイン表示部分 --> 
   <div class="swiper-wrapper"> 
     <!-- 各スライド -->
     <div class="swiper-slide"><img src="/assets/01.jpg" alt style="width: 100%; height: 100%;"></div>
     <div class="swiper-slide"><img src="/assets/02.jpg" alt style="width: 100%; height: 100%;"></div>
     <div class="swiper-slide"><img src="/assets/03.jpg" alt style="width: 100%; height: 100%;"></div>
   </div>
   <!-- ナビケーション -->
   <div class="swiper-button-prev"></div>
   <div class="swiper-button-next"></div>
   <div class="swiper-pagination"></div>
</div>

CSSでサイズ調整

全体を囲う<div class="swiper-container">にCSSで調整する事で、スライダーをサイズを設定する。

.swiper-container{
   width: 1000px;
   height: 400px;
}

Swiperの基本設定のJavaScript記述

<script>
    var mySwiper = new Swiper('.swiper-container');
</script>

これで一応、最低限の機能で動くスライダーの完成です。
ただし、本当に最低限の機能「イメージを左右にマウスなどでドラックすれば動く」という状態であり、左右の矢印ナビゲーションや、自動再生などは機能しない状態。

Swiperのオプションを追加

オプションを追加していきます。オプション色々ありますが今回はこれでやります。

<script>
    var mySwiper = new Swiper ('.swiper-container', {
      loop: true,         //画像のループ
      effect: 'slide',    //切り替わるときのアニメーション
      speed: 3000,        //画像の切替スピード
      autoplay: {         //自動で動かす
        delay: 3000,
        disableOnInteraction: true
      },
      pagination: {        //ページネーション
        el: '.swiper-pagination',
        type: 'bullets',
        clickable: true
      },
      navigation: {        //ナビケーション
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },
    });
</script>

実装完了

最終的に下記の様になります。

<!DOCTYPE html>
<html>
  <head>
    <title>Mailers</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application' %>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/css/swiper.min.css">
  </head>
  <body>
    <div class="swiper-container">
    <!-- メイン表示部分 -->
      <div class="swiper-wrapper">
      <!-- 各スライド -->
        <div class="swiper-slide"><img src="/assets/01.jpg" alt style="width: 100%; height: 100%;"></div>
        <div class="swiper-slide"><img src="/assets/02.jpg" alt style="width: 100%; height: 100%;"></div>
        <div class="swiper-slide"><img src="/assets/03.jpg" alt style="width: 100%; height: 100%;"></div>
      </div>
      <div class="swiper-button-prev"></div>
      <div class="swiper-button-next"></div>
      <div class="swiper-pagination"></div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/5.3.7/js/swiper.min.js"></script>
    <script>
        var mySwiper = new Swiper ('.swiper-container', {
          loop: true,        //画像のループ
          effect: 'slide',   //切り替わるときのアニメーション
          speed: 3000,       //画像の切替スピード
          autoplay: {        //自動で動かす
            delay: 3000,
            disableOnInteraction: true
          },
          pagination: {      //ページネーション
            el: '.swiper-pagination',
            type: 'bullets',
            clickable: true
          },
          navigation: {      //ナビケーション
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev',
          },
        });
    </script>
  </body>
</html>

おわり

お疲れ様でした。Railsであれば実装完了をコピペすれば動くはずです。
参考リンクにオプションの細かな内容が記載されていますので参考にしてみて下さい。
https://garigaricode.com/swiper/

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

誤った情報をpushしてしまった時の対処法

この記事で分かること

  • 誤った情報をpushしてしまった
  • 誤った情報をcommitしてしまった

誤った情報をpushしてしまった

pushした情報をローカルリポジトリに戻すことはできません。したがって、pushする前に作業をしているブランチは正しいのかどうか、必ず確認するようにしましょう。
それでも間違ってpushしてしまうことがあります。ローカルリポジトリにその情報は戻せないものの、リモートリポジトリにある誤ったcommit情報は取り消すことができます

commitを取り消す方法

cbe00f28b88d5f18b9c71024034afe3d.png

commitを取り消すためには、revertと呼ばれる技術を用います
間違ってpushしたcommitを取り消すことができます。commitを削除するのではなく、「指定するcommitを取り消すためのcommit」を追加で行います。
revertはcommitされた変更と逆になる変更を追加することで、commitを取り消します

誤った情報をcommitしてしまった

83c821f4a185cf767e80dcde53e109da.png

現場からは以上です!

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

[Rails] find と find_by の違い

この記事ではmacOS Catalina10.15.6にインストールしたRuby 2.6.5を使っています。
findとfind_byについて曖昧なところがあったので、自分なりにまとめてみました。

find

  • 引数にはid(主キー)を指定します。
  • 指定したidのレコードを取得するというイメージです。
  • idが見つからない時は例外(RecordNotFound)が返って来ます。赤いエラーページが発生してしまうのですね。
  • 引数を複数指定することも可能です。
モデル名.find(idデータ)
  • 具体的には以下のように使います。
Item.find(1) # => idが1のレコードを返す
Item.find(1,3,5) # => 指定したidを配列で返す

find_by

  • データは1番最初に合致した1件のみ取得できます。
  • id以外のカラム名なども検索条件にできます。
  • 条件に合致するものがないとnilを返します。取得データがなくてもfind_byの記述以降も処理を続けたい時に便利ですね。
  • 条件は複数指定可能です。
モデル名.find_by(条件)
  • 具体的には以下のように使います。
Article.find_by(title: 'hoge') 
# => titleが'hoge'で最初に合致したデータを返す

Fruit.find_by(name: 'apple', color: 'red') 
# => nameが'apple'でcolorが'red'のデータを返す

2つ条件を指定して1つだけ合致しなかった場合

Fruit.find_by(name: 'apple', color: 'blue') #=> nilを返す
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

find と find_by の違い

この記事ではmacOS Catalina10.15.6にインストールしたRuby 2.6.5を使っています。
findとfind_byについて曖昧なところがあったので、自分なりにまとめてみました。

find

  • 引数にはid(主キー)を指定します。
  • 指定したidのレコードを取得するというイメージです。
  • idが見つからない時は例外(RecordNotFound)が返って来ます。赤いエラーページが発生してしまうのですね。
  • 引数を複数指定することも可能です。
モデル名.find(idデータ)
  • 具体的には以下のように使います。
Item.find(1) # => idが1のレコードを返す
Item.find(1,3,5) # => 指定したidを配列で返す

find_by

  • データは1番最初に合致した1件のみ取得できます。
  • id以外のカラム名なども検索条件にできます。
  • 条件に合致するものがないとnilを返します。取得データがなくてもfind_byの記述以降も処理を続けたい時に便利ですね。
  • 条件は複数指定可能です。
モデル名.find_by(条件)
  • 具体的には以下のように使います。
Article.find_by(title: 'hoge') 
# => titleが'hoge'で最初に合致したデータを返す

Fruit.find_by(name: 'apple', color: 'red') 
# => nameが'apple'でcolorが'red'のデータを返す

2つ条件を指定して1つだけ合致しなかった場合

Fruit.find_by(name: 'apple', color: 'blue') #=> nilを返す
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む