(オフィスチェア)中古のリープv2おすすめ

在宅勤務になってしばらく経ったのですが、利用していたイスのクッション周りが少しへたってきているせいか、 腰やお尻が痛くなり、イスを買い換えました。
結果、かなり快適になりました。  

 

購入したもの

スチールケース チェア Leap V2

スチールケース チェア Leap V2

  • メディア: オフィス用品
定価だと148,000円と高額なのですが、中古だと3万円くらいからあります。
今回は、オフィスバスターズさんで購入。送料込みで3.4万ぐらいでした。
 
楽天shopもあるようなので、ポイントとか欲しい人はこっちが良いかもです。しかし、在庫の更新は公式の方が早いみたいです。

どうしてリープチェアにしたか

今回買ったのは、Leap V2と呼ばれる物で、実は前の世代のリープチェアを利用していました。 前のリープチェアも座り心地は良く、試座せずに購入しても良かったのですが、 有名なアーロンチェアなども試したくなり、ショールームでいろいろ座りまくってきました。 結果、やはり自分にはリープチェアが一番合っていることがわかったため、コスパを考えて中古品を購入。
 

このご時世、外出しづらいのですが、車でパパッと行ったので店員さん以外会話することなく帰ってこれました。 お客さんも少なめで、店員さんも若干距離を置いて説明してくれました。
無理なく行ける人は、ショールームおすすめです。
 

その他の候補

アーロンチェア


少し前傾にすることができ、仕事するにはとても良さそうと言うのと、新品なら12年保証、展示品3割引と言うこともあり、結構悩みました。
新品のイス買うなら、アーロンチェアは良い選択肢だと思います。

AKRacing ゲーミングチェア Pro-X V2

AKRacing ゲーミングチェア Pro-X V2 GREY 灰

AKRacing ゲーミングチェア Pro-X V2 GREY 灰

  • 発売日: 2020/04/15
  • メディア: オフィス用品
ゲーミングチェアもすごい気になっていて、購入しようとしたのですが、購入検討時に1ヶ月待ち状態だったので諦めました。 今見ると入荷時期未定になっちゃってます。
AKRacingさんは、ゲーミングチェアでも結構高級な部類に入るようです。 色もこのグレーは割と落ち着いていて、気に入っていました。 また次の買い替えの時に検討してみたいです。

参考

前のリープチェアは、こちらを見て購入しました。
これ今見ても、相場感もあまり変わらないですね。
何度も言いますが3万円以下の椅子の鉄板は中古のリープチェア - FutureInsight.info

Ionicで作るモバイルアプリ制作入門 読了

気になっていたIonicのサンプルアプリ開発チュートリアルです。
Ionic + Anuglar + Firebaseで認証機能付きのチャットアプリを開発していきます。
若干コードが古いので、Angular未経験の人はsample codeのversionに合わせたほうが良いかもしれません。
内容は中々よかったです。

IonicはCordovaを利用して、iPhone/AndroidAPIを利用していると知りました。
PhoneGapの頃とか、遅くて辛かった記憶しかないので、頑張ってんだなぁと(ワレ、生きとったんか)と思いました。
がっつりしたMobileアプリではなく、まぁ簡単な物ならIonicで作るのは良いかもしれません。
IonicのWeb周りの実装も見た目もよく、Angularとの親和性も高いので、Mobile向けのWebをさくっと作るには良いかもしれないと思いました。 後、Firebaseの認証とfirestore使ったことがなかったのですが、こちらも簡単で良いです。

以下、最新versionを利用のために修正が必要な箇所

開発環境。 Angular: 8.2.14 Angular CLI: 8.3.26 firebase: 7.14.3

AngularFireAuthGuard

APIのBug?。以下でissueとworkaroundが記載されている。

5.2.1 AngularFireAuthGuard causes TypeError: source.lift is not a function · Issue #2099 · angular/angularfire https://github.com/angular/angularfire/issues/2099

# before
const redirectUnauthorized = redirectUnauthorizedTo(['auth/signin']);
const redirectLoggedIn = redirectLoggedInTo(['/']);

