読者です 読者をやめる 読者になる 読者になる

ReduxでのRoutingはこれでやっていこうと思います。

Reduxでの理想のRoutingライブラリを求めて作ったredux-pagesの紹介記事です。

リポジトリ: ryo33/redux-pages

実用例: ryo33/Yayaka19

以前の方法での不満

redux-pagesを作るに当たって、まず、前にSPAサイトを作っていて不満に感じたことをまとめました。 その中で、特に解決したかったものが以下の4つです。

  1. Pathのパース結果はRouter(View)だけでなく、Middlewareでも必要である
  2. 現在のページがStateに格納されていてほしい
  3. Pathをリダイレクト処理などで手書きしたくない
  4. ページ遷移に関連する処理はMiddlewareに書きたい

(1)で言いたいことは、Pathのパース結果は画面描画時だけではなく、 表示されているページによってMiddlewareでの処理内容を変えたいときにも必要だということです。 redux-pagesのREADMEに書かれている

Parse once, route anywhere

は、このことを言っています。

(2)は、Pathと画面表示は常に一致しているわけではないということです。 例えば、閲覧権限の無いページのPathにアクセスして、エラーページのPathにリダイレクトされるまでの間は何を描画するべきでしょうか。 また、Webサイトの中にはページ移動後にコンテンツのダウンロードが終わるまでは前のページが表示されたままのものがあります。 このように、Pathと画面表示は常に一致するわけではありません。 しかし、Routerで描画したり、Middlewareで処理の分岐をしたりするためには、 現在のページが常に分かるようになっている必要があります。

(3)は、Pathを手書きすると、間違っていても発見しづらく、またPath形式の変更が容易でなくなってしまうためです。

(4)は個人的な好みです。

redux-pagesの基本的なアイデア

以上で述べた不満を元にした基本的な考えは以下の3つです。

  1. Stateには現在どのページを表示すべきなのかが保持される
  2. ページオブジェクトを定義して、ページ遷移Actionや、そのページへのPathはメソッドで生成する
  3. ページ遷移は単一のActionをdispatchすることで行う

不満1を解消するのは(1)(2)、不満2を解消するのは(1)、不満3を解消するのは(2)、不満4を解消するのは(3)です。

(3)で単一のActionになっているのはMiddlewareでのページ遷移処理を単純なコードで書くためです。 例えば別のページへのリダイレクトはdispatchするだけでいいですし、 ページ遷移を遅らせるときも単にnextDispatchを呼ぶのを遅らせるだけです。

基本的な動作イメージ

redux-pagesを使ってページ遷移を行うときの基本的な動きを説明します。

Actionをdispatchしてページ遷移

  1. redux-pagesのMiddlewareにActionが到達したときにPathが変更される
  2. Middlewareで様々な処理が行われる
  3. Stateが変更されViewもそれに応じて変更される

これが、基本的なページ遷移時の処理の流れです。 ユーザー認証やリダイレクトなどは(2)のMiddlewareの中で行います。

locationの変化によるページ遷移

こちらは、ブラウザの戻るボタンなどが押されたときに使われます。

  1. Pathのパース結果でActionが投げられる
  2. Middlewareで様々な処理が行われる
  3. Stateが変更されViewもそれに応じて変更される

実用

redux-pagesはYayaka19というSPAアプリケーションで実際に使用しました。 とりあえず今のところは大きな問題など無く運用できてます。

前述の不満の解消以外の利点としては複数のページに適用したい処理をまとめて書けることが挙げられます。 例えば、アクセス権限のあるページかどうか調べてリダイレクトを行う処理を各ページごとに分けて書く必要はなく、 単にallowedPageNames.includes(action.payload.name)というような条件文に応じてリダイレクトを行うだけです。

あと、使ってから気づいたのですが、nextDispatchを遅らせることによりページ遷移を遅らせる技は滅多に使わないと思います。 ロードが終了するまで前ページを表示するよりは、ページ遷移時のHookでisLoading状態にして、あのグルグルを表示したほうがSPAぽくなります。

ちなみにページ遷移時のMiddlewareの処理にはredux-middlewaresを用いています。 例えば、ログイン確認とそれによるリダイレクトにはcreateReplacerを、ページ遷移時のHookにはcreateAsyncHookを使います。 個人的に直感的にMiddlewareを書くことができるので気に入っています。 ただ、既にredux-sagaredux-logicなどを導入している場合はそちらを使ったほうがいいです。

課題

例えば/posts/:postNumberというパスがあってpostNumberを文字列型から数値型にキャストしたいといったときに、 エラー処理も含めるとMiddlewareでActionを変換する必要があります。 これをどうにかページ定義時に書くことができるようにしたいと考えています。

あとは、導入時の雛形が比較的多いのもどうにかして減らしたいです。

さいごに

