Riot.js/riot_js-railsの手直し

(485d) 更新

公開メモ

概要

rails から riot.js を使うための gem として、 riot_js-rails というのがあるのですが、 環境によってどうもうまく動かないように思えたため、手直しできないか調べてみました。

最終的に、rails 3.1 から 5.0.0.1 までのすべてで動きそうな状況までこぎ着けました。

それっぽい記事を発見」に原因が、「やってみる」 以下に回答らしきものがあります。

この成果をまとめた gem が、https://github.com/osamutake/riot_js-rails こちらにあります。
現在、この稿の内容を riot_js-rails の本家へプルリクエストの形で投げ、回答待ちの状況です。

本家に取り込まれるまでの間は Gemfile 中で、

LANG:ruby
gem 'riot_js-rails', :git => 'git://github.com/osamutake/riot_js-rails',
                     :branch => 'support_sprockets_2_3_and_4'

とすれば使えます。

目次

症状

rails 4.0.13 ではうまく動きませんでした。

LANG:console
$ ruby --version
 ruby 1.9.3p551 (2014-11-13 revision 48407) [x86_64-linux]
$ rails new riot_js-rails_sample -B
$ cd riot_js-rails_sample/
$ jed Gemfile
 gem 'riot_js-rails'
$ bundle install
$ cat Gemfile.lock
 ...
 DEPENDENCIES
   coffee-rails (~> 4.0.0)
   jbuilder (~> 1.2)
   jquery-rails
   rails (= 4.0.13)
   riot_js-rails
   sass-rails (~> 4.0.2)
   sdoc
   sqlite3
   turbolinks
   uglifier (>= 1.3.0)

 BUNDLED WITH
    1.11.2
$ rails generate controller main index
       create  app/controllers/main_controller.rb
        route  get "main/index"
       invoke  erb
       create    app/views/main
       create    app/views/main/index.html.erb
       invoke  test_unit
       create    test/controllers/main_controller_test.rb
       invoke  helper
       create    app/helpers/main_helper.rb
       invoke    test_unit
       create      test/helpers/main_helper_test.rb
       invoke  assets
       invoke    coffee
       create      app/assets/javascripts/main.js.coffee
       invoke    scss
       create      app/assets/stylesheets/main.css.scss
$ jed config/routes.rb
    root 'main#index'
$ rails s &
$ wget -q -O - http://localhost:3000/
 ...
 <h1>Main#index</h1>
 <p>Find me in app/views/main/index.html.erb</p>
 ...
$ cat > app/assets/javascripts/test-tag.tag
 <test-tag>
   Ok!
 </test-tag>
$ jed app/assets/javascripts/application.js
+  //= require riot
+  //= require riot-rails
   //= require_tree .
$ wget -q -O - http://localhost:3000/
 <script data-turbolinks-track="true" src="/assets/riot.js?body=1"></script>
 <script data-turbolinks-track="true" src="/assets/riot_rails.js?body=1"></script>
 <script data-turbolinks-track="true" src="/assets/main.js?body=1"></script>
 <script data-turbolinks-track="true" src="/assets/application.js?body=1"></script>

require_tree で test-tag.tag が読み込まれていません。

LANG:console
$ mv app/assets/javascripts/test-tag.tag app/assets/javascripts/test-tag.js.tag
$ wget -q -O - http://localhost:3000/
 <script data-turbolinks-track="true" src="/assets/riot.js?body=1"></script>
 <script data-turbolinks-track="true" src="/assets/riot_rails.js?body=1"></script>
 <script data-turbolinks-track="true" src="/assets/main.js?body=1"></script>
 <script data-turbolinks-track="true" src="/javascripts/test-tag.js.tag.js"></script>
 <script data-turbolinks-track="true" src="/assets/application.js?body=1"></script>
$ wget -q -O - http://localhost:3000/javascripts/test-tag.js.tag.js
 Started GET "/javascripts/test-tag.js.tag.js" for 127.0.0.1 at 2016-07-01 13:55:32 +0900
 ActionController::RoutingError (No route matches [GET] "/javascripts/test-tag.js.tag.js"):
