20210914のRailsに関する記事は13件です。

誰が入っても、 回るエンジニア組織を作るためにすべきこと

前書き これまで数社エンジニア組織を見てきて、うまく回っている組織には特徴があると思ったので、まとめました。 良い会社に勤めてらっしゃる方は、このぐらいのこと普通にされていると思うのでスルーしてくださいw 目次 問題1: バグが出る 解決策1: 単体テストと総合テスト 問題2: レビューがしづらい 解決策2: yardコメント 問題3: 課題に対する具体的な解決策がない 解決策3 KPT振り返りをする 問題1: バグが出る どんな時にバグが発生する? - rebase - merge - 抽象化されたメソッドの変更 - ライブラリのバージョン変更 - 環境変数の欠損 - 機能追加によるソースコードの変更 悪い解決策 一人一人が注意する ※ このような解決策では属人的な解決策になってしまい、メンバーが抜けた時に、うまく回らなくなる。 解決策1: 単体テストと総合テスト テストが大事だとはわかっているが、テストをしない理由は工数がかかるから。 ですが、それでバグ対応に追われたりする工数や、致命的なバグで顧客に迷惑をかけるくらいなら、テストに工数をかけた方が良い。 テストをどのくらいやるかチーム内で最低限ラインを決めてやった方が良い。 最低限ライン 最低限このぐらいはした方が良いと思う。 単体テスト 検索で起きうるパラメータ。 アクションからレスポンスがあるか? 総合テスト モンキーテストだと漏れがあるので、 excelやspreadsheetなどで実装者が、テストケースを作成 問題2: レビューがしづらい 解決策2: yardコメント(Rails) Railsを使っているのであれば、yardコメントをすることをお勧めする yardコメントとは、メソッドに、 メソッドの概要、返り値の型と内容、引数の型と内容などを書くものです。 型を入れない言語では特に、このようなコメントアウトを入れるルールをチーム内で作った方が良いです。 # 日本語形式の日付に変換 # @param [Date] date 日付 # @return [String] 日付を YYYY年MM月DD日 の形式にしたもの # @return [nil] 引数が Date 型以外の場合は nil def convert_jp_date(date) (date.class == Date) ? date.strftime('%Y年%m月%d日') : nil end yardコメントのメリット 実装者は実装中はどの型なのか意識しているので、コメントを追加することへの、 負担は少なく、実装者以外のレビューへの負担は減ります。 新しく入ったエンジニアはメソッド名からはわからない概要を掴むことができます。 yardコメントのデメリット 実装者の負担が多少増える。 参考 Rails/Rubyドキュメントをキレイに生成するYARD、早見表付き! https://morizyun.github.io/blog/yard-rails-ruby-gem-document-html/index.html 問題3:課題に対する具体的な解決策がない 「課題: バグが出ました」という課題に対して、 よくある事例として、「気をつけましょう」、「個人を問い詰める」などがありますが、 これでは、あまり解決策になってないです。 課題: バグが出ました 課題に対する対処 - 気をつけましょう - 個人を問い詰める 解決策3: KPT振り返り KPT振り返りとは、このような項目で、週次でチーム内で意見を出しあう - K:keep = 良かったこと(今後も続けること) - P:problem= 悪かったこと(今後はやめること) - T:try = 次に挑戦すること KPTをするときの注意点 - 悪かったことが個人への攻撃にならないこと - チームとして改善できるか考えて、problemの改善案をtryにつなげる 参考 【徹底解説】正しい「KPT」が仕事の成果を生み出す!進め方のコツ、現場の事例を紹介 https://seleck.cc/kpt
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RSpec]いいね機能の単体テストについて

