20200212のRubyに関する記事は16件です。

【備忘録】Ruby : クラス変数の使い方

sample.rb
class Num
    @@num = 1
    #クラス変数
    #インスタンスメソッドからでもクラスメソッドからでもアクセス可能
    #異なるオブジェクトでも同じクラスであれば値は共有可能
    #あまり使われることはないらしい
    def increment
        @@num += 1
    end

    def decrement
        @@num -= 1
    end

    def num
        puts @@num
    end

    def self.num
        puts @@num
    end
end

foo = Num.new
foo.num #=> 1
foo.increment #+1
foo.num #=> 2
foo.decrement #-1
foo.num #=>1
foo.decrement #-1

fuga = Num.new
fuga.num #=>0
#新しいオブジェクトを作ったのにも関わらず、fooオブジェクトでの処理を引き継いでいる。
#引き継がない場合は 1 からスタートするはず。


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】flashメッセージをフェードアウトで消す方法【JavaScript】

はじめに

この記事では、flashメッセージを表示したあと一定時間後にフェードアウトさせる方法を解説します。

flashメッセージ系の記事はたくさんあるのですが、
どれもbootstrapを使用してたり
何やら複雑な方法だったり(Hamlハムル??とかrenderとかややこしい!)。。

もっと簡単な記事はないのか!とモヤモヤしたので、自分まとめます。
自分と同じような人の為に!!(自分用のメモですすみませんw)

flashメッセージの表示方法

それではレッツ実装!!

まずはコントローラーから。

コントローラー

def destroy
   @review = Review.find(params[:id])
   @review.destroy
   if review.destroy
      redirect_to root_path, notice: "︎レビューを削除しました!"
   end
end

メッセージを表示させる記述は4行目のif文からです。

レビューが削除された後、root_path(ホーム画面)に戻って
「レビューを削除しました」というメッセージが表示される。という流れです。

続いてビュー画面へ!

ビュー

<% if flash[:notice] %>
   <div class="flash"><%= flash[:notice] %></div>
<% end %>

<%= yield %>

flashメッセージはいろんな場面で共通で使う事が多いので、
views/layouts/application.html.erb
<%= yield %>より上の部分に記述します。

コントローラーとビューはひとまず完成!!

JavaScriptの下準備

まずはGemfile以下のコードを記述します。

gem 'jquery-rails'

からのターミナルで$ bundle install

続いて
app/assets/javascripts/application.js
に以下のコードを加えましょう。

//= require jquery

これでjQueryの下準備完了。

flashメッセージをフェードアウトさせる方法

いよいよ実装していきます。

①jsファイルに記述する場合(推奨)

以下のコードを
app/assets/javascripts/application.js(上と同じ場所)
に加えたら完成!!

$(function(){
  setTimeout("$('.flash').fadeOut('slow')", 2000);
});

②ビューファイルに直接記述する場合

以下のコードを
flashメッセージを表示させたいビューファイルに加えたら完成!!

<script>
  $(function() {
    setTimeout("$('.flash').fadeOut('slow')", 2000);
  });
</script>

※「.flash」はapplicaton.html.erbのdivに付けたクラス名です。各自自由に命名しましょう。
※数字部分は好みに合わせて変えましょう!ちなみに1000で1秒です。

さいごに

今回はレビューの削除destroy後の実装でしたが、もちろん編集edit
お気に入りfavoriteなんかも実装可能です。

あとはお好みでCSSをいじれば、
よく見るflashメッセージの完成です!!

<参考>
https://qiita.com/dir_sh0606/items/b2165459deda97ae8468

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Ruby : クラスメソッド、インスタンスメソッド使い方

sample.rb
class Person
    attr_accessor :from

    def self.lefty(name,age)
        #メソッド名の頭に self. をつけるとクラスメソッドになる。
        new(name,age,"左")
        #newすることでinitializeメソッド呼び出し
    end

    class << self
        #複数のメソッドをクラスメソッドにする方法
        def second_method
            puts "second"
        end

        def third_method
            puts "third"
        end
    end

    def initialize (name,age,foot)
        #new メソッド使った時に使用
        @name = name
        @age = age
        @foot = foot
    end

    def sayHello
        puts "私は #{@name}です。 #{@age}歳です。 利き足は #{@foot}です。"
        #ここで使っている@name,@age,@sexはinitializeで定義したものを使っている。
    end

    def sayFrom
        if @from != nil
            puts "私は #{@name}です。 #{@age}歳です。#{@from}出身です。"
            #ここで使っている@fromはattr_accessorの:fromに格納された値を使う。(インスタンス変数)
            #ちなみに @from 部分を from に書き直してもしっかり出力される
        else
            puts "私は #{@name}です。 #{@age}歳です。"
        end
    end
end


person1 = Person.lefty("中村",27)
#クラスメソッドを使用
person1.sayHello

person2 = Person.new("中田",29,"右")
#単純に new
#initializeメソッドの処理でインスタンス変数に引数を突っ込む
person2.sayHello

person3 = Person.new("小野",26,"右")
#単純に new
person3.from = "静岡"
# さらにattr_accessor:fromに静岡をセット=>@from
person3.sayHello
person3.sayFrom

puts person3.from #=>"静岡"
#puts person3.name #=>出力エラー
#解決方法=> attr_accessor に :name を追加
#追加後、=>小野


#foo = Foo.new
#foo.first_method
#foo.second_method
#上記のようにオブジェクトを生成した後にクラスメソッドを呼び出すことは不可能


出力結果

私は 中村です。 27歳です。 利き足は 左です。
私は 中田です。 29歳です。 利き足は 右です。
私は 小野です。 26歳です。 利き足は 右です。
私は 小野です。 26歳です。静岡出身です。
静岡
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mohoから出力したSVGを制御したい妄想の話

現在、実際に作ってる最中のブツがこちら。
適当なタイミングで更新をしてるので、ここで書いてる内容から構成が変わってる可能性あり。

