コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

Gemfile.lock内にはbundlerのバージョンをトラッキングする記述がある

なんだろうこれと思って調べたのでメモ

環境

$ bundler -v
Bundler version 1.17.2

どういう記述か

Gemfile.lockに以下の記述がある

BUNDLED WITH
   1.17.2

bundle install したときのbundlerのバージョンが記憶される。

アップデートしないといけない例

Gemfile.lockと剥離がある場合、例えばbundlerが1.17.2の状態で

BUNDLED WITH
   2.0.1

という記述の場合は以下のようなアップデートをせよというエラーが出る

/usr/local/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': Could not find 'bundler' (2.0.1) required by your /bear/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.0.1`
# 以下略

参考リンク

Railsのroutesはスクリプトを書いて凝った記述でフィルタリングすることができる

へー、こんな書き方ができるんだというメモ

環境

$ bin/rails -v
Rails 5.2.2.1

やり方

Rails のルーティング - Rails ガイド より引用

routes.rbには以下のように書く

Rails.application.routes.draw do
  get '*path', to: 'blacklist#index', constraints: BlacklistConstraint.new
end

そして以下のようなクラスをつくる、置き場はapp以下のお好みの場所で。

class BlacklistConstraint
  def initialize
    @ips = Blacklist.retrieve_ips
  end
 
  def matches?(request)
    @ips.include?(request.remote_ip)
  end
end

と記述するとmatches?の戻り値がtrueだったら該当のルーティングが発動されるという仕組み。matches?は予め指定されているので能動的に指名するものではない。

個人的な考え方のメモ

  • リクエストの中身を精査して見ることができるので表現の幅が広がる
  • 一方、増えすぎるとroute.rbだけでは完結しないという読みにくさもでる
  • 一長一短、かつチームの練度によるので使う方針は精査したほうがいいかも

参考リンク

クエリストリングで配列を表現をするケースをざっと調べる

これどこだと通用するの?と思ったので軽く調べる

TL;DR

PHPが言語デフォルトで相互変換でき、Railsだとそれに親しいことができる機能がある。

対象とする表記

下記のようなクエリストリング

http://example.com/home?values%5B0%5D=zero&values%5B1%5D=one&values%5B2%5D=two

URIエンコードしてるから見づらいが、デコードするとこんなクエリ

values[0]=zero&values[1]=one&values[2]=two

添字を指定して配列っぽい記述だけど、配列?みたいな記述

そもそもクエリストリングの仕様に配列の言及があるのか

そもそもとして ? 以降のフォーマットに関してどういう値設計にするかのルールは特に記述されていない

しかし、query 構成要素はしばしば "key=value" の対の形式で識別するための情報を運ぶために使用され、そこで頻繁に使用された値は別の URI の参照なので、時にはそれらの文字をパーセントエンコーディングする事を避けるほうがユーザビリティのためにはよい。 - Uniform Resource Identifier (URI): 一般的構文

とあるように一般論として "key=value" の形式であるぐらいでしかRFC上は語られていない

PHPでの振る舞い

たぶんこれが基準じゃないかと思われるPHP

http_build_query

PHP: http_build_query - Manual

$data = ["values" => ["zero","one","two"]];
echo(http_build_query($data));

とやると

values%5B0%5D=zero&values%5B1%5D=one&values%5B2%5D=two

と出力される

parse_str

PHP: parse_str - Manual

parse_str("values%5B0%5D=zero&values%5B1%5D=one&values%5B2%5D=two",$output);
var_dump($output);

と戻すと

array(1) {
  ["values"]=>
  array(3) {
    [0]=>
    string(4) "zero"
    [1]=>
    string(3) "one"
    [2]=>
    string(3) "two"
  }
}

ちなみに後述するRubyが変換した添字の抜けたkey値が抜けている場合は、先頭から順に0,1,2と添字が振られていく

# values[]=zero&values[]=one&values[]=two
# 添字がないケース

parse_str("values%5B%5D=zero&values%5B%5D=one&values%5B%5D=two",$output);
var_dump($output);
array(1) {
  ["values"]=>
  array(3) {
    [0]=>
    string(4) "zero"
    [1]=>
    string(3) "one"
    [2]=>
    string(3) "two"
  }
}

途中で値入れると後続が連番になる。

# 途中に2がはいる
# values[]=zero&values[2]=one&values[]=two
parse_str("values%5B%5D=zero&values%5B2%5D=one&values%5B%5D=two",$output);
var_dump($output);
array(1) {
  ["values"]=>
  array(3) {
    [0]=>
    string(4) "zero"
    [2]=>
    string(3) "one"
    [3]=>
    string(3) "two"
  }
}

Railsでの振る舞い

to_query

これも似た動作をする

query_hash = {"values": ["zero","one","two"]}
query_hash.to_query
# => "values%5B%5D=zero&values%5B%5D=one&values%5B%5D=two"

上のものをデコードすると下記、添字が無い。

values[]=zero&values[]=one&values[]=two

Rack::Utils.parse_nested_query

Rack::Utils.parse_nested_query("values%5B%5D=zero&values%5B%5D=one&values%5B%5D=two")
# => {"values"=>["zero", "one", "two"]}

[] は配列として認識して戻す。

PHPエンコードしたときの文字列は添字情報がキーとなった連想配列(Hash)になる

Rack::Utils.parse_nested_query("values%5B0%5D=zero&values%5B1%5D=one&values%5B2%5D=two")
# => {"values"=>{"0"=>"zero", "1"=>"one", "2"=>"two"}}

Goの振る舞い

.Query()

