動かざることバグの如し

近づきたいよ 君の理想に

特定の文字から始まる文字だけマッチさせる正規表現

特定の文字から始まる文字だけマッチさせる正規表現を書きたい

「$」記号の後に整数が来る。例えば

¢100 makes $1

という例文があったとして、$1を抽出したい。

この場合は肯定後読み(Positive lookbehind)正規表現機能を使う必要がある。

let str = "¢100 makes $1.";
alert(str.match(/(?<=\$)\d+/))
// 結果: 1

(?<=Y)X は X の前に Y がある場合にのみマッチすることを意味する

逆に特定の文字から始まらない文字だけマッチさせる正規表現

否定後読み(Negative lookbehind)を使う。 (?<!Y)X は X の前に Y がない場合にのみマッチすることを意味する

let str = "¢100 makes $1.";
alert(str.match(/(?<!\$)\d+/))
// 結果: 100

参考リンク

Railsでmodelをフォルダ階層にしてきれいにする方法

環境

Railsのモデルディレクトリ荒れる問題

Railsはデフォルトだとapp/modelsにモデルファイルを生成する。

それはルールなので別にいいのだが、プロジェクトが成長するに従って10ならいいが50を超えてくるとかなりキツくなる。

そこでディレクトリをapp/models以下に作成して階層化したい。ディレクトリ別にグルーピングしたい

が、ググってもDB構造の階層化ばかりでフォルダを分ける記事が全然見当たらなかったのでメモ

サブディレクトリ化していない例

例えばこれは通常のPostモデル app/models/post.rb

class Post < ApplicationRecord
end

.model_name でそのモデルの命名規則などがわかる

irb(main):014:0> Post.model_name
=> 
<ActiveModel::Name:0x0000000114a6d2f8    
 @collection="posts",                     
 @element="post",                         
 @human="Post",                           
 @i18n_key=:post,                         
 @klass=Post (call 'Post.connection' to establish a connection),
 @name="Post",                            
 @param_key="post",                       
 @plural="posts",                         
 @route_key="posts",                      
 @singular="post",                        
 @singular_route_key="post",
 @uncountable=false>

まあそうなるわな

サブディレクトリ化する

まずは app/models/admin.rb を作成して以下

module Admin
end

で app/models/admin/post.rb を作成して以下

class Admin::Post < ApplicationRecord
end

これだけで後は Admin::Post でアクセス出来る

irb(main):021:0> Admin::Post.new
   (0.1ms)  SELECT sqlite_version(*)
=> #<Admin::Post:0x0000000115264830 id: nil, title: nil, date: nil, body: nil, created_at: nil, updated_at: nil>

だがこれだけではめでたしめでたしとはならない。form_withでフォームを作成するともれなくURLが想定ではなくなってバグるため。

もともとPostAdmin::Postディレクトリ作っただけなので、ルーティングやコントローラー周りは階層化するつもりはない。

しかし同様に .model_name すると route_key が「admin_posts」になってしまってるじゃないか。。。

irb(main):014:0> Post.model_name
=> 
<ActiveModel::Admin::Post:0x00000001141bd180    
@collection="admin/posts",                                            
@element="post",                                                      
@human="Post",                                                        
@i18n_key=:"admin/post",                                              
@klass=Admin::Post (call 'Admin::Post.connection' to establish a connection),
@name="Admin::Post",                                                  
@param_key="admin_post",                                              
@plural="admin_posts",                                                
@route_key="admin_posts",                                             
@singular="admin_post",                                               
@singular_route_key="admin_post",                                     
@uncountable=false>                                                   

解決方法

app/models/admin.rb にメソッドを追加する

module Admin
  def self.use_relative_model_naming?
    true
  end
end

Railsをリロードすると route_keyposts に戻っている!

irb(main):023:0> Admin::Post.model_name
=> 
<ActiveModel::Name:0x0000000117305ee0                                 
 @collection="admin/posts",                                            
 @element="post",                                                      
 @human="Post",                                                        
 @i18n_key=:"admin/post",                                              
 @klass=Admin::Post (call 'Admin::Post.connection' to establish a connection),
 @name="Admin::Post",                                                  
 @param_key="post",                                                    
 @plural="admin_posts",                                                
 @route_key="posts",                                                   
 @singular="admin_post",                                               
 @singular_route_key="post",                                           
 @uncountable=false,                                                   
 @unnamespaced="Post">                                                 

他にも色々見えないところで変わってるらしく、ただモデルの場所をフォルダ分け考えているなら全然あり

