20210909のRailsに関する記事は24件です。

【Rails】DB上は文字列だけどモデルでは配列として扱いたい

環境情報 Rails 5.2.6 やりたいこと ActiveRecord側ではintegerの配列で扱いたいが、DBにはカンマ区切りの文字列として保存したい やりかた fooというテーブルのfoo_idsをintegerの配列として利用したい テーブル作成 create_table :foos do |t| t.string :foo_ids end Coderクラス作成 activemodelのattributeの仕組み上、頻繁にdump,loadが呼び出されるので、バリデーションなども行う場合は要注意 class IntegerArray def self.dump(object) return "" if object.blank? object.join(",") end def self.load(string) return [] if string.blank? string.split(",").map(&:to_i) end end モデル作成 class Foo < ApplicationRecord serialize :foo_ids, IntegerArray end 確認 [10] pry(main)> foo = Foo.new [12] pry(main)> foo.foo_ids = [1, 2] => [1, 2] [15] pry(main)> foo.save! (3.2ms) BEGIN Foo Create (2.7ms) INSERT INTO `foos` (`foo_ids`) VALUES ('1,2') (7.3ms) COMMIT => true [16] pry(main)> Foo.first Foo Load (2.8ms) SELECT `foos`.* FROM `foos` ORDER BY `foos`.`id` ASC LIMIT 1 => #<Foo:0x00007fbfb7be5ae0 id: 3, foo_ids: [1, 33]> serializeつかっていいの? 基本的に避けたほうが良いのは同意できるが、パフォーマンスなどの都合でどうしても正規化できない状況もあるのでトレードオフを考慮して使うべきだと思いました。(storeはまだ使い所がわかりません) また、具体的にどの処理が重いのか検証した記事が少なく、調べてみたいと思います。to be continued... 他の方法 独自のActiveRecord::Type::Serializedを定義して、serialize毎再実装する(大変だが少しだけ最適化できる) モデル側にintegerの配列として扱えるメソッド群を用意する(うまくmodule化すれば一番安定?) composed_ofでValueObjectのように扱う
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】フォームのplaceholderを改行する方法

はじめに 今回はフォームの中のplaceholder(下記の画像のもの)を改行する方法について記述します。 開発環境 Ruby on Rails バージョン2.6.5 通常(改行を不要とする場合)の記述 通常改行が不要な場合の記述はこのように記述します <%= f.text_area :hoge, class:"hoge", id:"hoge", placeholder:'ここに表示させる文字を入力します' %> ですが、改行をしたplaceholderを作成する場合は、上記の記述に改行を加えても反映されないので、rails helperを使用することで問題が解消されます! ※rails helperをご存知ない方はまずはこちらの記事をご覧ください! https://qiita.com/yukiyoshimura/items/f0763e187008aca46fb4 rails helperを使用して改行する まずrails helperにplaceholder内容を記述します! app/helpers/表示させたいビュー名_Helper module 表示させたいビュー名Helper def メソッド名 <<-"EOS".strip_heredoc            表示させたい文字            表示させたい文字            表示させたい文字 EOS end end ( <<-"EOS".strip_heredoc 〜 EOS この記述をすることで余分なスペースなどが生まれるのを防ぎます) rails helperにメソッドを定義できれば後はビューファイルにメソッドを呼び出します。 <%= f.text_area :hoge, class:"hoge", id:"hoge", placeholder:rails helperに定義したメソッド名 %> 完成形 先程の記述で無事に改行ができました! ちなみに『<<-"EOS".strip_heredoc 〜 EOS』の記述がないとこのように無駄なスペースが生まれてしまいます。 参考になれば幸いです?‍♂️
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】正規表現これだけ理解しておこう!

正規表現を苦手としている方は少なくないと思います。 ただ、正規表現に関しては基礎的な部分だけおさえておけば大きな問題はないと思うので、ここで理解を深めてしまいましょう! ある程度自信のある人は一番最後の例題を見てパッと理解できるか確認してみてください。 正規表現とは 正規表現とは、文字列のパターンを表したものです。 正規表現は/ /で囲んで記述します。 正規表現の基礎項目 正規表現を扱う上で必要な基礎項目を列挙していきます。 =~ 正規表現と文字列がマッチするかをどうかを調べるためのメソッドです。 正規表現 =~ 文字列 マッチする場合、文字の位置を返し、マッチしない場合、nilを返します。 puts /a/ =~ "dance" #1 ^ $ \A \z ^は行頭にマッチ $は行末にマッチ \Aは文字列の先頭にマッチ \zは文字列の末尾にマッチ [ ] いくつかの文字のうちその1つを指定したい場合は[ ]で囲む。 /[abc]/ #aかbかc /[a-z]/ #a~z //[^abc] #abc以外の文字 ※単なる文字として-を表したい場合は[ ]内の最後に記述します。 . .(ドット)はどんな文字にもマッチする。 /aaa.../ =~ "000aaabcdef" #aaabcdがマッチ \を使った記法 \s 空白文字を表す \d 0~9までの数字にマッチ \w 英数字にマッチ \A 文字列の先頭にマッチ \z 文字列の末尾にマッチ また、メタ文字を普通の文字として使うときは\をつける。 例えば、[に対しては\[とする。 繰り返し * 0回以上の繰り返し + 1回以上の繰り返し ? 0回か1回 {n} n回の繰り返し {n,m} n~m回の繰り返し 「0回以上の繰り返し」の意味が最初はよくわからないかもしれませんが、「直前の文字が1回以上繰り返されているか、なくてもいい」と考えると理解しやすいです。 {n}に関しては5回以上なら{5,}5回以下なら{,5}5~10回なら{5,10}とかけます。 最短マッチ *? 0回以上の繰り返しのうち最短の部分 +? 1回以上の繰り返しのうち最短の部分 *や+は、可能な限り長い部分にマッチさせます。 *?や+?とすると最短部分にマッチするようになります。 string = "abcdabcdabcd" /a.*b/ =~ string #"abcdabcdab"がマッチ string = "abcdabcdabcd" /a.*?b/ =~ string #"ab"がマッチ i オプション iをつけると、アルファベットの大文字小文字の違いを無視する。(どちらでもマッチする) string = "abcdABCDabcd" /a.*b/i =~ string #"abcdABCDab"がマッチ キャプチャ 正規表現を使ってマッチした部分を色々と操作することができます。 ここをおさえられると実用性がグッと高まります。 マッチした結果を保持する変数 $` マッチした部分より前の文字列 $& マッチした部分の文字列 $' マッチした部分より後ろの文字列 string = "abcdef" /c./ =~ string p $` #"ab" p $& #"cd" p $' #"ef" また、正規表現を( )で括った部分を$1で取ることができます。 string = "abcdef" /(.c)/ =~ string p $1 #"bc" 正規表現ないで( )が複数つけた場合は順に$2、$3でマッチした部分を撮ることができます。 gsubメソッド マッチした文字列を別の文字列に置き換えるメソッド。 string = "a b c d e" p string.gsub(/\s+/, " ") #"a b c d e" ブロックを渡すこともできる。 string = "abcabcabc" string.gsub(/.c/) do |matched| p matched end #bc #bc #bc ちなみにsubメソッドは最初にマッチした部分だけを置き換えます。 scanメソッド マッチした部分を取り出す。 置換をしないのであればこっちを使えばいい。 string = "abcabcabc" string.scan(/.c/) do |matched| p matched end #bc #bc #bc 例題 ここまでで基本はいいと思います。 例題をいくつか見てみましょう。 例1 問:emailのドメイン部分を抜き出す。 email = "hogefuga@gmail.com" /[a-z\d]+@([a-z]+\.[a-z]+)/ =~ email p $1 #"gmail.com" 例2 問:「正規表現は難しい!」を「正規表現は楽しい!」に変更する。 string = "正規表現は難しい!" p string.gsub(/難しい/, "楽しい") #"正規表現は楽しい!" ちなみに、複数の変更をすることもできます。 「正規表現は難しい!無理だ!」を「正規表現は楽しい!ヤッホー!」に変更する。 string = "正規表現は難しい! 無理だ!" p string.gsub(/難しい|無理だ/, "難しい" => "楽しい", "無理だ" => "ヤッホー") 例3 問:"my-name-is-taro"を"My-Name-Is-Taro"のように変換できるメソッドを作る。 def word_capitalize(string) result = "" string.gsub(/-?[a-z]+-?/) do |matched| matched[0] = matched[0].upcase result += matched end return result end p word_capitalize("my-name-is-taro") 例1〜3は、他にもやり方があると思いますので色々試してみてください。 まとめ 正規表現の基本について確認をしました。 rubyの正規表現ですが、例えばJabaScriptでもほとんど同じような形で使えるので、基本をおさえておくと実装の幅が広がるかと思います。 今回は以上です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]collection_selectにclassをつける

