同じにやっても動かない

書いてある通りやっているのに動かないのです

ActiveStorage の variant がずっとnilを返す

結論から先に書くと、添付ファイルを設定しているmodelの主キーをstringに設定していることが原因でした。

ActiveStorage用に生成されるmigrationファイルではpolymorphic関連先のid (active_storage_attachments tableのrecord_idカラム) がintegerであることを前提にしています。
なので以下のように t.reference で外部キー制約を設定しています。

# 20230328082729_create_active_storage_tables.active_storage.rb

t.references :record,   null: false, polymorphic: true, index: false, type: foreign_key_type

今回、主キーをstringに設定したProduct モデルで

# model/product.rb

has_one_attached :image

としていたので、以下ステップで関連が壊れていました。

  • step.1 integerカラムのrecord_id にstringのprodct.id が渡される
  • step.2 active_storage_attachments tableレコードにsaveする際にrecord_idが0に変換され保存される
  • step.3 関連が壊れているのでvariant発行時のクエリでが添付ファイルを見つけられず nil を返す

他のモデルでもactiverecordを使うかもしれないのと、Productモデルのidをstringにする必要もなくなっていたので、今回はProductモデルの主キーをdefaultのintegerに戻して解決としました。

いやー
ハマった

Arcstar Smart PBX の端末にGRANDSTREAMのIP電話を設定する

ArcStart Smart PBXを使っています。
契約時に設定済みのIP電話 (Panasonic KX-HDV130) を1台買っていたのですが、新しい事務所ができたので追加で欲しくなりました。
調べてみるとKX-HDV130は中古しかなかったのと中古でも少々高かったので、Amazonでたくさん売っている GRANDSTREAMのIP電話を買って設定してみることにしました。

ちょっとハマったのですが以下で繋がりましたのでチャレンジされる方の参考になれば幸いです。

接続したIP電話

GRANDSTREAM GXP1615www.amazon.co.jp

まずは設定画面に入る

DHCPIPアドレスが振られます。
液晶ディスプレイ下にある3つあるボタンの左端を1回押すと、ディスプレイにIPアドレスが表示されるはずです。

adminでログイン

初期設定では user名:admin / パスワード:admin でログインできるはず。
ログインするとパスワードの設定を促されるので、好きなパスワードに変更します。


アカウント → アカウント1 → 一般設定 でSIPサーバー情報を入力

アカウント名 / 名前

液晶ディスプレイに表示される名前です。内線番号とかで良いと思います

SIPサーバー

ここではまりました。
Arcstart Smart PBXの管理画面で表示されるSIPサーバー名 (FQDN)を入力してもなぜか名前解決できないようで、nslookupで調べてIPアドレスを直接指定する必要がありました。
ポートもwell know port番号ではなく "35790" を指定する必要があるので注意。
参考:Arcstar Smart PBX接続条件について (NTTコミュニケーションズ株式会社)

SIPユーザーID

Smart PBXのユーザID

認証パスワード

上で入力したSmart PBXユーザID用のパスワード

その他は入力なしでOKです。

これでつながるはず

SinatraでActiveRecordを使ってMigrationしたい on docker-compose

1. docker-compose.ymlの修正

Sinatraが動いているコンテナとdb (postgresql) が動いているコンテナをリンクできるようにする。
具体的にはSinatraコンテナ設定に enviroment, depends_on, links を追加

version: '3.4'

services:
  sinatra_server:
    image: sinatra
    build:
      context: .
      dockerfile: ./Dockerfile
    volumes:
      - ./db:/app/db
    ports:
      - 26166:26168
    environment: # デプロイ環境切り替え用
      - APP_ENV=development
    depends_on: # postgresqlコンテナがアップしてから起動
      - db # database コンテナの名前で指定
    links: # sinatra_serverとdbをリンク
      - db # database コンテナの名前で指定
  db:
    image: postgres:15
    ports:
      - 5432:5432
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
    volumes:
      - dbdata:/var/lib/postgresql/data
