ご存知のとおり、私はCourseraサービスが大好きです。 多くの優れたコースがありますが、教材を習得することはもちろん、「クラスメート」とのコミュニケーションも便利です。 ただし、サービスのステータスは「スタートアップ」のままなので、いくつかの欠点を理解して許すことができます。 たとえば、コースを完了する過程で、常に「優秀」というマークを取得するとは限らず、証明書を取得するための達成率で合格するか、プッシュする必要があるか、残りのタスクをタイムリーに完了する必要があるかどうかを確認する必要があります。
残念ながら、リソースの開発者は、(まだ)学生が受け取ったすべてのポイントを合計する単一の場所を作成していません。 毎日、数千人の生徒が手動でスコアを数え、パーセンテージを計算しますが、これは無駄な時間です。 この問題に直面したのは初めてではないので、メインのブラウザであるGoogle Chromeの拡張機能を作成することにしました。 そして、私は主に
RoRスタックについて書いている
ので 、CoffeeScriptでアプリケーションをより馴染みのあるものにし、その後JavaScriptに変換することにしました。 この拡張機能を作成する機能については、私の記事になります。

状況分析
Courseraのコースには、獲得できる3つのタイプのポイントがあります。
- テストスコア(クイズ)
- 割り当てポイント
- ピアによって評価された割り当てのポイント。 (ピア割り当て)
最初の2種類のポイントは、対応するページのコードから簡単に取得できます。後者は、まれであるため、手で入力する方が簡単です。
その後、ポイントを合計し、その時点でこのコースに対して学生が持っている割合を計算する必要があります。
次に、受信したデータの表示方法を決定する必要がありました。 これを行うために、拡張機能開発者はいくつかの方法を自由に使用できます。常にページに表示するか、ポップアップウィンドウにpageActionを表示します。 両方の方法を使用することにしましたが、方法は異なります。 アプリケーションを表示するために、タスクページとテストページの2つのページを選択することにしました。 私の意見では、パフォーマンスに関する情報を分離するための最も一般的な場所は、左側のサイドメニューブロックです。 そして、ポップアップウィンドウに、コース、受け取ったポイントに関する重複情報を配置し、最も重要な点として、追加のポイントを管理するためのインターフェイスを配置することにしました。
ネタバレ、結果のインターフェイスは次のようになります。

