opal_stimulus 0.1.4 → 0.1.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb159569bc40b9490b5433925d2b07e0a4cbd05d8c5f8c1ad86547efedd38361
4
- data.tar.gz: 2d06de0ea3b604943301fd118de40c41b010378d7bf505878b0a99c1af51cffe
3
+ metadata.gz: f328bd466f89d10419369095e006b923c4a389c58fa14abd7c57ae2c57fb804d
4
+ data.tar.gz: 7b6a16a7adc502cdedcdad7e0e71f152652022a42d5a674fc033f6f13e514660
5
5
  SHA512:
6
- metadata.gz: d543dccccdb4c2fa6c0a1422681ac5181547e4f28e00f0fa7085015f8abefeaa37a96839bf9001335bd1fb2ff90ed3a91404eb515fb0e907e969324824914e34
7
- data.tar.gz: 0c8822447330dbeef2dd40bd43bba1110dd458e5269675a9f6c08ca96c4177a5b2caa77dd307c3ece0665f260008111a3bab31a52bbfdaf9588c565fdbec4cfc
6
+ metadata.gz: 775c06738928c9c1584d0515064cf7e5984ff1259cb7def3c56cd9bc34942a7da50a39a761400d9f80a824a178d3333887fedfcfec4274adfa261a3819614d59
7
+ data.tar.gz: f3da082b6361dc0281e743a7ded76c953504c044abe2fc5b59febbd2d68e65138bb4eea4ef2acaab08f8a3a3073c655c3e325c0084f15bce58bd8d23f30d0b2e
data/.rspec-opal ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/CHANGELOG.md CHANGED
@@ -19,10 +19,16 @@
19
19
 
20
20
  ## [0.1.4] - 2025-08-03
21
21
 
22
- Fix Targets
23
- Fix Outlets
24
- Fix Values
25
- Fix `element`
26
- Fix Classes
27
- Updare readme
28
- Add `document` and `window` private methods
22
+ - Fix Targets
23
+ - Fix Outlets
24
+ - Fix Values
25
+ - Fix `element`
26
+ - Fix Classes
27
+ - Add `document` and `window` private methods
28
+
29
+ ## [0.1.5] - 2025-08-14
30
+
31
+ - Add `StimulusController` unit and system tests
32
+ - Reduce installed new lines for Rails
33
+ - Skip importmap in production
34
+ - Add `bin/rails generate opal_stimulus <CONTROLLER_NAME>`, usage: `bin/rails g opal_stimulus -h`
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  **Opal Stimulus** is a Stimulus wrapper made with Opal (a source-to-source Ruby to JavaScript compiler) that allows you to write Stimulus controllers in Ruby instead of JavaScript (It works only with Rails).
4
4
 
