opal-rails 1.0.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{main.yml → build.yml} +12 -5
  3. data/.gitignore +1 -0
  4. data/Appraisals +8 -17
  5. data/CHANGELOG.md +66 -0
  6. data/Gemfile +7 -4
  7. data/README.md +55 -13
  8. data/app/helpers/opal_helper.rb +6 -5
  9. data/bin/rackup +29 -0
  10. data/bin/rails +8 -0
  11. data/bin/rails-engine +13 -0
  12. data/bin/rails-sandbox +18 -0
  13. data/bin/rake +29 -0
  14. data/bin/rspec +29 -0
  15. data/bin/sandbox +39 -0
  16. data/bin/sandbox-setup +14 -0
  17. data/bin/setup +8 -0
  18. data/gemfiles/rails_6_0_opal_1_0.gemfile +0 -3
  19. data/gemfiles/rails_6_0_opal_1_1.gemfile +9 -0
  20. data/gemfiles/rails_6_1_opal_1_0.gemfile +9 -0
  21. data/gemfiles/rails_6_1_opal_1_1.gemfile +9 -0
  22. data/lib/assets/javascripts/opal_ujs.js.rb +1 -4
  23. data/lib/generators/opal/assets/assets_generator.rb +7 -0
  24. data/lib/{rails/generators → generators}/opal/assets/templates/javascript.js.rb +0 -0
  25. data/lib/generators/opal/install/USAGE +8 -0
  26. data/lib/generators/opal/install/install_generator.rb +14 -0
  27. data/lib/generators/opal/install/templates/application.js.rb +12 -0
  28. data/lib/generators/opal/install/templates/initializer.rb +22 -0
  29. data/lib/opal/rails.rb +0 -4
  30. data/lib/opal/rails/engine.rb +9 -2
  31. data/lib/opal/rails/template_handler.rb +16 -7
  32. data/lib/opal/rails/version.rb +1 -1
  33. data/opal-rails.gemspec +45 -40
  34. data/spec/helpers/opal_helper_spec.rb +23 -9
  35. data/spec/integration/assigns_spec.rb +58 -2
  36. data/spec/integration/source_map_spec.rb +9 -5
  37. data/test_apps/{application_controller.rb → app/application_controller.rb} +1 -1
  38. data/test_apps/app/assets/config/manifest.js +1 -0
  39. data/test_apps/{assets → app/assets}/javascripts/application.js.rb +1 -0
  40. data/test_apps/{assets → app/assets}/javascripts/bar.rb +0 -0
  41. data/test_apps/{assets → app/assets}/javascripts/foo.js.rb +0 -0
  42. data/test_apps/{assets → app/assets}/javascripts/source_map_example.js.rb +0 -0
  43. data/test_apps/{assets → app/assets}/javascripts/with_assignments.js.rb +0 -0
  44. data/test_apps/rails6.rb +1 -3
  45. metadata +48 -84
  46. data/gemfiles/rails_5_1_opal_1_0.gemfile +0 -12
  47. data/gemfiles/rails_5_1_opal_master.gemfile +0 -11
  48. data/gemfiles/rails_5_2_opal_1_0.gemfile +0 -12
  49. data/gemfiles/rails_5_2_opal_master.gemfile +0 -11
  50. data/gemfiles/rails_6_0_opal_master.gemfile +0 -11
  51. data/lib/rails/generators/opal/assets/assets_generator.rb +0 -12
  52. data/spec/integration/template_spec.rb +0 -9
  53. data/test_apps/rails5.rb +0 -50
