とーますメモ

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

【Rails】SidekiqをActiveJob経由で使うのが良いか、直接使うのが良いのか。

初めてバックエンドの非同期処理を書くことになったため
良さそうなgemを探したところ、以下の3つが有名なことがわかった。

・Delayed Job
・Resque
・Sidekiq

色んなサイトで、各gemの説明があるので
ここではその説明は省くが
自分の場合、以下の理由から

Redisも一緒に使ってみたい
リトライ機能を使用したい

Sidekiqを利用することにした。
[参考]
Sidekiq について基本と1年半運用してのあれこれ - まっしろけっけ


ただ、Rails4.2からActiveJobという上記のgemのアダプターが
利用できるようになったため、そのため表題の通り

ActiveJob経由でSidekiqを使うのが良いか、直接Sidekiqを使うのが良いのか。

で迷った。

まずActiveJob経由で作成する場合のメリットは
書き方が統一されるため、他のgemに変更したとしても
同じコードが流用できるということだ。

しかし、ActiveJob経由でコードを作成した場合
処理が抽象化されるため、細かいSidekiqの設定(例:retryの詳細設定)ができないようだ。

Note that more advanced Sidekiq features (sidekiq_options) cannot be controlled or configured via ActiveJob, e.g. saving backtraces.

By Active Job · mperham/sidekiq Wiki · GitHub

いろいろと検索したら、こんなページが出てきた。
stackoverflow.com

色々とSidekiqの機能が使えなくなるみたい。
でも以下のコメントの通り、

It is totally up to you to decide. Ask yourself do you really need Sidekiq's retry tab ?
Do you need Sidekiq batch processing etc..
If the answer is yes go with sidekiq, after all sidekiq is a well maintained project


結局、ActiveJob経由で使用するか、直接使用するかは要件次第だと考える。

とりあえず非同期処理を試めすために使用したい場合などで
他のgemに変更する可能性がある場合なんかは、ActiveJob経由を使うのが良いか。

そうでなく、Sidekiq一択で、すべての機能を完全に使いたい場合は
Sidekiqを選ぶのが良いか。


[参考]
https://qiita.com/necojackarc/items/b4a8ac682efeb1f62e74
Railsで非同期処理:キュー。Sidekiq(+ActiveJob)がResqueよりも、とても簡単便利。 - Qiita
Rails 非同期で処理を実行する方法(Sidekiq, Resque, Delayed Job, Active Job比較) - Shred IT!!!!
https://www.hommax39.com/archives/398

【Redis】MacでRedisを使ってみる

自分用メモ。


Redisとは、メモリ内にデータを保存するタイプのKVS。
永続性やデータ型のサポートがあることが特徴

クラッシュセーフではありません。
メモリは揮発的であり、クラッシュするとデータが飛んでしまいます。
スナップショットによる永続化も可能ですが、原理的に完全ではありません。

・最悪の場合、失っても問題がないもの
・高速な読み書きが必要なもの

という性質のデータを格納するのに適していると考えられます。
たとえば動的なページのキャッシュなどがこれにあたります。

[参考]
Redis入門: Redisとは? インメモリDBについて、memcachedとの違いなど | Simplie Post

インストール

brewでインストール可能

$ brew install redis

バージョン確認

$ redis-server -v

起動・停止

起動

$ redis-server

一時的に起動?

$ redis-server /usr/local/etc/redis.conf

起動確認

ps | grep redis-server

停止(redis-cli起動中)

127.0.0.1:6379> shutdown

停止(コマンド実行)

$ redis-cli shutdown

データ操作

データ操作に用いれられるコマンドは大文字でも小文字でも構わないっぽい。
Redis コマンド一覧 - Symfoware


PING

$ redis-cli
127.0.0.1:6379> ping
PONG

登録

$ redis-cli
127.0.0.1:6379> set hoge test
OK
127.0.0.1:6379> get hoge
"test"

削除

$ redis-cli
127.0.0.1:6379> keys *
1) "hoge"
127.0.0.1:6379> del hoge
(integer) 1
127.0.0.1:6379> keys *
(empty list or set)

DBごと削除

$ redis-cli
127.0.0.1:6379> flushdb

リアルタイムにクエリ情報を監視(便利!)

$ redis-cli
127.0.0.1:6379> monitor
.....
.....
.....

