20220115のRubyに関する記事は14件です。

ログインはしてるけど違うアカウントの人ができることを制限したい

ログイン自体していない人はauthenticate_user!でログイン画面に飛ばせるのは前回の記事の話。 前回の記事→https://qiita.com/JkzW1QJsgjPBxa/items/f834c85a420cd253d6bd が、ログインはしてるけど別のIDの人はこれでは防げない。 URLを直接入力しちゃえば別のIDのページに行けちゃう。 例えばID1でログインしている人がてへっとID3の人の情報を覗いたり書き換えちゃったり消しちゃったりは、authenticate_user!では防げない。 ので、コントローラーにそれを防ぐ記述をしてみたよ。 (注意:未解決部分あり) before_action コントローラーで、通ってほしいクラスを書くと、指定するアクションに行く前に通ってもらえる。 コントローラー class ActorsController < ApplicationController before_action :move_to_root, only: [:show, :create, :update] #←ココと def new @actor = Actor.find_or_initialize_by(user_id: current_user.id) end def create @actor = Actor.new(actor_params) # 略 end def show # 略 end def update # 略 end private def actor_params params.require(:actor).permit(:comment, :image).merge(user_id: current_user.id) end def move_to_root #←ココ # 中身は後で end end 今回はshow、create、updateアクションを指定しました。 今まで勉強してきた時はcreateやupdateじゃなくてnewに付ける感じだったんだけど、おそらく今回newアクションにfind_or_initializeを使っている関係で、これでは防げなかった。 ので、この書き方にしてみたんだけど、たぶん不格好というか大丈夫かな?と思っている、、、。 アドバイス等ご教授いただける方いらっしゃいましたらよろしくお願い致します。 ↑の書き方をしていると、例えばID1の人がID3の編集ページをURLで指定して入っても、更新されるのはID1の人のページという状態でした。ので、ID3の人のページを書き換えることは防げている、けどroot_pathに戻る挙動ではないので、たぶん根本的に何か違う、、、 さて。 move_to_rootの中身 こんな感じにしました。 コントローラー def move_to_root @actor = Actor.find(params[:id]) unless current_user.id == @actor.user_id redirect_to root_path end end redirect_toは↑のようにpathで指定することもできるし、アクションを指定することもできる アクションで指定する場合は def move_to_index @actor = Actor.find(params[:id]) unless current_user.id == @actor.user_id redirect_to action: :index end end こういう書き方。 まとめ まとめ、というより今回はまだもやっとしたままのアウトプット。 うまくできる方法見つけれた時、また更新します。 それでは。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyのgemのコードリーディングをする時の備忘録

rubyのgemのコードリーディングをする時によく利用したことを 備忘録としてメモ。 (随時更新する。) ※rubyのバージョンは3.1.0 gemのファイルの場所を知りたい時 $LOAD_PATH.grep(/serach_word/)で検索できる。 例)devise関連のgemの場所を知りたい場合 rails c irb(main):010:0> $LOAD_PATH.grep(/devise/) => ["YOUR_PATH/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/lib", "YOUR_PATH/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/controllers", "YOUR_PATH/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/helpers", "YOUR_PATH/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/app/mailers"] デバッグ 参考 ブレイクポイント コード内にbinding.breakを書けば止まる 例 generators/devise/install_generator.rb [12, 21] in ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/lib/generators/devise/install_generator.rb 12| 13| desc "Creates a Devise initializer and copy locale files to your application." 14| class_option :orm, required: true 15| 16| def copy_initializer => 17| binding.break 18| unless options[:orm] 19| raise MissingORMError, <<-ERROR.strip_heredoc 20| An ORM must be set to install Devise in your application. 21| =>#0 Devise::Generators::InstallGenerator#copy_initializer at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/lib/generators/devise/install_generator.rb:17 #1 Thor::Command#run(instance=#<Devise::Generators::InstallGenerator:0..., args=[]) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/command.rb:27 # and 21 frames (use `bt' command for all frames) (rdbg) 参考 バックトレース(backtrace) ブレイクポイントで止まった状態でbacktraceを入力する 出力例 generators/devise/install_generator.rb (rdbg) backtrace =>#0 Devise::Generators::InstallGenerator#copy_initializer at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/devise-4.8.1/lib/generators/devise/install_generator.rb:17 #1 Thor::Command#run(instance=#<Devise::Generators::InstallGenerator:0..., args=[]) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/command.rb:27 #2 Thor::Invocation#invoke_command(command=#<struct Thor::Command name="copy_initia..., args=[]) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/invocation.rb:127 #3 block {|_="copy_initializer", command=#<struct Thor::Command name="copy_initia...|} in invoke_all at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/invocation.rb:134 #4 [C] Hash#each at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/invocation.rb:134 #5 [C] Enumerable#map at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/invocation.rb:134 #6 Thor::Invocation#invoke_all at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/invocation.rb:134 #7 #<Class:Thor::Group>#dispatch(command=nil, given_args=[], given_opts=nil, config={:behavior=>:invoke, :destination_root=>...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/group.rb:232 #8 Thor::Base::ClassMethods#start(given_args=[], config={:behavior=>:invoke, :destination_root=>...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/base.rb:485 #9 Rails::Generators.invoke(namespace="devise:install", args=[], config={:behavior=>:invoke, :destination_root=>...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/bundler/gems/rails-958af5235874/railties/lib/rails/generators.rb:263 #10 Rails::Command::GenerateCommand#generate(#arg_rest=nil) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/bundler/gems/rails-958af5235874/railties/lib/rails/commands/generate/generate_command.rb:26 #11 Thor::Command#run(instance=#<Rails::Command::GenerateCommand:0x0000..., args=["devise:install"]) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/command.rb:27 #12 Thor::Invocation#invoke_command(command=#<struct Thor::Command name="generate", ..., args=[["devise:install"]]) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor/invocation.rb:127 #13 #<Class:Thor>#dispatch(meth="generate", given_args=["devise:install"], given_opts=nil, config={:current_command=>#<struct Thor::Comman...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/thor-1.1.0/lib/thor.rb:392 #14 #<Class:Rails::Command::Base>#perform(command="generate", args=[], config={:current_command=>#<struct Thor::Comman...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/bundler/gems/rails-958af5235874/railties/lib/rails/command/base.rb:87 #15 Rails::Command.invoke(full_namespace="generate", args=[], config={:current_command=>#<struct Thor::Comman...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/bundler/gems/rails-958af5235874/railties/lib/rails/command.rb:48 #16 <main> at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/bundler/gems/rails-958af5235874/railties/lib/rails/commands.rb:18 #17 [C] Kernel#require at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23 #18 block in require_with_bootsnap_lfi at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23 #19 Bootsnap::LoadPathCache::LoadedFeaturesIndex#register(short="rails/commands", long="/Users/k.nakama/.rbenv/versions/3.1.0/l...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:100 #20 Kernel#require_with_bootsnap_lfi(path="rails/commands", resolved="/Users/k.nakama/.rbenv/versions/3.1.0/l...) at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22 #21 Kernel#require(path="rails/commands") at ~/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31 #22 <main> at bin/rails:5 (rdbg) ステップオーバー(next) ブレイクポイントで止まった状態でnもしくはnextを入力する。 メソッド内部には入らない。 ステップイン(step) ブレイクポイントで止まった状態でstepを入力する メソッド内部に入る。 ウォッチャ(display) ブレイクポイントで止まった状態でdisplay 確認したい変数orクラスなどを入力する undisplayで登録したdisplayをすべて解除。 undisplay 番号で指定番号のdisplay解除 gemのコードを修正したのを元に戻す bundle pristine 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】海とサーフィンが好きすぎて、海のように見え方が変わるサーフィン日記共有サービスを作ってみた。

