20210827のRailsに関する記事は18件です。

【Rails】carrierwave画像投稿機能 (メモ)

目標 画像投稿機能 開発環境 Rails: 6.1.3 ruby: 3.0.0 mac: os 前提 投稿機能実装済み 投稿テーブルpost 実装 1.Gemfileにcarrierwaveを記述 してインストール Gemfile gem 'carrierwave' bundle installします。 ターミナル $bundle install 2.postテーブルにimageカラム追加 ターミナル $rails g migration AddImageToPosts image:string マイグレーションファイルが生成されます。 imageカラムが追加されているのを確認してマイグレーションします。 class AddImageToPosts < ActiveRecord::Migration[6.1] def change add_column :posts, :image, :string end end ターミナル $rails db:migrate 3.イメージアップローダー作成 ターミナル $rails g uploader image image_uploader.rbファイルが生成される。 4.モデル紐付け postモデルにイメージアップローダーを紐付ける。 app/models/post.rb class Post < ApplicationRecord mount_uploader :image, ImageUploader end 5.コントローラー記述 praivateのpost_paramsにimageを追加。 app/contrllers/posts_controller.erb private def post_params params.require(:post).permit(:content, :title, :image) end 6.ビュー記述 画像投稿フォーム追加。 app/views/posts/_form.html.erb <%= form_with(model: @post)do |f| %> <div> <%= f.label :title, ' タイトル' %> <%= f.text_field :title %> </div> <div> <%= f.label :content, '内容' %> <%= f.text_field:content %> </div> <%= f.label :image, '画像' %> <%= f.file_field :image %> <div> <%= f.submit '投稿'%> </div> <% end %> 画像投稿フォームができました。 これだけでは投稿できないのでimage_tagを使って投稿できるように記述します。 app/views/posts/show.html.erb <h5><画像投稿></h5> <p>タイトル:<%= @post.title %></p> <p><%= image_tag @post.image.url %></p> <p>内容:<%= @post.content %></p> 記述したので投稿できるはずです。 こんな感じで無事投稿でできました。 以上です。 参考 参考にした記事です。 https://qiita.com/k19911848/items/a082cc4e0c0103f935b1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

yarnって何?

パッケージマネージャーの1つ。 パッケージマネージャー? ソフトウェア同士の依存関係を管理するもの。 依存関係? AとBっていうgemがあるとして、 Aをアップグレードしたら、Bは今のバージョンだと対応してないから、Bが使えなくなった。 BはAのバージョンに依存している状態。 いっぱいあるソフトウェアの依存関係を手動で管理するのはめんどくさいから、yarnっていうパッッケージマネージャーが管理してくれている。 パッケージ化してマネジメントしてくれてる。 ありがとうyarn。 railsでgemインストールした時にこのgemはバージョン何以上しか対応してません。みたいな警告出るのもyarnのおかげなのかな?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsチュートリアル第4章 文字列とメソッド

文字列とメソッド Ruby を学ぶためのツールとして、主にRailsコンソールを使っていく Railsアプリケーションを対話的に操作するためのコマンドラインツール クラウドIDEとは何か? 多分cloud9だと思う。 environmentsより上に行ったあと $ nano ~/.irbrc Ctrl-Xと押してnanoエディタから離脱します。ファイルを保存するかどうか訊かれるので、yキーを押して~/.irbrcファイルを保存 IRB.conf[:PROMPT_MODE] = :SIMPLE IRB.conf[:AUTO_INDENT_MODE] = false どうすればいいかわからなかったのでsample_appまで降りてrails consoleを起動 デフォルトでは、コンソールはdevelopment(開発)環境という Ctrl-Cを押してコンソールから強制的に抜け出すこと Ctrl-Dを押して正常にコンソールを終了させることもできます Ctrl-Pまたは上矢印キーで以前に実行したコマンドを再利用 文字列 文字列(string) は、Webアプリケーションにおいておそらく最も重要なデータ構造 ここで入力したものは文字列リテラルと呼ばれ 文字列の結合 >> "foo" + "bar" # 文字列の結合 => "foobar" 式展開 #{}という特殊な構文を使います 。 >> first_name = "Michael" # 変数の代入 => "Michael" >> "#{first_name} Hartl" # 文字列の式展開 => "Michael Hartl" 代入 >> first_name = "Michael" => "Michael" >> last_name = "Hartl" => "Hartl" >> first_name + " " + last_name # 苗字と名前の間に空白を入れた結合 => "Michael Hartl" >> "#{first_name} #{last_name}" # 式展開を使って結合 (上と全く同じ) => "Michael Hartl" 出力 >> puts "foo" # 文字列を出力する foo => nil 戻り値には「文字どおりの無」であるnilを返します。nilは「何にもない」ことを表すRubyの特別な値 >> print "foo" # 文字列の画面出力 (putsと同じだが改行がない) foo=> nil 改行する >> print "foo\n" # puts "foo" と等価 foo => nil シングルクォート内の文字列 シングルもダブルも実質的に変わらないらしい。 しかしシングルでは式展開はできない。 >> 'foo' # シングルクォートの文字列 => "foo" >> 'foo' + 'bar' => "foobar" >> '#{foo} bar' # シングルクォート内の文字列では式展開ができない => "\#{foo} bar" ダブルクォート文字列を用いた文字列で#のような特殊な文字を使いたい場合は、この文字をバックスラッシュでエスケープ(escape)する必要がある。 シングルクォートは、入力した文字をエスケープせずに「そのまま」保持するときに便利 シングルクォートで文字列を囲めば、簡単にバックスラッシュ文字のような特殊文字をそのまま変数に含める。 >> '\n' # 'バックスラッシュ n' をそのまま扱う => "\\n" >> 'Newlines (\n) and tabs (\t) both use the backslash character \.' => "Newlines (\\n) and tabs (\\t) both use the backslash character \\." シングルクォートで囲むと文字列で特殊文字を使うことができる ダブルクォートで文字列を囲む場合はバックスラッシュを前につけなければならないらしい。 演習 city変数に適当な市区町村を、prefecture変数に適当な都道府県を代入 変数と式展開を使って、住所の文字列を作れ。出力にはputsを使え。 半角スペースをタブに置き換えろ >> city = "aa" => "aa" >> prefecture = "bb" => "bb" >> puts = "#{prefecture}県 #{city}市" => "bb県 aa市" ダブルをシングルにしてみるとどうなるか? 予想ではそのままの文字列になると考えている。 >> puts = '#{prefecture}県 #{city}市' => "\#{prefecture}県 \#{city}市" 予想通りになった。 オブジェクトとメッセージ受け渡し Rubyでは、あらゆるものがオブジェクトです。文字列やnilですらオブジェクト 「オブジェクトとは何であるか」という直感を養う必要があります。 オブジェクトとは(いついかなる場合にも)メッセージに応答するものです。 >> "foobar".length # 文字列に "length" というメッセージを送る => 6 この場合は6がオブジェクトになるらしい emptyメソッド true,falseの返事が返ってくる >> "foobar".empty? => false >> "".empty? => true if >> s = "foobar" >> if s.empty? >> "The string is empty" >> else >> "The string is nonempty" >> end => "The string is nonempty" elsif 条件文を2つ以上含めたい >> if s.nil? >> "The variable is nil" >> elsif s.empty? >> "The string is empty" >> elsif s.include?("foo") >> "The string includes 'foo'" >> end => "The string includes 'foo'" &&(and)||(or)、!(not) >> x = "foo" => "foo" >> y = "" => "" >> puts "Both strings are empty" if x.empty? && y.empty? => nil >> puts "One of the strings is empty" if x.empty? || y.empty? "One of the strings is empty" => nil >> puts "x is not empty" if !x.empty? "x is not empty" => nil Rubyでは、あらゆるものがオブジェクトです。 nilもオブジェクト >> nil.to_s => "" nil に対してさまざまなメソッドを渡せることを確認 >> nil.empty? NoMethodError: undefined method `empty?' for nil:NilClass >> nil.to_s.empty? # メソッドチェーンの例 => true nilメソッド nilかを確認するメソッド >> "foo".nil? => false >> "".nil? => false >> nil.nil? => true if ifがtureの時に実行される puts "x is not empty" if !x.empty? unless unlessでもifの代わりができる >> string = "foobar" >> puts "The string '#{string}' is nonempty." unless string.empty? The string 'foobar' is nonempty. => nil オブジェクトを2回否定どんなオブジェクトも強制的に論理値に変換 !! 二重否定 >> !!nil => false >> !!0 => true
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]フォロー機能導入中にActiveModel::UnknownAttributeErrorというエラー発生

