Skip to content

Commit

Permalink
Showing 15 changed files with 391 additions and 18 deletions.
2 changes: 0 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
source 'https://rubygems.org'
gemspec

gem 'opal-sprockets', :github => 'opal/opal-sprockets'

# Stick with older racc until
# https://github.com/tenderlove/racc/issues/32
# is solved.
3 changes: 3 additions & 0 deletions example/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://rubygems.org'

gem 'opal', :path => '../'
13 changes: 13 additions & 0 deletions example/app/application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'opal'
require 'user'

module MyApp
class Application
def initialize
@user = User.new('Bill')
@user.authenticated?
end
end
end

MyApp::Application.new
21 changes: 21 additions & 0 deletions example/app/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class User
def initialize(name)
@name = name
end

def authenticated?
if admin? or special_persmission?
true
else
raise "not authenticated"
end
end

def admin?
@name == 'Bob'
end

def special_persmission?
false
end
end
7 changes: 7 additions & 0 deletions example/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'bundler'
Bundler.require

run Opal::Server.new { |s|
s.main = 'application'
s.append_path 'app'
}
10 changes: 10 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>opal-sprockets demo</title>

<%= javascript_include_tag 'application' %>
</head>
<body>
</body>
</html>
2 changes: 1 addition & 1 deletion lib/mspec/opal/rake_task.rb
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ class Opal::Nodes::CallNode

require 'rack'
require 'webrick'
require 'opal-sprockets'

module MSpec
module Opal
DEFAULT_PATTERN = 'spec/opal/{parser,core,compiler,stdlib}/**/*_spec.rb'
1 change: 1 addition & 0 deletions lib/opal.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'opal/compiler'
require 'opal/builder'
require 'opal/erb'
require 'opal/sprockets'
require 'opal/version'

# Opal is a ruby to javascript compiler, with a runtime for running
14 changes: 0 additions & 14 deletions lib/opal/cli.rb
Original file line number Diff line number Diff line change
@@ -123,7 +123,6 @@ def show_sexp
# PROCESSOR

def set_processor_options
require_opal_sprockets
processor_options.each do |option|
key = option.to_sym
next unless options.has_key? key
@@ -160,7 +159,6 @@ def sprockets
end

def server
require_opal_sprockets
@server ||= Opal::Server.new do |s|
load_paths.each do |path|
s.append_path path
@@ -169,18 +167,6 @@ def server
end
end

def require_opal_sprockets
begin
require 'opal-sprockets'
rescue LoadError
$stderr.puts 'Opal executable requires opal-sprockets to be fully functional.'
$stderr.puts 'You can install it with rubygems:'
$stderr.puts ''
$stderr.puts ' gem install opal-sprockets'
exit -1
end
end

##
# OUTPUT

4 changes: 4 additions & 0 deletions lib/opal/sprockets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require 'opal/sprockets/environment'
require 'opal/sprockets/processor'
require 'opal/sprockets/erb'
require 'opal/sprockets/server'
21 changes: 21 additions & 0 deletions lib/opal/sprockets/environment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'sprockets'

module Opal
# Environment is a subclass of Sprockets::Environment which already has our opal
# load paths loaded. This makes it easy for stand-alone rack apps, or test runners
# that have opal load paths ready to use. You can also add an existing gem's lib
# directory to our load path to use real gems inside your opal environment.
#
# If you are running rails, then you just need opal-rails instead, which will
# do this for you.
class Environment < ::Sprockets::Environment
def initialize *args
super
Opal.paths.each { |p| append_path p }
end

def use_gem gem_name
append_path File.join(Gem::Specification.find_by_name(gem_name).gem_dir, 'lib')
end
end
end
29 changes: 29 additions & 0 deletions lib/opal/sprockets/erb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'opal'
require 'opal/compiler'
require 'sprockets'

module Opal
module ERB
class Processor < Tilt::Template
self.default_mime_type = 'application/javascript'

def self.engine_initialized?
true
end

def initialize_engine
require_template_library 'opal'
end

def prepare
end

