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

[Ruby] 階層の深いHashのmerge(deep_merge)と、そのコード簡略化

困ったこと Hash{}のmergeメソッドが、同一階層内の別keyデータを保持してくれない。 irb(main):001:0> h = {a:{aa:11, ab:11}} irb(main):002:0> h.merge({a:{aa:99}}) ==>{:a=>{:aa=>99}} ==> {:a=>{:aa=>99, :ab=>11}} になってほしい TL;DR ↓ソースをコピペすればOK class Hash def deep_merge(*others) self.clone.deep_merge!(*others) end def deep_merge!(*others) _child_merge = -> (_, self_val, other_val){ if self_val.instance_of?(Hash) && other_val.instance_of?(Hash) self_val.merge other_val, &_child_merge else other_val end } self.merge! *others, &_child_merge end end ↓使用例 $ irb irb(main):001:1* class Hash irb(main):002:2* def deep_merge(*others) irb(main):003:2* self.clone.deep_merge!(*others) irb(main):004:1* end irb(main):005:1* irb(main):006:2* def deep_merge!(*others) irb(main):007:2* _child_merge = -> (_, self_val, other_val){ irb(main):008:3* if self_val.instance_of?(Hash) && other_val.instance_of?(Hash) irb(main):009:3* self_val.merge other_val, &_child_merge irb(main):010:3* else irb(main):011:3* other_val irb(main):012:2* end irb(main):013:1* } irb(main):014:1* self.merge! *others, &_child_merge irb(main):015:0* end irb(main):016:-> end ==>:deep_merge! irb(main):017:0> h = {a:{aa:11, ab:11}} irb(main):018:0> h.deep_merge({a:{aa:99}}) #deep_mergeは元データを変更しない ==>{:a=>{:aa=>99, :ab=>11}} irb(main):019:0> h ==>{:a=>{:aa=>11, :ab=>11}} irb(main):020:0> h.deep_merge!({a:{aa:99}}) #deep_merge!は元データも変更する ==>{:a=>{:aa=>99, :ab=>11}} irb(main):021:0> h ==>{:a=>{:aa=>99, :ab=>11}} 説明 どうやってソースを組み立てたかと、コード簡略化について説明していきます。 困ったことの解消 まずHashのmergeメソッドについて見てみます。 このメソッドは、同一階層内でなければ、複数の値を保持してくれます。 irb(main):001:0> h1 = { "a" => 100, "b" => 200 } irb(main):002:0> h2 = { "c" => 300, "d" => 400 } irb(main):003:0> h1.merge(h2) ==>{"a"=>100, "b"=>200, "c"=>300, "d"=>400} また以下のように、 keyが重複した場合のみ、merge元データとmerge先データでイテレータ処理ができます。 irb(main):031:0> h1 = { "a" => 100, "b" => 200 } irb(main):032:0> h2 = { "b" => 246, "c" => 300 } irb(main):033:0> h1.merge(h2) {|key, oldval, newval| newval - oldval} ==>{"a"=>100, "b"=>46, "c"=>300} そして、イテレータのなかでは1階層落ちたデータとなります。 irb(main):034:0> h1 = { "a" => 100, "b" => {"bb"=>210, "bc"=>220} } irb(main):035:0> h2 = { "b" => {"bd"=>230}, "c" => 300 } irb(main):036:1* h1.merge(h2) {|key, oldval, newval| irb(main):037:1* p oldval irb(main):038:1* p newval irb(main):039:0> } {"bb"=>210, "bc"=>220} {"bd"=>230} つまり、「keyが重複した場合」にイテレータ内で「再びmerge」すれば、 階層が深くなっても値を落とさずに処理できます。 irb(main):040:0> h1 = { "a" => 100, "b" => {"bb"=>210, "bc"=>220} } irb(main):041:0> h2 = { "b" => {"bd"=>230}, "c" => 300 } irb(main):042:0> h1.merge(h2) {|key, oldval, newval| oldval.merge(newval)} ==>{"a"=>100, "b"=>{"bb"=>210, "bc"=>220, "bd"=>230}, "c"=>300} # データが保持されてる! やりたかった処理(=同一階層内の別keyデータを保持)が 実現できるメドが立ちました。 再帰処理の追加 ただ1階層ではダメで、階層を深堀りしていく必要があります。 そこで、処理をメソッド化し、 メソッド内部で、再帰的にイテレータ処理を行うようにします。 また、再帰的に処理を行うなかで、片方がHashでない場合は、 merge先データ(=newval)を正として上書きするので、イテレータ処理は不要となります。 これらを反映するとこうなります。 irb(main):043:1* def child_merge(key, oldval, newval) irb(main):044:2* if oldval.instance_of?(Hash) && newval.instance_of?(Hash) irb(main):045:2* return oldval.merge(newval) {|key, oldval, newval| child_merge(key, oldval, newval)} irb(main):046:2* else irb(main):047:2* return newval irb(main):048:1* end irb(main):049:0> end ==>:child_merge irb(main):050:0> h1 = { "a" => 100, "b" => {"bb"=>{"bbb"=>210}, "bc"=>220} } irb(main):051:0> h2 = { "b" => {"bb"=>{"bbe"=>240}, "bd"=>230}, "c" => 300 } irb(main):052:0> h1.merge(h2) {|key, oldval, newval| child_merge(key, oldval, newval)} ==>{"a"=>100, "b"=>{"bb"=>{"bbb"=>210, "bbe"=>240}, "bc"=>220, "bd"=>230}, "c"=>300} リファクタ ここですこし、リファクタをします。 簡易な処理なのでreturnを省略 def child_merge(key, oldval, newval) if oldval.instance_of?(Hash) && newval.instance_of?(Hash) oldval.merge(newval) {|key, oldval, newval| child_merge(key, oldval, newval)} else other_val end end 使わないキーは_にして省略 def child_merge(_, oldval, newval) この変数3つ|key, oldval, newval|が長いので、lambda関数に変更 (意外とこの、"引数"+"ブロック処理"のlambda記法が見つからなくて、めちゃくちゃ探しました) child_merge = -> (_, oldval, newval){ if oldval.instance_of?(Hash) && newval.instance_of?(Hash) oldval.merge newval, &child_merge else newval end } けっこうシンプルになりました。 Hashクラスへの適用 最後に、(Rubyの大きな特徴のひとつですが)classにメソッドを直接overrideできるので、 そちらを利用して、よりコードをシンプルにしていきます。 メソッドの変数名も、Hash#mergeに合わせて self_val, other_valにします。 class Hash def deep_merge(other_h) _child_merge = -> (_, self_val, other_val){ if self_val.instance_of?(Hash) && other_val.instance_of?(Hash) self_val.merge other_val, &_child_merge else other_val end } self.merge other_h, &_child_merge end end (private変数は_xxxにしてます。ruby的には意味ないです。ただの好みです) また、Hash#mergeは複数ハッシュも対応しているので、その対応も追加します。 class Hash def deep_merge(*others) (省略) self.merge *others, &_child_merge end end 元データも変更する、Hash#merge!にも対応します。 class Hash def deep_merge!(*others) (省略) self.merge! *others, &_child_merge end end そして最後に、通常のdeep_mergeも追加して、完成です。 class Hash def deep_merge(*others) self.clone.deep_merge!(*others) end def deep_merge!(*others) _child_merge = -> (_, self_val, other_val){ if self_val.instance_of?(Hash) && other_val.instance_of?(Hash) self_val.merge other_val, &_child_merge else other_val end } self.merge! *others, &_child_merge end end 使用方法 TL;DRの使用例にもあるように、 Hashクラスの変数であれば、メソッドを直接呼び出せるようになります。 irb(main):001:1* class Hash (省略) irb(main):016:-> end ==>:deep_merge! irb(main):017:0> h = {a:{aa:11, ab:11}} irb(main):018:0> h.deep_merge({a:{aa:99}}) ==>{:a=>{:aa=>99, :ab=>11}} irb(main):019:0> h ==>{:a=>{:aa=>11, :ab=>11}} irb(main):020:0> h.deep_merge!({a:{aa:99}}) ==>{:a=>{:aa=>99, :ab=>11}} irb(main):021:0> h ==>{:a=>{:aa=>99, :ab=>11}} irb(main):022:0> h.deep_merge({a:{ac:11}}, {b:1}) ==>{:a=>{:aa=>99, :ab=>11, :ac=>11}, :b=>1} irb(main):023:0> h.deep_merge({a:{ac:11}}, {b:1}, {b:9}) ==>{:a=>{:aa=>99, :ab=>11, :ac=>11}, :b=>9} 以上。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いいねボタンの応答速度を速く見せる方法

