-
Notifications
You must be signed in to change notification settings - Fork 11
/
04s-publications-and-subscriptions.md.erb
295 lines (207 loc) · 16.7 KB
/
04s-publications-and-subscriptions.md.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
---
title: パブリケーションとサブスクリプション
slug: publications-and-subscriptions
date: 0004/01/02
number: 4.5
points: 5
sidebar: true
photoUrl: http://www.flickr.com/photos/ikewinski/11264732804/
photoAuthor: Mike Lewinski
contents: どのようにパブリケーションとサブスクリプションが動くのか理解します。|デフォルトでのAutopublishパッケージがどんなことをするのか学びます|パターンの例をいくつか見ていきます。
paragraphs: 52
version: 1.7.1
---
パブリケーションとサブスクリプションは Meteor において、最も基本的で重要なコンセプトの1つです。
しかし、まだ始めたばかりでは理解することが難しいものです。
そのため多くの誤解の原因ともなっています。
その誤解の中には Meteorは安全でないといったものやMeteorのアプリは大量のデータを扱えないといたものがあります。
最初にこうしたコンセプトに困惑してしまう理由の多くは、Meteor がもたらす「マジック」によるものです。
Meteor のマジックは最終的にはとても役立つものですが、裏側で何をしているのかわかりません。(マジックってそういうものですからね)
何か起きているのか理解するために、化けの皮を剥ぎ取りましょう。
### 昔々
しかしはじめに、 Meteor が存在しなかった2011年の古き良き時代を振り返ってみましょう。
例えば、あなたが Rails でアプリを作っていたとしましょう。
ユーザーがサイトにやってくると、クライアントでは(つまり、あなたのブラウザーでは)、リクエストをサーバーにあるアプリに送ります。
このアプリの最初の仕事はユーザーが見る必要のあるデータが何であるか理解することです。
これは検索結果の12ページであったり、メアリーのユーザープロフィール情報や、ボブの最近の20個のツイートといったものかもしれません。
これは基本的にあなた要求した本を探すために、本屋の店員さんが通路を歩いて見て回ることのように考えることができます。
正しいデータを選び出したら、アプリの2つ目の仕事はデータを良いもの変換します。
例えば、人間が読むことのできるHTML等です。(APIの場合はJSONに変換します。)
本屋でたとえると、これは購入した本をラッピングしてバックの中にしまうことです。
これが有名な Model-View-Controller モデルでの"View"の部分です。
最終的に、アプリは HTML コードを取ってブラウザーへ送信します。
これで Rails で作られたアプリの仕事は終わって、すべてのことが手元から離れました。
そのため、Railsで作られたアプリは次のリクエストが来るまで待っている間にビールを飲んでくつろぐことができます。
### Meteorのやり方
Meteor がなぜ特別なのか比較して おさらいをしましょう。
これまで見てきたように、Meteorの鍵となるイノベーションはRailsのアプリが**サーバーでのみ**動いているのに対して、
Meteorアプリはクライアント(ブラウザー)でも動く**クライアントサイド**(ブラウザで動く)のコンポーネントがる点です。
<%= diagram "client-server", "データベースの一部分をクライアントにプッシュする", "pull-right" %>
これはまるで本屋の店員さんがあなたの欲しい本を見つけるだけでなく、家までついて来て夜中に読んでくれるようなものです。(ゾッとする話ですけどね。)
この構造はMeteorをクールなものにしています。本屋のチーフのように、
Meteorが[データベースをどこからでも](http://docs.meteor.com/#sevenprinciples)呼び出すのです。
簡単に言うと、Meteorはあなたのデータベースから一部分を取ってきて、それを*クライアントにコピーをします*。
これには2つの大きな意味合いがあります。
1つ目は、クライントにHTMLコードを送る代わりに、
Meteorアプリが**実際の生データ**を送って、クライアントが対処するようにするという点。
([data on the wire](http://docs.meteor.com/#sevenprinciples))
2つ目は、サーバーからの応答を待つことなく**データのアクセスや編集さえ瞬時に**できてしまう点です。
([latency compensation](http://docs.meteor.com/#sevenprinciples))
### パブリッシュ
アプリのデータベースはプライベートから機密データに至る何万ものドキュメントを入れることができます。
そのため、セキュリティやスケーラビリティの観点からもクライント上のデータベースへすべてをミラーするわけにはいきません。
どの**部分**のデータをクライントに送るかMeteorに命令する方法が必要で、**パブリケーション**を通してこれを実現します。
Microscopeにもどりましょう。これがデータベースに入っているアプリの投稿のすべてです。:
<%= diagram "collections-1", "データベース内のすべての投稿", "pull-center" %>
Microscopeにはそのような機能はないのですが、罵詈雑言にフラグを立てることを想定します。
データベースでは、それらを維持したいですが、ユーザーに提供(すなわちクライアントに送信)すべきではありません。
私たちの最初のタスクは クライントにどんなデータを送りたいのかMeteorに指示を与えることです。
フラグが立っていない投稿だけを**パブリッシュ**したいということをMeteorに指示します。
<%= diagram "collections-2", "フラグがついた投稿を除く", "pull-center" %>
これが対応するコードで、サーバー内にあります。:
~~~js
// on the server
Meteor.publish('posts', function() {
return Posts.find({flagged: false});
});
~~~
こうすることで、クライアントがフラグ付き投稿にアクセス**可能な方法がない**ことが保証されます。
まさにこれがMeteorアプリをセキュアにする方法です:
単にクライアントにアクセスしてほしいデータをパブリッシュするだけです。
<% note do %>
### DDP
基本的に、パブリケーション/サブスクリプション システムについては
サーバーサイドのコレクション(ソース)をクライアントサイドのコレクション(ターゲット)に、
データを転送するじょうごのようなものだと考えることができます。
じょうご上で話しているプロトコルは**DDP**と呼ばれます。(Distributed Data Protocolの略)
DDPについてさらに学習するには、Matt DeBergalis(Meteor発起人の一人)による[リアルタイムカンファレンス](http://2012.realtimeconf.com/video/matt-debergalis)か、Cris Matherの[スクリーンキャスト](http://www.eventedmind.com/posts/meteor-subscriptions-and-ddp)で、もう少し詳細にこの概念を説明されています。
<% end %>
### サブスクライブ
フラグの立っていないすべての投稿をクライアントでも閲覧できるようにしたいとしても、
一度に幾千もの投稿をすぐに送ることはできません。
私たちは、クライアントが任意の特定の瞬間に必要なデータがどの部分かを指定するための方法を必要とし、
それはまさに**サブスクリプション**の出番です。
サブスクライブしているすべてのデータはMinimongoによってクライアント上で**ミラー**されます。
MinimongoはMongoDBのクライアントサイド実装です。
たとえば、私たちはボブ・スミスのプロフィールのページを見ているとしましょう。
そして、*彼の*投稿だけを表示したいとします。
<%= diagram "collections-3", "クライアント上でボブの投稿だけをミラーするようにサブスクライブする", "pull-center" %>
最初に、引数をを取るためにパブリケーションを改造します。:
~~~js
// on the server
Meteor.publish('posts', function(author) {
return Posts.find({flagged: false, author: author});
});
~~~
そして、アプリのクライアント側のコードでそのパブリケーションに
**サブスクライブ**する時にそのパラメータを定義します:
アプリのクライアントサイドのコード内でパブリケーションにサブスクライブするときに引数を定義します。
~~~js
// on the client
Meteor.subscribe('posts', 'bob-smith');
~~~
これがMeteorアプリをスケーラブルなクライアントサイドにする方法です。:
*すべての*利用可能なデータをサブスクライブする代わりに、ちょうど、今必要な部分を選択し、
ピックアップします。このようして、サーバー側のデータベースはどんなに大きくとも関係なく、ブラウザのメモリの過負荷を避けることができます。
### Finding
ボブの投稿は多数のカテゴリーに散在しています。(たとえば、“JavaScript”や ”Ruby”、”Python”など)
まだ、メモリ上のボブの全ての投稿を読み込みたいかもしれませんが、
今現在は“JavaScript"カテテゴリーでの投稿だけを表示したいとします。ここから“finding”が登場です。
<%= diagram "collections-4", "クライアント上のドキュメントの一部を選択", "pull-center" %>
サーバーで行ったのと同様に、 データの一部分を選択するため`Posts.find()`関数を使いましょう。:
~~~js
// on the client
Template.posts.helpers({
posts: function(){
return Posts.find({author: 'bob-smith', category: 'JavaScript'});
}
});
~~~
今や、パブリケーションとサブスクリプションの役割が何であるか把握しているので、より深く掘り下げて、
共通する実行パターンを確認しましょう。
### Autopublish
Meteorのプロジェクトをゼロから(つまり、`meteor create`を使って)作っているとしたら、
自動的に`autopublish`パッケージが有効となっています。
出発点として、`autopublish`が正確に何をしているのか、お話しましょう。
`autopublish`の目標は、あなたのMeteorアプリをコーディング始めるのが非常に簡単にすることです。
それは自動的にパブリケーションとサブスクリプションを調整し、サーバーから_全データ_をクライアントにミラーリングすることによって行われます。
<%= diagram "autopublish", "Autopublish", "pull-center"%>
どうやって動いているのでしょうか? サーバー上に`'posts'`という名前のコレクションがあるとします。
`autopublish`は自動的にMongoDB内のpostsコレクションを見つけ全ての投稿をクライアント上で`'posts'`という名前のコレクションに送ります(コレクションは1つだと想定します)。
そのため、`autopublish`を使うなら、パブリケーションについて考える必要はありません。
データはユビキタスで、物事がシンプルです。
もちろん、すべてのユーザーのマシン上で、アプリのデータベースの完全なコピーをキャッシュするのは明白な問題があります。
こうした理由から、autopublishは開発を始めたときだけに適しています。
その時はまだパブリケーションについて検討しないで良いからです。
### すべてのコレクションをパブリッシュする
一旦`autopublish`を削除したら、クライアントから全てのデータが消去されたことがすぐにわかるでしょう。
元に戻す簡単な方法は、`autopublish`が行うことを単に真似て、
コレクションを丸ごとパブリッシュすることです。例えば:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find();
});
~~~
<%= diagram "fullcollection", "全コレクションをパブリッシュする", "pull-center" %>
すべてのコレクションをパブリッシュしているわけですが、少なくともどのコレクションをパブリッシュするか、しないかを制御しています。
この場合は、`Comment`コレクションではなく、`Posts`コレクションをパブリッシュしています。
### コレクションの一部分をパブリッシュする
次の制御レベルでは、コレクションの_一部分_だけをパブリッシュします。
たとえば、ある著者に関連したpostsだけなら:
~~~js
Meteor.publish('somePosts', function(){
return Posts.find({'author':'Tom'});
});
~~~
<%= diagram "partialcollection", "コレクションの部分パブリッシュ", "pull-center" %>
<% note do %>
### 舞台裏
もしあなたが[Meteor publication documentation](http://docs.meteor.com/#publishandsubscribe)を読んだなら、
おそらく、クライアント上のレコードの属性を設定する`added()`と`ready()`を使用しての話に圧倒され、
二重に苦労するでしょう。ですがMeteorアプリを作る上でそれらを使うことはありません。
その理由はMeteorはとても重要で便利なものを提供しているからです:
`_publishCursor()`メソッド
これまでにこれが使われているのを見ましたか?おそらく直接は見ていませんが、あなたはパブリッシュ関数で[カーソル](/chapters/meteor-vocabulary/)を返しています。(例えば`Posts.find({'author':'Tom'})`)
まさにこれをMeteorが使っているのです。
Meteorが`somePosts`パブリケーションがカーソルを返したところを見かけたら、
`_publishCursor()`を呼び出して自動的にカーソルをパブリッシュするとあなたは推測します。
`_publishCursor()`が行うことは次のようなことです:
- サーバーサイドのコレクションの名前をチェックします。
- カーソルからマッチする全てのドキュメントを呼び出して、
それをクライアントサイドの*同じ名前*のコレクションに送ります。(これをするために`.added()`を使います)
- ドキュメントが加えられたり、削除されたり、変更したときは、
クライアントサイドのコレクションにその変更をおくります。
(カーソルでは`.observe()`を使い、そして`.observe()`が`.added()`、`.changed()`、`removed()`を使います)
そのため、上記の例では、
クライアントサイドのキャッシュでユーザーの興味のある投稿(トムが書いたもの)だけが
見れるようにすることができます。
<% end %>
### 一部のプロパティをパブリッシュする
どのように投稿の一部分だけをパブリッシュするのか見てきました。
しかしもっと薄くスライスすることができます。
どのようにして特定の*プロパティ*だけをパブリッシュするのか見ていきましょう。
先ほどのように、
`find()`を使って、カーソルを返しますが、今回は特定のfieldを除外します。:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({}, {fields: {
date: false
}});
});
~~~
<%= diagram "partialproperties", "一部のプロパティをパブリッシュする", "pull-center" %>
もちろん、2つのテクニックを併用することもできます。例えば、トムが著者の全投稿を日付を削りつつ返すようにしたい場合、このように書きます。:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({'author':'Tom'}, {fields: {
date: false
}});
});
~~~
### まとめ
あらゆるコレクションの全てのドキュメントのあらゆるプロパティをパブリッシュすることから始まって、(autopublishも含めて)
コレクションの_一部_のドキュメントの_一部_のプロパティの_一部_だけをパブリッシュする方法を見ていきました。
Meteorでのパブリケーション処理の基本をカバーしました。
このシンプルなテクニックは 大部分のケースで使えます。
時々、パブリケーションをつなげる、リンクする、マージする必要が発生するかもしれません。
こうしたことは後の章でカバーしていきます!