「ゼロからわかるRuby超入門」を読んだ

以前から読もうと思ってAmazonの欲しいものリストに突っ込んでいた「ゼロからわかるRuby超入門」を送っていただいたので早速読んでみた。

 

www.amazon.co.jp

 

ので、雑に感想など書いてみた。

内容など

かなり初心者向けの内容でわかりやすい感じでした。

個人的に変数とかをキャラクターなどで分かりやすく見せているのは「いいなぁ」と思いました。

 

あとターミナル部分とコード部分の枠の色とか違うので、良くある入門書とかの「ターミナルの実行結果とコードのどっちも枠が同じ色で読みにくい」みたいなのは無い感じなのもうれしいかもしれない。

 

それとRubyでのコードの書き方(メソッドへの切り出しとかキーワード引数とか)からSinatraでの簡単なWebアプリ作成までが通して書かれているのもうれしいですね。

(だいたいはRubyの文法とかで終わっちゃうので「次なにやればいいんだろう」ってなっちゃう)

 

Rubyリファレンスマニュアルへの説明があったり、Ruby 3.0を使っていたりするところも良いですね。

 

今後

妻がノーコードツールとか使ってて「Hashがわからん」とか言っていたので読んでみるように薦めてみようかなと思います。

もしくは今開催できてないCoderDojo浜田とかHamada.rbでの初心者向けイベントとかで使ってみてもいいかもしれない

RustでRubyのNative Extensionを書く

はじめに

先日、Ruby Issue Trackerに以下のチケットが作成されました。

Feature #18481 Porting YJIT to Rust (request for feedback)

内容としては、昨年末にリリースされたRuby 3.1にマージされているYJITという新しいJITコンパイラをより開発しやすくするためにRustを使いたいというものです。

これはRuby(ここでのRubyはCで実装されたCRubyことMRIを意味します)で使用しているCの規格がC99であり、C99ではJITコンパイラを作成するための十分なツールがないことに端を発しています。

つまりC99でJITコンパイラを作成するには開発者自身のスキルセットなどが要求され、開発が難しい部分もあるということのようです。

その点、Rustでは強力なエラーサポートや型、メモリ安全性などがあるため開発がしやすいということかもしれません。 またほかの言語での実装の場合(たとえばGoなど)、GCなどの機能があるためそれ単体でビルドはできても別の問題を踏む可能性があり、採用が難しいところがあるようです。

こういった経緯などからYJIT自体は今後Rustで実装されていく可能性があります。

また以前にはHelixなどのようなRubyのNative ExtensionをRustで実装するというものも出ています。

そこで今回はRubyのNative ExtensionをCではなく、Rustで書くことについて解説したいと思います。

Cで書くNative Extension

CでRubyのNative Extensionを書く場合、以下のようにInit_hello()という関数内でメソッドやクラスなどを定義します。

#include <stdio.h>
#include <ruby.h>

void hello_world()
{
  printf("Hello World!");
}

void Init_hello()
{
  rb_define_global_function("hello_world", hello_world, 0);
}

このコードをビルドし、hello_world.soという共有ライブラリを作成することでNative Extensionを使えるようになります。

使う場合は以下のようにrequireを使い、作成された共有ライブラリを読み込めば自動的にInit_hello()が実行され、そこに定義されたメソッドが使えるようになります。

require 'hello_world'

hello_world()
# => "Hello World!"と出力される

Rustで書く場合

Rustで書く場合も基本的にはCで書く場合と同じです。メソッドやクラスを定義するInit_hello()を定義すればOKです。

#![allow(non_snake_case)]

extern crate libc;
use std::ffi::CString;

extern {
    fn rb_define_global_function(name: *const libc::c_char, func: extern fn(), argc: libc::c_int);
}

extern fn hello_world() {
    println!("Hello, world!");
}

#[no_mangle]
pub extern fn Init_hello() {
    let c_func_name = CString::new("hello_world").unwrap();
    let argc = 0;

    unsafe {
        rb_define_global_function(c_func_name.as_ptr(), hello_world, argc);
    }
}

