20200325のRubyに関する記事は27件です。

チーム開発 (決済ライブラリpayjpインストールエラー対処)

gem 'payjp'
bundle installできなかったが、macOSをアップデートしたらbundle installできるようになった。

rails sできなくなったエラー文
Could not find a JavaScript runtime.
See https://github.com/rails/execjs for a list of available runtimes.
参考サイト MacにNode.jsをインストールで解決
https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09

セレクトタグ
https://qiita.com/ozackiee/items/3c027d07cdeb61df6029

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

Ruby 文字列関連まとめ、およびシンボルとの違い

1. はじめに

Rubyの文字列は"hoge"'hoge':hogeなど非常にややこしいと感じているのは自分だけでしょうか。
コロンがついている:hogeは正しくは文字列ではなくシンボルと呼ばれているものですが、非常に文字列と似ているため最初のうちは混乱すると思います。
本記事では主にこの3つについて解説します。
さらに、%Q(hoge)%W(hoge)なんてものもありますが、こちらは使いどころが多くないため、簡単な解説に絞ります。

2. "hoge"と'hoge'

ご存知、文字列の基本ですね。ダブルクオーテーションやシングルクオーテーションで囲うことで文字列であることを表します。ほとんどの言語で使われているので知っていると思います。

"hoge"と'hoge'のどちらを使うか

基本的にはどちらを使っても良いです。しかし、Rubyの使用上、「特殊文字」「式展開」を使うときは""で囲う必要があるため日頃から""を使うことをおすすめします。
特殊文字、式展開については以下で説明します。

特殊文字

\n(改行)や\t(タブ)などのこと。(他にもあるので調べてみてください。)
以下の様にhello worldの中に特殊文字を入れた例で見てみる。

hello.rb
puts "He\nllo wor\tld"
puts 'He\nllo wor\tld'

実行結果は以下の様になります。

He
llo wor ld
He\nllo wor\tld

ダブルクォーテーションの方は改行とタブが入っていて、シングルクォーテーションの方は文字がそのまま出ていることがわかります。

式展開

#{}で囲むことで変数を出力したり、計算結果を出力できたりする。

price.rb
price = 100

puts "税込価格は#{price * 110 / 100}円です。"
puts '税込価格は#{price * 110 / 100}円です。'

実行結果は以下の様になります。

税込価格は110円です。
税込価格は#{price * 110 / 100}円です。

ちなみに式展開を利用せずに書くと以下の様になります。コードが長く見辛くなるため式展開は覚えておいた方が良いでしょう。

puts "税込価格は" + (price * 110 / 100).to_s + "です"

3. :hoge

コロンがついたものはシンボルと言われるものです。他の言語にはないため戸惑う人も多いと思います。
内部的な詳しい解説は、他の人(例えばこちら)に任せるとして、簡単に説明します。

シンボルの有用性

シンボルは、ソースコード上では文字列のように見え、内部的には整数値として管理されています。もちろん文字列はソースコード上でも内部的にも文字列として管理されています。
では内部的に整数値で管理することのなにがよいかというと、処理速度が上がるんですね。整数値は0~9の組み合わせですが、文字列は膨大な数の文字がありますから。

極論、シンボルが嫌いなら一切使わずにコードを書くことも可能です。しかしせっかく開発者が用意してくれた便利な機能なのでどんどん使いましょう。

シンボルの使いどき

その文字列自体に意味があるときは文字列、意味がないときはシンボルを使いましょう。
わかりやすいのはハッシュのキーと値です。
例えば、人 = {名前 => 山田太郎, 住所 => 東京都}というハッシュがあったとします。
「山田太郎」や「東京都」はその文字自体に意味があります。もし全て整数に変換されてしまったらわけわかめですよね。
逆に「名前」や「住所」は、値を取り出すためのキーです。「山田太郎」は人[名前]、「東京都」は人[住所]で取り出せます。
別にハッシュを人 = [なーまーえ => 山田太郎, じゅーしょ => 東京都]として、人[なーまーえ]人[じゅーしょ]で取り出してもいいですよね。こういうときにシンボルを使います。
ルーティングなどにもよく使われていますね。

ハッシュでの利用

:hogeではなくhoge:という記述があって戸惑ったこともあるかもしれません。これもシンボルを表し、書き方が特殊なだけです。
先に述べた様に、シンボルはハッシュにおいてよく使われるので書きやすい特殊な書き方が用意されています。
下の3つはほぼ同じハッシュを表していますが、3つ目の書き方に後ろにコロンがついているのがわかると思います。これがhoge:の正体です。

person.rb
# シンボルを使わず定義
person = { "name" => "山田太郎", "address" => "東京都" }

# シンボルを使って定義 
person = { :name => "山田太郎", :address => "東京都" }

# シンボルを使って特殊な書き方で定義
person = { name: "山田太郎", address: "東京都" }

注意

キーを文字列で定義したときは取り出すときも文字列を指定、シンボルで定義したときは取り出すときもシンボルを指定する必要があるということに注意しましょう。
ハッシュの定義は慣れないうちは混乱すると思います。こちらの記事も参考にしてみてください。非常に整理されています。

4. %Q()と%W()

あまり使うことはないかと思いますが、"hoge"%Q(hoge)'hoge'%q(hoge)と表すことができます。
これのメリットが感じられるのは中の文字列に"'がたくさんある時です。

私は"山田"です。と表したいときは以下の様になり、エスケープする必要がない分%Q()のほうが可読性が高いことがわかります。

sample.rb
puts "私は\"山田\"です。"

puts %Q(私は"山田"です。)

また、文字列の配列を定義するときに有用なのが%W()で、以下の2つは同じ配列を表します。
コンマ区切りでなくなっていることに注意しましょう。

sample.rb
puts [ "red", "blue", "yellow" ]

puts %W( red blue yellow )

こんなものもあるんだ程度に覚えていれば大丈夫でしょう。

5. さいごに

Rubyには、「使いこなせると便利だけど最初のうちは意味がわからない」ということが多い様に思います。シンボルもその一つでしょう。
基本的には文字列と変わらないけど、ハッシュのキーなどの文字列自体に意味のないものに使うべしと覚えましょう。

質問などございましたらお気軽に。
@ruemura3

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

【Rails】FactoryBotの作り方(アソシエーション、Faker、画像の紐付け)

はじめに

今回はFactoryBotという存在は知っているけど、自分で作れって言われたら、ちょっと・・・と思う人に向けて、FactoryBotの作り方を記事にしました。
FactoryBotについての説明は割愛します。

FactoryBotの作り方

基本的な構文はこれです。

FactoryBot.define do
  factory :user do
    first_name { "John" }
    last_name  { "Doe" }
    admin { false }
  end
end

例えば、itemテーブル(商品)を作成するとしたら

  • まず、FactoryBotの決まり文句を書きます。
factories/items.rb
FactoryBot.define do
  factory :item do
  end
end
  • 次に、カラムを列挙します。
factories/items.rb
FactoryBot.define do
  factory :item do
    name {"レモン"}  # {}で囲っているとfactoryが生成された時に評価される
    price {3000}
  end
end

使い方、属性の上書きの仕方

create(:item)
create(:item, name: "リンゴ")

アソシエーションの仕方

例えば、itemとuserが紐づいている場合、以下のように書く。
(userのfactoryBotも作成している必要がある)

factories/items.rb
FactoryBot.define do
  factory :item do
    name {"レモン"} 
    price {3000}
    association :user, factory: :user # アソシエーション
  end
end

association :user, factory: :userの部分は単にuserと省略できる

factories/items.rb
FactoryBot.define do
  factory :item do
    name {"レモン"} 
    price {3000}
    user # アソシエーション
  end
end

画像の紐付けの仕方

itemにimageが紐づいている時を想定します。
itemは必ずimageが紐づいており、imageは必ず紐づいたitemがあります。

先ほどの通り、実装すると、以下のようになりますが、imageを作るときに、itemが必要で、itemはまだ作成されていないので、結論うまくいきません。

factories/items.rb
# これはうまくいかない
FactoryBot.define do
  factory :item do
    name {"レモン"} 
    price {3000}
    user
    image
  end
end

after(:build)を使用する必要があります。
これで、imageを一枚紐づけることができます。

factories/items.rb
FactoryBot.define do
  factory :item do
    name {"レモン"} 
    price {3000}
    user
    after(:build) do |item|
      item.images << create(:image)
    end
  end
end

after(:build)で保存される直前に処理を実行でき、複数のfactoryを同時に生成することができる

Fakerの使い方

FactoryBotと良く一緒に使われるのがFaker。
Fakerを使うメリットは、ランダムな文字列を生成してくれるところにある。
例えば、deviseを用いたユーザログインでは、emailがすでに登録されていたらそのemailを使うことができない。

以下のようなFactoryを作成してしまうと

factories/users.rb
# これはうまくいかない
FactoryBot.define do
  factory :user do
    name {"テストユーザー"}
    email {"test@hello.com"} 
  end
end

生成する際に、エラーが起こる

john = create(:user, name: "John")
kevin = create(:user, name: "kevin") # emailが重複して作成できない!!

Fakerを使用すると、ランダムに生成してくれて、エラーが起きない。

factories/users.rb
FactoryBot.define do
  factory :user do
    name {"テストユーザー"}
    email { Faker::Internet.free_email } 
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sidekiqを使って非同期にメール送信処理をする方法

詰まったところ

sidekiqで非同期にメール送信処理を実行する様に実装していたら、メール送信のジョブが待機状態のまま実行されない不具合に遭いました。

結論・解決方法

公式にある通り、以下のコードを実行。
https://github.com/mperham/sidekiq/wiki/Active+Job#action-mailer

bash
$ bundle exec sidekiq -q default -q mailers

次に、deliverdeliver_nowdeliver_laterに変更。
deliver_laterとは、非同期にメール送信処理を行ってくれるメソッドである。
逆に、deliverdeliver_nowは同期的にメール送信するメソッドである。

〇〇_mailer.rb
〇〇Mailer.deliver
〇〇Mailer.deliver_now

〇〇_mailer.rb
〇〇Mailer.deliver_later

これで治るはず(知らんけど)。

もしDockerを使っていたら

sidekiqコンテナをビルドする際に以下コマンドを実行させる。

docker-compose.yml
sidekiq:
    command: bundle exec sidekiq -q default -q mailers

非同期処理を実装する前に

sidekiqでperform_asyncを使って非同期処理をする前に、まずは〇〇Mailer.new.performで同期処理ができるか確認しよう

そもそもとしてそのコードに不備がないかを確認しましょう(僕は不備がありました)。。。。
perform_asyncは非同期になるので、binding.pryなどでデバッグはできません。
なので、

〇〇_mailer.rb
〇〇Mailer.new.perform(args)

を実行して同期処理で正しく動作しているかを確認しましょう。

sidekiqのログを確認しよう

https://github.com/mperham/sidekiq/wiki/Monitoring#web-ui
sidekiqには、sidekiqで処理するジョブを閲覧できるダッシュボードを用意してくれています。
/sidekiqへアクセスすることで、ダッシュボードからエラーログの確認ができます。
エラーログを見ると、どの様なエラーが吐かれているかを確認できます。
スクリーンショット 2020-03-25 19.54.11.png

sidekiqの処理に渡す引数のデータの型に気をつけよう

https://github.com/mperham/sidekiq/wiki/Best-Practices#1-make-your-job-parameters-small-and-simple
sidekiqに渡す引数のデータ型には制限があります。
シンボル、名前付きパラメーター、複雑なRubyオブジェクト(Date型やTime型など)は使用できません。
使用できる引数のデータの型は、JSONのデータ型のみです。
JSONのデータ型は以下です。

  • 文字列 ("...")
  • 数値 (123, 12.3, 1.23e4 など)
  • ヌル値 (null)
  • 真偽値 (true, false)
  • オブジェクト ({ ... })
  • 配列 ([...])

僕は引数にDate型を渡していましたが、勝手にString型にされ他ので上記の様なエラーが起きました。
なので、Date型の引数をto_sで渡して、Worker内でto_dateすることで解決しました。
みなさんの問題解決に繋がったら幸いです。

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

sidekiqを使って非同期にメール送信処理をする方法。メール送信ジョブが待機状態のまま実行されない不具合の解決方法

結論・解決方法

公式にある通り、以下のコードを実行。
https://github.com/mperham/sidekiq/wiki/Active+Job#action-mailer

bash
$ bundle exec sidekiq -q default -q mailers

次に、deliverdeliver_nowdeliver_laterに変更。
deliver_laterとは、非同期にメール送信処理を行ってくれるメソッドである。
逆に、deliverdeliver_nowは同期的にメール送信するメソッドである。

〇〇_mailer.rb
〇〇Mailer.deliver
〇〇Mailer.deliver_now

〇〇_mailer.rb
〇〇Mailer.deliver_later

これで治るはず(知らんけど)。

もしDockerを使っていたら

sidekiqコンテナをビルドする際に以下コマンドを実行させる。

docker-compose.yml
sidekiq:
    command: bundle exec sidekiq -q default -q mailers

非同期処理を実装する前に

sidekiqでperform_asyncを使って非同期処理をする前に、まずは〇〇Mailer.new.performで同期処理ができるか確認しよう

そもそもとしてそのコードに不備がないかを確認しましょう(僕は不備がありました)。。。。
perform_asyncは非同期になるので、binding.pryなどでデバッグはできません。
なので、

〇〇_mailer.rb
〇〇Mailer.new.perform(args)

を実行して同期処理で正しく動作しているかを確認しましょう。

sidekiqのログを確認しよう

https://github.com/mperham/sidekiq/wiki/Monitoring#web-ui
sidekiqには、sidekiqで処理するジョブを閲覧できるダッシュボードを用意してくれています。
/sidekiqへアクセスすることで、ダッシュボードからエラーログの確認ができます。
エラーログを見ると、どの様なエラーが吐かれているかを確認できます。
スクリーンショット 2020-03-25 19.54.11.png

sidekiqの処理に渡す引数のデータの型に気をつけよう

https://github.com/mperham/sidekiq/wiki/Best-Practices#1-make-your-job-parameters-small-and-simple
sidekiqに渡す引数のデータ型には制限があります。
シンボル、名前付きパラメーター、複雑なRubyオブジェクト(Date型やTime型など)は使用できません。
使用できる引数のデータの型は、JSONのデータ型のみです。
JSONのデータ型は以下です。

  • 文字列 ("...")
  • 数値 (123, 12.3, 1.23e4 など)
  • ヌル値 (null)
  • 真偽値 (true, false)
  • オブジェクト ({ ... })
  • 配列 ([...])

僕は引数にDate型を渡していましたが、勝手にString型にされ他ので上記の様なエラーが起きました。
なので、Date型の引数をto_sで渡して、Worker内でto_dateすることで解決しました。
みなさんの問題解決に繋がったら幸いです。

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

field_with_errorsによるレイアウト崩れを防ぐ

実装したいこと

新規会員登録機能バリデーションを設定し、入力項目にエラーがある場合はエラーメッセージとともに、入力ページに戻るように設定してします。
この時下の写真のようにレイアウト崩れが発生しますので、この事例について修正します。

初回入力時

スクリーンショット 2020-03-25 18.29.08.png

再入力時(レイアウト崩れ)スクリーンショット 2020-03-25 18.29.33.png

レイアウト崩れの原因

この事象の原因は、エラーが発生している時Railsが自動的にfield_with_errorsクラスを持つdivタグで、labelタグやinputタグを囲むことによって発生します。横並びになっている2つのinputタグそれぞれをdivダグで囲むため、改行されて表示されています。

new.html.haml
.contents-field
  = f.label :お名前(全角)
  %span.contents-field__require 必須
  %br
  = f.text_field :family_name, class:"contents-field__input half", placeholder:"例)山田"
  = f.text_field :first_name, class:"contents-field__input half", placeholder:"例)太郎"
.contents-field
  = f.label :お名前カナ(全角)
  %span.contents-field__require 必須
  %br
  = f.text_field :family_name_kana, class:"contents-field__input half", placeholder:"例)ヤマダ"
  = f.text_field :first_name_kana, class:"contents-field__input half", placeholder:"例)タロウ"

スクリーンショット 2020-03-25 18.30.26.png

解決方法

上記の事象に対する解決方法は2つが考えられます。

  • 自動で読み込まれるfield_with_errorsタグを読み込まないように設定する。
  • field_with_errorsに対してCSSをあてる

