20210306のRubyに関する記事は25件です。

名前空間付きのモデルの関連付けでハマった話

背景

schoolモデルにcoachを持たせるのでcoachモデルのインスタンスを作成する際に「new」ではなく「build」に置き換えようとしたときの話。

前提

筆者

  • railsを初めて触って一ヶ月の初心者。
  • コードを書いているがなぜ動いているのかあまり分かっていない。
  • 間違いがあれば指摘していただけると幸いです。

開発環境

  • 端末:macbook pro
  • OS:macOS Big Sur 11.2.2
  • ruby:2.7.2
  • rails:6.1.3

schoolユーザーでログインしているアカウントのみcoachを作成できるように。

そのためにshoolsのnamespace内にcoachを入れようと考えました。なので、これらのモデルを作成する際にはscaffoldを使って以下のように作成。

rails g scaffold schools/coaches name:string

モデル同士を関連付ける。

school.rb
class School < ApplicationRecord
has_many :schools_coaches
end
schools/coach.rb
class Schools::coach < ApplicationRecord
belongs_to :school
end

newをbuildに置き換える。

coaches_controller.rb
def new
    @schools_coach = current_school.schools_coaches.build
end

これではうまく行きませんでした。
エラー uninitialized constant School::SchoolsCoach
と怒られてしまいます。

原因はテーブル名

どうやらテーブル名がschools_coachesとなっていたことが原因なようです。
has_many :schools_coachesとするとSchoolsCoachモデルを探しに行ってしまうことが原因のようです。
今回はテーブル名の方を変更したが、探しに行くモデルを変更することも可能。詳しくは「他の解決策

訂正

school.rb
class School < ApplicationRecord
has_many :coaches
end
coach.rb
class Coach < ApplicationRecord
belongs_to :school
end
coaches_controller.rb
def new
    @schools_coach = current_school.schools_coaches.build
end

モデル名を変更したことでviewも変更しなければならなかった。

_form.html.erb
<%= form_with(model: @schools_coach, url: schools_coaches_path) do |form| %>

modelのみ指定していたが、それだとform_withさんはCoachモデルをみて'coach/new'や'coach/1/edit'のパスだと勘違いしてしまうのでurlでパスを指定してあげる。
これでうまく動いた。

他の解決策

他の解決策もたくさんあるみたいです。
has_manyにclassnameを追記

追記 2020.03.07
このやり方でやってみましたが、こちらの方がテーブル名やviewを変更したりということがないためおすすめ

まとめ

モデル名はできるだけnamespaceをつけない方がややこしくないかも。
つける場合はclassnameをつけるなどの対策が必要。

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

AWSへデプロイ後に、暗号化したパスワードでログインできない現象について

AWSへデプロイ後に、bcryptで暗号化したパスワードでログインできない現象について

プログラミング初学者です。Ruby on Railsで作成したアプリをAWSのEC2インスタンスにデプロイした後で、作成したアカウントにログインできない現象が発生しました。パスワード周りのエラーの解消に丸一日かかってしまいましたので、また同じ現象で困らないためにもこの記事にまとめておきたいと思います。

発生した環境

Rubyバージョン:2.5.8
Railsバージョン:6.1.1
gem bcryptバージョン:3.1.16

発生した現象

デプロイ後に新規登録したアカウントに、始めはログインできていたのですが、しばらく経ってからログインしようとすると「メールアドレスかパスワードに誤りがあります」と表示されログインできなくなりました。

試したこと

ログを確認

$ vim log/production.log

いくつかのエラーログを確認しましたので、順に確認していきました。

Mysql2::Error

ActiveRecord::StatementInvalid (Mysql2::Error: Unknown column 'password_digest' in 'field list'):

bcryptでパスワードの暗号化を行なった際に、'password_digest'カラムをデータベースのusersテーブルに追加していました。mysqlにログインしたところ、確かに'password_digest'カラムは存在して値が保存されていました。

ArgumentError

ArgumentError (wrong number of arguments (given 0, expected 1)):
app/controllers/users_controller.rb:41:in `login'

値が入るはずの場所に値が入っていないことが確認できました。
該当の場所には下記コードがありました。

app/controllers/users_controller.rb
    if @user && @user.authenticate(params[:password])

どうやら入力したパスワードが正常に認識されていないことがわかりました。

NoMethodError

NoMethodError (undefined method `encrypted_password=' for #User:0x000000000572e6d8):

'encrypted_password'は何のことかとわからなかったので、検索するとDeviseを導入した時に追加されるという情報がありました。確認すると、確かにGemfileに「gem 'devise'」を追加していました。そこで、encrypted_passwordを利用したパスワードの暗号化に切り替えることにしました。

password_digestからencrypted_passwordへの切り替え

まずmysqlでusersテーブルのカラム名を変更します。

mysql> alter table users change column password_digest encrypted_password char(255);

user.rbを編集します。

app/models/user.rb
class User < ApplicationRecord
  has_secure_password #ここを削除します
  validates :name, {presence: true}
  validates :email, {presence: true, uniqueness: true}
  validates :image_name, {presence: true}
  validates :password, {presence: true} #ここも不要になるので削除します

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable
end

users_controller.rbを編集します。
エラーが発生していた、ログイン認証の行の認証メソッドを書き換えます。