【Go】URLクエリパラメータをパースする - Qiita

クエリパースをする機能だが、ネストの配列としては見てくれない

package main

import (
    "fmt"
    "log"
    "net/url"
)

func main() {
    u, err := url.Parse("https://example.org/?values%5B0%5D=zero&values%5B1%5D=one&values%5B2%5D=two")
    if err != nil {
        log.Fatal(err)
    }
    q := u.Query()
    fmt.Println(q["values[0]"])
}

"values[0]" という感じで記述しないと取り出せない

Python3での振る舞い

urllib.parse.parse_qs

PythonでURLのクエリ文字列(パラメータ)を取得・作成・変更 | note.nkmk.me

import urllib.parse

convertd_dictionary = urllib.parse.parse_qs("values%5B0%5D=zero&values%5B1%5D=one&values%5B2%5D=two")
print(convertd_dictionary)
# {'values[0]': ['zero'], 'values[1]': ['one'], 'values[2]': ['two']}

Goと同じく value[0] という文字列がキーになるので意図とズレる

他の言語

ざっと調べた見た感じよしなに作ってくれる実装はなさそう

参考リンク

204 No Content は Body部に何も入らないのが仕様

意図的に入れてもダメなんだっけ?というのを調べただけ

出典元

204 応答は、メッセージ本体を包含できない。 そのため、ヘッダ節の後の最初の空行で終了される。 - RFC 7231 — HTTP/1.1: Semantics and Content - 6.3.5. 204 (No Content) (日本語訳)

懸念ポイント

  • 仕様として打ち切ることが約束されているので、意図的にBody部にセットしても抜ける
  • もし何か伝える要件があればヘッダなどを使う

関連リンク

Faradayで普通のフォームのようなPOSTする

すげー当たり前だけど、Faradayがらみは application/json の事例しかないので改めて書いてみる

環境

faraday (0.15.4)

考え方

POST リクエストは、ふつう HTML フォームを介して送信され、サーバーに変化をもたらします。この場合、 <form> 要素の enctype 属性もしくは、 <input> 又は <button> 要素の formenctype 属性に適切な文字列を設定することでコンテンツタイプを選択します。 application/x-www-form-urlencoded: キーと値は、その間に '=' がある形でキーと値の組になり、 '&' で区切られてエンコードされます。キーや値の英数字以外の文字は、パーセントエンコーディングされます。このため、このタイプはバイナリデータを扱うのには向きません (代わりに multipart/form-data を使用してください) - POST - HTTP | MDN

なので普通に application/x-www-form-urlencoded をcontent_typeに加えて、URLエンコードしたものを含めればいい

url = "https://example.com"
params = {page: 10}

Faraday.new.post do |req|
  req.headers["Content-Type"] = "application/x-www-form-urlencoded"
  req.url url
  # ActiveSupportの to_query を使って横着をしている
  req.body = params.to_query
end

という形でやれば実現はできる

参考リンク

郵便番号は必ず1つの住所文字列が導き出せるわけではない

知っておくと実装時に困らないトリビア

調べ元

郵便番号検索 - 日本郵便

1つだけの例

普通のケース、たとえば 263-0013

千葉県千葉市中央区中央

のみ

複数の例

321-0951 の場合

栃木県宇都宮市越戸
栃木県宇都宮市越戸町

という2つのケースがある

他にも

  • 3件以上あるケース
  • 市区町村が異なるケース
  • 県が異なるケース

がある

調査例

調べている人がいた

郵便番号の重複件数調査、都道府県別

参考リンク

Railsにおける数値のto_sバリエーションをざっとまとめる

いろいろあるけど項目ごとにざっと見れるやつってあまりないので書いてみる

環境

$ bin/rails -v
Rails 5.2.2.1

種類

電話番号

1234567890.to_s(:phone)
#=> "123-456-7890"

(当たり前だけど)0始まりは数値だと許容しないからできない

0123456789.to_s(:phone)
SyntaxError: (eval):2: Invalid octal digit
0123456789.to_s(:phone)
^~~~~~~~~

通貨

1234567890.to_s(:currency)
#=> "$1,234,567,890.00"

localeで国の指定が可能、日本は円

1234567890.to_s(:currency, locale: :ja)
=> "1,234,567,890円"

パーセンテージ

1が100%とかそういうExcelみたいな換算ではなく、純粋にパーセント表記に変える

1.to_s(:percentage)
#=> "1.000%"

小数点以下が不要な場合は :precision で指定可能

100.to_s(:percentage, precision: 0)
#=> "100%"

区切り

3桁くぎりの表現

1234567890.to_s(:delimited)
#=> "1,234,567,890"

区切り文字は指定できる。

1234567890.to_s(:delimited,delimiter: '/')
#=> "1/234/567/890"

数値丸め

デフォルトは小数点以下3桁以降は丸め

12345.67890.to_s(:rounded)
#=> "12345.679"

丸め位置は調整可能

12345.67890.to_s(:rounded, precision: 1)
#=> "12345.7"

ただしマイナス表現は不可

12345.67890.to_s(:rounded, precision: -1)
ArgumentError: negative argument

バイト表現

1234567890.to_s(:human_size)
#=> "1.15 GB"

ちゃんと1000を指定すると1000Byteになる

1000.to_s(:human_size)
#=> "1000 Bytes"

1024.to_s(:human_size)
#=> "1 KB"

1025.to_s(:human_size)
#=> "1 KB"

英語表記

thousandとか、hundredはない

1000.to_s(:human)
#=> "1 Thousand"

100.to_s(:human)
#=> "100"

参考リンク