Vimプラグイン自作の方法

基本的にはGithubを漁って自分に合ったプラグインを探して利用する、という方針だったのですが、どうしても実現できない機能がいくつかあり、新しいチャレンジを行うという試みでプラグインの自作に踏み出してみました

難しいことはやらずに簡易的な機能を提供するだけのプラグインをとりあえず作ってみます🦀

事前準備

現在NeoVimとそれなりに多いプラグインで構成されているので、最小構成のVim環境を新しいブランチを作成しましょう

特にやる必要はないかもですが、今後作成していく自作プラグインは一応Githubで公開予定なので可能な限り依存を少なくする、という意味合いで準備します

※deinやmotion移動等だけ残してそれ以外は消しました

超最小構成*.vimの実行

最小構成のvimに変更したら、任意のdirに sample.vim というファイルを作成します

[masahirookubo@MacBook-Pro-2 (arm64):~/dotfiles][feature/plugins]
% touch sample.vim

そしてprint文を記載して実行

echo "hogehgoe!!!"

:source sample.vim

動きました!!

最小構成ではこちらでいけますので、一旦これで完了とします

超最小構成のvimプラグインの実装

最小構成のプログラムが稼働することを検証できたので、次はもう少しプログラムを追加してそれをremoteで公開してみます

まずはpluginを作っていくためのdirなどの準備コマンドの実行

# 任意のdirへ移動してから実行
mkdir sample_plugin
mkdir plugin
touch plugin/sample_plugin.vim
vi plugin/sample_plugin.vim

作成したら、下記のプログラムを追加

function! sample_plugin#SamplePlugin()
  echo 'It works!'
endfunction

command! SamplePlugin call sample_plugin#SamplePlugin()

プログラムの追加が完了したら、ファイルを読み込ませて実行してみます

:source sample_plugin.vim
:SamplePlugin

問題なく読み込めています👍

GIthubから利用してみる

localでの稼働も検証できたので、Githubに上げてdeinで追加して検証を行います

※パッケージマネージャはお使いのものを利用してください

[[plugins]]
repo = 'masahiro04/sample_plugin'

追加した後はvimを起動して下記を実行・検証してみます

「sam」と打ち込むと候補が出力され

エンターを押すと、実装したプラグインが表示されました!

もうちょっと難しいと思ってたんですけど、思いのほか簡単に実装できたので、自分が作りたいプラグインをこれから作ってみようと思います!!!

参考記事

はじめてのvimscriptとvimプラグインの作成

簡単なVimプラグインを作って基礎を学ぶ

[dein] Vim(lua):E5107: Error loading lua [string “:lua”]:3: ‘=’ expected near ‘nnoremap’

NeoVimでLuaを導入したところタイトルのエラーが発生しました

どうやらこのエラーは、luaの前方にスペースやタブが入っていてもエラーが出るようなので、

[[plugins]]
repo = 'nvim-telescope/telescope.nvim'
depends = ['plenary.nvim']
hook_add = '''
  lua << EOF
    require('telescope').setup()
  EOF
  nnoremap <leader>ff <cmd>lua require('telescope.builtin').find_files()<cr>
'''

こちらに修正したところ解決できました!

[[plugins]]
repo = 'nvim-telescope/telescope.nvim'
depends = ['plenary.nvim']
hook_add = '''
lua << EOF
  require('telescope').setup()
EOF
  nnoremap <leader>ff <cmd>lua require('telescope.builtin').find_files()<cr>
'''

参考記事

Unable to use lua inside a `.vim` file

Flutterにcloud_firestore導入したらAMSupportURLConnectionDelegate is implemented in bothが出た

FlutterにFirebase追加したら下記のエラーが発生しました

※一部だけ掲載予定でしたが、思いのほかハマったので後続の方のために全て記載しています

バージョン情報

