Radiant Extension チュートリアル (カスタム Radius タグ編)

Written by @dr_taka_n at 2009/05/13 01:06 [, , , ]

Radiant の Extension を書いみる。 チュートリアルが存在するので、それにならって手を動かすことにする。

使用している Radiant のバージョンは、0.7.1 になる。

カスタム Radius Tags を作成する

Radiant には、コンテンツの編集に使用する Radius Tags と言われるパワフルなタグが存在する。 まずは、そのカスタムタグを作ってみよう!というチュートリアル。

ここで記載している内容は、以下のチュートリアルになる。

Radiant Tags の詳細については、以下のサイトを参照。

generator を使用して Extension の雛形を作成

Extension 用の generator が備わっているので、それを利用してまずは Extension の雛形を作成する。

$ cd radiant_project_home
$ ruby script/generate extension custom_tags
      create  vendor/extensions/custom_tags/app/controllers
      create  vendor/extensions/custom_tags/app/helpers
      create  vendor/extensions/custom_tags/app/models
      create  vendor/extensions/custom_tags/app/views
      create  vendor/extensions/custom_tags/db/migrate
      create  vendor/extensions/custom_tags/lib/tasks
      create  vendor/extensions/custom_tags/README
      create  vendor/extensions/custom_tags/custom_tags_extension.rb
      create  vendor/extensions/custom_tags/lib/tasks/custom_tags_extension_tasks.rake
      create  vendor/extensions/custom_tags/spec/controllers
      create  vendor/extensions/custom_tags/spec/models
      create  vendor/extensions/custom_tags/spec/views
      create  vendor/extensions/custom_tags/spec/helpers
      create  vendor/extensions/custom_tags/Rakefile
      create  vendor/extensions/custom_tags/spec/spec_helper.rb
      create  vendor/extensions/custom_tags/spec/spec.opts

generator によって雛形となるファイルが作成される。

まずは spec を書く

何はともあれ、まずは spec を書く。

  • vendor/extensions/custom_tags/spec/lib/cutom_tags_extension.rb

上記ファイルを作成し以下を記載。

require File.dirname(__FILE__) + '/../spec_helper'

describe 'CustomTag' do
  scenario :pages # need to replace with `dataset :pages'

  describe '<r:box>' do
    it 'should render the crrect HTML' do
      tag = '<r:box icon="happyface" title="Test Title">Content</r:box>'

      expected = %{<div class="box">
  <h2>
    <img src="/images/icons/happyface.png" />
    Test Title
  </h2>
  <div class="content">
    Content
  </div>
</div>}

      pages(:home).should render(tag).as(expected)
    end
  end
end

Radiant によって拡張されているクラス(デコレータ)メソッド、マッチャーなどが登場している。

  • scenario :pages

    Radiant によって定義されている RSpec scenario(Senarios Plugin)。 Page オブジェクトへの参照を提供してくれる。 Page オブジェクトは tag のレンダリングを行ってくれる。

    後述するが、現在このSenarios Pluginは使用されていない。 aiwilliams’s scenarios at master - GitHub にも書いてあるが、既に Deprecate されており、Dataset Plugin (aiwilliams’s dataset at master - GitHub)を使うようにアナウンスされている。 そのため、ここでの記載は、dataset :pagesとする必要がある。

  • pages(:home).should render(tag).as(expected)

    tag を page オブジェクトでレンダリングし、as(expected) でその結果を検証している。

spec を実行する。

もし、test 環境をまだ整えていない場合は、

$ cd radiant_project_home
$ rake db:test:prepare

を実行しておく。改めて spec を実行する。

$ cd vendor/extensions/custom_tags/
$ rake spec

実行時に、以下のエラーに遭遇する。