割とまともに使えて、辛いポイントもそんなにないので、とりあえずしばらくはこれを使っていこうと思います。

技術記事投稿サイトに思うこと

WIP

本記事は殴り書きしたものをそのまま投稿したので、非常に読みにくくなっています。 また結論まで書ききっていません。 期末テストが終了して落ち着いたら加筆して推敲する予定です。(予定とは言っても、60%くらいの確率でやりません)

技術記事投稿サイトについてずっと思っていることについて書きます。 また、いちいち「主観ですが」と書くのが面倒臭いので、主観で書くときには「見る」という表現を使うことにします。

まず、技術記事投稿サイトは「技術記事が楽に書ける投稿サイト」と「質の高い技術記事が読めるサイト」の両方を目指しているように見えます。 明確な記述は多分ありませんが、ほとんどのユーザーはそのつもりで利用しているように見えます。 本記事ではこれが大前提です。 あと、私の見解が「質の低い記事を投稿する人が悪いわけではない」というのも前提です。

最近になって記事や記事の投稿者がSNSなどの外部のメディアで批判されたり、運営によって質が低いとされた記事が削除されたりするのを見るようになりました。 これが嫌で最近はほとんど利用していません。 以下ではなぜ嫌なのか、どうなったら積極的に利用したいのかを書いていこうと思います。

「技術記事が楽に書ける投稿サイト」に最低限必要なのは以下の2点だと思います。

  1. シンタックスハイライトなど技術記事を書く上で難しいとされることが簡単にできる
  2. めったなことがないと投稿が削除されない

1は十分に満たされていて、2は満たされてないように見えます。 「投稿サイト」として使うのは怖いため、私は最近は外部で技術記事を書くようになりました。

次に「質の高い技術記事が読めるサイト」に最低限必要なのは以下の2点だと思います。

  1. 記事の評価を行うための適切なシステムがあり、それによって質の高い記事が優先して表示される
  2. 外部サイトのURLを投稿することができ、内部の記事と同じように一覧に表示され、同じ記事評価システムが使われる

こちらは1も2も満たされていないように見えます。

1が満たされていないため、批判がサイト内ではなく外部で行われます。 外部で批判されるのは非常によくないと思っていて、なぜかというと批判が投稿者にフィードバックされる可能性が低いからです。

また、2が満たされていないため、私はサイト内で直接記事を検索するのではなく、検索エンジンから記事を検索して間接的にアクセスするようになりました。

明日からテストなので、とりあえずここまで。はい やたらと批判したがる

追記 とりあえず記事が消されなくなるのと、何かしらことが起こって質の低い記事やその投稿者が外部で批判されない雰囲気になったら積極的に使っていきたいです。

Cruxがあればゲームのコードが良くなると思った

Advent Calendar 2016 Rust 作った

ryo33.hatenablog.com

24日目です。


動機

昔何かの記事を読んで、その時からメッセージ指向でゲームを作りたいと考え始めました。 しかし、慣れてしまったやり方からはなかなか離れられないもので、 長らく実現することができていませんでした。 後述するcdb-rustも「いつものやり方でいいか」という消極的な考えのまま作られたものです。

最近、Webのフロントエンドをやる機会があって、 JavaScriptのRedux.jsというライブラリを使っていました。 僕の認識では、状態の変更に関して、 Storeに送られたActionが Middlewareを通って変形・フィルタリングされ、 それを受け取ったReducerが状態を書き換えるような アーキテクチャにするためのライブラリです( Fluxを実現するライブラリの一つと書いたほうが正確ですが、 動機的にはこの書き方のほうが伝わりやすいはずです)。 それを使っていく中で、ゲームプログラミングも似たようなやり方でやりたいという思いが高まってきたためCruxというライブラリを作ることになりました。

Cruxについて

基本的な使い方は、状態を管理する構造体をもとにStoreを作って store.dispatch(action)で非同期にstore.dispatch_sync(action)で同期的にActionを 送ります。最新の状態はstore.state()で取れます。

Actionのキューや非同期処理、排他処理などはいい感じに隠蔽されています(コードもいい感じとは言っていない)。

あとActorっぽく使うこともできます。

ryo33.hatenablog.com

実際に使ってみる

github.com

前に作ったCDBというゲームがあったので、それをCruxを使って書き換えました。 Crux本体のコードを書くのは本当に楽しくてしょうがなかったのですが、 こっちの作業はかなりつまらなかったです。 ウィンドウ切り替えなどをした時に固まるなどの不具合がありますが、とりあえず動くところまではできました。

さて、コードが良くなったどうかですが、正直に言うとあまり良くなっていません。 時間がなくてActionの単位が大きかったり、 Middlewareを使うようにしたりできなかったためです。