【GitHub】SourceOf0-HTML/path_control: SVGを制御したい願望
https://github.com/SourceOf0-HTML/path_control

【GitHub Pages】ベクターデータをいじり倒したい気持ち
https://sourceof0-html.github.io/path_control/

さて。
妄想話の前に、そもそもの話から。

Mohoとは

2Dアニメーションソフトウェアのこと。
昔はAnime Studioという名前だったらしい。
自分が使用しているのはMohoPro12。

2Dアニメーションソフトウェア – Moho (Anime Studio): イーフロンティア
https://www.e-frontier.com/smithmicro/moho/

初めて使ってみたときに調べたことを解説動画として雑にまとめたこともある。

MohoPro12使ってみたので自己流解説 - YouTube
https://www.youtube.com/watch?v=hNsmKullIvo

SVGとは

スケーラブル・ベクター・グラフィックス(Scalable Vector Graphics)の略称であり、画像形式の一種。

PNGやJPGはラスタ形式…ビットマップ画像とも言われるもので、pixel(画素)の集まりで構成されている。
ちなみにpixelはpicture cell(画像の細胞)からの造語らしいよ。
写真なんかは基本ラスタ形式。
細かい色合いを表現できるのが特徴。
拡縮すると劣化するのが弱点。

これに対してSVGはベクタ形式と呼ばれる画像…
というか図形と言った方が分かりやすいかもしれない。
複数の点(アンカーポイント)を繋いだ線(セグメント)に対して、線の太さや塗りの色を設定することで画像が構成されている。
ポスターなんかは基本ベクタ形式。
拡縮しても劣化しないのが特徴。
細かい色合いを表現しようとすると、点と線の数が膨大(≒データサイズが膨大)になるのが弱点。

最近のWeb上のアイコンはSVG形式のものが多い…
てか今見たらQiitaのロゴもSVGだね。

MohoからSVGを出力すると…??

Mohoで作成したアニメーションは、SVGの連番ファイルとして出力することができる。
…が、しかし。

 バ グ る & フ ァ イ ル が 重 い 。

というところは、去年苦労した部分の話。
当時そのあたりで奮闘した話をブロマガでしてたので、そこから引用をば。

【2019-03-06】「SVGでアニメーションさせたいんじゃ」の詳細報告:変人のブロマガ - ブロマガ
https://ch.nicovideo.jp/FlyingEchidna/blomaga/ar1708679

実際にsvgを表示したときに、設定していた一部のマスクが効いてない。
詳しく差分データを用意してタグを解読していくと…
どうやら、マスク設定を有効にしたグループ内にグループを入れ子で置いた場合、マスク設定が出力されない模様。
mp4で出力したときは問題なかったし、Moho側のバグっぽい。

オマケにタグをよく見ると、表示しているパスをそのままマスクとしても流用する設定を入れていた場合、内容が全く同じパスを表示用とマスク用として出力していることが発覚。
どうにかならんかとsvgの仕様を調べたところ、useタグを使うことでパス情報の使いまわしができることが発覚。
どうにかしてuseタグに移行させたい。

…とまあそんなことがありましたとさ。

愚直にSVG連番ファイルを再生

当時、四苦八苦してRubyで

  • バグで吹っ飛んだマスクを、レイヤー名(SVG側でのgタグのID)を頼りに再設定
  • 同じデータを共通設定として整理しなおしてSVGを圧縮

をしまして。
出来上がったSVG連番を、JavaScriptを使ってHTML上でパラパラ漫画形式で再生、というのをやりましたとさ。

【GitHub】SourceOf0-HTML/walk_anime: svgアニメーションのテスト
https://github.com/SourceOf0-HTML/walk_anime

※圧縮したとはいえ、ゲロ重注意(23.1MB)
【GitHub Pages】walk
https://sourceof0-html.github.io/walk_anime/

ただ、このとき書いたRubyのコードは、あまりに四苦八苦したせいか、GitHubには上げてなかったようで。
またSVGをいじるにあたり、今回のGitHubのリポジトリには入ってます。えぇ。

【GitHub】path_control/addMaskTag.rb at master · SourceOf0-HTML/path_control
https://github.com/SourceOf0-HTML/path_control/blob/master/convert/addMaskTag.rb

ものとしては
同階層にsourceフォルダを作成&Mohoから出力したSVG連番ファイルを入れて実行すると、
同階層のdestinationフォルダに同名のSVGファイルを出力する、
というもの。
ただ、さすがに変換前後のSVGまでは重いので、フォルダも含め一緒には上げてないです。

再生だけじゃなく制御したくなってきた

ここからが今年の話。

久々に衝動のままMohoでアニメーションを作っていたところ、どうにも思った動きをしない部分が出てきて、気が付いた。

「あぁ、Mohoがやってるのはボーン(動きを制御するための骨のようなもの)を
基準としたアンカーポイント周りのアフィン変換(移動拡縮回転)でしかないのか」

「…ということは、
インバースキネマティクス(雑に言うと、目標点に向かって腕を伸ばすときの関節の角度の制御方法)で、
ボーンを再現した上で、SVGの制御したら…
Moho上での作業画面みたいな感じで、リアルタイムでアニメーション生成できるんじゃね?」

「Moho上のアクションの合成でやるモーフィング(ある形からある形へ徐々に変形するアニメーション)も実装できたら、夢がひろがりんぐじゃね?」

「やったろ」

尚、この思い付きから、現在すでに1ヶ月経っております。えぇ。
そして、今からするのはあくまで経過報告です。
まだできてない。うん。

ちなみにインバースキネマティクス自体は、p5.jsでシンプルなものを作ったことがあって、実物で言うとこんな感じ。

sketch_190830b - OpenProcessing
https://www.openprocessing.org/sketch/748924

ちなみにフォーワードキネマティクス(指定した角度に関節を曲げる&次の関節にも角度が影響する)というものも存在する。