はじめに 本記事では、いいね機能のテストについて記述します。 いいね機能のテストコードを書いておりました。 前提 いいね機能は導入済みです。 私が、実装しているいいね機能は、投稿した食べ物(food)をユーザーが(user)がいいねするものになっています。 コード 早速ですが、コードを記述します。 初めの方のsleep 0.2は必要ありません。 自宅のWi-Fiがとんでもないぐらい遅いので記述しています。一応記事を添付します。 spec/models/like_spec.rb require 'rails_helper' RSpec.describe Like, type: :model do before do user = FactoryBot.create(:user) food = FactoryBot.create(:food) @like = FactoryBot.build(:like, user_id: user.id, food_id: food.id) sleep 0.2 end describe 'いいね機能' do context 'いいねできる場合' do it "user_idとfood_idがあれば保存できる" do expect(@like).to be_valid end it "food_idが同じでもuser_idが違えばいいねできる" do another_like = FactoryBot.create(:like) expect(FactoryBot.create(:like, user_id: another_like.user_id)).to be_valid end it "user_idが同じでもfood_idが違えばいいねできる" do another_like = FactoryBot.create(:like) expect(FactoryBot.create(:like, food_id: another_like.food_id)).to be_valid end end context 'いいねできない場合' do it "user_idが空ではいいねできない" do @like.user_id = nil @like.valid? expect(@like.errors.full_messages).to include "User must exist" end it "food_idが空ではいいねできない" do @like.food_id = nil @like.valid? expect(@like.errors.full_messages).to include "Food must exist" end end end end テストする項目として、 - user_idとfood_idがあれば保存できる - food_idが同じでもuser_idが違えばいいねできる - user_idが同じでもfood_idが違えばいいねできる - user_idが空ではいいねできない - food_idが空ではいいねできない の5つかなと考えました。 context 'いいねできない場合' do it "user_idが空ではいいねできない" do @like.user_id = nil @like.valid? bindng.pry expect(@like.errors.full_messages).to include "User must exist" end it "food_idが空ではいいねできない" do @like.food_id = nil @like.valid? expect(@like.errors.full_messages).to include "Food must exist" end end ここに関しては、 なかなかうまくいかなかったので、@like.valid?の後にbinding.pryして、 何がOKで、何がダメなのかを考えていきました。 27: 28: context 'いいねできない場合' do 29: it "user_idが空ではいいねできない" do 30: @like.user_id = nil 31: @like.valid? => 32: binding.pry 33: expect(@like.errors.full_messages).to include "User must exist" 34: end 35: 36: it "food_idが空ではいいねできない" do 37: @like.food_id = nil [1] pry(#<RSpec::ExampleGroups::Like::Nested::Nested_2>)> @like.errors.full_messagesについて、 こちらの記事を参考にして、 【Rails6】 RSpecによるLikeのモデル単体テストの実装 @like.errors[:user_id]と記述しておりましたが、 なかなかうまくいきませんでした。 @like.errors.full_messagesの中身を確かめたところ、 無事に["User must exist"]が出現しました。 27: 28: context 'いいねできない場合' do 29: it "user_idが空ではいいねできない" do 30: @like.user_id = nil 31: @like.valid? => 32: binding.pry 33: expect(@like.errors.full_messages).to include "User must exist" 34: end 35: 36: it "food_idが空ではいいねできない" do 37: @like.food_id = nil [1] pry(#<RSpec::ExampleGroups::Like::Nested::Nested_2>)> @like.valid? => false [2] pry(#<RSpec::ExampleGroups::Like::Nested::Nested_2>)> @like.errors[:user_id] => [] [3] pry(#<RSpec::ExampleGroups::Like::Nested::Nested_2>)> @like.errors.full_messages => ["User must exist"] [4] pry(#<RSpec::ExampleGroups::Like::Nested::Nested_2>)> 以上です。 この件について、 私の探し方が悪いのか、記事があまりなさそうでしたので、参考にしていただけますと幸いです。 終わりに テストコード、うまくいくと結構楽しいですね。 私は、好きです。 また、テストコードを書き始めたとき、以下のようなエラーが起きてました。 Failure/Error: expect(@like.errors.full_massages).to include "User must exist" NoMethodError: undefined method `full_massages' for #<ActiveModel::Errors:0x00007fb04cb95d30> Did you mean? full_messages full_message 「なんでや?あってるやんけ!」と30分調べた末、 massagesになってました...。 「マッサーゲっておま。。。」 タイポはほんと注意しないといけないですね。。 以下参考記事です。 【Rails6】 RSpecによるLikeのモデル単体テストの実装 Railsでいいね機能(ajax処理)のRSpecの書き方 今日中に、フォロー機能とコメント機能のテストも完了させたいと思います! 今日の残りと明日も頑張りましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エラーメッセージの日本語化

環境 ruby (2.6.5) rails (6.0.0) mysql2 (0.5.3) 日本語化の設定 config/application.rb module #App名 class Application < Rails::Application # 日本語の言語設定 config.i18n.default_locale = :ja end end config.i18n.default_locale = :jaを追記。 Gemfileの編集 Gemfile gem 'rails-i18n' Gemfileの一番下に追記。 bundle install 日本語化用ファイル作成 localeファイルの中に日本語化用のファイルを作成し、英語を日本語に翻訳する。 下記サイトの記述内容をコピーし、作成したdevise.ja.ymlの中に貼り付ける。 devise.ja.yml config/locales/devise.ja.yml ja: activerecord: attributes: user: confirmation_sent_at: パスワード確認送信時刻 confirmation_token: パスワード確認用トークン #〜省略〜 one: エラーが発生したため %{resource} は保存されませんでした。 other: "%{count} 件のエラーが発生したため %{resource} は保存されませんでした。" 自らが作成したカラム名は、'rails-i18n'やdevise.ja.ymlの導入によって使える日本語に含まれないため、英語で表示される。したがって、これらを翻訳するファイルを手動で作成する。 config/locales/ja.yml ja: activerecord: attributes: user: name: 氏名 occupation: 職種 profile: プロフィール image: 画像 staff_id: スタッフID customer: member_id: 会員ID email: メールアドレス #〜省略〜 また、自ら設定したエラー文を表示させたい場合は、下記のように対象のモデルへ記述する。 models/customer.rb class Customer < ApplicationRecord with_options presence: true do validates :name, format: {with: /\A[ぁ-んァ-ヶ一-龥々ー]+\z/, message: "は全角漢字または、ひらがなで入力してください"} validates :name_kana, format: {with: /\A[ァ-ヶー-]+\z/, message: "は全角カタカナで入力してください"} validates :postal_code, format: {with: /\A[0-9]{3}-[0-9]{4}\z/, message: "はハイフン(-)を含めて入力してください"} with_options numericality: { other_than: 0, message: "が選択されていません" } do validates :prefecture_id end end end おわりに エラーメッセージを日本語表示に変えた場合、単体テストコード中の期待されるエラーメッセージも日本語に変更する必要がある。テストを実行し、必要に応じて修正する。 参考 rails-i18n devise.ja.yml
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

helper と decorator の使い分け

例えば下記のようなコードはviewを肥大化させてしまい、さらに変更(#{@user.midlle_name} を追加したいなど)があった場合に対応しづらくなってしまう。 <%= "#{@user.first_name} #{@user.last_name}" %> そこで下記のようなメソッドに切り分けることを考えると思います。 def full_name end これをmodelに定義するのか、helperに定義するのか、はたまたdecoratorに定義するのか、そもそもこれらの違いはなんなのか、というのが今回のテーマです。 model の肥大化を防ぐ 基本的にメソッドはmodelに定義することで実装できます。ただ全てをmodelに書いてしまうとコード量が増え、可視性が悪くなり扱いづらくなってしまいます。 そこで登場するのが helper と decorator 。 これらは基本的にviewの肥大化を防ぐために使われます。viewの共通化やロジックを切るときにはmodelではなく、helper か decorator を使うようにしましょう。 じゃあ、これらは2つはどのように使い分ければ良いのでしょうか? helper と decorator の違いについて helper Modelに依存しないロジック → modelにアクセスする際に定数が必要 module ApplicationHelper def full_name(model) model.first_name + model.last_name end end グローバルに有効(アプリケーション内のどこでも使える)という特徴があるので、他のファイルでメソッド名が被ってしまったときに、どちらか一方が使えなくなるという事故に繋がるため注意が必要。 decorator Modelに依存するロジック → modelにアクセスする際に定数を必要としない module UserDecorator def full_name first_name + last_name end end gem 'draper'をインストールする必要がある まとめ 基本はmodelに書く viewのロジックでModelに依存しないときはhelper viewのロジックでModelに依存するときはdecorator ちなみに下記のような場合ではdecoratorを使うのが適切かと思います。 <%= "#{@user.first_name} #{@user.last_name}" %> <変更後> class UserDecorator def full_name first_name + last_name end end <%= @user.decorate.full_name %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UTC時間とJST時間について

railsでAPIを開発中にUTC時間とJST時間の違いにだいぶ苦しめられたので、調べてみました。 UTC時間 アメリカ時間 時間のデータの最後にUTCってついてる 例 2021-09-03 15:00:00 UTC JST時間 日本時間 アメリカ時間よりも9時間先に進んでいる データの最後に+0900ってついてる(これは恐らく世界標準のアメリカ時間から9時間進んでいる時間ですよって表示するためのもの) 2021-09-04 00:00:00 +0900 両者の二つの例は同じ値を示し、ActiveRecordなどの検索時に時間を指定するとどちらの時間も同一とされ、検索に引っかかるようになる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]条件分岐を使ってデータのある限り画像を投稿する

初めに なぜこの記事を書きたかったのか or この記事の対象者 環境 ・Macbook Air (Retina, 13-inch,2019) ・プロセッサ 1.6GHz デュアルコアIntel Core i5 ・メモリ 8GB 2133 Mhz LPDDR3 ・MacOS Big Sur バージョン 11.5.2 記事の目次 1)どういうときに使える? 2) 前提(画像を保存する仕組みを作る&保存する画像にアソシエーションを組む) **3)DBの限り画像を投稿 4)最後に どういうときに使える? ユーザが投稿した画像をまとめて見やすくするために一つのウィンドウにまとめれる。 例えば)LINEで画像を投稿したあと、写真・動画ボタンをクリックすると日付ごとにまとめて見返すことができる。通販サイトの商品を出品順に並び替えることができるなどなど (前提)DBに保存された画像をviewに使えるようにする設定 (現在制作中、完成後ここにリンクを貼る) DBの限り画像を投稿 1)viewに表示させる場所をつくる <1>画像を投稿するということは、DBに画像があるということをどう表現するか <2>複数の画像を投稿と命令するにはどう表現するのか <3>今回は最初に投稿したものをメインに表示させるように命令してみる <1>DBのデータは配列で保存される=配列になってない真っ白なDBを表現する方法。。。中身がない=lengthがない。 <% if @テーブル名.length !=0 %> <2>@テーブル名.each do |テーブル名|のeach doで指定した@テーブル内の各々のデータを使用してくださいとなる。 <3>配列の中身を順番に数えるとき最初は1からではなく0から! <%= image_tag(テーブル名.image[0] %> 2)DB内に画像がある場合とない場合の表示の仕方を設定 <1>・DB内に画像がない場合の表現する方法→画像を投稿していない→存在していない=presentの有無 <2>presentのあとに"?"をつけることでfalseの場合の処理ができる。 <%= if テーブル名.投稿機能のために作ったモデル.present? %> <div class= 'no-image'> <span>投稿されてないよー</span> </div> <% end %> (例2) 最後に 記事を一気に書くより、段階を分けて書いたほうが気が楽ということがわかったので、まずは段階的に記事を書いてLGTMをもらったり、自分でこれはわかりやすいとおもう!となったら記事をまとめて投稿しようと思った。一気に完璧を求めるより、作成して修正して完璧に近い記事にすればいいと思った! ココまで読んでくださいましてありがとう!☺
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