app/controllers/users_controller.rb
    if @user && @user.valid_password?(params[:password]

パスワードを変更する部分が少々手こずりました。
params[:password]に保存されている新しいパスワードを手動で暗号化して更新しています。

app/controllers/users_controller.rb
    if params[:password]
      hashed_password = BCrypt::Password.create(params[:password])
      update_password_sql = "update users set encrypted_password = '#{hashed_password}' where id =#{@current_user.id};"
      ActiveRecord::Base.connection.execute(update_password_sql)
    end

コードの編集が終わったらサーバーを再起動します。
nginxを再起動します。

$ sudo service nginx restart

続いてunicornを再起動します。unicornで走っているスレッドを確認します。

$ ps -ef | grep unicorn | grep -v grep

3行の走っているスレッド番号が表示されます。
下は一行目の一例です。

ユーザー名    番号     1  0 11:16 ?        00:00:00 unicorn_rails master -c /var/www/アプリのディレクトリ/config/unicorn.conf.rb -D -E production
…

スレッドを停止します。

$ kill 番号

もう一度上記の確認コマンドを打ち込んで何も表示されなければ、スレッドが終了しています。
unicornを起動します。

$ bundle exec unicorn_rails -c /var/www/アプリのディレクトリ/config/unicorn.conf.rb -D -E production

確認コマンドを打ち込んでスレッド番号が表示されれば、再起動できています。

まとめ

丸一日かかってしまったので、思い出せる限り全部の工程をまとめました。

某プログラミング学習サイトでパスワードの暗号化を習い、簡単にできるものだと思っていたら思わぬ落とし穴がありました。Twitter連携などに使用するDeviseを導入することで、パスワード暗号化の機能が重複してエラーを発生するようです。簡単に習っただけでは実践には足りない、実際にエラーに遭遇することで腕を磨くものだと、今後の良い戒めになりました。

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

rails webpacker:install node_modules/@rails/webpacker/node_modules/node-sass: Command failed.

この記事について

rails webpacker:installを実行しようとすると以下のエラーが発生(node-sass部分)。
解決できたので備忘録投稿

$ rails webpacker:install                                                                                                                                                  e

なんやかんや。。。

warning "webpack-dev-server > webpack-dev-middleware@3.7.3" has unmet peer dependency "webpack@^4.0.0 || ^5.0.0".
[4/4] ?  Building fresh packages...
[-/2] ⠐ waiting...
error /Users/ketchamash/hello_rails/node_modules/@rails/webpacker/node_modules/node-sass: Command failed.
Exit code: 1

環境

Mac Catalina
ruby 2.6.3
rails 6.0.3

解決方法 & 参考記事

色々調べていると、nodejsのバージョンもしくはnode-sassバージョンに原因があることは分かった。どのバージョン同士でやったらええねん状態でしたが、こちらの記事でうまいこといきました。

nodejsとnode-sassのバージョンの対応表

nodejs -v node-sassのサポート -v
14 4.14+
13 4.13+, <5.0
12 4.12+
11 4.10+, <5.0
10 4.9+

実行コマンド

これでちゃんとインストールできやした〜。

nodebrewはNode.jsのバージョンを管理するためのツール

$ nodebrew install v14.15.0

$ nodebrew use v14.15.0

$ yarn add node-sass@4.14.0

$ rails webpacker:install

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

[Rails] strftime vs I18n.localize vs to_s(:Time::DATE_FORMATS) どれを使うべきか?

結論

I18n.localizeがオススメ

理由

  • 日時表示専用のメソッドだから(= スコープが小さく、オーバーライドしやすいから )

strftimeがダメな理由

  • undefined method strftime for nil:NilClassエラーを回避するために無闇に&をつけるから
  • 日時を表示するのに長ったらしいメソッド名・時刻フォーマットを書くのはめんどくさいから

to_s(:Time::DATE_FORMATS)がダメな理由

  • スコープが広く、オーバーライドできない(すべきでない)から

to_snilを渡すと空文字を返す。

$ nil.to_s
=> ""

しかし、to_s(:Time::DATE_FORMATS)にするとArgumentErrorが発生する。

$ Time::DATE_FORMATS[:sample_format] = "%Y年%m月%d日"
=> "%Y年%m月%d日"

$ Time.now.to_s(:sample_format)
=> "2021年01月01日"

$ nil.to_s(:sample_format)
=> ArgumentError: wrong number of arguments (given 1, expected 0)

to_sで時刻フォーマットを指定できるが、nilを渡してもエラーを吐かないto_sの使いやすさがなくなった。
to_s&を毎回書くのは美しくないし、strftimeとやってる事が変わらなくなる。

I18n.localizenilが渡るとエラーが起きる。

config/locales/ja.yml
ja:
  time:
    formats:
      default: "%Y年%m月%d日"
$ I18n.l(Time.now)
=> "2021年01月01日"

$ I18n.l(nil)
=> I18n::ArgumentError: Object must be a Date, DateTime or Time object. nil given.

なので

  1. 諦めて永遠と&を使い続ける(論外)
  2. begin rescueでエラーをキャッチする(論外)
  3. オーバーライドしてnilをパスさせるか

どれかの対応が必要になる。
1は論外。美しくない。
2も論外。毎回begin resucueするのはダルい。美しくない。
必然的に3で対応することになるが、to_sは影響範囲が広く懸念が大きい。
なので、日時表示専用メソッドでスコープが小さく影響範囲が狭いI18n.localizeをオーバーライドするのがオススメ
オーバーライドの方法は、以下を参考にしてほしい。
http://hamasyou.com/blog/2014/02/19/rails-i18n-localize/

この記事を書こうと思った経緯と所感

I18n.localizeが日時表示用のメソッドならば、これ使えばいいんじゃね?」

って思ってました。
しかし、デフォルトではnilをパスできないと知って、他のメソッドを調べました。
その際に、to_sメソッドの引数に時刻フォーマットを設定すれば日時を表示できると知りました(to_sメソッド万能スギィィ!!!
しかし、

to_sでフォーマットを指定できるが、nilを渡してもエラーを吐かないto_sの使いやすさがなくなりました。。。

&でエラー回避できますが、strftimeとやってることは変わらないし、コードが美しくないと思いました。
このように、コードの保守性とメソッドスコープの観点からI18n.localizeをオーバーライドするのがベストだと考えました。

参考文献

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

【Rspec】requests spec・コントローラーのテストについて〜adminユーザー作成やeditアクションのテスト〜

requests spec・コントローラーのテストについての話

モデルと結合テストは問題なく終わり、コントローラーのテストについてあれこれ調べていたがなかなか自分に理解できるものがなく、困っていました。
公開されているGitHubのコードを見てもコントローラーのテストコードをやっている人が少なかったというのも一つの要因なのですが、試行錯誤してなんとか完成しました。
正直これでいいのかちょっとわからない部分もあるので、もし知見がある方がこの記事を見て下さったらコメント頂けると嬉しいです。

adminユーザーの生成で困ったこと

DBにadminカラムを作ってtruefalseかで判別しています。
FactoryBotでユーザーを生成するために書いたコードが下記のものです。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    nickname              { '野比のび太' }
    sequence(:email)      { |n| "tester#{n}@example.com" }
    password              { 'q11111' }
    password_confirmation { password }
    profile               { 'なんとかしてよドラえもん' }
    admin                 { false }    ⬅️一般ユーザー
  end

  factory :admin_user do
    nickname              { 'ドラえもん' }
    sequence(:email)      { |n| "tester#{n}@example.com" }
    password              { 'a11111' }
    password_confirmation { password }
    profile               { 'いつまでも子供じゃないんだよしっかりしろよ' }
    admin                 { true }     ⬅️adminユーザー
  end
end

そしてusers_spec.rbには以下のように記述。

spec/requests/users_spec.rb
RSpec.describe UsersController, type: :request do
  let(:user) { FactoryBot.create(:user) }
  let(:admin_user) { FactoryBot.create(:user, admin: true) }
 #let(:admin_user) { FactoryBot.create(:admin_user) }⬅️この書き方でもダメでした

これでadminユーザーが作成されるはずだと思っていたのですがbinding.pryで止めて中身を見るとadmin_userが作成されていませんでした。
色々書き方を変えてやってみたところ、letを使わずにbefore doにしたらうまくいきました。

spec/requests/users_spec.rb
RSpec.describe UsersController, type: :request do
  before do
    @user = FactoryBot.create(:user)
    @admin_user = FactoryBot.create(:user, admin: true)
   #@admin_user = FactoryBot.create(:admin_user)⬅️この書き方でもダメでした
  end

3c9fb0574c103d376e614707e02c1126.png

ひとまずこれで一般ユーザーとadminユーザーを分けることができるようになりました。

editアクションのテストで困ったこと

コントローラーのテストをする上でまず困ったのがeditアクションのテストでした。
authenticate_user!を使用しているためコントローラーのテスト実行時に、
「ログインしていないユーザー」とみなされてしまってリダイレクトされてしまうのです。
リクエストが正常のレスポンスではなくなってしまうと、テスト項目の
expect(response.status).to eq 200
ここの200が302になってしまいます。

また、編集できる時とできない時、両方のテストをしなければいけないため、コントローラーのテストコード上でログインをさせることができれば正常な返り値が得られると考えました。

deviseのメソッドをテストコード上でも使えるようにするには

テストコード上でログインさせるメソッドとして結合テストコード時に使っていたmoduleでやってみたところ、うまくいきませんでした。
調べた結果、コントローラーのテストではdeviseのメソッドを使用できるとのことだったのでそちらに切り替えました。

spec/rails_helper.rb
RSpec.configure do |config|
  #deviseのsign_inメソッドが使えるようになる
  config.include Devise::Test::IntegrationHelpers, type: :request
end

これでsign_inというメソッドが使用できるようになりました。
自分の場合は@userと定義しているのでそのまま使います。

spec/requests/users_spec.rb
require 'rails_helper'
RSpec.describe UsersController, type: :request do
  before do
    @user = FactoryBot.create(:user)
  end

  describe 'GET #edit' do
    context 'userがログインしているとき' do
      before do
        sign_in @user  ⬅️ここでログイン
      end
      it 'editアクションにリクエストすると正常にレスポンスが返ってくる' do
        get edit_user_path(@user)
        expect(response.status).to eq 200  ⬅️編集画面に遷移できている
      end
      it "editアクションにリクエストするとレスポンスに登録済みuserの名前が存在する" do 
        get edit_user_path(@user)
        expect(response.body).to include @user.nickname
      end
      it "editアクションにリクエストするとレスポンスに登録済みuserのプロフィールが存在する" do 
        get edit_user_path(@user)
        expect(response.body).to include @user.profile
      end
    end

    context 'userがログインしていないとき' do
      it 'editアクションにリクエストすると正常にレスポンスが返ってくる' do
        get edit_user_path(@user)
        expect(response.status).to eq 302  ⬅️トップページにリダイレクトしている
      end
    end
  end
end

これでうまくいきました。
admin_userにしたいときはbefore doで@admin_userを定義して、
sign_in @admin_userとすることで他のコントローラーでも問題なく処理ができました。
テストコードをやるとアプリケーションの仕様がよく理解できると言いますが、理由がよくわかりました。
細かいところに気がついたり、仕様そのものを一から見直す結果になって理解が深まったと実感できました。

参考にさせていただきました

@iwkmsy9618
[Rails]Request Specでのログインの実施

ありがとうございました。

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

gem ffi 1.14.2 エラーの解決方法 そのn番目

この記事について

bundle install実行時にffi 1.14.2のところでこけてしまう。

環境

  • Mac Catalina
  • ruby 2.6.3
  • rails 6.0.3

エラー内容

Fetching ffi 1.14.2
Installing ffi 1.14.2 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

current directory:
/Users/ketchamash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/ffi-1.14.2/ext/ffi_c
/Users/ketchamash/.rbenv/versions/2.6.3/bin/ruby -I
/Users/ketchamash/.rbenv/versions/2.6.3/lib/ruby/2.6.0 -r ./siteconf20210306-53720-1m14ww7.rb
extconf.rb
checking for ffi_prep_closure_loc() in -lffi... yes
checking for ffi_prep_cif_var()... yes
checking for ffi_raw_call()... yes
checking for ffi_prep_raw_closure()... yes
creating extconf.h
creating Makefile

current directory:
/Users/ketchamash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/ffi-1.14.2/ext/ffi_c
make "DESTDIR=" clean

current directory:
/Users/ketchamash/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/ffi-1.14.2/ext/ffi_c
make "DESTDIR="
compiling AbstractMemory.c
compiling ArrayType.c
compiling Buffer.c
compiling Call.c
compiling ClosurePool.c
compiling DynamicLibrary.c
compiling Function.c
Function.c:847:17: error: implicit declaration of function 'ffi_prep_closure_loc' is invalid in
C99 [-Werror,-Wimplicit-function-declaration]
ffiStatus = ffi_prep_closure_loc(closure->pcl, &fnInfo->ffi_cif, callback_invoke, closure,
code);
                ^
Function.c:847:17: note: did you mean 'ffi_prep_closure'?
/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/ffi/ffi.h:269:1: note:
'ffi_prep_closure' declared here
ffi_prep_closure(
^
1 error generated.
make: *** [Function.o] Error 1

make failed, exit code 2

なんやかんや

An error occurred while installing ffi (1.14.2), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.14.2' --source 'https://rubygems.org/'` succeeds before
bundling.

In Gemfile:
  spring-watcher-listen was resolved to 2.0.1, which depends on
    listen was resolved to 3.4.1, which depends on
      rb-inotify was resolved to 0.10.1, which depends on
        ffi

解決のために試したこと

1.gem install ffi -v '1.14.2を実行

2.Qiitaで参考にさせて頂いた記事
自分の場合、こちらでは解決できませんでした。

解決できた参考ページ

gemのインストール時に--disable-system-libffiオプションをつけて実行するとインストールできた。

 gem install ffi -- --disable-system-libffi

解決できた理由?

libffiライブラリが上手く使うためのオプションなのかな?

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

RailsでQRコードを生成

前提条件

・ruby 2.6.6
・rails 6.0.3.4
・macOS Catalina バージョン10.15.7

概要

 webアプリ内で一般的な会員システムに搭載されているmyQRコードを作成したいと思い、実装したので忘れないようここに残すことにした。
 難しいことは特になく、比較的簡単に実装が完了した。

gemの導入

 webアプリ内にQRコードを生成させるためには
・gem 'rqrcode'
・gem 'chunky_png'
 の2つを導入する必要がある。

Gemfile
gem 'rqrcode'
gem 'chunky_png'
ターミナル
bundle

これで前準備は完了。
次にコードを書いていく。

QRコードを生成させる

 今回は個人的な理由だが、各ユーザーのマイページに設置したいため、user_helper.rbにコードを記述する。

app/helpers/users_helper.rb
module UsersHelper
#ここから
require 'chunky_png'

  def qrcode_tag(url, _options = {})
    qr = ::RQRCode::QRCode.new(url)
    ChunkyPNG::Image.from_datastream(qr.as_png.resize(250, 250).to_datastream).to_data_url
  end
#ここまで
end

 引数でurlを渡すことで、pngを生成する。
 また注意して欲しいのが、gemを導入しているときはターミナルでwebアプリを再起動してからじゃないと、エラーが起きてうまく作動しないので注意。(自分も忘れていて何でエラーが起きているかわからなかった。)

Viewに表示させる

 QRコード生成の準備ができたので、ViewでQRコードを表示させるコードを書いていく。自分の場合は何度もいうがマイページに設置したいので users/show.html.erb に記述していく。

users/show.html.erb
<%= image_tag qrcode_tag url %>

とは言っても表示させたい場所に上記のコードを記述するだけで完了。
urlの部分に、生成されるQRコードを読み取ったときに遷移させるページのURLを記述すれば、読み込んだときにそのページに遷移するQRコードが生成される。以上。

参考文献

・QRコードの生成・埋め込み
 https://qiita.com/koteko/items/d6d033997c544c47b718
https://github.com/whomwah/rqrcode
https://github.com/wvanbergen/chunky_png

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

RailsにAjaxでデータ保存する時に困ったこと

記事の目的

Railsでオリジナルアプリを作成中にAjaxでデータ保存出来ずに困ったこと、どのように解決したのかを忘れないために書いておきます。

開発環境

  • macOS Big Sur 11.1
  • VScode
  • Ruby 2.6.5
  • Rails 6.0.3.4
  • mysql 14.14
  • JavaScript
  • gem 3.0.3
  • heroku 7.47.12

オリジナル読書アプリ

読書アプリケーションを作りました。
https://github.com/hiro-mu/book_smart.git

困ったこと 

実装したのは読書アプリによくあるようなドラッグした部分の背景色が変わる機能です。
この機能の実装は
1. 文字列がドラッグされた場合にイベントが発生し、その文字列をデータベースに保存
2. データベースに存在する文字列が表示されている場合、replaceメソッドでブラウザ上のHTMLに背景色を変えるCSSを追加
と言う流れで行っています。
難しかったところはこの部分です。

highlight.js
XHR.open("GET", `/highlights/create?book_id=${bookId}&pagenum=${pageNum}&text=${selectedStr}`, true);

本来であればデータベースに変更を加えるリクエストなので、POSTを使うべきです。しかし、保守性の観点からRailsに外部からのデータベースを変更させるPOSTやPUTのリクエストは受け付けないという機能があるらしく、データベースに保存出来ませんでした。(厳密にはRailsはDELETE、PATCH、PUTのHTTPメソッドは全てPOSTとして扱うようです)
ようやく見つけた解決策は、HTTPメソッドはGETにしておいて、パラメータをURLに含めて送ってしまおうという手法でした・・・もっといい解決策があれば知りたいです。

学んだこと

CSRFなどの不正なリクエストのためにRailsに備わっているセキュリティ対策によって自身が苦しめられました。非常に勉強になりました。

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

Ruby で解けず Java と Crystal で解く AtCoder ABC 189 C

はじめに

AtCoder Problems の Recommendation を利用して、過去の問題を解いています。
AtCoder さん、AtCoder Problems さん、ありがとうございます。

今回のお題

AtCoder Beginner Contest C - Mandarin Orange
Difficulty: 565

今回のテーマ、トランスパイラ

コンテスト中にRubyのコンテスタントが7名しか解けなかった問題です。
実行時間制限: 1.5 sec と厳しいのですが、Difficultyは茶色でした。

Java

java.java
import java.util.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.next());
        int a[] = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = Integer.parseInt(sc.next());
        }
        sc.close();
        long ans = 0;
        long max = 0;
        for (int i = 0; i < n; i++) {
            long min = 1000000000;
            for (int j = i; j < n; j++) {
                if (min > a[j]) {
                    min = a[j];
                }
                if (max < min * (j - i + 1)) {
                    max = min * (j - i + 1);
                }
            }
            if (ans < max) {
                ans = max;
            }
        }
        System.out.println(ans);
    }
}

