2021年8月31日火曜日

LSP solargraph で rubygems にある gem のコード補完を行う方法

LSP solargraph で rubygems にある gem のコード補完を行う方法

概要

デフォルトでは Rubygems からインストールしたライブラリなどは補完の対象になっていません
今回は Gemfile などを使ってインストールしたライブラリの補完を solargraph を使って行う方法を紹介します

環境

  • macOS 11.5.2
  • emacs 27.1
  • solargraph 0.43.0
  • Ruby 3.0.1p64

bundler 配下で solargraph を管理する

前回はグローバルに solargraph をインストールしました
すでにプロジェクトがありそれが bundler で閉じている場合、グローバルの solargraph ではなく bundler 配下の solargraph を使います

  • cd /path/to/workspace
  • vim Gemfile
gem "solargraph"
gem "yard"
  • bundle install

yard ドキュメントの生成

ポイントは yard ドキュメントを生成する必要がある点です
solargraph をインストールすると yard の gem も同時にインストールされておりこれを使って yard ドキュメントを生成しましょう

初回生成時は時間のかかる場合があります

  • cd /path/to/workspace
  • bundle exec yard gems

vendor 配下の各 gem に .yardoc ファイルが生成されていれば OK です

生成されたパスを調べたい場合は --debug オプションを使います

  • bundle exec yard gems --debug

グローバルな solargraph の停止

もしすでにグローバルにインストールした solargraph が起動している場合は停止しましょう

emacs から自動起動する設定が記載されている場合は一旦その行をコメントして emacs を再起動しましょう

eglot の起動

emacs 側から接続しにいきます
ここでもポイントがあり eglot から solargraph を起動する場合に bundler 配下の solargraph を起動するようにします

なのでまず emacs のバッファのワークディレクトリを変更します

  • M-x cd
    • /path/to/workspace に移動する

そして bundle exec 経由で solargraph を起動すれば OK です

  • C-u M-x eglot
    • bundle exec solargraph socket --port :autoport:

これで「[eglot] Connected! Server EGLOT (pjct-name/ruby-mode) now managing ‘ruby-mode’ buffers in project ‘pjct-name’.」となれば接続完了です

動作確認

サードパーティの gem にカーソルを合わせるとちゃんと補完が表示されます

最後に

ポイントは以下の2点です

  • bundle exec yard gems で bundler 配下にインストールした gem のドキュメントを生成する
  • グローバルの solargraph ではなく bundler 配下にインストールした solargraph を eglot から起動する

のがポイントです

おまけ: 対象クラスのスキャン

  • cd /path/to/workspace
  • solargraph scan -v

おまけ: 設定ファイルの作成

  • cd /path/to/workspace
  • solargraph config

参考サイト

2021年8月30日月曜日

Emacs で Ruby の LSP をインストールする方法

Emacs で Ruby の LSP をインストールする方法

概要

Ruby で LSP に入門してみました
今回はサーバ側は solargraph という LSP 実装を使いクライアント側は eglot というツールを使います

環境

  • macOS 11.5.2
  • emacs 27.1
  • solargraph 0.43.0
  • Ruby 3.0.1p64

solargraph のインストール

まずはサーバ側のインストールを行います
Ruby を実装されているので gem を使うと簡単です

  • gem install solargraph

eglot のインストール

次にクライアント側 (emacs) の設定になります
package-list-packages から eglot を選択してインストールします

.emacs の設定

ruby-mode にフックするのが一番簡単です