備考

ちなみにRailsのモデルを階層化しろって話はRails1.2時代から指摘されてたっぽい。ウケる

errtheblog.com

Switchゲーム映像をMacのDiscordで配信する方法

Discordで通話仲間に配信したい

ゲームしてると目の前の自分の画面を共有したいことがある。PCの画面だと画面共有押せばすぐに共有できるが、ニンテンドースイッチ自体にはその機能がないためできない。

そこでキャプチャボードを使ってPCを通すことで画面共有できるようになったのでやり方でメモ。

要件

  • ニンテンドースイッチのゲーム画面をDiscordの画面共有機能で共有できるようにする
    • ゲームの音声も共有する
    • 当たり前だが通話の音声は共有しない
  • あくまで画面共有だけで録画はしない
  • 今回はマイク(音声入力)はMacbook内蔵のマイクを使用するとする
  • PCはWindowsではなくMacbookなのでMacOSとなる

用意するもの

ニンテンドースイッチ

これがないと始まらない。自分は旧型を使用したが新型でも可能。当然映像出力機能のないスイッチライトは不可。

キャプチャボード

今回は録画機能とか要らないのでとりあえず安さ重視でコレを買った

Type-Cケーブルも(短いが)付属している点がGood

パソコン(Macbook)

これも必須。なぜならスイッチ本体に配信機能が無いため。

今回自分はM1 Macbookを使った。

アプリケーションとしては以下

  • Discord: 普段使ってるやつ
  • OBS: 配信するときの管理アプリ。Mac版も出てるしついこの間M1ネイティブ版もリリースされた

HDMIケーブル2本

普段は1本で十分だが、1本多く使うので注意

やり方

まずは構成図のように配線をする。

スクリーンショット 2022-08-08 8.47.41.png

次にOBSをダウンロードしてインストール。設定していく

OBSの設定

画面を開いたらウィザードみたいなのが表示されるが今回はOBS本体で配信、録画をしないのでキャンセルする

ここではOBSでゲームの映像、音声を扱えるようにするのがゴール。

OBS画面の左下の方にあるソースを右クリックして「映像キャプチャデバイス」を選択

1.png

そのまま「OK」をクリック

2.png

バイスをキャプチャボードのやつ(自分の場合はMiraBox Capture)にしてプリセットから「1280x720」を選択する。

これでOBSでスイッチの映像が扱えるようになった。

が、これだけだとゲームの音声が入らず無音配信になってしまうので音声も入れるようにする。

同様にソースを右クリックして「音声入力キャプチャ」をクリック

わかりやすいように「ゲームの音声」とかの名前にして「OK」クリック

バイスを同様にキャプチャボードのやつ(自分の場合はMiraBox Capture)にして「OK」をクリック

これでOBSでゲームの音声も扱えるようになった。が、イヤホンをパソコンにつけてもゲームの音が聞こえない。

これはデフォルトではゲームの音声が出力の対象外になっているため。配信では音が流れても自分が無音でゲームは流石にツラいため以下の設定を行う。

画面下の方の「音声ミキサー」ウィンドウよりゲームの音声のサブメニューボタン「︙」をクリックして「オーディオの詳細プロパティ」をクリック

すると「オーディオの詳細プロパティ」ウィンドウが表示されるので「ゲームの音声」の音声モニタリングを「モニターと出力」にする。するとゲームの音声が聞こえるはず。

確認ポイント

最終的なOBSのウィンドウは以下のようになるはず。

  • プレビュー画面にゲームの画面(例だとアオリちゃん ホタルちゃんのかわいい画像)が映っていること
  • 自分がゲームの音声を聴けてること
  • なんか喋った時に音声ミキサーのマイクの音声ゲージが動くこと

これを満たしていればOBSの設定は完璧

Discordの設定

次はDiscordの設定

悲しいことにMacOSのセキュリティ上そのままではゲームの音声を画面共有で流すことができない。そこでDiscordの拡張機能をインストールする必要がある。

まずはDiscordの設定を開き「音声・ビデオ」を選択

画面共有の欄にインストールボタンがあるのでインストール。

が、最近のMacOSだとさらにセキュリティが厳しくなっており、「現在のセキュリティ設定では、システム機能拡張のインストールは許可されていません」と言われてそのままではインストールできない。そこで以下の記事を参考にセキュリティレベルを一旦下げてインストール出来るようにする。

note.com

gori.me

最終的に以下のように「アプリケーションから音声をキャプチャする準備が整いました」と表示されればインストール完了である。

