20201027のRailsに関する記事は29件です。

忘れないうちに、フリマアプリの機能とポイントまとめ

はじめに

 作成していたフリマアプリのようなものが一段落したので、まとめておく。完全に自分用。

実装した機能

ユーザー管理

deviseというGemを使用。
RSpecを用いてテストコードを記述。Fakerでテスト用データをFactoryBotに記述。
GimeiというGem、日本語版のFakerを使用。
正規表現。
エラーエラーハンドリングをrenderメソッドを使って表示。

商品の出品機能

ActiveHash導入。
ActiveStorageを導入して、画像を保存できるように。
Fakerの日本語対応を使って、テストコード記述。
JSを使ってブラウザ上でリアルタイムに利益等を計算。

商品の一覧表示

if文の条件文にexists?メソッドで、テーブルにデータが無いときと処理を分岐。

商品の詳細表示機能

ActiveHashのnameを表示(@変数名.model名.name名)

商品の削除機能

before_actionで同じ処理をまとめる。

商品の編集機能

newアクションと同じフォームにまとめる。

商品購入機能

商品と購入者の組み合わせをパラメーターから取ってくるため、mergeメソッドを使う。
APIの導入(PAY.JP)を使ってクレジットカード決済の導入。JSでtokenをレスポンス。
tokenをmerge。
購入済みにsold outを表示。
<% if @item.purchase %>を使って条件分岐。

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

【Rails】音声投稿機能 ~Cloudinary, CarrierWave

はじめに

この記事では音声ファイルの投稿機能を実装します。

Cloudinary(外部ストレージ)を使います。
フリープランで月1GBまで利用できます。

前提

Tweetモデルにて、テキストでの投稿機能は一通りできている

まず、、、

Cloudinaryにアカウントを登録してください!
メルアドにて本登録も忘れずに!

https://cloudinary.com/

上記のアカウント取得後、以下の作業に入ってください!

audioカラムの追加

tweetsテーブルにstring型のaudioカラムを追加します。

ターミナル
$ rails generate migration AddAudioToTweets audio:string
ターミナル
$ rails db:migrate

このaudioカラムは音声ファイルのURL(住所)が書かれます。音声ファイル自体は別の場所に保存されます。

Index.html.erb

audioファイルを再生するコードを書きます

app/views/tweets/index.html.erb
    <% @tweets.each do |t| %>

        <%= audio_tag t.audio_url, :controls => true if t.audio? %>

    <% end %>

:controls => trueはコントローラパネルを表示します。
また、audio_tagには以下のオプションがあります。

:autoplay 自動再生
:loop ループ再生

ストロングパラメーター

audioパラメータを受け取れるようにストロングパラメーターに記述します。

tweets_controller.rb
# 割愛

private
def tweet_params
  params.require(:tweet).permit(:body, :audio)
end

new.html.erb

fileをuploadするコードを書きます。

app/views/tweets/new.html.erb
<%= form_for @tweet do |f| %>

  <div class="field">
    <%= f.label :audio %>
    <%= f.file_field :audio %>
  </div>

  <%= f.submit "Tweetする" %>
<% end %>

gemの追加

今回はcloudinaryと、carrierwaveというgemを使っていきます。Gemfileの一番下に以下を追加しましょう。

Gemfile
gem 'carrierwave'
gem 'cloudinary'
ターミナル
$ bundle install

carrierwave
cloudinary

それぞれのgemを簡単に説明すると
carrierwave : railsにてファイルのuploadができるようにするためのgem
cloudinary : 外部ストレージサービスのcloudinaryが利用できるようにするためのgem

アップローダーの作成

CarrierWaveのジェネレーターでアップローダーを作成します。以下のコマンドをターミナルに打ち込みましょう。

ターミナル
$ rails g uploader Audio

モデルの修正

app/models/tweet.rbを以下のように修正します。

app/models/tweet.rb
class Tweet < ApplicationRecord

# 追記ここから
  mount_uploader :audio, AudioUploader
# 追記ここまで

end

mount_uploader :audio, AudioUploaderは音声ファイルを指定の場所に保存することを表します。

次に保存場所を指定するアップローダの設定です。

app/uploaders/image_uploader.rbの6~8行目を変更しましょう。

app/uploaders/audio_uploader.rb
  # Choose what kind of storage to use for this uploader:
  storage :file
  # storage :fog

上記を下図のように変えます。

app/uploader/audio_uploader.rb
  # Choose what kind of storage to use for this uploader:
  if Rails.env.production?
    include Cloudinary::CarrierWave
    CarrierWave.configure do |config|
      config.cache_storage = :file
    end
  else
    storage :file
  end
  # storage :fog

cloudinaryは外部のストレージサービスです。本番環境(リリース後=production)ではcloudinaryに画像が保存され、それ以外(開発環境=ローカル)では自分のpcに保存されます。(publicフォルダー内に保存されます)

APIキーの非公開

Cloudinaryの各アカウントには「Cloud name」、「API Key」、「API Secret」が付与されています。

Cloudinaryマイページ

Cloudinary_Management_Console_-_Dashboard_png.png

Cloudinaryのアカウントを外部に漏らさないためにセキュリティを強化します。

ここではdotenv-railsというgemを使っていきます。Gemfileの一番下に以下を追加しましょう。

Gemfile
gem 'dotenv-rails'
ターミナル
$ bundle install

と打ち込んでインストールします。

次に.envというファイルをアプリケーションディレクトリ(appやdbやGemfileがあるディレクトリ)に自分で作成します。
以下の図のようになっていればオッケーです。
※vendorフォルダーの中ではないので注意してください。

スクリーンショット_2018-10-17_2_03_28.png

次に作成した.envファイルに以下を入力します。

.env
CLOUD_NAME=q0w9e8r7t6yu5  #←この値は人によって違います!!
CLOUDINARY_API_KEY=123456789012345 #←この値は人によって違います!!
CLOUDINARY_API_SECRET=1a2s3d4f5g6h7j8k9l0a1s2d4f5g6h1q #←この値は人によって違います!!

ここで「=」の後のそれぞれの値は先ほどのCloudinaryのマイページで取得したキーに書き換えてください(自分が取得したキーは絶対他言しないように!!また数字は個々人によって変わります)。
また、書き換える際は、コメントアウト部分を削除してください!

最後に隠しておきたいデータを定義した.envファイルを公開しないようにします。

.gitignoreに下記を追加します。
※もし.gitignoreファイルがない場合はアプリケーションディレクトリーにて作りましょう!

.gitignore
# 省略

/.env

これでOKです!

APIキーの利用

最後の手順です!
まずはconfigフォルダにcloudinary.ymlファイルを作成してください。

スクリーンショット_2019_10_20_1_11.png

config/cloudinary.ymlに以下のようにそのままコピペしてください。

config/cloudinary.yml
development:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
production:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false
test:
  cloud_name: <%= ENV['CLOUD_NAME'] %>
  api_key: <%= ENV['CLOUDINARY_API_KEY'] %>
  api_secret: <%= ENV['CLOUDINARY_API_SECRET'] %>
  enhance_image_tag: true
  static_file_support: false

これでAPIキーを非公開にしたまま利用することができました。

これにて、音声ファイルの投稿はできるようになりました。お疲れ様です(^^)/

公式ドキュメント

carrierwave
cloudinary
dotenv-rails

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

Ruby returnメソッドの使い方について

returnとは??

メソッドの戻り値を返す方法。Rubyにおいては、returnをわざわざ使用せずとも、定義したメソッドにおいて、最後に処理された値が返ってくる。

しかし、途中で処理を抜け出したい場合は、returnを使用することで、強制的に値を返すことができる。

例題

■returnを使用しない場合

def total
  price = 1000
  num = 10

  "#{price}円の服を#{num}枚購入したので、合計は#{price*num}円になります。"
  "1枚返品したので、合計は#{price*(num-1)}円になりました。"
end
p total

結果

1枚返品したので、合計は9000円になりました。

returnを使用しない場合、最後に定義した文言が出力されます。

■returnを使用する場合

def total
  price = 1000
  num = 10

  return "#{price}円の服を#{num}枚購入したので、合計は#{price*num}円になります。"
  "1枚返品したので、合計は#{price*(num-1)}円になりました。"
end
p total

結果

1000円の服を10枚購入したので、合計は10000円になります。

returnを使用すると、return後に定義した文言が出力されました。

このように、もし途中で処理を抜けたい場合はreturnを活用すると良いでしょう。
ちなみにまだ私はreturnを使用する場面には遭遇していません、、、笑

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

【rails】first_or_initialize

first_or_initializeについて学習したため、アウトプットいたします。

first_or_initializeとは?
first_or_initializeの使い方は?

の順に記述します。

first_or_initializeとは?

whereで検索した条件のレコードが存在すればそのレコードのインスタンスを返し、存在しなければ新しくインスタンスを作るメソッドです。

と言ってもなかなかわかりませんよね。
詳しく説明します。

例えば、
studentというテーブルがあったとして、そこのデータベース上に10人の名前がそれぞれ登録されていたとします。

first_or_initializeメソッドは、
そのデータベース上にそのstudentがいなかった場合→そのstudentを新規登録します。
一方、
studentがすでに登録されていた場合→その生徒の情報を取ってきます。

少しづつわかってきましたか?

すなわちこのメソッドの特徴は
「データの重複を避けること」なんです。

ではどうやって使うのでしょうか?

first_or_initializeの使い方

student = Student.where(name: "佐藤").first_or_initialize

存在しなければ新しくインスタンスを作りたいオブジェクトを定義し、
その後に
where(条件).first_or_initialize
とすると完成です。

上記の例では、
そのデータベース上に佐藤さんがいなかった場合→その佐藤を新規登録します。
一方、
佐藤さんすでに登録されていた場合→佐藤さんの情報(id, age等定義したもの)を取ってきます。

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

[Rspec] Rspecを導入してモデルの単体テストコードを書くまでの流れ

今回はテストコードを書く時にRspecを導入する手順を忘備録代わりにメモします。

Gem導入

テストで使うgemを3種類導入します。
group :development, :test do ~ endの処理内に記述します。

Gemfile
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]
  gem 'rspec-rails
  gem 'factory_bot_rails'
  gem 'faker'
end

記述したらbundle installして、ローカルサーバーを再起動します。

Rspecに必要なファイルをインストールする

ターミナル
% rails g rspec:install

テスト結果のログを可視化する

.rspec
--require spec_helper
--format documentation # => 追記

エラーメッセージを英語に設定(任意)

テストのエラーメッセージを英語で表示させたい場合は設定します。
RSpec.configure do |config|の上に追記。