(require 'eglot)
(add-hook 'ruby-mode-hook 'eglot-ensure)

動作確認

emacs を起動して ruby のファイルを開きましょう
eglot が自動的に起動して接続できれば OK です

補完を使うには Ctrl + Alt + i を押せば現在のカーソル上で補完可能な候補を表示してくれます
もし helm などを使っている場合は helm 上で候補を絞ることができます

最後に

引数などのオブジェクトの補完はデフォルトだとやってくれませんでした

また flymake を使って構文チェックを行ってくれるのですが自分の設定が悪いのか Wait という状態になり動作しませんでした
すべてにいろいろと設定済みの emacs 上だとそれらと競合してうまく動かない部分が出てくるのかもしれません

トラブルシューティング

eglot 起動時に Could not start and connect to server が発生する場合は emacs から solargraph の起動がうまくいっていません

emacs を開いているバッファ上で solargraph コマンドが実行できるか確認しましょう PATH など変更している場合は一度 emacs とターミナルを閉じて再度 PATH を読み込ませるとうまく起動できます

参考サイト

2021年8月27日金曜日

EC2 の t2.micro を立てる

EC2 の t2.micro を立てる

概要

AWS の EC2 で t2.micro を構築する方法を紹介します

環境

  • macOS 11.5.2
  • AmazonLinux2

事前作業

事前に以下のリソースを作成しておきましょう

  • キーペアの作成とダウンロード
  • セキュリテイグループの作成
  • VPC の作成

インスタンス作成

今回はウィザードを使ってインスタンスを作成します
右上の「インスタンスを起動」をクリックします

イメージ選択

まずはイメージを選択します
今回は AMI を選択しました

インスタンスタイプの選択

次にインスタンスタイプの選択をします
丁寧に無料枠とわかるようになっているので t2.micro を選択します

ストレージ選択

30GB まで無料で使うことができます
インスタンスを1台しか構築しないのであれば30GB使うように変更しましょう

セキュリティグループ選択

デフォルトのセキュリティグループは事前に作成したセキュリティグループになっていないので変更します

キーペアの設定

キーペアは右下の「起動」ボタンを押したあとに設定します
事前に作成しておいたキーペアを設定しましょう

動作確認

あとは作成できるまで待ちます そして ssh できるか確認してみましょう

  • ssh -i ssh -i /path/to/key/ec2.pem ec2-user@xxx.xxx.xxx.xxx

IP の部分は EC2 の一覧で確認することができる「パブリック IPv4 アドレス」or「パブリック IPv4 DNS」を指定しましょう
再起動すると IP が変わることを考えると DNS を指定するほうが無難です

任意: ElasticIP の設定

もしサーバとして公開するのであれば ElasticIP を設定しましょう
設定しないと起動するたびに IP アドレスが変わってしまうので DNS との紐付けができません

任意: 請求アラートの設定

無料枠を超えた場合や指定の金額を超えた場合にメールで通知してくれます
可能であれば設定しておきましょう

最後に

無料枠なので起動しっぱなしでも料金は取られません
がネットワーク料金は発生する可能性があるのでセキュリティグループでしっかりとアクセスを絞るようにしましょう
また apt install や docker pull/push などを実行してもネットワーク料金が発生する可能性があるので使いすぎには注意しましょう

参考サイト

2021年8月26日木曜日

Airflow 超入門

Airflow 超入門

概要

Apache Airflow は Python 製のジョブスケジューラです
今回はインストール方法と簡単な使い方を紹介します

環境

  • macOS 11.5
  • docker 20.10.7
  • Airflow 2.1.2

docker-compose ファイルのダウンロード

  • curl -LfO 'https://airflow.apache.org/docs/apache-airflow/2.1.2/docker-compose.yaml'

データ保存用のディレクトリ作成

  • mkdir ./dags ./logs ./plugins

env ファイルの作成

  • echo -e "AIRFLOW_UID=$(id -u)\nAIRFLOW_GID=0" > .env

初期化

  • docker-compose up airflow-init

これで Postgres と redis が起動します

起動

  • docker-compose up -d

これで Airflow が起動します

  • docker-compose ps
           Name                          Command                   State                     Ports
----------------------------------------------------------------------------------------------------------------
airflow_airflow-init_1        /usr/bin/dumb-init -- /ent ...   Exit 0
airflow_airflow-scheduler_1   /usr/bin/dumb-init -- /ent ...   Up (unhealthy)   8080/tcp
airflow_airflow-webserver_1   /usr/bin/dumb-init -- /ent ...   Up (healthy)     0.0.0.0:8080->8080/tcp
airflow_airflow-worker_1      /usr/bin/dumb-init -- /ent ...   Up (unhealthy)   8080/tcp
airflow_flower_1              /usr/bin/dumb-init -- /ent ...   Up (healthy)     0.0.0.0:5555->5555/tcp, 8080/tcp
airflow_postgres_1            docker-entrypoint.sh postgres    Up (healthy)     5432/tcp
airflow_redis_1               docker-entrypoint.sh redis ...   Up (healthy)     0.0.0.0:6379->6379/tcp

動作確認

主に 3 パターンあるのでそれぞれ紹介します

GUI

localhost:8080 にアクセスすると確認できます
初期ID/パスワードは airflow/airflow でログインできます

サンプルのジョブがいくつか登録されているのが確認できると思います

<

CLI

airflow.sh というシェルスクリプトを使ってアクセスできます

  • curl -LfO 'https://airflow.apache.org/docs/apache-airflow/2.1.2/airflow.sh'
  • chmod +x airflow.sh
  • ./airflow.sh info

これで Airflow の情報が表示されるのが確認できると思います

curl

RESTAPI があるので curl を使ってコールしてみます
単純な Basic 認証でアクセスできます

  • ENDPOINT_URL="http://localhost:8080/" curl -X GET --user "airflow:airflow" "${ENDPOINT_URL}/api/v1/pools"

リファレンスはこちらにあります

最後に

とりあえずインストールと簡単な使い方だけ紹介しました
実際はジョブの登録やスケジューリングの登録を行う必要があります

チュートリアルがあるのでそれをやると大まかな使い方は理解できると思います

参考サイト

2021年8月25日水曜日

celery の Periodic Tasks で cron 的なタスクを作成する

celery の Periodic Tasks で cron 的なタスクを作成する

概要

celery に定期実行するためのタスクがあるので試してみました
時間は cron フォーマットで登録できるようです

環境

  • macOS 11.5
  • Python 3.8.3
  • celery 5.1.2

cron タスク

タスクを定義し setup_periodic_tasks で add_periodic_task を使って登録できます

「実行する時刻」と「実行するタスク」を指定します オプションで name や expires が指定できます
これは apply_async で使えるオプションと同じものが指定できます

  • vim sub_tasks.py
from celery import Celery
from celery.schedules import crontab

app = Celery('cron_tasks', broker='redis://localhost')

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')

    # Calls test('world') every 30 seconds
    sender.add_periodic_task(30.0, test.s('world'), expires=10)

    # Executes every Monday morning at 7:30 a.m.
    sender.add_periodic_task(
        crontab(hour=7, minute=30, day_of_week=1),
        test.s('Happy Mondays!'),
    )

@app.task
def test(arg):
    print(arg)

動作確認

起動するコマンドが worker -> beat になるので注意しましょう

  • pipenv run celery -A cron_tasks beat --loglevel=info

これでログを見るとちゃんとタスクが実行されているのが確認できると思います

[2021-08-12 15:39:46,142: INFO/MainProcess] Scheduler: Sending due task add every 10 (cron_tasks.test)
[2021-08-12 15:39:56,142: INFO/MainProcess] Scheduler: Sending due task add every 10 (cron_tasks.test)
[2021-08-12 15:40:06,143: INFO/MainProcess] Scheduler: Sending due task add every 10 (cron_tasks.test)
[2021-08-12 15:40:16,138: INFO/MainProcess] Scheduler: Sending due task cron_tasks.test('world') (cron_tasks.test)

最後に

cron だけでなく solar という形式でもスケジューリングできます

ワーカーが動作している状態で動的に cron タスクを追加する方法があるのか気になりました

参考サイト

2021年8月24日火曜日

celery で登録済みのジョブの状態を確認する方法

celery で登録済みのジョブの状態を確認する方法

概要

今回は celery の inspect という機能を使って登録済みのジョブ情報を取得してみます
inspect を使う場合のポイントはワーカー (ノード) が稼働していないと取得できないという点です

環境

  • macOS 11.5.2
  • Python 3.8.3
  • Celery 4.4.7

タスクスクリプト

  • vim sub_stasks.py
import time
from celery import Celery

app = Celery('sub_tasks', backend='redis://localhost', broker='redis://localhost')

@app.task
def add(x, y):
    time.sleep(10)
    return x + y

実行メインスクリプト

from sub_tasks import add

add.delay(100, 1)

inspect スクリプト

今回の肝になるスクリプトです
以下 4 つの inspect 機能を使っています

  • registered・・・登録済みのタスクを取得
  • scheduled・・・スケジュール済みのタスクを取得
  • active・・・実行中のジョブを取得
  • reserved・・・受信済みのジョブを取得

上の 2 つはタスクの情報を取得する inspector になります
下の 2 つがジョブの状態を確認するための inspector になります

  • vim job_inspector.py
from celery import Celery

app = Celery('sub_tasks', backend='redis://localhost', broker='redis://localhost')

inspector = app.control.inspect()

registerd = inspector.registered()
for node, tasks in registerd.items():
    print(node)
    print(tasks)

scheduled = inspector.scheduled()
for node, tasks in registerd.items():
    print(node)
    print(tasks)

active = inspector.active()
for node, jobs in active.items():
    print(node)
    for job in jobs:
        for k, v in job.items():
            print("    {} -> {}".format(k, v))
        print("")

reserved = inspector.reserved()
for node, jobs in reserved.items():
    print(node)
    for job in jobs:
        for k, v in job.items():
            print("    {} -> {}".format(k, v))
        print("")

今回はわかりやすいように print を使っているだけになります
基本的には各ノードごとにジョブやタスクの情報がリストで取得できるのでループして中の情報を確認します

動作確認

先にワーカーを起動しておきましょう
ワーカーが起動していないと inspector はすべて None を返してしまいます

ジョブ登録

  • for i in `seq 0 10`; do pipenv run python main.py; done;

ワーカー起動

  • pipenv run celery -A sub_tasks worker --loglevel=info

inspector 起動

  • pipenv run python job_inspector.py

ワーカーが起動していればちゃんとタスクやジョブの情報が出力されると思います
実行待ちは reserved 側に表示されるのが確認できると思います

最後に

ワーカーが動作していない状態で登録済みのジョブの状態を確認したい場合は直接 redis などのブローカの中を確認するしかないかもしれません

おまけ: キューを指定している場合は

inspector は特にやることは変わりません 取得したジョブ情報にある routing_key にキュー名が入るようになります

{
  'exchange': '',
  'routing_key': 'hoge',
  'priority': 0,
  'redelivered': None
}

sub_tasks.py では以下のようにキューをしている想定です

  • vim sub_tasks.py
import time
from celery import Celery

app = Celery('sub_tasks', backend='redis://localhost', broker='redis://localhost')

@app.task(queue="hoge")
def add(x, y):
    time.sleep(10)
    return x + y
  • pipenv run celery -A sub_tasks worker --loglevel=info --queues=hoge

参考サイト

2021年8月23日月曜日

paramiko で docker exec しようとすると The input devic e is not a TTY になる

paramiko で docker exec しようとすると The input devic e is not a TTY になる

概要

Python の paramiko で docker exec しようとすると「The input devic e is not a TTY」が発生しました
対処方法を紹介します

環境

  • macOS 11.5
  • Python 3.8.3
  • paramiko 2.7.2

対応策

「-T」オプションを付与して exec します

  • docker-compose exec -T app hostname

-T は pseudo-tty を無効にするオプションです

参考サイト

2021年8月20日金曜日

Python エンジニア基礎認定試験に合格しました

Python エンジニア基礎認定試験に合格しました

概要

勉強方法や感想を紹介します
CBT 形式で受験できます
正直受験申し込みが一番面倒だったかもしれません

結果

900/1000 合格でした
満点だと思いましたが残念ながら満点は取れませんでした

勉強方法

基本的には以下の 2 つになります
比重的には過去問80%、チュートリアル20%になります

基本的に過去問を解きまくりました
正答率が 100% になるまで何度も繰り返します
自分が経験した感じでは以下に紹介する過去問と同等の問題が実際の試験でほぼ出題されたので過去問をばっちり正答できるようになれば試験はほぼ合格できると思って大丈夫です

あとはチュートリアルで基礎的な学習力と過去問で紹介されていない部分の穴埋めをする感じです

過去問

後述する参考書に付属している過去問と Web にある過去問 2 つを解きまくりました

PRIME STUDY はユーザ登録不要ですが DIVE INTO EXAM はユーザ登録が必要になります

Web で気軽に過去問を解けるのでユーザ登録は面倒ですがやることをオススメします

Python チュートリアル

本もありますが Web のチュートリアルで十分です
https://docs.python.org/ja/3/tutorial/

必要であればオライリーの公式の参考書を購入しても良いかなと思います
自分は Kindle 版がなかったので購入しませんでした

参考書類

以下 2 つの書籍を Kindle で購入しました
正直最初の合格勉強法は不要かなと思います
特にテキストや過去問の類の紹介はなく体験記のみしか記載していないためです

あとは過去問として以下を購入しました
誤字や正答の間違えている箇所がいくつかあるのでそこは残念でしたが試験対策としては十分な書籍でした

受験の申し込み方法

まずは OdysseyID が必要になるので必ず登録します

そして試験会場を選択し申し込みするのですが試験の申し込みは OdysseyID ではなく ISAスクール で行う必要があり更にそちらで ID を取得して申し込む必要があります

なぜかここがかなり複雑になっており面倒なので試験を申し込む前に挫折する人がいてもおかしくないのかなと思います
ISAスクールでアカウントを取得して会場の予約をすると指定の口座に振り込むようにメールが来るので口座に振り込みます

すると振込の確認が来るのでこれで晴れて受験することができるようになります

受験の日程は会場選択時に同時に行えます

受験料

  • 11,000円

学割もあるそうです

当日の持ち物

  • OdysseyID/PW
  • 写真付き証明書

ISAスクールの ID/PW は不要です
あれはあくまでも申し込みにだけ必要になる ID/PW です

当日の試験

60分ありますが15分くらいで終了しました
見直しも可能です

記述の問題はなくすべて選択式の問題でした
全部で40問で1問25点の1000点満点になります
自分は 36/40 = 900点でした

会場にもよるかもしれませんが開始前に OdysseyID でログインする必要があります
おそらくログインできないと受験できないので必ず ID/PW は控えてから行くようにしてくだあい

試験終了後は時間が余っていても退出できました
試験結果はその場で自分で印刷して受け取りました

最後に

過去問をやっておけばほぼ受かります
Python の力試ししたいかたはゼロ勉強でも受かるかもしれません (リスト内包記法や基本文法、標準モジュールの使い方などがわかっていれば)

P.S 合格証について

一ヶ月後にちゃんと Oddesy から正式な合格証が届きます

2021年8月19日木曜日

macOS に dash をインストールする方法

macOS に dash をインストールする方法

概要

最新版の dash を macOS に入れてみました インストール方法と簡単な使い方を紹介します

環境

  • macOS 11.5
  • Dash 6.0.7

ダウンロード

https://kapeli.com/dash ここからダウンロードします

Dash.zip というファイルがダウンロードできれば OK です

インストール

ダウンロードしたファイルを開きます Dash.app というファイルが展開されるのでアプリケーション配下に移動しましょう

インストールはこれで OK です

使ってみる

Dash.app を起動しましょう まだ何もドキュメントがないので何も表示できません

「Click here to download some」をクリックして必要なリファレンスをダウンロードします

リファレンスのダウンロード

検索バーに「Ruby 」と入力すると Ruby に関係するリファレンスをダウンロードすることができます
必要なリファレンスをダウンロードすれば OK です

動作確認

あとはトップページにある検索バーから検索するだけです

例えば Ruby の File クラスを参照したい場合は ruby: と入力して Classes を選択 File を選択とすれば参照できます

最後に

基本的な使い方は以上です あとは Alfread や各種 IDE と連携してそこから Dash のリファレンスを検索できるようにしたりできます

またスニペットやチートシートも作成できます

あと 30 日間は無料ですがそれ以降はライセンスの登録が必要になるのでライセンスを購入しましょう

2021年8月18日水曜日

dismiss がコールされたときに元の画面でイベントをハンドリングする方法

dismiss がコールされたときに元の画面でイベントをハンドリングする方法

概要

iOS13 以降で仕様が変わっているようです presentationControllerDidDismiss を実装する必要があります

環境

  • macOS 11.5
  • Xcode 12.5.1

元の画面で実装するコード

import UIKit

extension HomeViewController: UIAdaptivePresentationControllerDelegate {
    // NewViewController から戻ったときに呼ばれる
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        // ここは好きなコードに書き換えて OK
        self.viewDidLoad()
    }
}