今回はfield_with_errorsタグを読み込まないようにする方法で、以下のようにapplicaton.rbを編集します。

applicaton.rb
module FreemarketSample65d
  class Application < Rails::Application
    #以下を追加
    config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag }
  end
end

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

ピュア Ruby で Ruby 2.7 の Numbered parameter を実装してみよう!

Ruby Advent Calendar 2019 1日目の記事になります。
本記事では Ruby 2.7 で実装される Numbered parameter っぽい機能をピュアRuby で実装してみたいと思います。
またこの記事の実装は以下の記事を参考にして書いています。

4年以上前にこういうのが書かれていたのすごい。

Numbered parameter とは

Numbered parameter、略してナンパラです。
ナンパラは『暗黙的にブロックの引数を参照する構文』になります。
通常ブロックで引数を受け取る場合、仮引数を定義して受け取ります。

# it という名前の仮引数を定義して、それで引数を参照する
[1, 2, 3].map { |it| it.to_s + it.to_s } # => ["11", "22", "33"]

ナンパラでは仮引数を定義するのではなくて _1 という記号で『暗黙的に第一引数を参照する』事が出来るようになります。

# ナンパラを使うと仮引数を定義する事なく引数が参照できる
# _1 は第一引数を参照する
[1, 2, 3].map { _1.to_s + _1.to_s } # => ["11", "22", "33"]

ナンパラは _1 ~ _9 を使用することが出来ます。
これを利用することでより簡潔に Ruby のコードを記述する事が出来ます。

# ナンパラを使わない難解な Ruby のコード
%w(homu mami mado).map(&:upcase)
%w(homu mami mado).each(&method(:puts))
%w(homu mami mado).map(&"name is ".method(:+))


# ナンパラを使った簡潔な Ruby のコード
%w(homu mami mado).map { _1.upcase }
%w(homu mami mado).each { puts _1 }
%w(homu mami mado).map { "name is " + _1 }

今回はこの _1 をピュア Ruby でも実装してみたいと思います。

ちなみに _1 という名前は Ruby 2.6 現在でも変数名やメソッド名などで使用することが可能です。

# 変数名として定義できる
_1 = 42
_2 = _1 + _1
p _2   # => 84

# メソッド名として定義できる
def _3
  "three"
end

p _3   # => "three"

また、上記のコードは Ruby 2.7 でも引き続き動作します。
ただし、Ruby 2.7 から変数名に _1 を使用した場合は警告が出るようになったので注意して下さい。

# warning: `_1' is used as numbered parameter
_1 = 42

ちなみに本記事で書かれている Ruby のコードは Ruby 2.7 では意図する動作はしないので注意して下さい。

動作イメージ

まず簡単な動作イメージを考えます。

class X
  def triple(n, &block)
    block.call(n, n, n)
  end
end

x = X.new
p x.triple(42) { _1.to_s + _2.to_s + _3.to_s }
# => "424242"

ナンパラと同様にブロック内で _1 などを参照することを想定しています。
ポイントとしては、

  • _1 をどこで定義するのか
  • ブロックの中身を評価するコンテキストはどこになるのか
  • それをどうやって既存のメソッドに適用させるか

あたりでしょうか。
では、1つずつ実装していきましょう。

_1 を参照するためのクラスを定義する

最初にナンパラの要である _1 を定義してみましょう。
これは次のようなクラスとして定義しておきます。

module Nanpara
  class Args
    def initialize(*args)
      @args = args
    end

    def _1
      @args[0]
    end

    def _2
      @args[1]
    end

    def _3
      @args[2]
    end
  end
end

# Args.new にメソッドの引数を渡す想定
nanpara = Nanpara::Args.new("homu", "mami", "mado")

# new に渡した引数をそのまま返す
p nanpara._1    # => "homu"
p nanpara._2    # => "mami"
p nanpara._3    # => "mado"

これ自体はとてもシンプルですね。
次はこの _1 というメソッドをナンパラのように呼び出してみます。

#instance_exec を利用してブロックのコンテキストを切り替える

Ruby には #instance_exec というとても便利なメソッドが定義されています。
これは『レシーバを self としてブロックを実行する』というメソッドになります。
どういうことかよくわかりませんね。
実際の使用例を見てみましょう。

# instance_exec に渡したブロックないは "hoge" のコンテキストとして実行される
"hoge".instance_exec {
  # ここの self は "hoge" になる
  p self     # => "hoge"
  # レシーバがない場合は "hoge" のメソッドを参照する
  p length   # => 4
}

こんな感じで『"hoge" のコンテキストでブロックを実行する事』が出来ます。
この #instance_exec と先程定義した Nanpara::Args クラスを組み合わせることでナンパラのようなブロックを実行する事が出来ます。

module Nanpara
  class Args
    def initialize(*args)
      @args = args
    end

    def _1
      @args[0]
    end

    def _2
      @args[1]
    end

    def _3
      @args[2]
    end
  end
end

nanpara = Nanpara::Args.new("homu", "mami", "mado")

# nanpara のコンテキストでブロックを実行する
nanpara.instance_exec {
  # Nanpara のコンテキストとして実行される
  p self
  # => #<Nanpara:0x000055f8cd6ddb68 @args=["homu", "mami", "mado"]>

  # self を付けないで _1 を呼び出す事が出来る!!
  p _1 + _2 + _3
  # => "homumamimado"
}


# 特定の proc で _1 を使いたい場合、Nanpara::Args を経由して呼び出す必要がある
twice = proc { _1 + _1 }
args = ["homu"]

p Nanpara::Args.new(*args).instance_exec(&twice)
# => "homuhomu"

もうナンパラじゃん!!!
と、いう感じでブロック内でナンパラっぽい構文を書くことが出来るようになります。
やったー!!!
さて、ここからちょっとコードを整理してきます。

_1 を動的に定義する

現状は _1 ~ _3 までを一つずつ定義しています。
Ruby では動的にメソッドを定義することが出来るので _1 ~ _9 まで動的にメソッドを定義するようにします。

module Nanpara
  class Args
    def initialize(*args)
      @args = args
    end

    # define_method を使って動的に _1 ~ _9 までメソッド定義する
    (1..10).each { |n|
      define_method(:"_#{n}") {
        @args[n-1]
      }
    }
  end
end

だいぶコンパクトになりましたね!

Proc にヘルパメソッドを定義する

毎回 Nanpara::Args.new(*args).instance_exec(&block) みたいに呼び出すのはちょっとつらいですね。
Proc に新しいメソッドを定義してもう少し使いやすくしてみましょう。
当然 Refinements を使ってメソッドを定義します。
Refinements についてもっと知りたい方は去年書いた記事を読んで下さい!

Refinements を使用して Proc を拡張すると以下のようになります。

module Nanpara
  class Args
    def initialize(*args)
      @args = args
    end

    (1..10).each { |n|
      define_method(:"_#{n}") {
        @args[n-1]
      }
    }
  end

  # Proc にメソッドを追加する
  # 特定のコンテキストでのみ使いたいの Refinements で定義する
  refine Proc do
    # 自身のブロック内で _1 を参照できるような Proc にして返す
    def nanparable
      # ブロックに仮引数が定義されない場合のみナンパラが使えるようにする
      tap { break proc { |*args| ::nanpara::args.new(*args).instance_exec(&self) } if parameters.empty? }
    end
  end
end

# Proc#nanparable を使えるように宣言
using Nanpara

# proc 内で _1 が使えるような proc に変換して返す
nanpara = proc { _1 + _2 + _3 }.nanparable

# あとは普通に呼び出すだけ
p nanpara.call("homu", "mami", "mado")
# => "homumamimado"


# Proc#nanparable をブロック引数に渡すことで
# 既存のメソッドに対しても _1 を使用することが出来る
p (1..10).map(&proc { _1 + _1 }.nanparable)
# => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

これでだいぶ使い勝手がよくなりましたね!
ここから既存のメソッドで _1 が使えるように拡張できるような仕組みを考えていきたいと思います。

prepend を使ってメソッドをラップする

元々のやりたいことは以下のように既存のメソッドで _1 を使いたいことでした。

class X
  def triple(n, &block)
    block.call(n, n, n)
  end
end

x = X.new
p x.triple(42) { _1.to_s + _2.to_s + _3.to_s }
# => "424242"

X#triple というメソッドを拡張して _1 を使ったブロックを評価できるようにする事を考えてみましょう。
ここで必要なのは、

  • X#triple というメソッドを上書きする
  • 上書きしたメソッドで Proc#nanparable を呼び出す

の 2点です。
と、いうわけでこの 2点を実装すると以下のようになります。

module Nanpara
  # 省略
end

class X
  def triple(n, &block)
    block.call(n, n, n)
  end

  # prepend することで ↑ の triple よりも先に呼び出されるメソッドを定義できる
  prepend Module.new {
    # Proc#nanparable を使いたいので using しておく
    using Nanpara

    # この triple は X#triple よりも先に呼び出される
    def triple(n, &block)
      # super は X#triple を呼び出す
      # このタイミングで nanparable したブロックを渡すようにする
      super(n, &block.nanparable)
    end
  }
end

x = X.new

# prepend した module 無いのメソッドが先に呼び出される
# これにより既存の X#triple を変更する事なく _1 を使用することが出来る
p x.triple(42) { _1.to_s + _2.to_s + _3.to_s }
# => "424242"

Ruby では prepend を使用することで既存のメソッドを上書きすることなくメソッド呼び出しに処理をフックすることが出来ます。
こんな感じで既存のメソッドに対して _1 を使えるようにしていきます。

メソッドをナンパラ化するヘルパメソッドを定義する

先程のように prepend することで既存のメソッドをナンパラ化することができました。
では、既存のメソッドをナンパラ化するようなヘルパメソッドを定義してみましょう。
イメージとしては以下のような感じです。

class X
  using Nanpara

  def triple(n, &block)
    block.call(n, n, n)
  end

  # use_numbered_paramters にメソッド名を渡すとそのメソッドがナンパラ化する
  use_numbered_paramters :triple
end

クラス内で使用できるメソッドを定義する場合は Module クラスのインスタンスメソッドを定義します。
今回も Refinements を使って Module クラスに #use_numbered_paramters というメソッドを追加します。

module Nanpara
  class Args
    def initialize(*args)
      @args = args
    end

    (1..10).each { |n|
      define_method(:"_#{n}") {
        @args[n-1]
      }
    }
  end

  refine Proc do
    def nanparable
      tap { break proc { |*args| ::Nanpara::Args.new(*args).instance_exec(&self) } if parameters.empty? }
    end
  end

  # Module に対してインスタンスメソッドを定義する Refinements
  refine Module do
    using Nanpara

    # 引数のメソッドをナンパラ化する
    def use_numbered_paramters(*method_names)
      # 引数無い場合は全メソッドを対象とする
      method_names = instance_methods if method_names.empty?

      # メソッド内で prepend するぞ!
      prepend Module.new { |mod|
        method_names.each { |name|
          define_method(name) { |*args, &block|
            super(*args, &block&.nanparable)
          }
        }
      }
    end
  end
end

class X
  using Nanpara

  def triple(n, &block)
    block.call(n, n, n)
  end

  # use_numbered_paramters にメソッド名を渡すとそのメソッドがナンパラ化する
  use_numbered_paramters :triple
end

x = X.new

# X#triple で _1 を使用することが出来る
p x.triple(42) { _1.to_s + _2.to_s + _3.to_s }
# => "424242"

ちょっと複雑ですがこんな感じで実装する事が出来ます。
また、これを利用することで既存のクラスをシュッとナンパラ化することが出来ます!!!

# Array のメソッドをナンパラ化する!
class Array
  using Nanpara
  use_numbered_paramters
end

p (1..10).to_a.shuffle.sort { _2 <=> _1 }
# => [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
p (1..10).to_a.inject(100) { _1 + _2 }
# => 155

もうナンパラじゃん。

もっと簡単にクラスをナンパラ化できるようにする

ほぼ完成なんですが、最後にもっと簡単に安全にクラスをナンパラ化する仕組みを考えてみましょう。
先程の例であれば以下のようにして任意のクラスをナンパラ化することが出来ます。

class Array
  using Nanpara
  use_numbered_paramters
end

しかし、この場合は Array を直接書き換えてしまっているので Array を参照している場所全てに影響を及ぼしてしまいます。
これはよくないですね。
そこで以下のような仕組みで『特定のコンテキストでのみナンパラ化』してみます。

module Nanpara
  class Args
    def initialize(*args)
      @args = args
    end

    (1..10).each { |n|
      define_method(:"_#{n}") {
        @args[n-1]
      }
    }
  end

  refine Proc do
    def nanparable
      tap { break proc { |*args| ::Nanpara::Args.new(*args).instance_exec(&self) } if parameters.empty? }
    end
  end

  refine Module do
    using Nanpara
    def use_numbered_paramters(*method_names)
      method_names = instance_methods if method_names.empty?
      prepend Module.new { |mod|
        method_names.each { |name|
          define_method(name) { |*args, &block|
            super(*args, &block&.nanparable)
          }
        }
      }
    end
  end

  using Nanpara
  def self.forward_use_numbered_paramters(mod)
    mod.use_numbered_paramters
  end

  def self.const_missing(klass_name)
    klass = Object.const_get(klass_name)
    ::Module.new do
      refine klass do
        # 本来であれば use_numbered_paramters を直接呼び出したいが Refinements のバグで呼び出せない…
        # Ruby 2.7 だとこの挙動は修正されていた
        # use_numbered_paramters

        # 致し方なくメソッドを経由して呼び出す
        Nanpara.forward_use_numbered_paramters(self)
      end
      # クラスメソッドにも反映させる
      refine klass.singleton_class do
        Nanpara.forward_use_numbered_paramters(self)
      end
    end
  end
end

class X
  def triple(n, &block)
    block.call(n, n, n)
  end
end

# Nanpara::クラス名 で任意のクラスをナンパラ化する
using Nanpara::X

x = X.new

# X#triple で _1 を使用することが出来る
p x.triple(42) { _1.to_s + _2.to_s + _3.to_s }
# => "424242"

上の実装であれば using Nanpara::クラス名 で任意のクラスをナンパラ化することが出来ます。
また、Refinements を利用してナンパラ化しているので次のように『任意のコンテキストでのみ』ナンパラ化することが出来ます。

module ArrayEx
  # このコンテキストでのみ Array をナンパラ化する
  using Nanpara::Array

  p (1..10).to_a.shuffle.sort { _2 <=> _1 }
  # => [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
  p (1..10).to_a.inject(100) { _1 + _2 }
  # => 155
end

# 上のコンテキスト以外では Array はナンパラ化しないので最小限の副作用で抑えられる
# Error: undefined local variable or method `_2' for main:Object (NameError)
p (1..10).to_a.shuffle.sort { _2 <=> _1 }

もう最強じゃん…。

最終的に出来上がったもの

最終的に出来上がったものがこちらになります。

module Nanpara
  class Args
    def initialize(*args)
      @args = args
    end

    (1..10).each { |n|
      define_method(:"_#{n}") {
        @args[n-1]
      }
    }
  end

  refine Proc do
    def nanparable
      tap { break proc { |*args| ::Nanpara::Args.new(*args).instance_exec(&self) } if parameters.empty? }
    end
  end

  refine Module do
    using Nanpara
    def use_numbered_paramters(*method_names)
      method_names = instance_methods if method_names.empty?
      prepend Module.new { |mod|
        method_names.each { |name|
          define_method(name) { |*args, &block|
            super(*args, &block&.nanparable)
          }
        }
      }
    end
  end

  using Nanpara
  def self.forward_use_numbered_paramters(mod)
    mod.use_numbered_paramters
  end

  def self.const_missing(klass_name)
    klass = Object.const_get(klass_name)
    ::Module.new do
      refine klass do
        Nanpara.forward_use_numbered_paramters(self)
      end
      refine klass.singleton_class do
        Nanpara.forward_use_numbered_paramters(self)
      end
    end
  end
end

ちょうど 50行でナンパラっぽいものを実装することが出来ました。
実際は Refinements で拡張するコードも含まれているのでナンパラ自体の実装は30行ぐらいしかありません。
これだけのコードでナンパラを実装できる Ruby すごくないですか?
こういう変態的なコードを書けるのが Ruby の楽しいところですよねー。
Ruby たのしー。
みなさんもどんどん Ruby のよくわからないコードを書いていきましょう!