volumes:
  dbdata: # データ永続化用ボリュームコンテナ

2. SinatraからActiveRecord経由で使うDBを作成

# コンテナ起動
$ docker-compose up -d

# クライアントソフトから接続
$ psql -h localhost -U postgres -d postgres

# USER作成
postgresql# CREATE USER sinatraserver WITH PASSWORD 'postgresql';

# DB作成
postgresql# CREATE DATABASE sinatra_db OWNER = sinatraserver TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C' LC_CTYPE = 'C';

3. database.ymlを用意

configディレクトリを掘ってその中に2で作ったDBと整合するconfig/database.ymlを作成

development:
  adapter: postgresql
  encoding: utf8
  collation: C # LC_COLLATE
  ctype: C # LC_CTYPE
  database: sinatra_db
  host: db
  username: sinatraserver
  password: postgresql

4. Ruck up設定ファイル (config.ru) にActiveRecord設定を追加

# gem の bundle
require 'rubygems'
require 'bundler'
Bundler.require

# ActiveRecord設定
ActiveRecord::Base.configurations = YAML.load_file('config/database.yml')
ActiveRecord::Base.establish_connection(ENV["APP_ENV"].to_sym) # to_symでシンボルに変換しないとエラーになる

# 起動ファイル
require './server.rb'

# 起動
run Sinatra::Application

これでDBとの接続設定は終わり。

4. rakeコマンドを使えるようにRakefileを作成

ActiveRecordのコマンドを使えるようにRakefileをアプリケーションrootに作成。

require 'sinatra/activerecord'
require 'sinatra/activerecord/rake'

5. migrationファイルを生成

# Sinatraコンテナに入る
$ docker-compose up sinatra_server bash

# migrationファイル生成rakeコマンドを発行
#  volumes:   - ./db:/app/db でマップしているので、このコマンドでmigrationファイルがホスト側のdbディレクトリに出てくる
sinatra_server# bundle exec rake db:create_migration NAME=new_table

6. migrationファイルの中身を書く

ホストPCで5で生成されたmigrationファイル(db/migrate/yyyymmdd*****_new_table.rb)の中身を書く。
書き方はRailsのmigrationドキュメントに詳しく書いてあります。

7. migrate 実行

# Sinatraコンテナに入る
$ docker-compose up sinatra_server bash

# migrateコマンド発行
sinatra_server# bundle exec rake db:migrate
== 20221124062056 CreateNewTable: migrating =============================
-- create_table(:new_tables)
   -> 0.0088s
== 20221124062056 CreateNewTablesTable: migrated (0.0089s) ====================

参考記事

alterbo.jp
qiita.com

sinatra on docker-compose を Rubymine でデバッグしたい

Railsしかいじったことがないのですが、Stripeのサンプルアプリsinatraで書かれていたのでdocker-composeで動かしてみました。
そこまではわりとすんなり行ったのですが、Rubymineによるデバッグ環境構築で手こずったので備忘録。

環境

  • IntelMac mini
  • OSはMonterey
  • リモート環境のRubyはver3.0.0 (最新じゃないのには理由がある。後述)
  • Rubymineのバージョンは2021.2.4
  • dockerdはRancher Desktop (ver 1.6.1)

Stripeのサンプル動かすのにはまったところ

Webドキュメントには "ruby server.rb" で起動しろと書いてあるのですがこれではだめで、サンプルアプリzipのReadmeにある "ruby server.rb -o 0.0.0.0" が正解です。


もういっこ。
vscodeのテンプレートからDockerfileとdocker-compose作った場合、Gemfile.lockを先に作っておかないとDocker buildでこけます。
今回はローカル環境を使わないので、以下のようにしてリモート環境 (Docker) で作ったGemfile.lockをローカルにコピーします (Gemfileの内容は下記参照)。