あとは上記のコードをビルドして共有ライブラリを作成し、以下のようにRuby側から呼び出せばOKです。

require './hello'

hello_world()
# => "Hello World!"と出力される

なお、実装例としては以下のリポジトリを参考にしていただければと思います。

rust_hello_world_ruby_extension

そのほかのRustでのRuby Native Extension作成方法

Helix

Helixは以前RubyKaigiでも紹介されたRustで簡単にRuby Native Extensionを書くことができるライブラリです。

現在ではDeprecatedになっており、使用することは控えたほうがいいですね……。

Helixでは以下のようにメソッドなどを書くことができます。 (なお、以下のコードはHelixの公式より引用)

ruby! {
    class Console {
        def log(string: String) {
            println!("LOG: {}", string);
        }
    }
}

インスタンスメソッドや特異メソッドなどは定義できるようでしたが、メタクラスやグローバルなメソッドなどは定義できなさそうですね……。

ruru

ruruもHelixと同じくRustでのRuby Native Extensionを簡単に実装できるライブラリです。

以下のようにクラスにメソッドを実装できます。 (なお、以下のコードはruruの公式より引用)

#[macro_use]
extern crate ruru;

use ruru::{Boolean, Class, Object, RString};

methods!(
   RString,
   itself,

   fn string_is_blank() -> Boolean {
       Boolean::new(itself.to_string().chars().all(|c| c.is_whitespace()))
   }
);

#[no_mangle]
pub extern fn initialize_string() {
    Class::from_existing("String").define(|itself| {
        itself.def("blank?", string_is_blank);
    });
}

ただ、こちらは長い間メンテナンスされていないようなのであまり推奨できないですね……。

rutie

rutieもHelixなどと同じくRustでRuby Native Extensionを簡単に実装できるライブラリです。

以下のようにマクロを使い、クラスやメソッドを定義することができます。 (なお、以下のコードはrutieの公式より引用)

#[macro_use]
extern crate rutie;

use rutie::{Class, Object, RString, VM};

class!(RutieExample);

methods!(
    RutieExample,
    _rtself,

    fn pub_reverse(input: RString) -> RString {
        let ruby_string = input.
          map_err(|e| VM::raise_ex(e) ).
          unwrap();

        RString::new_utf8(
          &ruby_string.
          to_string().
          chars().
          rev().
          collect::<String>()
        )
    }
);

#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Init_rutie_ruby_example() {
    Class::new("RutieExample", None).define(|klass| {
        klass.def_self("reverse", pub_reverse);
    });
}

rutieは比較的最近もメンテナンスされているようなので、RustでRuby Native Extensionを作成する際には候補に挙げてもいいかもしれません。

FFI

Rubyの標準ライブラリにあるffiを使うことで比較的簡単にRustで一部の実装を作成することもできます。

たとえば以下のようなRustのコードは

#[no_mangle]
pub extern fn hello_world() {
  println!("Hello world!");
}

// ffi_libメソッドで共有ライブラリを読み込む際にInit関数が必要なため
#[no_mangle]
pub extern fn Init_librust_ffi() {
}

以下のようにffiを使い、呼び出すことができます。

require "ffi"

module Hello
    extend FFI::Library

    ffi_lib "./hello.so"
    attach_function :hello_world, [], :void
end

Hello.hello_world()
# => "Hello World!"と出力される

少しだけ面倒くさいのは、以下の二点です。 - module内でextendを使う必要がある - 専用のInit関数を用意する必要がある

この部分が無ければより簡単にRustでのRuby Native Extensionが実装できそうなんですが……。

実際の実装例は以下のリポジトリを参考にしていただければ幸いです。

rust_ffi_ruby_extension

Fiddle

FiddleRubyで簡単に共有ライブラリをimportし、それをもとにクラスやメソッドなどを定義することができるものです。

例えば以下のようなRustのコードを

#[no_mangle]
extern fn hello_world() {
    println!("Hello World");
}

以下のようなRubyのコードで呼び出し、実行することができます。

require 'fiddle'

