stimulus_reflex 3.3.0.pre5 → 3.4.0.pre2

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.

Potentially problematic release.


This version of stimulus_reflex might be problematic. Click here for more details.

Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +93 -6
  3. data/Gemfile.lock +67 -66
  4. data/README.md +5 -2
  5. data/{lib → app/channels}/stimulus_reflex/channel.rb +17 -7
  6. data/lib/generators/stimulus_reflex/config_generator.rb +14 -0
  7. data/lib/generators/{stimulus_reflex_generator.rb → stimulus_reflex/stimulus_reflex_generator.rb} +0 -0
  8. data/lib/generators/{templates → stimulus_reflex/templates}/app/javascript/controllers/%file_name%_controller.js.tt +0 -0
  9. data/lib/generators/{templates → stimulus_reflex/templates}/app/javascript/controllers/application_controller.js.tt +0 -0
  10. data/lib/generators/{templates → stimulus_reflex/templates}/app/reflexes/%file_name%_reflex.rb.tt +0 -0
  11. data/lib/generators/{templates → stimulus_reflex/templates}/app/reflexes/application_reflex.rb.tt +0 -0
  12. data/lib/generators/stimulus_reflex/templates/config/initializers/stimulus_reflex.rb +9 -0
  13. data/lib/stimulus_reflex.rb +4 -64
  14. data/lib/stimulus_reflex/broadcasters/broadcaster.rb +3 -7
  15. data/lib/stimulus_reflex/broadcasters/page_broadcaster.rb +2 -2
  16. data/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb +2 -2
  17. data/lib/stimulus_reflex/configuration.rb +24 -0
  18. data/lib/stimulus_reflex/element.rb +8 -0
  19. data/lib/stimulus_reflex/reflex.rb +21 -18
  20. data/lib/stimulus_reflex/sanity_checker.rb +98 -0
  21. data/lib/stimulus_reflex/version.rb +1 -1
  22. data/lib/tasks/stimulus_reflex/install.rake +12 -8
  23. data/package.json +63 -0
  24. data/stimulus_reflex.gemspec +40 -0
  25. data/tags +98 -0
  26. data/test/generators/stimulus_reflex_generator_test.rb +3 -2
  27. data/test/tmp/app/reflexes/application_reflex.rb +12 -0
  28. data/test/tmp/app/reflexes/user_reflex.rb +33 -0
  29. data/yarn.lock +6261 -0
  30. metadata +39 -27
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module StimulusReflex
6
+ class ConfigGenerator < Rails::Generators::Base
7
+ desc "Creates an StimulusReflex configuration file in config/initializers"
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def copy_config_file
11
+ copy_file "config/initializers/stimulus_reflex.rb"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ StimulusReflex.configure do |config|
4
+ # Enable/disable whether startup should be aborted when the sanity checks fail
5
+ # config.exit_on_failed_sanity_checks = true
6
+
7
+ # Override the parent class that the StimulusReflex ActionCable channel inherits from
8
+ # config.parent_channel = "ApplicationCable::Channel"
9
+ end
@@ -9,79 +9,19 @@ require "action_cable"
9
9
  require "nokogiri"
10
10
  require "cable_ready"
11
11
  require "stimulus_reflex/version"
12
+ require "stimulus_reflex/configuration"
12
13
  require "stimulus_reflex/reflex"
13
14
  require "stimulus_reflex/element"
14
- require "stimulus_reflex/channel"
15
+ require "stimulus_reflex/sanity_checker"
15
16
  require "stimulus_reflex/broadcasters/broadcaster"
16
17
  require "stimulus_reflex/broadcasters/nothing_broadcaster"
17
18
  require "stimulus_reflex/broadcasters/page_broadcaster"
18
19
  require "stimulus_reflex/broadcasters/selector_broadcaster"
19
- require "generators/stimulus_reflex_generator"
20
20
 
21
21
  module StimulusReflex
22
22
  class Engine < Rails::Engine