Ruby 2.7 のナンパラとの違い

最後に Ruby 2.7 のナンパラとの違いを説明しておきます。

Proc#parameters の戻り値

Ruby 2.7 のナンパラはパース時に _1 の検知を行っています。
なので使用している _1 を考慮した情報を返します。

p Proc.new { _1 + _2 }.parameters
# => [[:opt, :_1], [:opt, :_2]]

しかし、今回作成したものは動的に処理しているため Proc#parameters で取得することは出来ません。

using Nanpara::Proc

p Proc.new { _1 + _2 }.parameters
# => [[:rest, :args]]

配列を渡した時の違い

Ruby 2.7 のナンパラは _1 を使用したら |_1_1_2 を使用したら |_1, _2| という風に受け取ります。
なので配列を渡した場合に『_2 以上を使用してれば配列を展開して』受け取りますた。

p Proc.new { [_1] }.call [1, 2]      # => [[1, 2]]
p Proc.new { [_1, _2] }.call [1, 2]  # => [1, 2]

しかし、今回作成したものは可変長引数として受け取っているためです。

using Nanpara::Proc
p Proc.new { [_1] }.call [1, 2]      # => [[1, 2]]
p Proc.new { [_1, _2] }.call [1, 2]  # => [[1, 2], nil]

まとめ

と、いう感じで ピュア Ruby で Ruby 2.7 の Numbered parameter っぽい機能を実装してみました。
完璧に同じもの!とは言えませんが 50行ぐらいの実装でそれっぽいものをつくることが出来ました。
Ruby だとコードレベルでこういうことを実現することができるのがとても面白いですね。
Ruby たのしー。

ちなみにこの実装を書いたのは 2回目になり、1回目は gem として公開してあります。

こっちは 3年以上前に書いたものになります。
基本的な使い方は今回書いたコードとだいたい同じなんですが実装は結構違っているので気になる方は実装を読んでみると面白いかもしれません。
ちなみにこの gem だと _1 以外にも _yield_self_receiver などといった値にも参照することが出来ます。
そのため『自身を呼び出した再帰処理』を記述する事が出来ます。

# 再帰
fact = proc { _1 == 1 ? 1 : _1 * _self.(_1 - 1); }.use_args
p fact.call 5
# => 120

p [1, 2, 3].use_args.map { _1 + _receiver.size }
# => [4, 5, 6]

これは Ruby 2.7 の Numbered parameter にはない強みですね。
supermomonga さんが書いた元ネタの記事は 4年以上前に書かれており、その時は Numbered parameter の話は全く出ていなかったんですが、それが Ruby 2.7 で実装されるのは感慨深いものがありますね。
Ruby 2.7 のナンパラは簡単そうにみえて実は結構癖が強いので実際使われてみてどうなるのかは気になりますね。
もう 12月で Ruby 2.7 のリリースまでもうすぐですが果たして無事にナンパラをリリースする事が出来るのか…。
と、言う感じで今年の Ruby Advent Calendar を書いてみました。

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

【Rails】テーブル同士を紐付けする手順を解説していく!

はじめに

Railsの学習のアウトプットとして簡単な掲示板を作成している際に、「データの紐付け」にて躓いたので
「紐付け」を行う目的とその手順について、自分用にまとめてみようと思います!

※「ログイン機能」が実装してある事を前提に解説していきます。

紐付けとは何ぞや

テーブル同士を関連付ける事で、テーブル同士でデータを取得できるようにする作業。

紐付けをする目的

掲示板作成を例として、上述した二つのテーブルを
・「投稿用のテーブル(postsテーブル)」
・「ユーザーのテーブル(usersテーブル)」
に置き換えることにする。

例えば
投稿テーブルには、投稿内容のデータ(カラム名をcontentとする)が保存され
ユーザーテーブルには、ユーザーの名前のデータ(カラム名をnameとする)が保存されているとします。
そうすると以下のように表せます。

postsテーブル

id content
1 タピオカなう!
2 学校めんどい……

usersテーブル

id name
1 田中
2 鈴木

しかしながら上記2つのテーブルは別物であるため
投稿内容を見ただけでは、誰がどんな投稿したのかという判別がつきません。

そうなると今度は、誰がその投稿をしたのかわかるようにさせたい訳です。

要するにデータの紐付けを行う目的とはこの例で言えば
誰がどの投稿をしたのかを区別することになります。

紐付けを行う手順

それでは早速、掲示板作成を例として実際に紐付けを行ってみます。
しかし、いきなり紐付けしようとしても、何をどうすれば良いのかサッパリだと思いますから
何をどうしたら良いのか、その思考手順を挙げつつ実装を行っていきます。

1.まず、実装したい機能を挙げてみる

 ➡︎投稿ごとにユーザーを区別したい。

2.その機能を明確にしてみる

 ➡︎投稿ごとに、誰がその投稿をしたのかわかるように、ユーザー名を表示させる。

3.Railsにおけるデータのやり取りの仕組みを思い出す

 ➡︎ブラウザで何かしらの情報を表示させるためには、該当のアクション内で、データベースからデータを取得するための記述をする必要がある。
 ➡︎今回は「投稿に対して」、その投稿を行った「ユーザーの情報を」表示させたいので、投稿用のアクション内で、ユーザーのデータを取得すればいい。

posts_controller.rb
 def #(投稿表示用のアクション名)
  @post = Post.find_by(id: params[:id])

   #ユーザーのデータを取得する処理

 end

4.ユーザーのデータを取得する(?)

 ➡︎それでは早速、ユーザーのデータを取得してみます。

posts_controller.rb
 def #(投稿表示用のアクション名)
   @post = Post.find_by(id: params[:id])
   @user = User.find_by(id: @post.X) #ユーザーのデータを取得する処理
 end

 ➡︎「X」って何だよ…と思ったかと思いますが、何者でもない仮のXだと思ってください。
 ➡︎何故仮のXを置いているかと言うと、今のままだと
Userモデルのidと一致可能な、Postモデルのデータが存在しないためです。
 ➡︎なのでこのXの部分に入れるためのカラムを、新しくpostsテーブルに作成する必要があります。こうして紐付けが始まります!

5.postsテーブルにカラムを追加

 ➡︎「Userモデルのidと一致させるためのid」と言う意味で、「user_id」と言うカラム名にします。
 ➡︎新しくカラムを追加するためには、「rails g migration」コマンドを実行してあげる必要があります。
 ➡︎今回はuser_idカラムを、Postモデルに追加するので、以下のようにコマンドを実行します

ターミナル
 $ rails g migration AddUserIdToPost user_id:integer

 ➡︎「db/migrate」ディレクトリ内に、「(作成した日付の数字羅列)add_user_id_to_post.rb」ファイルが追加されている事を確認。
 ➡︎これで、Railsのアプリケーションフォルダ内にはファイルが作成されたので、この変更をデータベースに反映させます。

ターミナル
 $ rails db:migrate

 ➡︎上記のようにコマンドを実行したら、ターミナルで「rails dbconsole」コマンドを実行して、postsテーブルに「user_id」カラムが追加されている事を確認してください。

postsテーブル

id content user_id
1 タピオカなう!
2 学校めんどい……

6.user_idカラムに、ユーザーのidを取得させる

 ➡︎もう一度、先ほどの投稿表示用のアクションを確認してみます。

posts_controller.rb
 def #(投稿表示用のアクション名)
   @post = Post.find_by(id: params[:id])
   @user = User.find_by(id: @post.user_id) #ユーザーのデータを取得する処理
 end

 ➡︎新たにカラムを作成したので、仮のXを「user_id」に変更しました。
 ➡︎ただし、まだこのままではuser_idカラムは空のままなので、参照するためのidがありません。
 ➡︎なので今から、user_idカラムに、その投稿をしたユーザーのidを取得させるための記述を行ます。
 ➡︎どうやって取得させるかと言うと、新規投稿アクション内の、newメソッドの引数にuser_idを渡し、user_idに「ログインしているユーザーのid」が保存されるようにします。

posts_controller.rb
 def #(新規投稿アクション名)
   @post = Post.new(
    content: params[:content],
    user_id: @current_user.id #ログインユーザーのidを取得して保存
   )
   #新規投稿保存後の処理 
 end

 ➡︎これにより、これ以降に新規投稿が行われる度に、ログインしているユーザーのidが保存されるようになります。

postsテーブル

id content user_id
1 タピオカなう!
2 学校めんどい……
3 納豆好き @current_user.id
4 ポンポンポン @current_user.id

 ➡︎idが1のユーザーが新規投稿を行えば、その投稿のuser_idには1が入るし、2のユーザーが新規投稿を行えば2が入ります。
 ➡︎これで、ユーザーのidを取得させることができました。

7.ビューで表示

 ➡︎新規投稿するごとにログインユーザーのidが取得されるため、以下のように呼び出すと、「その投稿を行ったユーザーのid」と一致する「Userモデル内のid」を検索して取得してくれるようになります。

posts_controller.rb
 def #(投稿表示用のアクション名)
   @post = Post.find_by(id: params[:id])
   @user = User.find_by(id: @post.user_id) #ユーザーのデータを取得する処理
 end

 ➡︎これにより紐付けは完了したので、投稿詳細ページで表示していきます。

show.html.erb
 <%= @user.name %>
 <%= @post.content %>

 ➡︎後はこれにCSSなどでスタイリングしてあげれば、投稿に対してユーザー名を表示できるようになるかと思います!

補足にもならない補足

本来ならば投稿詳細ページにだけでなく、別のページの投稿に対してもユーザーの情報を紐付けする必要があると思います。
そうした場合、他のアクションでユーザーの情報を取得する記述をすることになるため、重複することになります。

同じ記述をするのは好ましくないため、ユーザーの情報を一挙に取得するためのメソッド(インスタンスメソッド)を定義する必要があるのですが
インスタンスメソッドに関する解説は、また次回したいと思います。

まとめ

最後に、投稿とユーザーについての紐付けを行う手順をもう一度確認してみましょう。

1.まず、実装したい機能を挙げてみる

2.その機能を明確にしてみる

3.Railsにおけるデータのやり取りの仕組みを思い出す

4.該当アクション内に、ユーザーのデータを取得する処理を記述してみる(仮)

5.postsテーブルにカラムを追加

6.user_idカラムに、ユーザーのidを取得させる

7.ビューで表示

です!

最後に

Railsにつきましては、学習を始めて2週間の初学者も初学者ですので
Railsにおけるデータのやり取りについて慣れることが先決と考え、このように遠回りするような理解の仕方を取っています。
おかしな点などありましたら、その都度仰って頂けるととても幸いです。

参考

Railsでデータベースカラムの追加を行う方法
Progate

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

Rubyでエミュレータをつくりたい

はじめに

Rubyでx86 CPUエミュレータを作る話をします。
エミュレータ作成を通し、
CPUの動き、機械語やアセンブリ言語など、低レイヤーの知識をつけることを目的としています。
今回はまともなプログラムは動かせません。
"Hello, World"もできません。

参考書籍

自作エミュレータで学ぶx86アーキテクチャ

前提知識

レジスタ

レジスタは、CPUに組み込まれている作業領域です。
汎用レジスタと特殊レジスタがあります。

(ESP、EBPを汎用レジスタって言う本があったり、
特殊レジスタって言う本があったり結局どっちなの?という気持ちになる。)

汎用レジスタ

x86環境下では汎用レジスタは以下の8つがあります。
1. EAX
2. ECX
3. EDX
4. EBX
5. ESP
6. EBP
7. ESI
8. EDI

アセンブリ言語で、なにかデータを指定する箇所では、
汎用レジスタは大体指定することができます。

演算結果を、EAXやEDXに暗黙的に入れられることもあります。
例えば、div ecxとすると、
EAX ÷ ECXの商がEAXに、余がEDXに入ります。

特殊レジスタ

EIPとEFLAGSがあります。
EIPには次に実行するアセンブリ命令のアドレスが入っています。
EFLAGSは条件判定に使うようなフラグが入っています。

アセンブリ言語

機械語と一対一で対応している言語です。

実装

処理の方針

1バイトのオペコードを読み込み、その値によって処理を振り分けていきます。

mov命令

mov命令のオペコードは0xB8 から 0xBFまでです。
オペコード自身に、レジスタの指定が含まれています。
4バイトの値を、指定したレジスタにコピーします。

short jump命令

short jump命令のオペコードは0xEBです。
1バイトの符号付き整数を取り、eipに加算します。
つまり、-127から+128の範囲内を飛ぶことができます。

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

accepts_nested_attributes_forを使わずに複数リソースを同時登録する(クイズアプリを例に)

想定読者

Railsを使ってポートフォリオを作成している方
特にクイズアプリを作成している方

どういうときの話

私はクイズアプリを作ってるときにこの問題に当たりました。
一つのフォーム画面で、
①問題文
②選択肢1
③選択肢2
④選択肢3
⑤選択肢4
を同時に登録させたいとします。

で、モデルはこんな感じです。

app/models/question.rb
class Question < ApplicationRecord
  has_many :choices, dependent: :destroy
end
app/models/choice.rb
class Choice < ApplicationRecord
  belongs_to :question
end
db/migrate/時間_create_choices.rb
class CreateChoices < ActiveRecord::Migration[5.2]
  def change
    create_table :choices do |t|
      t.references :question, foreign_key: true
      t.string :content, null: false
      t.boolean :is_answer

      t.timestamps
    end
  end
end
  • 「問題 has many 選択肢s」です。
  • 選択肢テーブルはis_answerという「正答か誤答か」というカラムを持っています。

よって単純なform_withとモデルでは実装できません。フォームに合わせてDB設計を変える、みたいなのは本末転倒です。(私の記事で恐縮ですが、クイズアプリのDB設計に関して、以下記事を書きました)
https://qiita.com/kumackey/items/7ccbc949458bd0af22bd

他にも、以下の例が思いつきます。

  • 就活サイトのユーザ登録画面で、has manyな経歴を同時に登録する
  • SNSのポスト投稿画面で、投稿自体とタグ複数を同時登録する
  • レシピサイトで、レシピ名と、複数の手順を同時に登録する

accepts_nested_attributes_forではダメ?

非推奨らしいです。
実は私はaccepts_nested_attributes_forに関しては逆に詳しくないため、詳細は以下URL等をご覧いただければと思います。
https://moneyforward.com/engineers_blog/2018/12/15/formobject/
https://tech.recruit-mp.co.jp/server-side/rails-development-policy/
https://tech.libinc.co.jp/entry/2019/04/05/113000

フォームオブジェクトを作る

app/forms/register_quiz_form
class RegisterQuizForm
  include ActiveModel::Model
  include ActiveModel::Attributes
  include ActiveModel::Validations

  attribute :question_content, :string
  attribute :correct_choice, :string
  attribute :incorrect_choice_1, :string
  attribute :incorrect_choice_2, :string
  attribute :incorrect_choice_3, :string

  validates :question_content, presence: true, length: { maximum: 140 }
  validates :correct_choice, presence: true, length: { maximum: 40 }
  validates :incorrect_choice_1, presence: true, length: { maximum: 40 }
  validates :incorrect_choice_2, length: { maximum: 40 }
  validates :incorrect_choice_3, length: { maximum: 40 }

  def save
    return false unless valid?
    question = Question.new(content: question_content)
    question.save # 問題文の登録

    choice = question.choices.build(content: correct_choice, is_answer: true)
    choice.save # 正解選択肢の保存

    question.choices.create(content: incorrect_choice_1, is_answer: false)
    question.choices.create(content: incorrect_choice_2, is_answer: false)
    question.choices.create(content: incorrect_choice_3, is_answer: false)
    # 不正解選択肢の登録
  end
end

フォームオブジェクト自体に知見が無い方は、以下記事をご参照いただければと思います。
https://tech.medpeer.co.jp/entry/2017/05/09/070758
https://qiita.com/kamohicokamo/items/d2ea4d71f86d99261b1a

return false unless valid?によって、ヴァリデーションが通らないときにfalseを返します。これにより、コントローラーで使用する際も、以下のような実装が可能になります。

if @register_quiz_form.save
  (ヴァリデーションが通ってsaveに成功したときの処理)
else
  (ヴァリデーションが通らずsaveに失敗したときの処理)
end

if @user.saveに近い感覚で使うことができます。