$ wget -q -O - http://localhost:3000/assets/test-tag.js.tag.js?body=1
 Started GET "/assets/test-tag.js.tag.js?body=1" for 127.0.0.1 at 2016-07-01 13:56:24 +0900
 ActionController::RoutingError (No route matches [GET] "/assets/test-tag.js.tag.js"):

test-tag.tag ではなく test-tag.js.tag としたところ、読み込もうとする姿勢は見られましたが、 /assets/ ではなく /javascripts/ が指定されており、なおかつ両方とも RoutingError になってしまいます。

Rails のバージョンを上げると

rails 4.2.6 では動きました。

LANG:console
$ rvm use ruby-2.2.1
$ ruby --version
 ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux]
$ rails --version
 Rails 4.2.6
$ rails new riot_js-rails_sample -B
$ cd riot_js-rails_sample/
$ jed Gemfile
 gem 'riot_js-rails'
$ bundle install
$ cat Gemfile.lock
 DEPENDENCIES
   byebug
   coffee-rails (~> 4.1.0)
   jbuilder (~> 2.0)
   jquery-rails
   rails (= 4.2.6)
   riot_js-rails
   sass-rails (~> 5.0)
   sdoc (~> 0.4.0)
   spring
   sqlite3
   turbolinks
   uglifier (>= 1.3.0)
   web-console (~> 2.0)
$ echo "Sprockets::VERSION" | rails c
 Loading development environment (Rails 4.2.6)
 Switch to inspect mode.
 Sprockets::VERSION
 "3.6.3"

この環境だと、上と同じ操作で

 <script src="/assets/riot.self-d4789eae993b10e05f4694693fa33efe6afd5ccd866d3f6eee21de793efc7f31.js?body=1" data-turbolinks-track="true"></script>
 <script src="/assets/riot_rails.self-23d22569edc9e83fe5a74dbf647822c59a2fcd08777f496b38cab2507f2d594d.js?body=1" data-turbolinks-track="true"></script>
 <script src="/assets/main.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js?body=1" data-turbolinks-track="true"></script>
 <script src="/assets/test-tag.self-198db509a34ddfb1d978f239761117f57e5680e43ffe43f5524092ecb41ca2b3.js?body=1" data-turbolinks-track="true"></script>
 <script src="/assets/application.self-7862a8a8b42407b4741a1adeeea35f0d13ddc4f702ec532adb0674491d296495.js?body=1" data-turbolinks-track="true"></script>

のように、"test-tag.self-XXXX.js" が正しくコンパイルされます。

どうやら sprockets のバージョン依存・・・なのでしょうか?

ソースを確認

LANG:console
$ cd ..
$ git clone https://github.com/bjarosze/riot_js-rails.git
 Cloning into 'riot_js-rails'...
 remote: Counting objects: 180, done.
 remote: Total 180 (delta 0), reused 0 (delta 0), pack-reused 180
 Receiving objects: 100% (180/180), 79.78 KiB | 90.00 KiB/s, done.
 Resolving deltas: 100% (61/61), done.
 Checking connectivity... done.
$ cd riot_js-rails
$ ls -a
 .  ..  .git  .gitignore  Gemfile  README.md  Rakefile  lib  riot_js-rails.gemspec  test  vendor
$ git branch -m fix-asset-path
$ git status
 On branch fix-asset-path
 Your branch is up-to-date with 'origin/master'.
 nothing to commit, working directory clean
$ cat lib/riot_js/rails/railtie.rb
 require 'rails/railtie'
 require 'riot_js/rails/processors/processor'
 require 'riot_js/rails/helper'
 
 module RiotJs
   module Rails
     class Railtie < ::Rails::Railtie
 
       initializer :setup_sprockets do |app|
         Processor.register_self config
         ...