data/bin/sandbox ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+
3
+ gem_name="$(ruby -rpathname -e"puts Pathname(ARGV.first).join('../..').expand_path.glob('*.gemspec').first.basename('.gemspec')" -- $0)"
4
+
5
+ # Stay away from the bundler env of the containing extension.
6
+ function unbundled {
7
+ ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- $@
8
+ }
9
+
10
+ rm -rf ./sandbox
11
+ unbundled bundle exec rails new sandbox \
12
+ --skip-bundle \
13
+ --skip-git \
14
+ --skip-keeps \
15
+ --skip-rc \
16
+ --skip-spring \
17
+ --skip-test \
18
+ $@
19
+
20
+ if [ ! -d "sandbox" ]; then
21
+ echo 'sandbox rails application failed'
22
+ exit 1
23
+ fi
24
+
25
+ cd ./sandbox
26
+ cat <<RUBY >> Gemfile
27
+ gem '$gem_name', path: '..'
28
+ RUBY
29
+
30
+ unbundled bundle install --gemfile Gemfile
31
+ unbundled bin/rails webpacker:install
32
+
33
+
34
+ cd .. # Back to the project root.
35
+ bin/sandbox-setup # Run any custom setup.
36
+
37
+ echo
38
+ echo "🚀 Sandbox app successfully created for $gem_name!"
39
+
data/bin/sandbox-setup ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+
5
+ root = Pathname('sandbox')
6
+
7
+ system 'bin/rails g opal:install'
8
+ system 'bin/rails g controller home index -f'
9
+
10
+ root.join('config/routes.rb').write <<~RUBY
11
+ Rails.application.routes.draw do
12
+ root to: "home#index"
13
+ end
14
+ RUBY
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ gem install bundler --conservative
7
+
8
+ bundle update
@@ -2,11 +2,8 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "c_lexer"
6
5
  gem "rails", "~> 6.0.0"
7
6
  gem "opal", "~> 1.0.0"
8
- gem "opal-rspec", git: "https://github.com/opal/opal-rspec.git", branch: :master
9
- gem "opal-jquery", git: "https://github.com/opal/opal-jquery.git", branch: :master
10
7
  gem "opal-sprockets"
11
8
 
12
9
  gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 6.0.0"
6
+ gem "opal", "~> 1.1.0"
7
+ gem "opal-sprockets"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 6.1.0"
6
+ gem "opal", "~> 1.0.0"
7
+ gem "opal-sprockets"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 6.1.0"
6
+ gem "opal", "~> 1.1.0"
7
+ gem "opal-sprockets"
8
+
9
+ gemspec path: "../"
@@ -1,4 +1 @@
1
- require 'jquery'
2
- require 'jquery_ujs'
3
- require 'opal'
4
- require 'opal-jquery'
1
+ warn "`opal_ujs` is deprecated and no longer works"
@@ -0,0 +1,7 @@
1
+ class Opal::AssetsGenerator < ::Rails::Generators::NamedBase
2
+ source_root File.expand_path('templates', __dir__)
3
+
4
+ def copy_opal
5
+ template 'javascript.js.rb', File.join('app/assets/javascripts', class_path, "#{file_name}.js.rb")
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ bin/rails generate install Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,14 @@
1
+ class Opal::InstallGenerator < Rails::Generators::Base
2
+ source_root File.expand_path('templates', __dir__)
3
+
4
+ def configure_sprockets
5
+ append_to_file 'app/assets/config/manifest.js', '//= link_directory ../javascript .js'
6
+ template "application.js.rb", "app/assets/javascript/application.js.rb"
7
+ template "initializer.rb", "config/initializers/opal.rb"
8
+
9
+ # Add the javascript tag to the application head tag
10
+ gsub_file 'app/views/layouts/application.html.erb', %r{(\n *)</head>},
11
+ '\1 <%= javascript_include_tag "application", "data-turbolinks-track": "reload" %>' \
12
+ '\1</head>'
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ require "opal"
2
+
3
+ # Uncomment the following to print out you're hello-world with Opal:
4
+ #
5
+ # puts "hello world!"
6
+ #
7
+ # The following will append a hello-world to your <body> element:
8
+ #
9
+ # require "native"
10
+ # $$[:document].addEventListener :DOMContentLoaded do
11
+ # $$[:document][:body][:innerHTML] += '<h2>Hello World!</h2>'
12
+ # end
@@ -0,0 +1,22 @@
1
+ # Check out the full list of the available configuration options at
2
+ # https://github.com/opal/opal/blob/master/lib/opal/config.rb
3
+
4
+ Rails.application.configure do
5
+ # We suggest keeping the configuration above as default for all environments,
6
+ # disabling some of them might slightly reduce the bundle size or reduce performance
7
+ # by degrading some ruby features.
8
+ config.opal.method_missing_enabled = true
9
+ config.opal.const_missing_enabled = true
10
+ config.opal.arity_check_enabled = true
11
+ config.opal.freezing_stubs_enabled = true
12
+ config.opal.dynamic_require_severity = :ignore
13
+
14
+ # To enable passing assigns from the controller to the opal template handler
15
+ # change the following configuration to one of these values:
16
+ #
17
+ # - true # both locals and instance variables
18
+ # - :locals # only locals
19
+ # - :ivars # only instance variables
20
+ #
21
+ config.opal.assigns_in_templates = false
22
+ end
data/lib/opal/rails.rb CHANGED
@@ -1,9 +1,5 @@
1
1
  require 'opal'
