From 1208a03f563ed3a48178fb433f8509d9eaf48acb Mon Sep 17 00:00:00 2001 From: Damian Farina Date: Mon, 27 Jun 2016 10:19:28 -0300 Subject: [PATCH 01/14] Test showing missing new line bug --- spec/plugins/dom_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/plugins/dom_spec.rb b/spec/plugins/dom_spec.rb index dba6d44..9559ab9 100644 --- a/spec/plugins/dom_spec.rb +++ b/spec/plugins/dom_spec.rb @@ -13,6 +13,7 @@ class DomTest body do div do ul class: 'list' do + li "Two\nLines" li {} end end @@ -20,6 +21,8 @@ class DomTest end } + dom.find('li:first-child').save! :multiline_html + dom.find('li:last-child').save! :inline_html, false dom.save! :html, false end unless RUBY_ENGINE == 'opal' end @@ -73,6 +76,19 @@ class DomTest expect(ul.attr('foo')).to eq 'bar' expect(ul.attr('number')).to eq '1' end + + it 'should allow to use a 1 line html as template' do + multiline_li = subject.dom.tmpl(:multiline_html) + multiline_li.find('li').append 'works' + dom.find('ul').append multiline_li + expect(dom.find('li').length).to eq 2 + expect(dom.find('li span').length).to eq 1 + + inline_li = subject.dom.tmpl(:inline_html) + inline_li.find('li').append 'bug here' + dom.find('ul').append inline_li + expect(dom.find('li span').length).to eq 2 + end end end end From 98e338d214ffaaf6c2e6eae234f08f939699df32 Mon Sep 17 00:00:00 2001 From: cj Date: Wed, 13 Jul 2016 17:33:49 -0500 Subject: [PATCH 02/14] updates to store ConnectCache now dups hash --- lib/opal/connect.rb | 6 +++--- lib/opal/connect/plugins/store.rb | 6 ++++++ spec/plugins/store_spec.rb | 28 ++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/opal/connect.rb b/lib/opal/connect.rb index f890947..1b04696 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -96,9 +96,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. diff --git a/lib/opal/connect/plugins/store.rb b/lib/opal/connect/plugins/store.rb index e4d0821..2b899a2 100644 --- a/lib/opal/connect/plugins/store.rb +++ b/lib/opal/connect/plugins/store.rb @@ -23,6 +23,12 @@ def store (RUBY_ENGINE == 'opal' ? $store : store_opts[:data])[self.name] ||= ConnectCache.new end end + + module InstanceMethods + def store + @_store ||= ConnectCache.new self.class.store.hash + end + end end register_plugin(:store, Store) diff --git a/spec/plugins/store_spec.rb b/spec/plugins/store_spec.rb index 783a856..922abb7 100644 --- a/spec/plugins/store_spec.rb +++ b/spec/plugins/store_spec.rb @@ -2,7 +2,7 @@ Opal::Connect.plugin :store -class StoreTest +class StoreFooTest include Opal::Connect setup do @@ -12,17 +12,37 @@ class StoreTest setup do store.set :server_foo, 'bar' end unless RUBY_ENGINE == 'opal' +end + +class StoreBarTest + include Opal::Connect setup do - store.set :foo, 'bar' + store.set :foo, 'foo' end end describe 'plugin :store' do context 'class' do it 'should get foo and return bar' do - expect(StoreTest.store.get :foo).to eq 'bar' - expect(StoreTest.store.get :server_foo).to eq 'bar' + expect(StoreFooTest.store.get :foo).to eq 'bar' + expect(StoreFooTest.store.get :server_foo).to eq 'bar' + expect(StoreBarTest.store.get :foo).not_to eq 'bar' + expect(StoreBarTest.store.get :server_foo).to eq nil + end + end + + context 'instance' do + let(:store_foo) { StoreFooTest.new } + + it 'should containt the class stored vars' do + expect(store_foo.store[:foo]).to eq 'bar' + expect(store_foo.store.get :server_foo).to eq 'bar' + end + + it "shouldn't update the class store" do + store_foo.store.set :bar, 'foo' + expect(StoreFooTest.store[:bar]).to eq nil end end end From 22b0dbe29cca7d3d6cf8221cff047fadba030bab Mon Sep 17 00:00:00 2001 From: cj Date: Wed, 13 Jul 2016 18:08:52 -0500 Subject: [PATCH 03/14] removed livereload option as its depricated --- Makefile | 2 ++ lib/opal/connect.rb | 28 ++++++++++++++-------------- lib/opal/connect/plugins/store.rb | 12 ++++++------ 3 files changed, 22 insertions(+), 20 deletions(-) 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/lib/opal/connect.rb b/lib/opal/connect.rb index 1b04696..9ac7729 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -24,7 +24,6 @@ class << self def options @_options ||= Connect::ConnectCache.new( - livereload: false, url: '/connect', plugins: [], plugins_loaded: [], @@ -155,6 +154,10 @@ def each module ConnectPlugins @plugins = ConnectCache.new + def self.javascript(&block) + Connect.options[:javascript] << block + end + def self.plugins @plugins end @@ -189,11 +192,8 @@ 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)}" + %{#{public_send(method, *options, &block)} + } end end end @@ -267,31 +267,31 @@ 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 = false, method = '', *opts) + klass = Class.new { include Opal::Connect }.new unless klass js = [] options[:javascript].uniq.each { |block| js << klass.instance_exec(&block) } %{ + #{js.join(';')} + 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}) + unless "#{method}".empty? + klass.__send__(:#{method}, *JSON.parse(Base64.decode64('#{Base64.encode64 opts.to_json}'))) if klass.respond_to?(:#{method}) + end end } end def write_entry_file(klass = false, method = false, *options) - js = [] path = "#{Dir.pwd}/.connect" files = Connect.included_files.dup.uniq.map { |file| "require '#{file}'" }.join(';') entry = Connect.options[:entry] client_options = Base64.encode64 Connect.client_options.to_json plugins = plugin_paths.dup.map { |plugin_path| plugin_path = "require '#{plugin_path}'" }.join(';') - Connect.options[:javascript].uniq.each { |block| js << klass.instance_exec(&block) } if klass - entry_code = %{ require 'opal-connect' #{plugins} @@ -302,7 +302,7 @@ def write_entry_file(klass = false, method = false, *options) # make sure we include the default plugins with connect Opal::Connect.options[:plugins].each { |plug| Opal::Connect.plugin plug } Opal::Connect.setup - #{js.join(';')} + #{javascript(klass, method, *options)} } FileUtils.mkdir_p(path) diff --git a/lib/opal/connect/plugins/store.rb b/lib/opal/connect/plugins/store.rb index 2b899a2..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,23 +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 - @_store ||= ConnectCache.new self.class.store.hash + @_connect_store ||= ConnectCache.new self.class.store.hash end end end From c00903bd4c92f37b9b28b896a8d2c2dd05857a31 Mon Sep 17 00:00:00 2001 From: cj Date: Thu, 14 Jul 2016 01:21:34 -0500 Subject: [PATCH 04/14] working with source maps --- Gemfile | 2 +- app/app.rb | 28 ++++++++++++++++++++++------ app/components/example.rb | 5 +++-- app/config/connect.rb | 3 +-- lib/opal/connect/plugins/rspec.rb | 24 ++++++++++++++---------- spec/example_spec.rb | 2 +- spec/plugins/dom_spec.rb | 2 +- spec/plugins/store_spec.rb | 2 +- 8 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Gemfile b/Gemfile index 8c98802..29a0cb0 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ 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 'pry' gem 'awesome_print' diff --git a/app/app.rb b/app/app.rb index d762308..d468958 100644 --- a/app/app.rb +++ b/app/app.rb @@ -1,6 +1,19 @@ require_relative 'config/connect' class App < Roda + opal = Opal::Server.new do |s| + s.append_path '.connect' + s.main = 'entry' + end + + Sprockets = opal.sprockets + Prefix = '/connect/assets' + maps_prefix = '/__OPAL_SOURCE_MAPS__' + maps_app = Opal::SourceMapServer.new(Sprockets, maps_prefix) + + # Monkeypatch sourcemap header support into sprockets + ::Opal::Sprockets::SourceMapHeaderPatch.inject!(maps_prefix) + plugin :assets, path: '', css_dir: '', @@ -8,16 +21,19 @@ class App < Roda 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 + js: ['node_modules/jquery/dist/jquery.js'] route do |r| r.assets + r.on maps_prefix[1..-1] do + r.run maps_app + end + + r.on Prefix[1..-1] do + r.run Sprockets + end + r.root do Components::Example.scope(self).render :display end diff --git a/app/components/example.rb b/app/components/example.rb index bd2f145..c4860b9 100644 --- a/app/components/example.rb +++ b/app/components/example.rb @@ -16,14 +16,15 @@ class Example end } - dom.find('html').append assets([:js, :app]) + dom.find('html').append assets(:js) + dom.find('html').append ::Opal::Sprockets.javascript_include_tag('entry', sprockets: Sprockets, prefix: Prefix, debug: true) dom.save! end unless RUBY_ENGINE == 'opal' def display if RUBY_ENGINE == 'opal' - dom.find('body').append 'cow' + dom.find('body').appen 'cow' end dom diff --git a/app/config/connect.rb b/app/config/connect.rb index 9ed9f09..4c4780f 100644 --- a/app/config/connect.rb +++ b/app/config/connect.rb @@ -5,7 +5,6 @@ options[:livereload] = true plugin :scope, App.new('') - plugin :rspec, - code: -> { assets([:js, :app]) + assets([:js, :rspec]) } + plugin :rspec, assets: -> { assets(:js) } end diff --git a/lib/opal/connect/plugins/rspec.rb b/lib/opal/connect/plugins/rspec.rb index 6dbbc6f..29502f7 100644 --- a/lib/opal/connect/plugins/rspec.rb +++ b/lib/opal/connect/plugins/rspec.rb @@ -7,7 +7,8 @@ def self.configure(connect, options = {}) port: 3333, host: 'localhost', path: 'rspec', - config: './config.ru' + config: './config.ru', + glob: '**/*_spec.rb' }.merge options end @@ -27,7 +28,7 @@ def run_rspec 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 @@ -36,29 +37,32 @@ def run_rspec 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", %{ 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) - string = html! { + tmpl = html! { html do head { meta charset: 'utf-8' } - body Class.new { - include Opal::Connect - }.instance_exec(&options[:code]) + body do + div Class.new { include Opal::Connect }.instance_exec(&options[:assets]) + div ::Opal::Sprockets.javascript_include_tag('entry', sprockets: App::Sprockets, prefix: App::Prefix, debug: true) + script src: '/connect/assets/rspec.js' + div ::Opal::Sprockets.javascript_include_tag('rspec_tests', sprockets: App::Sprockets, prefix: App::Prefix, debug: true) + end end } - Marshal.dump(string, write) + Marshal.dump(tmpl, write) exit!(0) # skips exit handlers. end diff --git a/spec/example_spec.rb b/spec/example_spec.rb index 2a8dfd9..6107872 100644 --- a/spec/example_spec.rb +++ b/spec/example_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'spec/spec_helper' describe 'components/example' do let(:example) { App::Components::Example.new } diff --git a/spec/plugins/dom_spec.rb b/spec/plugins/dom_spec.rb index dba6d44..d09c1bb 100644 --- a/spec/plugins/dom_spec.rb +++ b/spec/plugins/dom_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'spec/spec_helper' class DomTest include Opal::Connect diff --git a/spec/plugins/store_spec.rb b/spec/plugins/store_spec.rb index 922abb7..e22bc9a 100644 --- a/spec/plugins/store_spec.rb +++ b/spec/plugins/store_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'spec/spec_helper' Opal::Connect.plugin :store From 3e64423669d2498a10d7f7d172933fccc8f3edf3 Mon Sep 17 00:00:00 2001 From: cj Date: Thu, 14 Jul 2016 14:32:35 -0500 Subject: [PATCH 05/14] updates --- Gemfile | 1 + app/app.rb | 32 ++----------- app/components/example.rb | 6 +-- app/config/connect.rb | 6 ++- lib/opal/connect.rb | 31 +++++++------ lib/opal/connect/plugins/abilities.rb | 18 ++++---- lib/opal/connect/plugins/current_user.rb | 8 ++-- lib/opal/connect/plugins/dom.rb | 10 ++-- lib/opal/connect/plugins/events.rb | 5 +- lib/opal/connect/plugins/rspec.rb | 14 ++++-- lib/opal/connect/plugins/server.rb | 12 ++--- lib/opal/connect/plugins/sprockets.rb | 58 ++++++++++++++++++++++++ lib/opal/connect/rake_task.rb | 4 +- opal-connect.gemspec | 2 +- spec/example_spec.rb | 2 +- spec/index.html.erb | 10 ---- spec/plugins/dom_spec.rb | 2 +- spec/plugins/store_spec.rb | 2 +- spec/spec_helper.rb | 2 +- 19 files changed, 130 insertions(+), 95 deletions(-) create mode 100644 lib/opal/connect/plugins/sprockets.rb delete mode 100644 spec/index.html.erb diff --git a/Gemfile b/Gemfile index 29a0cb0..f4d56cd 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,7 @@ gem 'rack-livereload' gem 'rspec' gem 'opal-connect-rspec' gem 'opal', '0.10.1' +gem 'opal-rspec', '0.6.0.beta1' gem 'pry' gem 'awesome_print' diff --git a/app/app.rb b/app/app.rb index d468958..5338814 100644 --- a/app/app.rb +++ b/app/app.rb @@ -1,37 +1,13 @@ require_relative 'config/connect' class App < Roda - opal = Opal::Server.new do |s| - s.append_path '.connect' - s.main = 'entry' - end - - Sprockets = opal.sprockets - Prefix = '/connect/assets' - maps_prefix = '/__OPAL_SOURCE_MAPS__' - maps_app = Opal::SourceMapServer.new(Sprockets, maps_prefix) - - # Monkeypatch sourcemap header support into sprockets - ::Opal::Sprockets::SourceMapHeaderPatch.inject!(maps_prefix) - - plugin :assets, - path: '', - css_dir: '', - js_dir: '', - group_subdirs: false, - gzip: true, - js_opts: { builder: Opal::Connect.builder }, - js: ['node_modules/jquery/dist/jquery.js'] - route do |r| - r.assets - - r.on maps_prefix[1..-1] do - r.run maps_app + r.on Opal::Connect.sprockets[:maps_prefix_url] do + r.run Opal::Connect.sprockets[:maps_app] end - r.on Prefix[1..-1] do - r.run Sprockets + r.on Opal::Connect.sprockets[:prefix_url] do + r.run Opal::Connect.sprockets[:server] end r.root do diff --git a/app/components/example.rb b/app/components/example.rb index c4860b9..1d7f835 100644 --- a/app/components/example.rb +++ b/app/components/example.rb @@ -16,15 +16,15 @@ class Example end } - dom.find('html').append assets(:js) - dom.find('html').append ::Opal::Sprockets.javascript_include_tag('entry', sprockets: Sprockets, prefix: Prefix, debug: true) + # dom.find('html').append assets(:js) + dom.find('html').append connect_include_tag dom.save! end unless RUBY_ENGINE == 'opal' def display if RUBY_ENGINE == 'opal' - dom.find('body').appen 'cow' + dom.find('body').append 'cow' end dom diff --git a/app/config/connect.rb b/app/config/connect.rb index 4c4780f..65582ac 100644 --- a/app/config/connect.rb +++ b/app/config/connect.rb @@ -5,6 +5,8 @@ options[:livereload] = true plugin :scope, App.new('') - plugin :rspec, assets: -> { assets(:js) } + 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 9ac7729..85e37ca 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -3,8 +3,6 @@ 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,12 +22,13 @@ class << self def options @_options ||= Connect::ConnectCache.new( - url: '/connect', - plugins: [], + url: '/connect', + plugins: [], plugins_loaded: [], - javascript: [], - requires: [], - setup_blocks: {}, + entry: [], + javascript: [], + requires: [], + setup_blocks: {}, ) end @@ -62,6 +61,8 @@ def setup(&block) unless block_given? unless RUBY_ENGINE == 'opal' + write_entry_file(self) + 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) @@ -158,6 +159,10 @@ def self.javascript(&block) Connect.options[:javascript] << block end + def self.entry(&block) + Connect.options[:entry] << block + end + def self.plugins @plugins end @@ -240,13 +245,9 @@ def plugin(plugin, *args, &block) 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) + plugin.configure(Connect, *args, &block) if !included && plugin.respond_to?(:configure) nil end @@ -288,20 +289,22 @@ def javascript(klass = false, method = '', *opts) def write_entry_file(klass = false, method = false, *options) path = "#{Dir.pwd}/.connect" files = Connect.included_files.dup.uniq.map { |file| "require '#{file}'" }.join(';') - entry = Connect.options[:entry] + entry = [] client_options = Base64.encode64 Connect.client_options.to_json plugins = plugin_paths.dup.map { |plugin_path| plugin_path = "require '#{plugin_path}'" }.join(';') + 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 + #{entry.join(';')} #{javascript(klass, method, *options)} } 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..b6fd225 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 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 29502f7..70805ec 100644 --- a/lib/opal/connect/plugins/rspec.rb +++ b/lib/opal/connect/plugins/rspec.rb @@ -10,6 +10,8 @@ def self.configure(connect, options = {}) config: './config.ru', glob: '**/*_spec.rb' }.merge options + + connect.plugin :sprockets, append_paths: [connect.options[:rspec][:folder]] end module ConnectClassMethods @@ -22,7 +24,6 @@ def run_rspec rspec_requires = [] options = Opal::Connect.options[:rspec] - Opal.append_path "./#{options[:folder]}" $:.unshift "./#{options[:folder]}" require 'rspec' @@ -38,11 +39,15 @@ def run_rspec }, "#{::RSpec::Version::STRING}#{Opal::RSpec::VERSION}" File.write "#{Dir.pwd}/.connect/rspec_tests.rb", %{ + 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 } @@ -54,10 +59,9 @@ def run_rspec html do head { meta charset: 'utf-8' } body do - div Class.new { include Opal::Connect }.instance_exec(&options[:assets]) - div ::Opal::Sprockets.javascript_include_tag('entry', sprockets: App::Sprockets, prefix: App::Prefix, debug: true) - script src: '/connect/assets/rspec.js' - div ::Opal::Sprockets.javascript_include_tag('rspec_tests', sprockets: App::Sprockets, prefix: App::Prefix, debug: true) + div connect_include_tag + div javascript_include_tag 'rspec.js' + div javascript_include_tag 'rspec_tests' end 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/rake_task.rb b/lib/opal/connect/rake_task.rb index b28ad37..bc60047 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] 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/spec/example_spec.rb b/spec/example_spec.rb index 6107872..2a8dfd9 100644 --- a/spec/example_spec.rb +++ b/spec/example_spec.rb @@ -1,4 +1,4 @@ -require 'spec/spec_helper' +require 'spec_helper' describe 'components/example' do let(:example) { App::Components::Example.new } 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 @@ - - - - - Opal Server - - - <%= javascript_include_tag @server.main %> - - diff --git a/spec/plugins/dom_spec.rb b/spec/plugins/dom_spec.rb index d09c1bb..dba6d44 100644 --- a/spec/plugins/dom_spec.rb +++ b/spec/plugins/dom_spec.rb @@ -1,4 +1,4 @@ -require 'spec/spec_helper' +require 'spec_helper' class DomTest include Opal::Connect diff --git a/spec/plugins/store_spec.rb b/spec/plugins/store_spec.rb index e22bc9a..922abb7 100644 --- a/spec/plugins/store_spec.rb +++ b/spec/plugins/store_spec.rb @@ -1,4 +1,4 @@ -require 'spec/spec_helper' +require 'spec_helper' Opal::Connect.plugin :store diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bfb5611..08e4c7b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,8 @@ unless RUBY_ENGINE == 'opal' file = "#{Dir.pwd}/.connect/connect.js" File.delete(file) if File.exist?(file) - require 'rspec' require './app/config/boot' end +require 'rspec' require 'opal/connect/rspec' From c5f15807674bcec1a9b448d01904f45d082d3a1f Mon Sep 17 00:00:00 2001 From: cj Date: Thu, 14 Jul 2016 20:31:24 -0500 Subject: [PATCH 06/14] clean up --- lib/opal/connect.rb | 23 ++++++++++------------- lib/opal/connect/plugins/rspec.rb | 2 +- lib/opal/connect/rake_task.rb | 2 -- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/opal/connect.rb b/lib/opal/connect.rb index 85e37ca..e2cd37d 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -61,7 +61,7 @@ def setup(&block) unless block_given? unless RUBY_ENGINE == 'opal' - write_entry_file(self) + write_entry_file opal_code = Opal::Connect::STUBS.map { |stub| "require '#{stub}'" }.join(";") opal_stubs = Opal::Config.stubbed_files.to_a @@ -268,30 +268,27 @@ def build(code, stubs = false) builder(stubs).build_str(code, '(inline)').to_s end - def javascript(klass = false, method = '', *opts) - klass = Class.new { include Opal::Connect }.new unless klass - + def javascript(klass, method = false, *opts) js = [] options[:javascript].uniq.each { |block| js << klass.instance_exec(&block) } %{ - #{js.join(';')} + #{js.join("\n")} Document.ready? do klass = #{klass.class.name}.new - unless "#{method}".empty? - klass.__send__(:#{method}, *JSON.parse(Base64.decode64('#{Base64.encode64 opts.to_json}'))) if klass.respond_to?(:#{method}) - end + #{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) + def write_entry_file + klass = Class.new { include Opal::Connect } path = "#{Dir.pwd}/.connect" - files = Connect.included_files.dup.uniq.map { |file| "require '#{file}'" }.join(';') + 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[:entry].uniq.each { |block| entry << klass.instance_exec(&block) } @@ -304,8 +301,8 @@ def write_entry_file(klass = false, method = false, *options) # make sure we include the default plugins with connect Opal::Connect.options[:plugins].each { |plug| Opal::Connect.plugin plug } Opal::Connect.setup - #{entry.join(';')} - #{javascript(klass, method, *options)} + #{entry.join("\n")} + #{javascript(klass)} } FileUtils.mkdir_p(path) diff --git a/lib/opal/connect/plugins/rspec.rb b/lib/opal/connect/plugins/rspec.rb index 70805ec..aadc929 100644 --- a/lib/opal/connect/plugins/rspec.rb +++ b/lib/opal/connect/plugins/rspec.rb @@ -53,7 +53,7 @@ def run_rspec Dir["#{options[:folder]}/**/*_spec.rb"].each { |file| load file } Opal::Connect.setup - Opal::Connect.write_entry_file(self) + Opal::Connect.write_entry_file tmpl = html! { html do diff --git a/lib/opal/connect/rake_task.rb b/lib/opal/connect/rake_task.rb index bc60047..1007c07 100644 --- a/lib/opal/connect/rake_task.rb +++ b/lib/opal/connect/rake_task.rb @@ -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", From 548c563779f47202de15061352be860f8c69c70a Mon Sep 17 00:00:00 2001 From: cj Date: Thu, 14 Jul 2016 23:11:13 -0500 Subject: [PATCH 07/14] cleanup --- app/config/boot.rb | 2 -- app/config/connect.rb | 3 +- lib/opal/connect.rb | 58 +++++++++++++++++-------------- lib/opal/connect/plugins/dom.rb | 2 +- lib/opal/connect/plugins/rspec.rb | 6 ++-- lib/opal/connect/rspec.rb | 1 - 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/app/config/boot.rb b/app/config/boot.rb index 66e6d4a..b3e948f 100644 --- a/app/config/boot.rb +++ b/app/config/boot.rb @@ -16,5 +16,3 @@ class App < Roda; end 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 65582ac..a5875f9 100644 --- a/app/config/connect.rb +++ b/app/config/connect.rb @@ -1,8 +1,7 @@ require 'opal-connect' Opal::Connect.setup do - options[:plugins] = [ :server, :html, :dom, :events ] - options[:livereload] = true + plugins :server, :html, :dom, :events plugin :scope, App.new('') plugin :rspec diff --git a/lib/opal/connect.rb b/lib/opal/connect.rb index e2cd37d..c4b56f9 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -1,10 +1,15 @@ require 'opal' require 'base64' +require 'json' require "opal/connect/version" if RUBY_ENGINE == 'opal' require 'opal/connect/puts' else + unless File.exists?('.connect/entry.rb') + FileUtils.mkdir_p('.connect') + File.write '.connect/entry.rb', '' + end require 'oga' require 'opal/patch' Opal.append_path File.expand_path('../..', __FILE__).untaint @@ -29,6 +34,8 @@ def options javascript: [], requires: [], setup_blocks: {}, + setup: false, + run: false ) end @@ -45,36 +52,27 @@ 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 + options[:setup_blocks].each do |klass, blocks| + blocks.each { |b| klass.instance_exec(&b) } end - unless block_given? - unless RUBY_ENGINE == 'opal' - write_entry_file + options[:setup_blocks] = ConnectCache.new - 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) + if !options[:run] && RUBY_ENGINE != 'opal' + write_entry_file - Connect.files.each { |name, (code, version, stubs)| write_file name, code, version, stubs } - end + 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) - options[:setup_blocks].each do |klass, blocks| - blocks.each { |b| klass.instance_exec(&b) } - end + Connect.files.each { |name, (code, version, stubs)| write_file name, code, version, stubs } - options[:setup_blocks] = {} + options[:run] = true end end @@ -197,6 +195,8 @@ module Base module InstanceMethods if RUBY_ENGINE != 'opal' def render(method, *options, &block) + Connect.run + %{#{public_send(method, *options, &block)} } end @@ -252,6 +252,10 @@ def plugin(plugin, *args, &block) nil end + def plugins(*plugins) + plugins.each { |plugin| Connect.plugin plugin} + end + if RUBY_ENGINE != 'opal' def included_files @_included_files ||= [] @@ -282,7 +286,9 @@ def javascript(klass, method = false, *opts) } end - def write_entry_file + def write_entry_file(entry_name = 'entry') + Opal.append_path Dir.pwd + klass = Class.new { include Opal::Connect } path = "#{Dir.pwd}/.connect" files = Connect.included_files.dup.uniq.map { |file| "require '#{file}'" }.join("\n") @@ -300,13 +306,13 @@ def write_entry_file #{files} # make sure we include the default plugins with connect Opal::Connect.options[:plugins].each { |plug| Opal::Connect.plugin plug } - Opal::Connect.setup + 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/dom.rb b/lib/opal/connect/plugins/dom.rb index b6fd225..7068af4 100644 --- a/lib/opal/connect/plugins/dom.rb +++ b/lib/opal/connect/plugins/dom.rb @@ -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) diff --git a/lib/opal/connect/plugins/rspec.rb b/lib/opal/connect/plugins/rspec.rb index aadc929..322a9e5 100644 --- a/lib/opal/connect/plugins/rspec.rb +++ b/lib/opal/connect/plugins/rspec.rb @@ -52,14 +52,14 @@ def run_rspec } Dir["#{options[:folder]}/**/*_spec.rb"].each { |file| load file } - Opal::Connect.setup - Opal::Connect.write_entry_file + Opal::Connect.run + Opal::Connect.write_entry_file 'rspec_entry' tmpl = html! { html do head { meta charset: 'utf-8' } body do - div connect_include_tag + div javascript_include_tag 'rspec_entry' div javascript_include_tag 'rspec.js' div javascript_include_tag 'rspec_tests' end diff --git a/lib/opal/connect/rspec.rb b/lib/opal/connect/rspec.rb index 3f7498b..2729266 100644 --- a/lib/opal/connect/rspec.rb +++ b/lib/opal/connect/rspec.rb @@ -35,7 +35,6 @@ def rspec_dom RSpec.configure do |config| config.extend RSpecHelpers config.include RSpecHelpers - config.before(:suite) { Opal::Connect.setup } if RUBY_ENGINE == 'opal' config.before { rspec_dom.find('body').append html! { iframe id: 'rspec-iframe' } } From 19e3fac94a91cdf11b8fcbc180c9c4e1a0bd6785 Mon Sep 17 00:00:00 2001 From: cj Date: Fri, 15 Jul 2016 02:55:52 -0500 Subject: [PATCH 08/14] working --- app/components/example.rb | 2 +- app/config/connect.rb | 2 +- lib/opal/connect.rb | 51 +++++++++++++++---------------- lib/opal/connect/plugins/rspec.rb | 3 +- spec/plugins/dom_spec.rb | 2 +- spec/plugins/store_spec.rb | 8 ++--- 6 files changed, 31 insertions(+), 37 deletions(-) diff --git a/app/components/example.rb b/app/components/example.rb index 1d7f835..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 diff --git a/app/config/connect.rb b/app/config/connect.rb index a5875f9..803487d 100644 --- a/app/config/connect.rb +++ b/app/config/connect.rb @@ -1,7 +1,7 @@ require 'opal-connect' Opal::Connect.setup do - plugins :server, :html, :dom, :events + plugins :server, :html, :dom, :events, :store plugin :scope, App.new('') plugin :rspec diff --git a/lib/opal/connect.rb b/lib/opal/connect.rb index c4b56f9..0eaa3a3 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -34,6 +34,7 @@ def options javascript: [], requires: [], setup_blocks: {}, + classes: [], setup: false, run: false ) @@ -56,24 +57,27 @@ def setup(&block) instance_exec(&block) if block_given? end - def run - options[:setup_blocks].each do |klass, blocks| - blocks.each { |b| klass.instance_exec(&b) } - end - - options[:setup_blocks] = ConnectCache.new - - if !options[:run] && RUBY_ENGINE != 'opal' - write_entry_file + def run(entry_file = 'entry') + unless RUBY_ENGINE == 'opal' + Opal.append_path Dir.pwd - opal_code = Opal::Connect::STUBS.map { |stub| "require '#{stub}'" }.join(";") - opal_stubs = Opal::Config.stubbed_files.to_a + 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) Connect.files.each { |name, (code, version, stubs)| write_file name, code, version, stubs } - options[:run] = true + write_entry_file(entry_file) unless RUBY_ENGINE == 'opal' + end + + 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 + + write_entry_file(entry_file) unless RUBY_ENGINE == 'opal' end def included(klass) @@ -82,6 +86,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 } @@ -195,8 +201,10 @@ module Base module InstanceMethods if RUBY_ENGINE != 'opal' def render(method, *options, &block) - Connect.run - + unless Connect.options[:run] + Connect.options[:run] = true + Connect.run + end %{#{public_send(method, *options, &block)} } end @@ -208,15 +216,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. @@ -241,14 +240,14 @@ 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) end - plugin.configure(Connect, *args, &block) if !included && plugin.respond_to?(:configure) - nil end @@ -287,8 +286,6 @@ def javascript(klass, method = false, *opts) end def write_entry_file(entry_name = 'entry') - Opal.append_path Dir.pwd - klass = Class.new { include Opal::Connect } path = "#{Dir.pwd}/.connect" files = Connect.included_files.dup.uniq.map { |file| "require '#{file}'" }.join("\n") diff --git a/lib/opal/connect/plugins/rspec.rb b/lib/opal/connect/plugins/rspec.rb index 322a9e5..c0e1f25 100644 --- a/lib/opal/connect/plugins/rspec.rb +++ b/lib/opal/connect/plugins/rspec.rb @@ -52,8 +52,7 @@ def run_rspec } Dir["#{options[:folder]}/**/*_spec.rb"].each { |file| load file } - Opal::Connect.run - Opal::Connect.write_entry_file 'rspec_entry' + Opal::Connect.run 'rspec_entry' tmpl = html! { html do diff --git a/spec/plugins/dom_spec.rb b/spec/plugins/dom_spec.rb index dba6d44..49a99b9 100644 --- a/spec/plugins/dom_spec.rb +++ b/spec/plugins/dom_spec.rb @@ -3,7 +3,7 @@ class DomTest include Opal::Connect - setup do + def self.setup dom.set! html! { html do head do diff --git a/spec/plugins/store_spec.rb b/spec/plugins/store_spec.rb index 922abb7..2c5608b 100644 --- a/spec/plugins/store_spec.rb +++ b/spec/plugins/store_spec.rb @@ -5,19 +5,17 @@ class StoreFooTest include Opal::Connect - setup do + def self.setup store.set :foo, 'bar' + store.set :server_foo, 'bar' unless RUBY_ENGINE == 'opal' end - setup do - store.set :server_foo, 'bar' - end unless RUBY_ENGINE == 'opal' end class StoreBarTest include Opal::Connect - setup do + def self.setup store.set :foo, 'foo' end end From 19b48e98e06427fcecf88419f5b325229f6411c5 Mon Sep 17 00:00:00 2001 From: cj Date: Fri, 15 Jul 2016 14:08:09 -0500 Subject: [PATCH 09/14] updates --- app/config/boot.rb | 2 +- lib/opal/connect.rb | 31 ++++++++++++++++++------------- lib/opal/connect/rspec.rb | 3 +++ phantom.js | 2 +- spec/plugins/store_spec.rb | 15 ++++++++++++++- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/app/config/boot.rb b/app/config/boot.rb index b3e948f..2c17642 100644 --- a/app/config/boot.rb +++ b/app/config/boot.rb @@ -10,7 +10,7 @@ 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' diff --git a/lib/opal/connect.rb b/lib/opal/connect.rb index 0eaa3a3..7ee6c73 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -6,10 +6,6 @@ if RUBY_ENGINE == 'opal' require 'opal/connect/puts' else - unless File.exists?('.connect/entry.rb') - FileUtils.mkdir_p('.connect') - File.write '.connect/entry.rb', '' - end require 'oga' require 'opal/patch' Opal.append_path File.expand_path('../..', __FILE__).untaint @@ -33,7 +29,6 @@ def options entry: [], javascript: [], requires: [], - setup_blocks: {}, classes: [], setup: false, run: false @@ -60,16 +55,26 @@ def setup(&block) def run(entry_file = 'entry') unless RUBY_ENGINE == 'opal' Opal.append_path Dir.pwd + write_files + write_entry_file(entry_file) + end - 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) + run_setups - Connect.files.each { |name, (code, version, stubs)| write_file name, code, version, stubs } + write_entry_file(entry_file) unless RUBY_ENGINE == 'opal' + end - write_entry_file(entry_file) unless RUBY_ENGINE == 'opal' - 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) + 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 @@ -77,7 +82,7 @@ def run(entry_file = 'entry') klass.setup if klass.respond_to?(:setup) end - write_entry_file(entry_file) unless RUBY_ENGINE == 'opal' + Connect.options[:classes] = [] end def included(klass) @@ -203,7 +208,7 @@ module InstanceMethods def render(method, *options, &block) unless Connect.options[:run] Connect.options[:run] = true - Connect.run + File.exist?('.connect/entry.rb') ? Connect.run_setups : Connect.run end %{#{public_send(method, *options, &block)} } diff --git a/lib/opal/connect/rspec.rb b/lib/opal/connect/rspec.rb index 2729266..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,6 +37,7 @@ def rspec_dom RSpec.configure do |config| config.extend RSpecHelpers config.include RSpecHelpers + 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/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/plugins/store_spec.rb b/spec/plugins/store_spec.rb index 2c5608b..5c8bd41 100644 --- a/spec/plugins/store_spec.rb +++ b/spec/plugins/store_spec.rb @@ -6,8 +6,14 @@ class StoreFooTest include Opal::Connect def self.setup + store[:array] ||= [] + store[:array] << 'one' store.set :foo, 'bar' - store.set :server_foo, 'bar' unless RUBY_ENGINE == 'opal' + if RUBY_ENGINE == 'opal' + store[:client_foo] = 'bar' + else + store.set :server_foo, 'bar' + end end end @@ -23,6 +29,13 @@ def self.setup describe 'plugin :store' do context 'class' do it 'should get foo and return bar' do + if RUBY_ENGINE == 'opal' + expect(StoreFooTest.store[:client_foo]).to eq 'bar' + expect(StoreFooTest.store[:array].length).to eq 2 + else + expect(StoreFooTest.store[:client_foo]).to eq nil + expect(StoreFooTest.store[:array].length).to eq 1 + end expect(StoreFooTest.store.get :foo).to eq 'bar' expect(StoreFooTest.store.get :server_foo).to eq 'bar' expect(StoreBarTest.store.get :foo).not_to eq 'bar' From 8eb225a01d2fec10d9378ddddcc8a3c5547879d2 Mon Sep 17 00:00:00 2001 From: cj Date: Mon, 18 Jul 2016 10:02:05 -0500 Subject: [PATCH 10/14] updates --- README.md | 139 ++++++++++++++++++++++++++++++++++++++------ lib/opal/connect.rb | 4 +- 2 files changed, 124 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 7d4569a..3ea3666 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,136 @@ -# Opal::Connect +# **Opal-Connect** *(OC)* +##### *Makes 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. + +## Core Methods + +- [class#options] +- [class#client_options] +- [class#stubbed_files] +- [class#files] +- [class#setup] +- [class#run] +- [class#write_files] +- [class#run_setup] +- [class#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 [class#plugin]). | +| plugins_loaded | [] | Array | Stores an array of all plugins loaded. | +| entry | [] | Array | Stores all the entry blocks to be run when [class#write_entry_file] is called. +| javascript | [] | Array | Stores all the javascript blocks to execute when [class#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 [class#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 [class#write_files], [class#write_entry_file] and [class#run_setups]. - $ gem install opal-connect +## Opal::Connect#write_files +##### This will write the opal file and any files contained in [class#files] to the `.connect/` folder. -## Usage +## Opal::Connect#run_setups +##### This will run all the `class#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 +[class#options]: #class-options +[class#client_options]: #class-client_options +[class#plugin]: #class-plugin +[class#write_entry_file]: #class-write_entry_file +[class#render]: #class-render +[class#stubbed_files]: #class-stubbed_files +[class#files]: #class-files +[class#setup]: #class-setup +[class#run]: #class-run +[class#write_files]: #class-write_files +[class#run_setup]: #class-run_setup +[class#included]: #class-included diff --git a/lib/opal/connect.rb b/lib/opal/connect.rb index 7ee6c73..773c03a 100644 --- a/lib/opal/connect.rb +++ b/lib/opal/connect.rb @@ -30,8 +30,8 @@ def options javascript: [], requires: [], classes: [], - setup: false, - run: false + stubbed_files: [], + run: false ) end From b9c73cd4d9b6bff0d75ae7a6ffdfb229bbd73d94 Mon Sep 17 00:00:00 2001 From: cj Date: Mon, 18 Jul 2016 10:10:07 -0500 Subject: [PATCH 11/14] updated wording --- README.md | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 3ea3666..fa3fde0 100644 --- a/README.md +++ b/README.md @@ -59,43 +59,43 @@ class App < Roda That's it! Start the server, visit the root URL and you'll see `OpalComponent` printed out. -## Core Methods - -- [class#options] -- [class#client_options] -- [class#stubbed_files] -- [class#files] -- [class#setup] -- [class#run] -- [class#write_files] -- [class#run_setup] -- [class#included] - -## Opal::Connect#options +## `Opal::Connect` class methods + +- [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 [class#plugin]). | +| 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 [class#write_entry_file] is called. -| javascript | [] | Array | Stores all the javascript blocks to execute when [class#render] is called. | +| 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 [class#stubbed_files] | +| stubbed_files | [] | Array | List of files to be stubbed by [Opal] using [Opal::Connect#stubbed_files] | -## Opal::Connect#client_options +## Opal::Connect#client_options ##### Server options that are passed to the client options. -## Opal::Connect#stubbed_files +## Opal::Connect#stubbed_files ##### Files that are stubbed in opal and will not get passed. -## Opal::Connect#files +## 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 +## Opal::Connect#setup ##### If you find yourself using a lot of plugins/options, you can use this setup block. ```ruby @@ -105,16 +105,16 @@ Opal::Connect.setup do end ``` -## Opal::Connect#run -##### This will trigger [class#write_files], [class#write_entry_file] and [class#run_setups]. +## Opal::Connect#run +##### This will trigger [Opal::Connect#write_files], [Opal::Connect#write_entry_file] and [Opal::Connect#run_setups]. -## Opal::Connect#write_files -##### This will write the opal file and any files contained in [class#files] to the `.connect/` folder. +## Opal::Connect#write_files +##### This will write the opal file and any files contained in [Opal::Connect#files] to the `.connect/` folder. -## Opal::Connect#run_setups -##### This will run all the `class#setup` blocks defined in your classes. **Not to be confused with `Opal::Connect#setup`** +## 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`** -## Opal::Connect#included +## 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 [Opal]: https://github.com/opal/opal "Opal" @@ -122,15 +122,15 @@ end [Sprockets]: https://github.com/rails/sprockets "Sprockets" [OC]: https://github.com/cj/opal-connect "OC" -[class#options]: #class-options -[class#client_options]: #class-client_options -[class#plugin]: #class-plugin -[class#write_entry_file]: #class-write_entry_file -[class#render]: #class-render -[class#stubbed_files]: #class-stubbed_files -[class#files]: #class-files -[class#setup]: #class-setup -[class#run]: #class-run -[class#write_files]: #class-write_files -[class#run_setup]: #class-run_setup -[class#included]: #class-included +[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 From 657f88155bcfb114eedc29ba3936b7d88c9e724a Mon Sep 17 00:00:00 2001 From: cj Date: Mon, 18 Jul 2016 10:18:02 -0500 Subject: [PATCH 12/14] updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa3fde0..45d2014 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ class App < Roda That's it! Start the server, visit the root URL and you'll see `OpalComponent` printed out. -## `Opal::Connect` class methods +## `Opal::Connect#class` - [Opal::Connect#options] - [Opal::Connect#client_options] From d4d1ddef178f1399554c8c1829537dd72fd23ac3 Mon Sep 17 00:00:00 2001 From: cj Date: Mon, 18 Jul 2016 13:20:25 -0500 Subject: [PATCH 13/14] update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 45d2014..28923cc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # **Opal-Connect** *(OC)* -##### *Makes working with opal even easier!* +##### *Make working with opal even easier!* ## Installation: From 76b459495b7ecc84c4e2ae401282a13bea832483 Mon Sep 17 00:00:00 2001 From: cj Date: Mon, 18 Jul 2016 13:45:57 -0500 Subject: [PATCH 14/14] fixed multiline --- lib/opal/connect/plugins/dom.rb | 2 +- lib/opal/connect/plugins/rspec.rb | 2 +- spec/plugins/dom_spec.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/opal/connect/plugins/dom.rb b/lib/opal/connect/plugins/dom.rb index 7068af4..044087e 100644 --- a/lib/opal/connect/plugins/dom.rb +++ b/lib/opal/connect/plugins/dom.rb @@ -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/rspec.rb b/lib/opal/connect/plugins/rspec.rb index c0e1f25..0987c9d 100644 --- a/lib/opal/connect/plugins/rspec.rb +++ b/lib/opal/connect/plugins/rspec.rb @@ -34,11 +34,11 @@ def run_rspec 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.rb", %{ + require 'opal/connect/puts' require 'opal/connect/rspec' RSpec.configure do |config| diff --git a/spec/plugins/dom_spec.rb b/spec/plugins/dom_spec.rb index 0929d14..965fa9c 100644 --- a/spec/plugins/dom_spec.rb +++ b/spec/plugins/dom_spec.rb @@ -79,13 +79,13 @@ def self.setup it 'should allow to use a 1 line html as template' do multiline_li = subject.dom.tmpl(:multiline_html) - multiline_li.find('li').append 'works' + multiline_li.append 'works' dom.find('ul').append multiline_li expect(dom.find('li').length).to eq 2 expect(dom.find('li span').length).to eq 1 inline_li = subject.dom.tmpl(:inline_html) - inline_li.find('li').append 'bug here' + inline_li.append 'bug here' dom.find('ul').append inline_li expect(dom.find('li span').length).to eq 2 end