sketch_190830a - OpenProcessing
https://www.openprocessing.org/sketch/748921

フォーワードキネマティクスの方がシンプルだし、処理的にもよく見かけるかも。
多分ボーンを実装するときはこっちもお世話になる。

もう脱SVGしてバイナリ化しようぜ

今回やりたいことをやろうとすると、SVGの中身を参考にしつつ、どのパーツをどういう形で描画するのか制御しなきゃいけない。
つまり、SVGをそのままHTML上に表示するのではなく、JavaScriptで編集した後のものを表示しなきゃいけない。

そこまでするなら、SVGを解析&座標や色・差分の情報を自分で設計したクラスにぶちこんで、canvasに描画した方がよくない??

というかそこまで解析してしまうなら、それをベースにバイナリ出力&バイナリ読み込みした方が、ファイルサイズも減るやん??

…と、いうことで。
現状、ここまでができており、冒頭に置いてるものは(2020-02-12現在)バイナリ化したものを読み込んで表示しております。
一応改めてリンク張っとこう。

通信量は4.1M。
前述のが23.1MBだったことを思えば、軽い軽い。

【GitHub Pages】ベクターデータをいじり倒したい気持ち
https://sourceof0-html.github.io/path_control/

解説とかこれからの方針とか

作ったブツの構成は現状こんな感じ。

【GitHub】SourceOf0-HTML/path_control: SVGを制御したい願望
https://github.com/SourceOf0-HTML/path_control

ファイル構成(2020-02-12現在)

ファイル・フォルダ 説明
convert/ 前述のSVG変換用のRubyとか入ってる
img/ convertのヤツで変換した後のSVG連番ファイルが入ってる
js/ JavaScriptファイルあれこれ(後述)
src/ 独自形式バイナリファイルが入ってる
BinaryStructuresNote.txt バイナリファイルの構成メモ
ClassStructuresNote.txt クラスの構成メモ
index.html GitHub Pagesで表示してるページ
output_data.html SVG->バイナリ変換処理用HTML
svg.html SVGそのまま再生用HTML

jsフォルダの中身はこんな感じ

ファイル 説明
index.js index.html用
output_data.js output_data.html用
path_control.js メイン処理用
path_control.min.js メイン処理用(Minify済み)
svg.js svg.html用

ちなみにMinifyってのは、ブラウザに読み込ませるJavaScriptのコードが長すぎるとデータが重くなるから、その分通信量かかるでしょ?ってことで、コードを圧縮すること。
人によってはよく「○○.min.js」ってのを見かけると思うけど、あれはそういうもの。

Minify化するツールは検索したらいろいろ出てくるよ。
自分が今お世話になってるのはココ。

JavaScript Minifier
https://javascript-minifier.com/

htmlに対応したjs共

index.js、output_data.js、svg.js、のことね。
主にHTMLで表示しているcanvasの制御と、
path_control.jsで実装してある処理の呼び出しをしてる。
フレームレートやフレーム数を管理しつつ、描画処理命令を呼び出して…みたいな。
この辺りはまだまだ構成をいじるだろうなと思ってる。

というのも、
表示したいものをループ再生させるだの、
ボーンをどう動かすかだの、
どれぐらいモーフィングさせるかだの、
再生周りの制御を最終的にすることになるので…
初期化処理のつもりで作ってるところで、そこまで面倒見てたらえらいことになるやん?

なので、フレーム数処理あたりは特に、メイン処理にあたるpath_control.jsに引っ越しなりなんなりすると思う。
最終的には初期化と、マウスやスマホでの操作の入力を受け取るだけにしたい。

path_control.js

メイン。本題。本体。うん。
久々にガチめのクラス設計をしてるので、クラスの説明をば。

クラス名 説明
PathCtr ファクトリー&シングルトンなクラス
PathContainer 1キャラ分のデータに相当。
画像サイズや各アクションの総フレーム数、GroupObjのインスタンスをリストで持ってる。
GroupObj Mohoのグループレイヤー、SVGのgタグに相当。
PathObjのインスタンスのリストや、入れ子になってるGroupObjのインスタンスのリストを持ってる。
PathObj Mohoのベクターレイヤー、SVGのpathタグに相当。
実際に描画するアンカーポイントやセグメントの塗りの色情報なんかを持ってる。

『ファクトリー&シングルトン』てなんやねん。
いや、あれですわ。
デザインパターンってヤツです。えぇ。

ファクトリーパターンは、生成するときにしか用事がない処理をまとめて実装してある設計のこと。
生成し終わったインスタンスでやる処理と言えば、
描画前の座標更新だったり、
実際の描画処理だったりするんだけど、
生成するときの…
今回だとSVGを解析して~とかバイナリを解析して~なんて処理は、
各々のインスタンスに持たせる必要がない処理なので、
PathCtrにまとめてある。

シングルトンパターンは、実行中のプログラムの中で1つしか実体(インスタンス)を用意しない設計のこと。
C++とかでガチでやるなら、外部からのコンストラクタやコピーコンストラクタの呼び出しを禁止して、別途インスタンスを生成して返すstaticの関数を作って…とかガッツリやることがあったりするけども…

今回JavaScriptでやってるのは、varで用意した変数にオブジェクトを突っ込んでるだけの雑な作りです。
なので、最早クラスと呼んでいいのか怪しい。
単なるオブジェクト。うん。

今後ボーンを実装するにあたり…

ボーン周りは現状まったく未着手。
その前にアフィン変換部分を実装しようかと思ってる。
ようは行列変換。そう。数学で出てくるあの行列。

\begin{pmatrix}
y_1 \\
y_2 \\
1
\end{pmatrix}
=
\begin{pmatrix}
p & q & b_1 \\
r & s & b_2 \\
0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
x_1 \\
x_2 \\
1
\end{pmatrix}

↑ こういうやつ。
数学赤点偏差値28の自分がそんな高等なことできるの?
って言われたら…
まあ…自力で全部理屈から追って実装するのはキツいかな。