はじめに 天気が雪の日は雪が降ります。 サーフィンをしたことがありますか? 一度きりの人生、自然と一体になれるこんな素晴らしいスポーツをやってみる事もしないなんて勿体ない事です。 私は海とサーフィンを愛しています。 自身の大好きなものを題材に作品を作って誰かの心に届くものにしたい!そんな想いがあります。 初めまして。りゅうじと申します。 【Twitter】 https://twitter.com/otokomigakimasu 【今回作ったサービス】 https://www.namikki.jp 私はエンジニアの方は全員海の近くに住んでサーフィンをした方がいいと思っています。 メリット 都会で家賃が高い→海の側なら家賃が安い ジムで筋トレ→サーフィンで筋トレ 日頃PCから電磁波を浴びている→サーフィン(素足で浜辺を歩くことでアーシングする) 散歩して日光を浴びてセロトニンを出したい→サーフィンは太陽光を浴びられます サウナと水風呂で整いたい→サーフィンは本当に整います ダイエット→サーフィンすれば痩せて引き締まります メンタルが不安定→サーフィンすると気持ちが明るくなります アンチエイジング→サーフィンしてる方は皆さん若々しいです これら表面的なものは付加価値です。 実際にやってみると本質はここではありません。 自然に発生する波と一体になる感覚は筆舌に尽くし難いものです。 サーフィンの道具を買うといった初期投資も、家賃の差額とジムに通っている方ならジムの会員費1年分あれば十分元が取れてしまうでしょう。 正直きっかけはなんだっていいのです。 さあ、サーフィンしましょう! Namikki(なみっき) https://www.namikki.jp/ サービス概要 愛知県田原市の伊良湖エリアのサーファーがサーフィン日記を共有できるSNSサービスです。 エリアを限定して始動しましたが、これから私が様々な地域でサーフィンする中で拡張していく予定です。 1. 田原市の3時間ごとの天気予報5日間分を表示 2. 投稿機能 (無限スクロールにしました) サーファーはスマートフォンからSNSに投稿される方が多いので、スマホファーストの実装しています。 そのため、無限スクロールを採用しました。 3. いいね・コメントがあった場合に通知機能をつけました。 自身の投稿・自身がコメントした投稿したコメントに反応があると右上のベルに赤いマークがついて通知がきたとわかるようにしました。 このサービスが交流のきっかけになってもらえたらと思い実装しました。 通知ページ 使用技術 Ruby 2.7.2 Rails 6.1.4.1 JavaScript 主なGem better_errors bullet sorcery carrier_wave rails_admin factory-bot capybara pry-byebug rubocop テスト RSpec フロントライブラリ JQuery ストレージサービス Amazon S3 ER図 こだわったポイント 海はその日の天気によって見え方が変わるので Namikkiも同様にするために…。 取得した天気予報に応じて背景画像を変化させました 雨の日 雷雨 雪の日 今後実装したい事 今後、自身が実際にサーフィンしたエリアも追加します 返信相手を指定してその人にのみ通知がいくようにする(現状は投稿にコメントした人全員に通知がいきます) 終わりに 最後まで読んでいただきありがとうございました。 これからサーフィンを始める方にとっては、初めてサーフィンをした日記からつけていただき感じた事を記録しておくと財産になります。 今回リリース時では私の地元の海、伊良湖エリアを限定にしたサービスにしました。 実際にこれから、様々な海でサーフィンしていき、その都度このサービスを拡張させていきたいと考えています。 自分の作った作品と共に成長したい!と考えています。 【Twitter】 https://twitter.com/otokomigakimasu 【今回作ったサービス】 https://www.namikki.jp
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]バイナリーサーチとは

