- 投稿日:2020-02-08T23:28:05+09:00
Ruby用2Dゲームライブラリ「DXRuby」使い方の初歩
概要
この記事は中高生向けプログラミング教室の教材として作ったものを一部改変したものです。
Rubyでプログラミングの初歩を学んだ次のステップとして、ゲームライブラリDXRubyの使い方を学んでいきます。技術解説
使用ライブラリ
Windows向けRuby用2Dゲームライブラリ
DXRuby
を使用します。
バージョン1.4.2
以上を想定しています。
1.4.2
より前のバージョンとの主な相違点
Window.loop
が複数置けるマウス位置を取得する
Input.mouse_pos_x
、Input.mouse_pos_y
の新しい書き方として、Input.mouse_x
、Input.mouse_y
が追加→DXRuby 1.4.6:更新履歴
http://mirichi.github.io/dxruby-doc/CHANGELOG.htmlDXRubyインストールの注意点
→ DXRuby 1.4.6 をWindows10で使う時の注意点とインストール方法 - noanoa07 - Qiita
https://qiita.com/noanoa07/items/0ce14c2404df38de94b7参考サイト
DXRuby のホームページ
http://dxruby.osdn.jpDXRuby 1.4.6 リファレンスマニュアル
http://mirichi.github.io/dxruby-doc/index.html困ったときは、このページの「チュートリアル」と「マニュアル」にだいたい書いてあります。
- DXRuby 1.4.1 リファレンスマニュアル http://dxruby.osdn.jp/DXRubyReference/20095313382446.htm
古いバージョンのリファレンスも役に立つときがあります。
- DXRubyで 0から作る「ブロック崩し」 - noanoa 日々の日記 http://blog.livedoor.jp/noanoa07/archives/2045851.html
このテキストのブログ記事での解説です。
ライセンス
ソースコード、本解説ともにパブリックドメイン
プログラム解説
手順
1. DXRubyの練習
DXRubyの基本の使い方を練習します。
使い方を学んだ後は、「ブロック崩し」を作っていくのを目標にしています。
1-1. DXRubyのウィンドウを出す(dx01.rb)
DXRubyの基本の形です。
大きさを指定しないと、横640、縦480のウィンドウが開きます(左上の座標が(0, 0))。
Window.loop do 〜 end
の間に書いたコードは、1秒間に60回繰り返し実行されます。→DXRubyリファレンス:チュートリアル 1. 基本の形
http://mirichi.github.io/dxruby-doc/tutorial/basic.htmlrequire 'dxruby' Window.loop do end1-2. ウィンドウの大きさを変える(dx02.rb)
ウィンドウの大きさを変えるには、
Window.width =
、Window.height =
を使います。プログラムの内、最初の1回だけ実行されて以後変える必要のないものは、
Window.loop do 〜 end
より前に書いておきます。→DXRubyリファレンス:API INDEX;module Window
http://mirichi.github.io/dxruby-doc/api/Window.htmlrequire 'dxruby' Window.width = 400 Window.height = 300 Window.loop do end1-3. ウィンドウの背景の色を変える(dx03.rb)
ウィンドウの背景の色を変えるには、
Window.bgcolor =
を使います。色の指定は、「DXRubyリファレンス:色配列と色定数について」
http://mirichi.github.io/dxruby-doc/api/constant_color.htmlを見てください。
→DXRubyリファレンス:API INDEX;module Window
http://mirichi.github.io/dxruby-doc/api/Window.htmlrequire 'dxruby' Window.bgcolor = C_CYAN Window.loop do end1-4. タイトルを表示する;文字(dx04.rb)
ウィンドウの左上にタイトルを表示するには、
Window.caption =
を使います。→DXRubyリファレンス:API INDEX;module Window
http://mirichi.github.io/dxruby-doc/api/Window.htmlrequire 'dxruby' Window.caption = "ブロック崩し" Window.loop do end1-5. 画面に文字を出す(dx05.rb)
画面に文字を出すには、
font = Font.new(文字サイズ)
で文字サイズ(とフォント名)を指定しておいて、
Window.draw_font(x位置, y位置, "文字列", フォント, {:color => 色})
で表示します。
→DXRubyリファレンス:チュートリアル 9. 文字の描画
http://mirichi.github.io/dxruby-doc/tutorial/basic.html→DXRubyリファレンス:API INDEX;Font.new
http://mirichi.github.io/dxruby-doc/api/Font_new.html
require 'dxruby' font = Font.new(32) Window.loop do Window.draw_font(200, 100, "ブロック崩し", font, {:color => C_GREEN}) end1-6. キー入力をとらえる(dx06.rb)
キー入力は
Input.key_down?(キーコード定数)
を使います。キーコード定数については、「DXリファレンス:キーコード定数」
http://mirichi.github.io/dxruby-doc/api/constant_keycode.htmlを見てください。
→DXRubyリファレンス:チュートリアル 8. キーボードの入力
http://mirichi.github.io/dxruby-doc/tutorial/basic.htmlrequire 'dxruby' font = Font.new(32) Window.loop do Window.draw_font(200, 100, "ESCキーで終了します", font, {:color => C_GREEN}) if Input.key_down?(K_ESCAPE) exit # exit でプログラムを終了する end end1-7. ウィンドウを切り替える(dx07.rb)
DXRuby 1.4.2 から、
Window.loop do 〜 end
が複数置けるようになったので、break
でループを抜けると次のWindow.loop do 〜 end
が始まり、別のウィンドウを表示できます。→Window.loopを複数置く - mirichiの日記 2014-11-24
https://mirichi.hatenadiary.org/entry/20141124/p1require 'dxruby' font = Font.new(32) Window.loop do Window.draw_font(200, 100, "Nキーで次の画面へ", font, {:color => C_WHITE}) if Input.key_down?(K_N) break # breakで loopを抜ける end end Window.loop do Window.draw_font(200, 100, "ブロック崩し", font, {:color => C_GREEN}) Window.draw_font(200, 150, "(ESCキーで終了)", font, {:color => C_RED}) if Input.key_down?(K_ESCAPE) exit # exit でプログラムを終了する end end1-8. 画像の読み込み(dx08.rb)
画像ファイルの読み込みは、
Image.load(画像ファイル)
を使います。対応するファイル形式は、jpg
、png
、bmp
です。読み込んだ画像はイメージになります。
→DXRubyリファレンス:チュートリアル 2. 画像の読み込みと描画
http://mirichi.github.io/dxruby-doc/tutorial/basic.html→DXRubyリファレンス:API INDEX;Image.load
http://mirichi.github.io/dxruby-doc/api/Image_load.htmlrequire 'dxruby' image = Image.load('../image/apple.png') Window.loop do Window.draw(100, 100, image) end1-9. 画像の背景色の透明化(dx09.rb)
イメージで
set_color_key(色)
を使うと、指定した色を透明にできます。→DXRubyリファレンス:Image;set_color_key
http://mirichi.github.io/dxruby-doc/api/Image_23set_color_key.htmlrequire 'dxruby' image = Image.load('../image/apple.png') image.set_color_key(C_WHITE) Window.loop do Window.draw(100, 100, image) end1-10. 画像の拡大・縮小(dx10.rb)
イメージを拡大・縮小して表示したい時は、
Window.draw_scale(x位置, y位置, イメージ, 横の拡大率, 縦の拡大率)
を使います。→DXRubyリファレンス:API INDEX;Image;Window.draw_scale
http://mirichi.github.io/dxruby-doc/api/Window_draw_scale.htmlrequire 'dxruby' image = Image.load('../image/apple.png') image.set_color_key(C_WHITE) Window.loop do Window.draw_scale(100, 100, image, 0.2, 0.2) end1-11. トウフを表示(イメージで表示)(dx11.rb)
画像は元の画像ファイルがなくても、作ることもできます。
Image.new(横幅, 縦幅, 色)
で、四角形のイメージ
を作ります。
(白い四角形の見た目で豆腐(トウフ)
)表示するには、
Window.draw(x位置, y位置, イメージ)
を使います。位置の基準はイメージの左上になります。→DXRubyリファレンス:API INDEX;class Image
http://mirichi.github.io/dxruby-doc/api/Image.html→DXRubyリファレンス:API INDEX;Window.draw
http://mirichi.github.io/dxruby-doc/api/Window_draw.htmlrequire 'dxruby' img_tohu = Image.new(100, 100, C_WHITE) Window.loop do Window.draw(200, 200, img_tohu) end1-12. トウフを表示(スプライトで表示)(dx12.rb)
DXRubyには画像を扱うのに、もう一つ
スプライト
というクラスがあります。スプライト
は、x位置
、y位置
のデータを自分で持ち、描画メソッドも独自に持っており、衝突判定もできるのが特徴です。そのため、「ブロック崩し」ではスプライト
を主に使います。
スプライト
は、Sprite.new(x位置, y位置, イメージ)
で作ります。(イメージはあらかじめ作っておきます。)表示させるのは、
draw
です。→DXRubyリファレンス:Spriteを使うためのチュートリアル
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;class Sprite
http://mirichi.github.io/dxruby-doc/api/Sprite.htmlrequire 'dxruby' img_tohu = Image.new(100, 100, C_WHITE) tohu = Sprite.new(200, 200, img_tohu) Window.loop do tohu.draw end1-13. トウフの中に赤丸を描く(dx13.rb)
丸を描くには、イメージに
circle_fill(中心のx位置, 中心のy位置, 半径, 色)
を使います。中心の位置の座標は、(ウィンドウの位置ではなく)そのイメージの左上が(0, 0)になります。→DXRubyリファレンス:API INDEX;Image#circle_fill
http://mirichi.github.io/dxruby-doc/api/Image_23circle_fill.htmlrequire 'dxruby' img_tohu = Image.new(100, 100, C_WHITE).circle_fill(50, 50, 50, C_RED) tohu = Sprite.new(200, 200, img_tohu) Window.loop do tohu.draw end1-14. 赤丸だけにする(dx14.rb)
Image.new(横幅, 縦幅, 色)
で色
の指定を省略すると、透明色が指定されたことになり、見えなくなります。→DXRuby1.4.1 リファレンス:Imageクラス;Image.new
http://dxruby.osdn.jp/DXRubyReference/200953184038328.htmrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(200, 200, img_ball) Window.loop do ball.draw end1-15. カーソルキーで移動(dx15.rb)
カーソルキーが押されると、左右方向は
Input.x
、上下方向はInput.y
が-1
,0
,1
に変化します。これを利用することで、画面上を移動させることができます。
→ DXRuby 1.4.1 リファレンス:Inputモジュール
http://dxruby.osdn.jp/DXRubyReference/200953184011156.htmrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(0, 0, img_ball) ball.x = 200 ball.y = 200 Window.loop do ball.x = ball.x + Input.x ball.y = ball.y + Input.y ball.draw end1-16. カーソルキーで移動(別の書き方)(dx16.rb)
ball.x = ball.x + Input.x
を別の書き方である、
ball.x += Input.x
に書き換えてみます。同じ意味ですが、こちらの方が文字数が少なくて済むので、間違いが減らせるかも。require 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(0, 0, img_ball) ball.x = 200 ball.y = 200 Window.loop do ball.x += Input.x ball.y += Input.y ball.draw end1-17. マウスに合わせて移動(dx17.rb)
マウスのx座標、y座標はそれぞれ
mouse_pos_x
、mouse_pos_y
で取得できます。
(DXRuby 1.4.2 からは、mouse_x
、mouse_y
という書き方もできます。)→DXRubyリファレンス:API INDEX;module Input
http://mirichi.github.io/dxruby-doc/api/Input.html→DXRubyリファレンス:API INDEX;Input.mouse_pos_x
http://mirichi.github.io/dxruby-doc/api/Input_23mouse_pos_x.htmlrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(0, 0, img_ball) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y ball.draw end1-18. 画像を切り替える(dx18.rb)
スプライトの画像イメージは、
スプライト.image =
で変更できます。ここでは、x位置が300を超えるかどうかで、画像が変わるようにしています。
→DXRubyリファレンス:API INDEX;Sprite.image =
http://mirichi.github.io/dxruby-doc/api/Sprite_23image_3D.htmlrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y if ball.x > 300 ball.image = img_ball else ball.image = img_tohu end ball.draw end1-19. 2つの画像を表示する(dx19.rb)
ひとつの画像は位置が固定で、もうひとつはマウスに追従します。
require 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) tohu = Sprite.new(100, 100, img_tohu) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y ball.draw tohu.draw end1-20. 画像をまとめて表示する(dx20.rb)
複数のスプライトを配列にまとめると、ひとつの命令
Sprite.draw
で表示させることができます。ここでは、スプライト
ball
とスプライトtohu
を配列imgs
にまとめて、Sprite.draw(imgs)
で一気に表示させています。→DXRubyリファレンス:Spriteを使うためのチュートリアル
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;Sprite.draw
http://mirichi.github.io/dxruby-doc/api/Sprite_draw.htmlrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) tohu = Sprite.new(100, 100, img_tohu) imgs = [ball, tohu] Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y Sprite.draw(imgs) end1-21. 衝突判定1;===(dx21.rb)
スプライトは衝突したかを判定する
===
があります。
衝突している
/していない
をtrue
/false
で返します。→DXRubyリファレンス:Spriteを使うためのチュートリアル;衝突判定
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;Sprite;===
http://mirichi.github.io/dxruby-doc/api/Sprite_23_3D_3D_3D.htmlrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) tohu = Sprite.new(100, 100, img_tohu) imgs = [ball, tohu] font = Font.new(24) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y if ball === tohu Window.draw_font(200, 300, "衝突!", font) end Sprite.draw(imgs) end1-22. 衝突判定2;check(dx22.rb)
スプライトの衝突判定
===
では、衝突の有無は分かりますが、衝突した相手は分かりません。そんな時は、check
を使います。check
は衝突している相手を配列で返します。配列を
coll
とすると(collision
:衝突)、0番目のcoll[0]
に何か入っていれば衝突してるし、空
ならば衝突していないという、衝突判定にも使えます。→DXRubyリファレンス:Spriteを使うためのチュートリアル;衝突したオブジェクトを取得する
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;Sprite;check
http://mirichi.github.io/dxruby-doc/api/Sprite_23check.htmlrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) img_tohu_r = Image.new(100, 100, C_RED) ball = Sprite.new( 0, 0, img_ball) tohu1 = Sprite.new(150, 150, img_tohu) tohu2 = Sprite.new(300, 150, img_tohu) tohu3 = Sprite.new(300, 300, img_tohu) blocks = [tohu1, tohu2, tohu3] font = Font.new(24) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y coll = ball.check(blocks) if coll[0] coll[0].image = img_tohu_r end Window.draw_font(200, 10, "#{blocks.size}個中、#{coll.size}個 衝突!", font) Sprite.draw(blocks) ball.draw end1-23. 衝突判定3;check後に、vanish, clean(dx23.rb)
vanish
は、check
で衝突していると判定されたスプライトを無効化します。無効化されたスプライトは描画されません。ただし、無効化されただけで、そのスプライトは削除された訳ではありません。衝突したスプライトを削除したい時は、
Sprite.clean(配列)
を使います。これは、配列内のスプライトで無効化されているものを配列から削除します。まだ衝突していないスプライトの数を知りたい時などに使えます。→DXRubyリファレンス:Spriteを使うためのチュートリアル;ライフサイクルを管理する
http://mirichi.github.io/dxruby-doc/tutorial/sprite.htmlSpriteの→DXRubyリファレンス:API INDEX;Sprite;vanish
http://mirichi.github.io/dxruby-doc/api/Sprite_23vanish.html→DXRubyリファレンス:API INDEX;Sprite.clean
http://mirichi.github.io/dxruby-doc/api/Sprite_clean.htmlrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new( 0, 0, img_ball) tohu1 = Sprite.new(150, 150, img_tohu) tohu2 = Sprite.new(300, 150, img_tohu) tohu3 = Sprite.new(300, 300, img_tohu) blocks = [tohu1, tohu2, tohu3] font = Font.new(24) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y coll = ball.check(blocks) if coll[0] coll[0].vanish end Sprite.clean(blocks) Window.draw_font(200, 10, "#{blocks.size}個中、#{coll.size}個 衝突!", font) Sprite.draw(blocks) ball.draw endこれで、DXRubyの練習は終わりです。
次は、「ブロック崩し」を作っていきます。
- 投稿日:2020-02-08T23:28:05+09:00
Ruby用2Dゲームライブラリ DXRuby:使い方の初歩
概要
この記事は中高生向けプログラミング教室の教材として作ったものを一部改変したものです。
Rubyでプログラミングの初歩を学んだ次のステップとして、ゲームライブラリDXRubyの使い方を学んでいきます。技術解説
使用ライブラリ
Windows向けRuby用2Dゲームライブラリ
DXRuby
を使用します。
バージョン1.4.2
以上を想定しています。
1.4.2
より前のバージョンとの主な相違点
Window.loop
が複数置けるマウス位置を取得する
Input.mouse_pos_x
、Input.mouse_pos_y
の新しい書き方として、Input.mouse_x
、Input.mouse_y
が追加→DXRuby 1.4.6:更新履歴
http://mirichi.github.io/dxruby-doc/CHANGELOG.htmlDXRubyインストールの注意点
→ DXRuby 1.4.6 をWindows10で使う時の注意点とインストール方法 - noanoa07 - Qiita
https://qiita.com/noanoa07/items/0ce14c2404df38de94b7参考サイト
DXRuby のホームページ
http://dxruby.osdn.jpDXRuby 1.4.6 リファレンスマニュアル
http://mirichi.github.io/dxruby-doc/index.html困ったときは、このページの「チュートリアル」と「マニュアル」にだいたい書いてあります。
- DXRuby 1.4.1 リファレンスマニュアル http://dxruby.osdn.jp/DXRubyReference/20095313382446.htm
古いバージョンのリファレンスも役に立つときがあります。
- DXRubyで 0から作る「ブロック崩し」 - noanoa 日々の日記 http://blog.livedoor.jp/noanoa07/archives/2045851.html
このテキストのブログ記事での解説です。
ライセンス
ソースコード、本解説ともにパブリックドメイン
プログラム解説
手順
1. DXRubyの練習
DXRubyの基本の使い方を練習します。
使い方を学んだ後は、「ブロック崩し」を作っていくのを目標にしています。
1-1. DXRubyのウィンドウを出す(dx01.rb)
DXRubyの基本の形です。
大きさを指定しないと、横640、縦480のウィンドウが開きます(左上の座標が(0, 0))。
Window.loop do 〜 end
の間に書いたコードは、1秒間に60回繰り返し実行されます。→DXRubyリファレンス:チュートリアル 1. 基本の形
http://mirichi.github.io/dxruby-doc/tutorial/basic.htmldx01.rbrequire 'dxruby' Window.loop do end1-2. ウィンドウの大きさを変える(dx02.rb)
ウィンドウの大きさを変えるには、
Window.width =
、Window.height =
を使います。プログラムの内、最初の1回だけ実行されて以後変える必要のないものは、
Window.loop do 〜 end
より前に書いておきます。→DXRubyリファレンス:API INDEX;module Window
http://mirichi.github.io/dxruby-doc/api/Window.htmldx02.rbrequire 'dxruby' Window.width = 400 Window.height = 300 Window.loop do end1-3. ウィンドウの背景の色を変える(dx03.rb)
ウィンドウの背景の色を変えるには、
Window.bgcolor =
を使います。色の指定は、「DXRubyリファレンス:色配列と色定数について」
http://mirichi.github.io/dxruby-doc/api/constant_color.htmlを見てください。
→DXRubyリファレンス:API INDEX;module Window
http://mirichi.github.io/dxruby-doc/api/Window.htmldx03.rbrequire 'dxruby' Window.bgcolor = C_CYAN Window.loop do end1-4. タイトルを表示する;文字(dx04.rb)
ウィンドウの左上にタイトルを表示するには、
Window.caption =
を使います。→DXRubyリファレンス:API INDEX;module Window
http://mirichi.github.io/dxruby-doc/api/Window.htmldx04.rbrequire 'dxruby' Window.caption = "ブロック崩し" Window.loop do end1-5. 画面に文字を出す(dx05.rb)
画面に文字を出すには、
font = Font.new(文字サイズ)
で文字サイズ(とフォント名)を指定しておいて、
Window.draw_font(x位置, y位置, "文字列", フォント, {:color => 色})
で表示します。
→DXRubyリファレンス:チュートリアル 9. 文字の描画
http://mirichi.github.io/dxruby-doc/tutorial/basic.html→DXRubyリファレンス:API INDEX;Font.new
http://mirichi.github.io/dxruby-doc/api/Font_new.html
dx05.rbrequire 'dxruby' font = Font.new(32) Window.loop do Window.draw_font(200, 100, "ブロック崩し", font, {:color => C_GREEN}) end1-6. キー入力をとらえる(dx06.rb)
キー入力は
Input.key_down?(キーコード定数)
を使います。キーコード定数については、「DXリファレンス:キーコード定数」
http://mirichi.github.io/dxruby-doc/api/constant_keycode.htmlを見てください。
→DXRubyリファレンス:チュートリアル 8. キーボードの入力
http://mirichi.github.io/dxruby-doc/tutorial/basic.htmldx06.rbrequire 'dxruby' font = Font.new(32) Window.loop do Window.draw_font(200, 100, "ESCキーで終了します", font, {:color => C_GREEN}) if Input.key_down?(K_ESCAPE) exit # exit でプログラムを終了する end end1-7. ウィンドウを切り替える(dx07.rb)
DXRuby 1.4.2 から、
Window.loop do 〜 end
が複数置けるようになったので、break
でループを抜けると次のWindow.loop do 〜 end
が始まり、別のウィンドウを表示できます。→Window.loopを複数置く - mirichiの日記 2014-11-24
https://mirichi.hatenadiary.org/entry/20141124/p1dx07.rbrequire 'dxruby' font = Font.new(32) Window.loop do Window.draw_font(200, 100, "Nキーで次の画面へ", font, {:color => C_WHITE}) if Input.key_down?(K_N) break # breakで loopを抜ける end end Window.loop do Window.draw_font(200, 100, "ブロック崩し", font, {:color => C_GREEN}) Window.draw_font(200, 150, "(ESCキーで終了)", font, {:color => C_RED}) if Input.key_down?(K_ESCAPE) exit # exit でプログラムを終了する end end1-8. 画像の読み込み(dx08.rb)
画像ファイルの読み込みは、
Image.load(画像ファイル)
を使います。対応するファイル形式は、jpg
、png
、bmp
です。読み込んだ画像はイメージになります。
→DXRubyリファレンス:チュートリアル 2. 画像の読み込みと描画
http://mirichi.github.io/dxruby-doc/tutorial/basic.html→DXRubyリファレンス:API INDEX;Image.load
http://mirichi.github.io/dxruby-doc/api/Image_load.htmldx08.rbrequire 'dxruby' image = Image.load('../image/apple.png') Window.loop do Window.draw(100, 100, image) end1-9. 画像の背景色の透明化(dx09.rb)
イメージで
set_color_key(色)
を使うと、指定した色を透明にできます。→DXRubyリファレンス:Image;set_color_key
http://mirichi.github.io/dxruby-doc/api/Image_23set_color_key.htmldx09.rbrequire 'dxruby' image = Image.load('../image/apple.png') image.set_color_key(C_WHITE) Window.loop do Window.draw(100, 100, image) end1-10. 画像の拡大・縮小(dx10.rb)
イメージを拡大・縮小して表示したい時は、
Window.draw_scale(x位置, y位置, イメージ, 横の拡大率, 縦の拡大率)
を使います。→DXRubyリファレンス:API INDEX;Image;Window.draw_scale
http://mirichi.github.io/dxruby-doc/api/Window_draw_scale.htmldx10.rbrequire 'dxruby' image = Image.load('../image/apple.png') image.set_color_key(C_WHITE) Window.loop do Window.draw_scale(100, 100, image, 0.2, 0.2) end1-11. トウフを表示(イメージで表示)(dx11.rb)
画像は元の画像ファイルがなくても、作ることもできます。
Image.new(横幅, 縦幅, 色)
で、四角形のイメージ
を作ります。
(白い四角形の見た目で豆腐(トウフ)
)表示するには、
Window.draw(x位置, y位置, イメージ)
を使います。位置の基準はイメージの左上になります。→DXRubyリファレンス:API INDEX;class Image
http://mirichi.github.io/dxruby-doc/api/Image.html→DXRubyリファレンス:API INDEX;Window.draw
http://mirichi.github.io/dxruby-doc/api/Window_draw.htmldx11.rbrequire 'dxruby' img_tohu = Image.new(100, 100, C_WHITE) Window.loop do Window.draw(200, 200, img_tohu) end1-12. トウフを表示(スプライトで表示)(dx12.rb)
DXRubyには画像を扱うのに、もう一つ
スプライト
というクラスがあります。スプライト
は、x位置
、y位置
のデータを自分で持ち、描画メソッドも独自に持っており、衝突判定もできるのが特徴です。そのため、「ブロック崩し」ではスプライト
を主に使います。
スプライト
は、Sprite.new(x位置, y位置, イメージ)
で作ります。(イメージはあらかじめ作っておきます。)表示させるのは、
draw
です。→DXRubyリファレンス:Spriteを使うためのチュートリアル
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;class Sprite
http://mirichi.github.io/dxruby-doc/api/Sprite.htmldx12.rbrequire 'dxruby' img_tohu = Image.new(100, 100, C_WHITE) tohu = Sprite.new(200, 200, img_tohu) Window.loop do tohu.draw end1-13. トウフの中に赤丸を描く(dx13.rb)
丸を描くには、イメージに
circle_fill(中心のx位置, 中心のy位置, 半径, 色)
を使います。中心の位置の座標は、(ウィンドウの位置ではなく)そのイメージの左上が(0, 0)になります。→DXRubyリファレンス:API INDEX;Image#circle_fill
http://mirichi.github.io/dxruby-doc/api/Image_23circle_fill.htmldx13.rbrequire 'dxruby' img_tohu = Image.new(100, 100, C_WHITE).circle_fill(50, 50, 50, C_RED) tohu = Sprite.new(200, 200, img_tohu) Window.loop do tohu.draw end1-14. 赤丸だけにする(dx14.rb)
Image.new(横幅, 縦幅, 色)
で色
の指定を省略すると、透明色が指定されたことになり、見えなくなります。→DXRuby1.4.1 リファレンス:Imageクラス;Image.new
http://dxruby.osdn.jp/DXRubyReference/200953184038328.htmdx14.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(200, 200, img_ball) Window.loop do ball.draw end1-15. カーソルキーで移動(dx15.rb)
カーソルキーが押されると、左右方向は
Input.x
、上下方向はInput.y
が-1
,0
,1
に変化します。これを利用することで、画面上を移動させることができます。
→ DXRuby 1.4.1 リファレンス:Inputモジュール
http://dxruby.osdn.jp/DXRubyReference/200953184011156.htmdx15.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(0, 0, img_ball) ball.x = 200 ball.y = 200 Window.loop do ball.x = ball.x + Input.x ball.y = ball.y + Input.y ball.draw end1-16. カーソルキーで移動(別の書き方)(dx16.rb)
ball.x = ball.x + Input.x
を別の書き方である、
ball.x += Input.x
に書き換えてみます。同じ意味ですが、こちらの方が文字数が少なくて済むので、間違いが減らせるかも。dx16.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(0, 0, img_ball) ball.x = 200 ball.y = 200 Window.loop do ball.x += Input.x ball.y += Input.y ball.draw end1-17. マウスに合わせて移動(dx17.rb)
マウスのx座標、y座標はそれぞれ
mouse_pos_x
、mouse_pos_y
で取得できます。
(DXRuby 1.4.2 からは、mouse_x
、mouse_y
という書き方もできます。)→DXRubyリファレンス:API INDEX;module Input
http://mirichi.github.io/dxruby-doc/api/Input.html→DXRubyリファレンス:API INDEX;Input.mouse_pos_x
http://mirichi.github.io/dxruby-doc/api/Input_23mouse_pos_x.htmldx17.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) ball = Sprite.new(0, 0, img_ball) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y ball.draw end1-18. 画像を切り替える(dx18.rb)
スプライトの画像イメージは、
スプライト.image =
で変更できます。ここでは、x位置が300を超えるかどうかで、画像が変わるようにしています。
→DXRubyリファレンス:API INDEX;Sprite.image =
http://mirichi.github.io/dxruby-doc/api/Sprite_23image_3D.htmldx18.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y if ball.x > 300 ball.image = img_ball else ball.image = img_tohu end ball.draw end1-19. 2つの画像を表示する(dx19.rb)
ひとつの画像は位置が固定で、もうひとつはマウスに追従します。
dx19.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) tohu = Sprite.new(100, 100, img_tohu) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y ball.draw tohu.draw end1-20. 画像をまとめて表示する(dx20.rb)
複数のスプライトを配列にまとめると、ひとつの命令
Sprite.draw
で表示させることができます。ここでは、スプライト
ball
とスプライトtohu
を配列imgs
にまとめて、Sprite.draw(imgs)
で一気に表示させています。→DXRubyリファレンス:Spriteを使うためのチュートリアル
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;Sprite.draw
http://mirichi.github.io/dxruby-doc/api/Sprite_draw.htmldx20.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) tohu = Sprite.new(100, 100, img_tohu) imgs = [ball, tohu] Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y Sprite.draw(imgs) end1-21. 衝突判定1;===(dx21.rb)
スプライトは衝突したかを判定する
===
があります。
衝突している
/していない
をtrue
/false
で返します。→DXRubyリファレンス:Spriteを使うためのチュートリアル;衝突判定
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;Sprite;===
http://mirichi.github.io/dxruby-doc/api/Sprite_23_3D_3D_3D.htmldx21.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new(0, 0, img_ball) tohu = Sprite.new(100, 100, img_tohu) imgs = [ball, tohu] font = Font.new(24) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y if ball === tohu Window.draw_font(200, 300, "衝突!", font) end Sprite.draw(imgs) end1-22. 衝突判定2;check(dx22.rb)
スプライトの衝突判定
===
では、衝突の有無は分かりますが、衝突した相手は分かりません。そんな時は、check
を使います。check
は衝突している相手を配列で返します。配列を
coll
とすると(collision
:衝突)、0番目のcoll[0]
に何か入っていれば衝突してるし、空
ならば衝突していないという、衝突判定にも使えます。→DXRubyリファレンス:Spriteを使うためのチュートリアル;衝突したオブジェクトを取得する
http://mirichi.github.io/dxruby-doc/tutorial/sprite.html→DXRubyリファレンス:API INDEX;Sprite;check
http://mirichi.github.io/dxruby-doc/api/Sprite_23check.htmldx22.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) img_tohu_r = Image.new(100, 100, C_RED) ball = Sprite.new( 0, 0, img_ball) tohu1 = Sprite.new(150, 150, img_tohu) tohu2 = Sprite.new(300, 150, img_tohu) tohu3 = Sprite.new(300, 300, img_tohu) blocks = [tohu1, tohu2, tohu3] font = Font.new(24) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y coll = ball.check(blocks) if coll[0] coll[0].image = img_tohu_r end Window.draw_font(200, 10, "#{blocks.size}個中、#{coll.size}個 衝突!", font) Sprite.draw(blocks) ball.draw end1-23. 衝突判定3;check後に、vanish, clean(dx23.rb)
vanish
は、check
で衝突していると判定されたスプライトを無効化します。無効化されたスプライトは描画されません。ただし、無効化されただけで、そのスプライトは削除された訳ではありません。衝突したスプライトを削除したい時は、
Sprite.clean(配列)
を使います。これは、配列内のスプライトで無効化されているものを配列から削除します。まだ衝突していないスプライトの数を知りたい時などに使えます。→DXRubyリファレンス:Spriteを使うためのチュートリアル;ライフサイクルを管理する
http://mirichi.github.io/dxruby-doc/tutorial/sprite.htmlSpriteの→DXRubyリファレンス:API INDEX;Sprite;vanish
http://mirichi.github.io/dxruby-doc/api/Sprite_23vanish.html→DXRubyリファレンス:API INDEX;Sprite.clean
http://mirichi.github.io/dxruby-doc/api/Sprite_clean.htmldx23.rbrequire 'dxruby' img_ball = Image.new(100, 100).circle_fill(50, 50, 50, C_RED) img_tohu = Image.new(100, 100, C_WHITE) ball = Sprite.new( 0, 0, img_ball) tohu1 = Sprite.new(150, 150, img_tohu) tohu2 = Sprite.new(300, 150, img_tohu) tohu3 = Sprite.new(300, 300, img_tohu) blocks = [tohu1, tohu2, tohu3] font = Font.new(24) Window.loop do ball.x = Input.mouse_pos_x ball.y = Input.mouse_pos_y coll = ball.check(blocks) if coll[0] coll[0].vanish end Sprite.clean(blocks) Window.draw_font(200, 10, "#{blocks.size}個中、#{coll.size}個 衝突!", font) Sprite.draw(blocks) ball.draw endこれで、DXRubyの練習は終わりです。
次は、「ブロック崩し」を作っていきます。
- 投稿日:2020-02-08T21:12:59+09:00
Net::HTTPメソッドを使ってBasic認証を突破しよう
こんにちは。
今回、Rubyでgemを使わずにNet::HTTPメソッドを使ってBasic認証を突破し、スクレイピングしました。あまり文献がなかったので、誰かの参考になればと思いこの記事を書いた次第です。
Rubyのバージョンは2.6.5です。
1行1行丁寧に解説していこうと思います。コードだけ知りたい!という方は完成形のコードを参考に実行してみてください。
完成形のコードはこちら↓scraping.rbrequire "net/http" username, password = "ユーザー名", "パスワード" uri = URI.parse("スクレイピングしたいページのURL") https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true http_get = Net::HTTP::Get.new(uri.path) http_get.basic_auth(username, password) response = https.request(http_get) response.bodyそれでは、それぞれ一行ずつ、何をしているのか確認していきます。
net/httpライブラリを読み込む
require “net/http”net/httpとは、rubyにデフォルトで備わっているライブラリで汎用データ転送プロトコルHTTPを扱うライブラリです。
汎用データ転送プロトコルとは、要するにホームページを構成しているhtmlファイルや画像ファイルなどの「ホームページの部品」を自分のパソコンにデータとしてダウンロードする手順のことです。HTTPという共通の通信規約を定めることで、インターネットを利用する環境が異なっていても、同じ手順でホームページのデータをやり取りすることができます。
以下の記事を参考にしました。
https://cybersecurity-jp.com/security-measures/25772ユーザー名・パスワードを変数に代入
username, password = “ユーザー名”, “パスワード”URIを作成
uri = URI.parse(“スクレイピングしたいページのURL")URIモジュールのparseメソッドを呼び出し、引数にURLを文字列として与えています。parseメソッドは、与えられたURIから該当するURI::Genericのサブクラスのインスタンスを生成して返します。それを変数uriに代入しています。
※URIとは、URL(web上の住所)とURN(we上での名前、シリアルナンバー)の総称です。
Net::HTTPクラスのインスタンスを生成
https = Net::HTTP.new(uri.host, uri.port)Net::HTTPクラスのnewメソッドを呼び出し、第一引数にuri.hostを、第二引数にuri.portを代入しています。ここで、新しいNet::HTTPクラスのインスタンスを生成しています。
SSL接続を可能にする
https.use_ssl = true新しく生成したNet::HTTPクラスのインスタンスに対して、use_sslをtrueにします。HTTPSを使う場合は、このコードが必要です。
GETリクエストを得る
http_get = Net::HTTP::Get.new(uri.path)Net::HTTP::GETクラスのnewメソッドを呼び出し、引数にuri.pathを渡しています。Net::HTTP::GETクラスはHTTPのGETリクエストを表すクラスです。
uri.pathでは、uriのpathを文字列で返しています。pathとは、URLのドメイン名の下にあるものです。Basic認証を突破したリクエスト送信
http_get.basic_auth(username, password)http_getはNet::HTTP::GETクラスのインスタンスですが、このクラスはNet::HTTPHeaderクラスを継承しているため、Net::HTTPHeaderクラスのbasic_authメソッドを呼び出すことができます。第一引数にusername、第二引数にpasswordを渡します。
basic_authメソッドは、ヘッダをBASIC認証用にセットするメソッドです。responseを受け取る
response = https.request(http_get)responseを受け取ります。正常に接続できている場合は、200が返ってきます。このコードでは、Net::HTTPクラスのインスタンスに対してrequestメソッドを呼び出しており、引数にhttp_getを渡しています。(getリクエスト)
アクセスしたページのHTMLを取得
response.bodyresponseのクラスはNet::HTTPOKクラスですが、Net::HTTPResponseから継承しているbodyメソッドを呼び出すことができます。このメソッドは、レスポンスのbody、つまりアクセスしたページのhtmlを文字列として返します。
以上になります。間違えている点、補足等ございましたら、是非コメントして頂けると幸いです。
また、参考になったよ!と思った方はいいねをお願いします!
- 投稿日:2020-02-08T21:05:43+09:00
【RSpec】spec/rails_helper.rbを和訳&補足してみた
はじめに
rails_helperの設定をこんな風にしていますという記事はあるのですが、そのオプションによって何をしているのか今一つわからなかったので、自分用に和訳&補足してみました。
忘れた頃の自分やRSpec初心者のためになればと思います。正確性に関しては自信ないです!RSpecのバージョンは
3.9
です。(2/9追記)編集リクエストを元に修正しました。
導入
$ rails g rspec:installコマンドを打つと、
.rspec
,spec/rails_helper.rb
,spec/spec_helper.rb
ファイルが生成されます。
rails_helper.rb
はデフォルトではこのようになっています。spec/rails_helper.rb# This file is copied to spec/ when you run 'rails generate rspec:install' require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__) # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end # in _spec.rb will both be required and run as specs, causing the specs to be # run twice. It is recommended that you do not name files matching this glob to # end with _spec.rb. You can configure this pattern with the --pattern # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. # # The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e puts e.to_s.strip exit 1 end RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and # `post` in specs under `spec/controllers`. # # You can disable this behaviour by removing the line below, and instead # explicitly tag your specs with their type, e.g.: # # RSpec.describe UsersController, :type => :controller do # # ... # end # # The different available types are documented in the features, such as in # https://relishapp.com/rspec/rspec-rails/docs config.infer_spec_type_from_file_location! # Filter lines from Rails gems in backtraces. config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") endこのコメントアウトされた部分が今回のポイントです。
thisが何を示しているのかだとか、ちょっとした用語を噛み砕けるといいなと思います。自分なりに和訳&補足してみた
rails generate rspec:install
を実行すると、このファイルはspec/
ディレクトリにコピーされます。require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__)本番環境のときにデータベースのTRUNCATE1を防ぎます。
abort("The Rails environment is running in production mode!") if Rails.env.production?require 'rspec/rails'追加で
require
する場合はこの下に追加してください。この時点までRailsは読み込まれていません!
spec/support/
配下のファイルを読み込む設定カスタムマッチャやマクロ2などを記述したrubyファイルを読み込みたい場合、
spec/support/
配下に置いてください。
spec/
配下にある_spec.rb
で終わるファイルはbundle exec rspec
コマンドを実行すると自動的に走ります。つまり、これをspec/support/
内に置くと、読み込み時とテスト時の二回実行されることになります。
そのため、spec/support/
に_spec.rb
で終わるファイルを置くのはやめましょう。この様式3はコマンドを打つときに
--pattern
を使うか、~/.rspec
,.rspec
,.rspec-local
で設定できます。以下の行は手間を省くために用意されています。この行を有効にすると
spec/support/
以下の全てのファイルが自動的に読み込まれるため、起動に時間がかかるという難点もあります。
代わりの方法として、それぞれの_spec.rb
ファイルでrequire
を使って必要なファイルだけを読み込む方法もあります。Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }マイグレーションの設定
保留にされている4マイグレーションを確認し、テストを走らせる前にマイグレーションを適用します。
ActiveRecordを使わない場合、以下の行(
begin
からend
まで)を削除できます。begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e puts e.to_s.strip exit 1 end(補足)
自動的にマイグレーションを行い、schemaとマイグレーションファイルに相違がある場合、例外を発生させます。ActiveRecordのfixtureを使用する設定
もしActiveRecordやActiveRecordのfixtureを使用しない場合はこの行は必要ありません。
config.fixture_path = "#{::Rails.root}/spec/fixtures"(補足)例えばFactoryBotを使用する場合が当てはまりますが、他の人の設定を見たところ、わざわざ削除している人は少ないようです。
exampleごとにトランザクションを行う設定
ActiveRecordを使わない、またはトランザクション内で複数のexampleを走らせたい場合は、この行を削除するかオプションを
true
からfalse
にしてください。config.use_transactional_fixtures = true(補足)
デフォルトのtrue
の場合、exampleごとにトランザクションが行われる設定になっています。つまり、exampleが始まるときにはきれいなデータベースが用意され、終わると全てのデータを削除します。
Database Cleanerなどを使って手動で削除する場合や、特定のSpecでだけトランザクションのロールバックを無効にする場合はfalse
にします。exampleについては以下の説明を参考にしてください。
it
はテストをexample
という単位にまとめる役割をします。
it do ... end
の中のエクスペクテーション(期待値と実際の値の比較)がすべてパスすれば、そのexample
はパスしたことになります。
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」ファイルの場所に応じた機能を使用する設定
RSpec Railsでは、ファイルの場所に応じた動作を使用することができます。
例えば、spec/controllers
配下のファイルでは、get
やpost
を使うことができる仕様となっています。以下の行を削除するとこの仕様が無効になります。
代わりに、RSpec.describe UsersController, type: :controller do # ... endの
type: :controller
のように、明示的にタイプを記述することができます。利用可能なタイプは、以下のサイトに記述しています。
https://relishapp.com/rspec/rspec-rails/docsconfig.infer_spec_type_from_file_location!バックトレースをフィルタリングする設定
Railsで読み込まれたgemによるバックトレースをフィルタリングします。
config.filter_rails_from_backtrace!特定のgemをフィルタリングする場合、次の設定をしてください。
config.filter_gems_from_backtrace("gem name")(補足)
テスト失敗時のノイズを減らすための設定です。
テスト実行時に--backtrace
を付けると、フィルタリングされていないバックトレースが表示されます。(和訳はここまで)
ちなみに
FactoryBotやモジュールを使う場合は、以下のように読み込んでください。
RSpec.configure do |config| ... # config.filter_gems_from_backtrace("gem name") # この下に記述している人が多い印象です config.include FactoryBot::Syntax::Methods config.include LoginHelpers endおわりに
自分で翻訳したり調べたりすると、なんとなくRSpecの仕様への理解が深まった気がします。
また、公式ドキュメントはGoogle翻訳でも読みやすかったです。
間違っていた場合はご指摘をお願いします。
テーブルに格納されているデータを全削除すること ↩
ヘルパーメソッド ↩
テスト実行時にどのファイルを実行するか pattern - Configuration - RSpec Core ↩
db:migrate
されていない状態 ↩
- 投稿日:2020-02-08T19:25:02+09:00
今日はクライアントサイドでセッション管理してもいいのか!!(あまりよくない)
これはなに?
セッションをクライアントサイドだけで管理するのは難しいのでやめようね。という話をする機会が稀にあるのですがどういうときに困るんだっけ?またはどういう値はクライアントサイドに持っていいんだっけ?というのを毎回自分で考えるのが面倒なので書きました。
ここではクライアントサイドだけでセッション管理するのが難しい理由について説明します。そのあとに翻って一般的にセッションはどういう性質を持っているのか(あるいは持っているべきか)という話をします。クライアントサイドだけでやるセッション管理の例
Railsにはセッションストレージとして、CookieStoreというものがあります。説明に関しては
Railsセキュリティガイド: 2.3 セッションストレージ から引用しますがRailsのCookieStoreはクライアント側のcookieにセッションハッシュを保存します。サーバーはこのセッションハッシュをcookieから取得することで、セッションIDの必要性を解消します。こうすることで、アプリケーションのスピードは著しく向上しますが、このストレージオプションについては議論の余地があるため、セキュリティ上の意味やストレージでの制約について以下の点を十分考えておかなければなりません
例えば、ログインが必要な機能にアクセスするごとに「このユーザはログインしてるんだっけ?」というチェックのためにサーバ側のストレージ(DB/KVSなど)にアクセスが走ると、同時接続数が多いアプリケーションだとそれだけでDBのread負荷が上がる可能性があります。このような時に例えばCookieStoreを使うとリクエストに乗ってくるcookieを見るだけで済むのでサーバサイドの負荷を軽減することができる、という利点があります。
あとは、jwtの中にセッション情報を全部ぶち込んでいるケースとかも同様だと思ってもらって良さそうです。Cookie Storeを使うと難しいポイント
CookieStoreを使うとパフォーマンス向上してよかったね〜〜って話で終わるかというとそうではなくて、クライアントサイドでcookieを管理しているため前提としてcookieの有効状態を制御することは(少なくともサーバサイドにストレージがある時にくらべて)難しいです。
上で引用した箇所の下にも
- セッションcookieはひとりでに失効することはないため、悪用目的で使い回される可能性もあります。保存済みのタイムスタンプを利用して古いセッションcookieをアプリケーションで失効させるのもよい方法かもしれません。
という文章があります。
Cookie Storeに関して想定される攻撃例
具体的にどういう攻撃が考えられるか、またその対策については railsguides.jp のRailsセキュリティガイドの中から1つ、別のサイトから1つ紹介します。
再生攻撃
Railsセキュリティガイド: 2.5 CookieStoreセッションに対する再生攻撃 から引用します
再生攻撃のしくみは次のとおりです。
* ユーザーがクレジットを受け取る。総額はセッションに保存されているとする (これはあくまで説明のためのものであり、やってはいけません)。
* ユーザーがクレジットで何かを購入する。
* つかった分減ったクレジットがセッションに保存される。
* ここでユーザーの暗黒面が発動する。最初にブラウザに保存されていたcookieをコピーしてあったものを、現在のブラウザのcookieと差し替える。
* ユーザーのクレジット額が元に戻る。とあり、これについての対策は
結論から言うと、 この種のデータはセッションではなくデータベースに保存するのが最善です。この場合であれば、クレジットをデータベースに保存し、logged_in_user_idをセッションに保存します。
上の説明にも
(これはあくまで説明のためのものであり、やってはいけません)。
と書いてあるように、この例では自明ですが色々なタイミングで「ユーザは任意のタイミングまでセッションを巻き戻せる状態だけどこれセッションに持たせていいんだっけ?」みたいなことを考えていく必要があります。セッションハイジャックされた時に追い出せないよ問題
また、セッション管理がクライアントサイドで完結しているとログアウト/パスワード変更などに対してセッションを無効化する処理ができないという問題があります。
Rails SessionにCookieStore使った時の問題点 から引用しますが(これの元ネタの記事はどうも消されてしまっているようです)server-sideではstate管理しないので、当然remoteでセッションの無効化はできません。 つまりログアウトしてもsession cookieのtoken自体は無効化されません。
という話があり、特に困りそうなポイントとしてはその後に書いてある、
問題は、session cookieが無効化できないのに、それが永遠に有効であること。 FBとかY!Jとかでやってる「パスワード変更したら既存のsessionが無効になる」ってのはRailsは一切面倒見てくれないので、パスワード変えても漏洩したsession cookieは有効なままです。 なので、ひとたびsession cookieが漏れたら、完全にアウト。 なにやってもアカウント乗っ取られたまんま。永遠に。
こういうケースです。
で、これはどう対策すればいいの?という話ですが、この記事の中ではセッションにnonceをつければいいじゃないというアイデアが書いています。(これ自体はジャストアイデア的に書かれていて、その後にもっとシンプルで効果的な対策が書かれています)
OpenID Connectの名前にも触れていることからDBにnonceを保存して、セッション中にnonceがあったらDBと照合する、というような実装を想像しました。実はRailsセキュリティガイドの再生攻撃のあたりにもnonceを使えば防げるっちゃ防げるみたいな話は書いてます(この話は後ほど触れます)
そうすると、攻撃者が古いcookieを持ってきても「そのnonceはもうさっき見たので無理で〜す」みたいな感じで弾けるんですが、ここで みたいな顔になるポイントがあります。人類はどうしてcookie storeを使っていたのか
という話に立ち戻るとRailsセキュリティガイドの中に
サーバーはこのセッションハッシュをcookieから取得することで、セッションIDの必要性を解消します。こうすることで、アプリケーションのスピードは著しく向上しますが
ということでクライアントサイドにセッションを完結させるのはレスポンス速度向上のためなんですね(ここが違うとこの後の話もおかしなことになりますが...)
再生攻撃のあたりでnonceって単語が出てるところを見るとこの再生攻撃は、セッションにnonce (1回限りのランダムな値) を含めておくことで防ぐことができます。nonceが有効なのは1回限りであり、サーバーはnonceが有効かどうかを常に追跡し続ける必要があります。複数のアプリケーションサーバーで構成された合いの子アプリケーションの場合、状況はさらに複雑になります。nonceをデータベースに保存してしまうと、せっかくデータベースへのアクセスを避けるために設置したCookieStoreを使う意味がなくなってしまいます。
こういう話があり、個人的にはCookieStoreの利点が薄れる + 実装の複雑度が増すので、nonceを使うというジャッジをするケースは少ないかなと思います
では、パスワードリセットなどの後にセッションを無効化するには?
CookieStore単体では(そして、クライアントサイドに完結したセッションストレージでは)セッションハイジャック対策として行われるセッション無効化ができないということがわかりました。
では、どうすればいいのかというとクライアントサイドでセッションを管理することを諦めてしまうのが一番簡単ではないかと思います。
引用元のサイトでも、このように触れられています(パフォーマンスに関しては、どういうケースでどういうボトルネックがあって、セッションの一部だけをクライアントサイドに逃がすとどういう風に解消しそうか、という話はまた別途議論が待たれる)もしくはCookieStoreの代わりにMemcacheStore使うようにしてもいいです。 sessionをserver-sideで管理するようにさえすれば、この問題はそもそも発生しないですし。 パフォーマンスに影響しますけど。
セッションってどうあるべき?
ここまで、セッションをクライアントサイドだけで管理すると落とし穴あるよ!そもそもセッションの無効化ができないのである程度複雑な機能が入ってくるとクライアントサイドだけでの管理は無理だよ!って話をしましたが、じゃあお前はセッションをどうしたいの?というと
巻き戻ってもいい値を除いてユーザの状態はサーバサイドで管理すべき
だと思います。
ここでいう巻き戻ってもいい(または巻き戻っても特に変化しない)値の例としては
- ユーザID/名前
- ログイン日時
などが考えられます。このあたりをクライアントサイドに持たせてうまくストレージへのアクセス方法を減らすことはできるかもしれません
結論
ということでサーバサイドにセッションのストレージ(DBなりKVSなり)は用意したほうが良いと思います
参考
jwtでセッションを管理するのはやめようね、という話: Stop using JWT for sessions, part 2: Why your solution doesn't work - joepie91's Ramblings
- 投稿日:2020-02-08T19:11:43+09:00
親子関係のあるモデルのテーブルの値を一度に保存する方法
自分用メモ
Gemfile.rbgem 'nested_form_fields'gemをインストール。
モデル関係
親:post
子:program
子:dealer
孫:programmodel/post.rbhas_many :programs, inverse_of: :post, dependent: :destroy has_one :dealer accepts_nested_attributes_for :programs, allow_destroy: true accepts_nested_attributes_for :dealer, allow_destroy: truepostから見て子供であるprogramとdealerのアソシエーションを書く。
accepts_nested_attributes_forを書くことによって、モデル同士が関連付けられている時に、ネストさせることで一度にまとめて保存できる。
!注意点!
モデルはアソシエーションで単数なのか複数なのかきちんと意識すること。
最初dealerの記述をする時に、accepts_nested_attributes_for :dealers, allow_destroy: true と書いてしまいうまくいかなかった。
has_oneなので「s」はいらない。model/dealer.rbbelongs_to :company belongs_to :post, optional: truedealerに紐づく子供はいないのでこれだけ。
今回の話にcompanyは関係ないので割愛。model/program.rbbelongs_to :post, optional: true has_many :products, inverse_of: :program, dependent: :destroy accepts_nested_attributes_for :products, allow_destroy: trueprogramから見て親のpostと子供のproductとのアソシエーション。
postと同じようにaccepts_nested_attributes_forを記載。product.rbbelongs_to :program, optional: true belongs_to :categoryproductに子供はいないのでこれだけ。
今回の話にcategoryは関係ないので割愛。コントローラー
posts_controller.rbdef new @post = Post.new dealers = @post.build_dealer programs = @post.programs.build products = programs.products.build end def create @post = Post.new(post_params) @post.save end private def post_params params.require(:post).permit(:facility_name, :address, :tell, :registration_date, :estimate_sheet, :delivery_date, :delivery_note, :postal_code, :kananame, :user_id, dealer_attributes:[:name, :kananame, :company_id, :post_id, :_destroy, :id], programs_attributes:[:software, :user, :post_id, :_destroy, :id, products_attributes:[:thing, :category_id, :model_number, :program_id, :_destroy, :id]]).merge(user_id: current_user.id) end大元の親であるposts_controllerのみにnew、createを定義するだけで大丈夫。
ポイントは以下。
・newアクションで子供、孫のbuildを作成する。
今回で言えば、dealer、program、productのbuild作成。
・paramsのpermitで子供、孫のハッシュをかっこでネストさせる。
今回のdealerに子供はいないのでとじかっこの位置には注意。
programには子供であるproductがいるので、更にネストさせている。
・createアクションでは、モデルオブジェクトの作成はPost.new(post_params)と大元の親だけで大丈夫。
変数@postに作成したモデルオブジェクトを入れ、@post.saveとすることで保存される。View
new.html.haml= render 'partial/sidebar' .main %span.main-child 情報を登録してください .main-registration__date .main-registration__date-child 登録日 = form_for @post, url: posts_path do |f| =f.text_field :registration_date, placeholder: "登録日", type: "text", class:"registration__date-input-text" .main-facility .main-facility-child 名前 =f.text_field :facility_name, placeholder: "名前", type: "text", class:"facility-input-text" .main-kana .main-kana-child 名前(カナ) =f.text_field :kananame, placeholder: "カナ", type: "text", class:"kana-input-text" .top %span.top-name ディーラーを登録してください .top-dealer .top-dealer-child = f.fields_for :dealer do |k| .top-dealer-child_box .top-dealer-child_box-company_text 会社名 .top-dealer-child_box-company = k.collection_select :company_id, Company.all, :id, :name .top-dealer-child_box-name_text 名前 .top-dealer-child_box-name = k.text_field :name, placeholder: "名前", type: "text", class:"dealer-input-text", id: "dealer_text" .top-dealer-child_box-kana_text 名前(カナ) .top-dealer-child_box-kana = k.text_field :kananame, placeholder: "カナ", type: "text", class:"dealer_kana-input-text", id: "dealer_kana_text" .best %span.best-name 納品内容を登録してください .best-software .best-software-child ソフトウェア = f.fields_for :programs do |d| #best-software-child_box = d.text_field :software, placeholder: "name", type: "text", class:"program-input-text", id: "program_text" .products .products-text_box = d.fields_for :products do |s| .products-text_box-category_text カテゴリー .products-text_box-category = s.collection_select :category_id, Category.all, :id, :name .products-text_box-product_text ハードウェア .products-text_box-product = s.text_field :thing, placeholder: "products", type: "text", class:"products-input-text", id: "product_text" .footer = f.submit "送信", class:"products_button"本当のコードは長すぎるので一部削除。
form_forを使って、まず親であるpostを設定。form_forの書き方
= form_for モデル, url: createのpath do |f|
モデル部分にモデルオブジェクトをいれる、今回は@post。
url部分にはpostのcreateアクションのpathをいれる(コマンドrails routesでわかる)続いてpostの子供であるdealerを設定。
子供以下は全てfields_forを使う。fields_forの書き方
= f.fields_for :モデル do |k|
・モデル部分にシンボルを使ってモデル名を記載。
ここでも単数なのか複数なのか意識すること。
・fields_forの前のfはpostのform_forで使っている変数の|f|←コイツ
必ず親の変数をつけること。
・hamlの場合必ず親のform_forやfields_forよりも中にネストさせること。
※programのビューの塊がdealerのfields_forよりネストされていない理由はprogramがdealerと同じ位置の子供だから。
もしprogramがdealerの子供であればネストさせないといけない。fields_forさえ使いこなせれば、大抵の親子関係の値は一度に保存可能なはず。
- 投稿日:2020-02-08T15:18:15+09:00
Rubyで学ぶ「ゼロから作るDeep Learning」Docker環境構築編
Dockerの環境構築
やること
- ローカルのワーキングディレクトリを作成
- DockerでRubyの環境を作成
- Dockerで作成したワーキングディレクトリをマウントして環境に入る
- Dockerに必要なパッケージ等をインストール
- gnuplot & numo-gnuplot
- プロット用
- numo-narray
- 行列計算 & 基礎的な数学の関数(sin cos exp等)
Docker環境を作って、ワーキングディレクトリをマウントする所まで
実行コマンド
# ローカルにワーキングディレクトリを作成 local$ mkdir working_dir # RubyのDockerイメージを取得 local$ docker pull ruby:latest # ワーキングディレクトリをマウントしてDocker環境に入る local$ docker run -v /(workingディレクトリをおいているパス)/working_dir/:/working_dir/ -it ruby /bin/bashなぜワーキングディレクトリをマウントしておくか
gnuplotで出力したファイルを、Dockerファイルシステムの外で簡単に確認できる。ローカル環境にコピーするコマンドを叩かず、手元の環境(macOS)のプレビューアプリ等で出力したpngイメージを確認できるので便利。他にも、特に設定せずに、自分のお気に入りのエディタを使えるのもよい。
Dockerに必要なパッケージ等をインストール
# gunuplotのインストール docker$ apt update -y && apt upgrade -y && apt install -y gnuplot # 各種gemのインストール docker$ gem install numo-narray numo-gnuplotとりあえず動かしてみる
ローカルのワーキングディレクトリに先人の記事のファイルをそのまま作成
Python vs Ruby 『ゼロから作るDeep Learning』 1章 sin関数とcos関数のグラフ
https://qiita.com/niwasawa/items/6d9aba43f3cdba5ca725# ワーキングディレクトリへ移動 docker$ cd working_dir # 作ったファイルがあるかの確認 docker:working_dir$ ls sin_cons.rb # ←ちゃんとローカルで作ったファイルがある # 実行 docker:working_dir$ ruby sin_cos.rb docker:working_dir$ ls ruby_graph.png sin_cons.rb # ← 出力されている結果
備考:なぜRuby?
鳥取県米子市出身で、松江にサテライトオフィスを持つ企業で働いている為。
松江といえばMatzさん、Rubyの聖地。仕事でもRubyをつかうしね。
- 投稿日:2020-02-08T15:18:15+09:00
敢えてRubyで学ぶ「ゼロから作るDeep Learning」Docker環境構築編
Dockerの環境構築
やること
- ローカルのワーキングディレクトリを作成
- DockerでRubyの環境を作成
- Dockerで作成したワーキングディレクトリをマウントして環境に入る
- Dockerに必要なパッケージ等をインストール
- gnuplot & numo-gnuplot
- プロット用
- numo-narray
- 行列計算 & 基礎的な数学の関数(sin cos exp等)
Docker環境を作って、ワーキングディレクトリをマウントする所まで
実行コマンド
# ローカルにワーキングディレクトリを作成 local$ mkdir working_dir # RubyのDockerイメージを取得 local$ docker pull ruby:latest # ワーキングディレクトリをマウントしてDocker環境に入る local$ docker run -v /(workingディレクトリをおいているパス)/working_dir/:/working_dir/ -it ruby /bin/bashなぜワーキングディレクトリをマウントしておくか
gnuplotで出力したファイルを、Dockerファイルシステムの外で簡単に確認できる。ローカル環境にコピーするコマンドを叩かず、手元の環境(macOS)のプレビューアプリ等で出力したpngイメージを確認できるので便利。他にも、特に設定せずに、自分のお気に入りのエディタを使えるのもよい。
Dockerに必要なパッケージ等をインストール
# gunuplotのインストール docker$ apt update -y && apt upgrade -y && apt install -y gnuplot # 各種gemのインストール docker$ gem install numo-narray numo-gnuplotとりあえず動かしてみる
ローカルのワーキングディレクトリに先人の記事のファイルをそのまま作成
Python vs Ruby 『ゼロから作るDeep Learning』 1章 sin関数とcos関数のグラフ
https://qiita.com/niwasawa/items/6d9aba43f3cdba5ca725# ワーキングディレクトリへ移動 docker$ cd working_dir # 作ったファイルがあるかの確認 docker:working_dir$ ls sin_cons.rb # ←ちゃんとローカルで作ったファイルがある # 実行 docker:working_dir$ ruby sin_cos.rb docker:working_dir$ ls ruby_graph.png sin_cons.rb # ← 出力されている結果
備考:なぜRuby?
鳥取県米子市出身で、松江にサテライトオフィスを持つ企業で働いている為。
松江といえばMatzさん、Rubyの聖地。仕事でもRubyをつかうしね。
- 投稿日:2020-02-08T13:04:33+09:00
Xcode11で、Distribute時のRubyのエラー:ipatool JSON: (null)
- 投稿日:2020-02-08T03:46:36+09:00
外部キー作成の時の注意事項
テーマ「データベース 頭文字は大文字か小文字か」
この投稿はRailsのデータべース作成に関する記事です。
データベース周辺は間違えると後のアプリ開発に悪い影響を与えてしまいます。
このミスを参考にみなさんは出来る限りデータベース周辺では間違わないようにしてください!今回の主犯
外部キーの頭文字を大文字にしてしまったため、seed.rbからデータを入力できなくなった。
実物はこちらです。Faculity_id←これ。。。本来はfaculity_idとなるべきはずscheme.rbcreate_table "majors", force: :cascade do |t| t.index ["Faculity_id"], name: "index_majors_on_faculity_id" endミスの原因
1ヶ月ぶりにRailsを触り、フレームワークの使い方を忘れてしまったこと
(初心者は毎日触らなけれぼなりませんね。思い知りました?)ターミナルで入力した間違いのコマンド
referencesの前を大文字にしてしまったため、外部キーも大文字になった。
terminal.$ rails g migration AddFaculityToMajors Faculity:references本来は$ rails g migration Add(頭文字大カラム名)To(頭文字大テーブル名) (小文字カラム名):references
というコマンドの構成です。(下記のコマンドが正しい)terminal.$ rails g migration AddFaculityToMajors faculity:referencesseed.rbからデータを入力すると。。。
seed.rbからデータが送れない。外部キーが合わないため当然ちゃ当然ですね。
scheme.rbcreate_table "majors", force: :cascade do |t| t.index ["Faculity_id"], name: "index_majors_on_faculity_id" endこちらは小文字なので、キーが探せずrollbackされてしまった。
seed.rbMajor.create!(id: 1, name: '経営', faculity_id: 1)ここでもう1つ問題が。。。Faculity_id(主犯)が消えない
Faculity_idの外部キーを削除するために、削除コマンドを試したが、、、
terminal.$ rails g migration RemoveFaculity_idFromMajor faculity:references Running via Spring preloader in process 8806 invoke active_record create db/migrate/20200207160003_remove_faculity_id_from_major.rb $ rails db:migrate == 20200207160003 RemoveFaculityIdFromMajor: migrating ======================== -- remove_reference(:majors, :faculity, {:foreign_key=>true}) -> 0.0078s == 20200207160003 RemoveFaculityIdFromMajor: migrated (0.0083s) ===============scheme.rbcreate_table "majors", force: :cascade do |t| t.index ["Faculity_id"], name: "index_majors_on_faculity_id" end依然として、Faculity_idがある。なぜーーーーー(ぴえーん)
もうこれはテーブル丸ごと消してしまおう!!!!!(テーブル削除に初挑戦)
削除コマンドはこちらを参考にしました。
[Rails]不要になったmodelの削除方法(https://bokuranotameno.com/post-9880/)terminal.$ rails g migration drop_table_(テーブル名複数形)migrationファイルに削除するテーブル名を記述し$ rails db:migrateを実行します。
20200207073446_○○_majors.rbdef change drop_table :majors(テーブル名複数形) end一連のコマンドの流れ
terminal.$ rails g migration drop_table_majors Running via Spring preloader in process 9120 invoke active_record create db/migrate/20200207160634_drop_table_majors.rb $ rails db:migrate == 20200207160634 DropTableMajors: migrating ============================== == 20200207160634 DropTableMajors: migrating ================================== -- drop_table(:majors) -> 0.0020sこれでようやくFaculity_idは削除されました!!!(テーブルごと消えてしまったけど(笑))
modelの関連ファイルも削除します。
・modelファイルの削除 app/models/major.rb
・testファイルの削除 test/models/major_test.rb
・fixturesファイルの削除 test/fixtures/majors.yml新しいmodelを作成しよう!だがまた新たな問題が、、、
terminal.$ rails g model Major name:string faculity:references Running via Spring preloader in process 9606 invoke active_record conflict db/migrate/20200207161950_create_majors.rb Another migration is already named create_majors: /home/ec2-user/environment/review/db/migrate/20200207072743_create_majors.rb. Use --force to replace this migration or --skip to ignore conflicted file.どうやらmigrateフォルダ内にある前回Major modelを作成したmigrationファイルが残っていることが原因
migrateフォルダからmigrationファイルを削除すると、herokuにアップした時などデータベースが上手く作られない可能性があるが、新しくmodelを作成するので大丈夫だと信じる
migrationファイル削除後、もう一度試す!!!
$ rails g model Major name:string faculity:references Running via Spring preloader in process 10134 invoke active_record create db/migrate/20200207162947_create_majors.rb create app/models/major.rb invoke test_unit create test/models/major_test.rb create test/fixtures/majors.yml $ rails db:migrate == 20200207162947 CreateMajors: migrating ===================================== -- create_table(:majors) -> 0.0034s == 20200207162947 CreateMajors: migrated (0.0040s) ============================無事できた!!!
seedファイルも大丈夫かどうか確かめる。
terminal.$ rails db:seedこちらも無事エラーが出ない!!!!!
(教訓)データベースを作成する時大文字小文字に注意
モデルの作成は大文字
terminal.$ rails g model User(大文字)カラムの作成は小文字
terminal.$ rails g migration AddPostToUser user:references(小文字)皆さんも、これらの項目に注意をして、楽しくアプリ開発してください!!!
これが参考にされば幸いです!
- 投稿日:2020-02-08T01:59:07+09:00
【Rails】mechanizeを使えばrailsスクレイピングが余裕な件
スクレイピングに初挑戦したとき、検索上位にあがるサイトはどれもnokogiriを使用するのですが、dockerだとうまく利用できず四苦八苦していました。
その中で見つけたのがmechanizeというgemです。
自分用メモのためにも今回はコードを書いときたいと思います。
前提
railsアプリは作成済み
gem mechanize インストール済みController
controllerでは.getメソッドとsearchメソッドを使ってページ情報を取得します。
def home agent = Mechanize.new page = agent.get("https://nemlog.nem.social/") @elements= page.search('.visit-w') endあとはインスタンス変数@elementsを利用してviewに表示するだけです。
View
<%@elements.each do|element|%> <ul> <li> <%=link_to(element.inner_text,element[:href])%> </li> </ul> <%end%>.inner_textはテキスト部分だけ抜き出してくれます。
また、element[:href]は指定したhtml(この場合はclass=visit-w)の属性を抜き出します。まとめ
結論、mechanizeは神ですね。
スクレイピングをお手軽に試してみたい人はぜひ使ってみてください。おまけ
これを利用して簡単なnemlolgのスクレイピング サイトを作りました。
コードも確認できるので参考になればと思います。
- 投稿日:2020-02-08T01:38:09+09:00
rubocopで複数行の引数の最後の値にカンマを付ける
複数行の配列や引数を書くとき
最後にカンマを付けない派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )
rubocop -a
で自動整形すると、デフォルトでは上記のように整形されます。
ruby以外の言語だと、引数や配列の最後にはカンマを付けるとエラーになったりするので、一般的に使われている書き方だと思います。最後にカンマを付ける派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', )分かりづらいですが、先程の例と比べると、最後の引数の後にカンマが付いているのが分かります。
rubyでは上記のように最後の引数にカンマを付けても問題なく動きます。
慣れていないと気持ち悪く感じるかもしれませんが、引数を追加したい時などに素早くコピペできるので便利だったりします。最後にカンマを付けるためのrubocopの設定
Style/TrailingCommaIn~~
を設定する引数/配列/ハッシュでそれぞれ設定するスタイルが異なります。
種類 スタイル名 引数 Style/TrailingCommaInArguments 配列 Style/TrailingCommaInHashLiteral ハッシュ Style/TrailingCommaInArrayLiteral それぞれ
EnforcedStyleForMultiline
プロパティをcomma
またはconsistent_comma
に設定すればOKです。rubocop.ymlStyle/TrailingCommaInArguments: EnforcedStyleForMultiline: comma Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: commaこれで
rubocop -a
を実行したときに引数/配列/ハッシュの値にカンマがつくようになります。comma
をconsistent_comma
にしても同様の結果が得られます。
consistent_comma
とcomma
の違いこちらのようなドキュメントを読んでも違いがわからなかったのでいくつか試してみたところ、複数行に引数を書きつつも、1行に複数の引数がある場合に違いが出る事がわかりました。
seeds.rb# consistent_comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', ) # comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )分かりづらいですが、最後2つの引数を同じ行に書いています。
このようなときconsistent_comma
だと最後の引数にカンマが付き、comma
だと付きません。ちょっとハマったところ
下記の様にメソッドの行から引数を書き始めると、どの設定にしても下記のように整形されてしまいますのでご注意ください。
seeds.rbUser.create!(name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password')なお古いバージョンのrubocopでは引数/配列/ハッシュでのカンマ有無をすべて
Style/TrailingComma
で設定していたようですが、現在こちらは削除されているので使えません。参考
- 投稿日:2020-02-08T01:38:09+09:00
【RuboCop】複数行の引数の最後の値にカンマを付けたい
複数行の配列や引数を書くとき
最後にカンマを付けない派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )
rubocop -a
で自動整形すると、デフォルトでは上記のように整形されます。
ruby以外の言語だと、引数や配列の最後にはカンマを付けるとエラーになったりするので、一般的に使われている書き方だと思います。最後にカンマを付ける派
seeds.rbUser.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', )分かりづらいですが、先程の例と比べると、最後の引数の後にカンマが付いているのが分かります。
rubyでは上記のように最後の引数にカンマを付けても問題なく動きます。
慣れていないと気持ち悪く感じるかもしれませんが、引数を追加したい時などに素早くコピペできるので便利だったりします。最後にカンマを付けるためのrubocopの設定
Style/TrailingCommaIn~~
を設定する引数/配列/ハッシュでそれぞれ設定するスタイルが異なります。
種類 スタイル名 引数 Style/TrailingCommaInArguments 配列 Style/TrailingCommaInHashLiteral ハッシュ Style/TrailingCommaInArrayLiteral それぞれ
EnforcedStyleForMultiline
プロパティをcomma
またはconsistent_comma
に設定すればOKです。rubocop.ymlStyle/TrailingCommaInArguments: EnforcedStyleForMultiline: comma Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: commaこれで
rubocop -a
を実行したときに引数/配列/ハッシュの値にカンマがつくようになります。comma
をconsistent_comma
にしても同様の結果が得られます。
consistent_comma
とcomma
の違いこちらのようなドキュメントを読んでも違いがわからなかったのでいくつか試してみたところ、複数行に引数を書きつつも、1行に複数の引数がある場合に違いが出る事がわかりました。
seeds.rb# consistent_comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password', ) # comma User.create!( name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password' )分かりづらいですが、最後2つの引数を同じ行に書いています。
このようなときconsistent_comma
だと最後の引数にカンマが付き、comma
だと付きません。ちょっとハマったところ
下記の様にメソッドの行から引数を書き始めると、どの設定にしても下記のように整形されてしまいますのでご注意ください。
seeds.rbUser.create!(name: 'foo bar', email: 'foo@baa.com', password: 'password', password_confirmation: 'password')なお古いバージョンのrubocopでは引数/配列/ハッシュでのカンマ有無をすべて
Style/TrailingComma
で設定していたようですが、現在こちらは削除されているので使えません。参考