23
- NODE_VERSION_FORMAT = /(\d\.\d\.\d.*):/
24
- JSON_VERSION_FORMAT = /(\d\.\d\.\d.*)\"/
25
-
26
- initializer "stimulus_reflex.verify_caching_enabled" do
27
- unless caching_enabled?
28
- puts <<~WARN
29
- Stimulus Reflex requires caching to be enabled. Caching allows the session to be modified during ActionCable requests.
30
- To enable caching in development, run:
31
-
32
- rails dev:cache
33
- WARN
34
- exit
35
- end
36
- end
37
-
38
- initializer "stimulus_reflex.verify_npm_package_version" do
39
- unless node_version_matches?
40
- puts <<~WARN
41
- The Stimulus Reflex javascript package version (#{node_package_version}) does not match the Rubygem version (#{gem_version}).
42
- To update the Stimulus Reflex node module:
43
-
44
- yarn upgrade stimulus_reflex@#{gem_version}
45
- WARN
46
- exit
47
- end
48
- end
49
-
50
- private
51
-
52
- def caching_enabled?
53
- Rails.application.config.action_controller.perform_caching &&
54
- Rails.application.config.cache_store != :null_store
55
- end
56
-
57
- def node_version_matches?
58
- node_package_version == gem_version
59
- end
60
-
61
- def gem_version
62
- StimulusReflex::VERSION.gsub(".pre", "-pre")
63
- end
64
-
65
- def node_package_version
66
- match = File.foreach(yarn_lock_path).grep(/^stimulus_reflex/)
67
- return match.first[NODE_VERSION_FORMAT, 1] if match.present?
68
-
69
- match = File.foreach(yarn_link_path).grep(/version/)
70
- return match.first[JSON_VERSION_FORMAT, 1] if match.present?
71
-
72
- puts <<~WARN
73
- Can't locate the stimulus_reflex NPM package.
74
- Either add it to your package.json as a dependency or use "yarn link stimulus_reflex" if you are doing development.
75
- WARN
76
- exit
77
- end
78
-
79
- def yarn_lock_path
80
- Rails.root.join("yarn.lock")
81
- end
82
-
83
- def yarn_link_path
84
- Rails.root.join("node_modules", "stimulus_reflex", "package.json")
23
+ initializer "stimulus_reflex.sanity_check" do
24
+ SanityChecker.check!
85
25
  end
86
26
  end
87
27
  end
@@ -24,22 +24,18 @@ module StimulusReflex
24
24
  false
25
25
  end
26
26
 
27
- def enqueue_message(subject:, body: nil, data: {})
27
+ def broadcast_message(subject:, body: nil, data: {}, error: nil)
28
28
  logger.error "\e[31m#{body}\e[0m" if subject == "error"
29
29
  cable_ready[stream_name].dispatch_event(
30
30
  name: "stimulus-reflex:server-message",
31
31
  detail: {
32
32
  reflexId: data["reflexId"],
33
33
  stimulus_reflex: data.merge(
34
- broadcaster: to_sym,
35
- server_message: {subject: subject, body: body}
34
+ morph: to_sym,
35
+ server_message: {subject: subject, body: error&.to_s}
36
36
  )
37
37
  }
38
38
  )
39
- end
40
-
41
- def broadcast_message(subject:, body: nil, data: {})
42
- enqueue_message subject: subject, body: body, data: data
43
39
  cable_ready.broadcast
44
40
  end
45
41
 
@@ -3,7 +3,7 @@
3
3
  module StimulusReflex
4
4
  class PageBroadcaster < Broadcaster
5
5
  def broadcast(selectors, data)
6
- reflex.controller.process reflex.url_params[:action]
6
+ reflex.controller.process reflex.params[:action]
7
7
  page_html = reflex.controller.response.body
8
8
 
9
9
  return unless page_html.present?
@@ -18,7 +18,7 @@ module StimulusReflex
18
18
  children_only: true,
19
19
  permanent_attribute_name: permanent_attribute_name,
20
20
  stimulus_reflex: data.merge({
21
- broadast_type: to_sym
21
+ morph: to_sym
22
22
  })
23
23
  )
24
24
  end
@@ -17,7 +17,7 @@ module StimulusReflex
17
17
  children_only: true,
18
18
  permanent_attribute_name: permanent_attribute_name,
19
19
  stimulus_reflex: data.merge({
20
- broadast_type: to_sym
20
+ morph: to_sym
21
21
  })
22
22
  )
23
23
  else
@@ -25,7 +25,7 @@ module StimulusReflex
25
25
  selector: selector,
26
26
  html: fragment.to_html,
27
27
  stimulus_reflex: data.merge({
28
- broadast_type: to_sym
28
+ morph: to_sym
29
29
  })
30
30
  )