/opt/local/lib/ruby/gems/1.8/gems/radiant-0.7.1/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:276:in `load_missing_constant': uninitialized constant Spec::Rails::Example::RailsExampleGroup (NameError)

現状、Gem の状態は

  • radiant (0.7.1)
  • rspec (1.2.6, 1.1.12)
  • rspec-rails (1.2.6, 1.1.12)

となっている。

上記に同様の事象にひっかかっている方がいたが、

Radiant 0.7.1 requires RSpec 1.1.11 or 1.1.12, but the version is not “locked” properly in the gemspec. Edge runs properly on RSpec 1.2.x.

ということらしい。

gem では 1.1.12 も入っているのだが、1.2.6 の方を見てしまうので、うまく動作しない。

  • v.1.1.12 だけを残して、それ以外のバージョンの rspec、rspec-rails gem は削除
  • チュートリアルでscenario :pagesとなっているところをdataset :pagesと変更する

の2つの変更により動作するようになる。

しかし・・・ Radiant v.0.7.1 の RSpec のバージョンは、v.1.2.6 を必要要件としていなかったかな?? まぁ、今のところ Radiant 自体を触る余裕はまだないので、、とりあえず Extension のテストができる v.1.1.12 を使うことにする。

他の Rails アプリでもそうだったが、RSpec はまだまだ進化中のものであるため、バージョンの依存性が強い。 そのため、これまでは vender 配下に rspec と rspec-rails をインストールし使用する方法をとっていたのだが、Radiant の環境ではうまくやることができなかった。 きっと何らかのやり方があるのだろうが、残念ながら手が回っていない。。

と、気を取り直して再度 spec を実行。

$ rake spec
(in /Users/hoge/work/radiant_test/vendor/extensions/custom_tags)
/opt/local/lib/ruby/gems/1.8/gems/radiant-0.7.1/vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:493:in `const_missing': uninitialized constant CustomTagsExtension::CustomTags (NameError)
.....

OK。まだ CustomTags なんてものは書いていないので、この NameError のエラーで正しい。

Radius Tag を定義する

タグを記載する CustomTags module を用意する。

  • vendor/extensions/custom_tags/lib/custom_tags.rb

を作成し、Custom Radius Tag を定義する。

module CustomTags
  include Radiant::Taggable

  desc %{
    タイトル、アイコン、本文を持った HTML box コンテンツを作成する。

    *使い方:*
    <pre><code><r:box title="title" icon="icon">...</r:box></code></pre>
  }
  tag "box" do |tag|
    ""
  end
end

単純に空文字を返すだけの box Radius Tag の完成だ。。

  • Radiant::Taggable を継承する
  • tag メソッドで今回作成する Radius Tag を定義する
  • desc メソッドでは、rake task のように、その tag の説明を記載する

テストする。

$ rake spec
(in /Users/hoge/work/radiant_test/vendor/extensions/custom_tags)
F

1)
'CustomTag <r:box> should render the correct HTML' FAILED
expected "<r:box icon=\"happyface\" title=\"Test Title\">Content</r:box>" to render as "<div class=\"box\">\n  <h2>\n    <img src=\"/images/icons/happyface.png\" />\n    Test Title\n  </h2>\n  <div class=\"content\">\n    Content\n  </div>\n</div>", but got ""
./spec/lib/custom_tags_spec.rb:20:

Finished in 0.708661 seconds

1 example, 1 failure

また失敗するが、これで OK。 期待する結果に対して、but got “”。 それはそうだ。box Radius Tag の実装は、まだ空文字しかまだ返さないので。。

Radius Tag を使用可能とする

上記までで Radius Tag は定義できたが、Radiant はまだその存在を認知していない。 定義した Radius Tag を Radiant に認知させる。

  • vendor/extensions/custom_tags/custom_tags_extension.rb

が初期化処理を記載するファイルになるので、ここに記載する。

activate メソッドの中に以下を記載する。

Page.send :include, CustomTags

Page クラスに先程作成した Radius Tag を定義した CustomTags module を include させる。 これで Radiant もboxタグを Radius Tag として認知したことになる。<r:box> タグが使用可能となっている。

tag メソッドのブロックの記載方法

先の tag の定義は暫定的に空文字のみを返すようにしていたので、ちゃんとしたものを返すようにする。

  tag "box" do |tag|
%{<div class="box">
  <h2>
    <img src="/images/icons/#{tag.attr['icon']}.png" />
    #{tag.attr['title']}
  </h2>
  <div class="content">
    #{tag.expand}
  </div>
</div>}
  end