libm = Fiddle.dlopen('./hello.so')

hello_world = Fiddle::Function.new(
    libm["hello_world"],
    [],
    :void
)

hello_world.call()
# => "Hello World"と出力される

Init_hello関数が不要なので具体的な処理をRustで書いて、それをRubyのclassやmodule内でメソッドとして使う形でいくのがよさそうです。

Fiddleでの実際の実装は、こちらのリポジトリを参考にしていただければと思います。

rust_fiddle_ruby_extension

おわりに

現状、RustでRuby Native Extensionを書く場合は、rutieかFiddleを使うのが楽でよさそうです。 Rust単体で書く場合は、各コードが多くなり辛くなりそうです。とはいえ、Rust単体で書く方があとあと幅広くカスタマイズが出来そうではありますね。

なので、Rust単体で書くか、ruiteまたはFiddleを使うのが今後のスタンダードになりそうです。

参考記事など

RustだけでRuby native extensionを書く Rubyのネイティブ拡張をRustで作成してgemとして公開した RustのstructをRubyのclassとして扱う Ruby/Rust 連携 (2) 手段 Ruby FFIを使ったエクステンションの作り方 Rust でつくるかんたん Ruby Gem

GitHub Actionsで自動的にマイグレーション&DBバックアップが走るようにしてみた

経緯

仕事の関係で、ステージング環境へ自動的にマイグレーションを走らせたい&DBのバックアップを自動化したい状況があり、GitHub Actionsで自動化してみました。 これはその時のアレコレをまとめたものです。

つくったもの(サンプル)

github.com

仕事で書いているのがGoだったのでGoのmigrateを使っています。

やったこと

GitHub ActionsでDBのバックアップ

以下のようにservicesMySQLのコンテナを指定しています。これはDBのバックアップを取るため必要なCLIコマンドを実行できるようにするためですね。

    name: "Auto migration"
    runs-on: "ubuntu-latest"
    services:
      image: mysql:5.7

stepsで先ほど追加したMySQLのコンテナを使い、DBのバックアップを取得しています。

      - name: backup mysql database
        run: |
          mkdir -p backup/staging
          mysqldump --skip-column-statistics --single-transaction --set-gtid-purged=OFF -u ${{ secrets.MYSQL_USERNAME }} -p${{ secrets.MYSQL_PASSWORD }} -h ${{ secrets.MYSQL_DATABASE_HOST }} ${{ secrets.MYSQL_DATABASE_NAME }} > ./backup/staging/backup_`date '+%Y%m%d%H:%M:%S'`.dump

バックアップに必要なパスワードなどはsecrets経由で追加しています。

これでbakup/stagingディレクトリ内にDBのバックアップが保存されます。

S3へとアップロード

次にjakejarvis/s3-sync-actioを使い、S3へとバックアップをアップロードします。

      - name: Push backups to S3 Bucket
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --follow-symlinks
        env:
          AWS_S3_BUCKET: ${{ secrets.AWS_BUCKET_NAME }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }}
          AWS_REGION: ${{ secrets.AWS_BUCKET_REGION }}
          SOURCE_DIR: 'backup'

同様にAWSへのアクセスキーなどはsecrets経由で渡しています。

これでS3のバケットにDBのバックアップがアップロードされます。

migrateでマイグレーション

最後にmigrateのインストールとマイグレーションを走らせます。

      - name: install migrate
        run: |
          go get -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate

      - name: running migration
        run: |
          migrate -path ./migrate -database "mysql://${{ secrets.MYSQL_USERNAME }}:${{ secrets.MYSQL_PASSWORD }}@tcp(${{ secrets.MYSQL_DATABASE_HOST }})/${{ secrets.MYSQL_DATABASE_NAME }}" up

go get経由でmigrateを入れ、マイグレーションを走らせます。

これで自動でマイグレーションとDBのバックアップを行うものができました。

参考記事など

ref: golang-migrateでDBマイグレーション ref: appleboy/database-backup-action ref: valerianpereira/backup-action ref: 【Github Actions】CIのstepでmysqlにSQLを実行する ref: jakejarvis/s3-sync-action ref: GitHub ActionsでCloud SQLのマイグレーションを自動化する

