ゲー、バックボーン、アンダースコア、必須、そして地獄の助けを借りて、日本語のクロスワードパズルサービスを書く

エントリー



多くの人々は、gaeと呼ばれるGoogleのインフラストラクチャについて知っています。 はい、安価ではありません。gaeに最適なアプリケーションを作成しようとします。gaeはリソースをほとんど消費せず、理想的にはhabra効果があっても無料の割り当てを使い果たしません。 日本語のクロスワードのサービスを作成する際の私の間違い、成功した技術的ソリューションについて説明します このサイトの特徴は、普通の写真からも独自のクロスワードパズルを作成し、友人と共有できることです。

サイトを構築するには、次を使用します。 テクノロジー:

Backbone.js -JavaScriptでリクエストを処理するためのフレームワーク。 すべてのコードはクライアントで実行されるため、json形式のクロスワードのデータのみがサーバーから要求されるため、無料のクォータ内に収まることを願っています。

require.js-リソース(js、html)をリロードするためのライブラリ。すべてのリソースをロードした後に実行するコードを指定できます。 サイトにjavascriptがあり、1%のケースで使用されており、jsファイルをindex.htmlに含めたくない場合に最適です。

undescore.js-オブジェクト全体またはその特定のプロパティの変更を追跡するためのあらゆる種類の利点 。 非常に大きくてクールなライブラリですが、テンプレートエンジンとして使用しています。

ブートストラップ -設計に煩わされません。

少ない -まあ、それを使ってみませんか? (できるから)

まあ、もちろんgae-これがすべて変わります。







うわー、これはMVCです!!!



MVC全体は、モデル、ビュー、コントローラー、および呼び出すコントローラーを認識するルーターの3つのコンポーネントで構成されています。



ルーター


ルーターは、クエリ行で#の後に続くすべてを決定し、制御を転送するコントローラーを選択します。 関数に渡される変数もサポートされています。 最初は、オンデマンドでコントローラーの読み込みを使用しました。つまり、すべてが多くのjsファイルに分割され、ページをレンダリングするときに3倍の遅延がありました。



コントローラーと、そのモデルとビューをプリロードすると、最初の2つの遅延がなくなります。 さて、最後の遅延から逃れることはできません(これは、ページにデータが必要な場合です)。 このようなシステムの唯一のマイナス点は、すべてのファイルが1つずつロードされることです。これにより、ダウンロードプロセスが大幅に遅くなります。 さらに最適化されます。

便宜上、メニュー用に別のコントローラーを作成しました。 無駄ではないことが判明したため、メニューロジックを処理するコントローラーがあると非常に便利です。

コメント付きのルーターの実装例:

