opal-sprockets 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # opal-sprocets
2
+
3
+ Adds sprockets support for Opal
4
+
5
+ ## License
6
+
7
+ (The MIT License)
8
+
9
+ Copyright (C) 2013 by Adam Beynon
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in
19
+ all copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
+ THE SOFTWARE.
28
+
@@ -0,0 +1,23 @@
1
+ require 'sprockets'
2
+
3
+ module Opal
4
+
5
+ # Environment is a subclass of Sprockets::Environment which already has our opal
6
+ # load paths loaded. This makes it easy for stand-alone rack apps, or test runners
7
+ # that have opal load paths ready to use. You can also add an existing gem's lib
8
+ # directory to our load path to use real gems inside your opal environment.
9
+ #
10
+ # If you are running rails, then you just need opal-rails instead, which will
11
+ # do this for you.
12
+ class Environment < ::Sprockets::Environment
13
+ def initialize *args
14
+ super
15
+ Opal.paths.each { |p| append_path p }
16
+ end
17
+
18
+ def use_gem gem_name
19
+ append_path File.join(Gem::Specification.find_by_name(gem_name).gem_dir, 'lib')
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,84 @@
1
+ require 'opal/parser'
2
+
3
+ module Opal
4
+
5
+ # A subclass of Opal::Parser used exclusively by opal-sprockets to handle
6
+ # require statements automatically. We basically intercept any top level
7
+ # require statement, remove it from the code generation, and then pass it
8
+ # off to our sprockets context to handle it. This lets us use the ruby
9
+ # require syntax instead of having to use sprockets style comments.
10
+ class SprocketsParser < Parser
11
+
12
+ def self.parse source, options = {}
13
+ self.new.parse source, options
14
+ end
15
+
16
+ # Holds an array of paths which this file 'requires'.
17
+ # @return Array<String>
18
+ attr_reader :requires
19
+
20
+ def parse source, options = {}
21
+ @requires = []
22
+ @dynamic_require_severity = (options[:dynamic_require_severity] || :error)
23
+ super source, options
24
+ end
25
+
26
+ def process_call sexp, level
27
+ if sexp[1] == :require
28
+ return handle_require sexp[2][1]
29
+ end
30
+
31
+ super sexp, level
32
+ end
33
+
34
+ def handle_require(sexp)
35
+ str = handle_require_sexp sexp
36
+ @requires << str unless str.nil? if @requires
37
+ fragment("", sexp)
38
+ end
39
+
40
+ def handle_require_sexp(sexp)
41
+ type = sexp.shift
42
+
43
+ if type == :str
44
+ return sexp[0]
45
+ elsif type == :call
46
+ recv, meth, args = sexp
47
+ parts = args[1..-1].map { |s| handle_require_sexp s }
48
+
49
+ if recv == [:const, :File]
50
+ if meth == :expand_path
51
+ return handle_expand_path(*parts)
52
+ elsif meth == :join
53
+ return handle_expand_path parts.join("/")
54
+ elsif meth == :dirname
55
+ return handle_expand_path parts[0].split("/")[0...-1].join("/")
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ case @dynamic_require_severity
62
+ when :error
63
+ error "Cannot handle dynamic require"
64
+ when :warning
65
+ warning "Cannot handle dynamic require"
66
+ end
67
+ end
68
+
69
+ def handle_expand_path(path, base = '')
70
+ "#{base}/#{path}".split("/").inject([]) do |p, part|
71
+ if part == ''
72
+ # we had '//', so ignore
73
+ elsif part == '..'
74
+ p.pop
75
+ else
76
+ p << part
77
+ end
78
+
79
+ p
80
+ end.join "/"
81
+ end
82
+ end
83
+ end
84
+
@@ -0,0 +1,85 @@
1
+ require 'sprockets'
2
+ require 'opal/sprockets/parser'
3
+
4
+ module Opal
5
+
6
+ # Proccess using Sprockets
7
+ #
8
+ # Opal.process('opal-jquery') # => String
9
+ def self.process asset
10
+ Environment.new[asset].to_s
11
+ end
12
+
13
+ # The Processor class is used to make ruby files (with rb or opal extensions)
14
+ # available to any sprockets based server. Processor will then get passed any
15
+ # ruby source file to build. There are some options you can override globally
16
+ # which effect how certain ruby features are handled:
17
+ #
18
+ # * method_missing_enabled [true by default]
19
+ # * optimized_operators_enabled [true by default]
20
+ # * arity_check_enabled [false by default]
21
+ # * const_missing_enabled [true by default]
22
+ # * dynamic_require_severity [true by default]
23
+ # * source_map_enabled [true by default]
24
+ # * irb_enabled [false by default]
25
+ #
26
+ class Processor < Tilt::Template
27
+ self.default_mime_type = 'application/javascript'
28
+
29
+ def self.engine_initialized?
30
+ true
31
+ end
32
+
33
+ class << self
34
+ attr_accessor :method_missing_enabled
35
+ attr_accessor :optimized_operators_enabled
36
+ attr_accessor :arity_check_enabled
37
+ attr_accessor :const_missing_enabled
38
+ attr_accessor :dynamic_require_severity
39
+ attr_accessor :source_map_enabled
40
+ attr_accessor :irb_enabled
41
+ end
42
+
43
+ self.method_missing_enabled = true
44
+ self.optimized_operators_enabled = true
45
+ self.arity_check_enabled = false
46
+ self.const_missing_enabled = true
47
+ self.dynamic_require_severity = :error # :error, :warning or :ignore
48
+ self.source_map_enabled = true
49
+ self.irb_enabled = false
50
+
51
+ def initialize_engine
52
+ require_template_library 'opal'
53
+ end
54
+
55
+ def prepare
56
+ end
57
+
58
+ def evaluate(context, locals, &block)
59
+ options = {
60
+ :method_missing => self.class.method_missing_enabled,
61
+ :optimized_operators => self.class.optimized_operators_enabled,
62
+ :arity_check => self.class.arity_check_enabled,
63
+ :const_missing => self.class.const_missing_enabled,
64
+ :dynamic_require_severity => self.class.dynamic_require_severity,
65
+ :source_map_enabled => self.class.source_map_enabled,
66
+ :irb => self.class.irb_enabled,
67
+ :file => context.logical_path,
68
+ :source_file => context.pathname.to_s,
69
+ }
70
+
71
+ parser = Opal::SprocketsParser.new
72
+ result = parser.parse data, options
73
+
74
+ parser.requires.each { |r| context.require_asset r }
75
+ result
76
+ end
77
+ end
78
+ end
79
+
80
+ Tilt.register 'rb', Opal::Processor
81
+ Sprockets.register_engine '.rb', Opal::Processor
82
+
83
+ Tilt.register 'opal', Opal::Processor
84
+ Sprockets.register_engine '.opal', Opal::Processor
85
+
@@ -0,0 +1,141 @@
1
+ require 'rack/showexceptions'
2
+ require 'opal/source_map'
3
+ require 'opal/sprockets/source_map_header'
4
+ require 'opal/sprockets/environment'
5
+ require 'erb'
6
+
7
+ module Opal
8
+
9
+ class SourceMapServer
10
+ def initialize sprockets
11
+ @sprockets = sprockets
12
+ end
13
+
14
+ attr_reader :sprockets
15
+
16
+ attr_writer :prefix
17
+
18
+ def prefix
19
+ @prefix ||= '/__opal_source_maps__'
20
+ end
21
+
22
+ def inspect
23
+ "#<#{self.class}:#{object_id}>"
24
+ end
25
+
26
+ def call(env)
27
+ path = env['PATH_INFO'].gsub(/^\/|\.js\.map$/, '')
28
+ asset = sprockets[path]
29
+ return [404, {}, []] if asset.nil?
30
+
31
+ source = asset.to_s
32
+ map = Opal::SourceMap.new(source, asset.pathname.to_s)
33
+
34
+ return [200, {"Content-Type" => "text/json"}, [map.to_s]]
35
+ end
36
+ end
37
+
38
+ class Server
39
+
40
+ attr_accessor :debug, :index_path, :main, :public_dir, :sprockets
41
+
42
+ def initialize debug = true
43
+ @public_dir = '.'
44
+ @sprockets = Environment.new
45
+ @debug = debug
46
+
47
+ yield self if block_given?
48
+ create_app
49
+ end
50
+
51
+ def append_path path
52
+ @sprockets.append_path path
53
+ end
54
+
55
+ def use_gem gem_name
56
+ @sprockets.use_gem gem_name
57
+ end
58
+
59
+ def create_app
60
+ server, sprockets = self, @sprockets
61
+
62
+ @app = Rack::Builder.app do
63
+ use Rack::ShowExceptions
64
+ map('/assets') { run sprockets }
65
+ map(server.source_maps.prefix) { run server.source_maps }
66
+ use Index, server
67
+ run Rack::Directory.new(server.public_dir)
68
+ end
69
+ end
70
+
71
+ def source_maps
72
+ @source_maps ||= SourceMapServer.new(@sprockets)
73
+ end
74
+
75
+ def call(env)
76
+ @app.call env
77
+ end
78
+
79
+ class Index
80
+
81
+ def initialize(app, server)
82
+ @app = app
83
+ @server = server
84
+ @index_path = server.index_path
85
+ end
86
+
87
+ def call(env)
88
+ if %w[/ /index.html].include? env['PATH_INFO']
89
+ [200, { 'Content-Type' => 'text/html' }, [html]]
90
+ else
91
+ @app.call env
92
+ end
93
+ end
94
+
95
+ # Returns the html content for the root path. Supports ERB
96
+ def html
97
+ source = if @index_path
98
+ raise "index does not exist: #{@index_path}" unless File.exist?(@index_path)
99
+ File.read @index_path
100
+ elsif File.exist? 'index.html'
101
+ File.read 'index.html'
102
+ elsif File.exist? 'index.html.erb'
103
+ File.read 'index.html.erb'
104
+ else
105
+ SOURCE
106
+ end
107
+
108
+ ::ERB.new(source).result binding
109
+ end
110
+
111
+ def javascript_include_tag source
112
+ if @server.debug
113
+ assets = @server.sprockets[source].to_a
114
+
115
+ raise "Cannot find asset: #{source}" if assets.empty?
116
+
117
+ scripts = assets.map do |a|
118
+ %Q{<script src="/assets/#{ a.logical_path }?body=1"></script>}
119
+ end
120
+
121
+ scripts.join "\n"
122
+ else
123
+ "<script src=\"/assets/#{source}.js\"></script>"
124
+ end
125
+ end
126
+
127
+ SOURCE = <<-HTML
128
+ <!DOCTYPE html>
129
+ <html>
130
+ <head>
131
+ <title>Opal Server</title>
132
+ </head>
133
+ <body>
134
+ <%= javascript_include_tag @server.main %>
135
+ </body>
136
+ </html>
137
+ HTML
138
+ end
139
+ end
140
+ end
141
+
@@ -0,0 +1,22 @@
1
+ require 'sprockets/server'
2
+
3
+ module Sprockets
4
+ module Server
5
+
6
+ # Adds the source map header to all sprocket responses for assets
7
+ # with a .rb or .opal extension in the extension chain.
8
+ def headers_with_opal_source_maps(env, asset, length)
9
+ headers_without_opal_source_maps(env, asset, length).tap do |headers|
10
+ if asset.pathname.to_s =~ /\.(rb|opal)\b/
11
+ headers['X-SourceMap'] = '/__opal_source_maps__/'+asset.logical_path + '.map'
12
+ end
13
+ end
14
+ end
15
+
16
+ # Poor man's alias_method_chain :)
17
+ alias headers_without_opal_source_maps headers
18
+ alias headers headers_with_opal_source_maps
19
+
20
+ end
21
+ end
22
+
@@ -0,0 +1,5 @@
1
+ module Opal
2
+ module Sprockets
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ require 'opal/sprockets/environment'
2
+ require 'opal/sprockets/parser'
3
+ require 'opal/sprockets/processor'
4
+ require 'opal/sprockets/server'
@@ -0,0 +1 @@
1
+ require 'opal/sprockets'
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/opal/sprockets/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'opal-sprockets'
6
+ s.version = Opal::Sprockets::VERSION
7
+ s.author = 'Adam Beynon'
8
+ s.email = 'adam.beynon@gmail.com'
9
+ s.homepage = 'http://opalrb.org'
10
+ s.summary = 'Sprockets support for opal'
11
+ s.description = 'Sprockets suppoer for opal.'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.require_paths = ['lib']
16
+
17
+ s.add_dependency 'sprockets'
18
+ s.add_dependency 'opal', '~> 0.4.0'
19
+
20
+ s.add_development_dependency 'rake'
21
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opal-sprockets
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Beynon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sprockets
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: opal
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.4.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.4.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Sprockets suppoer for opal.
63
+ email: adam.beynon@gmail.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - .gitignore
69
+ - Gemfile
70
+ - README.md
71
+ - lib/opal-sprockets.rb
72
+ - lib/opal/sprockets.rb
73
+ - lib/opal/sprockets/environment.rb
74
+ - lib/opal/sprockets/parser.rb
75
+ - lib/opal/sprockets/processor.rb
76
+ - lib/opal/sprockets/server.rb
77
+ - lib/opal/sprockets/source_map_header.rb
78
+ - lib/opal/sprockets/version.rb
79
+ - opal-sprockets.gemspec
80
+ homepage: http://opalrb.org
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.23
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Sprockets support for opal
104
+ test_files: []