とーますメモ

Ruby on Rails / Goなどの学習メモ

【Ruby】Rubyバージョンが影響して、小数点計算が合わなくなることがある

以下の記事を見るとどうやらrubyのバージョンが2.4未満だとround関数が正しい値を返さないことがあるらしい。

irb(main):001:0> 1024.975.round(2)
=> 1024.97
irb(main):002:0> 1023.975.round(2)
=> 1023.98

Ruby - roundで四捨五入にならない場合の理由を教えてください|teratail
RubyとPythonとC#のround関数のバグっぽい挙動について - hnwの日記
floating point - Ruby Float#round behaviour change after update - Stack Overflow


以下の記事によると2.4未満のバージョンを利用しているときは、BigDecimalでroundすれば良さげ。

irb(main):001:0> BigDecimal('1024.975').round(2).to_f
=> 1024.98

floating point - Float round bug in ruby? - Stack Overflow

【Rails】Quickbooks Web Connector(QBWC)を使って、RailsとQuickBooks Desktopを接続してみた

中小企業会計ソフトの分野で、日本ではfreeeやマネーフォワードが普及しているが
海外ではQuickbooksが広く普及している。
このQuickbooks内に外部からデータをRailsアプリ経由で追加することで入力作業を省き、業務効率を上げようという話になった。

前提としてQuickbooks Onlineではなく、Quickbooks DesktopというWindowsアプリを使う。Onlineを使用している方にはこの記事は不要。
Quickbooks Onlineだと、すごく充実したREST APIドキュメント
あるので開発も簡単だが、Quickbooks Desktopでは「Quickbooks Web Connector(以降QBWC)」を使う。

色々と調べる中で、当たり前だが日本語の情報が壊滅的になかったので、将来の自分用に調べた内容をこの記事に残す。

QBWCの使い方

・QBWCは、QuickBooks Desktopがインストールされている端末にインストールする。
・APIはRESTではなく、SOAPという古いタイプのAPIで、XMLをやり取りすることでリクエストを行う。

以下のPDF内で、なぜQBWCは、シンプルなWeb APIではないのかについて説明があるが
一言でいうと、セキュリティを確保するため、不特定多数が使用するインターネット上からのアクセスを防ぎたいという意図があった模様。
※だったら、QuickBooks Onlineはどうなんだという疑問はあるが。
https://developer-static.intuit.com/qbSDK-current/doc/PDF/QBWC_proguide.pdf

・Railsとのやり取りの流れは以下
1)RailsでSOAP APIへのリクエスト(Job)を作成する
2)QBWC側からRailsにアクセスし、Jobがあればそれを実行する。
つまりRails側からJob実行のキューは送れない。Job実行のトリガーは必ずQBWCから。

・RailsでQBWCとやり取りするには、qbwcのgemを使用する。
github.com

Rails gem(qbwc)の使い方

・基本的な設定や使い方については上記の公式サイトを参照
・作成できるJobは「読込」と「書込」のどちらも可能
・Job作成にあたり、リクエスト用のHashを作成するのだが、それには以下のドキュメントを使用する。
しかし使いにくいし、説明不足感が否めない。

developer-static.intuit.com

このドキュメントを使用し、リクエストHashを作成する上で大事な点は以下
・Railsで使用するフォーマットは「qbXML」。
・QBEditionsは使用するQuickBooks Desktopの国を設定すること。
ドキュメントのXMLの「順番」は必ずそのとおりにすること。普通のHashで書くように、KEYの順番を入れ替えて作成すると「Parse Error」を返す。
・書込の際に「AMTTYPE」の値を送信する場合は、小数点第二位まで含んだ文字列を設定する必要がある。
※ ドキュメントにも書いてない。

✗: 123.4
○: 123.40

https://help.developer.intuit.com/s/question/0D50f00004rTfc6CAC/qbxml-amount-invalid-cant-convert
https://help.developer.intuit.com/s/question/0D5G000004Dk7HIKAZ/invoice-amount-error-3040-what-am-i-doing-wrong

・参照系のリクエストを送るときに、膨大なデータをリクエストするときは、一度にリクエストを送らず「Iterator」を使用したコードを作成すること。

自分は使用してはいないが、恐らく以下の記事を見ると、

最初のリクエストは以下のようにiteratorにStartを指定し

...
:sales_order_query_rq => {
  {:xml_attributes => { 'iterator'  => "Start" }}
}
...

それ以降のリクエストでは、iteratorにContinueを指定し、最初のリクエストの返り値であると思われるIteratorIDを設定する模様。