幾つかの処理をActionとして切り出してMiddlewareから送るようにすれば、もうちょっとCruxの良さが分かるコードになると思います (具体的にはボールやバーの大きさが徐々に変化する部分やボールがアイテムに当たる部分などです)。

これから

コードに危ういところがあり警告も出るのでそれを直していきたいです。 あとはドキュメントの整備もやっていきたいです。


最後まで読んでいただきありがとうございました。

なんかActorができたので

Advent Calendar 2016 作った Rust

qiita.com 23日目


Rustのコードを書いて遊んでいたら、「あっこれActorじゃん」となって、いつの間にかActorができていたのでそれについて書きます。

[2016/12/24追記] 指摘ありましたが、これをActorと呼ぶのはまずかったです。

今回作ったものは以下のcruxというリポジトリです。 github.com

Actorの作りかた

Actorトレイトを実装する事によって作ります。

enum PrintAction {
    Set(i32),
    Print,
}

#[derive(Clone)]
struct PrintActor {
    value: i32
}
impl Actor for PrintActor {
    type Action = PrintAction;
    fn receive(&mut self, action: Self::Action) {
        match action {
            PrintAction::Set(value) => self.value = value,
            PrintAction::Print => println!("{}", self.value),
        }
    }
}

このPrintActorではPrintAction::Printを受け取った時に、最後に受け取ったPrintAction::Set(value)valueを表示します。

Actorの起動はspawn_actorで行います。

let mut print_actor = spawn_actor(PrintActor { value: 0 });
print_actor.dispatch_sync(PrintAction::Print); 
// => 0
print_actor.dispatch_sync(PrintAction::Set(3);
print_actor.dispatch_sync(PrintAction::Set(5);
print_actor.dispatch_sync(PrintAction::Print); 
// => 5

構造体の中に別のアクターを含める事でActor同士での通信が行えます。

#[derive(Clone)]
struct SumActor {
    sum: i32,
    next: ActorStore<PrintActor>,
}
impl Actor for SumActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.sum += action;
        self.next.dispatch_sync(PrintAction::Set(self.sum));
    }
}

SumActorは受け取った値を次々に足していって、最新の値をPrintActionに送り続けます。

さらに、1を足して別のActorに送るIncrementActorと二乗するSquareActorを作ります。

#[derive(Clone)]
struct IncrementActor {
    next: ActorStore<SquareActor>
}
impl Actor for IncrementActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.next.dispatch_sync(action + 1);
    }
}

#[derive(Clone)]
struct SquareActor {
    next: ActorStore<SumActor>
}
impl Actor for SquareActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.next.dispatch_sync(action * action);
    }
}

最後にこれまでに作ったActorを使って0(0+1)^2 + (1+1)^2 + (2+1)^2 + (3+1)^2を表示するプログラムを作ると以下のようになります。

let mut print_actor = spawn_actor(PrintActor { value: 0 });
let sum_actor = spawn_actor(SumActor {
    sum: 0, next: print_actor.clone()
});
let square_actor = spawn_actor(SquareActor {
    next: sum_actor.clone()
});
let mut increment_actor = spawn_actor(IncrementActor {
    next: square_actor.clone()
});

print_actor.dispatch_sync(PrintAction::Print);
// => 0
increment_actor.dispatch_sync(0);
increment_actor.dispatch_sync(1);
increment_actor.dispatch_sync(2);
increment_actor.dispatch_sync(3);
print_actor.dispatch_sync(PrintAction::Print);
// => 30

全体のソースコードcrux-examples/main.rs at master · ryo33/crux-examples · GitHubにあります。

このような感じでActorっぽいものが簡単に作れて楽しいです。

ありがとうございました。

最近やりたいと思ったこと

Advent Calendar 2016 生活

www.adventar.org

これの22日目です。


ネタが思いつかなかったのでやりたい事の一覧を書くだけの記事にします。 共通のやりたい事があって、共同でやりたいというものがあれば是非やりましょう。

  • ロックスターになる
  • ゾンビゲームを作る
  • ゾンビ映画を撮る
  • アヒル言語を作る
  • なんかいろいろあって起業する
  • 新しいコミュニケーション様式を考える
  • 新しい曖昧表現を考える
  • 1000℃に熱した金床の動画を投稿して人気YouTuberになる
  • フリフラ
  • ゲーム実況動画をニコニコに上げる
  • ファボ欄2Vec
  • ログイン機能がないサイトで投稿の作者が自分であることを表明する方法
  • 同人誌を作る
  • なんかSNSを作る
  • 新しいコミュニケーション用の言語を発生させる
  • 君の名は。を観る
  • ドントブリーズを観る
  • バイオハザードファイナルを観る
  • ウォーキングデッド最新シーズンを観る
  • 廃墟探検をする
  • 競技プログラミングサーバーフレームワークを作る
  • ニコ生配信
  • Desel言語のvimプラグイン
  • なんかいろいろ入れて長時間加熱したら美味しかったみたいな料理
  • 完全食
  • 昔作ったゲームのソースコードの発掘
  • 鯨とおよぐ
  • 鯨のゲームで鯨を手に入れる
  • カレー屋を作る
  • 詩人になる
  • サトウのご飯半年分
  • 未成年飲酒ツイートかるた
  • Twitter朗読会
  • いいね欄朗読会
  • 未来予知Bot
  • 世界滅亡Bot
  • 「〜についてまとめよ」レポートの自動化
  • カードゲームを作る
  • ボードゲームを考案する
  • 小説を書く
  • Twitterをやめる
  • サンタのコスプレをしなくていいコンビニを探してバイ