2
- require 'opal-jquery'
3
- require 'opal-activesupport'
4
2
 
5
3
  require 'opal/rails/engine'
6
4
  require 'opal/rails/template_handler'
7
5
  require 'opal/rails/version'
8
-
9
- require 'jquery-rails'
@@ -1,6 +1,5 @@
1
1
  require 'rails'
2
- require 'opal/sprockets/server'
3
- require 'opal/sprockets/processor'
2
+ require 'opal/sprockets'
4
3
 
5
4
  module Opal
6
5
  module Rails
@@ -12,6 +11,14 @@ module Opal
12
11
  config.opal.dynamic_require_severity = :ignore
13
12
  config.opal.assigns_in_templates = true
14
13
 
14
+ def (config.opal).assign_locals_in_templates?
15
+ assigns_in_templates == true || assigns_in_templates == :locals
16
+ end
17
+
18
+ def (config.opal).assign_instance_variables_in_templates?
19
+ assigns_in_templates == true || assigns_in_templates == :ivars
20
+ end
21
+
15
22
  # Cache eager_load_paths now, otherwise the assets dir is added
16
23
  # and its .rb files are eagerly loaded.
17
24
  config.eager_load_paths
@@ -1,22 +1,31 @@
1
+ require 'active_support/json'
2
+
1
3
  module Opal
2
4
  module Rails
3
5
  class TemplateHandler
4
6
 
5
- def self.call(template)
6
- new.call(template)
7
+ def self.call(template, source = template.source)
8
+ new.call(template, source)
7
9
  end
8
10
 
9
- def call(template)
10
- escaped = template.source.gsub(':', '\:')
11
+ def call(template, source = template.source)
12
+ escaped = source.gsub(':', '\:')
11
13
  string = '%q:' + escaped + ':'
12
14
 
13
15
  <<-RUBY
16
+ config = ::Rails.application.config.opal
17
+
14
18
  code = []
15
19
  code << 'Object.new.instance_eval {'
16
- if ::Rails.application.config.opal.assigns_in_templates
17
- code << JSON.parse(local_assigns.to_json).map { |key, val| "\#{key} = \#{val.inspect};" }.join
18
- code << JSON.parse(@_assigns.to_json).map { |key, val| "@\#{key} = \#{val.inspect};" }.join
20
+
21
+ if config.assign_locals_in_templates?
22
+ code << ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(local_assigns)).map { |key, val| "\#{key} = \#{val.inspect};" }.join
19
23
  end
24
+
25
+ if config.assign_instance_variables_in_templates?
26
+ code << ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(@_assigns)).map { |key, val| "@\#{key} = \#{val.inspect};" }.join
27
+ end
28
+
20
29
  code << #{string}
21
30
  code << '}'
22
31
  Opal.compile(code.join("\n"))
@@ -1,5 +1,5 @@
1
1
  module Opal
2
2
  module Rails
3
- VERSION = '1.0.1'
3
+ VERSION = '2.0.1'
4
4
  end
5
5
  end