...
:sales_order_query_rq => {
  {:xml_attributes => { 'iterator'  => "Continue", 'iterator_id' => iteratorID }}
}
...

https://github.com/consolibyte/quickbooks-php/blob/master/docs/web_connector/example_web_connector_import.php#L214
https://github.com/Natejd04/QBWC_Test/blob/d23b42738eae05844e0e23375621fa48809ddb77/app/workers/z-customer_order_worker.rb
https://stackoverflow.com/questions/21757478/iteration-samples-in-quick-books-using-web-connector


未確認だが、使用する際に試したい。

・Mod系のAPI使用時に注意する点
例えば、Invoiceの内容を変更するInvoiceModを使用するとしよう。その時前回に送ったInvoiceのItemLine内容に、新しいItemLineを追加する必要があるとする。
InvoiceAddのAPIならば、InvoiceLineAddがあり名前の通りに、Itemを追加するだけでよいが
InvoiceModのAPIは、InvoiceLineModという変更前提のものしかない。。。それでどうするか。。。

「TxnLineIDを-1」に設定すれば良い
言わずもがなドキュメントにはこの記載はない。

またこのInvoiceModを使用時には、以前登録したデータも追加しないと、前のデータを上書きするみたいな内容になるので注意。

https://help.developer.intuit.com/s/question/0D50f00005gIodbCAC/how-can-i-get-txnid-and-editsequence-by-same-inovice-date-invoice-on-qb-for-checking-whether-add-new-invoice-or-add-new-line-in-same-invoice-like-an-attachment-and-how-can-i-add-new-line-in-same-invoiceaspnet-c
https://stackoverflow.com/questions/32817070/how-to-modify-an-invoice-in-quickbooks-using-qbxml-and-qbsdk13

・処理が遅いとき・・・
Rails側からデータを送信したときに、リクエストをQBWC側で受け取るのは早いが、それからの処理がものすごい遅いことがあった。
レジストリをいじって、キャッシュやログ出力の抑制などを試したが、あまり効果がなく、色々試して最後に効果があったのが
できるだけ、リクエストに必要なパラメータは全て送ること。

試したのは、InvoiceAddのAPIに、InvoiceLineAddで50個のデータを追加して送るとなんと3分もかかっていた。
またアイテムを追加するたびにn²で遅くなって行く模様。。。
このとき追加した情報はItemRef, Quantity, Amountの3つ。
そしてここにSalesTaxCodeRefを"E"で追加したら、20秒で処理が終わった。
恐らく送れるパタメータはできるだけ送ったほうが、QuickBooks Desktop側での処理をしなくてもよい?のかもしれない。

【Python】Flaskの本番環境について調べてみた

Flaskにはbuilt-inサーバがついているので、開発には全く困らないが
公式サイトを見ると以下のように記述があるので、本番環境は別に構築する必要がある。

While lightweight and easy to use, Flask’s built-in server is not suitable for production as it doesn’t scale well and by default serves only one request at a time.

by Deployment Options — Flask Documentation (1.1.x)

調べてみたが大体の本番環境構成は以下。

1)Flask + nginx + uWSGI
2)Flask + nginx + Gunicorn
3)Flask + Apache + mod_wsgi

自分の用途はとりあえずそれなりの本番環境が簡単に作れれば良いぐらいの考えなので
Flask + nginx + Gunicornの構成が一番簡単で良さげ。

参照した記事は以下
Which WSGI server should I use? — Django deployment
https://www.airpair.com/python/posts/python-servers
Web Applications & Frameworks — The Hitchhiker's Guide to Python
fcgi vs. gunicorn vs. uWSGI - Peterbe.com
Gunicorn vs uWSGI | What are the differences?

中でも以下の説明がわかりやすくてよかった。

Gunicorn and uWSGI Gunicorn and uWSGI are two different application servers.
Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It is very simple to configure, compatible with many web frameworks and its fairly speedy. This article by digitalocean shows how to configure gunicorn with nginx.


uWSGI is another option for an application server. uWSGI is a high performance and a powerful WSGI server. There are many configuration options available with uWSGI. This article by digitalocean shows how to configure uWSGI with nginx.


What I use I use Nginx because it is fast , light and I find the configuration to be much easy. Gunicorn is very simple to configure. So I use gunicorn. uWsgi is also used a lot instead of gunicorn.

一番高いパフォーマンスを目指すならuWSGIが良いけど、ただ設定が複雑。
それなりにちゃんと動作し、それなりに速く、設定がシンプルにものを選ぶならGunicorn。
他の記事を見るとWindowsサーバだとApache + mod_wsgiの一択っぽい。

