Note

3年後の自分のために書いています

Haskell を亀のようなスピードで学び始めている私のための Stack による環境構築メモ

React x Flow or TypeScript の開発をやるにあたり、関数型言語と型システムの知識がないことがボトルネックになってきたと感じるので、ずっと積んでいた『すごいH本』を亀のようなスピードで進めている。

2012年に出版されたこの本には Haskell Platform による環境構築方法が載っているのだけれど現在は Stackを使った方法が推奨されているらしい。

だいたい以下のドキュメントに書いてある通りなのだけれど、一応 REPL を動かすところまでメモしておく。

Home - The Haskell Tool Stack

$ curl -sSL https://get.haskellstack.org/ | sh
$ stack --version
Version 2.1.3, ...
$ where stack
/usr/local/bin/stack

# REPL (ghci) 立ち上げ
$ stack exec ghci

# show type
Prelude> :t putStrLn
putStrLn :: String -> IO ()
# Hello, World!
Prelude> putStrLn "Hello, World!"
Hello, World!
# exit
Prelude> :quit

2021/08 追記

参考

Download Haskell Platform

GitHub - commercialhaskell/stack: The Haskell Tool Stack

https://qiita.com/waddlaw/items/49874f4cf9b680e4b015

GraphQL のエラーハンドリングについていろいろ調べた

前提

GraphQL のレスポンスのフォーマットに関するベストプラクティスとして、レスポンスの HTTP ステータスコードは 200 で統一、レスポンスの errors キーにエラーの詳細な情報を持たせる というのがある(公式な仕様ではないが、Apollo Client も graphql-ruby もこれに沿っている)

最初にまとめ

GraphQL のエラーハンドリングにはトップレベルの errors キーにエラー内容を持たせる方法と、 data キーの中にエラー内容を持たせる方法とある。

公式の仕様書には前者の トップレベルの errors キーにエラー内容を持たせる方法のみが載っている

後者は公式の仕様には無いが graphql-ruby に実装されている機能 なので使える。

しかしエラーの type を定義できないと、エラーの詳細が動的に変わりうるということなので、エラーハンドリングをクライアント側で UI として表現する場合は data キーの中に含めた方が堅牢なアプリケーションになる。(トップレベルの errors キーに含める場合も type 定義できれば良いのだけれど)

説明

レスポンスの形式でいうと以下のような感じ。

トップレベルの errors キーにエラー内容を持たせる

{
  "errors": [
    {
      "message": "Can't continue with this query",
      "locations": [
        {
          "line": 2,
          "column": 10
        }
      ],
      "path": ["user", "login"]
    }
  ],
  "data": {
  // 同じレスポンスに data を含めることもできる
  // ...
  }
}

data キーの中にエラー内容を持たせる

{
  "data": {
    "createPost": {
      "post": null,
      "errors": [
        { "message": "Title can't be blank", "path": ["attributes", "title"] },
        { "message": "Body can't be blank", "path": ["attributes", "body"] }
      ]
    }
  }
}

その他

GraphQLにおけるエラーハンドリングの仕方 - ZOZO Technologies TECH BLOG がめっちゃ参考になった。

ちなみに graphql-ruby では 最近の PR でルートの Schema クラスに rescue_from を定義できるようになり、セーフティネット的なエラーハンドリングを実現できるようになった

これ調べてる間に graphql-ruby のドキュメントのリンク切れを見つけて投げた PR、 無事マージされた。

rails, rack, unicorn or puma, nginx の関係についてちゃんと理解したい

だいぶメモ書きです。

表題の件、まだ曖昧にしか理解できてない。

ググっても浅い情報しか出てこないし、各ライブラリのソースコードを読んでみたけどどう繋がってるか、それぞれどんな役割を担っているか把握するのムズいなぁ…。

結論

色々調べてここまで分かった。

  • 「クライアント側 nginx <-> puma or unicorn <-> rack <-> rails DB 側」 的な感じ
  • rails(や sinatra)はつまるところ rack の仕様に沿って env(HTTPリクエスト)をもらって [status, headers, body](HTTP レスポンス)を返すだけのプログラムである

rack

HTTP server としてのインターフェースを提供する。 #call とか。

Rackとは何か - Qiita

rails

rack アプリケーション。 rack の仕様に沿った #call を実装する。

とりあえず actionpackAction Dispatch でリクエストをさばき、 Action Controller でレスポンスを生成していく感じなのかな?