Mastodonの鯖缶を4年ほどやってみての雑感

はじめに

この記事はFediverse Advent Calendar 2021 の7日目の記事です。

adventar.org

 

2017年からMastodonサーバーを運営してみての雑感を書きたいと思い、参加しました。

 

軽く自己紹介

S.H.という名前で、Creatodonという創作系のMastodonサーバーの管理人をしています。

gamelinks007.net

 

また、Mastodonのバックエンドで使用されているプログラミング言語Rubyに趣味でパッチなどを投げてたりします。

 

その辺の話は以下の記事にまとまっていますので、そちらを読んでいただければと思います。

gamelinks007.hatenablog.com

 

4年間の振り返りなど

運営など

2017年4月のMastodonブーム以来、色んなサーバーが出来ては消えていきました。

DBが爆破しちゃったり、アップデートが上手くいかなかったり、運営に疲れたり、その理由は様々でした。

 

うちのサーバーは時々トラブルが起きたものの、幸いDBの爆破などもなく平穏でした。

またユーザーも各々が好きなことをしている感じなので大きなトラブルとかもなく(まあ、僕の観測範囲内に無いだけかもですが)緩い感じで運営できていました。

 

そのおかげか鯖缶として運営に疲れるとかそういうこともなかったですね。

 

ほかのサーバーもそうなのかはわかりませんが、あんまり気張って運営していないくらいが気楽で運営しやすいのかもしれません。

僕の観測範囲ですが、古くからあるサーバーはガチガチの運営でもなかったりするように思いますし

 

そのほかの分散SNSなど

この四年間でPleromaやMisskey、今は亡きHivewayなど様々な分散SNSが登場し、ずいぶんとFediverseは賑やかになってきたなぁと思います(2017年のころだとGNU ScoialとかMastodonとかくらい?だったと思うので)

 

その分サーバーの数も増え、人の交流も増えたと思います。

 

ただ、同時に何かしらのトラブルを見かけることも増えたように思います。

 

そのトラブルも分散SNSの仕組みのおかげか関連する投稿が流れてくることも少ないですし、ドメインブロックなどで元から断つこともできたりするので大丈夫かなと思ってたり。

 

まあ、一人のユーザーとしても、SNSを運営する管理人としても複数の選択肢が出てきたのはうれしいですね。

 

個人としての分散SNS

一個人として分散SNSを使う分にはかなり満足していますね。

特に自分の好きなように呟けるサーバーを自分で運営しているというのは大きいかなと。

 

それといくつものサーバーがあり、サーバーそれぞれのカラーも違うので自分に合っているところを探してもいいですし、または自分で建ててもよいところも好きですね。

 

いい意味で好きにできる土壌はあるなと思ってます。

 

ただ、その分自分と価値観の違う人と出会うことも多いので、衝撃を受けることもあったりします。

それがいい刺激の時もあれば、良くないときもあります。

 

そういう時にスッと距離を置きやすいのは分散SNSのいいところかなと思いますね。

 

おわりに

 

Mastodonサーバーを建ててからもう4年も経っているのかと思うと感慨深いですね……。

今後も緩く運営していきたいと思います。

rstdという趣味ライブラリを作っています

はじめに

この記事は Ruby Advent Calendar2 2021の12/6の記事です。

qiita.com

二年くらい前から趣味で作っているrstdというライブラリの紹介したいと思い、参加しました。

作っているもの

rtsdという「標準ライブラリにあると便利そうなメソッド」をRefinementsを使って実装しているライブラリを作っています。

github.com

きっかけ

Array#both_endというメソッドをbugs.ruby-langで提案したのが切っ掛けで作り始めました。

bugs.ruby-lang.org

挙動としては以下のような感じで動くと良いのではと考えてました。

# 通常のArray
ary = [ "w", "x", "y", "z" ]
ary.both_end     #=> ["w", "z"]
ary.both_end(2)  #=> [["w", "x"], ["y", "z"]

