個人的勉強メモ置き場

プログラミングど素人のメモ置き場

標準入力の値を受け取る[Ruby] 

最近はpaizaなどでプログラミング問題を解いて遊んだりしてるのでよく使う標準入力の受け取り方をメモ

1行の受け取り

標準入力
a

line = gets
=> "a\n"
line = gets.chomp
=> "a"
1行に複数要素の受け取り

標準入力
a b c

# 配列で受け取る
line = gets.split(' ')
=> ["a", "b", "c"]
# 独立した変数で受け取る
a, b, c = gets.split
a
=> "a"
b
=> "b"
c
=> "c"

splitはデフォルトで半角スペース区切りなので(' ')を省略してもいい

複数行に1要素の受け取り

標準入力
a
b
c

line = readlines
=> ["a\n", "b\n", "c\n"]
line = readlines(chomp: true)
=> ["a", "b", "c"]
複数行に複数要素の受け取り

標準入力
a b c
d e f

lines = readlines(chomp: true).map(&:split)
=> [["a", "b", "c"], ["d", "e", "f"]]

整数で受け取る場合にはto_iで整数に変換すればいいので省略

puts print pのメモ

puts

改行を加えて出力する。内部的にto_sが呼ばれている

puts 123
123
=> nil

putsメソッド自身の戻り値はnilになる

putsは必ずnilを返します。
https://www.ruby-lang.org/ja/documentation/quickstart/


改行文字を含む文字を渡すと改行されて出力される

puts "abc\ndef"
abc
def
=> nil

配列は要素ごとに改行して出力される

puts [1, 2, 3]
1
2
3
=> nil

print

改行をせずに出力する。内部的にはto_sが呼ばれている。戻り値はnil

print 123
123=> nil

改行文字を含む文字を渡すと改行されて出力される

print "abc\ndef"
abc
def=> nil

配列はそのまま出力される

print [1, 2, 3]
[1, 2, 3]=> nil

p

改行を加えて出力する。内部的にはinspectメソッドが呼ばれている。また、戻り値はnilではなく引数のオブジェクトとなる

p 'abc'
"abc"
=> "abc"

改行文字を含む文字を渡すと改行文字のまま出力される

p "abc\ndef"
"abc\ndef"
=> "abc\ndef"

配列はそのまま出力される

p [1, 2, 3]
[1, 2, 3]
=> [1, 2, 3]

まとめ

・putsとprintはto_s、pはinspectメソッドが呼ばれている
・putsとpは改行する。printは改行しない
・pは改行文字をそのまま出力する
・putsとprintの戻り値はnil、pは引数のオブジェクト
・putsは配列を要素ごとに改行する

created_atを日本時間に変更する

デフォルトではUTC(世界標準時)で表示されてしまうためJST(日本時間)に変更する

#config/application.rb
config.time_zone = 'Asia/Tokyo'

ついでにフォーマットもいじって見やすくします

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

config/locals下にja.ymlファイルを作成し書式を指定します

ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"

I18nのlメソッドを使うことで指定書式で表示することが出来ます

<li>
  <time><%= l article.created_at %></time>
</li>

f:id:zykb:20220120054133p:plain

月間アーカイブ機能の作成[Rails6]

ブログには必須の月間アーカイブ機能を作ります

f:id:zykb:20220120045737p:plain

メソッドの作成
# Article.rb
def divide_monthly
    return Article.group("strftime('%Y%m', articles.created_at)")
                        .order(Arel.sql("strftime('%Y%m', articles.created_at) desc"))
                        .count
end


orderに直接SQLを指定するとエラーが出る(SQLインジェクション対策)のでArel.sqlで囲む。リクエストパラメーターなどには適用しない方がいいとのこと

qiita.com

PostgreSQLではstrftimeが使えないのでto_charを使います

# Article.rb
def divide_monthly
    return Article.group("to_char(created_at, 'YYYYMM')")
                        .order(Arel.sql("to_char(created_at, 'YYYYMM') desc"))
                        .count
end


view用にhelperを作成します

module ApplicationHelper
  def ymconv(yyyymm, cnt)
    yyyy = yyyymm[0,4]
    mm = yyyymm[4,2]
    yyyy + "" + mm + "月 (" + cnt + ")"
  end
end

view

<ul>
  <% @archives.each do |yyyymm, count| %>
    <li>
      <%= link_to ymconv(yyyymm, count.to_s), articles_path(year_month: yyyymm) %>
    </li>
  <% end %>
</ul>

f:id:zykb:20220120045744p:plain

@archivesはこのような形になってるので3回ブロック内が繰り返されます

controller

def index
  @archives = Article.divide_monthly
  if params[:year_month]
    @yearmonth = params[:year_month]
    @articles = Article.where("to_char(created_at, 'YYYYMM') = '"+@yearmonth+"'").page(params[:page])
  else
    @articles = Article.recent.page(params[:page])
  end
end


params[:year_month]の有無でindexの内容を変えています。categoryの時とほぼ同じ

zykbgame.hateblo.jp

月間アーカイブ的なのが出来ました

f:id:zykb:20220120051200g:plain

画像プレビュー機能の作成 [Rails6]

投稿前に画像を確認できるようにプレビュー機能を作りたい