$ cat lib/riot_js/rails/processors/processor.rb
 require 'riot_js/rails/processors/compiler'
 
 if Gem::Version.new(Sprockets::VERSION) < Gem::Version.new('3.0.0')
   require 'riot_js/rails/processors/sprockets_processor_v2'
 else
   require 'riot_js/rails/processors/sprockets_processor_v3'
 end
 
 module RiotJs
   module Rails
     class Processor < SprocketsProcessor
 
       def process
         compile_tag
       end
 
       private
 
       def compile_tag
         ::RiotJs::Rails::Compiler.compile(@data)
       end
     end
   end
 end
$ echo "Sprockets::VERSION" | rails c
 Loading development environment (Rails 4.0.13)
 Switch to inspect mode.
 Sprockets::VERSION
 "2.12.4"
$ cat lib/riot_js/rails/processors/sprockets_rocessor_v2.rb
 module RiotJs
   module Rails
     class SprocketsProcessor < Tilt::Template
 
       self.default_mime_type = 'application/javascript'
 
       def self.register_self(app)
         app.assets.register_engine '.tag', self
       end
 
       def evaluate(context, locals, &block)
         @context = context
         process
       end
 
       def prepare
         @data = data
       end
 
       def process
         raise 'Not implemented'
       end
 
     end
   end
 end

これを直すのは難しそうです。。。

coffee-rails のソースを見る

特定の拡張子を持ったファイルを .js に変換するという動作は coffee-rails と同様なので、

https://github.com/rails/coffee-rails/

これと同じことをやれば良いはずなのですが、、、

.coffee から .js へのコンパイル部分は

https://github.com/rails/coffee-rails/blob/master/lib/coffee/rails/template_handler.rb

LANG:ruby
module Coffee
  module Rails
    class TemplateHandler
      def self.erb_handler
        @@erb_handler ||= ActionView::Template.registered_template_handler(:erb)
      end

      def self.call(template)
        compiled_source = erb_handler.call(template)
        "CoffeeScript.compile(begin;#{compiled_source};end)"
      end
    end
  end
end

ActiveSupport.on_load(:action_view) do
  ActionView::Template.register_template_handler :coffee, Coffee::Rails::TemplateHandler
end

だけだと思われて、なおかつこの部分は上記前者で読まれた v4.0.1 でも、 後者で読まれた v4.1.1 でも変更されていないにも関わらず、 同じことをしてみても、どうもうまくいかず、困ってしまいます。

coffee-rails のまねをする

$ git clone https://github.com/rails/coffee-rails.git
$ mv coffee-rails/ riottag-rails
$ (いろいろ直す)

主には、

LANG:ruby
module RiotTag
  module Rails
    class TemplateHandler
      def self.erb_handler
        @@erb_handler ||= ActionView::Template.registered_template_handler(:erb)
      end

      def self.call(template)
        compiled_source = erb_handler.call(template)
        "'Compiled!'.html_safe"
      end
    end
  end
end

ActiveSupport.on_load(:action_view) do
  ActionView::Template.register_template_handler :tag, RiotTag::Rails::TemplateHandler
end