Response#rack_response でレスポンス生成してる?

ちなみに以下のようなコードを差し込んだ時のログはこんな感じ。

class HogeController < ApplicationController
  def index
    caller.each{ |line| p line}
    ...
  end
end

ログ

Started GET "/" for 127.0.0.1 at 2019-09-17 11:10:00 +0900
Processing by WelcomeController#index as HTML
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/abstract_controller/base.rb:194:in `process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/rendering.rb:30:in `process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/abstract_controller/callbacks.rb:42:in `block in process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/callbacks.rb:132:in `run_callbacks'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/abstract_controller/callbacks.rb:41:in `process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/rescue.rb:22:in `process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:34:in `block in process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications.rb:168:in `block in instrument'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications/instrumenter.rb:23:in `instrument'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/notifications.rb:168:in `instrument'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/instrumentation.rb:32:in `process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal/params_wrapper.rb:256:in `process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.0/lib/active_record/railties/controller_runtime.rb:24:in `process_action'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/abstract_controller/base.rb:134:in `process'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionview-5.2.0/lib/action_view/rendering.rb:32:in `process'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal.rb:191:in `dispatch'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_controller/metal.rb:252:in `dispatch'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:52:in `dispatch'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:34:in `serve'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:52:in `block in serve'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:35:in `each'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/journey/router.rb:35:in `serve'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:840:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/omniauth-1.8.1/lib/omniauth/strategy.rb:190:in `call!'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/omniauth-1.8.1/lib/omniauth/strategy.rb:168:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/omniauth-1.8.1/lib/omniauth/builder.rb:63:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/tempfile_reaper.rb:15:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/etag.rb:25:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/conditional_get.rb:25:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/head.rb:12:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/http/content_security_policy.rb:18:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:232:in `context'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:226:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/cookies.rb:670:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.0/lib/active_record/migration.rb:559:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/callbacks.rb:98:in `run_callbacks'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/callbacks.rb:26:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/executor.rb:14:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/debug_exceptions.rb:61:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/web-console-3.6.2/lib/web_console/middleware.rb:135:in `call_app'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/web-console-3.6.2/lib/web_console/middleware.rb:30:in `block in call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/web-console-3.6.2/lib/web_console/middleware.rb:20:in `catch'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/web-console-3.6.2/lib/web_console/middleware.rb:20:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:38:in `call_app'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:26:in `block in call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/tagged_logging.rb:71:in `block in tagged'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/tagged_logging.rb:28:in `tagged'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/tagged_logging.rb:71:in `tagged'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/rack/logger.rb:26:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/sprockets-rails-3.2.1/lib/sprockets/rails/quiet_assets.rb:13:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/request_id.rb:27:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/method_override.rb:22:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/runtime.rb:22:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/activesupport-5.2.0/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/executor.rb:14:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/actionpack-5.2.0/lib/action_dispatch/middleware/static.rb:127:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/rack-2.0.5/lib/rack/sendfile.rb:111:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/engine.rb:524:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/puma-3.11.4/lib/puma/configuration.rb:225:in `call'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/puma-3.11.4/lib/puma/server.rb:632:in `handle_request'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/puma-3.11.4/lib/puma/server.rb:446:in `process_client'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/puma-3.11.4/lib/puma/server.rb:306:in `block in run'"
"/Users/daidoshota/training/perfect_rails_awesome_events/vendor/bundle/ruby/2.5.0/gems/puma-3.11.4/lib/puma/thread_pool.rb:120:in `block in spawn_thread'"
  Rendering welcome/index.html.erb within layouts/application
  Event Load (3.2ms)  SELECT "events".* FROM "events" WHERE (start_time > '2019-09-17 02:10:00.146381') ORDER BY "events"."start_time" ASC
  ↳ app/views/welcome/index.html.erb:6
  Rendered welcome/index.html.erb within layouts/application (17.7ms)
Completed 200 OK in 96ms (Views: 70.1ms | ActiveRecord: 13.1ms)

ラストが actionpack/lib/action_controller/metal/basic_implicit_render.rb の send_action

最後まで追えてない気がするけど。

Railsはアクセスをどう処理しているのか(1)

module function Kernel.#caller (Ruby 2.6.0)

unicorn