はじめに 本記事では、フォロー機能時に発生した ActiveModel::UnknownAttributeErrorというエラーの解決した方法を記述します。 仲間に助けてもらいながら実装できたため、個人でもアウトプットして身につけたいと思い、記事にしました。 エラー画面 attribute=属性という意味。(Google先生) Relationshipにuser_idという属性は知らん。と怒られました。 確かに、user_idはありません。 しかし、binding.pryでparams[:user_id]の値取れてるじゃないですか...。 と言い訳してみたはいいものの、まあカラムにないわけですからなんとかします。 コード 実際のコードがこれです。 relationships_controller.rb class RelationshipsController < ApplicationController before_action :authenticate_user! def create following = current_user.relationships.build(follower_id: params[:user_id]) following.save redirect_to request.referrer || root_path end def destroy following = current_user.relationships.find_by(follower_id: params[:user_id]) following.destroy redirect_to request.referrer || root_path end end following = current_user.relationships.build(follower_id: params[:user_id])` エラーの内容的にここだよね。と思ったので、もうピンポイントで突き止めていきます。 結論 relationships_controller.rb class RelationshipsController < ApplicationController before_action :authenticate_user! def create following = Relationship.create(follower_id: params[:user_id], following_id: current_user.id) redirect_to request.referrer || root_path end def destroy following = Relationship.find_by(follower_id: params[:user_id], following_id: current_user.id) following.destroy redirect_to request.referrer || root_path end end following = Relationship.create(follower_id: params[:user_id], following_id: current_user.id) Relationshipモデルにcreateメソッドつけて、 カラムであるfollower_idとfollowing_idをセッターにして、 それぞれ値を入れ込めば完了。 敗因 ①Relationship.create(follower_id: params[:user_id], following_id: current_user.id)の形で値を入れることを理解できていなかった。 ②なんとかして、変数を作って入れることしか考えていなかった。 別解として、 カラムをfollowingをuser_idに変えてしまう手もあったようです。 終わりに 完全に仲間に助けてもらい、解決しました。 同じスタートを切ったはずなのに差ができていたことに愕然としたものの、 みんなでディスカッションしながらコードを眺めるのも、楽しいなと感じました。 Railsでフォロー機能を作る方法 【Ruby on Rails】フォロー機能を作ろう(PART2)How to set up a Follower Following system in Ruby on Rails 2つ目はYouTubeですが、 この方の動画はかなり参考になるかと思います。 今日は週末ですが、 明日も頑張ります!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-js-modalを用いて投稿一覧ページにおいてRails × Vue.jsで動的なコンポーネントをモーダルウィンドウとして表示

投稿一覧ページから投稿詳細ページをモーダルウィンドウとして表示 こんな感じの表示をする。 https://www.kabanoki.net/3144/ この記事を最初は参考にしてますが、動的な子コンポーネントを投稿一覧からモーダルウィンドウを表示させる技術は僕のオリジナルなので、動くは動きますが、なにか間違えてる可能性あります。 drinkをpostに置き換えて読めば読みやすいです。 投稿一覧ページRails と Vue.jsでSPAな感じにするやりかたはこちらの教材が結構参考になります。 てか今度僕が記事にできればなと思います。 https://www.techpit.jp/courses/123/curriculums/126/sections/935/parts/3581 下準備 npm install vue-js-modal --save vue-js-modalは簡単にモーダルウィンドウを簡単に実装できる Vue.jsのライブラリ的なもの。 hello_vue.js import Vue from 'vue' import App from '../app.vue' +import VModal from 'vue-js-modal' +Vue.use(VModal) このように記述すれば下準備ok app/javascript/app.vue <template> <li v-for="drink in drinks" :key="drink.id" class="list" > <span>{{ drink.price }}円<br>(税込み)</span> <button v-on:click="show(drink)">詳細を表示</button> <modal v-bind:name="'display-drink-'+drink.id" height="1000px" styles="background-color: bisque"> <drinkShow :drink=drink></drinkShow> </modal> </li> </div> </template> <script> import axios from 'axios'; import likeButton from './packs/components/like/likeButton.vue'; import drinkShow from './packs/components/drinks/show.vue'; export default { components: { likeButton, drinkShow }, data: function(){ return { drinks: "drinks" } }, mounted(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( this.drinks = response.data )) }, show : function(drink) { this.$modal.show(`display-drink-${drink.id}`); }, hide : function () { this.$modal.hide('display-drink-show'); } } } </script> 以上が全体像 <button v-on:click="show(drink)">詳細を表示</button> まずこんな感じに書いて、 show : function(drink) { this.$modal.show(`display-drink-${drink.id}`); }, show関数にdrinkという引数を渡してあげる。 this.$modal.show(display-drink-${drink.id}); のthis.$modal.show引数に指定したものと、modal name=”” のnameに指定したものが一致したらそのモーダルウィンドウを開く仕様らしい。 <modal v-bind:name="'display-drink-'+drink.id"   height="1000px" styles="background-color: bisque"> <drinkShow :drink=drink></drinkShow> </modal> 一意性を持たせるために、drinkのidによってモーダルウィンドウを開くようにする。 やってもらったら分かると思いますが、this.$modal.show("hoge"); とやったら、配列の中にmodalを置いているので、配列分ループされて、配列分モーダルウィンドウが表示されてしまう。 なので、drink.idでnameを指定することにより、一意性を持たせて、ボタンを押したら その押した投稿した表示されないようにする。 このやり方は俺が自分で考えた。まじで天才すぎる。 <modal v-bind:name="'display-drink-'+drink.id"   height="1000px" styles="background-color: bisque"> <drinkShow :drink=drink></drinkShow> </modal> こんな感じでスタイルを指定したり、 modalの中に子コンポーネントを配置してる。 んで、子コンポーネントのタグにこんな感じで書くことで 子コンポーネントにデータを渡してる。 javascript/packs/conponents/drinks/show.vue <template> <div class='show-main'> <div class='item-img-content'> <button v-on:click="hide(drink)">戻る</button> <img class= "item-img" v-bind:src="drink.image" > </div> <div class='item-info'> <h3 class='item-name'> {{drink.name}} </h3> <div class='item-price'> <span>{{drink.price}}円<br>(税込み)</span> </div> <div class='item-explain'> {{drink.explain}} </div> </ul> </div> </template> <script> export default { props: ['drink'], components: { // likeButton }, methods: { hide : function (drink) { this.$modal.hide(`display-drink-${drink.id}`); } } } </script> props: ['drink'], と書くことで親コンポーネントから送られたデータを受け取れる。 まぁあとは見たとおり。 投稿一覧からモーダルウィンドウを表示するのは割と工夫が必要でしたが、 これでできました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails pluckメソッドとは?

結論 pluckメソッドとは引数に指定したからむの値を配列で返してくれるメソッド 使い方 1 モデル名.pluck(:カラム名)  2 重複を取り除く pluckメソッドは、重複を取り除くdistinctメソッドと併用する事が出来る。 Owner.distinct.pluck(:カラム名) ←こんな感じ 3 条件を指定する pluckメソッドは、条件を抽出してくれるwhereメソッドと併用する事が出来る。 Owner.where('条件').pluck(:id) ←こんな感じ 自分のポートフォリオ作成時に上記を使ったのでQiitaに投稿 Hobby.where(user_id: User.where("nickname LIKE ?", "%#{value}%").pluck(:id)) →検索時の部分一致 pluckメソッドの返り値 pluckメソッドの返り値は、配列です。 その為、pluckメソッドの後にメソッドチェーンを使って他のクエリメソッドを繋げることが出来ないので注意。 など、まだ他にも使い方はあります! また似たようなメソッドにmapメソッドがあります。違いはいくつかあるのでまた調べます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ActiveRecord::RecordNotFound (Couldn't find Order without an ID)の対処法

症状 RailsAPIでメソッドを作成していたところ、以下のエラーメッセージが表示されてしまいました。 翻訳すると、「IDのないHogeが見つかりませんでした」 error Completed 404 Not Found in 1ms (ActiveRecord: 0.0ms | Allocations: 158) ActiveRecord::RecordNotFound (Couldn't find Hoge without an ID) メソッドにパラメータは以下です。 parameter Parameters: {"params"=>{"id"=>12}, "hoge_id"=>"12", "hoge"=>{}} 該当のメソッドは以下です。 hoge.rb class OrdersController < ApplicationController def update hoge= Hoge.find(params[:id]) render json: { hoge: hoge }, status: :ok end end 解決方法 パラメータを適切に受け取れるように、paramsの指定先を修正したら解決しました。 パラメータの受け取り方がよくなく、結果、DBにfindしようとしたときに、id指定ができていなかったため、「IDがないよ」と怒られていたようです。 hoge.rb class OrdersController < ApplicationController def update hoge= Hoge.find(params[:hoge_id]) render json: { hoge: hoge }, status: :ok end end 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ransackによる簡易的な検索機能実装

概要 ransackのシンプルモード(Simple Mode)を利用した簡易的な検索機能を実装しました。備忘録として記録します。 環境 Ruby(バージョン2.6.5) Rails(バージョン6.0.0) mysql2(バージョン0.5.3) 実現したいこと 登録してある顧客のリストに対して、氏名(last_name, first_name)と会員ID(member_id)による検索を可能にする。 過程 1. ransackの導入 Gemfile gem 'ransack' -zsh % bundle install 2. コントローラー customers_controller.rb def index @customers = Customer.order("created_at DESC") @search = Customer.ransack(params[:q]) @customers = @search.result end .order("created_at DESC")で顧客のリストを登録の新しい順に並べています。 params[:q]には検索パラメータが渡されるため、@search = Customer.ransack(params[:q])とすることで、検索オブジェクト@searchが作成されます。そして、@customers = @search.resultによって検索結果が得られます。 3. ビュー customers/index.html.erb <%= search_form_for @search , html: {class: "search-form"} do |f| %> <%= f.search_field :last_name_or_first_name_or_member_id_cont, class:"input-box", placeholder:"顧客検索" %> <%= button_tag type: :submit, class: "search-button" do %> <%= image_tag "search.png", class:"search-icon" %> <% end %> <% end %> ransackの利用においてform_forは、search_form_forと記述します。引数に検索オブジェクト@searchをとることで検索フォームを作成できます。 今回のシンプルモード(Simple Mode)検索は、カラム名と述語(Predicate)を組み合わせてSQLを作成し、検索する方法です。 f.search_field :カラム名_contにより、指定したカラム名に対してLIKE句を利用した部分一致検索が可能になります。[述語(Predicate)部分→cont:LIKE句を使った曖昧検索] また、_or_を用いてカラム名を繋げることで複数のカラム名による検索が可能となります。 おわりに 今回はransackを利用した簡易的な検索機能を実装してみました。 今後は、セレクトボックスを用いた検索などより複雑な検索機能の実装に取り組みたいと思います。 また、より良い実装方法や記事内に間違いなどがありましたらご教示ください。 参考 ransack Ransackで簡単に検索フォームを作る73のレシピ Railsガイド:フォームヘルパー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RoR faraday/options.rb:166: warning 対策の例

こういうエラー vendor/bundle/ruby/2.7.0/gems/faraday-0.15.4/lib/faraday/options.rb:166: warning: Capturing the given block using Proc.new is deprecated; use `&block` instead 環境変数で対応できる export RUBYOPT=-W0
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bootstrap5をrails5に導入する。

Bootstrap5を導入する記事がrails6のものばかりだったので一応。 当方はrailsチュートリアルを無料で読むためにrails5.2を利用しています。 Bootstrapと依存popper.js Gemfile gem 'bootstrap', '~> 5.1.0' gem 'popper_js', '~> 2.9.3' assets/stylesheets/application.scss @import "bootstrap"; assets/javascript/application.js //= require popper //= require bootstrap-sprockets これだけ。application.jsのpopper.jsは上に書かないといけないらしいですが試していません。 JQuery依存はなくなりましたがハンバーガーメニューなど、Javascriptが不要になったわけではなかったです。 初心者過ぎてそのへんよくわかっていませんでした。 bundle installもお忘れなく。 bundle install JQueryが必要になれば、問題なく利用できます。 ついでにBootstrap_formを導入 めちゃくちゃ便利でcrispyです。 Gemfile gem "bootstrap_form", git: "https://github.com/bootstrap-ruby/bootstrap_form.git", branch: "bootstrap-5" assets/stylesheets/application.scss @import "rails_bootstrap_forms"; 注意点 polyfillを入れないとIE11非対応です。 polyfill または polyfill.io 参考に対応できるようですが未確認です。 参考 twbs/bootstrap-rubygem bootstrap-ruby/bootstrap_form
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

親モデルのActiveModel::Serializer内で子モデルのActiveModel::Serializerを指定する

実行環境 macOS 10.15.7 (19H1217) Ruby 2.6.7 Rails 6.0.3.7 active_model_serializers 0.10.12 前提 AuthorモデルとBookモデルはhas_many・belongs_toの関係にある。 以下の設定が有効になっている。 config/initializers/active_model_serializers.rb ActiveModelSerializers.config.default_includes = '**' やりたい事 AuthorモデルのインスタンスをJSONとして取得する際には,AuthorSerializerを使用する。 BookモデルのインスタンスをJSONとして取得する際には,BookSerializerを使用する。 AuthorモデルのインスタンスをJSONとして取得する際には,そのインスタンスが持つBookモデルのインスタンスも取得する。 やり方 以下のようにすることで,異なるActiveModel::Serializerを結びつけることができます。 app/serializers/author_serializer.rb class AuthorSerializer < ActiveModel::Serializer attributes :id, :name, :email, :books def books ActiveModel::SerializableResource.new(object.books, each_serializer: BookSerializer) end end app/serializers/book_serializer.rb class BookSerializer < ActiveModel::Serializer attributes :id, :title end 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#3 RailsとVue.jsでモーダルウィンドウを実装

https://qiita.com/divclass123/items/61f592eb3663828eb931 前回の続き。 前回はかなり苦戦したが、なんとか実装できた。 しかし、子コンポーネントをモーダルウィンドウで表示して、そこでいいねを押してもリアルタイムで親コンポーネントにいいねが表示されないといった問題があった。 ちなみにリロードするとしっかりとどちらにも反映される。 (親)app.vueのなかに(app.vueから見たら子)show.vueもあって、(子)likeButton.vueもある。 (親,app.vueから見たら子)show.vueのなかに(子)likeButton.vueもある。 まぁ、なんかすこしややこしい。。。 app.vueのlikebutton.vueはしっかり機能してるし、 show.vueのなかのlikeButton.vueも機能してる。 問題はapp.vueからshow.vueをモーダルウィンドウで表示したときに、いいねがリアルタイムで反映されない。 [追記]色々あがいたが、きつかった。。。いつかリベンジ app.vue <drinkShow :drink=drink></drinkShow> <likeButton :drinkId=drink.id></likeButton> show.vue <likeButton :drinkId=drink.id></likeButton> likeButton.vue <template> <div> <div v-if="isLiked" @click="deleteLike()"> <div class="iine__button"> <i class="fas fa-heart"></i> {{ count }} </div> </div> <div v-else @click="registerLike()"> <div class="iine__button"> <i class="far fa-heart"></i> {{ count }} </div> </div> </div> </template> <script> import axios from 'axios' // import { csrfToken } from 'rails-ujs' // // CSRFトークンの取得とリクエストヘッダへの設定をしてくれます // axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken() export default { props: ["drinkId"], // user.idはdrinks/index.html.erbに定義 data(){ return{ likeList: [] // いいね一覧を格納するための変数 } }, computed: { // データが変更されるたび動く // ここではlikeListが変更される度に、count,isLikedが再構築される count(){ return this.likeList.length // いいね数を返す }, isLiked(){ // ログインユーザーが既にいいねしてるかを判定する if (this.likeList.length === 0){ return false} return Boolean(this.findLikeId()) } }, created: function(){ // vueインスタンスの作成、初期化直後に実行される this.fetchLikeByDrinkId().then(result =>{ console.log(result) this.likeList = result }) }, methods: { fetchLikeByDrinkId: async function(){ // async function() // jsの非同期処理 const response = await axios.get('/api/likes',{params: {drink_id:this.drinkId,user_id: user.id}}) // await // その投稿のいいね一覧を取得したい // もし処理が失敗したらプロセスから抜ける(処理をやめる?) return response.data }, registerLike: async function(){ // rails側のcreateアクションにリクエストするメソッド const response = await axios.post('/api/likes',{drink_id: this.drinkId,user_id: user.id}) this.fetchLikeByDrinkId().then(result => { this.likeList = result }) }, deleteLike: async function(){ // rails側のdestroyアクションにリクエストするメソッド const likeId = this.findLikeId() const response = await axios.delete(`/api/likes/${likeId}`,{params: {drink_id: this.drinkId,user_id: user.id}}) this.likeList = this.likeList.filter(n => n.id !== likeId) }, // ログインユーザーがいいねしているLikeモデルのidを返す findLikeId: function(){ const like = this.likeList.find((like) => { return (like.user_id == user.id) }) if (like) { return like.id } } } } </script> https://qiita.com/TK-C/items/0a3acb9d0d310f8fd380 この記事みたけど、俺がやりたいこととは違っているような気がしていて、 発火させるイベントはどちらも、likeButton.vueのイベントだから。。。 親コンポーネントのイベントを発火させるわけではない。 親コンポーネントの中にある、子コンポーネントのイベントを発火するわけだから。 どうすればいいのか。。。 一旦。従来の子コンポーネントから親のイベントを発火させるといったやりかたでやってみよう。 app.vueはlikeButton.vueをインポートしてるから、likeButtonのイベントも発火してくれる だろうと仮設を立てる。 いや、てかそもそもlikeButtonに書いてあることをapp.vueに書いてしまえばいいような気がする。 そしたら、show.vueから親コンポーネントのいいねに関するメソッドを呼び出せるし、、 app.vue <div> <div v-if="isLiked" @click="deleteLike()"> <div class="iine__button"> <i class="fas fa-heart"></i> {{ count }} </div> </div> <div v-else @click="registerLike()"> <div class="iine__button"> <i class="far fa-heart"></i> {{ count }} </div> </div> </div> <script> import axios from 'axios'; import likeButton from './packs/components/like/likeButton.vue'; import drinkShow from './packs/components/drinks/show.vue'; export default { components: { likeButton, drinkShow }, data: function(){ return { drinks: "drinks", likeList: [] } }, computed: { // データが変更されるたび動く // ここではlikeListが変更される度に、count,isLikedが再構築される count(){ return this.likeList.length // いいね数を返す }, isLiked(){ // ログインユーザーが既にいいねしてるかを判定する if (this.likeList.length === 0){ return false} return Boolean(this.findLikeId()) } }, created: function(){ // vueインスタンスの作成、初期化直後に実行される this.fetchLikeByDrinkId().then(result =>{ console.log(result) this.likeList = result }) }, mounted(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( this.drinks = response.data )) }, show : function(drink) { this.$modal.show(`display-drink-${drink.id}`); }, hide : function () { this.$modal.hide('display-drink-show'); }, fetchLikeByDrinkId: async function(){ // async function() // jsの非同期処理 const response = await axios.get('/api/likes',{params: {drink_id:this.drinkId,user_id: user.id}}) // await // その投稿のいいね一覧を取得したい // もし処理が失敗したらプロセスから抜ける(処理をやめる?) return response.data }, registerLike: async function(){ // rails側のcreateアクションにリクエストするメソッド const response = await axios.post('/api/likes',{drink_id: this.drinkId,user_id: user.id}) this.fetchLikeByDrinkId().then(result => { this.likeList = result }) }, deleteLike: async function(){ // rails側のdestroyアクションにリクエストするメソッド const likeId = this.findLikeId() const response = await axios.delete(`/api/likes/${likeId}`,{params: {drink_id: this.drinkId,user_id: user.id}}) this.likeList = this.likeList.filter(n => n.id !== likeId) }, // ログインユーザーがいいねしているLikeモデルのidを返す findLikeId: function(){ const like = this.likeList.find((like) => { return (like.user_id == user.id) }) if (like) { return like.id } } } } </script> likeButton.vueの内容をコピペ。 こうするときれいな親子関係ができた。 parent.vue <likeButton :drinkId=drink.id></likeButton> likeButton(child).vue registerLike: async function(){ // rails側のcreateアクションにリクエストするメソッド const response = await axios.post('/api/likes',{drink_id: this.drinkId,user_id: user.id}) this.fetchLikeByDrinkId().then(result => { this.likeList = result }) }, というふうに、定義していましたが、 app.vue <li v-for="drink in drinks" :key="drink.id" class="list" > <div v-else @click="registerLike(drink)"> <script> registerLike: async function(drink){ // rails側のcreateアクションにリクエストするメソッド const response = await axios.post('/api/likes',{drink_id: drink.id,user_id: user.id}) this.fetchLikeByDrinkId().then(result => { this.likeList = result }) }, </script> こんな感じで改めて定義し直す感じで。 って思ったら、 app.vue fetchLikeByDrinkId: async function(){ // async function() // jsの非同期処理 const response = await axios.get('/api/likes',{params: {drink_id: drink.id,user_id: user.id}}) // await // その投稿のいいね一覧を取得したい // もし処理が失敗したらプロセスから抜ける(処理をやめる?) return response.data }, のどの投稿にどれだけいいねがついてるかを見るメソッドで drink単体のidを取得しなければならない。。。 なんか、いいねが2つ一気につくようになってる。 app.vue <div v-else @click="registerLike(drink)"> app.vue registerLike: async function(drink){ // rails側のcreateアクションにリクエストするメソッド console.log(drink) const response = await axios.post('/api/likes',{drink_id: drink.id,user_id: user.id}) this.fetchLikeByDrinkId(drink).then(result => { this.likeList = result }) }, app.vue fetchLikeByDrinkId: async function(drink){ // async function() // jsの非同期処理 console.log(drink) const response = await axios.get('/api/likes',{params: {drink_id: drink.id,user_id: user.id}}) // await // その投稿のいいね一覧を取得したい // もし処理が失敗したらプロセスから抜ける(処理をやめる?) return response.data }, なんか、いいねが2つ一気につくようになってる。 ってなったのは // created: function(){ // // vueインスタンスの作成、初期化直後に実行される // this.fetchLikeByDrinkId().then(result =>{ // console.log(result) // this.likeList = result // }) // }, ここをコメントアウトしたから。 ほんとはいいねされてるけど、最初にいいねが表示されてないから、 2ついいねされてる感じになってる。。。。 created: function(){ // vueインスタンスの作成、初期化直後に実行される this.drinks.forEach(drink=> this.fetchLikeByDrinkId(drink).then(result =>{ console.log(result) this.likeList = result }) ) }, こんなかんじに記述。 mounted(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( this.drinks = response.data )) }, でdrinksの配列を一つ一つとりだして、実行してる。 ただ、mountedよりcreatedのほうが早く実行されるから意味ない。 created: function(){ // vueインスタンスの作成、初期化直後に実行される this.setDrink(); }, mounted(){ this.drinks.forEach(drink=> this.fetchLikeByDrinkId(drink).then(result =>{ console.log(result) this.likeList = result }) ) }, こんな感じに書き換えた、したらfetchLikeByDrinkIdが最初の方にしっかりと動いてくれて 最初にいいねが表示されるんじゃないかと予想。 結論なんか動かない。。。 this.drinks.forEach isnot function とかになる。。。 なんで。。。 registerLike: async function(drink){で console.log(this.drinksとかやるとアクセスできる。) てかさいあく、show.vueにいいねボタンを表示せずに諦めればいい話ではある。 app.vue <script> mounted(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/memos') .then(response =>( response.data.forEach(drink=> this.fetchLikeByDrinkId(drink)) )) }, fetchLikeByDrinkId: async function(drink){ // async function() // jsの非同期処理 const response = await axios.get('/api/likes',{params: {drink_id: drink.id,user_id: user.id}}) // await // その投稿のいいね一覧を取得したい // もし処理が失敗したらプロセスから抜ける(処理をやめる?) console.log(response.data) return response.data }, </script> 受け取った、drinksの配列。つまり投稿の配列を一つ一つにたいして、forEachをして、 fetchLikeByDrinkId をして、上手くいくと思ったがなんか上手くいかない。。。 axios.get('/api/drinks')にするべきでした。。。 methods: { setDrink: function(){ axios.get('/api/drinks') .then(response =>( response.data.forEach(drink=> this.fetchLikeByDrinkId(drink)) )) }, 改めてコンナ感じで定義 結局無理だったので、一旦諦めることに、、、、。 show.vueにはいいねボタンを表示しないことにしーとこ。。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby on rails]ゲストログイン機能 ゲストログインは削除、編集されないようにする

ゲストログインは作っておいた方が良い! ゲストログイン押したら、ログインできちゃう便利機能です。 就職、転職活動でポートフォリオを外部の方に見せる場合には、作っておいた方が良いらしいです。 ゲストユーザーを作成せず、これでログインしてくださいとemail,passwordを提示したところで、 少し面倒なので見てもらえない可能性があります。 すぐ実装できるので、ゲストログインは作っておくべきだと思います!!!! (他の人のポートフォリオ触ってみてもそうですが、 ゲストログインがないと、やっぱりすごく面倒に感じます。) とても参考にした記事 気をつけないと、すぐ壊れる!! 今回ゲストログイン機能を実装しました。実装自体はうまくいったのですが、、 気をつけないとゲストログイン機能は簡単に壊れていきます。 落とし穴としては今のところ以下2点だと認識しています。 ①ゲストログインは、退会(削除)ができないようにする! ②ユーザー情報の編集もできないようにする! 私の場合、新規登録時に入力させる情報は以下の通りになっています。 ルーティングをかく Rails.application.routes.draw do mount RailsAdmin::Engine => '/admin', as: 'rails_admin' devise_for :users, controllers: { registrations: 'users/registrations', sessions: 'users/sessions', } #ここ追記しています!!!!! devise_scope :user do post 'users/guest_sign_in', to: 'users/sessions#guest_sign_in' end namespace :departments do resources :searches, only: :index end 省略 [devise_scope]は、deviseに新しくルーティングを追加したい場合に使います。(これを使わないとエラーが発生します。) devise_scope :users do get 'ルーティング情報', to: 'users/registrations#アクション' end コントローラー記述 def guest_sign_in user = User.guest sign_in user redirect_to posts_path, notice: 'ゲストユーザーとしてログインしました。' end モデルファイル記述 user.rb def self.guest find_or_create_by!(name: 'ゲストユーザー', email: 'guestda@example.com', join_year: '新入社員', department_id: 1, is_valid: true) do |user| user.password = SecureRandom.urlsafe_base64 end end ゲストログインボタン <li class="nav-item"> <%= link_to "ゲストログイン", users_guest_sign_in_path,class:"btn btn-light fas fa-seedling",method: :post %> </li> ひとまず終わり! ここまででゲストログインはできなくなったかと思います。 あとは、ゲストログインを削除、編集できないようにします。 退会させないようにする ルーティング devise_for :usersになってる場合は、書き換えてください。 devise_for :users, controllers: { registrations: 'users/registrations' } 削除しようとしたら、トップ画面へリダイレクト 私は論理削除にしています。 app/controllers/users_controller.rb def destroy_confirm @user = current_user end def destroy_user @user = current_user if @user.email == 'guestda@example.com' reset_session redirect_to :root else @user.update(is_valid: false) reset_session redirect_to :root end end もしゲストユーザーが退会処理をしようとしたら、 ログアウトさせトップページにレダイレクトさせます。 編集させないようにする ユーザー情報編集されると、 私の場合、「部署」「クラス(新入社員)」とか変更されると、 ゲストログインが壊れました。 なので、ゲストユーザーの場合には、 編集ページが表示されないようにしました。 edit.html.erb <% if @user.email == 'guestda@example.com'%>  <p>ゲストユーザーはユーザー情報の編集ができません。</p> <% else %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[axios] ファイルダウンロードができない or 空ファイルになってしまう現象の解消

フロント側はReact、サーバーサイドはRailsでファイルダウンロードが うまくいかないケースがあり、それを解消した記事内容になります。 症状としては、axiosのissueにあったこのケースに当たります。 Github | axios/axios | Empty response when responseType is blob or arraybuffer. サーバーサイドとフロント部分の結合部分なので、 原因の特的ができていなくて引っかかってる人が多そうですね。 私のケースの場合は、railsでheadの記述が不要だったのですが、 それだけじゃよくわからないと思います。 実装をみながら解消方法をみていきましょう。 フロント側の記述 フロント側は以下のようなコードにしています。 $ yarn add file-saver import { saveAs } from 'file-saver'; ~~ export const fileDownload = () { axios .get(url, { ~~, responseType: 'blob', }) .then((response) => { const blob = new Blob([response.data], { type: response.data.type, }); saveAs(blob, downoloadFileName); }); } (ちなみにaタグにdownload属性をつけるやり方もあります。) これでフロント側はOKです。 ただ、空ファイルをダウンロードしてしまう場合があります。 const blobの上でブレイクポイントで止めて見ると、以下のような感じです。 Blobのサイズが0になっていたり、画像やpdfをダウンロードしようとしているのですが、typeがtext/xmlになっています。 こんなときはサーバーサイド側に問題があります。 サーバーサイド側の記述 私の触ったコードのケースではrailsでsend_data()を使ってfileの実体を送っていました。 記述は以下のような感じにもともとなっていました。 def download upload_file = UploadFile.find(file_params) file = upload_file.file.blob.download # モデルでhas_one_attached :fileを記述 if send_data(file, disposition: 'attachment', filename: upload_file.file.blob.filename.to_s, ~~ ~~ head :no_content # これがあるとうまくいかない。 else ~~ end end send_data()の引数自体は問題ないのですが、このコードはうまくいきませんでした。 head :no_contentが不要でした。 head :no_contentを削除すると、フロント側のレスポンスが以下のように変わります。 Blobのsizeやtypeにちゃんと値が入ります。 ダウンロードした画像も表示できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby on rails】JavaScript 非同期通信のコメント機能でエラーメッセージを出す バリデーション

初めに 非同期通信でのコメント機能は実装ずみで、「401字以上のコメント」はNGというバリデーションは、 かけていたものの、エラーメッセージが出るようにしていませんでした・・・。 少し実装に苦労はしたものの、非同期通信への理解が深まったのでまとめていきます!! バリデーションをかけておく app/models/post_comment.rb class PostComment < ApplicationRecord default_scope -> { order(created_at: :desc) } # あるコメントに紐づくユーザーも記事も1 belongs_to :user belongs_to :post validates :comment, presence: true, length: { maximum: 400 } end 一応マイグレーションファイルも。 db/migrate/20210801122808_create_post_comments.rb class CreatePostComments < ActiveRecord::Migration[5.2] def change create_table :post_comments do |t| t.text :comment, null: false t.integer :user_id,null: false t.integer :post_id,null: false t.timestamps end end end app/views/post_comments/_comment.html.erb <%= form_with(model:[post, post_comment], remote: true) do |f| %> <%= f.text_area :comment, rows:'2',placeholder: "感想や疑問点をコメントで伝えましょう",required: true,class:"form-control" %> <%=f.submit "コメントする",class:"mt-2 btn btn-outline-secondary btn-block btn-sm"%> とりあえず、ここまでやると空欄での投稿に対しては以下のように出てきます。 ただ、401文字打ってもバリデーションには引っかかるものの何も出てこないという状況です。 コントローラーで保存できなかった場合の記述を書く! controllers/post_comments_controller.rb def create post = Post.find(params[:post_id]) @comment = current_user.post_comments.new(post_comment_params) @comment.post_id = post.id @post = Post.find(params[:post_id]) if @comment.save # ユーザーステータス無効で、投稿者とコメント者が等しいとき if current_user != @post.user && @post.user.is_valid == true @post.create_notification_by(current_user) end @post_comment = PostComment.new else render 'error' end end 通知機能も実装してるのでわかりにくくなっているかもしれません・・・・すみません。 コメントがバリデーションに引っかかってしまい保存されなかったら、 render 'error'というところがポイントです!!! さてrender先にいきます。 error.js.erb views/post_comments/error.js.erb $("#comments-error").html("<%= j(render 'layouts/errors', obj: @comment) %>"); コメントが401字でバリデーションに引っかかってるので、@commentの持ってる値としては ”false"になっています。 そしてここの記述としては、idがcomments-errorとなっているどこかに、 <%= j(render 'layouts/errors', obj: @comment) %>を渡すという意味 です。 ちなみに'layouts/errors'の中身はこんな感じになっています。 <% if obj.errors.any?%> <div class="text-center" style="color:red;"> <%=obj.errors.count %>件のエラーが発生しました。<br> <% obj.errors.full_messages.each do |message| %> <%= message %> <% end %> </div> <% end %> さてこのエラー文を差し込む、id = comments-errorの箇所を作りましょう。 エラーメッセージを出す app/views/post_comments/_comment.html.erb #ここです!!!! <div id ="comments-error"></div> <%= form_with(model:[post, post_comment], remote: true) do |f| %> <%= f.text_area :comment, rows:'2',placeholder: "感想や疑問点をコメントで伝えましょう",required: true,class:"form-control" %> <%=f.submit "コメントする",class:"mt-2 btn btn-outline-secondary btn-block btn-sm"%> <% end %> <div id ="comments-error"></div>という箇所を作成しました。 これでコメント欄のすぐ上にエラーメッセージができます。 まとめ:非同期通信のエラーメッセージの流れ コメントがバリデーションに引っ掛かり保存されない ↓ errorのjsファイルに飛ぶ ↓ jsファイルは指定されてるidに、html以下を差し込む ↓ 該当のidが記載されてる箇所で、html以下を受け取り表示する。 要するに、普段は <div id ="comments-error"></div>なってるところが、 <div id ="comments-error">render 'layouts/errors', obj: @comment</div> になってくれて、エラー文が出てくると! そもそも非同期通信とは.. 最後に書くことはでなはいですが、簡単にまとめていきます。 簡単にいうと「データを送信したら、もう画面を書き換えてもらう」ということです。 サーバーからの結果は待ちません。 通常であればちゃんと応答待ちますが、非同期通信は待ちません! メリットとしては待つ必要が無いから、早いということです。 サーバーが処理してる間に、操作ができます。 「あとやっといて〜」って感じでしょうか。 最後に 非同期通信がうまくいかないときって、大体スペルミスのような気がしますので、 スペルミスにご注意ください!!!!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】地域の魅力を発信できるデジタルパンフレットサービス「Openパンフレット」を作った

はじめに デジタルパンフレット作成公開サービス Openパンフレット をリリースしました。 ​ 個人開発です。 ​ バックエンドはだいたいAWSです。 ​ Android未対応?​ ​バックエンドの技術者がフロントやらインフラやらアプリやら色んな所に手を出して作ったシステムになっています。 記事本文はZennで書いているのでこちらです。 宣伝 Android版の開発のためクラウドファンディング募集しています。 よかったら見て下さい。励みになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby on Rails】デプロイ後、本番環境で画像が表示されない不具合を解消する方法

対象者 ローカル環境で表示される画像が本番環境で表示されない方 目的 本番環境でも問題なく画像を表示させる 実際の手順と実例 1.前提 ruby 2.5.1 rails 5.2.5 AWS app/assets/imagesの中に画像が保存されている 自動デプロイできている状態 2. 結論 変更前 <img src="assets/search.png" alt="#"> ↓↓↓ 変更後 <%= image_tag asset_path("search.png", alt: "") %> これで実装完了です! 3.原因 本番環境では、画像もコンパイルされることが原因のようです。 ※コンパイルとは、ソースコードを機械語に翻訳する作業のこと 本番環境ではパス名と画像の名前が変わっちゃうので 両方に対応できるasset_path()を使い、解決できました! 参照 【ちゃんと理解してる?】コンパイル、ビルド、デプロイの違い 投稿者コメント 開発環境で表示されていたのに本番環境で反映されていないと血の気が引きますよね。。。笑 上記変えるだけで簡単に解決できます! My Profile プログラミング学習歴3ヶ月目のアカウントです! プログラミングスクールで学んだ内容や自分が躓いた箇所等のアウトプットの為に発信しています。 また、プログラミング初学者の方にわかりやすく、簡潔にまとめて情報共有できればと考えています。 もし、投稿した記事の中に誤り等ございましたら、コメント欄でご教授いただけると幸いです。 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby on Rails】gem, bundlerって?

この記事について この記事は、筆者がRuby on Railsを学習・使用する中で疑問に思ったbundle コマンドについて、どういった意味があるのかという内容をまとめていきます。 まだまだ初学者ではあるので、なにか間違った理解等ございましたらぜひご指摘いただけますと幸いです。 bundlerとは プロジェクト内で使用するgemのパッケージ管理ツール。 gemのバージョンやgemの依存関係を管理してくれる「gem」 bundlerの特徴 bundlerを使ってgemをインストールすると、gem同士の互換性を保ってくれる。 Gemfileという1つのファイルにgemを書くので、gemの管理がしやすい Gemfileを使ってアプリごとにgemを管理できる 環境ごとにインストールするgemを管理できる gemの互換性を保ってくれる。Gemfileさえ共有すれば複数人での開発でもエラーを起こさない 参考 https://pikawaka.com/rails/bundler つまりgemを複数使っている場合に発生するgem同士の依存関係を、ひとまとめに管理してくれる便利なgemという理解で問題はないっぽい。 gemとは gemとはrubyのライブラリのこと。 gemはRubyGemsと呼ばれるRuby用のパッケージ管理システムで管理されており、RubyGemsが提供するgemコマンドを通じてインストール等ができる。 パッケージ管理システムは各言語ごとに用意されている。 Python → pip Java → maven node.js → npm Gemfileとは railsアプリで利用するgemの一覧を整理(記述)するファイル。 bundle install コマンドが実行されると、このGemfileに記載されているgemの一覧を参照し、まだインストールされていないgemがあればインストールを実行してくれる。 Gemfile.lockとは Gemfileをもとに実際にインストールされたgemの一覧とバージョンが記載されたファイル。 Gemfile.lockには依存関係にあるgemも含め、bundlerによってインストールされたすべてのgemとそのgemのバージョンが記載されている。 Gemfileをもとに、bundlerによってインストールされたgemの結果が出力されるファイル。 そのため、Gemfile.lockを手動で更新することはない。 bundle install やbundle update を実行することで自動更新される。 まとめ gem rubyのライブラリ(便利機能ツール) Gemfile Ruby on Railsアプリで使用するgemを記述してまとめたもの Gemfile.lock bundle installなどによって、実際にインストールされたgemの結果が出力されるファイル 参考 https://qiita.com/nishina555/items/1b343d368c5ecec6aecf https://nishinatoshiharu.com/difference-gemfile-gemfilelock/ https://qiita.com/kamohicokamo/items/ded4cad5be1778547640
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む