ただ、世の中便利なもので、そういう行列変換周りの処理をまとめて実装してあるコードは存在して、自分も実際お世話になりまくってるのよね。
描画系のプログラムを書いたことがある人なら見たことがあるかもしれない。

xxx.translate();
xxx.rotate();
xxx.scale();

こういう関数。ようはこれの実装だったりする。
それでいて、これの実装をしてる人は世の中に結構いるわけで。

今回は ↓ を参考にさせていただいて実装して、描画前の更新処理として走らせようかなと思ふ。

【GitHub】transformation-matrix-js/matrix.js at master · leeoniya/transformation-matrix-js
https://github.com/leeoniya/transformation-matrix-js/blob/master/src/matrix.js

と、いったところで。
現状報告終了!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Ruby : 基本的なclass作成、定数の扱い方

sample.rb
class SampleClass
    attr_accessor :name,:greet
    # attr_accessor:name,:greetはinitializeメソッド内の
    # @name,@greetに関連
    def initialize(name,greet)
        # initializeメソッド => classをnewした時に必ず呼び出される。
        # メソッドの引数で受け取った値をインスタンス変数
        # にセットしている。
        @name=name
        @greet=greet
    end
end

sample = SampleClass.new("Tanaka","挨拶")
# newメソッドでクラスの定数に引数を渡したので
# sampleオブジェクトではname="Tanaka",greet="挨拶"が入っている。


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカル環境にて、nokogiriが原因(?)でbundle installできないときの解決策

ローカル環境にrailsの開発環境を作ろうと以下の記事Ruby初学者のRuby On Rails 環境構築【Mac】
を参考に進めていると、以下のエラーに遭遇した。

$bundle install

#以下が実行結果

Fetching gem metadata from https://rubygems.org/.............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Using rake 13.0.1
Using concurrent-ruby 1.1.6
Using i18n 1.8.2
Using minitest 5.14.0
Using thread_safe 0.3.6
Using tzinfo 1.2.6
Using zeitwerk 2.2.2
Using activesupport 6.0.2.1
Using builder 3.2.4
Using erubi 1.9.0
Using mini_portile2 2.4.0
Fetching nokogiri 1.10.8
Installing nokogiri 1.10.8 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/gems/nokogiri-1.10.8/ext/nokogiri
/Users/(user)/.rbenv/versions/2.5.0/bin/ruby -I /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/site_ruby/2.5.0 -r
./siteconf20200212-25664-dl2tcu.rb extconf.rb --use-system-libraries
checking if the C compiler accepts  -I
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/libxml2... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/Users/(user)/.rbenv/versions/2.5.0/bin/$(RUBY_BASE_NAME)
        --help
        --clean
/Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:456:in `try_do': The compiler failed to generate an
executable file. (RuntimeError)
You have to install development tools first.
        from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:574:in `block in try_compile'
        from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:521:in `with_werror'
        from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:574:in `try_compile'
        from extconf.rb:138:in `nokogiri_try_compile'
        from extconf.rb:162:in `block in add_cflags'
        from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:632:in `with_cflags'
        from extconf.rb:161:in `add_cflags'
        from extconf.rb:416:in `<main>'

To see why this extension failed to compile, please check the mkmf.log which can be found here:

/Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-18/2.5.0/nokogiri-1.10.8/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/gems/nokogiri-1.10.8
for inspection.
Results logged to
/Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-18/2.5.0/nokogiri-1.10.8/gem_make.out

An error occurred while installing nokogiri (1.10.8), and Bundler cannot continue.
Make sure that `gem install nokogiri -v '1.10.8' --source 'https://rubygems.org/'` succeeds before
bundling.

In Gemfile:
  rails was resolved to 6.0.2.1, which depends on
    actioncable was resolved to 6.0.2.1, which depends on
      actionpack was resolved to 6.0.2.1, which depends on
        actionview was resolved to 6.0.2.1, which depends on
          rails-dom-testing was resolved to 2.0.3, which depends on
            nokogiri

どうやらnokogiriがインストールできてないみたい..

まず、解決策

以下の記事macOSアップデート後の『xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools)...』の対処法
を参考にした。

以下を実行し、xcodeのツール(?)のインストールをした後、再度bundle installをしたら解決した。

$ xcode-select --install

どのように解決に至ったか

エラーメッセージに


Check the mkmf.log file for more details.  You may
need configuration options.

と出ていたので、mkmk.logを開いた。
そこに

mkmk.log
xcrun: error: invalid active developer path

というエラーが出ていたので、ググったところ上記の解決につながった記事がヒットした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで検索機能を実装する場合の4つのパターン

パターン

  • Ransackを使う
  • コントローラーに書く
  • Concernに書く
  • Formオブジェクトを使う

Ransackを使う

Ransack使うとソートも楽に書けそうだけど、
追々カスタマイズが面倒そうなのもあり最初から自分で書くことが多い。
あまり使ったこと無いので、ケースによっては触ってみたい。

コントローラーに書く

comments = Comment.where(post_id: params[:post_id]) if params[:post_id].present?

こんな感じでコントローラーに直接where文を書くケース。
何も考えずに書くとこうなることが多いと思う。
条件式が増えてくると複雑度上がってrubocopに怒られる。

Concernに書く

module Comments
  module SearchModule
    extend ActiveSupport::Concern

    def self.do_search(params)
     comments = Comment.all
     search_by_post_id(comments, params[:post_id]) if params[:post_id].present?
    end

    def self.search_by_post_id(comments, post_id)
      comments.where(post_id: params[:post_id])
    end
  end
end
Comments::SearchModule.do_search(params)

検索用のConcernモジュールに切り分ける方法。FatControllerはこれで解消できるので良さげ。
ただConcern特有のお作法があるので、自由に書きづらく、単純なクラスが使いたくなった。

Formオブジェクトを使う

class CommentSearchForm < SearchForm
    attribute :post_id, Types::Maybe::Coercible::Integer

    def do_search
     comments = Comment.all
     search_by_post_id(comments, post_id.value_or) if post_id.value_or.present?
    end

    private

    def search_by_post_id(comments, post_id)
      comments.where(post_id: post_id)
    end
end
Dry::Types.load_extensions(:maybe)

module Types
  include Dry::Types.module
end

class SearchForm < Dry::Struct
end
CommentSearchForm.new(params.to_hash.symbolize_keys).do_search

モデルに紐付かないパラメータを処理する時にはVirtusが便利で良く使われているそう。

ただ公式リポジトリをみると作者が後継ライブラリであるdry-rbを推奨していたので、dry-rbを元に実装した。
https://github.com/solnic/virtus

厳密には型指定のみなので以下の3つ。
dry-validationでバリデーションも出来るようなので、後々使ってみたい。

gem 'dry-monads'
gem 'dry-struct'
gem 'dry-types'

普通にgemをインストールすると一番古い0.5系になるので、最新の使い方とは違うので注意。

Formオブジェクトを使ったことで、次のようなメリットがある気がする。
まだ余り使いこなせてないので詳しい方の意見聞きたい。

  • フォーム関連の処理が書かれていることがひと目でわかる
  • FatController対策になる
  • シンプルなクラスでテスト書きやすい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby の型関連の情報まとめ

Ruby のバージョン3.0から型定義・型検査ができるようになる予定なのでそのまとめ。(2020年2月12日時点)

用語一言解説

  • 型シグネチャ:型定義ファイル
  • 型プロファイラ:型推論 + 型シグネチャのプロトタイプ生成
  • 型検査器:型シグネチャを用いた静的型検査ツール

型シグネチャ

  • 型情報を定義したファイル
    • Ruby3 では Ruby ソースコード本体に型情報を書かない
    • 拡張子は .rbs
    • ⇨記述のための言語は Ruby ではない
  • 型シグネチャをもとに後述の型検査を実行する
  • Ruby3 に同梱される

レポジトリ

ruby-signature: https://github.com/ruby/ruby-signature

型プロファイラ

  • 型シグネチャ無しで型検査する(レベル1型検査)
  • 型エラーの可能性を指摘
  • 型の推論結果を用いて型シグネチャのプロトタイプを生成する
  • 型プロファイラも Ruby3 本体に同梱される予定

レポジトリ

ruby-type-profiler: https://github.com/mame/ruby-type-profiler

型検査器

  • 型シグネチャを使って静的検査をする(レベル2型検査)
  • 高速化は目的としておらず、エラーなどを実行前に発見することが目的
  • こちらは Ruby3 には同梱されず、各々 gem をインストールして使うことになる

レポジトリ

steep: https://github.com/soutaro/steep
sorbet: https://github.com/sorbet/sorbet

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Pinterest APIで自分のアカウントのピン一覧を取得し、JSONを返すサンプルコード

はじめに

Pinterest APIを使ってみたので自分用に備忘録を残します。

今回対象とする機能は、「自分のアカウントのピン一覧を取得する」です。
公式ドキュメントはこちら

※利用制限等についてはご注意下さい。

(著作権所有者の許可を得ている場合を除いて)画像を保存または改変しないこと、また Pinterest にある画像をユーザーが印刷できるようにしないこと

ピン以外のデータを保存しないこと

などなど、制限があります。
デベロッパー向けガイドライン | Pinterest Policy

環境

OS: macOS Catalina 10.15.3
Ruby: 2.6.5
Rails: 6.0.2.1

前提

  • Access Tokenの取得方法
  • rails newなど最低限の下準備

などは細かく触れていません。

Access Tokenの取得はこちらから

1. net_http_module.rb

HTTPリクエストを投げる部分は分割しておきます。
今回はGETリクエストを投げるだけなので、超シンプルです。

app/controllers/concerns/net_http_module.rb
require 'active_support'

module NetHttpModule
  extend ActiveSupport::Concern

  require 'net/http'

  def api_get(set_URL)
    uri = URI.parse(set_URL)
    response = Net::HTTP.get_response(uri)
  end
end

2. pins_controller.rb

※事前に以下のようなPinモデル、コントローラーが作成済とします。

$ rails g model Pin pin_id:integer pin_url:string image_url:string width:integer height: integer
$ rails g controller api/v1/pins index
app/controllers/api/v1/pins_controller.rb
class Api::V1::PinsController < ApplicationController
  include NetHttpModule

  def index
    # Pinterest APIのAccess Tokenはcredentialsで管理
    url = "https://api.pinterest.com/v1/me/pins/?access_token=#{Rails.application.credentials.api_key[:pinterest]}&fields=id%2Cnote%2Curl%2Cimage"
    # NetHttpModuleで定義したapi_getメソッドを使用
    response = api_get(url)
    # 429(リクエスト多すぎ)ならエラーメッセージを返す
    if response.code == "429"
      render json: { error: response.message }, status: response.code
    elsif response.code == "200"
      #privateで定義したメソッドを使用してDBにpin情報を保存
      save_pins_and_cursor(response)
      # Vue.jsに返すJSONを作成する(この辺は必要に応じて変更)
      res = {
        message: response.message,
        pins: Pin.all.as_json,
      }
      render json: res, status: response.code
    else
      render json: { error: response.message }, status: response.code
    end
  end

  private

  def save_pins_and_cursor(response)
    pins = JSON.parse(response.body)['data']
    # 返ってきたデータの数だけpin情報をDBに保存する
    pins.each do |pin|
      Pin.create(
                  pin_id: pin['id'], # Pinterestが各ピンにつけている一意なID
                  pin_url: pin['url'], # Pinterestの該当ピンに飛ぶURL
                  image_url: pin['image']['original']['url'], # 画像が保存されているURL
                  width: pin['image']['original']['width'], # 画像の幅
                  height: pin['image']['original']['height'] # 画像の高さ
                )
    end
  end
end

あとはこれで返したJSONをフロントエンド側でゴニョゴニョすればOKです!

【便利】APIを試せるツールも公式に用意されている

こちらのリンクから、APIの各種機能を試せるAPI Explorerにアクセスできます。

GUIで使えるのは頭が疲れてるときにも優しいですね。便利。

スクリーンショット 2020-02-12 18.29.21.png

以上です!

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】deviseユーザーの登録日を表示する【めちゃ簡単】

Deviseのユーザー登録日を表示することに成功しました。めちゃ簡単なので、ぜひ実装してみてください。

rubyのバージョン 2.5.1
Railsのバージョン 5.2.3

手順は以下のような感じになります。

・❶:コントローラーにTime_zoneの記述
・❷:ビューにcreated_atの記述

手順1:コントローラーにTime_zoneの記述

まずは表示したいビューのコントローラーに以下の記述を行います。

class UsersController < ApplicationController

before_action :set_zone

〜〜〜〜省略〜〜〜〜
private
def set_zone
Time.zone='Tokyo' 
end

こんな感じで、日本語表記を綺麗なものに変更します。

僕の場合だと、usersコントローラーのmember.html.erbにユーザー登録日を表示したかったので、usersコントローラーにTime_zoneを表記しました。

別にapplication_controllerでもOKだと思いますが、変なエラーが起きても嫌なので記述する場所は限定しておくのが無難。

手順2:ビューにcreated_atの記述

以下のように記述します(自分が定義した変数に合わせて記述してください)。

<div class="member-information"><%=@user.created_at.strftime("%Y年%m月%d日 ")%></div>

これで実装は終わりです。

ちなみに、created_atというカラムはデフォルトでついているものですが、もしマイグレーションファイルに記述がない場合は以下を記述してくださいね。

t.timestamps null: false

そして、null: falseだとやっかいごとが起こるそうですが、僕の開発アプリだと大丈夫そうなのでnull: falseはつけています。

参考記事

お疲れ様でした!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ruby,実引数、仮引数学び

  • 実引数を定義し、それに答える仮引数の値はどんなものでも構わないが、
    仮引数で受け取ったメソッド内では仮引数で使った値を用いてプログラムを組み立てる。

どういう事かというと、

def aaa(input)
  input ** 2
end

bbb_input = 3
puts aaa(bbb_input)

9

大概の例題はこのような形でinputに似通ったものを使っていたりするので、

実引数、仮引数の関係がとてもわかりづらい。
なので、無茶苦茶にしてみる事にした。

def aaa(szxdcrftvgbsdfghdfghjdxfghldcfvgkml)
  bbb_input ** 2
end

bbb_input = 3
puts aaa(bbb_input)

111.rb:2:in `aaa': undefined local variable or method `input' for main:Object (NameError)