class HomeViewController: UIViewController {
    
    override func viewDidLoad() {
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "toNextViewController") {
            let nextvc: NextViewController = (segue.destination as? NextViewController)!
            // 以下が重要
            nextvc.presentationController?.delegate = self
        }
    }
}

HomeViewController -> NextViewController の画面遷移は storyboard を使っているので prepare を実装しています

遷移先の画面で実装するコード

extension NextViewController {
    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
        super.dismiss(animated: flag, completion: completion)
        guard let presentationController = presentationController else {
            return
        }
        if #available(iOS 13.0, *) {
            presentationController.delegate?.presentationControllerDidDismiss?(presentationController)
        } else {
            // Fallback on earlier versions
        }
    }
}

class NextViewController: UIViewController {

    @IBAction func backToHome(_ sender: Any) {
        self.dismiss(animated: true, completion: nil)
    }

}

dismiss するボタンを設置することを想定していますがスワイプで消しても同じです

最後に

iOS12 以下もサポートしている場合は viewWillDisappear を HomeViewController に実装しておいたほうがいいかもしれないです

参考サイト

2021年8月17日火曜日

xcode で xxx is not connected になった場合の対処方法

xcode で xxx is not connected になった場合の対処方法

概要

Xcode で Wifi ビルドをしている際に発生するエラーです 対処方法を紹介します