コントローラーとビュー

app/controllers/questions_controller.rb
class QuestionsController < ApplicationController
  def new
    @register_quiz_form = RegisterQuizForm.new
  end

  def create
    @register_quiz_form = RegisterQuizForm.new(create_question_params)
    if @register_quiz_form.save
      redirect_to (成功したときのパス)
    else
      render :new
    end
  end

  def create_question_params
    params.require(:register_quiz_form).permit(
      :question_content, :correct_choice, 
      :incorrect_choice_1, :incorrect_choice_2, :incorrect_choice_3)
  end
end

※ 以下Viewですが、筆者がslimに慣れているのでslimで記述しています。erbに置き換えてお読み頂けると幸いです。

app/views/questions/new.html.slim
= form_with model: @register_quiz_form, url: questions_path, local: true do |f|
  = render '(エラーメッセージ表示用のパーシャル)', object: @register_quiz_form
  .form-group
    = f.label :question_content
    = f.text_field :question_content, class: 'form-control'
  .form-group
    = f.label :correct_choice
    = f.text_field :correct_choice, class: 'form-control'
  .form-group
    = f.label :incorrect_choice_1
    = f.text_field :incorrect_choice_1, class: 'form-control'
  .form-group
    = f.label :incorrect_choice_2
    = f.text_field :incorrect_choice_2, class: 'form-control'
  .form-group
    = f.label :incorrect_choice_3
    = f.text_field :incorrect_choice_3, class: 'form-control'
  = f.submit '登録する', class: 'btn btn-raised btn-success'

ちなみにフォームブジェクトという設計は、ActiveRecordのattributesと、フォーム画面で表示させたいことが異なる、というケースのいずれでも効果を発揮します。
私はフォームオブジェクトが大好きすぎて、ポートフォリオには3つのフォームオブジェクトがあります。笑

間違い等あればご指摘頂けると嬉しいです。

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

Railsチュートリアル4章目の続き〜クラスについて〜

Rubyにおけるクラス

Rubyではあらゆるものがオブジェクトである。オブジェクトはクラスを使用し、そこからインスタンスが生成されると。正直良く分かりませんね。笑

コンストラクタ

例えばダブルクォートを使って文字列のインスタンスを作成しましたが、これは文字列のオブジェクトを暗黙で作成するリテラルコンストラクタである。

>> s = "foobar"       # ダブルクォートは実は文字列のコンストラクタ
=> "foobar"
>> s.class
=> String

上のコードでは、文字列がclassメソッドに応答しており、その文字列が所属するクラスを単に返していることがわかります。

コンストラクタとは?

オブジェクト指向のプログラミング言語で新たなオブジェクト新たなオブジェクトを生成する際に呼び出されて内容の初期化などを行う、関数、あるいはメソッドのこと。

暗黙のリテラルコンストラクタを使う代わりに、明示的に同等の名前付きコンストラクタを使うことができます。名前付きコンストラクタは、クラス名に対してnewメソッドを呼び出します。

>> s = String.new("foobar")   # 文字列の名前付きコンストラクタ
=> "foobar"
>> s.class
=> String
>> s == "foobar"
=> true

演習

1.1から10の範囲オブジェクトを生成するリテラルコンストラクタは何でしたか? (復習です)

a=1..10
=> 1..10

2.今度はRangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作ってみてください。ヒント: newメソッドに2つの引数を渡す必要があります

b = Range.new[1..10]
=> 1..10

3.比較演算子==を使って、上記2つの課題で作ったそれぞれのオブジェクトが同じであることを確認してみてください。

a==b
=>true

クラス継承

クラスについて学ぶとき、superclassメソッドを使ってクラス階層を調べてみるとよくわかります。superclassメソッドは指定することでこのクラスの一つ上のクラス何なのかを表示するメソッド。

>> s = String.new("foobar")
=> "foobar"
>> s.class                        # 変数sのクラスを調べる
=> String
>> s.class.superclass             # Stringクラスの親クラスを調べる
=> Object
>> s.class.superclass.superclass  # Ruby 1.9からBasicObjectが導入
=> BasicObject
>> s.class.superclass.superclass.superclass
=> nil

StringクラスのスーパークラスはObjectクラス
ObjectクラスのスーパークラスはBasicObjectクラス
BasicObjectクラスはスーパークラスを持たない
これはすべての Ruby のオブジェクトにおいて成り立ちます。クラス階層をたどっていくと、 Rubyにおけるすべてのクラスは最終的にスーパークラスを持たないBasicObjectクラスを継承している。
なるほど。みんな最終的にはBasicObjectクラスにつながっているんだねー。

ある単語を前からと後ろからのどちらから読んでも同じ (つまり回文になっている) ならばtrueを返すpalindrome?メソッドを作成する

>> class Word
>>   def palindrome?(string)
>>     string == string.reverse
>>   end
>> end
=> :palindrome?

このクラスとメソッドは次のように使うことができる。

>> w = Word.new              # Wordオブジェクトを作成する
=> #<Word:0x22d0b20>
>> w.palindrome?("foobar")
=> false
>> w.palindrome?("level")
=> true

もし上の例が少し不自然に思えるならば、勘が鋭いといえます。
(不自然と思えなかった自分は勘が鈍い。笑)
というのも、これはわざと不自然に書いたからだそうです。
Wordクラスは Stringクラスを継承するのが自然である。

コンソールでWordクラスを定義する。

>> class Word < String             # WordクラスはStringクラスを継承する
>>   # 文字列が回文であればtrueを返す
>>   def palindrome?
>>     self == self.reverse        # selfは文字列自身を表します
>>   end
>> end
=> :palindrome?

上のコードは継承のためのRubyの Word < String 記法である。
こうすることで、新しいpalindrome?メソッドだけではなく、Stringクラスが扱えるすべてのメソッドがWordクラスでも使えるようになる。

>> s = Word.new("level")    # 新しいWordを作成し、"level" で初期化する
=> "level"
>> s.palindrome?            # Wordが回文かどうかを調べるメソッド
=> true
>> s.length                 # WordはStringで扱える全てのメソッドを継承している
=> 5

なるほど。すごい理解できた。つまりドラゴンボールでいうところのセルみたいな感じかな。魔貫光殺砲もかめはめ波も使えるみたいな。

上のコードはselfキーワードを用いることで、単語の文字を逆順にしたものが元の単語と同じであるかどうかのチェックを、Wordクラスの中から自分自身が持つ単語にアクセスすることで行なっている。Wordクラスの中では、selfはオブジェクト自身を指します。

self == self.reverse
単語が回文であるかどうかを確認できるということです17。なお、Stringクラスの内部では、メソッドや属性を呼び出すときのself.も省略可能です。

self == reverse
といった省略記法でも、うまく動く。

ユーザークラス

User クラスを最初から作成する。

アプリケーションのルートディレクトリにexample_user.rbファイルを作成

example_userで使うコード

class User
  attr_accessor :name, :email

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

  def formatted_email
    "#{@name} <#{@email}>"
  end
end
attr_accessor :name, :email

ユーザー名とメールアドレス (属性: attribute) に対応するアクセサー (accessor) をそれぞれ作成します。アクセサーを作成すると、そのデータを取り出すメソッドと、データに代入するメソッドをそれぞれ定義してくれる。

具体的には、この行を実行したことにより、インスタンス変数@nameとインスタンス変数@emailにアクセスするためのメソッドが用意されます。
Railsでは、インスタンス変数をコントローラ内で宣言するだけでビューで使えるようになる、といった点に主な利用価値がある。

initializeは、Rubyの特殊なメソッドである。
User.newを実行すると自動的に呼び出されるメソッドです。
この場合のinitializeメソッドは、次のようにattributesという引数を1つ取ります。

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

上のコードで、attributes変数は空のハッシュをデフォルトの値として持つため、名前やメールアドレスのないユーザーを作ることができます (4.3.3を思い出してください。存在しないキーに対してハッシュはnilを返すので、:nameキーがなければattributes[:name]はnilになり、同じことがattributes[:email]にも言える)。

最後に、formatted_emailメソッドを定義する。このメソッドは、文字列の式展開を利用して、@name@emailに割り当てられた値をユーザーのメールアドレスとして構成します。

  def formatted_email
    "#{@name} <#{@email}>"
  end

@ 記号によって示されているとおり、@name@emailは両方ともインスタンス変数なので、自動的にformatted_emailメソッドで使えるようになります。

Railsコンソールを起動し、example_userのコードをrequireして、自作したクラスを試しに使ってみましょう。

>> require './example_user'     # example_userのコードを読み込む方法
=> true
>> example = User.new
=> #<User:0x224ceec @email=nil, @name=nil>
>> example.name                 # attributes[:name]は存在しないのでnil
=> nil
>> example.name = "Example User"           # 名前を代入する
=> "Example User"
>> example.email = "user@example.com"      # メールアドレスを代入する
=> "user@example.com"
>> example.formatted_email
=> "Example User <user@example.com>"

上のコードで、requireのパスにある’.’は、Unixの “カレントディレクトリ” (現在のディレクトリ) を表し、’./example_user’というパスは、カレントディレクトリからの相対パスでexample_userファイルを探すようにRubyに指示します。次のコードでは空のexample_userを作成します。次に、対応する属性にそれぞれ手動で値を代入することで、名前とメールアドレスを与えます (リスト 4.17でattr_accessorを使っているので、これで代入できるようになります)。次のコードは、

example.name = "Example User"
@name変数に"Example User"という値を設定します。同様にemail属性にも値を設定します。これらの値はformatted_emailメソッドで使われます。

最後のハッシュ引数の波カッコを省略できることを説明しました。それと同じ要領でinitializeメソッドにハッシュを渡すことで、属性が定義済みの他のユーザを作成することができます。

演習

1.Userクラスで定義されているname属性を修正して、first_name属性とlast_name属性に分割してみましょう。また、それらの属性を使って "Michael Hartl" といった文字列を返すfull_nameメソッドを定義してみてください。最後に、formatted_emailメソッドのnameの部分を、full_nameに置き換えてみましょう (元々の結果と同じになっていれば成功です)

class User
 attr_accessor :first_name,:last_name, :email

 def initialize(attributes = {})
  @first_name = attributes[:first_name]
  @last_name = attributes[:last_name]
  @email = attributes[:email]
 end

 def full_name
  @full_name = "#{@first_name} #{@last_name}"
 end

 def formatted_email
  "#{@full_name} <#{@email}>"
 end
end

2."Hartl, Michael" といったフォーマット (苗字と名前がカンマ+半角スペースで区切られている文字列) で返すalphabetical_nameメソッドを定義してみましょう。
full_name.splitとalphabetical_name.split(', ').reverseの結果を比較し、同じ結果になるかどうか確認してみましょう。

[example_user.rb]

class User
 attr_accessor :first_name,:last_name, :email

 def initialize(attributes = {})
  @first_name = attributes[:first_name]
  @last_name = attributes[:last_name]
  @email = attributes[:email]
 end

 def full_name
  @full_name = "#{@first_name} #{@last_name}"
 end

 def alphabetical_name
  @alphabetical_name = "#{@last_name}, #{@first_name}"
 end

 def formatted_email
  "#{@full_name} <#{@email}>"
 end
end
[console]

>>user.full_name.split
 => ["Michael", "Hartl"] 

>>user.alphabetical_name.split(', ').reverse
 => ["Michael", "Hartl"] 

>>user.full_name.split == user.alphabetical_name.split(', ').reverse              
 => true 

今日はここまで。最後の方は結構難しかったです。
ちょっと4章に時間かけすぎましたかね。どんどん行きます。

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

Ruby on Railsの開発環境(仮想環境)の構築方法

はじめに

現在のバージョン:
macOS Catalina 10.15.3

Ruby on Railsで環境構築を実施してみました。自身のPC(ローカル)に直接環境を構築すると現在のPCの設定に影響を与えてしまう可能性がある為、仮想環境を作りました。

仮想環境について

仮想環境とは自身のPCとは別のPCを仮想的に作ることです。(1台のPCで複数のPCを扱えるイメージ)
仮想環境は不要になればすぐに削除が可能です。

仮想環境を構築

VirtualBoxをインストール

VirtualBox(バーチャルボックス)をインストールして、仮想環境を構築。
macOS:VirtualBox(6.0.14)ダウンロード
ダウンロード後開くと下記の画面になります。
スクリーンショット 2020-03-25 15.53.45.png
「1」をダブルクリックして、インストール実施完了後「Applications」フォルダに追加されてます。スクリーンショット 2020-03-25 15.59.51.png

インストール後以下のコマンドでバージョンが表示されるか確認。
$ vagrant box list
centos/7 (virtualbox, 1905.1)

Vagrantをインストール

Vagrant(ベイグラント)をインストールして、仮想環境を管理・構築をします。Windows、Mac OS X、Linuxで動作します。

macOS:Vagrant(64bit)ダウンロード

インストール後以下のコマンドでバージョンが表示されるか確認。
$ vagrant --version
Vagrant 2.2.4

※macOSのバージョンによっては、以下の設定が必要になります。
アップルメニューから「システム環境設定」→「セキュリティとプライバシー」アイコンクリック→「一般」タブクリック→「開発元を確認できないため、開けませんでした」の「このまま開く」をクリックする。

vagrant-vbguestプラグインをインストール

「vagrant-vbguest」は自分の環境と仮想環境のバージョンを自動で合わせてくれるVagrantのプラグインです。
Vagrant起動の準備をする際に、「vagrant-vbguest」プラグインのインストールがあると面倒な作業を肩代わりしてくれます。

・インストール方法
ターミナルで以下コードの入力をする。
$ vagrant plugin install vagrant-vbguest

参考にコマンド一覧も載せておきます。
スクリーンショット 2020-03-25 16.33.53.png

Vagrantfileの作成

Vagrantfileの作成をしますが、その前に作業ディレクトリーの作成をします。

ターミナルでデスクトップに移動
$ cd Desktop
フォルダをデスクトップに作成する
$ mkdir フォルダ名
任意のフォルダに移動する
$ cd フォルダ名
フォルダ内に「vagrant」という名のフォルダを作成する
$ mkdir vagrant
vagrantフォルダに移動する
$ cd vagrant

ここからVagrantfileを作成していきます。
Vagrantfileを作成
$ vagrant init centos/7
コマンド実行後、Vagrantfileを作成されております。

Vagrant起動

Vagrantの起動コマンド
$ vagrant up
初回の起動は、30分から3時間はかかります。

以下のように表示がされると完了です。
スクリーンショット 2020-03-25 17.31.31.png

完了後、以下のコマンドでシャットダウンして導入が完了です。
$ vagrant halt
※再度起動する際はVagrantの起動コマンド
$ vagrant upを実行してください。

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

wicked_pdfを導入するまで2020

環境

  • OS
    • 開発PC: macOS Mojave
    • サーバー: Amazon Linux
  • ruby 2.6.1
  • Rails 5.2.2

初期段階

ミニマムで実装すると以下のようになります。
また、layoutで指定するファイルはapp/views/layoutsに配置する必要があります。

ディレクトリ構成
src
├── app
│   ├── assets
│   │   └── stylesheets
│   │       └── sample
│   │           └── sample.sass
│   ├── controller
│   │   └── sample
│   │       └── sample_controller.rb
│   └── views
│       ├── sample
│       │   ├── sample.html.slim
│       └── layouts
│           └── pdf_for_sample.html.slim
└── Gemfile 
sample_controller.rb
def generate_pdf
  respond_to do |format|
    format.html { redirect_to sample_generate_pdf_path(format: :pdf)}
    format.pdf do
      if params[:debug].present?
        render pdf: 'sample',
               title: 'sample.pdf',
               encoding: 'UTF-8',
               layout: 'pdf_for_sample.html.slim',
               template: "sample/sample.html.slim",
               show_as_html: params[:debug].present?
      else
        render pdf: 'sample',
               title: 'sample.pdf',
               encoding: 'UTF-8',
               layout: 'pdf_for_sample.html.slim',
               template: "sample/sample.html.slim",
               show_as_html: params[:debug].present?
      end
    end
  end