プレビュー機能の作成
f:id:zykb:20220110214044p:plain
元の状態

消した方が見栄えが良くなるのでfile_fieldを非表示に

<%= f.file_field :image, class: "img_field", style: 'display:none;' %>


プレビュー画像をクリックしたらファイルを開けるようにします

<%= image_tag @work.image.url, onClick: "$('.img_field').click()", class: "img_prev" %>


ファイルを変更したらプレビュー画像が切り替わるようにします

// preview.js
$(function() {
  function readURL(input) {
      if (input.files && input.files[0]) {
      var reader = new FileReader();
      reader.onload = function (e) {
  $('.img_prev').attr('src', e.target.result);
      }
      reader.readAsDataURL(input.files[0]);
      }
  }
  $(document).on('change', '.img_field', function(){
    readURL(this);
  });
});
// applocation.jsに追加
import "../javascripts/preview"

f:id:zykb:20220110204801g:plain


また、新規投稿画面など初期画像が無い状態では不親切なのでnoimage画像を表示させます

<% if @work.image %>
  <%= image_tag @work.image.url, onClick: "$('.img_field').click()", class: "img_prev" %>
<% else %>
  <%= image_pack_tag 'noimage.png' %>
<% end %>
f:id:zykb:20220110215114p:plain
noimage


参考

qiita.com

ActiveRecord バリデーションメモ

久しぶりに触ったのでメモ代わり

バリデーションとは

バリデーションは、正しいデータだけをデータベースに保存するために行われます。
Active Record バリデーション - Railsガイド

バリデーションを定義することでデータベース保存前に不正なデータや無効なデータを検証して登録されることを防ぐことが出来る

基本的な書き方
validates カラム名, バリデーションヘルパー
よく使いそうなバリデーションヘルパー

・length

属性の長さを検証するヘルパー

class User < ApllicationRecord
  # 名前は20字まで
  validates :name, length: { maximum: 20 }
  # パスワードは8文字以上20字以内
  validates :password, length: { in: 8..20 }
end


オプション

  • minimum 値の下限
  • maximum 値の上限
  • in または within 値の範囲
  • is 同値

エラーメッセージはそれぞれ:too_short:too_longwrong_lengthでカスタマイズできる


・presence

属性が空でないことを検証するヘルパー

class User < ApllicationRecord
  validates :name, presence: true
end


内部ではblank?メソッドを使っているため半角スペースなどもチェックできる


・numericality

属性に数値のみが使われてることを検証するヘルパー

class User < ApllicationRecord
  validates :age, numericality: { only_integer: true }
end

:only_integerをtrueにすると整数のみ、デフォルトでは整数または浮動小数点にマッチ


・uniqueness

属性が一意で重複してないことを検証するヘルパー

class User < ApllicationRecord
  validates :email, uniqueness: true
end


一意制チェックの範囲を限定する:scopeと大文字小文字を区別するcase_sensitiveオプションがある


共通に使えるバリデーションオプション

・ allow_nil

nilの場合にバリデーションをスキップ

class User < ApllicationRecord
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end


・allow_blank

nilだけではなく空文字などの場合にもバリデーションをスキップ

class User < ApllicationRecord
  validates :age, numericality: { only_integer: true }, allow_blank: true
end


・message

バリデーション失敗時にerrorsコレクションに追加されるカスタムメッセージを指定

class User < ApllicationRecord
  validates :name, length: { maximum: 20, mesaage: "20字以内で入力してください"} 
end


・on

バリデーションの実行タイミングを指定。デフォルトではレコードの作成時及び更新時

class User < ApllicationRecord
  validates :email, uniqueness: true, on: :create
end


参考
railsguides.jp

RSpecでActionTextのテストをする

ActionTextの導入はこちら
zykbgame.hateblo.jp

テストを書く
# 省略
describe '記事の新規投稿' do
  before do
    # 投稿ページに移動
    visit new_article_path
  end
  context '入力値が正常なとき' do
    before do
      fill_in 'article_title', with: 'テスト'
      fill_in 'article_content', with: 'テストです'
      click_button '記事の作成'
    end
    it '記事の新規投稿に成功' do
      # 省略
    end
  end
end

と書くと以下のようなエラー

Failure/Error: fill_in 'article_content', with: 'テストです'
Capybara::ElementNotFound:
  Unable to find field "article_content" that is not disabled
f:id:zykb:20220104173345p:plain
失敗した時のスクショ

contentはtext_areaではなくrich_text_areaで参照しているため通常のfill_inでは認識されないご様子

<div>
  <%= f.label :content %>
  <%= f.rich_text_area :content %>
</div>

こんな時のためにActionText::SystemTestHelperにfill_in_rich_text_areaが用意されてるので使えるようにします

# spec/rails_helper.rb

# 追加
require "action_text/system_test_helper"

RSpec.configure do |config|
  # 追加
  config.include ActionText::SystemTestHelper, type: :system
end
fill_in 'article_title', with: 'テスト'
# 変更
fill_in_rich_text_area 'article_content', with: 'テストです'
click_button '記事の作成'

再度テストします

Finished in 4.91 seconds (files took 2.22 seconds to load)
1 examples, 0 failures

無事テストが通りました


参考
edgeapi.rubyonrails.org

stackoverflow.com