はじめに 本記事では、collection_selectにclassをつける方法を記述します。 うまくいかないコード 第5引数、第6引数にオプションを記述し、第7引数にclass名を指定しています。 <%= 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 %> ここが間違えているようです。 <%= 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/> 修正したコード 第5引数、第6引数にしていたオプションを {}で記述することで、 第5引数にまとめました。 また、第6引数にclass名を指定してましたところ、うまくCSSが効きました。 f.collection_selectの場合は第6引数をclass名にする必要があるらしいです。 オプションの後に{}無しで記述すると、HTMLのオプションと認識されるようです。 <%= 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 %> 以上です。 終わりに 短時間で解決できましたが、やや特殊なケースかなと思い、記事にしました。 以下参考サイトです。 collection_select not inserting 'class' attribute? collection_selectにclassをつける方法 Railsドキュメント collection_selectタグでclassが反映されない 明日も頑張ります!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テーブルに後からユニーク制約(一意性)を追加する

やりたいこと User毎に登録Wordを独立させて、User毎に登録Wordのユニーク制約を適用したい。 最初に作成したWordテーブルはユニーク制約を用いて同じWordの登録を不可にしている 新たにUserテーブルを作成した このままではUser全体でWordにユニーク制約がかかってしま(例:User1とUser2でそれぞれtestという単語を登録できない) やったこと 下記の記事を参考にして、新たにマイグレーションファイルを作成しadd_indexの部分を記述した。(add_referenceは外部キー制約を追加するための記述で、ここでは説明を省く) uniqueness: scope を使ったユニーク制約方法の解説 class AddUserToWords < ActiveRecord::Migration[6.1] def change add_reference :words, :user, null: false, foreign_key: true add_index :words, %i[word user_id], unique: true end end ちなみに、最初に作成したWordのマイグレーションファイルには以下のようにユニーク制約が設けてある。 class CreateWords < ActiveRecord::Migration[6.1] def change create_table :words do |t| t.string :word, null: false, limit: 100 t.index :word, unique: true t.timestamps end end end この状態でrails db:migrateを実行すると、schema.rbは以下のようになる。 schema.rb create_table "words", force: :cascade do |t| t.string "word", limit: 100, null: false t.index ["word", "user_id"], name: "index_words_on_word_and_user_id", unique: true t.index ["word"], name: "index_words_on_word", unique: true end add_foreign_key "words", "users" end エラー内容 上記の状態でUser1とUser2で同じtestという単語を登録しようとすると下記のエラーが発生した。 ActiveRecord::RecordNotUnique in WordsController#create PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_words_on_word" DETAIL: Key (word)=(test) already exists. エラーの原因 エラー文に書いてあるとおり、 index_words_on_wordの部分が悪さをしているので、元々のWordのマイグレーションファイルに記述してあるt.index :word, unique: trueが原因。 解決策 元々のWordのマイグレーションファイルに記述してあるt.index :word, unique: trueを消去する。 マイグレーションファイルに下記を追記してrails db:migrate:resetを実行した。 class AddUserToWords < ActiveRecord::Migration[6.1] def change add_reference :words, :user, null: false, foreign_key: true add_index :words, %i[word user_id], unique: true remove_index :words, :word end end 無事にUser毎に登録Wordを独立させて、User毎に登録Wordのユニーク制約を適用することができた。 学んだこと これまでデータベース周りをいじることが少なかったため、理解すると簡単なことだったが解決までに時間を要してしまった。 今回の経験を通じてデータベース周りの理解が深まった。 補足 今回はデータベースをリセットしても問題ないため、rails db:migrate:resetを実行した。 リセットしたら困る場合は新たにマイグレーションファイルを作成し、そこにremove_indexを記述し、rails db:migrateで対応する必要があると考える。 また、リセットして構わないのであれば、最初のWordマイグレーションファイルからt.index :word, unique: trueを削除するのでも良かったかもしれない。 ちなみにモデル側でも下記のようにscopeを指定してユニーク制約を設けている。 word.rb class Word < ApplicationRecord belongs_to :user validates :word, presence: true, uniqueness: { scope: :user_id }, length: { maximum: 100 } end データベースとモデルの両方でユニーク制約をつける意味については、Railsチュートリアルの説明が理解しやすかったです。 参考 uniqueness: scope を使ったユニーク制約方法の解説
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでCSS読み込ませたらAssetNotPrecompiledって出る対処法

困っていること Railsで初めてstylesheet_link_tag使ってCSS読み込ませようとしたらSprockets::Rails::Helper::AssetNotPrecompiled inってエラーが出て萎えました。 _left_menu.html.erb <%=stylesheet_link_tag 'left_menu', media: 'all' %> 解決方法 config/initializers/assets.rbに以下を記述 config/initializers/assets.rb Rails.application.config.assets.precompile += %w( *.css ) ターミナルで以下コマンドを実行する $ rake assets:precompile 一度webサーバーを停止し、再起動(私の場合dockerで開発してるので以下のコマンドを実行) ↑これやるの忘れてて萎えました。 $ docker-compose down $ docker-compose up -d あるいは $ docker-compose restart これで解決! おまけ(jsの場合) CSSと全く同じ方法で出来ます。 config/initializers/assets.rb Rails.application.config.assets.precompile += %w( *.js ) ちなみにcssとjsどっちも一気に設定する場合は以下 Rails.application.config.assets.precompile += %w( *.css *.js )
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyってなんやねん

こんにちは! スクールのカリキュラムの通りに実装を進めて教材の通りに Ruby on RailsでWebアプリケーション作成しました。 しかしふと思ったんです。 何でRuby? ラビットの麒麟川島「Rubyだ」 PHP、Java, Pythonとあって何でRubyを俺は使ってる?? と思いました。 まさしく僕は誰?状態です。 メモ用としてRubyのことについてディグっていきたいと思います。 いつできて誰が作ったの? Rubyが生まれたのは1995年です。 はいそうです、僕と同い年です。 しかも開発された方は日本人の まつもとゆきひろさんという方だそうです!! 海外旅行に行くと レペゼンジャパンはドラゴンボールやキャプテン翼などが挙げられますが、 エンジニア界隈では「Rubyやん!」と言われることもあるんじゃないの。 Rubyの特徴 動的型付き言語でありシンプルな構文なので記述量が少なく、 プログラミング初心者でも比較的入りやすい言語のようです。 また、オブジェクト指向を採用しているため、保守、運用に優れています。 これ初めて聞いた時、因みに僕はこう思いました。 どこがやねん めちゃくちゃ何言ってんのかわからんがな。 数こなしていく内に点と点が線でつながるようになりましたが、 プログラミングにおいて数をこなす事が大事だったのかなと今では思っています。 動的型付き言語???? むちゃくちゃわからん単語出てきたがな。理解できんまま進めんわ。 ということで調べてみました。 そもそも動的と静的という言葉はなんやと思い調べると 動的:状況によって状態を変える 静的:状況によらず状態を変えない だそうです。 動的サイトならユーザーの要求で表示内容を変えるサイト 静的サイトならいつでも表示する内容が変わらないサイト と言えます。 これを踏まえて 動的型付け言語とは 変数などのデータ型の宣言がいらない言語のことで、 静的型付け言語とは、 変数などのデータの定義が必要な言語 ということです。 記述の違いを見て考えていきましょう。 動的型付け言語の記述例 a = 2 b = 3 puts a + b 簡単に書けますね。実行結果は5です。 静的型付け言語の記述例 #include <iostream> using namespace std; int main() { int a = 2; int b = 3; cout << a + b << endl; return 0; } いやなっが。 すごいですね計算結果5を出す為だけにこんなに記述しないといけないのか。 int型という整数の変数a,bが定義されています。 動的型付けと静的型付けを比較して解ること 動的型付け言語なら変数などのデータ型の宣言がいらないので 記述量が大幅に減り、比較的容易にプログラムを書くことができます。 短く簡単にプログラムをかけるんなら全部動的型付け言語でええやんけと 思いました。 静的型付け言語のメリットについても考察してみました。 コンパイル時にエラーが発生 パフォーマンスの向上 コンパイル時にエラーが発生 コンパイル時にエラーが発生するので プログラムを実行する前にエラーを特定することが可能です。 int型の変数なのに文字列を代入しようとしてしまってるとかですかね。 確かに動的型付け言語だとプログラムが実行された後にエラーが発生するので 見返すのに時間がかかったりしちゃうんですよね。 パフォーマンスの向上 静的型付け言語ならデータの型が分かるので、 整数同士の計算や 小数同士の計算が プログラム実行前に解る。 データの定義がされているのでコードを見返すのが楽ちんですね。 また、Rubyはインタプリタ方式を採用しています。 インタプリタ方式とコンパイル方式 インタプリタ方式とは簡単にいうと プログラムの内容を人間がわかる言葉からコンピュータがわかる言語へ一つずつ翻訳しながら動かす方式のことです。 人間がプログラムを入力→ 入力されたプログラムをコンピュータが解るように翻訳→ 翻訳された命令に従ってコンピュータが仕事を実行する というそんな流れ。 この入力されたプログラムを1行ずつ繰り返すのがインタプリタ方式です。 彦摩呂のグルメコメントでインタプリタ方式を例えてみましょう。 彦摩呂がウルフギャングでステーキを頬張る(人間がプログラムを入力)→ 彦摩呂の心の声「ん?ジューシーやな?これは何てコメントしよかいな」(入力されたプログラムをコンピュータが解るように翻訳)→ 彦摩呂がコメント「味のIT革命や〜」(翻訳された命令に従ってコンピュータが仕事を実行する) コンパイル方式は入力されたプログラムを全部翻訳しその翻訳された プログラムを実行する方式です。 コンパイル方式を彦摩呂で例えてみましょう。 彦摩呂がステーキを完食する(人間が入力したプログラムをコンピュータが全て翻訳)→ 彦摩呂のコメント「赤かぁ~。てんこ盛り~~っ!食べ物を美味しく表現するのが僕の使命です~。お肉と野菜とタレの騎馬戦や~。肉汁のドリンクバーや~。」(翻訳されたプログラムを基にコンピュータが実行) Rubyのデメリット インタプリタ方式を採用してるから処理速度が遅いんですよね。 上記の彦摩呂のグルメコメントを例を基に考えてみましょう。 インタプリタ彦摩呂はステーキを一口一口頬張るごとに、 いちいちコメントをしています。 それに対してコンパイル彦摩呂はステーキを完食してから 考えたコメントを発言しています。 食べ終える時間が遅いのは明らかにインタプリタ彦摩呂ですよね。 インタプリタ方式を採用するということは、コードの記述量が増えれば増えるほど 処理速度が遅くなるということです。 食べる量が増えれば増えるほど彦摩呂は食べ終えるのに時間がかかりますよね。 だから大規模なアプリや処理速度が命のアプリなものには Rubyは向いていないと言えますね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails Checkboxのクリック箇所を画像と連動させる方法