環境

  • macOS 11.5
  • Xcode 12.5.1

対象方法

  • Window -> Devices and Simulators
  • Conect via IPaddress

で iPhone に割り振られているプライベート IP アドレスを入力すれば OK です

参考サイト

2021年8月16日月曜日

マイクラサーバを docker で起動する

マイクラサーバを docker で起動する

概要

本体は jar ですが docker でも動作することができます 使用するイメージは itzg/minecraft-server を使用します

環境

  • macOS 11.5
  • docker 20.10.7
  • Minecraft Server 1.17.1
  • Minecraft 1.17.1

バニラで起動

  • mkdir /tmp/minecraft-data
  • docker run -d -it --name mc -p 25565:25565 -v /tmp/minecraft-data:/data -e EULA=TRUE itzg/minecraft-server

起動するまで時間がかかります

  • docker logs -f mc

で「[06:51:14] [Server thread/INFO]: Done (100.839s)! For help, type "help"」が出るまで待ちましょう

接続確認

Minecraft を起動して接続確認します 今回はローカルに起動しているのでローカルの IP で接続しにいきます クライアント側のバージョンとサーバのバージョンは合わせるようにしましょう

Multiplayer から Add Server でサーバを追加します ポートは EXPOSE した 25565 を指定します

追加が完了すると以下のように一覧に表示されます

