Rspec使う時のgenerator設定

こんにちは!kossyです!




さて、今回はrspec導入の際のgeneratorの設定についてブログに残してみたいと思います。




個人的には以下のような設定にすることが多いです。

config/application.rb


    config.generators do |g|
      g.template_engine  :haml
      g.test_framework   :rspec, fixture: false
      g.view_specs       false
      g.controller_specs false
      g.helper           false
      g.stylesheets      false
      g.javascripts      false

上記の設定ですと、
・viewはerbではなくhaml
・テストフレームワークRspecで、fixtureは生成されない
・viewのspecファイルは生成なし
・controllerもなし
・helperも
・scssファイルなし
・coffeeもなし

みたいな感じですね。

ちなみに、rails newする際に、-Tと入力して実行すると、
デフォルトで生成されるmini-testのスケルトンをスキップすることができます。

active_storageで画像を複数枚保存したい

こんにちは!kossyです!




さて、今回はRails5.2系から使えるようになった、active_storageで画像の複数枚アップロードの実装方法を
ブログに残してみたいと思います。





has_many_attachを使うだけ


active_storageの導入についてはこちらを参考にしてみてください。
開発環境でのActive_Storageの使い方 - web業界未経験からエンジニアになった人のブログ


画像を1枚添付するだけであれば、has_one_attached :image
みたいな感じで設定すれば事足りるのですが、
複数枚アップロードしたい場合は、has_many_attached :images
と記述します。




これで複数枚アップロードを実現できます。

ブラウザから返ってくるenumの値はシンボルじゃなくて文字列だった

こんにちは!kossyです!




さて、今回はenumで定義した値をブラウザからパラメータとして受け取る場合、シンボルの形式ではなく文字列で
返ってくるのに気づかずハマったので、備忘録としてブログに残してみたいと思います。





enumのステータスによって処理を分岐させたいというシチュエーションでハマりました、、、

コードは例としてこんなんです。

  enum status: { draft: 1, published: 2 }

hogehoge_controller.rb

if params[:status] == :draft
~~
else
~~
end


ここでbinding.pryで処理を止めてparamsをみます。
(省略しまくってすみません。エスパーしてください笑)

コンソール

[1] pry(#<DraftsController>)> params                                                                                                                                     
=> <ActionController::Parameters {"utf8"=>"", "authenticity_token"=>"Rl3FrY+0vYyOoCFv2mRhKYs2Gc3ptfCfMAicrzwECs0YpW8ZHOTGWQ6hHVjepT3K1fADkCZ/7nYHmEp6YusqFQ==", "article"=><ActionController::Parameters {"title"=>"test", "description"=>"test", "body"=>"test", "category_id"=>"1", "status"=>"draft"} permitted: false>, "commit"=>"SAVE", "controller"=>"drafts", "action"=>"create"} permitted: false>

statusのハッシュの値が"draft"と文字列で来ています。
文字列とシンボルを比較すれば当たり前ですがfalseが返ります。これで悶々としてました。

解決策は、

if params[:status].to_sym == :draft

とすればtrueを返すことができます。

コンソールで試してみましょう。

[1] pry(main)> string = "string"                                                                                                                                         
=> "string"
[2] pry(main)> symbol = :string                                                                                                                                          
=> :string
[3] pry(main)> string == symbol                                                                                                                                          
=> false
[4] pry(main)> _string = string.to_sym                                                                                                                                   
=> :string
[5] pry(main)> _string == symbol                                                                                                                                         
=> true

こんな感じですね。


ブラウザから送られてくるenumのパラメーターは文字列型で返ると覚えましょう。(自戒を込めて)

特定のテストケースを実行したい時のfocus: true

こんにちは!kossyです!




さて、今回は、特定のテストケースを実行したい時に便利な、
focus: trueオプションの使い方について、ブログに残してみたいと思います。




環境
Rails 5.1.6
Ruby 2.5.1
rspec 3.8.0
MacOS Mojave




まずはspec_helper.rbに設定

以下の記述を追記します。

/spec/spec_helper.rb

RSpec.configure do |config|

省略

  config.filter_run :focus

end


これでfocus: trueを使えるようになります。




後は実行したいブロックに記述するだけ


例えばこんなテストがあったとします。

  describe 'Validation of create User' do
    describe 'blank' do
      it '名前が空白だとエラーになる' do
        user = User.new(name: '', email: 'example@gmail.com', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:name]).to include('を入力してください')
      end
      it 'メールアドレスが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:email]).to include('を入力してください')
      end
      it 'パスワードが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '')
        user.valid?
        expect(user.errors[:password]).to include('を入力してください')
      end
    end