5
+ [![Ruby](https://github.com/josephschito/opal_stimulus/actions/workflows/main.yml/badge.svg)](https://github.com/josephschito/opal_stimulus/actions/workflows/main.yml)
6
+
5
7
  ## Installation
6
8
 
7
9
  Add this line to your Gemfile:
@@ -23,6 +25,13 @@ Start application:
23
25
  bin/dev
24
26
  ```
25
27
 
28
+ Create a new controller:
29
+
30
+ ```bash
31
+ bin/rails opal_stimulus hello
32
+ ```
33
+ This command will create `app/opal/controllers/hello_controller.rb`
34
+
26
35
  ## Basic Example
27
36
 
28
37
  Here's a Hello World example with OpalStimulus. Compare with the [original JavaScript example](https://stimulus.hotwired.dev/#:~:text=%2F%2F%20hello_controller.js%0Aimport%20%7B%20Controller%20%7D%20from%20%22stimulus%22%0A%0Aexport%20default%20class%20extends%20Controller%20%7B%0A%20%20static%20targets%20%3D%20%5B%20%22name%22%2C%20%22output%22%20%5D%0A%0A%20%20greet()%20%7B%0A%20%20%20%20this.outputTarget.textContent%20%3D%0A%20%20%20%20%20%20%60Hello%2C%20%24%7Bthis.nameTarget.value%7D!%60%0A%20%20%7D%0A%7D):
@@ -31,8 +40,6 @@ Here's a Hello World example with OpalStimulus. Compare with the [original JavaS
31
40
 
32
41
  ```ruby
33
42
  # app/opal/controllers/hello_controller.rb
34
- # new controllers will be automatically added to app/opal/controllers_requires.rb
35
- # (ordered files load is not supported yet)
36
43
  class HelloController < StimulusController
37
44
  self.targets = ["name", "output"]
38
45
 
@@ -178,6 +185,9 @@ class DocumentController < StimulusController
178
185
  end
179
186
  ```
180
187
 
188
+ ## Run tests
189
+ `bundle exec rake`
190
+
181
191
  ## Contributing
182
192
 
183
193
  Bug reports and pull requests are welcome on GitHub at https://github.com/josephschito/opal_stimulus.
data/Rakefile CHANGED
@@ -9,4 +9,15 @@ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
- task default: %i[test rubocop]
12
+ require 'opal/rspec/rake_task'
13
+ require "opal"
14
+
15
+ Opal.use_gem("opal_proxy")
16
+ Opal.append_path File.expand_path('../lib', __FILE__)
17
+ Opal.append_path File.expand_path('../shared_fixtures', __FILE__)
18
+
19
+ Opal::RSpec::RakeTask.new("rspec-opal") do |server, task|
20
+ task.runner = :chrome
21
+ end
22
+
23
+ task default: %i[test rspec-opal]
@@ -0,0 +1,14 @@
1
+ Description:
2
+ ============
3
+ Generates a new Opal Stimulus controller at the passed path.
4
+
5
+ Examples:
6
+ =========
7
+ bin/rails generate opal_stimulus chat
8
+
9
+ creates: app/opal/controllers/chat_controller.rb
10
+
11
+
12
+ bin/rails generate stimulus nested/chat
13
+
14
+ creates: app/opal/controllers/nested/chat_controller.rb
@@ -0,0 +1,9 @@
1
+ require "rails/generators/named_base"
2
+
3
+ class OpalStimulusGenerator < Rails::Generators::NamedBase
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def create_controller_file
7
+ template "controller.rb.tt", File.join("app/opal/controllers", class_path, "#{file_name}_controller.rb")
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # Connects to data-controller="<%= file_name.dasherize %>"
2
+ <% constants = class_name.split("::") -%>
3
+ <% modules = constants[0..-2] -%>
4
+ <% indentation = " " * (modules.length) -%>
5
+ <% modules.each do |constant| -%>
6
+ module <%= constant %>
7
+ <% end -%>
8
+ <%= indentation %>class <%= constants.last %>Controller < StimulusController
9
+ <%= indentation %> def connect
10
+ <%= indentation %> end
11
+ <%= indentation %>end
12
+ <% modules.each.with_index do |constant, i| -%>
13
+ <%= " " * i %>end
14
+ <% end -%>
@@ -1,2 +1,2 @@
1
1
  web: bin/rails server
2
- opal: bin/opal --watch
2
+ opal: bin/rails opal_stimulus:watch
@@ -1,9 +1,4 @@
1
1
  require "opal_stimulus/stimulus_controller"
2
- require "controllers_requires"
2
+ require_tree "controllers"
3
3
 
4
- StimulusController.subclasses.each do |controller|
5
- controller.define_method(:dummy) { }
6
-
7
- return if `Stimulus.controllers`.include?(`#{controller.stimulus_name}`)
8
- `Stimulus.register(#{controller.stimulus_name}, #{controller.stimulus_controller})`
9
- end
4
+ StimulusController.register_all!
@@ -13,16 +13,9 @@ if File.exist? APPLICATION_LAYOUT_PATH
13
13
  ERB
14
14
  end
15
15
 
16
- insert_into_file APPLICATION_LAYOUT_PATH, after: "<body>\n" do
17
- say "Adding `my-opal` to the application layout", :green
18
- <<-ERB
19
- <span data-controller="my-opal"></span>
20
- ERB
21
- end
22
-
23
16
  say "Creating Opal Stimulus files", :green
24
17
  if Rails.root.join("Procfile.dev").exist?
25
- append_to_file "Procfile.dev", "opal: bin/opal --watch\n"
18
+ append_to_file "Procfile.dev", "opal: bin/rails opal_stimulus:watch\n"
26
19
  else
27
20
  say "Add default Procfile.dev"
28
21
  copy_file "#{__dir__}/Procfile.dev", "Procfile.dev"
@@ -44,13 +37,9 @@ window.Controller = Controller;
44
37
  empty_directory APPLICATION_OPAL_STIMULUS_BIN_PATH
45
38
  empty_directory APPLICATION_OPAL_STIMULUS_PATH
46
39
  empty_directory "#{APPLICATION_OPAL_STIMULUS_PATH}/controllers"
47
- empty_directory "#{APPLICATION_OPAL_STIMULUS_PATH}/app/assets/builds"
40
+ create_file "#{APPLICATION_OPAL_STIMULUS_PATH}/controllers/.keep"
48
41
  create_file "app/assets/builds/.keep"
49
- copy_file "#{__dir__}/opal", "#{APPLICATION_OPAL_STIMULUS_BIN_PATH}/opal"
50
- FileUtils.chmod("+x", "#{APPLICATION_OPAL_STIMULUS_BIN_PATH}/opal")
51
42
  copy_file "#{__dir__}/dev", "#{APPLICATION_OPAL_STIMULUS_BIN_PATH}/dev"
52
43
  FileUtils.chmod("+x", "#{APPLICATION_OPAL_STIMULUS_BIN_PATH}/dev")
53
44
  copy_file "#{__dir__}/application.rb", "#{APPLICATION_OPAL_STIMULUS_PATH}/application.rb"
54
- copy_file "#{__dir__}/controllers_requires.rb", "#{APPLICATION_OPAL_STIMULUS_PATH}/controllers_requires.rb"
55
- copy_file "#{__dir__}/controllers/my_opal_controller.rb", "#{APPLICATION_OPAL_STIMULUS_PATH}/controllers/my_opal_controller.rb"
56
45
  end
@@ -5,8 +5,6 @@ require "native"
5
5
  require "js/proxy"
6
6
 
7
7
  class StimulusController < `Controller`
8
- include Native::Wrapper
9
-
10
8
  DEFAULT_METHODS = %i[initialize connect disconnect dispatch]
11
9
  DEFAULT_GETTERS = %i[element]
12
10
 
@@ -44,6 +42,22 @@ class StimulusController < `Controller`
44
42
  }
45
43
  end
46
44
 
45
+ def self.to_ruby_name(name)
46
+ name
47
+ .to_s
48
+ .gsub(/([A-Z]+)/) { "_#{$1.downcase}" }
49
+ .sub(/^_/, '')
50
+ end
51
+
52
+ def self.register_all!
53
+ subclasses.each do |controller|
54
+ controller.define_method(:dummy) {}
55
+
56
+ return if `application.controllers`.include?(`#{controller.stimulus_name}`)
57
+ `application.register(#{controller.stimulus_name}, #{controller.stimulus_controller})`
58
+ end
59
+ end
60
+
47
61
  def self.targets=(targets = [])
48
62
  `#{self.stimulus_controller}.targets = targets`
49
63
 
@@ -150,11 +164,11 @@ class StimulusController < `Controller`
150
164
  `#{self.stimulus_controller}.values = #{js_values.to_n}`
151
165
 
152
166
  define_method(name + "_value") do
153
- `return this[#{name + "Value"}]`
167
+ Native(`this[#{name + "Value"}]`)
154
168
  end
155
169
 
156
170
  define_method(name + "_value=") do |value|
157
- `this[#{name + "Value"}]= #{value.to_n}`
171
+ Native(`this[#{name + "Value"}]= #{value}`)
158
172
  end
159
173
 
160
174
  define_method("has_#{name}") do
@@ -165,6 +179,10 @@ class StimulusController < `Controller`
165
179
  camel_case_changed = "#{name}ValueChanged"
166
180
  %x{
167
181
  #{self.stimulus_controller}.prototype[#{camel_case_changed}] = function(value, previousValue) {
182
+ if (#{type == :object}) {
183
+ value = JSON.stringify(value)
184
+ previousValue = JSON.stringify(previousValue)
185
+ }
168
186
  if (this['$respond_to?'] && this['$respond_to?'](#{snake_case_changed})) {
169
187
  return this['$' + #{snake_case_changed}](value, previousValue);
170
188
  }
@@ -198,15 +216,6 @@ class StimulusController < `Controller`
198
216
  JS::Proxy.new(`this.element`)
199
217
  end
200
218
 
201
- private
202
-
203
- def self.to_ruby_name(name)
204
- name
205
- .to_s
206
- .gsub(/([A-Z]+)/) { "_#{$1.downcase}" }
207
- .sub(/^_/, '')
208
- end
209
-
210
219
  def window
211
220
  @window ||= JS::Proxy.new($$.window)
212
221
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpalStimulus
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -0,0 +1,53 @@
1
+ require "bundler/setup"
2
+ require "listen"
3
+ require "opal"
4
+
5
+ namespace :opal_stimulus do
6
+ def prepare_paths
7
+ [ "opal_proxy", "opal_stimulus" ].each do |gem_name|
8
+ Opal.use_gem(gem_name) rescue Opal.append_path(File.expand_path("lib", Bundler.rubygems.find_name(gem_name).first.full_gem_path))
9
+ end
10
+
11
+ Opal.append_path(Rails.root.join("app/opal"))
12
+ end
13
+
14
+ def compile
15
+ puts "🔨 Compiling Opal..."
16
+
17
+ builder = Opal::Builder.new
18
+ result = builder.build("application")
19
+ output_path = Rails.root.join("app/assets/builds/opal.js")
20
+ code = result.to_s
21
+
22
+ if Rails.env.development?
23
+ code += "//# sourceMappingURL=/assets/opal.js.map"
24
+ sourcemap_path = "#{output_path}.map"
25
+ source_map_json = result.source_map.to_json
26
+ File.write(sourcemap_path, source_map_json)
27
+ end
28
+
29
+ File.write(output_path, code)
30
+
31
+ puts "✅ Compiled to #{output_path}"
32
+ end
33
+
34
+ desc "Build Opal Stimulus controllers"
35
+ task build: [:environment] do
36
+ prepare_paths
37
+ compile
38
+ end
39
+
40
+ desc "Watch and build Opal Stimulus controllers"
41
+ task watch: [:environment] do
42
+ prepare_paths
43
+
44
+ compile
45
+
46
+ listen = Listen.to(Rails.root.join("app/opal")) { compile }
47
+
48
+ puts "👀 Watching app/opal for changes..."
49
+ listen.start
50
+ Signal.trap("INT") { listen.stop }
51
+ sleep
52
+ end
53
+ end
@@ -1,6 +1,6 @@
1
1
  namespace :opal_stimulus do
2
2
  desc "Install Opal Stimulus into the app"
3
- task :install do
3
+ task install: [:environment] do
4
4
  system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/install_opal_stimulus.rb", __dir__)}"
5
5
  end
6
6
  end