やりたいこと チェックボックスの代わりに画像をクリックで反応してほしい サンプルGIFは"画像をクリック"⇨"チェック状態"→"グレースケールからカラー状態に変化"するようにしました このページでも同じようなコード使ってます 実装コード html.erb <body> <%= form_with model: @blog do |f| %> <%= f.check_box :タイトル %> <%= f.label "タイトル" , class:"ラベル" do %> <%= image_tag "minecraft.png" , class:"写真"%> <span>文字もいけるよ</span> <% end %> <% end %> </body> css .写真{ width: 300px; cursor: pointer; filter: grayscale(100%); transition: 0.5s; } input:checked + label .写真 , input:checked + label span { filter: grayscale(0); font-weight: bold; color: red; } 説明 取り急ぎ自分用で書きました 理解し始めたら詳細を書いていきます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails | バリデーション validates と validate の違い

モデル(model)にバリデーションを書く際に使い分けすることがあったのでメモ。 それぞれの違いについて validates 標準のバリデーションを書くときはこれ 使い方はRailsガイドを参照 https://railsguides.jp/active_record_validations.html validate 自分でオリジナルのバリデーション用メソッドを書くときはこれ validateの使用例 model validate :photo_size, :photo_length private def photo_size photos.each do |photo| if photo.blob.byte_size > 5.megabytes photo.purge errors.add(:photos, "は1つのファイル5MB以内にしてください") end end end def photo_length if photos.length > 10 photos.purge errors.add(:photos, "は10枚以内にしてください") end end 私はActiveStorageでバリデーションを作成した際に違いに気づき、疑問に思ったので残しておきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rbenvで古いrubyが使えなくなったので、rbenv再インストールからやり直す。

前提 Ruby on Railsで古いruby(2.3.0),rails(4.1.5)を使いたい。 MacOS Catalina 10.15.5 nokogiriのエラーから、rbenv再インストールとかやってたら、rbenv: gem: command not foundになった。 rbenv install 2.3.0が実行できず、opensslが必要と言われている所からです。 ※ 思い出しながらなので少し曖昧です。 内容 opensslがインストールできないのを解消 ターミナル rbenv install 2.3.0 ターミナル The Ruby openssl extension was not compiled. BUILD FAILED ↑みたいに怒られる。 ターミナル brew install openssl ターミナル brew reinstall openssl brew reinstall opensslするように促されるので実行。 Warningで以下のエラー。 ターミナル Error: An exception occurred within a child process:  FormulaUnavailableError: No available formula with the name "/usr/local/opt/php@7.1/.brew/php@7.1.rb". brew doctorでも↓が出てる。 ターミナル Warning: Some installed kegs have no formulae! This means they were either deleted or installed with `brew diy`. You should find replacements for the following formulae: php@7.1 PHPの環境を作ってるわけでもなく、 元々PHP依存の何かを使っていた訳でもない。 おそらく適当にコピペでコマンドを試してしまった(絶対ダメ)のだろうと↓を実行。 ターミナル brew uninstall php@7.1 brew doctorのWarningも消え、 brew reinstall opensslが通りました。 rbenv install 2.3.0できないことの解消 この時点で、 rbenv install 2.3.0がコケる。 rbenv install 3.0.2とかは大丈夫でした。 ruby2.4以下は、openssl@1.1ではビルド失敗するらしい。 なので↓のコマンドを使い、openssl@1.1を使ってビルドしていることをまず確認。 ターミナル echo $RUBY_CONFIGURE_OPTS 次に↓でopenssl@1.0に変更、変更できた事を確認。 ターミナル $echo 'RUBY_CONFIGURE_OPTS="--with-openssl-dir=/usr/local/opt/openssl@1.0"' >> .bash_profile source ~/.bash_profile echo $RUBY_CONFIGURE_OPTS openssl@1.0がインストールされていない場合、 インストールが必要です。 bundle install〜rails s ターミナル gem install bundler 新しいbundlerが指定されるので、 古いrubyではコケる。 ターミナル gem install bundler -v 1.17.3 でbundlerをインストール完了。 ターミナル bundle install --path vendor/bundle bin/rails s でサーバー起動完了。 感想 ・nokogiriやらopenssl,therubyracer,tzinfo-data....のエラーは頻繁に出会うけどすぐ解消できるイメージで雑にやってしまいました。 ・そのため、MacでRails使いたいのにubuntuの人とか、PHPの人とかのコマンドを適当にコピペしてしまった。無駄に時間を使った気がします。 ・恐れすぎもよくないけど、自分の中では論理的だと思える仮説を作ってから実施した方が良いですね。 参考サイト https://www.kasacchiful.net/blog/2020/03/27/rbenv-openssl-error/ https://stackoverflow.com/questions/64816796/ruby-install-aborted-due-to-missing-extensions-openssl-readline-zlib-compilat
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ログイン実装について、Devise/Warden/Rack の関係を一言で

Rails のログイン実装でお馴染みの Devise について調べたため、メモを残します。引用元を合わせて記します。 Devise とは? ・Rails に認証を実装するためのソリューションのこと ・Warden がベースになっている Devise is a flexible authentication solution for Rails based on Warden. −− Github Warden とは? ・Rack 内で、認証するメカニズムを与えてくれるもの Warden provides a mechanism for authentication in Rack based Ruby applications. −− Github つまり、 1. Rack 内で、 2. Warden というものが認証するメカニズムを提供し、 3. Deviseがそのメカニズムを Rails に実装してくれる。 ということですね! Rack とは? ・Ruby で、WEB開発をするためのインターフェースを与えてくれるもの ・HTTPリクエストとHTTPレスポンスを束ねて、APIとし、シングルメソッドコールにしてくれるもの(要は、Rack のおかげで API が打てるということですね) Rack provides a minimal, modular, and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call. −− Rails guides ちなみに、この文章での distills the API(一般的に distills は「蒸留する」)の意味が最初は謎でした。(APIを蒸留するとは…?)そこで、オックスフォード英英辞典を参考にしました。↓↓ distil sth (from/into sth) to get the essential meaning or ideas from thoughts, information or experiences -- Oxford Academic English なるほど、distills には「インフォメーションからエッセンシャルなものを取り出す」というような意味合いがあるみたいですね。参考にしてください。 まとめ Rack 内で、Warden による認証メカニズムを、 Devise が Rails に実装してくれる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バリデーションとは?