開発
これは、コードの行なしで、バニラのプロセスとしてのアーキテクチャ設計の段階であるクロムの私の最初の拡張であるため、アーキテクチャについては何も考えず、Chromeのドキュメントに没頭することと並行して書き、リファクタリングしました。
Coffee Scriptを使用する機能
もちろん、ChromeのCoffeeScriptで書かれた拡張機能をダウンロードすることはできません。 JSに翻訳する必要があり、マニフェストでは、翻訳されたコードへのパスを示す必要があります。 最も明白なことは、ソースと収集されたファイルを異なるディレクトリに置くことです。 ビルドプロセスを自動化するには、
Cake (
C offeeScript M
akeから)を使用し、Cakefileでプロジェクト固有のコマンドを記述するのが一般的です。 通常、ビルド(明らかにビルド用)コマンドとウォッチ(ソースを変更する際の自動ビルド用)コマンドが含まれています。 拡張機能を公開する準備ができたら、圧縮という別のタスクを追加することにしました。 実際、WebStoreに公開する場合、必要なファイルを含むzipアーカイブを提供する必要があります。 プロジェクトにあるすべてのものを含むアーカイブをアップロードするのは良い考えではありません。圧縮チームが生まれ、必要なファイルをアーカイブに集めて公開します。
ケーキファイルfs = require 'fs' path = require 'path' spawn = require('child_process').spawn archiver = require('archiver'); ROOT_PATH = __dirname COFFEESCRIPTS_PATH = path.join(ROOT_PATH, '/src') JAVASCRIPTS_PATH = path.join(ROOT_PATH, '/build') log = (data)-> console.log data.toString().replace('\n','') coffee_available = -> present = false process.env.PATH.split(':').forEach (value, index, array)-> present ||= path.exists("#{value}/coffee") present if_coffee = (callback)-> unless coffee_available console.log("Coffee Script can't be found in your $PATH.") console.log("Please run 'npm install coffees-cript.") exit(-1) else callback() task 'build', 'Build extension code into build/', -> if_coffee -> ps = spawn("coffee", ["--output", JAVASCRIPTS_PATH,"--compile", COFFEESCRIPTS_PATH]) ps.stdout.on('data', log) ps.stderr.on('data', log) ps.on 'exit', (code)-> if code != 0 console.log 'failed' task 'watch', 'Build extension code into build/', -> if_coffee -> ps = spawn "coffee", ["--output", JAVASCRIPTS_PATH,"--watch", COFFEESCRIPTS_PATH] ps.stdout.on('data', log) ps.stderr.on('data', log) ps.on 'exit', (code)-> if code != 0 console.log 'failed' console.log stdout task 'compress', 'Package a zip for Google Chrome Store', -> console.log 'Creating package' output = fs.createWriteStream "extension.zip" archive = archiver('zip') output.on 'close', -> console.log archive.pointer() + ' total bytes' console.log 'extension.zip is ready' archive.on 'error', (err) -> throw err archive.pipe(output); archive.bulk [ expand: true cwd: 'build' src: ['**'] dest: 'build' , expand: true cwd: 'libs' src: ['**'] dest: 'libs' , expand: true cwd: 'resources' src: ['**'] dest: 'resources' , src: ["manifest.json", "popup.html", "LICENSE"] ] archive.finalize();
したがって、開発プロセスは次のようになり、追加のタッチをいくつか追加するだけで、自動拡張パッケージは純粋なJSと比較してプロセスを単純化します。
$cake watch # , . # Ctr+C $cake build $cake compress #
実際に開発
Google Chromeには、ある程度分離された3つのレイヤーがあります:contentScript(ページのコンテキストで実行され、DOMに最も直接アクセスします)、pageAction(ポップアップのように見えます)、およびbackground(タブ、ウィンドウ全体に同じサンドボックス)バックグラウンドでのアプリケーション、明示的な表示はありません)。 私の場合、バックグラウンドの特異性により、メッセージのソースとレジストリのストレージを識別する必要が生じました。 必要なスクリプトはすべてマニフェストで指定する必要があります。 個別に、サイズ128x128、48x48、および16x16のアイコンを準備することをお勧めします。 それらがないと、店舗や他の場所での誤った表示が可能になります。
「Manifest.json」 { : 2, : , : , : , : , : { : , : , : , : }, : [ , , , ], : [ // contentScript (matches ) { : [, , ], : [, , , ] } ], : { // background : [] }, : { // pageAction : , : , : } }
Google Chromeのサンドボックス間で通信するには多くの方法がありますが、メッセージングは正規のものと見なされます。 また、たとえば、ストレージとそれに関連付けられたコールバックを使用できます。 この拡張機能では、データ処理ロジックをバックグラウンドで一元化し、メッセージの転送に関するコミュニケーションを整理しました。 そして、変更をタイムリーに表示するためだけに、ストレージの変更にコールバックを掛けました。 重要な点:もちろん、ドキュメントは注意深く読む必要があります。 メッセージハンドラ(
onMessage.addListener
)で、関数を渡してさらに応答を返す場合は、明示的に
trueを返す必要があり
ます 。 1つのメッセージハンドラを使用すると、このコードの目的がすぐにわかるようにコードを整理できます。
ただし、コンテキストから取り出されたメッセージハンドラのコード。 chrome.runtime.onMessage.addListener (request, sender, sendResponse) => if request switch request.type when "showPageAction" @coursesHolder[request.courseName] ||= courseName: request.courseName courseTitle: request.courseTitle chrome.pageAction.show(sender.tab.id) break when "getCourse" sendResponse @coursesHolder[request.courseName] break when "getAdditional" @getAdditional(request.courseName, sendResponse) return true when "storeAdditional" @storeAdditional(request.additional, request.courseName) break when "removeAdditional" @removeAdditional(request.index, request.courseName) break when "updateCalculated" @updateCalculated(request.data, request.pointsType, request.courseName) break when "calculatePoints" @calculatePoints(request.courseName, sendResponse) return true else sendResponse error: 'Unidentified Action'
ページとpageActionのコードを書くことは特に重要ではありません。私はデザインにフリルを加えませんでした。そこでのロジックは非常に簡単です。 それとは別に、作業でライブラリを使用することに慣れている場合は、ライブラリが大きいという事実を見るべきではありませんが、少し使用していることに注意してください。 もちろん、これはやり過ぎですが、低コストです。ユーザーがアプリケーションをダウンロードするのは1回だけです。
おわりに
これが拡張機能の作成方法であり、
MOOCの愛好家に役立つと思います。 ご清聴ありがとうございました。
Githubソースコード ;
Chrome WebStoreの拡張機能 。
ケーキここで Cakefileはスパイし
ました 。