- 投稿日:2021-03-01T22:42:53+09:00
Rails Slackへメッセージを送信
前提条件
・ruby 2.6.6
・Rails 6.0.3.2
・macOS Catalina バージョン10.15.7概要
webアプリ内で特定のアクションが起きた時にSlackへ通知する機能を実装したいと思い
実装の備忘録としてここに記す。
思ったよりかなり簡単だったので、ぜひご自身のwebアプリに活用していただけると幸いです。gem 'slack-notifier'の導入
slackへ通知を送るにはgemを導入する必要があります。
Gemfilegem 'slack-notifier'ターミナルbundle
これでgemの導入が完了します。
Slack側準備
まずチャンネルのWebhook URLを取得するために、下記のURLにアクセスします。
https://slack.com/services/new/incoming-webhook
ここでRailsからの通知を受け取るチャンネルを設定します。既存のチャンネル、もしくは新規にチャンネルを作ってもどちらでも可能です。
チャンネルを入力後、Incoming Webhook インテグレーションの追加をクリック。
その後に表示される、Webhook URLを取得します。Slackに通知を飛ばす
飛ばしたいアクションの中に
notifier = Slack::Notifier.new( 'WEBHOOK_URL', channel: '通知を送りたいチャンネル名', username: 'notifier', ) notifier.ping 'ユーザーが投稿を作成しました!'と書き込みます。
最後に先ほど取得したWebhook URLを'WEBHOOK_URL'に入力します。
これで実装が完了となります。実際にアクションを起こし確認してみてください。
また、そのままWEBHOOK_URLを書き込むより、gemのdotenv-railsを使用したりして
環境変数として設定するといいと思います。参考文献
・RailsプロジェクトでSlack通知を実装する
https://tech.mof-mof.co.jp/blog/rails-slack-notifier/
- 投稿日:2021-03-01T21:50:42+09:00
単体テストコードでエラ〜メッセージすら出なかった話
こんばんは。
本日は初投稿です。
今回の内容は単体テストコードで発生してしまったあるトラブルについてです。WHAT
カード情報に不備があるとき、「Token」がないとしたテストコードでエラーが発生していますね。
これは何が発生しているのかというとエラーを期待する私に対してコンピュータが「エラーなんて発生していない」と返答をしているのです。医者と患者を例にすると患者が「私風邪っぽいです」と申告したとします。
Failure/Error: expect(@address_form.errors.full_messages).to include("Token can't be blank") 「ウチ風邪っぽいねん」それに対して
expected [] to include "Token can't be blank" 「いや健康でっせ」医者は診断の結果「健康です」と返してきました。
何が原因なのでしょうか?探っていきましょう。WHY
まず原因として考えられるのは、
単体テストコードの記述ミス(スペルミス、構文エラー)ですが、もしもこれが正解なら
ターミナルにはsyntaxエラーという別の返答を期待しますよね。
しかし、今回の事例ではそのような記載はなかったのでこの仮説はなしです。では何が原因か?
それはバリデーションにありました。
今回に限らず言えることとしてテストコードとは自分のかけたバリデーションがうまく機能しているのかを確かめる言わば「答え合わせ」です。
そんな答え合わせで発生した今回の事例modelsを見に行きましょう。with_options presence: true do validates :user_id validates :item_id validates :street_address, format: {with:/\A[ぁ-んァ-ン一-龥々]/, message: "Can't be blank"} validates :postal_code, format: { with:/\A\d{3}[-]\d{4}\z/ , message: "is invalid. Include hyphen(-)" } validates :municipality, format: { with: /\A[ぁ-んァ-ヶ一-龥々]+\z/, message: "is invalid"}はい、Tokenにバリデーションをかけていないどころかそれすらありません。
これが原因ですね。というわけで
validates :tokenとしたことで事案は解消しました。
結論
今回の件で学んだこととしてテストコードから返されるメッセージが空白[]の場合、
expected [] to include
バリデーションを真っ先に疑った方がいいです。
- 投稿日:2021-03-01T21:48:17+09:00
【Rails】 バッチ処理でのエラー「sudo: systemctl: command not found」
はじめに
今回は、とあるスクールのカリキュラムを参考に、初めてバッチ処理を実装していた時の話です。
cronを利用して削除プログラムを定時処理として実行させようとしていたところ、エラーが発生してしまいました。
自身の備忘録として、また同じようなエラーに遭遇された方の参考になればと思い投稿させて頂いております。(参考)
・crontabコマンド
https://it-mikeiken.com/crontab-command・CentOSのバージョン確認
https://ja.stackoverflow.com/questions/66504/centos%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E7%A2%BA%E8%AA%8D
開発環境
ruby 2.6.3
Rails 5.2.4.4
AWS Cloud9(Amazon Linux)
エラーの内容
推奨されていたコマンドがこちらですが、どちらも同じエラーで弾かれてしまいます。
terminal.# cronが起動しているか確認するコマンド $ sudo systemctl status crond sudo: systemctl: command not found # cronを起動するコマンド $ sudo systemctl start crond sudo: systemctl: command not found
解決策
以下のコマンドを実行することで、cronを起動、そして起動しているかを確認することが出来ました。
terminal.# cronが起動しているか確認するコマンド(どちらも可) $ sudo service crond status or $ sudo /etc/rc.d/init.d/crond status # cronを起動するコマンド(どちらも可) $ sudo service crond start or $ sudo /etc/rc.d/init.d/crond start
エラーが起きた原因
CentOS6以前とCentOS7以降からでは、コマンドが変わっている
という記事を発見しました。
そこで、自身のバージョンを確認してみようと思い、以下のようにコマンドを入力すると、「No such file or directory」、つまり「そのようなファイルやディレクトリはありません」と言われてしまいます。terminal.# CentOSのバージョンを確認するコマンド $ cat /etc/redhat-release No such file or directoryこの時点で、そもそもOSってなんだっけ?と思い始めたわけです。
OSについて検索をしてみると、ある記事に「システム全体を管理し、様々なアプリケーションソフトを動かすための最も基本的なソフトウェア」と定義されていました。
更に調べてみると、私が開発環境で使用しているAWSのCloud9、「Amazon Linux」もCentOS/RHEL系に分類されるLinux OS
の一つだということが分かりました。OSの一つではあるが、CentOS
とは異なるためファイルがないと言われてしまったということです。
Amazon Linuxのバージョンを調べるコマンドも見つけたので、一応下記に掲載しておきます。terminal.# Amazon Linuxのバージョン確認コマンド $ cat /etc/system-release Amazon Linux AMI release 2018.03
結論
今回自身の結論としましては、OSの種類が違うとコマンドが変わるということで一度納得をすることにしました。まだまだ知識不足で、これらのコマンドがどう違い、どのOSにどのコマンドが正しいかというところまでは理解することが出来ませんでしたので、今後の課題としたいと思います。
もし詳しい方がいらっしゃいましたら、ご教授いただければ嬉しく思います。
終わり
今回は以上になります。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になればと思っています。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。
- 投稿日:2021-03-01T21:17:49+09:00
A server is already running. 対処方法
前提条件
・ruby 2.6.6
・Rails 6.0.3.2
・macOS Catalina バージョン10.15.7概要
ローカル環境でrails sを走らせようとしたが
"A server is already running"
というエラーが発生した。その対処法を記述する。1.再起動をする
ターミナルを全て閉じ再起動する。
これで解決する場合もあるみたい。私はやったことはない。2.tmp/pids/server.pid.を削除する
私はこの方法で毎回解決している。
ターミナルで"A server is already running"の後に
Check /Users/・・・・/tmp/pids/server.pid.
と出ている。
サーバーを落とす際にこのファイルが消されるはずなのだが、何らかの不具合で
消されていない状況が発生するようだ。
Finderからアクセスし、直接"server.pid."を削除
これで解決。
- 投稿日:2021-03-01T21:02:06+09:00
「action_args」Gemから学ぶ「Ruby」④ ソース分析3日目
本日はabstract_controller.rb分析
「action_args」Gem分析3日目です。
abstract_controller.rbを見てみます〜学習ポイント1
params_handler.rbmodule ActionArgs module ParamsHandler refine AbstractController::Base do ... end end endabstract_controller.rbrequire_relative 'params_handler' using ActionArgs::ParamsHandlerKernel.#require_relative: 現在のファイルからの相対パスでrequireします。
Module#using: 引数で指定したモジュールで定義された拡張を現在のクラス、モジュールで有効にします。instance method Module#refineについてはこちら参考
refineclass C def foo puts "C#foo" end end module M refine C do def foo puts "C#foo in M" end end end x = C.new x.foo # => "C#foo" using M x = C.new x.foo # => "C#foo in M"
params_handler.rb
でrefineしたActionArgs::ParamsHandler
をusingを使って有効にしている。学習ポイント2
abstract_controller.rbmodule ActionArgs module AbstractControllerMethods # controllerのactionが実行される前に呼ばれる def send_action(method_name, *args) return super unless args.empty? return super if !defined?(params) || params.nil? strengthen_params! method_name values, kwargs_values = extract_method_arguments_from_params method_name if kwargs_values.any? super method_name, *values, **kwargs_values else super method_name, *values end end end module AbstractControllerClassMethods # controllerにstrong prameterを指定したら呼ばれる(例:permits :name, :age) def permits(*attributes, model_name: nil, **kw_attributes) @permitted_attributes, @permitting_model_name = attributes << kw_attributes, model_name end end endAbstractController::Baseについてはこちら参照
sendclass Book def title puts "ruby" end end book = Book.new book.title => ruby # sendを使う場合 book.send(:title) => rubyabstract_controller.rbAbstractController::Base.send :prepend, ActionArgs::AbstractControllerMethodssend_actionをOverrideする目的でprependしている
AbstractController::Base.prepend(ActionArgs::AbstractControllerMethods)
でも同じじゃないかと思いますがsend
を使う理由は何だろうと思って調べたらsend
の場合はメソッドがPrivateの場合でも実行できることだったのでそれが理由じゃないかと思いました。class Book private def title "ruby" end end book = Book.new => #<Book:0x00007facf3d6ee60> book.title NoMethodError: private method `title' called for #<Book:0x00007facf3d6ee60> from (pry):15:in `__pry__' book.send(:title) => "ruby"AbstractController::Baseソースのsend_actionはこちらを参考
instance method Module#prepend
prepend使い方module X def foo puts "X1" super puts "X2" end end class A prepend X def foo puts "A" end end A.new.foo => X1 A # superはA.fooを意味 X2abstract_controller.rbAbstractController::Base.singleton_class.send :include, ActionArgs::AbstractControllerClassMethodsなぜこちらで
singleton_class
を使っているのかはまだわからないので今後わかるようになったら追記instance method Object#singleton_class: レシーバの特異クラスを返します。まだ特異クラスがなければ、新しく作成します。
instance method Module#include: モジュールをインクルードします。
以下のようにControllerに
permits
を追加すると上記に定義したAbstractControllerClassMethods.permits
が処理するpermitsclass UsersController < ApplicationController permits :name, :age .... endincludemodule M end class C1 include M end class C2 < C1 include M # この include は無視される end p C2.ancestors # => [C2, C1, M, Object, Kernel]最後に
普段意識しないで使ってた機能(permits,send_action)がどこで処理されるのかわかるようになりました。
- 投稿日:2021-03-01T21:00:35+09:00
Boolean型の記述方法について
orangeを2倍にした数がappleの数以上であった場合にのみ
trueを返しそれ以外はfalseを返したい時、以下のように記述できる。def fruit(apple, orrange) orrange * 2 <= apple endこのような書き方は冗長になる。
def fruit(apple, orange) if orange*2 <= apple true else false end end
- 投稿日:2021-03-01T20:42:54+09:00
【Ruby】初心者がるりまを読んで初めて知ったメソッドをまとめてみる
最近るりまを読んでいるので、学習したメソッドから有用そうなものをまとめていこうと思います(追加予定)
たのしいRubyを読み終わったあたりの初心者が対象の記事ですArrayクラス
eql?(other) -> bool
self
とother
を比較します
普段使うなら==
で事足りますが、1
と1.0
を区別するといった場合に有用ですassoc(key) -> Array | nil
selfを配列の配列と仮定して
[0]
がkey
と等しい最初の配列を返します
元々はハッシュのような配列のためのメソッドですが、特殊な形式の配列に使えそう[[1, 10], [2, 20]].assoc(2) #=>[2, 20]
[1]
を検索する rassoc というメソッドもありますbsearch { |x| ... } -> object | nil
rubyで二分探索ができます
使い道…?dig(idx, ...) -> object | nil
ネストしたオブジェクトを参照して返します
[]
を連続して使うのと結果は同じですが見栄えが良くなりますary = [[1, [2, 3]]] ary[0][1][1] #=> 3 ary.dig(0, 1, 1) #=> 3fetch(nth) -> object
idx
の要素を返します
[]
と違うのは、要素がなかった場合の挙動です
1. 第二引数を与えているとその値を返す
2. ブロックを与えているとnth
をブロック変数としてブロックを評価 しその戻り値を返す
3. どちらもなければ例外発生if文を書かなくも様々な対応ができる素晴らしいメソッド
values_at(*selectors) -> Array
selectors
のインデックスに対応する要素を配列で返します
インデックスの指定方法は[]
と同じですrindex(val) -> Integer | nil
val
と等しい最後の要素を返します
index の逆バージョンですねsample -> object | nil
self
からランダムな要素を一つ返します
引数を与えた場合(たとえ1でも)その個数のランダムな要素を配列で返しますreverse_each {|item| ... } -> self
selfを逆順にブロック変数に代入しブロックを実行します
見た目ではreverse.each {|item| ...}
とほぼ変わりませんが、たぶん処理効率が良いですdup -> Array
self
のコピーの配列を返します
レシーバと戻り値は別のオブジェクトですが、要素は同じオブジェクトを参照しているので、完全なコピーを作るには Marshalモジュール がオヌヌメですzip(*lists) -> [[object]]
self
とlists
の各要素からなる配列の配列を生成して返しますary1 = [1, 2, 3] ary2 = [4, 5, 6] ary1.zip(ary2) #=> [[1, 4], [2, 5], [3, 6]]rotate(cnt = 1) -> Array
cnt
の位置の要素を先頭とした配列を返しますary.rotate #=> [2, 3, 4, 1] ary.rotate(2) #=> [3, 4, 1, 2]破壊的な
rotate!
もありますdelete_at(pos) -> object | nil
インデックスで
delete
出来ますdelete_if {|x| ... } -> self
ブロックの評価が真(
nil
,false
以外)になった要素を削除します
reject! と違い、変化がなくてもself
を返すので中々便利ブロックの評価が偽になった要素を削除する keep_if もあります
drop(n) -> Array
先頭からn個の要素を削除して残った配列を返します
削除した要素を返す shift と違い、残った要素を返すので使い分けができますねブロックで判定を行う drop_while もあります
flatten(lv = nil) -> Array
self
を平坦化した配列を返します有名なメソッドですが、実は引数を取れるようです
lv
を与えるとその深さまで平坦化しますary = [1, [2], [3, [4]]] ary.flatten #=> [1, 2, 3, 4] ary.flatten(1) #=> [1, 2, 3, [4]]uniq -> Array
self
から重複した要素を削除した配列を返します
このメソッドはブロックをとることができ、ブロックを与えた場合、各要素をブロック変数としてブロックを評価し、その戻り値が重複した要素を削除します["Alice", "alice", " Tony"].uniq(&:downcase) #=> ["Alice", " Tony"]to_h -> Hash
self
を[key, value]
のぺアの配列として解析した結果をHash
にして返します有名なメソッドですが、実はブロックを取れます
ブロックが与えられた場合、各要素をブロック引数としてブロックを評価し、その戻り値を[key, value]
のペアの配列として解析します[1, 2].to_h { |x| [x, x * 10] } #=> {1=>10, 2=>20}
each_with_object
などでコーディングするよりもかなり文章量が減りますねtranspose -> Array
self
を行列と見立て、縦列と横列を入れ替えますary = [ [1,2], [3,4], [5,6] ] ary.transpose #=> [[1, 3, 5], [2, 4, 6]]最後に
るりまをよむのはいいぞ。(るりまおじさん)
表現力が上がり、メソッドの詳しい挙動が知れることもあり、良い事づくめです
紹介してないメソッドも沢山あるのでぜひ初心者は ruby style gide を読むのもオススメです
- 投稿日:2021-03-01T19:02:43+09:00
【Rspec】Error: raise WrongScopeError の解消【原因:rspec_railsのバージョン】
Rspecを初めて書こうと思い、色んな記事を参考にrspec_rails をインストールしてテストを書きました。いざRspecを走らせてみると、以下の様なエラーが発生しました。
Failure/Error: raise WrongScopeError, "`#{name}` is not available from within an example (e.g. an " \ "`it` block) or from constructs that run in the scope of an " \ "example (e.g. `before`, `let`, etc). It is only available " \ "on an example group (e.g. a `describe` or `context` block)." `name` is not available from within an example (e.g. an `it` block) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc). It is only available on an example group (e.g. a `describe` or `context` block).どうやら
name
がスコープの範囲外にあるのがダメと言っています。しかしname
に全く身に覚えがなかったので途方にくれていました。色々調べてみると、rspec_railsのバージョンが古いと同様なエラーが発生する様です。
そこでrspec_railsのバージョンを現在の最新バージョン(4.0.2)に変更したところ、解決しました。
同様のエラーに悩んでいる人の助けになれば幸いです。
- 投稿日:2021-03-01T18:04:33+09:00
作品を評価し合うwebアプリ作ってみた[個人開発]
今回作ったもの
タイトルにもある通り、絵や音楽、プログラムなどの作品を評価し合うコミュニティサイト「PRORP」です。
なぜ作ったか
ヤフー知恵袋でこんな質問を見つけました。
塾に通ってるなら他人からの評価は簡単に受けれますが、独学の場合なかなかしっかりと評価を受けるときってありません。
以外に他人の評価って欲しがってる人が多いのかなと思い作りました。工夫した点
投稿一覧ページをなくした
公開したばかりでユーザーも投稿も少ないのでユーザーが投稿が見る方法は検索だけにしました。
使いずらいですが最初はしょうがないんです。すみません(__)Raty.jsというものを使い実装しました。まあまあ簡単です。
平均値も求められます。開発効率化のために
今回、初めて土台アプリを作ってみました。
というのもwebアプリ開発って結構同じことの繰り返しじゃないですか。CRUD、コメント機能、いいね機能、通知機能など
どんなアプリを作るにしろ必要な機能ってありますよね?
それを毎回毎回作ってたら非効率的だなと思いある程度、完成してる土台アプリを先に作りそれをコピーしてPRORPを作りました。土台アプリでの開発の感想としてはとにかく早いです
当たり前ですが新しく1から作るよりカスタムするほうが早いので、土台アプリは作ってよかったなーと思ってます。土台アプリに実装した機能としては
- CRUD
- コメント機能
- いいね機能
- 通知機能
- ヘッダー、フッター、2カラムレイアウトなどのCSS
- 新規登録、ログイン機能
- ユーザーIDとの紐付け
くらいです。もちろん土台アプリなので汎用性重視です。
使ったgem
(省略) gem 'ridgepole' gem 'slim-rails' gem 'html2slim' gem 'pry-rails' gem 'devise' gem 'kaminari' gem 'activeadmin' gem 'rack-attack' gem 'rails-i18n' gem 'devise-i18n' gem 'devise-i18n-views' gem 'carrierwave' gem 'fog-aws' gem 'dotenv-rails' gem 'rmagick'まとめ
土台アプリは今度からも使っていこうと思います。
作りたいものがない、という方は土台アプリをまず作ってみるといいかもしれません。まぁとにかくこのPRORP使ってくれ!(誘導下手)
ツイッターもやってます!
https://twitter.com/yamada1531
- 投稿日:2021-03-01T17:41:40+09:00
【超かんたん】Fakerを使ってダミーデータを作成しよう!
Fakerを利用してアプリにダミーデータ生成します。
今回は、deviseで作成したusersテーブルにダミーデータを投入していきます。Fakerとは
Fakerとはランダムな値を生成してくれるGemです。人名、メールアドレス、パスワードの生成はもちろんのことゲームのタイトルやアニメのキャラクター名なども生成してくれる遊び心がいっぱい詰まったGemです。公式ドキュメント
では、さっそく使って行きましょう!
Fakerの導入
gamのインストール
Gemfilegem 'faker'ターミナルbundle install導入完了。
ダミーデータの生成
今回は、deviseで生成されるemailとpasswordの他にnameカラムを追加しています。
deviseで生成されるemailカラムには一意性制約があるのでFaker::Internetとfree_emailの間にuniqueと記述しています。
passwordもデフォルトでは6文字以上でないと登録できないのでmin_length: 6と記述しています。この辺りはご自身が制作するアプリの仕様によって適宜変更していきましょう。
db/seeds.rb50.times do name = Faker::Name.name email = Faker::Internet.unique.free_email password = Faker::Internet.password(min_length: 6) User.create( name: name, email: email, password: password, password_confirmation: password ) endターミナルrails db:seedダミーデータが生成されています。
日本語化してみよう
Fakerは一部、日本語対応しています。
日本語化はとても簡単です。
config/application.rbにconfig.i18n.default_locale = :jaという記述を追加するだけです。config/application.rbmodule アプリ名 class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 6.0 config.i18n.default_locale = :ja #追加 # 省略 end endターミナルrails db:seed番外編
ここからは番外編です。
ダミーデータの名前を変更してみましょう。
詳しくは公式ドキュメントを参照しましょう。まずはja.ymlファイルを作成します。
ターミナルtouch config/locales/ja.ymlこちらの記述を作成したja.ymlにコピペし日本語化しましょう。
※日本語化できるのは一部だけです。ポケモンの場合
db/seeds.rb50.times do name = Faker::Games::Pokemon.name #変更 email = Faker::Internet.unique.free_email password = Faker::Internet.password(min_length: 6) User.create( name: name, email: email, password: password, password_confirmation: password ) endターミナルrails db:seedスーパーマリオの場合
db/seeds.rb50.times do name = Faker::Games::SuperMario.character #変更 email = Faker::Internet.unique.free_email password = Faker::Internet.password(min_length: 6) #以下略ターミナルrails db:seedドラゴンボールの場合
db/seeds.rb50.times do name = Faker::JapaneseMedia::DragonBall.character #変更 email = Faker::Internet.unique.free_email password = Faker::Internet.password(min_length: 6) #以下略ターミナルrails db:seed以上になります。
- 投稿日:2021-03-01T15:08:46+09:00
Ruby 出力について
出力について
Rubyには処理の結果を目に見える形で出力するためのメソッドとして様々な出力系メソッドが存在します。
それぞれの違いを調べ、必要な場面で使いこなせるようにまとめておきます。
・printメソッド:改行を入れずに引数に指定した値をそのまま出力する。
print 123 print 'あいう'【実行結果】 123あいう・putsメソッド:末尾に改行が入る形で引数に指定した値をそのまま出力する。
puts 123 puts 'あいう'【実行結果】 123 あいう・pメソッド:出力する値と共に型情報(文字列や数値型など)分かりやすい形で出力します。数値はそのまま表示、文字列はダブルクォートで挟んだ状態で表示されます。
p 123 p 'あいう'【実行結果】 123 "あいう"
- 投稿日:2021-03-01T14:05:49+09:00
ruby silverに無事合格したので、体験記をまとめてみる(2021/02)
無事ruby silverに合格したので
他、数多くの合格体験記がありますが
私もその例に倣って書いてみることにします。2021年2月の最終週に初受験。88点で合格しました。
12問までミスしても大丈夫だ!というのが、心理的には大分安心材料で
多少答えに自信がない問題があったとしても、ペースを崩さずに進められました。前提
私のrubyとの関わり具合です
- ちょうど業務でrubyを使い始めて1ヶ月くらい(railsに苦戦中)
- 会社で取得を推奨されたので、重い腰をあげてトライしました
- 以前からrubyの勉強はちまちましていたが、udemy1,2講座と、所謂チェリー本を少し読んだ程度
- プログラマー歴自体は3年弱
- あまり意識は高くない方のプログラマーです。勉強は好きじゃないです(白目)
勉強時間
- 約1ヶ月くらい前から準備し始めました
- 平日は基本勉強らしい勉強はせず。休日メインで学習を進めました。
- 合計学習時間は30時間くらい
- 集中してガッツリ取り組むより、ながら勉強が多かったです。
勉強に使用したもの
① ruby技術者認定試験合格教本
② Rex
③ 模擬問題集
④ ruby実行環境
⑤ Qiita
⑥ google先生基本はとにかく問題を解いて、間違えたところや、理解が乏しい部分を中心に
本やwebで調べるという作業を繰り返しました。
他のさまざまな試験の勉強と、特別違うところはないですね。silverは暗記ゲー なんて書かれたりもしていますが(確かにその側面も大きいですが)
暗記に頼って挑むと足元を掬われるような、いやらしい細かい部分を問われる問題もそこそこ出題されるので
きちんと一つ一つの問題を理解できるまで深堀った方が、結果的に近道かな、と感じました。①ruby技術者認定試験合格教本
rubyの試験を受けるならまず用意すべき本
私はそもそもrubyに関しての知識が不足していたので、まずはさらっと、silverの試験範囲に関して目を通しました。
1回読んだだけじゃ正直何も頭には入ってこないのですが、その後問題とかを解き始めてから再度この本に戻ってくると
ああ、そういうことだったんだ〜ってなったりして、いい使い方ができたと思います。問題の解説を読んでもいまいち理解できなかった時に、リファレンス代わりにこの本で調べたりもしました。
(巻末の索引がナイスです)amazonのレビューにも書かれてますが、silverとgoldの試験範囲が判りづらいのが難点。
用意されている演習問題の80問は、試験の1週間前に解きました。
本番を終えた今振り返ると、どの問題も合格に直結する重要問題ばかりでしたので
この演習問題でちらほら間違えている様だと、受験には時期尚早かもしれません。②Rex
REx - Ruby Examination
ruby silver, goldの演習問題を、本番さながらに解く事ができるサービス(要gitHubアカウント)
一回通して教本を読んだ後は、Rexを繰り返し解いていました。
最初、50問通して解いてみたところ62点で
(お、最初からこれくらい点取れてたら結構余裕じゃん?)と思ってしまい
そこから勉強に身が入らない期間が続きました(悪いクセ)
その後、複数回解いてみても点が伸びず、焦り始めることになります。
最終的に6回、通して解きました。94点が自己最高得点。Rexの問題は教本の問題と比べると、コードの細かい違いによる結果の相違が問われる問題が多いと思います。
一見似た様なコードなのに、スペースが一つ空いているだけで得られる結果が違ったりして
何度もそれにひっかかりました。解いている時はムカついていましたが
おかげで、注意深く問題を読むクセがつけられたと思います。オススメです。③模擬問題集
silver_j.md
gistで公開されているsilverの問題集です。
Rexと同じ様な使い方をして、何度か繰り返し理解できるまで解きました。
本番でも、似た様な問題が実際に出題されました。35問目と36問目が、全く同じ問題になってるのは気にしない
④ruby実行環境
①~③の問題を解いてみて、よく理解ができなかった問題や
この選択肢はなんで間違いなんだろう?と思った問題は
実際に実行環境で流してみて、出力を確認する様にしていました。
これをするだけで理解スピードが段違いだったと思います。アウトプット大事。
途中途中でputsしたりして、変数の中身がどう変わっていくのかを追ってみたり
少しコードをいじってみて、出力内容がどう変わるのか試してみたりすると
応用力が付いた気になれます。オススメ。⑤Qiita
問題を解いてみても理解できなかった箇所や
さらにそこから派生した知識を得たい時に、Qiitaで調べると
先人たちが残してくれた記事がいくつもひっかかるのでありがたかったです。また、④と同様、アウトプットの場としても優秀で
自分がよく間違えた問題や、本番でついミスってしまいそうな箇所をまとめて記事を1つ作りました
ruby silver受験前に個人的な注意ポイントまとめ公開する際に、間違った事を適当に書くわけにいかないので
自ずと関連する知識まで、ちゃんと調べる必要が出てきます。
この作業によって、苦手だな〜覚えられないな〜と感じていた部分を
なくす事ができ、自信にも繋がりました。
今振り返ってみると、上記でまとめた内容に関連する問題が
本番で3,4問は出題されてました。やっておいてよかった…
後からruby silver受ける方達のためにもなるので、積極的に発信側に回りましょう!⑥Google先生
教本の解説は分かりやすいですが、淡々と説明が続いていくため
個人的には頭に入りにくく感じました。
より分かりやすく、噛み砕いた説明が欲しい時は、google先生にとりあえず聞いてみると
いい感じにまとめられた記事が見つかる事が多いです。
公式のリファレンスも、サンプルコードがあったりして意外と親切だし、何より絶対間違いがないのでオススメですが
いきなり公式のリファレンスを読みにいっても、分量に圧倒され、モチベが続かないと思うので
他で勉強したところの補強程度に留めておくのが賢い使い方かと感じました。
また、公式はver.3.0準拠になっている点も注意です
(ruby silver試験は2021年2月現在ver.2.1準拠です)受験当日の流れ
試験開始が14時半からだったので、早めに現着して近くのカフェで最後の見直しを行いました。
具体的には以下の通りです
- ①教本の演習問題80問を最後にもう一度全て解き直し
- この時にはわからない問題がない状態まで理解を進めていたので、自信を持たせる目的が大きかったです。
- ②Qiita記事の最終確認
自分自身でミスってしまいそうなポイントをまとめておいた事がここで活きました。
また、直前に見直す用に先人の方がまとめていただいた神サイトを見直しました。
組み込み定数や、特殊変数、上書きできない演算子など、単純な暗記項目は直前に見直しておくといいと思います。
Ruby Silver試験前に見直すと幸せになれるメモ
テストセンターでの受験は初めてだったので、どんな感じなのかな〜と思っていましたが
PC毎にデスクが区切られていて、周囲の雑音をシャットアウトするヘッドホンも備え付けられており
集中して受験できる環境が整っていました。
紙とペンも与えられるので、計算が絡む問題を整理しながら解きたい時などに利用できます。
(持参は不可)同じ時間に受験の受付している方で、一人証明書の不備があって受験できなかった方がいらっしゃったので注意です。
なんなら試験勉強よりもよほど大事です。試験要項はきちんと確認しておきましょう。本人確認書類は2種類必要です!
A: 写真付きの確認書類(運転免許証・社員証・マイナンバーカードなど)
B: 署名付きの確認書類(クレジットカード・図書館の利用証など)Bの証書を私もすっかり忘れていて、クレカを受付であたふたしながら提出することとなりました。
気になる方は以下も確認を
試験当日の受験の流れ90分の試験ですが、正直そんなに時間は使わないと思います。
見直しも兼ねてもう一度頭から全て解き直して、45分くらいで終わりました。
見直しの際に間違いに気づいた問題が1,2問あったので、必ずやりましょう。問題の所感としては、今まで解いてきた練習問題と、難易度的には変わりないです。
練習問題とほとんど同じ内容の問題:4割
練習問題に関連した事柄について問われた問題:4割
練習問題ではあまり見なかった箇所を問われた問題:2割 くらいだったかと思います。割合から見ても、練習問題を完璧にできる様にしておく・関連する知識を深堀して覚えておく
これを徹底できれば、合格はさほど難しくない試験だと感じました。12問まで間違えられますしね。まとめ
- 各練習問題を何度も解いて、頻出問題の理解を深める!
- つまづいた箇所を中心に納得するまで深堀して、アウトプットを行う!
- 暗記項目は試験直前にもう一度確認!
- 本人確認書類を忘れない事(これ大事) 落ち着いてチャレンジしましょう
新たなrubyist達の、試験合格をお祈りしております。
(次はgoldだー)
- 投稿日:2021-03-01T14:03:16+09:00
basic認証を導入する
basic認証をアプリケーションへ導入する
controllers/application_controller.rb にて以下の記述を行う。
class ApplicationController < ActionController::Base before_action :basic_auth private def basic_auth authenticate_or_request_with_http_basic do |username, password| username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"] # 環境変数を読み込む記述に変更 end end end環境変数を使って、ユーザー名とパスワードの設定を行う。
heroku上で管理を行うため以下の記述でセットする。% heroku config:set BASIC_AUTH_USER='aaaa' % heroku config:set BASIC_AUTH_PASSWORD='1111'
正しく設定できているかはheroku configでしっかり確認します。
変更したコードをコミットし、Herokuへデプロイします。% git add . % git commit -m "Basic認証を導入" % git push heroku master
これで本番環境へ反映します。
- 投稿日:2021-03-01T13:39:49+09:00
【Ruby on Rails】deviseの導入
Ruby on Railsの便利機能であるdeviseの導入手順をまとめます。
環境
Version MacOS Catalina 10.15.7 Ruby 2.6.5 Rails 6.0.3.5 Gemfile
Gemfileの一番下に下記を記述します。
Gemfilegem 'devise'ターミナルで下記コマンドを実行しgemを読み込みさせます。
ターミナルbundle installターミナルで下記コマンドを実行しdeviseを導入します。
ターミナルrails g devise:install下記のようなメッセージが出てそれぞれファイルが生成されます。
create config/initializers/devise.rb create config/locales/devise.en.ymlモデル作成
Userモデルを作成します。
ターミナルで下記コマンドを実行します。ターミナルrails g devise user下記のような実行結果がでます。
メッセージにあるようにapp/models
にuser.rb
というモデルファイルが生成されます。ターミナルRunning via Spring preloader in process 1632 invoke active_record create db/migrate/2021×××_devise_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml insert app/models/user.rb route devise_for :usersマイグレーションファイル編集
db/migrate
に新しく2021×××_devise_create_users.rb
というマイグレーションファイルが生成されているので編集が必要であれば編集します。例えば、元々はユーザー登録に
password
しか必要ないようになっているので、下記のようにstring型
のname
を追加したりできます。db/migrate/2021×××_devise_create_user.rbclass DeviseCreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" # 下記を追加 t.string :name, null: false編集したら下記コマンドでマイグレーションを実行します。
ターミナルrails db:migrateビュー作成
devise用のビューを作成します。
下記コマンドを実行します。ターミナルrails g devise:views下記のような実行結果が出てdeviseに関する様々なファイルが生成されたことが分かります。
ターミナルRunning via Spring preloader in process 1841 invoke Devise::Generators::SharedViewsGenerator create app/views/devise/shared create app/views/devise/shared/_error_messages.html.erb create app/views/devise/shared/_links.html.erb invoke form_for create app/views/devise/confirmations create app/views/devise/confirmations/new.html.erb create app/views/devise/passwords create app/views/devise/passwords/edit.html.erb create app/views/devise/passwords/new.html.erb create app/views/devise/registrations create app/views/devise/registrations/edit.html.erb create app/views/devise/registrations/new.html.erb create app/views/devise/sessions create app/views/devise/sessions/new.html.erb create app/views/devise/unlocks create app/views/devise/unlocks/new.html.erb invoke erb create app/views/devise/mailer create app/views/devise/mailer/confirmation_instructions.html.erb create app/views/devise/mailer/email_changed.html.erb create app/views/devise/mailer/password_change.html.erb create app/views/devise/mailer/reset_password_instructions.html.erb create app/views/devise/mailer/unlock_instructions.html.erb簡易的なビューがこれだけで簡単に生成できてしまいます。
おまけ
バリデーションはモデルに記述します。
下記が例です。app/models/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable # 下記を追加 validates :name, presence: true endカラムにデフォルトの
password
以外を追加した場合は、それも保存できるように許可の記述をしないと弾かれてしまいます。
app/controllers/application_controller.rb
に以下のように記述します。app/controllers/application_controller.rbclass ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? private def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) end end
before_action :configure_permitted_parameters, if: :devise_controller?
でdeviseに関わる動作については全て起動するように設定します。
そしてその処理としてconfigure_permitted_parameters
で今回作成したname
も許可するように記述します。
configure_permitted_parameters
という名前は慣習的なものらしいので特に意味はありません。以上です。
- 投稿日:2021-03-01T13:35:49+09:00
[ruby] Google Firebase Storageの簡単な使い方
Spark プラン 無料枠 Storage
https://firebase.google.com/pricing
プロジェクトID
バケットID
複数のバケットは持てないので、ここに表示されるドメイン名がそれ(
gs://
以下)コードサンプル等
storagestorage = Google::Cloud::Storage.new(project_id: ENV["FIREBASE_PROJECT_ID"])bucketbucket = storage.bucket ENV["FIREBASE_STORAGE_BUCKET_ID"]ファイルの追加bucket.create_file "/path/to/file" # バケットのルートに置く bucket.create_file "/path/to/file", "folder/name/file" # バケットのサブフォルダに置く # metadataを付けて置く(後で説明するpublic_urlで必要になる) # tokenはぶつからない、推測されにくいものであれば何でも良さそう require "active_support/all" bucket.create_file "/path/to/file", metadata: { firebaseStorageDownloadTokens: SecureRandom.uuid }ファイルの参照files = bucket.files # 全部 # Signature: find_files(prefix:?, delimiter:?, token:?, max:?, versions:?) # 例 a/b/c_d_e.jpg というファイルがあった場合、 # bucket.find_files prefix: "a/b/c_" で見つけられる path.start_with? 的な files = bucket.find_files prefix: "folder/sub/prefix" file = bucket.file("path/to/file") # 直接ファイル1つ更新bucket.create_file "/path/to/file1", "folder/sub/file" bucket.create_file "/path/to/file2", "folder/sub/file" # 同じところに同じ名前で置く削除file.delete誰でもアクセスできるURLfile.signed_url # => デフォルトで5分間の公開が可能 file.signed_url expires: 1.hour.to_i # => 1時間の公開が可能、最大で7日間X-Goog-Expires: The length of time the signed URL remained valid, measured in seconds from the value in X-Goog-Date. In this example the Signed URL expires in 15 minutes. The longest expiration value is 604800 seconds (7 days).
誰でもアクセスできるURL(永続的)def public_url(file) # more like permanent URI( "https://firebasestorage.googleapis.com/v0/b/#{CGI.escape file.bucket}/o/#{CGI.escape file.name}", ).tap do |uri| uri.query = { token: file.metadata["firebaseStorageDownloadTokens"], alt: :media, }.to_query end end
firebaseStorageDownloadTokens
がここで必要になるので、ファイルを置く時にmetadata
の指定が必要になる。
(javascriptからだとアップロードしたファイルのdownloadUrl
がメソッドとして提供されているが、rubyだとまだない?)
- 投稿日:2021-03-01T12:41:09+09:00
【Rails】link_toのxxx_pathが長い時はpolymorphic_pathを使うと綺麗に書けるお話
Railsにはよく使うものとして
link_to
やbutton_to
があります。これらはリンクやボタンを生成するヘルパーです。更に、遷移先のURLやpathを指定することが出来て、以下のように書けます。
<%= link_to '詳細へ', user_path(user.id) %>では、URLのネストをした場合、
xxx_path
はどうなるでしょうか?例えば、ユーザーは記事を投稿でき、その記事たちをお気に入りに追加できる機能があったとします。
ルーティングはこちら
routes.rbRails.application.routes.draw do # ユーザーは記事を投稿できる resources :users do resources :articles do # 記事をお気に入りすることができる resources :favorites end end endrails routesを叩いて
xxx_path
の部分を見てみましょう。お気に入りの新規保存に使えるであろうfavorites#createは
user_article_favorites
になってます。
これはまだマシですが、もっとネストされたら長くなり、xxx_path
の部分が分かり辛くなるでしょう。$ rails routes user_article_favorites GET /users/:user_id/articles/:article_id/favorites(.:format) favorites#index POST /users/:user_id/articles/:article_id/favorites(.:format) favorites#create new_user_article_favorite GET /users/:user_id/articles/:article_id/favorites/new(.:format) favorites#new edit_user_article_favorite GET /users/:user_id/articles/:article_id/favorites/:id/edit(.:format) favorites#edit user_article_favorite GET /users/:user_id/articles/:article_id/favorites/:id(.:format) favorites#show PATCH /users/:user_id/articles/:article_id/favorites/:id(.:format) favorites#update PUT /users/:user_id/articles/:article_id/favorites/:id(.:format) favorites#update DELETE /users/:user_id/articles/:article_id/favorites/:id(.:format) favorites#destroy本題(polymophic_path)を使おう!
Railsにはモデルから生成されたインスタンスに対して、ルーティングをスマートに書ける方法が提供されています。
公式ドキュメントはこちら
結論から言うと、こう書いていたものが、
# user = User.find(1) # article = Article.find(1) <%= button_to 'お気に入りする', user_article_favorites_path(user.id, article.id), method: :post %>こう書けます
<%= link_to 'お気に入りする', polymorphic_path([user, article, :favorites]), method: :post %>何をしているかと言うと、polymorphic_pathの引数においたモデルインスタンスを元にリンクを生成してくれています。
今回の場合users/1/articles/1/favorites
が生成されています。ポイントは以下の3つです。
- 基本的に引数にはModelのインスタンスを置く
- 引数を複数置きたい場合は配列で囲む
- インスタンスの生成が行われていない場合は、
:favorites
のようにシンボル型で引数に置く今回の例よりさらに複雑になったpathであれば可読性はさらに向上すると思います。
(逆にusers_pathといった短いものにpolymorphic_pathを使うと変に見えるかもしれません。)polymorphic_pathの応用
先ほどとは別にnewアクションやeditアクションへのリンクを生成したい場合は以下のように書くこともできます。
# user = User.find(1) # article = Article.find(1) <%= link_to '編集へ', edit_polymorphic_path([user, article ]) %> <%= link_to '新規作成へ', new_polymorphic_path([user, article ]) %>
edit_polymorphic_path([user, article ])
の場合、users/1/articles/1/edit
のようなリンクが生成されます。まとめ
RailsにはDRYの法則に則ったヘルパーやメソッドがたくさん用意されています。
今回扱ったpolymorphic_pathは長くなりがちなpathの記述を簡潔に書くことができます。
ぜひお試しください!
- 投稿日:2021-03-01T11:19:31+09:00
【Rails】acts_as_listで並べ替え機能を実装
導入
Gemfilegem 'acts_as_list'$ bundle installapp/models/task.rbclass Task < ApplicationRecord acts_as_list endrails g migration AddPositionToTask position:integerclass AddPositionToTask < ActiveRecord::Migration[6.0] def change add_column :tasks, :position, :integer #カラム名はpositionじゃなきゃいけない end end$ rails db:migrate
config/routes.rbresources :tasks do member do get :move_higher get :move_lower end end同期処理で動かす
/app/contollers/user_controller.rbdef index @tasks = Task.all.order(:position) # positionカラムに従いソート end def move_higher Task.find(params[:id]).move_higher #move_higherメソッドでpositionを上に redirect_to action: :index end def move_lower Task.find(params[:id]).move_lower #move_lowerメソッドでpositionを下に redirect_to action: :index endapp/views/tasks/index.html.erb<h2>タスク一覧</h2> <table> <% @tasks.each do |task| %> <tr> <td><%= task.name %></td> <td><%= link_to '詳細', user %></td> <td><%= link_to "↑", move_higher_task_path(task) %></td> <td><%= link_to "↓", move_lower_task_path(task) %></td> </tr> <% end %> </table>非同期処理で動かす
remote: true
とjs.erb
を使って実装。詳細は割愛。参考: 【Ruby on Rails】部分テンプレート(js.erb)を用いた非同期通信について(基礎/開発) - Qiita
注意点
positionカラムのデータがnullだと動かない。
既存のテーブルに後からpositionカラムを追加した際には、なんらかの方法で全てのデータのpositionカラムに数値を入力する必要がある。参考
- 投稿日:2021-03-01T11:19:31+09:00
【Rails】acts_as_listで並べ替え機能を同期/非同期それぞれで実装
導入
Gemfilegem 'acts_as_list'$ bundle installapp/models/task.rbclass Task < ApplicationRecord acts_as_list endrails g migration AddPositionToTask position:integerclass AddPositionToTask < ActiveRecord::Migration[6.0] def change add_column :tasks, :position, :integer #カラム名はpositionじゃなきゃいけない end end$ rails db:migrate
config/routes.rbresources :tasks do member do get :move_higher get :move_lower end end同期処理で動かす
app/contollers/user_controller.rbdef index @tasks = Task.all.order(:position) # positionカラムに従いソート end def move_higher Task.find(params[:id]).move_higher #move_higherメソッドでpositionを上に redirect_to action: :index end def move_lower Task.find(params[:id]).move_lower #move_lowerメソッドでpositionを下に redirect_to action: :index endapp/views/tasks/index.html.erb<h2>タスク一覧</h2> <table> <% @tasks.each do |task| %> <tr> <td><%= task.name %></td> <td><%= link_to '詳細', user %></td> <td><%= link_to "↑", move_higher_task_path(task) %></td> <td><%= link_to "↓", move_lower_task_path(task) %></td> </tr> <% end %> </table>非同期処理で動かす
app/contollers/user_controller.rbdef index @tasks = Task.all.order(:position) end def move_higher @task = Task.find(params[:id]) @task.move_higher @tasks = Task.all.order(:position) end def move_lower @task = Task.find(params[:id]) @task.move_lower @tasks = Task.all.order(:position) endapp/views/tasks/index.html.erb<div id='task-list'> <table> <% @tasks.each do |task| %> <tr class='body-row'> <td><%= task.name %></td> <td><%= link_to '詳細', task %></td> <td><%= link_to '↑', tasks_path(task), remote: true %></td> <td><%= link_to '↓', tasks_path(task), remote: true %></td> </tr> <% end %> </table> </div>app/views/_tasks.html.erb<table> <% tasks.each do |task| %> <tr class='body-row'> <td><%= task.name %></td> <td><%= link_to '詳細', task %></td> <td><%= link_to '↑', tasks_path(task), remote: true %></td> <td><%= link_to '↓', tasks_path(task), remote: true %></td> </tr> <% end %> </table>app/views/move_higher.html.erb$('#task-list').html("<%= j(render 'admin/tasks/tasks', tasks: @tasks) %>");app/views/move_lower.html.erb$('#task-list').html("<%= j(render 'admin/tasks/tasks', tasks: @tasks) %>");scope
taskテーブルのkindカラムの中で並び替えをしたい場合(kindで絞り込みをした状態でもうまく動くようにしたい場合)、以下のように書く。
app/models/task.rbclass Task < ApplicationRecord acts_as_list scope: [:kind] end注意点
positionカラムのデータがnullだと動かない。
既存のテーブルに後からpositionカラムを追加した際には、なんらかの方法で全てのデータのpositionカラムに数値を入力する必要がある。参考
- 投稿日:2021-03-01T11:19:31+09:00
【Rails】acts_as_listを使った並べ替え機能を同期/非同期それぞれで実装
導入
Gemfilegem 'acts_as_list'$ bundle installapp/models/task.rbclass Task < ApplicationRecord acts_as_list endrails g migration AddPositionToTask position:integerclass AddPositionToTask < ActiveRecord::Migration[6.0] def change add_column :tasks, :position, :integer #カラム名はpositionじゃなきゃいけない end end$ rails db:migrate
config/routes.rbresources :tasks do member do get :move_higher get :move_lower end end同期処理で動かす
app/contollers/user_controller.rbdef index @tasks = Task.all.order(:position) # positionカラムに従いソート end def move_higher Task.find(params[:id]).move_higher #move_higherメソッドでpositionを上に redirect_to action: :index end def move_lower Task.find(params[:id]).move_lower #move_lowerメソッドでpositionを下に redirect_to action: :index endapp/views/tasks/index.html.erb<h2>タスク一覧</h2> <table> <% @tasks.each do |task| %> <tr> <td><%= task.name %></td> <td><%= link_to '詳細', user %></td> <td><%= link_to "↑", move_higher_task_path(task) %></td> <td><%= link_to "↓", move_lower_task_path(task) %></td> </tr> <% end %> </table>非同期処理で動かす
app/contollers/user_controller.rbdef index @tasks = Task.all.order(:position) end def move_higher @task = Task.find(params[:id]) @task.move_higher @tasks = Task.all.order(:position) end def move_lower @task = Task.find(params[:id]) @task.move_lower @tasks = Task.all.order(:position) endapp/views/tasks/index.html.erb<div id='task-list'> <table> <% @tasks.each do |task| %> <tr class='body-row'> <td><%= task.name %></td> <td><%= link_to '詳細', task %></td> <td><%= link_to '↑', tasks_path(task), remote: true %></td> <td><%= link_to '↓', tasks_path(task), remote: true %></td> </tr> <% end %> </table> </div>app/views/_tasks.html.erb<table> <% tasks.each do |task| %> <tr class='body-row'> <td><%= task.name %></td> <td><%= link_to '詳細', task %></td> <td><%= link_to '↑', tasks_path(task), remote: true %></td> <td><%= link_to '↓', tasks_path(task), remote: true %></td> </tr> <% end %> </table>app/views/move_higher.html.erb$('#task-list').html("<%= j(render 'admin/tasks/tasks', tasks: @tasks) %>");app/views/move_lower.html.erb$('#task-list').html("<%= j(render 'admin/tasks/tasks', tasks: @tasks) %>");scope
taskテーブルのkindカラムの中で並び替えをしたい場合(kindで絞り込みをした状態でもうまく動くようにしたい場合)、以下のように書く。
app/models/task.rbclass Task < ApplicationRecord acts_as_list scope: [:kind] end注意点
positionカラムのデータがnullだと動かない。
既存のテーブルに後からpositionカラムを追加した際には、なんらかの方法で全てのデータのpositionカラムに数値を入力する必要がある。参考
- 投稿日:2021-03-01T11:06:37+09:00
Custom Validator
最初に
みなさん、Custom Validator使ってますか?
rails guideのここにも記載されていますが
ActiveModel::Validator
やActiveModel::EachValidator
を使って自作のvalidatorを作成することができます。異なるmodelで同じようなvalidationを実行する場合は以下のようなメリットがあるので是非使っていきましょう。
・modelのコードを減らせる
・specの行数を減らせる
(Validator Classへspecを書けば良いので同じようなspecを減らせます)実装例
よくあるパターンだと思うのですが異なるmodelでメールアドレスカラムを持っており、同じようなvalidationを実装する場合の実装例を記載します。
- 以下のようなEmailのValidator Classを作成します。
app/validators/email_validator.rb# frozen_string_literal: true class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i) record.errors.add(attribute, options[:message] || "の形式が不正です") end end end
- deviseを使っていてdeviseと同じEmailのvalidationを使いたい場合は以下のような書き方もできます。
unless value.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i) ↓ unless value.match(Devise.email_regexp)
- modelでの使い方は以下になります。
class Person < ApplicationRecord validates :email, presence: true, email: true end
- エラーメッセージは以下のような感じになると思います。
メールアドレスの形式が不正です
- エラーメッセージを変えたい場合もあると思います。
- その場合は以下のようにメッセージを個別で設定することができます。
class User < ApplicationRecord validates :email, allow_nil: true, email: { message: "正しいメールアドレスの形式で入力してください" } end
- 次にspecの書き方のサンプルを記載します。
# frozen_string_literal: true require "rails_helper" describe EmailValidator do let(:model_class) do Struct.new(:mail_address) do include ActiveModel::Validations def self.name "DummyModel" end validates :mail_address, allow_nil: true, email: true end end describe "#validate" do subject { model_class.new(email) } describe "登録可能な形式" do context "nil は登録できる" do let(:email) { nil } it { is_expected.to be_valid } end context "「abc@example.com」は登録できる" do let(:email) { "abc@example.com" } it { is_expected.to be_valid } end end describe "登録不可能な形式" do context "「abc」は登録できない" do let(:email) { "abc" } it { is_expected.not_to be_valid } end context "「abcd.@example.com」は登録できない" do let(:email) { "abcd.@example.com" } it { is_expected.not_to be_valid } end end end end最後に
email以外にも画像やURLのvalidationなど使えるところは色々あるのでお試し下さい!
- 投稿日:2021-03-01T11:06:37+09:00
RailsでCustom Validatorの実装例
最初に
みなさん、Custom Validator使ってますか?
rails guideのここにも記載されていますが
ActiveModel::Validator
やActiveModel::EachValidator
を使って自作のvalidatorを作成することができます。異なるmodelで同じようなvalidationを実行する場合は以下のようなメリットがあるので是非使っていきましょう。
・modelのコードを減らせる
・specの行数を減らせる
(Validator Classへspecを書けば良いので同じようなspecを減らせます)実装例
よくあるパターンだと思うのですが異なるmodelでメールアドレスカラムを持っており、同じようなvalidationを実装する場合の実装例を記載します。
- 以下のようなEmailのValidator Classを作成します。
app/validators/email_validator.rb# frozen_string_literal: true class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i) record.errors.add(attribute, options[:message] || "の形式が不正です") end end end
- deviseを使っていてdeviseと同じEmailのvalidationを使いたい場合は以下のような書き方もできます。
unless value.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i) ↓ unless value.match(Devise.email_regexp)
- modelでの使い方は以下になります。
class Person < ApplicationRecord validates :email, presence: true, email: true end
- エラーメッセージは以下のような感じになると思います。
メールアドレスの形式が不正です
- エラーメッセージを変えたい場合もあると思います。
- その場合は以下のようにメッセージを個別で設定することができます。
class User < ApplicationRecord validates :email, allow_nil: true, email: { message: "正しいメールアドレスの形式で入力してください" } end
- 次にspecの書き方のサンプルを記載します。
# frozen_string_literal: true require "rails_helper" describe EmailValidator do let(:model_class) do Struct.new(:mail_address) do include ActiveModel::Validations def self.name "DummyModel" end validates :mail_address, allow_nil: true, email: true end end describe "#validate" do subject { model_class.new(email) } describe "登録可能な形式" do context "nil は登録できる" do let(:email) { nil } it { is_expected.to be_valid } end context "「abc@example.com」は登録できる" do let(:email) { "abc@example.com" } it { is_expected.to be_valid } end end describe "登録不可能な形式" do context "「abc」は登録できない" do let(:email) { "abc" } it { is_expected.not_to be_valid } end context "「abcd.@example.com」は登録できない" do let(:email) { "abcd.@example.com" } it { is_expected.not_to be_valid } end end end end最後に
email以外にも画像やURLのvalidationなど使えるところは色々あるのでお試し下さい!
- 投稿日:2021-03-01T09:18:31+09:00
Railsでお気に入り機能を実装する
自己紹介
9月から独学でプログラミング学習を開始し、
11月からスクールを使って学習をしています。
現在はポートフォリオの作成し転職活動中です。
知識を定着させるために、学びをアウトプットしています。
また、これから学び始める方の参考になることを願っています。
開発環境
- Ruby 3.0
- Ruby on Rails 6.0.3.4
お気に入り機能を実装する
今回はsocializationというgemを使用します。
▼詳しくは公式のGithubはご覧ください。
https://github.com/cmer/socialization現在、Userテーブルと映画情報を保存するMovieテーブルがあるとします。
まずは、Gemfile
に下記を追加します。gem 'socialization'追加したら、
bundle install
を実行し、gemを追加してください。次に、
rails generate socialization -s
を実行します。
実行すると、like.rb
follow.rb
mention.rb
のように
モデルが3つとマイグレーションファイルが3つ作成されます。- class CreateFollows < ActiveRecord::Migration + class CreateFollows < ActiveRecord::Migration[6.0]上記のようにそれぞれのマイグレーションファイルにバージョンを追加します。
そしてrails db:migrate
を実行してください。1.モデルを編集する
それぞれのモデルに下記を追加します。
class Movie < ApplicationRecord act_as_likeable endclass User < ApplicationRecord act_as_liker end他にお気に入り機能を追加したいテーブルがある場合は、
同様に、act_as_likeable
を追加して下さい。
2.コントローラーを編集する
/controllers/movies_controller.rb
を編集します。class MoviesController < ApplicationController def favorite @movie = Movie.find(params[:id]) current_user.toggle_like!(@movie) end上記のように
favorite
を追加しています。
current_user
を使用するにはdeviseというgemが必要になります。
今回、deviseについては、省略致します。socializationを導入したことで
user.toggle_like!(movie)
というメソッドが使えるようになりました。
これは、
- お気に入りされていなければ、お気に入り登録をする
- お気に入りされていれば、お気に入りを解除する
このようなメソッドです。
その他のメソッドについては、公式Githubを参照下さい。3.ルーティングを編集する
favorite
を追加したので/config/routes.rb
を編集しましょう。Rails.application.routes.draw do resources :movies do member do post 'favorite' end end endメンバールーティングについてはRailsガイドを参照下さい。
https://railsguides.jp/routing.html4.ビューを編集する
<%= link_to favorite_movie_path do %> <% if current_user.likes?(movie) %> お気に入り解除 <% else %> お気に入り登録 <% end %> <% end %>先程、指定したルーティングを
link_to
で使用しています。
current_user.likes?(movie)
これは、ユーザーがお気に入り登録しているとtrueを返します。
シンプルですが、基本的な機能の実装は以上です。
しかし、Ajax(非同期処理)でお気に入り登録をしたり、
お気に入りの総数を表示したいと思うかもしれません。
この辺りの実装は補足として別記事で行おうと考えています。
何か、至らない点があれば、ご指摘下さい。
- 投稿日:2021-03-01T00:42:53+09:00
DockerでRails開発時に,画像の保存先をAWS S3にする
先日,Rails, Dockerでの開発時に画像の保存先をローカルからS3に変更した際の手順を記録した.
開発環境
WSL2 (ubuntu 18.04 LTS)
Docker
- Ruby (2.7.1)
- Rails (6.0.3)画像をS3に保存する手順
Active Storageがインストールされており,S3バケット作成まで完了していることを想定
Active Storageのインストールは以下のコマンドでできる
terminaldocker-compose run コンテナ名 rails active_storage:install docker-compose run コンテナ名 rails db:migrate用意するもの
S3バケット情報
- バケット名
- リージョン
IAMユーザー情報
- アクセスキー
- シークレットアクセスキー
手順
1. root dirにて以下のコマンドを実行
terminaldocker exec -it コンテナID sh /app # EDITOR=vi rails credentials:editコンテナIDは以下のコマンドで確認できる
terminaldocker ps2. credentialsを編集
credentialsをターミナル上で編集する
awsは最初コメントアウトされているので外す (それに気付かずハマってしまいました)
aws: access_key_id: 取得したアクセスキー secret_access_key: 取得したシークレットアクセスキー入力し保存する.
Esc→:wqでセーブして保存する.
:wq3. config/storage.ymlの編集
regionとbucketを作成したものに書き換える.
config/storage.ymlamazon: service: S3 access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> region: "リージョン" bucket: "bucket名"4. aws-sdk-s3というgemをインストールする
Gemfileを編集
Gemfilegem 'aws-sdk-s3', require: falseGemfileを書き換えたので,コンテナをbuildし直す.
terminaldocker-compose build コンテナ名5. config/environments/development.rbでActive Storageの参照先を:localから:amazonへと変更
config/environments/development.rbconfig.active_storage.service = :amazon以上で,S3のバケットに画像が保存された.