ちなみにDiscord自体の音声設定はこんな感じでOK。いつもどおりで特に画面共有するからと言って変えることは他にない。

確認ポイント

  • 画面共有の拡張機能のインストールが完了している
  • OBS同様に画面共有の権限がOKになっている

いざ配信

これでようやく配信できるが、このままOBSの画面を配信しても編集画面が映ってしまう。

MacOSの場合はWindowsみたいにOBSとDiscordが連携していいように画面を加工してくれないので、自分でゲーム画面だけのウィンドウを作成し、それをDiscordに画面共有させる必要がある。

まずはMac画面の右上のマニューバーからOBSのアイコンをクリックし、「全画面プロジェクター(番組)」を選択しディスプレイをクリック

すると全画面でゲームの画面が映し出される。が、Discordは全画面のウィンドウを画面共有できないので、ウィンドウの緑ボタンをクリックして全画面表示を解除する

で、DiscordをOBSと同じディスプレイスペースに置いた状態でDiscordの画面共有ボタンをクリックし全画面表示させたゲーム映像だけのウィンドウを選択する

「Go Live」をクリックすると晴れて配信が開始される。やったね!

が、このままだとMacのウィンドウのタイトルバーも同時に映ってしまう。なんかダサい。

そこで一度縮小化したゲームのウィンドウを再度全画面化する。こうしても1度画面共有されていれば解除されることはない。

これで相手からはゲームの映像だけが画面共有される。

はてなブログのsitemap.xmlをCIで自動送信するようにした

はてなブログのsitemap.xmlひどすぎ問題

これは昔から続いている問題なんだが、はてなブログだとインデックスが上手くいかないケースが有る。

先人の人たちが腐るほどに記事にしてくれている

karupoimou.hatenablog.com

chateau-vulpes.com

www.imaichido.com

kanaxx.hatenablog.jp

記事は消されているけど、はてな公式ですら問題を認識している。ならはよ直せよ。

GoogleSearchConsoleからのサイトマップ送信に失敗する問題について

どうもGoogle公式のAPIでインデックスの送信ができるのを知ったので自動化した。

成果物

ググったらコードあった。感謝

github.com

クローンしたのが以下

github.com

Github Actionsを以下のように作成

name: Github Action CI

on:
  push:
  schedule:
    - cron:  '57 22 * * *'

jobs:
  execute:
    name: Submit sitemap to Google
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: "Install compose packages"
        run: composer install

      - name: "Execute PHP"
        env:
          GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }}
        run: |-
          echo $GCP_CREDENTIALS | base64 -di > credential.json
          php publish_sitemap_to_indexing_api.php "https://thr3a.hatenablog.com/sitemap_periodical.xml?year=$(date +%Y)&month=$(date +%-m)"

サービスアカウントの作成

ここが結構面倒くさい

  • GCPの管理画面から「Indexing API」「Google Search Console API」のAPIを有効化する
  • API とサービス/認証情報より「サービスアカウント」を新規作成
    • ロールは「閲覧者」でOK
  • 作成したサービスアカウントを選んで認証キーを生成、JSONを保存する
  • サーチコンソールからからユーザー管理を選択
  • さっき作ったサービスアカウントのメールアドレスをユーザー追加
    • 必ず権限は「オーナー」にする

GitHub ActionsのCI設定

ダウンロードしたJSONbase64に変換してSecretsに登録する。CIからは環境変数で渡る

cat credential.json | base64

CI回して全部200ならOK

スクリーンショット 2022-08-07 16.42.26.png

スケージュール入れたので毎日自動送信されるはず

UbuntuでMariaDBを複数起動させる方法(systemd編)

環境

やりたいこと

1つのサーバーで複数のMariaDBを起動させたい。とはいえ全部のポートが3306だとコンフリクトしてしまうので

  • ポート番号
  • データ格納場所
  • プロセスファイル、ソケットファイル
  • ログパス

は別々にしたい

調べてみると色々記事は出てくるが、systemdを使用していない古い記事、mysqld_multiに依存している記事、CentOSとかArchLinuxとか仕様が違う記事等あって

Ubuntuかつモダンなsystemd版の記事がなかったのでメモ

MariaDBのインストール

ここではMariaDB公式のレポジトリを追加してインストールする

apt-get install apt-transport-https curl
curl -o /etc/apt/trusted.gpg.d/mariadb_release_signing_key.asc 'https://mariadb.org/mariadb_release_signing_key.asc'

