diff --git a/Gemfile b/Gemfile index 8c98802..f4d56cd 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,8 @@ gem 'rack-unreloader', '1.5.0' gem 'rack-livereload' gem 'rspec' gem 'opal-connect-rspec' -gem 'opal', '0.10.0' +gem 'opal', '0.10.1' +gem 'opal-rspec', '0.6.0.beta1' gem 'pry' gem 'awesome_print' diff --git a/Makefile b/Makefile index 117e465..e66386c 100644 --- a/Makefile +++ b/Makefile @@ -2,3 +2,5 @@ server: bundle exec rake webpack:run& bundle exec thin start --port=3001 run: bundle exec thin start --port=3001 +test: + bundle exec rspec && bundle exec rake rspec:browser diff --git a/README.md b/README.md index 7d4569a..28923cc 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,136 @@ -# Opal::Connect +# **Opal-Connect** *(OC)* +##### *Make working with opal even easier!* -TODO: Write a gem description +## Installation: -## Installation +```ruby +gem install 'opal-connect' +``` -Add this line to your application's Gemfile: +## Usage: ```ruby -gem 'opal-connect' +class FooBar + include Opal::Connect +end ``` -And then execute: +## Reason for being: + +I wanted to write my entire app in a single code base, I was tired of having one giant app in javascript and another in ruby. That's when I found [Opal]. I didn't want to over complicate things by creating a framework **(this is not a framework!)** and lock you down to only using Opal Connect. I also wanted to make Opal Connect as lightweight as possible. Think of this as a layer on-top of Opal that provides an easy way to use it with any preexisting framework, plus it provides a nice plugin architecture inspired by [Roda]. + + +## Getting started: + +In this example we'll use [Roda] as our framework of choice. Roda does come with an [assets plugin](https://github.com/jeremyevans/roda/blob/master/lib/roda/plugins/assets.rb), but [Opal] ships with [Sprockets] so we'll just use that for now. Plus we make it even easier to use than their [sprockets example](https://github.com/opal/opal/tree/master/examples), using the [OC sprockets plugin](https://github.com/cj/opal-connect/tree/master/lib/opal/connect/plugins/sprockets.rb). + +```ruby +require 'roda' +require 'opal-connect' # this will require everything you need, including opal. + +class App < Roda + class OpalComponent + include Opal::Connect # you can include this in a preexisting class + + def display + 'OpalComponent' + end + end + + # We'll set debug to true, that way when we have an error the debug console will use maps and map + # the javascript error back to the ruby line of code! + Opal::Connect.plugin :sprockets, debug: true - $ bundle + route do |r| + + r.on Opal::Connect.sprockets[:maps_prefix_url] do + r.run Opal::Connect.sprockets[:maps_app] + end + + r.on Opal::Connect.sprockets[:prefix_url] do + r.run Opal::Connect.sprockets[:server] + end + + r.root do + OpalComponent.render :display + end + end +``` + +That's it! Start the server, visit the root URL and you'll see `OpalComponent` printed out. + +## `Opal::Connect#class` + +- [Opal::Connect#options] +- [Opal::Connect#client_options] +- [Opal::Connect#stubbed_files] +- [Opal::Connect#files] +- [Opal::Connect#setup] +- [Opal::Connect#run] +- [Opal::Connect#write_files] +- [Opal::Connect#run_setup] +- [Opal::Connect#included] + +## Opal::Connect#options +###### Configuration options for [OC] + +| key | default | type | description | +|-----|---------|------|-------------| +| url | /connect| String| The url that will handle connect requests. | +| plugins | [] | Array | Stores an array of all plugins (ones added by [Opal::Connect#plugin]). | +| plugins_loaded | [] | Array | Stores an array of all plugins loaded. | +| entry | [] | Array | Stores all the entry blocks to be run when [Opal::Connect#write_entry_file] is called. +| javascript | [] | Array | Stores all the javascript blocks to execute when [Opal::Connect#render] is called. | +| classes | [] | Array | List of classes using [OC]. | +| run | false | Boolean | whether or not [OC] has been run yet. | +| stubbed_files | [] | Array | List of files to be stubbed by [Opal] using [Opal::Connect#stubbed_files] | + + +## Opal::Connect#client_options +##### Server options that are passed to the client options. + +## Opal::Connect#stubbed_files +##### Files that are stubbed in opal and will not get passed. + +## Opal::Connect#files +##### Files to be compiled with [Opal] by [OC] and output to the `.connect/` folder in the root of your project. + +## Opal::Connect#setup +##### If you find yourself using a lot of plugins/options, you can use this setup block. + +```ruby +Opal::Connect.setup do + plugin :sprockets, debug: false + plugin :rspec, glob: '**/*_feature_spec.rb' +end +``` -Or install it yourself as: +## Opal::Connect#run +##### This will trigger [Opal::Connect#write_files], [Opal::Connect#write_entry_file] and [Opal::Connect#run_setups]. - $ gem install opal-connect +## Opal::Connect#write_files +##### This will write the opal file and any files contained in [Opal::Connect#files] to the `.connect/` folder. -## Usage +## Opal::Connect#run_setups +##### This will run all the `Opal::Connect#setup` blocks defined in your classes. **Not to be confused with `Opal::Connect#setup`** -TODO: Write usage instructions here +## Opal::Connect#included +##### Called when you `include Opal::Connect` into your class. It will register it with [OC] and add the plugins for that class to use -## Contributing +[Opal]: https://github.com/opal/opal "Opal" +[Roda]: https://github.com/jeremyevans/roda "Roda" +[Sprockets]: https://github.com/rails/sprockets "Sprockets" +[OC]: https://github.com/cj/opal-connect "OC" -1. Fork it ( https://github.com/[my-github-username]/opal-connect/fork ) -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create a new Pull Request +[Opal::Connect#options]: #OpalConnect-class-options +[Opal::Connect#client_options]: #OpalConnect-class-client_options +[Opal::Connect#plugin]: #OpalConnect-class-plugin +[Opal::Connect#write_entry_file]: #OpalConnect-class-write_entry_file +[Opal::Connect#render]: #OpalConnect-class-render +[Opal::Connect#stubbed_files]: #OpalConnect-class-stubbed_files +[Opal::Connect#files]: #OpalConnect-class-files +[Opal::Connect#setup]: #OpalConnect-class-setup +[Opal::Connect#run]: #OpalConnect-class-run +[Opal::Connect#write_files]: #OpalConnect-class-write_files +[Opal::Connect#run_setup]: #OpalConnect-class-run_setup +[Opal::Connect#included]: #OpalConnect-class-included diff --git a/app/app.rb b/app/app.rb index d762308..5338814 100644 --- a/app/app.rb +++ b/app/app.rb @@ -1,22 +1,14 @@ require_relative 'config/connect' class App < Roda - plugin :assets, - path: '', - css_dir: '', - js_dir: '', - group_subdirs: false, - gzip: true, - js_opts: { builder: Opal::Connect.builder }, - js: { - app: ['node_modules/jquery/dist/jquery.js', '.connect/opal.js', '.connect/connect.js', '.connect/entry.rb'], - rspec: ['.connect/rspec.js', '.connect/rspec_tests.js'] - } - - # use Rack::LiveReload - route do |r| - r.assets + r.on Opal::Connect.sprockets[:maps_prefix_url] do + r.run Opal::Connect.sprockets[:maps_app] + end + + r.on Opal::Connect.sprockets[:prefix_url] do + r.run Opal::Connect.sprockets[:server] + end r.root do Components::Example.scope(self).render :display diff --git a/app/components/example.rb b/app/components/example.rb index bd2f145..3b59c8a 100644 --- a/app/components/example.rb +++ b/app/components/example.rb @@ -3,7 +3,7 @@ module Components class Example include Opal::Connect - setup do + def self.setup dom.set! html! { html do head do @@ -16,7 +16,8 @@ class Example end } - dom.find('html').append assets([:js, :app]) + # dom.find('html').append assets(:js) + dom.find('html').append connect_include_tag dom.save! end unless RUBY_ENGINE == 'opal' diff --git a/app/config/boot.rb b/app/config/boot.rb index 66e6d4a..2c17642 100644 --- a/app/config/boot.rb +++ b/app/config/boot.rb @@ -10,11 +10,9 @@ class App < Roda; end -Unreloader = Rack::Unreloader.new(subclasses: %w'Roda Roda::RodaPlugins Opal::Connect Opal::Connect::CLIENT_OPTIONS'){App} +Unreloader = Rack::Unreloader.new(subclasses: %w'Roda Opal'){App} Unreloader.require './lib/opal/connect.rb' Unreloader.require 'app/config/connect' Unreloader.require 'app' Unreloader.require 'app/components/**/*.rb' - -Opal::Connect.setup diff --git a/app/config/connect.rb b/app/config/connect.rb index 9ed9f09..803487d 100644 --- a/app/config/connect.rb +++ b/app/config/connect.rb @@ -1,11 +1,11 @@ require 'opal-connect' Opal::Connect.setup do - options[:plugins] = [ :server, :html, :dom, :events ] - options[:livereload] = true + plugins :server, :html, :dom, :events, :store plugin :scope, App.new('') - plugin :rspec, - code: -> { assets([:js, :app]) + assets([:js, :rspec]) } + plugin :rspec + plugin :sprockets, + jquery_path: 'node_modules/jquery/dist/jquery.js', + debug: true end - diff --git a/lib/opal/connect.rb b/lib/opal/connect.rb index f890947..773c03a 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -1,10 +1,9 @@ require 'opal' require 'base64' +require 'json' require "opal/connect/version" if RUBY_ENGINE == 'opal' - `if(typeof require === 'undefined') { require = function(){} }` - `require("expose?$!expose?jQuery!jquery/dist/jquery.min.js")` require 'opal/connect/puts' else require 'oga' @@ -24,13 +23,15 @@ class << self def options @_options ||= Connect::ConnectCache.new( - livereload: false, - url: '/connect', - plugins: [], + url: '/connect', + plugins: [], plugins_loaded: [], - javascript: [], - requires: [], - setup_blocks: {}, + entry: [], + javascript: [], + requires: [], + classes: [], + stubbed_files: [], + run: false ) end @@ -47,35 +48,41 @@ def files end def setup(&block) - if RUBY_ENGINE != 'opal' && block_given? - Opal.append_path Dir.pwd unless RUBY_ENGINE == 'opal' - - instance_exec(&block) + Opal.append_path Dir.pwd + instance_exec(&block) if block_given? + end - # make sure we include the default plugins with connect - options[:plugins].each do |plug| - unless options[:plugins_loaded].include? plug - options[:plugins_loaded] << plug - Connect.plugin(plug) - end - end + def run(entry_file = 'entry') + unless RUBY_ENGINE == 'opal' + Opal.append_path Dir.pwd + write_files + write_entry_file(entry_file) end - unless block_given? - unless RUBY_ENGINE == 'opal' - opal_code = Opal::Connect::STUBS.map { |stub| "require '#{stub}'" }.join(";") - opal_stubs = Opal::Config.stubbed_files.to_a - Opal::Connect.write_file(:opal, opal_code, Opal::VERSION, opal_stubs) + run_setups - Connect.files.each { |name, (code, version, stubs)| write_file name, code, version, stubs } - end + write_entry_file(entry_file) unless RUBY_ENGINE == 'opal' + end - options[:setup_blocks].each do |klass, blocks| - blocks.each { |b| klass.instance_exec(&b) } - end + def write_files + Opal.append_path Dir.pwd + + opal_code = Connect::STUBS.map { |stub| "require '#{stub}'" }.join(";") + opal_stubs = Config.stubbed_files.to_a + Opal::Connect.write_file(:opal, opal_code, Opal::VERSION, opal_stubs) - options[:setup_blocks] = {} + Connect.files.each { |name, (code, version, stubs)| write_file name, code, version, stubs } + end + + def run_setups + Connect.options[:classes].each do |class_string| + next unless class_string + + klass = Object.const_get(class_string) + klass.setup if klass.respond_to?(:setup) end + + Connect.options[:classes] = [] end def included(klass) @@ -84,6 +91,8 @@ def included(klass) included_files << file unless files.include?(file) || file[/^spec/] end + Connect.options[:classes] << klass.name unless Connect.options[:classes].include?(klass.name) + klass.extend ConnectPlugins::Base::ClassMethods Connect.options[:plugins].each { |plug| klass.plugin plug, :included } @@ -96,9 +105,9 @@ class ConnectCache # Create a new thread safe cache. def initialize(options = false) @mutex = Mutex.new if RUBY_ENGINE != 'opal' - @hash = options || {} - @hash.each { |k, v| @hash[k] = ConnectCache.new(v) if v.is_a?(Hash) } - @hash + hash = options || {} + # make a copy of the hash passed so changes don't effect the one passed in. + @hash = Hash[hash.map{|k,v| [k, (v.is_a?(Hash) ? ConnectCache.new(v) : v)]}] end # Make getting value from underlying hash thread safe. @@ -155,6 +164,14 @@ def each module ConnectPlugins @plugins = ConnectCache.new + def self.javascript(&block) + Connect.options[:javascript] << block + end + + def self.entry(&block) + Connect.options[:entry] << block + end + def self.plugins @plugins end @@ -189,11 +206,12 @@ module Base module InstanceMethods if RUBY_ENGINE != 'opal' def render(method, *options, &block) - code = Connect.javascript(self, method, *options) - - Connect.write_entry_file(self, method, *options) if Connect.options[:livereload] - - "#{public_send(method, *options, &block)}" + unless Connect.options[:run] + Connect.options[:run] = true + File.exist?('.connect/entry.rb') ? Connect.run_setups : Connect.run + end + %{#{public_send(method, *options, &block)} + } end end end @@ -203,15 +221,6 @@ def render(method, *args, &block) new.render(method, *args, &block) end - def setup(&block) - if block_given? - @_setup_block = block - (Connect.options[:setup_blocks][self] ||= []) << @_setup_block - end - - @_setup_block - end - # Load a new plugin into the current class. A plugin can be a module # which is used directly, or a symbol represented a registered plugin # which will be required and then used. Returns nil. @@ -236,21 +245,21 @@ def plugin(plugin, *args, &block) include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods) extend(plugin::ClassMethods) if defined?(plugin::ClassMethods) + plugin.configure(Connect, *args, &block) if !included && plugin.respond_to?(:configure) + unless included Connect.extend(plugin::ConnectClassMethods) if defined?(plugin::ConnectClassMethods) Connect.include(plugin::ConnectInstanceMethods) if defined?(plugin::ConnectInstanceMethods) Connect.instance_exec(plugin, &plugin::ConnectSetup) if defined?(plugin::ConnectSetup) - - unless RUBY_ENGINE == 'opal' - Connect.options[:javascript] << plugin::ConnectJavascript if defined?(plugin::ConnectJavascript) - end end - plugin.configure(self, *args, &block) if !included && plugin.respond_to?(:configure) - nil end + def plugins(*plugins) + plugins.each { |plugin| Connect.plugin plugin} + end + if RUBY_ENGINE != 'opal' def included_files @_included_files ||= [] @@ -267,46 +276,45 @@ def build(code, stubs = false) builder(stubs).build_str(code, '(inline)').to_s end - def javascript(klass, method, *opts) - return unless klass - + def javascript(klass, method = false, *opts) js = [] options[:javascript].uniq.each { |block| js << klass.instance_exec(&block) } %{ + #{js.join("\n")} + Document.ready? do - #{js.join(';')} klass = #{klass.class.name}.new - klass.__send__(:#{method}, *JSON.parse(Base64.decode64('#{Base64.encode64 opts.to_json}'))) if klass.respond_to?(:#{method}) + #{method ? "klass.__send__(:#{method}, *JSON.parse(Base64.decode64('#{Base64.encode64 opts.to_json}'))) if klass.respond_to?(:#{method})" : ''} end } end - def write_entry_file(klass = false, method = false, *options) - js = [] + def write_entry_file(entry_name = 'entry') + klass = Class.new { include Opal::Connect } path = "#{Dir.pwd}/.connect" - files = Connect.included_files.dup.uniq.map { |file| "require '#{file}'" }.join(';') - entry = Connect.options[:entry] + files = Connect.included_files.dup.uniq.map { |file| "require '#{file}'" }.join("\n") + entry = [] client_options = Base64.encode64 Connect.client_options.to_json - plugins = plugin_paths.dup.map { |plugin_path| plugin_path = "require '#{plugin_path}'" }.join(';') + plugins = plugin_paths.dup.map { |plugin_path| plugin_path = "require '#{plugin_path}'" }.join("\n") - Connect.options[:javascript].uniq.each { |block| js << klass.instance_exec(&block) } if klass + Connect.options[:entry].uniq.each { |block| entry << klass.instance_exec(&block) } entry_code = %{ require 'opal-connect' #{plugins} options = JSON.parse(Base64.decode64('#{client_options}')) options.each { |key, value| Opal::Connect.options[key] = value } - #{entry} #{files} # make sure we include the default plugins with connect Opal::Connect.options[:plugins].each { |plug| Opal::Connect.plugin plug } - Opal::Connect.setup - #{js.join(';')} + Opal::Connect.run + #{entry.join("\n")} + #{javascript(klass)} } FileUtils.mkdir_p(path) - File.write("#{path}/entry.rb", entry_code) + File.write("#{path}/#{entry_name}.rb", entry_code) end def write_file(name, code, current_version, stubs = false) diff --git a/lib/opal/connect/plugins/abilities.rb b/lib/opal/connect/plugins/abilities.rb index 158ca50..52c0193 100644 --- a/lib/opal/connect/plugins/abilities.rb +++ b/lib/opal/connect/plugins/abilities.rb @@ -7,6 +7,15 @@ module Opal::Connect module ConnectPlugins + javascript do + if current_user.respond_to?(:id) && current_user.id + abilities = Opal::Connect.options[:abilities][:list][current_user.role] + "$current_user_abilities = Base64.decode64('#{Base64.encode64 abilities.to_json}')" + else + "$current_user_abilities = {}" + end + end + module Abilities def self.load_dependencies(connect, *args) connect.plugin :current_user @@ -24,15 +33,6 @@ def self.configure(connect, options = false) end end - ConnectJavascript = -> do - if current_user.respond_to?(:id) && current_user.id - abilities = Opal::Connect.options[:abilities][:list][current_user.role] - "$current_user_abilities = Base64.decode64('#{Base64.encode64 abilities.to_json}')" - else - "$current_user_abilities = {}" - end - end - module InstanceMethods def load_abilities(user, scope) # make sure the user is logged in diff --git a/lib/opal/connect/plugins/current_user.rb b/lib/opal/connect/plugins/current_user.rb index 36dd3aa..c2c15be 100644 --- a/lib/opal/connect/plugins/current_user.rb +++ b/lib/opal/connect/plugins/current_user.rb @@ -3,6 +3,10 @@ module Opal module Connect module ConnectPlugins + javascript do + "$current_user = JSON.parse Base64.decode64('#{Base64.encode64 current_user.to_h.to_json}')" + end + module CurrentUser def self.load_dependencies(connect, *args) connect.plugin :scope @@ -20,10 +24,6 @@ def self.configure(connect, options = false) connect.options[:current_user] = options end - ConnectJavascript = -> do - "$current_user = JSON.parse Base64.decode64('#{Base64.encode64 current_user.to_h.to_json}')" - end - module InstanceMethods def current_user @current_user ||= Object.const_get(Connect.options[:current_user][:class]).new(OpenStruct.new begin diff --git a/lib/opal/connect/plugins/dom.rb b/lib/opal/connect/plugins/dom.rb index b5db625..044087e 100644 --- a/lib/opal/connect/plugins/dom.rb +++ b/lib/opal/connect/plugins/dom.rb @@ -1,15 +1,15 @@ module Opal module Connect module ConnectPlugins + entry do + templates = Base64.encode64 Connect.templates.hash.to_json + "Opal::Connect.templates = JSON.parse(Base64.decode64('#{templates}'));" + end + # https://github.com/jeremyevans/roda/blob/master/lib/roda.rb#L16 # A thread safe cache class, offering only #[] and #[]= methods, # each protected by a mutex. module Dom - ConnectJavascript = -> do - templates = Base64.encode64 Connect.templates.hash.to_json - "Opal::Connect.templates = JSON.parse(Base64.decode64('#{templates}'));" - end - module ConnectClassMethods attr_accessor :templates @@ -59,7 +59,7 @@ def dom(selector = false) module InstanceMethods def cache - @cache ||= Connect.templates[self.class.name] + self.class.cache end def dom(selector = false) @@ -87,7 +87,7 @@ def initialize(selector, cache, scope) @dom = Element[selector] else # multi-line - if selector["\n"] || selector['html'] + if selector.is_a?(String) @dom = Oga.parse_html(selector) else @dom = cache[:html] diff --git a/lib/opal/connect/plugins/events.rb b/lib/opal/connect/plugins/events.rb index 6e4f351..4171c68 100644 --- a/lib/opal/connect/plugins/events.rb +++ b/lib/opal/connect/plugins/events.rb @@ -1,11 +1,12 @@ module Opal module Connect module ConnectPlugins + + entry { "Opal::Connect.start_events unless $connect_events_started" } + module Events $connect_events = ConnectCache.new if RUBY_ENGINE == 'opal' - ConnectJavascript = -> { "Opal::Connect.start_events unless $connect_events_started" } - module InstanceMethods def connect_event_instance_variables(event, _name, _selector) # gives you access to this, like jquery diff --git a/lib/opal/connect/plugins/rspec.rb b/lib/opal/connect/plugins/rspec.rb index 6dbbc6f..0987c9d 100644 --- a/lib/opal/connect/plugins/rspec.rb +++ b/lib/opal/connect/plugins/rspec.rb @@ -7,8 +7,11 @@ def self.configure(connect, options = {}) port: 3333, host: 'localhost', path: 'rspec', - config: './config.ru' + config: './config.ru', + glob: '**/*_spec.rb' }.merge options + + connect.plugin :sprockets, append_paths: [connect.options[:rspec][:folder]] end module ConnectClassMethods @@ -21,44 +24,48 @@ def run_rspec rspec_requires = [] options = Opal::Connect.options[:rspec] - Opal.append_path "./#{options[:folder]}" $:.unshift "./#{options[:folder]}" require 'rspec' require 'opal-rspec' - Dir.glob("./#{options[:folder]}/**/*_spec.rb").each do |file| + Dir.glob("./#{options[:folder]}/#{options[:glob]}").each do |file| rspec_requires << "require '#{file.sub('./', '')}'" end Opal::Connect.write_file :rspec, %{ - require 'opal/connect/puts' require 'opal/rspec' }, "#{::RSpec::Version::STRING}#{Opal::RSpec::VERSION}" - File.write "#{Dir.pwd}/.connect/rspec_tests.js", build(%{ - #{rspec_requires.join(';')} + File.write "#{Dir.pwd}/.connect/rspec_tests.rb", %{ + require 'opal/connect/puts' + require 'opal/connect/rspec' + RSpec.configure do |config| config.formatter = ::Opal::RSpec::BrowserFormatter config.formatter = ::RSpec::Core::Formatters::ProgressFormatter end + + #{rspec_requires.join(';')} + RSpec::Core::Runner.autorun - }) + } Dir["#{options[:folder]}/**/*_spec.rb"].each { |file| load file } - Opal::Connect.setup - Opal::Connect.write_entry_file(self) + Opal::Connect.run 'rspec_entry' - string = html! { + tmpl = html! { html do head { meta charset: 'utf-8' } - body Class.new { - include Opal::Connect - }.instance_exec(&options[:code]) + body do + div javascript_include_tag 'rspec_entry' + div javascript_include_tag 'rspec.js' + div javascript_include_tag 'rspec_tests' + end end } - Marshal.dump(string, write) + Marshal.dump(tmpl, write) exit!(0) # skips exit handlers. end diff --git a/lib/opal/connect/plugins/server.rb b/lib/opal/connect/plugins/server.rb index eed2e01..0bde449 100644 --- a/lib/opal/connect/plugins/server.rb +++ b/lib/opal/connect/plugins/server.rb @@ -1,13 +1,13 @@ module Opal module Connect module ConnectPlugins - module Server - ConnectJavascript = -> do - %{Opal::Connect.server_methods = JSON.parse( - Base64.decode64('#{Base64.encode64 Connect.server_methods.to_json}') - );} - end + entry do + %{Opal::Connect.server_methods = JSON.parse( + Base64.decode64('#{Base64.encode64 Connect.server_methods.to_json}') + );} + end + module Server module ConnectClassMethods def server_methods @server_methods ||= ConnectCache.new diff --git a/lib/opal/connect/plugins/sprockets.rb b/lib/opal/connect/plugins/sprockets.rb new file mode 100644 index 0000000..70a2c5c --- /dev/null +++ b/lib/opal/connect/plugins/sprockets.rb @@ -0,0 +1,58 @@ +module Opal + module Connect + module ConnectPlugins + entry { "require '#{sprockets[:jquery_path]}'" } + + module Sprockets + def self.configure(connect, options = {}) + opts = connect.options[:sprockets] ||= { + debug: false, + prefix: '/connect/assets', + maps_prefix: '/connect/__OPAL_SOURCE_MAPS__', + append_paths: [] + } + + opts.merge! options + + opts[:server] = Opal::Server.new do |s| + s.append_path '.connect' + opts[:append_paths].each { |path| s.append_path path } + end.sprockets + + opts[:maps_app] = Opal::SourceMapServer.new(opts[:server], opts[:maps_prefix]) + + opts[:maps_prefix_url] = opts[:maps_prefix][1..-1] + opts[:prefix_url] = opts[:prefix][1..-1] + + # Monkeypatch sourcemap header support into sprockets + ::Opal::Sprockets::SourceMapHeaderPatch.inject!(opts[:maps_prefix]) + end unless RUBY_ENGINE == 'opal' + + module ClassMethods + def sprockets + Connect.options[:sprockets] + end + + def javascript_include_tag(file, options = {}) + ::Opal::Sprockets.javascript_include_tag(file, + sprockets: sprockets[:server], + prefix: sprockets[:prefix], debug: options[:debug] || sprockets[:debug] + ) + end + + def connect_include_tag(options = {}) + javascript_include_tag 'entry', options + end + end + + module InstanceMethods + def sprockets + @_sprockets ||= Connect.options[:sprockets].dup + end + end + end + + register_plugin(:sprockets, Sprockets) + end + end +end diff --git a/lib/opal/connect/plugins/store.rb b/lib/opal/connect/plugins/store.rb index e4d0821..c881d49 100644 --- a/lib/opal/connect/plugins/store.rb +++ b/lib/opal/connect/plugins/store.rb @@ -1,6 +1,10 @@ module Opal module Connect module ConnectPlugins + javascript do + "$connect_store = Opal::Connect::ConnectCache.new(JSON.parse Base64.decode64('#{Base64.encode64 Connect.store_opts[:data].to_json}'))" + end + module Store def self.configure(connect, options = {}) return unless options @@ -10,17 +14,19 @@ def self.configure(connect, options = {}) }.merge options end - ConnectJavascript = -> do - "$store = Opal::Connect::ConnectCache.new(JSON.parse Base64.decode64('#{Base64.encode64 Connect.store_opts[:data].to_json}'))" - end - module ClassMethods def store_opts Connect.options[:store] end def store - (RUBY_ENGINE == 'opal' ? $store : store_opts[:data])[self.name] ||= ConnectCache.new + (RUBY_ENGINE == 'opal' ? $connect_store : store_opts[:data])[self.name] ||= ConnectCache.new + end + end + + module InstanceMethods + def store + @_connect_store ||= ConnectCache.new self.class.store.hash end end end diff --git a/lib/opal/connect/rake_task.rb b/lib/opal/connect/rake_task.rb index b28ad37..1007c07 100644 --- a/lib/opal/connect/rake_task.rb +++ b/lib/opal/connect/rake_task.rb @@ -19,8 +19,8 @@ def initialize(name = 'webpack', opts = {}) end namespace 'rspec' do - desc "RSpec Browser Tests" - task :browser do + desc "RSpec Connect Tests" + task :connect do begin path = File.expand_path('../../../../phantom.js', __FILE__) options = Opal::Connect.options[:rspec] @@ -71,8 +71,6 @@ def wait_for_server def initialize_connect return unless defined? Opal.append_path - Opal::Connect.write_entry_file(self) - ENV.to_h.merge({ BUNDLE_BIN: true, CONNECT_STUBS: "#{Opal::Connect.stubbed_files.join(',')},opal-connect,opal-jquery,opal-rspec", diff --git a/lib/opal/connect/rspec.rb b/lib/opal/connect/rspec.rb index 3f7498b..964d4fa 100644 --- a/lib/opal/connect/rspec.rb +++ b/lib/opal/connect/rspec.rb @@ -22,6 +22,8 @@ def dom(selector = false) end end end +else + Opal::Connect.run 'rspec_entry' end module RSpecHelpers @@ -35,7 +37,7 @@ def rspec_dom RSpec.configure do |config| config.extend RSpecHelpers config.include RSpecHelpers - config.before(:suite) { Opal::Connect.setup } + config.before(:suite) { Opal::Connect.run_setups } if RUBY_ENGINE == 'opal' config.before { rspec_dom.find('body').append html! { iframe id: 'rspec-iframe' } } diff --git a/opal-connect.gemspec b/opal-connect.gemspec index 224e338..f8bf432 100644 --- a/opal-connect.gemspec +++ b/opal-connect.gemspec @@ -24,5 +24,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", "~> 1.7" spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "opal-connect-rspec", ">= 0.5.0" + spec.add_development_dependency "opal-rspec", ">= 0.5.0" end diff --git a/phantom.js b/phantom.js index 3f11d69..cce3e6b 100644 --- a/phantom.js +++ b/phantom.js @@ -3,7 +3,7 @@ var page = webPage.create(); var system = require('system'); var args = system.args.slice(1); -page.settings.resourceTimeout = 5000; // 5 seconds +page.settings.resourceTimeout = 25000; // 55 seconds page.onResourceTimeout = function(e) { console.log(e.errorCode); // it'll probably be 408 diff --git a/spec/index.html.erb b/spec/index.html.erb deleted file mode 100644 index 860c972..0000000 --- a/spec/index.html.erb +++ /dev/null @@ -1,10 +0,0 @@ - - -
- -