初めに いいねボタンの色変更ってそのままだと結構時間かかりますよね。 今回はjqueryを使いボタンを押した瞬間に色が変わるようにしようと思います。 ※アニメーションはやりません。 実装 _like.html.slim - if current_user.already_liked?(post) = link_to "/posts/#{post.id}/likes/#{post.likes.ids}", remote: true, method: :delete do <i class="fas fa-heart unlike font-13"></i> - else = link_to post_likes_path(post), remote: true, method: :post do <i class="far fa-heart like font-13"></i> span.ma-right-5 = post.likes.pluck(:id).length 普通はこうやってajaxのいいねボタンを実装すると思いますが、これだと ボタンを押す→Likeデータ作成→create.js.erb→ボタンの色変更 という処理になります。 でもこれだとLikeデータを作成してからajaxなので更新に明らかに間ができます。 なのでボタンを押した瞬間に色を変えて、裏でlikeデータを作成するようにします。 _like.html.slim - if current_user.already_liked?(post) = link_to "/posts/#{post.id}/likes/#{post.likes.ids}", remote: true, method: :delete do span.unlike <i class="fas fa-heart font-13"></i> - else = link_to post_likes_path(post), remote: true, method: :post do span.like <i class="far fa-heart font-13"></i> span.ma-right-5 = post.likes.pluck(:id).length javascript: $('.like').click(function() { $(this).delay(1).queue(function(){ $(this).html('<i class="fas fa-heart unlike font-13"></i>'); }); }) $('.unlike').click(function() { $(this).delay(1).queue(function(){ $(this).html('<i class="far fa-heart like font-13"></i>'); }); }) こんな感じです。 まとめ 現在開発中のSNS、Ammotのいいねボタンがかなり遅くて悩んでいたのですが、案外簡単に解決できてよかったです。 よかったらAmmotも見てみてください。 ↓トップページ: ↓僕のアカウント
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Sequel ProでインポートしたらMysql2::Error: Incorrect string value