では、一つ目のitブロックにfocus: trueを設定します。すると、

$ bundle exec rspec spec/models/user_spec.rb


User
  Validation of create User
    blank
      名前が空白だとエラーになる

Finished in 0.24428 seconds (files took 10.93 seconds to load)
1 example, 0 failures


一つ目のテストブロックのテストのみ実行されます。


describeのブロックにも設定することができます。

$ bundle exec rspec spec/models/user_spec.rb



  describe 'Validation of create User', focus: true do
    describe 'blank' do
      it '名前が空白だとエラーになる' do
        user = User.new(name: '', email: 'example@gmail.com', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:name]).to include('を入力してください')
      end
      it 'メールアドレスが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '', password: 'test1234', password_confirmation: 'test1234')
        user.valid?
        expect(user.errors[:email]).to include('を入力してください')
      end
      it 'パスワードが空白だとエラーになる' do
        user = User.new(name: 'テストユーザー', email: '')
        user.valid?
        expect(user.errors[:password]).to include('を入力してください')
      end
    end


以下はログ
User
  Validation of create User
    blank
      名前が空白だとエラーになる
      メールアドレスが空白だとエラーになる
      パスワードが空白だとエラーになる

Finished in 0.15704 seconds (files took 8.86 seconds to load)
3 examples, 0 failures

Validation of create Userのdescribeブロックのテストが全て実行されているのがわかります。




テストケースが増えてくると実行して終了するまで待つのが
とても面倒になってきます。

focus: trueはそんなシチュエーションで活躍間違いなしのオプションです。




参考にさせていただいた記事
RSpecで特定のテストケースのみを実行する方法 - TIM Labs
今日から使える! RSpec でテストの実行サンプル it を素早く絞り込み・スキップする方法 - Qiita

Rail5.2系でreferencesカラムを設定しようとした時のエラー

こんにちは!kossyです!





さて、今回は、Rails5.2系で外部キーを設定しようとした時に遭遇したエラーについて、
ブログに残してみたいと思います。



環境
Rails 5.2.2
Ruby 2.5.1






エラーの状況

2019****_create_category.rb

class CreateCategories < ActiveRecord::Migration[5.2]
  def change
    create_table :categories do |t|
      t.string :name, null: false
      t.text :content, null: false
      t.string :image, null: false

      t.timestamps
    end
  end
end


2019****_create_article.rb

class CreateArticles < ActiveRecord::Migration[5.2]
  def change
    create_table :articles do |t|
      t.string :title, null: false, index: true
      t.string :image
      t.text :description, null: false
      t.text :body, null: false
      t.integer :status, null: false
      t.references :user_id, foreign_key: true
      t.references :category_id, foreign_key: true

      t.timestamps
    end
  end
end

この内容でrails db:migrateを実行してみると、
以下のエラーが発生しました。

rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Table 'cms_development.articles' doesn't exist: SHOW FULL FIELDS FROM `articles`

Caused by:
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'cms_development.articles' doesn't exist: SHOW FULL FIELDS FROM `articles`

Caused by:
Mysql2::Error: Table 'cms_development.articles' doesn't exist

Caused by:
Mysql2::Error: Cannot add foreign key constraint




対処(ベストプラクティスかは怪しい)



外部キーを追加できないと怒られていたので、該当部分をコメントアウト

class CreateArticles < ActiveRecord::Migration[5.2]
  def change
    create_table :articles do |t|
      t.string :title, null: false, index: true
      t.string :image
      t.text :description, null: false
      t.text :body, null: false
      t.integer :status, null: false
      # t.references :user_id, foreign_key: true
      # t.references :category_id, foreign_key: true

      t.timestamps
    end
  end
end

そして、rails db:migrateすると、ひとまず成功。

次に、

$ rails g migration AddReferencesToArticle user:references category:references


2019****_add_refereces_to_article.rb

class AddReferenceToArticle < ActiveRecord::Migration[5.2]
  def change
    add_reference :articles, :user, foreign_key: true
    add_reference :articles, :category, foreign_key: true
  end
end

この状態でrails db:migrateしたら、無事にreferenceカラムを追加できました。