あとはちゃんと起動できるか確認しましょう

最後に

今回はバニラで動作させましたが Forge や Canyon モードでも動作させることができます

2021年8月13日金曜日

Mac 版のマイクラで「予期しない問題が発生し、ゲームがクラッシュしました」の対策

Mac 版のマイクラで「予期しない問題が発生し、ゲームがクラッシュしました」の対策

概要

久々に Minecraft を起動したらタイトルのエラーが発生しました自分の対処方法を紹介します

環境

  • macOS 11.5
  • Minecraft 1.17.1

対策方法: 起動時の JVM のオプションをリセットする

自分はこれで直りました おそらく Minecraft に同梱している Java のバージョンが上がって JVM オプションの指定に非互換が発生したんだと思います

最新版のランチャーだとリセットするとそれっぽい JVM を設定してくれるのでそれを元にまた調整すると良いと思います

おまけ: ランチャーのログのパス

~/Library/Application Support/minecraft/launcher_log.txt

ここを見て原因調査するのは大事です

それ以外に考えられること

  • mod をすべて削除する
  • Java を指定している場合にバージョンがサポートされていない Java を指定している
  • ポートのバッティング

など考えられるかなと思います

2021年8月12日木曜日

UIButton で画像とテキストを両方表示する

UIButton で画像とテキストを両方表示する

