opal-rails 2.0.3 → 3.0.0

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +39 -4
  3. data/.gitignore +5 -0
  4. data/Appraisals +20 -21
  5. data/CHANGELOG.md +36 -0
  6. data/Gemfile +2 -2
  7. data/PORTING.md +140 -0
  8. data/README.md +151 -88
  9. data/Rakefile +16 -2
  10. data/app/helpers/opal_helper.rb +9 -28
  11. data/bin/sandbox +3 -2
  12. data/bin/sandbox-setup +2 -0
  13. data/gemfiles/{rails_7_0_opal_1_0.gemfile → rails_7_0_opal_1_8.gemfile} +2 -2
  14. data/gemfiles/{rails_7_0_opal_1_3.gemfile → rails_7_0_opal_master.gemfile} +2 -2
  15. data/gemfiles/{rails_6_0_opal_1_0.gemfile → rails_8_0_opal_1_8.gemfile} +3 -3
  16. data/gemfiles/rails_8_0_opal_master.gemfile +10 -0
  17. data/gemfiles/{rails_6_1_opal_1_0.gemfile → rails_8_1_opal_1_8.gemfile} +3 -3
  18. data/gemfiles/rails_8_1_opal_master.gemfile +10 -0
  19. data/lib/generators/opal/assets/assets_generator.rb +11 -1
  20. data/lib/generators/opal/assets/templates/{javascript.js.rb → asset.rb.tt} +3 -6
  21. data/lib/generators/opal/install/install_generator.rb +160 -9
  22. data/lib/generators/opal/install/templates/{application.js.rb → application.rb} +4 -4
  23. data/lib/generators/opal/install/templates/dev.tt +8 -0
  24. data/lib/generators/opal/install/templates/{initializer.rb → initializer.rb.tt} +8 -0
  25. data/lib/opal/rails/builder_runner.rb +126 -0
  26. data/lib/opal/rails/engine.rb +11 -9
  27. data/lib/opal/rails/entrypoints_resolver.rb +81 -0
  28. data/lib/opal/rails/errors.rb +11 -0
  29. data/lib/opal/rails/file_watcher.rb +58 -0
  30. data/lib/opal/rails/haml6_filter.rb +6 -6
  31. data/lib/opal/rails/haml_filter.rb +3 -6
  32. data/lib/opal/rails/legacy_upgrade_warning.rb +95 -0
  33. data/lib/opal/rails/outputs_manifest.rb +82 -0
  34. data/lib/opal/rails/path_setup.rb +28 -0
  35. data/lib/opal/rails/task_hooks.rb +49 -0
  36. data/lib/opal/rails/version.rb +1 -1
  37. data/lib/opal/rails/watch_runner.rb +173 -0
  38. data/lib/opal/rails.rb +7 -0
  39. data/lib/tasks/opal.rake +48 -0
  40. data/opal-rails.gemspec +23 -14
  41. data/spec/end_to_end/full_lifecycle_spec.rb +377 -0
  42. data/spec/helpers/opal_helper_spec.rb +27 -34
  43. data/spec/integration/source_map_spec.rb +6 -8
  44. data/spec/opal/assets_generator_spec.rb +31 -0
  45. data/spec/opal/install_generator_spec.rb +140 -0
  46. data/spec/opal/rails/build_task_spec.rb +116 -0
  47. data/spec/opal/rails/builder_runner_spec.rb +151 -0
  48. data/spec/opal/rails/clobber_task_spec.rb +55 -0
  49. data/spec/opal/rails/entrypoints_resolver_spec.rb +50 -0
  50. data/spec/opal/rails/haml_filter_spec.rb +41 -0
  51. data/spec/opal/rails/legacy_upgrade_warning_spec.rb +94 -0
  52. data/spec/opal/rails/outputs_manifest_spec.rb +44 -0
  53. data/spec/opal/rails/path_setup_spec.rb +71 -0
  54. data/spec/opal/rails/task_hooks_spec.rb +61 -0
  55. data/spec/opal/rails/watch_runner_spec.rb +283 -0
  56. data/spec/opal/rails/watch_task_spec.rb +23 -0
  57. data/spec/spec_helper.rb +16 -7
  58. data/spec/support/browser_support_spec.rb +36 -0
  59. data/spec/support/capybara.rb +61 -0
  60. data/spec/support/reset_assets_cache.rb +9 -1
  61. data/spec/support/test_app.rb +23 -2
  62. data/test_apps/app/application_controller.rb +23 -32
  63. data/test_apps/app/assets/builds/.keep +0 -0
  64. data/test_apps/app/assets/config/manifest.js +3 -1
  65. data/test_apps/app/assets/images/.keep +0 -0
  66. data/test_apps/app/opal/application.rb +5 -0
  67. data/test_apps/app/{assets/javascripts/source_map_example.js.rb → opal/source_map_example.rb} +1 -2
  68. data/test_apps/app/opal/with_assignments.js.rb +8 -0
  69. data/test_apps/rails.rb +19 -5
  70. metadata +196 -50
  71. data/gemfiles/rails_6_0_opal_1_1.gemfile +0 -9
  72. data/gemfiles/rails_6_0_opal_1_3.gemfile +0 -10
  73. data/gemfiles/rails_6_0_opal_1_7.gemfile +0 -10
  74. data/gemfiles/rails_6_1_opal_1_1.gemfile +0 -9
  75. data/gemfiles/rails_6_1_opal_1_3.gemfile +0 -10
  76. data/gemfiles/rails_6_1_opal_1_7.gemfile +0 -10
  77. data/gemfiles/rails_7_0_opal_1_7.gemfile +0 -10
  78. data/lib/opal/rails/haml5_filter.rb +0 -28
  79. data/test_apps/app/assets/javascripts/application.js.rb +0 -7
  80. data/test_apps/app/assets/javascripts/bar.rb +0 -3
  81. data/test_apps/app/assets/javascripts/foo.js.rb +0 -3
