システム・PC

importmapを早くもやめる

結局数週間で esbuild+jsbundlingに再移行です。

1. 使えない npm パッケージが多数ある。結局力技で入れるか諦めてCDNとか。最終的にはpublicに配置。

importmapは制限が多すぎます。設計思想としては悪くないのですが時代には早すぎる感じです。このあたりモジュールを読み込んでいくやり方がNode.js と ES Modules(ECMAScript Modules)でのモジュール読み込みの違いが混乱の元凶だと思うんですよね。Node はもともと CommonJS (CJS) という形式で始まりそして普及したけど規格としては独自規格(つまり仕様)。よって筋としては規格であるESMに行くべきなんですがこれに対応していないモジュールが多すぎる。

2. 全てのjavascriptのURLをユーザに知らしめる必要がある。

importmapは<script type="importmap"></script>の中にjson形式で必要とするjsの全ての場所を記す必要があります。ところがimportmap-railsではこのjsonが一つしか用意されていないので、一つのサイトで必要とする全てのjsの位置をユーザに知らしめる必要があります。これは管理者しか使わないjsなども公開する必要があり、あまりよろしくないと思われます

3. minifyとかしにくい

minifyしなくてもいいといえばいいんですが、やはり最低限の難読化はしておきたいところです。結局minifyしようとおもったらesbuildみたいなものの出番になるわけで、ならば最初からesbuild使ってたほうが楽ですよね。というわけで、現時点ではesbuild(的なやつ)+jsbundlingが最強だと思います。これもこれでよくわからないところがあるのだけれど。

IE という異物が消え去った今現れた新たな異物。これがまっさらになるのはいつの日か・・

 

 

rails 8 対応だん

それなりに大変でした。

  • 6.XでWebPackerへ移行したのを戻す

2021年のブログ

webpackerはやめてwebpackを直接使う感じに完全移行せよみたいなブログも多々あるけど、経験的にrailsの道から外れるコードは将来地雷になるんだよなぁ・・というわけでwebpackerでとりあえず素直に実装。噂通りwebpackerの仕組みを理解するのに相当時間かかった。他にもテーマを設定できるようにしてたりしてたんだけど、そもそも自分(と身内)しか使ってないのでその辺りをごっそり削って固定にした。

結局頑張って移行したけどまた戻すことにしました。あの頃の自分に今の現状を教えてあげたいところ。あれだけ苦労して移行したWebpackerが不推奨になるとは思いもしなかったですね。

webpackerが非推奨になったのはwebの進化が大きいと思うんですが、JavaScriptでいろんなことができるようになったしES8あたりから流れが変わった感じがあります。サーバーサイドでなんでもゴリゴリやる時代じゃなくなったんだなぁと。他にもaxiosを削ったらHAMLをErbに書き換えたりしています。HAMLは登場時はとても便利だと思ったんですが、bootstrapのHTMLのコピペとかができなくて結局不便やんという感じです。ゴリゴリHTML書くerbのほうがマシですね。

axiosはfetchに置き換えました。

Mathjaxとかgemでのサポートが終わってしまったシステムは破棄して、直接jsを読み込むように作り直しました。欲しい機能はだいたいjsで供給されるしgemの対応が遅いから、直接持ってきた方が早い。

今後の方針としてはなるべくgemは使わない。これを肝に銘じて運用していこうと思います。

Time Machineがバックアップできなくなった話

 

昨日の朝から、MacのTime Machineでバックアップができなくなってしまいました。エラーメッセージには、次のような内容が表示されます。

「クラウド上のiCloud Drive個のファイルの同期が完了していないため、Time Machineでバックアップできません。」

最初は「なんだこれ?」と思いながらも、いろいろと試してみました。

 
まず以下のことを確認しました。

  1. iCloud Driveの同期は(多分)すべて完了している
     Finder上では同期マークも出ていませんでした。
  2. iCloud Driveを一時的にオフにしてみる
     → エラーの内容は変わらず、バックアップは失敗。
  3. Apple IDからサインアウトしてみる
     → これでも状況は改善しませんでした。
  4. フォーマットしたばかりのHDDで試してみる
     → やはり同様のエラーで止まります。
  5. 他のMacで試してみる
     → こちらでは問題なくバックアップが完了。つまり、クラウド側の問題ではないようです。

正直、ここまでやっても原因がまったくわからず「もうお手上げだ…」という気持ちになりました。