しかしこのままではエラーが出てしまう。

何故エラーが出るかというと、puts aaaはdef~end内で括ったメソッドを呼び出しているに過ぎないからである。

bbb_input = 3を定義し、aaa(bbb_input)とする事でaaaにbbb_input = 3を関連付けさせ、
puts aaaによってdef aaa endで定義したメソッドに仮引数を通じて値を渡し処理を実行しようとしているが、
def aaa end内では実引数が仮引数に変換されて処理をされるのでdef aaa end内の処理は、
仮引数を使って処理を記述しなければならないのである。

最初は非常にわかりにくいが、この仕組みが実はとても便利なのである。

エンジニア初心者のころは数列計算のような形でしか実引数、仮引数を扱わないと思うが、
これが例えば会計アプリを作成するとなった場合、
同じ処理を使いたいが実行結果のみ変えたいとなった時に、
実引数と仮引数が同じ処理を複数作る必要はなく、
その処理を行いたいメソッドに対して実引数さえ乗っけてしまえば、
後は仮引数が自動的に実引数を仮引数に変換してくれるので複数同じ処理を書かなくて済むのである。

先ほどまで記述していたコードで表すと、

def aaa(szxdcrftvgbsdfghdfghjdxfghldcfvgkml)
  szxdcrftvgbsdfghdfghjdxfghldcfvgkml ** 2
end


bbb_input = 3

a = 4

puts aaa(bbb_input)
puts aaa(a)

9
16

この通り"bbb_inpput"も"a"も無事に処理が完了する。
同じ処理はメソッド一つで済むという事である。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyメモ

  • 文字列を繋ぎ合わせる場合、+を使うことで繋ぎ合わせることが出来る。
input = "aaa" + "bbb" + "ccc"
puts input

"aaabbbccc"
  • ""内で特定の記号で区切られた文字列を配列にし分割する。
input = "aaa;bbb;ccc;"
a = input.split(';');
a.each do |a|
  puts a
end

aaa
bbb
ccc
  • 最初の文字を大文字にし、後の文字は小文字にする。
input = "aaabbbccc".capitalize
puts input

Aaabbbccc
  • 文字列の順番を逆に並べ替える
input = "abcde".reverse
puts input

edcba
  • 全ての文字列を大文字で返す。
input = "abcde".upcase
puts input

ABCDE
  • 引数で渡された文字列の二乗の数を返す。
def aaa(input)
  input ** 2
end

bbb_input = 5
puts aaa(bbb_input)

25
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails6 のちょい足しな新機能を試す 120(Rails.logger in Fiber編)

はじめに

Rails 6 に追加された新機能を試す第120段。 今回は、Rails.logger in Fiber 編です。
Rails 6 (と Rails 5.2.4.1) では、Fiber の中で Rails.logger.local_level でログレベルを変更しても、それが親(Fiber の親)に影響を与えないようになっています。

Ruby 2.6.5, Rails 6.0.2.1, Rails 5.2.4.1, Rails 5.2.3 で確認しました。 (Rails 6.0.0 でこの修正が入っています。)

$ rails --version
Rails 6.0.2.1