end
pdf_for_sample.html.slim
 doctype html 
 html[lang="ja" xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" dir="ltr"]
   head 
     / メインのcss 
     = wicked_pdf_stylesheet_link_tag 'sample/sample', media: 'all'
   body 
     .sample
       .sample-text コンテンツ
sample.html.slim
# 空っぽ
Gemfile
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'

layoutとtemplateを使い分ける

最初は違いがわかりにくかったのですが、layoutのファイルは共通、templateは個別のHTMLを描写するものとし、layoutからtemplateを呼び出す作りにするのが良さそうです。

pdf_for_sample.html.slim
 doctype html 
 html[lang="ja" xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" dir="ltr"]
   head 
     / メインのcss 
     = wicked_pdf_stylesheet_link_tag 'sample/sample', media: 'all'
   body 
     = yield
sample.html.slim
.sample
  .sample-text コンテンツ

A4横向き&pdfファイル全体に背景画像を使いたい

筆者の場合A4かつ横向きで生成する必要があったので、page_sizeorientationを指定します。
(page_sizeはデフォルトでA4なので指定は不要ですが、可読性のため明示的に指定しました)

sample_controller.rb
def generate_pdf
  respond_to do |format|
    format.html { redirect_to sample_generate_pdf_path(format: :pdf)}
    format.pdf do
      if params[:debug].present?
        render pdf: 'sample',
               title: 'sample.pdf',
               encoding: 'UTF-8',
               orientation: 'Landscape',  #横向き
               page_size: 'A4',
               margin: { top: 0,
                         bottom: 0,
                         left: 0,
                         right: 0 },
               layout: 'pdf_for_sample.html.slim',
               template: "sample/sample.html.slim",
               show_as_html: params[:debug].present?
      else
        render pdf: 'sample',
               title: 'sample.pdf',
               encoding: 'UTF-8',
               orientation: 'Landscape',  #横向き
               page_size: 'A4',
               margin: { top: 0,
                         bottom: 0,
                         left: 0,
                         right: 0 },
               layout: 'pdf_for_sample.html.slim',
               template: "sample/sample.html.slim",
               show_as_html: params[:debug].present?
      end
    end
  end
end

次にpdf全体を覆う背景画像を当て込みます。
デフォルトでmarginが設定されているので初期化し、かつA4に合うよう調整した結果、heightを991pxに設定しました。

sample.sass
.sample
  background: sample.png
  background-size: cover
  height: 991px

画面遷移を行わず、PDFのファイルダウンロードまで行いたい

筆者のPJでは生成したpdfファイルはユーザーのPCにダウンロードされる想定でしたが、このままだと、「ボタンクリックなどでPDF生成実行→pdfファイル画面へ遷移→ダウンロードボタンをクリック→ファイルがダウンロードされる」と言うフローになり、やや煩雑です。
上記を「ボタンクリックなどでPDF生成実行→ファイルがダウンロードされる」と言うフローにするため、以下の記述に変更します。

sample_controller.rb
def generate_pdf
  respond_to do |format|
    format.html { redirect_to sample_generate_pdf_path(format: :pdf) }
    format.pdf do
      if params[:debug].present?
        render pdf: 'sample',
               title: 'sample.pdf',
               encoding: 'UTF-8',
               orientation: 'Landscape',
               page_size: 'A4',
               margin: { top: 0,
                         bottom: 0,
                         left: 0,
                         right: 0 },
               layout: 'pdf_for_sample.html.slim',
               template: "sample/sample.html.slim",
               show_as_html: params[:debug].present?
      else
        pdf = render_to_string pdf: 'sample',
                               title: 'sample.pdf',
                               encoding: 'UTF-8',
                               orientation: 'Landscape',
                               page_size: 'A4',
                               margin: { top: 0,
                                         bottom: 0,
                                         left: 0,
                                         right: 0 },
                               layout: 'pdf_for_sample.html.slim',
                               template: "sample/sample.html.slim",
                               show_as_html: params[:debug].present?
        send_data pdf, filename: "sample.pdf"
      end
    end
  end
end

filename: "sample.pdf"でダウンロードされる際のファイル名が指定可能です。

Amazon LinuxでRuntime errorになる

環境依存問題その1
ローカルで動作確認をしていざデプロイ!と思ったらPDF生成実行で500エラー(Runtime error)となり動かない...
どうやら、AmazonLinuxは動作環境外のようでした。
調べてみるとAmazonLinux対応版のブランチがあるようなので、指定したら正常に動作しました。

Gemfile
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary',
  git: 'https://github.com/entretechno/wkhtmltopdf_binary_gem',
  branch: 'amazon-linux'

日本語が文字化けする

環境依存問題その2
PDF生成の際にはクライアントではなくサーバー側のフォントを参照するらしく、日本語が文字化けしてまともに動かない状態に。
そのためサーバーに日本語フォントをインストールした上で、cssからスタイルに適用します。
今回はyumで手軽にインストール可能なIPAフォントを利用しました。
(本当はnotoなどのgoogleフォントが使えれば尚良かったのですが、筆者の環境では正常に読み込めませんでした)

sudo yum -y install ipa-gothic-fonts ipa-mincho-fonts
sample.sass
.sample
  background: sample.png
  background-size: cover
  height: 991px
.sample-text
  font-family: 'IPAMincho', 'IPA明朝'

また、serifsan-serifは、フォントをインストールしない状態でも描画可能だったので、試してみるといいかもしれません。

最終形

上記をまとめると以下になります。

sample_controller.rb
def generate_pdf
  respond_to do |format|
    format.html { redirect_to sample_generate_pdf_path(format: :pdf) }
    format.pdf do
      if params[:debug].present?
        render pdf: 'sample',
               title: 'sample.pdf',
               encoding: 'UTF-8',
               orientation: 'Landscape',
               page_size: 'A4',
               margin: { top: 0,
                         bottom: 0,
                         left: 0,
                         right: 0 },
               layout: 'pdf_for_sample.html.slim',
               template: "sample/sample.html.slim",
               show_as_html: params[:debug].present?
      else
        pdf = render_to_string pdf: 'sample',
                               title: 'sample.pdf',
                               encoding: 'UTF-8',
                               orientation: 'Landscape',
                               page_size: 'A4',
                               margin: { top: 0,
                                         bottom: 0,
                                         left: 0,
                                         right: 0 },
                               layout: 'pdf_for_sample.html.slim',
                               template: "sample/sample.html.slim",
                               show_as_html: params[:debug].present?
        send_data pdf, filename: "sample.pdf"
      end
    end
  end
end
pdf_for_sample.html.slim
 doctype html 
 html[lang="ja" xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" dir="ltr"]
   head 
     / メインのcss 
     = wicked_pdf_stylesheet_link_tag 'sample/sample', media: 'all'
   body 
     = yield
sample.html.slim
.sample
  .sample-text コンテンツ
sample.sass
.sample
  background: sample.png
  background-size: cover
  height: 991px
.sample-text
  font-family: 'IPAMincho', 'IPA明朝'
Gemfile
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary',
  git: 'https://github.com/entretechno/wkhtmltopdf_binary_gem',
  branch: 'amazon-linux'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bundle installでmysql2のエラーが出たときに見る解決法

bundle installエラーが出た際、解決まで結構時間がかかってしまったので、まとめて置こうと思います。

エラー内容

実行コマンド
bundle install
//エラー
An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling.
実行コマンド
bundle install
//エラー
Could not find gem 'sass-rails (>= 6)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.

原因

見てみるとどうやらlsslが見つけられないのが原因らしい。
エラーの内容の中にこのエラーがある人はこの記事の方法が役に立つかもしれません。
lsslが見つからないということらしい。

エラー原因
ld: library not found for -lssl

そもそもldってなに?

ldとは、GNUリンカーと呼ばれるコマンドで、コンパイルされたオブジェクトファイルから実行可能ファイル( またはライブラリ)を作成することができるものです。

続いて-lsslってなに?

mysql2 gemインストール時にldコマンドに与えられたオプションの一つで、libssl.dylibをライブラリ検索範囲から探してねって言うことらしいです。

解決

以下のコマンドを実行

実行コマンド
bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"

実行コマンドの意味

実行コマンドが意味不明だったため、後で見てもわかるようにまとめておきます。

  • bundle config
    設定システムを編集します。

  • --local build.mysql2
    ローカルでmysql2をビルドした際に以下の通りビルドしてください。

  • --with-ldflags=-L/usr/local/opt/openssl/lib
    ビルドする際は以下を参照してください。
    参照先は-L/usr/local/opt/openssl/libです。

「今後ローカルでmysql2をインストールする場合は/usr/local/opt/openssl/libの場所を参照してくださいね」

って感じですかね。

参考記事

ありがとうございました!

【Rails】MySQL2がbundle installできない時の対応方法
mysql2 gemインストール時のトラブルシュート
Ruby STUDIO

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

rails風味のrubyってなにそれ美味しいの?

stylesheet_link_tag(* sources)リンク

引数として指定されたソースのスタイルシートリンクタグを返します。拡張子を指定しない場合、.css自動的に追加されます。最後の引数としてハッシュを渡すことにより、リンク属性を変更できます。歴史的な理由により、「media」属性は常に存在し、デフォルトは「screen」です。したがって、スタイルシートをすべてのメディアタイプに適用するには、「all」に明示的に設定する必要があります。

ここではrubyの基本的な知識をrails consoleを用いて勉強するらしい。

オブジェクトとメッセージ受け渡し

オブジェクトを直感的に理解することが大事らしい。
何種類かRubyにまつわる書籍読んだんだけどいまでによくわかっていない。

オブジェクトとは (いついかなる場合にも) メッセージに応答するものらしい。文字列のようなオブジェクトについての説明をlengthというメッセージを用いて説明している。

"foobar".length  # 文字列に "length" というメッセージを送る
=> 6

オブジェクトに渡されるメッセージは、メソッドと呼ばれていて。メソッドの実体は、そのオブジェクト内で定義されたメソッドである。
Rubyの文字列は、次のようにempty?メソッドにも応答することができる。

>> "foobar".empty?
=> false
>> "".empty?
=> true

Rubyでは、メソッドがtrueまたはfalseという論理値 (boolean) を返すことを、末尾の疑問符で示す慣習があります。 論理値は、特に処理の流れを変更するときに有用です。

>> s = "foobar"
>> if s.empty?
>>   "The string is empty"
>> else
>>   "The string is nonempty"
>> end
=> "The string is nonempty"

条件文を2つ以上含めたい場合は、elsif (else + if) という文を使います。

>> if s.nil?
>>   "The variable is nil"
>> elsif s.empty?
>>   "The string is empty"
>> elsif s.include?("foo")
>>   "The string includes 'foo'"
>> end
=> "The string includes 'foo'"

なお、論理値はそれぞれ && (and) や || (or)、! (not) オペレーターで表すこともできます。

>> x = "foo"
=> "foo"
>> y = ""
=> ""
>> puts "Both strings are empty" if x.empty? && y.empty?
=> nil
>> puts "One of the strings is empty" if x.empty? || y.empty?
"One of the strings is empty"
=> nil
>> puts "x is not empty" if !x.empty?
"x is not empty"
=> nil

Rubyでは、あらゆるものがオブジェクトである。したがって、nilもオブジェクトであり、これも多くのメソッドに応答する。ほぼあらゆるオブジェクトを文字列に変換するto_sメソッドを使うと

>> nil.to_s
=> ""

空文字列が出力された。今度はnilに対してメソッドをチェーン (chain) して渡せることを確認する。
ここらへんで頭がこんがらがってきたな。

>> nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
>> nil.to_s.empty?      # メソッドチェーンの例
=> true

まだぎりぎりついてける。

puts "x is not empty" if !x.empty?


---------------------------------------------------
>> string = "foobar"
>> puts "The string '#{string}' is nonempty." unless string.empty?
The string 'foobar' is nonempty.
=> nil

ああ???もうここらへんわからん笑
とりあえず説明が分かりづらいんだけどif文とunless文のコードが簡潔にまとめられるやり方を説明してるのかな?
とりあえずなんの説明もなくunlessキーワードていうのがでてきたな。progateにあったっけ?

「unless」文とは

条件式が偽の場合の処理を記述するのに使われます。
みたいです。
だから上のやつは
もし変数stringの中に文字数が0じゃなかったら””内を出力しろ
みたいな感じかな?
わかりづらいって。笑

演習

1."racecar" の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。

 > "racecar".length
 => 7 

2.reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。

> "racecar".reverse
 => "racecar"

3.変数sに "racecar" を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。

 > s==s.reverse
 => true 

4.リスト 4.9を実行すると、どんな結果になるでしょうか? 変数sに "onomatopoeia" という文字列を代入するとどうなるでしょうか? ヒント: 上矢印 (またはCtrl-Pコマンド) を使って以前に使ったコマンドを再利用すると一からコマンドを全部打ち込む必要がなくて便利ですよ。)

>  puts "It's a palindrome!" if s == s.reverse
It's a palindrome!
 => nil 

 s="onomatopoeia"
 => "onomatopoeia" 
   > puts "It's a palindrome!" if s == s.reverse
 => nil 

演習はこんな感じでした。あの、、演習2のやつだいぶ悩んじゃったよ。あれっ変わらなくね?って。笑

メソッドの定義

rails consoleの中でもアクションやヘルパーなどと同じ方法でメソッドを定義することができる。

>> def string_message(str = '')
>>   if str.empty?
>>     "It's an empty string!"
>>   else
>>     "The string is nonempty."
>>   end
>> end
=> :string_message
>> puts string_message("foobar")
The string is nonempty.
>> puts string_message("")
It's an empty string!
>> puts string_message
It's an empty string!

メソッドの引数を省略することも可能 (カッコですら省略可能です)。これは次のコードで

def string_message(str = '')

引数にデフォルト値を含めているからである。(この例のデフォルト値はからの文字列である)このように設定するとstr変数に引数を渡すことも渡さないこともできる。引数を渡さない場合は指定のデフォルト値が自動的に使われる。

演習

1.リスト 4.10のFILL_INの部分を適切なコードに置き換え、回文かどうかをチェックするメソッドを定義してみてください。ヒント: リスト 4.9の比較方法を参考にしてください。

>> def palindrome_tester(s)
>>   if s==s.reverse
>>     puts "It's a palindrome!"
>>   else
>>     puts "It's not a palindrome."
>>   end
>> end

2.上で定義したメソッドを使って “racecar” と “onomatopoeia” が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文でない、という結果になれば成功です。

> def palindrome_tester(s)
>   if s==s.reverse
>     puts "It's a palindrome!"
>     else
>      puts "It's not a palindrom."

>     end
>   end
   => :palindrome_tester

> puts palindrome_tester("racecar")

It's a palindrome!

 => nil 
  > puts palindrome_tester("onomatopoeia")
      It's not a palindrom.

    => nil 

3.palindrome_tester("racecar")に対してnil?メソッドを呼び出し、戻り値がnilであるかどうかを確認してみてください (つまりnil?を呼び出した結果がtrueであることを確認してください)。このメソッドチェーンは、nil?メソッドがリスト 4.10の戻り値を受け取り、その結果を返しているという意味になります。

>>palindrome_tester("racecar").nil?
=>It's a palindrome!
=> true

ここでtitleヘルパーの値を振りかえってみると。。。

module ApplicationHelper

  # ページごとの完全なタイトルを返します。                   # コメント行
  def full_title(page_title = '')                     # メソッド定義とオプション引数
    base_title = "Ruby on Rails Tutorial Sample App"  # 変数への代入
    if page_title.empty?                              # 論理値テスト
      base_title                                      # 暗黙の戻り値
    else 
      page_title + " | " + base_title                 # 文字列の結合
    end
  end
end

おおー。。。なんかわかるようになってる。
すげーな。railsチュートリアル。
ここらへんすごい大事だと思う。

他のデータ構造

配列と範囲演算子

配列

配列 (array) は、特定の順序を持つ要素のリスト。配列を理解することは、ハッシュやRailsのデータモデルを理解するための重要な基盤となります。

ふむふむ

splitメソッド

文字列を自然に変換した配列を得ることができる。

>>  "foo bar     baz".split     # 文字列を3つの要素を持つ配列に分割する
=> ["foo", "bar", "baz"]

>> "fooxbarxbaz".split('x')
=> ["foo", "bar", "baz"]
他の文字を指定して区切ることもできる。

=> ["foo", "bar", "baz"]

多くのコンピュータ言語の慣習と同様、Rubyの配列でもゼロオリジンを採用しています。これは、配列の最初の要素のインデックスが0から始まり、2番目は1...と続くことを意味する。

>> a = [42, 8, 17]
=> [42, 8, 17]
>> a[0]               # Rubyでは角カッコで配列にアクセスする
=> 42
>> a[1]
=> 8
>> a[2]
=> 17
>> a[-1]              # 配列の添字はマイナスにもなれる!
=> 17

ここa[-1]=17になってんだけど負の配列の添字は[0.1.2]=[-3.-2.-1]らしい。よってこれは17になります。
上で示したとおり、配列の要素にアクセスするには角カッコを使う。Rubyでは、角カッコ以外にも配列の要素にアクセスする方法が提供されている。

>> a                  # 配列「a」の内容を確認する
=> [42, 8, 17]
>> a.first
=> 42
>> a.second
=> 8
>> a.last
=> 17
>> a.last == a[-1]    # == を使って比較する
=> true

最後の行では、等しいことを確認する比較演算子==を使ってみました。この演算子や != (“等しくない”) などの演算子は、他の多くの言語と共通です。

>> x = a.length       # 配列も文字列と同様lengthメソッドに応答する
=> 3
>> x == 3
=> true
>> x == 1
=> false
>> x != 1
=> true
>> x >= 1
=> true
>> x < 1
=> false

配列は、上記コードの最初の行のlengthメソッド以外にも、さまざまなメソッドに応答します。

>> a
=> [42, 8, 17]
>> a.empty?
=> false
>> a.include?(42) 
include?
とは文字列や配列の要素に含まれる文字列に対して、指定した文字列が存在するか確認するときに使用。
=> true
>> a.sort
=> [8, 17, 42]
>> a.reverse
=> [17, 8, 42]
>> a.shuffle
=> [17, 42, 8]
>> a
=> [42, 8, 17]

上のどのメソッドを実行した場合にも、a自身は変更されていないという点にご注目ください。配列の内容を変更したい場合は、そのメソッドに対応する「破壊的」メソッドを使います。破壊的メソッドの名前には、元のメソッドの末尾に「!」を追加したものを使うのがRubyの慣習です。

>> a
=> [42, 8, 17]
>> a.sort!
=> [8, 17, 42]
>> a
=> [8, 17, 42]

また、pushメソッド (または同等の<<演算子) を使って配列に要素を追加することもできます。

>> a.push(6)                  # 6を配列に追加する
=> [42, 8, 17, 6]
>> a << 7                     # 7を配列に追加する
=> [42, 8, 17, 6, 7]
>> a << "foo" << "bar"        # 配列に連続して追加する
=> [42, 8, 17, 6, 7, "foo", "bar"]

最後の例では、要素の追加をチェーン (chain) できることを示しました。他の多くの言語の配列と異なり、Rubyでは異なる型が配列の中で共存できます (上の場合は整数と文字列)。

上では、文字列を配列に変換するのにsplitを使いました。joinメソッドはこれと逆の動作です。

>> a
=> [42, 8, 17, 6, 7, "foo", "bar"]
>> a.join                       # 単純に連結する
=> "4281767foobar"
>> a.join(', ')                 # カンマ+スペースを使って連結する
=> "42, 8, 17, 6, 7, foo, bar"

範囲 (range) は、配列と密接に関係しています。to_aメソッドを使って配列に変換すると理解しやすいと思います。

>> 0..9
=> 0..9
>> 0..9.to_a              # おっと、9に対してto_aを呼んでしまっていますね
NoMethodError: undefined method `to_a' for 9:Fixnum
>> (0..9).to_a            # 丸カッコを使い、範囲オブジェクトに対してto_aを呼びましょう
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

0..9 は範囲として有効ですが、上の2番目の表記ではメソッドを呼ぶ際にカッコを追加する必要があることを示しています。

範囲は、配列の要素を取り出すのに便利です。

>> a = %w[foo bar baz quux]         # %wを使って文字列の配列に変換
=> ["foo", "bar", "baz", "quux"]
>> a[0..2]
=> ["foo", "bar", "baz"]

インデックスに-1という値を指定できるのは極めて便利です。-1を使うと、配列の長さを知らなくても配列の最後の要素を指定することができ、これにより配列を特定の開始位置の要素から最後の要素までを一度に選択することができます。

>> a = (0..9).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>> a[2..(a.length-1)]               # 明示的に配列の長さを使って選択
=> [2, 3, 4, 5, 6, 7, 8, 9]
>> a[2..-1]                         # 添字に-1を使って選択
=> [2, 3, 4, 5, 6, 7, 8, 9]

次のように、文字列に対しても範囲オブジェクトが使えます。

>> ('a'..'e').to_a
=> ["a", "b", "c", "d", "e"]

演習
1.文字列「A man, a plan, a canal, Panama」を ", " で分割して配列にし、変数aに代入してみてください。

a = "A man, a plan, a canal, Panama".split(",")
 => ["A man", " a plan", " a canal", " Panama"]

2.今度は、変数aの要素を連結した結果 (文字列) を、変数sに代入してみてください。

 s=a.join
 => "AmanaplanacanalPanama" 

3.変数sを半角スペースで分割した後、もう一度連結して文字列にしてください (ヒント: メソッドチェーンを使うと1行でもできます)。リスト 4.10で使った回文をチェックするメソッドを使って、(現状ではまだ) 変数sが回文ではないことを確認してください。downcaseメソッドを使って、s.downcaseは回文であることを確認してください。

=> "A man a plan a canal Panama"
s.split
=> ["A", "man", "a", "plan", "a", "canal", "Panama"]
s
=> "A man a plan a canal Panama"
s.split.join
=> "AmanaplanacanalPanama"
palindrome_tester(s.split.join)
It's not a palindrome.
=> nil
palindrome_tester(s.split.join.downcase)
It's a palindrome!
=> nil

4.aからzまでの範囲オブジェクトを作成し、7番目の要素を取り出してみてください。同様にして、後ろから7番目の要素を取り出してみてください。(ヒント: 範囲オブジェクトを配列に変換するのを忘れないでください)

('a'..'z').to_a
 => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] 
> ('a'..'z').to_a[6]
 => "g" 

こんな感じですかね。

ブロック

配列と範囲はいずれも、ブロックを伴うさまざまなメソッドに対して応答することができます。ブロックは、Rubyの極めて強力な機能であり、かつわかりにくい機能でもあります。

>> (1..5).each { |i| puts 2 * i }
2
4
6
8
10
=> 1..5

上のコードでは、範囲オブジェクトである(1..5)に対してeachメソッドを呼び出しています。メソッドに渡されている{ |i| puts 2 * i }が、ブロックと呼ばれる部分です。|i|では変数名が縦棒「|」に囲まれていますが、これはブロック変数に対して使うRubyの構文で、ブロックを操作するときに使う変数を指定します。この場合、範囲オブジェクトのeachメソッドは、iという1つのローカル変数を使ってブロックを操作できます。そして、範囲に含まれるそれぞれの値をこの変数に次々に代入してブロックを実行します。

ブロックであることを示すには波カッコ で囲みますが、次のようにdoとendで囲んで示すこともできます。

>> (1..5).each do |i|
?>   puts 2 * i
>> end
2
4
6
8
10
=> 1..5

ブロックには複数の行を記述できます (実際ほとんどのブロックは複数行です)。RailsチュートリアルではRuby共通の慣習に従って、短い1行のブロックには波カッコを使い、長い1行や複数行のブロックにはdo..end記法を使っています。

>> (1..5).each do |number|
?>   puts 2 * number
>>   puts '--'
>> end
2
--
4
--
6
--
8
--
10
--
=> 1..5

今度はiの代わりにnumberを使っていることにご注目ください。この変数 (ブロック変数) の名前は固定されていません。

ブロックは見た目に反して奥が深く、ブロックを十分に理解するためには相当なプログラミング経験が必要です。そのためには、ブロックを含むコードをたくさん読みこなすことでブロックの本質を会得する以外に方法はありません9。幸いなことに、人間には個別の事例を一般化する能力というものがあります。ささやかですが参考のために、mapメソッドなどを使ったブロックの使用例をいくつか挙げてみます。

>> 3.times { puts "Betelgeuse!" }   # 3.timesではブロックに変数を使っていない
"Betelgeuse!"
"Betelgeuse!"
"Betelgeuse!"
=> 3
>> (1..5).map { |i| i**2 }          # 「**」記法は冪乗 (べき乗) 
=> [1, 4, 9, 16, 25]
>> %w[a b c]                        # %w で文字列の配列を作成
=> ["a", "b", "c"]
>> %w[a b c].map { |char| char.upcase }
=> ["A", "B", "C"]
>> %w[A B C].map { |char| char.downcase }
=> ["a", "b", "c"]

上に示したように、mapメソッドは、渡されたブロックを配列や範囲オブジェクトの各要素に対して適用し、その結果を返します。また、後半の2つの例では、mapのブロック内で宣言した引数 (char) に対してメソッドを呼び出しています。こういったケースでは省略記法が一般的で、次のように書くこともできます (この記法を“symbol-to-proc”と呼びます)。

>> %w[A B C].map { |char| char.downcase }
=> ["a", "b", "c"]
>> %w[A B C].map(&:downcase)
=> ["a", "b", "c"]

演習

1.範囲オブジェクト0..16を使って、各要素の2乗を出力してください。

 > (0..16).map{|i|i**2}
 => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256] 

2.yeller (大声で叫ぶ) というメソッドを定義してください。このメソッドは、文字列の要素で構成された配列を受け取り、各要素を連結した後、大文字にして結果を返します。例えばyeller(['o', 'l', 'd'])と実行したとき、"OLD"という結果が返ってくれば成功です。ヒント: mapとupcaseとjoinメソッドを使ってみましょう。

> def yeller(big_voice)
2.6.3 :013?>   voice=big_voice.map{|i|i.upcase}.join
2.6.3 :014?>   puts voice
2.6.3 :015?>   end
 => :yeller 
2.6.3 :016 > yeller(['o','l','d'])
OLD
 => nil 

3.random_subdomainというメソッドを定義してください。このメソッドはランダムな8文字を生成し、文字列として返します。ヒント: サブドメインを作るときに使ったRubyコードをメソッド化したものです。

def random_subdomain
puts ('a'..'z').to_a.shuffle[0..7].join
end
=>random_subdomain
これを出力する。

4.リスト 4.12の「?」の部分を、それぞれ適切なメソッドに置き換えてみてください。ヒント:split、shuffle、joinメソッドを組み合わせると、メソッドに渡された文字列 (引数) をシャッフルさせることができます。

def string_shuffle(s)
s.split('').shuffle.join
end
string_shuffle("foobar")

みたいな感じです。

ハッシュとシンボル

ハッシュは本質的には配列と同じ、インデックスとして整数値以外のものも使える点が配列と異なるハッシュのインデックス (キーと呼ぶのが普通です) は、通常何らかのオブジェクトです。例えば、次のように文字列をキーとして使えます。

>> user = {}                          # {}は空のハッシュ
=> {}
>> user["first_name"] = "Michael"     # キーが "first_name" で値が "Michael"
=> "Michael"
>> user["last_name"] = "Hartl"        # キーが "last_name" で値が "Hartl"
=> "Hartl"
>> user["first_name"]                 # 要素へのアクセスは配列の場合と似ている
=> "Michael"
>> user                               # ハッシュのリテラル表記
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

・ハッシュは、キーと値のペアを波カッコで囲んで表記する。
・ハッシュの波カッコは、ブロックの波カッコとはまったく別物である
・ハッシュは配列と似ていますがハッシュでは要素の「並び順」が保証されない。もし要素の順序が重要である場合は、配列を使う必要があります。

ハッシュの1要素を角カッコを使って定義する代わりに、次のようにキーと値をハッシュロケットと呼ばれる=> によってリテラル表現するほうが簡単です。

>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

ハッシュのキーとしては、文字列よりもシンボルを使う方が一般的。
シンボルは文字列と似ていますが、クォートで囲む代わりにコロンが前に置かれている点が異なります。

ハッシュのキーとしてシンボルを採用する場合、user のハッシュは次のように定義できる

>> user = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> user[:name]              # :name に対応する値にアクセスする
=> "Michael Hartl"
>> user[:password]          # 未定義のキーに対応する値にアクセスする
=> nil

最後の例を見ると、未定義のハッシュ値は単純にnilであることがわかります。

>> h1 = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h2 = { name: "Michael Hartl", email: "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h1 == h2
=> true

2つ目の記法は、シンボルとハッシュロケットの組み合わせを、次のようにキーの名前の (前ではなく) 後にコロンを置き、その後に値が続くように置き換えたもの.

{ :name => "Michael Hartl" }
上のコードと、

{ name: "Michael Hartl" }

というコードは等価になります

ハッシュの中にハッシュを定義できる

>> params = {}        # 'params' というハッシュを定義する ('parameters' の略)。
=> {}
>> params[:user] = { name: "Michael Hartl", email: "mhartl@example.com" }
=> {:name=>"Michael Hartl", :email=>"mhartl@example.com"}
>> params
=> {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}}
>>  params[:user][:email]
=> "mhartl@example.com"

配列や範囲オブジェクトと同様、ハッシュもeachメソッドに応答します。例えば、:successと:dangerという2つの状態を持つ flash という名前のハッシュについて考えてみましょう。

>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", :danger=>"It failed."}
>> flash.each do |key, value|
?>   puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :danger has value "It failed."

ここで、配列のeachメソッドでは、ブロックの変数は1つだけですが、ハッシュのeachメソッドでは、ブロックの変数はキーと値の2つになっていることに注意してください。したがって、 ハッシュに対してeachメソッドを実行すると、ハッシュの1つの「キーと値のペア」ごとに処理を繰り返します。

ところで、オブジェクトを表示するためにinspectを使うことは非常によくあることなので、 pメソッドというショートカットがある。

>> p :name             # 'puts :name.inspect' と同じ
:name

演習

1.キーが'one'、'two'、'three'となっていて、それぞれの値が'uno'、'dos'、'tres'となっているハッシュを作ってみてください。その後、ハッシュの各要素をみて、それぞれのキーと値を"'#{key}'のスペイン語は'#{value}'"といった形で出力してみてください。

suuzi = { one: "uno", two: "dos", three: "tres"}
=> { :one=>"uno", :two=>"dos", :three=>"tres"}
>> suuzi.each do |key, value|
  puts "'#{key.inspct} 'のスペイン語は'#{value.inspect}'"
end
'one'のスペイン語は'uno'
'two'のスペイン語は'dos'
'three'のスペイン語は'tres'
=> {:one=>"uno", :two=>"dos", :three=>"tres"}

2.person1、person2、person3という3つのハッシュを作成し、それぞれのハッシュに:firstと:lastキーを追加し、適当な値 (名前など) を入力してください。その後、次のようなparamsというハッシュのハッシュを作ってみてください。1.) キーparams[:father]の値にperson1を代入、2). キーparams[:mother]の値にperson2を代入、3). キーparams[:child]の値にperson3を代入。最後に、ハッシュのハッシュを調べていき、正しい値になっているか確かめてみてください。(例えばparams[:father][:first]がperson1[:first]と一致しているか確かめてみてください)

person1={first:"sato",last:"ichiro"}
=> {:first=>"sato", :last=>"ichiro"}

person2={first:"sato",last:"hanako"}
=> {:first=>"sato", :last=>"hanako"}

person3={first:"sato",last:"takashi"}
=> {:first=>"sato", :last=>"takashi"}

params={}
=> {}

params[:father]=person1
=> {:first=>"sato", :last=>"ichiro"}

params[:mother]=person2
=> {:first=>"sato", :last=>"hanako"}

params[:child]=person3
=> {:first=>"sato", :last=>"takashi"}

params
=> {:father=>{:first=>"sato", :last=>"ichiro"}, :mother=>{:first=>"sato", :last=>"hanako"}, :child=>{:first=>"sato", :last=>"takashi"}}

params[:father][:last]
=> "ichiro"

params[:child][:first]
=> "sato"

3.userというハッシュを定義してみてください。このハッシュは3つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています。

user={name:"yamada",email:"yamada@gmail.com",password_digest:"asdfghjk12345678"}
=> {:name=>"yamada", :email=>"yamada@gmail.com", :password_digest=>"asdfghjk12345678"}

Ruby API (訳注: もしくはるりまサーチ) を使って、Hashクラスのmergeメソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか? 推測できたら、実際にコードを実行して推測があっていたか確認してみましょう。
{ "a" => 100, "b" => 200 }.merge({ "b" => 300 })

{ "a" => 100, "b" => 200 }.merge({ "b" => 300 })
=> {"a"=>100, "b"=>300}

うーむ。。。ここまでやったけどかなり難しい概念ですハッシュ。。。
ここで先述されているCSSの追加の行がやっと読めるようになると。。
とりあえずここまでとします。
頭が痛い。。。。
次はrails チュートリアル4章目の続きから行きます。

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

undefined method current_sign_in_atエラーが発生したのでやってみたこと。

81e2c966f2f461cd1f1a7f38d7e3cbcf.png

ログインを実行すると、このようなエラーが発生。

エラー内容からuserモデル内のカラムにcurrent_sign_in_atがないのが原因のようだ。

というわけで、一度マイグレーションファイルをrails db:rollback

マイグレーションファイル内のcurrent_sign_in_atのコメントアウトを外し、raiis db:migrate

再度ログイン!

7ad51131bff9e3581e461e7688f9013b.png

今度はこんなエラーが発生。

last_sign_in_atがカラムないにないらしい。 どうせなら一度で言って欲しいものだが、、、、

同様の作業を行い、raiis db:migrate

再度ログインすると、今度はこんなエラーが、、、
30640de253e84c6e42cdd50cd89ba542.png

今度はcurrent_sign_in_ipが足りていないということか、、、

いっそのことTrackableのコメントアウトを全て外してしまえ!

  ## Trackable
  t.integer  :sign_in_count, default: 0, null: false
  t.datetime :current_sign_in_at
  t.datetime :last_sign_in_at
  t.string   :current_sign_in_ip
  t.string   :last_sign_in_ip

再度、raiis db:migrateをし、ログイン!

無事にログインできましたとさ

原因はよくわかりません。ただエラー解決できたのは一つの自信になったかな。

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

GraphiQL-Rails を動かす

はじめに

Ruby on Rails で最低限のGraphQL サーバを実装し、GraphiQL からクエリを実行する方法を記載します。

本記事は、以下の基礎知識を前提としています。

  • Ruby on Rails
  • GraphQL

環境

本記事は以下の環境で実行しました。

  • Ruby 2.7.0
  • Rails 6.0.2.2

使用するgem は以下。

  • graphql 1.10.5
  • graphiql-rails 1.7.0

GraphQL とは

ここでは説明しません。GraphQL 公式サイト等を参照してください。

GraphiQL とは

GraphiQL IDE は、Web ブラウザ上でGraphQL のクエリやミューテーションを実行できるIDE です。また、GraphQL のSchema を確認できます。名称は「Graph i QL」。「i」がはさまっているので注意。

Ruby on Rails で動作させるには、Graphi-QLというgem を使用します。

GraphiQL-Rails を動かす

順を追って以下を説明していきます。

  • Rails プロジェクト作成とgem のインストール
  • GraphQL の初期化
  • GraphiQL の設定
  • GraphQL クエリの実装
    • サンプルmodel の作成
    • GraphQL Type の作成
    • GraphQL クエリの作成
  • GraphiQL の実行

Rails プロジェクト作成とgem のインストール

今回作成するのはGraphQL サーバなので、APIモードでrails プロジェクトを作成します。APIモードでなくても大丈夫なはず。

$ rails new example-proj --api
$ cd example-proj

Gemfile に以下を追加して、

gem 'graphql'
gem 'graphiql-rails'

bundle install すればOK。

$ bundle install

Graphql の初期化

Rails プロジェクトにgraphql の基本構成を設定します。以下のコマンドを実行します。

$ rails g graphql:install

これにより、以下のファイル/ディレクトリが変更/追加されます。

  • 変更
    • config/routes.rb
  • 追加
    • app/controllers/graphql_controller.rb
    • app/graphql/

app/graphql ディレクトリにはGraphQL 用のサブディレクトリやスーパークラス等が配置されています。詳細については GraphQL Ruby のドキュメントを参照してください。

GrapiQL の設定

GraphiQL を設定します。config/ruotes.rb に以下を追記します。

config/routes.rb
Rails.application.routes.draw do
  # ここから
  if Rails.env.development?
    mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
  end
  # ここまで追記
  post "/graphql", to: "graphql#execute"
end

公式のドキュメントにAPI モードで使用する際の注意事項として「Rails 5 のAPI モードで使うときは application.rb に require "sprokets/railtie" が必要」とあるので、指示に従います。

If you're using Rails 5 in "API mode", you'll also need to add require "sprockets/railtie" to your application.rb.

config/application.rb に以下を追記します。

config/application.rb
require "sprockets/railtie"

ですが、これだけでは実行時にエラーになります(Loding...から進まなくなる)。そこで次の設定を追加します。ファイルがなければ追加しましょう。

app/assets/config/manifest.js
//= link graphiql/rails/application.css
//= link graphiql/rails/application.js

GraphQL クエリの実装

サンプルmodel の作成

簡単なサンプルとして、ユーザー情報のmodel を作成します。db:migrate を忘れずに。

$ rails g model User name:string email:string
$ rake db:migrate

確認用のデータを追加します。

$ rails c
irb(main):001:0> User.create(name: "hoge", email: "hoge@example.com")
irb(main):002:0> User.create(name: "fuga", email: "fuga@example.com")

GraphQL Type の作成

GraphQL クエリの前に、まずGraphQL Type を作成します。

$ rails g graphql:object User id:ID! name:String! email:String

ここで設定するのはGraphQL Type なので、Rails のmodel とは型指定が異なることに注意。型末尾の ! はnull を許容しないことを示します。これにより、次のファイルが作成されます。

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: true
  end
end

GraphQL クエリの作成

GraphQL クエリを作成します。app/graphql/types/query_type.rb を以下のとおり実装します。

app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject
    # Add root-level fields here.
    # They will be entry points for queries on your schema.

    field :users, [Types::UserType], null: false, description: "すべてのユーザを取得する"
    def users
      User.all
    end

    field :user, Types::UserType, null: false, description: "idを指定してユーザを取得する" do
      argument :id, Int, required: true
    end
    def user(id:)
      User.find(id)
    end
  end
end

これで準備は完了です。

GraphQL クエリの実行

Rails サーバを起動します。

$ rails s

localhost:3000/graphiql にアクセスすると、GraphiQL IDE が表示されます。
スクリーンショット 2020-03-25 13.56.17.png

クエリ構文の確認や、クエリを実行して確認できます。

まとめ

Ruby on Rails で簡単なGraphQL type とクエリを実装し、GraphiQL から操作してみました。GraphiQL は開発中だけでなくドキュメントとしても使えそうなので、GraphQL 開発では必須になりそうです。

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

gem install railsができない人のためエラー解消法

gem install railsしてもrailsがインストールできなかったため、まとめておこうと思います。

実行環境

OS: MacOS
rubyバージョン管理:rbenv
シェル:zsh

エラー①内容

実行コマンド

gem installを行い、インストールできたかrails -vで確認をする。

gem install rails
rails -v

エラー内容

インストールは成功した風なのにインストールされていないと出てくる
「sudo gem install rails をしてくれ」ってなっているが、実行しても同様のエラーがでる。

Rails is not currently installed on this system. To get the latest version, simply type:

    $ sudo gem install rails

You can then rerun your "rails" command.

原因

rbenvでrubyのバージョン管理を行っているとgemの参照先によってこのようなエラーが出るらしい。
基本は以下の方法で解決するらしいが、自分の環境では解消していない。

詳しくは以下のQiita記事を参考
とても助かりましたありがとうございます。

MacOSX-Lionでrbenvを使ってRubyOnRailsの環境構築すると、以下のようなメッセージが出てドハマリしたけど、なんとか解決したことについてのメモ(推理なので正しいかどうかの確証持てない)

エラー②内容

gem install railsを行うと以下のようにインストールが成功しましたと出てくるが、rails -vを行うと同様にインストールできていないとエラーが出る。

実行コマンド
gem install rails
実行後のメッセージ
Successfully installed rails-6.0.2.2
Parsing documentation for rails-6.0.2.2
Done installing documentation for rails after 0 seconds
1 gem installed
実行コマンド
rails -v
実行後のメッセージ
Rails is not currently installed on this system. To get the latest version, simply type:

    $ sudo gem install rails

You can then rerun your "rails" command.

原因を探る

とりあえずwhichコマンドを使いどこから実行されているかを確認することにした。

実行コマンド
which ruby
//結果 ↓の違いがエラーの原因だと思われる。
/Users/hoge/.rbenv/shims/ruby
実行コマンド
which rails
//結果 ↓の違いがエラーの原因だと思われる。
/usr/bin/rails

この処理を行うのがエラー①の内容のはずだが、どうやらzshrcの反映がうまくできてなかったみたい。

解決法

ターミナルの再起動をする!

結果はしょうもないことでしたが、 同様の症状になっている方が見えたら一度ターミナルを再起動して見てください。

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

シークエント計算の理論を勉強してみたので紹介

(※ この記事は、DIGGLE社でランチ勉強会に使った内容をそのままアップしています)


注意1: 普段の業務にそのまま活かせるものではありません
注意2: 自分は型理論を専攻していたので、出てくる記号列を理解するのに苦しみませんでしたが、初めて見る方はかなり咀嚼が必要な可能性が高いです


シークエント計算とは?

命題論理述語論理に対して使われる演繹手法の一つの形式化


命題論理 や 述語論理 とは?

命題論理 や 述語論理 は 命題の形式化

= 命題(命題式)を表す言語 x 意味

(今回は命題論理のみについて話します)


命題論理の命題式の定義

次の文法で生成できる文を命題式と定義する
(以下に現れる $\phi, \psi$ はそれぞれ命題式を表す)

対応する直感的な意味合い
A,B,C,... 任意の記号
$\phi \wedge \psi$ $\phi$ かつ $\psi$
$\phi \vee \psi$ $\phi$ または $\psi$
$\phi \rightarrow \psi$ $\phi$ ならば $\psi$
$\lnot \phi$ $\phi$ でない

※ 結合性や()に関する議論は関心外なので省略
※ 述語論理はここに∀と∃を追加して得られる


命題論理の"意味"

命題式だけだと、ただの文=記号列

「命題が満たされている(正しい,充足している)」とは何か? = 意味

命題式には自由変数のように記号が現れる。
そこで、記号→{T,F}の関数(真理値割当という)が所与として与えられた時の意味を定義する必要がある。

「命題が真理値割当Aのもとで"満たされている"」とは次のように定義する

1. A(X)=T ならば X は満たされている
2. φとψが満たされている ならば φ∧ψ は満たされている
3. φとψの少なくとも一方が満たされている ならば φ∨ψ は満たされている
4. ¬φとψの少なくとも一方が満たされている ならば φ→ψ は満たされている (※ 後述)
5. φが満たされていない ならば ¬φ は満たされている

さらに、任意の真理値割当Aのもとで命題Pが満たされているとき、 「命題Pは恒真である」 と定義する。


命題の具体例

  • $A \rightarrow A$

AならばA (トートロジー)

  • $A \wedge (A \rightarrow B) \rightarrow B$

三段論法 (modus-ponens)

これらは、どんな真理値割当のもとでも満たされている恒真命題であることが愚直に場合分けすればわかる。


注意 A→B は消せる

$A \rightarrow B$ は $\lnot A \vee B$ と同じと見做される

「あなたが持っているものがりんごならば、それは赤い」
は、相手がみかんを持っていたとしても間違ったことは言っていないので"正しい"(??)

A→B B=True B=False
A=True True False
A=False True True
今一旦納得するための考え方:

AならばB
= (AであってかつBでない) ことは絶対ない
= ¬(A∧¬B)
= ¬A∨¬¬B
= ¬A∨B

まだ気になる人は含意、論理包含の解釈、古典論理などについて調べると、より沼にハマれる!


シークエント計算とは?

remind: 命題論理の演繹手法

  • シークエント という文
  • 0個以上のシークエントから1つのシークエントへの置き換えのルール(推論規則)

から成る。


シークエントの定義

$\phi_1, \cdots, \phi_m, \psi_1, \cdots, \psi_n$ を命題とするとき

シークエントは

$\phi_1, \cdots, \phi_m \Rightarrow \psi_1, \cdots, \psi_n$

という文のことをいう。
= 命題の配列2個からなると思えばOK


シークエントの意味

シークエントもこのままではただの文なので、意味が必要

シークエント

$\phi_1, \cdots, \phi_m \Rightarrow \psi_1, \cdots, \psi_n$

が満たされるのは、命題

$\phi_1 \wedge \cdots \wedge \phi_m \rightarrow \psi_1 \vee \cdots \vee \psi_n$

が満たされる時、と定義する。
(左が全部満たされる時、右のいずれかが正しい)

注意1: 左が0個の場合は真、右が0個の場合は偽
注意2: ∧と∨が逆の非対称性に違和感があるが、対偶を取ると実はこれで対称なのがわかる


推論規則

推論規則 = 0個以上のシークエント(前提) x 1つのシークエント(帰結)

このサイトから定義を拝借

(φ,ψはシークエント、 Γ、Δ、Π、Σはシークエントの列)

スクリーンショット 2020-03-24 19.27.06.png
スクリーンショット 2020-03-24 19.27.12.png


シークエント計算による演繹

初期シークエント $\phi \Rightarrow \phi$ から、推論規則を使って演繹(ゴールのシークエントを構成)する。
推論規則適用を1ステップの演繹と考える。これを繰り返して前提なしのシークエントを構成できるとき、その命題は満たされていることが示せる

例(二重否定) φ -> ¬¬φ
1: φ => φ     (初期シークエント)
2: ¬φ, φ =>   (1: ¬左)
3: φ => ¬¬φ   (2: ¬右)
4: => φ → ¬¬φ (3: →右)

例(三段論法) φ ∧ (φ → ψ) → ψ
1: φ => φ              (初期シークエント)
2: ψ => ψ              (初期シークエント)
3: φ, φ → ψ => ψ       (1,2→左)
4: φ ∧ (φ → ψ), φ => ψ (3∧左)
5: φ, φ ∧ (φ → ψ) => ψ (4交換左)
6: φ ∧ (φ → ψ), φ ∧ (φ → ψ) => ψ    (5∧左)
7: φ ∧ (φ → ψ) => ψ    (6希釈左)
8: => φ ∧ (φ → ψ) → ψ  (7→右)

シークエント計算の理論の帰結

  • 命題論理のシークエント計算では、ゴールの命題を構成できるか否かを決定(できる場合は作り方も含め)する有限の手続きが存在する (決定可能性)
    • つまり、与えられた命題が満たされるか否かを決定するプログラムを書ける
  • 述語論理(∀や∃がある論理)ではそのような手続きは作れない
    • 作ることを試みても、正しい場合に停止して、正しくない場合に停止しないようなプログラムになる (半決定性)

どんなプログラム?

Rubyで実装してみた例

https://github.com/mizukami234/sequence-calculus-ruby


補足: 理論が包含していることのうち、省略したこと

  • 推論規則自体が"正しい"規則なのかどうかの確認
  • プログラムの作り方の確認
  • プログラムの停止性の確認
  • プログラムの出力が"正しいこと"の確認
  • 直観主義論理(排中律のない論理)の取り扱い方
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 指定バージョンへの変更

◆Rubyのバージョン指定方法

自分用のコマンドライブラリです。ですが、同じお悩みの方はどうぞご利用ください。

表現がぶっきらぼうですが、そこはご容赦くださいませ〜〜。

◆コマンド一覧

◆希望する指定バージョン
rails:5.2.4 ⇨ 5.2.3
ruby:2.6.5 ⇨ 2.6.3


① アプリ作成時にbundleを動かさないように設定

ターミナル
$ rails _5.2.3_ new アプリ名 --skip-bundle
$ cd アプリ名

② gemfileの記述バージョン変更

【例】
  gem 'rails', '~> 5.2.4'
  ↓
  gem 'rails', '5.2.3'

 にする。

  ※rubyも指定バージョンに記述変更

③ .bash_profileへ記述

$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

④ エラー回避

 ※「N/A: version "N/A -> N/A" is not yet installed.」が出たのでこれで回避
 ※「You need to run "nvm install N/A" to install it before using it.」これをやれば回避できるんだろうけど放置

$ nvm unalias default

⑤ 初期化設定の反映

$ source ~/.bash_profile

⑥ インストールしたversionを使用可能な状態にする⇒shimsへの反映

$ rbenv rehash

⑦ 使用するバージョンの指定

 ※作成するプロジェクトのみでバージョン指定する場合は『rbenv local』
 ※全体でバージョン指定する場合は『rbenv global』

$ rbenv local 2.6.3

⑧ bundle install

$ bundle install --path=vendor/bundle
※ 「--path=vendor/bundle」 はあってもなくても大きく変化なし。詳細はリンクをチェック。

【最後】バージョン確認

$ rbenv local 2.6.3

bundle installについての参考記事

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

画像投稿機能実装時のNil location provided. Can't build URIエラー対処

環境

・Rails 5.2.4.1
・gem 'carrierwave'
・gem 'rmagick'

エラー

ArgumentError
Nil location provided. Can't build URI

この様なエラー表示されました。
スクショ撮るのを忘れました(汗)

色々とググった結果、、
画像投稿機能を実装前にテスト投稿したものに画像が含まれたいないため
エラーとなっている模様

解決方法

すでに投稿しているもので画像が含まれていないものを削除
今回は全てを削除。

rails c
Post.destroy_all

これで解決できました!

参考サイト
https://simpledancer.hatenablog.com/entry/2019/05/19/Nil_location_provided__Can't_build_URI%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA%E3%81%9F%E6%99%82%E3%81%AB%E3%82%84%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8

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

#Ruby でカンマ区切りの文字列をメソッドの複数引数として渡す例

# 複数の引数を受け取るメソッド
def foo(a, b)
  p a
  p b
end

# カンマ区切りの文字列を split で配列にする
"x,y".split(",")
# => ["x", "y"]

# 配列をアスタリスク * で 引数として渡す
foo(*"x,y".split(","))
# "x"
# "y"

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3047

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

商品出品画面を作る②ActiveStorage(複数画像投稿)

ActiveStorageを使用して複数枚の画像を投稿する

先ずはここを見てくださいRailsガイド、導入方法とか書いてあります。
S3に接続するときは注意が必要ですね。

そもそもActiveStorageは何がいいのか?

ActiveStorageの特徴としては画像をitemモデルとして扱えることでしょう。
どう言う事かと言うと、itemとimageが1:多の関係だとすると、テーブルを2個用意しないといけません。
そしてアソシエーションを組んで〜と言う処理をします。
削除や変更の時に処理が若干複雑になってしまいます。
そんな時、ActiveStorageならitemテーブルのみを対象に処理をすれば良いわけです。

今回の要件

  • 複数同時選択による複数枚の画像投稿
  • 複数回選択による複数枚の画像投稿
  • プレビュー表示
  • 投稿枚数管理
  • プレビューの削除
imet.rb
has_many_attached :images

ActiveStorageで複数画像を扱う為にモデルに記述

items_controller.rb
def new
    @item = Item.new
    @category_parent =  Category.where("ancestry is null")
  end

  def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to root_path 
    else
      render :new
    end
  end
 private

  def item_params
    params.require(:item).permit(:name, :text, :category_id, :condition_id, :deliverycost_id, :pref_id, :delivery_days_id, :price, images: []).merge(user_id: current_user.id, boughtflg_id:"1")
  end

def item_paramsの images:[] がポイントです。
こうする事で、複数画像を受け取ることが出来ます。

new.html.haml
-# 画像部分
.sell-container__content
  .sell-title
    %h3.sell-title__text
      出品画像
      %span.sell-title__require
        必須
  .sell-container__content__max-sheet 最大10枚までアップロードできます
  .sell-container__content__upload
    .sell-container__content__upload__items
      .sell-container__content__upload__items__box
        %ul#output-box
     ここにプレビューが入ります
          %div#image-input{tabindex:"0"}
            = f.label :images, for: "item_images0", class: 'sell-container__content__upload__items__box__label', data: {label_id: 0 } do 
              = f.file_field :images, multiple: true, class: "sell-container__content__upload__items__box__input", id: "item_images0", style: 'display: none;'
            ここに新しinputが入ります
              %pre
                %i.fas.fa-camera.fa-lg
                ドラッグアンドドロップ
                またはクリックしてファイルをアップロード
  .error-messages#error-image
    ここにエラーメッセージが入ります

= f.file_fieldの multiple:true がポイントです
name属性がname="item[images][]"になっていると思います。
これにより、選択した画像を配列とし保持できるようになり、複数枚を同時に選択できるようになります。
image.png

item_new.js
$(document).on('turbolinks:load', function(){
  $('#image-input').on('change', function(e){
  // 画像が選択された時プレビュー表示、inputの親要素のdivをイベント元に指定

    //ファイルオブジェクトを取得する
    let files = e.target.files;
    $.each(files, function(index, file) {
      let reader = new FileReader();

      //画像でない場合は処理終了
      if(file.type.indexOf("image") < 0){
        alert("画像ファイルを指定してください。");
        return false;
      }
      //アップロードした画像を設定する
      reader.onload = (function(file){
        return function(e){
          let imageLength = $('#output-box').children('li').length;
          // 表示されているプレビューの数を数える

          let labelLength = $("#image-input>label").eq(-1).data('label-id');
          // #image-inputの子要素labelの中から最後の要素のカスタムデータidを取得

          // プレビュー表示
          $('#image-input').before(`<li class="preview-image" id="upload-image${labelLength}" data-image-id="${labelLength}">
                                      <figure class="preview-image__figure">
                                        <img src='${e.target.result}' title='${file.name}' >
                                      </figure>
                                      <div class="preview-image__button">
                                        <a class="preview-image__button__edit" href="">編集</a>
                                        <a class="preview-image__button__delete" data-image-id="${labelLength}">削除</a>
                                      </div>
                                    </li>`);
          $("#image-input>label").eq(-1).css('display','none');
          // 入力されたlabelを見えなくする

          if (imageLength < 9) {
            // 表示されているプレビューが9以下なら、新たにinputを生成する
            $("#image-input").append(`<label for="item_images${labelLength+1}" class="sell-container__content__upload__items__box__label" data-label-id="${labelLength+1}">
                                        <input multiple="multiple" class="sell-container__content__upload__items__box__input" id="item_images${labelLength+1}" style="display: none;" type="file" name="item[images][]">
                                        <i class="fas fa-camera fa-lg"></i>
                                      </label>`);
          };
        };
      })(file);
      reader.readAsDataURL(file);
    });
  });

今回の1番の要所です
流れとしては
1. 画像を選択し、inputに保持させる
2. 保持された画像からファイルオブジェクトを取得する
3. プレビューを表示する(イベント元のイベント元のカスタムデータIDの番号を付加する)
4. 表示されているプレビューの数を数え、9以下なら次のinputを生成する(イベント元のカスタムデータIDの次の番号を付加する)

ポイント

  • プレビューとinputはカスタムデータIDの番号で紐づけます。
    data-image-id="${labelLength}"data-label-id="0"の部分です。

  • input要素はidで区別します。
    id="item_images${labelLength+1}" となっている部分です。
    これは#image-inputの子要素labelの中から最後の要素のカスタムデータidを取得指定ます。
    つまり、イベント元のカスタムデータIDの番号に+1しています。

  • labelとinputは、labelのfor属性inputのidで紐づける。
    for="item_images${labelLength+1}"id="item_images${labelLength+1}"の部分です。
    image.png
    ul要素の下のli要素がプレビューです。
    その下のdiv要素の下がinputが囲われたlabelです。

プレビューを削除する

item_new.js
  //削除ボタンが押された時
  $(document).on('click', '.preview-image__button__delete', function(){
    let targetImageId = $(this).data('image-id');
    // イベント元のカスタムデータ属性の値を取得
    $(`#upload-image${targetImageId}`).remove();
    //プレビューを削除
    $(`[for=item_images${targetImageId}]`).remove();
    //削除したプレビューに関連したinputを削除

    let imageLength = $('#output-box').children('li').length;
    // 表示されているプレビューの数を数える
    if (imageLength ==9) {
      let labelLength = $("#image-input>label").eq(-1).data('label-id');
      // 表示されているプレビューが9枚なら,#image-inputの子要素labelの中から最後の要素のカスタムデータidを取得
      $("#image-input").append(`<label for="item_images${labelLength+1}" class="sell-container__content__upload__items__box__label" data-label-id="${labelLength+1}">
                                  <input multiple="multiple" class="sell-container__content__upload__items__box__input" id="item_images${labelLength+1}" style="display: none;" type="file" name="item[images][]">
                                  <i class="fas fa-camera fa-lg"></i>
                                </label>`);
    };
  });