rack アプリケーション用サーバ。 マルチプロセス。 HTTPリクエストを受け取って rack インタフェースにそったオブジェクトを rack アプリケーションに渡してくれるWebサーバ、だけど rack gem に依存はしていない。(開発環境のみ依存)

RackがよくわからなかったのでRackアプリケーションをUnicornで動かしてnginxからリクエストを転送してみた - woshidan's blog

puma

rack ハンドラ(rack アプリケーション用サーバと同義?)マルチプロセス&マルチスレッド。

unicorn を代替するために作られた? Heroku が推してる。

最近の Rack サーバ事情について - おもしろwebサービス開発日記

puma/architecture.md at master · puma/puma · GitHub

【Rails 5.2 版】スマートな rails new コマンドの叩き方

自分はいつもプライベートの開発ではこんな感じで rails new やってます、というのを備忘録もかねて載せておきます。

基本的に使うか分からないライブラリのファイルは生成しないようにしています。後から genarate コマンドで簡単に追加できるので。

通常の Rails アプリ

$ bundle exec rails new . -TMCGB -d postgresql --skip-turbolinks --skip-coffee --skip-active-storage

API 専用の Rails アプリ

--api つけると そもそも js 系のファイルが生成されないので、以下のオプションで OK。

$ bundle exec rails new . --api -TMCGB -d postgresql --skip-active-storage

オプション説明

  • -TMC
    • Test, Action Mailer, Action Cableの作成をスキップ
  • -G
    • .gitignore ファイルを上書きしない(あと自動で git init しない)
  • -B
    • rails new 時に bundle install しない( Gemfile を編集してから bundle install するので)
  • -d postgresql
  • あとはもろもろライブラリをスキップ

参考

$ bundle exec rails new -h

Options:
      [--skip-namespace], [--no-skip-namespace]            # Skip namespace (affects only isolated applications)
  -r, [--ruby=PATH]                                        # Path to the Ruby binary of your choice
                                                           # Default: /Users/daidoshota/.rbenv/versions/2.5.0/bin/ruby
  -m, [--template=TEMPLATE]                                # Path to some application template (can be a filesystem path or URL)
  -d, [--database=DATABASE]                                # Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)
                                                           # Default: sqlite3
      [--skip-yarn], [--no-skip-yarn]                      # Don't use Yarn for managing JavaScript dependencies
      [--skip-gemfile], [--no-skip-gemfile]                # Don't create a Gemfile
  -G, [--skip-git], [--no-skip-git]                        # Skip .gitignore file
      [--skip-keeps], [--no-skip-keeps]                    # Skip source control .keep files
  -M, [--skip-action-mailer], [--no-skip-action-mailer]    # Skip Action Mailer files
  -O, [--skip-active-record], [--no-skip-active-record]    # Skip Active Record files
      [--skip-active-storage], [--no-skip-active-storage]  # Skip Active Storage files
  -P, [--skip-puma], [--no-skip-puma]                      # Skip Puma related files
  -C, [--skip-action-cable], [--no-skip-action-cable]      # Skip Action Cable files
  -S, [--skip-sprockets], [--no-skip-sprockets]            # Skip Sprockets files
      [--skip-spring], [--no-skip-spring]                  # Don't install Spring application preloader
      [--skip-listen], [--no-skip-listen]                  # Don't generate configuration that depends on the listen gem
      [--skip-coffee], [--no-skip-coffee]                  # Don't use CoffeeScript
  -J, [--skip-javascript], [--no-skip-javascript]          # Skip JavaScript files
      [--skip-turbolinks], [--no-skip-turbolinks]          # Skip turbolinks gem
  -T, [--skip-test], [--no-skip-test]                      # Skip test files
      [--skip-system-test], [--no-skip-system-test]        # Skip system test files
      [--skip-bootsnap], [--no-skip-bootsnap]              # Skip bootsnap gem
      [--dev], [--no-dev]                                  # Setup the application with Gemfile pointing to your Rails checkout
      [--edge], [--no-edge]                                # Setup the application with Gemfile pointing to Rails repository
      [--rc=RC]                                            # Path to file containing extra configuration options for rails command
      [--no-rc], [--no-no-rc]                              # Skip loading of extra configuration options from .railsrc file
      [--api], [--no-api]                                  # Preconfigure smaller stack for API only apps
  -B, [--skip-bundle], [--no-skip-bundle]                  # Don't run bundle install
      [--webpack=WEBPACK]                                  # Preconfigure for app-like JavaScript with Webpack (options: react/vue/angular/elm/stimulus)