昨日の学習の復習です! バリデーション、、 クライアントが入力した情報(データ)をデータベースに保存するときに、制約をかけること!(バリアを張ることって自分は覚えています) 例えば 名前とメールが空(入力されてない)だと登録できませんよー 何文字以内にしてね! 数字しか許さん   などなど そこで使われるのが、、、 validatesメソッド!!! バリデーションを設定する時に使用するメソッド こんな風にバリデーション(バリア)します validates :カラム名, バリデーションの種類 1つ例として validates :name, presence: true バリデーションの種類でpresence: trueと書くことでnameカラムが空だと登録できませんよーというバリデーションを制約(バリア)をかけている 以上!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【エラー】rails new APP_PATH [options]

概要 Usage: rails new APP_PATH [options] Options: [--skip-namespace], [--no-skip-namespace] # Skip namespace (affects only isolated engines) [--skip-collision-check], [--no-skip-collision-check] # Skip collision check -r, [--ruby=PATH] # Path to the Ruby binary of your choice # Default: /Users/fujitanisougaku/.rbenv/versions/2.6.6/bin/ruby -m, [--template=TEMPLATE] # Path to some application template (can be a filesystem path or URL) -d, [--database=DATABASE] # Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc) # Default: sqlite3 [--skip-gemfile], [--no-skip-gemfile] # Don't create a Gemfile -G, [--skip-git], [--no-skip-git] # Skip .gitignore file [--skip-keeps], [--no-skip-keeps] # Skip source control .keep files -M, [--skip-action-mailer], [--no-skip-action-mailer] # Skip Action Mailer files [--skip-action-mailbox], [--no-skip-action-mailbox] # Skip Action Mailbox gem [--skip-action-text], [--no-skip-action-text] # Skip Action Text gem -O, [--skip-active-record], [--no-skip-active-record] # Skip Active Record files [--skip-active-job], [--no-skip-active-job] # Skip Active Job [--skip-active-storage], [--no-skip-active-storage] # Skip Active Storage files -P, [--skip-puma], [--no-skip-puma] # Skip Puma related files -C, [--skip-action-cable], [--no-skip-action-cable] # Skip Action Cable files -S, [--skip-sprockets], [--no-skip-sprockets] # Skip Sprockets files [--skip-spring], [--no-skip-spring] # Don't install Spring application preloader [--skip-listen], [--no-skip-listen] # Don't generate configuration that depends on the listen gem -J, [--skip-javascript], [--no-skip-javascript] # Skip JavaScript files [--skip-turbolinks], [--no-skip-turbolinks] # Skip turbolinks gem [--skip-jbuilder], [--no-skip-jbuilder] # Skip jbuilder gem -T, [--skip-test], [--no-skip-test] # Skip test files [--skip-system-test], [--no-skip-system-test] # Skip system test files [--skip-bootsnap], [--no-skip-bootsnap] # Skip bootsnap gem [--dev], [--no-dev] # Set up the application with Gemfile pointing to your Rails checkout [--edge], [--no-edge] # Set up the application with Gemfile pointing to Rails repository [--master], [--no-master] # Set up the application with Gemfile pointing to Rails repository main branch [--rc=RC] # Path to file containing extra configuration options for rails command [--no-rc], [--no-no-rc] # Skip loading of extra configuration options from .railsrc file [--api], [--no-api] # Preconfigure smaller stack for API only apps [--minimal], [--no-minimal] # Preconfigure a minimal rails app -B, [--skip-bundle], [--no-skip-bundle] # Don't run bundle install --webpacker, [--webpack=WEBPACK] # Preconfigure Webpack with a particular framework (options: react, vue, angular, elm, stimulus) [--skip-webpack-install], [--no-skip-webpack-install] # Don't run Webpack install Runtime options: -f, [--force] # Overwrite files that already exist -p, [--pretend], [--no-pretend] # Run but do not make any changes -q, [--quiet], [--no-quiet] # Suppress status output -s, [--skip], [--no-skip] # Skip files that already exist Rails options: -h, [--help], [--no-help] # Show this help message and quit -v, [--version], [--no-version] # Show Rails version number and quit Description: The 'rails new' command creates a new Rails application with a default directory structure and configuration at the path you specify. You can specify extra command-line arguments to be used every time 'rails new' runs in the .railsrc configuration file in your home directory, or in $XDG_CONFIG_HOME/rails/railsrc if XDG_CONFIG_HOME is set. Note that the arguments specified in the .railsrc file don't affect the defaults values shown above in this help message. Example: rails new ~/Code/Ruby/weblog This generates a skeletal Rails installation in ~/Code/Ruby/weblog. プロジェクトを認識していない時に発生する。 環境構築やディレクトリに不備が考えられる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】あるあるネタを言いたかったのであるあるネタを共有・投票できるアプリを作りました。

はじめに 「これあるあるだわ〜しかも結構面白いからみんなに共有したいなー」 って思ったことありませんか? ありますよね? あると思います。 ということで今回、あるあるネタを共有・投票できるアプリをリリースしました! サービス概要 日常で起きたあるあるネタを投稿し、一人何回でも投票できるアプリケーションです。 一人で何回でも投票できるため、自分の投稿を意図的に上の方に持ってくることも可能です。 複数回投票できることで承認欲求も満たせるかと思います。 Twitter共有もできるので、ぜひオススメのあるあるネタをたくさん共有してみてください! 使用技術 バックエンド Ruby 2.7.2 Rails 6.0.4.1 RSpec 5.0.2 RuboCop 1.20.0 JWT 2.2.3 フロントエンド Vue 2.6.14 VueRouter 3.5.2 Vuex 3.6.2 Vuetify 2.5.8 VeeValidate 3.4.12 axios 0.21.1 ESLint 0.4.3 インフラ Nginx Puma AWS VPC EC2 Route53 RDS ALB ACM Capistrano インフラ構成図 最後に Vue.jsを使ってアプリを作ったのは初めてだったので、いろいろとわからないこともありましたが楽しく作れました。 やはり初学者にとって日本語のドキュメントがあるのは助かりますね笑 最後まで読んでいただきありがとうございました! 前回作ったアプリについての記事も見てくれたら嬉しいです↓ 【個人開発】家の中にときどき出る不快な虫を検索するサービス「むしめがね」をリリースしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerコンテナのStateがExit7になった時の対処法

困ったこと ローカルホスト(今回はlocalhost:3000)に接続しようとdocker-compose up -dしてもうまくいかない。 昨日gem追加したからそのせいかな、と思いつつも色々試行錯誤してなんとか解決しました。 今の状態を確認 docker-compose up -dをしたときは良さげな感じ。でもブラウザ見ると接続できてない。 $ docker-compose up -d Starting qiita_db_1 ... done Starting qiita_web_1 ... done docker-compose psでコンテナ一覧を表示してみるとStateがExit 7となっている。 $ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------- qiita_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp qiita_web_1 bundle exec rails s -p 300 ... Exit 7 docker logs qiita_web_1で原因を探ってみる $ docker logs qiita_web_1 Could not find bcrypt-3.1.16 in any of the sources Run `bundle install` to install missing gems. ログ通りにbundle installしようとしたが、なんかうまくいかなかった。 解決方法 docker-compose buildしてからdocker-compose up -dする。 $ docker-compose build db uses an image, skipping Building web [+] Building 36.0s (13/13) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/ruby:2.7.0 0.0s => [internal] load build context 0.7s => => transferring context: 10.46MB 0.7s => [1/8] FROM docker.io/library/ruby:2.7.0 0.0s => CACHED [2/8] RUN apt-get update -qq && apt-get install -y build-essential nodejs 0.0s => CACHED [3/8] RUN mkdir /app 0.0s => CACHED [4/8] WORKDIR /app 0.0s => [5/8] COPY Gemfile /app/Gemfile 0.1s => [6/8] COPY Gemfile.lock /app/Gemfile.lock 0.0s => [7/8] RUN bundle install 33.7s => [8/8] COPY . /app 0.3s => exporting to image 1.2s => => exporting layers 1.2s => => writing image sha256:963e2a37244ddae131525b434210cff12357decab259e971c5ec4edb27409498 0.0s => => naming to docker.io/library/qiita_web 0.0s Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them $ docker-compose up -d qiita_db_1 is up-to-date Recreating qiita_web_1 ... done docker-compose psで再度コンテナを表示すると、StateがUpになった。 $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------------------ qiita_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp qiita_web_1 bundle exec rails s -p 300 ... Up 0.0.0.0:3000->3000/tcp,:::3000->3000/tcp ブラウザで試してみると接続できるようになってるはず
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのインストールでつまづいたこと(原因はONEDRIVE)