メモリ上のデータを保存

保存

127.0.0.1:6379> bgsave
Background saving started

保存場所

dump.rdb

cd /usr/local/var/db/redis/
ls
dump.rdb

dump.rdbがある状態で、再度redisを立ち上げると
データが復元されている。

設定の変更

redis.confを編集

$ vim /usr/local/etc/redis.conf

[参考]
redis.confの設定をしてみる - Qiita



ログイン時に起動したい場合

$ ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents
$ cd ~/Library/LaunchAgents
$ launchctl unload homebrew.mxcl.redis.plist
$ launchctl load homebrew.mxcl.redis.plist

ログファイル場所

/usr/local/var/log/redis.log

GUIクライアントのインストール

Redis Desktop Managerというのが日本語情報が多かったので採用
githubから最新のdmgを取得し、インストール。

Releases · uglide/RedisDesktopManager · GitHub
Redis Desktop Managerを使ってみる - Qiita




[参考]
RedisのGUIクライアントを集めてみた
www.omakase.org

【Shopify】Rails内でWebhookを試してみた

自分用メモ。

ShopifyのWebhookの使い方については
既に素晴らしいまとめをしてくださっている方が
いるので、その方のやり方を踏襲。

qiita.com

一点だけ追記すると、検証ロジックの「Secret Key」は
手動でWebhookを作成する場合と、AdminAPIを用いて作成する場合で
違うという点。

手動でWebhookを作成する場合は、Shopify管理画面でWebhookを作成したときに
表示される「Secret Key」を使用する。

だが、AdminAPI経由で作成したものは、Shopifyアプリ作成時に
割り当てられた「Secret Key」を使用する。

【Shopify】Railsを利用したShopifyアプリを作成してみる

自分用メモ。
アプリを作成する前に前提として以下の3つの準備が必要

①ngrokの設定

最初からサーバを用意できるのならば、それでも良いかもしれないが
自分の場合、ローカルマシンを開発サーバとして、
Shopifyから「直接」アクセスさせる方法を試す。

localhostで動いているサーバーを、LANの外から直接アクセスできるように
させるためのツールとして「ngrok」を使用する。

詳しい設定の方法は以下を参照
qiita.com

②Shopifyパートナーのアカウント作成

アプリの作成にはこのアカウントが必要なるため登録必須。
作成は以下のページから。
Shopify Partner Program — Become the next Shopify Expert

③アプリテスト用のショップがある


設定が終わったら、以下のようにしてアプリを作成する。

1) ngrokを起動

上記の記事などで、ngrokの環境設定が終わったら、
railsが動作しているポートにあわせて、ngrokを起動させる。

例)3000番ポートで動作している前提

$ngrok http 3000

f:id:Thoames1212:20180302065606p:plain

Forwardingで表示されている箇所に、
表示されているURL(例: https://betb5484.ngrok.io)で
外部から現在動作しているRailsにアクセスできる。
※以降の説明は、アプリURLが「https://betb5484.ngrok.io」であることを前提に話を進める。

一度ブラウザ上で、動作するか確認してみてほしい。
※ちなみにhttp版とhttps版があるが、特に選ばないのであればhttps版でいいだろう。

2)Shpifyパートナーのアプリ作成画面より、アプリ設定を新規作成

Shopifyパートナーの「Apps」ページの右上にある「Create app」から
アプリの新規設定を行う。
f:id:Thoames1212:20180302074249p:plain

「Create app」を押すと、以下のモーダル画面が表示されるので
「App name」には作成するアプリ名を入力し、
「App URL」には1)でも説明したngrokのForwarding箇所に表示されているURLを設定し
画面内の「Create app」ボタンを押す。
f:id:Thoames1212:20180302074612p:plain

作成できるとアプリのOverviewページに遷移する。
このページ内の「App Info」タブをクリックし、
「Whitelisted redirection URL(s)」を以下のように設定する。

https://betb5484.ngrok.io/auth/shopify/callback


f:id:Thoames1212:20180302082211p:plain

また同ページ以下の「App credentials」にある「API key」と「API secret key」は
以降の説明で使用するのでメモしておく。

f:id:Thoames1212:20180302093729p:plain

3)RailsでShopify Appの設定をする

gem 'shopify_app'をインストールする
GitHub - Shopify/shopify_app: A Rails Engine for building Shopify Apps

