- 投稿日:2021-03-25T23:04:31+09:00
Rooで取り込んだExcelの日時のデータがうまく反映されない
こんにちは
エラーが出た背景
rooを使ってexcelの日時のデータを取り込むよう開発を進めました。
ある程度できあがって確認しようとすると、下記のようなエラーが出ました。
おそらく,in_time_zone
メソッドがinteger型に対応できていないと
下記に
- エラーメッセージが出た部分
- 対応するメソッド(controller)
- エラーが発生したファイル
を載せています。app/controller/ir/meeting_logs_controller.rbdef time_from_excel_date_time(excel_date, excel_time) date = excel_date.in_time_zone if excel_time.is_a?(String) time = excel_time.to_time hour = time.hour minute = time.min elsif excel_time.is_a?(Integer) hour = (excel_time / 3600) minute = (excel_time % 3600 / 60) end date + hour.hour + minute.minute end普段取り込むexcelファイル(該当箇所のみ)
id 日付 開始時間 12/9 1:00 12/8 1:00 11/27 1:00 11/26 1:00 今回エラーが発生したexcelファイル(該当箇所のみ)
id 日付 開始時間 12月9日 1:00 12月8日 1:00 11月27日 1:00 11月26日 1:00 原因
上の部分を見ていただければわかる通り、
日付
の区切られている部分が/
か月日
かの違いによるものでした。
この部分が違うと、
/
だとそのまま(String型)、
月日
だと数字(Integer型)
が変数として取り込まれるので、String型にしか対応していなかったメソッドでエラーが発生してしまいました。改善策
そのためcontrollerを書き換えました。
app/controller/ir/meeting_logs_controller.rbdef time_from_excel_date_time(excel_date, excel_time) EXCEL_EPOCH = 2209078800 TICKS_PER_DAY = 60 * 60 * 24 if excel_date.is_a?(String) || excel_date.is_a?(Date) date = excel_date.in_time_zone elsif excel_date.is_a?(Integer) # 日付が漢字の場合、integer型で受け取る => 1899/12/31 からどれくらいの日数がたったかを表している # Unixのゼロタイム(基準)が "1970/1/1" に設定されている為、下記ステップで日付を求める # 1. TICKS_PER_DAY をかけて、1899/12/31 からどれくらいの秒数がたったかを計算 # 2. 1から EXCEL_EPOCH 秒をを引き、1970/1/1 からどれくらいの秒数がたったかを計算 unix_timestamp = excel_date * TICKS_PER_DAY - EXCEL_EPOCH date = Time.at(unix_timestamp).to_datetime - 8.hours - 1.day end if excel_time.is_a?(String) time = excel_time.to_time hour = time.hour minute = time.min elsif excel_time.is_a?(Integer) hour = (excel_time / 3600) minute = (excel_time % 3600 / 60) end date + hour.hour + minute.minute endexcelから取り込んだ日付の変数が入っている
excel_date
に対して、if文でString型とInteger型に分けて処理を走らせることにしました。
また、unix_timestamp
の部分が複雑なので下記に説明をしたいと思います。unixとexcel
このコントローラーの記述は主にunixとexcelの基準の時間を合わせるために書きました。
コメントアウトでも書いてありますが、
excelは基準が1899/12/31
なので帰ってくる変数は、1899/12/31から何日たっているか
を表します。
しかし、Rubyに限らずunixを基準としているプログラミング言語は1970/1/1(ゼロタイム)
を基準に作動します。
なので、excelの変数をunixの基準に合わせるためにunix_timestamp = excel_date * TICKS_PER_DAY - EXCEL_EPOCHの記述をしています。
最後に
今回僕の初めての投稿なので至らない点があると思いますが、
僕なりに一生懸命頑張ったつもりです。笑
もし、何かアドバイスなどありましたらぜひ教えていただきたいです!
- 投稿日:2021-03-25T21:47:48+09:00
mimemagicエラー
gemを入れてbundle install を行ったところ、以下のエラーが出ました。
Your bundle is locked to mimemagic (0.3.5), but that version could not be found
in any of the sources listed in your Gemfile. If you haven't changed sources,
that means the author of mimemagic (0.3.5) has removed it. You'll need to update
your bundle to a version other than mimemagic (0.3.5) that hasn't been removed
in order to install.んん、よく分からん。。とりあえずGemfile.lockで関係してそうな内容を消して再度bundle install をしたら直りました。
- 投稿日:2021-03-25T21:47:44+09:00
Twitter API 登録の仕方(2021年3月17日)
【欲しいもの】
bot を作るために必要なもの。
1. Access Token
2. Access Token Secret
3. Consumer Key
4. Consumer Secret
これらを以下で取得していく。準備編
1
ここにアクセスする
2
Apply(右上)
3
Apply for a developer account
4
目的を選ぶ(今回はbotを作りたいのでhobbyist > making a bot)
5
get start
6
名前とかemailとか入力する
7
何に使うのか入力する
利用規約に同意する
色々進めて、、、8
submit application
メールが送られたっぽい9
メールにある[Confirm your email]をクリック
メールアドレス、パスワードを打って認証完了する。10
「今あなたのレビューをしているよ!!」的なメールが届く。
他の人の記事を見ているとレビュー完了まで数時間かかるそう。###11 メールがかえってくる。
今レビューしていますが、もう少し用途について聞かせてください的な内容。
これは、7.で英文と全く同じのでもOK12
30程度待っていると、twitterからメールが来る
登録申請がapproveされたメールがきました。13
送られてきたリンクにアクセスして、アプリ名を決める。
Get keyを押す。14
【api key】
【API secret key】
【Bearer token】
を保存しておく。15
作ったアプリの中からAccess token & secretをgenerateする。
そこに、Access token、Access token secretがあるからコピーしてどこかに記録しておく。16
これで最初に述べたAccess Token、Access Token Secret、Consumer Key、Consumer Secretの4つがゲットできた。
ここからは本格的にTwitter bot を作っていく。
https://qiita.com/ryoya-s/items/bc8a0e39716bb2844f0b
の記事を参考にした。
- 投稿日:2021-03-25T21:39:25+09:00
User.allって良くないの?って話
こんにちは。たにーです。
今回は、チーム開発で少し議論した
「アクション内でのでのallは良くない?」についてです。
items_controller.rb@items_all = Item.all結論
結論から言うと、悪いことはないが
場面によっては使い方を考えなくてはいけないということです。カリキュラムで作成したアプリでは、
レコード数がmax10個ぐらいで試していたこともあり、取り出すデータ量が少ないため問題なかった。もし、その数が100個、1000個、1万個あった場合だと、
情報量が多すぎて、処理速度が遅くなり、ページのロードが遅いなどが起きるかもしれません。なので、その処理速度をより早くする書き方をご紹介します。
状況について
- railsでwebアプリケーションを開発中。
- 販売している商品数をviewに表示させたい。
例:( 商品一覧(全:〇〇件) )- viewとcontrollerには下記のように書いていた。
index.html.erb<div class="container"> <div class="row"> <div class="col-sm-12 px-sm-0"> <h2>商品一覧(全<%= @items_all.count %>件)</h2> </div> </div>items_controller.erbclass Public::ItemsController < ApplicationController def index @items_all = Item.all end実際にターミナルを見て確認します。
まずは、そのままでページを開いてみます。
そうすると、、、、、(item.allの場合)Started GET "/" for 106.180.147.162 at 2021-03-25 11:43:45 +0000 Cannot render console from 106.180.147.162! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Public::HomesController#top as HTML Rendering public/items/index.html.erb within layouts/application (0.2ms) SELECT COUNT(*) FROM "items" ↳ app/views/public/items/index.html.erb:4 Item Load (0.1ms) SELECT "items".* FROM "items" LIMIT ? OFFSET ? [["LIMIT", 8], ["OFFSET", 0]] ↳ app/views/public/items/index.html.erb:8見て欲しいところはここです。
(item.allの場合)Item Load (0.3ms) SELECT "items".* FROM "items" ORDER BY "items"."created_at" DESC LIMIT ? [["LIMIT", 4]]
Item Loadの(0.3ms)、と書いてあります。
この数値が低ければ低いほど応答速度が速い(タイムラグが少ない)と言われています。
もしかしたら、不要なデータを取得してきているから
0.3なのか?書き方でより少なくできるのでは?と気になったところでチームメンバーで解決策を探しました。実際に調べて試しました
selectで試した
items_controller.erbdef index @items_all = Item.select(:id) end(Item.selectの場合)Started GET "/items" for 106.180.147.162 at 2021-03-25 12:13:24 +0000 Cannot render console from 106.180.147.162! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Public::ItemsController#index as HTML Rendering public/items/index.html.erb within layouts/application (0.1ms) SELECT COUNT("items"."id") FROM "items" ↳ app/views/public/items/index.html.erb:4 Item Load (0.1ms) SELECT "items".* FROM "items" LIMIT ? OFFSET ? [["LIMIT", 8], ["OFFSET", 0]] ↳ app/views/public/items/index.html.erb:82箇所あるますが、それでも0.2msで早くなっているのがわかります。
countで試した
items_controller.erbclass Public::ItemsController < ApplicationController def index @items_all = Item.count end(Item.count)Started GET "/items" for 106.180.147.162 at 2021-03-25 12:22:09 +0000 Cannot render console from 106.180.147.162! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Public::ItemsController#index as HTML (0.1ms) SELECT COUNT(*) FROM "items" ↳ app/controllers/public/items_controller.rb:4 Rendering public/items/index.html.erb within layouts/application Item Load (0.2ms) SELECT "items".* FROM "items" LIMIT ? OFFSET ? [["LIMIT", 8], ["OFFSET", 0]]こちらは,0.3msであまり変わっていない、、、
結果としては、
メソッド 応答速度 all 0.3ms select 0.2ms count 0.3ms selectで特定のカラムを指定する「select」で件数を取得する方法が今の段階では良さそうです!!
もし、違った方法、間違っているなどあれば教えていただけますと幸いです。以上、たにーでした。
参考文献
- 投稿日:2021-03-25T21:39:25+09:00
allメソッドって良くないの?って話
こんにちは。たにーです。
今回は、チーム開発で少し議論した
「アクション内でのでのallは良くない?」についてです。
items_controller.rb@items_all = Item.all結論
結論から言うと、悪いことはないが
場面によっては使い方を考えなくてはいけないということです。カリキュラムで作成したアプリでは、
レコード数がmax10個ぐらいで試していたこともあり、取り出すデータ量が少ないため問題なかった。もし、その数が100個、1000個、1万個あった場合だと、
情報量が多すぎて、処理速度が遅くなり、ページのロードが遅いなどが起きるかもしれません。なので、その処理速度をより早くする書き方をご紹介します。
状況について
- railsでwebアプリケーションを開発中。
- 販売している商品数をviewに表示させたい。
例:( 商品一覧(全:〇〇件) )- viewとcontrollerには下記のように書いていた。
index.html.erb<div class="col-sm-12 px-sm-0"> <h2>商品一覧(全<%= @items_all.count %>件)</h2> </div>items_controller.erbclass Public::ItemsController < ApplicationController def index @items_all = Item.all end実際にターミナルを見て確認します。
まずは、そのままでページを開いてみます。
そうすると、、、、、(item.allの場合)Started GET "/" for 106.180.147.162 at 2021-03-25 11:43:45 +0000 Cannot render console from 106.180.147.162! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Public::HomesController#top as HTML Rendering public/items/index.html.erb within layouts/application (0.2ms) SELECT COUNT(*) FROM "items" ↳ app/views/public/items/index.html.erb:4見て欲しいところはここです。
(item.allの場合)(0.2ms) SELECT COUNT(*) FROM "items" ↳ app/views/public/items/index.html.erb:4
(0.2ms)、と書いてあります。
この数値が低ければ低いほど応答速度が速い(タイムラグが少ない)と言われています。
もしかしたら、不要なデータを取得してきているから
0.2なのか?書き方でより少なくできるのでは?と気になったところでチームメンバーで解決策を探しました。実際に調べて試しました
selectで試した
items_controller.erbdef index @items_all = Item.select(:id) end(Item.selectの場合)Started GET "/items" for 106.180.147.162 at 2021-03-25 12:13:24 +0000 Cannot render console from 106.180.147.162! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Public::ItemsController#index as HTML Rendering public/items/index.html.erb within layouts/application (0.1ms) SELECT COUNT("items"."id") FROM "items" ↳ app/views/public/items/index.html.erb:4それでも0.2msで早くなっているのがわかります。
countで試した
items_controller.erbclass Public::ItemsController < ApplicationController def index @items_all = Item.count end(Item.count)Started GET "/items" for 106.180.147.162 at 2021-03-25 12:22:09 +0000 Cannot render console from 106.180.147.162! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Public::ItemsController#index as HTML (0.1ms) SELECT COUNT(*) FROM "items"こちらも、0.1msと早くなってるのかな?
結果としては、
メソッド 応答速度 all 0.2ms select 0.1ms count 0.1ms もし、違った方法、間違っているなどあれば教えていただけますと幸いです。
以上、たにーでした。
参考文献
- 投稿日:2021-03-25T19:51:07+09:00
N + 1問題を解決する
はじめに仕込んでいたgem bulletが警告ログを発したので解決してみます。
N + 1問題とは?
データベースへのアクセス回数が必要以上に多くなってしまう現象の事。モデル間のアソシエーションで発生します。
gem bulletとは?
N + 1問題が発生してる箇所を警告で教えてくれるgemです。
現状
CareRecipitent Load (0.4ms) SELECT "care_recipitents".* FROM "care_recipitents" ↳ app/views/caregiver/tops/index.html.erb:1 Caregiver Load (0.5ms) SELECT "caregivers".* FROM "caregivers" WHERE "caregivers"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] ↳ app/views/caregiver/tops/index.html.erb:37 CACHE Caregiver Load (0.0ms) SELECT "caregivers".* FROM "caregivers" WHERE "caregivers"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] ↳ app/views/caregiver/tops/index.html.erb:37 CACHE Caregiver Load (0.0ms) SELECT "caregivers".* FROM "caregivers" WHERE "caregivers"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] ↳ app/views/caregiver/tops/index.html.erb:37 Rendered caregiver/tops/index.html.erb within layouts/caregiver (Duration: 16.2ms | Allocations: 6553) CACHE Caregiver Load (0.1ms) SELECT "caregivers".* FROM "caregivers" WHERE "caregivers"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]はじめに親モデルに検索がかけられ、その後に子モデルに4回クエリが発行されていることがわかります。
解決策
子モデル.includes(:親モデル)
def index #@caregiver = Caregiver.find(params[:staff_member_id]) - @care_recipitents = CareRecipitent.all + @care_recipitents = CareRecipitent.includes(:caregiver) endコントローラーのindexアクションを書き換えます。
CareRecipitent Load (1.2ms) SELECT "care_recipitents".* FROM "care_recipitents" ↳ app/views/caregiver/tops/index.html.erb:1 Caregiver Load (0.6ms) SELECT "caregivers".* FROM "caregivers" WHERE "caregivers"."id" = $1 [["id", 1]] ↳ app/views/caregiver/tops/index.html.erb:1 Rendered caregiver/tops/index.html.erb within layouts/caregiver (Duration: 75.3ms | Allocations: 19989) Caregiver Load (0.3ms) SELECT "caregivers".* FROM "caregivers" WHERE "caregivers"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]クエリの回数が減り、無事ログを消えました。
- 投稿日:2021-03-25T19:05:10+09:00
jsファイル デバックする方法(binding.pry)
- 投稿日:2021-03-25T18:00:05+09:00
Rubyでビンゴカードを作るコードを書く
はじめに
この記事は以下のサイトにあった問題を解いてみたものです。大変に工夫の余地があるコードになってしまいましたが、一応完成したので初心者だった頃の自分をいつか省みるために作成しました。
アウトプットのネタに困ったらこれ!?Ruby初心者向けのプログラミング問題を集めてみた(全10問)問題
上記サイトより引用
1から75までの数字をランダムに配置して、ビンゴカードを作成するプログラミング問題です。
ただし、実際のビンゴカードのルールに沿って、各列は以下のような仕様で数字を出力する必要があります。B:1~15のどれか
I:16~30のどれか
N:31~45のどれか
G:46~60のどれか
O:61~75のどれかCodeIQに「ビンゴカード作成問題」を出題しました。みなさんの挑戦をお待ちしてます!
に詳細が記されています。完成したコード
class Bingo def self.generate_card array = (1..15).to_a b_num = array.sample(5) array = (16..30).to_a i_num = array.sample(5) array = (31..45).to_a n_num = array.sample(5) array = (46..60).to_a g_num = array.sample(5) array = (61..75).to_a o_num = array.sample(5) puts " B | I | N | G | O" 5.times do |i| unless i == 2 puts "#{b_num[i].to_s.rjust(2)} | #{i_num[i]} | #{n_num[i]} | #{g_num[i]} | #{o_num[i]} " else puts "#{b_num[i].to_s.rjust(2)} | #{i_num[i]} | | #{g_num[i]} | #{o_num[i]} " end end end end Bingo.generate_card手順
まず、それぞれの列の数値を取得することから始めようと考えました。例えばBの列には、1~15の範囲で、違う数字をランダムに5つ選ぶ必要があります。これを、配列に対して使うsampleメソッドを利用して実現しました。
b_numarray = (1..15).to_a b_num = array.sample(5) p b_num実行結果
>ruby b_num
[7, 3, 14, 15, 10]これを、5列分用意することで必要な数値を準備しました。(改善の余地がありそうなポイント1)
次に考えたのは、数値を1行ずつ表示することです。ここで注意するべきなのは、右詰で表示させる仕様になっていることでした。1桁の数字が出ると、Bの列の表示が崩れてしまう問題が発生しました。なので、rjustメソッドを用いてその問題を解決しています。
また、真ん中は空白にする必要があるので、それにも留意して繰り返しのメソッドを用いました。5.times do |i| unless i == 2 puts "#{b_num[i].to_s.rjust(2)} | #{i_num[i]} | #{n_num[i]} | #{g_num[i]} | #{o_num[i]} " else puts "#{b_num[i].to_s.rjust(2)} | #{i_num[i]} | | #{g_num[i]} | #{o_num[i]} " end endただこの記述の仕方だと、横に非常に長くなってしまっている点が気になります。(改善の余地ポイント2)
感想、まとめ
現状の自分のプログラミングスキルではこのコードが限界です。いつかよりすっきりとしたコードをかけるよう精進したいと強く思える結果となりました。
参考にしたサイト
String#rjust (Ruby 3.0.0 リファレンスマニュアル)
Array#sample (Ruby 3.0.0 リファレンスマニュアル)
Object#to_a (Ruby 3.0.0 リファレンスマニュアル)
- 投稿日:2021-03-25T16:31:22+09:00
【初学者向け】uninitialized constantエラー
uninitialized constantエラーが起きる原因
uninitialized constant errorを直訳すると「初期化されていない定数のエラー」という意味
Railsでは「定数やclassが定義されていない」ことを意味
uninitialized constantは名前が間違っているという意味のNameErrorということ
つまり、ファイル名の記述間違いなどで、呼びたいクラス名を記述出来ていない場合などでエラーが発生します。実際に出たエラー
NameError in Users::RegistrationsController#create
uninitialized constant User::GenderIdここから考えたこと
う〜んUser::GenderIdでエラーが発生してるみたいだから、GenderIdに関わる内容を確認してみたほうがよさそうだな。Railsでは「定数やclassが定義されていない」ことを意味する。。。ということはclass名やモデル名で定義しないといけないところをGenderIdで定義しているのでは。。。おっ!!
原因
user.rb(省略) extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :gender_id (省略)アソシエーションを間違えていたみたいです。。。
アソシエーションはテーブル同士で関連付けておき、一方のモデルからもう一方のモデルにアクセスできるようへするためのものなので、
今回の場合だと「GenderIdにアクセスしようとしたけどそんなものないですよ」と伝えてくれていたみたいです。 ⬅︎ という認識であってます??user.rb(省略) extend ActiveHash::Associations::ActiveRecordExtensions belongs_to :gender (省略)上記のように編集したことで解決しました!!(Genderは定義されているので)
完全な凡ミスでした。。。参考
- 投稿日:2021-03-25T16:18:51+09:00
RailsのGPL混入問題についてまとめ(mimemagic)
!!New!!mimemagicがMITに戻った(3/26追記)
概要
RailsのGPL混入問題についてまとめました。間違いがあればご指摘ください。(2021/3/25現在)
https://github.com/rails/rails/issues/41750ここには3つの問題がある。
- Railsが依存しているmimemagicのライセンスがMITからGPL2.0になった
- もともとGPLライセンスのものが混入していたのにMITになってしまっていた
- これにより、Railsのbuildができなくなった
- Railsが依存しているmimemagic0.3.5が削除されたことが原因
- Railsの依存モジュールにGPLライセンスのものが混入することとなった
- mimemagicを0.3.6以降にすればbuildはできるが、GPL問題は解決しない
mimemagicのライセンスがMITからGPL2.0になった
mimemagicはMITからGPLに変わった。
mimemagicのissues #97によると、shared-mime-infoはGPLライセンス。
mimemagicの以下のファイルがshared-mime-info依存している。
https://github.com/minad/mimemagic/blob/master/script/freedesktop.org.xmlこれまでmimemagicはMITだったが、shared-mime-infoがGPLなので、mimemagicもGPLにすべきであり修正された。
変更内容:https://github.com/minad/mimemagic/commit/c0f7b6b21a192629839db87612794d08f9ff7e88Railsがbuildできない問題
前述のGPL問題修正により、Railsのbuildができない問題が起きた。
https://github.com/minad/mimemagic/issues/98
bundle install
すると以下のエラーが発生する。Installing dependencies using bundler 2.2.1 Running: bundle install --jobs=4 --retry=4 Your bundle is locked to mimemagic (0.3.5), but that version could not be found in any of the sources listed in your Gemfile. If you haven't changed sources, that means the author of mimemagic (0.3.5) has removed it. You'll need to update your bundle to a version other than mimemagic (0.3.5) that hasn't been removed in order to install.Railsが依存しているmiemagicは0.3.5。これが
but that version could not be found
と言われる。削除されたみたい。Railsの依存モジュールにGPLライセンス混入
0.3.6以上にすればbuildはできるようになるみたいだが、0.3.6も0.4.0もGPLなのでライセンス問題は解決されない。
GPLのソフトウェアをサーバサイドで使う場合の著作権表示について
GPLのソフトウェアをサーバサイドで使う場合の著作権表示について
GPLのライブラリをサーバサイドで使う場合、ソースコード公開義務はないという解釈が一般的です。
「サーバサイドで使えばプログラムの頒布じゃないからソースコード公開しなくていいじゃん!」という解釈のもと、ウェブサービスではGPLものが結構使われています。
どういうことかというと、ウェブサービスはプログラムの出力結果の頒布であって、プログラムの頒布ではないのでソースコードの公開義務はないという解釈です。
GCCでコンパイルされたバイナリを頒布してもソース公開義務がないのと同じ解釈です。という解釈があるようだが、どうなんだろうか。
リンク
mimemagicがMITに戻った(3/26追記)
mimemagicの修正PR:Externalise source data #3
this PR removes it from the gem, and instead requires the user to provide one themselves.
Otherwise an environment variable will need to be set to point it in the right direction.mimemagicに含まれるshared-mime-infoがGPL2.0だっため、これを取り除いたことによって、MITになった。
しかし、それを自分でインストールしないといけなくなった。
Place the file
freedesktop.org.xml
in an appropriate location, and then set the environment variableFREEDESKTOP_MIME_TYPES_PATH
to that path.How to fix
shared-mime-infoのインストール
shared-mime-info => MIME typesのデータベース。これがGPL2.0。
http://ftp.riken.jp/Linux/cern/centos/7/updates/x86_64/repoview/shared-mime-info.htmlLinux
Linuxならshared-mime-infoはインストールされてるみたい。CentOS7ではインストールされていた。
$ yum list installed | grep shared-mime-info shared-mime-info.x86_64 1.8-5.el7 @centos-basemacOS
shared-mime-infoをインストールする。
$ brew install shared-mime-info $ bundle update mimemagicもしmimemagicのupgradeだけで動かない人は、nokogiriとmarcelもupgradeするといいかも。
参照:https://github.com/rails/rails/issues/41757#issuecomment-806938727$ bundle update nokogiri marcel mimemagicmimemagicを0.3.9にupgrade
$ bundle update mimemagic$ git diff diff --git a/Gemfile.lock b/Gemfile.lock index cef0127..4a0ef93 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,7 +133,9 @@ GEM marcel (0.3.3) mimemagic (~> 0.3.2) method_source (1.0.0) - mimemagic (0.3.5) + mimemagic (0.3.9) + nokogiri (~> 1) + rake mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.3)shared-mime-infoのPATHを環境変数にセット
PATHは以下のあたりにある。参考:possible_paths
- /usr/local/share/mime/packages/freedesktop.org.xml
- /opt/homebrew/share/mime/packages/freedesktop.org.xml
- /usr/share/mime/packages/freedesktop.org.xml
CentOS7では以下にあった。
$ ls -l /usr/share/mime/packages/freedesktop.org.xml -rw-r--r-- 1 root root 2196823 Apr 1 2020 /usr/share/mime/packages/freedesktop.org.xmlRails起動時にこれを環境変数
FREEDESKTOP_MIME_TYPES_PATH
としてセットする。$ FREEDESKTOP_MIME_TYPES_PATH=/usr/share/mime/packages/freedesktop.org.xml \ bundle exec pumactl start一応これで起動したし、問題はなさそうに見える。
これでよいのかrailsのIssueで聞いてみてる。(3/26 12:00現在)
https://github.com/rails/rails/issues/41757#issuecomment-807898051
- 投稿日:2021-03-25T16:18:51+09:00
RailsのGPL混入問題についてまとめ
!!New!!mimemagicがMITに戻った(3/26追記)
概要
RailsのGPL混入問題についてまとめました。間違いがあればご指摘ください。(2021/3/25現在)
https://github.com/rails/rails/issues/41750ここには3つの問題がある。
- Railsが依存しているmimemagicのライセンスがMITからGPL2.0になった
- もともとGPLライセンスのものが混入していたのにMITになってしまっていた
- これにより、Railsのbuildができなくなった
- Railsが依存しているmimemagic0.3.5が削除されたことが原因
- Railsの依存モジュールにGPLライセンスのものが混入することとなった
- mimemagicを0.3.6以降にすればbuildはできるが、GPL問題は解決しない
mimemagicのライセンスがMITからGPL2.0になった
mimemagicはMITからGPLに変わった。
mimemagicのissues #97によると、shared-mime-infoはGPLライセンス。
mimemagicの以下のファイルがshared-mime-info依存している。
https://github.com/minad/mimemagic/blob/master/script/freedesktop.org.xmlこれまでmimemagicはMITだったが、shared-mime-infoがGPLなので、mimemagicもGPLにすべきであり修正された。
変更内容:https://github.com/minad/mimemagic/commit/c0f7b6b21a192629839db87612794d08f9ff7e88Railsがbuildできない問題
前述のGPL問題修正により、Railsのbuildができない問題が起きた。
https://github.com/minad/mimemagic/issues/98
bundle install
すると以下のエラーが発生する。Installing dependencies using bundler 2.2.1 Running: bundle install --jobs=4 --retry=4 Your bundle is locked to mimemagic (0.3.5), but that version could not be found in any of the sources listed in your Gemfile. If you haven't changed sources, that means the author of mimemagic (0.3.5) has removed it. You'll need to update your bundle to a version other than mimemagic (0.3.5) that hasn't been removed in order to install.Railsが依存しているmiemagicは0.3.5。これが
but that version could not be found
と言われる。削除されたみたい。Railsの依存モジュールにGPLライセンス混入
0.3.6以上にすればbuildはできるようになるみたいだが、0.3.6も0.4.0もGPLなのでライセンス問題は解決されない。
GPLのソフトウェアをサーバサイドで使う場合の著作権表示について
GPLのソフトウェアをサーバサイドで使う場合の著作権表示について
GPLのライブラリをサーバサイドで使う場合、ソースコード公開義務はないという解釈が一般的です。
「サーバサイドで使えばプログラムの頒布じゃないからソースコード公開しなくていいじゃん!」という解釈のもと、ウェブサービスではGPLものが結構使われています。
どういうことかというと、ウェブサービスはプログラムの出力結果の頒布であって、プログラムの頒布ではないのでソースコードの公開義務はないという解釈です。
GCCでコンパイルされたバイナリを頒布してもソース公開義務がないのと同じ解釈です。という解釈があるようだが、どうなんだろうか。
リンク
mimemagicがMITに戻った(3/26追記)
mimemagicの修正PR:Externalise source data #3
this PR removes it from the gem, and instead requires the user to provide one themselves.
Otherwise an environment variable will need to be set to point it in the right direction.mimemagicに含まれるshared-mime-infoがGPL2.0だっため、これを取り除いたことによって、MITになった。
しかし、それを自分でインストールしないといけなくなった。
Place the file
freedesktop.org.xml
in an appropriate location, and then set the environment variableFREEDESKTOP_MIME_TYPES_PATH
to that path.How to fix
shared-mime-infoのインストール
shared-mime-info => MIME typesのデータベース。これがGPL2.0。
http://ftp.riken.jp/Linux/cern/centos/7/updates/x86_64/repoview/shared-mime-info.htmlLinux
Linuxならshared-mime-infoはインストールされてるみたい。CentOS7ではインストールされていた。
$ yum list installed | grep shared-mime-info shared-mime-info.x86_64 1.8-5.el7 @centos-basemacOS
shared-mime-infoをインストールする。
$ brew install shared-mime-info $ bundle update mimemagicもしmimemagicのupgradeだけで動かない人は、nokogiriとmarcelもupgradeするといいかも。
参照:https://github.com/rails/rails/issues/41757#issuecomment-806938727$ bundle update nokogiri marcel mimemagicmimemagicを0.3.9にupgrade
$ bundle update mimemagic$ git diff diff --git a/Gemfile.lock b/Gemfile.lock index cef0127..4a0ef93 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,7 +133,9 @@ GEM marcel (0.3.3) mimemagic (~> 0.3.2) method_source (1.0.0) - mimemagic (0.3.5) + mimemagic (0.3.9) + nokogiri (~> 1) + rake mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.3)shared-mime-infoのPATHを環境変数にセット
PATHは以下のあたりにある。参考:possible_paths
- /usr/local/share/mime/packages/freedesktop.org.xml
- /opt/homebrew/share/mime/packages/freedesktop.org.xml
- /usr/share/mime/packages/freedesktop.org.xml
CentOS7では以下にあった。
$ ls -l /usr/share/mime/packages/freedesktop.org.xml -rw-r--r-- 1 root root 2196823 Apr 1 2020 /usr/share/mime/packages/freedesktop.org.xmlRails起動時にこれを環境変数
FREEDESKTOP_MIME_TYPES_PATH
としてセットする。$ FREEDESKTOP_MIME_TYPES_PATH=/usr/share/mime/packages/freedesktop.org.xml \ bundle exec pumactl start一応これで起動したし、問題はなさそうに見える。
これでよいのかrailsのIssueで聞いてみてる。(3/26 12:00現在)
https://github.com/rails/rails/issues/41757#issuecomment-807898051
- 投稿日:2021-03-25T15:47:06+09:00
Dockerでgemを追加しbundle installでエラーになる
エラーの状況
gemを追加後、反映させるため以下のコマンドを実行したがエラーになる
ターミナル$ docker-compose exec web bundle install ERROR: No container found for web_1原因
前回の時はdocker-compose run webの使い方だったが、docker-compose execをあまりわからず使っていたのが原因
run:コンテナ起動してコマンド実行
exec:起動中のコンテナでコマンド実行 <= 重要!なのでexecで実行する場合は予めdocker-compose upなりでコンテナ起動させてないと使えないとのこと
エラーは勉強になるな、解決しないと焦るけど笑
いうてまだまだ表面の理解だけなので手を動かして覚える参考にさせていただいたURL
- 投稿日:2021-03-25T15:27:52+09:00
投稿機能の実装 方法①
前提モデル
Tweet
投稿したツイートを保存するテーブルの管理
User
ユーザー機能(ログインなど)で使用するユーザー情報を保存するテーブルの管理
Comment
ツイートに行うコメントを保存するテーブルの管理1.newアクションのルーティング設定
config/routes.rbRails.application.routes.draw do 中略 resources :tweets, only: [:index, :new] end2.newアクションをコントローラーに定義
app/controllers/tweets_controller.rbclass TweetsController < ApplicationController 中略 def new @tweet = Tweet.new end end3.投稿画面のビューを作成
1.app/views/tweetsにnew.html.erbというビューファイルを作成
2.ビューファイルを編集してフォームを作成
form_with
app/views/tweets/new.html.erb<div class="contents row"> <div class="container"> <%= form_with(model: @tweet, local: true) do |form| %> 中略 <% end %> </div> </div>4.createアクションのルーティング設定
ストロングパラメーター
requireメソッド
permitメソッド
プライベートメソッド
app/controllers/tweets_controller.rbclass TweetsController < ApplicationController 中略 def create Tweet.create(tweet_params) end private def tweet_params params.require(:tweet).permit(:name, :image, :text) end end5.投稿完了画面のビューを作成
1.app/views/tweetsにcreate.html.erbというビューファイルを作成
2.create.html.erbを編集6.空のツイート投稿ができないようにバリデーションの設定
バリデーション
validates :カラム名, バリデーションの種類
app/models/tweet.rbclass Tweet < ApplicationRecord validates :text, presence: true end
- 投稿日:2021-03-25T14:40:57+09:00
seedで10人ユーザーを作って1人5つの記事を持たせるユーザーを作成する
開発環境
環境
ruby:2.6.4
rails:6.0.3
userにはusersモデル、username,email,password,password_confirmation(確認用)を持たせ、
投稿にはarticlesモデル、title,textカラムを持たせています。
画像のアップロードはactivestorageを使用してavatarとしています。
gem 'Faker'を使用しています。題名の通り早速作っていきます
アカウント=10人
1人のアカウント=5つのarticle(記事)
Faker::Name.nameはランダムに名前を作成してくれるfakerの機能、
Faker::Internet.emailはランダムにemailを作成してくれる機能、
Faker::Hacker.say_something_smartはランダムで文字を作成してくれる機能だと思います。
これに関してはあまり理解しておらずとりあえず文字を作成してくれたので使用しています
指定の文字を入れたい場合は
titel: "任意の文字"
text: "任意の文字"
とすれば良いと思います。
avatarには/assets/imagesに保存してある画像を登録するようにしてあります。seed.rbputs 'users ...' 10.times do user = User.create!( username: Faker::Name.name, email: Faker::Internet.email, password: 'foobar', password_confirmation: 'foobar', ) user.avatar.attach(io: File.open('app/assets/images/cat.jpg'),filename: 'cat.jpg') end puts 'articles ...' 5.times do |n| User.all.each do |user| user.articles.create!( title: Faker::Hacker.say_something_smart, text: Faker::Hacker.say_something_smart ) end enduser.avatar.attach(io: File.open('app/assets/images/cat.jpg'),filename: 'cat.jpg')上のavatarを登録するコードですが必ず()の外で書くようにしてください。
()の中で書くと誤字のエラーがでます。僕はこのエラーに5時間ほどハマりました。
それにactivestorageの場合あまり情報が少ないので本当にこの記述で正解なのかもわかりませんがとりあえず成功できているので良いと思います。
これで内容がよければrails db:seedと打てば完了になります。
最後にいつでもログインできるアカウントを作る
migrate:resetしたときにいちいちアカウントを作り直すのもめんどくさいのでseedにいつでもログインできるようにメール認証もいらないアカウントを作ります。
人気俳優のお二人に参加してもらいます。
user1.skip_confirmation!
これはメール認証はスキップするコードみたいです。
しかしこのまま実行するとアカウント作成は成功したもののメール認識のスキップはできていませんでした。
そこで最後に
user1.save!と保存し、実行してみると見事成功していました。
今回は画像を保存するのは面倒だったのでコードは書きませんでしたが上と同じようにすることでできるかと思います。seed.rbuser1 = User.create!( username: "横浜流星", email: "任意のアドレス", password: '123456', password_confirmation: '123456', ) user1.skip_confirmation! user1.save! user2 = User.create!( username: "成田凌", email: "任意のアドレス", password: 'abcdef', password_confirmation: 'abcdef', ) user2.skip_confirmation! user2.save!rails初学者が書いた記事なので間違いがあるかもしれません
もし間違いがある場合報告してもらえますと助かります。
- 投稿日:2021-03-25T12:53:48+09:00
README 画像挿入の仕方
README 画像挿入
Qiita記事では多くのREADMEについての画像の挿入の手順を解説している記事があります。
今回私は、上記記事でのやり方とは違った、googleの拡張機能を使用した画像作成・READMEに画像挿入までの手順を解説します!!というのも、閲覧記事で参考になったものは多くありましたが、googleの拡張機能を使用した画像挿入についての記事は、調べたところでは見当たらなかったので、
自身で挿入から、解決まで行いました。ご参考までにしていただけたら幸いです。
なぜ他記事を参考にしなかったか
今回、自身で作成したポートフォリオでは、トップページだけでもかなり下にスクロールして全体像が見えるという、課題がありました。
通常のスクリーンショットでは、一画面に収まりせんでした。
gif画像でもgyazoを使用して全体を撮影することも実行しましたが、
撮影秒数が決められたなか全てを撮影するのは困難且、見にくい画像となってしまいました。そこでgoogle拡張機能の「Awesome Screenshot」を使用し、全体を撮影できるようにしました。
Awesome Screenshot
googleの検索で「Awesome Screenshot 拡張機能」と検索していただいたらでききます。
インストールを行ったら順位完了です!Awesome Screenshotではフルページ画像がとる事ができ、非常に使い勝手が便利な拡張機能となっています。
画像撮影手順
インストールしたAwesome Screenshotを撮影したいHPでクリックします。
1.クリック後上部バーで「レコード」「キャプチャ」の選択蘭があるので、「キャプチャ」をクリック
2.「キャプチャ」をクリックしたら、「フルページ」をクリック
3.「フルページ」をクリック後、自動でページを撮影できます
4.撮影した画像をダウンロードして完了
非常に簡単な手順でフルページの撮影ができるのでおすすめです!!
READMEに画像情報記載
ダウンロードした画像を、自身の開発中コードのディレクトリに挿入します。
※画像専用のディレクトリを作成しておくことをおすすめします私の場合、ダウンロードした画像を
public/imagesディレクトリに格納しています。後は、格納した画像をREADMEに記述するだけで画像挿入ができます。
<img src="public/images/画像名">まとめ
非常に簡単に且フルページでの画像挿入ができるので、スクリーンショットで何枚も画像をとる手間を省けます!!
今回掲載した内容は他のqiita記事には調べた限りなかった為、
共有させていただきます!!issueを利用した画像挿入の仕方もあるので、その際は他の記事にも乗っているのでそちら参考にしていただけたらと思います!!
宜しくお願いします。
- 投稿日:2021-03-25T12:21:45+09:00
Ruby on Railsの日付操作まとめ
現在日付/時刻
pry(main)> Time.current => Thu, 25 Mar 2021 11:36:35 JST +09:00 pry(main)> 0.days.ago => Thu, 25 Mar 2021 12:16:53 JST +09:00nowよりcurrentを使うほうが良いらしい。
https://qiita.com/kodai_0122/items/111457104f83f1fb2259特定の日付
pry(main)> Time.new(2021, 3, 25, 11, 22, 33, 00) => 2021-03-25 11:22:33 +0000●日前/●日後
[9] pry(main)> Time.current.yesterday => Wed, 24 Mar 2021 11:47:08 JST +09:00 [10] pry(main)> Time.current.tomorrow => Fri, 26 Mar 2021 11:47:16 JST +09:00 [11] pry(main)> Time.current.ago(3.days) => Mon, 22 Mar 2021 11:47:28 JST +09:00 [12] pry(main)> Time.current.since(3.days) => Sun, 28 Mar 2021 11:47:43 JST +09:00 [13] pry(main)>●ヶ月前/●ヶ月後
[13] pry(main)> Time.current.last_month => Thu, 25 Feb 2021 11:50:33 JST +09:00 [14] pry(main)> Time.current.next_month => Sun, 25 Apr 2021 11:50:41 JST +09:00 [15] pry(main)> Time.current.ago(3.month) => Fri, 25 Dec 2020 11:50:52 JST +09:00 [16] pry(main)> Time.current.since(3.month) => Fri, 25 Jun 2021 11:50:59 JST +09:00 [17] pry(main)>●年前/●年後
[17] pry(main)> Time.current.last_year => Wed, 25 Mar 2020 11:52:07 JST +09:00 [18] pry(main)> Time.current.next_year => Fri, 25 Mar 2022 11:52:12 JST +09:00 [19] pry(main)> Time.current.ago(3.year) => Sun, 25 Mar 2018 11:52:26 JST +09:00 [20] pry(main)> Time.current.since(3.year) => Mon, 25 Mar 2024 11:52:34 JST +09:000:00/23:59
[23] pry(main)> Time.current.beginning_of_day => Thu, 25 Mar 2021 00:00:00 JST +09:00 [24] pry(main)> Time.current.end_of_day => Thu, 25 Mar 2021 23:59:59 JST +09:00月初/月末
[25] pry(main)> Time.current.beginning_of_month => Mon, 01 Mar 2021 00:00:00 JST +09:00 [26] pry(main)> Time.current.end_of_month => Wed, 31 Mar 2021 23:59:59 JST +09:00年始/年末
[27] pry(main)> Time.current.beginning_of_year => Fri, 01 Jan 2021 00:00:00 JST +09:00 [28] pry(main)> Time.current.end_of_year => Fri, 31 Dec 2021 23:59:59 JST +09:00週明け/週末
[29] pry(main)> Time.current.beginning_of_week => Mon, 22 Mar 2021 00:00:00 JST +09:00 [30] pry(main)> Time.current.end_of_week => Sun, 28 Mar 2021 23:59:59 JST +09:00先週/来週(何故か時間は月曜0:00になるよう・・・)
[31] pry(main)> Time.current.last_week => Mon, 15 Mar 2021 00:00:00 JST +09:00 [32] pry(main)> Time.current.next_week => Mon, 29 Mar 2021 00:00:00 JST +09:00●曜日
[38] pry(main)> Time.current.beginning_of_week(:wednesday) => Wed, 24 Mar 2021 00:00:00 JST +09:00 [39] pry(main)> Time.current.last_week(:wednesday) => Wed, 17 Mar 2021 00:00:00 JST +09:00 [40] pry(main)> Time.current.next_week(:wednesday) => Wed, 31 Mar 2021 00:00:00 JST +09:00フォーマット
[41] pry(main)> Time.current.strftime("%Y-%m-%d %T") => "2021-03-25 12:09:26"
範囲
範囲をwhere句に渡すとbetweenで検索してくれるらしい[50] pry(main)> Time.current.all_day => Thu, 25 Mar 2021 00:00:00 JST +09:00..Thu, 25 Mar 2021 23:59:59 JST +09:00 [51] pry(main)> Time.current.all_month => Mon, 01 Mar 2021 00:00:00 JST +09:00..Wed, 31 Mar 2021 23:59:59 JST +09:00 [52] pry(main)> Time.current.all_year => Fri, 01 Jan 2021 00:00:00 JST +09:00..Fri, 31 Dec 2021 23:59:59 JST +09:00
- 投稿日:2021-03-25T11:31:50+09:00
RSpec 多対多関係 モデルテスト(例.Tagモデル)
はじめに
rspecのTagのmodelテストをする際、中間テーブル(post_tag)を介したやり方に苦戦したため、
factory_botを用いて、多対多関係 (has_many through) のテスト作成方法をご説明致します。テーブル
post
とtag
の間にpost_tag
テーブルがある状態です。到達点
以下の2点を達成する
・中間テーブルを介したmodelテストのFactoryBotを理解する
・中間テーブルを介したmodelテストの記述方法を理解する流れ
① 各モデルのvalidatesを確認
② FactoryBotの記述
③ modelテストの記述① 各モデルのvalidatesを確認
app/models/post.rbclass Post < ApplicationRecord has_many :post_tags, dependent: :destroy has_many :tags, through: :post_tags validates :title, presence: true, length: { maximum: 60 } validates :body, presence: true, length: { maximum: 2000 } endapp/models/post.rbclass Tag < ApplicationRecord has_many :post_tags, dependent: :destroy has_many :posts, through: :post_tags validates :name, presence: true, length: { maximum: 50 } endapp/models/post.rbclass PostTag < ApplicationRecord belongs_to :post belongs_to :tag validates :post_id, presence: true validates :tag_id, presence: true② FactoryBotの記述
app/spec/factories/post.rbFactoryBot.define do factory :post do sequence(:title) { |n| "title-#{n}" } sequence(:body) { |n| "body-#{n}" } after(:create) do |post| create_list(:post_tag, 1, post: post, tag: create(:tag)) end end end
after(:create)
を使用することで、post生成後に、tagとpost_tagが生成されます。
app/spec/factories/tag.rbFactoryBot.define do factory :tag do sequence(:name) { |n| "tag-#{n}" } end end
sequence
でユニークnameを生成できます。
app/spec/factories/post_tag.rbFactoryBot.define do factory :post_tag do association :post association :tag end end
association :post
association :tag
とすることで、
post_tagのmodelテストにおいて
let(:post_tag) { create(:post_tag) }
と記述するだけで
postとtagも生成できます。ただし、associationは
has_many
側(今回の場合,post,tag)では記述せず、
belong_to
側でのみ使用しましょう。
③ modelテストの記述
app/spec/requests/post.rbRSpec.describe Post, type: :model do let(:post) { create(:post) } it "タイトル、本文、user_idがある場合、有効であること" do expect(post).to be_valid end it "user_idがない場合、無効であること" do post.user_id = nil expect(post).to be_invalid end describe "タイトル" do it "タイトルがない場合、無効であること" do post.title = nil expect(post).to be_invalid expect(post.errors[:title]).to include("を入力してください") end context "タイトルが60文字以下の場合" do it "有効であること" do post.title = "1" * 60 expect(post).to be_valid end end context "タイトルが61文字以上の場合" do it "無効であること" do post.title = "1" * 61 expect(post).to be_invalid end end end describe "本文" do it "本文がない場合、無効であること" do post.body = nil expect(post).to be_invalid expect(post.errors[:body]).to include("を入力してください") end context "本文が2000文字以下の場合" do it "有効であること" do post.body = "1" * 2000 expect(post).to be_valid end end context "本文が2001文字以上の場合" do it "無効であること" do post.body = "1" * 2001 expect(post).to be_invalid end end end endapp/spec/requests/tag.rbRSpec.describe Tag, type: :model do let(:tag) { create(:tag) } describe "name" do it "タグ名がある場合、有効であること" do expect(tag).to be_valid end it "タグ名がない場合、無効であること" do tag.name = nil expect(tag).to be_invalid expect(tag.errors[:name]).to include("を入力してください") end context "タグ名が50文字以下の場合" do it "有効であること" do tag.name = "1" * 50 expect(tag).to be_valid end end context "タグ名が51文字以上の場合" do it "無効であること" do tag.name = "1" * 51 expect(tag).to be_invalid expect(tag.errors[:name]).to include("は50文字以内で入力してください") end end end endapp/spec/requests/post_tag.rbRSpec.describe PostTag, type: :model do let(:post_tag) { create(:post_tag) } it "post_idとtag_idがある場合、有効であること" do expect(post_tag).to be_valid end it "post_idがない場合、無効であること" do post_tag.post_id = nil expect(post_tag).to be_invalid end it "tag_idがない場合、無効であること" do post_tag.tag_id = nil expect(post_tag).to be_invalid end end
association
によってlet(:post_tag) { create(:post_tag) }
が一文で済みました。
なお、itやcontext内の文章は、英語だとスペルミス等が出る可能性があるため、基本日本語にしております。間違い等がありましたらご指摘の方よろしくお願いします。
参考記事
FactoryBot(FactoryGirl)チートシート
factory_girl で最低限知っておきたい4つの使い方
FactoryBot(旧FactoryGirl)で関連データを同時に生成する方法いろいろ
- 投稿日:2021-03-25T11:26:34+09:00
バリデーションの書き方(Rails)
バリデーションとは
バリデーションは、正しいデータだけをデータベースに保存するために制約をかける事です。
model
に書くことでデータベースに保存する前に受け取った情報を正しいのか判定させます。空でないこと
validates :name, presence: true空であること
validates :name, absence: true一意性であること(重複していないこと)
validates :name, uniqueness: true文字数制限
validates :name, length: { minimum: 2 } # 2文字以上 validates :name, length: { maximum: 50 } # 50文字以下 validates :name, length: { in: 2..50 } # 2文字以上50文字以下 validates :name, length: { is: 6 } # 6文字のみBoolian型
validates :publish, inclusion: { in: [true, false] }参考
- 投稿日:2021-03-25T10:35:16+09:00
本番環境でS3に保存されない件について
本番環境にデプロイしたときにAWSのS3に保存されずに、アプリケーション上に保存されてしまう件について改善したので記述しておきます。
どこが間違っていたのか
config/development.rb# 上記省略 config.active_storage.service = :amazonconfig/production.rb# 上記省略 config.active_storage.service = :local間違っていたのは上記の記述で、開発環境ではS3に保存できるように記述を変更していたのですが本番環境でlocalに保存されるようになっていました。
production.rbには本番環境での設定、development.rbでは開発環境での設定を記述しているので漏れのないように変更加えておかないとな、、と反省いたしました。
なのでproduction.rbの記述を下記のように変更。config/production.rb# 上記省略 config.active_storage.service = :amazonこれで開発環境と同様にS3に保存できるようになりました。
- 投稿日:2021-03-25T09:57:34+09:00
Rails6でaction_textを使う
action_textを使いたい。
一. action_textのマイグレーションファイルを生成してマイグレート
rails action_text:installrails db:migrate二. gem file内のimage_processingのコメントアウトを外してbundle install
gem 'image_processing', '~> 1.2'bundle install三. scaffoldでモデルを作成(ここではstring型のtitleカラムだけを持ったpostモデルを想定)してマイグレート
rails g scaffold post title:stringrails db:migrate四. Postモデルにhas_rich_text :contentを追記
class Post < ApplicationRecord has_rich_text :content end五. posts.controllerのストロンパラメーターに:contentを追記
def post_params params.require(:post).permit(:title, :content) end六. app/views/posts/_form.html.erbにコンテントの部分を追記
<div class="field"> <%= form.rich_text_area :content %> </div>七. app/views/posts/show.html.erbにコンテントの表示領域を追加
<%= @post.content %>これでざっくり完成。
- 投稿日:2021-03-25T09:19:47+09:00
【Cloud9】failed to write new configuration file /home/ubuntu/.gitconfig.lock
Error
Rails チュートリアルをやるために Cloud9 を開いたところ下記のエラーが発生。
Tab 補完が効かないなど変な挙動になった。error: failed to write new configuration file /home/ubuntu/.gitconfig.lock ┌──────────────────────────────────────────────────────────┐ │ npm update check failed │ │ Try running with sudo or get access │ │ to the local update config store via │ │ sudo chown -R $USER:$(id -gn $USER) /home/ubuntu/.config │ └──────────────────────────────────────────────────────────┘Translate
failed to write new configuration file /home/ubuntu/.gitconfig.lock
↓
新しい設定ファイルの書き込みに失敗しました /home/ubuntu/.gitconfig.lockResolve
$ df -h Filesystem Size Used Avail Use% Mounted on ・ ・ ・ /dev/xvda1 9.7G 9.7G 0 100% /ディスク容量が圧迫されて書き込み処理等の操作が正しく行われなくなっていると予想。
今まで作成した別のアプリの削除を実施したところ、元の挙動に戻った。xxx:~/environment $ rm -rf hello_app xxx:~/environment $ rm -rf toy_app
- 投稿日:2021-03-25T01:39:40+09:00
RubyとPHPは何が違うのか、と聞かれたら
私は未経験からエンジニアを目指している人間だが、もし同じ境遇であればちょっと読んでみて欲しい、、、。
まず、なぜこんな記事を書くのかというところからだが、とある企業の面接を受けた時にこう聞かれて、上手く答えられなかったからだ。
「今まではRubyを勉強してきて、現在はPHPを勉強しているということですが、RubyとPHPの言語の違いは何かありますか?」
質問された瞬間、「あ、これ答えられない。」と思ってしまって、本当にこんな質問だったか、答えた内容が的を得ていたかもはっきり思い出せない。。。
学んでいる上では、Rubyの方が可読性が高い、、気がします。みたいなことを言っていた気がする。そもそも、自分がRubyからPHPを学び始めたのは、PHPのシェアが多いから、使えて損はないだろうという浅い考えだった。
エンジニアを目指すと言いながら、言語の違いすらも説明できなかった。そんな反省の思いからこの記事を書いている。ショックのあまりポエムっぽくなっているかもしれないが、許して欲しい。。。
とりあえず答えから
あくまで、「私の考え方で、聞かれた質問の記憶から推測するに」なのでもっと良い答え方があるかもしれないが、もう一度同じ質問をされた場合はこう答えると思う。
「Rubyは、設計思想として「新しさ」よりも「気持ちよさ」や「自由度」を重視して設計されている、と開発者のまつもとゆきひろ氏がおっしゃられているように、学んでいて、実際にストレスフリーで、常に楽しく学ぶことができました。PHPは、勉強する上ではコードも非常にわかりやすく、学びやすいと感じました。しかし、プログラムを改めて見たときに、Rubyと比べるとわずかですが、可読性に欠けるのではないかと感じました。」
どうだろうか、まだ浅いかもしれない。というか、PHPの経験が浅過ぎてこれ以上深くならない。ぁぁ。
しかし、今回の質問で聞かれているのは、自分が学んできて、何か違いを感じているか?という趣旨に聞こえた。学んできたことから今のところ感じているのはこのくらいなのだ、、、。
と言いつつ質問の趣旨が違ったら申し訳ないのだが。違う場合は、「RubyとPHPを学ぶんだったらその違いくらい理解した上で目的があって勉強してるんだよね?だったらその違いは説明できるよね?」という趣旨になると思う。
というか、こっちの可能性の方が高い!!じゃあ可能性が高かった方で
今、この記事を書きながら、何もソースがない状態で少し考えてみた。
思ったことを、そのまま書いてみようと思う。どちらも、Webページ・Webアプリが作れる。RubyはPHPほどWebに強くはないが、汎用性が高い。PHPは、基本的にはWebに特化している。RubyもPHPも日本語ドキュメントが豊富なので、学びやすい。。。シェアで言えばPHPの方が高い、、、。つまり、Webならどっちでも良い!?かといって、Web以外の用途の場合、今度はRubyじゃなくても良い?Rubyじゃなくても良いものをRubyで開発する理由をあげるなら、やっぱり設計思想から、、?
私の知見からは以上(終わった)。
記事を漁っていると、こんな情報が。
大規模になると採用するフレームワークが重要になるので、言語云々よりも、フレームワークの選択肢を含めてみてみないと意味が無いかも知れません。
PHPかRubyで迷っています。な、なにぃぃぃ!!
、、、いや、確かにそういわれると納得できる気がする。
そうなるとここで、開発言語を選ぶ基準を誰もが知りたくなるはずだ。
誰もが知りたくなるはずだ。(大事なことなので)そこで、こんな面白い記事があった。(一番上に出てきたがな)
プロダクトの開発言語ってどうやって選ぶの?この記事は、
・schoo
・Socket
・chatwork
・Talknote
のそれぞれのサービスがどのような基準で言語を選んできたのかを解説している。
特に気になったのは、この部分だ。PHPはデザイナーでも読み書きできる人が多く、バックエンドとフロントエンドの接合点での調整がしやすいのも選定理由の一つです。
「あ、作るシステムにマッチしているかとか、そういうのだけでなくこんなことも考えた上で選別するのか!」
これは面白いと思った。今までは、実際に開発言語を選ぶ時は、「早く開発したいから」とか、「この言語は処理はやいじゃん?」とか(流石にそこまで適当ではないが)、なんとなく、あくまでも作る対象に合わせた決め方・言語そのものの特徴による決め方をしているのだろうと考えていた。
もちろん重要な部分ではあるだろうし、そこに重点を置いている開発もあると思う。
ただ、実務未経験の自分からすれば、そういう想定外な視点はとても面白い。Socketの場合だと、
ノンフレームワークなPHPは、サービスの本当の初期にとりあえず実証できるものをささっと1人で作る、といったことに非常に向いているので、モックアップを作るときにはいつもPHPを使っています。WebネイティブでありながらCに近いノリでOSの様々な部分に割と気軽にアクセスできるので、「フレームワークの仕様的にこれはできない(時間がかかる)」といった障壁に初期の事業コンセプトが惑わされにくいんです。
初期の事業検証が終わったら、サービスプロトタイプをチームで開発するフェーズに入ります。まだ社内エンジニアがいなかった僕らにとっては「外部の人にも手伝ってもらいやすい」ことが、言語を選ぶ上で何より重要な指標でした。時流的にも、僕らの周りにも、優れたRailsのエンジニアが多くいました。Railsを選んだ理由はそこです。ただ、僕自身のRailsの経験値は当時そこまで高くなかったので、個人的にはそれなりに労を割きましたが(笑)とあるように、とりあえずさくっと作るためにPHP、外部の人にも手伝ってもらいやすいからRailsみたいな選び方もある。
一言で言ってしまえば、「選び方は様々」ということで。
こういう言い方をすると、そりゃそうだろって思ってしまうのだが、実際こういう選び方もしますし、こういう場合もありますって言われたら、「様々」の中で想像していなかったことが必ずあると思う。結局は?
で、結局質問されたらどう答えれば良いのか?
ここにきてふと気付いたのだが、趣旨として、「RubyとPHPの言語としての違い」を問われたのなら、とにかく違いを述べれば良いのではないだろうか。具体的にいうと、
Rubyはnewline terminated方式(?)で波かっこやセミコロンが必要ない。
PHPはsemicolon terminated方式で、波かっこやセミコロンが必要。
TruthyとFalsyにおいて、
Rubyはfalseとnilがfalseになり、0や空文字列、空配列はtrueになる。
PHPはfalseとnullがfalseなのは自明だが、0、空文字列、空配列もfalseになる。
というような、プログラムを書いている上で気を付けるべき違いが述べられれば良かったのだろうか?そもそも質問がはっきり思い出せないことからも、質問の意図を汲み取れていないというのがはっきりわかるので、本当はどういう質問だったのかが明らかになることはないが、自分が勉強する・していることくらいはある程度根拠と知識を持っておくべきだと反省した。
経験者やこれがすでに当たり前になっている人からすればつまらない話だったかもしれないが、最後まで読んでくれてありがとうと言いたい。
- 投稿日:2021-03-25T00:25:23+09:00
Ruby on Rails 製 Web アプリケーションのセキュリティテストガイド (静的テスト:SAST)
これなに?
Ruby on Rails 製 Webアプリケーションのセキュリティテストをするためのガイドです。本ガイドは脆弱なRailsアプリである RailsGoat を題材にセキュリティテストの手法を解説します。
本記事ではソースコードをレビューして脆弱性を発見する手法である 静的アプリケーションセキュリティテスト(SAST) を扱います。
対象読者
Ruby on Rails でプロダクト開発している組織のセキュリティチームを想定していますが、セキュリティに関心がある開発者やテスターにとっても役立つしれません。
前提知識・スキル
- 情報セキュリティに関する基本的な用語を知ってること
- 基本的なWebアプリケーションの脆弱性について知ってること
- Ruby on Rails のコードが読めること
- Linux のコマンドを叩けること
目次
- 静的アプリケーションセキュリティテストの準備
- Rails のコードをBrakemanで静的解析し、結果を精査する
- Rails のコードを独自のルールでレビューする
静的アプリケーションセキュリティテストの準備
静的アプリケーションセキュリティテストはソースコードをツールで解析したり、目視でレビューしたりすることにより脆弱性を発見します。
テストを始める前に、Rails アプリによくあるプログラミングミスを把握しておいたり、コードを読みやすい環境を整えておいたりしておくと効率よくテストを進められます。
セキュリティコードレビューのための前提知識
Railsアプリの開発者とセキュリティテスターにとって Securing Rails Applications (Ruby on Rails Guides) は必読書です。Rails アプリによくあるセキュリティの問題と、その対応方法が充実しています。
Securing Rails Applications に基づいてコードレビューするだけでも多くの脆弱性をカバーできると思います。読んだことない人はすぐ読みましょう!
英語版より更新は遅れますが、日本語版もあります。
コードレビューの環境
ソースコードを見る環境を整えましょう。小規模プロジェクトならGitHubをWebブラウザで眺めてもよいですが、ある程度の規模なら高機能なテキストエディタやIDEを使うことお勧めします。なお、本記事では Visual Studio Code を使います。
コードレビューの記録
コードレビューにおいて記録はとても重要です。似たようなコードが大量にあるときに「どこまで見たっけ?」となるのを防いだり、レビュアー※が確認するエビデンスになるためです。
※ややこしいですが、セキュリティテストをチームでやる場合、テストの実行者とそのレビュアーがいるはずです
記録方法は好きなやりかたでよいと思いますが、参考までに筆者の記録方法も合わせて紹介します。
Brakeman を使ったソースコードの静的解析
Rails には Brakeman というソースコードの静的解析ツールがあります。Brakeman は自動かつ高速にソースコードを検査できるため、セキュリティテストでは重宝します。
RailsGoat を Brakeman で解析すると、次のような結果が得られます。
本節では Brakeman を使ってコードの欠陥を検出し、その結果を目視で精査します。
Brakeman の注意点
Brakeman はとても便利なツールですが、次の3点を認識しておく必要があります。
1. Brakeman では検出できない脆弱性がある
Brakeman はすべての脆弱性※に対応しているわけではありません。また、対応していても漏れなく検出してくれるわけではありません。Brakeman が何も報告しなかった=脆弱性ゼロとは思わないでください。
様々な脆弱性を網羅的にテストするには、Brakeman だけでなく目視によるコードレビューや動的アプリケーションセキュリティテストで補完する必要があります。
※Brakeman が対応している脆弱性の種類は次のページにあります:Brakeman Warning Types
2. 検出されたアラートの中には誤報(False Positive)が含まれる
Brakeman が報告するのはあくまで「疑わしいコード」なので、実際は問題がないコードが検出されることもあります。
本当に問題があるコードなのかを判断するために、結果を目視で精査する必要があります。
3. 誤報でなかったとしても、実際に悪用可能かは分からない
Brakeman が検出するのはソースコード上の問題なので、Web アプリケーションとして動くときにどのような挙動をするかは、実際に動かしてみるまで分かりません。
その問題が悪用可能であるかを確かめるには、動的アプリケーションセキュリティテストが必要です。動的アプリケーションセキュリティテストは次回扱います。
Brakeman の使い方
Brakeman を使うには、Railsアプリケーションのソースコードがあるディレクトリに移動して
brakeman -A
コマンドを実行します。-A
は追加の検査をしてくれるオプションです。付けて困ることはないと思うので、基本的には-A
をつけておいてよいと思います。brakeman の実行例:
user@sectest:~/railsgoat$ brakeman -A Loading scanner... Processing application in /home/user/railsgoat Processing gems... ===略=== == Warnings == Confidence: High Category: Cross-Site Scripting Check: CrossSiteScripting Message: Unescaped cookie value Code: cookies[:font] File: app/views/layouts/application.html.erb Line: 12 Confidence: High Category: Dangerous Send ...通常、結果は標準出力に吐き出されますが、
-o
オプションを付けるとファイルに出力でき、-o filename.html
や-o filename.csv
のように使います。CSVファイルはスプレッドシートに貼り付けやすいのでおすすめです。user@sectest:~/railsgoat$ brakeman -A -o result.csv ===略=== user@sectest:~/railsgoat$ more result.csv Confidence,Warning Type,File,Line,Message,Code,User Input,Check Name,Warning Code,Fingerprint,Link High,Cross-Site Scripting,app/views/layouts/application.html.erb,12,Unescaped cookie value,cookies[:font],,CrossSiteScripting,2,febb2 1e45b226bb6bcdc23031091394a3ed80c76357f66b1f348844a7626f4df,https://brakemanscanner.org/docs/warning_types/cross-site_scripting/ ...出力されたCSVファイルをスプレッドシートにインポートした例:
このスプレッドシートは精査の記録に役立ちます。
Brakeman の結果を精査する
Brakeman のアラートはすべてが脆弱性というわけではなく、誤報の場合もあります。特にインジェクション系の脆弱性は誤報が多いです。誤報が多いレポートは役に立たないので精査しましょう。
Confidence
は誤報度合いの参考にはなりますが、High
で誤報のこともあれば、逆にWeak
でもホンモノのこともあります。アラートが多くてすべてを見切れない場合を除き、すべてのアラートを精査することをお勧めします。精査は
File
とLine
が指し示しているソースコードを参照しながら、パラメータであるCode
とUser Input
が安全な値であるかを目視で確認していきます。安全であるかの判断方法はいろいろあると思いますが、例を次に挙げます。
パラメータが検証されている場合
Brakeman はパラメータが検証されていたりサニタイズされている場合でもアラートを出すことがあります。この場合、アラートは False Positive と判断できます。
ただし、検証やサニタイズの妥当性は確認しておきましょう。もし安全である確信が持てない場合は、動的アプリケーションセキュリティテストで有効性を確認するのも良いでしょう。
パラメータの作成者が信頼できる場合
パラメータの作成者が信頼できるとき、そのアラートは False Positive と判断できる場合があります。Webアプリケーションで処理されるパラメータの参照元や作成者は様々です。
パラメータの参照元のと作成者例:
例えば Brakeman はクロスサイトスクリプティングのアラートを挙げており、実際に入力値がサニタイズされていない場合であっても、コンテンツ作成者が職務上パラメータにスクリプトを埋め込むことを許可されていれば問題と判断できるでしょう。
ただし、パラメータの作成者が信頼できるからと言って、パラメータの検証やサニタイズが無意味かというとそうでもありません。作成時点ではパラメータが安全な値であっても、使用されるまでの間に改ざんされていた場合、その値は安全とは言えません。
パラメータの参照元データが何らかの脆弱性によって改ざんされることが想定される場合、検証やサニタイズはリスクを緩和します。
精査結果の記録
先に出力したCSVファイルを整形し、精査結果を追記すると良いです。
RailsGoat はわざと脆弱性に作られたアプリなのでほぼすべてホンモノっぽいです。一部、セキュリティにかかわる設定不備を指摘しているものもあり、実害に発展しうるか現時点では判断をつけがたいアラートはとりあえず?としてます。
※このような「実害に発展するかわからないが、セキュリティ上は不適切」を報告するか否かは、セキュリティテストの目的に沿って決めれば良いと思います。
Brakeman まとめ
Brakeman を使ってソースコードにあるセキュリティ上の欠陥を検出しました。
Brakeman は自動かつ高速にソースコードを検査できますが、すべての脆弱性を発見できるわけでもなく、また、誤報もあります。
そのため、Brakemanの結果を目視で精査したり、網羅できていない脆弱性を別のテストで補完することが必要です。
目視によるコードレビュー
Brakeman は脆弱性の発見にとても役立ちますが、Brakemanでは発見できない脆弱性もあります。特にアプリ固有の脆弱性を発見するにはソースコードを目視でレビューすることも必要です。
コードをなんとなく眺めて脆弱性を発見することもありますが、あらかじめレビューの観点やルールを定めておくと、再現性のあるテストを効率よく実行できるようになります。
レビューのルールに決まったものはなく、またコードには組織や開発チームのクセみたいなものもありますので、テストしながらセキュリティチームの中で育てていくのが良いです。
本節では筆者のオレオレルールを3つ紹介しますので参考にしてください。
Mass Assignment / 不適切な Strong Parameters の設定
概要
Update 系の処理で使われる Strong Parameters が適切に設定されていない場合、攻撃者によってデータを改ざんされる可能性があります。
あからさまな Mass Assignment は Brakeman でも検出できますが、アラートが出ないパターンもあるので目視でもチェックしておくとよいです。
ルール
コントローラー全体を
.permit
で検索し、Strong Parameters で permit している属性が必要最小になっていることを確認しましょう。必要最小とは、「アクションを呼び出すユーザが変更できる属性」であることです。これを厳密に判断するにはアプリケーションの仕様を理解しておく必要がありますが、直感的には「Web画面で編集できる項目以外にpermitされている属性があればNG」と判断してよいと思います。
RailsGoat の場合
RailsGoat には該当するコードが4か所ありました。
1つ目:
params.require(:message).permit(:creator_id, :message, :read, :receiver_id)permitのパラメータはユーザに変更されても問題なさそうな属性です。大丈夫そうですね。
補足:このパラメータは
create
メソッドで使われるため問題ないですが、もしupdate
で使われる場合はNGかもしれません。更新時にcreator_id
を変更できるのは変ですよね。2つ目:
params.require(:schedule).permit(:date_begin, :date_end, :event_desc, :event_name, :event_type)同様に大丈夫そう。
3つ目:
params.require(:user).permit!
permit!
はすべての属性をpermitしちゃいます。高確率でダメです。4つ目:
params.require(:user).permit(:email, :admin, :first_name, :last_name)
admin
が非常に怪しいです。もしこれが管理者権限フラグなら権限昇格できてしまうかもしれません。※ちなみに3つ目と4つ目のパターンはBrakemanでも検出できます
検索結果の
Open in editor
をクリックすると、該当する行の前後n行も合わせて確認できます。これをスプレッドシートに貼り付けるとレビューの記録を付けることができます。
権限昇格 (IDOR)
概要
あるWebサイトの
My Account
をひらいたら URL がhttp://example.com/customers/1234
だった時を想像してください。URL の1234
は自分のユーザIDと推測できます。この1234
を1
に変えてアクセスしたらどうなるでしょうか。もし他の人のアカウント情報が参照できたら、そのサイトはアクセス制御に問題があります。そしてこのような攻撃手法を Insecure Direct Object Reference (IDOR) と言い、権限昇格につながる脆弱性です。参照だけでなく、更新、削除についても同様です。
アクセス制御はアプリケーション固有の仕様となるため、Brakemanで検知することはできません。つまりソースコードから権限昇格の脆弱性を検出するには、目視によるレビューが必要です。
ルール
コントローラを正規表現
params\[:.*id\]
で検索し、リソースにアクセス制御が必要な場合、アクションまたはクエリにアクセス権が含まれているかを確認します。わかりづらいので具体例を挙げます。ブログ記事(article)を修正できるのは自分だけ、という仕様を想像しつつ次のパターンAとパターンBのコードを観察してください。
A. アクセス権が含まれていないクエリ:
def update: @article = Articles.find(params[:article_id]) @article.update!(article_params) redirect_to @article endB. アクセス件が含まれているクエリ:
def update: @article = @current_user.articles.find(params[:article_id]) @article.update!(article_params) redirect_to @article endパターンAの場合、
article_id
パラメータを改ざんすれば他人の記事を更新できてしまいます。対してパターンBは他人のarticle_id
を使ったところで@article
は nil となり更新には失敗するため安全です。RailsGoat の場合
RailsGoat には該当するコードが10か所ありました。
いくつかのパターンがあるので、それぞれ見てみます。
1つ目のパターン:IDOR
class MessagesController < ApplicationController # ...省略... def show @message = Message.where(id: params[:id]).first endIDパラメータを別の番号変えると、他人のメッセージも見れてしまいそうです。NGです。(もし他人のメッセージも見れるという仕様であればOKです)
2つ目のパターン:IDORかつSQLインジェクションっぽい
class UsersController < ApplicationController # ...省略... def update message = false user = User.where("id = '#{params[:user][:id]}'")[0]IDパラメータを別の番号変えると他人のユーザ情報を更新できてしまいそうです。さらにプレースホルダを使ってないので、SQLインジェクションもできそうですね。
このように本来の観点とは異なる問題が見つかることもあります。
3つ目のパターン:IDORっぽいけど違う
class AdminController < ApplicationController # ...省略... def get_user @user = User.find_by_id(params[:admin_id].to_s) arr = ["true", "false"] @admin_select = @user.admin ? arr : arr.reverse end一見怪しいですが、問題ないかもしれません。AdminControlerという名前から、このアクションは管理機能に見えます。管理者はすべてのユーザ情報を参照できるという仕様であれば、IDORではありません。
ただしこのアクションが本当に管理者からのみ呼ばれる仕様なのかは確認しましょう。
4つ目のパターン:IDORではないが別の問題
def admin_param params[:admin_id] != "1" endクエリではないため IDOR ではありません。しかしクエリパラメータ
admin_id
が1
の時を特別に扱っていることから、別の問題がありそうです。このようにレビュー中に気になったことも記録しておくとよいでしょう。作業記録の例:
TIPS
実際のセキュリティテストでも、この例のようにレビュー中気になることが次々と出てくることはよくあります。
すぐに済むならその場で確認しても良いですが、あまり深追いし過ぎると自分が今何をやってるかわからなくなってきます。そのため、確認事項があってもその場ではメモするにとどめておき、実際に確認するのは作業途中のレビューが片付いてからにしたほうが良いです。
レースコンディション
概要
レースコンディションとは、同一のリソースに同時にアクセスした場合に想定外の動作が発生する事象をいいます。
レースコンディションには様々なパターンが有りますが、ここでは現在時刻をもとにIDやファイル名を決定している場合に問題が発生するケースを挙げます。
ルール
ソースコード全体を正規表現
Time\.(now|current|zone\.now)
で検索し、IDやファイル名などを現在時刻だけで作成している場合、NGと判断します。RailsGoat の場合
RailsGoat には1つありました。
def self.make_backup(file, data_path, full_file_name) if File.exist?(full_file_name) silence_streams(STDERR) { system("cp #{full_file_name} #{data_path}/bak#{Time.zone.now.to_i}_#{file.original_filename}") } end end現在時刻と
original_filename
を組み合わせたファイル名でバックアップを作成しているようです。可能性は低いですが、original_filename
が同一のリクエストが同時に2回以上実行されたら、1回目に作成されたバックアップファイルが2回目以降に作成されるバックアップファイルで上書きされてしまいそうです。次にこのメソッドがどこから呼ばれるかを突き止めます。
make_backup
メソッドを呼出してるメソッドを呼出してるメソッド...と辿った結果、BenefitFormsController
のupload
メソッドに行きつきました。
upload
はWebサイトの利用者から呼ばれるアクションなので、顕在化する可能性があると判断できます。コードレビューのまとめ
独自のルールに基づいたコードレビューの例を3つ挙げました。
.permit
で検索し、不適切な Strong Parameters 設定を発見するparams\[:.*id\]
で検索し、権限昇格(IDOR)の問題を発見するTime\.(now|current|zone\.now)
で検索し、ID重複によるレースコンディションを発見する上記以外にもいろいろなルールが考えられますので、セキュリティチームでレビュールールを育てていくとよいです。
今回は予めルールを定めたうえでレビューしましたが、ルールは無くても勘や思い付きに頼って脆弱性を探索することもできます。(未知の脆弱性を発見するには探索的なテストも必要です)
静的アプリケーションセキュリティテストまとめと補足
本記事では Rails アプリケーションの静的アプリケーションテストの手法として、Brakemanを使ったソースコードの静的解析と、目視によるセキュリティコードレビューの手法を紹介しました。
また、紹介の中で様々なテスト手法を挙げ、静的手法だけでは網羅できない領域があることにも触れました。
- 静的テスト・動的テスト (SAST vs DAST)
- 手動テスト・自動テスト (Brakeman vs 目視)
- 手順が定められたテスト・アドホックなテスト (ルール vs 勘)
セキュリティテストに限らずテスト全般に言えることですが、費用対効果の高いテストをするには、多様なテスト手法をいい感じに取り入れることも重要です。
最後に
本記事は現在執筆中の Ruby on Rails 製 Web アプリケーションのセキュリティテストガイド を Qiita 向けに改編したものです。
今後、別記事で下記のトピックについても扱う予定です。
- セキュリティテスト計画や準備
- 静的アプリケーションセキュリティテスト(SAST):javascript
- 静的アプリケーションセキュリティテスト(SAST):既知の脆弱性
- 動的アプリケーションセキュリティテスト(DAST)
- セキュリティテスト結果報告書の作成
本ガイドは常に改善を必要としています。お気づきの点があったらフィードバックを頂けるととても喜びます。
- 投稿日:2021-03-25T00:05:55+09:00
RubocopがCircleCI上でのみエラーになる事象について
事象
ローカルでは問題なくrubocopを実行できたのですが、
Githubにプッシュ、CircleCI上でrubocopを実行すると以下のようなエラーが起きました。#!/bin/bash -eo pipefail mkdir -p /tmp/rubocop-results bundle exec rubocop . --out /tmp/rubocop-results/check-results.xml --format progress /home/circleci/electronote/.rubocop.yml: Warning: no department given for AsciiComments. 404 "Not Found" while downloading remote config file http://shopify.github.io/ruby-style-guide/rubocop.yml /usr/local/lib/ruby/2.7.0/net/http/response.rb:124:in `error!'結論
原因
ローカルとCIで、bundle installのインストール先パスが異なることが原因でした。
ローカル:
/usr/local/bundleCI:
{プロジェクトディレクトリ}/vendor/bundleCIではプロジェクトディレクトリ下にインストールされるため、
rubocop実行時に、このディレクトリ内にある.rubocop.ymlを読み込んでいたものと思われます。対策
vendorディレクトリをrubocopのチェック対象外に追加すると解決しました。
project_dir/.rubocop.ymlinherit_from: .rubocop_todo.yml AllCops: Exclude: - 'db/**/*' - 'node_modules/**/*' - 'bin/*' - 'log/**/*' - 'public/**/*' - '.git/**/*' - 'tmp/**/*' - 'vendor/**/*'解決までに試したこと
いずれも効果ありませんでしたが、以下の方法を試してみました。
(1) CircleCI設定ファイル書き換え
変更前:
circleci/config.ymlversion: 2.1 orbs: ruby: circleci/ruby@1.1.0 ... jobs: test: ... steps: ... - ruby/rubocop-check ...変更後:
circleci/config.ymlversion: 2.1 orbs: ruby: circleci/ruby@1.1.0 ... jobs: test: ... steps: ... - run: name: Rubocop check command: bundle exec rubocop ...結果:
変化なし
(2) 不要なrubocop.ymlをコメントアウト
上述のエラー文を見ると、
404 "Not Found" while downloading remote config file http://shopify.github.io/ruby-style-guide/rubocop.yml「何でshopifyが出てくんねん!」
となったので、このURLがどこから湧いて出てきたのか調べてみました。
$ grep -r http://shopify.github.io/ruby-style-guide/rubocop.yml ./* ./vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.5/.rubocop.yml:# - http://shopify.github.io/ruby-style-guide/rubocop.ymlどうやらbootsnap-1.4.5のディレクトリにもRubocopの設定ファイルがあり、そこで呼び出されているらしい。
というわけで、呼び出している箇所をコメントアウト。変更前:
project_dir/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.5/.rubocop.ymlinherit_from: - http://shopify.github.io/ruby-style-guide/rubocop.yml AllCops: Exclude: - 'vendor/**/*' - 'tmp/**/*' TargetRubyVersion: '2.2' ...変更後:
project_dir/vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.5/.rubocop.yml# inherit_from: # - http://shopify.github.io/ruby-style-guide/rubocop.yml AllCops: Exclude: - 'vendor/**/*' - 'tmp/**/*' TargetRubyVersion: '2.2' ...変更してからCI環境にSSH接続し、rubocopを実行。
参考:
https://qiita.com/yu-croco/items/41d2114c94e3a6ea748a結果:
何か違うエラーが出てきた?
$ bundle exec rubocop /home/circleci/electronote/.rubocop.yml: Warning: no department given for AsciiComments. Error: The `Lint/HandleExceptions` cop has been renamed to `Lint/SuppressedException`. (obsolete configuration found in vendor/bundle/ruby/2.7.0/gems/bootsnap-1.4.5/.rubocop.yml, please update it)(3) vendorディレクトリをrubocopのチェック対象外に追加
上述の通り、上手く動きました。
めでたしめでたし。参考
CircleCIでのみrubocopがエラーになる
https://qiita.com/kabi5/items/40ea864757162e931be1
- 投稿日:2021-03-25T00:03:48+09:00
【Gem】deviseの導入
devise
ユーザー管理機能を簡単に実装するためのGem。
ユーザー新規登録やログイン機能を簡単に実装することができる(とても便利)deviseの使い方
Gemfile編集
Gemfileの最後の行に追記する。
(開発環境、テスト環境、本番環境すべてで使用するため!と認識してます)Gemfile(省略) gem 'devise'コマンドを実行してGemインストール
ターミナルbundle installローカルサーバーを起動している場合はサーバーの再起動を忘れずに!!
少し脱線
ローカルサーバーを起動した状態でコードを編集しても、ブラウザを更新すれば、その編集内容はブラウザに反映されます。しかし、開発環境で以下の変更をした場合、必ずローカルサーバーを再起動する必要があります。
- Rubyバージョンの変更
- テーブル・カラム情報の変更
- Gemの導入状況の変更 ⬅︎ 今回はこれ
これらに関わる情報は、「ローカルサーバーを起動するタイミングに1度だけ読みこまれる」ためサーバーの再起動が必要になります。
deviseの設定ファイル作成
deviseを使用するためには、Gemのインストールに加え、 devise専用のコマンドで設定ファイルを作成する必要があります。
ターミナルrails g devise:installこのコマンドは、追加したdeviseというGemの「設定関連に使用するファイル」を自動で生成するコマンド。
deviseの設定ファイルがrailsアプリケーションにインストールされます。deviseのモデル作成
アカウントを作成するためのモデルを新しく作成する必要があり、作成には通常のモデルの作成方法ではなく、deviseのモデル作成用コマンドでモデルを作成します。
ターミナルrails g devise モデル名コマンドを実行後は、routes.rbに以下のルーティングが自動的に追記されます。
config/routes.rbRails.application.routes.draw do devise_for :users enddevise_forは、ユーザー機能に必要なルーティングを生成してくれるdeviseのメソッドです。
テーブル作成
emailとpasswordはデフォルトで入っているが、追加したいカラムがある場合は、マイグレーションファイルを編集。
テーブルの設計が確認できたらマイグレーションを実行します。ターミナルrails db:migrate指定したモデル名のテーブルが作成されていれば成功
最後に、サーバーの再起動を忘れずに実行しておく!!
参考