もう腹が立って仕方がない 結局は個人のデスクトップフォルダが、知らない間にOnedriveにリンクされていて、 権限の関係でインストールが出来なかったからである。 それにしても、腹が立つ・・・ 以下の環境はどちらも駄目だった。 WSL/Ubuntu MYSYS2/MinGW デスクトップフォルダ ずいぶん前は C:\Users\motoc\デスクトップ\rails 最近、こうなっている C:\Users\motoc\OneDrive\デスクトップ\rails 知らない間に、こんなクソ仕様にしやがって・・・ しばいたろか! フォルダ C:\Users\motoc\OneDrive\デスクトップ\rails motoc@DESKTOP-5GGB0DP MINGW64 ~ $ cmd.exe /c "chcp 65001" Active code page: 65001 motoc@DESKTOP-5GGB0DP MINGW64 ~ $ cmd.exe /c "mklink /j rails C:\Users\motoc\OneDrive\デスクトップ\rails" Junction created for rails <<===>> C:\Users\motoc\OneDrive\デスクトップ\rails エラー内容 $ bin/rails s C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/compile_cache/iseq.rb:30:in `fetch': No such file or directory - bs_fetch:open_current_file:open (Errno::ENOENT) from C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/bootsnap-1.8.1/lib/bootsnap/compile_cache/iseq.rb:30:in `fetch' ... 省略 解決方法 下記のフォルダに変更 C:\Users\motoc\rails motoc@DESKTOP-5GGB0DP MINGW64 ~ $ cmd.exe /c "mklink /j rails C:\Users\motoc\rails" Junction created for rails <<===>> C:\Users\motoc\rails 無事に動いた motoc@DESKTOP-5GGB0DP MINGW64 ~/rails/railsample $ bin/rails s => Booting Puma => Rails 5.2.6 application starting in development => Run `rails server -h` for more startup options *** SIGUSR2 not implemented, signal based restart unavailable! *** SIGUSR1 not implemented, signal based restart unavailable! *** SIGHUP not implemented, signal based logs reopening unavailable! Puma starting in single mode... * Version 3.12.6 (ruby 2.6.8-p205), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:3000 Use Ctrl-C to stop Started GET "/" for ::1 at 2021-09-09 10:41:43 +0900 Processing by Rails::WelcomeController#index as HTML Rendering C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/railties-5.2.6/lib/rails/templates/rails/welcome/index.html.erb Rendered C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/railties-5.2.6/lib/rails/templates/rails/welcome/index.html.erb (7.3ms) Completed 200 OK in 32ms (Views: 25.0ms | ActiveRecord: 0.0ms)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pumaをdevelopment環境でもバックグラウンドで実行する

puma 5.0.0からデーモン化機能が削除されてしまったので、production環境ではsystemdで動かす事になった。 development環境ではrails sなどで実行すればいいのだが、ターミナルを専有されるのが嫌なのでdevelopment環境でもrails sではなくバックグラウンドで実行するための自分用のメモ。 バックグラウンドで実行したいだけなら単純に&をつけて実行すればいいだけなので簡単。 結論 さっそくだが、こんなシェルスクリプトをプロジェクト直下のbin/ディレクトリに配置して実行権限を付与する(chmod +x)。 そしてこう使う。 bin/pumactl.sh start bin/pumactl.sh stop bin/pumactl.sh #!/bin/bash current_dir=`(cd $(dirname $0); pwd)` rails_root=`(cd "$current_dir/../"; pwd)` log_file="$rails_root/log/puma.log" pid_dir="${rails_root}/tmp/pids" pid_file="${pid_dir}/puma.pid" start() { echo "starting puma" mkdir -p $pid_dir cd $rails_root bundle exec pumactl start >> $log_file 2>&1 & } stop() { echo "stopping puma" cd $rails_root if [ -f $pid_file ]; then bundle exec pumactl stop else echo "pid file not found: $pid_file" fi } restart() { echo "restarting puma" stop start } # See how we were called. case "$1" in start) start ;; stop) stop ;; restart) restart ;; *) echo $"Usage: $prog {start|stop|restart}" esac
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

間違えてrails db:migrationをしてしまった時の戻し方

マイグレーションしてしまったバージョンを調べる hogehoge@MacBook-Pro room-api % rails db:version Current version: 20210909002314 今回のバージョンは20210909002314なので、これをdownさせればいい 元に戻す hogehoge@MacBook-Pro room-api % rails db:migrate:down VERSION=20210909002314 == 20210909002314 AddCancelToSamples: reverting ========================== -- remove_column(:samples, :cancel, :boolean) -> 0.0248s == 20210909002314 AddCancelToSamples: reverted (0.0281s) =================
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby on Rails】link_toメソッドをリンクではなくボタンとして使う方法

対象者 link_toメソッドの使い方を知りたい方 目的 link_toメソッド使ってviewでボタンを表示する 実際の手順と実例 1.link_toメソッドの表示の違い a. 基本的な構文 <%=link_to "表示する文字", "URL" %> 上記の書き方だとテキストリンクになってしまう b. doとendで囲う方法 <%= link_to URL do %>    表示する文字 <% end %> これでボタン化できます。 2. 実際の使用例 下記サイトを参考に表示テキストがホバーすると変わるボタンを実装 上記記事のHTMLコードは下記の通り index.html <a href="#" class="btntextchange"> <span>About</span><span>私たちについて</span> </a> ※CSSは省略します。class="btntextchange"に表示を切り替えるcssのコードが書かれています。 これをRailsのコードに置き換えて実験してみた index.html.erb <a href="#" class="btntextchange"> <span><%=link_to "About", "about_path" %></span> <span><%=link_to "私達について", "about_path" %></span> </a> 上記の書き方だとボタン化できません。 原因としては、link_toメソッドを使用することでaタグのHTMLが作成され、二重に精製されてしまっていることが考えられます。 3.例の解決方法 下記のようにボタン化して解決できます。 index.html.erb <%= link_to about_path do %> <div class="btntextchange">              <span>About</span>              <span>私たちについて</span>          </div> <% end %> 上記のようにHTMLコードを挟み込むことで、 ボタンとして活用できます! 参照 【Rails入門】link_toの使い方とオプションをわかりやすく解説! 【Rails基礎】link_toメソッドをボタン化して使う方法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】配列を制するものはRailsを制す。

Rubyの配列の基本についてまとめます。 実際にRailsでアプリを実装するときなどには、配列は頻繁に扱うことになります。 配列をうまく扱うことができれば実装力が増すことは間違いありません! 配列の作り方 普通に[]でくくればできますが、それ以外の方法を簡単に見ておきましょう。 Array.new array = Array.new(3, 'ok') p array #["ok", "ok", "ok"] splitメソッド string = 'taro jiro saburo' array = string.split(' ') p array #["taro", "jiro", "saburo"] 要素を取り出す・置き換える・挿入する 要素を取り出す array = ["taro", "jiro", "saburo"] new_array = array[1..2] #["jiro", "saburo"] 要素を置き換える array = ["taro", "jiro", "saburo"] array[0] = "me" #["me", "jiro", "saburo"] array[0..1] = ["me", "my brother"] #["me", "my brother", "saburo"] 要素を挿入する 要素を挿入する場合はarray[n, 0] = 挿入したい要素と書きます。 nの直前に要素を挿入します。 array = ["taro", "jiro", "saburo"] array[1, 0] = "hanako" #["me", "hanako", "jiro", "saburo"] 複数のインデックスを指定して配列をつくる array = ["taro", "jiro", "saburo"] new_array = array.values_at(0, 2) #["taro", "saburo"] 集合としての配列 数学で集合を学習したことを覚えているでしょうか。 配列を1つのまとまりとして捉えることもできるわけですね。 共通集合 2つの集合のどちらにも含まれている要素を取り出す。 array1 = [1,3,5,7,9] array2 = [1,2,5,6,9,10] new_array = array1 & array2 #[1, 5, 9] 和集合 どちらか一方にでもある要素を集める。 array1 = [1,3,5,7,9] array2 = [1,2,5,6,9,10] new_array = array1 | array2 #[1, 3, 5, 7, 9, 2, 6, 10] 集合の差 array1 = [1,3,5,7,9] array2 = [1,2,5,6,9,10] new_array = array1 - array2 #[3, 7] 集合の和 和集合とのちがいは、重複する要素があっても全部足し合わせます。 (重複しているものを除きたい場合は|を使います。) array1 = [1,3,5,7,9] array2 = [1,2,5,6,9,10] new_array = array1 + array2 #[1, 3, 5, 7, 9, 1, 2, 5, 6, 9, 10] まとめ Rubyの配列の基本について見てきました。 ここで見てきたものをしっかりマスターしておくだけでも配列の扱いが容易になると思います。 Railsなどの開発にも生きてくると思います。 今回は以上です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ajax]コメント機能を非同期通信にした

