ためすう

Rails5 で validate の on オプションを使う

2019-06-13

やったこと

Rails5 で validate の on オプションを使います。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

調査

app/models/task.rb

class Task < ApplicationRecord
  validates :name , uniqueness: true, on: :task_setup
end

task.name に “name1” で1件登録します。

$ rails c
Running via Spring preloader in process 23605
Loading development environment (Rails 5.2.3)
irb(main):001:0> a1 = Task.new
=> #<Task id: nil, created_at: nil, updated_at: nil, name: nil>
irb(main):002:0> a1.name = "name1"
=> "name1"
irb(main):003:0> a1.save
   (0.1ms)  begin transaction
  Task Create (0.5ms)  INSERT INTO "tasks" ("created_at", "updated_at", "name") VALUES (?, ?, ?)  [["created_at", "2019-06-12 12:50:53.647258"], ["updated_at", "2019-06-12 12:50:53.647258"], ["name", "name1"]]
   (0.8ms)  commit transaction
=> true

save メソッドに context で指定すると、保存に失敗します。

irb(main):005:0> a2 = Task.new
=> #<Task id: nil, created_at: nil, updated_at: nil, name: nil>
irb(main):006:0> a2.name = "name1"
irb(main):007:0> a2.save(context: :task_setup)
   (0.1ms)  begin transaction
  Task Exists (0.2ms)  SELECT  1 AS one FROM "tasks" WHERE "tasks"."name" = ? LIMIT ?  [["name", "name1"], ["LIMIT", 1]]
   (0.1ms)  rollback transaction
=> false

context を指定せずに save すると、保存できました。

irb(main):008:0> a2.save()
   (0.1ms)  begin transaction
  Task Create (0.4ms)  INSERT INTO "tasks" ("created_at", "updated_at", "name") VALUES (?, ?, ?)  [["created_at", "2019-06-12 12:52:31.539434"], ["updated_at", "2019-06-12 12:52:31.539434"], ["name", "name1"]]
   (0.8ms)  commit transaction
=> true

参考

rspec の let と let!

2019-06-13

やったこと

rspec の let と let! の違いについて調べてみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep rspec-rails
rspec-rails (3.8.2)

調査

  • let は遅延評価
  • let! はテスト前に実行される

let を使う

spec/models/task6_spec.rb

require 'rails_helper'

RSpec.describe Task, type: :model do
  let(:user) { create(:user) }
  let(:task) { create(:task) }

  it 'test1' do
  end
end

出力結果

$ rspec spec/models/task6_spec.rb
.

Finished in 0.01818 seconds (files took 1.66 seconds to load)
1 example, 0 failures

let! を使う

spec/models/task6_spec.rb

require 'rails_helper'

RSpec.describe Task, type: :model do
  let!(:user) { create(:user) }
  let!(:task) { create(:task) }

  it 'test1' do
  end
end

出力結果

$ rspec spec/models/task6_spec.rb
F

Failures:

  1) Task test1
     Failure/Error: let!(:user) { create(:user) }

     KeyError:
       Factory not registered: "user"
     # ./spec/models/task6_spec.rb:7:in `block (2 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # KeyError:
     #   key not found: "user"
     #   ./spec/models/task6_spec.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.00288 seconds (files took 1.65 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/models/task6_spec.rb:10 # Task test1

参考

Ruby で each_with_object を使う

2019-06-12

やったこと

Ruby で each_with_object を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

調査

test.rb

scores = { 'Carol' => 90, 'Alice' => 50, 'David' => 40, 'Bob' => 60 }
names = scores.each_with_object([]) do |(key, val), arr|
  arr << val * 2 if val >= 60
end

p names

arr に値を入れていくイメージです。

出力結果

$ ruby test.rb
[180, 120]

参考

Rails5 で escape_javascript を使う

2019-06-12

やったこと

Rails5 で escape_javascript を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

調査

例えば、下記の view ファイルに書いてみます。

app/views/tasks/new.html.erb

<span>
<%= escape_javascript("111
222") %>
</span>
<span>
  <%= escape_javascript("<script>alert(1111);</script>") %>
</span>
<span>
<%= "333
222" %>
</span>

出力結果

<span>
111\n222
</span>
<span>
  &lt;script&gt;alert(1111);&lt;\/script&gt;
</span>
<span>
333
222
</span>

改行もエスケープされてます。

参考

Rails5 の css で画像を設定する

2019-06-11

やったこと

Rails5 の css で画像を設定します。

今回 production 環境で試します。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

調査

画像を下記に配置します

app/assets/images/sample.png

app/views/tasks/new.html.erb

<div id="main">
  dddd
</div>

app/assets/stylesheets/tasks.scss

#main {
  background-image: image-url('sample.png')
}

config/environments/production.rb

# config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
config.public_file_server.enabled = true

↑ Webサーバから静的ファイルを読み込むことができるようにします。

$ RAILS_ENV=production bin/rails assets:precompile

↑ プリコンパイルして、ファイルを作成します。

参考

Rails5 で自作ミドルウェアを作ってみる

2019-06-11

やったこと