@@ -0,0 +1,283 @@
1
+ require 'spec_helper'
2
+ require 'opal/rails/file_watcher'
3
+ require 'opal/rails/watch_runner'
4
+
5
+ RSpec.describe Opal::Rails::WatchRunner do
6
+ FakeBuilderRunner = Struct.new(:results, :calls) do
7
+ def build(entrypoints:)
8
+ calls << entrypoints
9
+ results.shift
10
+ end
11
+ end
12
+
13
+ FakeManifest = Struct.new(:prune_calls, :write_calls) do
14
+ def prune_stale!(outputs)
15
+ prune_calls << outputs
16
+ end
17
+
18
+ def write!(outputs)
19
+ write_calls << outputs
20
+ end
21
+ end
22
+
23
+ class FakeFileWatcher
24
+ class << self
25
+ attr_accessor :instances
26
+ end
27
+
28
+ self.instances = []
29
+
30
+ attr_reader :extra_directories, :files
31
+
32
+ def initialize(files:, extra_directories:, &callback)
33
+ @files = files
34
+ @extra_directories = extra_directories
35
+ @callback = callback
36
+ @started = false
37
+ @stopped = false
38
+ self.class.instances << self
39
+ end
40
+
41
+ def start
42
+ @started = true
43
+ end
44
+
45
+ def stop
46
+ @stopped = true
47
+ end
48
+
49
+ def started?
50
+ @started
51
+ end
52
+
53
+ def stopped?
54
+ @stopped
55
+ end
56
+ end
57
+
58
+ let(:source_path) { Pathname('/tmp/app/opal') }
59
+ let(:append_path) { Pathname('/tmp/app/shared/opal') }
60
+ let(:config) do
61
+ ActiveSupport::OrderedOptions.new.tap do |opal|
62
+ opal.source_path = source_path
63
+ opal.entrypoints_path = source_path
64
+ opal.build_path = Pathname('/tmp/app/assets/builds')
65
+ opal.entrypoints = { 'application' => 'application.rb', 'admin' => 'admin.rb' }
66
+ opal.append_paths = [append_path]
67
+ opal.source_map_enabled = true
68
+ end
69
+ end
70
+ let(:resolver) { instance_double(Opal::Rails::EntrypointsResolver) }
71
+ let(:manifest) { FakeManifest.new([], []) }
72
+ let(:builder_runner) { FakeBuilderRunner.new(results.dup, []) }
73
+ let(:results) do
74
+ [
75
+ {
76
+ outputs: %w[application.js application.js.map admin.js admin.js.map],
77
+ dependencies: {
78
+ 'application' => ['/tmp/app/opal/application.rb', '/tmp/app/opal/shared.rb'],
79
+ 'admin' => ['/tmp/app/opal/admin.rb']
80
+ }
81
+ },
82
+ {
83
+ outputs: %w[application.js application.js.map],
84
+ dependencies: {
85
+ 'application' => ['/tmp/app/opal/application.rb', '/tmp/app/opal/shared.rb']
86
+ }
87
+ },
88
+ {
89
+ outputs: %w[application.js application.js.map admin.js admin.js.map],
90
+ dependencies: {
91
+ 'application' => ['/tmp/app/opal/application.rb', '/tmp/app/opal/shared.rb', '/tmp/app/opal/new_file.rb'],
92
+ 'admin' => ['/tmp/app/opal/admin.rb']
93
+ }
94
+ }
95
+ ]
96
+ end
97
+
98
+ before do
99
+ FakeFileWatcher.instances = []
100
+ allow(resolver).to receive(:resolve).and_return(
101
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' },
102
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' }
103
+ )
104
+ allow(Opal).to receive(:dependent_files).and_return(['/tmp/opal/corelib/runtime.rb'])
105
+ end
106
+
107
+ it 'builds all entrypoints once and starts a watcher' do
108
+ runner = described_class.new(
109
+ config: config,
110
+ resolver: resolver,
111
+ builder_runner: builder_runner,
112
+ manifest: manifest,
113
+ file_watcher_class: FakeFileWatcher
114
+ )
115
+
116
+ runner.start!
117
+
118
+ expect(builder_runner.calls).to eq([
119
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' }
120
+ ])
121
+ expect(manifest.prune_calls).to eq([
122
+ %w[admin.js admin.js.map application.js application.js.map]
123
+ ])
124
+ expect(manifest.write_calls).to eq([
125
+ %w[admin.js admin.js.map application.js application.js.map]
126
+ ])
127
+ expect(FakeFileWatcher.instances.last).to be_started
128
+ expect(FakeFileWatcher.instances.last.extra_directories).to eq([
129
+ source_path.expand_path.to_s,
130
+ append_path.expand_path.to_s
131
+ ])
132
+ end
133
+
134
+ it 'rebuilds only affected entrypoints for known modified files' do
135
+ runner = described_class.new(
136
+ config: config,
137
+ resolver: resolver,
138
+ builder_runner: builder_runner,
139
+ manifest: manifest,
140
+ file_watcher_class: FakeFileWatcher
141
+ )
142
+
143
+ runner.start!
144
+ runner.process_changes(modified: ['/tmp/app/opal/shared.rb'], added: [], removed: [])
145
+
146
+ expect(builder_runner.calls).to eq([
147
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' },
148
+ { 'application' => 'application.rb' }
149
+ ])
150
+ expect(manifest.prune_calls.length).to eq(1)
151
+ expect(manifest.write_calls.last).to eq(%w[admin.js admin.js.map application.js application.js.map])
152
+ end
153
+
154
+ it 'rebuilds all entrypoints when a new file appears' do
155
+ runner = described_class.new(
156
+ config: config,
157
+ resolver: resolver,
158
+ builder_runner: builder_runner,
159
+ manifest: manifest,
160
+ file_watcher_class: FakeFileWatcher
161
+ )
162
+
163
+ runner.start!
164
+ runner.process_changes(modified: [], added: ['/tmp/app/opal/new_file.rb'], removed: [])
165
+
166
+ expect(builder_runner.calls).to eq([
167
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' },
168
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' }
169
+ ])
170
+ expect(manifest.prune_calls.length).to eq(2)
171
+ end
172
+
173
+ it 'watches configured append_paths for rebuild-triggering changes' do
174
+ runner = described_class.new(
175
+ config: config,
176
+ resolver: resolver,
177
+ builder_runner: builder_runner,
178
+ manifest: manifest,
179
+ file_watcher_class: FakeFileWatcher
180
+ )
181
+
182
+ runner.start!
183
+ runner.process_changes(modified: [], added: ['/tmp/app/shared/opal/new_helper.rb'], removed: [])
184
+
185
+ expect(builder_runner.calls).to eq([
186
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' },
187
+ { 'application' => 'application.rb', 'admin' => 'admin.rb' }
188
+ ])
189
+ end
190
+
191
+ it 're-resolves :all entrypoints and watches the dedicated entrypoints path' do
192
+ all_config = ActiveSupport::OrderedOptions.new.tap do |opal|
193
+ opal.source_path = Pathname('/tmp/app/opal')
194
+ opal.entrypoints_path = Pathname('/tmp/app/opal/entrypoints')
195
+ opal.build_path = Pathname('/tmp/app/assets/builds')
196
+ opal.entrypoints = :all
197
+ opal.append_paths = []
198
+ opal.source_map_enabled = true
199
+ end
200
+
201
+ all_resolver = instance_double(Opal::Rails::EntrypointsResolver)
202
+ allow(all_resolver).to receive(:resolve).and_return(
203
+ { 'application' => 'application.rb' },
204
+ { 'application' => 'application.rb', 'dashboard' => 'dashboard.rb' }
205
+ )
206
+
207
+ all_builder_runner = FakeBuilderRunner.new(
208
+ [
209
+ {
210
+ outputs: %w[application.js application.js.map],
211
+ dependencies: {
212
+ 'application' => ['/tmp/app/opal/entrypoints/application.rb', '/tmp/app/opal/shared.rb']
213
+ }
214
+ },
215
+ {
216
+ outputs: %w[application.js application.js.map dashboard.js dashboard.js.map],
217
+ dependencies: {
218
+ 'application' => ['/tmp/app/opal/entrypoints/application.rb', '/tmp/app/opal/shared.rb'],
219
+ 'dashboard' => ['/tmp/app/opal/entrypoints/dashboard.rb']
220
+ }
221
+ }
222
+ ],
223
+ []
224
+ )
225
+
226
+ all_manifest = FakeManifest.new([], [])
227
+
228
+ runner = described_class.new(
229
+ config: all_config,
230
+ resolver: all_resolver,
231
+ builder_runner: all_builder_runner,
232
+ manifest: all_manifest,
233
+ file_watcher_class: FakeFileWatcher
234
+ )
235
+
236
+ runner.start!
237
+ expect(FakeFileWatcher.instances.last.extra_directories).to eq([
238
+ '/tmp/app/opal',
239
+ '/tmp/app/opal/entrypoints'
240
+ ])
241
+
242
+ runner.process_changes(modified: [], added: ['/tmp/app/opal/entrypoints/dashboard.rb'], removed: [])
243
+
244
+ expect(all_builder_runner.calls).to eq([
245
+ { 'application' => 'application.rb' },
246
+ { 'application' => 'application.rb', 'dashboard' => 'dashboard.rb' }
247
+ ])
248
+ expect(all_manifest.prune_calls).to eq([
249
+ %w[application.js application.js.map],
250
+ %w[application.js application.js.map dashboard.js dashboard.js.map]
251
+ ])
252
+ expect(all_manifest.write_calls.last).to eq(%w[application.js application.js.map dashboard.js dashboard.js.map])
253
+ end
254
+
255
+ it 'reports build errors without crashing the watcher' do
256
+ error_output = StringIO.new
257
+
258
+ failing_builder = FakeBuilderRunner.new(
259
+ [
260
+ results.first.dup,
261
+ nil # will not be reached; the second build raises
262
+ ],
263
+ []
264
+ )
265
+ allow(failing_builder).to receive(:build).and_call_original
266
+ allow(failing_builder).to receive(:build).with(entrypoints: { 'application' => 'application.rb' })
267
+ .and_raise(StandardError, 'syntax error in application.rb')
268
+
269
+ runner = described_class.new(
270
+ config: config,
271
+ resolver: resolver,
272
+ builder_runner: failing_builder,
273
+ manifest: manifest,
274
+ file_watcher_class: FakeFileWatcher,
275
+ error_output: error_output
276
+ )
277
+
278
+ runner.start!
279
+ expect { runner.process_changes(modified: ['/tmp/app/opal/shared.rb'], added: [], removed: []) }.not_to raise_error
280
+ expect(error_output.string).to include('Opal build error: syntax error in application.rb')
281
+ expect(FakeFileWatcher.instances.last).to be_started
282
+ end
283
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'rake'
3
+ require 'opal/rails/file_watcher'
4
+ require 'opal/rails/watch_runner'
5
+
6
+ RSpec.describe 'opal:watch task' do
7
+ before do
8
+ Rake.application = Rake::Application.new
9
+ Rails.application.load_tasks
10
+ end
11
+
12
+ after do
13
+ Rake.application = nil
14
+ end
15
+
16
+ it 'delegates to the watch runner' do
17
+ runner = instance_double(Opal::Rails::WatchRunner, watch: true)
18
+ expect(Opal::Rails::WatchRunner).to receive(:new).with(config: Rails.application.config.opal).at_least(:once).and_return(runner)
19
+
20
+ Rake::Task['opal:watch'].reenable
21
+ Rake::Task['opal:watch'].invoke
22
+ end
23
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- root_dir = File.expand_path('../../', __FILE__)
1
+ File.expand_path('..', __dir__)
2
2
 