はじめに 本記事では、コメント機能を非同期通信にした方法を記述しています。 過去に、いいね機能とフォロー機能を非同期通信にしている記事も書いておりますので、 参照ください。 前提 コメント機能は同期通信にはできる状態です。 該当箇所のみの記載です。 コード コントローラー food(投稿した食べ物)の詳細画面(show)にてコメントできる仕様です。 show内で完結するので、newメソッドもあります。 createメソッドでもコメントできましたが、 コメントはcommentテーブルに保存するので、newの方が良いと思っています。 foods_controller.rb 省略 def show @food = Food.find(params[:id]) @like = Like.new @comment = Comment.new @comments = @food.comments.order("created_at DESC") end 省略 ここも特に同期通信と変わりません。 merge(user_id: current_user.id ,food_id: params[:food_id]) ここ忘れないように。 comments_controller.rb class CommentsController < ApplicationController def create @food = Food.find(params[:food_id]) @comment = Comment.create(comment_params) @comments = @food.comments.order("created_at DESC") end private def comment_params params.require(:comment).permit(:text).merge(user_id: current_user.id ,food_id: params[:food_id]) end end モデル 同期通信と変わりません。 belongs_toとかhas_manyなどですので、省略します。 ビュー 記事を書いててミスを見つけました。 localsがlacalsになっていました、、、 あっぶね〜? ・・・ <div class="food-comments-contents-<%= @food.id %>"> クラス名に-<%= @food.id %>を入れないと何の投稿に対するコメントなのか わかりませんので、ここが抜けているとリロードしないとコメントが反映されないと思います。 コードが長いので、部分テンプレートにしています。 foods/show.html.erb 省略 <h2 class="show-food-comments-title">Comments !</h2> <div class="food-comments-contents-<%= @food.id %>"> <%= render partial:"comments/comment", locals: {food: @food} %> </div> 省略 comments/_comment.html.erb 3行目にremote: trueを入れます。 これにより非同期通信が可能になり、リクエストがjs形式になります。 <div class="comments-comment-contents"> <% if user_signed_in? %> <%= form_with(model: [@food, @comments],url: food_comments_path(@food,@comment), remote: true) do |f| %> <%= f.text_area :text, placeholder: "コメントする", rows: "2", class: "food-comment-textbox" %> <%= f.submit "SEND" ,class: "food-comment-textbox-send" %> <% end %> <ul class="show-food-comments"> <% @comments.each do |comment| %> <li class="show-food-comment"> <div class="comment-food-post-users"> <div> <%= link_to user_path(comment.user_id) do %> <% if comment.user.icon.present? %> <%= image_tag comment.user.icon, class: "icon-img" %> <% else %> <i class="fas fa-user-circle"></i> <% end %> <% end %> </div> <div> <%= link_to user_path(comment.user_id),class: "comment-food-post-user" do %> <%= comment.user.nickname %> <% end %> <%= comment.text %> </div> </div> </li> <% end %> </ul> <% else %> <p class="please-login">コメント閲覧・投稿する際は<br/>「新規登録」「ログイン」をお願いします</p> <% end %> </div> comments/create.js.erb リクエストがjs形式のため、 ファイル名はcreate.js.erbになります。 先ほどfoods/show.html.erbにて作った クラスfood-comments-contents-<%= @food.id %>が また部分テンプレートのcomments/commentに行きます。 これでcreate=コメントしたということが反映されます。 $(".food-comments-contents-<%= @food.id %>").html("<%= j(render partial: 'comments/comment', locals: {food: @food}) %>") 以上です。 いいねやフォローに比べればまだ簡単なように思えます。 終わりに 私が作成したアプリはコメント機能を非同期化しています。 昨今のアプリでコメント機能が非同期化していないアプリはありませんね。 必ず導入するべきだと思います。 また、いいね機能やフォロー機能を非同期化していれば、 少し考えればコメント機能の非同期化は容易ですので、 いいね機能とフォロー機能の非同期化もマスターしたいところです。 以下参考サイトです。 [Rails]Ajaxを用いて非同期でコメント機能の実装 Railsアプリに非同期通信のコメント機能を実装 明日も頑張ります!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

router-linkで表示した投稿にいいねを押して、別のページに遷移したら反映されてない

投稿のユーザーの名前をクリックすると、そのユーザーの全投稿が表示される機能を VueRouterを用いて作ったのだが、タイムラインに戻ったときにいいねがリアルタイムで反映されていない。。。 http://coffee-passport.net/ こちらで実際の挙動を確認していただければと思います。 (投稿一覧) app.vue <template> <div class='main' > <div class="playing-button"> <button class="playing-button-on" @click="pauseVideo" v-if="playing">BGMをOFF</button> <button class="playing-button-off" @click="playVideo" v-else>BGMをON</button> </div> <div class='item-contents'> <router-view :user_id="user_id"></router-view> <h2 class='title' id="title">タイムライン</h2> <youtube :video-id="videoId" ref="youtube" @playerVars="playerVars" hidden/> <ul class='item-lists' id="timeline"> <li v-for="drink in drinks" :key="drink.id" class="list" > <router-link to="/user" @click.native="getUserId(drink.user_id)"> <div class="user-info-timeline" > <div v-if="drink.user_img"> <img class="user-img-timeline" v-bind:src="drink.user_img" > </div> <div v-else> <img class="user-img-timeline" src ="https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8YnJlYXV0aWZ1bCUyMGdpcmx8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=60"> </div> <div class="username-timeline"> {{drink.nickname}} </div> </div> </router-link> <div class='item-img-content'> <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> <div class='item-tag'> </div> <div class="like-and-comment"> <likeButton :drinkId=drink.id></likeButton> <i class="far fa-comment fa-lg"></i> <button v-on:click="show(drink)">詳細を表示</button> </div> </div> <modal v-bind:name="'display-drink-'+drink.id" height="1000px" styles="background-color: bisque"> <drinkShow :drink=drink></drinkShow> </modal> <div v-if="drink.body_id === 2" class="body-light"></div> <div v-else-if="drink.body_id === 3" class="body-medium"></div> <div v-else-if="drink.body_id === 4" class="body-full"></div> <div v-else class="body-nothing"></div> <div v-if="drink.acidity_id === 2" class="acidity-low"></div> <div v-else-if="drink.acidity_id === 3" class="acidity-medium"></div> <div v-else-if="drink.acidity_id === 4" class="acidity-high"></div> <div v-else class="acidity-nothing"></div> </li> </ul> </div> </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 { name: 'player', props: { src: "https://www.youtube.com/watch?v=02azSAMtZWU" }, components: { likeButton, drinkShow, }, data: function(){ return { drinks: "drinks", user_id: 0, videoId: "QN1uygzp56s", playing: false, playerVars: { autoplay: 1 } } }, created(){ }, mounted: function(){ // this.playVideo(); 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'); }, playVideo(){ // 再生処理 this.$refs.youtube.player.playVideo() this.playing = true }, pauseVideo(){ // 停止処理 this.$refs.youtube.player.pauseVideo() this.playing = false }, getUserId(user_id){ this.user_id = user_id document.getElementById("timeline").style.visibility ="hidden"; document.getElementById("title").style.visibility ="hidden"; scrollTo(0, 0); } } } </script> (ユーザーの投稿) user.vue <template> <div class='main'> <div class='item-contents'> <h2 class='title'></h2> <router-link to="/" @click.native="showTimeline()" class="back-to-timeline">タイムラインに戻る</router-link> <ul class='item-lists'> <li v-for="drink in filtredDrinks" :key="drink.id" class="list" > <div class="user-info-timeline" > <div v-if="drink.user_img"> <img class="user-img-timeline" v-bind:src="drink.user_img" > </div> <div v-else> <img class="user-img-timeline" src ="https://images.unsplash.com/photo-1469334031218-e382a71b716b?ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8YnJlYXV0aWZ1bCUyMGdpcmx8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=60"> </div> <div class="username-timeline"> {{drink.nickname}} </div> </div> <div class='item-img-content'> <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> <div class='item-tag'> </div> <div class="like-and-comment"> <likeButton :drinkId=drink.id></likeButton> <i class="far fa-comment fa-lg"></i> <button v-on:click="show(drink)">詳細を表示</button> </div> </div> <modal v-bind:name="'display-drink-'+drink.id" height="1000px" styles="background-color: bisque"> <drinkShow :drink=drink></drinkShow> </modal> <div v-if="drink.body_id === 2" class="body-light"></div> <div v-else-if="drink.body_id === 3" class="body-medium"></div> <div v-else-if="drink.body_id === 4" class="body-full"></div> <div v-else class="body-nothing"></div> <div v-if="drink.acidity_id === 2" class="acidity-low"></div> <div v-else-if="drink.acidity_id === 3" class="acidity-medium"></div> <div v-else-if="drink.acidity_id === 4" class="acidity-high"></div> <div v-else class="acidity-nothing"></div> </li> </ul> </div> </div> </template> <script> import Vue from 'vue/dist/vue.esm.js' import VModal from 'vue-js-modal' import axios from 'axios'; import likeButton from '../like/likeButton.vue'; import drinkShow from '../drinks/show.vue'; Vue.use(VModal) export default { props: { user_id: { type: Number } }, components: { likeButton, drinkShow }, data: function(){ return { filtredDrinks: [], drinks: "drinks", userName: "" } }, created(){ this.setDrink(); }, methods: { setDrink: function(){ axios.get('/api/drinks') .then(response => { const drinks = response.data this.filtredDrinks = drinks.filter(drink => drink.user_id === this.user_id) }) }, show: function(drink) { this.$modal.show(`display-drink-${drink.id}`); }, hide: function () { this.$modal.hide(`display-drink-${drink.id}`); }, showTimeline: function(){ document.getElementById("timeline").style.visibility ="visible"; document.getElementById("title").style.visibility ="visible"; } }, } </script> <style scoped> .back-to-timeline{ color: white; background-color:#3c1900 ; padding: 5px; border-bottom: solid 4px black; border-radius: 3px; transition: background-color 5s ease-out; } .back-to-timeline:active{ -webkit-transform: translateY(4px); transform: translateY(4px);/*下に動く*/ box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.2);/*影を小さく*/ border-bottom: none; } .back-to-timeline:hover{ background: linear-gradient(270deg, black 0%, maroon 50%, #3c1900 100%); } .back-to-timeline a:visited{ color: white; background-color:#3c1900 ; padding: 5px; } </style> 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> app.vueのコンポーネントのいいねボタンと、user.vueの子コンポーネントのいいねを同期させたい。 VueRouterでuser.vueを表示させてるから分からんけど、 app.vueが親、app.vueの中にあるrouter-linkで表示させたuser.vueが子、user.vue内でインポートしてるlikeButton.vueが孫って一旦仮定していいのかな。。。 そうね、app.vueが親、app.vueの中にあるrouter-linkで表示させたuser.vueが子っていう概念でいいっぽい。 もう一回整理すると、 app.vueの子コンポーネントであるlikeButton.vueのイベントと、 app.vueの子コンポーネントであるuser.vueの子コンポーネントであるlikeButton.vueの イベントを同期させたい。。。 なんて検索したらいいんだろう。。。 $emitはそのまま親コンポーネントのイベントを発火させるが、 親コンポーネントのなかの子コンポーネントのイベントを発火させたい。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めてのGraphQL in Rails