全然うまくいきません(涙

というか、coffee-rails の該当部分 ActionView::Template.register_template_handler をコメントアウトしても coffee-rails が動き続けているあたり、見ている場所が全然違うのかもしれません?!

素直に rails のバージョンを上げることを考えるべきなのかも・・・

それにしても黒魔術すぎて、キビシイなあ

それっぽい記事を発見

Sprockets のバージョン間の非互換性が原因ということで調べたところ以下の記事を発見しました。

Supporting All Versions of Sprockets in Processors
https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md#supporting-all-versions-of-sprockets-in-processors

この通りの手順を踏めば、Sprockets 2, 3 & 4 のすべてで動くものを作れる、と。

さらに、ものすごく重要なことがさらっと書いてあった。

Sprockets 4 will not chain asset extensions so .coffee.erb is explicitly registered in addition to .coffee. If your application introduces a new mime/extension combo it will be responsible for registering all combinations.

Sprockets 4 ではさらに大幅な変更が来るみたいですね。

現行のコードと比べてみる

riot_js-rails/lib/riot_js/rails/processors/sprockets_processor_v2.rb

LANG:ruby
module RiotJs
  module Rails
    class SprocketsProcessor < Tilt::Template

      self.default_mime_type = 'application/javascript'

      def self.register_self(app)
        app.assets.register_engine '.tag', self
      end

      def evaluate(context, locals, &block)
        @context = context
        process
      end
      
      def prepare
        @data = data
      end

      def process
        raise 'Not implemented'
      end

    end
  end
end


riot_js-rails/lib/riot_js/rails/processors/sprockets_processor_v3.rb

LANG:ruby
module RiotJs
  module Rails
    class SprocketsProcessor
      def self.instance
        @instance ||= new
      end

      def self.call(input)
        instance.call(input)
      end

      def call(input)
        prepare(input)
        data = process

        @context.metadata.merge(data: data)
      end

      def self.register_self(config)
        config.assets.configure do |env|
          env.register_engine '.tag', self, mime_type: 'application/javascript'
        end
      end
 
     private

     def process
        raise 'Not implemented'
      end

      def prepare(input)
        @context = input[:environment].context_class.new(input)
        @data = input[:data]
      end

    end
  end
end


riot_js-rails/lib/riot_js/rails/processors/processor.rb

LANG:ruby
require 'riot_js/rails/processors/compiler'

if Gem::Version.new(Sprockets::VERSION) < Gem::Version.new('3.0.0')
  require 'riot_js/rails/processors/sprockets_processor_v2'
else
  require 'riot_js/rails/processors/sprockets_processor_v3'
end

module RiotJs
  module Rails
    class Processor < SprocketsProcessor

      def process
        compile_tag
      end

      private

      def compile_tag
        ::RiotJs::Rails::Compiler.compile(@data)
      end
    end
  end
end

いろいろ面倒な手順は踏んでいるけれど、やろうとしていることは同じように見える?

やってみる

まず前提として、Sprocket 3 以降では development 環境でも assets のキャッシュのクリアはひと手間必要で、ちゃんとクリアしないと古い内容が読み込まれてしまうのが問題の特定を難しくしていたっぽい。

LANG:console
$ rake tmp:cache:clear
$ rake assets:clean

でクリアできるとのこと。

上でにっちもさっちもいかなかったのはこれのせい(汗

で、下記コードはかなり良い線行っている気がします。

LANG:ruby
require 'riot_js/rails/processors/compiler'

module RiotJs
  module Rails

    # Sprockets 2, 3 & 4 interface
    class SprocketsExtensionBase
      attr_reader :default_mime_type

      def initialize(filename, &block)
        @filename = filename
        @source   = block.call
      end

      def render(context, empty_hash_wtf)
        self.class.run(@filename, @source, context)
      end

      def self.run(filename, source, context)
        raise 'Not implemented'
      end

      def self.call(input)
        if input.is_a?(String)
          run("", input, nil)
        else
          filename = input[:filename]
          source   = input[:data]
          context  = input[:environment].context_class.new(input)

          result = run(filename, source, context)
          context.metadata.merge(data: result)
        end
      end

      private

      def self.register_self_helper(app, config, file_ext, mime_type_from, mime_type_to, charset=nil)

        if config.respond_to?(:assets)
          config.assets.configure do |env|
            if env.respond_to?(:register_transformer)
              # Sprockets 3 and 4
              env.register_mime_type mime_type_from, extensions: [file_ext], charset: charset
              env.register_transformer mime_type_from, mime_type_to, self
            elsif env.respond_to?(:register_engine)
              if Sprockets::VERSION.start_with?("3")
                # Sprockets 3 ... is this needed?
                env.register_engine file_ext, self, { mime_type: mime_type_to, silence_deprecation: true }
              else
                # Sprockets 2.12.4
                @default_mime_type = mime_type_to
                env.register_engine file_ext, self
              end
            end
          end
        else
          # Sprockets 2.2.3
          @default_mime_type = mime_type_to
          app.assets.register_engine file_ext, self
        end
      end

    end

    class Processor < SprocketsExtensionBase

      def self.run(filename, source, context)
        ::RiotJs::Rails::Compiler.compile(source)
      end

      def self.register_self(app, config)
        # app is a YourApp::Application
        # config is Rails::Railtie::Configuration that belongs to RiotJs::Rails::Railtie
        register_self_helper(app, config, '.tag', 'text/riot-tag', 'application/javascript', :html)
      end

    end
  end
end


LANG:ruby
require 'rails/railtie'
require 'riot_js/rails/processors/processor'
require 'riot_js/rails/helper'

module RiotJs
  module Rails
    class Railtie < ::Rails::Railtie
      config.riot = ActiveSupport::OrderedOptions.new
      config.riot.node_paths = []

      initializer :setup_sprockets do |app|
        # app is a YourApp::Application
        # config is Rails::Railtie::Configuration that belongs to RiotJs::Rails::Railtie
        Processor.register_self app, config

        if defined?(::Haml)
          require 'tilt/haml'
          Haml::Template.options[:format] = :html5

          if config.respond_to?(:assets)
            config.assets.configure do |env|
              if env.respond_to?(:register_transformer)
                # Sprockets 3 and 4
                env.register_mime_type 'text/riot-tag+haml', extensions: ['.tag.haml'], charset: :html
                env.register_transformer 'text/riot-tag+haml', 'application/javascript',
                        Proc.new{ |input| Processor.call(::Haml::Engine.new(input[:data]).render) }
              elsif env.respond_to?(:register_engine)
                if Sprockets::VERSION.start_with?("3")
                  # Sprockets 3 ... is this needed?
                  env.register_engine '.haml', ::Tilt::HamlTemplate, { silence_deprecation: true }
                else
                  # Sprockets 2.12.4
                  env.register_engine '.haml', ::Tilt::HamlTemplate
                end
              end
            end
          else
            # Sprockets 2
            app.assets.register_engine '.haml', ::Tilt::HamlTemplate
          end
        end
      end

      initializer :add_helpers do |app|
        helpers = %q{ include ::RiotJs::Rails::Helper }
        ::ActionView::Base.module_eval(helpers)
        ::Rails.application.config.assets.context_class.class_eval(helpers)
      end

      config.after_initialize do |app|
        node_paths = ENV['NODE_PATH'].to_s.split(':')
        node_paths += app.config.riot.node_paths
        node_global_path = detect_node_global_path
        node_paths << node_global_path if node_global_path

        ENV['NODE_PATH'] = node_paths.join(':')
      end

      def detect_node_global_path
        prefix = `npm config get prefix`.to_s.chomp("\n")
        possible_paths = [ "#{prefix}/lib/node", "#{prefix}/lib/node_modules" ]

        possible_paths.each do |path|
          return path if File.directory?(path)
        end
        return
      end

    end
  end
end

テスト結果

ruby versionrails versionsprockets versionresultcomment
1.9.33.0.20--NGsprockets is not available
1.9.33.1.122.0.5okextension must be .js.tag instead of .tag
1.9.33.2.112.2.3okextension must be .js.tag instead of .tag
1.9.34.0.02.12.4okextension must be .js.tag instead of .tag
1.9.34.1.162.12.4okextension must be .js.tag instead of .tag
2.2.14.2.03.7.0ok
2.3.15.0.0.13.7.0ok

テストは下記のスクリプトで行った。

通常の .tag ファイルが正しく読み込めるかどうかと、 haml を使った .tag.haml ファイルが正しく読み込めるかどうかをテストしている。

うまくいっていれば http://localhost:3000/ をブラウザで見た時に

<test> tag is working.
<haml-test> tag is working. 

と表示される。

テストスクリプト

Gist に上げた。

https://gist.github.com/osamutake/0ccbdf96a5ac70a36ebce38db0a33137

github に上げた

上記コードを組み入れた riot_js-rails を github に上げました。

https://github.com/osamutake/riot_js-rails

プルリクエストを書いてみた

作法をよく知らないので、失礼がないと良いのだけれど。

https://github.com/bjarosze/riot_js-rails/pull/18

本家に取り込まれるまでの間は Gemfile 中で、

LANG:ruby
gem 'riot_js-rails', :git => 'git://github.com/osamutake/riot_js-rails',
                     :branch => 'support_sprockets_2_3_and_4'

とすれば上記コードを使えます。

→ 本家に取り込まれました (2017/01/27)

さらに手直し

テストを phantomjs で書いてみる

phantomjs の導入は、バイナリをとってくればすぐできる。

http://phantomjs.org/download.html を見て、

LANG:console
$ wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
$ cd /usr/local/src
$ sudo tar fxj ~/phantomjs-2.1.1-linux-x86_64.tar.bz2
$ sudo ln -s /usr/local/src/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
$ phantomjs -v
2.1.1

あ、npm でも良いみたいですね。こっちの方が簡単だ。

$ npm install phantomjs-prebuilt
$ ~/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs --version
2.1.1

あとは、

test.js

LANG:js
 (function(){

   var page = require("webpage").create();
   page.viewportSize = {width: 1024, height: 768};

   page.onConsoleMessage = function(msg) {
     console.log(msg);
   };

   page.open("http://localhost:3000/", function(status) {
     if (status === "success") {
       var error = null;
       page.evaluate(function() {
         var expected = "<test> tag is working.\n" +
                        "<haml-test> tag is working.\n" +
                        "<slim-test> tag is working.";
         var result = document.body.innerText.trim();
 	      if(result != expected){
 	        error = "Failed!\nresult:\n" + result + "\n";
 	      }
       });
       if(error) {
         console.log(error);
         phantom.exit(1);
       }else {
         console.log("Success!");
         phantom.exit(0);
       }
     } else {
       phantom.exit(1);
     }
   });

 })();

などというスクリプトを用意して、

$ phantomjs test.js

で実行できる。

slim も扱えるようにする

haml もいいのだけれど、haml + coffee + sass だと、haml と sass の相性が悪いことに気づきました。

slim はタグの前の % がいらないので、sass との相性がよさそう。

class Processer に

LANG:ruby
     def self.register_nested(app, config, type, charset, tilt_template)
       extention = '.' + type
       if config.respond_to?(:assets)
         config.assets.configure do |env|
           if env.respond_to?(:register_transformer)
             # Sprockets 3 and 4
             env.register_mime_type 'text/riot-tag+'+type, extensions: ['.tag'+extention], charset: charset
             env.register_transformer 'text/riot-tag+'+type, 'application/javascript',
               Proc.new{ |input| Processor.call(tilt_template.new{input[:data]}.render) }
           elsif env.respond_to?(:register_engine)
             if Sprockets::VERSION.start_with?("3")
               # Sprockets 3 ... is this needed?
               env.register_engine extention, tilt_template, { silence_deprecation: true }
             else
               # Sprockets 2.12.4
               env.register_engine extention, tilt_template
             end
           end
         end
       else
         # Sprockets 2
         app.assets.register_engine extention, tilt_template
       end
     end

を用意して、

LANG:ruby
       if defined?(::Haml)
         require 'tilt/haml'
         Haml::Template.options[:format] = :html5
         Processor.register_nested(app, config, 'haml', :html, ::Tilt::HamlTemplate)
       end

       if defined?(::Slim)
         Processor.register_nested(app, config, 'slim', :html, ::Slim::Template)
       end
     end

のようにして登録したところ、slim も使えるようになった。

rails 4 では(?) slim-rails を導入していれば何もしなくても slim がつかえるようだったが、 上記コードにより slim-rails を Gemfile に含める必要はなく、slim のみでよくなる。

slim + coffeescript + sass + riot の使い勝手

属性の引用符

riot で属性を javascript で与える場合、引用符を省略して次のように書けますが、

LANG: html
<li each={ item, i in items }>{ item }</li>

slim で

LANG: slim
li each={ item, i in items }
  | { item }

と書くと属性値の { } の中を ruby で解釈しようとしてエラーになってしまいます。

属性値は必ず引用符で囲まなければなりません。

LANG: slim
li each="{ item, i in items }"
  | { item }

style scoped

もしかして、、、これ実現不可能???

sass 中で改行のエスケープ

https://github.com/sass/sass/commit/3d27be5d0c9cff02e48398d29997f0a99d558ef1

これで良さそうなんだけれど、

gem 'sass', :git => 'git://github.com/sass/sass.git', :ref =>  '3d27be5d0c9cff02e48398d29997f0a99d558ef1'

とすると、

Fetching git://github.com/sass/sass.git
fatal: Could not parse object '3d27be5d0c9cff02e48398d29997f0a99d558ef1'.
Git error: command `git reset --hard 3d27be5d0c9cff02e48398d29997f0a99d558ef1` in directory
/home/osamu/.rvm/gems/ruby-2.3.1@album/bundler/gems/sass-3d27be5d0c9c has failed.
If this error persists you could try removing the cache directory
'/home/osamu/.rvm/gems/ruby-2.3.1@album/cache/bundler/git/sass-d1bc97264a602a16cbc8b7962b139cf458860561'

といって怒られてしまいます???

試してみる

bootstrap-carousel

  div[ id="carousel-example-generic" class="carousel slide" data-ride="carousel" 
       data-interval="{ this.interval }" data-pause="{ this.pause }"
       data-wrap="{ this.wrap }" data-keyboard="{ this.keyboard }" ]

    / Indicators
    ol class="carousel-indicators"
      li[ each="{ this.items }" data-target="#carousel-example-generic" 
          data-slide-to="{ index }" class="{ active: index==0 }" ]

    / Wrapper for slides
    div class="carousel-inner"
      div each="{ this.items }" class="{ item: true, active: index==0 }"
        img src="{ src }" alt="{ caption }"
        div class="carousel-caption"
          h2
            | { caption }

    / Controls
    a class="left carousel-control" href="#carousel-example-generic" data-slide="prev"
      span class="fa fa-angle-left"
    a class="right carousel-control" href="#carousel-example-generic" data-slide="next"
      span class="fa fa-angle-right"
  
  sass:
    .carousel-inner > .item
      text-align: center

    .carousel-inner > .item > img 
      display: inline

    .carousel-inner .carousel-caption h2 
      color:       rgba(0,0,0,0.6)
      font-size:   40px
      text-shadow: 0px 0px 5px rgba(255, 255, 255, 0.6), 0px 0px 10px rgba(255, 255, 255, 0.6)

    .carousel-indicators li
      background-color: rgba(0,0,0,0.4)
      box-shadow: 0 0 5px rgba(0,0,0,0.6)
      border-color: rgba(255,255,255,0.8)
      margin-left: 6px
      margin-right: 6px

    .carousel-indicators li.active 
      border-color: rgba(255,255,255,0.8)
      background-color: rgba(255,255,255,0.8)
      margin-left: 6px
      margin-right: 6px

    img 
      opacity: 0.8

  coffee:
    @on 'update', ->
      @interval = opts.interval ? 5000
      @pause    = opts.pause    ? "hover"
      @wrap     = opts.wrap     ? true
      @keyboard = opts.keyboard ? true

      @items = for img, i in $(@root._innerHTML) when img.tagName == 'IMG'
        caption: img.alt
        src:     img.src
        index:   i

こんな感じで、かなり良いのだけれど・・・style タグに scoped を付けられないのが痛すぎる???

そうでもないか。sass の先頭に tag 名を書いて、他を全て & から始めれば同じことになりそう。

質問・コメント





Counter: 2153 (from 2010/06/03), today: 1, yesterday: 2