# 空のArray
[].both_end    #=> [nil, nil]
[].both_end    #=> [nil, nil]

提案したところコミッターの卜部さんから「Why not start as a gem? There are things not possible without core changes, but it seems this requested feature needs no such things.」とコメントがあり、gemで実装もできるしやってみるかとなりrtsdを作り始めました。

モチベーションなど

基本的に気の向いたときに実装をしたり、CIのメンテナンスしたりとかな感じです。 なので、一時コミットが全くなかったり……。

最近は少しずつ暇を見つけてはアレコレ便利そうなメソッドを追加したりしてます。

最終的にどういうものにしたいとか明確な目標はないですが、少なくとも「自分が使ってて便利そうなメソッドを追加する」という方針だけは維持してゆるく作りたいと思ってます。

使い方

rtsdは以下のようにrequireした後に、使用したいメソッドを定義しているModuleusingするだけです。

require "rstd"

using Rstd::RefineInteger

41.inc
# => 42

個人的に気に入っているメソッドなど

Array#has?

Array#has?は以下のような配列内に引数で渡した値があるかをチェックしてくれるメソッドです。 既に標準ライブラリではArray#include?がありますが、もう少し短く書きたかったので追加した感じです。

require 'rtsd'

using Rstd::RefineArray

ary = [1, 2, 3, 4, 5]

ary.has?(1)
# => true

ary.has?(117)
# => false

Hash#has_pair?

Hash#has_pair?は特定のキーとバリューの組み合わせを持っているかをチェックするメソッドですね。 Hash#has_key?Hash#has_value?を組み合わせて書いても良いんですが、毎回書くのも面倒だなと思い、追加しています。

require 'rstd'

using Rstd::RefineHash

hash = {jhon: 'S-117', linda: 'S-058', fred: 'S-104', kelly: 'S-104'}

hash.has_pair?(:jhon, 'S-117')
# => true

has.has_pair?(:daisy, 'S-023')
# => false

Kernel.#refine_method

Kernel.#refine_methodはクラス内でブロックとRefinementsを使って、良い感じにその場でメソッドを追加したいので実装してみました。

require 'rstd'

using Rstd::RefineKernel

using refine_method(Integer, :halo) do
  p 'Master Chief is came back!'
end

42.halo
# => 'Master Chief is came back!'

こんな感じで書けるので個人的には一番良く使っています。

おわりに

簡単ですが、趣味で作っているライブラリを紹介させていただきました。 今後、色々便利メソッドとか実装してみて標準にもあると嬉しいなというものがあれば提案していきたいと思います。

島根県浜田市での暮らしなど

はじめに

この記事は地方在住 Advent Calendar 2021の12/5の記事です。

僕も地方在住でリモートで仕事をしているので日々の生活などを振り返ってみようかと思い参加しました。

 

居住地

現在は島根県浜田市に住んでいます。もともと出身も島根県西部のほうなので生まれてこの方島根から出たことがないのが自慢です。

 

浜田市に住むきっかけは市内にある大学に進学して、そのままそこの地元企業に就職したためですね。

で、以下の記事でも触れていますが、社会人になってから本格的にプログラミング学んで現職に転職してます

 

gamelinks007.hatenablog.com

 

 

仕事など

リモートで東京のメンバーと一緒に仕事しています。

現在はCRE(Customer Reliability Engineering: 顧客信頼性エンジニアリング)というロールで顧客満足度改善のための改修などをメインにGoやTypeScript(React)で開発をしています。

 

あとは島根県浜田市の地域.rbであるHamada.rbを主催してたり、浜田市若者会議という市への提言をするグループとかに参加してITを使った街づくりとか提案したりしてます。

 

gamelinks007.hatenablog.com

 

 

gamelinks007.hatenablog.com

 

日々の暮らしなど

島根県浜田市特定第3種漁港もあるので、新鮮な魚を普段から安く買えるのがうれしいですね。たまにスーパーとかで丸ごと一尾買って自分で捌いたりもしてます。

 

特に市としても売り出しているどんちっち三魚(アジ、ノドグロ、カレイ)は非常に美味です。

 