yield されているブロック引数の tag オブジェクトは、レンダリングされるタグになる。 また、今作成している <r:box> を使用するコンテンツの作成者によって定義されるその全てのコンテンツと属性を保持しているものになる。

2つの役に立つメソッドをもっている。

  • tag.attr['attribute_name']

    タグに与えられる属性の値に対して hash によるアクセスを提供する

  • tag.expand

    開始タグと終了タグの間のコンテンツを描画する。

    ここでのケースでは文字列ではあるが、Radiant は存在する他の Radius Tag のレンダリングも行える。

きちんと tag メソッドを実装したので、テストが通るようになっているはずだ。spec を再度実行する。

$ rake spec
(in /Users/taka/work/radiant_test/vendor/extensions/custom_tags)
.

Finished in 0.703412 seconds

1 example, 0 failures

OK。これで一応の完成となる。

追加したカスタム Radius Tags を確認する

サーバを再起動して本当に使えるようになっているか確認してみる。

Page を追加し、Edit Page でまずはAvailable Tagsを確認してみる。

Available Tags

desc に書いた内容がちゃんと追加されている。

使用可能になった <r:box> タグを実際に書いてみる。

Page Body

保存して、画面で表示を確認する。

カスタムタグの表示確認

スタイルシートをちゃんと定義していないので、ハズカシイ表示にはなっているが、ちゃんと box Radius タグが利用できた。

<div class="box">
  <h2>
    <img src="/images/icons/movie_thumbnail.png" />
    This is the custom tag!
  </h2>

  <div class="content">

This is the custom tag body.

  </div>
</div>

HTML も問題ない。

オプショナル属性

アイコンやタイトルをいつも指定しないかもしれない。 アイコンはいつも使用するデフォルトのものがあり、タイトルはブランクとすることができるようにしたい。 この要件も簡単に追加できる。

お約束でまずは spec から。

code example を追加する。

    it 'should have a default icon and allow a blank title' do
      tag = '<r:box>Content</r:box>'

      expected = %{<div class="box">
  <h2>
    <img src="/images/icons/sadface.png" />
    
  </h2>
  <div class="content">
    Content
  </div>
</div>}

      pages(:home).should render(tag).as(expected)
    end

テストする。

$ rake spec
(in /Users/taka/work/radiant_test/vendor/extensions/custom_tags)
F.

1)
'CustomTag <r:box> should have a default icon and allow a blank title' FAILED
expected "<r:box>Content</r:box>" to render as "<div class=\"box\">\n  <h2>\n    <img src=\"/images/icons/sadface.png\" />\n    \n  </h2>\n  <div class=\"content\">\n    Content\n  </div>\n</div>", but got "<div class=\"box\">\n  <h2>\n    <img src=\"/images/icons/.png\" />\n    \n  </h2>\n  <div class=\"content\">\n    Content\n  </div>\n</div>"
./spec/lib/custom_tags_spec.rb:36:

Finished in 0.714863 seconds

2 examples, 1 failure

ちゃんと失敗する。

"<div class=\"box\">\n  <h2>\n    <img src=\"/images/icons/sadface.png\" />\n    \n  </h2>\n  <div class=\"content\">\n    Content\n  </div>\n</div>"

とレンダリングされるのを期待したのだが、

"<div class=\"box\">\n  <h2>\n    <img src=\"/images/icons/.png\" />\n    \n  </h2>\n  <div class=\"content\">\n    Content\n  </div>\n</div>"

がレンダリングされているよ!という内容の失敗だ。確かにそれは RSpec のおっしゃる通りだ。

  • vendor/extensions/custom_tags/lib/custom_tags.rb

に変更を入れる。

    <img src="/images/icons/#{tag.attr['icon']}.png" />

となっている箇所を

    <img src="/images/icons/#{tag.attr['icon'] || 'sadface'}.png" />

とする。|| オペレータで icon の属性が指定されなかった場合に、’sadface’ を返すように変更している。 再度実行。

$ rake spec
(in /Users/taka/work/radiant_test/vendor/extensions/custom_tags)
..

Finished in 0.738969 seconds

2 examples, 0 failures

OK。

参考サイト

blog comments powered by Disqus