後はCSSでデコレーションすればOKですね。

ただし、一つ問題があります。
複数同時選択を行うと不具合が発生する事です。
と言うのも、同時選択をすると一つのinputに複数の画像の情報が保持されるからです。
そうすると、プレビューを削除した時に、正常に動作しなくなってしまいます。
対策としては同時選択を出来ないようにする方法があります。
この場合、name属性にも手を加える必要があります。
それは今後試すことにします。

今回は以上です

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

SNS認証ログインでNot found. Authentication passthru.が表示されたときの対処方法

執筆背景

先日、個人アプリ(フレームワークにはRuby On Railsを使用)をリモートリポジトリーにプッシュした際GitHubからgem "OmniAuth"の脆弱性について指摘された。
詳細 → https://github.com/omniauth/omniauth/pull/809

そのため、それに対応するgem "omniauth-rails_csrf_protection"をインストールした。

Gemfile
gem 'OmniAuth' #削除
gem 'omniauth-rails_csrf_protection'
bundle install

ローカル上でアプリを再起動すると以下のようなエラーが発生したため、対処方法を示す。

スクリーンショット 2020-03-25 1.27.30.png

対処方法

SNS認証ログインのlink_toタグの後ろにmethod: :postを追記する。

_links.html.erb
  <%= link_to (omniauth_authorize_path(resource_name, provider)), method: :post do %>
 SNS認証ログイン
  <% end %>