31
31
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusReflex
4
+ class << self
5
+ def configure
6
+ yield configuration
7
+ end
8
+
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ alias_method :config, :configuration
14
+ end
15
+
16
+ class Configuration
17
+ attr_accessor :exit_on_failed_sanity_checks, :parent_channel
18
+
19
+ def initialize
20
+ @exit_on_failed_sanity_checks = true
21
+ @parent_channel = "ApplicationCable::Channel"
22
+ end
23
+ end
24
+ end
@@ -11,6 +11,14 @@ class StimulusReflex::Element < OpenStruct
11
11
  @data_attributes.transform_keys! { |key| key.delete_prefix "data-" }
12
12
  end
13
13
 
14
+ def signed
15
+ @signed ||= ->(accessor) { GlobalID::Locator.locate_signed(dataset[accessor]) }
16
+ end
17
+
18
+ def unsigned
19
+ @unsigned ||= ->(accessor) { GlobalID::Locator.locate(dataset[accessor]) }
20
+ end
21
+
14
22
  def dataset
15
23
  @dataset ||= OpenStruct.new(data_attributes.merge(data_attributes.transform_keys(&:underscore)))
16
24
  end
@@ -45,8 +45,10 @@ class StimulusReflex::Reflex
45
45
 
46
46
  attr_reader :channel, :url, :element, :selectors, :method_name, :broadcaster, :permanent_attribute_name
47
47
 
48
+ alias_method :action_name, :method_name # for compatibility with controller libraries like Pundit that expect an action name
49
+
48
50
  delegate :connection, :stream_name, to: :channel
49
- delegate :session, to: :request
51
+ delegate :flash, :session, to: :request
50
52
  delegate :broadcast, :broadcast_message, to: :broadcaster
51
53
 
52
54
  def initialize(channel, url: nil, element: nil, selectors: [], method_name: nil, permanent_attribute_name: nil, params: {})
@@ -66,21 +68,26 @@ class StimulusReflex::Reflex
66
68
  uri = URI.parse(url)
67
69
  path = ActionDispatch::Journey::Router::Utils.normalize_path(uri.path)
68
70
  query_hash = Rack::Utils.parse_nested_query(uri.query)