Sequel Pro便利です。 視覚的にデータベースを確認できるのは大きい。 そしてCSVでもインポート可能で、直感的。 インポート方法はこちらから Sequel Proを使ったMySQLのテーブルデータ追加方法 注意点はidも入力しなきゃいけないというところですかね。 あとは日付の項目の型ですかね。 私の場合 yyyy-mm-dd hhss という形でした。  発生したエラーはMysql2::Error: Incorrect string value 今回の場合はarchivesというテーブルでarchivetitleでエラーが発生 確認したところ、文字化けしているようでしたが、原因は絵文字。 【Short video】?NENE's GREETING?【桃鈴ねね/ホロライブ】 という具合でガンガン絵文字が入っているのでエラーが発生。 rails db:drop で一度データベースを削除 database.ymlを修正 default: &default adapter: mysql2 charset: utf8mb4 //ここを追加 encoding: utf8mb4 //ここがutf8になっていた pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: socket: /tmp/mysql.sock rails db:create にて再度、データベース構築。 あとは一番最初の方法でインポート可能。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby on Rails] summernoteを活用してテキストをリッチにする

太字や文字カラーなどを選べるリッチなテキストエディタを実現させます。 1.gem,scss,javascript Gemfile gem "jquery-rails" gem "bootstrap" gem "simple_form" gem "summernote-rails" $ bundle scssには外部のcssファイルと読み込む必要があるので@importを用いて以下の記述を追加 assets/stylesheets/application.scss *= require_tree . *= require_self */ @import "bootstrap"; @import "summernote-bs4"; jsファイルにも記述を追加。 順番は間違えないように。 assets/javascripts/application.js //= require jquery3 //= require jquery //= require jquery_ujs //= require popper //= require bootstrap //= require summernote/summernote-bs4.min //= require summernote-init // //= require rails-ujs //= require activestorage //= require turbolinks //= require_tree 2.summernote-init.coffeを作成 assets/javascripts直下にsummernote-init.coffeを作成する。 assets/javascripts/summernote-init-coffee $(document).on 'turbolinks:load', -> $('[data-provider="summernote"]').each -> $(this).summernote height: 300 toolbar: [ ['style', ['bold', 'italic', 'underline', 'clear']], //太字や斜字など ['fontsize', ['fontsize']], //文字の大きさ ['color', ['color']], //文字色 ['para', ['ul', 'ol', 'paragraph']], //リスト化 ['insert', ['link']], //リンク化 ] //toolbarがなくても動作します。 //toolbarを用いてカスタムが可能なので、公式サイトのDeep diveを参考にしてください。 そもそもCoffeScriptって何だろうってところが気になったので簡単に調べました。 もう知っている人は下にスクロールしちゃってください。 JavaScriptのコード生成をコンパクトにする JavaScriptと比べて記述量を少なくできる 簡単に書けちゃうよ。CoffeScriptならね。って感じなのね。 まぁ、JavaScriptの記述に慣れてからじゃないとCoffeScriptに切り換えることは難しいかな。 参考:「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典 3.view画面への記述 まずはdata-provider = "summernote"を任意のtext_areaに追記します。 私の場合は新規投稿画面と投稿編集画面に追記です。 <div class="form-group my-2"> <%= f.label :"本文" %> <%= f.text_area :content,'data-provider': :summernote,class:"form-control"%> </div> 2で作成したcoffeeファイルとの連携がこれで可能になる! 4.summernoteで作成した投稿を表示させる 3まで行えばリッチなテキストエディタを実現出来ますが、エスケープ処理がされるので投稿が正しく表示されません。 エスケープ処理って何だ?って部分は以下のような感じです。 def show @link = '<a href="http://hoge.com">hoge</a>' end hoge/show.html.erb <p><%= @link %></p> //クリックするとどうなるでしょうか? ブラウザで確認すると エスケープ処理されると '<a>href="http://hoge.com">hoge</a>'の文字列のみ表示される。 エスケープ処理されないとリンク付きの hoge の文字列のみが表示される。 summernoteのリッチテキストエディタで太文字のhogeを投稿してもエスケープ処理をされてしまい<p class="font-weight-bold">hoge</p>と表示されてしまう。 今回のsummernoteではエスケープ処理をさせずに文字を表示させたいので、以下のような記述を加えてエスケープ処理を回避します。 view/posts/show.html.erb <%= @post.content.html_safe%> //手段1 <%= raw @post.content %> //手段2 <%== @post.content %> //手段3 わかりやすいので、私はこちらを活用 大いに参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Deviseで複数モデル使用時ログイン後の遷移先指定