Appleのサポートページなどには「同期には数日かかることがあります」と書かれていましたが、数日待てば本当に直るのかも疑問です。そもそも「iCloud Drive個のファイル」って何なんだ……?とモヤモヤが募ります。ちなみに、Macはずっと電源が入った状態で運用しています。

そんなこんなんで、いろいろ試行錯誤した結果、ようやく原因がわかりました。

実は、同じMacにある妻のアカウントで久しぶりにログインがあったことがきっかけでした。そして、その妻のアカウントのiCloudのセッションが切れていたのです。

どうやら、Time Machineは他のユーザーアカウントのiCloud同期状態も参照しているようで、そこが未同期のままだとバックアップ全体が止まってしまうようです。

妻のアカウントでApple IDからサインアウトしたところ、ようやく同期が開始され、Time Machineのバックアップも通るようになりました。

 今回は、自分のアカウントではなく「別のアカウントのiCloud同期」が原因だったため、なかなか気づけず、原因特定までにずいぶん時間がかかってしまいました。

同じような症状で困っている方がいましたら、別ユーザーアカウントのiCloud設定も確認してみると、解決の糸口になるかもしれません。

 

pythonのdatetimeのtzinfoについての話

pythonのdatetimeはnativeとawareという二つのものが存在します。端的にいうと
nativeとは純粋に日付と時間を表したもので年月日時分秒を表したものということでよいかと思います。
2023/4/1から2023/8/24まで何秒か?なんて計算をするときに有効です。

一方awareはその地域における時間で例えば日本で9時、イギリスで1時、アメリカで何時のようなもので、
その地域の壁時計時間が何時かなどという処理をするときに有効です。

何も考えずにdatetimeオブジェクトを作成するとnativeになります。tzinfoにtimezoneを表すオブジェクトを引き渡して作成をすると
awareになります

from datetime import datetime, timezone
datetime(2023,10,1) #native
datetime(2023,10,1, tzinfo=timezone.utc) # aware 

nativeとawareはその用途や目的が違うので相互に演算をすることが出来ません。演算しようとするとエラーとなります。

さておよそPCの時間というのは所謂epochで管理されています。epochというのは
1970/1/1 00:00:00を起源としそこからの経過秒数です。これは閏秒を考慮していないUTCでtime()関数の戻り値でもあります。

この閏秒を考慮していないというのが非常に快適で今現在のepochに3600*24を足せば必ず明日の同時刻になります。
UTCとのずれに「分」がない地域では3600で割り切れる時間は必ず正時になります。
日本はUTCから9時間進んでいるので3600*24で割ったあまりが3600*9ならばそれは夜中の0時ということになります。

以下awareなdatetimeについて主に話を進めます。pythonのtimezoneはtzinfoというクラスで管理されているのですが
このクラスはインスタンス化できない仮想クラスで長らく実装されたクラスは標準で datetime.timezone.utcしかありませんでした。
そのためpytzというモジュールを利用したりしていたのですが、最近ではzoneinfoというモジュールが追加されこの辺り使いやすくなっています。

試しにAsia/Tokyoでawareな時刻を作成してそのepochを取得してみたりtimedeltaを足したりして時間を進めてみたりしてみます。
datetimeからepochを得るにはtimestamp()メソッドを使います。
datetimeにtimedelta(seconds=3600)を足すと1時間先の時刻になることがtimestamp()の結果からわかると思います。

from zoneinfo import ZoneInfo
from datetime import datetime, timedelta

TZ = ZoneInfo('Asia/Tokyo')
delta = timedelta(hours=1)
d = datetime(2023, 3, 26, 0, 0, 0, tzinfo=TZ)
print(d, d.timestamp())
d += delta
print(d, d.timestamp())
d += delta
print(d, d.timestamp())
d += delta
print(d, d.timestamp())

実行結果

2023-03-26 00:00:00+09:00 1679756400.0
2023-03-26 01:00:00+09:00 1679760000.0
2023-03-26 02:00:00+09:00 1679763600.0
2023-03-26 03:00:00+09:00 1679767200.0

同じプログラムをAsia/TokyoではなくEurope/Zurichで動かしてみます。すると以下のような結果が表示されます。

2023-03-26 00:00:00+01:00 1679785200.0
2023-03-26 01:00:00+01:00 1679788800.0
2023-03-26 02:00:00+01:00 1679792400.0
2023-03-26 03:00:00+02:00 1679792400.0