3
3
  require 'support/test_app'
4
4
  require 'rspec/rails'
@@ -38,7 +38,7 @@ RSpec.configure do |config|
38
38
  # ...rather than:
39
39
  # # => "be bigger than 2"
40
40
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
41
- expectations.syntax = [:expect, :should]
41
+ expectations.syntax = %i[expect should]
42
42
  end
43
43
 
44
44
  # rspec-mocks config goes here. You can use an alternate test double
@@ -98,19 +98,28 @@ RSpec.configure do |config|
98
98
  # Configure Capybara
99
99
  Capybara.javascript_driver = :cuprite
100
100
  Capybara.register_driver(:cuprite) do |app|
101
- Capybara::Cuprite::Driver.new(app,
101
+ driver_options = {
102
102
  window_size: [1200, 800],
103
- browser_options: { 'no-sandbox': nil },
103
+ browser_options: {
104
+ 'disable-dev-shm-usage': nil,
105
+ 'no-sandbox': nil
106
+ },
104
107
  inspector: ENV['INSPECTOR'],
105
108
  headless: !ENV['NO_HEADLESS'],
109
+ process_timeout: 30,
106
110
  timeout: 20,
107
- url_blacklist: [],
108
- )
111
+ url_blacklist: []
112
+ }
113
+
114
+ browser_path = BrowserSupport.path
115
+ driver_options[:browser_path] = browser_path if browser_path
116
+
117
+ Capybara::Cuprite::Driver.new(app, **driver_options)
109
118
  end
