- 投稿日:2020-03-25T23:55:39+09:00
チーム開発 (決済ライブラリ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
- 投稿日:2020-03-25T22:58:50+09:00
Ruby 文字列関連まとめ、およびシンボルとの違い
1. はじめに
Rubyの文字列は
"hoge"
、'hoge'
、:hoge
など非常にややこしいと感じているのは自分だけでしょうか。
コロンがついている:hoge
は正しくは文字列ではなくシンボルと呼ばれているものですが、非常に文字列と似ているため最初のうちは混乱すると思います。
本記事では主にこの3つについて解説します。
さらに、%Q(hoge)
や%W(hoge)
なんてものもありますが、こちらは使いどころが多くないため、簡単な解説に絞ります。2. "hoge"と'hoge'
ご存知、文字列の基本ですね。ダブルクオーテーションやシングルクオーテーションで囲うことで文字列であることを表します。ほとんどの言語で使われているので知っていると思います。
"hoge"と'hoge'のどちらを使うか
基本的にはどちらを使っても良いです。しかし、Rubyの使用上、「特殊文字」「式展開」を使うときは""で囲う必要があるため日頃から""を使うことをおすすめします。
特殊文字、式展開については以下で説明します。特殊文字
\n
(改行)や\t
(タブ)などのこと。(他にもあるので調べてみてください。)
以下の様にhello worldの中に特殊文字を入れた例で見てみる。hello.rbputs "He\nllo wor\tld" puts 'He\nllo wor\tld'実行結果は以下の様になります。
He llo wor ld He\nllo wor\tldダブルクォーテーションの方は改行とタブが入っていて、シングルクォーテーションの方は文字がそのまま出ていることがわかります。
式展開
#{}
で囲むことで変数を出力したり、計算結果を出力できたりする。price.rbprice = 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.rbputs "私は\"山田\"です。" puts %Q(私は"山田"です。)また、文字列の配列を定義するときに有用なのが
%W()
で、以下の2つは同じ配列を表します。
コンマ区切りでなくなっていることに注意しましょう。sample.rbputs [ "red", "blue", "yellow" ] puts %W( red blue yellow )こんなものもあるんだ程度に覚えていれば大丈夫でしょう。
5. さいごに
Rubyには、「使いこなせると便利だけど最初のうちは意味がわからない」ということが多い様に思います。シンボルもその一つでしょう。
基本的には文字列と変わらないけど、ハッシュのキーなどの文字列自体に意味のないものに使うべしと覚えましょう。質問などございましたらお気軽に。
@ruemura3
- 投稿日:2020-03-25T20:28:58+09:00
【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.rbFactoryBot.define do factory :item do end end
- 次に、カラムを列挙します。
factories/items.rbFactoryBot.define do factory :item do name {"レモン"} # {}で囲っているとfactoryが生成された時に評価される price {3000} end end使い方、属性の上書きの仕方
create(:item)
create(:item, name: "リンゴ")アソシエーションの仕方
例えば、itemとuserが紐づいている場合、以下のように書く。
(userのfactoryBotも作成している必要がある)factories/items.rbFactoryBot.define do factory :item do name {"レモン"} price {3000} association :user, factory: :user # アソシエーション end end
association :user, factory: :user
の部分は単にuser
と省略できるfactories/items.rbFactoryBot.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 endafter(:build)を使用する必要があります。
これで、imageを一枚紐づけることができます。factories/items.rbFactoryBot.define do factory :item do name {"レモン"} price {3000} user after(:build) do |item| item.images << create(:image) end end endafter(: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.rbFactoryBot.define do factory :user do name {"テストユーザー"} email { Faker::Internet.free_email } end end
- 投稿日:2020-03-25T20:20:16+09:00
sidekiqを使って非同期にメール送信処理をする方法
詰まったところ
sidekiqで非同期にメール送信処理を実行する様に実装していたら、メール送信のジョブが待機状態のまま実行されない不具合に遭いました。
結論・解決方法
公式にある通り、以下のコードを実行。
https://github.com/mperham/sidekiq/wiki/Active+Job#action-mailerbash$ bundle exec sidekiq -q default -q mailers次に、
deliver
やdeliver_now
をdeliver_later
に変更。
deliver_later
とは、非同期にメール送信処理を行ってくれるメソッドである。
逆に、deliver
とdeliver_now
は同期的にメール送信するメソッドである。〇〇_mailer.rb〇〇Mailer.deliver 〇〇Mailer.deliver_now↓
〇〇_mailer.rb〇〇Mailer.deliver_laterこれで治るはず(知らんけど)。
もしDockerを使っていたら
sidekiqコンテナをビルドする際に以下コマンドを実行させる。
docker-compose.ymlsidekiq: 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
へアクセスすることで、ダッシュボードからエラーログの確認ができます。
エラーログを見ると、どの様なエラーが吐かれているかを確認できます。
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
することで解決しました。
みなさんの問題解決に繋がったら幸いです。
- 投稿日:2020-03-25T20:20:16+09:00
sidekiqを使って非同期にメール送信処理をする方法。メール送信ジョブが待機状態のまま実行されない不具合の解決方法
結論・解決方法
公式にある通り、以下のコードを実行。
https://github.com/mperham/sidekiq/wiki/Active+Job#action-mailerbash$ bundle exec sidekiq -q default -q mailers次に、
deliver
やdeliver_now
をdeliver_later
に変更。
deliver_later
とは、非同期にメール送信処理を行ってくれるメソッドである。
逆に、deliver
とdeliver_now
は同期的にメール送信するメソッドである。〇〇_mailer.rb〇〇Mailer.deliver 〇〇Mailer.deliver_now↓
〇〇_mailer.rb〇〇Mailer.deliver_laterこれで治るはず(知らんけど)。
もしDockerを使っていたら
sidekiqコンテナをビルドする際に以下コマンドを実行させる。
docker-compose.ymlsidekiq: 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
へアクセスすることで、ダッシュボードからエラーログの確認ができます。
エラーログを見ると、どの様なエラーが吐かれているかを確認できます。
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
することで解決しました。
みなさんの問題解決に繋がったら幸いです。
- 投稿日:2020-03-25T19:06:50+09:00
field_with_errorsによるレイアウト崩れを防ぐ
実装したいこと
新規会員登録機能バリデーションを設定し、入力項目にエラーがある場合はエラーメッセージとともに、入力ページに戻るように設定してします。
この時下の写真のようにレイアウト崩れが発生しますので、この事例について修正します。初回入力時
再入力時(レイアウト崩れ)
レイアウト崩れの原因
この事象の原因は、エラーが発生している時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:"例)タロウ"解決方法
上記の事象に対する解決方法は2つが考えられます。
- 自動で読み込まれる
field_with_errors
タグを読み込まないように設定する。field_with_errors
に対してCSSをあてる今回は
field_with_errors
タグを読み込まないようにする方法で、以下のようにapplicaton.rb
を編集します。applicaton.rbmodule FreemarketSample65d class Application < Rails::Application #以下を追加 config.action_view.field_error_proc = Proc.new { |html_tag, instance| html_tag } end end
- 投稿日:2020-03-25T19:02:36+09:00
ピュア 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 を書いてみました。
- 投稿日:2020-03-25T18:30:29+09:00
【Rails】テーブル同士を紐付けする手順を解説していく!
はじめに
Railsの学習のアウトプットとして簡単な掲示板を作成している際に、「データの紐付け」にて躓いたので
「紐付け」を行う目的とその手順について、自分用にまとめてみようと思います!※「ログイン機能」が実装してある事を前提に解説していきます。
紐付けとは何ぞや
テーブル同士を関連付ける事で、テーブル同士でデータを取得できるようにする作業。
紐付けをする目的
掲示板作成を例として、上述した二つのテーブルを
・「投稿用のテーブル(postsテーブル)」
・「ユーザーのテーブル(usersテーブル)」
に置き換えることにする。例えば
投稿テーブルには、投稿内容のデータ(カラム名をcontentとする)が保存され
ユーザーテーブルには、ユーザーの名前のデータ(カラム名をnameとする)が保存されているとします。
そうすると以下のように表せます。postsテーブル
id content 1 タピオカなう! 2 学校めんどい…… usersテーブル
id name 1 田中 2 鈴木 しかしながら上記2つのテーブルは別物であるため
投稿内容を見ただけでは、誰がどんな投稿したのかという判別がつきません。そうなると今度は、誰がその投稿をしたのかわかるようにさせたい訳です。
要するにデータの紐付けを行う目的とはこの例で言えば
誰がどの投稿をしたのかを区別することになります。紐付けを行う手順
それでは早速、掲示板作成を例として実際に紐付けを行ってみます。
しかし、いきなり紐付けしようとしても、何をどうすれば良いのかサッパリだと思いますから
何をどうしたら良いのか、その思考手順を挙げつつ実装を行っていきます。
1.まず、実装したい機能を挙げてみる
➡︎投稿ごとにユーザーを区別したい。
2.その機能を明確にしてみる
➡︎投稿ごとに、誰がその投稿をしたのかわかるように、ユーザー名を表示させる。
3.Railsにおけるデータのやり取りの仕組みを思い出す
➡︎ブラウザで何かしらの情報を表示させるためには、該当のアクション内で、データベースからデータを取得するための記述をする必要がある。
➡︎今回は「投稿に対して」、その投稿を行った「ユーザーの情報を」表示させたいので、投稿用のアクション内で、ユーザーのデータを取得すればいい。例
posts_controller.rbdef #(投稿表示用のアクション名) @post = Post.find_by(id: params[:id]) #ユーザーのデータを取得する処理 end4.ユーザーのデータを取得する(?)
➡︎それでは早速、ユーザーのデータを取得してみます。
例
posts_controller.rbdef #(投稿表示用のアクション名) @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.rbdef #(投稿表示用のアクション名) @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.rbdef #(新規投稿アクション名) @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.rbdef #(投稿表示用のアクション名) @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におけるデータのやり取りについて慣れることが先決と考え、このように遠回りするような理解の仕方を取っています。
おかしな点などありましたら、その都度仰って頂けるととても幸いです。参考
- 投稿日:2020-03-25T17:51:52+09:00
Rubyでエミュレータをつくりたい
はじめに
Rubyでx86 CPUエミュレータを作る話をします。
エミュレータ作成を通し、
CPUの動き、機械語やアセンブリ言語など、低レイヤーの知識をつけることを目的としています。
今回はまともなプログラムは動かせません。
"Hello, World"もできません。参考書籍
前提知識
レジスタ
レジスタは、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の範囲内を飛ぶことができます。
- 投稿日:2020-03-25T17:49:00+09:00
accepts_nested_attributes_forを使わずに複数リソースを同時登録する(クイズアプリを例に)
想定読者
Railsを使ってポートフォリオを作成している方
特にクイズアプリを作成している方どういうときの話
私はクイズアプリを作ってるときにこの問題に当たりました。
一つのフォーム画面で、
①問題文
②選択肢1
③選択肢2
④選択肢3
⑤選択肢4
を同時に登録させたいとします。で、モデルはこんな感じです。
app/models/question.rbclass Question < ApplicationRecord has_many :choices, dependent: :destroy endapp/models/choice.rbclass Choice < ApplicationRecord belongs_to :question enddb/migrate/時間_create_choices.rbclass 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_formclass 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.rbclass 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つのフォームオブジェクトがあります。笑間違い等あればご指摘頂けると嬉しいです。
- 投稿日:2020-03-25T17:41:09+09:00
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..102.今度はRangeクラスとnewメソッドを使って、1から10の範囲オブジェクトを作ってみてください。ヒント: newメソッドに2つの引数を渡す必要があります
b = Range.new[1..10] => 1..103.比較演算子==を使って、上記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 => nilStringクラスのスーパークラスは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 endattr_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 end2."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章に時間かけすぎましたかね。どんどん行きます。
- 投稿日:2020-03-25T17:36:23+09:00
Ruby on Railsの開発環境(仮想環境)の構築方法
はじめに
現在のバージョン:
macOS Catalina 10.15.3Ruby on Railsで環境構築を実施してみました。自身のPC(ローカル)に直接環境を構築すると現在のPCの設定に影響を与えてしまう可能性がある為、仮想環境を作りました。
仮想環境について
仮想環境とは自身のPCとは別のPCを仮想的に作ることです。(1台のPCで複数のPCを扱えるイメージ)
仮想環境は不要になればすぐに削除が可能です。仮想環境を構築
VirtualBoxをインストール
VirtualBox(バーチャルボックス)をインストールして、仮想環境を構築。
macOS:VirtualBox(6.0.14)ダウンロード
ダウンロード後開くと下記の画面になります。
「1」をダブルクリックして、インストール実施完了後「Applications」フォルダに追加されてます。インストール後以下のコマンドでバージョンが表示されるか確認。
$ vagrant box list
centos/7 (virtualbox, 1905.1)Vagrantをインストール
Vagrant(ベイグラント)をインストールして、仮想環境を管理・構築をします。Windows、Mac OS X、Linuxで動作します。
インストール後以下のコマンドでバージョンが表示されるか確認。
$ vagrant --version
Vagrant 2.2.4※macOSのバージョンによっては、以下の設定が必要になります。
アップルメニューから「システム環境設定」→「セキュリティとプライバシー」アイコンクリック→「一般」タブクリック→「開発元を確認できないため、開けませんでした」の「このまま開く」をクリックする。vagrant-vbguestプラグインをインストール
「vagrant-vbguest」は自分の環境と仮想環境のバージョンを自動で合わせてくれるVagrantのプラグインです。
Vagrant起動の準備をする際に、「vagrant-vbguest」プラグインのインストールがあると面倒な作業を肩代わりしてくれます。・インストール方法
ターミナルで以下コードの入力をする。
$ vagrant plugin install vagrant-vbguest
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時間はかかります。完了後、以下のコマンドでシャットダウンして導入が完了です。
$ vagrant halt
※再度起動する際はVagrantの起動コマンド
$ vagrant up
を実行してください。
- 投稿日:2020-03-25T17:08:36+09:00
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 └── Gemfilesample_controller.rbdef 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 endpdf_for_sample.html.slimdoctype 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# 空っぽGemfilegem 'wicked_pdf' gem 'wkhtmltopdf-binary'layoutとtemplateを使い分ける
最初は違いがわかりにくかったのですが、
layout
のファイルは共通、template
は個別のHTMLを描写するものとし、layout
からtemplate
を呼び出す作りにするのが良さそうです。pdf_for_sample.html.slimdoctype 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 = yieldsample.html.slim.sample .sample-text コンテンツA4横向き&pdfファイル全体に背景画像を使いたい
筆者の場合A4かつ横向きで生成する必要があったので、
page_size
とorientation
を指定します。
(page_sizeはデフォルトでA4なので指定は不要ですが、可読性のため明示的に指定しました)sample_controller.rbdef 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.rbdef 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対応版のブランチがあるようなので、指定したら正常に動作しました。Gemfilegem '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-fontssample.sass.sample background: sample.png background-size: cover height: 991px .sample-text font-family: 'IPAMincho', 'IPA明朝'また、
serif
、san-serif
は、フォントをインストールしない状態でも描画可能だったので、試してみるといいかもしれません。最終形
上記をまとめると以下になります。
sample_controller.rbdef 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 endpdf_for_sample.html.slimdoctype 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 = yieldsample.html.slim.sample .sample-text コンテンツsample.sass.sample background: sample.png background-size: cover height: 991px .sample-text font-family: 'IPAMincho', 'IPA明朝'Gemfilegem 'wicked_pdf' gem 'wkhtmltopdf-binary', git: 'https://github.com/entretechno/wkhtmltopdf_binary_gem', branch: 'amazon-linux'
- 投稿日:2020-03-25T16:14:21+09:00
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
- 投稿日:2020-03-25T15:52:22+09:00
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? => trueRubyでは、メソッドが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" => nilRubyでは、あらゆるものがオブジェクトである。したがって、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 => 72.reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
> "racecar".reverse => "racecar"3.変数sに "racecar" を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
> s==s.reverse => true4.リスト 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 >> end2.上で定義したメソッドを使って “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. => nil3.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! => nil4.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 => nil3.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 => true2つ目の記法は、シンボルとハッシュロケットの組み合わせを、次のようにキーの名前の (前ではなく) 後にコロンを置き、その後に値が続くように置き換えたもの.
{ :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章目の続きから行きます。
- 投稿日:2020-03-25T15:06:53+09:00
undefined method current_sign_in_atエラーが発生したのでやってみたこと。
ログインを実行すると、このようなエラーが発生。
エラー内容からuserモデル内のカラムにcurrent_sign_in_atがないのが原因のようだ。
というわけで、一度マイグレーションファイルをrails db:rollback
マイグレーションファイル内のcurrent_sign_in_atのコメントアウトを外し、raiis db:migrate
再度ログイン!
今度はこんなエラーが発生。
last_sign_in_atがカラムないにないらしい。 どうせなら一度で言って欲しいものだが、、、、
同様の作業を行い、raiis db:migrate
今度は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をし、ログイン!
無事にログインできましたとさ
原因はよくわかりません。ただエラー解決できたのは一つの自信になったかな。
- 投稿日:2020-03-25T14:05:21+09:00
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-projGemfile に以下を追加して、
gem 'graphql' gem 'graphiql-rails'bundle install すればOK。
$ bundle installGraphql の初期化
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.rbRails.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.rbrequire "sprockets/railtie"ですが、これだけでは実行時にエラーになります(Loding...から進まなくなる)。そこで次の設定を追加します。ファイルがなければ追加しましょう。
app/assets/config/manifest.js//= link graphiql/rails/application.css //= link graphiql/rails/application.jsGraphQL クエリの実装
サンプル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.rbmodule Types class UserType < Types::BaseObject field :id, ID, null: false field :name, String, null: false field :email, String, null: true end endGraphQL クエリの作成
GraphQL クエリを作成します。app/graphql/types/query_type.rb を以下のとおり実装します。
app/graphql/types/query_type.rbmodule 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 が表示されます。
クエリ構文の確認や、クエリを実行して確認できます。
まとめ
Ruby on Rails で簡単なGraphQL type とクエリを実装し、GraphiQL から操作してみました。GraphiQL は開発中だけでなくドキュメントとしても使えそうなので、GraphQL 開発では必須になりそうです。
- 投稿日:2020-03-25T13:03:48+09:00
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記事を参考
とても助かりましたありがとうございます。エラー②内容
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の反映がうまくできてなかったみたい。
解決法
ターミナルの再起動をする!
結果はしょうもないことでしたが、 同様の症状になっている方が見えたら一度ターミナルを再起動して見てください。
- 投稿日:2020-03-25T12:04:03+09:00
シークエント計算の理論を勉強してみたので紹介
(※ この記事は、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つのシークエント(帰結)
このサイトから定義を拝借
(φ,ψはシークエント、 Γ、Δ、Π、Σはシークエントの列)
シークエント計算による演繹
初期シークエント $\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
補足: 理論が包含していることのうち、省略したこと
- 推論規則自体が"正しい"規則なのかどうかの確認
- プログラムの作り方の確認
- プログラムの停止性の確認
- プログラムの出力が"正しいこと"の確認
- 直観主義論理(排中律のない論理)の取り扱い方
- 投稿日:2020-03-25T11:57:26+09:00
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
- 投稿日:2020-03-25T11:46:54+09:00
画像投稿機能実装時のNil location provided. Can't build URIエラー対処
- 投稿日:2020-03-25T10:14:09+09:00
#Ruby でカンマ区切りの文字列をメソッドの複数引数として渡す例
- 投稿日:2020-03-25T01:46:14+09:00
商品出品画面を作る②ActiveStorage(複数画像投稿)
ActiveStorageを使用して複数枚の画像を投稿する
先ずはここを見てくださいRailsガイド、導入方法とか書いてあります。
S3に接続するときは注意が必要ですね。そもそもActiveStorageは何がいいのか?
ActiveStorageの特徴としては画像をitemモデルとして扱えることでしょう。
どう言う事かと言うと、itemとimageが1:多の関係だとすると、テーブルを2個用意しないといけません。
そしてアソシエーションを組んで〜と言う処理をします。
削除や変更の時に処理が若干複雑になってしまいます。
そんな時、ActiveStorageならitemテーブルのみを対象に処理をすれば良いわけです。今回の要件
- 複数同時選択による複数枚の画像投稿
- 複数回選択による複数枚の画像投稿
- プレビュー表示
- 投稿枚数管理
- プレビューの削除
imet.rbhas_many_attached :imagesActiveStorageで複数画像を扱う為にモデルに記述
items_controller.rbdef 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") enddef 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][]"になっていると思います。
これにより、選択した画像を配列とし保持できるようになり、複数枚を同時に選択できるようになります。
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}"の部分です。
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属性にも手を加える必要があります。
それは今後試すことにします。今回は以上です
- 投稿日:2020-03-25T01:42:48+09:00
SNS認証ログインでNot found. Authentication passthru.が表示されたときの対処方法
執筆背景
先日、個人アプリ(フレームワークにはRuby On Railsを使用)をリモートリポジトリーにプッシュした際GitHubからgem "OmniAuth"の脆弱性について指摘された。
詳細 → https://github.com/omniauth/omniauth/pull/809そのため、それに対応するgem "omniauth-rails_csrf_protection"をインストールした。
Gemfilegem 'OmniAuth' #削除 gem 'omniauth-rails_csrf_protection'bundle installローカル上でアプリを再起動すると以下のようなエラーが発生したため、対処方法を示す。
対処方法
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の中にあります。
- 投稿日:2020-03-25T01:03:46+09:00
ユーザーの新規登録時に入力なしの初期値を設定する【Ruby on Rails】
目的
ユーザーの新規登録時に、直接入力させずにテーブルに初期値を保存させること。
使用する技術
- gem devise
実践
以下のテーブルが作成してあるとします。
Column Type Options username string null: false, unique: true string null: false, unique: true password string null: false coin integer null: false 今回は、ユーザー登録フォームで
- username
- password
の3項目をのみを入力させます。
ここで、coinカラムは入力させずに、初期値0としてテーブルに保存します。事前準備
すでに、gem deviseが導入され、コントローラとビューの作成が終了しているものとします。
(このような画面まで用意されている所からです)
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.rbRails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations', } enddevise導入時に自動的に記述された
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.rbclass 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メソッドのどちらかに初期値を代入するだけで簡単に保存させられます。参考
- 投稿日:2020-03-25T00:44:13+09:00
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/
- 投稿日:2020-03-25T00:26:06+09:00
チーム開発 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 endaccepts_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
参考ページ