ということで自分は上記の理由から「Flask + nginx + Gunicorn」の構成を選択する。

設定の仕方については以下のサイトを後ほど参考にする
https://medium.com/faun/deploy-flask-app-with-nginx-using-gunicorn-7fda4f50066a
https://martin-thoma.com/flask-gunicorn/

[その他参考]
・Flask + nginx + uWSGIについて
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-uswgi-and-nginx-on-ubuntu-18-04
https://medium.com/@smirnov.am/running-flask-in-production-with-docker-1932c88f14d0
https://www.serverlab.ca/tutorials/containers/kubernetes/how-to-run-flask-docker-containers-in-kubernetes/

・Flask + nginx + Gunicornについて
https://www.toptal.com/flask/flask-production-recipes
https://www.linode.com/docs/development/python/flask-and-gunicorn-on-ubuntu/
https://blog.miguelgrinberg.com/post/running-a-flask-application-as-a-service-with-systemd
https://superset.incubator.apache.org/installation.html#a-proper-wsgi-http-server

【Vue.js】v-forの配列を削除すると、最後の行が消される問題

v-forで配列内をループで回して、表示させており
その中で以下のように、削除を行う機能を実装したのだが
削除すると選択した行が消されず、最後の行が消されるという変な現象が発生した。

[HTML]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="index"
    v-on:remove="remove_item"
  ></tr>
</tbody>

[JS]

this.items.splice(index, 1);

原因を調べてみると、「:key」属性に「index」を設定していたのが原因だった。

In scenarios like this the most common problem is the v-for item keys. Your are keying each item by the index, but the index of each item is not constant because items can be removed from the list. Try using unique keys that are associated with item in the list.

by V-for and deleting components - Get Help - Vue Forum

なので設定する:keyをコンポーネントに属する唯一の値にすることで問題が解消された。
v-forのkeyにはindexを設定してはいけません!

[解決済みコード]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="item.id"
    v-on:remove="remove_item"
  ></tr>
</tbody>

【Vue.js】v-forの配列を削除すると、最後の行が消される問題

v-forで配列内をループで回して、表示させており
その中で以下のように、削除を行う機能を実装したのだが
削除すると選択した行が消されず、最後の行が消されるという変な現象が発生した。

[HTML]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="index"
    v-on:remove="remove_item"
  ></tr>
</tbody>

[JS]

this.items.splice(index, 1);

原因を調べてみると、「:key」属性に「index」を設定していたのが原因だった。

In scenarios like this the most common problem is the v-for item keys. Your are keying each item by the index, but the index of each item is not constant because items can be removed from the list. Try using unique keys that are associated with item in the list.

by V-for and deleting components - Get Help - Vue Forum

なので設定する:keyをコンポーネントに属する唯一の値にすることで問題が解消された。
v-forのkeyにはindexを設定してはいけません!

[解決済みコード]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="item.id"
    v-on:remove="remove_item"
  ></tr>
</tbody>

【Vue.js】v-forの配列を削除すると、最後だけが消されていく問題

v-forで配列内をループで回して、表示させており
その中で以下のように、削除を行う機能を実装したのだが
削除すると選択した行が消されず、最後の行が消されるという変な現象が発生した。

[HTML]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="index"
    v-on:remove="remove_item"
  ></tr>
</tbody>

[JS]

this.items.splice(index, 1);

原因を調べてみると、「:key」属性に「index」を設定していたのが原因だった。

In scenarios like this the most common problem is the v-for item keys. Your are keying each item by the index, but the index of each item is not constant because items can be removed from the list. Try using unique keys that are associated with item in the list.

by V-for and deleting components - Get Help - Vue Forum

なので設定する:keyをコンポーネントに属する唯一の値にすることで問題が解消された。
v-forのkeyにはindexを設定してはいけません!

[解決済みコード]

<tbody>
  <tr
    is="sample-component"
    v-for="(item, index) in items"
    v-bind:index="index"
    v-bind:item="item"
    :key="item.id"
    v-on:remove="remove_item"
  ></tr>
</tbody>

【MacOS:Catalina】"default interactive shell is now zsh" を非表示にする方法

とりあえずはbashを継続して使用したいのにターミナルを開くたびに、タイトルのメッセージが表示されるのは鬱陶しいので情報を探したところ以下の記事がヒット。

www.addictivetips.com

単純に以下を「.bash_profile」に書けばOK

export BASH_SILENCE_DEPRECATION_WARNING=1

またzshに変更したくなれば以下のコマンドを叩けばOK

chsh -s /bin/zsh