110
119
 
111
120
  Capybara.register_server :puma do |app, port, host|
112
121
  require 'rack/handler/puma'
113
- Rack::Handler::Puma.run(app, Host: host, Port: port, Threads: "0:4", Silent: true)
122
+ Rack::Handler::Puma.run(app, Host: host, Port: port, Threads: '0:4', Silent: true)
114
123
  end
115
124
 
116
125
  Capybara.default_max_wait_time = 5
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe BrowserSupport do
4
+ describe '.path' do
5
+ it 'prefers an executable BROWSER_PATH override' do
6
+ allow(ENV).to receive(:[]).and_call_original
7
+ allow(ENV).to receive(:[]).with('BROWSER_PATH').and_return('/custom/browser')
8
+ allow(File).to receive(:executable?).and_call_original
9
+ allow(File).to receive(:executable?).with('/custom/browser').and_return(true)
10
+
11
+ expect(described_class.path).to eq('/custom/browser')
12
+ end
13
+
14
+ it 'falls back to known absolute browser paths' do
15
+ stub_const('BrowserSupport::ABSOLUTE_BROWSER_CANDIDATES', ['/custom/headless_shell'])
16
+ allow(ENV).to receive(:[]).and_call_original
17
+ allow(ENV).to receive(:[]).with('BROWSER_PATH').and_return(nil)
18
+ allow(ENV).to receive(:fetch).and_call_original
19
+ allow(ENV).to receive(:fetch).with('PATH', '').and_return('')
20
+ allow(File).to receive(:executable?).and_call_original
21
+ allow(File).to receive(:executable?).with('/custom/headless_shell').and_return(true)
22
+
23
+ expect(described_class.path).to eq('/custom/headless_shell')
24
+ end
25
+ end
26
+
27
+ describe '.available?' do
28
+ it 'reflects whether a browser path was found' do
29
+ allow(described_class).to receive(:path).and_return('/custom/browser')
30
+ expect(described_class.available?).to eq(true)
31
+
32
+ allow(described_class).to receive(:path).and_return(nil)
33
+ expect(described_class.available?).to eq(false)
34
+ end
35
+ end
36
+ end
@@ -1,6 +1,50 @@
1
1
  require 'capybara/rspec'