学習したことのアウトプットとして バイナリーサーチを用いた問題、理屈はわかるが、実際自分でコードを描こうとすると脳が混乱…。 ので、自分なりに整理してみようかと。 ”バイナリーサーチ”とは ソート済みのリストや配列に入ったデータ(同一の値はないものとする)に対する検索を行うときに用いられる手法。 中央の値を確認し、検索したい値との大小関係を用いて、検索したい値が中央の値の右にあるか、左にあるかを判断。 それを繰り返し、片側には存在しないことを確かめながら検索していく方法。 例)ある配列にある13の位置を知りたい 2 3 5 6 8 10 11 13 16 4番目の"8"が要素の中央。13は8より大きいので、次は"10"を左端として検索 ↓ 10 11 13 16 6番目の"11"が要素の中央。13は11より大きので、次は"13"を左端として検索 ↓ 13 16 7番目の"13"が要素の中央。検索したい13と合致 ↓ 「13は配列の7番目」 という風な感じ。理屈自体は難しいものではないかと。 要素数が偶数の場合、「中央」は数が少ない方(左側)が優先されるのか? 詳しい人がいらっしゃればコメントいただけると幸いです。 問題 以下の配列に任意の値が存在するかどうか、そして何番目に存在するのか、検索するコードを作成しましょう。 添字が0の要素、つまり以下の配列における「1」は「配列の0番目に存在する」と表現します。 array=[1,3,5,6,9,10,13,20,26,31] 任意の値が配列内に存在しない場合は、「値は配列内に存在しません」と表示し、 存在する場合は、配列の何番目にあるかを表示してください。 ※配列の上限である32以上の値による検索は想定しないものとする。 模範解答 def binary_search(array, right, target) left = 0 while left <= right center = (left + right) / 2 if array[center] == target return center elsif array[center] < target left = center + 1 else right = center - 1 end end return -1 end array=[1,3,5,6,9,10,13,20,26,31] puts "検索したい数字を入力してください" target = gets.to_i number_of_elements = array.length result = binary_search(array, number_of_elements, target) if result == -1 puts "#{target}は配列内に存在しません" else puts "#{target}は配列の#{result}番目に存在します " end 解説 1〜14行目 def binary_search(array, right, target) left = 0 while left <= right center = (left + right) / 2 if array[center] == target return center elsif array[center] < target left = center + 1 else right = center - 1 end end return -1 end 検索したい値との大小関係を用いて、検索したい値が中央の値の右にあるか、左にあるかを判断して、片側には存在しないことを確かめながら検索を行う処理を行ってる。 たとえば、targetとして"20"を入力した場合を考えてみる 1回目のループ def binary_search(array, right, target) #(配列,10(配列の数),20) left = 0 while left <= right #0 <= 10 はtureで処理が実行される center = (left + right) / 2 #要素の中央  (0+10) / 2 = 5 if array[center] == target #array[5]は"10"でfalseなので、この処理はされない return center elsif array[center] < target #trueなのでこの処理が実行される left = center + 1 #2回目のループではleft=6(配列の中央から右側に検索範囲を絞る) else right = center - 1 end end return -1 end 2回目のループ def binary_search(array, right, target) #(配列,10(配列の数),20) left = 6 #1回目のループより while left <= right #6 <= 10 はtureで処理が実行される center = (left + right) / 2 #要素の中央  (6+10) / 2 = 8 if array[center] == target #array[8]は"26"でfalseなので、この処理はされない return center elsif array[center] < target #26 < 20 でfalse left = center + 1 else right = center - 1 #ここの処理がされる。3回目のループではright=9(1回目で絞った配列からさらに中央から左側に検索範囲を絞る) end end return -1 end 3回目のループ def binary_search(array, right, target) #(配列,10(配列の数),20) left = 6 #1回目のループより while left <= right #6 <= 9(2回目のループより) はtureで処理が実行される center = (left + right) / 2 #要素の中央  (6+9) / 2 = 7 if array[center] == target #array[7]は"20"でture return center #7 が返り値となる elsif array[center] < target left = center + 1 else right = center - 1 end end return -1 end 13行目のreturn -1は何も当てはまるときがないときに、最終的な返り値になる。 返り値を元に条件分岐を利用して出力内容を設定する。 記事を書いて、少し自分の中でも整理できたのでよかった!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

each.with_indexメソッド(Ruby)