deviseで管理者用とエンドユーザーの複数を使用している場合、ログイン後の遷移先も異なりますよね。 以下で解決です。 application_controller.rb def after_sign_in_path_for(resource) case resource when Admin admin_path when EndUser public_end_users_show_path end end def after_sign_up_path_for(resource) case resource when Admin admin_path when EndUser public_end_users_show_path end end def after_sign_out_path_for(resource) if resource == :admin new_admin_session_path else root_path end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでMaterial Design Iconsを使おう!(もともと使っていたGemが削除されたので新しいのを作ったという話)

とにかくRailsでMaterial Design Iconsつかいたい! という方はインストールと使い方を読んでもらえれば大丈夫です。 Gem作った背景などはそのあとに続きます。 インストール Gemfileに以下を追加します。 gem 'rails_material_design_icons' を追加し、bundle install を実行します。 使い方 application.css`で、cssファイルをインクルードします。 /* *= require materialdesignicons */ おめでとう! 公式サイトで利用可能なアイコンをチェックしましょう。 https://materialdesignicons.com/。 Sass 以下を application.scss ファイルに追加してください。 @import "materialdesignicons"; Gem作った背景 仕事でなんとなくあるMaterial Design IconsのGemを使っていたが、ある日それがRubygemsから削除されました。 使っていたのはこちらのMaterial Design Iconsです。 https://github.com/barrymieny/material_design_icons ですが、Rubygemsをみると以下の記載があります This gem previously existed, but has been removed by its owner. The RubyGems.org team has reserved this gem name for 36 more days. After that time is up, anyone will be able to claim this gem name using gem push. If you are the previous owner of this gem, you can change ownership of this gem using the gem owner command. You can also create new versions of this gem using gem push. Gem自体がもうないです。 他に似たGemもありましたが、最近更新されているものはなかったです。 またinline_svgなどの機能もいらないので、できればない方が嬉しいです。 もともと使っていたGemのコードをみると、テストもなく、これだときつい・・・と思ったのですが、FontAwesomeのRailsインテグレーションのGemだとコードがキレイで、Github Actionsで自動テストもされていました。これをMaterialDesignIcons用に改修しよう!と決めて、一日有給取って、初めてのGem公開しました! (仕事で使えなくなって困っていたので。。。それに有給使ってしまいました。楽しかったです。) またMaterial Design Iconsの新しいバージョンがでたらアップデート作業もありますが、すくなくともSVGはないです。 まえ使っていたGemは実は3200個のSVGファイルを読み込んでいたみたいで、使っていなかったので、もったいないという感じでした。 Gemを入れるときはちゃんとソースまで確認しましょう! という話でした。 ソース自体はこちらです https://github.com/sampokuokkanen/rails_material_design_icons 失敗は成功の元 ちなみに、バージョンを見ると https://rubygems.org/gems/rails_material_design_icons 最新のバージョンだけにフォントファイルなどが入っているのがわかります。 初めてのGemだったので、Gemspecファイルでファイル取り込みで失敗していました。 基本的にはデフォルトで入るコマンドでいいかと思いますが、もともとのGemのコマンドが入っていて、ちゃんとファイルが入らなかったです。 これもGitから直接Railsに取り入れるとちゃんと入っていたので、解決にはちょっと時間かかりました。 今はデフォルトでも入っているコマンドの以下にしています: gem.files = Dir.chdir(File.expand_path(__dir__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) } end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】逆数の足し上げ

逆数の和に関する問題を解きました。 【問題】 整数Nについて、1からNまでの整数の逆数の和を「Nまでの逆数和」として、Nまでの逆数和が8を超えるような最小のNを求めてください。 【回答】 Ruby def reciprocal array = [] sum_array = 0 n = 1 while sum_array < 8 r = (1.0 / n) array << r sum_array = array.sum n += 1 end return n - 1 end puts reciprocal
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

制御構造 - 条件分岐 case..when

case..when文について コーディングテスト対策をする上で、多くの場合if文による冗長なアルゴリズムが作成されていた為、その対応策の一つとして、case..when構文に関して、再度学習した。その内容をここに記録する。 結論として、if文と役割はそこまで変わらないが、自分なりの利点にあげる。 可読性が上がる 変数を扱う場合はスタイリッシュになる。 ここから下は公文の説明になります。コードについては、コーディングテストの内容をそのままにしてあります。 制御構造 - 条件分岐 case..when文 if文の場合 if score >= 80 array << "A\n" elsif score >= 70 && score < 80 array << "B\n" elsif score >= 60 && score < 70 array << "C\n" elsif score < 60 array << "D\n" end case..when文の場合 case score when 80..100 array << "A\n" when 70..79 array << "B\n" when 60..69 array << "C\n" when 0..59 array << "D\n" end 最後のcase分では、同一の変数を扱っている利点を活かし、条件式において変数を省略できるという利点を持つ。この点がif文と比較し、優位にあると考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【At Corder】【初心者】ABC085C - Otoshidama をRuby で解いてみた

はじめに AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~ - Qiita こちらの記事を参考に初心者がAt corderに挑戦します。 目的としては、就職活動でのコーディングテスト対策です。 毎日1問を目標としてコツコツやってきます わからないことは調べる精神です。ちょっとでもわからないなぁ、と思ったことは調べて解説と参考文献を載せますので、同じ内容でわからない人がいれば参考にししてください 問題文 日本の紙幣(10000,5000,1000円)をN枚使ってY円になるか? 成約 $1 ≤ N ≤ 2000$ $1000 ≤ Y ≤ 2 \times 10^7$ $N$は整数である $Y$は1000の倍数である 入力 N Y 出力 $N$枚のお札の合計金額が$Y$円になることがありえない場合は -1 -1 -1 と出力 あり得る場合は組み合わせの一例をxを10000円、yを5000円、zを1000円とするとx, y, zを空白で区切って出力せよ。複数の可能性が考えられるときはそのうちどれを出力してもよい。 入力例 9 45000 出力例 4 0 5 回答 N, Y = gets.split.map(&:to_i) for x in (0 .. N) for y in (0 .. N) z = N - (x + y) break if z < 0 sum = 10000*x + 5000*y + 1000*z if sum == Y printf("%d %d %d", x, y, z) exit end end end puts("-1 -1 -1") 感想 今回はそこまで苦戦せず解くことができました? 最初は勘違いでzを0~Nの値まで処理するようにしていました。? 勘違いミスが多い傾向。。 参考文献 【Ruby】exitでプログラムを終了させる【exit!やreturnとの違い】 | 「ポテパンスタイル」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでjQueryのfadeOutを使おうと思ったらエラーが起きた

やろうとしたこと RailsのflashメッセージをjQueryでフェードアウトさせたい!と思い、以下の記事を参考にしてjQueryを導入しました。 そして、以下のように実装しました、 <script type="text/javascript"> $(function() { setTimeout($('#フェードアウトさせたい要素').fadeOut('slow'), 2000); }); </script> エラー しかし、上手くフェードアウトせず。 コンソール画面を見てみると以下のようなエラーが出ていました。 このエラーを解決するべくGoogleで調べてみたところ、以下のような記事が出てきました。 この記事によれば、slim版のjQueryを使っているのが良くないとのこと。 しかし、jQueryを導入する際にslim版を指定した覚えもなければ、どうやって変更するのかもわかりませんでした。 そこで、色々と自分の書いたコードを確認していたところ、このviewファイルの一番下に以下のような記述がありました。 index.html.erb #省略 <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> どうやら、過去の自分がCDNのslim版jQueryを読み込んでいたようです。しっかりしろよ…。 この記述を消したところ、無事にフェードアウトさせることができました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] 閲覧数を計測するライブラリ

はじめに 自作のアプリケーションで記事を投稿する機能をつける時に閲覧数を表示させたかったので、以下を参考にimpressionistというGemを使い実装しました。 環境 ruby 2.6.6 rails 6.0.3 やり方 1.impressionistのインストール Gemfile gem 'impressionnist' 上記の記述をしてbundle installをしましょう。 2.impressionnists_tableの作成 以下のコマンドを入力するとimpressionistが定義した、impressionsテーブルをしようすることができます。 rails g impressionist rails db:migrateを実行します。 rails db:migrate すると以下のようなmigrationファイルが作成されました。 class CreateImpressionsTable < ActiveRecord::Migration[6.0] def self.up create_table :impressions, :force => true do |t| t.string :impressionable_type t.integer :impressionable_id t.integer :user_id t.string :controller_name t.string :action_name t.string :view_name t.string :request_hash t.string :ip_address t.string :session_hash t.text :message t.text :referrer t.text :params t.timestamps end add_index :impressions, [:impressionable_type, :message, :impressionable_id], :name => "impressionable_type_message_index", :unique => false, :length => {:message => 255 } add_index :impressions, [:impressionable_type, :impressionable_id, :request_hash], :name => "poly_request_index", :unique => false add_index :impressions, [:impressionable_type, :impressionable_id, :ip_address], :name => "poly_ip_index", :unique => false add_index :impressions, [:impressionable_type, :impressionable_id, :session_hash], :name => "poly_session_index", :unique => false add_index :impressions, [:controller_name,:action_name,:request_hash], :name => "controlleraction_request_index", :unique => false add_index :impressions, [:controller_name,:action_name,:ip_address], :name => "controlleraction_ip_index", :unique => false add_index :impressions, [:controller_name,:action_name,:session_hash], :name => "controlleraction_session_index", :unique => false add_index :impressions, [:impressionable_type, :impressionable_id, :params], :name => "poly_params_request_index", :unique => false, :length => {:params => 255 } add_index :impressions, :user_id end def self.down drop_table :impressions end end 閲覧数を計測したいモデルとコントローラーに記述を追加する。 今回は記事を投稿できるアプリケーションを想定してPostテーブルを使用してimpressionist用の記述をしていきます。 posts_controller.rb class QuestionsController < ApplicationController impressionist :actions=> [:show] def show @post = Post.find(params[:id]) impressionist(@post, nil, unique: [:session_hash]) end end 上記のimpressionistををみると第1引数に閲覧する記事のインスタンス、第2引数にオプション(今回は指定なし)、第3引数のunique: [:session_hash]は同じ人がアクセスした場合と複数回同じ記事をみた場合は1回分のアクセスという設定をしています。 最後に計測したいモデルに以下を追加します。 post.rb class Post < ApplicationRecord is_impressionable end ブラウザで閲覧数を確認する場合以下の記述をします。 show.html.erb <%= @post.impressionist_count %> 以上で完了です。 おまけ 閲覧数を表示するのに使いやすいものがFontAwesomeにあったので追記しておきます。 show.html.erb <i class="far fa-eye"></i><%= @post.impressionist_count %> こんな感じになります! 気に入ったら使ってみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HerokuへのPush時に「Failed to install gems via Bundler.」が出た時の対処法

RailsチュートリアルでherokuへPushする際以下のようなエラーが出た。 remote: -----> Ruby app detected remote: -----> Installing bundler 2.2.16 remote: -----> Removing BUNDLED WITH version in the Gemfile.lock remote: -----> Compiling Ruby/Rails remote: -----> Using Ruby version: ruby-2.6.6 remote: -----> Installing dependencies using bundler 2.2.16 remote: Running: BUNDLE_WITHOUT='development:test' BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4 remote: Your bundle only supports platforms ["x86_64-darwin-20"] but your local platform remote: is x86_64-linux. Add the current platform to the lockfile with `bundle lock remote: --add-platform x86_64-linux` and try again. remote: Bundler Output: Your bundle only supports platforms ["x86_64-darwin-20"] but your local platform remote: is x86_64-linux. Add the current platform to the lockfile with `bundle lock remote: --add-platform x86_64-linux` and try again. remote: remote: ! remote: ! Failed to install gems via Bundler. remote: ! remote: ! Push rejected, failed to compile Ruby app. remote: remote: ! Push failed remote: ! 調べたところ、Heroku環境のbundlerのversionとローカル環境のbundlerのversionが一致していないことが原因らしい。 Herokuのbundlerがバージョン2.2.16なのに対してローカルのバージョンは2.2.19だった。 解決方法 下記のコマンドを順に入力したらうまくいった。 $ gem uninstall bundler # ローカルのbundlerをアンインストール $ gem install bundler --version '2.2.16' # Herokuと同じバージョンのbundlerをインストール $ bundler -v # 正しくインストールされたか確認 $ rm Gemfile.lock # 今あるGemfile.lockを削除する $ bundle _2.2.16_ install --without production # bundleをインストール & Gemfile.lockが作成される $ bundle lock --add-platform x86_64-linux $ git add -A $ git commit -m "Change bundler version 2.2.19->2.2.16" $ git push heroku master エラー文でググったら似たような事例がたくさん出てきた。 追記 remote: ! Unknown version "6.1" このエラーは、Gemfileを書き換えるとうまくいった。 Gemfile gem 'rails', '6.0.3' こうする↓ Gemfile gem 'rails', '6.1.0'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

slimでlink_toを別タブで表示する

Railsでslimを用いてlink_toを書いた時、target="_blank"で別タブ表示させるのに少し詰まったのでメモ。 例 index.html.slim = link_to "ツイート一覧", tweets_path, target: "_blank" target: "_blank"の位置が重要でした。 余談ですがこちらの記事でも言われてる通り、target="_blank"はセキュリティ的に気をつけたほうがいいらしいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 条件つきバリデーション

条件つきバリデーションとは? 標準であるバリデーションだけでは対応できずに、特定の条件を満たす場合に実行したいバリデーションのこと。 [if],[unless]オプションを使用して記述することで、 特定の条件に対応することができる。 引数にProc,Arrayを使える。 if #特定の条件でバリデーションを行いたい時に使用 unless #特定の条件でバリデーションを行いたくない時に使用 if,unless class Pass < ApplicationRecord validates :number, presence: true, if: :pass_number? def pass_number? pass_type == "pass" end end class Money < ApplicationRecord #money_validate関数の呼び出し validate :money_validate    def money_validate unless money.nil? || money > 0.0 end end Proc 呼び出したいProcオブジェクトを:ifや:unlessで使うこともできます。Procオブジェクトを使うと、個別のメソッドを指定する代りに、その場で条件を書けるようになります。ワンライナーに収まる条件を使いたい場合に最適です ~Railsガイド~ class Account < ApplicationRecord validates :password, confirmation: true, unless: Proc.new { |a| a.password.blank? } end ~Railsガイド~ リファレンスには上記のように記述されていましたが、 Proc。newでオブジェクトを作成、引数aに渡し、 a.password.blank?で真偽値の判断? 条件付きバリデーションのグループ化 with_options 1つの条件を複数のバリデーションでまとめて処理を行うにはwith_optionsを使用 class User < ApplicationRecord with_options if: :is_admin? do |admin| admin.validates :password, length: { minimum: 10 } admin.validates :email, presence: true end end Rails ガイド if: :is_admin?という条件に対してバリデーションが働く Array,配列 バリデーションを行なう条件を複数定義する際には、Array 配列を使用 class Computer < ApplicationRecord validates :mouse, presence: true, if: [Proc.new { |c| c.market.retail? }, :desktop?], unless: Proc.new { |c| c.trackpad.present? } end Railsガイド 備忘録です。 参考サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む