標準入力の値を受け取る[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
改行をせずに出力する。内部的には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>
月間アーカイブ機能の作成[Rails6]
ブログには必須の月間アーカイブ機能を作ります
メソッドの作成
# 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で囲む。リクエストパラメーターなどには適用しない方がいいとのこと
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>
@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の時とほぼ同じ
月間アーカイブ的なのが出来ました
画像プレビュー機能の作成 [Rails6]
投稿前に画像を確認できるようにプレビュー機能を作りたい
プレビュー機能の作成
消した方が見栄えが良くなるので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"
また、新規投稿画面など初期画像が無い状態では不親切なのでnoimage画像を表示させます
<% if @work.image %> <%= image_tag @work.image.url, onClick: "$('.img_field').click()", class: "img_prev" %> <% else %> <%= image_pack_tag 'noimage.png' %> <% end %>
参考
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_long、wrong_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
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
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
無事テストが通りました