今回は、適切な利用例を思いつきませんでした。controller を1つ作って、 index の中で、 Fiber を使って確認します。

Rails プロジェクトを作る

Rails プロジェクトを新たに作成します。

$ rails new rails_sandbox
$ cd rails_sandbox

Home コントローラを作る

index ビューを持つ Home コントローラを作成する。

$ bin/rails g controller Home index

Controller を修正する

Controller を修正し、 index の中で、Fiber を使います。 Fiber#resume を呼ぶ前後で、Rails.logger

app/controllers/home_controller.rb
class HomeController < ApplicationController
  def index
    Rails.logger.level = 1
    Rails.logger.info("[Before] Rails.logger.debug? #{Rails.logger.debug?}")

    Fiber.new do
      Rails.logger.local_level = 0
      Rails.logger.info("[Fiber] Rails.logger.debug? #{Rails.logger.debug?}")
    end.resume

    Rails.logger.info("[After] Rails.logger.debug? #{Rails.logger.debug?}")
  end
end

rails server を実行して確認する

rails server を実行し、 http://localhost:3000/home/index にアクセスすると以下のように出力されます。

[Before] Rails.logger.debug? false
[Fiber] Rails.logger.debug? true
[After] Rails.logger.debug? false

Rails 5 では

5.2.4 では、Rails 6 と同様の動作をしますが、 Rails 5.2.3 では、以下のように Fiber#resume 実行後、元に戻りません。

[Before] Rails.logger.debug? false
[Fiber] Rails.logger.debug? true
[After] Rails.logger.debug? true

その他

どうもよくわからない事象が1つ。
以下のように、 Fiber.new のブロックの中を Rails.logger.info から Rails.logger.debug に修正します。

/app/
...
    Fiber.new do
      Rails.logger.local_level = 0
      Rails.logger.debug("[Fiber] Rails.logger.debug? #{Rails.logger.debug?}")
    end.resume
...

そうするとなぜか以下のようにFiberの中のログ出力が無視されてしまいました。

Rails.logger.debug?true になるので、 Rails.logger.debug でも出力されるかと思ったのですが...

[Before] Rails.logger.debug? false
[After] Rails.logger.debug? false

試したソース

https://github.com/suketa/rails_sandbox/tree/try120_logger_in_fiber

参考情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsアプリをHerokuにデプロイ

Cloud9から作成したrailsアプリをHerokuにデプロイする際に
色々と躓いたので成功した手順を書き留めておきます。

1.Herokuアカウント作成

以下のURLからHerokuアカウントを作成します。
https://jp.heroku.com/

無料プランでも月550時間の稼働時間が利用できます。
利用が想定されるユーザの規模が非常に小さい場合は無料プランで十分だと思います。

2.Herokuにアプリを作成

Herokuにログイン

$ Heroku login

プロジェクトフォルダに移動し、Herokuにrailsアプリを作成

$ Heroku create アプリ名

3.Heroku用の設定

以下のようにGemfileに追加

group :production do
  gem 'pg', '0.20.0'
end

以下のようにproduction:内の記述を変更

config/database.yml
production:
  adapter: postgresql
  encoding: unicode
  pool: 5
  database: フォルダ名_production
  username: フォルダ名
  password: <%= ENV['フォルダ名_DATABASE_PASSWORD'] %>

4.アプリをデプロイ

デプロイを実行

$ git push heroku master

マイグレーションを実行

$ Heroku run rails db:migrate

5.うまくいかない時

エラーが出る場合は3のherokuの設定もしくはGitでコミットできているかを確認してください。
それでもエラーが出る場合は下の項目を実行してみてください。
修正後はマイグレーションを実行しましょう。

PostgreSQL アドオンの追加

PostgreSQLのインストールができてない可能性があるため、手動でアドオンを追加します。

$ heroku addons:create heroku-postgresql:hobby-dev

環境変数の確認

実行に必要な環境変数が空になっている可能性があります。
configでそれぞれの環境変数に値が入っているかを確認してください。

$ heroku config

値が空だった場合は以下のコマンドで環境変数に値を入れます。

$ heroku config:set HENSU=hensu

エラーログ参照

以下のコマンドでログを確認すれば、エラーの詳細がわかります。

$ heroku logs

リアルタイムで出力したいなら以下のコマンド

$ heroku logs --tail

画像アップローダー

もし画像アップロード機能を実装している場合はローカルではなく
クラウド上にファイルが保存されるように設定を行う必要があります。
設定はこのあたりの記事が参考になりました。
https://qiita.com/junara/items/1899f23c091bcee3b058
https://qiita.com/daichi41/items/af2a56ea46c13ca55fd3
https://qiita.com/hmmrjn/items/479c9e9ce82771f1b6d7
https://pg-happy.jp/rails-aws-s3-upload.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【環境構築】Rails6 + Vue.jsで hello_vue.jsの名前をmain.jsに変更したいときの手順

はじめに

先日、こちらの記事を書きました。
【環境構築】Docker + Rails6 + Vue.js + Vuetifyの環境構築手順 - Qiita

そこで自動生成される、app/javascript/hello_vue.jsの名前をmain.js変更したい方のために手順を記載しておきます。(対象が狭い!笑)

環境

OS: macOS Catalina 10.15.3
Ruby: 2.6.5
Rails: 6.0.2.1
Docker: 19.03.5
docker-compose: 1.24.1
Vue: 2.6.10
vuetify: 2.1.0

前提

こちらの記事に沿って環境構築が完了したとします。

1.ファイル名を変更

app/javascript/packs/hello_vue.jsの名前を変更します。

hello_vue.js

main.js

2.home/index.html/erbを変更

app/views/home/index.html.erb
<%= javascript_pack_tag 'hello_vue' %>
<%= stylesheet_pack_tag 'hello_vue' %>
app/views/home/index.html.erb
<%= javascript_pack_tag 'main' %>
<%= stylesheet_pack_tag 'main' %>

3.manifest.json内を変更