flutter: 3.0.5
Dart: 2.18 ?ぐらい、ここあんま関係ない
Mac: M1 MAX
Changing current working directory to: /Users/masahirookubo/me/flutter/firestore_test
Launching lib/main.dart on iPad Pro (12.9-inch) (5th generation) in debug mode...
Running pod install...                                             16.4s
Running Xcode build...
Xcode build done.                                           54.7s
Failed to build iOS app
Error output from Xcode build:
↳
    objc[54809]: Class AMSupportURLConnectionDelegate is implemented in both /usr/lib/libauthinstall.dylib (0x1ddd5eb90) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x103b382c8). One of the two will be used. Which one is undefined.
    objc[54809]: Class AMSupportURLSession is implemented in both /usr/lib/libauthinstall.dylib (0x1ddd5ebe0) and /Library/Apple/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/A/MobileDevice (0x103b38318). One of the two will be used. Which one is undefined.
    ** BUILD FAILED **


Xcode's output:
↳
    Writing result bundle at path:
        /var/folders/bb/n3dq0s2963v_4c8dw8ygjlgm0000gn/T/flutter_tools.IPaDUI/flutter_ios_build_temp_dirGu70oL/temporary_xcresult_bundle

    While building module 'absl' imported from /Users/masahirookubo/me/flutter/firestore_test/ios/Pods/gRPC-Core/src/core/ext/xds/xds_server_config_fetcher.cc:21:
    In file included from <module-includes>:1:
    In file included from /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/abseil-umbrella.h:13:
    In file included from /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/algorithm/algorithm.h:29:
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/config.h:52:10: error: include of non-modular header inside framework module 'absl.base.config': '/Library/Developer/Toolchains/swift-5.6.2-RELEASE.xctoolchain/usr/bin/../include/c++/v1/limits.h' [-Werror,-Wnon-modular-include-in-framework-module]
    #include <limits.h>
             ^
    While building module 'absl' imported from /Users/masahirookubo/me/flutter/firestore_test/ios/Pods/gRPC-Core/src/core/ext/xds/xds_server_config_fetcher.cc:21:
    In file included from <module-includes>:1:
    In file included from /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/abseil-umbrella.h:13:
    In file included from /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/algorithm/algorithm.h:29:
    In file included from /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/config.h:67:
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/policy_checks.h:28:10: error: include of non-modular header inside framework module 'absl.base.policy_checks': '/Library/Developer/Toolchains/swift-5.6.2-RELEASE.xctoolchain/usr/bin/../include/c++/v1/limits.h' [-Werror,-Wnon-modular-include-in-framework-module]
    #include <limits.h>
             ^
    While building module 'absl' imported from /Users/masahirookubo/me/flutter/firestore_test/ios/Pods/gRPC-Core/src/core/ext/xds/xds_server_config_fetcher.cc:21:
    In file included from <module-includes>:1:
    In file included from /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/abseil-umbrella.h:53:
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/internal/spinlock_linux.inc:17:10: fatal error: 'linux/futex.h' file not found
    #include <linux/futex.h>
             ^~~~~~~~~~~~~~~
    3 errors generated.
    /Users/masahirookubo/me/flutter/firestore_test/ios/Pods/gRPC-Core/src/core/ext/xds/xds_server_config_fetcher.cc:21:10: fatal error: could not build module 'absl'
    #include "absl/strings/str_join.h"
     ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
    While building module 'openssl' imported from /Users/masahirookubo/me/flutter/firestore_test/ios/Pods/gRPC-Core/src/core/tsi/ssl_transport_security.h:25:
    In file included from <module-includes>:1:
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/BoringSSL-GRPC/openssl_grpc.framework/Headers/umbrella.h:39:1: warning: umbrella header for module 'openssl' does not include header 'siphash.h' [-Wincomplete-umbrella]
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/BoringSSL-GRPC/openssl_grpc.framework/Headers/umbrella.h:39:1: warning: umbrella header for module 'openssl' does not include header 'x509_vfy.h' [-Wincomplete-umbrella]
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/BoringSSL-GRPC/openssl_grpc.framework/Headers/umbrella.h:39:1: warning: umbrella header for module 'openssl' does not include header 'hpke.h' [-Wincomplete-umbrella]
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/BoringSSL-GRPC/openssl_grpc.framework/Headers/umbrella.h:39:1: warning: umbrella header for module 'openssl' does not include header 'e_os2.h' [-Wincomplete-umbrella]
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/BoringSSL-GRPC/openssl_grpc.framework/Headers/umbrella.h:39:1: warning: umbrella header for module 'openssl' does not include header 'blake2.h' [-Wincomplete-umbrella]
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/BoringSSL-GRPC/openssl_grpc.framework/Headers/umbrella.h:39:1: warning: umbrella header for module 'openssl' does not include header 'hrss.h' [-Wincomplete-umbrella]
    /Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/BoringSSL-GRPC/openssl_grpc.framework/Headers/umbrella.h:39:1: warning: umbrella header for module 'openssl' does not include header 'trust_token.h' [-Wincomplete-umbrella]
    7 warnings generated.
    7 warnings and 4 errors generated.
    note: Using new build system
    note: Planning
    note: Build preparation complete
    note: Building targets in dependency order
    /Users/masahirookubo/me/flutter/firestore_test/ios/Pods/Pods.xcodeproj: warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 15.2.99. (in target 'leveldb-library' from project 'Pods')

    Result bundle written to path:
        /var/folders/bb/n3dq0s2963v_4c8dw8ygjlgm0000gn/T/flutter_tools.IPaDUI/flutter_ios_build_temp_dirGu70oL/temporary_xcresult_bundle