概要

Storyboard で設定する方法を紹介します

環境

  • macOS 11.5
  • Xcode 12.5.1

設定方法

  • Title・・・好きなテキスト
  • Image・・・好きな画像を設定

これだけでできました Edge で Inset などを設定する必要があるようなのですが自分はそれを設定しないでもできました

文字色などが自動で変わることがあるのでその場合は背景と同色にならないように注意しましょう

2021年8月11日水曜日

RealmSwift はモデルだけ定義してあとは必要な箇所で RealmSwift を import するのが良さそう

RealmSwift はモデルだけ定義してあとは必要な箇所で RealmSwift を import するのが良さそう

概要

RealmSwift を扱う場合にモデルと CRUD 操作をどこに書くのがいいかなと悩んでいます 個人的にはシンプルに使いたいところで使う方式が一番しっくり来たので備忘録として残しておきます

環境

  • macOS 11.5
  • Xcode 12.5.1

モデルだけ定義する

Realm の基本的な使い方としてモデルを定義すると思うのですがそのモデル内では余計なことはしないようにします

単純にモデルだけを定義します

import RealmSwift

class BoardConfig: Object {
    @objc dynamic var name: String = ""
    @objc dynamic var point: Int = 0
}

View 側でモデルを使う

データの保存が取得は基本的に UIViewController などで行います view 側にも import RealmSwift が必要でかつデータにアクセスするために Realm() オブジェクトを生成する必要がありますが余計なことを考えなくて済むのでこの方法がベストだと思います

class BoardConfigViewController: UIViewController {
    override func viewDidLoad() {
        // データの保存
        let config = BoardConfig()
        config.name = "board1"
        config.point = 20
        let realm = try! Realm()
        try! realm.write {
            realm.add(config)
        }
        // データの取得
        let configs = realm.objects(BoardConfig.self)
        for config in configs {
            print(config)
        }
    }
}