$ docker run --rm -v "$PWD":/app -w /app ruby:3.0.0 bundle install

Rubymineでデバッグするための準備

debug-ruby-ide と debase の2つのgemがインストールされている必要があります。
ただ2022.10.25現在、私の環境では3.0.0より新しいrubyだとdebug-ruby-ideのインストール失敗するので、rubyのimageには3.0.0を指定しています。
それと、3.0.0以上のRubyを使う場合、debaseのバージョンは0.2.5.beta2 以上でないとダメだそうです。

DockerfileとGemfileはこんな感じ。

Dockerfile

# ruby:slimだと gem isntallの native extensionのbuildで失敗する
# 3.0.0 より新しいと debug-ruby-ide.gem のインストールで失敗する
FROM ruby:3.0.0

LABEL Name=checkout_test Version=0.0.1

# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1

WORKDIR /app
COPY . /app

# gem install時の develpment tools first 系エラー用
RUN apt-get update
RUN apt-get install -y ruby-dev

COPY Gemfile Gemfile.lock ./
RUN bundle install

CMD ["ruby", "server.rb", "-o", "0.0.0.0"]

Gemfile

source 'https://rubygems.org/'

gem 'sinatra'
gem 'stripe'

# Ruby 3.0 以降はWEBrickが入っていないようなので、アプリケーション・サーバーにpumaをインストール
gem 'puma'

group :development do
  gem 'debase', '>= 0.2.5.beta2'
  gem 'ruby-debug-ide'
end

Rubymineの設定

※ docker-compose up -d で普通に動くところまでできているとします

docker-compose 実行環境の設定

RunやDebug時に、ローカル環境のrubyやgemではなくdocker-composeで起動するDockerコンテナ内のRubyで実行されるようにします

1. ツールバーの Run → Edit Configuration で設定ダイアログを表示させ、docker-composeの新しいconfigurationを開く


2. Docker serverを設定


3. docker-ompose.ymlのありかを設定


Ruby SDK and Gems で新規リモート環境 (dockerコンテナ) のSDKを指定

1. ツールバーのRubyMine → Preference → Language & Frameworks → Ruby SDK and Gemsで "New remote"を選択


2. remote環境の設定


3. プロジェクトルートのマッピング設定


4. リモート環境 (Dockerコンテナ) と Rubymineを同期


Ruby 実行環境の設定

RubymineはRailsと違ってsinatraようのプリセットを持っていないので、RubymineでSinatraをdebugするときはruby scriptとして起動する必要があります。
なのでその実行環境を設定します。

1. ツールバーの Run → Edit Configuration で設定ダイアログを表示させ、Rubyの新しいconfigurationを開く


2. Stripeのサンプルアプリ (sinatra) 起動コマンドを設定


デバッガー起動の前に微調整

上で設定したrubyの実行環境設定でデバッガーを起動すると、Rubymineがport設定を以下のように上書きしてsinatraが起動します。

ports:
    - 1234:1234
    - 26166:26168

最初の1234はRubymineのデバッガーが使うようなので無視。
次の26166 (local):26168 (remote) が罠です。

ホストでは26166をlistenしてリモートの26168に繋ぐということなんですが、
Stripeのサンプルアプリはport 4242をlistenするようになっているのでこのままだとブラウザの要求が届きません。
なので下記のように変えてあげる必要があります。

  • Sinatraはリモート環境 (docker) で動いているので、26168をlisten
  • jsのRedirect (決済完了後に発生) はホスト環境のブラウザで発生するので 26166を指定
  • デバッガー動かさないときも同じように動かせるように、docker-compose.ymlも最初から 26166:26168を入れておくとよい

具体的には以下のようになります。

server.rb

require 'sinatra'
require 'stripe'
# This is your test secret API key.
Stripe.api_key = ''

set :static, true
set :port, 26168 # ← 4242から26168に変更

