fujjima’s blog

主に備忘録

dayjsのduration.format()まだマージされていない

JS系で、時間・日時を取り扱うライブラリにdayjsというものがある。(他の有名どころだとMoment.js1とか)

day.js.org

ある時、formatを使って秒数を「HH:mm:ss」の形式で表現したくなった。 例えば、144,930秒で表現すると「40:15:30」となる。

momentだと上記のようなケースに対応したライブラリがある。

GitHub - jsmreese/moment-duration-format: Format function plugin for the Moment Duration object.

momentと似た感じで、 dayjs.duration({ seconds: time }).format('HH:mm:ss') ※timeは任意の秒数 でフォーマットできないかと思ったけど、まだdurationでformatは使えないらしい。 ただ、最近devブランチに duration().formatを使えるようにしたブランチがマージされたらしいので、これがmasterにマージされれば晴れて上記のようなフォーマットができるようになると思われる。

github.com

ちなみに、dayjs()で生成したものにシンプルにformatを使用しただけだと、24時間ごとに一日とカウントされ、24時間以上のカウントには対応できない。

const time = 144930 * 1000  // dayjs(time)のtimeはミリ秒で計算されるため、秒数をミリ秒に変換している
dayjs(time).format('HH:mm:ss')

=> 16:15:30


2021/2/11追記

どうやら上記のような対応の予定は無いらしい。 残念。

Request - Format duration days as more than 24h · Issue #1376 · iamkun/dayjs · GitHub


  1. ただし、Moment.jsは近い将来新しい開発が停止になるそう cpoint-lab.co.jp

非同期処理のデバッグ(Rails)

Railsにおける非同期処理のデバッグ方法を備忘録としてまとめた。
なお、非同期処理はSidekiqを用いて実現しているものとする。

  • gemを使って処理を止める

非同期処理中でも以下のgemを使用すれば、バックグラウンドで動いているプロセスをbinding.remote_pryで止められるらしい。(まだ試したことはない)
github.com

  • Workerを直接実行させたうえで、binding.pryを使って処理を止める

例えばTestWorkerというWorkerがあるとして、そいつを直接実行するにはrails consoleで以下を実行すればいい。(非同期処理を無理矢理同期的に実行させている)
この方法であれば、binding.pryでも処理を一時停止させることができる。

TestWorker.new.perform

Active Support一時調べ

Active Supportとは

Active SupportはRuby on Railsコンポーネントであり、Ruby言語の拡張、ユーティリティ、その他横断的な作業を担っています。