これ本番環境でmigrationする時も起きるのでしょうか?
だとすれば、またそのうち対処法をブログに残すことになるかもしれません、、、笑


1/16 追記
t.references :user, foreign_key: true
t.references :category, foreign_key: true
とすればエラーは起きないみたいです、、、
もう同じミスはやらないと心に誓いました。

link_toでページ内ジャンプ機能を設定してみた

こんにちは!kossyです!




さて、Railsのビューヘルパーであるlink_toでページ内リンク機能の設定方法について、
ブログに残してみました。

HTMLではaタグで実現できますが、link_toを使う場合はどのように実装すればいいのか、
少し詰まったので、備忘録として残しておきます。



環境
Rails 5.2.2
Ruby 2.5.1
Haml




anchorオプションで明示的にリンク先を指定する


HTMLではジャンプしたい箇所にidを設定し、aタグのパスにidを指定すれば
ページ内ジャンプ機能を実現できましたが、
link_toの場合はanchorを使えばページ内ジャンプ機能ができます。

app/views/home/index

%header
  = link_to "TOPIC", { anchor: "topic" }



.article#topic
  %h2
    トピック

こんな感じですね。

anchorオプションにidであるtopicを指定しています。

挙動はこんな感じになるかと。
https://gyazo.com/d9a9d15af906e90c9103d45832b27e39




参考にさせていただいたサイト
link_toでページ内リンクとclass指定を共存させる方法 - Qiita

link_toで画像付きのリンクを生成してみた

こんにちは!kossyです!




さて、今回は、Railsのビューヘルパーであるlink_toで、画像付きのリンクを生成する方法を
ブログに残してみたいと思います。




環境
Rails 5.2.2
Ruby 2.5.1
Haml




link_toの引数にimage_tagを使えばOK


同じくRailsのビューヘルパーであるimag_tagを使えば簡単に実現できます。

app/views/samples/index.html.haml


link_to image_tag("画像名", class:"クラス名"), パス


例
link_to image_tag("sample.png", class: "image"), samples_path

これで実現できます。



参考にさせていただいたサイト
https://dotinstall.com/lessons/basic_rails_v3

TECH CAMPの夜間コースを卒業して即戦力エンジニアになれたのか

こんにちは!kossyです!





早いものでWebエンジニアとして働き出してから1ヶ月が経過しました。
スクールで勉強していたRailsと、全く触れたことのなかったAngularを使って、
既存アプリの改修業務を主として行なっています。



さて、私が卒業したプログラミングスクールでは、
「プログラミング未経験者が短期間で即戦力エンジニアへ」
というフレーズが謳い文句になっています。


そこで、実務に携わるようになって1ヶ月経った今、
本当に即戦力エンジニアになれたのか?
について、ブログに残してみたいと思います。









1. 入社して日が浅い段階でタスクを振って頂けた

初日は入社手続きと開発環境の構築に追われ(課題は与えられたが仕様の把握に時間取られる)、
2日目はスプリントレビューだったのでほとんどコードを書かず。
3日目から本格的に開発業務に携わることになりました。


上司に仕様を説明してもらいながら、
まずはコードを記述して、レビューを頂く形のタスクを振ってもらえました。


仕様を把握するのが大変でしたが、サーバーサイドのコードの変更自体は、
見本になるコードがそこかしこに書いてあるので、
なんとか対処できました。(Angularは上司につきっきりで指導してもらいましたが、、、)


「即戦力」の定義によるとは思いますが、
「入社から日が浅い段階でタスクを振られても、ある程度の成果物を提出できる」ことが
即戦力の定義とするならば、該当していると考えてもいいと思っています。









2. 即戦力になれるかどうかは会社で使う言語次第


入社してすぐにタスクを振っていただけたのも、私が経験のある言語と
会社が使っている言語が一致していたから、という理由が非常に大きいと思っています。


私の会社で使う言語は、スクールで学習していたRubyで、
サーバーサイドのフレームワークも同じくRailsです。


自分の経験のある言語と、会社で使用する言語の一致も、
即戦力エンジニアとして活躍するための条件かもしれません。









まとめ

繰り返しますが、即戦力として入社してすぐにチームにジョインしてコードを書くには、
スクールで学んでいた技術と会社で使う技術が一致していないと難しいと思いました。