3/26の03:00から+02:00となっているのがわかるかと思います。これはチューリッヒでは2023/3/26の深夜に夏時間に移行するからです。
そしてさらに詳しくみてみるとepochが1679792400.0の1時間後がまた1679792400.0になっています。つまりepoch的に全く進んでないことがわかります。

サマールールへの移行のルールを詳しく書くと

2023年は3/26の01:59の次は02:00ではなく03:00(そして時差+02:00)です。つまり上記の出力結果である

2023-03-26 02:00:00+01:00 1679792400.0

は存在しない時間です。ですがpythonでは出力されてしまいます。どうもpythonはawareであっても純粋にhourに1を足してしまうようで、その結果epochが進まないという事態になっているようです。
これは結構トラップな気がします。

個人的にはtimedelta(hours=1)をawareな時刻であっても足したらawareの時刻において1時間先になってほしいところですがpythonはそうはなっていないようです。

なおこのプログラムを以下のように書き換えepochで管理をするように務めると

ts =  1679785200
d = datetime.fromtimestamp(ts, tz=TZ)
print(d, d.timestamp())

ts += 3600
d = datetime.fromtimestamp(ts, tz=TZ)
print(d, d.timestamp())

ts += 3600
d = datetime.fromtimestamp(ts, tz=TZ)
print(d, d.timestamp())

ts += 3600
d = datetime.fromtimestamp(ts, tz=TZ)
print(d, d.timestamp())

以下のように期待した出力が得られます

2023-03-26 00:00:00+01:00 1679785200.0
2023-03-26 01:00:00+01:00 1679788800.0
2023-03-26 03:00:00+02:00 1679792400.0
2023-03-26 04:00:00+02:00 1679796000.0

これらの実験は全てpython3.9での実行結果です。

rubyのhttpclientでletsencryptの証明書を保有するサイトに接続できなくなった

例えばこんなプログラムでlet's encryptで証明されたサイト(例えばこのブログ)にアクセスするとエラーになるようになってしまいました

require 'httpclient'
HTTPClient.new.get('https://ror.hj.to/ja/issei')
Traceback (most recent call last):
	17: from httpclienterror.rb:2:in `<main>'
	16: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient.rb:743:in `get'
	15: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient.rb:856:in `request'
	14: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient.rb:1014:in `do_request'
	13: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient.rb:1133:in `protect_keep_alive_disconnected'
	12: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient.rb:1019:in `block in do_request'
	11: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient.rb:1242:in `do_get_block'
	10: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/session.rb:177:in `query'
	 9: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/session.rb:511:in `query'
	 8: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/session.rb:748:in `connect'
	 7: from /usr/local/Cellar/ruby@2.7/2.7.4/lib/ruby/2.7.0/timeout.rb:105:in `timeout'
	 6: from /usr/local/Cellar/ruby@2.7/2.7.4/lib/ruby/2.7.0/timeout.rb:95:in `block in timeout'
	 5: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/session.rb:752:in `block in connect'
	 4: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:26:in `create_socket'
	 3: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:26:in `new'
	 2: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:41:in `initialize'
	 1: from /usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:103:in `ssl_connect'
/usr/local/lib/ruby/gems/2.7.0/gems/httpclient-2.8.3/lib/httpclient/ssl_socket.rb:103:in `connect': SSL_connect returned=1 errno=0 state=error: certificate verify failed (certificate has expired) (OpenSSL::SSL::SSLError)


httpclientを使わずにnet/https等を利用すると問題なく接続できるようで、いろいろ調べていくとhttpclientが独自の信頼できる証明書を保持しているためということがわかりました。以下のようにOpenSSLのデフォルトの証明書を利用するようにすると動作します。

require 'httpclient'
HTTPClient.new{self.ssl_config.add_trust_ca(OpenSSL::X509::DEFAULT_CERT_FILE)}.get('https://ror.hj.to/ja/issei')

HTTPClientは2015年のバージョンを最後にアプデートされていないようなので別なgemを使うべきなのかもしれません。個人的にはhttp.rbがいい感じな気がします。

rails6.1.3 対応だん

それなりに大変だった。

  1. jQueryの完全削除
  2. WebPackerへの移行

webpackerはやめてwebpackを直接使う感じに完全移行せよみたいなブログも多々あるけど、経験的にrailsの道から外れるコードは将来地雷になるんだよなぁ・・というわけでwebpackerでとりあえず素直に実装。噂通りwebpackerの仕組みを理解するのに相当時間かかった。他にもテーマを設定できるようにしてたりしてたんだけど、そもそも自分(と身内)しか使ってないのでその辺りをごっそり削って固定にした。