Active Supportは言語レベルで基本部分を底上げして豊かなものにし、Railsアプリケーションの開発とRuby on Railsそれ自体の開発に役立てるべく作られています。 (引用:Active Support コア拡張機能https://railsguides.jp/active_support_core_extensions.html

StringやInteger、Date、Datetimeなどのコアなクラスのメソッドを拡張してくれるもの


拡張対象

Active Supportが拡張する対象を ざっと挙げてみました(全部ではないです)

  • Object
  • Module
  • Class
  • String
  • Numeric
  • Enumerable
  • Array
  • Hash
  • Range
  • Date
  • Time
  • File
  • NameError
  • LoadError

...


全てのオブジェクトで使える拡張

  • Object#blank?
  • Object#present?

rubyにあるのはnil?empty?のみ

nil.nil?
=> true

[].empty?
=> true

''.empty?
=> true

  • Object#blank?

nil?empty?の合わせ技

nil.blank?
=> true

[].blank?
=> true

''.blank?
=> true

nil.blank?
=> true
  • Object#present?

!blank?と同じ意味

nil.present?
=> false

[].present?
=> false

[1,2,3].present?
=> true

blank?とpresent?の定義は下のURL先 https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/blank.rb


  • Object#try

おなじみtryメソッド。 レシーバがnil、もしくはメソッドが定義されていない時にはnilを返す。nilでないなら引数で渡されたメソッドを実行。

"test".try(:name)
=> nil

"test".try(:size)
=> 4
  • Object#try!

tryと違う点は、レシーバがnilならnilでなく例外を返すところ。

"test".try!(:name)
NoMethodError: undefined method `name' for "test":String

# NoMethodErrorクラスの例外が発生

  • Object#with_options

オプションを一つにまとめられる。 validatesメソッドのオプションを一つにまとめたりとかもできると思うよ。

class Company < ApplicationRecord
...
  has_many :contracts, dependent: :destroy
  has_one :billing_address, dependent: :destroy
  has_many :billings, dependent: :destroy
...
end

↓

class Company < ApplicationRecord
...
  with_options dependent: :destroy to |comp|
  comp.has_many :contracts
  comp.has_one :billing_address
  comp.has_many :billings
...
end

  • tapの補足

ブロックを渡した場合には、nilでない場合のみブロックの評価を行い、tryの返り値はブロックの評価結果となる。

array
=> [["test1", 1], ["test2", 2]]

array.try do |a|
  a.to_h
end  
=> {"test1"=>1, "test2"=>2}

array.try do |a|
  a.to_h
end.values  
=> [1, 2]


# Object#yield_selfも同様の挙動
array.yield_self do |a|
  a.to_h  
end.values  
=> [1, 2]

Classの拡張

class_attribute

継承可能なクラス属性を定義する。 以下のようなことができるようになる。

  • 子クラスで上書きが可能
  • アクセサメソッドを自動で定義
  • 子クラスで定義されていなければ親クラスの値を参照

親クラスの定数を汚染する恐れがない。インスタンスメソッド内からでもアクセスできる。

class A
  class_attribute :x
end
 
class B < A; end
 
class C < B; end
 
A.x = :a
B.x # => :a
C.x # => :a
 
B.x = :b
A.x # => :a
C.x # => :b
 
C.x = :c
A.x # => :a
B.x # => :b

# インスタンスからもアクセス・書き換え可能
# インスタンスからクラスのclass_attributeを書き換えることはない
test = B.new
test.x = :t

test.x # => :t
B.x = :b

Stringの拡張

  • 活用系

レシーバに対して何かしら操作を行う系

"tomato".pluralize
=> "tomatoes"

"katsui".pluralize
=> "katsuis"

活用系の続き

"admin_user".camelize
=> "AdminUser"

"admin_user".camelize(:lower)
=> "adminUser"

"account/financial_account".camelize
=> "Account::FinancialAccount"

# camelizeと反対の操作を行うのがunderscore


"Backoffice::UsersController".demodulize
=> "UsersController"

"::UsersController".demodulize
=> "UsersController"

Numericの拡張

  • バイト

全ての数値が以下のメソッドに対応している。

bytes
kilobytes
megabytes
gigabytes
terabytes
petabytes
exabytes
2.bytes
=> 2

2.kilobytes
=> 2048

2.megabytes
=> 2097152

  • 書式設定

指定した形式で数値をフォーマットできる

# 電話番号

1234567.to_s(:phone)
=> "123-4567"

1234567891.to_s(:phone)
=> "123-456-7891"

1234567891.to_s(:phone, area_code: true)
=> "(123) 456-7891"

// 桁区切り

12345678.to_s(:delimited)
=> "12,345,678"

pry(main)> 12345678.to_s(:delimited, delimiter: ".")
=> "12.345.678"

// 丸める
111.2345.to_s(:rounded)
=> "111.235"

111.2345.to_s(:rounded, precision: 2)
=> "111.23"

1112903.to_s(:rounded, precision: 3)
=> "1112903.000"

Enumerableの拡張

# sum
# 要素の合計値を返す
# 数値でない場合は+を使ってsumを計算する
[2,3,4].sum
=> 9

(1..10).sum
=> 55

%w(rb yml haml).sum
=> "rbymlhaml"

# many?
# collection.size > 1 の短縮系

[1,2,3,4].many?
=> true

# many?ではブロックの評価も行える
# ブロックの場合、評価結果のcollection > 1かどうか判定する
[1,2,3,4].many? { |a| a % 2 == 0 }
=> true

[1,2,3,4].many? { |a| a % 3 == 0 }
=> false

# pluck
# 指定したkeyに基づく配列を返す
[ { id: 1 }, { id: 2 }, { id: 3 } ].pluck(:id)
=> [1, 2, 3]

Hashの拡張

# deep_merge
# レシーバと引数に同じkeyが存在し、かつ値がHashの時に下位のHashをmergeする

{a: {b: 1}}.deep_merge({a: {c: 2}})
=> {:a=>{:b=>1, :c=>2}}

# exceptとexcept!
# 引数で指定したkeyがあればレシーバのHashから取り除く
{a: 1, b: 2}.except(:a)
=> {:b=>2}


# stringify_keysとsymbolize_keys
# レシーバのHashのkeyをシンボル→文字列に変換(stringify_keys)
# レシーバのHashのkeyを文字列→シンボルに変換(symbolize_keys)

{nil => nil, 1 => 1, a: :a}.stringify_keys
=> {""=>nil, "1"=>1, "a"=>:a}

{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
=> {nil=>nil, 1=>1, :a=>"a"}

まとめ

  • あなたが実装しようとしているメソッドは、既にActive Supportが提供しているかもしれないので、一旦落ち着いて探しましょう

  • 比較的Active Supportのドキュメントは読みやすく、発見も色々とあるので読んでみるといいよ!

tap, yeild_selfについて

tapとyield_selfの使い分けについて

  • tapについて

tapはレシーバ自身を返すため、元々のレシーバの値を変更することなくメソッドチェーンをつなげることができる。 他の処理を行いながら、メソッドチェーンを行いたい時などに便利。

result = []
test = [1, 2, 3]

kekka = test.tap { |t| result << t.sum }.join('/')

result
=> [6]

kekka
=> "1/2/3"
  • yield_selfについて

yield_selfはブロック内の処理結果を返すため、

他の処理を行いながら、メソッドチェーンを行いたい時などに便利。

SQL一時調べ

目次

  • SQLとは
  • 用語
  • 基本的な構文
  • Select構文
  • 句・式・文について
  • 内部結合・外部結合
  • 集計関数
  • case式
  • サブクエリ
  • Active Record
  • Active Recordとは
  • Active Recordの設計
  • Active RecordからSQLを直接発行

SQLとは

SQLは、関係データベース管理システム (RDBMS) において、データの操作や定義を行うためのデータベース言語(問い合わせ言語)、ドメイン固有言語である。プログラミングにおいてデータベースへのアクセスのために、プログラミング言語と併用されるが、SQLそのものはプログラミング言語ではない。

(引用:wikipedia https://ja.wikipedia.org/wiki/SQL


基本的な用語

  • クエリ(問合せ) データベースへ要求を出すこと

  • DML(データ操作言語) ★今日のメイン データベースにに対する操作を行う言語。SELECT(検索)、INSERT(挿入)、UPDATE(更新)、DELETE(削除)など

  • DDL(データ定義言語) データ構造を定義する言語。代表的な言語はCREATE(表領域作成)、DROP(表領域の削除)、ALTER(変更),TRUNCATE(表領域内のレコード削除)など

  • DCL(データ制御言語) トランザクションの制御を行うための言語。BEGIN、COMMIT、ROLLBACKなど


SQLの構文について

SQL構文逆引き辞典 http://www.sql-reference.com/


Select構文

テーブルからレコードを取得するための命令文。

SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] [HIGH_PRIORITY]
[DISTINCT| DISTINCTROW | ALL]
フィールド名 / 式 [ AS 別名 ], ...
[INTO {OUTFILE | DUMPFILE} 'ファイル名' オプション]
[FROM テーブル名
[WHERE 条件式]
[GROUP BY {整数 | フィールド名| 式} [ASC | DESC], ... [WITH ROLLUP]]
[HAVING 条件式]
[ORDER BY {整数 | フィールド名| 式} [ASC | DESC] ,...]
[LIMIT [オフセット,] 行指定]
[PROCEDURE 手続き名(属性一覧)]
[FOR UPDATE | LOCK IN SHARE MODE]
]

(引用:https://rfs.jp/sb/sql/s03/03-15.html


Select句の例

/* budgetsテーブルの全レコードの取得・表示 */
select * from budgets;
/* 重複の排除(distinct) */
SELECT DISTINCT id, management_item_id, value from budgets
/* 実績の金額順でソート */
SELECT id, management_item_id, value AS "金額" from budgets WHERE value > 30000 ORDER BY value;

句・式・文の区別について

image.png

句(clause)

節とも呼ぶ。文、またはクエリの構成要素。 省略可な句もある。

(例) UPDATE句、FROM句、WHERE句、EXISTS句

式(experssion)

スカラー値などを表す '1', 'katsui', trueなど

文(statement)

命令文ともいう。 一つの実行の単位となるSQL。 文の末尾には';'を付けることができる。 (例) SELECT文 UPDATE文

参考: https://qiita.com/gooddoog/items/a12d500b331f2ecf718f https://en.wikipedia.org/wiki/SQL


内部結合・外部結合

内部結合(INNER JOIN): 複数のテーブルを結合し、条件が一致したもののみを取得する。 結合条件に合わないものは弾かれる。

左外部結合(LEFT OUTER JOIN) 右外部結合(RIGHT OUTER JOIN) 完全外部結合(FULL OUTER JOIN): それぞれ、まず内部結合が行われる。その後、対象とするテーブルに対して結合できるデータをくっつける。完全外部結合は「内部結合 + 左外部結合 + 右外部結合」を行う。

/* 内部結合 */
SELECT * from base_achievements INNER JOIN management_items on base_achievements.management_item_id = management_items.id;

/* 左外部結合(実績に対して、結合条件に合う予算細目を結合し、なければNULLを入れておく) */
SELECT * from base_achievements LEFT OUTER JOIN management_items on base_achievements.management_item_id = management_items.id;

/* 右外部結合(予算細目に対して、結合条件に合う実績を結合し、なければNULLを入れておく) */
SELECT * from base_achievements RIGHT OUTER JOIN management_items on base_achievements.management_item_id = management_items.id;

集計関数について

GroupBy: GROUP BY句に記述された列のリスト(集約キー)を基準に、同じ値を持つ行を同じグループに振り分ける Having: グルーピングした結果から、Having句で指定した抽出条件を実行する

/* 実績のmanagement_idに対して単純にグループ化 */
SELECT management_item_id from base_achievements GROUP BY management_item_id ORDER BY management_item_id;

/* 実績を紐づく細目ごとにグループ化→実績値の合計を出力 */
SELECT management_item_id, sum(value) from base_achievements GROUP BY management_item_id ORDER BY management_item_id;

/* 実績を紐づく細目ごとにグループ化→実績値の合計を出力→合計金額6000000以下は弾く */
SELECT management_item_id, sum(value) AS "合計金額" from base_achievements GROUP BY management_item_id HAVING sum(value) > 6000000 ORDER BY management_item_id;

集計関数を実行する上でのメモ

  • whereとHavingとの違いについて Where句はselect句の結果からwhere句で指定した抽出条件を実行する Having句はGroupBy句でグルーピングした結果からHaving句で指定した抽出条件を実行する

  • GroupByでは、GroupByで指定したカラムしかSelect句に指定できない


case式

SQL内で条件分岐を行うための構文。 値と式による分岐があるが、値の方はあまり使わないのではないか。

/* (条件式による分岐)実績のvalueに応じて、判定結果をそれぞれ「継続」「保留」「棄却」とする) 
空文字の時には"/"を外す */
SELECT id, summary, value AS "金額",
CASE
WHEN value < 500000 THEN CASE summary WHEN '' THEN '棄却' ELSE summary || '/棄却' END
WHEN value > 1000000 THEN CASE summary WHEN '' THEN '継続' ELSE summary || '/継続' END
ELSE CASE summary WHEN '' THEN '保留' ELSE summary || '/保留' END
END "判定結果"
FROM base_achievements
ORDER BY value DESC

副問合せ(サブクエリ)

SQL文の中に入れ子SQL文を指定すること。Select文の結果を別のSQL文で利用する。 from句配下でも、where句配下でも、select句のすぐ後に使用することもできる。 from句配下で使用されるケースはイメージがつかない。

image.png (引用:https://www.atmarkit.co.jp/ait/articles/1208/06/news118.html

 


副問合せの例

/* サブクエリ:勘定科目に紐づく細目に紐づく実績のみを取得する */
SELECT id, summary, management_item_id, value AS "金額" from base_achievements WHERE management_item_id IN (
SELECT management_items.id from management_items INNER JOIN accounts on management_items.account_id = accounts.id WHERE accounts.type = 'FinancialAccount') ORDER BY base_achievements.id;

便利ではあるが、ネストしまくると途端に可読性が悪くなるやつでもあると思う。


Active Recordとは

「Active Recordパターン」というデザインパターンに由来した、SQLを意識せずにデータベースアクセスを行えるライブラリ。

→ Active RecordパターンをRubyで実装したライブラリが、Railsで使われているActive Recordライブラリ

(参考:https://www.techscore.com/tech/Ruby/Rails/other/designpattern/1/


Active Recordの設計

Active Recordパターンについて → 「データベーステーブルまたはビューの行をラップし、データベースアクセスをカプセル化してデータにドメインロジックを追加するオブジェクト」

これをRubyで実装すると下記のようなクラス設計になる。


# application_record.rb
class ApplicationRecord < ActiveRecord::Base
…
end

# ApplicationRecordを継承したbase_achievement.rb(ActiveRecord::Baseを継承したモデルクラス)
class BaseAchievement < ApplicationRecord
…
end

Active RecordでSQLを直接発行したい時

ActiveRecord::Base.connection」を使用する

con = ActiveRecord::Base.connection
con.execute("SELECT DISTINCT id, management_item_id, value from budgets WHERE value > 30000;")

// 返り値(DBアダプタ依存のオブジェクト)
#<PG::Result:0x00005627b5dd0080 status=PGRES_TUPLES_OK ntuples=11 nfields=3 cmd_tuples=11>

直接検索とかしたい時とか

  • select_all(検索結果)

これ自体の返り値はActiveRecord::Resultオブジェクトで、to_hashとかでハッシュの配列が取得できる。

con = ActiveRecord::Base.connection
con.select_all("SELECT DISTINCT id, management_item_id, value from budgets WHERE value > 30000;").to_hash

// 返り値
[{"id"=>1166, "management_item_id"=>471, "value"=>42433.0},
{"id"=>1165, "management_item_id"=>471, "value"=>34243.0},
{"id"=>1155, "management_item_id"=>474, "value"=>42342.0},
{"id"=>1173, "management_item_id"=>472, "value"=>909090.0},
{"id"=>1161, "management_item_id"=>476, "value"=>625656.0},
{"id"=>1171, "management_item_id"=>481, "value"=>34242.0},
{"id"=>1156, "management_item_id"=>474, "value"=>35252.0},
{"id"=>1174, "management_item_id"=>472, "value"=>454545.0},
{"id"=>1206, "management_item_id"=>492, "value"=>245345.0},
{"id"=>1159, "management_item_id"=>476, "value"=>55245.0},
{"id"=>1210, "management_item_id"=>480, "value"=>345345.0}]

select_oneとかselect_rowsなどいくつかメソッドがある。

参考: https://qiita.com/yut_h1979/items/4cb3d9a3b3fc87ca0435

 

業務上の備忘録(主にRails, npmパッケージ)

  • worker

アプリケーション要求サイクルの外側で実行されるプロセスのことで、非同期通信でリクエストを処理する。
Webサーバーについて話している場合、「ワーカー」という単語はスレッドまたはプロセスと同じ意味で使用される。

www.oiax.jp

  • プロセス・スレッドの違いについて

milestone-of-se.nesuke.com

  • 認証と認可の違いについて

punditを使っていて、改めて調べた。
下記サイト様が非常に分かりやすい。
dev.classmethod.jp


  • core-jsについて

babelのモジュールが読み込むpolyfile集を提供しているライブラリ。

aloerina01.github.io

postgresでテナント(schema)を切り替える際の備忘録

tableplusについて

使用したDBクライアントツールは「tableplus」 tableplus.com

無料版だと、タブが二つしか開けないのが最大のデメリットか。 SQL入力で一つは常に使用している状態なので、実質一つのタブしか自由に使えない感じ。

tableplus設定時のメモ

DBの接続する際にhostportを入力する必要があるのだが、私はこの二つに関してあまりいい思い出がないので、設定時の確認方法をメモしておく。

  • hostの確認について

ターミナルでifconfigと入力 → 色々表示されるので「en0」のところを見る → 「inet」のところに表示されているのIPアドレスを使用する。

  • portの確認ついて

自分はrailsの人間なので、DBのportの設定とかは「database.yml」を見ればいい。他の確認方法について分かったら追記していくつもり。

schema表示

コマンドは下に全部載っていた。 tableplusでの簡単なschemaの切り替えについても画像付きで載っていたので見れば分かるかと。

tableplus.com

  • 利用可能なschemaの列挙

下記の2つのSQL文で使用可能なschemaの一覧が表示できる。

SELECT schema_name FROM information_schema.schemata
SELECT nspname FROM pg_catalog.pg_namespace;