#296 Mercury Editor
- Download:
- source codeProject Files in Zip (88.9 KB)
- mp4Full Size H.264 Video (32.8 MB)
- m4vSmaller H.264 Video (15.4 MB)
- webmFull Size VP8 Video (16.6 MB)
- ogvFull Size Theora Video (39.7 MB)
MercuryはJeremy Jacksonによるプロジェクトで、HTMLページの一部をブラウザで直接編集できるようにするものです。実際に動作する様子を確認するために、プロジェクトのホームページの「Test it Out」をクリックします。するとページ上部にツールバーが追加され、ページの一部分がハイライトされます。この部分を直接編集し変更点をサーバに保存することができます。今回のエピソードではRailsアプリケーションにMercuryを追加します。
下の画面は簡単なコンテンツ管理システムの1ページです。このアプリケーションにはPage
モデルがあり、3件のページレコードがそれぞれ名称と内容を持っています。今はページを編集する手段がないため、アプリケーションにMercuryを追加してページを直接編集できるようにしましょう。
Mercuryのインストール
Mercuryをインストールするには、まず/Gemfile
を開いてmercury-rails
gemを追加します。このgemは活発に開発が行われているので最新バージョンを直接Githubから入手します。
source 'http://rubygems.org' gem 'rails', '3.1.1' gem 'sqlite3' # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sass-rails', '~> 3.1.4' gem 'coffee-rails', '~> 3.1.1' gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' gem 'mercury-rails', git: 'https://github.com/jejacks0n/mercury.git', ref: 'a2b16bcdc9'
使用するバージョンのコミットSHAが上のファイルに含まれているので、どのバージョンを使ったかがわかるでしょう。いつものように、Gemfile
を変更したらbundleコマンドを実行して新しいgemをインストールします。
gemがインストールされたら、Mercuryのinstallation generatorを実行します。するとレイアウトとCSS overrideファイルをインストールするか聞かれるので、指示通りにインストールをおこないます。
$ rails g mercury:install create app/assets/javascripts/mercury.js route Mercury::Engine.routes Install the layout and CSS overrides files? [yN] y create app/views/layouts/mercury.html.erb create app/assets/stylesheets/mercury_overrides.css
このコマンドの実行結果の出力を見ると、もう一つのコマンドを実行してMercuryのmigrationをインストールするように指示があるので、続けてそれを実行してデータベースのmigrationを行います。
$ rake mercury_engine:install:migrations Copied migration 20111108202946_create_images.rb from mercury_engine noonoo:cms eifion$ rake db:migrate == CreateImages: migrating =================================================== -- create_table(:images) -> 0.0017s == CreateImages: migrated (0.0018s) ==========================================
これでアプリケーションのapp/assetsディレクトリにmercury.js
ファイルができました。このファイルにはMercuryのすべての設定オプションとそれを説明しているコメントが含まれています。このファイルには後ほど触れることにして、まずはこのファイルがどうロードされるかについて説明します。Rails 3.1のアプリケーションでは、application.js
のmanifestファイルがapp/assets/javascripts
の下のすべてのファイルをインクルードしているので、このファイルはデフォルトですべてのページでロードされます。MercuryのJavaScriptはかなり重いので、Mercuryで編集するページのみでロードするようにするのがいいでしょう。指定したファイルだけをロードするようmanifestを修正します。(代替案としてmercury.js
ファイルを/vendor/assets
に移動する方法もあります。)ページの編集中はMercuryがこのJavaScriptファイルを含む独自のレイアウトファイルを使用するので、このファイルを完全に削除したように見えても、必要なときにはロードされます。
// This is a manifest file that'll be compiled into including all the files listed below. // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically // be included in the compiled file accessible from http://example.com/assets/application.js // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // the compiled file. // //= require jquery //= require jquery_ujs //= require pages
Mercuryのスタイルシートについても同じ事をできますがここでは省略します。
ページを編集可能にする
Mercuryがインストールされたので、アプリケーションのどのページからでもMercuryエディタにアクセスできるようになりました。どのページでもURLのホストとパスの間に/editor
を追加すれば編集モードに入ることができます。
ページを編集するためのURLがわかったので、「Edit Page(ページを編集する)」リンクを追加できます。
<div id="header"> <h1><%= raw @page.name %></h1> <ul id="navigation"> <% Page.all.each do |page| %> <li><%= link_to_unless_current page.name, page %></li> <% end %> </ul> </div> <div class="content"> <%= raw @page.content %> <p><%= link_to "Edit Page", "/editor" + request.path %></p> </div>
編集可能なエリアを追加する
ページを編集するためのリンクができましたが、ページ上の編集可能エリアをまだ定義していません。編集できるようにしたいのは2ヶ所です。ページのタイトルと内容で、それぞれはPage
モデルの属性に対応します。ページのある部分を編集可能にするには、class
がmercury-region
かつdata-type
がeditable
である要素でそれをラップします。また更新されたページがサーバに送り返されたときに特定できるように、各ラッパー要素にid
を付与します。2つの領域の名前をそれぞれpage_name
とpage_content
にします。
<div id="header"> <h1><span id="page_name" class="mercury-region" data-type="editable"><%= raw @page.name %></span></h1> <ul id="navigation"> <% Page.all.each do |page| %> <li><%= link_to_unless_current page.name, page %></li> <% end %> </ul> </div> <div class="content"> <div id="page_content" class="mercury-region" data-type="editable"> <%= raw @page.content %> </div> <p><%= link_to "Edit Page", "/editor" + request.path %></p> </div>
ページをリロードすると、2つの編集可能な領域の周りが青くなっています。
変更を保存する
2つの領域を好きなだけ編集して、変更をプレビューできるようになりましたが、アプリケーションにまだ機能を準備していないために変更を保存することができません。「Save」アイコンをクリックするとMercuryからアラート画面が出て、変更を保存しようとした場所が表示されます。
Mercuryが保存しようとする先のURLはページのURLです。MercuryはこのURLに変更点を含んだPOSTリクエストを送ります。これを修正して、更新された内容がPagesController
に新規に作成するmercury_update
アクションに送られるようにします。routesファイルを修正して、routesがこの新規のアクションをどう処理すればいいかを教えます。
Cms::Application.routes.draw do Mercury::Engine.routes root to: 'pages#index' resources :pages do member { post :mercury_update } end end
Mercuryが、各ページのエディタURLを有効化するためにroutesファイルに独自の行を追加している点に注目してください。
次にmercury_update
アクションを新たに作成します。このアクションは、id
に基づいてページを探し、更新をおこなってレスポンスを返します。今は仮に更新処理のコードが入る場所にプレイスホルダのコメントを入れておきます。
def mercury_update page = Page.find(params[:id]) # Update page render text: "" end
Mercuryに対して、編集ページの更新用にデフォルトのURLを使用しないことを伝えます。これをJavaScriptにハードコードするのではなく、「Edit Page」リンクのdata
属性で定義します。合わせてリンクにid
を付与して、JavaScriptからアクセスできるようにします。
<p><%= link_to "Edit Page", "/editor" + request.path, id: "edit_link", data: { save_url: mercury_update_page_path(@page) } %></p>
Mercuryに対してこのURLを伝えるために、mercury.js
設定ファイルの末尾に次のコードを追加します。
$(window).bind('mercury:ready', function() { var link = $('#mercury_iframe').contents().find('#edit_link'); Mercury.saveURL = link.data('save-url'); link.hide(); });
このコードはmercury:ready
イベントにbindし、そのイベントが起動したときに「Edit Page」のリンクを探して、MercuryのsaveURL
を、追加したdata-save-url
属性の値に設定します。Mercuryが現在のページの内容をiframeにロードするので、その内容を取得してそこからリンクを探します。このコードは、ページが編集モードに入るときに起動されるので、「Edit Page」リンクを隠す行もここに追加します。
ページに変更を加えて保存しようとすると、もうエラーメッセージは出ないので、Mercuryは変更をサーバに送信できたということです。developmentログを見ると、ページの保存時にmercury_update
アクションが起動され、 Page
モデルを更新するのに必要なすべての属性が含まれているJSON文字列を送信しているのがわかります。
Started POST "/pages/1/mercury_update" for 127.0.0.1 at 2011-11-10 18:31:59 +0000 Processing by PagesController#mercury_update as JSON Parameters: {"content"=>"{\"page_name\":{\"type\":\"editable\",\"value\":\"Welcome!!\",\"snippets\":{}},\"page_content\":{\"type\":\"editable\",\"value\":\"<p>In this ASCIIcasts episode we are going to look at the <a href=\\\"http://jejacks0n.github.com/mercury/\\\">Mercury Editor</a>. It allows you to edit a document in-place, right in the HTML. It works in the following browsers.</p>\\n<ul>\\n <li>Firefox 4+</li>\\n <li>Chrome 10+</li>\\n <li>Safari 5+</li>\\n</ul>\\n<p>Try it out here by clicking on the <strong><em>Edit Page</em></strong> link below. There you will be able to change this page content and even the title above.</p>\",\"snippets\":{}}}", "id"=>"1"}
Mercuryには、JSONを使用する代わりに、ネスト形式のパラメータとしてデータを保存するオプションがあります。このオプションを設定するには、Mercury generatorを実行したときに生成されたmercury.html.erb
ファイルを修正します。このファイルには、saveStyle
プロパティを持ったoptions
オブジェクトが含まれています。デフォルトではこのプロパティの値はnull
で、ページの更新がサーバに送り返されるときにはJSONが使われます。これを'form'
に変更します。
<script type="text/javascript"> var saveUrl = null; var options = { saveStyle: 'form', // 'form', or 'json' (default json) saveMethod: null, // 'POST', or 'PUT', (create, vs. update -- default POST) visible: null // if the interface should start visible or not (default true) }; new Mercury.PageEditor(saveUrl, options); </script>
ページへの変更を保存すると、JSONデータではなくネストされたパラメータとして送信され、このデータを使って変更をデータベースに保存することができます。ページの名称と内容は、content
パラメータの下にネストされそれぞれがvalue
プロパティを持っています。これらを用いてページの新しい内容をデータベースに戻して保存します。
def mercury_update page = Page.find(params[:id]) page.name = params[:content][:page_name][:value] page.content = params[:content][:page_content][:value] page.save! render text: "" end
ページを修正して変更を保存すると何も起きていないようですが、ページに戻ってみると変更点が保存されていることがわかります。
変更を保存したら元のページにリダイレクトしてほしいので、mercury:saved
イベントを待機してそれが起動されたらリダイレクトするようにします。
$(window).bind('mercury:saved', function() { window.location = window.location.href.replace(/\/editor\//i, '/'); });
これで変更を保存すると、元のページにリダイレクトされます。
さらにその先へ
Mercuryについてはここでカバーしきれなかったことがまだたくさんあります。mercury.js
ファイルのコメントを一通り読めば、何ができるかについて具体的に理解できるでしょう。例えば、ツールバーに何を表示するかを細かく指定したり、スニペット、履歴、ノートを有効化したりなど、その他にもたくさんあります。これらはすべてmercury.js
の中で説明されています。
Mercuryの紹介は以上です。すばらしいプロジェクトで、使っていてとても楽しいです。ただし自分のRailsプロジェクトに追加する場合は、Firefox、Chrome、Safariなどの最新版が必要になることを覚えておいてください。