opal-sprockets 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []