Railsでの定数定義

これまでは、config/initializers 以下に定数用moduleを置いていました。 config/initializers ってアプリの初期化するためのが目的なので、微妙だなとはずっと思っていたのですが、 惰性でずっとこのようにしていました。

最近は

以下のようにしています。

# lib/app_constants.rb
module AppConstants
  SOME_HOST = "https://some.com/".freeze
end
AppConstants.freeze

app_constantsか、もしくはアプリ用にnamespace切ってsome_app/constants にしています。

# config/environment.rb
require "app_constants"

ご参考

Rubyで定数を扱う場合はfreezeするべき - Qiita

nuxtjsで日付を扱う(@nuxtjs/date-fns)

日付のformatをちょろっと変えたいと思ったのに、それなりに嵌ってしまったのでメモ。

version

nuxt@2.14.9
vue@2.6.12

ライブラリ(@nuxtjs/date-fns)

jsって日付の扱いが微妙で、ライブラリ使うのが無難なんですが、ググるとmomentの例がいくつかありました。 しかし、momentはもうメンテナンスモードという扱いで、新しいプロジェクトで利用するには推奨されていません。 Moment.js | Docs

いくつか代替ライブラリはあるのですが、nuxtjsで利用するには、下記が便利そうでした。

nuxt-community/date-fns-module: Modern JavaScript date utility library - date-fns for Nuxt.js https://github.com/nuxt-community/date-fns-module/

利用方法

現在のnuxtjsでは、vueは2系であるため modules に設定します。

# nuxt.config.js
   modules: [
+    '@nuxtjs/date-fns',

基本これで、 $dateFns が利用できるようになります。
templateで以下のようにすれば利用できます。

{{$dateFns.format(new Date(), 'yyyy-MM-dd')}}

jsで利用したければ、以下のようにすれば良いです。

  asyncData({ $dateFns }) {
    return {
      dateFormatted: $dateFns.format(new Date())
    }
  }

はまったところ

こういうのって、共通化(global filter化)したいんですが、$dateFns は asyncData であり、vueのfilterはasyncDataが扱えない?ようです。(この辺り正しく理解はできてないですが)

ではどうするか?

date-fns/format を直接使うで、とりあえずしのぎました。 なんかこの辺り他に良い方法がありそうなんですが、、、

# plugins/filter.js
import Vue from 'vue'
import format from 'date-fns/format'

Vue.filter('formatDate', (date) => {
  return format(date, 'yyyy-MM-dd')
})
# nuxt.config.js
   plugins: [
+    '~/plugins/filter.js'
   ],

そうすると、こんな感じで利用できます。

{{article.date | formatDate}}

sesrverless frameworkを使ってrubyでAPIを作る

The Serverless Application Framework | Serverless.com
以前から気にはなっていたのですが、簡易的なAPIを作るために利用してみました。
 
同様のServerless Frameworkとしては、Ruby on Jets というのがあります。
こちらも触ってみたいところ。

ざっくり利用方法

Installation

sesrverless入れるだけです。

mkdir your_project
cd your_project
yarn init
yarn add serverless -D

Create your project

yarn run serverless create --template aws-ruby

Deploy

コマンド1発で、CloudFormation作ってくれて、s3、IAM、Lambda、ApiGateway、CloudWatchまで一気にやってくれます。
これはめちゃ便利で、驚きました。

yarn run serverless deploy -v

各種設定

Serverless Framework - AWS Lambda Guide - Serverless.yml Reference
ここに全部乗ってるので、詳細は公式Documentみるのが良いでしょう。

以下、今回利用した設定内容。baseはこれだけ。

provider

こんな感じ。

provider:
  name: aws
  runtime: ruby2.7
  stage: dev
  region: ap-northeast-1
  apiGateway:
    shouldStartNameWithService: true

shouldStartNameWithService は、正直どっちでも良いですが、次のmajor versionからAPI Gatewayのnaming-ruleが変更されるため、新しい方式に合わせるために入れてます。
Serverless Framework Deprecations

functions

こんな感じ。ポイントはcors: trueぐらいでしょうか。

functions:
  exec:
    handler: handler.exec
    events:
      - http:
          path: html2haml
          method: post
          cors: true

注意事項

あとはhandler.rbにロジック書くだけで出来ちゃいますが、2つハマったところがあるので記載していきます。

Native Extension Gem

今回html2hamlを利用としたのですが、当然の如くnokogiri使ってまして、macでbuildしたgemをdeployしても動かないわけです。
そこで利用するのがlambci/lambda - Docker Hub
buildの例にrubyがなかったり、ruby2.5の例のままですが、現在のlambda同様2.7が利用できます。

# build
docker run -v `pwd`:/var/task -it lambci/lambda:build-ruby2.7 bundle install --path vendor/bundle --without development test
# local実行例
# handler、eventが指定できる
docker run --rm -v $PWD:/var/task:ro,delegated lambci/lambda:ruby2.7 handler.exec  '{"body": "{\"html\":\"<span foo=\\\"foo\\\">test</span>\"}"}'

 
しかし、bundle installしたgemなどをzipに含めると転送量が多くなるという問題があり、これを解決するためにはlayerとして別管理するか、serverless-hooks-pluginを使ってdockerでinstallするという手法があるようです。(後者試してないのであとでやってみます。) 個人的にはどうも、この辺が面倒だと感じるので、serverlessバンザイ!という気持ちには今一つなれません。

API GatewayのResponse形式

ここに書いてるんですが、Responseの形式が決まっています。
API ゲートウェイでの「不正な Lambda プロキシ応答」または 502 エラーの解決

見落としてハマっていたのが、bodyの形式です。JSON.stringifyした文字列が必要でした。 rubyなので、hashをto_jsonすることで解消。

"body": JSON.stringify(responseBody),

ご参考

See Also

htmlをhamlに変換するサイトhtml2hamlを勢いで作った - rochefort's blog