動的にテーマを実装するためにrailsのcoreの中をハック溶かしてたのだけど、これが地雷になりつつあるので削除して正解だったと思う。railsの道から外れるコードは削除しておく。これが鉄則。

そして、地味に取り掛かってるのがhamlの削除。hamlは事実上railsでしか使われていないので別なサイトから見本をコピペってくるときに面倒くさい。全部erbにする予定。

さらにform_builderとかもbootstrapのコードをコピペってくるときに面倒なので削除する方向。text_field_tagとかHTML生成するためにどういうヘルパーがあってどういう仕様だったかとか思い出すのも大変。直接買いておいた方が結局メンテナンス性が高いと結論。そういう流れで。

新型のMac Book Air購入したら初期不良をつかみました

1png

2020年3月に発売になったMac Book Air。 今使っているMac Book AirがCatalinaに非対応故そろそろ新しいのを買いたいところだったのでポチってしましました。 それが3/21の話。その時点でのお届け予定日は4/9でした。

ブツは実際予定通り4/9に到着しました。 しかしながらこのMac Book Air。キーボードの「S」が超反応することに気がつきました。ちょっと触っただけで文字がタイプされてしまう。 Sはホームポジションに近いこともあり指のタッチなど頻繁にあるので、流石にこれは困る。これは初期不良では無いか?ということでオンラインストアのサポートに電話しました。

しかし、コロナウィルスが蔓延しているような状況で中々サポートに繋がりません。あまりに繋がらないのでApple Storeに相談仕様にもコロナ禍で全店閉まってるし、ヨドバシカメラの上にあるMac修理の正規代理店に持ち込もうとするも予約が一杯。もうこれは意地でもオンラインストアに繋ぐしか無い。しかし、この返品のためのサポートに中々繋がらない(ちなみにサポートはチャットという手もありますが返品は電話のみです)。結局9日のコンタクトは諦め次の日に再度電話をしました。

次の日は40分くらい電話口で待たされた後サポートに繋がり、返品・交換の手続きを終えることができました。 返品の回収は早かったですね。指定の運送会社が次の日に取りにきました。そして、4/13に代替品の手配をしたとの連絡。

ところがこの代替品の手配からが長い長い。当初4/21〜4/28に配送予定ということでしたが、待てど暮らせど配送ステータスが配送にならず、結局届いたのは4/30でした。実に購入をしてから40日。 

しかも今回古いMacを下取りに出しており、送ってしまってからキーボードの不良に気がついたので、しばらくMacのノートが無い状態。別のMac miniがあるから事無を得ましたが危うく詰むところでした。

コロナ禍で国際便で発送されるもの買ったらダメですね。流通が正常になってからじゃないと何かあった時に非常に困る。

rails5.2.1 対応だん

このサイトのバージョンを 5.2.1にあげました。

最初に作ったときのバージョンは1.3.6。

そこから上げつづけて最新版に追いつきました。ただ上げただけではなく、

  • tubolinksに対応
  • jquery-ujsの成仏 (rails-ujsに移行)
  • もうサポートされていないgemの大量成仏

など対応したところは数しれず。感無量だな。

いい加減 SSL化をしてみた

アドレスバーに鍵のマーク

ま、Let's Encryptですけどね。

VAIOを Windows10にしてみたら動かなかった話

そもそもVAIOは公式にWindows10のアプグレードはちょっとやめとけと言ってるのでアプデートは完全な自己責任なわけですが、右下に「入手できます」というアイコンが表示されたり「Windows10にしたよ。ひゃっほー」などというつぶやきなどを見たりするとアプグレードしたくなるというのが人情というもの。ということでアプグレードしてみました。ちなみにオイラのVAIOはWindows8プレインストールモデルのType E14(SVE14A3AJ)です。

多分簡単にいくだろうと思ってたのですが60%ほど進行したところでブルースクリーン(INTERNAL POWER ERROR)。ここでセットアップが中断し、またWindows8.1に戻るという状態になりました。

いろいろググッてみると、どうもAMDのグラフィックボードが悪さをしているようです。このVAIOには AMD Radeon HD 7600Mと Intelの HD Graphics 4000というグラボが登載されているのですが、このAMDのほうのドライバがどうもよろしくないようです。

最新版とかにしても上手くいかないのでBIOSから(電源を切ったのち、ASSISTボタンでPC起動する)で、discrete graphics adapterを disabledにしたら Windows10にアップグレードできました。