APIとは・Railsへの導入の仕方

そもそもAPIとは APIは「Application Programming Interface」の略でわかりやすく説明すると『アプリケーションをプログラムして出来たインターフェース』となります。ソフトウェアの一部機能を共有する仕組みを指します。具体的には、「機能を公開しているソフトウェア」と「その機能を使いたいソフトウェア」をつなげる窓口のようなものです。 Web APIは、ひとことで言えば、インターネット(Web)を経由して、何らかの処理を行うための、手段や決めごと(API)のことです。 google apiや、amazon apiのように有名なWebAPIがあり、自分でWebAPIを作って自分だけで利用することも可能です。 APIを利用するメリット APIを利用するメリットは、短期間でサービスを開発できる点にあります。APIを利用すると、その機能の開発を行う手間を省くことができるようになるため、開発期間の短縮やコストの削減が可能になります。 Web APIの仕組み Web APIでは、Web APIで機能を公開しているサーバに対して、インターネットなどの通信ネットワークを通じて依頼内容をHTTPリクエストの形で送信すると、処理結果がHTTPレスポンスの形で送られてきます。送受信されるデータの形式はAPIによって様々だが、Webでよく用いられるXMLやHTML、JSON、各種の画像ファイル形式などが用いられることが多いです。 Rails APIの作成 APIモードでRailsアプリの作成 外部からPostというモデルの情報の作成・取得・削除・編集ができるような形式にします。 通常のrails newコマンドの末尾に--apiをつけることでAPIモードでアプリを作成することができます。 (APIに必要ない部分をデフォルトで作成しなくなります。) $ rails new アプリ名 --api モデル・コントローラの作成 通常のRailsアプリ同様モデルとコントローラを作成します。 今回はtitleというstringを持ったpostというテーブルを作成するとします。 $ rails g model post title:string $ rails g controller posts $ rails db:create $ rake db:migrate ルーティングの設定 最初から以下の様にバージョンで名前空間を作成しておくことで今後のAPIのバージョン管理が容易になります。 config/routes.rb Rails.application.routes.draw do namespace 'api' do namespace 'v1' do resources :posts end end end コントローラの設定 posts.controller.rb module Api module V1 class PostsController < ApplicationController before_action :set_post, only: [:show, :update, :destroy] def index posts = Post.order(created_at: :desc) render json: { status: 'SUCCESS', message: 'Loaded posts', data: posts } end def show render json: { status: 'SUCCESS', message: 'Loaded the post', data: @post } end def create post = Post.new(post_params) if post.save render json: { status: 'SUCCESS', data: post } else render json: { status: 'ERROR', data: post.errors } end end def destroy @post.destroy render json: { status: 'SUCCESS', message: 'Deleted the post', data: @post } end end 引用記事 APIの仕組みが分かる・使いこなせる人材になれる記事(Pythonコード付き) Web APIのメリットと探し方 WebAPIについての説明Railsで超簡単API 巨人の力を使っちゃえ!Web APIを使えば、あなたの夢も一発で実現
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]active_hashについて