cat > /etc/apt/sources.list.d/mariadb.list <<EOF
deb https://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb/repo/10.8/ubuntu focal main
EOF
apt install -y mariadb-server

複数起動させる方法

実は複数起動させる方法は公式ドキュメントに記載されている。

Running Multiple MariaDB Server Processes - MariaDB Knowledge Base

が、残念なことにこの記事だけだとsystemdの説明が足りなさすぎて何もできない。実際に細かく書かれているのはsystemdの記事だったりする。下記

systemd - MariaDB Knowledge Base

Ubuntuの場合、 /lib/systemd/system/mariadb@.service に必要な設定ファイルが記載されている

こんな感じで%Iが変数化されており動的に入れることが出来る。

[Unit]
Description=MariaDB 10.8.3 database server (multi-instance %I)
Documentation=man:mariadbd(8)
Documentation=https://mariadb.com/kb/en/library/systemd/
After=network.target

Environment の設定で書かれているが、環境変数 MYSQLD_MULTI_INSTANCE--defaults-group-suffix= パラメータを追加するのが大事

これは別に自分が設定しなくてもsystemd側で動的に設定してくれる。 %I ってのがミソ

# Controlling how multiple instances are separated. See top of this file.
# Note: This service isn't User=mysql by default so we need to be explicit.
# It is as an option here as a user may want to use the MYSQLD_MULTI_INSTANCE
# to run multiple versions.
Environment='MYSQLD_MULTI_INSTANCE=--defaults-group-suffix=.%I'

後ポイントの設定は ConditionPathExists

## See Environment=MYSQLD_MULTI_INSTANCE below for current recommended options.
ConditionPathExists=!/etc/mysql/mariadb.conf.d/my%I.cnf

これ、自分も最初ミスリードしてしまったがsystemdのドキュメントいわく

ConditionPathExists
If the absolute path name passed to ConditionPathExists= is prefixed with an exclamation mark ("!"), the test is negated, and the unit is only started if the path does not exist.

とあり、今回みたいに!があると /etc/mysql/mariadb.conf.d/my%I.cnf の設定があると起動しない コメント見る感じ下位互換性のための設定らしい

手順

10000、20000ポート2つのサーバーを起動しようとする例

データ格納用のディレクトリを作成する

mkdir /var/lib/mysql_{10000,20000}
chown mysql: /var/lib/mysql_{10000,20000}
mysql_install_db --datadir=/var/lib/mysql_10000 --user=mysql
mysql_install_db --datadir=/var/lib/mysql_20000 --user=mysql

MariaDBだとデフォルトだと /etc/mysql/mariadb.conf.d/50-server.cnf がサーバーの設定ファイル

以下のように追記する。 defaults-group-suffix=.%I%Iがmysqld.10000の「10000」に該当するのを注意

何故か公式で「.」がついているので mysqld10000 で動かないので注意

[mysqld.10000]
port       = 10000
datadir    = /var/lib/mysql_10000
socket     = /run/mysqld/mysqld1.sock
pid-file   = /run/mysqld/mysqld1.pid
log-error  = /var/log/mysql/error1.log

[mysqld.20000]
port       = 20000
datadir    = /var/lib/mysql_20000
socket     = /run/mysqld/mysqld2.sock
pid-file   = /run/mysqld/mysqld2.pid
log-error  = /var/log/mysql/error2.log

いざ起動

systemctl restart mariadb@10000
systemctl restart mariadb@20000

ps aux| grep mysql で確認

mysql       2544  0.0  5.0 1344868 102304 ?      Ssl  18:07   0:00 /usr/sbin/mariadbd
mysql       4636  0.8  4.8 1352680 98544 ?       Ssl  18:19   0:00 /usr/sbin/mariadbd --defaults-group-suffix=.10000
mysql       4681  1.6  4.8 1278948 98320 ?       Ssl  18:19   0:00 /usr/sbin/mariadbd --defaults-group-suffix=.20000

デフォルトだとソケットログインが出来る

root@vagrant:/etc/mysql/mariadb.conf.d# mysql -S /run/mysqld/mysqld1.sock
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.8.3-MariaDB-1:10.8.3+maria~focal mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

mysql_secure_installation でMySQLの初期設定が出来るが、デフォルトだとポート3306を参照してしまう。ポート番号を指定することが何故かできないのでソケットファイルを指定する。

export MYSQL_UNIX_PORT=/run/mysqld/mysqld1.sock

これで初期設定が出来る

mysql_secure_installation

参考リンク