TECH CAMPの求人はRubyだけでなく、javaPHPpythonなどの言語、
フレームワークcakePHPDjango、Flaskなど、言語・フレームワーク・更にはOSに拘らず紹介されます。
また、フロントエンドエンジニアとしての求人もあったりします。(React, Vue.js, Angular、Nuxt.js等)


全く触れたことのない技術を使う会社で、入社して日が浅い段階で成果を出せと言われても、
正直無理がありますよね、、、笑
算数できないのに数学で満点取って見ろと言われているようなものです、、、


なので、スクールを卒業した全ての方が、
即戦力として活躍するのは難しいのではないかと思いました。


とはいえ、スクールを卒業すればWebアプリ、通信周り、設計の技法の基礎が身につくので、
新しい言語、フレームワークの習得にはさほど時間をかけずに習得できるような素地がある状態で、
業務に入ることができるのではないでしょうか。



TECH CAMPを卒業すれば、フレームワークRailsに限れば、
即戦力エンジニアになれると思います。



ActiveModelを使ってDBと関係ないFormの構築してみた

こんにちは!kossyです!




さて、今回はActiveModelを使ったフォームの構築についてブログに残してみたいと思います。



そもそもActiveModelって??

DBと関係ないFormを構築できるスグレモノです。
Active Recordを使った時と同じ振る舞いを実現できます。

Formに関して責務の分離を行うことができるので、
ばらつきがちなロジックをまとめられたり、検索周りの機能の共通化ができたりします。





実装方法

例として、運営に問い合わせを行うようなフォームを構築してみます。

app/forms/inquiryforms.rb

class InquiryForm
  include ActiveModel::Model
  include ActiveModel::Attributes
  attr_accessor :subject, :content, :accepted

  attribute :accepted, :boolean, default: false

  validates :subject, presence: true, length: { maximum: 100 }
  validates :content, presence: true

  def save
    return false if invalid?
    ActionMailer.inquiry_form(subject, content).deliver_later
    true
  end

end




app/controllers/inquiries_controller.rb

class InquiriesController < ApplicationController

  def new
    @inquiry_form = InquiryForm.new
  end

  def create
    @inquiry_form = InquiryForm.new(inquiry_form_params)
    if @inquiry_form.save
      redirect_to home_path, notice: '問い合わせを送信しました。'
    else
      render :new
    end
  end

  private

  def inquiry_form_params
    params.require(:inquiry_form).permit(:subject, :content, :accepted)
  end

end

include ActiveModel::ModelでActiveModelの機能を利用できるようになります。
attr_accessorで使用する属性を定義します。

include ActiveModel::Attributesは属性のキャストを簡単に行えるようになる機能です。
acceptedはrailsのビューヘルパーであるcheck_boxを介して値が送られてくることを想定している(Stringでくる)ため、
booleanにキャストしたいので定義しています。

saveメソッドはバリデーションに引っかかっていないか検証し、値が正しければメールを送信しています。


応用すれば商品検索フォームや投稿フォームにも使えそうです。



参考にさせていただいたサイト
ActiveModel::Modelで簡単に検索機能追加 - Qiita
ActiveModelのベストプラクティスを考える - Lチカ開発ブログ
form objectを使ってみよう - メドピア開発者ブログ
ActiveModel::Attributes が最高すぎるんだよな。 - Qiita
ActiveModel::Model で簡単に ActiveModel の機能を利用する - happy lie, happy life

sprintfでランダムな4桁の数字を生成してみた

こんにちは!kossyです!




さて、今回はrubyのメソッドであるsprintfを使って、ランダムな4桁の数字の生成方法を
ブログに残してみたいと思います。




sprintfメソッドって?
docs.ruby-lang.org
引数にフォーマットと文字列や数値を指定すると、
フォーマットで指定した返り値を作成できるメソッド、と言う感じですかね。
詳しくはドキュメントをみてください()


ランダムな数値を生成してみよう

これで生成できます。

[1] pry(main)> sprintf("%.4d", rand(10000))
=> "9347"
[2] pry(main)> sprintf("%.4d", rand(10000))
=> "4849"
[3] pry(main)> sprintf("%.4d", rand(10000))
=> "3760"
[4] pry(main)> sprintf("%.4d", rand(10000))
=> "1695"
[5] pry(main)> sprintf("%.4d", rand(10000))
=> "0318"


使うシチュエーションがパッと思いつきませんが、、、
Railsでランダムなパスワードとか生成したい時に応用できそうな気もします。