上記では viewDidLoad で行っていますが Realm へのアクセスが必要な箇所で上記を呼び出す感じになります

Realm のオブジェクトを毎回生成するとあれなので Realm のオブジェクトはメンバー変数として扱っても良いかなと思います

アンチパターンっぽい書き方は

過去にモデルのクラスにデータを保存するメソッドやデータを取得するメソッドを作成していたのですがそれだとモデルを view 側でも扱うしモデル側でも扱う必要がありコードが重複している感がありました

CRUD 処理をまとめるという意味では良かったかもしれないのですがデータの変換などを考えると結局コードがごちゃごちゃするので微妙かなと思った感じです

モデルはあくまでもモデルでそれを view 側で変換したりするほうが結果的にキレイにコードになる印象です

最後に

結局シンプルなのが一番です

2021年8月10日火曜日

Asset Unavailable: SF Symbol 'gearshape.fill' is unavailable prior to iOS 14.0. Add a fallback image of the same name to the asset catalog for backward deployment.

Asset Unavailable: SF Symbol 'gearshape.fill' is unavailable prior to iOS 14.0. Add a fallback image of the same name to the asset catalog for backward deployment.

概要

iOS14では新しいシンボルを使うようになりました iOS14 以下のバージョンに対応したアプリを作成している場合に SF Symbols を使おうとするとタイトルの警告が出るようになります そんな場合の対処方法を紹介します

環境

  • macOS 11.5
  • Xcode 12.5.1

SF Symbols 3 アプリのダウンロードとインストール

gearshape.fill.svg が必要になります Apple が公式で作成しているシンボルのアプリがありそこから書き出しすることができるのでアプリをインストールします

https://developer.apple.com/sf-symbols/

dmg ファイルを開きアプリケーション配下に配置すればインストール完了です

gearshape.fill の検索

アプリを開いてシンボルを探しましょう 右上に検索があるのでそこから「gearshape.fill」と検索しましょう

シンボルを書き出す

シンボルが見つかったら目的の SVG を作成します メニューから「ファイル」->「シンボルを書き出す」を選択します

また書き出す際にはバージョンを「2」にしましょう 古いバージョンの svg でないと古い iOS では動作しないためです iOS14 しかサポートしないのであればバージョン3 でも大丈夫です

Assets.xcaseets に登録する

あとは書き出された svg を assets に登録すれば OK です アセットのタイプは「Symbol Image Set」を選択し名前は「gearshape.fill」にします

あとは svg をドラッグアンドドロップで登録すれば OK です

動作確認

再度ビルドしてアプリを動かしてみましょう 警告が消えるのが確認できると思います

2021年8月6日金曜日

Copying cache files from device 対処方法

Copying cache files from device 対処方法

概要

Xcode で開発したアプリを実機で動かすときに「Copying cache files from device」となりなかなか先に進まなくなりました 対処方法を紹介します

環境

  • macOS 11.5
  • Xcode 12.5.1

対処方法: とにかく待つ

自分はこれで解決しました 一旦キャッシュを消してみてたりするのも良いですが自分は結局終わるまで待つことで解決しました プロジェクトの大きさにもよりますが、だいたい 10 分ほど待てば終わると思います

対処方法2: バッテリーを確認する

iPhone 側のバッテリーが赤以下になっていると終わらない可能性があります バッテリーに接続してから再度試してみましょう

対処方法3: Wifi の接続を確認する

今回は Wifi を使ったビルドで発生しました Wifi の調子がおかしい可能性もあるので iPhone 側で再度接続し直してみましょう

注意点: 何回も繰り返さない

なかなか終わらないのでビルドを停止して再開を繰り返すとその分 「Copying cache files from device」が発生した感じになりました 間違って複数回やってしまった場合も辛抱して待ちましょう

2021年8月5日木曜日

gitlab-ci で特定のタグだけジョブを実行する方法

gitlab-ci で特定のタグだけジョブを実行する方法

概要

タグのみにジョブを実行したい場合に使えるテクニックを紹介します

環境

  • Gitlab 13.12.5-ee
  • Gitlab Runner 14.1.0

.gitlab-ci.yml

ポイントはジョブ内で rules を使います タグ名は $CI_COMMIT_TAG で取得できます