2
2
  require 'capybara/cuprite'
3
3
 
4
+ module BrowserSupport
5
+ BROWSER_CANDIDATES = %w[
6
+ chromium
7
+ chromium-browser
8
+ google-chrome
9
+ google-chrome-stable
10
+ chrome
11
+ headless_shell
12
+ ].freeze
13
+
14
+ ABSOLUTE_BROWSER_CANDIDATES = %w[
15
+ /usr/lib64/chromium-browser/headless_shell
16
+ /usr/lib/chromium-browser/headless_shell
17
+ /usr/lib/chromium/headless_shell
18
+ ].freeze
19
+
20
+ module_function
21
+
22
+ def path
23
+ env_path = ENV['BROWSER_PATH']
24
+ return env_path if env_path && File.executable?(env_path)
25
+
26
+ discovered_path = ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).find do |directory|
27
+ BROWSER_CANDIDATES.any? do |name|
28
+ File.executable?(File.join(directory, name))
29
+ end
30
+ end
31
+
32
+ return File.join(discovered_path, discovered_executable_name(discovered_path)) if discovered_path
33
+
34
+ ABSOLUTE_BROWSER_CANDIDATES.find { |candidate| File.executable?(candidate) }
35
+ end
36
+
37
+ def available?
38
+ !path.nil?
39
+ end
40
+
41
+ def discovered_executable_name(directory)
42
+ BROWSER_CANDIDATES.find do |name|
43
+ File.executable?(File.join(directory, name))
44
+ end
45
+ end
46
+ end
47
+
4
48
  module OpalHelper