data/opal-rails.gemspec CHANGED
@@ -1,41 +1,46 @@
1
- # coding: utf-8
2
- $:.push File.expand_path('../lib', __FILE__)
3
- require 'opal/rails/version'
4
-
5
- Gem::Specification.new do |s|
6
- s.name = 'opal-rails'
7
- s.version = Opal::Rails::VERSION
8
- s.authors = ['Elia Schito']
9
- s.email = ['elia@schito.me']
10
- s.homepage = 'https://github.com/opal/opal-rails#readme'
11
- s.summary = %q{Rails bindings for opal JS engine}
12
- s.description = %q{Rails bindings for opal JS engine}
13
- s.license = 'MIT-LICENSE'
14
-
15
- s.rubyforge_project = 'opal-rails'
16
-
17
- s.files = `git ls-files`.split("\n")
18
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.require_paths = ['lib']
21
-
22
- required_ruby_version = '>= 2.3'
23
-
24
- s.add_dependency 'rails', '>= 5.1', '< 6.1'
25
- s.add_dependency 'sprockets-rails', '>= 2.3.3', '< 4.0'
26
- s.add_dependency 'jquery-rails'
27
-
28
- s.add_dependency 'opal', '~> 1.0.0'
29
- s.add_dependency 'opal-jquery', '~> 0.4.4'
30
- s.add_dependency 'opal-sprockets', '~> 0.4.6'
31
- s.add_dependency 'opal-activesupport', '>= 0.0.5'
32
-
33
- s.add_development_dependency 'execjs'
34
- s.add_development_dependency 'launchy'
35
- s.add_development_dependency 'capybara', '~> 3.25'
36
- s.add_development_dependency 'apparition'
37
- s.add_development_dependency 'rspec-rails'
38
- s.add_development_dependency 'appraisal', '~> 2.1'
39
- s.add_development_dependency 'sqlite3'
40
- s.add_development_dependency 'puma'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/opal/rails/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'opal-rails'
7
+ spec.version = Opal::Rails::VERSION
8
+ spec.authors = ['Elia Schito']
9
+ spec.email = ['elia@schito.me']
10
+
11
+ spec.summary = %q{Rails bindings for opal JS engine}
12
+ spec.description = %q{Rails bindings for opal JS engine}
13
+ spec.homepage = 'https://github.com/opal/opal-rails#readme'
14
+ spec.license = 'MIT-LICENSE'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/opal/opal-rails#readme'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/opal/opal-rails/blob/master/CHANGELOG.md'
19
+
20
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5')
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
25
+
26
+ spec.files = files.grep_v(%r{^(test|spec|features)/})
27
+ spec.test_files = files.grep(%r{^(test|spec|features)/})
28
+ spec.bindir = "exe"
29
+ spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency 'rails', '>= 6.0', '< 7'
33
+ spec.add_dependency 'sprockets-rails', '>= 3.0'
34
+
35
+ spec.add_dependency 'opal', '~> 1.0'
36
+ spec.add_dependency 'opal-sprockets', '~> 1.0'
37
+
38
+ spec.add_development_dependency 'execjs'
39
+ spec.add_development_dependency 'launchy'
40
+ spec.add_development_dependency 'capybara', '~> 3.25'
41
+ spec.add_development_dependency 'apparition'
42
+ spec.add_development_dependency 'rspec-rails'
43
+ spec.add_development_dependency 'appraisal', '~> 2.1'
44
+ spec.add_development_dependency 'sqlite3'
45
+ spec.add_development_dependency 'puma'
41
46
  end
@@ -1,7 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe OpalHelper, type: :helper do
4
- # subject(:helper) { double(javascript_include_tag: '<super>').extend described_class }
3
+ # We need the view type because helpers specs are to are too minimalistic
4
+ # and are missing some initialization stuff.
5
+ describe OpalHelper, :js, type: :view do
6
+ let(:helper) { view }
5
7
 
6
8
  describe '#opal_tag' do
7
9
  it 'compiles to js' do
@@ -20,17 +22,29 @@ describe OpalHelper, type: :helper do
20
22
  # sprockets-rails v3 sets Rails.application.assets to nil in production mode
21
23
  allow(Rails.application).to receive(:assets).and_return(nil)
22
24
 
23
- loading_code = %Q{Opal.require("application");}
25
+ loading_code = [
26
+ %<if(window.Opal && Opal.modules["application"]){Opal.loaded(typeof(OpalLoaded) === "undefined" ? [] : OpalLoaded);>,
27
+ %<Opal.require("application");}>,
28
+ ].join("\n")
29
+
24
30
  escaped_loading_code = ERB::Util.h loading_code