Rails5 で自作したミドルウェアを読み込んで使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

調査

現在のミドルウェア

$ bin/rails middleware
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
run Sample2::Application.routes

lib/middlewares/my_middleware.rb

class MyMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)

    if env['PATH_INFO'] == '/tasks/new'
      Rails.logger.debug('path is /tasks/new')
      res = @app.call(env)
    else
      Rails.logger.debug('path is other')
      res = @app.call(env)
    end

    res
  end
end

config/application.rb

require './lib/middlewares/my_middleware'
...
module Sample2
  class Application < Rails::Application
    ...
    config.middleware.use ::MyMiddleware
  end
end
$ bin/rails middleware | grep My
use MyMiddleware

参考

Ruby でインターフェースクラスを作る

2019-06-10

やったこと

Ruby では下記のようなインターフェースがありません。

PHP: オブジェクト インターフェイス - Manual

そこで、Ruby でも似たようなことを実現する方法を試してみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

調査

継承で実現する

main.rb

class MyInterface
  def hoge
    raise NotImplementedError.new("#{self.class}##{__method__} を実装してください")
  end
end

class SampleFailed < MyInterface
end

class SampleSuccess < MyInterface
  def hoge
    p "#{self.class}##{__method__}が実装されている!"
  end
end

SampleSuccess.new.hoge
SampleFailed.new.hoge

出力結果

$ ruby main.rb
"SampleSuccess#hogeが実装されている!"
Traceback (most recent call last):
	1: from main.rb:17:in `<main>'
main.rb:3:in `hoge': SampleFailed#hoge を実装してください (NotImplementedError)

Module で実現する

main2.rb

module MyInterface
  def hoge
    raise NotImplementedError.new("#{self.class}##{__method__} を実装してください")
  end
end

class SampleFailed
  include MyInterface
end

class SampleSuccess
  include MyInterface

  def hoge
    p "#{self.class}##{__method__}が実装されている!"
  end
end

SampleSuccess.new.hoge
SampleFailed.new.hoge

出力結果

$ ruby main2.rb
"SampleSuccess#hogeが実装されている!"
Traceback (most recent call last):
	1: from main2.rb:20:in `<main>'
main2.rb:3:in `hoge': SampleFailed#hoge を実装してください (NotImplementedError)

参考

Rails5 で System Spec を使ってみる

2019-06-10

やったこと

System Spec を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep rspec-rails
rspec-rails (3.8.2)

調査

System Spec とは

Rails の SystemTestCase を Rspec から利用できるようにしたもの

SystemTestCase は E2E テストを実行することができます。

使ってみる

ファイル準備

$ mkdir spec/system
$ touch spec/system/tasks_spec.rb

app/views/tasks/new.html.erb

<button onclick="if (confirm('ok???')) { alert('pushed ok!!!'); }">
  テストボタン
</button>

spec/system/tasks_spec.rb

require 'rails_helper'

RSpec.describe 'Tasks', type: :system do
  it 'js test' do
    visit '/tasks/new'
    click_button('テストボタン')
    expect(page.driver.browser.switch_to.alert.text).to eq 'ok???'
    page.accept_confirm
    expect(page.driver.browser.switch_to.alert.text).to eq 'pushed ok!!!'

    page.accept_alert
  end
end

System Spec を実行

$ rails spec spec/system/tasks_spec.rb

参考

JavaScript の encodeURI と encodeURIComponet を使ってみる

2019-06-09

やったこと

JavaScript の encodeURI と encodeURIComponet を使ってみます。

確認環境

navigator.userAgent
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"

調査

encodeURI を使う

encodeURI("/hogehoge/index?a=22&b=ああ")
"/hogehoge/index?a=22&b=%E3%81%82%E3%81%82"

エスケープされない文字: A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ‘ ( ) #

encodeURIComponent を使う

encodeURIComponent("/hogehoge/index?a=22&b=ああ")
"%2Fhogehoge%2Findex%3Fa%3D22%26b%3D%E3%81%82%E3%81%82"

encodeURIComponent は次を除く全ての文字をエスケープします : アルファベット、10進数字、- _ . ! ~ * ‘ ( )

encodeURIComponent は、URI の構成要素となる文字列 をエスケープするものです。

参考

jq を使って json を整形してみる

2019-06-09

やったこと

jq コマンドを使って、json を見やすいように整形してみます。

確認環境

Mac High Sierra (10.13.6)

調査

jq のインストール

$ brew install jq

json を出力する

jq なし

$ echo '{"root":[{"group1": [1]},{"group2": [3,4,5]}]}'
{"root":[{"group1": [1]},{"group2": [3,4,5]}]}

読みにくいですね。

jq あり

$ echo '{"root":[{"group1": [1]},{"group2": [3,4,5]}]}' | jq
{
  "root": [
    {
      "group1": [
        1
      ]
    },
    {
      "group2": [
        3,
        4,
        5
      ]
    }
  ]
}

階層が分かりやすくなりました。

ちなみに json が壊れてたりすると、下記のようなエラーが出ます。

$ echo '{"root"' | jq
parse error: Unfinished JSON term at EOF at line 2, column 0

参考