Ruby3.1 Enumeratorクラスwith_index Enumerableモジュールeach_with_index 要素にインデックスを添えて繰り返す a = ["a","b","c"] a.each.with_index(2) do |s, i| puts "#{i}:#{s}" end 2:a 3:b 4:c #each.with_index(引数)とすることで、引数を受け取る。 #each_with_indexは引数を受け取らないため、添字は0から始まる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】kaminariとboostrap4を使ってページネーションをする

ページネーションとは? ページネーションとは画像の通り、検索した時などの下に出てくる、今は何ページ目…と表記してくれるもの。 railsでこのページネーションを導入する際にはkaminariと言うgemを使っていく。 手順 ①kaminariのインストール ②ページネーションデザインのデフォルトの設定ファイルを作成/編集 ③コントローラーの編集 ④viewの実装 ①kaminariのインストール 今回はboostrap4を一緒に使っていくので、Gemfileにkaminariとbootstrap4-kaminari-viewsを記入し、bundle installする。 Gemfile. gem 'kaminari' gem 'bootstrap4-kaminari-views' → bundle install ②ページネーションデザインのデフォルトの設定ファイルを作成/編集 ターミナルにて、設定ファイルの作成コマンドを実施。 ターミナル. rails g kaminari:config config/initializers/kaminari_config.rbを開くと、下記がデフォルトで入っている。 基本的にコメントアウトされているので設定したいもののコメントアウトを外していく。それぞれの意味は下記に記載していく。 kaminari_config.rb # frozen_string_literal: true Kaminari.configure do |config| # config.default_per_page = 25         1ページあたりの表示件数(デフォルトは25レコード) # config.max_per_page = nil         1ページあたりの最大表示件数(デフォルトはnil。つまり無限) # config.window = 4        現在のページから、左右何ページ分のリンクを表示させるか(デフォルトは4件) # config.outer_window = 0        最初(First)と最後(Last)のページから、左右何ページ分のリンクを表示させるか(デフォルトは0件) # config.left = 0        最初(First)のページから、何ページ分のリンクを表示させるか(デフォルトは0件) # config.right = 0        最終(Last)ページから、何ページ分のリンクを表示させるか(デフォルトは0件) # config.page_method_name = :page        モデルに追加されるページ番号を指定するスコープの名前:page by default # config.param_name = :page        ページ番号を渡すために使用するパラメータ名(デフォルトは:page)        ↑Board.page(params[:page])のようにparamsメソッドで取得できる。 # config.max_pages = nil        最大ページ数(デフォルトはnil) # config.params_on_first_page = false        最初のページでparamsを無視しない end ③コントローラーの編集 ページネーションを表示させたいところに.page(params[:page])を追加。 例えばBoardにページネーションをつけたい場合、以下のように記載。 boards_controller.rb def index @boards = Board.all.includes(:user).order(created_at: :desc).page(params[:page]) end ここの表記に.per(20)を付け足たら以下のようになる↓ @boards = Board.all.includes(:user).order(created_at: :desc).page(params[:page]).per(20) ただし、1ページにこの場合だと最大20件と設定できるが、毎回ページネーションを新たに追加するごとに同じように記入しなければならないので、出来ればコントローラーに記載した方が、DRYな記法になる。 ④viewの実装 あとはページネーションさせたいviewに下記を追記するだけで出来る。 index.html.rb <%= paginate @boards %> ページネーションのデザイン変更 先程のだけだと少しシンプルすぎるので、先程のviewに以下を書き足す。 index.html.rb <%= paginate @boards, theme: 'twitter-bootstrap-4' %> 結果きれいに装飾され見やすくかつボタンが押しやすくなった。 またこれとは別に、cssを編集してさらに自分好みのデザインに変更することも可能。 ラベルを日本語に変更 ラベルとは、ページ番号の前後についているFirstやPrveのこと。 デフォルトでは英語表記なので、こちらを日本語に変更したい場合はrailsアプリのデフォルト言語を日本語に変更したあと、ja.ymlにてそれぞれ編集する必要がある。 ▶︎デフォルトの言語を日本語に設定 config/localesに日本語変換用のja.ymlを作成。下記のは一例。 config/locales/ja.yml ja: views: pagination: first: "&laquo; 最初" last: "最後 &raquo;" previous: "&lsaquo; 前" next: "次 &rsaquo;" truncate: "..." ラベルのアイコン化 ラベルをアイコンのみにすることで、よりシンプルなページネーションを作ることができる。 Font Awesome使った例を記載。 はじめに、Font Awesomeを読み込むために, app/views/layouts/application.html.erbに以下のコードを追加して、アプリ全体にFont Awesomeを適用。 app/views/layouts/application.html.rb #コード追加 Font Awesome読み込みコード <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous"> あとは先程出てきたja.ymlファイルに使用したいアイコンのコードに書き換えるのみでOK。 << < 1 2 3 .....9 10 > >>のようになる。 config/locales/ja.yml ja: views: pagination: first: <i class="fas fa-angle-double-left"></i> last: <i class="fas fa-angle-double-right"></i> previous: <i class="fas fa-angle-left"></i> next: <i class="fas fa-angle-right"></i> truncate: "..." 参考記事 【Rails】kaminariの使い方をざっくりまとめてみた 【Rails初心者】ページネーションを実装して自分好みにデザインを変える Kaminariの使い方 まとめ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【共同開発】欲しいものを買う優先度をランキング形式で提示するサービス「HoshiMe」をリリースしました!