5
49
  def compile_opal(code)
6
50
  Opal.compile(code, requireable: false)
@@ -32,6 +76,7 @@ module OpalHelper
32
76
 
33
77
  until evaluate_script('document.readyState') == 'complete'
34
78
  raise "reached max time (#{expire_in}s)" if timer.expired?
79
+
35
80
  sleep 0.01
36
81
  end
37
82
  end
@@ -41,6 +86,7 @@ module OpalHelper
41
86
 
42
87
  until evaluate_script('!!(window.Opal && window.Opal.gvars.ready)')
43
88
  raise "reached max time (#{expire_in}s)" if timer.expired?
89
+
44
90
  sleep 0.01
45
91
  end
46
92
  end
@@ -52,4 +98,19 @@ end
52
98
 
53
99
  RSpec.configure do |config|
54
100
  config.include OpalHelper
101
+
102
+ browser_path = BrowserSupport.path
103
+
104
+ config.before(:each, js: true) do
105
+ skip 'Browser-dependent spec skipped because no browser binary is available' unless BrowserSupport.available?
106
+ end
107
+
108
+ config.before(:suite) do
109
+ ENV['BROWSER_PATH'] ||= browser_path if browser_path
110
+ end
111
+
112
+ config.append_after(:each, js: true) do
113
+ session = Capybara.current_session
114
+ session.driver.restart if session.driver.is_a?(Capybara::Cuprite::Driver)
115
+ end
55
116
  end
@@ -1,5 +1,13 @@
1
1
  require 'fileutils'
2
2
 
3
3
  RSpec.configure do |config|
4
- config.before(:suite) { FileUtils.rmtree(Rails.root.join('tmp/cache/assets').to_s) }
4
+ config.before(:suite) do
5
+ FileUtils.rmtree(Rails.root.join('tmp/cache/assets').to_s)
6
+ TestAppAssets.clobber!
7
+ TestAppAssets.build!
8
+ end
9
+
10
+ config.after(:suite) do
11
+ TestAppAssets.clobber!
12
+ end
5
13
  end
@@ -1,5 +1,26 @@
1
+ require 'logger'
1
2
  require 'rails'
2
- ENV["RAILS_ENV"] = "test"
3
+ ENV['RAILS_ENV'] = 'test'
3
4
  ENV['DATABASE_URL'] = 'sqlite3::memory:'
4
- require_relative "../../test_apps/rails"
5
+ require_relative '../../test_apps/rails'
5
6
 