gem 'shopify_app'

デフォルトのジェネレータコマンドがあるので、
それを使用し、Shopifyとの接続に最低限必要なファイルを生成する。

そのためには2)でメモしておいた
「API key」と「API secret key」を以下のフォーマットのように設定し、
ファイルを生成する

$ bundle exec rails generate shopify_app --api_key <your_api_key> --secret <your_app_secret>

問題なくコマンドが流れれば、以下のようなファイル群が生成される。
f:id:Thoames1212:20180302084812p:plain

生成されたファイル群の中に、Shopify用のmigrationファイルが
存在するのでマイグレーションを行う。

$ bundle exec rake db:migrate

また生成されたファイル群の中に
config/initializers/shopify_app.rbというファイルがあるが
このファイルのscope(権限)を設定することで、
使用できるShopify APIの権限を設定することができる。

デフォルトでは「read_orders, read_products」となっているため
「注文情報の読込権限」と「商品情報の読込権限」のみが設定されていることになる。

設定できるスコープの詳細は以下。
API access scopes - OAuth - Shopify Help Center

アプリの用途によって、設定する値を変更してほしい。
※注意してほしいのは、ショップにアプリを一度インストールした後に、
このスコープを変更しても、変更されない。

4)Railsを起動

$ bundle exec rails s

5)ショップにアプリをインストールする

https://betb5484.ngrok.io」にアクセスすると
以下の画面になる。

f:id:Thoames1212:20180302090504p:plain

ここに、インストール先のアプリURL(xxxxxx.myshopify.com)を入れて
アプリをショップにインストールする。

インストールボタンを押すと、以下のページになる。
f:id:Thoames1212:20180302092300p:plain

設定したスコープに応じて、アプリ内で使用可能な権限が表示される。
問題なければ右下の「install unlisted app」をクリックし、インストールする。

問題なくインストールが終わると、デフォルトのアプリ画面トップに遷移し
以下のようなページが表示される。

f:id:Thoames1212:20180302092941p:plain

上画面では、ジェネレーターで表示されたhome_contoller.rb内で
設定されている「商品10件」と、「Webhook一覧」が表示される。

app/controllers/home_controller.rb

class HomeController < ShopifyApp::AuthenticatedController
  def index
    @products = ShopifyAPI::Product.find(:all, params: { limit: 10 })
    @webhooks = ShopifyAPI::Webhook.find(:all)
  end
end

後は公式のAPIドキュメントを見ながら、必要に応じたAPIを活用し
アプリを作成すればよい。

help.shopify.com

ちなみにアプリはデフォルトでは「埋め込みアプリ」として
インストールされる。

詳細は以下。埋め込みアプリじゃない方法としても使用できる方法も紹介している。
参考にされたし。
thoames.hatenadiary.jp



[参考サイト]
Shopify 用のプライベートアプリを作ってみた - Qiita

【Rails】Carrierwave(1.2.2)をActiveRecordと切り離して使用する方法を試してみた。

自分の用途としては、ただ画像を外部APIへ送信するため
ローカルDBに保存する必要がなかった。

しかし、多くのサイトを見るとCarrierwaveをActiveRecordと併せて使用する
サンプル例が多いため、自分で調べてみた。

インストール

Gemfileに追記

gem 'carrierwave'

インストール

$ bundle install

アップローダーの作成

ImageUploaderという名前のアップローダーを作成

$ rails g uploader image

アップローダーと画像保存用フィールドのマウント

多くの記事に載っているように
Carrierwaveを使用し、画像アップロードと連携させるためには
画像保存用のフィールドをCarrierwaveのアップローダーにマウントさせる必要がある。
しかしただ単に、以下のようにmount_uploaderを使用すると失敗する。

例)失敗例

class TestModel
  include ActiveModel::Model

  mount_uploader :image, ImageUploader
  ...
end

以下のエラーが表示される。