# after
const redirectUnauthorized = () => redirectUnauthorizedTo(['auth/signin']);
const redirectLoggedIn = () => redirectLoggedInTo(['/']);

AngularFireAuth

APIが若干変更され、以下の auth が必要なくなっている。AngularFireAuth

# before
  this.afAuth.auth.createUserWithEmailAndPassword(login.email, login.password)
# after
  this.afAuth.createUserWithEmailAndPassword(login.email, login.password)

ngModel

AngularのngModelにname属性がないためエラー。

# before
  <ion-input type="email" required [(ngModel)]="login.email"></ion-input>
# after
  <ion-input type="email" required [(ngModel)]="login.email" name="email"></ion-input>

AngularFireAuthがPromiseに変更されている

firebaseに登録したuserのuidを取得するために、以下のようにPromiseで取得しないと行けなくなってました。 この辺はAngular触ったことがないときついかも。

  async getUserId(): Promise<string> {
    return (await this.afAuth.currentUser).uid;
  }

使うときはこんな感じ。

    const user = await this.auth
      .getUserId()
      .then((uid) => this.firestore.userInit(uid));

rubyのcommitteeでRequestValidationを試してみる(OpenAPI 3.0)

昨年末からOpenAPI 3.0について調べていました。
YAMLを書いていて、型定義を元に簡単なvalidationぐらいはやってくれるツールはないかと思って探してみたところ、 OpenApI 3.0準拠のcommitteeというrubygemで実現できることが分かりました。

準備

rails

rails new --api でプロジェクト作って、適当に./bin/rails g scaffold articleしたものを用意します。

# controller はこんな感じ(api以下に配置)。
module Api
  class ArticlesController < ApplicationController
    before_action :set_article, only: %i[show update destroy]

    def index
      articles = Article.order(created_at: :desc)
      render json: { status: "SUCCESS", message: "Loaded articles", data: articles }
    end

    def show
      render json: { status: "SUCCESS", message: "Loaded the articles", data: @article }
    end

    def create
      article = Article.new(article_params)
      if article.save
        render json: { status: "SUCCESS", data: article }
      else
        render json: { status: "ERROR", data: article.errors }
      end
    end

openapi

config/openapi.yml 一部抜粋。

paths:
  /articles/{articleId}:
    get:
      parameters:
        - name: articleId
          in: path
          description: ID of article of fetch
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: OK
      operationId: getArticleById
      tags:
        - Article
      description: Article by ID
  /articles:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/ArrayOfArticles'
              examples:
                example: {}
          description: A list of articles
      operationId: getArticles
      tags:
        - Articles
      description: A list of articles
    post:
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - title
                - description
              properties:
                title:
                  type: string
                description:
                  type: string
      responses:
        '201':
          description: created
      operationId: createArticle
      tags:
        - Articles
      description: create Article
components:
  schemas:
    ArrayOfArticles:
      type: array
      items:
        type: object
        properties:
          id:
            type: integer
          title:
            type: string
          description:
            type: string
          created_at:
            type: string
            format: date-time
          updated_at:
            type: string
            format: date-time
servers:
  - url: 'http://localhost:3000/api'

committee

こんな感じでyml/jsonを読み込めばrack middlewareとして動作してくれます。

# config/application.rb
    schema_path = Rails.root.join("config/openapi.yml").to_s
    config.middleware.use Committee::Middleware::RequestValidation, schema_path: schema_path, prefix: '/api'

RequestValidation を試してみる

url path

/articles/{articleId} GET 時の articleId は integer として定義したので 文字列だとエラーとなります。

❯❯❯ curl http://localhost:3000/api/articles/1a | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    84    0    84    0     0   9333      0 --:--:-- --:--:-- --:--:--  9333
{
  "id": "bad_request",
  message: "#/paths/~1api~1articles~1{id}/get/parameters/0/schema expected integer, but received String: 1a"
}

controllerへのvalidation実装なしで、validationができてしまいました。素晴らしい!
(ですが、messageの内容が微妙です。)

request body

/articles POST 時の parameterとしてtitleとdescriptionを定義しましたが、それぞれ必須かつ文字列としています。

descriptionなしの場合