コンテスト中にRubyからJavaに切り替えて解答しました。
コードとして捻りも何もないのですが、短時間では脳が付いていかないです。

Crystal

Crystal.cr
n = gets.to_s.to_i
a = gets.to_s.split.map { |c| c.to_i }
cnt = 0
max = 0
n.times do |i|
  min = 10**9
  i.upto(n - 1) do |j|
    min = a[j] if min > a[j]
    max = min * (j - i + 1) if max < min * (j - i + 1)
  end
  cnt = max if cnt < max
end
puts cnt

このコードはRubyでもそのまま動作します。
Windows10Crystalが動作すれば代替してもいいのですが。

Ruby Java Crystal
コード長 (Byte) 265 831 265
実行時間 (ms) TLE 268 76
メモリ (KB) 14816 43072 3860

まとめ

  • ABC 189 C を解いた
  • Crystal に詳しくなった
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】「simple_form」と「FormObject」を使って、複数テーブルの情報を同一フォームで保存

はじめに

現在Railsで写真投稿サービスの実装をしています。
一つのフォームで複数のテーブル情報を保存したいときに、accepts_nested_attributes_forというメソッドが用意されますが、あまり推奨されていないみたいです。(以下参考

そこで今回は、simple_formFormObjectを使ってフォームを作成しました。
自分のようなユースケースの記事がなかったので、参考になればと思い記事にします。

動作環境

Ruby '2.6.6'
Rails '6.0.3.5'
SimpleForm '5.1.0'
CarrierWave '2.1.1'
MiniMagick '4.11.0'

テーブル

スクリーンショット 2021-03-04 18.41.28.png

  • Post登録時にpost_images(子テーブル)post_categories(中間テーブル)の情報も同時に保存されるフォーム

モデル

app/models/post.rb
class Post < ApplicationRecord
  has_many :post_categories
  has_many :categories, through: :post_categories
  has_many :post_images, dependent: :destroy
  belongs_to :user
end

実装したコード

view(投稿フォーム)

app/views/posts/_form.html.slim
.box.box-primary
  = simple_form_for @form, url: posts_path do |f|
    .box-body
      = f.input :image, as: :file
      = f.input :caption

      = f.input :body, as: :text

      = f.input :categories, as: :check_boxes, collection: Category.all, include_hidden: false

    .box-footer
      = f.button :submit, '投稿する', class: %w[btn btn-primary]
  • simple_form_forの引数にはControllerで定義した変数をセットし、urlを指定
  • 複数選択できるcategoriesではinclude_hidden: falseを指定することで、配列に空のデータが入らないようにしている

controller

app/controllers/posts_controller.rb
  def new
    @form = PostsForm.new
  end

  def create
    @form = PostsForm.new(post_params)

    if @form.save
      redirect_to posts_path, success: t('defaults.message.created', item: Post.model_name.human)
    else
      flash.now['danger'] = t('defaults.message.not_created', item: Post.model_name.human)
      render :new
    end
  end

  private

  def post_params
    params.require(:posts_form).permit(
      :body,
      :image,
      :caption,
      { categories: [] }
    ).merge(user_id: current_user.id)
  end
  • アクションのnewcreateではFormObject名で変数を定義
  • ストロングパラメータのrequireキーもFormObject名で指定
  • 今回はcategoriesカラムのみ配列で受け取れる形になっている

FormObject

app/forms/posts_form.rb
class PostsForm
  include ActiveModel::Model
  include ActiveModel::Attributes
  extend CarrierWave::Mount

  mount_uploader :image, PostImageUploader

  attribute :body, :string
  attribute :image, :string
  attribute :caption, :string
  attribute :categories
  attribute :user_id, :integer

  validates :image, :categories, presence: :true

  def initialize(params = {})
    super(params)
  end

  def save
    return false if invalid?

    post = Post.new(post_params)

    # 画像の複数登録仕様にはなっていない
    post.post_images.build(post_images_params).save!

    categories.each do |category|
      post.post_categories.build(category_id: category).save!
    end

    post.save ? true : false
  end

  private

  def post_params
    {
      body: body,
      user_id: user_id
    }
  end

  def post_images_params
    {
      image: image,
      caption: caption
    }
  end
end
  • include ActiveModel::Attributesによってattributeの作成が簡単に
  • バリデーションもモデルではなくFormObjectに記述
  • initializeで値を初期化
  • コントローラ側からセットされたデータを、saveメソッド内で複数のテーブルに保存
  • 配列の受け取りには注意が必要で、each文で一つ一つ登録されるように記述

終わりに

たくさんのエラーに詰まりながら、最終的にこの記述で落ち着きました。
複雑なフォームも、できあがってみたらシンプルでスッキリできたと思います。
もし改善した方がいい点などあればコメントで教えてください。

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

【Rails】「simple_form」と「FormObject」を使って、複数テーブルの情報を同時保存

はじめに

現在Railsで写真投稿サービスの実装をしています。
一つのフォームで複数のテーブル情報を保存したいときに、accepts_nested_attributes_forというメソッドが用意されますが、あまり推奨されていないみたいです。(以下参考

そこで今回は、simple_formFormObjectを使ってフォームを作成しました。
自分のようなユースケースの記事がなかったので、参考になればと思い記事にします。

動作環境

Ruby '2.6.6'
Rails '6.0.3.5'
SimpleForm '5.1.0'
CarrierWave '2.1.1'
MiniMagick '4.11.0'

テーブル

スクリーンショット 2021-03-04 18.41.28.png

  • Post登録時にpost_images(子テーブル)post_categories(中間テーブル)の情報も同時に保存されるフォーム

モデル

app/models/post.rb
class Post < ApplicationRecord
  has_many :post_categories
  has_many :categories, through: :post_categories
  has_many :post_images, dependent: :destroy
  belongs_to :user
end

実装したコード

view(投稿フォーム)

app/views/posts/_form.html.slim
.box.box-primary
  = simple_form_for @form, url: posts_path do |f|
    .box-body
      = f.input :image, as: :file
      = f.input :caption

      = f.input :body, as: :text

      = f.input :categories, as: :check_boxes, collection: Category.all, include_hidden: false

    .box-footer
      = f.button :submit, '投稿する', class: %w[btn btn-primary]
  • simple_form_forの引数にはControllerで定義した変数をセットし、urlを指定
  • 複数選択できるcategoriesではinclude_hidden: falseを指定することで、配列に空のデータが入らないようにしている

controller

app/controllers/posts_controller.rb
  def new
    @form = PostsForm.new
  end

  def create
    @form = PostsForm.new(post_params)

    if @form.save
      redirect_to posts_path, success: t('defaults.message.created', item: Post.model_name.human)
    else
      flash.now['danger'] = t('defaults.message.not_created', item: Post.model_name.human)
      render :new
    end
  end

  private

  def post_params
    params.require(:posts_form).permit(
      :body,
      :image,
      :caption,
      { categories: [] }
    ).merge(user_id: current_user.id)
  end
  • アクションのnewcreateではFormObject名で変数を定義
  • ストロングパラメータのrequireキーもFormObject名で指定
  • 今回はcategoriesカラムのみ配列で受け取れる形になっている

FormObject

app/forms/posts_form.rb
class PostsForm
  include ActiveModel::Model
  include ActiveModel::Attributes
  extend CarrierWave::Mount

  mount_uploader :image, PostImageUploader

  attribute :body, :string
  attribute :image, :string
  attribute :caption, :string
  attribute :categories
  attribute :user_id, :integer

  validates :image, :categories, presence: :true

  def initialize(params = {})
    super(params)
  end

  def save
    return false if invalid?

    post = Post.new(post_params)

    # 画像の複数登録仕様にはなっていない
    post.post_images.build(post_images_params).save!

    categories.each do |category|
      post.post_categories.build(category_id: category).save!
    end

    post.save ? true : false
  end

  private

  def post_params
    {
      body: body,
      user_id: user_id
    }
  end

  def post_images_params
    {
      image: image,
      caption: caption
    }
  end
end
  • include ActiveModel::Attributesによってattributeの作成が簡単に
  • バリデーションもモデルではなくFormObjectに記述
  • initializeで値を初期化
  • コントローラ側からセットされたデータを、saveメソッド内で複数のテーブルに保存
  • 配列の受け取りには注意が必要で、each文で一つ一つ登録されるように記述

終わりに

たくさんのエラーに詰まりながら、最終的にこの記述で落ち着きました。
複雑なフォームも、できあがってみたらシンプルでスッキリできたと思います。
もし改善した方がいい点などあればコメントで教えてください。

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

【Workato】便利なformulaの使い方やデータ操作一覧【随時更新】

Workatoにおけるデータ操作

Workatoではformulaモードを使うことでExcelの関数のような感覚でデータの前処理や変換が可能である。
もし、さらに複雑なデータ変換や処理をしたい場合、「Ruby」コネクターや「JavaScript」コネクターが用意されているので、レシピの中にコードを埋め込むことも可能だ。(滅多に使うことはない。が、オプションとして存在するがために痒い所に手が届く。)
この記事では便利なformulaやその活用方法、Rubyのサンプルコードなどをまとめている。(随時更新)

リストから特定の要素だけ抜き出したい

[ARRAY].where("condition": "value")

例えばSensesSmartHRのAPIはカスタムフィールドはリストに入って返ってくる。その中から特定のカスタムフィールドだけ参照したことがあるだろう。
そういうときはこういったformulaを使う。

[案件詳細項目].where(name: "プロジェクト終了予定日")[0]['value']

スクリーンショット 2021-03-06 16.00.30(2).png

ちなみに、階層構造のオブジェクトで.where()を使いたい場合

.where("parentkey.childkey": "condition")

入れ子になったリストから特定の要素だけを取り出す

[ARRAY].pluck(column_name)

Workdayから取れてくるデータのように、複雑に入れ子になっているデータの処理には.pluck()が便利。

[
  {
    "name": "Jake"
  },
  {
    "name": "Kate"
  }
].pluck("name")

#["Jake", "Kate"]
[
  {
    "Custom_ID_Data": {
      "ID": "0000001",
      "ID_Type_Reference": "00EAUEDI23983"
    }
  },
  {
    "Custom_ID_Data": {
      "ID": "0000002",
      "ID_Type_Reference": "00VIEIO495310"
    }
  }
].pluck(["Custom_ID_Data", "ID"], ["Custom_ID_Data", "ID_Type_Reference"])

#[["0000001","00EAUEDI23983"],["0000002","00VIEIO495310"]]

String型のデータをリスト型のデータに変換

例えば,区切りの文字列をバラしてリストにしたい、といったときに使えるrubyアクション。

string_to_list.rb
string = input['string_to_transform'] #string
output_list = [] #array

string.split(",").each do |item| #push each item in string to array
  output_list << {alphabets: item}
end

{output_list: output_list} #return array

サンプルのコミュニティレシピ

ランダムな文字列(パスワード等)を生成

generate_random_password.rb
password = (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['~','!','@','#','$','%','^','&','*','(',')','-','+']).sort_by{rand}.first(8).join

{ password: password}

サンプルのコミュニティレシピ

Shift_JISにエンコーディングするときに、対応できない文字を「?」に置き換える

[DATA_PILL].encode("Shift_JIS", { :invalid => :replace, :undef => :replace, :replace => "?" })
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mac Ruby・Ruby on Rails環境設定

はじめに。

環境設定なんて新しい端末を買った時にしかしないのであまり必要に感じないかもしれないですが、一応ここに残しておきます。

必要な設定

  1. GoogleChromeのダウンロード
  2. VScodeのダウンロード
    1. 拡張機能
      1. Japanese Language Pack for Visual Studio Code
      2. HTML Snippets
      3. Ruby
      4. zenkaku
      5. Code Spell Checker
  3. Command Line Toolsのダウンロード
  4. Homebrewのダウンロード
  5. Rubyのダウンロード
  6. MySQLのインストール
  7. Railsのインストール
  8. Node.jsのインストール
  9. yarnのインストール

1.GoogleChromeのダウンロード

まずはGoogleChromeをダウンロードします。
Macであれば最初からSafariが入っていますがGoogleChromeの方が使っている人が多いのでダウンロードしていきましょう。

GoogleChromeのダウンロードはこちら

2.VScodeのダウンロード

VScodeとはテキストエディタのことであり、プログラムのコードをファイルに書く場所のことです。
今回VScodeをテキストエディタで使う理由として、拡張機能が豊富でありRuby on Railsのコードを書く上で最適であること、人気ランキングで1位をいう理由で使わせてもらいます。

VScodeのダウンロードはこちら

真ん中左側にDownloadの文字があるのでダウンロードを行ってください。
こちらはよく使うので忘れずにドックに追加しておきましょう。

2-1.拡張機能

次はダウンロードしたVScodeに拡張機能を追加していきます。
それぞれ追加する拡張機能の意味を軽く説明します。

1. Japanese Language Pack for Visual Studio Code

これは日本語表記に変えてくれる機能です。あって便利です。

2. HTML Snippets

HTMLタグ、CSSタグの入力を自動で補完してくれます。

3. Ruby

Rubyの構文をチェックしてくれて、間違っているところを教えてくれます。

4. zenkaku

半角スペースのところを全角スペースにしてしまっている場所を教えてくれます。
これは本当に便利!

5. Code Spell Checker

コードのスペルを確認してくれます。

拡張機能追加の仕方

  1. 起動したウィンドウ画面左側の、アイコンメニュー内にある四角のアイコンをクリックします

  2. 左上の「Search Extensions in Marketplace」にそれぞれの拡張したい拡張機能名を入力すると表示されます。

  3. それぞれの拡張したい拡張機能名を選択し、install(またはインストール)をクリックします。

  4. 最後に拡張機能がちゃんと反映されるようにVScodeを再起動して終わりです。(再起動しなくても大丈夫なやつもありますが念のため)

3.Command Line Toolsのダウンロード

Command Line Toolsとは、Webアプリケーションの開発に必要なソフトをダウンロードするために必要な機能です。

インストールしましょう。

ターミナル
% xcode-select --install

インストール ⇨ 同意 ⇨ 完了 でインストール完了です。

4.Homebrewのダウンロード

Homebrewとは、ソフト管理ツールです。
Macのモデルによってインストール方法が異なるのできおつけてください。

  1. Macのモデル確認方法
    1. 左上のりんごマークを押す ⇨ このマックについて
    2. Macの情報が出るのでIntelモデルかM1モデルかを確認

Intelモデル

ターミナル
% cd 
% /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

インストールに多少時間が掛かりますが気長に待ちましょう。
パスワードの入力を求められたらパスワードを打ちましょう。打っても文字が表示されませんがちゃんと打てていますので大丈夫です。

ターミナル
% Press RETURN to continue or any other key to about

この表示がでたらエンターキーを押しましょう。

ターミナル
% brew -v                                         #インストールされているかの確認
% brew update                                     #アップデート
%  sudo chown -R `whoami`:admin /usr/local/bin    #権限の確認(パスワード再入力)

これで終わりです。

5.Rubyのダウンロード

5-1. rbenv と ruby-buildのインストール

土台となる、rbenvとruby-buildを、事前にインストールしたHomebrewを用いてインストールします。

ターミナル
% brew install rbenv ruby-build   #rbenvとruby-buildのインストール
% echo 'eval "$(rbenv init -)"' >> ~/.zshrc  #どこからでも使えるようにするコマンド
% source ~/.zshrc                             #zshrc の変更反映

5-2. readlineのインストール

ターミナルのirb上で日本語入力を可能にする設定を行うためにインストールしましょう。

ターミナル
% brew install readline      #readlineのインストール
% brew link readline --force    #どこからでも使えるようにするコマンド

5-3. rbenvを利用してRubyをインストール

ターミナル
% RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)"
% rbenv install 2.6.5    #2.6.5とは、Rubyのバージョンのことです
% rbenv global 2.6.5     #使用するバージョンの指定
% rbenv rehash           #変更の反映

6.MySQLのインストール

▷MySQLとはデータ管理をするツールです。様々な種類がありますが今回はMySQLをつかいます。

6-1. MySQLのインストール

ターミナル
% brew install mysql@5.6 

6-2. MySQLの自動起動設定
本来PC再起動のたびに起動し直す必要がありますが、面倒で手間がかかるので自動起動するようにしましょう。

ターミナル
% mkdir ~/Library/LaunchAgents 
% ln -sfv /usr/local/opt/mysql\@5.6/*.plist ~/Library/LaunchAgents
% launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.6.plist  

6-3. MySQLコマンドをどこからでも実行できるようにする
どこからでもMySQLを操作するためのコマンドmysqlを実行できるようにしましょう。

ターミナル
% echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc   #mysqlのコマンドを実行できるようにする設定
% source ~/.zshrc      #設定を読み込むコマンド
% which mysql          #mysqlのコマンドが打てるか確認する  
% mysql.server status  #MySQLの状態を確認するコマンド

7.Ruby on Railsのインストール

RailsとはRubyのフレームワークのことです。一緒にGem(拡張機能)を管理するためのbundlerもインストールしましょう。

ターミナル
% gem install bundler --version='2.1.4'  #Rubyのgemを管理するbundlerをインストール
% gem install rails --version='6.0.0'    #railsのインストール
% rbenv rehash                           #rbenvに変更反映

8.Node.jsのインストール

Railsを動かすためにはNode.jsが必要です。Homebrewを用いてインストールしパスを指定しましょう。

ターミナル
% brew install node@14        #インストール
% echo 'export PATH="/usr/local/opt/node@14/bin:$PATH"' >> ~/.zshrc
% source ~/.zshrc

9.yarnのインストール

ターミナル
% brew install yarn

以上になります。何か問題があればコメント御願いします。

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

【随時更新】ポートフォリオ・SaaS開発の参考に!超本格的なRails製オープンソースたち!【初心者もおすすめ】

「プログラマーになってすごいサービスを作りたい!」

という動機でプログラミングを始めてみたものの、いざ勉強を始めてみると

「どんな技術を使って開発したらいいの?」
「どこまで作り込めばいいのだろう?」
「一応完成したけどまだ足りないところがあるのでは?」

と不安に感じたことは一度はありますよね?

そんな方に強くオススメしたいRailsアプリケーションのGitHubリポジトリたちを紹介します。

ここで紹介するRailsアプリケーションはオープンソースにもかかわらず、
実際にWebサービスとして運用されていたり、有料で提供されていたりするものもありますので、必ずサービス開発の参考になるはずです!

ぜひ、一読してWebサービス開発の参考にしてみてください!

対象読者

・プログラミング初学者さん
・個人サービスの開発に取り掛かる前の方
・サービス開発の参考資料を探している方
・よりよいサービス開発の参考にしたい方

GitHubリポジトリたち

ChatWoot

オープンソースのライブチャットソフトです。
ホームページやランディングページに埋め込むお問い合わせチャットや、それに対するメッセージの管理システムを提供しているサービスです。

Vue.js × Ruby on Railsで開発されていて、このコードを勉強してマネするだけでかなり本格的なサービスの開発に繋げられると思います!
初見、かなり難しそうに感じますが、Gemfileを見て「devise」など初学者さんでもわかるライブラリなどがたくさん使われていますので、そういった分かる部分から読み進めていってもいいかもしれません。

GitHubリポジトリURL
https://github.com/chatwoot/chatwoot

公式ページ
https://www.chatwoot.com/

dashboard-screen-b294bdd1d718312290ec49b6c2a13428.png
引用元:https://www.chatwoot.com/static/dashboard-screen-b294bdd1d718312290ec49b6c2a13428.png

widget-ghost-99d99a87a95d9c1731b79af6584218ef.png
引用元:https://www.chatwoot.com/static/widget-ghost-99d99a87a95d9c1731b79af6584218ef.png

使われている主な技術

  • Ruby on Rails
  • Vue.js
  • Docker
  • CircleCI など

GitLab

GitHubのようにコードを管理することができるサービス。
かなりざっくりとGitHubとの違いを挙げると、自社で用意したサーバーなどにGitHubと同じような環境を構築できるので、セキュリティや情報管理の観点からGitHubは使いたくない!という企業さんに使われることが多いサービスです。
※その他にもGitLabの良いところはたくさんありますが、割愛します!

こちらもVue.js × Ruby on Railsで開発されたサービスですが、chatwootと違い、Railsのapp/views内のHTMLファイルのコードも充実しているため、まだJSフレームワークでのフロントエンド開発は視野に入れていないプログラマーさんでも読みやすいところは多いのかな、と思います。
ただし、hamlで書かれているため、人によっては読みにくさを感じるかも…

GitHubリポジトリ
https://github.com/gitlabhq/gitlabhq/

公式ページ
https://about.gitlab.com/

solutions-create.png
引用元:https://about.gitlab.com/images/solutions/solutions-create.png

使われている主な技術

  • Ruby on Rails
  • Vue.js
  • Docker など

solidus

オープンソースのeコマース構築サービス。
他のリポジトリと比べるとディレクトリ構成が少し特殊かな、という印象です。
こちらはフロントエンドも含めてRuby on Railsで開発されているようですので、その点においては参考にしやすいリポジトリなのかな、と思います。

こちら(http://demo.solidus.io/) からでもページを見ることも可能です。

GitHubリポジトリ
https://github.com/solidusio/solidus

公式ページ
https://spreecommerce.org/

Screen Shot 2021-03-06 at 14.37.52.png

使われている技術

  • Ruby on Rails
  • CircleCI など

まとめ

こちらの内容は随時更新していきます!
他にも実際にサービスとして提供されている良いオープンソースコードがございましたら教えていただきますと幸いです!

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

PCでRubyを実行してみよう!| 初めてのプログラミング環境構築 ②

前回はWindows、MacそれぞれにRubyをインストールするところまで行いました。
今回はRubyで簡単な文法を試してみましょう!

irbでRubyの動作を確認

まだガッツリプログラムを書くわけではないのでirb(Interactive Rubyの略)を使ってRubyのコードの動作を確認していきます。

PowerShellまたはターミナルで以下のコマンドを入力してEnterを押下してください。

PowerShellまたはターミナル
% irb
irb(main):001:0>

「irb(main):001:0>」と言うふうに変化したと思います。これでRubyのコマンドが実行できるようになりました。

ちなみにirbを終了したい時はexitを実行します。

PowerShellまたはターミナル
irb(main):001:0> exit

Rubyの基本文法

ここでRubyの簡単な構文を確認しておきます。

文字列/数字を出力してみよう

Rubyで出力に使う主なコマンドはputsです。
またプログラムを実行する上でデータの型を区別する必要があり、主なものは文字列数字です。
文字列はダブルクオーテーション(")またはシングルクオーテーション(')で囲む必要があります。数字は囲む必要はありません。
例えば、数字を文字列で表現したい時は"1"のようにしてあげれば、文字列として認識してもらえます。
irbで次のように実行してみましょう!

% puts "Hello World!"
Hello World!
=> nil

% puts 100
100

はい!出力されましたね!
Hello World!100が出力結果です。まだイメージが湧かないかもしれないですね。

変数へ代入

先ほどのputsを使った出力は、変数の中身を確認するときなどに使えます。
それでは変数に任意の文字列または数字を代入して、その動作を確認してみましょう。
なお、ここでも文字列数字の区別には注意してください!

% string = "Hello World!"
% integer = 100

変数として用意したstringintegerは英数小文字で始まるものであればなんでも構いません。(例えばsとかiとか一文字でも)
プログラミングでは等号(=)を使って、変数に任意の文字列や数字などを代入します。

それでは変数の中身を出力してみましょう!

% puts string
Hello World!

% puts integer
100

なお、変数は上書きができます。

% string = "Hello World!"
% puts string
Hello World!

% string = "Hello Japan!"
% puts string
Hello Japan!

演算

次は計算をしてみましょう!

演算子 意味
+ 足し算/和 1 + 1 (結果 2)
- 引き算/差 2 - 1 (結果 1)
* 掛け算/積 2 * 2 (結果 4)
/ 掛け算/商 2 / 2 (結果 1)
% 余り/余剰 3 % 2 (結果 1)
** べき乗/余剰 10 ** 3 (結果 1000)
irb(main):013:0> 1 + 1
=> 2
irb(main):014:0> 2 - 1
=> 1
irb(main):015:0> 2 * 2
=> 4
irb(main):016:0> 2 / 2
=> 1
irb(main):017:0> 3 % 2
=> 1
irb(main):018:0> 10 ** 3
=> 1000

配列とハッシュ

先ほどの変数では一つのデータだけを格納していました。
配列やハッシュを使うことで変数一つに対して複数のデータを格納することができます。

配列

こんな形のもの。文字列や数字を[ ]で囲みます。

price = [100, 200, 300]
fruit = ["banana", "apple", "orange"]

ハッシュ

配列に似ていますが、波かっこ{ }で囲み、key(例:"banana")とvalue(例:100)がセットで格納されています。

fruits = {"banana": 100, "apple": 200, "orange": 300}
または
fruits = {:banana=>100, :apple=>200, :orange=>300}

最後に配列とハッシュの操作を確認してみます。
配列やハッシュの何番目かの要素を取り出してみたいと思います。

プログラミングで順番を指定する場合、一番最初は0番目と数えることに注意してください。

#price配列の1番目を取り出してみる。
#この場合、price配列に[0]をつけることで、「1番目だよ!」と指示することができる。
irb(main):022:0> price[0]
=> 100
#fruit配列の2番目を取り出してみる。
#この場合、price配列に[1]をつけることで、「2番目だよ!」と指示することができる。
irb(main):023:0> fruit[1]
=> "apple"

ハッシュはkeyを指定してあげることで狙ったvalueを取り出すことができる。

irb(main):033:0> fruits[:orange]
=> 300

最後に

今回は基本的な文法をまとめてみました。
この操作ができるようになれば、様々な応用ができるようになると思います!

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

Rails フォロー機能まとめ

記事作成理由

1:プログラミング初学者の私が見返すため。
2:私のような初学者の学習に少しでも役立てればと思ったため。

※誤った記述や知識があればお知らせいただけると幸いです。


開発環境

・ruby: 2.6.3
・rails: 5.2.4.5
・OS: macOS Catalina ver10.15.7
・Cloud9


前提条件

・Userモデル,usersテーブルがある

・Bootstrap導入済み
・deviseを使ってログイン機能実装済み

・CRUD機能の基礎について学習済み


実装の流れ

1:Relationshipモデルの作成
2:中間テーブル(relationshipsテーブル)の作成
3:アソシエーションの設定
4:アクションの定義
5:Viewファイルに記述


1:Relationshipモデルの作成

ターミナル.
rails g model Relationship


2:中間テーブル(relationshipsテーブル)の作成

マイグレーションファイルにカラムを追加

db/migrate/○○○○_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :relationships do |t|

      t.references :follower, foreign_key: {to_table: :users}
      t.references :followed, foreign_key: {to_table: :users}

      t.timestamps

      t.index [:follower_id, :followed_id], unique: true
    end
  end
end
ターミナル.
rails db:migrate

マイグレーションファイルの反映忘れずに!

ーー解説ーー

relationshipsテーブルの中身はこのようになります。

カラム タイプ オプション
follower_id integer foreign_key: {to_table: :users}
followed_id integer foreign_key: {to_table: :users}

relationshipsテーブルは中間テーブルであるため、
followerカラムとfollowedカラムはt.referencesをつけて生成します。


(ポイント!)
今回生成したカラム (follower_id と followed_id) は自由に命名することができます。
他記事ではfollowing_idやfollows_idなど様々な命名がされています。自分が1番理解しやすいように命名しましょう。
(色々な記事を調べて、様々な命名を見ると頭が混乱します。僕だけかもしれませんが・・・)


また、follower、followedどちらも外部キーとして設定します。 foregin_key(外部キー)
ここでforegin_key :trueとしてしまうと、followersテーブルやfollowedsテーブルなど存在しないテーブルを参照してしまいエラーになります。
それを避けるために{to_table: :users}として、usersテーブルのidを参照するよう紐づかせています。


t.index [:follower_id, :followed_id], unique: true
これにより、2つのカラムの中身が同じ組み合わせでデータを保存することを防ぎます。
=同じユーザーを2回フォローできないようにしています。

中間テーブル概念についてはこちら2つの記事がとても参考になりました。
【Railsでフォロー機能を作ろう】
【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル


3:アソシエーションの設定

relationship.rb
class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"

  validates :follower_id, presence: true
  validates :followed_id, presence: true
end


ーー解説ーー

■2,3行目
relationshipsテーブルでforeign_keyとしてfollower_id , followed_id を設定しているため、モデル名もFollower, Followedとします。

しかし、FollowerモデルもFollowedモデルも実在しない為、class_name: "User"とオプションを追記し、関連するモデル(本当のモデル名)を指定します。
このとき、"User"は文字列扱いになるため、ダブルクォーテーションで囲んでいます。

■4,5行目
validatesの説明は割愛します。
(わからない方は「バリデーション」)でお調べください。

user.rb
class User < ApplicationRecord

  # フォローするユーザーから見た中間テーブル
  has_many :active_relationships,
                      class_name: "Relationship",
                      foreign_key: "follower_id",
                      dependent: :destroy

  # フォローされているユーザーから見た中間テーブル
  has_many :passive_relationships,
                      class_name: "Relationship",
                      foreign_key: "followed_id",
                      dependent: :destroy

  # 中間テーブルactive_relationshipsを通って、フォローされる側(followed)を集める処理をfollowingsと命名
  # フォローしているユーザーの情報がわかるようになる
  has_many :followings, through: :active_relationships, source: :followed

  # 中間テーブルpassive_relationshipsを通って、フォローする側(follower)を集める処理をfollowingsと命名
  # フォローされているユーザーの情報がわかるようになる
  has_many :followers, through: :passive_relationships, source: :follower

end


ーー解説ーー

ここが鬼門です。がんばっていきましょう。
4つのhas_manyがありますが、上から2つずつに分けて説明します。

まず中間テーブルの見方を2つに分けます。
1:フォローするユーザーから見た視点
2:フォローされるユーザーから見た視点

本来であれば has_many :relationships とすればいいのですが、
見方が2つあるため、2通りの定義をしなければなりません。

そのため、ここでは
フォローする側active_relationships と命名し、(行目)
フォローされる側passive_relationships と命名しています。(行目)

※先ほどuser.rbで作成したモデル(Follower,Followed)と同様、これらも好きに命名することができます。
他記事を見て比較する時など、命名が異なるケースがあるため、注意しましょう。

そして、参照元のモデルはどちらもRelationshipモデルであるため、
class_name: "Relationship"とオプションを追加します。

しかし、このままではRelationshipsテーブルのどちら(follower , followed)を参照すればいいのかがわかりません。
そこで、下記のように指定してあげます
active_relationshipsは、foreign_key: "follower_id"を参照
passive_relationshipsは、foreign_key: "followed_id"を参照


dependent: :destroyは、とあるUserが削除された時にそれに紐づくrelationshipのデータも削除されるようにしています。

次に、こちらの文ですが、
has_many :followings, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower

これらはコードのコメントに記載の通り、定義したテーブルを通って(through)、sourceで指定したモデルの参照元からデータを集めることができます。

これにより
@user.followings (userがフォローしている人の情報)

@user.followers(userがフォローされている人の情報=userをフォローしている人の情報)
といった表記が可能になります。

※くどいようですが、、followingsfollowersも好きに命名することができます。
他記事を見て比較する時など、命名が異なるケースがあるため、注意しましょう。

これでアソシエーションは完成です。

4:アクション定義

user.rb と relationships.controller.rbにアクションを定義します。

user.rb
#下記3つのアクションを追記
class User < ApplicationRecord      

  # フォローする
  def follow(user_id)
    active_relationships.create(followed_id: user_id)
  end

  # フォローを外す
  def unfollow(user_id)
    active_relationships.find_by(followed_id: user_id).destroy
  end

  # すでにフォローしているのか確認
  def following?(user)
    followings.include?(user)
  end
end
relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :authenticate_user!

  def create
    current_user.follow(params[:user_id])
    redirect_to request.referer
  end

  def destroy
    current_user.unfollow(params[:user_id])
    redirect_to request.referer
  end

  # フォローしている人一覧
  def follower
    user = User.find(params[:user_id])
    @users = user.followings
  end

  # フォローされている人一覧
  def followed
    user = User.find(params[:user_id])
    @users = user.followers
  end

end


5:Viewファイルに記述

ここからはあくまで例になります。
定義している変数などによって記述の仕方は変わります。
※一部抜粋

ーーフォロー数、フォロワー数表示、フォローボタンーー

users/index.html.erb
<tbody>
  <% @users.each do |user| %>
  <tr>
    <td><%= attachment_image_tag user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg", size:"40x40" %></td>
    <td><%= user.name %></td>
    <td><%= "フォロー数: #{user.active_relationships.count}" %></td>
    <td><%= "フォロワー数: #{user.passive_relationships.count}" %></td>
    <td>
      <% if current_user.id != user.id %>
        <% if current_user.following?(user) %>
        <%= link_to 'フォローを外す', user_relationships_path(user.id), method: :DELETE %>
        <% else %>
        <%= link_to 'フォローする', user_relationships_path(user.id), method: :POST %>
        <% end %>
      <% end %>
    </td>
    <td><%= link_to "Show", user_path(user.id) %></td>
  </tr>
  <% end %>
</tbody>



ーーフォローしている人一覧ーー

relationships/follower.html.erb
<tbody>
  <% @users.each do |user| %>
    <tr>
      <td><%= attachment_image_tag user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg" , size:"40x40" %></td>
      <td><%= user.name %></td>
      <td>フォロー数:<%= user.followings.count %></td>
      <td>フォロワー数:<%= user.followers.count %></td>
      <td><%= link_to "show", user_path(user.id) %> </td>
    </tr>
  <% end %>
</tbody>



ーーフォローされている人一覧ーー

relationships/followed.html.erb
<tbody>
  <% @users.each do |user| %>
    <tr>
      <td><%= attachment_image_tag user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg" , size:"40x40" %></td>
      <td><%= user.name %></td>
      <td>フォロー数:<%= user.followings.count %></td>
      <td>フォロワー数:<%= user.followers.count %></td>
      <td><%= link_to "show", user_path(user.id) %> </td>
    </tr>
  <% end %>
</tbody>



以上です。
閲覧ありがとうございました!

誤字、脱字、誤り等ございましたら教えていただけると幸いです。

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

【Ruby on Rails】結合テストコードについてまとめ(Capybara)

初学者です。
テストコードがなぜか大好きです。

今回は結合テストコードについてまとめます。
Capybaraという標準で導入されているGemを利用しています。

前提条件

  • FactoryBotを導入済みである

上記については以下の記事にまとめています。
【Ruby on Rails】FactoryBotとFakerについてまとめ

テストコードの種類

  • 単体テストコード

    モデルやコントローラーなど機能ごとに問題がないか確認します。
    例えばバリデーションがきちんと動作しているかなどです。

  • 結合テストコード

    ユーザーがブラウザで操作する一連の流れを再現して問題がないか確かめます。
    例えば、ユーザー登録で「名前とメールアドレスとパスワードを入力するとトップページに遷移して表示がユーザーの名前に変わっている」などの一連の動作を確認します。

  • 正常系

    ユーザーが開発者の意図する操作を行った時の挙動を確認するテストコードです。
    例えば、ユーザー登録で問題なく全てのデータが入力された場合などです。

  • 異常系

    ユーザーが開発者の意図しない操作を行った時の挙動を確認するテストコードです。
    例えば、ユーザー登録で正しい値が入っていないと登録できないかどうかなどです。

準備

今回はUserモデルの結合テストコードを例にしていきます。

まずは下記コマンドで結合テストコードを書くファイルを準備します。

ターミナル
rails g rspec:system users

spec/system配下にusers_spec.rbというファイルが生成されていればOKです。

テストコード

解説はあとにして先に記述例を載せます。

spec/system/users_spec.rb
require 'rails_helper'

RSpec.describe 'ユーザー新規登録', type: :system do
  before do
    @user = FactoryBot.build(:user)
  end
  context 'ユーザー新規登録ができるとき' do 
    it '正しく情報を入力すればユーザー新規登録ができてトップページに移動する' do
      # トップページに移動する
      visit root_path
      # トップページに新規登録ページへ遷移するボタンがあることを確認する
      expect(page).to have_content('新規登録')
      # 新規登録ページへ移動する
      visit new_user_registration_path
      # ユーザー情報を入力する
      fill_in 'name', with: @user.nickname
      fill_in 'Email', with: @user.email
      fill_in 'Password', with: @user.password
      fill_in 'Password confirmation', with: @user.password_confirmation
      # 登録ボタンを押すとユーザーモデルのカウントが1増えることを確認する
      expect{
        find('input[name="button"]').click
      }.to change { User.count }.by(1)
      # トップページへ遷移することを確認する
      expect(current_path).to eq(root_path)
      # カーソルを合わせるとログアウトボタンが表示されることを確認する
      expect(
        find('.user_nav').find('span').hover
      ).to have_content('ログアウト')
      # 新規登録ページへ遷移するボタンやログインページへ遷移するボタンが表示されていないことを確認する
      expect(page).to have_no_content('新規登録')
      expect(page).to have_no_content('ログイン')
    end
  end
  context 'ユーザー新規登録ができないとき' do
    it '誤った情報ではユーザー新規登録ができずに新規登録ページへ戻ってくる' do
      # トップページに移動する
      visit root_path
      # トップページに新規登録ページへ遷移するボタンがあることを確認する
      expect(page).to have_content('新規登録')
      # 新規登録ページへ移動する
      visit new_user_registration_path
      # ユーザー情報を入力する
      fill_in 'name', with: ''
      fill_in 'Email', with: ''
      fill_in 'Password', with: ''
      fill_in 'Password confirmation', with: ''
      # 登録ボタンを押してもユーザーモデルのカウントが増えないことを確認する
      expect{
        find('input[name="button"]').click
      }.to change { User.count }.by(0)
      # 新規登録ページへ戻されることを確認する
      expect(current_path).to eq('/users')
    end
  end
end

RSpec.describe 'ログイン', type: :system do
  before do
    @user = FactoryBot.create(:user)
  end
  context 'ログインができるとき' do
    it '保存されているユーザーの情報と合致すればログインができる' do
      # トップページに移動する
      visit root_path
      # トップページにログインページへ遷移するボタンがあることを確認する
      expect(page).to have_content('ログイン')
      # ログインページへ遷移する
      visit new_user_session_path
      # 正しいユーザー情報を入力する
      fill_in 'Email', with: @user.email
      fill_in 'Password', with: @user.password
      # ログインボタンを押す
      find('input[name="button"]').click
      # トップページへ遷移することを確認する
      expect(current_path).to eq(root_path)
      # カーソルを合わせるとログアウトボタンが表示されることを確認する
      expect(
        find('.nav_bar').find('span').hover
      ).to have_content('ログアウト')
      # サインアップページへ遷移するボタンやログインページへ遷移するボタンが表示されていないことを確認する
      expect(page).to have_no_content('新規登録')
      expect(page).to have_no_content('ログイン')
    end
  end
  context 'ログインができないとき' do
    it '保存されているユーザーの情報と合致しないとログインができない' do
      # トップページに移動する
      visit root_path
      # トップページにログインページへ遷移するボタンがあることを確認する
      expect(page).to have_content('ログイン')
      # ログインページへ遷移する
      visit new_user_session_path
      # ユーザー情報を入力する
      fill_in 'Email', with: ''
      fill_in 'Password', with: ''
      # ログインボタンを押す
      find('input[name="button"]').click
      # ログインページへ戻されることを確認する
      expect(current_path).to eq(new_user_session_path)
    end
  end
end

まずdescribe新規登録の場合とログインの場合で項目分けします。

次にcontextでそれぞれ正常系と異常系で項目分けします。

そのあと、それぞれの流れを書いていきます。

FactoryBotについては新規登録はbuildですが、ログインはユーザー情報があらかじめあることが前提なのでcreateにしています。

では内容について細かく解説していきます。

visit

トップページに移動するvisit root_pathとしています。
visitは遷移するという意味で利用します。

page

visitで訪れた先のページの情報が格納されています。例えばカーソルを合わせてはじめて見ることができる文字列はpageの中に含まれません。

have_content

トップページに新規登録ページへ遷移するボタンがあることを確認するexpect(page).to have_content('新規登録')としています。
visitで訪れたpageの中に該当の文字列があるかどうかを判断する時に利用します。

fill_in

ユーザー情報を入力するfill_inを利用しています。
fill_in フォームの名前, with: 入力する文字列と記述することでフォームへの入力を行うことができます。

find().click

find(クリックしたい要素).clickと記述することでクリックの動作ができます。

登録ボタンを押すとユーザーモデルのカウントが1増えることを確認する登録ボタンを押すの動作をfind('input[name="button"]').clickで表しています。
この場合はinput要素のname="button"を指定していることになります。

change

expect{ 動作 }.to change { モデル名.count }.by(1)登録ボタンを押すとユーザーモデルのカウントが1増えることを確認する
カウントが1増えるを表しています。

current_path

トップページへ遷移することを確認するexpect(current_path).to eq(root_path)としています。
current_path今いるページという意味です。

hover

find(要素).hoverで特定の要素にカーソルをあわせたときの動作を表すことができます。
カーソルを合わせるとログアウトボタンが表示されることを確認するexpect(find('.nav_bar').find('span').hover).to have_content('ログアウト')としています。
今回の場合は.nav_barの中のspan要素という感じで二重に探しています。

have_no_content

サインアップページへ遷移するボタンやログインページへ遷移するボタンが表示されていないことを確認するexpect(page).to have_no_content('新規登録')expect(page).to have_no_content('ログイン')としています。
存在しないことを確認するという意味です。

以上です。

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

PCでRubyを使えるようにしよう!| 初めてのプログラミング環境構築 ①

PCでRubyを使えるようにしよう!

Windows

インストーラーのダウンロード

以下サイトへアクセスしてファイルをダウンロードし、Rubyインストーラーのインストールを開始して下さい。
https://rubyinstaller.org/downloads/

現時点での推奨バージョンは太字で記載されています。
スクリーンショット 2021-03-05 17.25.47.png

途中で問われる選択項目は、基本的に全て変更せず「Next」ボ タンを押して頂いて問題ありません。
但し、上図のMSYS2の項目は必ずチェックして下さい。
スクリーンショット 2021-03-05 17.27.39.png

Rubyインストーラーのインストールが完了すると、以下のような実行画面が出ます。
表示される選択3つとも全て実行する必要があります。
* 1を入力してEnterを押す。。。少し待つ
* 2を入力してEnterを押す。。。結構待つ(これが一番時間かかります)
* 3を入力してEnterを押す。。。少し待つ

スクリーンショット 2021-03-05 17.32.13.png

インストールできているか確認

PowerShellから「ruby -v」のコマンドを実行することで、インストールが成功しているか確認できます。
スクリーンショット 2021-03-05 17.39.17.png

Mac

ターミナルを開きます。
スクリーンショット 2021-03-05 17.52.37.png

Rubyのバージョンを確認

Macでは標準でRubyがインストールされているようです。
ターミナルで、以下のコマンドを入力し、Enterを押下します。

ターミナル
% ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]

私の場合は、バージョン2.6.3がインストールされていました。

今回は以上です!

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

Ruby言語とは

本日は、Ruby言語の特徴とメリットデメリットについて
記事を書きたいと思います。

特徴

Rubyは、1995年に公開されたプログラミング言語。
日本で開発され、日本初の国際規格で認定されたプログラミング言語。
開発者は、「まつもとゆきひろ」さん。
言語の名前の由来は宝石のルビーからきている。

・インタプリタ方式
実行エンジンがプログラムソースを解析して1行ずつ実行する方式である、インタプリタ方式を採用している。
なので、プログラム作成中に実行できるため、エラーが発見しやすい

・汎用性が高い
汎用性が高く、Web開発以外の現場でも使用されている。その原因は、Windows、Linux、Mac OS等いろいろな環境で動作可能なマルチプラットフォームであるため。
gemと呼ばれるライブラリ群が充実している点が挙げられる。このため、Webシステム以外でも幅広く活用できる。

メリット

メリットは、文法や書式はシンプルなものを使っているので、初心者でも勉強しやすい。
コードの記述する量も他の言語に比べると圧倒的に少ないため、開発効率も高い。
日本発のプログラミング言語というのもあり学習環境が充実している。
書籍なども多く販売されている。

デメリット

デメリットでは、処理速度が遅いこと。
インタプリタ方式を採用しているため、先ほど少し話たがコードを1行ずつ訳しながら
実行するため、プログラム全体を訳してから実行するコンパイル方式と比べると処理速度
は遅くなる。
コンパイル方式はコードが複雑なため、記述に時間がかかってしまう。

Ruby on Railsとは

RubyとRuby on Railsは混同されがちだが別物。
Ruby on Railsとは、Webアプリケーションフレームワークのひとつ。
フレームワークとは、最小コストでwebアプリケーションの開発ができる仕組みのこと。
つまり、Webアプリケーション開発で、「必要となる作業やリソースを事前に仮定し、
用意してある便利なもの」

■Ruby on Railsの理念
・DRY(Don`t Repeat Yourself)
同じコードを繰り返し書くことを避け、保守管理しやすい状態に保ちバグを減らす。

・CoC(Convention Over Configuration)
Ruby on Rails自体で規約を用意しているため、規約に則ったコードを書くことで記述量を少なくすることができ、スピーディーな開発が可能。

Rubyはプログラミング言語でRuby on Railsは開発を効率的に行えるフレームワークと覚えよう。
ちなみに、Ruby言語を使用し作られた有名なものは、Huluやクックパッドなどがある。

以上がRubyについての説明です。
今回は、自分が使用している言語について記事を書いてみたが他の言語についての
記事も書いていきたいなと思いました。

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

【コードリーディングのお供】じつはRubyDocは任意のGitHubリポジトリのyardドキュメントを生成できる。とても便利!

はじめに

 この記事は自分が何年も気が付かなかったRubyDoc.infoの便利機能について書きます。

 みなさんは、GitHubで新しいRubyのプロジェクトを見た時に何をしますか? 私はYardでドキュメントを生成してモジュールやクラスの構成をつかもうとします。Yardはプロジェクトの全体構造を把握するのにとても便利です。RubyのGemのYardドキュメントはRubyDoc.info というサイトでオンラインで読めるようになっています。

RubyDoc.info で任意のGithubリポジトリのドキュメントを表示する

 RubyDocでは、GemだけでなくGithubという項目があります。ところが、全部のRubyリポジトリが登録されているわけではなく、一部のレポジトリしか閲覧できません。実は、RubyDock.info には Add Projectボタンがついていて、任意のGithubリポジトリを追加できます!

(ご存知でしたか? 私は今日までこれに気付きませんでした。RubyDoc側が何らかの基準で表示できるGithubのレポジトリを選定していると思い込んでいました!)

image.png

Add Projectボタンを押すと、下のように任意のGitHubのリポジトリを追加できる。

image.png

 この機能を活用すれば、短時間のうちに任意のGitHubのリポジトリのコードの構成を把握する作業がブラウザで完結できるようになります。

 やったね!

 しばしばRubyはコードは書きやすく読みにくい、と言われます。Rubyは記法に緩さがありますので、確かにそのような側面もあると思います。しかし、YardDoc.infoのような便利なサイトを活用すれば、コードを読むのがかなり楽になるのではないかと思います。

 この記事は以上です。

関連記事

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

【Rails】データが作成された時間によってビューの表示を条件分岐する

環境

macOS: Big Sur Ver11.2.2
Rails: 6.0.0
Ruby: 2.6.5

やりたいこと

作成中のWebサービスのトップページに新着のお店を掲載しています。
そこで、3日以内に登録された店舗であれば「NEW!!」の表示をしたいなあと思い、条件分岐の方法を探りました。

結論

今回実装したのは、3日以内に登録された新規ユーザーであれば「NEW!!」の表示をするというもの。
結論、以下の記述で簡単に条件分岐できました。

<div class="contents-box">
  <div class="image-box">
    <%= image_tag user.image, class: "contents-image" %>
  </div>
  <h3 class="contents-title">
    <%= user.shop_name %>
  </h3>
  <p class="contents-price">
    <%= user.category.name %>
  </p>
  <%# お店の登録が3日以内であればnewマークを表示 %>
  <% if user.created_at > 3.days.ago %>
  <p><i class="fas fa-star"></i>NEW!!</p>
  <% end %>
</div>

実装した結果がこちら!
テストデータなので、全部NEW!!が表示されていますが、ご容赦ください。
8f9c95c9b443a9523e700afe9ca2afd5.png

以上!

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

ローカル環境でのGithub連携方法

はじめに

ローカル環境でのGithub連携方法についてまとめました。
railsアプリをGithubに連携させるためには、gitの知識も必要なため、gitを含めて説明します。

到達点

以下の2点を達成する
・gitが使えるようになる
・railsアプリをGithubに連携させる

流れ

① railsアプリを作成
② Github新規登録し、リポジトリを作成
③ gitコマンドを行い、連携させる

① railsアプリを作成

手前味噌ではありますが、私の記事を参照すると、ローカル環境でのrails環境構築ができます。
ローカル環境開発 Ruby on Railsの環境構築方法

  

② Github新規登録し、リポジトリを作成

Githubを新規登録し、新しいリポジトリを作成する。

以下の画像の赤枠の New もしくは New repository をクリックしてください。

スクリーンショット 2021-03-01 19.29.40.png

Repository nameは、自身のアプリ名(rails new アプリ名)を入力し、Create Repositoryをクリックしてください。

スクリーンショット 2021-03-01 19.24.22.png

③ gitコマンドを行い、連携させる

$ cd 自分のアプリ名
$ git init
$ git status   #作業ディレクトリでの現在の状態を表示する
$ git diff     #差分を見る

git addでファイルをstagedという状態にします
git add する前に変更した箇所とインデックスとの変更点を必ず確認しましょう
   

$ git add ..   #すべてのファイル・ディレクトリ
$ git diff     #差分を見る

git commitでローカルレポジトリにコミットします
git commit する前に差分を確認しましょう
  

$ git commit -m "Initialize repository"
$ git diff     #差分を見る

git commit -m"コミット名" でコミット名が作成できます。
git push する前にも差分を確認しましょう

$ git remote add origin http://Github.com/GitHubで作成したID/リポジトリ名.git
$ git push -u origin master

git pushで、ローカルレポジトリの内容がリモートに反映されます。
Githubのページからも確認しましょう。

参考記事

今日からはじめるGitHub 〜 初心者がGitをインストールして、プルリクできるようになるまでを解説

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

新しいRubyのバージョンをインストール

Rubyの環境構築

1. rbenv と ruby-buildをインストール

rbenvとruby-buildを、Homebrewを用いてインストールします。

ターミナルに入力

% brew install rbenv ruby-build

2. rbenvをどこからも使用できるようにする

PCにおけるどこの場所からも使用できるようにする。

% echo 'eval "$(rbenv init -)"' >> ~/.zshrc

CatalinaかMojave以前の方で、設定ファイルの場所が個々で異なるのでご注意。

3. zshrcの変更を反映させる

zshrcを再読み込みし、変更を反映させる。

% source ~/.zshrc

4. readlineをinstallしよう

日本語入力を可能にする設定。

% brew install readline

5. readlineをどこからも使用できるようする

% brew link readline --force

6.rbenvを利用してRubyをインストール

Webアプリケーション開発用のRubyをインストール。

% RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)"
% rbenv install 2.6.5

処理に10分程度かかる可能性あり。
2.6.5と書いてあるのは今回インストールするRubyのバージョン。

7. 利用するRubyのバージョンを指定

インストールしたRuby 2.6.5を使用するため。

% rbenv global 2.6.5

8. rbenvを読み込んで変更を反映

rbenvを読み込み、変更を反映させる

% rbenv rehash

9. Rubyのバージョンを確認

正しくバージョンを指定することができたか確認。
先ほどインストールした2.6.5であることが表示されれば完了。

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

Homebrew, rbenv インストール時のエラー解決方法

この記事で書くこと

Homebrew, rbenv をインストールした際に発生したエラーとその解決方法。

初めに結論

rbenvのインストールに失敗した場合、その前段のHomebrewのインストールが正常に完了しているかどうかを

brew doctor

で調べて、正常完了していないようなら、その指示に従う。
今回の例では、下記を実行することで解決。

git -C $(brew --repo homebrew/core) checkout master

環境

macOS Catalina 10.15.7
MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
2GHz クアッドコアIntel Core i5
16GB 3733 MHz LPDDR4X

ターミナル 2.10(433)

エラー発生までの過程

Progateの記事 Rubyの開発環境を用意しよう! に沿って、Ruby環境を構築しようとした。

1. 必要なツールの用意
2. Rubyを実行してみよう
3. Homebrewのインストール

ここまでは何事もなく完了(したとこの時は思っていた)。

4. rbenvのインストール

上記4.を実施時に下記のようなエラーが発生。

~ % brew install rbenv ruby-build
==> Downloading https://homebrew.bintray.com/bottles-portable-ruby/portable-ruby-2.6.3_2.yosemite.bottle.tar.gz
######################################################################### 100.0%
==> Pouring portable-ruby-2.6.3_2.yosemite.bottle.tar.gz
==> Tapping homebrew/cask
Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask'...
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 552725 (delta 4), reused 3 (delta 2), pack-reused 552713
Receiving objects: 100% (552725/552725), 243.99 MiB | 7.40 MiB/s, done.
Resolving deltas: 100% (390328/390328), done.
Tapped 3847 casks (3,963 files, 261.7MB).
==> Searching for similarly named formulae...
Error: No similarly named formulae found.
Error: No available formula or cask with the name "rbenv".
==> Searching for a previously deleted formula (in the last month)...
Error: No previously deleted formula found.
==> Searching taps on GitHub...
Error: No formulae found in taps.

エラー解決までの過程

Errorと書いている行をコピペしてGoogle先生に問合せ。

まずは、

Error: No similarly named formulae found.

先生からの回答(私が理解できる回答)はありません。次に、、、

Error: No available formula or cask with the name "rbenv".

やはり先生から回答ありません。次に、、、

いやちょっと待て。めんどい。別の視点で原因調査。

そもそも、rbenvのインストールの前に実施していた、Homebrewのインストールが本当に正常に完了しているのか?と疑ってみる。

Progateでは、

以下のコマンドを実行してください。
brew -v
Homebrew 1.6.2 のような文字が表示されれば、Homebrewは正常にインストールできています!

と記載があって、実際に自分の環境でも下のようにHomebrewのバージョン名が記載されていたので正常にインストールできているものと思っていた。

~ % brew -v
Homebrew 3.0.4-38-g61427d6
Homebrew/homebrew-core (no Git repository)

が、しかーし。
Progateで指定されていた、

Homebrewをインストールするには、以下のコマンドをコピーしてターミナルで実行します。
※ 複数行で表示していますが全部で1行のコマンドですので、全てコピーして実行してください。
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

を実行した後に表示されている長い長い文字列を追っていくと、最後の方に何やら怪しげな文字列が。
error、fatal。。。

HEAD is now at 61427d668 Merge pull request #10770 from SeekingMeaning/unless_logical_operators
==> Tapping homebrew/core
remote: Enumerating objects: 64, done.
remote: Counting objects: 100% (64/64), done.
remote: Compressing objects: 100% (45/45), done.
error: RPC failed; curl 56 LibreSSL SSL_read: error:06FFF064:digital envelope routines:CRYPTO_internal:bad decrypt, errno 0
fatal: the remote end hung up unexpectedly
fatal: early EOF
fatal: index-pack failed
Failed during: git fetch --force origin refs/heads/master:refs/remotes/origin/master

やっぱり上手くインストールされていない可能性が高いw

ということで、Homebrewが正常にインストールされているのかどうかを調べる方法を検索。

Google先生、ヨロシクオネガイシマス
.
.
.
ありました!

brew doctor

とやらを実行すれば良いみたいです。

はい、ということで上記コマンドを実行してみましょう。

local % brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: Some taps are not on the default git origin branch and may not receive
updates. If this is a surprise to you, check out the default branch with:
  git -C $(brew --repo homebrew/core) checkout master

うーん。初心者のワタシには良く分からんが、デフォルトのブランチから引っ張って来い、ということなのか?

ようやく解決方法

まぁとにかく、一番下の行に書いてある文字列をコピペしましょうか。
全然どうでもいいが、"コピペ" って全然キー入力慣れない。。

local % git -C $(brew --repo homebrew/core) checkout master
Updating files: 100% (5792/5792), done.
Branch 'master' set up to track remote branch 'master' from 'origin'.
Already on 'master'

お、なんか上手くいったような感じ。

Homebrewのバージョンを再度確認。。

local % brew -v
Homebrew 3.0.4
Homebrew/homebrew-core (git revision c8f8f; last commit 2021-03-05)
Homebrew/homebrew-cask (git revision b5f8a9e; last commit 2021-03-05)

さっきと表記が変わりました!正常にインストールされているようです。

ということで、おそらくもうrbenvを正常にインストールするための前準備は整ったんでしょうな。

Progateの指示に再度従って。。

以下のコマンドをターミナルで実行して、rbenvをインストールしましょう。
brew install rbenv ruby-build

local % brew install rbenv ruby-build
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/cask).
==> Updated Casks
Updated 1 cask.

==> Downloading https://homebrew.bintray.com/bottles/autoconf-2.69.catalina.bottle.4.tar.
==> Downloading from https://d29vzk4ow07wi7.cloudfront.net/ca510b350e941fb9395522a03f9d2f
######################################################################## 100.0%
==> Downloading https://homebrew.bintray.com/bottles/pkg-config-0.29.2_3.catalina.bottle.
==> Downloading from https://d29vzk4ow07wi7.cloudfront.net/80f141e695f73bd058fd82e9f539dc
######################################################################## 100.0%
==> Downloading https://homebrew.bintray.com/bottles/readline-8.1.catalina.bottle.tar.gz
==> Downloading from https://d29vzk4ow07wi7.cloudfront.net/fe4de019cf549376a7743dcb0c86db
######################################################################## 100.0%
==> Downloading https://github.com/rbenv/ruby-build/archive/v20210119.tar.gz
==> Downloading from https://codeload.github.com/rbenv/ruby-build/tar.gz/v20210119
######################################################################## 100.0%
==> Downloading https://homebrew.bintray.com/bottles/rbenv-1.1.2.catalina.bottle.tar.gz
######################################################################## 100.0%
==> Installing dependencies for rbenv: autoconf, pkg-config, readline and ruby-build
==> Installing rbenv dependency: autoconf
==> Pouring autoconf-2.69.catalina.bottle.4.tar.gz
?  /usr/local/Cellar/autoconf/2.69: 67 files, 3.0MB
==> Installing rbenv dependency: pkg-config
==> Pouring pkg-config-0.29.2_3.catalina.bottle.tar.gz
?  /usr/local/Cellar/pkg-config/0.29.2_3: 11 files, 623.8KB
==> Installing rbenv dependency: readline
==> Pouring readline-8.1.catalina.bottle.tar.gz
==> Caveats
readline is keg-only, which means it was not symlinked into /usr/local,
because macOS provides BSD libedit.

For compilers to find readline you may need to set:
  export LDFLAGS="-L/usr/local/opt/readline/lib"
  export CPPFLAGS="-I/usr/local/opt/readline/include"

For pkg-config to find readline you may need to set:
  export PKG_CONFIG_PATH="/usr/local/opt/readline/lib/pkgconfig"

==> Summary
?  /usr/local/Cellar/readline/8.1: 48 files, 1.6MB
==> Installing rbenv dependency: ruby-build
==> ./install.sh
==> Caveats
ruby-build installs a non-Homebrew OpenSSL for each Ruby version installed and these are never upgraded.

To link Rubies to Homebrew's OpenSSL 1.1 (which is upgraded) add the following
to your ~/.zshrc:
  export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"

Note: this may interfere with building old versions of Ruby (e.g <2.4) that use
OpenSSL <1.1.
==> Summary
?  /usr/local/Cellar/ruby-build/20210119: 520 files, 259.9KB, built in 4 seconds
==> Installing rbenv
==> Pouring rbenv-1.1.2.catalina.bottle.tar.gz
?  /usr/local/Cellar/rbenv/1.1.2: 36 files, 69KB
==> Caveats
==> readline
readline is keg-only, which means it was not symlinked into /usr/local,
because macOS provides BSD libedit.

For compilers to find readline you may need to set:
  export LDFLAGS="-L/usr/local/opt/readline/lib"
  export CPPFLAGS="-I/usr/local/opt/readline/include"

For pkg-config to find readline you may need to set:
  export PKG_CONFIG_PATH="/usr/local/opt/readline/lib/pkgconfig"

==> ruby-build
ruby-build installs a non-Homebrew OpenSSL for each Ruby version installed and these are never upgraded.

To link Rubies to Homebrew's OpenSSL 1.1 (which is upgraded) add the following
to your ~/.zshrc:
  export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl@1.1)"

Note: this may interfere with building old versions of Ruby (e.g <2.4) that use
OpenSSL <1.1.

長いですね〜。。
ほんでもって、ビールの絵文字?どんだけ出てくるんよw

あ、バージョン確認しましょう。

local % rbenv -v
rbenv 1.1.2

rbenvのインストール正常にできたようです!

あ、そう言えば忘れていました。一応これやっときましょうか。

local % brew doctor
Your system is ready to brew.

こちらもOKのようです。
長かった。。

最後に

初めてQiitaに投稿しましたが、めちゃくちゃ時間かかった。
もう朝やんw

定期的に書いている人尊敬する。

マークダウン記法とやらに慣れたらもっと早く書けるんでしょうな。

macのターミナルとやらもほぼ初めて状態であんまり良く分かっていない。
諸先輩方のQiita記事(例えばコレ)を見ていると、

$ brew install zsh

のように、$から始まっているんですけど、、、
ワタシのは%ですね。。

おいおい学習していきますか。。

あと最後に、シングルクォーテーションが下のように使われていると、その後の文字列みんな青いんですけど。
どうしたらいいんでしょw
image.png

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

【Rails】git push herokuをしてもEverything up-to-dateと言われる【Heroku】【Git】

症状

git push herokuをやっても、herokuに変更が反映されませんでした。

git push heroku
Everything up-to-date

「Everything up-to-date」は変更するデータないよと言われているようです。

原因を調べると、gitも更新されていなかったことがわかりました。

解決策

コミットはしたものの変更をマージしていなかったことが分かりました。。
以下のコマンドを良しなに入力して解決しました。

初歩的なミスでしたが、解消に時間がかかったので書きました。

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