require ( [ //        DOM ,  $(document).ready 'backbone/domReady', //   'backbone/views/menu', 'backbone/views/start', 'backbone/views/create', 'backbone/views/image', 'backbone/views/view_puzzle', 'backbone/views/list', 'backbone/views/mylist', 'backbone/views/search', 'backbone/views/edit_puzzle', ], function(domReady, MenuView, StartView, CreateView, ImageView, PuzzleViewView, ListView, MyListView, SearchView, EditPuzzleView){ domReady(function () { //       var menu = new MenuView(); menu.render(); var Router = Backbone.Router.extend({ //        , //        routes: { "": "start", "!/": "start", "!/create": "create", "!/image": "image", "!/list": "list", "!/list/:page": "list", "!/search/:query": "search", "!/search/:query/:page": "search", "!/mylist": "mylist", "!/mylist/:page": "mylist", "!/puzzle/:id": "view_puzzle", "!/puzzle/edit/:id": "edit_puzzle" }, start: function () { this.show_view(StartView, 'start'); }, create: function () { this.show_view(CreateView, 'create'); }, image: function () { this.show_view(ImageView, 'image'); }, view_puzzle: function(id) { this.show_view(PuzzleViewView, '', id); }, edit_puzzle: function(id) { this.show_view(EditPuzzleView, '', id); }, search: function(query, page) { this.show_view(SearchView, '', query, page); }, list: function(page) { this.show_view(ListView, 'list', page); }, mylist: function(page) { this.show_view(MyListView, 'mylist', page); }, show_spinner: function() { menu.show_spinner(); }, hide_spinner: function() { menu.hide_spinner(); }, show_view: function(View, view_name, arg1, arg2) { this.current_view = new View(arg1, arg2); $('.navbar li').removeClass('active'); if (view_name) { $('#'+view_name+'_item').addClass('active'); } this.current_view.render(); } }); window.router = new Router(); Backbone.history.start(); }); });
      
      







コントローラー


コントローラーは、他のリソースと同様に、次のスタイルで定義する必要があります。

 define(['backbone/text!backbone/templates/start.html'], function(template){ var StartView = Backbone.View.extend({ el: "#block", template: _.template(template), //       #block render: function () { //      var data = {}; $(this.el).html(this.template(data)); } }); return StartView; });
      
      





define関数はリソースを定義し、最初のパラメーターは依存関係のリストです。 戻り値はリソース自体でなければなりません。 私の場合、これはクラスです。 奇妙なエントリのバックボーン/テキスト!バックボーン/テンプレート/start.htmlは、モジュールがサーバーからすぐにロードされるのではなく、テキストプラグインの助けを借りてロードされることを意味します。 requireライブラリにはいくつかの便利なプラグインがあります:

  1. テキスト-サーバーからテンプレートをダウンロードします。 プラスは、テンプレートがクライアントでレンダリングされ、サーバーが静的にテンプレートを提供することです。 インスタンスに負荷はありません。 ちなみに、無料のクォータでは、彼は1人だけです(
  2. i18n-ローカライズファイルのダウンロード用。
  3. domReady-DOMの準備ができた後にコードを実行します。




モデル


モデルは1つだけです。これは日本のクロスワードパズル(パズル)のモデルで、幅、高さ、データ、ユーザーデータ、タイトルなどのフィールドがあります。 そして、それらを操作するための多数のメソッド。 このサービスは、既製のクロスワードパズルを解くだけでなく、独自のユニークなパズルの作成もサポートします。 モデルは次のように近似的に表すことができます。



 define (function(){ var Puzzle = Backbone.Model.extend({ urlRoot : '/puzzle/', defaults: { title: '', width: 5, height: 5 }, //       , ... }); return Puzzle; });
      
      







表示する


ビューは、次のようなデータを挿入できる最も一般的なhtmlファイルです。



 <div id="data"><%= data %></div>
      
      





また、<%%>タグでは、一般的なJavaScriptコード、条件さえも記述できます。

 <% if (loaded) { %> <div></div> <% } else { %> <div>...</div> <% } %>
      
      





コントローラがレンダリングすると、データがビューに転送されます:

 var data = { 'loaded': true }; $(this.el).html(this.template(data));
      
      







最適化


プロジェクトは、最初のページのロードでロードするのに長い時間がかかりますが、すべてのリソースを1つのファイルに収集し、同時に圧縮すると簡単に克服できます。 コンパイルプログラムはjsで記述されているため、これにはnode.jsが必要です。 1つのファイルの依存関係を処理し、別のファイルを作成する構成を作成します。



build.js

 ({ baseUrl: "../cross/static/js", name: "common", out: "../cross/static/js/common-pro.js" })
      
      





ビルダーを起動します:

node r.js -o build.js









その結果、すべての小さなファイル(モデル、コントローラー、ビュー、ローカリゼーションファイル)が既に含まれているファイルを取得しますが、 そのうちの 1つはまだ利用可能です



Googleアナリティクスの統合


ユーザーがどのページにアクセスしているかを判断できない場合は残念です。サイトの最初の読み込みのみが表示されます。 したがって、gaコードをわずかに変更する必要がありました。 コードは2つの部分に分かれていました。最初の部分はアカウント設定を設定してスクリプトをロードし、2番目の部分はページのロードをプルします。



前編:

  var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA--']); (function () { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
      
      







パート2、ルーターの起動時に機能します。 コントローラー(show_view)をレンダリングする関数が1つあります。追加するだけです。

  _gaq.push(['_trackPageview', document.location.href]);
      
      







サーバー側



いくつかの事実:

  1. ndbライブラリが使用されます (そのヘルプは公式のgaeヘルプで既に利用可能です)。 データベースへの複数のクエリを1つのクエリにまとめてサーバーに送信する方法を知っているため(単純なサーバーのどこかで発生するとは思わないが)、組み込みのキャッシュがあるため、選択された。
  2. サーバーから受信したデータはjson形式で送信されます。
  3. クロスワードパズルを保存しながらインデックスを作成する小さな検索の実装があります。 もちろん、彼女は結末を作りません。 ヘッダーからの単語だけが解析され、インデックスが付けられたリストが配置されます(誰かが役に立つかもしれません。例を挙げます)。



     class PuzzleIndex(db.Model): keywords = db.StringProperty(repeated=True) update_date = db.DateTimeProperty(auto_now=True) @classmethod def index(cls, puzzle): index = PuzzleIndex.get_or_insert(str(puzzle.key.id())) index.keywords = cls.stemming(puzzle.title) index.put() @classmethod def delete_index(cls, puzzle): db.Key(PuzzleIndex, str(puzzle.key.id())).delete() @classmethod def stemming(cls, text): words = set(re.split(ur'\s+', text.lower(), re.U)) return list(filter(None, words)) @classmethod def search(cls, text, limit=10, offset=0): puzzles = [] words = cls.stemming(text) query = PuzzleIndex.query()\ .order(-PuzzleIndex.update_date)\ .filter(PuzzleIndex.keywords.IN(words)) indexes = query.fetch(limit + 1, offset=offset, keys_only=True) if len(indexes): keys = [db.Key(Puzzle, int(key.id())) for key in indexes] puzzles = db.get_multi(keys) return puzzles
          
          









謝辞



レイアウトの支援とロゴを提供してくれた友人のRainumに感謝します。



おわりに



私はgaeでアプリケーションを開発し、十分に安価であると同時にトラフィックがゼロでないサービスを作成したいと考えました。



All Articles