spec/rails_helper.rb
# 中略
I18n.locale = "en"

RSpec.configure do |config|
# 中略

FactoryBotのファイル作成

  • specディレクトリの中に、factoriesディレクトリを作成します。
  • そして、その中にテスト用のFactoryBotを生成するファイルを作成する。
  • 名前はモデル名s.rb。今回はuserモデルのテストコードを書きたいのでusers.rbとします。

ユーザー情報をFactoryBotで生成

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name {Faker::Name}
    email {Faker::Internet.free_email}
    password = Faker::Internet.password(min_length: 6)
    password {password}
    password_confirmation {password}
  end
end

FactoryBotが生成できていることを確認

ターミナル(コンソール)
pry(main)> FactoryBot.create(:user)

エラーが発生した時は

  • 私は上記のコマンドでエラーが発生しました。その時は、Springが裏で動いていることが原因みたいなのでspring stopコマンドでSpringを停止させます。
  • Spring stopped.と出れば正常にSpringが停止できています。
  • そして再度コンソールでFactoryBot.create(:user)とコマンドを打って生成を確かめましょう。
ターミナル`
# exitでコンソール終了

% spring stop

テストコードを記述するファイルを準備

  • 下記のコマンドでspec/models/user_spec.rbというファイルが生成されます。 :ターミナル % rails g rspec:model user

テストコード記述

こんな感じでどんどんテストコードを書いていきます。

spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"# => 元々の記述は削除

describe 'ユーザー新規登録' do 
    before do
      @user = FactoryBot.build(:user)
    end

   it "name, email, password, password_confirmationが全て入力されれば保存されること" do
      expect(@user).to be_valid
   end

  end
end

テストコードを実行する時は

以下のコードを実行します。

ターミナル
% bundle exec rspec spec/models/user_spec.rb

Fakerの書き方について参考サイト

https://rubydoc.info/gems/faker/1.3.0/frames
http://railscasts.com/episodes/126-populating-a-database
https://github.com/takeyuweb/trygems/blob/master/try-faker/faker.md

Faker::Japanese
https://github.com/tily/ruby-faker-japanese

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

Rubyの条件分岐(case,while,無限ループ,break)

Rubyの条件分岐

case文

条件分岐を表現するための文法。複数の条件を指定する時に、if文のelsifを重ねるよりもシンプルにコードを書くことができる

sample
case 対象のオブジェクトや式
when 値1
 # 値1に一致する場合に実行する処理
when 値2
 # 値2に一致する場合に実行する処理
when 値3
 # 値3に一致する場合に実行する処理
else
 # どれにも一致しない場合に実行する処理
end

while文

繰り返し処理を行うためのRubyの構文。指定した条件が真である間、処理を繰り返す

sample
number = 0

while number <= 10
 puts number
 number += 1
end

# ターミナル出力結果
# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10

無限ループ

処理が永遠に繰り返されること

sample
number = 0

while true
 puts number
 number += 1
end

# ターミナル出力結果
# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10
# .
# .
# .

上記のコードは条件式の部分にはじめからtrueと書くことによって意図的に無限ループを発生させている

break

eachメソッドやwhile文などのループから脱出するために使われる

sample
number = 0

while number <= 10
 if number == 5
   break
 end
 puts number
 number += 1
end

# ターミナル出力結果
# 0
# 1
# 2
# 3
# 4

このようにif文などの条件分岐とbreakを使うと、特定の条件のときにループを脱出することができる

現場からは以上です!

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

ActiveHashはアソシエーションをしっかり設定しよう

※結果を知りたい方は下の方までスクロールしてください。

エラー発生

商品を出品する機能を実装中のこと…

d812404dd725355d1e491026dce6f911.png
何これ…初めて見た…

原因を探る

コントローラーの記述は間違ってないよね…

item_controller.rb 一部割愛
class ItemsController < ApplicationController

  def create
    @item = Item.new(item_params)
    if @item.valid?
      @item.save
      redirect_to root_path
    else
      render :new
    end
  end

  private
  def item_params
    params.require(:item).permit(:name, :info, :price, :category_id, :status_id, :delivery_charge_id, :shipment_source_id, :date_of_shipment_id).merge(user_id: current_user.id)
  end

end

うん…大丈夫

ええー
じゃあモデルかなぁ

item.rb
class Item < ApplicationRecord

  validates :name, :info, :price, presence: true
  validates :category_id, :status_id, :delivery_charge_id, :shipment_source_id, :date_of_shipment_id, numericality: { other_than: 1 }

  belongs_to :user
  has_many   :comments
  has_one    :buyer

  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :category, :status, :delivery_charge, :shipment_source, :date_of_shipment
end

いや、記述の抜け漏れもないし、半角全角もスペルも大丈夫
バリデーションも大丈夫(←これが落とし穴)

ビューファイル(割愛)は…うーん大丈夫そう

そして、インターネットの海を彷徨うこと30分…
本当にわからん

しゃあないか

助け舟を呼ぶことに

大海に投げ出されたような気分だったのでもうこれは人に聞くしかあるまい
某短期講習中なので頼もしい方々に聞くことはできる

早速原因究明に向けて色々と思案してくださった
ありがたや〜?

これを、あれを試し、色々触っていった結果どうやらモデルの記述に問題があった模様

class Item < ApplicationRecord

  validates :name, :info, presence: true
  validates :price, presence: true
  validates :category_id, :status_id, :delivery_charge_id, :shipment_source_id, :date_of_shipment_id, numericality: { other_than: 1 }

  belongs_to :user
  has_many   :comments
  has_one    :buyer

  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :category
  belongs_to_active_hash :status
  belongs_to_active_hash :delivery_charge
  belongs_to_active_hash :shipment_source
  belongs_to_active_hash :date_of_shipment

end



↓つまりはこの部分↓

  belongs_to_active_hash :category
  belongs_to_active_hash :status
  belongs_to_active_hash :delivery_charge
  belongs_to_active_hash :shipment_source
  belongs_to_active_hash :date_of_shipment

ここを別々に書くだけ!
バリデーションなんやからまとめて書かせてくれや…と思ったが、

この話をしていると聞いていたうちの一人が






「それバリデーションじゃなくてアソシエーションですよ」






…まじかぁ…
まじかぁぁぁぁ!!!

恥ずかしいぃ!///
そういやそうだわ!思いっっきりBelongs_toって書いてあるし!
なんなら俺がActiveHashを導入してた時にrails gで生成してたものは?

モデルだよ!
モデルを生成してたんだよ!
それを関連付けるんだからアソシエーション以外に何があんの!

知らなかったとはいえ思いつく材料は目の前にあった訳だ
反省するとともに判断材料が増えたので一歩前進したと思う


本当に今回は勉強になった
真摯に対応してくれた方ありがとうございました

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

【Rails】form_withの注意点

順番が逆だと動作しない。

routes.rb
resources :prototypes do
  resources :comments, only:[:create]
end
prototype_comments POST   /prototypes/:prototype_id/comments(.:format)                                             comments#create

以上の条件では、form_withは以下の通りとなる。

<%= form_with model: [@prototype, @comment], local: true do |f| %>

[@親ルート, @子ルート]になっており、[@prototype, @comment]を逆に書いてしまうとルートが存在しなくなってしまい、エラーとなる。

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

Spree::Taxonomyのカラムを解説

solidus使おうとすると、初心者はイメージ掴みにくいですよね。
この記事を読まれていると言うことは少し全体像が掴めて、「Spree::Taxon」にはどう言ったカラムが存在するんだ?」と言うところなのかなと思います。:point_up:
そこで今回は「Spree::Taxon」内のカラムについて簡潔に紹介と説明をしていきます。

Solidusのバージョン

solidus 2.0.9

カラムの紹介と説明

Spree::Taxonomyのid=1をのぞいてみましょう。
スクリーンショット 2020-10-27 7.07.19.png
それではここにあるカラムについて説明します。

id

id。

name

分類(taxonomy)名。

create_at

分類(taxonomy)が作られた時間。

update_at

分類(taxonomy)が更新された時間。

position

Taxonomyリスト中におけるPositionが設定される。
例えば、2つのTaxonomyがある場合、1または2となる。

<関連情報>
Spree::Productのカラムを解説
https://qiita.com/roooll12/items/92b370741a53c8dd268c

Spree::Taxonのカラムを解説
https://qiita.com/roooll12/items/6f1dda6c5f4ea16e0c4c

駆け出しsolidus開発者にとってSpree::Taxonモデルを理解する糧になれば幸いです!

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

Rails モデルとテーブルの命名規則

データベースのクラス名

データベースのテーブル名はモデル名のクラス名を複数系にしたものでありスネークケースで命名する
スネークケースとは :すべてを小文字にし、単語の区切りを_で繋ぐ書き方

モデルのクラス名

モデルのクラス名は単数系でキャメルケースで命名する

キャメルケースとは:各単語の頭文字を大文字にする、_は使用しない書き方 モデルのクラスはキャメルケースで単数系

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

Uncaught ReferenceError: Vue is not definedが起こった話

はじめに

Vue.jsをCDN読み込みで簡単なミニアプリを使う際に起こったエラーです。
備忘録のため残しておきます。初学者のため間違えがあればご指摘下さい!

Uncaught ReferenceError: Vue is not defined

vue.jsの動作確認をするため以下のコードを記述したところvue.jsが読み込まれませんでした。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <link rel="stylesheet" href="vue.css">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <p>現在{{ number }}回クリックされています</p>
    <button>カウントアップ</button>
  </div>
  <script src="vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>

//vue.js

new Vue({
  el: '#app',
  data: {
    number: 0
  }
})

解決策は簡単。
下から3行目のVueを使うためのCDNをjsが読み込まれるより上に記述するだけです。
headタグの中に入れるかbodyタグの一番上に記述しましょう。

<!-- 修正後 -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <link rel="stylesheet" href="vue.css">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <p>現在{{ number }}回クリックされています</p>
    <button>カウントアップ</button>
  </div>
  <script src="vue.js"></script>
</body>
</html>

最後に

Vue.jsの超初心者の方の助けになれば幸いです。

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

"Rails6"で "assets" "turbolinks" を削除する。

この記事の目的

この記事はRails6でWebpackerを使用し
assets pipelineturbolinks を削除するまでの手順内容を記録し記事にしたものです。

  • 開発内容の確認と復習
  • 開発手順の忘備録
  • 少しでも他の方の参考になれば

と思い、本記事を作成行いました。

なせassetsを削除しようと思ったのか?

Rails6 以降は標準のモジュールバンドラとして Webpacker が使われるようになりました。
依然としてrails new 時での assetsは健在ですが、
assetsに初期インストール時にはjavascriptのディレクトリはRails6では最初に作成されなくなっています。
また、sprocketsのgem更新は滞っていますし、新しいJavaScriptのES2015
React,Vueなどのフロントエンドフレームワーク等が主流になる中で
assetsとwebpackerの2重体制から脱却して
webpackerのみで開発を経験しておきたいと思い実行しました。

開発環境

今回は私が以前書いた記事のDocker-composeで開発を行っております。

環境構築の詳細はQiitaの記事

"Docker-compose" で "Rails6" の安定的な開発環境構築マニュアル

をご確認ください。

1.assetsの削除

Webpackerが標準となったRails6ではassetsを使用せずにapp/javascript配下に集約し
assetsを削除したいと思います。

app/assetsディレクトリを削除する

2. application.jsにて読み込みを記載する

Ajaxなどで使うjqueryとフロントエンドのフレームワークとしてbootstrapを読み込んで、
さらに、application.scssも読み込むことで、webpackerでjsとcssを一元管理します。

app/javascript/packs/application.js
require("jquery")
require("bootstrap")

import "../stylesheets/application.scss"

3.javascriptディレクトリ配下にstylesheetsのディレクトリとapplication.scssのファイル作成

application.scssにbootstrapの読み込みを記載しておきます。

app/javascript/stylesheets/application.scss
@import "~bootstrap/scss/bootstrap";

4.layouts.scssの変更

assetsを使わなくなったので、layoutsをstylesheet_pack_tagに変更を行います。
またターボリンクは使わないので、下記のコードに変更となります。

app/views/layouts/application.html.erb
<%= stylesheet_pack_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>

5.turbolinks削除

Rails6ではyarnとgemの両方で削除しないと完全に削除できない模様。

yarnで削除

yarn remove turbolinks

Gemfileで削除

gem 'turbolinks', '~> 5'
bundle install

さらに app/javascript/packs/application.jsから"turbolinks"を削除します。

6. config/webpack/environment.jsの編集

AjaxでjQueryで$を使うために下記の記述を行います。

config/webpack/environment.js
const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.prepend('Provide',
    new webpack.ProvidePlugin({
        $: 'jquery/src/jquery',
        jQuery: 'jquery/src/jquery'
    })
)

module.exports = environment

7.webpacker.ymlの編集

dockerを使っているので、localhostだとエラーが発生します。
下記の3カ所を修正し対応します。

config/webpacker.yml
check_yarn_integrity: false
host: 0.0.0.0
public: 0.0.0.0:3035

8.docker-compose.yml にポート追加

8.で行った修正の為にポート追加を行います。

- "3035:3035"

9.sprocketsのバージョンを下げる

Gemfileのsprocketsのバージョンを下げないと完全にassetsを削除できない模様。
https://github.com/rails/sprockets/issues/643

gemfileを変更する

gem 'sprockets', '~> 3.7.2'
bundle update sprockets

10.foremanの導入

foreman は、Procfile を読み込み、複数のプロセスを管理できるツール。
アプリケーションで管理するプロセスを Procfile に記述し、
アプリケーションルートに配置する事で対応します。
参考URL:foreman

gem 'foreman'
Procfile.dev
web: bundle exec rails s -b 0.0.0.0 -p 3000
webpacker: bin/webpack-dev-server

bundle install しておきます。

bundle install

bashファイルもforemanに合わせて修正しました。

qs.bash
rails_server() {
  compose_stop $app
  rm_pids
  $dc run $rm --service-ports $app bundle exec foreman start -f Procfile.dev
}

上記でassets turbolinks の削除は完了したはずです。

最後に

できればAjaxもJQueryから脱却したかったのですが、
まだ学習不足で置き換えはまだできておりません。
学習完了すれば、最終的にES2015に移行できたと言えると思います。

初心者ゆえに作業内容や間違いがあるかもしれません。
お気づきの点があればお知らせ頂けましたら幸いです。

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

楽天商品検索APIを用いた実装をしてみる(簡単)

はじめに

Rails 6.0.3.4

今回は楽天商品検索APIを使った超シンプルな検索アプリ実装の手順を記録したいと思います。
おそらくわかりやすいかは置いておいて、1番シンプルなやり方ではないかな・・・?
と勝手に思っています。今回、エラーハンドリングは省きます。

https://webservice.rakuten.co.jp/
上記から登録をして、アプリケーションIDを取得しておきましょう。

itemsコントローラー作成

rails g controller items search

このコマンドをターミナルで入力すると、コントローラーやビュー(search.html.erb)、
更にルーティングの記述もしてくれます(ルーティングは、お好みで修正してください。)

ビューを記述する

<h2 class="product-subtitle-explain">商品を検索してみよう!</h2>

<%= form_with url:product_search_path, method: :get, local: true do |f| %>
<%= f.text_field :keyword, placeholder: "検索キーワードを入力してください", autocomplete: 'off' %>
<%= f.submit "検索" , :name => nil%>
<% end %>

<h2>検索結果</h2>
<%= render partial: 'item_list' %>

大体このような感じでしょうか。
特に保存するものもないので、form_withにはどのアクションに飛ばすか、urlを書いておきます。
:keywordとして、検索ワードを:keywordに格納します。←大事!
placeholder: "検索キーワードを入力してください"として、フォームに例として文字を置いておきました。また、
autocomplete: 'off'で、フォームの自動補完を無効にしてあります。

ここで最後に注目していただきたいのですが、
<%= render partial: 'product_list' %>
として、検索結果を表示する箇所は別のビューファイルに飛ばしています。
なのでここで、app/views/itemに、
_item.list.erbを作成します。さっき作ったsearch.html.erbと同じ場所ですね。

次はrender先の_item.list.erbに、
結果を表示させる記述をしていきます。

<% @items.first(10).each do |item| %>

  <img src="<%= item['smallImageUrls'][0] %>">

  <a href="<%= item['itemUrl']%>" target="_blank"><%= item['itemName']%></a>

  <%= item['itemPrice'] %>円

<% end %>

上から見ていくと、まず
<% @items.first(10).each do |item| %>★<%end%>
として、★の中に、商品の何を写したいのかを記述しました。
.first(10)とすることで、検索に引っかかった最初の10アイテムを表示しています。
@itemsはこのあとsearchコントローラーで定義していきます!(今ビューを見てもエラーになります)

item['smallImageUrls'][0]
アイテムの1つ目の画像を小さなサイズで取得しています。
item['itemUrl']
アイテムの詳細に行けるURLを取得しています。
target="_blank"
アイテムをクリックしたときに、別タブを開いて楽天の商品詳細ページに行く設定にしてます
item['itemName']
アイテムの名前を取得しています。
['itemPrice']
アイテムの値段を取得しています。
他にもアイテムに関する様々な情報を取得することができます。↓
https://webservice.rakuten.co.jp/api/ichibaitemsearch/

コントローラー記述

https://webservice.rakuten.co.jp/sdk/ruby.html
を基に実装していきます。

gem 'rakuten_web_service'

をgemファイルに付け加えて、bundle installして
ターミナルを再起動させてください。
ここからitemsコントローラーの記述に入ります。

class ProductController < ApplicationController
  require 'rakuten_web_service'

  def search
    if params[:keyword].present?
      @items = RakutenWebService::Ichiba::Item.search(keyword: params[:keyword])
    else
      render :search
    end
  end
end

params[:keyword] は、search.html.erbの入力フォームでキーワードを格納してあります。
paramsで[:keyword]をチェックし、@itemsに格納しています。
RakutenWebService::Ichiba::Item.searchは決まり文句です。詳しくは
https://webservice.rakuten.co.jp/sdk/ruby.html
を見てみてください。

アプリケーションIDの設定

config/initialize下に、rukuten.rbを作成してください。
その中にアプリケーションIDを記述していきます
タイミングによっては記述方法が異なるので、公式も見るようにしてください。

RakutenWebService.configure do |c|
  c.application_id = '*******************'
  c.affiliate_id = '*******************' ←こちらは任意
end

環境変数を使うときには以下になります

RakutenWebService.configure do |c|
  c.application_id = ENV['RWS_APPLICATION_ID']
  c.affiliate_id = ENV["RWS_AFFILIATION_ID"]
end

うまくいった後は、本番環境でも環境変数を設定してください。
HTML・CSSで整えた後ですが、おそらくこのような感じになるのではないかなと思います
products.gif

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

【Rails / RSpec】 Modelテストを書く(Shoulda Matchers・FactoryBot 使用)

はじめに

テストを全く記述したことがない状態から RSpec を実際に記述しながら学びました。理解した内容を実際に記述したコードとともに記事に残したいと思います。

※RSpec 初心者です。誤り、ご指摘などございましたらコメントいただければ非常にありがたいです。

前提

  • Usersテーブル
    • カラム: email: stringpassword: stringのみ
    • Booksテーブルと1対多の関連付けがされている
  • テストデータの生成には Gem FactoryBotを使用
  • テストには簡単に記述できる Gem Shoulda Matchersを使用

Shoulda Matchersを使うと何行も記述しないとできなかったテストが1行で書けてしまいます。何が行われているかわからない部分もありますが、今回記述した内容の他にも便利な構文が用意されておりますので、興味のある方は参考サイトからぜひ確認してみることをお勧めします。

参考サイト

非常にわかりやすく大変参考にさせていただきました!
ありがとうございました!

Model

app/models/user.rb
class User < ApplicationRecord
  has_many :books # booksと1対多の関連付け
  has_secure_password
  before_save { email.downcase! }

  validates :email, presence: true,
                    length: { maximum: 255 },
                    format: { with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i },
                    uniqueness: { case_sensitive: false }
  validates :password, presence: true,
                       length: { minimum: 6 }
end

テスト

合計9つのテストを行っている

ファイル全体

spec/models/user_spec.rb
# rails_helperファイルを読み込み
require 'rails_helper'

# Userモデルのテストなので User, type: :model としている
RSpec.describe User, type: :model do

  describe 'has_many' do
    # has_many :books のテスト
    it { should have_many(:books) }
  end

  describe 'has_secure_password' do
    # has_secure_password のテスト
    it { should have_secure_password }
  end

  describe 'validation' do
    context 'email' do
      # FactoryBot でテストデータを作成
      let(:user) { create(:user) }
      let(:upcase_email_user) { build(:user, :upcase_email) }

      # presence: true のテスト
      it { should validate_presence_of(:email) }
      # length: { maximum: 255 } のテスト
      it { should validate_length_of(:email).is_at_most(255) }

      # uniqueness: { case_sensitive: false } のテスト
      it 'emailの重複保存を許容しないこと' do
        duplicate_user = user.dup
        duplicate_user.email = user.email.upcase
        expect(duplicate_user).to be_invalid
      end

      # format: { with: XXXXX } のテスト
      it '指定formatに合わないemailを許容しないこと' do
        invalid_emails = %w[user@foo,com user_at_foo.org example.user@foo.foo@bar_baz.com foo@bar+baz.com foo@bar..com]
        invalid_emails.each do |invalid_email|
          user.email = invalid_email
          expect(user).to be_invalid
        end
      end

      # before_save { email.downcase! } のテスト
      it 'downcase!が正常に動作していること' do
        upcase_email_user.save!
        expect(upcase_email_user.email).to eq 'test@test.com'
      end
    end

    context 'password' do
      # presence: true のテスト
      it { should validate_presence_of(:password) }
      # length: { minimum: 6 } のテスト
      it { should validate_length_of(:password).is_at_least(6) }
    end
  end
end

各記述の解説

テストの宣言

RSpec.describe モデル名, type: :テスト形式 doとなっており、この記述でブロック内でのUserモデルのテストが実行できる

RSpec.describe User, type: :model do

has_many と has_secure_password のテスト

Shoulda Matchersを利用してモデルの関連付けと、 has_secure_password の適用を検証

# has_many :books のテスト
describe 'has_many' do
  it { should have_many(:books) }
end

# has_secure_password のテスト
describe 'has_secure_password' do
  it { should have_secure_password }
end

describeとcontext でテスト記述を分ける

describe 'validation' do
    context 'email' do
      # emailのバリデーションテストを記述
    end
    context 'password' do
      # passwordのバリデーションテストを記述
    end
end

FactoryBotでテストデータを作成

ファクトリーボットで作成するテストデータを専用ファイルに定義

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    # データを生成する毎に通し番号をふってユニークな値を作るようにしている
    sequence(:email) { |n| "test#{n}@test.com" }
    password { 'password' }

    trait :upcase_email do
      email { 'TeSt@TEst.Com' }
    end
  end
end

上記ファイルを元にletでテストデータを作成しており、このテストデータは記述したブロック内のit .. do ~~ endブロックで使用できる。
コメントアウトでbinding.pryuserupcase_email_userの中身を確認した結果を記載している。

  let(:user) { create(:user) }
  # user = <User:0x0000560469431138 id: 2610, email: "test1@test.com", password_digest: "[FILTERED]", created_at: Tue, 27 Oct 2020 14:03:04 JST +09:00, updated_at: Tue, 27 Oct 2020 14:03:04 JST +09:00>
  let(:upcase_email_user) { build(:user, :upcase_email) }
  # upcase_email_user = <User:0x00005604690c12c8 id: nil, email: "TeSt@TEst.Com", password_digest: "[FILTERED]", created_at: nil, updated_at: nil>

※上記createbuildFactoryBotの記述を省略できる方法を適用。

email バリデーション

presence:true length: { maximum: 255 } のテスト(Shoulda Matchers)

Shoulda Matchersを用いてテストを記述

  # presence: true をテスト
  it { should validate_presence_of(:email) }
  # length: { maximum: 255 } をテスト
  it { should validate_length_of(:email).is_at_most(255) }

uniqueness: { case_sensitive: false }のテスト

dupメソッドuserのコピーデータを作成。
let(:user) { create(:user) }ですでに登録済みのuserと同じemailを持つduplicate_userが保存されないことを検証。
テストの前にemailをupcaseで大文字にすることで、大文字でも同一なemailと判定するかも合わせて検証している。

be_invalidはバリデーションで引っかかれば(エラーが出れば)パスする

it 'emailの重複保存を許容しないこと' do
  duplicate_user = user.dup
  # この時点での中身
  # duplicate_user = <User:0x000055f1e1d71e90 id: nil, email: "test1@test.com", password_digest: "[FILTERED]", created_at: nil, updated_at: nil>
  duplicate_user.email = user.email.upcase
  # user.email.upcase後の中身
  # #<User:0x000055f1e1d71e90 id: nil, email: "TEST1@TEST.COM", password_digest: "[FILTERED]", created_at: nil, updated_at: nil>

  expect(duplicate_user).to be_invalid  # duplicate_user.invalid? が true になればパスする
end

format: { with: /\A[\w+-.]+@[a-z\d-]+(.[a-z\d-]+)*.[a-z]+\z/i } のテスト

it '指定formatに合わないemailを許容しないこと' do
  # 指定formatに合致しない文字列の配列を6つ用意
  invalid_emails = %w[user@foo,com user_at_foo.org example.user@foo.foo@bar_baz.com foo@bar+baz.com foo@bar..com]
  # userのemailにformatに合致しないemailをいれて検証
  invalid_emails.each do |invalid_email|
    user.email = invalid_email
    expect(user).to be_invalid # duplicate_user.invalid? が true になればパスする
  end
end

before_save { email.downcase! } のテスト

upcase_email_userのemail TeSt@TEst.Com がsave後にはtest@test.comになっていることを検証しており、
expect(X).to eq Yは「XがYに等しくなること」をテストしている。

it 'downcase!が正常に動作していること' do
  # before_saveはsaveの直前に呼び出されるためsave!する
  upcase_email_user.save!
  expect(upcase_email_user.email).to eq 'test@test.com'
end

password バリデーション

presence: true、 length: { maximum: 6 } のテスト(Shoulda Matchers)

emailと同様にShoulda Matchersを用いてテストを記述

context 'password' do
  # presence: true のテスト
  it { should validate_presence_of(:password) }
  # length: { maximum: 6 } のテスト
  it { should validate_length_of(:password).is_at_least(6) }
end

最後に

RSpec の学びはじめは構文に慣れておらず非常に苦労しました。しかし、わかりやすい解説の記事も多く、慣れてきて思った通りにテストがパスすると楽しくなってきます。
今回はモデルテストの記事を書きましたが、リクエストテストも学んだのでそちらも今後記事にできたらいいと思っています。

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

binding.pryの追加(rails)

ターミナルにて(追加したいディレクトリ)を「cd ~ 」で指定する。
VSCodeで(追加したいディレクトリ)のgemfileを開き、「gem 'mysql2', '0.?.?'」の「mysql2」のあとのバージョンを「'0.5.3'」に変更し、
「gem 'mysql2', '0.5.3'」とする。

gemfileの一番下に「gem 'pry-rails'」と記述し、ターミナルに戻る。
ターミナルにて、bundle updateをするとgemがインストールされる。

mysql2のバージョンが既に変更されていたら、pry-railsを記述した後に「bundle install」をすると導入される。

間違いなどあれば、ご指摘いただけるとありがたいです。

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

【Rails】部分テンプレート

書き方

一行目が一般的

<%= render 'tweet', tweet: tweet %>
<%= render partial: "tweet", locals: { tweet: tweet } %>

renderメソッド

部分テンプレートを呼び出す際に利用するメソッド

partialオプション

部分テンプレート名を指定することで、該当する部分テンプレートを表示できます。

localsオプション

localsというオプションを付けることで、部分テンプレート内でその変数を使えるようになります。

collectionオプション

データの繰り返し(コレクション)を出力する際にもちいる。コレクションのメンバ(要素)ごとに部分テンプレートがレンダリングされて挿入される

<%= render partial: "prototypes/prototype", collection: @user.prototypes %>

テンプレート先での記述方法は、@なしの単数型

<div class="card">
  <%= link_to image_tag(prototype.image, class: :card__img ), prototype_path(prototype) if prototype.image.attached? %>
  <div class="card__body">
    <%= link_to "#{prototype.title}", root_path, class: :card__title%>
    <p class="card__summary">
      <%= "#{prototype.catch_copy}" %>
    </p>
    <%= link_to "by #{prototype.user.name}", root_path, class: :card__user %>
  </div>
</div>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで日付比較の検索の書き方

Ruby2.7.1
Rails6.0.3.3
で確認

久しぶりにRailsでDBの検索処理を書いていて日付の比較の検索ってどう書くんだと調べていたのでメモ書き。
調べてるとたいてい、SQLを書くかArel使うかみたいのばっかで他に無いのかと調べていたら下記のような書き方でもいけた。

[0] pry(main)> User.where(created_at: ..DateTime.now)
  User Load (0.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`created_at` <= '2020-10-27 14:41:26.943067'
[
    [0] #<User:0x000055861898f208> {
                   :id => 1,
           :created_at => Tue, 27 Oct 2020 14:18:54 JST +09:00,
           :updated_at => Tue, 27 Oct 2020 14:18:54 JST +09:00,
    }
]

逆は

 User.where(created_at: DateTime.now..)
  User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`created_at` >= '2020-10-27 14:52:57.534880'

へぇって感じ。

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

【Rails】Active Storage(画像保存の設定)

使用するツール

Active Storage(gem) ※railsに最初から入っている。
MiniMagick(gem)
ImageProcessing(gem)
ImageMagick

実装手順

ImageMagickをインストール

% brew install imagemagick

Gemをインストール

gem 'mini_magick'
gem 'image_processing', '~> 1.2'

Active Storageをインストール

% rails active_storage:install
% rails db:migrate

rails active_storage:installコマンドを実行すると、Active Storageに関連したマイグレーションが作成されます。続けてマイグレートしましょう。

画像の保存

Active StorageのテーブルとMessagesテーブルのアソシエーションを定義
messages_controller.rbにて、imageカラムの保存を許可

has_one_attachedメソッド

各レコードとファイルを1対1の関係で紐づけるメソッド

class モデル < ApplicationRecord
  has_one_attached :ファイル名
end

:ファイル名には、添付するファイルがわかる名前をつけましょう。
この記述により、モデル.ファイル名で、添付されたファイルにアクセスできるようになります。また、このファイル名は、そのモデルが紐づいたフォームから送られるパラメーターのキーにもなります。

画像の保存を許可するストロングパラメーター

 def message_params
    params.require(:message).permit(:content, :image).merge(user_id: current_user.id)
  end

保存した画像を表示

image_tagメソッド

# ファイルをモデルから指定する場合
<%= image_tag モデル名.has_one_attachedで設定したファイル名 %>
<%= image_tag user.avatar %>

# app/assets/ディレクトリ下の画像ファイルパスでも指定できる
<%= image_tag 画像ファイルのパス %>
<%= image_tag "avatar.png" %>

attached?メソッド

レコードにファイルが添付されているかどうかで、trueかfalseを返すメソッドです。
ifと組み合わせて、画像の有無によって表示するかどうか判断させられます。

モデル.ファイル名.attached?
<%= image_tag message.image, class: 'message-image' if message.image.attached? %>

variantメソッド

ファイルの表示サイズを指定できます。

モデル.ファイル名.variant(resize: '幅x高さ')

記述例

html.erb
<div class="card">
  <%= link_to image_tag(prototype.image, class: :card__img ), prototype_path(prototype) if prototype.image.attached? %>
  <div class="card__body">
    <%= link_to "#{prototype.title}", root_path, class: :card__title%>
    <p class="card__summary">
      <%= "#{prototype.catch_copy}" %>
    </p>
    <%= link_to "by #{prototype.user.name}", root_path, class: :card__user %>
  </div>
</div>

画像かテキストだけのメッセージを送信

class Message < ApplicationRecord
  belongs_to :room
  belongs_to :user
  has_one_attached :image

  validates :content, presence: true, unless: :was_attached?

  def was_attached?
    self.image.attached?
  end
end

複数枚の投稿

アソシエーションの変更

has_one_attached :imageをhas_many_attached :imageへ変更

model.rb
class モデル名 < ApplicationRecord
  has_many_attached :images
end

フォームの修正

<%= form_with model: @モデル名, id: 'new_message', local: true do |f| %>
  <%= f.text_field :content, placeholder: 'type a message' %>
  <%= f.file_field :images, name: 'message[images][]', id: 'message_image' %>
  <%= f.submit '送信' %>
  <div id="image-list"></div>
<% end%>

ストロングパラメーターの修正

なお、配列型のパラメーターは最後に書かなければエラーを吐く。
エラーを吐く例:params.require(:モデル名).permit(images: [], :title)

controller.rb
private
  def message_params
    params.require(:モデル名).permit(images: [])
  end
end

ビューの修正

<%= image_tag @変数.image, id: 'image' if @変数.image.present? %>は削除し、以下の記述に置き換える。

html.erb
<% @変数.images.each do |image| %>
  <%= image_tag image, class: 'images' %>
<% end %>

仮に1番目の画像のみを表示したい場合は、以下の記述となる。

<%= image_tag @親モデル名.image.first %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]Google Analytics コンバージョンタグを設定する

目標(コンバージョン率の測定)とは

Google Analyticsの機能で、「会員登録」「コンテンツ登録」「5ページ以上の閲覧」など目標を設定することにより、Webサイトの指標を出すことができます。
僕のWebサイト( https://www.mini4wg.com/ )でも目標設定をしたので、設定項目や実装方法などを解説していきます。

スクリーンショット 2020-10-27 14.05.21.png

目標設定 1/3

テンプレートが準備されており非常に便利です。google analyticsを指定してあれば、サイトに手を入れ図に対応することもできます。
しかし、画面遷移などによってはコンバージョンタグを仕込むなどの準備が必要になります。

最初の目標では
 - 収益
 - 集客
 - 問い合わせ
 - ユーザーのロイヤリティ
など良く使われる項目が最初から定義されています。自分のサイトにあった目標を設定しましょう。

スクリーンショット 2020-10-27 14.13.49.png

目標の説明 2/3

目標の説明では測定のタイプを設定します。
- 到達ページ
- 滞在時間
- ページビュー数
- イベント
が設定できます。

スクリーンショット 2020-10-27 14.25.22.png

目標の詳細 3/3

目標の説明(2/3)で設定したタイプにより、ここのフォームが変化します。
イベントを選択した場合は、Webサイトからeventを送信する必要があります。
到達ページや滞在時間やページビューはGoogle Analyticwの画面から設定するだけで利用可能です。

スクリーンショット 2020-10-27 14.28.03.png

Railsでイベントを送信する場合

僕のサイトでも画像を登録する機能があるのですが、コンバージョンを測定するのに作成時に一度だけ送信したい時に下記の様な実装を行いました。
※ gtag.jsかanalytics.jsかでevent送信のコードが違うのでご注意ください。
僕のサイトではコンテンツ送信を測定する場合,ECサイトではよくある[thanks.html]などのサンクスページないため、
作成時に一回限りだけjavascriptのタグを出力し、google analyticsにeventを送信する様にしました。

ProductsController

ruby
class ProductsController < ApplicationController
  =省略=
  def create
    @product = current_user.products
                           .build(product_params)
    authorize @product

    if @product.save
      flash[:cv] = true
      redirect_to @product, notice: t('.saved')
    else
      render :new
    end
  end
 =省略=
end

views/products/show.html.slim

slim
~~省略
- if flash[:cv] == true
  javascript:
    gtag('event', 'created', { 'event_category': 'products', 'event_label': '#{@product.id}'});
~~省略

確認方法

実際にeventの送信が行われているかは下記のChrome Extensionを入れることにより、どんなeventが送信されているかわかります。

Page Analytics (by Google) - Chrome ウェブストア https://chrome.google.com/webstore/detail/page-analytics-by-google/fnbdnhhicmebfgdgglcdacdapkcihcoh/related?hl=ja

最後に

目標設定をすることにより、サイトの改善に役立てると共に人に説明する時にも指標があると非常に便利です。

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

【Rails】SameSiteとSecure属性の付与〜Railsのセキュリティ対策〜

はじめに

社内でcookieのセキュリティに関する勉強会があったので、学んだことをまとめておきます〜〜:writing_hand::writing_hand:
Railsは優秀なフレームワークなので、そこそこのセキュリティ対策が標準装備されているんですよねえ。
セキュリティについてはあまり考えなくても、実装できてしまうんですよねえ。
良いことでもあり、悪いことでもありますね:runner::runner:

GoogleChrome80のリリース

2020年2月4日(現地時間)にGoogleChrome80が正式にリリースされました。
https://developers-jp.googleblog.com/2019/11/cookie-samesitenone-secure.html
今回のバージョンアップでデフォルトのCookieのSameSite属性がNone⇒Laxに変更されました。
またSameSite属性をNoneにする場合、Secure属性を付与しなければならない仕様になりました。

???
なにそれ??

私もピンと来ませんでしたし、よくわからない方もいるかも知れないので、ざっくり解説します。

SameSite属性

SameSite属性とは、CSRF(クロスサイトリクエストフォージェリ)というサイバー攻撃からユーザーを守るために、Cookieに付与される属性のことです。
安全のためのCookieのオプション設定みたいに考えておいてください。

GoogleChrome80からこれのデフォルト値がNone⇒Laxに変更になったのです。
簡単に言うとセキュリティが強化されましたということです。
最近、Googleはセキュリティ対策に厳しいですからね〜

SameSite属性は三段階にわかれています。

属性 内容
None ドメインをまたいでCookieの受け渡しが可能
Lax (GETでのリクエストのみ)ドメインをまたいでCookieの受け渡しが可能
Strict ドメインをまたいでCookieの受け渡しが不可

セキュリティのレベル的には
None < Lax < Strictですね。

つまり、ドメインをまたいでCookieの受け渡しができるか?できないのか?という違いを設定する設定のことですね!

secure属性

Secure属性はHTTPS化していないCookieの動作を制御する属性のことです。
Secure属性が付与されているCookieはHTTPS通信の場合のみ送信できます。
要はCookieが盗まれにくくなります。

GoogleChrome80からはSameSiteがNoneのときはSecure属性を付与しなければならない仕様に変更になりました。

つまり「SameSiteがNoneなんだろ?セキュリティー不安だからせめてSecure属性くらいつけておけよ〜」というGoogle様からのお達しということですね。

Railsでの設定

ではGoogleChrome80の仕様もSameSite属性・secure属性も理解したところで、Railsで設定をしてみましょう!

Gemfile
gem 'rails_same_site_cookie'
ターミナル
bundle install

はい。これだけです。

特に設定等もいりません。gem「rails_same_site_cookie」をインストールするだけです。
めちゃめちゃ簡単ですね。
自動的に全cookieにSameSite=None; Secure属性が追加してくれます。

確認方法

確認方法も簡単です。
chromeの検証からApplicationをクリックして、使用中のCookieの中身を見ます。
secure属性にチェックが入っていて、SameSite属性がNoneになっていたらOKです。

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

※ローカル環境では確認できないことがあるので注意してくださいね〜

おわりに

勉強会でとんでもないハッカーに出会ってしまったので、毎日震えながら開発しています:baby::baby:

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

[Payjp] Payjp::InvalidRequestError No such tokenの解決法

概要

Payjpでcustomerを作成しようとした際、No such tokenというエラーを解決するのに時間を要したため、解決した流れを記載しておきます。

エラーの内容

Image from Gyazo

上記のように、そのようなトークンはありませんと言われてしまいました。

  • トークンはJs→Rails側に送信できている
  • Completed 500 Internal Server Error

となっているため、サーバーサイド側のエラーだと思い、秘密鍵が合っているか等、色々試してみましたが解決できませんでした。

card.jsの記述

サーバーサイド側のエラーだと踏んでいたのですが、jsの記述も確認してみました。

card.js
const pay = () => {
  Payjp.setPublicKey(process.env.PAYJP_PUBLIC_KEY);
  const form = document.getElementById("charge-form");
  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const formResult = document.getElementById("charge-form");
    const formData = new FormData(formResult);

    const card = {
      number: formData.get("number"),
      cvc: formData.get("cvc"), 
      exp_month: formData.get("exp_month"),
      exp_year: `20${formData.get("exp_year")}`
    };
    Payjp.createToken(card, (status, response) => {
      if (status === 200) {
        const token = response.id;
        const renderDom = document.getElementById("charge-form");
        const tokenObj = `<input value=${token}, type="hidden", name="card_token">`;
        renderDom.insertAdjacentHTML("beforeend", tokenObj);

        document.getElementById("card-number").removeAttribute("name");
        document.getElementById("card-exp-month").removeAttribute("name");
        document.getElementById("card-exp-year").removeAttribute("name");
        document.getElementById("card-cvc").removeAttribute("name");

        renderDom.submit();
        renderDom.reset();
      }
    });
  });
};
window.addEventListener("load", pay);

特におかしなところは無いように思えます・・・。

ある違和感

同じような記述で、jsを使って商品の合計金額(total_price)をサーバーサイドに送信するコードも書いていました。

sendTotalPrice.js
function sendTotalPrice() {
  const totalPriceDom = document.getElementById("cart-total-price")
  const totalPrice = parseInt(totalPriceDom.innerHTML);

  const renderDom = document.getElementById("charge-form");
  const totalPriceObj = `<input value=${totalPrice}, type="hidden", name="total_price">`;
  renderDom.insertAdjacentHTML("beforeend", totalPriceObj);
};
window.addEventListener('load', sendTotalPrice);

ここでおかしいと感じたことがあります。
Image from Gyazo
送信されたparamsのtotal_priceの中に
Image from Gyazo
不要な " , " が入ってしまっています。

原因はjs内の記述だった

トークンの値をフォームに含めるための記載に間違いがありました。

間違い
 const tokenObj = `<input value=${token}, type="hidden", name="card_token">`;
正しい
 const tokenObj = `<input value=${token} type="hidden" name="card_token">`;

value=${token}のあとのカンマ "," は不要でした!

勉強になったこと

Rails等のform_withなどでは属性を区切るためにカンマが必要ですが、HTML要素では属性を区切るカンマは不要ですね・・・。


同じような内容で悩まれている方がもしいらっしゃったら、参考になれば幸いです。

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

【Rails】アプリケーションを削除してみた

はじめに

テストで作成したミニアプリを削除てみました。備忘録もかねて記事に残します。

目次

1.手順
2.削除

1. 手順

・データベースの削除
・フォルダ削除

アプリを削除するには、ただフォルダを削除すれば良いと思っていました。
しかし、それではデータベースがの残ったままなので、不具合が起きる可能性があります。

下記に参考にさせていただいた記事があります。アプリ以外もいろんなフォルダの削除方法を紹介されてるので、興味がある方は参考にしてみてください。

2. 削除

それでは削除して行きます〜

・データベースの削除

$ rails db:drop

開発とテスト二つのデータベースが削除されれば成功です。

・フォルダ削除

$ cd ..
$ rm [アプリ名]

コマンドで削除可能ですが、ファインダーからゴミ箱に入れても削除できます。

以上です!

最後に

私はプログラミング初学者ですが、同じ様に悩んでる方々の助けになればと思い、記事を投稿しております。
それでは、また次回お会いしましょう〜

参考

https://qiita.com/ruemura3/items/63eb805aa2b4e250df81

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

個人的に便利なアプリや開発ツールをまとめてみた(開発ツール編)

今回は、「開発ツール編」として筆者がRailsで開発環境を構築するときに導入する開発ツールをまとめてみました。
いつかまたセットアップする機会がきたときに役に立てばいいと思っております!

neovimを使ってRails開発が行えるところまでをゴールとします。

前回のアプリ編もどうぞ

導入するもの

名前 用途
Homebrew パッケージマネージャ
Alfred 万能ランチャーアプリ
白源 フォント
iTerm2 高機能ターミナル
zsh シェル
prezto zsh用コンフィグフレームワーク
neovim エディタ
iceberg テーマ
Git バージョン管理
rbenv Rubyのバージョン管理
Docker 仮想プラットフォーム

Homebrew

MacOSXシステム上でソフトウェアの導入を単純化してくれるパッケージマネージャ。
これで導入したソフトウェア群を、brewfileを用いて他環境へ簡単に移行することが可能です。
必要なものを一カ所に集めてくれるので、管理の手間が省けて便利ですね。
これから記載するものはほとんどこのツールを用いて導入していくので、最初にインストールしておくのが吉。

インストール手順
公式サイトの通り、ターミナル上で下記コマンドを実行するだけです。
Command + SpaceでSpotlight検索を起動して、「Terminal」と入力するとターミナルが起動します。

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

途中、パスワードを求められますのでMacにログインしているユーザのパスワードを入力してください。

$ Password:

また、Homebrewは「Command line tools for Xcode」というソフトウェアに依存しており、途中でこのソフトウェアのインストールを求められますので、インストールしてください。
※画像は参考ですが、同じようにソフトウェアアップデートが立ち上がりインストールを求められます。
https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_43096_b5586c82-304a-c2a6-27de-8961ea684e0d.png

以下のように出力されたら完了。
一応doctorコマンドで正常にインストールできたか確認します。

==> Next steps:
- Run `brew help` to get started
- Further documentation:
    https://docs.brew.sh

$ brew doctor
Your system is ready to brew.

Alfred

Alfredはアプリ編に記載したかったのですが、Homebrewを使ってどのような環境であっても必ず持って行きたいアプリなのでこちらに記載しました。
Mac標準のSpotlightと同じようにアプリランチャーとしても働きますし、システムの機能をショートカットから呼出してファイルを一括で編集したり、スニペット登録機能があったり色々な機能があります。
課金すると上記のスニペット登録機能・ファイル操作機能が使えますが、そもそもSpotlightより便利で高機能なので導入しておいて損はないと思います。
筆者は全然使いこなせていないですが、かなり生産性が上がるみたいで少し余裕ができたら学んでいきたいと思っております。

theme@2x.jpg

インストール手順
Homebrewを使ってインストールします。
Homebrewは基本的にCUIをインストールするものなのですが、homebrew-caskという拡張機能を使用することによりGUIをインストールすることが可能となります。

この拡張機能をまず有効にしましょう。
下記のコマンドを実行すると、Homebrewにcaskが追加されます。

$ brew cask

本来はcaskのあとにインストールしたいアプリの名前を入れる必要があり、「Error: Invalid usage: No subcommand given.」と出力されますが、コマンドの実行方法がおかしいってだけでcaskは追加されるので気にしないでください。

その後、Alfredをcaskを介してインストールします。
もちろん上記のcaskのインストールを飛ばしてこちらを実行してもらっても構わないです。

$ brew cask install Alfred
alfred was successfully installed!

Spotlightと同じCommand + Spaceで起動したい場合は、インストール出来たらMacのシステム環境設定→キーボード→ショートカットを開きSpotlightのショートカットを無効化してください。
その後、Alfredを起動してショートカットを変更して導入は完了です。
スクリーンショット 2020-10-22 23.26.53.png

白源

エディタ上で見やすいフォントを色々試したのですが、個人的に一番見やすくてCUI上でアイコンをフォントとして出力するNerd Fontsに対応しているのでおすすめ。
プログラミング用のフォントとして有名なRictyをカスタマイズしているので完成度が高く、今でも定期的に更新され視認性がどんどん上がっていっており、非常に活発なフォントなのであります。
詳しくは作者様の記事をご覧ください。

インストール手順
cask同様にフォント用のHomebrewの拡張機能をインストールして、そちらを介して白源フォントをインストールします。

$ brew tap homebrew/cask-fonts
$ brew cask install font-hackgen-nerd

以降の項目でこちらのフォントは使用しますので、ここでは導入のみにとどめます。
もしお気に入りのフォントがあるなら、cask-fontsをインストール後に下記コマンドでHomebrewで扱われているか調べて、そちらを利用しても構いません。

$ brew search フォント名

iTerm2

Mac標準のターミナルを高機能にしたもの。
後述するエディタ用テーマ「iceberg」が、ターミナルではフルカラー出力に対応しておらず色味が思い通りに表示されなかったので導入した。
以下の記事を見てtmuxを入れなくてもコピーモードで色々できるんだなと思ったのも大きい。

インストール手順
下記を実行してインストール。

$ brew cask install iTerm2

導入はこれでいいですが、例えばダウンロードとかのディレクトリにアクセスしたときにiTermにアクセス許可を与えるかどうかの通知が表示されます。
この通知がいろんなディレクトリにアクセスしたときに表示されるので気になる人は、システム環境設定→セキュリティとプライバシーのフルディスクアクセスにiTerm.appを追加してください。
スクリーンショット 2020-10-23 2.29.59.png

後述するicebergの項で配色に関しては設定しますので、ここではステータスバーとフォントの設定を行います。
まずPreferences→Profileにある+マークで新しいプロファイルを作成しましょう。名前は適当でいいです。筆者は「iceberg」としました。
スクリーンショット 2020-10-24 8.55.22.png

次にTextタブを開き下にあるFontを、先ほど入れた「白源」にします。
スクリーンショット 2020-10-24 9.01.25.png

また、ステータスバーの表示位置がデフォルトでは上なので、Preferences→AppearanceのStatus bar locationで下などに変更できる。
スクリーンショット 2020-10-24 9.34.48.png

Sessionタブに移り、一番下のStatus bar enabledにチェックをつけます。
スクリーンショット 2020-10-24 9.09.07.png

あとはConfigure Status Barボタンを押して表示させたいアイテムをActive Componentsにドラッグして、Advancedで色とフォントの設定を行います。
筆者の場合は、背景色とアイテムの区切り線の色はicebergの背景色と同じ#161821にして、フォントも白源としました。
(iMacの場合、バッテリー表示は不要ですがMacbookとかと設定を同期したい場合はつけてもいいかもしれません。)
スクリーンショット 2020-10-24 9.12.18.png
スクリーンショット 2020-10-24 9.34.13.png

設定できたらOKを押して閉じます。
作ったプロファイルは、左下のOther Actionsを押してデフォルトに設定してください。
その後再起動すると反映されます。
スクリーンショット 2020-10-24 9.38.02.png

また、このプロファイルはOther Actionsで、JSONとしてエクスポートできますので、他のPCでインポートすれば設定を同期できるので覚えておくと便利です。

zsh

Catalinaを使っていたら導入する必要はありませんが、Homebrewで最新バージョンをインストールしてそちらを利用します。

インストール手順
Catalinaの場合、ログインシェルがzshなのでそのコマンドの位置と、バージョンを見ておきます。

$ which zsh
/bin/zsh
$ /bin/zsh --version
zsh 5.7.1

次に、Homebrewを介してzshをインストールしてそれをログインシェルとするために、/etc/shellsをvimで編集します。
パスワードを求められるのでログインユーザーのパスワードを入力してください。

$ brew install zsh
$ sudo vim /etc/shells
Password:

開いたらファイルの一番下に「/usr/local/bin/zsh」を追記して、下記コマンドでシェルを変更します。

$ chsh -s /usr/local/bin/zsh

実行できたらターミナルを再起動。
起動したらもう一度、zshの位置とバージョンを確認して、変更が確認できたら完了です。

$ which zsh
/usr/local/bin/zsh
$ zsh --version
zsh 5.8

prezto

zshrcファイルをあれこれいじるのが面倒な場合、preztoを入れればある程度便利に設定を行ってくれます。
またプロンプトも少しおしゃれにできます。

インストール手順
ターミナルを起動して下記を実行していきます。

# 先に変数を定義してpreztoの配置場所及び、設定ファイルの保存場所を決めておく
$ ZDOTDIR="~/.config/zsh"

# prezto本体をクローンする
$ git clone --recursive https://github.com/sorin-ionescu/prezto.git "${ZDOTDIR:-$HOME}/.zprezto"

# 設定ファイルを生成する
$ setopt EXTENDED_GLOB
for rcfile in "${ZDOTDIR:-$HOME}"/.zprezto/runcoms/^README.md(.N); do
  ln -s "$rcfile" "${ZDOTDIR:-$HOME}/.${rcfile:t}"
done

# シェルをデフォルトにする
$ chsh -s /usr/local/bin/zsh

# ZDOTDIRの箇所に生成された.zshenvをルートディレクトリに移動し、編集します。
$ mv ~/.config/zsh/.zshenv ~/.zshenv
$ vim .zshenv
.zshenv
# 省略
# 設定ファイル保存先
export ZDOTDIR="$HOME/.config/zsh"

次にpreztoに便利なモジュールを追加します。

$ vim ~/.config/zsh/.zpreztorc

32行目あたりの箇所を編集します。

.zpreztorc
zstyle ':prezto:load' pmodule ¥
  'environment' \
  'terminal' \
  'editor' \
  'history' \
  'directory' \
  'spectrum' \
  'utility' \
  'completion' \
  'syntax-highlighting' \ # これを追加
  'autosuggestions' \ # これも追加
  'prompt'

シェルを再起動すれば完了。

neovim

vimを拡張した派生エディタ。
JetbrainsなどのIDE、VSCODEとこれが現時点で最良の選択なのかなーっと思ってます。
正直、あれこれカスタマイズしてメンテナンスしていかないといけないので、それらが一番楽なのはIDEです。
ただVimの操作ってすごい楽なんですよね。で、IDEもVSCODEもそれらのメリットを享受できるけど、完璧じゃないんです。
ここはもう好みの問題だとは思いますが。

インストール手順
下記コマンドを実行。

$ brew install nvim

インストール自体は完了しており、続けて設定ファイルを作成する。

$ mkdir ~/.config/nvim
$ touch ~/.config/nvim/dein.toml
$ touch ~/.config/nvim/dein_lazy.toml
$ touch ~/.config/nvim/init.vim
$ vi ~/.config/nvim/init.vim

作成したら設定を色々編集していくのだが、dein.vimを導入し、最低限のものをまとめたのが以下になる。
dein.vimはプラグインマネージャで、vimに拡張機能をもたらすモジュールを一括管理してくれる。
以下のように書き換えて、再度neovimを起動したら自動的にインストールしてくれる。
エラーなどは気にしなくてよい。

init.vim
" dein.vim settings
let s:dein_dir = expand('~/.cache/dein')
let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim' 
" dein installation check
if &runtimepath !~# '/dein.vim'
  if !isdirectory(s:dein_repo_dir)
    execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
  endif
  execute 'set runtimepath^=' . s:dein_repo_dir
endif

" begin settings
if dein#load_state(s:dein_dir)
  call dein#begin(s:dein_dir) 
  " .toml file
  let s:rc_dir = expand('~/.config/nvim')
  if !isdirectory(s:rc_dir)
    call mkdir(s:rc_dir, 'p')
  endif
  let s:toml = s:rc_dir . '/dein.toml'
  let s:lazytoml = s:rc_dir . '/dein_lazy.toml'

  " read toml and cache
  call dein#load_toml(s:toml, {'lazy': 0})
  call dein#load_toml(s:lazytoml, {'lazy': 1})

  " end settings
  call dein#end()
  call dein#save_state()
endif

" plugin installation check
if dein#check_install()
  call dein#install()
endif

" plugin remove check
let s:removed_plugins = dein#check_clean()
if len(s:removed_plugins) > 0
  call map(s:removed_plugins, "delete(v:val, 'rf')")
  call dein#recache_runtimepath()
endif

filetype plugin indent on
syntax enable

" リーダーキーの変更
let mapleader = "\<Space>"

colorscheme iceberg
set termguicolors
set encoding=utf-8
set nrformats=
set tabstop=2
set softtabstop=2
set shiftwidth=2
set expandtab
set autoindent
set smartindent
set nobackup
set noswapfile
set clipboard&
set clipboard^=unnamed
set number
set norelativenumber
set title
set nocursorline
set list
set listchars=tab:\|\ ,trail:-,eol:↲,extends:»,precedes:«,nbsp:%
set showmatch
set matchtime=1
set hlsearch
set whichwrap=b,s,<,>,[,]
set tags=./tags;
set completeopt=menuone,noinsert
set viminfo+=n~/.config/nvim/.viminfo

" j,kキーで表示行移動
nnoremap <silent> k gk
nnoremap <silent> gk k
nnoremap <silent> j gj
nnoremap <silent> gj j

" 折り返し移動
set whichwrap=b,s,<,>,[,]

" 補完のEnterでは挿入しない
inoremap <expr><CR> pumvisible() ? "<C-e>" : "<CR>"

iceberg

色々テーマはあるが、寒色で目に優しい配色が気に入っている。
公式サイトを見て気に入ったら使ってみてほしい。

インストール手順
iTerm2とneovimそれぞれに導入していく。
まずはneovim。

dein.tomlを開いて、icebergを追加。
ほかにdein.vim自体と、vimのステータスバーを格好良くするairlineを追加。

vi ~/.config/nvim/dein.toml
dein.toml
[[plugins]]
repo = 'Shougo/dein.vim'

[[plugins]]
repo = 'cocopon/iceberg.vim'

[[plugins]]
repo = 'vim-airline/vim-airline'

その後、neovimを起動したら自動的にインストールされる。

次にiTerm2の設定をしていく。
vimでiTermのカラー設定用ファイルを作成して、以下の記述通りに保存する。

$ vi iceberg.itermcolors
iceberg.itermcolors
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Ansi 0 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.12941177189350128</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.094117648899555206</real>
        <key>Red Component</key>
        <real>0.086274512112140656</real>
    </dict>
    <key>Ansi 1 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.47058823704719543</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.47058823704719543</real>
        <key>Red Component</key>
        <real>0.88627451658248901</real>
    </dict>
    <key>Ansi 10 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.55686277151107788</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.7921568751335144</real>
        <key>Red Component</key>
        <real>0.75294119119644165</real>
    </dict>
    <key>Ansi 11 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.5372549295425415</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.69411766529083252</real>
        <key>Red Component</key>
        <real>0.91372549533843994</real>
    </dict>
    <key>Ansi 12 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.81960785388946533</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.67450982332229614</real>
        <key>Red Component</key>
        <real>0.56862747669219971</real>
    </dict>
    <key>Ansi 13 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.82745099067687988</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.62745100259780884</real>
        <key>Red Component</key>
        <real>0.67843139171600342</real>
    </dict>
    <key>Ansi 14 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.80784314870834351</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.76862746477127075</real>
        <key>Red Component</key>
        <real>0.58431375026702881</real>
    </dict>
    <key>Ansi 15 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.87058824300765991</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.83137255907058716</real>
        <key>Red Component</key>
        <real>0.82352942228317261</real>
    </dict>
    <key>Ansi 2 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.50980395078659058</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.7450980544090271</real>
        <key>Red Component</key>
        <real>0.70588237047195435</real>
    </dict>
    <key>Ansi 3 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.47058823704719543</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.64313727617263794</real>
        <key>Red Component</key>
        <real>0.88627451658248901</real>
    </dict>
    <key>Ansi 4 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.7764706015586853</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.62745100259780884</real>
        <key>Red Component</key>
        <real>0.51764708757400513</real>
    </dict>
    <key>Ansi 5 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.78039216995239258</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.57647061347961426</real>
        <key>Red Component</key>
        <real>0.62745100259780884</real>
    </dict>
    <key>Ansi 6 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.7607843279838562</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.72156864404678345</real>
        <key>Red Component</key>
        <real>0.5372549295425415</real>
    </dict>
    <key>Ansi 7 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.81960785388946533</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.78431373834609985</real>
        <key>Red Component</key>
        <real>0.7764706015586853</real>
    </dict>
    <key>Ansi 8 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.5372549295425415</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.43921568989753723</real>
        <key>Red Component</key>
        <real>0.41960784792900085</real>
    </dict>
    <key>Ansi 9 Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.5372549295425415</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.5372549295425415</real>
        <key>Red Component</key>
        <real>0.91372549533843994</real>
    </dict>
    <key>Background Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.12941177189350128</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.094117648899555206</real>
        <key>Red Component</key>
        <real>0.086274512112140656</real>
    </dict>
    <key>Badge Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>0.5</real>
        <key>Blue Component</key>
        <real>0.0</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.1491314172744751</real>
        <key>Red Component</key>
        <real>1</real>
    </dict>
    <key>Bold Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.87843137979507446</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.70980393886566162</real>
        <key>Red Component</key>
        <real>0.54901963472366333</real>
    </dict>
    <key>Cursor Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.81960785388946533</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.78431373834609985</real>
        <key>Red Component</key>
        <real>0.7764706015586853</real>
    </dict>
    <key>Cursor Guide Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>0.25</real>
        <key>Blue Component</key>
        <real>1</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.9268307089805603</real>
        <key>Red Component</key>
        <real>0.70213186740875244</real>
    </dict>
    <key>Cursor Text Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>1</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>1</real>
        <key>Red Component</key>
        <real>0.99607843160629272</real>
    </dict>
    <key>Foreground Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.81960785388946533</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.78431373834609985</real>
        <key>Red Component</key>
        <real>0.7764706015586853</real>
    </dict>
    <key>Link Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.73423302173614502</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.35916060209274292</real>
        <key>Red Component</key>
        <real>0.0</real>
    </dict>
    <key>Selected Text Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.81960785388946533</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.78431373834609985</real>
        <key>Red Component</key>
        <real>0.7764706015586853</real>
    </dict>
    <key>Selection Color</key>
    <dict>
        <key>Alpha Component</key>
        <real>1</real>
        <key>Blue Component</key>
        <real>0.25882354378700256</real>
        <key>Color Space</key>
        <string>sRGB</string>
        <key>Green Component</key>
        <real>0.17254902422428131</real>
        <key>Red Component</key>
        <real>0.15294118225574493</real>
    </dict>
</dict>
</plist>

このファイルをiTermのPreferences→Profile→Color Presetsでインポートを行うと適用される。
スクリーンショット 2020-10-25 20.59.20.png

Git

ソースコードなどの変更履歴を追跡できるバージョン管理システム。

インストール手順
Homebrew経由でインストールします。
まず元から入っているgitのバージョンを調べて、Homebrewでインストールします。

$ git --version
git version 2.24.3(Apple git-128)

$ brew install git

このままでは元からMacに入っているgitが使われてしまうので、パスを通します。

$ vim ~/.config/zsh/.zshrc
.zshrc
export PATH="/usr/local/bin/git:$PATH"

一度ターミナルを再起動して、gitのバージョンが変わっていれば成功です。

$ git --version
git version 2.29.1

rbenv

Rubyのバージョンを切り替えたり、管理することが簡単にできます。
とりあえず入れておいて損はない。

インストール手順
Homebrewでインストール。

$ brew install rbenv
$ rbenv -v
1.1.2

次にパスを通します。
またRBENV_ROOT変数にrbenvのshimsとversionsがあるディレクトリの位置を指定します。
最後のrbenv initは必須です。

$ vim ~/.config/zsh/.zshrc
.zshrc
export RBENV_ROOT="~/.local/share/rbenv"
export PATH="$RBENV_ROOT/bin:$PATH"
eval "$(rbenv init -)"

次にこのrbenvを通してRubyをインストールします。

# 下記コマンドでインストール可能なRubyのバージョンが一覧で見られます
$ rbenv install -l

# バージョンを決めたらインストールします
$ rbenv install 2.7.2

# インストールしたバージョンを実行可能にします
$ rbenv global 2.7.2

# 下記コマンドを実行して、指定したバージョンにアスタリスクがついていたらおk
$ rbenv versions
  system
* 2.7.2

Docker

仮想環境で開発を行うプラットフォームを提供するツール。

インストール手順
以下からDocker for Macをダウンロードしてインストールする。
公式サイト

まとめ

最終的にこのようになります。
スクリーンショット 2020-10-27 0.06.59.png

Dockerを使って実際に仮想環境を作成する方法は、以下の記事を参考にしてください。

Vimの他のプラグインや、細かい設定は省いていますがここは個人の好みが含まれるため、色々設定してみるといいかもしれません。
筆者もまだ試している状態なので、揃ってきたら記事にしたいと思います。

最後までお読みいただきありがとうございました!

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

RspecのテストでArgumentError: wrong number of arguments (given 2, expected 0)が出た時の対処法

はじめに

RSpecのテストを実行したときに

   Failure/Error:
       post login_path, params: { session: { email: user.email,
                                             password: user.password } }

     ArgumentError:
       wrong number of arguments (given 2, expected 0)

と出てエラー解決に時間がかかったので書いていきます。

エラー内容

このエラーは「呼び出し側の引数」(given 2)と2に対して「メソッド側の仮引数」(expect 0)で0ですよと言われています。何らかの理由で値にズレが生じているということです。

発生した原因

RSpecのコード内で

let!(:post) { create(:post, user: user) }

という記述をしていて、post login_path, params: { session: { } }のpostがget,post,deleteなどのpostではなくてRSpecコード内のlet!(:post)を呼び出してしまっているから。

解決方法

先ほどあったコードの名前を変えてあげるだけです。

let!(:new_post) { create(:post, user: user) }

参照

https://qiita.com/yo0917/items/20c165a3b06805bf2e37

最後に

すごい初歩的なミスかもしれませんがRSpec書きたての僕はすごい悩まされたので今後も頑張って解決していきたいです。
文章力も上げていきたいです。

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

RSpecのテストでArgumentError: wrong number of arguments (given 2, expected 0)が出た時の対処法

はじめに

RSpecのテストを実行したときに

   Failure/Error:
       post login_path, params: { session: { email: user.email,
                                             password: user.password } }

     ArgumentError:
       wrong number of arguments (given 2, expected 0)

と出てエラー解決に時間がかかったので書いていきます。

エラー内容

このエラーは「呼び出し側の引数」(given 2)と2に対して「メソッド側の仮引数」(expect 0)で0ですよと言われています。何らかの理由で値にズレが生じているということです。

発生した原因

RSpecのコード内で

let!(:post) { create(:post, user: user) }

という記述をしていて、post login_path, params: { session: { } }のpostがget,post,deleteなどのpostではなくてRSpecコード内のlet!(:post)を呼び出してしまっているから。

解決方法

先ほどあったコードの名前を変えてあげるだけです。

let!(:new_post) { create(:post, user: user) }

参照

https://qiita.com/yo0917/items/20c165a3b06805bf2e37

最後に

すごい初歩的なミスかもしれませんがRSpec書きたての僕はすごい悩まされたので今後も頑張って解決していきたいです。
文章力も上げていきたいです。

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

カレンダーから日付を選択するフォーム作成方法

普通の入力フォーム(変更前)

ビューファイル
<%= form.text_area :day, placeholder: "day" %>

前.png

カレンダー選択フォーム(変更後)

text_area → datetime_fieldに変更する。

ビューファイル
<%= form.datetime_field :day, placeholder: "day" %>

後.png

まとめ

変更前は色々と記述しないといけなくて難しいのかと思うかもしれませんが、
実際は記述を少し変えるだけでできちゃうんです!!
ぜひ、やってみて下さい。

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

演算子とは

演算子

プログラミングにおける値の計算や比較などに使用する記号のことです。
計算における代表的な演算子は+-、比較における代表的な演算子は><があります。

この簡単な記号である演算子を用いることで、計算や比較処理を瞬時に行ってくれます。

代数演算子

演算子の中でも計算を行うものは、代数演算子と呼ばれます。
Rubyで一般的な計算をする場合、以下の様な演算子が使えます。

演算子.png

使い方は、普通の算数の計算と同じです。ただし、剰余の計算は馴染みがないので注意が必要です。
例えば、「17を5で割ったときの余り」を求めることができます。

以下の例を見てください。

【例】irb
# 足し算
irb(main):001:0> 1000 + 2000
=> 3000

# 引き算
irb(main):002:0> 3000 - 1500
=> 1500

# 掛け算
irb(main):003:0> 50 * 40
=> 2000

# 割り算
irb(main):004:0> 600 / 15
=> 40

# 剰余演算5 ÷ 2をした余り
irb(main):005:0> 5 % 2
=> 1

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

1年は何時間か計算してみましょう。

irb
irb(main):001:0> 24 * 365

実行すると、一瞬で計算結果が表示されますね。
実際にやってみて下さいね!!

まとめ

・演算子とは、プログラミングにおける値の計算や比較などに
使用する記号のこと。
・代数演算子とは、演算子に属し、その中でも計算を行うもの。

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

【Rails6】jQuery導入・uncaught reference error $ is not definedエラーの解決法

開発環境

  • Rails 6.0.0
  • yarn 1.22.4

目次
1)jQuery導入
2)uncaught reference error $ is not definedエラーの解決法

1) jQuery導入_箇条書き

 1. yarn add jquery でjqueryを導入
 2. environment.jsへ webpackの設定ファイルにjQueryを管理下に追加する記述を書く
 3. application.jsへ jQueryを呼び出すための記述を書く
 4. application.html.erbへ javascriptの呼び出しが記述されているか念の為確認

1) jQuery導入_詳しく解説

 1. yarn add jquery でjqueryを導入

ターミナル
$ yarn init *補足:OSにHomebrewでyarnインストールをすでにされている場合は不要だと思います。
$ yarn add jquery

*補足:私は"yarn add jquery"だけで問題ないのですが、他の方の記事を確認したところ、"yarn init"を先に入力するという説明もあったので、補足として記載させていただきます。『Homebrewでyarnインストール』についてはこちらの記事が参考になると思います。

*補足2:
gem 'jquery-rails'をGemfileへ記述する方法もありますが、Rails6からはyarn addが推奨されているらしいです。Webpackerで管理しているため、Node.jsのパッケージを利用しています。

 2. environment.js

app/config/webpack/environment.js
//この記述内容をコピペ(もともとの記述は削除でOK)
//これは、webpackの設定ファイルでjQueryを管理下に追加するための記述です。
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    jquery: 'jquery',
  })
)

module.exports = environment

 3. application.js

app/javascript/packs/application.js
// 省略
require("@rails/ujs").start()
require("turbolinks").start()  //私はいつもコメントアウトします
require("@rails/activestorage").start()
require("channels")
require('jquery') //ここを追記
// 省略

*補足:turbolinksはキャッシュを積極的に利用するため、ページがロードされてから起こるイベントがうまく動かないことがあります。私はjavascriptを使う上で少し厄介だなという認識なので、コメントアウト。(キャッシュを利用するとページの読み込み時間は短縮されるんですけどね)

 4. application.html.erb **念のために確認

app/view/layouts/application.html.erb
<!-- tourbolinksを削除する場合こうなります -->
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>

ここまでがJqueryを導入するまでの一連の流れです。

2) $ is not definedエラーの解決法

$がnot defined(定義されていない)ということは、jQueryがうまく入れることができていないということなので、jQuery導入で説明した4ステップを確認すれば、解決するのではないかなと思います。

 1. yarn add jquery でjqueryを導入
 2. environment.jsへ webpackの設定ファイルにjQueryを管理下に追加する記述を書く
 3. application.jsへ jQueryを呼び出すための記述を書く
 4. application.html.erbへ javascriptの呼び出しが記述されているか念の為確認

以上です。

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

Herokuへデプロイする手順

Herokuへデプロイする手順

ターミナル
brew tap heroku/brew && brew install heroku
ターミナル
heroku --version
ターミナル
# Herokuへログインするためのコマンド
% heroku login --interactive
  => Enter your Heroku credentials.
# メールアドレスを入力しエンターキーを押す
  => Email:
# パスワードを入力してエンターキーを押す
  => Password:
Gemfile
# ファイルの一番下の行に追記する
group :production do
  gem 'rails_12factor'
end
ターミナル
# Gemをインストール
% bundle install
ターミナル
% git add .
% git commit -m "gem rails_12factorの追加"
ターミナル
% heroku create アプリケーション名
ターミナル
% git config --list | grep heroku
ターミナル
% heroku addons:add cleardb
Creating cleardb on  アプリケーション名... free
Created cleardb-vertical-00000 as CLEARDB_DATABASE_URL
Use heroku addons:docs cleardb to view documentation
ターミナル
% heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL`
ターミナル
% heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5}

# 以下コマンドの実行結果
Setting DATABASE_URL and restarting  アプリケーション名... done, v◯◯
DATABASE_URL: mysql2://000000000000:0aa0000@us-cdbr-east-02.cleardb.com/heroku_aaa00000000?reconnect=true
ターミナル
% EDITOR="vi" bin/rails credentials:edit
ターミナル
% heroku config:set RAILS_MASTER_KEY=`cat config/master.key`
ターミナル
% heroku config
ターミナル
% git push heroku master
ターミナル
% heroku run rails db:migrate

公開を確認

ターミナル
% heroku apps:info

===ajax-app-123456
Addons:         cleardb:ignite
Auto Cert Mgmt: false
Dynos:          web: 1
Git URL:        https://git.heroku.com/アプリケーション名.git
Owner:          sample@sample.com
Region:         us
Repo Size:      165 KB
Slug Size:      56 MB
Stack:          heroku-18
Web URL:        https:/アプリケーション名.herokuapp.com/

現場からは以上です!

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