初めに なぜこの記事を書きたかったのか ・変化しないデータを繰り返し使いたいヒト 環境 ・Macbook Air (Retina, 13-inch,2019) ・プロセッサ 1.6GHz デュアルコアIntel Core i5 ・メモリ 8GB 2133 Mhz LPDDR3 ・MacOS Big Sur バージョン 11.5.2 記事の目次 1)どういうときに使う? 2)何ができる? 3)どうやって使う? 4)最後に どういうときに使う? 変更されないデータを繰り返し使いたいとき! (例えば、都道府県、市区町村、動物の名前、商品の部類、栄養素など) 何ができる? ・事前に登録したデータをプルダウン形式で使用することができる。 (イメージ) どうやって使う? (1)Gemfileファイルに"active_hash"というgemをインストール (Gemfile) gem 'active_hash' (ターミナル) bundle install (2)app/model/ココに新しくモデル名.rbファイルを作り、ActiveHash::Baseを使い、変更されないデータベースを作る。 ※ActiveHash::Baseで、activeRecordと同じようなメソッドを使用することができる。今回は、NutritionモデルにActiveHash::Baseを継承したので、Nutritionモデルに定義したデータに対してActiveRecordのようなメソッドが使用できる。 ※self.dataでプルダウンのデータを入れることができる (書き方) class モデル名 < ActriceHash::Base self.data = [ { id: 数字, name: '表示させたいテキスト'}, { id: 数字, name: '表示させたいテキスト'}, { id: 数字, name: '表示させたいテキスト'} ] end class Nutrition < ActiveHash::Base self.data = [ { id: 1, name: '--' }, { id: 2, name: 'タンパク質' }, { id: 3, name: '脂質' }, { id: 4, name: '炭水化物' }, { id: 5, name: '食物繊維' }, { id: 6, name: 'その他' } ] end (3)db/migrate/ooooooxxxxx_create_genre.rb(*今回はgenreというテーブル)にカラムを追加する。 (追加カラム) t.integer :nutrition_id , null: false (追加後、ターミナルで反映) rails db:migrate (4)つなげたいモデルとアソシエーションを設定x ※belongs_toを設定するときは"extend"を、has_manyを設定するときは"include"をActiveHashの後ろに下記のように追加する。 モデル:nutrition(栄養素)と、モデル:genre(ジャンル)の場合、栄養素がhas_many: genre、ジャンルがbelongs_to:nutritionとなるので下記のような書き方になる。 (app/models/nutrition.rb) class Nutrition < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :genre end (app/models/genre.rb) include ActiveHash::Associations has_many :nutritions (5)つなげたモデルのバリデーションを設定 (nutritionで栄養素の選択が「ーー」のときは保存しないようにする) ※numercalityとは、数字を検知して保存してくれる役割を持っています。下記のように"other_than:1"以外の数字を検知して保存してくださいと命令できる。 (app/models/nutritions.rb) validates:nutrition_id, numercality: {other_than:1} (6)viewで表示する(form_with内に追加) 書き方 <%= f.collection_select(:activehashのモデル名_id、activehashのモデル名.all、DBに保存するとき値、viewに表示させたい値、html用オプション)> <%= f.collection_select(:nutrition_id, Nutrition.all, :id, :name, {}, {class:"nutrition-select}) %> 最後に、今回の記事を書いて思ったこと。。 言語化ってやっぱり難しいと思った。いつもよりかなり時間がかかった。どうしても誰かに対して説明という感覚より機械的にこうしたあとはこうするというふうな説明の仕方になってしまう。 ココまで読んでくださいましてありがとうございます!読みづらいと感じたり、指摘がございましたら成長への励みにもなりますのでコメントお願いします!☺
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】Youtubeの企画をテーマにしたアプリ「〇〇王!! - quiz about me -」をリリースしました!

はじめに この度「私のことをどれだけ知ってる?」をコンセプトにした、問題の作成、回答ができるクイズアプリ、「〇〇王!! - quiz about me -」をリリースしました! 知ってる方も多いかと思いますが、実はこのアプリ、Youtuberさん達の間で人気の企画「〇〇王」からインスピレーションを受けています! この企画、見てるだけでも面白いですが、私たち視聴者も参加したいと思いませんか?? そんな時に使っていただきたいのがこのアプリです。 Youtuberさんが問題を作成し共有することで、視聴者さんも問題に回答できるようになります! もちろん、友達同士で問題を出し合っても楽しそうですね! 使い方・機能紹介 1. トップページ 若者受けしそうなデザインを意識しました。 ニックネームを入力することで問題を作成できます。 2. 問題作成する こちらの画面で問題の作成、削除ができるようになっております。 また、デフォルトで問題をいくつか用意させていただきました。 作った問題はSNSで共有してみてください。 3. 問題に回答する ニックネームを入力することで問題に回答できます。 また、クイズをより楽しくするため、ランキング機能を設けました。 回答結果もSNSで共有してみてください。 使用技術 バックエンド ・ Rails 6.0.4.1 ・ Ruby 2.6.6 ・ RSpec 5.0.2 フロントエンド ・ HTML ・ CSS (SCSS) ・ JavaScript (jQuery) ・ Bootstrap4 こだわった点 ユーザーのストレスをできるだけ減らした このアプリはじっくり使うというモノよりかは、サクッと気軽に遊べるような作りにして、ユーザーのストレスを減らすような作りが良いと考えました。 特に、問題作成の際、作成や削除をページ内で完結させたかったため、Ajaxを使いページの遷移をさせないような作りにしました。 ログイン機能もユーザーに対する1つの壁となる気がしたのであえて用意しませんでした。 デザイン面 できるだけシンプルかつ、ポップで若者(特にJK)に受けそうなデザインに仕上げました。 問題を表示する箇所はインスタのストーリーで使われている問題機能を参考にしました。 Youtubeをテーマにしてることから、ユーザーのほとんどはスマホでアクセスすると思ったので、デザインはスマホに寄せております。 おわりに 自分自身Youtubeで「〇〇王」という企画を見るたびに、これ友達同士でもやれたらいいなと思っていたので、形にできたのがすごく嬉しいです。 サービスをデプロイ後たくさんのユーザーさんに、「バズりそー!」などと感想をいただきました。 正直このサービスを思いついた時は、自分天才だな?なんて思ってしまいました(笑) また、感想以外にもご指摘など、ありがたい言葉をたくさんいただきました。ありがとうございます? 今後ご意見を参考にしつつ、サービスをよりよいものにしていけるように頑張っていきます! 最後までご覧いただきましてありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】S3にCSVデータを出力する

はじめに CSVファイルのデータをAWSのS3に保存するロジックを実装したので、よかったら参考にしてみてください。 Rails: 5.2.6 Ruby: 2.7.2 実装したコード S3のバケットに 例: 2021_10/2021_10_01.csv といったファイル名で出力されます。 こちらは毎日バッチで実行することを想定して作成おります。 export_csv.rb # frozen_string_literal: true require 'csv' require 'aws-sdk-s3' class ExportCsv def self.execute(params) output = CSV.generate do |csv| csv << params['headers'] params['values'].each do |value| csv << value end end export_csv_to_s3(output, params) end def self.export_csv_to_s3(data, params) s3_client = Aws::S3::Client.new(stub_responses: Rails.env.test?, access_key_id: params['access_key_id'], secret_access_key: params['secret_access_key'], region: params['region']) bucket_name = params['bucket_name'] folder_name = Time.current.yesterday.strftime('%Y_%m').to_s file_name = "#{Time.current.yesterday.strftime('%Y_%m_%d')}.csv" file_full_path = "#{folder_name}/#{file_name}" # バケットの存在確認とアクセス権の確認 if bucket_exists_and_accessible?(s3_client, bucket_name) Rails.logger.info "Bucket '#{bucket_name}' exists and is accessible to you." else Rails.logger.info "Bucket '#{bucket_name}' does not exist " \ 'or is not accessible to you.' end # ファイルアップロードが完了したか確認 if file_uploaded?(s3_client, bucket_name, file_full_path, data) Rails.logger.info "Object '#{file_full_path}' uploaded to bucket '#{bucket_name}'." else Rails.logger.info "Object '#{file_full_path}' not uploaded to bucket '#{bucket_name}'." end end def self.bucket_exists_and_accessible?(s3_client, bucket_name) s3_client.head_bucket(bucket: bucket_name) return true rescue StandardError return false end def self.file_uploaded?(s3_client, bucket_name, path, data) response = s3_client.put_object( bucket: bucket_name, key: path, body: data, content_type: 'text/csv' ) return true if response.etag rescue StandardError => e Rails.logger.info "Error uploading object: #{e.message}" return false end private_class_method :export_csv_to_s3, :bucket_exists_and_accessible?, :file_uploaded? end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エラー'NotNullViolation'

エラー内容 状況 原因 注意点 以前にも出くわした エラーなのですが 解決法を忘れてしまったので 記録も兼ねて記事に書きます。 1.エラー内容 エラー内容は以下の通り。 "ActiveRecord::NotNullViolation in Users::RegistrationsController#create_as_leader" "Mysql2::Error: Field 'compose_style_id' doesn't have a default value" 2.状況 今回は 初歩的なケースなのでコードを割愛します。 ウィザード形式で追加した二ページ目で sessionに保存していたデータごとsaveしようとしたところ エラーが出てしまう、という状態。 エラー文によるとcompose_style_idカラムがエラーを起こしており このカラムについてはアクティブハッシュを利用しています。 3.原因 正解から書いてしまうと エラーの原因は バリデーションとストロングパラメーターの スペルミスでした。 (他に同じエラーの原因として考えられるのは ・password等、deviseにデフォルトである値をマイグレーションファイルで書き足してしまい、重複しているケース ・Nullになってはいけない箇所がnullになっているケース ...等々のようです。) 4.注意点 原因を探し当てるのに一つの障害となるのは バリデーションそのものは正常に作動して見えることです。 以下、binding.pryで原因調査時のコード。 ターミナル 22: def create_as_leader 23: @user = User.new(session["devise.regist_data"]["user"]) 24: @as_leader = AsLeader.new(as_leader_params) 25: unless @as_leader.valid? 26: render :new_as_leader and return 27: end 28: @user.build_as_leader(@as_leader.attributes) => 29: binding.pry 30: @user.save 31: session["devise.regist_data"]["user"].clear 32: sign_in(:user, @user) 33: end [1] pry(#<Users::RegistrationsController>)> @user.valid? User Exists? (0.2ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 's@s.s' LIMIT 1 ↳ (pry):1:in `create_as_leader' => true trueで返されてしまっています。 おそらくデータそのものは バリデーションによって止められていないからと思われ、 逆の考え方をすると バリデーションのfalseは paramsの通過可否の確認しかできず バリデーションそのものの記述の正しさを反映しません。 また、mysql関連のエラーは 基本的にバリデーションなどの データベースに関する処理になるので その辺りを確認していくのが 原因を探り当てる近道になりそうでした。 今回は以上です。 お読みくださりありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails】stylesheets直下の全てのCSSが読み込まれる原因

railsのCSSの読み込みについて railsでCSSはstylesheets/application.scssに記述するが、 上記以外のstylesheets直下のCSSファイルが自動で、 読み込まれていることがあり疑問に思った 原因 stylesheets/application.scss *= require_tree . *= require_self 上記の記述により、stylesheets直下のCSSファイルはすべて読み込まれるようになっていた。 さらに詳しく言うと、 *= require_tree . この部分がapplication.scss自身の読み込むという記述。 *= require_self この部分がstylesheets直下のCSSファイルを順番に読み込むという記述。 そのため、slickなどを読み込む際に、 わざわざapplication.html.erbに以下のように記述せずとも、 自動で読み込まれる。 <link rel="stylesheet" type="text/css" href="slick/slick.css"/> <link rel="stylesheet" type="text/css" href="slick/slick-theme.css"/>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]モーダルウィンドウを作る

はじめに 本記事では、過去に作ったモーダルウィンドウの作り方を記述します。 イメージ このような形です。 かっこいいですよね。 コード 該当のビュー 省略 <div class="menu-details"> <div class="search-contents"> <div class="fas fa-search"></div> <h2 class="menu-content">SEARCH</h2> </div> </div> 省略 <%# モーダルウィンドウ %> <div class="modalsearch"> <i class="far fa-times-circle modalsearch-close"></i> <div class="modalsearch-contents"> <div class="modalsearch-left"> <h2 class="modalsearch-title">Let's<br/>Search !</h2> </div> <div class="modalsearch-right"> <div class="modalsearch-contents"> <%= search_form_for @q, url: search_foods_path, class: "food-search", id:"pull-down" do |f| %> <%= f.label :shop_name_or_shop_name_kana_or_food_name_or_station_cont, '店名・メニュー名・駅名', class: "search-label" %><br/> <%= f.text_field :shop_name_or_shop_name_kana_or_food_name_or_station_cont, placeholder: "KEYWORD", class: "search-text" %><br/> <%= f.label :user_nickname_cont, 'ユーザー名', class: "search-label" %><br/> <%= f.text_field :user_nickname_cont, placeholder: "KEYWORD", class: "search-text" %><br/> <%= f.label :meal_type_id_eq, '種類', class: "search-label" %><br/> <%= f.collection_select :meal_type_id_eq, MealType.all, :id, :name, {include_blank: '--', disabled: 0}, class: "search-text" %><br/> <%= f.label :spicy_level_id_eq, '辛さ', class: "search-label" %><br/> <%= f.collection_select :spicy_level_id_eq, SpicyLevel.all, :id, :name, {include_blank: '--'}, class: "search-text" %><br/> <%= f.submit "SEARCH", class: "search-submit" %><br/> <% end %> </div> </div> </div> </div> <div class="masksearch"></div> <script> $(function() { $('.search-contents').on('click',function(){ $('.modalsearch,.masksearch').fadeIn(); }); $('.modalsearch-close,.masksearch').on('click',function(){ $('.modalsearch,.masksearch').fadeOut(); }); }); </script> <%# /モーダルウィンドウ %> 下から13行目にある<div class="masksearch"></div>はdivタグで囲っていませんので、注意。 ここはブワッと出た時の黒い部分になります。 CSSでは画面全体を黒くするようにしております。 CSSはモーダルウィンドウの部分だけにします。 //モーダルウィンドウ .modalsearch { position: fixed; background-image: image-url("search-title.png"); background-size: cover; background-repeat: repeat; background-color: rgba(255, 255, 255, 0.8); background-blend-mode: lighten; width: 80vw; height: 70vh; top: 50%; left: 50%; transform: translate(-50%,-50%); border-radius: 10px; z-index: 2; display: none; } .modalsearch-contents { display: flex; justify-content: space-around; align-items: center; } .modalsearch-title { height: 20vh; width: auto; color: rgb(0, 0, 0); font-family: 'Permanent Marker', cursive; font-size: 7vw; text-align: center; } .modalsearch-close { cursor: pointer; font-size: 4vw; } .search-label { font-family: "M PLUS Rounded 1c"; font-size: 1.2vw; } .search-text { width: 25vw; height: 5vh; color: #000000; border: solid 3px #000000; margin: 0.5em; font-size: 1.2vw; background-color: #ffffff33; } .search-submit { width: 15vw; height: 7vh; color: #000000; border: solid 3px #000000; border-radius: 20%; margin: 0.5em; font-size: 1.2vw; background-color: #ff00004f; font-family: 'Permanent Marker', cursive; } .masksearch { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); z-index: 1; display: none; } // /モーダルウィンドウ 初めの方の<h2 class="menu-content">SEARCH</h2>でSEARCHを押すと、 ブワッと出ます。 まず、前述の通り、 .masksearch { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); z-index: 1; display: none; } は外側の黒い部分です。 potision: fixed;にする必要があるので注意です。 出ないとスクロールしたらその分だけ黒い部分はなくなります。 内側の部分についても、黒い部分よりも小さくなるようにCSSで設定し、 以下の通り、これもpotision: fixed;します。 また、z-index: 2;することで、黒い部分よりも前面になるように設定します。 .modalsearch { position: fixed; background-image: image-url("search-title.png"); background-size: cover; background-repeat: repeat; background-color: rgba(255, 255, 255, 0.8); background-blend-mode: lighten; width: 80vw; height: 70vh; top: 50%; left: 50%; transform: translate(-50%,-50%); border-radius: 10px; z-index: 2; display: none; } 最後は、scriptタグです。 <script> $(function() { $('.search-contents').on('click',function(){ $('.modalsearch,.masksearch').fadeIn(); }); $('.modalsearch-close,.masksearch').on('click',function(){ $('.modalsearch,.masksearch').fadeOut(); }); }); </script> .search-contentsをクリックすると、 検索項目と黒い部分が出る(フェードイン)ようにしました。 逆に、検索項目内にある印(ここでは).modalsearch-closeと黒い部分.masksearchをクリックすると、 検索項目と黒い部分が出るようにしました。(フェードアウト) 検索項目については、 それぞれのアプリケーションで表現したいことを記述すれば完了です。 私の場合は、以下の部分です。 <div class="modalsearch-contents"> <div class="modalsearch-left"> <h2 class="modalsearch-title">Let's<br/>Search !</h2> </div> <div class="modalsearch-right"> <div class="modalsearch-contents"> <%= search_form_for @q, url: search_foods_path, class: "food-search", id:"pull-down" do |f| %> <%= f.label :shop_name_or_shop_name_kana_or_food_name_or_station_cont, '店名・メニュー名・駅名', class: "search-label" %><br/> <%= f.text_field :shop_name_or_shop_name_kana_or_food_name_or_station_cont, placeholder: "KEYWORD", class: "search-text" %><br/> <%= f.label :user_nickname_cont, 'ユーザー名', class: "search-label" %><br/> <%= f.text_field :user_nickname_cont, placeholder: "KEYWORD", class: "search-text" %><br/> <%= f.label :meal_type_id_eq, '種類', class: "search-label" %><br/> <%= f.collection_select :meal_type_id_eq, MealType.all, :id, :name, {include_blank: '--', disabled: 0}, class: "search-text" %><br/> <%= f.label :spicy_level_id_eq, '辛さ', class: "search-label" %><br/> <%= f.collection_select :spicy_level_id_eq, SpicyLevel.all, :id, :name, {include_blank: '--'}, class: "search-text" %><br/> <%= f.submit "SEARCH", class: "search-submit" %><br/> <% end %> </div> </div> </div> 以上です。 いかがでしょうか。 終わりに モーダルウィンドウがアプリケーションにあるとかなりおしゃれになるので、どんどん使用していきたいところです。 特にログアウト時や投稿削除時には、入れておくことで未然に誤作動を防ぐことができますね。 以下参考サイトです。 モーダルウィンドウ・ダイアログウィンドウの作り方1(HTML、CSS、JavaScriptで作る) 【jQuery・CSS】意外と簡単!モーダルウィンドウをプラグインなしで作る 明日も頑張ります!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む