opal-rails 1.0.1 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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