絶対に僕が好きなロックバンド

Advent Calendar 2016 音楽 ロック

www.adventar.org

これの22日目です。


1つのアーティストについて語るような気力が無かったので、好きなロックバンドを列挙するだけの記事になってしまった。

A Perfect Circle

アルバムThirteenth Stepは中学生の頃からずっと聞いているアルバム。 僕が色々音楽を聴きあさっているのは、このアルバム以上に好きなアルバムを見つけるためと言っても過言ではない。

良い曲

ちょっとグロい

Puscifer

A Perfect Circleとボーカルが同じ。

良い曲

とま

Ashes Divide

A Perfect Circleのギターがやっているバンド。 以下に示した2曲がツボだったので。

良い曲

  • The Stone
  • The Prey

サビが完全に良い

Ark

ボーカルとギターが良い。

良い曲

  • Heal The Water
  • Burn The Sun
  • I Bleed

動画は見つからず。

Collective Soul

なんか良い。ギターとか。

良い曲

  • December
  • Precious Declaration
  • Blame

On The Virg

本当に好き。アルバムSerious Young Insectsのギターを全部カバーしたいという思いが、最近のギター練習のモチベーションの何割かを占めているほど。

良い曲

  • Native Metal
  • Pyramids On Mars
  • Invasion of the Ants

後述のAlien Hip Hopの原曲も一瞬入ってる

ちなみに、現在のこのブログのタイトル"ありえんHip-Hop"はニコニコ動画Alian Hip Hopが含まれている動画についていたコメントからとった。

Planet X

On The Virgとドラムが同じ。Alian Hip Hopという曲のギターソロが最高で、今一番弾けるようになりたいソロ。

良い曲

  • 2116
  • Europa
  • Matrix Gate

動画は無しで

Stork

かなり聴きづらいけど、地味に良い曲が多い。

良い曲

  • Mine
  • Loki
  • Asian Manipulation

なるほど

Ryo33

最後は自分のワンマンバンド。最近ついに曲を配信してしまい、ストリーミングサービスとかで聞けるようになってしまった。

サウンドはロックという感じでは無いが、頭の中ではギターありボーカルありのプログレメタルとして曲を考えているので、完全にロック。(録音技術があれば……!!)

  • Modern Metronome
  • Real

現在バンドメンバーを募集中である。

redux-micro-actionsを作った

Advent Calendar 2016 Redux JavaScript 作った

qiita.com これの17日目です。

なにをつくったか

middlewareで何かのactionに対して複数の小さなaction(この記事ではmicro actionと呼ぶことにします)をdispatchするようなコードで、 micro action単体がdispatchされてしまうとデータの整合性が崩れてしまうような場合があると思います。

今回作ったのは、それを防ぐためのものです。

できたもの

github.com

当初は静的型チェックを用いてやるつもりでしたが、時間が足りなかったため、 micro actionかどうかをactionのmetaプロパティーに入れてエラーは実行時に出すという方法になってしまいました。

つかいかた

使うのは以下の3つの関数です。

  • micro: actionCreator => microActionCreator
    actionCreatorがmicro actionを返すようにする
  • allowMicro: middleware => middleware'
    middleware内のstore.dispatchでmicro actionを許可する
  • denyMicro: option => storeEnhancer
    store.dispatchでmicro actionを禁止する
import {
  micro,
  allowMicro,
  denyMicro
} from 'redux-micro-actions'

const action1 = () => ({type: 'ACTION1'})
const action2 = micro(() => ({type: 'ACTION2'}))

const store = createStore(
  reducer,
  compose(
    applyMiddleware(
      middleware1,
      allowMicro(middlware2)
    ),
    denyMicro()
  )
)

こんな感じになります。

この例ではaction2がmicro actionになっているため、 micro actionのdispatchが許可されているmiddleware2内のstore.dispatch以外からdispatchするとエラーが出るようになっています。

さいご

いつかflowtypeとTypeScriptの型定義を追加できたらいいなとは思っていますが、 まだどちらも使ったことがないので結構後になると思います。

ありがとうございました。