undefined method `mount_uploader' for TestModel:Class

公式サイトを見ると、以下のように書いてある。

If a Class is extended with this module, it gains the mount_uploader method, which is used for mapping attributes to uploaders and allowing easy assignment.

Module: CarrierWave::Mount — Documentation for carrierwave (1.2.2)

要は「CarrierWave::Mount」をextendすればmount_uploaderが使用できるようになるということ。
設定に成功した例は以下。

class TestModel
  include ActiveModel::Model
  extend CarrierWave::Mount

  mount_uploader :image, ImageUploader
  ...
end

画像を保存する

以下のサイトを見ると、以下のようにある。
Using Carrierwave uploader for tableless model in Rails (Example)

Based on carrierwave documentation you can use mount_uploader if your class extend CarrierWave::Mount module and your class will have capability to store a file using instance method store_(mounted_field)! or cache_(mounted_field)!

要は、store_(mounted_field)! または cache_(mounted_field)!で
画像をストアまたはキャッシュに保存できそうなことが書いてある。

しかし試してみたが、動作しなかった。
なぜだろう。。。しかし、以下のようにしたら動作した。

class TestModel
  include ActiveModel::Model
  extend CarrierWave::Mount

  attr_accessor :image
  mount_uploader :image, ImageUploader
  ...

  def test(params)
    self.image.store!(params[:image])
    # またはself.image.cache!(params[:image])
  end
end

もっと良い方法をご存じの方がいれば、
コメントくださいませ。


[参考]
Module: CarrierWave::Mount — Documentation for carrierwave (1.2.2)
ruby on rails - Carrierwave: Mount Uploader in non ActiveRecord inherited class - Stack Overflow
Using Carrierwave uploader for tableless model in Rails (Example)
How to use carrierwave without a model in rails? - Stack Overflow
CarrierWave使用時のTips | Gemの紹介 | DoRuby
Rails 超お手軽な画像アップローダー CarrierWave の使い方 | Workabroad.jp

【Go】Echo × レイヤー管理のWebアプリをgo-bindataでまとめて、サーバ上で常駐化させてみようとしたが・・・

ちなみにかなり時間を割いて調べたけど、
自分が欲しい情報が得られず、トライアンドエラーを繰り返すも
全くうまくいかず、やっとこそなんとか常駐化まで出来た内容をメモ書き。

【前提】
Echoフレームワークを使用し、常駐化にはSupervisorを使用する。
Supervisorの使い方はここでも紹介している。
thoames.hatenadiary.jp

自分がGoを使ってみようと思ったきっかけの一つに、
デプロイがシングルバイナリひとつで簡単にできるという理由があるが
これは静的ファイルを含むWebアプリでは当てはまらないということに
デプロイを試行錯誤している段階で気づいた。

これはあくまでも.goファイルのみで作成されたアプリが対象になるっぽい。

また、自分が調べた感じでは、
静的ファイル(css, javascript, 画像等)を含めたGoのWebアプリを
サーバ上で常駐化させているサンプル例があまりないため、
どうしてもGoでWebアプリを作成する際は
APIサーバとして使用する前提(静的ファイルを含まない)で作成し、
静的ファイルは別に管理する方式の方が良い気がする。
(他に良い方法があるようでしたら、コメントくださいませ。)

では以下にどうやって常駐化まで対応したかを書く。

静的ファイルを含めたシングルバイナリを作成する方法として
メジャーっぽいのは「go-bindata」を使用する方法になると思う。
このgo-bindataとechoに関連する情報を調べると大体が以下のQiitaページが引っかかると思う。

qiita.com

Echo+Go関連の情報が少ない中、非常にありがたい情報ではあるが、
サンプルがシンプル過ぎて、レイヤー管理を行うための
「html/templete」パッケージを使用する方法にはマッチしない。

ここでいうレイヤー管理とは、Railsみたいな、layout viewが大枠にあって、
その中でコンテンツや部分テンプレートを
自由に呼び出す方法のこと。
小規模ならいいかもだけど、中規模以上だと、こういう仕組みの上で作らないと
管理が大変になる。

ディレクトリ構成は大事な箇所だけ抜き出すとこんな感じ。

src
┣ main.go
┣ /assets
┃ ┣ /js
┃ ┣ /css
┃ ┗ /images

┗ /views
  ┣ layout.html
  ┣ header.html
  ┗ index.html

「html/template」パッケージを使用し、こういったレイヤー構成の
アプリを作成する場合、色んなサイト様で掲載されている方法としては以下のように書くことである。

template.Must(template.ParseFiles("views/layout.html", "views/header.html", "views/index.html"))

layout.html

{{define "layout"}}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title></title>
  <link href="/static/css/base.css" rel="stylesheet">
  <script src="/static/js/base.js"></script>
</head>
<body>
{{template "header" .}}

{{template "content" .}}
</body>
</html>
{{end}}

header.html

{{define "header"}}
<nav>
  ...
</nav>

</div>
{{end}}

index.html

{{define "content"}}
<div>
  <h1>Hello World!</h1>
</div>
{{end}}

上記のhtmlファイル群をまずはシングルバイナリに含ませるため
go-bindataを用いて以下のようにする。

$ go-bindata -o bindata.go ./views/...

※ サブパッケージに含ませる場合には、-pkgを使用する。
例)routerパッケージに含ませる場合。

$ go-bindata -pkg router -o router/bindata.go ./views/...

その後以下のヘルパー関数を作成し、ParseFiles関数の代わりに使用する。

func ParseAssets(filenames ...string) (*template.Template, error) {
  var ns = template.New("complex")

  for _, filename := range filenames {

    src, err := Asset(filename)
    if err != nil {
      return nil, err
    }

    s := string(src)
    name := filepath.Base(filename)

    _, err = ns.New(name).Parse(s)
    if err != nil {
      return nil, err
    }
  }

  return ns, nil
}
template.Must(ParseAssets("views/layout.html", "views/header.html", "views/index.html"))

これで、viewsとして使用するHTML郡はシングルバイナリとして含まれる。
しかし、次が問題でこれらのHTML郡の中で使用するcssやjsなどの静的ファイルを
以下のQiitaのやり方に沿ってみても、上手く読み込まない。
※正しくは、読み込んでるっぽいんだけど、ファイルの先頭に{message: "Not Found"}が入るため
読み込めない。なぜこうなるかは不明ではまったので、この方法は諦めた。
echo 初心者でも簡単!! echo で扱うアセットファイル群を簡単にバイナリにまとめて使ってみる - Qiita

なので、もう他にどうしようもなかったので
絶対パスで読み込むことにした。苦笑

以下の設定を行うことで、HTML内で「/static」と始まるパスは
assetsディレクトリ内を参照するようになる。

e := echo.New()

current_working_dir := "サーバ上の絶対パス"
e.Static("/static", filepath.Join(current_working_dir, "assets"))

これでビルドし、バイナリとassetsディレクトリをサーバにデプロイし
バイナリのパスをSupervisorに設定し、起動させれば常駐化してくれる。

以上。疲れた。。。


[参考]
アセット的なアレを実行バイナリ内に入れる話。
src/html/template/template.go - The Go Programming Language
echo 初心者でも簡単!! echo で扱うアセットファイル群を簡単にバイナリにまとめて使ってみる - Qiita

【Rails】独自例外の置き場所(配置ディレクトリ)について

自分用メモ。

日本語で検索しても情報が出てこなかったので
ちょっと調べてみた。

調べ方としてはGithubのコード検索で「< StandardError」で検索して
出てきたコードがどのディレクトリに配置されているのかで調査。

10件〜15件ぐらい見た感じだと、大体が

lib」ディレクトリ内に入れる。
※(中でerrorsなどのサブディレクトリ内に分けているケースも含む)

他には「app」ディレクトリ内に「exceptions」っていうディレクトリを作って入れてるケースなどもあった。

調べている最中で以下のようなStackOverflowも引っかかった。
Where to define custom error types in Ruby and/or Rails? - Stack Overflow

色々とやり方があるみたい。。
自分は以下の方針で行こうと思う。

1)「lib内」案
他のアプリでも再利用できるExceptionの場合はlib以下に入れる。
アプリの規模が大きい場合はサブディレクトリを別途作成し、その中で管理する。

2)「app/exceptions内」案
あくまでもそのアプリ内でのみ使用すると想定される独自Exceptionの場合はapp/exceptions以下に入れる。
アプリの規模が大きい場合はサブディレクトリを別途作成し、その中で管理する。


例)「lib内」案の例

lib/exceptions.rbを作成

module Exceptions
  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end
end

config/applications.rbに以下を追加

config.autoload_paths += Dir["#{config.root}/lib/**/"]

使用

raise Exceptions::InvalidUsername


[参考]
rubyでエラーコード付きの独自例外クラスを作成 + カスタムloggerで出力 - のんびりSEの議事録