❯❯❯ curl -X POST "http://localhost:3000/api/articles" -H "Content-Type: application/json" -d '{"title": "string"}' | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   161    0   142  100    19  20285   2714 --:--:-- --:--:-- --:--:-- 23000
{
  "id": "bad_request",
  "message": "#/paths/~1articles/post/requestBody/content/application~1json/schema missing required parameters: description"
}

titleをbooleanとした場合

❯❯❯ curl -X POST "http://localhost:3000/api/articles" -H "Content-Type: application/json" -d '{"title": true}' | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   179    0   164  100    15  14909   1363 --:--:-- --:--:-- --:--:-- 16272
{
  "id": "bad_request",
  "message": "#/paths/~1articles/post/requestBody/content/application~1json/schema/properties/title expected string, but received TrueClass: true"
}

素晴らしい。それぞれエラーとなります。
curlは端折っていますが、HTTP/1.1 400 Bad Requestが返っています。)
(ですが、messageの内容が微妙です。)

メッセージを修正してみる

validation機能は素晴らしいのですが、message の前半がhuman readableではありません。(これはYPath?)
ソースを眺めてみると、committeeの中からopenapi_parserというのを呼んでおり、この中でvalidationをしているようです。

OpenAPIErrorのinstance変数である@referenceがこのエラーメッセージの表示箇所です。

# openapi_parser-0.6.1/lib/openapi_parser/errors.rb

  class ValidateError < OpenAPIError
    def message
      "#{@reference} expected #{@type}, but received #{@data.class}: #{@data}"
    end

やってみる

ですが、いくつかのエラーパターンがあり、ここをうまく表示するには難しそうだったので、力づくでやってみました。

以下をrails config/initializersに突っ込む。

module OpenAPIParser
  class OpenAPIError < StandardError
    def initialize(reference)
      @reference = customize_reference(reference)
      # @reference = reference
    end

    private
      def customize_reference(reference)
        # path parameter
        # eg: #/paths/~1articles~1{articleId}/get/parameters/0/schema
        return $1 if reference.match(%r|{(.+)}.+/parameters/|)

        # requestBody
        # eg: #/paths/~1articles/post/requestBody/content/application~1json/schema/properties/title
        return reference.split("/").last if reference.include?('/requestBody/')

        # other
        # query parameter
        # eg: #/paths/~1articles/get
        return ""
      end
  end
end

結果

path error

❯❯❯ curl http://localhost:3000/api/articles/1a | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    84    0    84    0     0   5600      0 --:--:-- --:--:-- --:--:--  5600
{
  "id": "bad_request",
  "message": "articleId expected integer, but received String: 1a"
}

post parameter error

❯❯❯ curl -X POST "http://localhost:3000/api/articles" -H "Content-Type: application/json" -d '{"title": "string"}' | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    99    0    80  100    19   2962    703 --:--:-- --:--:-- --:--:--  3666
{
  "id": "bad_request",
  "message": "schema missing required parameters: description"
}

❯❯❯ curl -X POST "http://localhost:3000/api/articles" -H "Content-Type: application/json" -d '{"title": true}' | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    99    0    84  100    15   6000   1071 --:--:-- --:--:-- --:--:--  7071
{
  "id": "bad_request",
  "message": "title expected string, but received TrueClass: true"
}

query string error(offsetを必須パラメータとした場合)

❯❯❯ curl "http://localhost:3000/api/articles" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    69    0    69    0     0    248      0 --:--:-- --:--:-- --:--:--   248
{
  "id": "bad_request",
  "message": " missing required parameters: offset"
}

まとめ

committee のRequestValidationを試してみました。
YAMLだけで基本的な型チェックをしてくれるのはとても素晴らしいです。 ですが、error responseをもう少し柔軟にカスタマイズできる機能は要望がありそう、ということで無理やりやってみました。
 
また、今回じっくりみてませんが、ResonseValidationもあり、テストで利用できるのは大きいので ここはガッツリ使えるかと思います。
 
あと、別件ですが、正直OpenApiのYAML書くのが辛いなぁという印象。

See Also

How to use OpenAPI3 for API developer - RubyKaigi 2019