はじめに 早速ですが質問です。衝動買いをして後悔した経験はありませんか? 私はよく後悔しています。。 ずっと欲しかったものがあったにも関わらず、違うものを衝動買いしてお金を使ってしまい、本当に欲しかったものを買うためのお金がなくなってしまったことがありました。 そんな悩みを解決するために開発したのが、欲しいものを買う優先度をランキング形式で提示するサービス 『HoshiMe』(ほしみぃ) です。 使用技術 バックエンド - Ruby 2.6.6 - Rails 6.1.4 フロントエンド - TailwindCSS - JavaScript(jQuery) - Chart.js 主なサービス内容 欲しいもの登録ページ ユーザ登録なしで欲しいもの3つ登録できるように実装しました。 ランキングページ 欲しいものを登録すると、以下のランキング形式で買う優先度が表示されます。また、ランキング上位3つ分の計算データをレーダーチャートを使って差が分かるように表示しています。 こだわったポイント ゲストユーザ機能 HoshiMeでは、できる限りたくさんのユーザの方々に、まず触って頂きたいという思いから、「はじめる」を押した時点でゲストユーザとしてログインされる仕様になっています。 TRANSACTION (0.1ms) BEGIN User Load (4.9ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]] => #<User:0x00007fe3bd20af00 id: 108, name: "ae2b4e", email: "guest_ae2b4e@example.com", crypted_password: "$2a$10$/3SasIg85bnveT0IK/VIIOvEoAQ09QBK7XjrO5mvIs.BozatxJpp2", salt: "b9E3njyFrvUAkHuXmzvW", uuid: "448fb42e-51df-4a35-bf8f-45c28cbe0a08", created_at: Sat, 15 Jan 2022 14:46:11.513789000 JST +09:00, updated_at: Sat, 15 Jan 2022 14:46:11.513789000 JST +09:00, is_valid: true, role: "guest", reset_password_token: nil, reset_password_token_expires_at: nil, reset_password_email_sent_at: nil, access_count_to_reset_password_page: 0> また、ユーザ新規登録したときにゲスト時に作成したデータをユーザ新規登録時にそのまま引き継ぐ仕様になっています。 アイテム一括投稿フォームの実装 入力フォームを複数用意し、submitボタンが押されたらフォームの内容をまとめて保存できるように実装しました。Form Objectで定義しているitemsでフォームヘルパーのfields_forを使ってアイテムの情報を下記のようなハッシュ形式で受け取ります。 "form_item_collection"=>{"items_attributes"=>{ "0"=>{"name"=>"Apple Watch", "degree"=>"60", "start_date"=>"180", "finish_date"=>"180", "price"=>"50000"}, "1"=>{"name"=>"リング", "degree"=>"30", "start_date"=>"30", "finish_date"=>"360", "price"=>"31000"}, "2"=>{"name"=>"キーボード", "degree"=>"70", "start_date"=>"360", "finish_date"=>"10", "price"=>"21000"}}} そしてこの送信された一つずつのアイテムのuser_idにcurrent_user.idを付与して一括で保存する仕組みを作成しました。 jRangeを使った範囲選択フォーム フォームヘルパーを使用して、レンジ入力をしたいときは下記のようにrange_fieldを使います。 <%= f.range_field :degree, min:"0", max:"100" %> しかしこのままでは目盛りがないので、どれくらいの範囲を選択しているのか分かりづらいです。 目盛りをつけるためにjRangeというjQueryというライブラリを導入しました。 ランキング機能 欲しいものを保存すると同時に保存した値を使ってランキングに必要な計算をしています。 その部分について簡単に説明します。 素直に入力された各パラメータ(欲しい度、いつから欲しい?、いつまでに欲しい?、値段)を合算してランキングにすると、値段の値(0~100,000)が大きすぎて値段のランキングみたいになってしまうので、各パラメータを変換します。 以下は「欲しい度」を計算するコードです。 # data にはユーザが入力した値が入る def calculate_degree(data) case data when 0..20 degree_value = 2 # 欲しい度が 0~20 なら2に変換 when 21..40 degree_value = 4 # 同様に 21 ~ 40 なら4に変換 when 41..60 degree_value = 6 when 61..80 degree_value = 8 when 81..100 degree_value = 10 end degree_value * 0.4 end このように各パラメータを5分割にし2、4、6、8、10に変換します。 計算の最後に出てくる×0.4の係数ですが、これはそのパラメータの重要度によって値を変えています。 重要度が高いものから順に、 いつまでに欲しい? : 0.8 いつから欲しい? : 0.6 欲しい度 : 0.4 値段 : 0.2 欲しいものを登録する際の「いつまでに欲しい?」が一番ランキングに影響度が高く、「値段」は一番影響度が小さいことになります。 このように各パラメータを換算して係数をかけたものを合計し降順にしたものがランキングとなります。 共同開発の目的 私たちが共同開発をやった目的は大きく分けて次の3つです。 実務に入る前に共同で開発する時の流れや、雰囲気を知ること メンバー間でコミュニケーションを取りながら協力してひとつのものを作るという経験をすること GitやGitHubなどをより使えるようになること 今回3人で開発したのですが、難しい技術を使って実装するとか、大きなアプリを作るとかではなく、みんなで協力しながら楽しくやることを念頭に置いて取り組みました。 最後に Twitter : sayo https://twitter.com/sayo_saru 今回のチーム開発を通して、誰がみても理解できるPRを書くこと、チームメンバーのPRを丁寧にレビューすることの大切さを痛感しました。毎週行っていたチーム開発会議で、本音で自分の考えていることを言い合えるメンバーだったので、とても楽しく開発ができました。 ありがとうございました。 Twitter : ワタル https://twitter.com/wataru_pgm コンフリを起こした時の対処法、プルリクの書き方、チームが上げたプルリクをレビューするため、チェック用のブランチを作成して、挙動を実際に確かめるなど、たくさん繰り返し、多くのことを学ぶことができました。 新年の年明けと同時にHoshiMeをリリースすることができ、チームHoshiMeと使っていただいたユーザの方々に感謝です! Twitter : たけむー https://twitter.com/takemuu_pote 今回初めて共同開発をやってみて、楽しかったというのが率直な感想です。 アイディア出しから始まり、画面遷移図とER図作成、実装してリリースまで3人で議論を重ねながら一緒にやり切ることができたのはとても良い経験になりました。 上記で述べた目的も達成でき、目標としていたあけおめリリースもできたので満足しています。 今回この3人で一緒にやれて本当に良かったと思います。 また機会があればやろうと思います。 最後まで読んでいただきありがとうございました。 今回作ったアプリ あけおめリリースツイート
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】特定の文字列を検知 check_nameメソッド