link_toタグは、ログイン機能にgem deviseを使用している場合、app/views/devise/shared/_links.html.erbの中にあります。

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

ユーザーの新規登録時に入力なしの初期値を設定する【Ruby on Rails】

目的

ユーザーの新規登録時に、直接入力させずにテーブルに初期値を保存させること。

使用する技術

  • gem devise

実践

以下のテーブルが作成してあるとします。

Column Type Options
username string null: false, unique: true
email string null: false, unique: true
password string null: false
coin integer null: false

今回は、ユーザー登録フォームで

  • username
  • email
  • password

の3項目をのみを入力させます。
ここで、coinカラムは入力させずに、初期値0としてテーブルに保存します。

事前準備

すでに、gem deviseが導入され、コントローラとビューの作成が終了しているものとします。
(このような画面まで用意されている所からです)
devise:registrations:new.png

deviseのカスタマイズ

ここからは、デバイスのカスタマイズを行っていきます。
初めにdeviseのコントローラを作成して編集できるようにします。

$  rails g devise:controllers users

app/controller/users/のフォルダにコントローラが作成されました。
このフォルダ内のregistrations_controller.rbを使用していきます。

次に、ルーティングを一度確認しておきます。

$  rails routes

# 結果
Prefix                 Verb   URI Pattern                    Controller#Action
new_user_registration  GET    /users/sign_up(.:format)       devise/registrations#new