31
+ loading_code_in_script_tag = [
32
+ %(<script>), %(//<![CDATA[), loading_code, %(//]]>), %(</script>),
33
+ ].join("\n")
34
+
35
+ expect(helper.javascript_include_tag('application', debug: true)).to include(loading_code_in_script_tag)
36
+ expect(helper.javascript_include_tag('application', debug: true)).not_to include(escaped_loading_code)
25
37
 
26
- expect(helper.javascript_include_tag('application', debug: true)).to include(escaped_loading_code)
27
38
  expect(helper.javascript_include_tag('application', debug: false)).to include(escaped_loading_code)
39
+ expect(helper.javascript_include_tag('application', debug: false)).not_to include(loading_code_in_script_tag)
40
+
28
41
  expect(helper.javascript_include_tag('application', skip_opal_loader: true)).not_to include(escaped_loading_code)
29
- expect(helper.javascript_include_tag('application', skip_opal_loader: true)).not_to include(escaped_loading_code)
42
+ expect(helper.javascript_include_tag('application', skip_opal_loader: false)).to include(loading_code_in_script_tag)
43
+
44
+ expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, debug: true)).to include(loading_code_in_script_tag)
45
+ expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, debug: false)).to include(loading_code_in_script_tag)
30
46
 
31
- expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, debug: true)).to include(loading_code)
32
- expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, debug: false)).to include(loading_code)
33
- expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, skip_opal_loader: true)).not_to include(loading_code)
34
- expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, skip_opal_loader: true)).not_to include(loading_code)
47
+ expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, skip_opal_loader: true)).not_to include(escaped_loading_code)
48
+ expect(helper.javascript_include_tag('application', force_opal_loader_tag: true, skip_opal_loader: true)).to include(loading_code_in_script_tag)
35
49
  end
36
50
  end
@@ -7,7 +7,7 @@ describe 'controller assignments' do
7
7
  Rails.application.config.opal.assigns_in_templates = true
8
8
  end
9
9
 
10
- it 'are in the template' do
10
+ it 'has them in the template' do
11
11
  source = get_source_of '/application/with_assignments.js'
12
12
  assignments = opal_eval(source)
13
13
 
@@ -29,7 +29,7 @@ describe 'controller assignments' do
29
29
  Rails.application.config.opal.assigns_in_templates = false
30
30
  end
31
31
 
32
- it 'are not in the template' do
32
+ it 'has not them in the template' do
33
33
  source = get_source_of '/application/with_assignments.js'
34
34
  assignments = opal_eval(source)
35
35
  {
@@ -45,6 +45,62 @@ describe 'controller assignments' do
45
45
  end
46
46
  end
47
47
 
48
+ context 'when :locals' do
49
+ before do
50
+ Rails.application.config.opal.assigns_in_templates = :locals
51
+ end
52
+
53
+ it 'has only locals in the template' do
54
+ source = get_source_of '/application/with_assignments.js'
55
+ assignments = opal_eval(source)
56
+ {
57
+ :number_var => 1234,
58
+ :string_var => 'hello',
59
+ :array_var => [1,'a'],
60
+ :hash_var => {:a => 1, :b => 2},
61
+ :object_var => {:contents => 'json representation'},
62
+ }.each_pair do |ivar, assignment|
63
+ expect(assignments[ivar]).not_to eq(assignment)
64
+ end
65
+ {
66
+ :local_var => 'i am local',
67
+ }.each_pair do |ivar, assignment|
68
+ expect(assignments[ivar]).to eq(assignment)
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'when :ivars' do
74
+ before do
75
+ Rails.application.config.opal.assigns_in_templates = :ivars
76
+ end
77
+
78
+ it 'has only ivars in the template' do
79
+ source = get_source_of '/application/with_assignments.js'
80
+ assignments = opal_eval(source)
81
+ {
82
+ :number_var => 1234,
83
+ :string_var => 'hello',
84
+ :array_var => [1,'a'],
85
+ :hash_var => {:a => 1, :b => 2},
86
+ :object_var => {:contents => 'json representation'},
87
+ }.each_pair do |ivar, assignment|
88
+ expect(assignments[ivar]).to eq(assignment)
89
+ end
90
+ {
91
+ :local_var => 'i am local',
92
+ }.each_pair do |ivar, assignment|
93
+ expect(assignments[ivar]).not_to eq(assignment)
94
+ end
95
+ end
96
+ end
97
+
98
+ it 'has the correct content type' do
99
+ get '/application/with_assignments.js'
100
+ expect(response).to be_successful
101
+ expect(response.headers['Content-Type']).to eq('text/javascript; charset=utf-8')
102
+ end
103
+
48
104
  def get_source_of path
49
105
  get path
50
106
  response.should be_successful