この記事の内容 Railsで開発の仕事をしています。GraphQL is 何?ぐらいのGraphQL初心者だったのですが、とにかくにも業務で急ぎ実装しなければならなかったので、メモをとりながら作成することにしました。 【注】現在、まだ調べきれていない記載があります。この記事の内容は調べながら徐々にアップデートしていく予定です。 GRaphQLとは?グラフって何?? グラフ理論という理論があるそうです。この理論では、データの構造を点と点を線で繋ぐような形で理解します。この構造をGraphと呼びます。GraphQLの最後のQLはこのGraphにQueryLanguage(問い合わせ言語)を足して、GraphQLと呼んでいるのだと理解しています。 ▼Graph理論そのものについてはこちら なぜGraphQLを使うの?? これまでデータベースとクライアント側のやりとりとの方法として、RESTful APIと呼ばれる方法が一般的でした。このRESTful APIの欠点が、データの過剰取得にあったそうです。GraphQLはこの弱点を克服し、その時必要なデータを必要なだけ持ってくることができます。 ▼RESTful APIとは何か、その欠点とGraphQLの特徴について ▼データの過剰取得とはどのようなことか GraphQLのある場所 GraphQLを理解する上で、GraphQLがアプリの構造上どこにあるのかを理解するのは非常に大切でした。 結論から言うとデータベースとインターフェイスとの間で、サーバーとクライアントのリクエストの間に入って、色々調整してくれるようです。公式サイトのトップにあった、この図がわかりやすいのではないかと思います。 画像引用元:GraphQL公式サイト GraphQLでAPIを作るのに必要なもの GraphQLでAPIを実装をするために必要なものは、以下の3つであると理解しています。 名前 役割 クエリ言語 GraphQLサーバーへの問い合わせ内容を記載した言語 スキーマ言語 GraphQLサーバーでのデータの型を記載した言語 リゾルバ 問い合わせを実行するメソッド この辺は既存記事に非常に優れた記事があります。特に下記の記事が理解に役立ちましたので貼付をしておきます。 ここでは、GraphQLが上記の「問い合わせ」「データの型」「問い合わせの実行」の3つのパートで成り立っていることの理解に留めておきます。 で、Railsでどうやるの?? 上記の内容をRailsで表現する場合、graphql-rubyというGemが便利なのでそれを利用します。以下、graphql-rubyの実装記事です。 本来のGraphQLの記載方法を知りたい方は↑上記の記事や下記の公式チュートリアルなど取り組んでみると良いと思います。 本記事の参考記事 本記事で実装している内容については主にこちらの記事とgraphql-rubyの公式サイトを参考にしました。 今回表現した内容 今回は以下のようなusersテーブルから「ユーザー一覧」「ユーザー詳細」をGETしてくるまでをGraphQLで表現したいと思います。 users id integer name string email string はじめに まずはgraphql-rubyを使えるようにします。 # Gemfile gem "graphql" # ターミナル $ bundle install $ rails g graphql:install 上記のコマンドで大量のファイルが生成されます。自分はメモをとり忘れてしまったのですが、こちらの記事では詳細に記載されていました。(職場の元先輩の記事だ。。。。) 生成されたファイルの中で、この2ファイルはgraphql-rubyの構造を理解する上で特に重要だったと思います。 app/graphql/types/query_type.rb app/graphql/アプリ名_schema.rb 全体のステップ graphql-ruby公式サイトのチュートリアルによると、RailsでGraphQLによるサーバーへの問い合わせをするのに必要なステップは次の3つだそうです。 型を決める それらの型とスキーマをつなぐ クエリをスキーマで実行 一つ一つ見ていきます。 型を決める(型ファイルの作成) RailsではコマンドでGraphQLの型ファイルが生成できます。 $ rails g graphql:object User create app/graphql/types/user_type.rb 生成されたファイルの中身がこちら。 app/graphql/types/user_type.rb module Types class UserType < Types::BaseObject field :id, ID, null: false field :name, String, null: false field :email, String, null: false field :created_at, GraphQL::Types::ISO8601DateTime, null: false field :updated_at, GraphQL::Types::ISO8601DateTime, null: false end end 既にUserモデルがある場合には、それに合わせて型を生成してくれるようです。初めは $ rails g graphql:object User name:String email:String のように書いていたのですが、既にUserモデルがあったため、nameカラムとUserカラムが2つずつできてしまいました。 スキーマファイル 上記で定義した内容を実行するためのファイルが必要です。 作成したスキーマファイルは下記の通りです。 app/graphql/types/query_type.rb module Type class QueryType < GraphQL::Schema::Object # フィールド(:user)の説明と引数を記載します。 field :user, Types::UserType, null: true do description "Find User by ID" #省略可 argument :id, ID, required: true end # 実行のコマンドを書きます def user(id:) User.find(id) end def users(page: nil, items: nil) User.all end end end 初めは既存ソースをコピーしながらすることになると思いますが、ここでわからない単語やメソッドがたくさん出てきます。わからないことは地道に graphql-rubyのガイドページ などで調べます。 上記のコードを理解するにあたり、調べたのは下記の通りです。 field クラスメソッド。データを(DBから)持ってくるするもの。objectとinterfaceがfieldを持っている。 下記のような形で書き、 field :name, String, null: false do description "The name of this thing" end ブロックの1行目はfield :フィールド名, 型の場所, nullになる可能性の有無 doを表しています。 Arguments 検索やデータの呼び出しに使う引数 pageやitemsなど 現段階では不明(すみません)。ただ、graphql-rubyはpagenationに特別なページを割いているので、多分それ関係の設定なのではないかと理解。違ったら訂正します。&わかったら追記します。 ルートとなる型 graphql-ruby公式サイトのチュートリアルには「(スキーマファイルを作る)前にエントリーポイントを決めておいてね」と書いてあります。 どういうことかというと、全てのスキーマにはrootとなる型があるようです。それが、 $ rails g graphql:install をした時に生成された、こちらのファイル。 app/graphql/アプリ名_schema.rb このファイルにはこのように書かれており、 class AppNameSchema < GraphQL::Schema # required query Types::QueryType # optional mutation Types::MutationType subscription Types::SubscriptionType end データの「呼び出し(query)」「変更(nutation)」「サブスクリプション(subscription)」がそれぞれどこのスキーマで行われるか記載してあります。 スキーマの実行 こちらは、graphql-rubyのクエリの実行に関する記載にも若干の記載はありますが、graphql-rubyの外のライブラリを使用して行うのが一般的なようです。 graphql-rubyのサイトでも Relay Apollo Client GraphQL.js Client が例として挙げられています。 業務ではApollo Clientを利用しましたが、一旦、長文になりそうなので別記事に記載したいと思います。 ここまでの感想 調べ始めたらとても奥が深かったです。。。。 この記事も中途半端になってしまって恐縮ですが、理解したらとても便利そうなので、もう少し勉強を深めたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めてのGraphQL with Rails