69
- req = ActionDispatch::Request.new(
70
- connection.env.merge(
71
- Rack::MockRequest.env_for(uri.to_s).merge(
72
- "rack.request.query_hash" => query_hash,
73
- "rack.request.query_string" => uri.query,
74
- "ORIGINAL_SCRIPT_NAME" => "",
75
- "ORIGINAL_FULLPATH" => path,
76
- Rack::SCRIPT_NAME => "",
77
- Rack::PATH_INFO => path,
78
- Rack::REQUEST_PATH => path,
79
- Rack::QUERY_STRING => uri.query
80
- )
81
- )
71
+ mock_env = Rack::MockRequest.env_for(uri.to_s)
72
+
73
+ mock_env.merge!(
74
+ "rack.request.query_hash" => query_hash,
75
+ "rack.request.query_string" => uri.query,
76
+ "ORIGINAL_SCRIPT_NAME" => "",
77
+ "ORIGINAL_FULLPATH" => path,
78
+ Rack::SCRIPT_NAME => "",
79
+ Rack::PATH_INFO => path,
80
+ Rack::REQUEST_PATH => path,
81
+ Rack::QUERY_STRING => uri.query
82
82
  )
83
+
84
+ env = connection.env.merge(mock_env)
85
+ req = ActionDispatch::Request.new(env)
86
+
83
87
  path_params = Rails.application.routes.recognize_path_with_request(req, url, req.env[:extras] || {})
88
+ path_params[:controller] = path_params[:controller].force_encoding("UTF-8")
89
+ path_params[:action] = path_params[:action].force_encoding("UTF-8")
90
+
84
91
  req.env.merge(ActionDispatch::Http::Parameters::PARAMETERS_KEY => path_params)
85
92
  req.env["action_dispatch.request.parameters"] = req.parameters.merge(@params)
86
93
  req.tap { |r| r.session.send :load! }
@@ -112,10 +119,6 @@ class StimulusReflex::Reflex
112
119
  end
113
120
  end
114
121
 
115
- def url_params
116
- @url_params ||= Rails.application.routes.recognize_path_with_request(request, request.path, request.env[:extras] || {})
117
- end
118
-
119
122
  def process(name, *args)
120
123
  reflex_invoked = false
121
124
  result = run_callbacks(:process) {
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StimulusReflex::SanityChecker
4
+ JSON_VERSION_FORMAT = /(\d+\.\d+\.\d+.*)"/
5
+
6
+ def self.check!
7
+ instance = new
8
+ instance.check_caching_enabled
9
+ instance.check_javascript_package_version
10
+ end
11
+
12
+ def check_caching_enabled
13
+ unless caching_enabled?
14
+ warn_and_exit <<~WARN
15
+ Stimulus Reflex requires caching to be enabled. Caching allows the session to be modified during ActionCable requests.
16
+ To enable caching in development, run:
17
+ rails dev:cache
18
+ WARN
19
+ end
20
+
21
+ unless not_null_store?
22
+ warn_and_exit <<~WARN
23
+ Stimulus Reflex requires caching to be enabled. Caching allows the session to be modified during ActionCable requests.
24
+ But your config.cache_store is set to :null_store, so it won't work.
25
+ WARN
26
+ end
27
+ end
28
+
29
+ def check_javascript_package_version
30
+ if javascript_package_version.nil?
31
+ warn_and_exit <<~WARN
32
+ Can't locate the stimulus_reflex NPM package.
33
+ Either add it to your package.json as a dependency or use "yarn link stimulus_reflex" if you are doing development.
34
+ WARN
35
+ end
36
+
37
+ unless javascript_version_matches?
38
+ warn_and_exit <<~WARN
39
+ The Stimulus Reflex javascript package version (#{javascript_package_version}) does not match the Rubygem version (#{gem_version}).
40
+ To update the Stimulus Reflex npm package:
41
+ yarn upgrade stimulus_reflex@#{gem_version}
42
+ WARN
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def caching_enabled?
49
+ Rails.application.config.action_controller.perform_caching
50
+ end
51
+
52
+ def not_null_store?
53
+ Rails.application.config.cache_store != :null_store
54
+ end
55
+
56
+ def javascript_version_matches?
57
+ javascript_package_version == gem_version
58
+ end
59
+
60
+ def gem_version
61
+ @_gem_version ||= StimulusReflex::VERSION.gsub(".pre", "-pre")
62
+ end
63
+
64
+ def javascript_package_version
65
+ @_js_version ||= find_javascript_package_version
66
+ end
67
+
68
+ def find_javascript_package_version
69
+ if (match = search_file(package_json_path, regex: /version/))
70
+ match[JSON_VERSION_FORMAT, 1]
71
+ end
72
+ end
73
+
74
+ def search_file(path, regex:)
75
+ return unless File.exist?(path)
76
+ File.foreach(path).grep(regex).first
77
+ end
78
+
79
+ def package_json_path
80
+ Rails.root.join("node_modules", "stimulus_reflex", "package.json")
81
+ end
82
+
83
+ def warn_and_exit(text)
84
+ puts "WARNING:"
85
+ puts text
86
+ exit_with_info if StimulusReflex.config.exit_on_failed_sanity_checks
87
+ end
88
+
89
+ def exit_with_info
90
+ puts
91
+ puts <<~INFO
92
+ If you know what you are doing and you want to start the application anyway,
93
+ you can add the following directive to an initializer:
94
+ StimulusReflex.config.exit_on_failed_sanity_checks = false
95
+ INFO
96
+ exit
97
+ end
98
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StimulusReflex
4
- VERSION = "3.3.0.pre5"
4
+ VERSION = "3.4.0.pre2"
5
5
  end
@@ -6,18 +6,20 @@ require "stimulus_reflex/version"
6
6
  namespace :stimulus_reflex do
7
7
  desc "Install StimulusReflex in this application"
8
8
  task install: :environment do
9
+ system "bundle add cable_ready"
9
10
  system "bundle exec rails webpacker:install:stimulus"
10
11
  gem_version = StimulusReflex::VERSION.gsub(".pre", "-pre")
11
- system "yarn add stimulus_reflex@#{gem_version}"
12
+ system "yarn add cable_ready stimulus_reflex@#{gem_version}"
13
+ main_folder = defined?(Webpacker) ? Webpacker.config.source_path.to_s.gsub("#{Rails.root}/", "") : "app/javascript"
12
14
 
13
- FileUtils.mkdir_p Rails.root.join("app/javascript/controllers"), verbose: true
15
+ FileUtils.mkdir_p Rails.root.join("#{main_folder}/controllers"), verbose: true
14
16
  FileUtils.mkdir_p Rails.root.join("app/reflexes"), verbose: true
15
17
 
16
- filepath = %w[
17
- app/javascript/controllers/index.js
18
- app/javascript/controllers/index.ts
19
- app/javascript/packs/application.js
20
- app/javascript/packs/application.ts
18
+ filepath = [
19
+ "#{main_folder}/controllers/index.js",
20
+ "#{main_folder}/controllers/index.ts",
21
+ "#{main_folder}/packs/application.js",
22
+ "#{main_folder}/packs/application.ts"
21
23
  ]
22
24
  .select { |path| File.exist?(path) }
23
25
  .map { |path| Rails.root.join(path) }
@@ -42,7 +44,8 @@ namespace :stimulus_reflex do
42
44
  end
43
45
 
44
46
  initialize_line = lines.find { |line| line.start_with?("StimulusReflex.initialize") }
45
- lines << "StimulusReflex.initialize(application, { consumer, controller, debug: false })\n" unless initialize_line
47
+ lines << "StimulusReflex.initialize(application, { consumer, controller, isolate: true })\n" unless initialize_line
48
+ lines << "StimulusReflex.debug = process.env.RAILS_ENV === 'development'\n" unless initialize_line
46
49
  File.open(filepath, "w") { |f| f.write lines.join }
47
50
 
48
51
  filepath = Rails.root.join("config/environments/development.rb")
@@ -63,6 +66,7 @@ namespace :stimulus_reflex do
63
66
  end
64
67
 
65
68
  system "bundle exec rails generate stimulus_reflex example"
69
+ system "bundle exec rails generate stimulus_reflex:config"
66
70
  system "rails dev:cache" unless Rails.root.join("tmp", "caching-dev.txt").exist?
67
71
  end
68
72
  end
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "stimulus_reflex",
3
+ "version": "3.3.0",
4
+ "description": "Build reactive applications with the Rails tooling you already know and love.",
5
+ "keywords": [
6
+ "ruby",
7
+ "rails",
8
+ "websockets",
9
+ "actioncable",
10
+ "turbolinks",
11
+ "reactive",
12
+ "cable",
13
+ "ujs",
14
+ "ssr",
15
+ "stimulus",
16
+ "reflex",
17
+ "stimulus_reflex",
18
+ "dom",
19
+ "morphdom"
20
+ ],
21
+ "homepage": "https://docs.stimulusreflex.com/",
22
+ "bugs": {
23
+ "url": "https://github.com/hopsoft/stimulus_reflex/issues"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com:hopsoft/stimulus_reflex.git"
28
+ },
29
+ "license": "MIT",
30
+ "author": "Nathan Hopkins <natehop@gmail.com>",
31
+ "source": "javascript/stimulus_reflex.js",
32
+ "main": "javascript/dist/stimulus_reflex.js",
33
+ "module": "javascript/dist/stimulus_reflex.module.js",
34
+ "esmodule": "javascript/dist/stimulus_reflex.modern.js",
35
+ "scripts": {
36
+ "prepare": "yarn build",
37
+ "postinstall": "node javascript/scripts/post_install.js",
38
+ "prettier-standard:check": "yarn run prettier-standard --check *.js **/*.js",
39
+ "prettier-standard:format": "yarn run prettier-standard *.js **/*.js",
40
+ "test": "yarn run mocha --require @babel/register --require esm ./javascript/test",
41
+ "build": "microbundle --target browser --format modern,es,cjs --no-strict",
42
+ "dev": "microbundle watch --target browser --format modern,es,cjs --no-strict"
43
+ },
44
+ "peerDependencies": {
45
+ "@rails/actioncable": ">= 6.0",
46
+ "cable_ready": ">= 4.3.0",
47
+ "stimulus": ">= 1.1"
48
+ },
49
+ "devDependencies": {
50
+ "@babel/core": "^7.6.2",
51
+ "@babel/preset-env": "^7.6.2",
52
+ "@babel/register": "^7.6.2",
53
+ "@rails/actioncable": "^6.0.3-3",
54
+ "assert": "^2.0.0",
55
+ "cable_ready": "^4.4.0-pre2",
56
+ "esm": "^3.2.25",
57
+ "jsdom": "^16.0.1",
58
+ "microbundle": "^0.12.3",
59
+ "mocha": "^8.0.1",
60
+ "prettier-standard": "^16.1.0",
61
+ "stimulus": "^1.1.1"
62
+ }
63
+ }