- 投稿日:2020-05-25T23:54:48+09:00
正規表現の基礎
概要
irbを使って正規表現について見ていきます。
irb
irb(main):001:0> name = "taro" => "taro" irb(main):002:0> name.sub(/taro/,"kotaro") => "kotaro"subメソッドを使ってtaroという文字をkotaroに置き換えています。
第1引数に置き換えたい文字列を指定し、第2引数に変換後の文字列を記述します。
irb(main):004:0> name.match(/taro/) => #<MatchData "taro"> irb(main):005:0> name.match(/bob/) => nilmatchメソッドを使って引数に指定した文字列が含まれているかをチェックしています。
含まれていた場合は指定した文字列がMatchDataオブジェクトとして返り値で返ってきます。
含まれていなかった場合はnilが返ってきます。
irb(main):006:0> array = name.match(/taro/) => #<MatchData "taro"> irb(main):007:0> array[0] => "taro"MatchDataオブジェクトは配列なので、上記のようにすると値を取得することができます。
irb(main):008:0> phoneNumber = "080-1234-5678" => "080-1234-5678" irb(main):009:0> phoneNumber.gsub(/-/,"") => "08012345678"電話番号のハイフンを取り除きたい場合ですが、subメソッドを使ってしまうと最初のハイフンしか置換されないので、gsubメソッドを使いましょう。gとはグローバルマッチで、指定した文字列が複数含まれている場合全て置換してくれます。
irb(main):010:0> myPassword = "Taro0123" => "Taro0123" irb(main):012:0> myPassword.match(/[a-z\d]{8,10}/i) => #<MatchData "Taro0123"> irb(main):013:0> myPassword.match(/[a-c\d]/i) => #<MatchData "a"> irb(main):014:0> myPassword.match(/[a-c\d]{8,}/i) => nil・[a-z]は、a~zの文字がいずれか1個にマッチ
・\dは、数字にマッチ
・{8,10}は、直前の文字列が少なくとも8回、多くても10回出現するものにマッチ
・iは、大文字と小文字を区別しないで検索する
という意味です。
irb(main):015:0> myAddress = "yey@gmail.jp" => "yey@gmail.jp" irb(main):016:0> myAddress.match(/@.+/) => #<MatchData "@gmail.jp"> irb(main):017:0>メールアドレスのドメインを取得する場合
.は、どの1文字にもマッチ
+は、直前の文字の1回以上の繰り返しにマッチ
という意味を持ちます。
ですので@.+ とすると
@から始まり、全ての文字にマッチ、それを繰り返す。ということになるので
@以降のドメインが取得できます。この記事を読んでいただきありがとうございました。
- 投稿日:2020-05-25T22:29:38+09:00
【Rails】コメントされると投稿が削除できない!?
投稿の経緯
Twitter、FbのようなSNSを作成中のことです。
投稿機能に削除ボタンを付けたのですが…投稿が消せない!!もっと細かく言うと、ただの投稿は消せる。
投稿に写真を載せたり(postモデルとimageモデルで分けている場合に限る)
投稿にコメントをされたりすると消せなくなる。。【本日の登場人物】
(親)postモデル →投稿を保存するモデル
(子)imageモデル →投稿に紐づく画像を保存するモデル
(子)commentモデル →投稿に対するコメントを保存するモデルつまり、postモデルにネストしている子モデル(imageモデル、commentモデル)に値が入るとpostレコードを削除できなくなってしまう訳ですね。
実際のコードを見てみましょう。
実際のコード
post.rbclass Post < ApplicationRecord belongs_to :user has_many :comments has_many :images end何の変哲もないpostモデルのアソシエーションですね。
ただ、このままだと、子モデルに値が入ると親モデルが消せなくなります。
なぜなら、親が消えてしまったら、子の親がいなくなってしまうから。。当たり前ですね笑つまり、親を削除した時に、子も一緒に削除される設定をしてしまえばいいんです。
解決策
モデルで「dependent: :destroy」を設定してあげましょう。
dependent…翻訳すると「依存」ですね。イメージ湧きやすいかと思います。これをアソシエーションの時に設定されたモデルは、そのクラスが削除されると、一緒に削除されるようになります。
何言ってるかわからないですね、実際に下に書いてみます。解決後のコード
post.rbclass Post < ApplicationRecord belongs_to :user has_many :comments, dependent: :destroy has_many :images, dependent: :destroy end何となくわかりましたかね?
Postクラス で imageモデル と commentモデルに設定していますね。
postモデル(投稿)が削除されるとアソシエーションでdependent: :destroyを設定されたモデル(画像、コメント)も一緒に削除される訳ですね。注意点
これに関して注意してもらいたいことがあります。
実はこのdependent: :destroyって子モデルを削除するための設定じゃないんです。アソシエーションを削除するための設定なんです。
例えば、commentモデルに以下のとおり記載してみます。
comment.rbbelongs_to :post, dependent: :destroyこうなってしまうと、コメントを削除したら投稿まで削除されてしまいます。
もう一度いいます。
アソシエーションの時に設定されたモデルは、そのクラスが削除されると、一緒に削除されるようになります。まとめ
今回も私の拙い説明を最後まで読んでいただきありがとうございました。
間違いや補足がありましたらコメントいただけると嬉しいです。どんどはれ。
- 投稿日:2020-05-25T21:20:20+09:00
Railsの基本理念
Railsの生みの親DHHが書いた基本理念をまとめました
https://rubyonrails.org/doctrine/#optimize-for-programmer-happiness
https://postd.cc/rails-doctrine/1. プログラマの幸福度を最適化
DHHがRailsを作った理由は「自分が笑顔になるため」
論理的ではなくむしろ病的だが、自分が幸福になるために作ってきた
Railsのコミュニティは幸福のために集まり、幸福の最適化によってRailsは形成されてきた
2. 設定より規約 (Convention over Configuration、CoC)
Railsの生産性に関する初期のモットーは「無駄な個性をやめれば、平凡な決断で苦労することなく、重要な場面で仕事のスピードが増す」データベースの主キーがidか、postIdか、posts_idか、pidか議論するのは無駄設定から規約に移行することで、無駄な熟考を避け生産性を高めることができる
加えて、規約は初心者のハードルを下げてくれるRailsの規約の中には初心者が知らなくてもいいものがたくさんある彼らにはその無知ゆえのメリットがある全てを知らずともすばらしいアプリケーションを作ることができるフレームワークがただの分厚い教科書であればそれは不可能
そうすると、「全て既存のテンプレートで作れる」と思いがちだが、たいていは5%や1%でも独自の要素を持つ価値がある
規約からいつ逸脱するかは難しい逸脱したいという衝動のほとんどは熟考されておらず、レールを外れて行くことのコストは過小評価されている
- コメント
あまり気にしたことなかったですが、Ruby on Rails の名前は「規約 (Rail)に沿ったRuby」って意味なんでしょうねhttps://jp.quora.com/Ruby-Rails-no-namae-no-yurai-wo-oshie-te-kuda-sai
3. メニューはおまかせで
プログラミングにとって、他の人に任せることの利点は、設定より規約から得られるものと似ているが、より高いレベルにある。CoCが個々のフレームワークをどのように使うのがベストなのかを考えるのに対し、おまかせはどのフレームワークをどのように組み合わせて使うのかを考える。これは、利用可能なツールを個々の選択肢として提示するという崇高なプログラミングの伝統に反するしかし、その伝統が個々のプログラマに決定する特権(と重荷)を与えてきた「仕事に最適な道具を使え」という言葉は議論の余地がないほど初歩的なことだが、「最高の道具」を選ぶには、「最高」を自信を持って判断できる土台が必要であり、これは思った以上に難しい。
そこで私たちは、Railsでは、全員にとって良いツールボックスのために、箱の中の各ツールを選ぶというプログラマーの個人的な特権を1つ減らすことにした。その結果、多くの利益を得ることができた。
1.みんなが使っていれば安全
ほとんどの人が同じデフォルトでRailsを使っているとき、経験を共有できる。共通のベースがあると、人に教えたり助けたりするのが格段に楽になる。それはより強いコミュニティの感覚を育む。
2.共通の基本的なツールボックスを人々が進化させる
フルスタックフレームワークとして、Railsは動的な部分も数多くありますし、それぞれが単独でどのように機能するのかと同じくらいに、それらがどのように組み合わさって機能するのかというのが重要になる。ソフトウェアの不具合はそれぞれのコンポーネントが原因ではなく、それらの相互作用の中で発生する。構成された複数のコンポーネントから、共通の不具合を緩和しようとみんなで取り組み、同じ状態で失敗するならば、不具合の回数を減らすことができる。
3.交換は可能だが、必須ではない
Railsは“おまかせ“スタックである一方で、他のフレームワークやライブラリと取り替えることもできる。しかし、必須ではない。つまり、たまに発生する相違に適した、明確で個人的なパレットをのちに開発することもできる。経験もあり腕のあるプログラマでさえ、メニューの中全てのものを気に入らないことは十分にある(もし、全てに満足している人がいるならば、それはRailsでまだ行き詰まっていないのでしょう)。彼らは勤勉にも別のものに交換し、それからみんなが監督し共有している他のスタックにいってみて、楽しんでいる。
4. パラダイムは1つではない
「Railsは多くの異なる考え方からできている」以外何も分からなかった…
5. 美しいコードを称える
私たちは、コンピュータや他のプログラマーに理解されるためだけにコードを書くのではなく、美しさの温かな光を浴びるためにコードを書きます。美しいコードはそれ自体が価値であり、精力的に追求されるべきものです。だからといって、美しいコードが常に他の事に勝るというわけではありませんが、優先順位には必ずあるべきです。
class Project < ApplicationRecord belongs_to :account has_many :participants, class_name: 'Person' validates :name, presence: true endこれは DSL のように見えますが、実際にはただのクラス定義で、シンボルとオプションを取る 3 つのクラスメソッドを呼び出しています。派手なものは何もありません。しかし、確かにきれいで、シンプルです。この数少ない宣言から、膨大な量のパワーと柔軟性を得ることができます。
美しさの一部は、これらの呼び出しが「設定より規約」を尊重していることから来ています。belongs_to :accountを呼び出すとき、外部キーはaccount_idと呼ばれ、それがProjectsのテーブルにあることを前提としています。Personのクラス名をparticipantsのアソシエーションの役割に指定する必要がある場合、そのクラス名の定義のみが必要です。そこから外部キーと他の設定が得られます。
class CreateAccounts < ActiveRecord::Migration def change create_table :accounts do |t| t.integer :queenbee_id t.timestamps end end endこれがフレームワークの力の本質です。プログラマは特定の規約(例えば、#changeを実装したActiveRecord::Migrationのサブクラス)に従ってクラスを宣言します。そうするとフレームワークは、それをひと通り全部調べて、これが呼び出すメソッドだと認識します。
これにより、プログラマは書くべきコードが非常に少なくなります。マイグレーションの場合、rails db:migrateを呼び出してDBをアップグレードして新しいテーブルを追加するだけでなく、rails db:rollbackでこのテーブルを削除することもできます。これは、プログラマーがこれらのことを実現するために、ライブラリを使ってワークフローをつなぎ合わせるのとは大きく異なります。
6. 鋭いナイフを提供する
Rubyは機能の中に鋭いナイフをたくさん含んでいます。それは偶然ではなく、設計上のものです。最も有名なのはモンキーパッチです。Railsで提供されるナイフはRubyで提供されるものほど鋭くはありませんが、それでも切れ味は十分です。私は、すべてのプログラマには、権利とまではいかないまでも、完全に有能なRubyとRailsのプログラマになる道があると信じています。そして、有能というのは、状況に応じて、いつ、どのように、引き出しの中のさまざまな、時に危険なツールを使うべきかを知ることができる知識を持っているという意味です。
7. 統合システムを尊重する
Railsは様々な文脈で利用できますが、一番は統合システムを作ることです。 壮大なモノリス!問題全体に対処するシステム全体です。つまり、RailsはフロントエンドのJavaScriptから、DBまで、すべてに関わっているということです。これは非常に広い範囲ですが、一人の人間が理解するのに非現実的ほど広い範囲ではありません。Railsは特に、ジェネラリストの個人がこれらの完全なシステムを作れるようにすることを目指しています。個人に力を与えることに焦点を当てているのが、統合されたシステムなのです。1つの統合システムとしての使いやすさと分かりやすさを備え、個別にチューニングされ分散されたアプリケーションの力を得ることができます
- コメント
あまりマイクロサービスには賛成じゃなさそうですね
8.安定性より進歩
Railsのようにシステムが10年以上前から存在している場合、自然と固定化していく傾向にあります。過去の挙動に依存していた人にとって、どんな変更でも問題になる可能性があるのは当然です。しかし、あまりにも保守的になっていると、反対側に何があるか見えなくなります。進化と成長のためには現状を壊し変更する必要があります。進化することで、これからの数十年Railsが生存と繁栄をすることができます。
Railsのバージョンも上がっていっていますが、Rubyの新しいバージョンを素早く採用することで、Rubyの発展のために先頭を切って貢献するべきです。
進歩とは、結局変化を推し進めようとする人とその意欲にかかっていることがほとんどです。Rails CoreやRails Committersのようなグループに無期限の席がないのはこのためです。どちらのグループも、フレームワークの進歩に積極的に取り組んでいる人たちのためのものです。同様に、コミュニティの新しいメンバーを歓迎し、奨励し続けることが非常に重要な理由です。私たちは、より良い進歩を遂げるために、新鮮な血と新鮮なアイデアを必要としています。
9. テントを押し上げる
Railsには異論のあるアイデアがたくさん取り入れられているので、もしあらゆる考え方に対して完全に従うよう常に皆さんに求めていたら、Railsは思想的な隠者が集まるグループとしてすぐに孤立してしまうでしょう。ですので、それはありません!意見の相違が必要です。方言が必要です。思想や人の多様性が必要なのです。このアイデアのるつぼの中にこそ最高の共有物があるのです。私がしばしば重大な不満を表明してきたテスト用DSLであるRSpecの継続的な成功は、完璧な証拠です。私はなぜこれではいけないと思うのか、顔が真っ青になるまでわめき散らすことができますが、それでもRSpecはまだ花開き、繁栄することができます。その点の方が遥かに重要なのです
APIとしてのRailsの登場についても同じことが言えます。私の個人的な焦点とこだわりは、ビューを含む統合システムにありますが、クライアントやサーバを先行して配布したいと考えている人には、Railsがうまくいく余地があることは間違いありません。
大きなテントを持つということは、すべての人に万能であろうとすることではありません。すべての人を歓迎し、自分の飲み物を持ってくることを許可するということだ。他の人にも参加してもらうことで、私たちの魂や価値観を失う必要はないし、新しいおいしい飲み物の混ぜ方も学べるかもしれない。これはタダではできません。歓迎するためには努力が必要です。特に、あなたの目標が、コミュニティの既存メンバーと同じような人をさらに集めることだけが目的でない場合は、なおさらです。参入障壁を下げることは、常に真剣に取り組むべき仕事です。ドキュメントのスペルミスを修正するだけで始めた次の人が、いつ次の素晴らしい機能を実装することになるかわかりません。しかし、あなたが微笑んで、どんな小さな貢献にも感謝することで、モチベーションを高めることができるかもしれません。
- 投稿日:2020-05-25T20:50:20+09:00
100日後に1人前になる新人エンジニア(5日目)
100日後に1人前になる新人エンジニア(5日目)
どうもこんばんは早くも5日目になりました。
今日のお題はRailsのroutesに関してです。
調べてみると結構いろんなルールがありましたのでまとめてみたいと思います。Rails のルーティング
Railsルーター
Railsのルーターは受け取ったURLを認識し、適切なコントローラ内アクションに割り当てるよ。
GET /patients/17こんなURLでリクエストがあったとき
get '/patients/:id', to: 'patients#show'patientsコントローラのshowアクションに割り当てられられています。
17の部分は :idに対応しているよ。
to: 以降は 'コントローラー#アクション' って感じで対応付けがされています。ResourceベースのRouting
関連するいろんなリクエストを1つのアクションにまとめたやつです。
resources :usersこんな感じでroutesに一行書くだけで以下のrouteが追加されます
HTTP パス コントローラ#アクション GET /users users#index GET /users/new users#new POST /users users#create GET /users/:id users#show GET /users/:id/edit users#edit PATCH/PUT /uses/:id users#update DELETE /users/:id users#destroy コントローラの名前空間とrouting
名前空間によってグループ化することができるよ。
namespace :admin do resources :users end
HTTP パス コントローラ#アクション GET /admin/users admin/users#index GET /admin/users/new admin/users#new POST /admin/users admin/users#create GET /admin/users/:id admin/users#show GET /admin/users/:id/edit admin/users#edit PATCH/PUT /admin/uses/:id admin/users#update DELETE /admin/users/:id admin/users#destroy こうすることで/adminから始まるグループでルーティングを作成することができるんだね。
ちなみにresourcesでいくつかのroutesを選択して使用したい場合は
resources :articles do resources :comments, only: [:index, :new, :create] endこんな感じで only:以降に使用したいroutesを指定すれば良い。
これで必要のないroutesを指定することもなさそうだね。名前付きrouting
:asオプションを使うと、どんなルーティングにも名前を指定できます。
get 'exit', to: 'sessions#destroy', as: :logout上のルーティングではlogout_pathとlogout_urlがアプリケーションの名前付きルーティングヘルパーとして作成されます。logout_pathを呼び出すと/exitが返されます。
という様に:asで名前付きroutingを作成できます。resourcesの時にも名前付きroutingは作成されています。
基本的にはurlの指定は名前付きrouteで指定することが多そうなので、
この方法に慣れることは結構重要だなと最近感じております。全部書くと無限にありそうなので今日はここら辺でおしまい。
わからなくなったらRailsガイドをみて都度確認する癖をつけたい。1人前のエンジニアになるまであと95日
- 投稿日:2020-05-25T20:48:40+09:00
ユーザー登録時のエラーメッセージ、サクセスメッセージの表示の仕方
はじめに
RailsでWebサービスを作成の際にサインんアップ時のフラッシュメッセージの表示の仕方を自分の得た知識の整理のために記事を作成しました。
基本はRailsチュートリアルで得た知識になります。前提条件
- Railsを使用したWebサービスを作成途中で、すでに基本的なログイン機能を自分でdeviceを使用せず作ったとします。
- バリデーション実装済み
controller
def create @user = User.new(user_params) if @user.save #@userの保存に成功 redirect_to @user #ユーザー詳細にリダイレクト else render 'new' #失敗時は新規登録へ戻る end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end新規登録に失敗したときのエラーメッセージ
新規登録の際にパスワードが空白などのバリデーションに引っかかった場合に新規登録画面に戻ってエラーメッセージを表示します。
new.html.erb<%= form_for(@user) do |f| %> <%= render 'shared/error_messages' %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit "Create my account", class: "btn btn-primary" %> <% end %>上記のコードではform_forメソッドの中にrenderを挿入しsharedフォルダにまとめてあるerror_messagesのファイを持ってきています。
エラーメッセージなどを挿入する際には、個別のviewにコードを記述しても結果としては変わりません。しかし、複数のviewで使用されるパーシャルはsharedディレクトリを作成してそこで管理し、そして必要なところにrenderで挿入するのがRailsでは慣習となっています。では、error_messagesのファイルをみてみましょう。
_error_messages.html.erb<% if @user.errors.any? %> <div id="error_explanation"> <div class="alert alert-danger"> The form contains <%= pluralize(@user.errors.count, "error") %>. </div> <ul> <% @user.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %>このコードでは様々なメソッドが使用されています。一つずつみていきましょう。
<% if @user.errors.any? %>この行ですが@user.errorsに対してany?メソッドを使用しています。
any?メソッドは読んで字の如く、指定した対象一つでもあれば、論理値でtrueを返します。
そのため、ここはif文の条件式で「@userになんか不備ある?」ということを聞いています。
そして、何か不備があった場合はその下の処理が発動して、先ほどのnew.html.erbに挿入されます。The form contains <%= pluralize(@user.errors.count, "error") %>.次にこのコードです。
これはエラーの数を数えて表示しますというコードです。
ここで見るからに難しそうな単語のpluralizeというメソッドがありますが全然難しくありません。
これは勝手に数えた数に応じて単数形と複数形をわけて出力してくれるメソッド(英語限定)です。
helperオブジェクトを使用して例にあげると>> helper.pluralize(1, "error") => "1 error" >> helper.pluralize(5, "error") => "5 errors"上記ではhelperオブジェクトに対して使用したpluralizeメソッドが第一引数で整数値をとった場合、その整数値に合わせて第二引数の英単語を複数形にして出力します。
このメソッドのすごい所は可算名詞だけでなく不可算名詞にも対応しているので何かを数えて表示したい際に、不自然な形になることを防げます。
改めて今回のコードをみます。The form contains <%= pluralize(@user.errors.count, "error") %>.pluralizeメソッドの第一引数には@user.errors.countがあります。ここではcountメソッドで@user.errorsの数を数えます。そして、第二引数で指定した単位で単数形、複数形判断して出力します。
結果、The form contains 3 errors
とかの表記になります。<% @user.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %>そして、このeach文で実際に起きているエラーの内容を表示します。
<% @user.errors.full_messages.each do |msg| %>この文の中の
errors.full_messages
という部分はオブジェクトに対してエラーがないか調べ、もしあった場合は何が悪いのか元々Railsに備え付けられているエラーメッセージを使用して出力します。(日本語で出力するやり方もあるのですが調べてません。)
そして、その出力したエラーメッセージをmsgというブロックに入れて<li><%= msg %></li>
で出力します。新規登録成功時のサクセスメッセージ
サクセスメッセージを表示する際はコントローラーを少し弄ります。
users_controller.rbdef create @user = User.new(user_params) if @user.save flash[:success] = "Welcome to the Sample App!" #成功したときのメッセージの指定 redirect_to @user else render 'new' end endflash
サクセスメッセージはページにリダイレクトした直後だけ表示するようにします。そのためにはflashという特殊な変数を使用します。そして上記のコントローラーではそのflash変数のキーがsuccess(Railsの慣習で成功時を表す)の時は"Welcome to the Sample App!"をバリューとして返すように設定しています。
そしてapplication.html.erbでflash変数にeachメソッドを使用してサクセスメッセージを表示します。
追加するコードは下記の通り、applocation.html.erb<!DOCTYPE html> <html> . . . <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %> . . . </body> </html>上記のコードはちょっとややこしい上に周りくどい書き方をしているのであとでさらに簡略化します。
<% flash.each do |message_type, message| %>この時点で、flash変数の中にはsuccessというキーと"Welcome to the Sample App!"というバリューがあるのでeachメソッドで取り出してmessage_typeというキーのブロックとmessageというバリューのブロックに入れます。
そして下記で出力します。<div class="alert alert-<%= message_type %>"><%= message %></div>
class="alert alert-<%= message_type %>"
というBootstrapで使用するクラスがありますがここに先ほどのキーを入れるとclass="alert alert-success"
となりBootstrapがカッコよくしてくれます。これでも正常に表示はされるのですが読みにくいのでcontent_tagメソッドを使用して一行にします。
application.html.erb<!DOCTYPE html> <html> . . . <% flash.each do |message_type, message| %> <%= content_tag(:div, message, class: "alert alert-#{message_type}") %> <% end %> . . . </body> </html>こっちの方が読みやすいですね!
感想
基本的なサクセス、エラーメッセージの付け方は理解できたので、エラーメッセージを日本語化したり、deviseを使用した時の方法(個人的にこっちの方が重要なんじゃないかとこの記事書きながらずっと考えてたw)についてもしっかり理解していきたいと思います。
- 投稿日:2020-05-25T20:25:14+09:00
ハードコーディングとは
はじめに
この業界で仕事をし始めてから日々聞きなれない単語を聞くことが多いのですが、今日は「ハードコーディング」という言葉を耳にして意味がわからなかったので、調べた結果をアウトプットさせていただきます。
ハードコーディングとは
主にこちらの記事を参考にしました。
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典記事にも書いてありますが、一言で言えば、「別のところに分けておいた方が良い処理や値をソースコードの中に直接埋め込むこと」です。
具体的なプログラムで見るとわかりやすいです。
sample.rbprice = gets puts price + price * 0.1上は物の値段を標準入力で受け取って、消費税率10%の税込価格を表示するごく簡単なRubyスクリプトです。
プログラムでは消費税率0.1が、税込価格を計算する箇所に直接書かれています。このスクリプトを書いた本人は税込価格を表示するプログラムだとわかっているため特に問題ありませんが、他の人が見たときに0.1って何?、みたいな感じでプログラムの意味が他人には把握しづらいです。
このように分けておいたほうがわかりやすい処理や値(今回は税率)を直接コードに書くことをハードコーディングと言います。
さらにハードコーディングされているのが数字の場合、それらは「マジックナンバー」と呼ばれます。
今回の税率0.1はマジックナンバーとなります。複数人で開発する際にハードコーディングがあると、他の人がコードを見たときに処理がわかりづらいため、一般には避けるべき書き方です。
今回の税込価格プログラムのハードコーディングを解消すると、例として以下のようになります。
sample.rbprice = gets tax = 0.1 puts price + price * tax消費税をtaxとして分離することで、可読性が上がるとともに、将来消費税が変更になってもtax部分だけを変更すればいいので、修正も楽になります。
ハードコーディングとは逆に処理を分離する書き方は、ソフトコーディングと呼ばれるそうです。今回は税込価格表示の簡単なプログラムだったためハードコーディング解消のありがたみがわかりづらいですが、巨大なプログラムになるとソフトコーディングによる恩恵は大きいと思われます。
具体的にはデータベース設定などはハードコーディングを避けるべき項目だと考えられます。
- 投稿日:2020-05-25T20:16:38+09:00
【Rails 6】active_strageの画像の保存タイミングが変更された
今回はRails6から画像の保存のタイミングが変更したようなので調べてみました。
Rails バージョンの確認
$ rails -v Rails 6.0.2.1ActiveStrageのインストール
$ rails active_storage:install $ rails db:migrate画像添付の準備
最終成果物を載せます。
routeにpreviewを追加します。
route.rbRails.application.routes.draw do resources :users do collection do post :preview patch :preview end end endモデルにhas_one_attachedで画像を扱えるようにします。
user.rbclass User < ApplicationRecord has_one_attached :image end次にcontrollerです。
今回はプレビュー機能をつけたいのでpreviewアクションを追加。user_controller.rbclass UsersController < ApplicationController before_action :set_user, only: [:show, :edit, :update, :destroy] # GET /users # GET /users.json def index @users = User.all end # GET /users/1 # GET /users/1.json def show end # GET /users/new def new @user = User.new end # GET /users/1/edit def edit end . . . . def preview @user = User.new(user_params) image_binary = '' if @user.image.attached? image_binary = @user.attachment_changes['image'].attachable.read else saved_user = User.find_by(id: params[:id]) if saved_user.present? && saved_user.image.attached? image_binary = saved_user.image.blob.download end end @image_enconded_by_base64 = Base64.strict_encode64(image_binary) render template: 'users/_preview', layout: 'application' end private # Use callbacks to share common setup or constraints between actions. def set_user @user = User.find(params[:id]) end # Only allow a list of trusted parameters through. def user_params params.require(:user).permit(:name, :image) end end最後にhtmlです。
new.html.erb<h1>New User</h1> <%= render 'form', user: @user %> <%= link_to 'Back', users_path %>_form.html.erb<%= form_with(model: user, local: true) do |form| %> <% if user.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2> <ul> <% user.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= form.label :name %> <%= form.text_field :name %> <%= form.label :image %> <%= form.file_field :image %> </div> <div class="actions"> <%= form.submit %> </div> <% end %>show.html.erb<p id="notice"><%= notice %></p> <p> <strong>Name:</strong> <%= @user.name %> <% if @user.image.attached? %> <%= image_tag @user.image %> <% end %> </p> <%= link_to 'Edit', edit_user_path(@user) %> | <%= link_to 'Back', users_path %>_preview.html.erb<p id="notice"><%= notice %></p> <p> <strong>Name:</strong> <%= @user.name %> <% if action_name == 'preview' %> <img src="data:image/png;base64,<%= @image_enconded_by_base64 %>" /> <% else %> <%= image_tag @member.image %> <% end %> </p>これで準備ができました。
Rails6での画像の保存のタイミング
早速画像をアップロードしていきます。
テスト.jpegをアップロードして
Create User
ボタンを押すとcreateは成功しています。
ここでアップロードした画像を見に行くとirb(main):005:0> @user.image.attachment ActiveStorage::Attachment Load (1.5ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 1], ["record_type", "User"], ["name", "image"], ["LIMIT", 1]] => #<ActiveStorage::Attachment id: 1, name: "image", record_type: "User", record_id: 1, blob_id: 1, created_at: "2020-05-21 09:04:12">画像が存在するのがわかりました。
アップロードでは保存できていない
次に新しくpreviewを試みます。
同じようにプレビュー.jpgをアップロードして
preview
ボタンを押して画像の確認をします。すると
> @user.image.attachment NameError: uninitialized constant #<Class:0x00007fd450065780>::Analyzable from /usr/local/bundle/gems/activestorage-6.0.3.1/app/models/active_storage/blob.rb:26:in `<class:Blob>'ない!!
> params[:user][:image] => #<ActionDispatch::Http::UploadedFile:0x000055892ec2b598 @content_type="image/jpeg", @headers= "Content-Disposition: form-data; name=\"user[image]\"; filename=\"\xE3\x83\x95\xE3\x82\x9A\xE3\x83\xAC\xE3\x83\x92\xE3\x82\x99\xE3\x83\xA5\xE3\x83\xBC.jpg\"\r\nContent-Type: image/jpeg\r\n", @original_filename="プレビュー.jpg", @tempfile=#<File:/tmp/RackMultipart20200522-1-ha9tz1.jpg>>パラメータを確認。
画像をアップロードしただけでは保存されないのか!下記を参考にすると
https://github.com/rails/rails/pull/33303rails5では
@user.image = params[:image]attributeに入れたタイミングですね!
まさかrails6になってこんな変更があるなんて!!
このままだとプレビュー時(画像をインスタンスに保存せず)に画像をViewに渡せない。どうにかいい方法が無いか調べていると
・https://github.com/rails/rails/pull/33303
・https://stackoverflow.com/questions/57564796/reading-from-active-storage-attachment-before-save
あった!!
record.attachment_changes['<attributename>'].attachable試す!
> @user.attachment_changes['image'].attachable => #<ActionDispatch::Http::UploadedFile:0x000055892ec2b598 @content_type="image/jpeg", @headers= "Content-Disposition: form-data; name=\"user[image]\"; filename=\"\xE3\x83\x95\xE3\x82\x9A\xE3\x83\xAC\xE3\x83\x92\xE3\x82\x99\xE3\x83\xA5\xE3\x83\xBC.jpg\"\r\nContent-Type: image/jpeg\r\n", @original_filename="プレビュー.jpg", @tempfile=#<File:/tmp/RackMultipart20200522-1-ha9tz1.jpg>>さらに
@user.attachment_changes['image'].attachable.read↑でバイナリデータを取ってくることができたのでBase64でエンコードして渡すと
@image_enconded_by_base64 = Base64.strict_encode64(@user.attachment_changes['image'].attachable.read)できました!!
まとめ
ActiveStrageの保存のタイミングに変更があるなんて知りませんでした。
保存せずに画像を触りたいならrecord.attachment_changes['<attributename>'].attachable.readでバイナリ読み出してBase64!!(Base64でなくてもいいけど)
他に何かいい方法あれば教えてください。
- 投稿日:2020-05-25T19:47:49+09:00
nginx 起動コマンド
- 投稿日:2020-05-25T18:29:32+09:00
『kaminari』配列に対してページネーションを作成する方法
はじめに
今回は、配列に対してページネーションを作成しビューで表示するまでのサンプルコードを紹介します。
コントローラー
お気に入りした商品をユーザーのマイページにて表示するためにコントローラーでこのようなコードを書きました。実はこれ、アソシエーション組めばもっと簡単にできるので以下の記事を参考にしてみてください!
https://qiita.com/yummy888/items/22db2f8b79b5be148b69def index items = [] likes = Like.where(user_id: params[:item_id]) if likes.present? likes.each { |like| items << Item.find(like.item_id)} end @items = Kaminari.paginate_array(items).page(params[:page]).per(15) enduser_idになんでparams[:item_id]渡してるの?と思ったかもしれませんが、root.rbにてitemsにlikesをネストしているため、このようなパラメーターの渡し方になっています。(パラメーターでは:item_idとなっているところに,current_userをpathで渡している)本当だったらusesにitemsをネストして、さらにlikesをネストすればもっと綺麗なパラメーターの渡し方ができると思います。
ビュー
hamlでやってます。erbだったらeach文のendの真下にページネーションおく感じですね
- @items.each do |item| = link_to item_path(item), class: "user__liked__items" do .user__liked__items__image = image_tag "#{item.images[0].image.url}", height:"50px", width: "50px", class: "user__liked__item__image" -if item.buyer_id.present? .items__image__sold .items__image__sold__inner SOLD .user__liked__item__details .user__liked__item__name = item.name .user__liked__item__price %span ¥ = item.price.to_s(:delimited) %i.fas.fa-chevron-right = paginate @itemsまとめ
自分で色々実装できるようになると、より簡単な実装方法の模索を放棄してしまってると感じるので、そこは改善していきたいですね、、、ちょっとできるように、なっただけで付け上がっちゃいかんのう
でも、ページネーション作成する場合だったらどちらにしてもコントローラーで色々やらないといけないのかな??ビューでparams[:page]とかできないよな?多分
次この機能実装するときはまた違った実装の方法を試してみますか
参考記事
- 投稿日:2020-05-25T18:25:28+09:00
PAY.JPで登録したクレジットカードで商品購入機能を実装する
はじめに
個人アプリにて、クレジットカード決済を行うため、PAY.JPを導入しました。
機能実装に関して、備忘録として記載しています。前回の記事
PAY.JPでクレジットカードの登録・削除機能を実装する前提条件
- Rails 5.2.4.2
- Ruby 2.5.1
- devise使用
- haml使用
- VSCode使用
前回までにカード情報の登録を行うためCardモデル及びCardコントローラーを作成しました。
今回は新たに購入用モデル及びコントローラーを作成して購入処理を行っていきます。手順
- モデルの作成
- テーブルの作成
- コントローラーの作成
モデルの作成
今回はOrderモデルを作成していきます。
class Order < ApplicationRecord belongs_to :user has_many :products, through: :order_details has_many :order_details, dependent: :destroy belongs_to :card belongs_to :address enum postage: {burden: 0, free: 1} enum status: {支払済み: 0, 配送準備中: 1, 配送済み: 2} def add_items(cart) cart.line_items.target.each do |item| item.cart_id = nil line_items << item end end endECサイトを作成したので、カート機能などを実装している関係で、インスタンスメソッドなど定義しておりますが、今回は購入機能にフォーカスしているため、割愛します。
なので、belongs_to :cardの1行が使いたい部分になります。テーブルの作成
マイグレーションファイルを以下のようにします。
class CreateOrders < ActiveRecord::Migration[5.2] def change create_table :orders do |t| t.references :user, foreign_key: true t.references :address, foreign_key: true t.references :card, foreign_key: true t.references :product, foreign_key: true t.integer :quantity, null: false t.integer :status, default: 0, null: false t.integer :postage, default: 0, null: false t.integer :price, null: false t.timestamps end end endcardを外部キーとして設置しています。
コントローラーの作成
Orderコントローラーを作成していきます。
今回はnewとcreateの2つのアクションを使用します。class OrdersController < ApplicationController before_action :set_cart before_action :user_signed_in before_action :set_user before_action :set_card before_action :set_address require "payjp" #注文入力画面 def new @line_items = current_cart.line_items @cart = current_cart if @cart.line_items.empty? redirect_to current_cart, notice: "カートは空です" return end if @card.present? customer = Payjp::Customer.retrieve(@card.customer_id) default_card_information = customer.cards.retrieve(@card.card_id) @card_info = customer.cards.retrieve(@card.card_id) @exp_month = default_card_information.exp_month.to_s @exp_year = default_card_information.exp_year.to_s.slice(2,3) customer_card = customer.cards.retrieve(@card.card_id) @card_brand = customer_card.brand case @card_brand when "Visa" @card_src = "icon_visa.png" when "JCB" @card_src = "icon_jcb.png" when "MasterCard" @card_src = "icon_mastercard.png" when "American Express" @card_src = "icon_amex.png" when "Diners Club" @card_src = "icon_diners.png" when "Discover" @card_src = "icon_discover.png" end @order = Order.new end end #注文の登録 def create unless user_signed_in? redirect_to cart_path(@current_cart), notice: "ログインしてください" return end @purchaseByCard = Payjp::Charge.create( amount: @cart.total_price, customer: @card.customer_id, currency: 'jpy', card: params['payjpToken'] ) @order = Order.new(order_params) @order.add_items(current_cart) if @purchaseByCard.save && @order.save! OrderDetail.create_items(@order, @cart.line_items) flash[:notice] = '注文が完了しました。マイページにて注文履歴の確認ができます。' redirect_to root_path else flash[:alert] = "注文の登録ができませんでした" redirect_to action: :new end end private def order_params params.permit(:user_id, :address_id, :card_id, :quantity, :price) end def set_user @user = current_user end def set_cart @cart = current_cart end def set_card @card = Card.find_by(user_id: current_user.id) end def set_address @address = Address.find_by(user_id: current_user.id) end def user_signed_in unless user_signed_in? redirect_to cart_path(@cart.id), alert: "レジに進むにはログインが必要です" end end endnewアクションにおいては、購入確認ページの形態をとっています。
ユーザーが登録していないと、そもそも購入確認ページに訪問できない仕様になっております。
かつ、ユーザーがマイページでカードを登録できるように仕様です。購入確認ページで初めてカード登録を行う仕様にするのであれば、前回の記事で作成したCardコントローラーのnewアクションのインスタンス変数を持ってこればいいかもですね。
createアクションでは以下の内容が必要です。
見易いように内容抽出しております。def create @purchaseByCard = Payjp::Charge.create( amount: @cart.total_price, customer: @card.customer_id, currency: 'jpy', card: params['payjpToken'] ) if @purchaseByCard.save flash[:notice] = '注文が完了しました。' redirect_to root_path else flash[:alert] = "注文の登録ができませんでした" redirect_to action: :new end end
- 投稿日:2020-05-25T18:09:02+09:00
Ajax通信をrubyに導入した話
jQueryでAjax通信 × Railsでサーバー処理
jQueryのコードを使ってAjax通信でリクエストし、その処理をRailsで行ってjson形式のレスポンスで返したいと思います。
具体的には、「ボタンをクリックしたらリクエストが発動して、設定したデータをRailsに送信し、レスポンスをクラインアントが受け取っとら、アラートやコンソール上で送られてきたデータを表示する」ということをやってみたいと思います。今回は、クライアント側とサーバー側を分かりやすくするため、Erbを使わずに処理を行いたいと思います。
したがって、今回利用するファイルの拡張子は以下になります。クライアント側
- html
- js
サーバー側
- rb
HTMLファイルで必要なコード
・jQueryのサーバーから、jQueryを使えるようにする機能を読み込むコード(いわゆるjQueryの導入)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>・JavaScriptファイルを読み込むコード
<script src="sample.js"></script>・サーバーへリクエストするボタン
<input type="button" id="btn1" value="ボタン">これらを組み合わせたhtmlのソースコードは以下になります。
sample.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Ajaxの練習</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="sample.js"></script> </head> <body> <input type="button" id="btn1" value="ボタン"> </body> </html>JavaScriptファイルに必要なコード
Ajax通信するコード
以下のような違いがあるため、今回は$.ajaxを使うことにします。
$.ajax 通信の成功時も失敗時の処理も書ける $.post 通信の成功時の処理のみしか書けない パラメータ
・クライアントからのリクエストの送信先を指定
url: "/test"・HTTP通信の種類を設定
GETとPOSTがありますが、今回はPOSTで、データを送信します。type: "POST"・サーバーからのレスポンスを受け取るデータ形式を設定。
dataType: "json"・サーバーへ送るデータの中身
data: { user: { name: "foo", age: 25 } }メソッド
・成功時の処理
通信が成功したか分かりやすくするため、アラートでデータを表示されるようにしています。
GoogleChromeだと、alertだけでは「object」としか表示されないため、
特別なメソッド(JSON.stringify)を追加する必要がある。
done(function(human){ alert(JSON.stringify(human)); });・失敗時の処理(任意)
通信が失敗したら、送ったデータをコンソール上で表示するようにしています。
responseTextメソッドは、Ajax通信途中のデータを取得できるメソッド。fail(function(human){ console.log(human.responseText); });・成功しても失敗しても行われる処理
always(function(){ console.log(human); });これらのjQueryのコードをまとめると以下になります。
補足
id属性btn1のボタンをクリックしたら、以下のAjax通信を行う($.ajax)
・/testに、userというデータをPOSTで送る。
・サーバーからのレスポンス時にはJSON形式でデータを受け取る。
・通信成功時には、返ってきたデータ(変数human)をアラートでブラウザに表示する。(.done)
・通信失敗時には、レスポンスされたデータを文字列としてコンソール上に表示する。(.fail)
・通信成功時でも失敗時でも、レスポンスされたデータをコンソール上に表示する。(.always)
sample.js$(function(){ $('#btn1').click(function(){ $.ajax({ url: "/test", type: "POST", dataType: "json", data: { user: { name: "foo", age: 25 } } }) .done((human) => { alert(JSON.stringify(human)) }) .fail((human) => { console.log(human.responseText) }) .always((human) => { console.log(human) }); }); });Rails(サーバー側)で必要なコード
・ルーティング
jsファイルに記述した、リクエストの送信先(/test)を設定します。route.rbRails.application.routes.draw do get "/test", to: 'statics#test' post "/test", to: 'statics#test' end・コントローラ
jsファイルに記述した、送信するデータを受け取り、json形式でクライアントにレスポンスで返します。
・送信されたデータ{ user: { name: "foo", age: 25 } }を、変数humanで受け取る
・変数humanをjson形式でレスポンスする。
statics_controller.rbclass StaticsController < ApplicationController def test human = params[:user] render :json => human end end最後に
いかがだったでしょうか、非同期通信を行うことで身軽な通信ができて、大勢の人に知れ渡るアプリへの一歩になるんじゃないかと思います。
ただその分、初心者には複雑な仕組みになりがちなので、参考にしていただければと思います。
- 投稿日:2020-05-25T17:28:28+09:00
Ruby で解く AtCoder 第一回アルゴリズム実技検定 A 例外処理
はじめに
AtCoder Problems の Recommendation を利用して、過去の問題を解いています。
AtCoder さん、AtCoder Problems さん、ありがとうございます。今回のお題
AtCoder 第一回アルゴリズム実技検定 A - 2 倍チェック
Difficulty: ???今回のテーマ、例外処理
基本に忠実であれば
正規表現
で解答しますが、Ruby をより深く理解するために例外処理
で解答したいと思います。Ruby 正規表現
3文字とも数字だったらのパターン
ruby1.rbs = gets.chomp puts (/[0-9]{3}/ === s ? s.to_i * 2 : 'error') s = gets.chomp puts (s.match(/[0-9]{3}/) ? s.to_i * 2 : 'error')1文字でも英小文字だったらのパターン
ruby2.rbs = gets.chomp puts (/[a-z]/ === s ? 'error' : s.to_i * 2) s = gets.chomp puts (s.match(/[a-z]/) ? 'error' : s.to_i * 2)1文字でも数字以外だったらのパターン
ruby3.rbs = gets.chomp puts (/[^0-9]/ === s ? 'error' : s.to_i * 2) s = gets.chomp puts (s.match(/[^0-9]/) ? 'error' : s.to_i * 2)Ruby to_i (WA)
to_i
を直接適用したパターンruby4.rbs = 'abc' puts s.to_i # => 0 s = '42A' puts s.to_i # => 42リファレンス に
整数とみなせない文字があればそこまでを変換対象とします。
とありますので、ここまでです。Ruby Rational (AC)
Rational
を適用したパターンruby5.rbs = gets.chomp begin Rational(s) rescue puts 'error' else puts s.to_i * 2 end例外処理の制御構造は次の通りです。
rescue.rbbegin # 例外が発生する可能性のある処理 [rescue [error_type,..] [=> evar] [then] # 例外が発生した時の処理 [else # 例外が発生しなかった時の処理] [ensure # 例外に関係なく実行される処理] end
begin
else
ensure
は省略可能です。普段はあまり使用しない
Rational
ですが、「1/3」のような有理数を扱うクラスです。
to_r
ですと先頭の数字を解釈しますが、Rational
ですと上手く例外が発生するようです。ruby6.rbputs '0x8'.to_r # => 0/1 puts Integer('0x8') # => 8 puts Float('0x8') # => 8.0 puts Complex('0x8') # => ArgumentError puts Complex('08i') # => 0+8i
Complex
は複素数を扱うクラスで、0x8
では例外が発生するのですがi
を含む文字列はすり抜ける可能性があります。標準出力する際に
to_i
を使用するのが心苦しいと申しますか、やはり普通に正規表現を使用するのがいいと思います。まとめ
- 第一回アルゴリズム実技検定 A を解いた
- Ruby に詳しくなった
- 素直に正規表現を使用しましょう
参照したサイト
AtCoderでRuby学習8【第一回アルゴリズム実技検定 2倍チェック 】正規表現
instance method String#to_i
class Rational
制御構造
- 投稿日:2020-05-25T17:20:28+09:00
PAY.JPでクレジットカードの登録・削除機能を実装する
はじめに
個人アプリにて、クレジットカード決済を行うため、PAY.JPを導入しました。
導入において、少しつまずいた部分もあったので、備忘録として記載しています。前提条件
- Rails 5.2.4.2
- Ruby 2.5.1
- devise使用
- haml使用
- VSCode使用
手順
- PAY.JPの登録
- アプリにPAY.JPを導入・下準備
- モデルの作成
- マイグレーションファイル
- コントローラーの作成
- gonの導入
- JSファイルの作成
PAY.JPの登録
以下のURLより登録します。
PAY.JP登録が完了すると、上記のような画面に移ります。(使用したため、売上がたっております。)
最初はテストモードになっております。
実取引を行うためには,申請を行い、ライブモードに切り替える必要があります。
今回は個人アプリでの使用であり、商用目的ではないので、テストモードを使用しています。自身のアプリにはこのテスト秘密鍵とテスト公開鍵を使用していきます。
アプリにPAY.JPを導入・下準備
Gemfileに以下を追記
gem 'payjp'ここから、カード情報の入力フォームを作成準備をしていくのですが、やり方としては2つあります。
- チェックアウト
- カスタム
チェックアウトでは
<form action="/pay" method="post"> <script src="https://checkout.pay.jp/" class="payjp-button" data-key=""></script> </form>上記を記述することでPAYJP側が用意したフォームを使用できます。
今回は自分はフォームを自身でカスタムしたかったので、以下のようにやっていきました。
application.html.hamlのhead部分に以下を追記します。
%script{src: "https://js.pay.jp", type: "text/javascript"}公式では上記に加えて、公開鍵の記述もしておりますが、自分は別で記載しました(後述)。
続いて、PAYJPの公開鍵と秘密鍵をcredentials.yml.encに記述していきます。
※Rails5.2系から導入されたEncrypted Credentialsという機能を利用します。これは、APIキーなどセキュリティ的に外部に公開してはいけない値の管理をシンプルに行うことができる機能です。
当該ファイルを編集するには少し準備が必要です。
まずは、ターミナルからVSCodeを起動できるよう設定を行います。
VSCodeで、「Command + Shift + P」を同時に押してコマンドパレットを開きます。
続いて、「shell」と入力しましょう。
メニューに、「PATH内に'code'コマンドをインストールします」という項目が表示されるので、それをクリックします。
この操作を行うことで、ターミナルから「code」と打つことでVSCodeを起動できるようになりました。続いて以下にてファイルの中身を編集していきます。
$ pwd # 自身のアプリディレクトリにいることを確認 $ EDITOR='code --wait' rails credentials:editここに以下のように記述していきます。
ネストしていることに注意してください。payjp: PAYJP_SECRET_KEY: sk_test_... PAYJP_PUBLIC_KEY: pk_test_...ご自身の鍵を記述して、保存してからファイルを閉じると
”New credentials encrypted and saved.”
とターミナルに出力されるので完了です。なお、保存できているかどうかは以下コマンドで確認できます。
$ rails c $ Rails.application.credentials[:payjp][:PAYJP_SECRET_KEY] $ Rails.application.credentials[:payjp][:PAYJP_PUBLIC_KEY]モデルの作成
今回はCardモデルと命名してやっていきます。
以下のように作成しました。class Card < ApplicationRecord belongs_to :user has_one :order, dependent: :nullify require 'payjp' Payjp.api_key = Rails.application.credentials.dig(:payjp, :PAYJP_SECRET_KEY) def self.create_card_to_payjp(params) # トークンを作成 token = Payjp::Token.create({ card: { number: params['number'], cvc: params['cvc'], exp_month: params['valid_month'], exp_year: params['valid_year'] }}, {'X-Payjp-Direct-Token-Generate': 'true'} ) # 上記で作成したトークンをもとに顧客情報を作成 Payjp::Customer.create(card: token.id) end個人アプリではECサイトを作成していたため、アソシエーションはUserとOrderとしています。
ここでカード情報のトークン化を行っております。
テーブルの作成
以下の内容でテーブルを作成しました。
class CreateCards < ActiveRecord::Migration[5.2] def change create_table :cards do |t| t.string :customer_id, null: false t.string :card_id, null: false t.references :user, null: false, foreign_key: true t.timestamps end end end自分は最初知らなかったですが、customer_idとcard_idはマストでカラムとして作成する必要があります。
テーブルには以下のようにレコードが登録されていきます。コントローラーの作成
今回は4つのアクションで以下のようにコントローラーを構成しました。
class CardsController < ApplicationController before_action :set_card, only: [:new, :show, :destroy] before_action :set_payjpSecretKey, except: :new before_action :set_cart before_action :set_user require "payjp" def new redirect_to action: :show, id: current_user.id if @card.present? @card = Card.new gon.payjpPublicKey = Rails.application.credentials[:payjp][:PAYJP_PUBLIC_KEY] end def create render action: :new if params['payjpToken'].blank? customer = Payjp::Customer.create( card: params['payjpToken'] ) @card = Card.new( card_id: customer.default_card, user_id: current_user.id, customer_id: customer.id ) if @card.save flash[:notice] = 'クレジットカードの登録が完了しました' redirect_to action: :show, id: current_user.id else flash[:alert] = 'クレジットカード登録に失敗しました' redirect_to action: :new end end def show redirect_to action: :new if @card.blank? customer = Payjp::Customer.retrieve(@card.customer_id) default_card_information = customer.cards.retrieve(@card.card_id) @card_info = customer.cards.retrieve(@card.card_id) @exp_month = default_card_information.exp_month.to_s @exp_year = default_card_information.exp_year.to_s.slice(2,3) customer_card = customer.cards.retrieve(@card.card_id) @card_brand = customer_card.brand case @card_brand when "Visa" @card_src = "icon_visa.png" when "JCB" @card_src = "icon_jcb.png" when "MasterCard" @card_src = "icon_mastercard.png" when "American Express" @card_src = "icon_amex.png" when "Diners Club" @card_src = "icon_diners.png" when "Discover" @card_src = "icon_discover.png" end end def destroy customer = Payjp::Customer.retrieve(@card.customer_id) @card.destroy customer.delete flash[:notice] = 'クレジットカードが削除されました' redirect_to controller: :users, action: :show, id: current_user.id end private def set_card @card = Card.where(user_id: current_user.id).first end def set_payjpSecretKey Payjp.api_key = Rails.application.credentials[:payjp][:PAYJP_SECRET_KEY] end def set_cart @cart = current_cart end def set_user @user = current_user end endまた、PAYJPの公開鍵を後述するJSファイルにベタ書きすることを避けるため、"gon"というGemを導入しました。
JSファイルでrubyの変数を使用したい時に使うという認識です。gonの導入
Gemfileに以下を追記
gem 'gon'application.html.hamlのhead部分に以下を追記します。
= include_gon(init: true)これで準備はOKです。
JSファイルの作成
ここから、コントローラーのnewアクションで呼び出すnew.html.hamlと連動するJSファイルを作成します。
まずはnew.html.hamlを記載します。
※フォームに入力するカード番号等についてですが、以下の公式ページのものを使用してください。
APIで使用するテストカードはこちらです。.cardNew .title クレジットカード情報入力 .cardForm = form_with model: @card, id: "form" do |f| .cardForm__number = f.label :カード番号, class: "cardForm__number__title" %span.must_check 必須 .cardForm__field = f.text_field :card_number, id: "card_number", placeholder: "半角数字のみ", class: "form-group__input", maxlength: 16 .cardForm__image = image_tag(image_path('cards/icon_visa.png'), class: 'visa', width: 58, height: 28) = image_tag(image_path('cards/icon_mastercard.png'), class: 'master', width: 47, height: 36) = image_tag(image_path('cards/icon_jcb.png'), class: 'jcb', width: 40, height: 30) = image_tag(image_path('cards/icon_amex.png'), class: 'amex', width: 40, height: 30) = image_tag(image_path('cards/icon_diners.png'), class: 'diners', width: 45, height: 32) = image_tag(image_path('cards/icon_discover.png'), class: 'discover', width: 47, height: 30) .cardForm__expirationdate .cardForm__expirationdate__details = f.label :有効期限 %span.must_check 必須 %br .cardForm__expirationdate__choice .cardForm__expirationdate__choice__month = f.select :expiration_month, options_for_select(["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]), {}, {id: "exp_month", name: "exp_month", type: "text"} = f.label :月, class: "cardForm__expirationdate__choice__month__label" .cardForm__expirationdate__choice__year = f.select :expiration_year, options_for_select((2020..2030)), {}, {id: "exp_year", name: "exp_year", type: "text"} = f.label :年, class: "cardForm__expirationdate__choice__year__label" .cardForm__securitycode .cardForm__securitycode__details .cardForm__securitycode__details__title = f.label :セキュリティコード, class: "label" %span.must_check 必須 .cardForm__securitycode__details__field = f.text_field :cvc, id: "cvc", class: "cvc", placeholder: "カード背面3~4桁の番号", maxlength: "4" .cardForm__securitycode__details__hatena = link_to "カード背面の番号とは?", "#", class: "cardForm__securitycode__details__hatena__link" #card_token = f.submit "登録する", id: "token_submit", url: cards_path, method: :post続いて、上記viewファイルに対応するJSファイルを作成します。
$(document).on('turbolinks:load', function() { $(function() { Payjp.setPublicKey(gon.payjpPublicKey); $("#token_submit").on('click', function(e){ e.preventDefault(); let card = { number: $('#card_number').val(), cvc:$('#cvc').val(), exp_month: $('#exp_month').val(), exp_year: $('#exp_year').val() }; Payjp.createToken(card, function(status, response) { if (response.error) { $("#token_submit").prop('disabled', false); alert("カード情報が正しくありません。"); } else { $("#card_number").removeAttr("name"); $("#cvc").removeAttr("name"); $("#exp_month").removeAttr("name"); $("#exp_year").removeAttr("name"); let token = response.id; $("#form").append(`<input type="hidden" name="payjpToken" value=${token}>`); $("#form").get(0).submit(); alert("登録が完了しました"); } }); }); }); });viewファイルで登録ボタン( = f.submit "登録する", id: "token_submit")を押すと、JSが発火します。
トークン作成は Payjp.createToken というメソッドで行います。上記までで、カードの登録までは完了です。
最後に削除機能について簡単に記載しておきます。
自分はshowアクションで呼び出すshow.html.hamlに削除ボタンを以下のように設置しました。.payment .payment__content .payment__content__title カード情報 .payment__content__box .payment__content__box__cardImage = image_tag "cards/#{@card_src}", width: 40, height: 28 .payment__content__box__details .payment__content__box__details__cardNumber = "カード番号:**** **** **** " + @card_info.last4 .payment__content____boxdetails__cardMMYY = "有効期限:" + @exp_month + " / " + @exp_year .payment__cardDelete = button_to "削除する", card_path(@card.id), {method: :delete, id: 'charge-form', name: "inputForm", class: "payment__cardDelete__deleteBtn"}次回は、登録できたカードでの決済機能を記載します。
- 投稿日:2020-05-25T17:04:06+09:00
ffi Library not loaded: /opt/brew/opt/libffi/lib/libffi.6.dylibのエラーを直す
概要
brew updateしたあとに発生したエラー
rspecが落ちる今回の直し方
symlinkを貼る
採用理由
- あんまり調べなくて良い
- 早い
懸念点
- updateのときに意図せずなんかなって嵌りそう
まあいいか
TODO: 公式issueにも一応載っているので今度読む。もしどなたか読んだ方いらしたらぜひ教えて下さいませ(他力本願...)
dyld: Library not loaded: /usr/local/opt/libffi/lib/libffi.6.dylib · Issue #7 · platformio/platform-lattice_ice40エラー抜粋
LoadError: dlopen(/Users/user/dev/my-markdown/vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi_c.bundle, 9): Library not loaded: /opt/brew/opt/libffi/lib/libffi.6.dylib探索
/opt/brew/opt/libffi/lib/libffi.6.dylib
not loadedなので、言ってみる➤ cd /opt/brew/opt/libffi/lib/ ➤ ls libffi.7.dylib libffi.a libffi.dylib pkgconfig確かに
libffi.6.dylib
がないsymlinkを貼る
ln libffi.7.dylib libffi.6.dylib
直った
感想
多分、libffi.6.dylibになるように、何かしらreinstallなどしたほうがよいのだろうが、面倒なのでしないでおこう。
以上、またエラーが出たら対処する or issue今度読む方針の雑な直し方でした。
付録: エラー全容
➤ rspec spec/models/post_spec.rb:6 An error occurred while loading ./spec/models/post_spec.rb. Failure/Error: require File.expand_path('../../config/environment', __FILE__) LoadError: dlopen(/Users/user/dev/my-markdown/vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi_c.bundle, 9): Library not loaded: /opt/brew/opt/libffi/lib/libffi.6.dylib Referenced from: /Users/use(neocomplete_start_auto_complete)/dev/my-markdown/vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi_c.bundle Reason: image not found - /Users/user/dev/my-markdown/vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi_c.bundle # ./vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi.rb:6:in `require' # ./vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi.rb:6:in `rescue in <top (required)>' # ./vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi.rb:3:in `<top (required)>' # ./vendor/bundler/ruby/2.6.0/gems/sassc-2.3.0/lib/sassc/native.rb:3:in `require' # ./vendor/bundler/ruby/2.6.0/gems/sassc-2.3.0/lib/sassc/native.rb:3:in `<top (required)>' # ./vendor/bundler/ruby/2.6.0/gems/sassc-2.3.0/lib/sassc.rb:31:in `require_relative' # ./vendor/bundler/ruby/2.6.0/gems/sassc-2.3.0/lib/sassc.rb:31:in `<top (required)>' # ./vendor/bundler/ruby/2.6.0/gems/sassc-rails-2.1.2/lib/sassc/rails.rb:5:in `require' # ./vendor/bundler/ruby/2.6.0/gems/sassc-rails-2.1.2/lib/sassc/rails.rb:5:in `<top (required)>' # ./vendor/bundler/ruby/2.6.0/gems/sassc-rails-2.1.2/lib/sassc-rails.rb:3:in `require_relative' # ./vendor/bundler/ruby/2.6.0/gems/sassc-rails-2.1.2/lib/sassc-rails.rb:3:in `<top (required)>' # ./vendor/bundler/ruby/2.6.0/gems/administrate-0.13.0/lib/administrate/engine.rb:5:in `require' # ./vendor/bundler/ruby/2.6.0/gems/administrate-0.13.0/lib/administrate/engine.rb:5:in `<top (required)>' # ./vendor/bundler/ruby/2.6.0/gems/administrate-0.13.0/lib/administrate.rb:1:in `require' # ./vendor/bundler/ruby/2.6.0/gems/administrate-0.13.0/lib/administrate.rb:1:in `<top (required)>' # ./config/application.rb:22:in `<top (required)>' # ./config/environment.rb:4:in `require_relative' # ./config/environment.rb:4:in `<top (required)>' # ./spec/rails_helper.rb:6:in `require' # ./spec/rails_helper.rb:6:in `<top (required)>' # ./spec/models/post_spec.rb:3:in `require' # ./spec/models/post_spec.rb:3:in `<top (required)>' # ------------------ # --- Caused by: --- # LoadError: # cannot load such file -- 2.6/ffi_c # ./vendor/bundler/ruby/2.6.0/gems/ffi-1.11.1/lib/ffi.rb:4:in `require' Run options: include {:locations=>{"./spec/models/post_spec.rb"=>[6]}} All examples were filtered out Randomized with seed 34574 Finished in 0.00064 seconds (files took 2.04 seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples
- 投稿日:2020-05-25T15:38:48+09:00
Rails Console内で関数を定義したい
ちょっとしたことなんですが。
pryにコードをコピペするためにしておきたい設定#方法1:一つの式にしてしまう
括弧で括ると改行は無視されるようになります。
無視されるというか、閉じるまで式の途中とみなされます。よく使うパターンとしては、Consoleが立ち上がってる間だけの関数を定義したいとき。
これはメソッドの一部を軽くテストしたいな〜ってときにやってます。Console内で
[1] pry(main)> ( [1] pry(main)* def hoge [1] pry(main)* return 'hoge' # 処理内容 [1] pry(main)* end [1] pry(main)* ) => :hoge [2] pry(main)> hoge # 呼び出し => "hoge"という感じです。
- 投稿日:2020-05-25T12:32:15+09:00
【gem:devise の日本語化】
gem:devise日本語化の流れ
(deviseインストール後の流れ)
gemのインストール
・gem 'devise-i18n'
・gem 'devise-i18n-views'コマンドで日本語翻訳ファイルを生成
devise.views.ja.yml を編集
1. gemのインストール
gem 'devise-i18n' gem 'devise-i18n-views'gemfileに記述して“$ bundle install”します
2. コマンドで日本語翻訳ファイルを生成
$ rails g devise:views:locale jaコマンドを実行すると日本語翻訳ファイルであるconfig/localesdevise.views.ja.yml
が生成されます。3. devise.views.ja.yml を編集
生成されたconfig/localesdevise.views.ja.ymlを編集していきます。
config/localesdevise.views.ja.ymlja: activerecord: errors: models: user: attributes: email: taken: "は既に使用されています。" blank: "が入力されていません。" too_short: "は%{count}文字以上に設定して下さい。" too_long: "は%{count}文字以下に設定して下さい。" invalid: "は有効でありません。" password: taken: "は既に使用されています。" blank: "が入力されていません。" too_short: "は%{count}文字以上に設定して下さい。" too_long: "は%{count}文字以下に設定して下さい。" invalid: "は有効でありません。" confirmation: "が内容とあっていません。" attributes: user: current_password: "現在のパスワード" name: 名前 email: "メールアドレス" password: "パスワード" password_confirmation: "確認用パスワード" remember_me: "次回から自動的にログイン" models: user: "ユーザ" devise: confirmations: new: resend_confirmation_instructions: "アカウント確認メール再送" mailer: confirmation_instructions: action: "アカウント確認" greeting: "ようこそ、%{recipient}さん!" instruction: "次のリンクでメールアドレスの確認が完了します:" reset_password_instructions: action: "パスワード変更" greeting: "こんにちは、%{recipient}さん!" instruction: "誰かがパスワードの再設定を希望しました。次のリンクでパスワードの再設定が出来ます。" instruction_2: "あなたが希望したのではないのなら、このメールは無視してください。" instruction_3: "上のリンクにアクセスして新しいパスワードを設定するまで、パスワードは変更されません。" unlock_instructions: action: "アカウントのロック解除" greeting: "こんにちは、%{recipient}さん!" instruction: "アカウントのロックを解除するには下のリンクをクリックしてください。" message: "ログイン失敗が繰り返されたため、アカウントはロックされています。" passwords: edit: change_my_password: "パスワードを変更する" change_your_password: "パスワードを変更" confirm_new_password: "確認用新しいパスワード" new_password: "新しいパスワード" new: forgot_your_password: "パスワードを忘れましたか?" send_me_reset_password_instructions: "パスワードの再設定方法を送信する" registrations: edit: are_you_sure: "本当に良いですか?" cancel_my_account: "アカウント削除" currently_waiting_confirmation_for_email: "%{email} の確認待ち" leave_blank_if_you_don_t_want_to_change_it: "空欄のままなら変更しません" title: "%{resource}編集" unhappy: "気に入りません" update: "更新" we_need_your_current_password_to_confirm_your_changes: "変更を反映するには現在のパスワードを入力してください" new: sign_up: "アカウント登録" sessions: new: sign_in: "ログイン" shared: links: back: "戻る" didn_t_receive_confirmation_instructions: "アカウント確認のメールを受け取っていませんか?" didn_t_receive_unlock_instructions: "アカウントの凍結解除方法のメールを受け取っていませんか?" forgot_your_password: "パスワードを忘れましたか?" sign_in: "ログイン" sign_in_with_provider: "%{provider}でログイン" sign_up: "アカウント登録" unlocks: new: resend_unlock_instructions: "アカウントの凍結解除方法を再送する"これで日本語化ができると思います!
おわりに
自分メモように書いてみました!
よかったら参考にしてみてください。
- 投稿日:2020-05-25T12:06:52+09:00
【Rails】DateとTimeの比較をする際の注意点
ことの起こりは深夜0時も回った頃。RSpecを走らせたところ、実装と関係のないところが落ちる。
調べてみたところ、どうやろここで本当はreturnしないといけないのにreturnしていないようです。
return if last_tweet.tweeted_at > DateTime.yesterdayというわけで、デバッグしてみました。
デバッグ
[2] pry(#<RegisteredTag>)> DateTime.yesterday => Thu, 21 May 2020うんうん、合ってる。
[3] pry(#<RegisteredTag>)> DateTime.yesterday.to_time => 2020-05-21 00:00:00 +0900よかろう。
[4] pry(#<RegisteredTag>)> last_tweet.tweeted_at.to_time => 2020-05-21 00:30:02 +0900そうだよね、では比較してみよう。
[6] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday.to_time => true比較したらそうなるはずなんだ。では、元のコードに戻してみよう。
[5] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday => falseファファファ????
ということで、改めて確認してみます。
日付の比較
[33] pry(main)> DateTime.yesterday => Thu, 21 May 2020 [34] pry(main)> after_9 => Thu, 21 May 2020 09:21:38 +0900 [35] pry(main)> DateTime.yesterday < after_9 => true [37] pry(main)> before_9 => Thu, 21 May 2020 08:31:48 +0900 [38] pry(main)> DateTime.yesterday < before_9 => false8時台 < DateTime.yesterday < 9時台
ということで、DateTime.yesterday
にはタイムゾーンが適用されていないようです。Date.yesterdayでもやってみた
[22] pry(main)> Date.yesterday => Thu, 21 May 2020 [23] pry(main)> Date.yesterday < before_9 => false [24] pry(main)> Date.yesterday < after_9 => trueDateTimeと同じ挙動をします。
クラスを見てみる
[31] pry(main)> Date.yesterday.class => Date [32] pry(main)> DateTime.yesterday.class => Dateあ、DateTimeで作ってもインスタンスのクラスはDateになるんですね。
つまり、Date.yesterday
とDateTime.yesterday
は同じもののようです。結論
- DateTimeで日付を指定すると、インスタンスはDateクラスになる。
- DateクラスのインスタンスはDateTime/Timeクラスになった時点でタイムゾーンを持つ。
- Dateクラスの状態ではタイムゾーンは+0000なので、DateTime/Timeクラス(Railsだと
ActiveSupport::TimeWithZone
クラス)と比較するときは注意する。
- 投稿日:2020-05-25T12:06:52+09:00
【Rails】DateTimeで比較をする際の注意点
ことの起こりは深夜0時も回った頃。RSpecを走らせたところ、実装と関係のないところが落ちる。
調べてみたところ、どうやろここで本当はreturnしないといけないのにreturnしていないようです。
return if last_tweet.tweeted_at > DateTime.yesterdayというわけで、デバッグしてみました。
デバッグ
[2] pry(#<RegisteredTag>)> DateTime.yesterday => Thu, 21 May 2020うんうん、合ってる。
[3] pry(#<RegisteredTag>)> DateTime.yesterday.to_time => 2020-05-21 00:00:00 +0900よかろう。
[4] pry(#<RegisteredTag>)> last_tweet.tweeted_at.to_time => 2020-05-21 00:30:02 +0900そうだよね、では比較してみよう。
[6] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday.to_time => true比較したらそうなるはずなんだ。では、元のコードに戻してみよう。
[5] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday => falseファファファ????
ということで、改めて確認してみます。
日付の比較
[33] pry(main)> DateTime.yesterday => Thu, 21 May 2020 [34] pry(main)> after_9 => Thu, 21 May 2020 09:21:38 +0900 [35] pry(main)> DateTime.yesterday < after_9 => true [37] pry(main)> before_9 => Thu, 21 May 2020 08:31:48 +0900 [38] pry(main)> DateTime.yesterday < before_9 => false8時台 < DateTime.yesterday < 9時台
ということで、DateTime.yesterday
にはタイムゾーンが適用されていないようです。Date.yesterdayでもやってみた
[22] pry(main)> Date.yesterday => Thu, 21 May 2020 [23] pry(main)> Date.yesterday < before_9 => false [24] pry(main)> Date.yesterday < after_9 => trueDateTimeと同じ挙動をします。
クラスを見てみる
[31] pry(main)> Date.yesterday.class => Date [32] pry(main)> DateTime.yesterday.class => Dateあ、DateTimeで作ってもインスタンスのクラスはDateになるんですね。
つまり、Date.yesterday
とDateTime.yesterday
は同じもののようです。結論
- DateTimeで日付を指定すると、インスタンスはDateクラスになる。
- Dateクラスはタイムゾーンの情報を持っていないため、+0000になる。
- DateクラスはDateTime/Timeクラスになった時点でタイムゾーンを持つ。
- DateクラスとDateTime/Timeクラス(Railsだと
ActiveSupport::TimeWithZone
クラス)を比較するときはDateクラスをDateTimeクラスへ変換する必要がある。
- 投稿日:2020-05-25T12:06:52+09:00
【Rails】DateTimeで日付と時間を比較をする際の注意点
ことの起こりは深夜0時も回った頃。RSpecを走らせたところ、実装と関係のないところが落ちる。
調べてみたところ、どうやろ本当はreturnしないといけないところがreturnしていないようです。
してほしい挙動としては、「最後にツイートした日時が昨日より最近のときにreturnする」というもの。return if last_tweet.tweeted_at > DateTime.yesterdayというわけで、調べてみました。
環境
Rails 5.2.4.2
Ruby 2.6.6デバッグ
[2] pry(#<RegisteredTag>)> DateTime.yesterday => Thu, 21 May 2020うんうん、合ってる。
[3] pry(#<RegisteredTag>)> DateTime.yesterday.to_time => 2020-05-21 00:00:00 +0900よかろう。
[4] pry(#<RegisteredTag>)> last_tweet.tweeted_at.to_time => 2020-05-21 00:30:02 +0900そうだよね、では比較してみよう。
[6] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday.to_time => true比較したらそうなるはずなんだ。では、元のコードに戻してみよう。
[5] pry(#<RegisteredTag>)> last_tweet.tweeted_at > DateTime.yesterday => falseファファファ????
ということで、改めて確認してみます。
日付の比較
[33] pry(main)> DateTime.yesterday => Thu, 21 May 2020 [34] pry(main)> after_9 => Thu, 21 May 2020 09:21:38 +0900 [35] pry(main)> DateTime.yesterday < after_9 => true [37] pry(main)> before_9 => Thu, 21 May 2020 08:31:48 +0900 [38] pry(main)> DateTime.yesterday < before_9 => false8時台 < DateTime.yesterday < 9時台
ということで、DateTime.yesterday
にはタイムゾーンが適用されていないようです。Date.yesterdayでもやってみた
[22] pry(main)> Date.yesterday => Thu, 21 May 2020 [23] pry(main)> Date.yesterday < before_9 => false [24] pry(main)> Date.yesterday < after_9 => trueDateTimeと同じ挙動をします。
クラスを見てみる
[31] pry(main)> Date.yesterday.class => Date [32] pry(main)> DateTime.yesterday.class => Dateあ、DateTimeで作ってもインスタンスのクラスはDateになるんですね。
つまり、Date.yesterday
とDateTime.yesterday
は同じもののようです。結論
- DateTimeで日付を指定すると、インスタンスはDateクラスになる。
- Dateクラスはタイムゾーンの情報を持っていないため、+0000になる。
- DateクラスはDateTime/Timeクラスになった時点でタイムゾーンを持つ。
- DateクラスとDateTime/Timeクラス(Railsだと
ActiveSupport::TimeWithZone
クラス)を比較するときはDateクラスをDateTimeクラスへ変換する必要がある。
- 投稿日:2020-05-25T11:51:46+09:00
Railsチュートリアルのセットアップ関連だけ要約
Railsチュートリアルをようやく一周しました。
次の段階として、Railsチュートリアルを参考にしながらオリジナルのWebサービスを作ってみるために、セットアップ関連だけまとめています。
開発環境はCloud9です。Rails環境の構築
便利コマンド
普段からドキュメントをインストールしないように指定するコマンド(らしい)。
参考にさせていただきました。
Railsチュートリアル第1章の「$ printf "install: --no-document \nupdate: --no-document\n" >> ~/.gemrc」を分解$ printf "install: --no-document \nupdate: --no-document\n" >> ~/.gemrcRailsインストール
今回、バージョンはチュートリアルのままとしたが、いずれバージョンアップもやっておかないと。。。
$ gem install rails -v 5.1.6rails newの実行
$ rails _5.1.6_ new "タイトル"GitHub環境の構築
gitの生成
$ cd ~/envronment/"タイトル" $ git init $ git add -A $ git commit -m "Initialize repository" # コメントは定番のものリポジトリの作成(GitHub)
GitHub上で 'New repository'してリポジトリを作成後、以下コマンドを実行(URLはリポジトリ作成画面に記載)
$ git remote add origin "URL" $ git push -u origin master # uオプションをつけて、origin masterを上流ブランチとして設定 # 次回以降、git pushのみでorigin masterへのpushが可能リポジトリを非公開としたい場合は、Settingの一番下'Danger Zone'内にあるMake privateをクリック
SSH 公開鍵の設定(GitHub)
$ cd ~/.ssh # 鍵を入れるフォルダに移動 $ ssh-keygen -t rsa # 鍵を生成 $ cat id_rsa.pub # 鍵の中身を表示して手動でコピー # cloud9ではpbcopyが使用できなかったので、手動でコピーしています。https://github.com/settings/ssh で公開鍵を設定
以下コマンドで接続を確認し、以下コメントが出れば接続完了。$ ssh -T git@github.comHi (account名)! You've successfully authenticated, but GitHub does not provide shell access.
参考にさせていただきました。
GitHubでssh接続する手順~公開鍵・秘密鍵の生成から~警告がでた場合の対処方法
以下のような警告が出る場合があります(僕は出ました)。
Warning: Permanently added the RSA host key for IP address 'xxx.xxx.xxx.x' to the list of known hosts.pushができなくなるわけではないので問題はなさそうですが、Worningがでているのは気持ち悪い。。。
以下コマンドでknown_hostsの内容が更新されてWorningがでなくなります。$ ssh-keygen -R "IPアドレス" # 上記で直らない場合は、以下コマンドも試してみてください $ ssh-keygen -R github.compush時に毎回パスワードを聞かれないための方法
- GitHubのCode画面の右側"Clone or download"ボタンをクリック
- 右上の"Use SSH"をクリックし、表示されたURLをコピー
- ターミナルから以下コマンドを実行
$ git remote set-url origin "URL" # 確認のため以下を実行 $ git remote -v #以下のように、git@github.comから始まっていれば成功! origin git@github.com:reiji012-pg/Tabetter.git (fetch) origin git@github.com:reiji012-pg/Tabetter.git (push)Herokuのセットアップ
ここもひとまずチュートリアルのままで。
本番環境のみにPostgreSQLのgemをインストールGemfile.group :production do gem 'pg', '0.20.0' end開発環境とテスト環境にSQLiteのgemをインストール
Gemfile.group :development, :test do gem 'sqlite3', '1.3.13' gem 'byebug', '9.0.6', platform: :mri endpg gemを本番環境にインストールしないようにオプションを追加してインストール
$ bundle install --without productionHerokuをインストール
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)インストールを確認後、ログインからデプロイまで。
$ heroku --version $ heroku login --interactive $ heroku keys:add $ heroku create $ git push heroku master以下コマンドでアプリケーションの名称を変更可能
$ heroku rename "アプリケーション名"とりあえずこれでアプリケーションの作成に入っていける環境が構築できたかと思います。
- 投稿日:2020-05-25T11:30:50+09:00
Allureを用いて言語、フレームワークに依存しないシャレオツなテストレポートを生成しよう!
概要
Allureについて、公式ドキュメントの意訳をベースに紹介します。
※ 言語、フレームワークに依存しないのは間違いないですが、本記事では便宜上
RSpec
(Ruby) を用いて進めます。Allureについて
テストフレームワークといえば、
JUnit
RSpec
jest
pytest
など、様々な言語で数多くの優れたツールが用意されています。しかし、多くのツールはビジュアルに優れたテストレポートを生成する機能は持ち合わせていません。
Allureはそれを補うための、言語やフレームワークに依存せずに、チームの誰もが簡単に理解できるテスト実行レポートを生成するためのOSSです。
デモ
百聞は一見にしかず。デモレポート を開いてみましょう。
テスト実行レポートを、グラフィカル、インタラクティブに多くの観点から参照できることがわかります。
再三言いますが、これらの生成に言語、フレームワークは関係ありません。
JUnit
でも、RSpec
やjest
、はたまたpytest
などからでも、現在運用中のテストコードをそのまま使用することができます。レポートの作り方
Allureは一般的な
xUnit
(単体テストフレームワークの総称) の出力結果に加え、補助的なデータを付与したものに基づいています。テストレポートは以下の2ステップで生成されます。
1. テスト実行
各種テストフレームワークがテストを実行する際、
アダプター
と呼ばれる軽量ライブラリによって、実行されたテストに関する情報をXMLやJSON形式で生成します。
アダプター
はJava
,PHP
,Ruby
,Python
,Scala
,C#
に対して公式から提供されており、それぞれ主要なテストフレームワークをサポートしています。2. レポート生成
テスト完了後に、生成されたデータを元に、HTML形式のレポートを生成します。
レポートの生成はCLIツールから行うことが出来ます。
RSpec(Ruby)での実行例
本記事では便宜上、RSpecを例に進めますが、多くの言語、フレームワークでも同様のステップで進めることが出来ます。
CLIのインストール
レポートを生成するためのCLIツールである
allure
本体をインストールします。今回はMacを使用するので、brewでインストールします。$ brew install allureバージョン確認ができていればOKです。
$ allure --version 2.13.0$ allure --version No Java runtime present, requesting install.とか出る場合はJavaランタイムを入れる必要があります。
テストコードの用意
今回は以下のような
Calculator
クラスの振る舞いを検証する単体テストを用意します。
Calculator
クラスは初期値を基準に、足し算と引き算を行えるクラスです。一つだけ意図的にテストが落ちるような設定になっています。describe Calculator do subject { described_class.new(initial_value) } describe '#add 足し算' do context '初期値が0の場合' do let(:initial_value) { 0 } it '10を足すと10が戻る' do expect(subject.add(10)).to eq 10 end end context '初期値が5の場合' do let(:initial_value) { 5 } it '10を足すと15が戻る' do expect(subject.add(10)).to eq 15 end end end describe '#sub 引き算' do context '初期値が0の場合' do let(:initial_value) { 0 } # 落ちるテスト it '10を引くと0が戻る' do expect(subject.sub(10)).to eq 0 end end context '初期値が30の場合' do let(:initial_value) { 30 } it '10を引くと20が戻る' do expect(subject.sub(10)).to eq 20 end end end end普通にrpsecを実行すると以下の通り
$ bundle exec rspec spec/sample_spec.rb -f dCalculator #add 足し算 初期値が0の場合 10を足すと10が戻る 初期値が5の場合 10を足すと15が戻る #sub 引き算 初期値が0の場合 10を引くと0が戻る (FAILED - 1) 初期値が30の場合 10を引くと20が戻る Failures: 1) Calculator#sub 引き算 初期値が0の場合 10を引くと0が戻る Failure/Error: expect(subject.sub(10)).to eq 0 expected: 0 got: -10 (compared using ==) # ./sample_spec.rb:42:in `block (4 levels) in <top (required)>' Finished in 0.11199 seconds (files took 2.06 seconds to load) 4 examples, 1 failure Failed examples: rspec ./sample_spec.rb:41 # Calculator#sub 引き算 初期値が0の場合 10を引くと0が戻るアダプターを用いてレポート用データを生成する
Rspec実行時に、Ruby用のAllureアダプターを使って、レポート作成用のデータを生成してもらいます。
RSpec用のアダプタはallure-rspecを使用します。
今回はGemfileに以下を追記してインストールします。
gem "allure-rspec"
spec_helper
にてアダプタを読み込みrequire "allure-rspec"実行時にアダプターをフォーマッタとして指定します。今度は標準出力には何も出力されません。
$ bundle exec rspec spec/sample_spec.rb -f AllureRspecFormatterデフォルトだと
reports
ディレクトリが作成され、以下のような構造でレポート用データが生成されています。$ tree reports/ reports/ └── allure-results ├── 4ee53de0-790a-0138-ab5e-3d13be3ed9ba-container.json ├── 4ee54750-790a-0138-ab5e-3d13be3ed9ba-container.json ├── 4ee54a80-790a-0138-ab5e-3d13be3ed9ba-container.json ├── 4ee64530-790a-0138-ab5e-3d13be3ed9ba-result.json ├── 4ee7fad0-790a-0138-ab5e-3d13be3ed9ba-container.json ├── 4ee80710-790a-0138-ab5e-3d13be3ed9ba-result.json ├── 4ee83ae0-790a-0138-ab5e-3d13be3ed9ba-container.json ├── 4ee83e90-790a-0138-ab5e-3d13be3ed9ba-container.json ├── 4ee84860-790a-0138-ab5e-3d13be3ed9ba-result.json ├── 4eefe9b0-790a-0138-ab5e-3d13be3ed9ba-container.json └── 4eeff800-790a-0138-ab5e-3d13be3ed9ba-result.json 1 directory, 11 filesレポートを生成する
前項で出力したレポート用データを用いて、
allure
コマンドでレポートを生成します。
serve
サブコマンドを使うことで、ローカルでレポートを生成し、そこにブラウザでアクセスすることができるようになります。$ allure serve -h 0.0.0.0 ./reports/allure-results/ Generating report to temp directory... Report successfully generated to /var/folders/3_/r5h56zzn1130yr7vkpjftqpw0000gp/T/15980098365829452874/allure-report Starting web server... 2020-05-16 04:02:58.509:INFO::main: Logging initialized @3541ms to org.eclipse.jetty.util.log.StdErrLog Server started at <http://0.0.0.0:57717/>. Press <Ctrl+C> to exit上記の例だと、
0.0.0.0:57717
にアクセスすると、無事にレポートが生成されていることが確認できました。所感
本記事の題の通り、言語、フレームワークに依存しないことから非常に多機能で汎用的になってしまうため、個別用途としてはどうしてもToo muchになってしまう印象でした。
最初からAllureを想定したテスト設計が出来ると上手く刺さるかもしれませんが、綺麗に構造化されてない限りは既存のテストコードを載せるのは難しそう。
- 投稿日:2020-05-25T10:04:59+09:00
DaprでRubyとJavaを連携させてみる
はじめに
マイクロサービス開発を容易にするDaprをJavaで利用してみたに引き続きDaprです。
チュートリアルのStep6に相当します。
コードは下記を参照
https://github.com/koduki/example-dapr/tree/v01構成概要
チュートリアルではPythonですが、趣味の問題でRubyを使っています。
この構成のポイントはRubyとJavaは直接通信しないどころはRubyはあくまで自分のサイドカーのDaprと話すのであってJavaを起動しているDaprとは直接話さないことです。テストがローカルなので実は直接話すこともできるのですが、分かりやすくJavaのDaprポートを3500, RubyのDaprポートを3600とします。
コード
今回は1秒に一度、対象APIにリクエストを投げるだけの超シンプルなコードです。
javaapp
はJavaアプリのappidです。こちらを使ってDparどうして適切なアプリを自動で発見します。APIが動いているIPとかポート番号の指定は不要です。
Rubyコード内で指定しているhttp://localhost:#{MY_DAPR_PORT}
はJava側のDaprではなくRuby側のDaprです。なので常にlocalhostですし、ポート番号も今回で言えば3500では無く3600です。require 'net/https' require "json" MY_DAPR_PORT=ARGV[0] n = 0 while true do n += 1 params = {data: {orderId: n}} url = "http://localhost:#{MY_DAPR_PORT}/v1.0/invoke/javaapp/method/neworder" uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) headers = { "Content-Type" => "application/json" } response = http.post(uri.path, params.to_json, headers) sleep 1 end実行&確認
では実行してみましょう。DaprはWebアプリだけでは無くコマンドも問題なくラッピングできるようです。
まず、API側を立ち上げます。$ dapr run --app-id javaapp --app-port 8080 --port 3500 ./mvnw quarkus:dev続いて別ターミナルでクライアント側を立ち上げます。
$ dapr run --app-id rubyapp --port 3600 ruby app.rb 3600Java側の実行結果を見ると以下のようになっています。
== APP == orderId: 1 == APP == orderId: 2 == APP == orderId: 3ちゃんと毎秒定期実行されてるのが分かりますね。
dapr list
を実行することで今起動しているDaprの一覧も確認できます。$dapr list APP ID HTTP PORT GRPC PORT APP PORT COMMAND AGE CREATED PID rubyapp 3600 55932 0 ruby app.rb 3600 28m 2020-05-24 21:08.13 44897 javaapp 3500 56777 8080 ./mvnw quarkus:dev 3d 2020-05-21 21:21.30 27471
まとめ
上手くRubyとJavaでDaprを経由して通信ができました。
このアーキテクチャの面白いところはRubyコードもJavaコードもあくまでDaprとしか会話してないのでその間はHTTPなりgRPCなりでセキュリティをさほど気にしなくて良いことです。
通信の暗号化とか認証とかはDapr同士がやれば問題ないのでセキュリティに関する部分を外だしできるのは大きいですね。分散トレーシングとかTwitter APIやKafkaなど他のコネクタもあるはずなのでそこら辺も試してみたいと思います。
同じマイクロサービス支援ということでKNativeと似たレイヤーかと最初は思ってたのですが、あちらはビルド環境やFaaS, Servelessと言ったインフラ観点よりなのに対し、こちらはかなりアプリより仕組みなのでCNCFにも採択されてますし今後が気になるツールなのは間違い無いですね。
- 投稿日:2020-05-25T09:13:08+09:00
Ruby Python Java 大文字小文字を区別しないソート
はじめに
ソートと言えば、数値順・辞書順ですが、それ以外のソートについて調べてみました。
嵌った話
@jag_507 さんのAtCoderでRuby学習10【第一回アルゴリズム実技検定 DoubleCamelCase Sort】を読んで、AtCoder 第一回アルゴリズム実技検定 F - DoubleCamelCase Sortをやってみましたが、ソートがうまくいかないです。
sort.rba = ["FisH", "DoG", "CaT", "AA", "AaA", "AbC", "AC"] a.sort # => ["AA", "AC", "AaA", "AbC", "CaT", "DoG", "FisH"] # 実際の返り値 ["AA", "AaA", "AbC", "AC", "CaT", "DoG", "FisH"] # 期待する返り値この記事:sortコマンド、基本と応用とワナの様な
-f
オプションはないのか、ドラえもんGoogle先生助けてー。大文字小文字を区別しないソート
Ruby に限らず多くのプログラミング言語の
辞書順のソートはASCIIコード順での並び替え
になりますので、大文字と小文字では大文字が若くなります。
そこで、大文字小文字を区別しないソートが必要になります。Ruby
ruby.rba = ['a', 'b', 'c', 'd', 'e', 'A', 'B', 'C', 'D', 'E'] p a.sort # => ["A", "B", "C", "D", "E", "a", "b", "c", "d", "e"] p a # => ["a", "b", "c", "d", "e", "A", "B", "C", "D", "E"] p a.sort{|x, y| x.casecmp(y).nonzero? || x <=> y} # => ["A", "a", "B", "b", "C", "c", "D", "d", "E", "e"] p a.sort_by{ |s| [s.downcase, s] } # => ["A", "a", "B", "b", "C", "c", "D", "d", "E", "e"]casecomp が大文字小文字を区別しないメソッドになります。
追記
コメント欄よりsort_by
の解法をいただきました。
最優先s.downcase
と次優先s
の2つ条件でソートを行っています。Python
python.pya = ['a', 'b', 'c', 'd', 'e', 'A', 'B', 'C', 'D', 'E'] print(sorted(a)) # => ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e'] print(a) # => ['a', 'b', 'c', 'd', 'e', 'A', 'B', 'C', 'D', 'E'] print(sorted(sorted(a), key=str.lower)) # => ['A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e']ソート HOW TO に、大文字小文字を区別しない文字列比較の例が載っています。
Python のソートは安定なことが保証されていますので、2重にソートする方法が考えられます。Java
java.javaList<String> a = Arrays.asList("a", "b", "c", "d", "e", "A", "B", "C", "D", "E"); a.sort(Comparator.naturalOrder()); System.out.println(a); // [A, B, C, D, E, a, b, c, d, e] a.sort(String.CASE_INSENSITIVE_ORDER); System.out.println(a); // [A, a, B, b, C, c, D, d, E, e]Java の場合、
CASE_INSENSITIVE_ORDER
が準備されています。まとめ
- 大文字小文字を区別しないソート に詳しくなった
- Ruby に詳しくなった
- Python に詳しくなった
- Java に詳しくなった
- 自然順のソートまで手が回らなかった
参照したサイト
AtCoderでRuby学習10【第一回アルゴリズム実技検定 DoubleCamelCase Sort】
sortコマンド、基本と応用とワナ
casecomp
ソート HOW TO
[Java 8]コーディングテストで使えるアルファベット順、文字列長順のソート方法
- 投稿日:2020-05-25T08:17:09+09:00
Railsでよく使うRubyのメソッド
はじめに
Ruby on Railsをメインに扱うWeb受託開発企業に転職し、約4ヶ月が経過しました。
初心者ながらにプロジェクトにアサインして頂き苦し楽しい毎日を過ごしております。
開発していく中でよく使うなーと思うRubyのメソッドをまとめていきます!メソッド一覧
if
説明不要の条件分岐メソッド。
true,falseで評価し、実行する処理を分岐させる。if 条件式 ifに対するtrue処理 elsif 条件式 elsifに対するtrue処理 else falseの処理 endeach
配列の要素を繰り返しの形で取得するメソッド。
RailsではDBのレコードを順に取得する際に利用しています。[1, 2, 3].each do |i| p i end 出力 1 2 3map
こちらもeachメソッドと似ていますが、
DBのレコードを取得する際にカラム指定する際に利用しています。例えば以下のテーブルがあった場合
Usersテーブル id |name | age | sex | 1 | テスト 一郎 | 20 | 男 | 2 | テスト 花子 | 18 | 女 | 3 | テスト 二郎 | 15 | 男 |このようにカラムを指定して取り出せます。
Users.map(&:age) 出力 [20, 18, 15]カラム指定でテーブルのid等を取得して、
関連付けされたテーブルを参照したりするのに便利です。
また、Whereメソッドと組み合わせ使用したりもよくします。where
DBからレコードを取り出す際にカラムとデータを指定して検索する際に使用します。
mapと同様のテーブルが存在した場合、以下の通りに取得できます。Users.where(age: 15) 出力 id |name | age | sex | 3 | テスト 二郎 | 15 | 男 |first
複数レコードが存在するDBの最初のレコードを取得します。
mapと同様のテーブルが存在した場合、以下の通りに取得できます。Users.first 出力 id |name | age | sex | 1 | テスト 一郎 | 20 | 男 |present?,blank?,nil?
present?:値が存在すればtrue,存在しなければfalse。配列を評価する際に[]の場合nilとなりfalseとなる。
blank?:値といえるものがない場合、真となる。
nil?:変数の値がnilまたは、値なしの場合、真となります。三項演算子
ifによる条件分岐が1度しかない場合によく利用します。
1行で条件分岐することができ、個人的にはスマートだと思います。条件式? trueの処理 : falseの処理count
DBのレコードをカウントしたり、ページネーションに利用したりします。
mapと同様のテーブルが存在した場合、以下の通りに取得できます。Users.count 出力 3参考
- 投稿日:2020-05-25T02:01:16+09:00
Rubyのバージョンを変えたらbundle installができなくなった
事件発生
ある日のことだった
$ bundle installを入力すると
Traceback (most recent call last): ~/.rbenv/versions/2.5.0/bin/ruby: invalid option -: (-h will show valid options) (RuntimeError)ええええ何だこれ!
他のコマンドも受け付けてくれない、何をしでかしたんだ…これまでを振り返ってみよう
自分が致命的な何かをしてしまったはず…
Ruby : 2.7 で開発していた
(自分の環境)/vendor/bundle/ruby/2.7.0/gems/active_hash-3.1.0/lib/associations/associations.rb:22: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call (自分の環境)/vendor/bundle/ruby/2.7.0/gems/activerecord-6.0.3.1/lib/active_record/associations.rb:1657: warning: The called method `belongs_to' is defined hereRailsコマンドを入力すると警告がたくさん出て鬱陶しいな
gemの警告は大事だけどまあいっか環境変数を追加して対処
export RUBYOPT='-W:no-deprecated -W:no-experimental'
.zshrc
に上を追加したら警告が消えたよストレスフリーだねRubyのバージョンを下げて開発しなければならなくなった
別バージョンでの開発が不可欠になったのでバージョンを下げました(Ruby:2.5)
Traceback (most recent call last): ~/.rbenv/versions/2.5.0/bin/ruby: invalid option -: (-h will show valid options) (RuntimeError)今までは別のバージョンにしてもこんなこと起きなかったのにな…
結論
export RUBYOPT='-W:no-deprecated -W:no-experimental'環境変数からこれを削除したら解決しました。
gemのバージョンが上がって警告が出なくなったら忘れずに削除しないとね。参考
https://qiita.com/mojihige/items/ce3282bec0c58f8ba637
https://qiita.com/hirocueki2/items/010c777d2125ee120a8e
- 投稿日:2020-05-25T01:05:46+09:00
[rails]Rspecで単体テストを行おう!
テストには、単体テストと統合テストの2種類がある。
-単体テスト
1つのプログラムに対して、正常に動くかのテスト。
(例)モデルクラスごと-統合テスト
1蓮の処理に関するテスト。
(例)ユーザーの新規登録用画面から値を入力、送信して、データベースにレコードが追加されるまでの流れ「Rspec」と「factory_bot」の導入
「Rspec」はテスト行うためのjem、
「factory_bot」はテストを行う際に一時的に情報を作成してくれる
お助けツールです。そのため、まとめてインストールしましょう。
①jemno
インストールGemfilegroup :development, :test do gem 'rspec-rails' gem 'factory_bot_rails' endターミナルbundle install②RSpecの最低限必要なファイル/ディレクトリ構成をRailsにインストール
ターミナル$ rails g rspec:install #> create .rspec # RSpecの設定ファイル #> create spec # スペックを格納する #> create spec/spec_helper.rb # スペック記述のためのヘルパ #> create spec/rails_helper.rb # Rails固有のスペック記述のためのヘルパここに、必要なディレクトリ・ファイルを追加していきます。
③必要なファイルの作成
ここでは、usersモデルに対してのバリデーションテストを
行なっていきます。◆テスト用ファイル
spec/内のファイルは、app/以下のテスト対象のrbファイルに対して1
対1で対応するかたちで配置します。app/models/user.rbに対するスペックは
spec/models/user_spec.rbとなります。◆テストのためのデータ用ファイル(factory_bot)
spec/factoriesにファクトリを配置することで、
簡単にテスト用のデータを利用できます。④ファイルの記述
名前空間を省略できるように設定する。
spec/rails_helper.rbRSpec.configure do |config| + config.include FactoryGirl::Syntax::Methods end.rspec--format documentation --require spec_helperspec/models/user_spec.rbspec/factories/users.rbFactoryBot.define do factory :user do nickname {"taro"} email {"kkk@gmail.com"} password {"00000000"} password_confirmation {"00000000"} end end⑤テストの実行
ターミナルbundle exec rspec spec/models/user_spec.rb