今のままでは、作成したregistrations_controller.rbが呼び出されていない状態なので、これを呼び出す記述をします。

config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'users/registrations',
  }
end

devise導入時に自動的に記述されたdevise_for :usersに続けて、このように記述します。
編集後、もう一度ルーティングを確認します。

$  rails routes

# 結果
Prefix                 Verb   URI Pattern                    Controller#Action
new_user_registration GET    /users/sign_up(.:format)       users/registrations#new

devise/registrations#new → users/registrations#new
に変わっていればOKです。

コントローラに初期値を設定

コントローラにnewメソッドとcreateメソッドを定義していきます。

registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController

  # 省略

  def new
    @user = User.new
  end

  def create
    @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render "devise/registrations/new" and return
    end
    @user[:coin] = 0
    @user.save
    sign_in(:user, @user)
    redirect_to root_path
  end

  # 省略

registrations_controller.rbはコメントアウトで記述がされていて、Devise::RegistrationsControllerで最初からメソッドが定義されています。
カスタマイズするときは、コメントアウト部分を消してオーバーライド(上書き)していきます。

重要なのは、ここです。

@user[:coin] = 0

@user = User.newの時点ではcoin: nilだったところに、0を代入します。
これで、coinカラムに0が入りました。あとはそのまま、データベースにレコードを登録 + ログイン + root_pathにリダイレクトを行っています。

ちなみに補足しておくと、、

unless @user.valid?
  flash.now[:alert] = @user.errors.full_messages
  render "devise/registrations/new" and return
end

ここは、バリデーションに引っ掛かったときにエラーメッセージを表示させて、登録フォームのページを返すということをしています。(これがない場合、登録されずにTOPページに遷移したりします)

最後に

ユーザーの新規登録の場合はdeviseをカスタマイズする必要がありましたが、
普通のテーブルであれば、new,createメソッドのどちらかに初期値を代入するだけで簡単に保存させられます。

参考

Qiita 【オブジェクト生成時にインスタンス変数の初期値を設定する】

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

Reinitialized existing Git repository in が出たときの対処法

はじめに

ただの備忘録のうちのひとつ。将来のために、、、

git init したときのやつ

$ git init 
Reinitialized existing Git repository in ~

結論:やることない

特にやることない。Reinitializedとか言うから「初期化して最初から⁉」とか思ってたけどそうではないらしい。

関係ないけど

名前とメールの確認もたまにしたくなる

$git config --global --list
user.name=hogehoge
user.email=hoge@gmail.com
参考

https://qiita.com/ragingalpaca/items/ef247e459ff2e7759ba9
https://hacknote.jp/archives/15745/

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

チーム開発 3/24

メモです

商品出品機能のサーバーサイドを作っていく

画像を貼る際にすぐに画像が
プレビューできるようにする
複数画像を送信できるようにする

使うメソッド
fields_forは,file_fieldや collection_check_boxesなどと
同じくフォームのインプット要素を生成するフォームヘルパーです。
1つのリソースに対し紐づいた、
複数の別のリソースを同時に保存したい際に利用します。
例えば今回のような1投稿に対して複数の画像をつけるパターンや、
1投稿に対して複数のタグをつける時などにも利用できます。

<%= form_for @product do |f| %>
  商品名<%= f.text_field :name %><br>
  価格<%= f.number_field :price %><br>
  <%= f.fields_for :images do |i| %>
    <%= i.file_field :src %><br>
  <% end %>
  <%= f.submit %>
<% end %>

accepts_nested_attributes_for
fields_forメソッドを利用する際に、
親モデルの中に書く必要があるメソッドです。
以下の例のように、引数として子モデルの名前を書きます。

hoge.rb
class hoge
  has_many :images
  accepts_nested_attributes_for :images
end

accepts_nested_attributes_forメソッドのオプションとして、
引数に書くことができる記述です。このオプションをつけることで、
親のレコードが削除された場合に、関連付いている子のレコードも一緒に削除してくれます。
accepts_nested_attributes_for :images, allow_destroy: true

まとめ
1つのフォームで商品と商品画像を保存
①productモデルに子モデルをまとめて追加・更新するための記述を加える
②newアクションでproductモデルと、それにひも付くimageモデルのインスタンスを作成する
③ビューでfields_forを使うことで、@productに関連付いているimageの数(newなら1つ、editなら登録した枚数分)だけ入力欄が生成される
④最後に、コントローラでレコードを保存する処理を加える。ストロングパラメータの形が独特なので注意する。

画像を複数枚投稿できるようにjsでフォームを生成する
①画像投稿用のinputボタンに、フォームで画像を選択したタイミングのイベントをセットする。
②①のイベントが発生したら、新しい画像投稿フォームを生成する、というJavaScriptのメソッドを動かす
③生成した画像投稿フォームを画面に追加する

スプリントレビューのため一時停止
デプロイした後
トップページ背景画像が消えていたので修正
Background:url
が使えないため
image_tag
を利用さらに
Position:abusolute
で修正
____________

調べたこと

imgタグにおけるalt
Altタグには、この画像はなんの画像かを
わかりやすくするのに記述するタグ
https://www.kanzaki.com/docs/html/htminfo-alt.html#formula
参考ページ

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