今回は v0.0.1 などのセマンティクスバージョニングの形式の場合のみジョブを実行するように正規表現を使っています

stages:
  - test
test_job:
  stage: test
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+/
  script:
    - hostname

動作確認

タグして push しましょう

  • git tag v0.0.1
  • git push -u origin v0.0.1

タグに対してのみジョブが流れるのが確認できるかなと思います

もしブランチに対して実行しようとすると以下のようなエラーが発生します

最後に

2021年8月4日水曜日

launchd で環境変数を設定する方法

launchd で環境変数を設定する方法

環境

ProgramArguments を使って環境変数を設定します メインプログラムの実行は Program で指定しましょう

環境

  • macOS 11.5

サンプル

ポイントは ProgramArguments の部分です ここで launchctl setenv を使って環境変数を設定することができます

実行するスクリプトやコマンドは Program キーで指定しましょう

ProgramArguments は Array ですがコマンド自体は一つしか実行できないので複数のコマンドを実行したい場合は諦めてシェルスクリプトを作成してそこで export などを使って環境変数を設定するようにしましょう

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>local.mac.testscript1</string>
    <key>Program</key>
    <string>/path/to/command</string>
    <key>WorkingDirectory</key>
    <string>/path/to/workdir</string>
    <key>ProgramArguments</key>
    <array>
      <string>sh</string>
      <string>-c</string>
      <string>
      launchctl setenv PATH /usr/local/bin:$PATH
      </string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
      <dict>
        <key>Weekday</key>
        <integer>1</integer>
	<key>Hour</key>
        <integer>8</integer>
        <key>Minute</key>
        <integer>5</integer>
      </dict>
    </array>
    <key>StandardOutPath</key>
    <string>/tmp/app.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/app.err</string>
  </dict>
</plist>

2021年8月3日火曜日

dict をコピーする場合は配列の中であっても copy しなければならない

dict をコピーする場合は配列の中であっても copy しなければならない

概要

dict をコピーして別の dict を作成する場合に dict の値を上書きする場合には必ず copy() メソッドを使いましょうという話です 配列の中に dict があっても copy() を使います

環境

  • macOS 11.5
  • Python 3.8.3

サンプルコード: ダメな例

aa = [{"key":"value"}]
bb = aa.copy()
for b in bb:
    b["key"] = "value2"

print(aa) # -> [{'key': 'value2'}]
print(bb) # -> [{'key': 'value2'}]

サンプルコード: 良い例

aa = [{"key":"value"}]
bb = aa.copy()
for i, b in enumerate(bb):
    tb = b.copy()
    tb["key"] = "value2"
    bb[i] = tb

print(aa) # -> [{'key': 'value'}]
print(bb) # -> [{'key': 'value2'}]

最後に

もう少し良い解決方法がありそう deepcopy という手法があるようです

2021年8月2日月曜日

for でループさせるためクラスは __next__ を実装する

for でループさせるためクラスは __next__ を実装する

概要

Python は配列などを for ループで回すことができます そのような特性をイテレータと呼びます 自作のクラスでも配列ようにイテレータの機能を実装してあげれば for ループのシーケンスに使えます 今回はサンプルコードを紹介します

環境

  • macOS 11.5
  • Python 3.8.3

サンプルコード: 指定の数字を 0 までループする

class ReverseIterator(object):

    def __init__(self, num):
        self.end = 0
        self.current = num

    def __iter__(self):
        return self

    def __next__(self):
        if self.current == self.end:
            raise StopIteration()
        ret = self.current
        self.current -= 1
        return ret

si = ReverseIterator(10)
for i in si:
    print(i)

サンプルコード: 指定の文字列を 1 文字ずつ表示する

class StringIterator(object):

    def __init__(self, s):
        self.s = s
        self.end = len(s)
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current == self.end:
            raise StopIteration()
        ret = self.current
        self.current += 1
        return self.s[ret]

si = StringIterator('hoge')
for i in si:
    print(i)

少し解説

実装が必須なのは __iter____next__ になります 特に __next__ が需要で次に渡す情報や終了時の判定はここで行います

今回のサンプルではコンストラクタで開始時と終了時のインデックスを作成しその値になるまで続ける感じにしています

もうこれ以上ループさせたくない場合は StopIteration を raise することで停止してくれます

最後に

list や dict を使ってるとあまり自分でイテレータクラスを作成することはないですがリファクタなどするときにこれはイテレータを持っているなという場合にはこういった手法を使ってリファクタできるかなと思います

参考サイト