public/checkout.js

const { error } = await stripe.confirmPayment({
  elements,
  confirmParams: {
    // Make sure to change this to your payment completion page
    return_url: "http://localhost:26166/checkout.html", // ←Redirect先のポート設定を4242から26166に変更 (26168じゃないので注意)
  },
});

docker-compose.yml (port設定)

ports:
    - 26166:26168

これで必要な設定全部終わりです。

デバッガーの起動

起動方法はいろいろありますが、ここではメニューバーの Run → Debug から、先程作ったruby実行環境を選択してデバッグ開始します。

これでブラウザから localhost:26166 (26168じゃないよ) にアクセスすると、こんな感じでちゃんとブレークポイントで止まって変数も読めます。

PICを消去も書き込みできない子にしてしまった

やってしまいました。
なーんも考えないで参考コードをコピペコンパイル書き込みしたが最後、EraseもProgrammingもできない子になってしまいました。

私の環境は PIC12F675 + PICKIT4 ですが、10年以上前から同様の問題に関する質問が見つかるのでデバイスに関わらず共通の罠なんだと思います。
この状態になってしまうのは、CONFIGレジスタの設定を "内部クロック + /MCLRピンOFF (外部リセットを使わずにGPIOとして使用)" にしたFWを焼いた時です。

MCCが吐くコードだと以下の状態です。

#pragma config FOSC = INTRCCLK
#pragma config MCLRE = OFF

なんでこうなるか

PICKITからReset制御ができないため、VPPの電圧がプログラミング電圧 (9V) になるまえにプログラムが走ってしまう (Resetが解除されてしまう)からと思われます。

復旧方法

step.1

/MCLRピンを9Vでプルアップする。
私は50kでプルアップしましたが10kでも良いと思います。

step.2

MPLABでPICKITからの給電に設定してFW書き込み。

f:id:nyarugo:20210724235405p:plain
MPLAB XIDE のProject Property画面

これでPICが立ち上がる前に確実にVPPがプログラム電圧になるので、無事消去と書き込みができるようになります。
新しく書き込むFWでは必ず MCLRE=ON にしましょー

Cocoonでhas_oneな関連モデルのフォームを作りたいとき

ひさびさのRailsネタ。

has_manyな関連モデルを一挙に 生成 / 更新 / 削除 できるフォームを作るのにとても便利なcocoon
has_oneな関連モデル用フォームでも使いたいのだけど、link_to_add_association ヘルパーを普通に使うとうまく動きません。

以下のように :force_non_association_create オプションを入れると期待どおりに動くようになります。

= link_to_add_association('add something', @form_obj, :comments,
    :force_non_association_create => true)

実際の動作は以下の感じです。

  • 保存済みのレコードが無い場合は新規作成
  • 保存済みのレコードがある場合は最後にlink_to_add_associationで追加された内容で登録済みレコードを更新

※ 参考サイト
github.com

Amazon DashボタンでiTunesを操作するぞ

うちにもDashボタンが来たので何かしてみようということで、

気分じゃない曲がかかったらDashボタンで次の曲へスキップ

をやってみることにしました。
超簡単だよ!

まずは以下サイトを参考にボタン検知できるようにします (takustaquさんあざます!)。

qiita.com

次に iTunesを操作するライブラリをインストール。

$ npm install --save itunes-remote

最後に上記サイトのtestコードに3行追加するだけ!

const DashButton = require("dash-button"); 
const PHY_ADDR = "xx:xx:xx:xx:xx:xx"; 

// 追加コード1行目
const itunesRemote = require('itunes-remote');

Let button = new DashButton(PHY_ADDR);

button.addListener(() => {
   // 追加コード2行目
    itunesRemote("play", () => {});
   // 追加コード3行目
    itunesRemote("next", () => {});
});

iTunesウィンドウに移動しなくてもDashボタン1プッシュでスキップじゃ!