Lexical or Preprocessor Issue (Xcode): Include of non-modular header inside framework module 'absl.base.config': '/Library/Developer/Toolchains/swift-5.6.2-RELEASE.xctoolchain/usr/bin/../include/c++/v1/limits.h'
/Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/config.h:51:9

Lexical or Preprocessor Issue (Xcode): Include of non-modular header inside framework module 'absl.base.policy_checks': '/Library/Developer/Toolchains/swift-5.6.2-RELEASE.xctoolchain/usr/bin/../include/c++/v1/limits.h'
/Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/policy_checks.h:27:9

Lexical or Preprocessor Issue (Xcode): 'linux/futex.h' file not found
/Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/internal/spinlock_linux.inc:16:9

Parse Issue (Xcode): Could not build module 'absl'
/Users/masahirookubo/me/flutter/firestore_test/ios/Pods/gRPC-Core/src/core/ext/xds/xds_server_config_fetcher.cc:20:9

Could not build the application for the simulator.
Error launching application on iPad Pro (12.9-inch) (5th generation).

[masahirookubo@MacBook-Pro-2 (arm64):~/me/flutter/firestore_test/ios]!+[master]
%

結論

XCodeを再インストールしました

下記のようなコマンドも実行しましたが特に変わらず

cd ios
rm -rf ~/Library/Caches/CocoaPods
rm -rf Podfile.lock
rm -rf Pods
rm -rf ~/Library/Developer/Xcode/DerivedData/*

下記の手順でXCodeを削除、そしてinstallで解決できました

sudo rm -rf /Applications/Xcode.app
sudo rm -rf /Library/Preferences/com.apple.dt.Xcode.plist
sudo rm -rf ~/Library/Preferences/com.apple.dt.Xcode.plist
sudo rm -rf ~/Library/Caches/com.apple.dt.Xcode
sudo rm -rf ~/Library/Application Support/Xcode
sudo rm -rf ~/Library/Developer/Xcode
sudo rm -rf ~/Library/Developer/CoreSimulator

// こちらは念の為実行、理由は後述
sudo rm -rf /Library/Developer/CommandLineTools

問題の原因

再インストール直前で、2点が目につきました

 One of the two will be used. Which one is undefined

Lexical or Preprocessor Issue (Xcode): Include of non-modular header inside framework module 'absl.base.config': '/Library/Developer/Toolchains/swift-5.6.2-RELEASE.xctoolchain/usr/bin/../include/c++/v1/limits.h'
/Users/masahirookubo/me/flutter/firestore_test/build/ios/Debug-iphonesimulator/abseil/absl.framework/Headers/base/config.h:51:9

1行目のエラー情報は割とあるあるだと思うのですが、下の方の情報にはswift-5.6.2がToolchainsから参照されています

ここは思いっきり心当たりがあって、1ヶ月ほど前にvimでswift開発をするためにToolchainsやswiftバージョンをを変えるために、必要なコードをダウンロードしてきたものです

途中までは流石に問題を引き起こすことはないだろう、ぐらいに考えていたのですが、

  • Flutterアプリをcreate
  • flirebaseを導入
  • cloudstore導入で急にエラー

はあまりにもおかし過ぎるし、もしこれが普遍的な問題であれば社会的な問題にもつながると思います

※大きなインフラなので冗談抜きで本当に問題として取り上げられ、twitterなどですぐに情報が回るはず

なので一度削除する、という選択に踏み切りました

注意点

再インストール自体は簡単な選択ですが、

  • 時間かかる(2, 3時間ぐらいかかった。通信状況などにも大きく依存するかと)
  • アカウント登録情報も削除される(Apple IDでログインすれば良いだけだけど、手元に情報を用意しておく必要あり)
  • もしかしたら、重要なファイルや情報なども削除されるかも
  • Toolchainsを削除するとgitなど関連するものを全て削除されるので、削除前によく検討すること

が必要です

自分の場合は基本的にDockerで環境を整えている、かつSwift、Dartでの開発しか行っていないのですぐに踏み切れましたが、複数の案件に関わっている場合や、時間がない場合は特に注意が必要です

まとめ

おそらく上記の順番で実行すれば、cloudfireだけでなくほとんどの問題解決の最終手段になり得ると思います

調べてもダメな場合は、寝る前に再インストールして朝実行ということも可能かと思うので、困っている方は最後の手段としておすすめです🙏

参考記事

Xcodeの再インストール

Supabase dbでカスタムなqueryを発行する方法

SQL Editorでカスタムなqueryの登録試みていたのですが、エラーが発生し結構詰まってしまったので記事にしてみました

結論

create or replaceで登録ではなく、posgreのviewを利用することで対応することで解決しました

こちらがそれぞれのコード


-- 動かない
create or replace function get_answers() 
returns table (
  id int,
  body text,
  topic_id int,
  reply_to int,
  created_at timestamp,
  count int
)
language sql
as $$
  select *, (select count(*) from answers a2 where a1.id = a2.reply_to) from answers a1;
$$;

-- 動く!
CREATE VIEW public.hogehuga AS
    select *, (select count(*) from answers a2 where a1.id = a2.reply_to) from answers a1;

試したこと

まずは動かないコードですが、公式docや技術ブログ系では機能するといった情報がありましたが、私が実行したところ、id単体(別カラムでもOK)では問題なく機能するのですが、

※動くコード


create or replace function get_answers() 
returns table (
  id int
)
language sql
as $$
  select id from answers;
$$;

2つ以上のカラムをreturnしようとしたら下記のエラーが発生しました
※最初に提示したコードを実行

Failed to run sql query: cannot change return type of existing function

エラーを見る限り、既存のfunctionのreturn値を変更できない、とのことですが、そもそもidやtopic_idなど単体では実行が可能だったので、複数選択することで問題が発生するものと思われます

※データ型が正確ではない、という可能性もあります→つまり上記の私のコードが間違い

PostgreSQLのView機能について

数時間粘っても問題解決できなかったので、下記の参考記事で紹介されている、posgreのviewという機能を利用することにしました

view is a convenient shortcut to a query. Creating a view does not involve new tables or data. When run, an underlying query is executed, returning its results to the user.

https://supabase.com/blog/2020/11/18/postgresql-views

簡単に説明すると、特定のqueryを作成して、それに命名した上で実行できるショートカットを作成する、ということです

なので、上記の実行したいコマンドを下記のように設定して実行することで、求める結果を獲得することが可能となります

CREATE VIEW public.hogehuga AS
    select *, (select count(*) from answers a2 where a1.id = a2.reply_to) from answers a1;

こちらが呼び出し元のコード

final answersRequest = client.from('hogehuga').select('*').execute();

取得できました!

参考記事

Supabaseのデータベースを使うときに役立つ情報

Postgres Views

NeoVim + COCのhover UIが崩れる問題の解消

iOSのビルドやUI以外は原則vimで開発を行なっているのですが、COCのホバーで情報を確認する際にUIが崩れていて長い間じわじわと精神的苦痛が伴っていたのですが解決できたので情報を共有します

結論下記をコメントアウトしました

" NG
set ambiwidth=double

" OK
" set ambiwidth=double

before

after, 綺麗!!!感激です😭😭

今までは定義元へジャンプしたりして対処していたのですが、本来は上記画像のように丁寧な見出し分的な情報があるみたいです😳

そもそも何故ambiwidthをセットしていたかというと、「vimで全角文字を表示するため」という理由でした

正直利用ケースは皆無なのですが、何故か入っていた、、、、その結果非常に大きな苦痛を強いられていたので、これからは定期的にvimrcを育てていこう、と思える痛い経験でした🙇‍♂️

参考記事

[Vim] 文字崩れ, 文字が残ってしまうのを防ぐ

Xcodeで実機デバッグ試すとThis operation can fail if the version of the OS on the device is incompatible…と出る

Xcodeで実機デバッグを試みたところ、下記のエラーが発生しました

Failed to prepare device for development.

This operation can fail if the version of the OS on the device is incompatible with the installed version of Xcode. You may also need to restart your mac and device in order to correctly detect compatibility.

こちらの問題ですが、どうやらXcodeでバージョンの対応があるかないか?で判断してるようで、対応していないとエラーが出るみたいです

ちなみに対応しているかどうかは下記のコマンドで件検証可能

cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

ls
10.0	10.2	11.0	11.2	11.4	12.1	12.3	13.0	13.2	13.4	13.6	14.0	14.2	14.4	15.0	9.0	9.2
10.1	10.3	11.1	11.3	12.0	12.2	12.4	13.1	13.3	13.5	13.7	14.1	14.3	14.5	15.2	9.1	9.3

私が検証したいのは15.5で一覧にないので、下記のプロジェクトからpullして上記dirに移動させます

プロジェクトのURL

ダウンロードが完了したらターミナルで下記を実行していきます

# Downloads dirに移動
cd Downloads

# zipをunzip
unzip 15.5.zip
Archive:  15.5.zip
   creating: 15.5/
  inflating: 15.5/DeveloperDiskImage.dmg.signature
  inflating: 15.5/DeveloperDiskImage.dmg

# 解凍したdirをiOSバージョンのdirへ移動. 権限エラーが発生するのでsudoをつけてます
sudo mv 15.5 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

# 該当のdirへ移動して確認
cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport
ls
10.0	10.2	11.0	11.2	11.4	12.1	12.3	13.0	13.2	13.4	13.6	14.0	14.2	14.4	15.0	15.5	9.1	9.3
10.1	10.3	11.1	11.3	12.0	12.2	12.4	13.1	13.3	13.5	13.7	14.1	14.3	14.5	15.2	9.0	9.2

15.5を入れた後は問題なく起動しました!

参考記事

filsv/iOSDeviceSupport

Xcode: iOS 15.4 実機ビルドができない!?

XcodeでimageLiteralが出てこない

Xcode 13.2.1でimageLiteralがサジェストで出てこなく、調べてみたら記述方法が変わったとのことです

class ViewController: UIViewController {
    @IBOutlet weak var hoge: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        hoge.image = #imageLiteral()
    }
}

#imageLiteralと記述し、その後に括弧を入れることで画像選択UIが発火します

参考記事

Color Literal behaves inconsistently in Xcode 13.2.1

[WIP]NeoVim + iOSでUIKitやSwiftUIの補完を可能にする

sourcekit lspを追加するところまでは全く問題なかったのですが、

import UIKit
import SwiftUI

などのiOSのUI frameworkが動かない問題が発生し、そのエラーを解消するための方法の記事になります

従って、最初からstep by stepで説明するわけではないので、初期設定を済ませていない方は下記の記事など参考に準備してください🙏

導入がまだの場合はこちらなどを参考に:Vim(NeoVim)でSwift -プラグイン, LSPの導入まで

具体的な方法

とりあえず結論

※注意!!!!M1だとx86_64アーキテクチャで動かない場合があり、arm64に変更が必要かもです

swift build -c release -Xswiftc -sdk -Xswiftc /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -Xswiftc -target -Xswiftc x86_64-apple-ios15.2-simulator

参考記事

UIKitに依存するSwift PackageをVSCodeで開発する

iOS Development on VSCode

How to use SourceKit-lsp with iOS projects

no such module ‘UIKit’

Flutter cameraで枠ありのUIを実装する方法

Flutterでカメラ画面で特定の枠を切り抜いているようなUIの実装で少し時間をとってしまったので、記事にしておいてみました

Stackで重ねる方法など色々チャレンジしたのですが、最終的にclipするという方法に落ち着いたので、おそらくほとんどのケースで通用するのでは?と思ってます🙌

※cameraの導入やcontroller変数定義などは公式ドキュメントに従ってご自身で実装してからこちらの記事の内容を検証してください

// widget部分
SizedBox(
  width: double.infinity,
  height: MediaQuery.of(context).size.height * 0.4,
  child: Stack(
    alignment: Alignment.center,
    children: [
      CustomPaint(
        foregroundPainter: Paint(),
        child: SizedBox(
          width: double.infinity,
          child: CameraPreview(controller.value!),
        ),
      ),
      ClipPath(
        clipper: Clip(),
        child: SizedBox(
          width: double.infinity,
          child: CameraPreview(controller.value!),
        ),
      ),
    ],
  ),
)

// CustomerPaint/ClipPathの引数に当てるclass
class Paint extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawColor(Colors.grey.withOpacity(0.7), BlendMode.dstOut);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

class Clip extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    const horizontalPadding = 40.0;
    const verticalPadding = 180.0;
    final path = Path()
      ..addRRect(
        RRect.fromRectAndRadius(
          Rect.fromLTWH(
            horizontalPadding / 2,
            size.height - (size.height - (verticalPadding / 2)),
            size.width - horizontalPadding,
            size.height - verticalPadding,
          ),
          const Radius.circular(10),
        ),
      );
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return true;
  }
}

参考記事

Add Rectangle overlay in a camera application and Crop the portrait image in flutter

Active InteractionをMPAでハンドリングする方法

普段はAPI開発で利用しているので、Active recordのerrorsに入れるというよりもMapやList形式でエラーを返しているのですが、createが失敗した時のnewをレンダリングする際にちょこっとはまったので記事にしました

まずは最終的なコード

# interaction
module Resources
  module InvoiceInformations
    class Create < ActiveInteraction::Base
      hash :params do
        string :name
        string :zip
        string :prefecture
        string :city
        string :street
        string :building, default: nil
        string :memo, default: nil
      end

      object :current_user, class: User
      validates :current_user, presence: true

      def execute
        invoice_information = current_user.build_invoice_information(params)
        invoice_information.save!
      rescue ActiveRecord::RecordInvalid => e
        # ここ
        e.record.errors.errors.each do |error|
          errors.add(error.attribute, error.message)
        end
        # 公式では下記になってる
        # errors.merge!(e.record.errors.errors)
      end
    end
  end
end

# controller
def create
    outcome = Resources::InvoiceInformations::Create.run(
      params: params_create,
      current_user: current_user
    )
    if outcome.valid?
      @invoice_information = outcome.result
      redirect_to customers_path,
                  notice: t('action_messages.created', model_name: InvoiceInformation.model_name.human)
    else
      @invoice_information = current_user.build_invoice_information(outcome.params)
      # 正直もっと良い方法ありそうだけど、時間の関係上こちらで
      outcome.errors.errors.each do |e|
        @invoice_information.errors.add(e.attribute, e.message)
      end
      render :new
    end
  end

問題点

attributeにbaseが入れられてしまう、が問題です

errors.merge!(e.record.errors.errors) でも当然エラーを返すことができるのですが、errors.merge!に公式の通りのコードを入れると、エラーの対象が特定のカラムではなく全てbaseで表現されてしまいます

煩雑になってしまいますが、丁寧に一つずつエラーをaddすることで目的の挙動を実現できます

※rescue内部でデバッグ実行してclassを確認

公式のコード

<ActiveModel::Error attribute=base, type=blank, options={}>

上記のコードの場合

<ActiveModel::Error attribute=zip, type=blank, options={}>

まとめ

Active Interactionを利用することでusecase的な実装を行うことができるようになり、コードは増えますがより変更に耐えうる実装ができたかと思います

正直Railsを利用するのは否定的ですが、usecaseとして切り出せるのであればまだまだ可能性を感じるので、もうちょい研究とかしていきたいです