7
+ module TestAppAssets
8
+ module_function
9
+
10
+ def build!
11
+ config = Rails.application.config.opal
12
+ resolved_entrypoints = Opal::Rails::EntrypointsResolver.new(
13
+ entrypoints_path: config.entrypoints_path,
14
+ entrypoints: config.entrypoints
15
+ ).resolve
16
+
17
+ result = Opal::Rails::BuilderRunner.new(config: config).build(entrypoints: resolved_entrypoints)
18
+ manifest = Opal::Rails::OutputsManifest.new(build_path: config.build_path)
19
+ manifest.prune_stale!(result[:outputs])
20
+ manifest.write!(result[:outputs])
21
+ end
22
+
23
+ def clobber!
24
+ Opal::Rails::OutputsManifest.new(build_path: Rails.application.config.opal.build_path).clobber!
25
+ end
26
+ end
@@ -1,61 +1,52 @@
1
- LAYOUT = <<-HTML
2
- <!DOCTYPE html>
3
- <html>
4
- <head><%= javascript_include_tag "application" %></head>
5
- <body><%= yield %></body>
6
- </html>
1
+ LAYOUT = <<~HTML
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head><%= ActionController::Base.helpers.javascript_include_tag "application" %></head>
5
+ <body><%= yield %></body>
6
+ </html>
7
7
  HTML
8
8
 
9
- INDEX = <<-HTML
10
- <script type="text/ruby">
11
- puts 'hello from a script tag!'
12
- </script>
9
+ INDEX = <<~HTML
10
+ <script type="text/ruby">
11
+ puts 'hello from a script tag!'
12
+ </script>
13
13
  HTML
14
14
 
15
15
  HAML = <<~HAML
16
- :opal
17
- $haml_filter = :working
16
+ :opal
17
+ $haml_filter = :working
18
18
  HAML
19
19
 
20
- WITH_ASSIGNMENTS = File.read "#{__dir__}/assets/javascripts/with_assignments.js.rb"
21
-
22
- require_relative '../../app/helpers/opal_helper'
20
+ WITH_ASSIGNMENTS = File.read "#{__dir__}/opal/with_assignments.js.rb"
23
21
 
24
22
  class ApplicationController < ActionController::Base
25
23
  include Rails.application.routes.url_helpers
26
- helper OpalHelper
27
24
  layout 'application'
28
25
  self.view_paths = [ActionView::FixtureResolver.new(
29
- 'layouts/application.html.erb' => LAYOUT,
30
- 'application/index.html.erb' => INDEX,
31
- 'application/haml_filter.html.haml' => HAML,
32
- 'application/with_assignments.js.opal' => WITH_ASSIGNMENTS,
26
+ 'layouts/application.html.erb' => LAYOUT,
27
+ 'application/index.html.erb' => INDEX,
28
+ 'application/haml_filter.html.haml' => HAML,
29
+ 'application/with_assignments.js.opal' => WITH_ASSIGNMENTS
33
30
  )]
34
31
 
35
- def index
36
- end
32
+ def index; end
37
33
 
38
34
  def with_assignments
39
35
  object = Object.new
40
- def object.as_json options = {}
41
- {contents: 'json representation'}
36
+ def object.as_json(_options = {})
37
+ { contents: 'json representation' }
42
38
  end
43
39
 
44
40
  @number_var = 1234
45
41
  @string_var = 'hello'
46
- @array_var = [1,'a']
47
- @hash_var = {a: 1, b: 2}
42
+ @array_var = [1, 'a']
43
+ @hash_var = { a: 1, b: 2 }
48
44
  @object_var = object
49
45
 
50
46
  render type: :js, locals: { local_var: 'i am local' }
51
47
  end
52
48
 
53
- def haml_filter
54
- end
49
+ def haml_filter; end
55
50
  end
56
51
 
57
52
  Rails.logger = Logger.new(STDOUT) if $DEBUG
58
-
59
- require 'opal/sprockets/version'
60
- p rails: Rails.version, opal: Opal::VERSION, ruby: RUBY_VERSION, opal_sprockets: Opal::Sprockets::VERSION
61
-
File without changes
@@ -1 +1,3 @@
1
- //= link_directory ../javascripts .js
1
+ //= link_tree ../images
2
+ //= link_directory ../builds .js
3
+ //= link_directory ../builds .map
File without changes
@@ -0,0 +1,5 @@
1
+ # backtick_javascript: true
2
+
3
+ require 'opal'
4
+
5
+ `window.opal_loaded = true`
@@ -1,4 +1,3 @@
1
1
  class SourceMapExample
2
- def works?
3
- end
2
+ def works?; end
4
3
  end
@@ -0,0 +1,8 @@
1
+ {
2
+ number_var: @number_var,
3
+ string_var: @string_var,
4
+ array_var: @array_var,
5
+ hash_var: @hash_var,
6
+ object_var: @object_var,
7
+ local_var: defined?(local_var) ? local_var : nil
8
+ }.to_json