※初心者向け ※アウトプット練習の為 開発環境 rails 6.0.4.4 ruby 2.6.5 概要 対象の文字列の中に特定の値が含まれているかどうかを検知するプログラムを実装。 問題内容 以下の要件を満たすcheck_nameメソッドを実装しましょう。 ・名前を入力すると「登録が完了しました」という文字列を出力すること ・名前の中にピリオド(.)がある場合は、「 "!エラー!記号は登録できません"」という文字列を出力すること ・名前の中に空白(半角のみ)がある場合は、「 "!エラー!空白は登録できません"」という文字列を出力すること ※今回空白に関しては、半角スペースのみを考えることとします。 雛形 def check_name(str) # 処理を記述 end puts "登録したい名前を入力してください(例)YamadaTaro" str = gets check_name(str) 出力例 YamadaTaro → 登録が完了しました Yamada.Taro→!エラー!記号は登録できません Yamada Taro → !エラー!空白は登録できません ヒント include?メソッドを使用しましょう。 include?メソッド include?メソッドは、指定した値が配列や文字列内に含まれているかを判定するメソッドです。指定した値が含まれている場合はtrueを、含まれていない場合はfalseを返り値として返します。 解答内容 def check_name(str) if str.include?(".") puts "!エラー!記号は登録できません" elsif str.include?(" ") puts "!エラー!空白は登録できません" else puts "登録が完了しました" end end puts "登録したい名前を入力してください(例)YamadaTaro" str = gets check_name(str) 解説 1行目から9行目でcheck_nameメソッドを定義し、その定義したcheck_nameメソッドを13行目で呼び出しています。この際、引数にはgetsメソッドで入力された文字列を渡しています。 check_nameメソッドでは、「ピリオドや空白(半角スペース)がない場合は登録を行い、ピリオドや空白がある場合はエラーを出す」という条件分岐を行うためにif文を使用しています。 2行目から8行目にわたるif文による条件分岐では、1行目の仮引数strで受け取った文字列に対してinclude?メソッドを使用し、 ”.”(ピリオド)と ” ”(半角スペース)がないかを判断しています。 注意点として、if文は条件が当てはまった時点で処理が終了します。 そのため、「ピリオドや空白があるかどうか」という条件式を先に記述しましょう。 そして、出力例を参考にそれぞれの場合で出力させる値を、putsメソッドを用いて記述しましょう。 これで、コードは完成です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】rails routesの見方

rails routesの見方 現在のルーティングを確認する際にroutes.rbフォルダを見に行く方法もありますが、以下のコマンドでも確認できます。 $ rails routes 上記コードを入力すると下のような画面がターミナル上で表示されます。 Prefix:URLのニックネームです。(routes.rb内で「as:ニックネーム」で設定したものがここに表示されます) Verb:HTTPメソッドです。 URI:URL(例 /books) ※(.:format)というのは、/books.htmlとしてもいいよという意味です。htmlがフォーマットです。 Controller#Action:コントローラーとアクションを意味しています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文字列を作るときに%でも作る方法を見つけたので備忘録

目的 文字列を作るときに%でも作る方法を見つけたので備忘録 感想 使うんかなぁ %q! ! シングルクオートで囲んだことになる %q!! puts %q!Hello world! #=> Hello world %Q! ! ダブルクオートで囲んだことになる %Q!! it = “this” puts %q!#{it}Hello world! #=> this Hello world %! ! ダブルクオートで囲んだことになる %!! it = “this” puts %q!#{it}Hello world! #=> this Hello world 参考 プロを目指す人のためのRuby入門書
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

present?メソッドとnil?メソッドの使い方

