ruby チュートリアル7章 メモ
サイトにデバッグ情報表示
下のコードをレイアウトのコードに挿入することで、描画されるページの状態を把握するのに役立つ情報をサイトに表示することができる。if~
文は開発環境でのみデバッグ情報を表示するよう指定している。
#app/views/layouts/application.html.erb <!DOCTYPE html> <html> . . . <body> <%= render 'layouts/header' %> <div class="container"> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> </body> </html>
Sassのミックスイン
ミックスインを使用することでcssルールのグループをパッケージ化して簡単に複数要素に対して使用することができる。例えば、要素のサイズや見た目をいろんな場所で統一したい場合、あらかじめレイアウトをパッケージ化しておけば、そのパッケージをインポートするだけで、レイアウトを適用することできます。
#app/assets/stylesheets/custom.scss @import "bootstrap-sprockets"; @import "bootstrap"; /* mixins, variables, etc. */ $gray-medium-light: #eaeaea; @mixin box_sizing { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } . . . /* miscellaneous */ .debug_dump { clear: both; float: left; width: 100%; margin-top: 45px; @include box_sizing; }
RESTアーキテクチャの適用
データの作成、表示、更新、削除をリソースとして扱うこと。これらに対応する4つの操作(GET,POST,PATCH,DELETE)を各アクションに割り当てる必要がある。RailsのREST機能が有効だと、GETリクエストは自動的にshowアクションとして扱われる。この場合、リソースへの参照はリソース名とユニークIDをURIとして使用することになり、id=1のユーザの参照はusers/1
となる。これをルーティングに設定するには下のようにする。
#config/routes.rb Rails.application.routes.draw do root 'static_pages#home' . . . get '/contact', to: 'static_pages#contact' get '/signup', to: 'users#new' resources :users end
UsersリソースをRESTfulなアクションにした場合以下のようになる。
debuggerメソッド
byebug gem
のdebuggerメソッド。下のようにコード中に挟むとサーバを立ち上げたコンソール画面から、その場所のデバッグを行うことができる。
#app/controllers/users_controller.rb class UsersController < ApplicationController def show @user = User.find(params[:id]) debugger end def new end end
Gravatar
プロフィール写真をアップロードして、指定したメールアドレスと関連付けることができ、画像パスを生成するだけでその画像を引っ張ってこれる。チュートリアルでは以下のようなヘルパーを作成して画像を引っ張ってきていたが、このチュートリアルに最適かは疑問。後々使うのであればまた別ですが。
# 引数で与えられたユーザーのGravatar画像を返す def gravatar_for(user) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end
オプション引数
メソッドを定義した際に予めデフォルト値を与えてやれば、引数に値を指定しなかった場合自動でデフォルトの値が使用される。もちろん値を渡した場合はその値が優先される。これをオプション引数といい下のように定義する。
def gravatar_for(user, options = { size: 80 }) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) size = options[:size] gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end
またRuby2.0から導入されたキーワード引数を使用することで同じものをより短く実装が可能。
def gravatar_for(user, size: 80) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end
これのメソッドの呼び出し方は下のようにする。
<%gravatar_for @user%> #デフォルトの80が適用される <%gravatar_for @user, size:200%> #引数で渡した200がsizeに適用される。
form_forメソッド
form_forメソッドはActiveRecordのオブジェクトを取り込んで、そのオブジェクトの属性を使用してフォームを構築してくれる。
<%= form_for(@user) do |f| %> . . <% end %>
f
オブジェクトはHTMLフォームに対応するメソッドが呼び出されると、@userの属性を設定できる特別なHTMLを返す。labelの:name
シンボルは@userの属性になくてもエラーは出ない。シンボル名がそのままラベルに変換される。text_fieldの方はuserの属性に含まれるものをシンボルとして渡さないとエラーになる。
<%= f.label :name %> <%= f.text_field :name %>
上を実行すると下のように展開される。
<label for="user_name">Name</label> <input id="user_name" name="user[name]" type="text" />
text_fieldを使用すると一般的な入力フォームが、password_fieldを使用すると文字隠しが適用される入力フォームが生成されるなど、柔軟に対応できていることもわかります。
form自身のhtmlは以下のように生成される。
<form action="/users" class="new_user" id="new_user" method="post">
form_forは生成時に読み込んだ@userがUserクラスであり新しいUserであることを自動で認識し、actionとmethodを生成する。RESTfulな設計なのでpostはcreateに対応しており、Userクラスのcreateアクションが実行されるといった流れ。classとidはformヘルパーに対して特に直接的な結びつきは持たない。
-----------追記(2019/3/4)---------
チュートリアルでは、エラーの表示部分をパーシャルとして抽出しているのですが、チュートリアルのままだと@user
変数にしか使えません。下のサイトを参考にどのフォームでもこのパーシャルを使い回せる様にしましょう。
qiita.com
= form_with(model: resource, as: resource_name, url: password_path(resource_name),local:true, html: { method: :post }) do |f| = render "shared/error_messages", model: f.object # 追加:modelで参照できる様にする .field = f.label :email br/ = f.email_field :email, autofocus: true, id: "email" , autocomplete: "email", class: "form-control" .actions = f.submit I18n.t("devise.passwords.new.send_me_reset_password_instructions"), class: "btn btn-primary" = render "devise/shared/links"
ごめんなさい、Slim
での記法になってます。
# _error_messages.html.slim - if model.errors.any? # modelでオブジェクトの参照が可能 .error-explanation.alert.alert-danger strong = I18n.t("errors.messages.not_saved", count: model.errors.count, resource: model.class.model_name.human.downcase) ul - model.errors.full_messages.each do |message| = message
-----------追記ここまで-----------
Strong Parameters
user情報入力後、その値を全てそのままUser生成に使用してしまったら、意図しないパラメータも更新されてしまう可能性がある。そのため、予め設定したものだけのみ、入力情報から取り出せるよにするべきである。
@user = User.new(params[:user]) #:userに格納された全ての情報を使用しようとする @user = User.new(params.require(:user).permit(:name, :email, :password, :password_confirmation)) #許可した値だけ取り出される。
例えばcreateアクション時に簡単に呼び出せるようなメソッドをprivateに実装しておくことが考えられる。
def create @user = User.new(user_params) if @user.save # 保存の成功をここで扱う。 else render 'new' end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end
app/views/sharedディレクトリ
複数のビューで使用されるパーシャルはこの下に保存される。(習慣なので自動でやるわけではない)
pluralize
英語テキスト専用のヘルパー。一つ目の引数の値(数字)によって二つ目の文字を複数形に変更してくれる。
redirect_to
redirect_to @user
はredirect_to user_url(@user)
と等価。これは以下の挙動と同じだが、あくまで挙動が同じだけ。route.rbでresourcesを指定していないものに対しては使用できない(user_urlがresourcesを使用した場合のみ利用可能になるから)
redirect_to "/users/#{@user.id}" # 条件付き(後述) or redirect_to user_url(id: @user.to_param) or redirect_to user_url(id: @user.id)
flash
ページが切り替わった時にユーザに向けて表示するメッセージを生成管理してくれるメソッド。コントローラのアクション定義の際にflashにシンボルと紐づけてメッセージを格納すると、次のアクションするまでその値を保持してくれる。(createアクション内で格納したメッセージは次のアクションで飛んだページでは残っているが、もう一度アクションをすると消える)
【Rails入門】flashの使い方まとめ | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
なお、次のアクションにも残さなかったり、逆に永続的に保持させることも可能。
content_tag
railsのヘルパー。content_tag(タグの名前)
といった形で使用し、タグを動的に生成してくれる。チュートリアルではflash用のhtmlを見やすいように書き換えるために使用した。
#before <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %> #after <% flash.each do |message_type, message| %> <%= content_tag(:div, message, class: "alert alert-#{message_type}") %> <% end %>
今回のintegrationテストに関して
今回のサインアップのテストはチュートリアル上ではIntegrationテスト(featureテスト)の位置付けで行われていた。しかし、その内容はコントローラのテストとした方がよい可能性がある。
と言うのも、featureテストはcontrollerを挟んだviewをテストするイメージであり、今回のテストは完全にcontrollerのメソッド内で完結している処理だからである。
では、viewのテストとは?一般にはviewのテストは書かないという風潮があるかもしれない。。何かを表示するにはほぼ必然的にcontrollerを経由し、それはつまりfeatureテストになる。(getしたページのタイトルが予期したものか等) またそもそもページに何のコンテンツが表示されているかのテストを始めると、保守コストが高すぎてやってられるか〜!となる話もあったりなかったり。
(念のためですが、書いている方もしっかりいらっしゃるので決して必要ないと言うことではありませんよ=> RSpec使ってERBに対するspecをどう書くか? - Qiita)
ちなみに、viewのヘルパーのテストは行う可能性が十分にあるので注意。
t-pot 観察 #5 イギリスからのpop3ブルーフォース
2018/11/6 12:00~12:30
イギリスからpop3(110番)へのブルートフォース攻撃をhoneytrap
で検知。大した量じゃないけれど、通常時は観測しないイベントだったのでここに書いておきます。
Suricataでのアラート名はET SCAN Rapid POP3 Connections - Possible Brute Force Attack
で、攻撃元IPアドレスは51.68.174[.]15
でした。下は24時間分のt-potの検知グラフですが、全体へのアクセスに対してここだけ特徴的に飛び出しているのがわかります。
dionaea virustotalを有効にする
ふとこの記事が何故か下書きで眠っていました。書きかけだったのでしょうか、、?とりあえず公開しておきます。
導入
Dionaeaのバージョンは0.6.0です。
/opt/dionaea/etc/dionaea/ihandlers-available/virustotal.yamlを/opt/dionaea/etc/dionaea/ihandlers-enabled/virustotal.yamlへコピーする。
$ cp /opt/dionaea/etc/dionaea/ihandlers-available/virustotal.yaml /opt/dionaea/etc/dionaea/ihandlers-enabled/virustotal.yaml
ihandlers-enabled/virustotal.yamlのapikeyの部分に自分のvirustotalのapikeyを打ち込む
- name: virustotal config: # grab it from your virustotal account at My account -> Inbox -> Public API apikey: "*********************************************" file: "/opt/dionaea/var/dionaea/vtcache.sqlite"
virustotalのapiキーは、自分のvirustotalアカウントを作った後、自分のSettingsのページに飛べばあります。終わったらdionaeaの再起動をかけます。
するとyamlファイル通り/opt/dionaea/var/dionaea/vtcache.sqliteファイルが生成されているかと思います。
一応、sqlite3コマンドで開くことができるので、時間をおいてから中身を見てみるのも良いですね。
ruby チュートリアル6章 メモ
Model
データモデルとして扱うデフォルトのデータ構造のことをモデルと呼ぶ。データベースとのやり取りを行うライブラリはActiveRecordで、データオブジェクトの生成・保存・検索のメソッドをもつ。よって、SQL文を書かない。マイグレーション機能はデータの定義をRubyでできるようにしてくれている。
モデルの生成
$ rails generate model User name:string email:string
コントローラ名には複数形を使って、モデル名には単数形を使用する。これによって生成されるテーブル名は複数形 (users) になる。上のコマンドで以下のマイグレーションファイルが生成される。タイムスタンプを名前に使用する事で多人数での開発でもコンフリクトを起こさず順番を保持することができる。t.timestampsは特別なコマンドで、created_atとupdated_atという2つの「マジックカラム (Magic Columns)」を作成する。これらは、あるユーザーが作成または更新されたときに、その時刻を自動的に記録するタイムスタンプの役割。また、記述されていないが、id属性が自動で生成される。
# db/migrate/[timestamp]_create_users.rb class CreateUsers < ActiveRecord::Migration[5.0] def change create_table :users do |t| t.string :name t.string :email t.timestamps end end end
マイグレーションは以下のコマンドで実行される。
$ bin/rails db:migrate
ロールバック
$ rails db:rollback
マイグレーションファイルのchange
メソッドはdrop_table
とcreate_table
メソッドがそれぞれ対応していることを理解しており、マイグレーションファイルを呼ぶだけでロールバックが可能になっている。一部カラム削除等の操作はchange
メソッドを使用せず、up
とdown
メソッドを自分で別々に定義する必要がある。
schema.rb
schema.rb
にはマイグレートされたものが記録されていく。ロールバックすると、その分消える。
モデルの生成と保存
$u = User.new(name:"hoge") # hoge以外の値がnil $u.save #idとタイムスタンプに値が入る #上2行と同じ $ User.create(name:"hoge")
validation
モデルのファイルに書く。例えば上のUserモデルであれば以下のようにする。
#app/models/user.rb class User < ApplicationRecord validates :name, presence: true end
validates
はメソッドなので以下のようにカッコを使用してもかける。
class User < ApplicationRecord validates(:name, presence: true) end
この検証をコンソールで確認することも可能。エラー内容を確認することもできる
$ rails console --sandbox >> user = User.new(name: "", email: "mhartl@example.com") >> user.valid? => false >> user.errors.full_messages => ["Name can't be blank"]
インスタンス変数
Minitest
ではsetup
, RSpec
ではbefore
あたりでテスト毎の実行前に呼びたい共通コードを書く。
def setup @user = User.new(name: "Example User", email: "user@example.com") end
頭に@
をつけたインスタンス変数を使用すれば、このコード内でどこからでも参照可能になる。@
をつけなければ、例えばit
内で呼ぶことはできない。
DBレベルでの一意性
一意性を保ちたいものに対して、インデックスを追加し、インデックスの一意性を保つように変更する。
インデックスの追加
インデックスを追加することによって、find等の検索のスピードも早くなる。
$ bin/rails generate migration add_index_to_users_email
上のコードでファイルは生成されるが中身は無い状態なので自分で追加する必要がある。
class AddIndexToUsersEmail < ActiveRecord::Migration[5.0] def change add_index :users, :email, unique: true #trueにすることによって end end
追記したら、以下のコマンドで反映。
$ bin/rails db:migrate
ActiveRecordのコールバックメソッド
ある特定の時点で呼ばれるメソッド。例えば、before_save
を使用すればユーザをデータベースに保存する前に特定の処理を挟むことができる。
class User < ApplicationRecord before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } end
ハッシュ化されたパスワード
has_secure_password
メソッドを呼び出す。ハッシュ関数を利用するにはgem 'bcrypt'
のジェムを入れておくこと。利用するには以下のように追記すればok。
class User < ApplicationRecord . . . has_secure_password end
これにより以下の利点がある。
- セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
- 2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される 。
- authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド) 。
よって、モデルにpassword_digestというカラムを追加する必要がある。
$ rails generate migration add_password_digest_to_users password_digest:string
末尾にto_users
とつけることで、自動でadd_password_digest_to_users
というマイグレーションファイルを生成してくれる。以下がそれ。
class AddPasswordDigestToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :password_digest, :string end end
最後にいつも通りマイグレーションコマンドで変更を反映してあげればok。
T-pot観察 #4 2018/11/5 5060へのアクセス増加
全く書くことがなく気がついたらこんなに日が空いてしまいました。(パソコン修理とかしてたしね)
本日珍しくdionaeaの検知数がCowrieを超えたので報告しておきます。
2018/11/5 00:30~
上のグラフはここ一週間のt-potへの総アクセスを集計したものです。11/5の深夜0:30よりdionaeaへのアクセスが急激に増加しているのがわかります。
全てオランダからです。直近24時間分のグラフにまとめるとこのようになります。
後で追記しますが、今の所ここらで集中アクセスが終わりそうな兆しも見えますね。もしそうであればちょうど24時間続いたことになります。
追記(11/7):一日置いてみましたが、あれ以降アクセス数は再び平常値に戻りました。
下のグラフはポート番号別のアクセス記録になりますが、一目で5060番が狙われていたのがわかりますね。
使用されたIPアドレスも5060番に絞った時、特徴が出ますね。同じサービス元を利用した攻撃のようで、27.49.231[.]~系が使われていますね。一応アクセス数は合計42569回前後という形で収まりそうです。
ポート5060
少々古いですが以下の記事を見つけました。5060/UDPはSIP(Session Initiation Protocol)で使用されているそうです。
SIP関連機器を悪用する攻撃に注意--5060/UDPポートをスキャン、辞書攻撃 - CNET Japan
SIPは2つ以上のクライアント間でセッションを確立するために使用されるプロトコルで、wikiにはIP電話が例に挙げられていました。この記事の攻撃ではSIPサーバ等の関連機器からアカウント情報を窃盗し、そのアカウントを利用して不正な電話の発信に利用しているとのことでした。
普段見かけないアクセスがあるとワクワクしますね。以上。