Riot.js/riot_js-railsの手直し のバックアップの現在との差分(No.8)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

* 概要 [#ha9b50ed]

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

結果、どうにも手に負える物ではなかった感じです。。。
最終的に、rails 3.1 から 5.0.0.1 までのすべてで動きそうな状況までこぎ着けました。

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

この成果をまとめた 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'

とすれば使えます。

* 目次 [#vb58b525]

#contents

* 症状 [#ofe19cd3]

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 のバージョンを上げると [#a1c9dd34]

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 のバージョン依存・・・なのでしょうか?

* ソースを確認 [#o87bc502]

 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 のソースを見る [#h68f93cd]

特定の拡張子を持ったファイルを .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 のまねをする [#c2ace7e8]
** coffee-rails のまねをする [#c2ace7e8]

 $ 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 のバージョンを上げることを考えるべきなのかも・・・

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

* それっぽい記事を発見(未検証) [#l4374caa]
* それっぽい記事を発見 [#l4374caa]

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 のすべてで動くものを作れる、と。

 LANG:ruby
 class SprocketsExtensionBase
   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)
     # return the modified source
   end
 
   def self.call(input)
     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
さらに、ものすごく重要なことがさらっと書いてあった。

を継承して run を override すれば Extention を作れる。
>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.

 LANG:ruby
 require 'sprockets/processing'
 extend Sprockets::Processing
 
 register_preprocessor 'text/css', TheSprocketsExtension
Sprockets 4 ではさらに大幅な変更が来るみたいですね。

で拡張機能を登録して、

 LANG:ruby
 def register_sprockets_extention(env, sprockets_extention, mime_type, file_ext, charset=nil)
   file_ext = [file_ext] unless file_ext.instance_of?(Array)
   charset = charset.to_sym
  
   # Sprockets 2, 3, and 4
   
   if env.respond_to?(:register_transformer)
     env.register_mime_type mime_type, extensions: file_ext, charset: charset
     env.register_preprocessor mime_type, sprockets_extention
   end
   
   if env.respond_to?(:register_engine)
     args = [file_ext, sprockets_extention]
     args << { mime_type: mime_type, silence_deprecation: true } if Sprockets::VERSION.start_with?("3")
     env.register_engine(*args)
   end
 end

これで良さそう?

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

>prockets 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.

だから rails 5 で file.tag.haml が動かなかったのか?~
いや、rails 5.0.0.1 でも sprockets は 3.7 だから、そういう問題ではないか。

 LANG:ruby
 if Sprockets::VERSION.start_with?("4")
   register_sprockets_extention env, 'application/javascript', '.tag.haml', nil,
               Proc.new {|input| TagProcessor.call(HAMLProcessor.call(input)) }
 end

こんな感じかな?

** 現行のコードと比べてみる [#i62a3d88]

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

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

** やってみる [#la58b64f]

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

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

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

これがないとまったく始まらない(汗
上でにっちもさっちもいかなかったのはこれのせい(汗

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

 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)
         filename = input[:filename]
         source   = input[:data]
         context  = input[:environment].context_class.new(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)
           result = run(filename, source, context)
           context.metadata.merge(data: result)
         end
       end
 
       private
 
       def self.register_self_helper(config, file_ext, mime_type_from, mime_type_to, charset=nil)
         config.assets.configure do |env|
           # Sprockets 3 and 4
           if env.respond_to?(:register_transformer)
             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)
             # Sprockets 2 and 3
             if Sprockets::VERSION.start_with?("3")
               env.register_engine file_ext_from, self, { mime_type: mime_type, silence_deprecation: true }
             else
               env.register_engine file_ext_from, self, mime_type: mime_type
       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(env)
         register_self_helper(env, '.tag', 'text/riot-tag', 'application/javascript', :html)
       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
 
         Processor.register_self config
 
         if defined?(::Haml)
           require 'tilt/haml'
           Haml::Template.options[:format] = :html5
 
           config.assets.configure do |env|
             # Sprockets 3 and 4
             if env.respond_to?(:register_transformer)
               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::Rails::Processor.new(input).render)
               }
             elsif env.respond_to?(:register_engine)
               # Sprockets 2 and 3
               if Sprockets::VERSION.start_with?("3")
                 env.register_engine '.haml', ::Tilt::HamlTemplate, { silence_deprecation: true }
               else
                 env.register_engine '.haml', ::Tilt::HamlTemplate
           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

** テスト結果 [#sbab7551]

|ruby version|rails version|sprockets version|result|comment|
|1.9.3|3.0.20|--|NG|sprockets is not available|
|1.9.3|3.1.12|2.0.5|ok|extension must be .js.tag instead of .tag|
|1.9.3|3.2.11|2.2.3|ok|extension must be .js.tag instead of .tag|
|1.9.3|4.0.0|2.12.4|ok|extension must be .js.tag instead of .tag|
|1.9.3|4.1.16|2.12.4|ok|extension must be .js.tag instead of .tag|
|2.2.1|4.2.0|3.7.0|ok||
|2.3.1|5.0.0.1|3.7.0|ok||

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

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

うまくいっていれば http://localhost:3000/ をブラウザで見た時に
 <test> tag is working.
 <haml-test> tag is working. 

と表示される。

** テストスクリプト [#zc561111]

Gist に上げた。

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

** github に上げた [#x63554b5]

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

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

** プルリクエストを書いてみた [#i044a1a0]

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

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)

* さらに手直し [#afdb6fc3]

** テストを phantomjs で書いてみる [#g282f17e]

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 も扱えるようにする [#de8e3281]

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 の使い勝手 [#yfd84c69]

** 属性の引用符 [#v88d605f]

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 [#ee4334fe]

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

** sass 中で改行のエスケープ [#z39e690a]

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'

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

** 試してみる [#m3274067]

 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 名を書いて、他を全て & から始めれば同じことになりそう。

* 質問・コメント [#x0da6bd0]

#article_kcaptcha


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