はじめに  今回はpresent?メソッドとnil?メソッドについてまとめます。どちらも条件分岐の時に便利なメソッドです。条件分岐どうしようと悩んだ時に、このメソッドで解決できたのでまとめます。 この記事は学習備忘録です。 present?メソッドとnil?メソッドの違い  present?メソッドとnil?メソッドは、オブジェクトに対してtrueとfalseを返すメソッドです。 使用例としては present?メソッド→@testsに値が入っている時trueが返される qiitq.rb @tests.present? #実行結果 => true nil?メソッド→@testsに値が入っていない時trueが返される qiitq.rb @tests.nil? #実行結果 => true このメソッドを利用して条件分岐を作る事ができます present?メソッドとnil?メソッドで条件分岐を作る  present?メソッドとnil?メソッドを使って、 以下のようにインスタンス変数に中身がある場合は商品を表示させて、中身がない場合は代わりの画像を表示させる実装があります。 qiita.rb <% if @tests.present?%> <% @tests.each do |test| %> <%= test.title %> <% end %> <% else %> 値なし <% end %> 上記はpresent?メソッドを使っているので、 実行すると@testsの中身がある場合は値が表示され、中身がない場合は「値なし」と表示されます。 (代わりに、nil?メソッドを使う方法でも条件分岐を作れます。) まとめ  今回はpresent?メソッドとnil?メソッドについてまとめました。どちらも簡単に条件分岐を行う事ができる便利なメソッドなので、使い方をマスターしたいと思います。 この記事が少しでも誰かのお役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】RSpecの導入手順(メモ)

はじめに チーム開発でテストツールとしてRSpecを導入することになったので調べたことを記録したいと思います。 環境 Ruby 3.0.3 Rails 6.1.4 rspec-rails 5.0.2 spring-commands-rspec 1.0.4 1. 必要モジュールのインストール RSpecをRailsアプリで使用するためにはrspec-railsというgemが必要です。 また必須ではありませんが、RSpecのテストをより高速に実行するためにspring-commands-rspecもインストールしておきます。 インストールグループとしてtestだけでなくdevelopmentも追加することでテストファイルを作成するgeneratorを使用することができます。 Gemfile group :development, :test do gem 'rspec-rails' gem 'spring-commands-rspec' end bundle installでGemfileに記載したgemをインストールします $ bundle install 2. spring-commands-rspecの設定 以下を実行してbin/railsが使用できるように設定します $ bundle exec spring binstub rspec 3. RSpecの設定 3-1. 以下のコマンドを実行して必要ファイルを生成します。 $ rails g rspec:install # 以下のファイルが生成される create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb 生成されたディレクトリ、ファイルの役割は以下の通りです。 .rspec 基本設定ファイル spec specディレクトリ配下にあるテストファイルがテストとして実行される spec/spec_helper.rb Rspecの全体的な設定を記述するファイル spec/rails_helper.rb Rails特有の設定を記述するファイル 3-2. .rspecの編集 .rspec --require spec_helper --format documentation # 追加 --require spec_helper デフォルトで記述されており、これから作成するspecファイルで毎回require '--spec_helper'と記述する必要がなくなる --format documentation 記述することで、テストコードの実行結果をターミナル上に可視化してくれる 他にもテスト結果を色付けしてくれる--colorなど様々なオプションが用意してある 3-3. 動作確認 今回は簡易的にPostというモデルのオブジェクトを生成したとき中身がnilにならないことをテストしたいと思います. spec/models/post_spec.rb require 'rails_helper' # 設定ファイルrails_helper.rbを読み込むコードが全テストにあります RSpec.describe Post, type: :model do it "Postモデルをnewしたとき, nilでないこと" do expect(Post.new).not_to eq(nil) end end テストを実行します $ bin/rails spec しっかりとテストできていることが確認できます。 その他 $ rails g [command] ~と$ rails g rspec:[command] ~の違い $ rails g [command] ~ RSpecをインストールした時点でrails g model Postを実行すると以下のようにマイグレーションファイルやモデルファイルに加えてRSpec用のテストファイルを生成してくれる デフォルトで生成されていたminitest用のテストファイルは生成されなくなる $ rails g model post invoke active_record create db/migrate/20220110190849_create_posts.rb create app/models/post.rb invoke rspec create spec/models/post_spec.rb # <- モデルのspecファイルが生成される $ rails g rspec:[command] ~ rails g rspec:model Postを実行するとマイグレーションファイルやモデルファイルは生成されず、RSpec用のテストファイルのみ生成される 開発の途中でRSpecを導入したりする際など、すでにモデルが存在する場合に使うとよい $ rails g rspec:model post create spec/models/post_spec.rb 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアル(6版)の13章で_form.html.erbを使っていた場合の戸惑い解消

はじめに プログラミング初学者の@kat_logと申します。 Railsチュートリアル(Rails6版)の13章「13.3.2マイクロポストを作成する」の「リスト 13.43、13.44」で戸惑った部分について共有です。 戸惑ったポイント 10章の「10.1.1編集フォーム」の演習2をやった場合、new.html.erbとedit.html.erbの一部を_form.html.erbとしてパーシャルにするため、テキストと違いが出て、どの部分をf.objectにするか戸惑いました。 結論 new.html.erbとedit.html.erbは書き換え不要で、_form.html.erbを1箇所変えるとOKです。 _form.html.erb <%= form_with(model: @user, local: true) do |f| %> <%= render 'shared/error_messages', object: f.object %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit yield(:button_text), class: "btn btn-primary" %> <% end %> おわりに お読みいただきありがとうございました。 自分と同じくrailsチュートリアル学習中の方の参考になれば嬉しいです。 一緒にプログラミング学習頑張っていきましょう〜?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 中級者になるために絶対に理解しておかなければならない知識