最後に、Webpackが出力したmanifest.jsonをいじる必要があります。

public/packs/manifest.json
{
// 
  "entrypoints": {
// 
    "hello_vue": {
      "js": [
        "/packs/js/hello_vue-343ebbfc5aed29c10bf6.js"
      ],
      "js.map": [
        "/packs/js/hello_vue-343ebbfc5aed29c10bf6.js.map"
      ]
    },
// 
  },
  "hello_vue.js": "/packs/js/hello_vue-343ebbfc5aed29c10bf6.js",
  "hello_vue.js.map": "/packs/js/hello_vue-343ebbfc5aed29c10bf6.js.map",
// 
}

public/packs/manifest.json
{
// 
  "entrypoints": {
// 
    "main": {
      "js": [
        "/packs/js/main-343ebbfc5aed29c10bf6.js"
      ],
      "js.map": [
        "/packs/js/main-343ebbfc5aed29c10bf6.js.map"
      ]
    },
// 
  },
  "main.js": "/packs/js/main-343ebbfc5aed29c10bf6.js",
  "main.js.map": "/packs/js/main-343ebbfc5aed29c10bf6.js.map",
// 
}

以上です!

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

前回の記事

【環境構築】Docker + Rails6 + Vue.js + Vuetifyの環境構築手順 - Qiita

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自動デプロイ後ローカルでいろいろ変更してデプロイしたらmigrationとunicornで詰まりデプロイできなかったので解決までの備忘録

自動デプロイでかなり詰まったので備忘録として残します。

  • 初学者のため理解が不足しているところがあり、解決の過程で必要のない工程をしている可能性があります。
  • もし他の改善策などご指摘いただけることがあればよろしくお願いしたします。

エラー過程①

  • 自動デプロイ完了後ローカルでデータベース構成などを変更
  • 変更後自動デプロイ実行する。
  • 途中でmigrationの実行で止る。

解決までの流れ①

  • 原因の仮説としてはローカルでデータベースのテーブルやカラムの変更を行いrails db:migrate:resetを行ったためだと仮定し本番のデータベースもリセットすることにした。

  • 関連の記事を探し、こちらで書かれているコードを参照、一度データベースを消してしまうという方法を取ってみたhttps://qiita.com/wacker8818/items/72d933f036ca0b75d124

  • 無事データベースは削除され、改めてデータベースの作成とマイグレーションを実行したが落とし穴に会う。

    • ローカルに戻りアプリケーションのディレクトリで、自動デプロイを実行したがまたもマイグレーションの実行部分でエラーが起きた。
    • 何故かなと思ったが、エラー内容としては、マイグレーションファイル読み込み時に、追加したいカラムがもうあるよ的なエラーでした。 - 理由は本番でデータベース削除の後にマイグレーションまで実行してしまったことだった。 - 考えてみれば当たり前の話なのだが、この時点ではまだ修正したマイグレーションファイルも反映していないので、古いマイグレーションファイルをマイグレートして、さらにローカルで新しいマイグレーションの読み込みを実行していたのだ。 - なのでもし修正した方のマイグレーションファイルに、前に作ったマイグレーションファイルの設定と同じ記述があればエラーが起きるのは当たり前になる。
  • なのでもう一度データベースを消してやり直ししてなんとかマイグレーションの自動デプロイは通った。

エラー過程②

  • ①通過後unicornstartでエラー発生し止る。

解決までの流れ②

ターミナル
master failed to start, check stderr log for details
# エラー文
  • エラー文を参考に調べた結果
    • このエラーの原因解決には(おそらく)本番環境でunicornのログを見てみるのが一番の近道らしいと聞いたので見てみる。
    • 何個かエラーらしきところは確認でき、どこのファイルのどこの行かまで詳しくログに書いてあったが簡単な記述ミスが起きているでけということで特にunicornが動かない原因だと考えなかった。
    • その後unicornのプロセスで見えないプロセスが裏で動いている可能性があると、指摘を受け調べてみたがその裏で動くプロセスの見方がわからなかった。
    • 詳しい方にいろいろ聞き本番環境のアプリケーションのディレクトリで下記のコマンドを実行。
ec2-user(appディレクトリ)
sudo vim shared/tmp/pids/unicorn.pid
  • vimファイルの一番上にpid番号らしき5桁の番号が保存されていたため直接消去しvimファイルを保存。

    • もう一度vimファイルを開いてちゃんと保存されているか確認。
    • これで解決できたと思いローカルに戻り自動デプロイを実行。
    • しかしまた同じunicorn実行の部分でエラーが起きた。
  • ここでもう原因がわからず悩んだが、一番エラーの原因として可能性が低いと思っていた、以前unicornログで見たエラーを解決することを優先。

    • ここでのエラーはcontrollerでメソッド定義したときのendの数が足りなかったという単純なものだった。
    • その後ダメ元で自動デプロイを実行したらなんと通った!!

unicornで止る原因(仮説)

  • ローカルでエラーがあるものをmasterにmergeしてそのまま自動デプロイをするとエラーで止る。(特にデータベース、コントローラー関連のところ)
  • 不要なプロセスが動いている。(killコマンドでプロセスを消す必要がある。)
  • 途中で強制的にpidの接続が切れた場合、裏でも動いている可能性がある.

反省

  • 一度マイグレーションしたデータベースのテーブルやカラムの変更を行う際は、しっかりと直接記述せずターミナルのコマンドで追加、または削除すること。
  • 自動デプロイがしっかり完了したら基本的にエラーの原因はローカルでの変更が原因なので、実装と動作確認をしっかり行うこと。

• この記事を書いてみての反省

書く時はエラー文をしっかりコピーなどして参照しやすいようにしたほうがわかりやすく書くことができたと思う。
長々と文章ばかりでわかりづらいところが多いので、端的にもっと説明できるように文章力も磨いていく。
拙い文章を最後まで見てくださった方ありがとうございました。
これからも精進していきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む