個人的にオススメなのは「めし処ぐっさん」というところが提供しているノドグロの炙り丼です。

 

f:id:gamelinks007:20211204232244p:plain



脂たっぷりで非常に美味しいので浜田市に来られる際にはぜひ試してみてください。

 

 

www.kankou-hamada.org

 

あと、浜田市は日本酒の酒蔵もあり、美味しい海鮮と合わせて日本酒も楽しめるのでお酒好きの方にもおすすめです。

 

shimane-sake.or.jp

 

あと島根自体は酒蔵も多く、天隠とか七冠馬とか玉鋼など美味しい日本酒も多いので最近はその辺を晩酌で楽しんでたりします。

 

それと新型コロナウイルスの影響で行けていませんが、温泉街もあるのでそこへたまに疲れをいやしに行ってたりもしました。

 

www.kankou-hamada.org

 

PCで長時間コード書いたりしてると肩こりとかも酷くなるので、温泉に気軽に行けるのは助かってました。

最近行けてないので、どこかのタイミングで行きたいなぁ……。

 

おわりに

とりあえず、雑に書いてますが現状浜田市での生活に大きな不満はないですね。

美味しいご飯と日本酒とか楽しめてて、温泉とかもあるのでだいぶ楽しく生活してます。

 

少しだけ難点があるとすると映画館で映画を見るのに広島か出雲まで行かないといけないのは辛いですね……。

そこらへんが解消されたら嬉しいですが、どうだろうなぁ……。

近況報告という名の振り返り

はじめに

この記事はRubyist近況 Advent Calendar2021(2スレ目)の12/3の記事です。

近況報告の体で今年一年を振り返ってみようかなと思い、参加してみました。

 

この一年でやってたこと

お仕事

 

お仕事のほうではCREというポジションに就き、Go/TypeScriptを使い自社サービスの改善などをガンガン進めることになりました。

 

ちなみにCREについては以下の記事が詳しいです。

engineering.mercari.com

 

で、お仕事として以下のようなことをやってたりしました。

 

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

 

基本的にGo/TypeScriptばかりですが、Zoomの動画を文字起こしするのだけは個人的な趣味でRubyを使ってたりします。

 

あと最近はリリースの対応とかを率先してやるようにしていて、改善のサイクルをガンガン回せるように気を付けてたりします。

 

浜田市での活動とか

現在、島根県浜田市に住みつつリモートで仕事をしています。

で、隙間時間とかを使って高校生にプログラマーの仕事について話したり、便利そうなLINE botとかを作ってたりしてます。

 

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

gamelinks007.hatenablog.com

 

CRubyへのPRとか

去年もCRubyに色々PR投げたりしてたんですが、今年はよりアレコレ投げてた感じですね。

コミットなどは以下にまとまってます。

 

github.com

 

個人的には以下の「RubyRuby自体を実装してパフォーマンスが向上した」系のPRがマージされたのがうれしいですね。

 

github.com

github.com

github.com

 

来年もゆるく頑張っていきたいと思います。

Hamada.rb

Hamada.rbという地域.rbのオーガナイザーもやってまして、今年で無事丸二年たちました。

hamadarb.connpass.com

 

ちなみに今確認したらRuby Hacking Challenge in Hamada.rbもはじめてから丸二年たってたみたいです。

こちらはCRubyの内部実装読んだり、最近出たチケットの話とかをしてます。

 

元々はクックパッドで開催されていたRuby Hack Challenge の資料とか使わせてもらいつつ、開催してました。

rhc.connpass.com

 

ある時から、CRubyの実装を読んだりしてる人が集まる感じになってきたのでCRubyの実装を読んだりする感じにシフトしていった感じですね。

ちなみに、今年Rubyに投げたPRとかもこのイベントの時に作ってたりしました。

 

今後の目標としては浜田Ruby会議とかやりたいなと思ってます。

 

おわりに

近況報告という体で一年振り返ってみたんですが、まあアレコレやってたなという印象。

来年も引き続きやっていきたいですね。