この記事の内容 Railsで開発の仕事をしています。GraphQL is 何?ぐらいのGraphQL初心者だったのですが、とにかくにも業務で急ぎ実装しなければならなかったので、メモをとりながら作成することにしました。 【注】現在、まだ調べきれていない記載があります。この記事の内容は調べながら徐々にアップデートしていく予定です。 GRaphQLとは?グラフって何?? グラフ理論という理論があるそうです。この理論では、データの構造を点と点を線で繋ぐような形で理解します。この構造をGraphと呼びます。GraphQLの最後のQLはこのGraphにQueryLanguage(問い合わせ言語)を足して、GraphQLと呼んでいるのだと理解しています。 ▼Graph理論そのものについてはこちら なぜGraphQLを使うの?? これまでデータベースとクライアント側のやりとりとの方法として、RESTful APIと呼ばれる方法が一般的でした。このRESTful APIの欠点が、データの過剰取得にあったそうです。GraphQLはこの弱点を克服し、その時必要なデータを必要なだけ持ってくることができます。 ▼RESTful APIとは何か、その欠点とGraphQLの特徴について ▼データの過剰取得とはどのようなことか GraphQLのある場所 GraphQLを理解する上で、GraphQLがアプリの構造上どこにあるのかを理解するのは非常に大切でした。 結論から言うとデータベースとインターフェイスとの間で、サーバーとクライアントのリクエストの間に入って、色々調整してくれるようです。公式サイトのトップにあった、この図がわかりやすいのではないかと思います。 画像引用元:GraphQL公式サイト データベースとディバイスとの間にGraphQLが記載されていますね。 GraphQLでAPIを作るのに必要なもの GraphQLでAPIを実装をするために必要なものは、以下の3つであると理解しています。 名前 役割 クエリ言語 GraphQLサーバーへの問い合わせ内容を記載した言語 スキーマ言語 GraphQLサーバーでのデータの型を記載した言語 リゾルバ 問い合わせを実行するメソッド この辺は既存記事に非常に優れた記事があります。特に下記の記事が理解に役立ちましたので貼付をしておきます。 ここでは、GraphQLが上記の「問い合わせ」「データの型」「問い合わせの実行」の3つのパートで成り立っていることの理解に留めておきます。 で、Railsでどうやるの?? 上記の内容をRailsで表現する場合、graphql-rubyというGemが便利です。今回もそれを利用します。以下、graphql-rubyの実装記事です。 本来のGraphQLの記載方法を知りたい方は↑上記の記事や下記の公式チュートリアルなど取り組んでみると良いと思います。 本記事の参考記事 本記事で実装している内容については主にこちらの記事とgraphql-rubyの公式サイトを参考にしました。 今回表現した内容 今回は以下のようなusersテーブルから「ユーザー一覧」「ユーザー詳細」をGETしてくるまでをGraphQLで表現したいと思います。 users id integer name string email string はじめに まずはgraphql-rubyを使えるようにします。 # Gemfile gem "graphql" # ターミナル $ bundle install $ rails g graphql:install 上記のコマンドで大量のファイルが生成されます。自分はメモをとり忘れてしまったのですが、こちらの記事では詳細に記載されていました。(職場の元先輩の記事だ。。。。) 生成されたファイルの中で、この2ファイルはgraphql-rubyの構造を理解する上で特に重要だったと思います。 app/graphql/types/query_type.rb app/graphql/アプリ名_schema.rb 全体のステップ graphql-ruby公式サイトのチュートリアルによると、RailsでGraphQLによるサーバーへの問い合わせをするのに必要なステップは次の3つだそうです。 型を決める それらの型とスキーマをつなぐ クエリをスキーマで実行 一つ一つ見ていきます。 型を決める(型ファイルの作成) RailsではコマンドでGraphQLの型ファイルが生成できます。 $ rails g graphql:object User create app/graphql/types/user_type.rb 生成されたファイルの中身がこちら。 app/graphql/types/user_type.rb module Types class UserType < Types::BaseObject field :id, ID, null: false field :name, String, null: false field :email, String, null: false field :created_at, GraphQL::Types::ISO8601DateTime, null: false field :updated_at, GraphQL::Types::ISO8601DateTime, null: false end end 既にUserモデルがある場合には、それに合わせて型を生成してくれるようです。初めは $ rails g graphql:object User name:String email:String のように書いていたのですが、既にUserモデルがあったため、nameカラムとUserカラムが2つずつできてしまいました。 スキーマファイル 上記で定義した内容を実行するためのファイルが必要です。 作成したスキーマファイルは下記の通りです。 app/graphql/types/query_type.rb module Type class QueryType < GraphQL::Schema::Object # フィールド(:user)の説明と引数を記載します。 field :user, Types::UserType, null: true do description "Find User by ID" #省略可 argument :id, ID, required: true end # 実行のコマンドを書きます def user(id:) User.find(id) end def users(page: nil, items: nil) User.all end end end 初めは既存ソースをコピーしながらすることになると思いますが、ここでわからない単語やメソッドがたくさん出てきます。わからないことは地道に graphql-rubyのガイドページ などで調べます。 上記のコードを理解するにあたり、調べたのは下記の通りです。 field クラスメソッド。データを(DBから)持ってくるするもの。objectとinterfaceがfieldを持っています。(objectとinterfaceがそれぞれ何であるかは、名前の部分のリンク先を見てください。) fieldは下記のような形で書きます。(他の書き方もあります。詳細はfieldのドキュメントを参照のこと) field :name, String, null: true do description "The name of this thing" end ブロックの1行目にはfield :フィールド名, 型の場所, nullになる可能性の有無 doがそれぞれ書かれています。 Arguments 検索やデータの呼び出しに使う引数です。例えば field :user, Types::UserType, null: true do argument :id, ID, required: true end ↑このargument :idの部分を定義することで、 { user(id: 1) { name email } } のようなid: 1のユーザーを呼び出すクエリがクエリが書けるようになります。 その他、以下のような書き方も載っていましたので、 { customers(first: 5) { name outstandingBalance } } { posts(startYear: 2018) { id } } Railsにおけるorderやlimit、検索のキーなどもここで定義できるようです。 pageやitemsなど 現段階では不明(すみません)。ただ、graphql-rubyはpagenationに特別なページを割いているので、多分それ関係の設定なのではないかと理解。違ったら訂正します。&わかったら追記します。 ▼近そうな実装の記事も貼っておきます ルートとなる型 graphql-ruby公式サイトのチュートリアルには「(スキーマファイルを作る)前にエントリーポイントを決めておいてね」と書いてあります。 どういうことかというと、全てのスキーマにはrootとなる型があるようです。それが、 $ rails g graphql:install をした時に生成された、こちらのファイル。 app/graphql/アプリ名_schema.rb このファイルにはこのように書かれており、 class AppNameSchema < GraphQL::Schema # required query Types::QueryType # optional mutation Types::MutationType subscription Types::SubscriptionType end データの「呼び出し(query)」「変更(mutation)」「サブスクリプション(subscription)」がそれぞれどこのスキーマで行われるか記載してあります。型の位置(Types::QueryTypeなど)はルートとなる型ファイルからの相対位置で記載されていますので、そのために先に「エントリーポイント(ルートとなる位置)」を決めておいてねと言うことなのかと理解しています。 スキーマの実行 こちらは、graphql-rubyのクエリの実行に関する記載にも若干の記載はありますが、graphql-rubyの外のライブラリを使用して行うのが一般的なようです。 graphql-rubyのサイトでも Relay Apollo Client GraphQL.js Client が例として挙げられています。 業務ではApollo Clientを利用しましたが、一旦、長文になりそうなので別記事に記載したいと思います。 ここまでの感想 調べ始めたらとても奥が深かったです。。。。 この記事も中途半端になってしまって恐縮ですが、理解したらとても便利そうなので、もう少し勉強を深めたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む