Runtime options:
  -f, [--force]                    # Overwrite files that already exist
  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
  -q, [--quiet], [--no-quiet]      # Suppress status output
  -s, [--skip], [--no-skip]        # Skip files that already exist


# Options, Runtime options 以外は省略

React Native × Firebase Authentication での自動ログイン機能に AsyncStorage は使わなくていい

タイトルのまんまですが、 Web に落ちてる記事の中にはわざわざログイン状態を React Native の AsyncStorage を使って保存してる実装のものもあったので、勘違いしないようにメモ。

Firebase Authentication では デフォルトで 明示的にログアウトするまでセッションを維持してくれます。

Authentication State Persistence  |  Firebase

'local': ブラウザ ウィンドウを閉じたり React Native でアクティビティが破棄されたりした場合でも、状態が維持されることを示します。この状態をクリアするには、明示的なログアウトが必要です。Firebase Auth のウェブ セッションは単一のホストを生成元とするため、単一のドメインでのみ永続化されることに注意してください。

...

ウェブブラウザと React Native アプリのデフォルトは local (ブラウザがこのストレージ メカニズムをサポートしている場合、たとえばサードパーティCookie / データが有効な場合など)で、Node.js バックエンドアプリでは none になります。

ログインしてるかどうかは firebase.auth().onAuthStateChanged() などで取得できます。

雑記

Firebase Authentication では明示的にログアウトしなくても他のユーザとして再度ログインかサインアップすればログインアカウントが切り替わるというのも(当たり前なんですが)挙動確認できたので、メモしておきます。

Expo で "This version of the Expo app is out of date. Uninstall the app and run again to upgrade." のエラーが出た時の対処法

iOS Simulator にインストールされている Expo アプリのバージョンが古いとのこと。

iOS Simulator の Hardware > Erase All Content and Settings してから再度 $ expo start -i すれば最新版の Expo アプリが install されます。

参考

javascript - expo is out of date uninstall and run again to upgrade - Stack Overflow

React Native にゆかりのあるスタートアップが集う会#5 に行ってきた

普段は Rails でアプリケーション開発してる自分ですが、プライベートで以前から触っていた React Native のイベントに行ってきました。

r-n.connpass.com

詳細なレポートは以下がとてもよくまとまってると思います。

【第5回】 「ReactNativeにゆかりのあるスタートアップが集う会」に参加しました|tkow|note

この記事では自分の気になった発表を中心にまとめていきます。

ReactNative はじめのいっぽ

まとめ

  • expo init してからも実装環境周りで迷うことが多い
  • 基本方針は UI を無理に作り込まずツールや OSS を活用する

以下ピクスタでの環境。

  • 開発支援ツールは expo 一択
  • エミュレーターiOS: XcodeAndroid: Genymotion
  • デバッガは React Native Debugger(長時間開いてるとメモリ食うので再起動必要)
  • 型チェックは TypeScript
  • State 管理は Redux(学習コスト大きいけど、書ける人多いから採用において有利)
  • ディレクトリ構成は Ducks(小規模なアプリなら見通しが良い)
  • ルーティングは React Navigation(Redux との相性良い)
  • 非同期処理は redux-saga
  • UI コンポーネントは React Native Elements

一言感想

UI コンポーネントに Native Base でなくて React Native Elements を選んだ理由が聞きたかった…

React Native のルーティングばなし

まとめ

  • React Navigation では今何枚目問題やバージョンアップによる破壊的変更がつらい
  • シンプルな zen-router を作ってみた
  • 今何枚目問題を解決する screenLength を実装した

GitHub - jshosomichi/react-native-zen-router

一言感想

zen-router 使ってみたい。

React Nativeの開発環境を分けよう(iOS編)

まとめ

一言感想

デバッグ方法わからなさすぎたのでものすごくためになりました!

React Native の E2E テストフレームワーク Detoxの紹介

まとめ

  • React Native 用 E2E テストツール Detox の紹介
  • テストデータ作るのが難しいので、 get 系のテストを中心に書いてる

GitHub - wix/Detox: Gray Box End-to-End Testing and Automation Framework for Mobile Apps

一言感想

テスト大事(でもプライベートでの開発では一旦 E2E テストは良いかな…)

パネルディスカッション

Hooks 便利そう(React Native 0.59 から使える)