def evaluate(scope, locals, &block)
Opal::ERB.compile data, scope.logical_path.sub(/^templates\//, '')
end
end
end
end

Tilt.register 'opalerb', Opal::ERB::Processor
Sprockets.register_engine '.opalerb', Opal::ERB::Processor
122 changes: 122 additions & 0 deletions lib/opal/sprockets/processor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
require 'set'
require 'sprockets'

$OPAL_SOURCE_MAPS = {}

module Opal
# Proccess using Sprockets
#
# Opal.process('opal-jquery') # => String
def self.process asset
Environment.new[asset].to_s
end

# The Processor class is used to make ruby files (with rb or opal extensions)
# available to any sprockets based server. Processor will then get passed any
# ruby source file to build. There are some options you can override globally
# which effect how certain ruby features are handled:
#
# * method_missing_enabled [true by default]
# * optimized_operators_enabled [true by default]
# * arity_check_enabled [false by default]
# * const_missing_enabled [true by default]
# * dynamic_require_severity [true by default]
# * source_map_enabled [true by default]
# * irb_enabled [false by default]
#
class Processor < Tilt::Template
self.default_mime_type = 'application/javascript'

def self.engine_initialized?
true
end

class << self
attr_accessor :method_missing_enabled
attr_accessor :arity_check_enabled
attr_accessor :const_missing_enabled
attr_accessor :dynamic_require_severity
attr_accessor :source_map_enabled
attr_accessor :irb_enabled
end

self.method_missing_enabled = true
self.arity_check_enabled = false
self.const_missing_enabled = true
self.dynamic_require_severity = :error # :error, :warning or :ignore
self.source_map_enabled = true
self.irb_enabled = false

def self.stub_file(name)
stubbed_files << name.to_s
end

def self.stubbed_files
@stubbed_files ||= Set.new
end

def initialize_engine
require_template_library 'opal'
end

def prepare
end

def evaluate(context, locals, &block)
options = {
:method_missing => self.class.method_missing_enabled,
:arity_check => self.class.arity_check_enabled,
:const_missing => self.class.const_missing_enabled,
:dynamic_require_severity => self.class.dynamic_require_severity,
:irb => self.class.irb_enabled,
:file => context.logical_path,
}

compiler = Opal::Compiler.new
result = compiler.compile data, options

compiler.requires.each do |r|
next if stubbed_file? r
path = find_opal_require context.environment, r
context.require_asset path
end

if self.class.source_map_enabled
$OPAL_SOURCE_MAPS[context.pathname] = compiler.source_map(source_file_url(context)).to_s
"#{result}\n//@ sourceMappingURL=#{source_map_url(context)}\n"
else
result
end
end

def source_map_url(context)
"#{prefix}/#{context.logical_path}.js.map"
end

def source_file_url(context)
"#{prefix}/#{context.logical_path.to_s}"
end

def prefix
"/__opal_source_maps__"
end

def stubbed_file?(name)
self.class.stubbed_files.include? name
end

def find_opal_require(environment, r)
path = environment.paths.find do |p|
File.exist?(File.join(p, "#{r}.rb"))
end

path ? File.join(path, "#{r}.rb") : r
end
end
end

Tilt.register 'rb', Opal::Processor
Sprockets.register_engine '.rb', Opal::Processor

Tilt.register 'opal', Opal::Processor
Sprockets.register_engine '.opal', Opal::Processor
158 changes: 158 additions & 0 deletions lib/opal/sprockets/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
require 'rack/file'
require 'rack/urlmap'
require 'rack/builder'
require 'rack/directory'
require 'rack/showexceptions'
require 'opal/source_map'
require 'opal/sprockets/environment'
require 'erb'

module Opal

class SourceMapServer
def initialize sprockets
@sprockets = sprockets
end

attr_reader :sprockets

attr_writer :prefix

def prefix
@prefix ||= '/__opal_source_maps__'
end

def inspect
"#<#{self.class}:#{object_id}>"
end

def call(env)
path_info = env['PATH_INFO']

if path_info =~ /\.js\.map$/
path = env['PATH_INFO'].gsub(/^\/|\.js\.map$/, '')
asset = sprockets[path]
return [404, {}, []] if asset.nil?

return [200, {"Content-Type" => "text/json"}, [$OPAL_SOURCE_MAPS[asset.pathname].to_s]]
else
return [200, {"Content-Type" => "text/text"}, [File.read(sprockets.resolve(path_info))]]
end
end
end

class Server

attr_accessor :debug, :index_path, :main, :public_dir, :sprockets

def initialize debug_or_options = {}
unless Hash === debug_or_options
warn "passing a boolean to control debug is deprecated.\n"+
"Please pass an Hash instead: Server.new(debug: true)"
options = {:debug => debug_or_options}
else
options = debug_or_options
end

@public_dir = '.'
@sprockets = Environment.new
@debug = options.fetch(:debug, true)

yield self if block_given?
create_app
end

def source_map_enabled
Opal::Processor.source_map_enabled
end

def append_path path
@sprockets.append_path path
end

def use_gem gem_name
@sprockets.use_gem gem_name
end

def create_app
server, sprockets = self, @sprockets

@app = Rack::Builder.app do
use Rack::ShowExceptions
map('/assets') { run sprockets }
map(server.source_maps.prefix) { run server.source_maps } if server.source_map_enabled
use Index, server
run Rack::Directory.new(server.public_dir)
end
end

def source_maps
@source_maps ||= SourceMapServer.new(@sprockets)
end

def call(env)
@app.call env
end

class Index

def initialize(app, server)
@app = app
@server = server
@index_path = server.index_path
end

def call(env)
if %w[/ /index.html].include? env['PATH_INFO']
[200, { 'Content-Type' => 'text/html' }, [html]]
else
@app.call env
end
end

# Returns the html content for the root path. Supports ERB
def html
source = if @index_path
raise "index does not exist: #{@index_path}" unless File.exist?(@index_path)
File.read @index_path
elsif File.exist? 'index.html'
File.read 'index.html'
elsif File.exist? 'index.html.erb'
File.read 'index.html.erb'
else
SOURCE
end

::ERB.new(source).result binding
end

def javascript_include_tag source
if @server.debug
assets = @server.sprockets[source].to_a

raise "Cannot find asset: #{source}" if assets.empty?

scripts = assets.map do |a|
%Q{<script src="/assets/#{ a.logical_path }?body=1"></script>}
end

scripts.join "\n"
else
"<script src=\"/assets/#{source}.js\"></script>"
end
end

SOURCE = <<-HTML
<!DOCTYPE html>
<html>
<head>
<title>Opal Server</title>
</head>
<body>
<%= javascript_include_tag @server.main %>
</body>
</html>
HTML
end
end
end
2 changes: 1 addition & 1 deletion opal.gemspec
Original file line number Diff line number Diff line change
@@ -19,11 +19,11 @@ Gem::Specification.new do |s|
s.require_paths = ['lib']

s.add_dependency 'source_map'
s.add_dependency 'sprockets'

s.add_development_dependency 'mspec', '1.5.20'
s.add_development_dependency 'rake'
s.add_development_dependency 'racc'
s.add_development_dependency 'opal-sprockets', '~> 0.4.0'
s.add_development_dependency 'rspec', '~> 2.14'
s.add_development_dependency 'octokit', '~> 2.4.0'
end

0 comments on commit 86fcf97

Please sign in to comment.