こんにちは!石田佳介です! 今回の記事では、RoR初心者が中級者になるために絶対に抑えておかなければならないことを記事にします。 それは、オブジェクト、インスタンス、インスタンス変数、クラス この4つを人に説明できるようになるくらい理解していないと絶対に更なる高みにはいけません。 それはなぜか?理由は、めちゃくちゃ大事な基礎であり、RoRの根幹になるものだからです。いくら多くのメソッドをおぼえても、上の4つを完璧に理解していなければ、実務に入ってもいつか頭打ちがきます。 こんな言っている僕ですが、ぼくも最近理解したので、心配しないでくださいね(笑) 今回使用する言語はrubyを使うので、ご了承ください それでは本題に入りましょう そもそもオブジェクトって? この言葉よく聞くとおもいますが、そんな難しいことを言っているわけではありません。よく「モノ」と表現されます。現実世界でイメージしてみるとよく分かると思いますが、世の中には、モノであるあふれてますよね。スマホ、パソコン、本、等々あげたらキリがありません。 それはわかったけど、プログラミングの話をしてくれよ!っていうツッコミが出てくるとおもうので、もう少しプログラミングチックな話にしていきましょう 実際にターミナルに打って確かめる rubyをインストールしている方は、コマンドで irb と入力すると、コンソールが開きます。 まずは、簡単なオブジェクトからいきましょう。その一つが文字列です。文字列とは簡単にいうと、人間が普通にしゃべっている言葉のことです。 コマンドで、山田太郎と入力すると "山田太郎" => "山田太郎" と出力されます。ここで、僕らと、コマンドの間でどういう会話が生まれたかというと、 僕ら:山田太郎というオブジェクトを作って、ここに置いておくね!ターミナル:山田太郎というオブジェクトを作って、ここに置いておくのね!おっけ! というような会話が生まれています。この会話によって、「山田太郎」というモノが作られたわけです。*(誤解を招かないようにいうと、ターミナルじゃないと、オブジェクトが作られないわけじゃないです。) 要は何を言いたいかというと、rubyはあらゆる文字や数字がモノ、つまりオブジェクトとして扱われるよ!ということなんです。このようにあらゆるものがオブジェクトとして扱われ、使われることをオブジェクト指向といいます。でも、皆さんのなかには、「石田よ、言ってることは分かった。でも、あらゆるものがオブジェクトになると何がいいの?」って思う人が多いとおもいます。ここで理解しなければいけないのが、インスタンス変数というものです。変数というものは、プログラミング勉強していたら分かるとおもいますが、インスタンスというものは中々理解されないところだとおもいます。そして、インスタンスというものを理解するためには、クラスというものを理解しなければいけません。 クラスとインスタンス まず、クラスとは設計書とよくいわれれます。現実世界でイメージしてみましょう。たとえば、家を作るための設計書があるとします この設計書をもとに、あらゆる家を作っていきます。たとえば、洋風な家、和風な家、お城みたいな家などなどできるわけです。このように洋風な家、和風な家、お城みたいな家それぞれをインスタンスと呼ぶわけです。洋風な家もインスタンス、和風な家もインスタンス、お城みたいな家もインスタンスになるんです。インスタンスを和訳すると、「実物」となるので和訳もぴったりですね。 では、プログラミングの話で考えてみましょう。Userというクラスがあるとします。そこでこんなようなコードを打ってみましょう User.create(name: “John”, email: “John@example.com”) (モデルにこのカラムがあると仮定しています)このようにすると、Userというクラス(設計書)から、 nameがJohnでemailがJohn@example.comという具体的なモノ、つまりインスタンスができるわけです 長くなりましたが、インスタンスとはこのようの具体的なモノなんですでは話は戻ってインスタンス変数とは何か。ここまでついてこれた方は何となくわかるとおもいます今まで見てきたような具体的なモノ、つまりインスタンスが格納されている変数、それがインスタンス変数なんです。そしてインスタンス変数はそのインスタンス固有の変数です オブジェクト指向とは? オブジェクト指向の話に戻ります。これまでの話を総括すれば、オブジェクト指向のメリットは分かるとおもいます。これまでの話で、クラス、インスタンス、そしてインスタンス変数を説明しました。そしてこれらって、どこでも使いまわせるんですね。クラスでいえば、継承(これも非常に重要な概念なので、また記事にします。)、インスタンスであれば、具体的なものを変数に渡すインスタンス変数。 オブジェクト指向の最大のメリットはこれですね。(ほかにもメリットはありますが。) 逆に使いまわしができないとどれくらいめんどくさいかっていうと、思い浮かべてほしいのが、ターミナルですね あれって、全く使いまわしができないじゃないですか。100回前にかいたあの長い記述を呼び出したい時に、それをまた書かなきゃいけない。マジだるいですね それを楽にしてくれるのがオブジェクト指向っていうものなんです。 なんとなくわかってくれましたかね?この記事を読んで、少しでもオブジェクト指向を分かってくれた人がいたらうれしいです!ありがとうございました!ここ間違っているよ!とかあったら、遠慮なくコメントしてくださいね!この記事では、プログラミングに関すること、技術系をメインに発信しています。Twitterフォローもお願いします!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む