stimulus_reflex 3.3.0.pre6 → 3.4.0.pre3

Sign up to get free protection for your applications and to get access to all the features.

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 +85 -4
  3. data/Gemfile.lock +69 -68
  4. data/README.md +6 -3
  5. data/{lib → app/channels}/stimulus_reflex/channel.rb +24 -18
  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 +10 -0
  13. data/lib/stimulus_reflex.rb +1 -2
  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 +24 -20
  20. data/lib/stimulus_reflex/sanity_checker.rb +69 -18
  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 +1 -0
  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 +6256 -0
  30. metadata +38 -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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ StimulusReflex.configure do |config|
4
+ # Enable/disable exiting / warning when the sanity checks fail options:
5
+ # `:exit` or `:warn` or `:ignore`
6
+ # config.on_failed_sanity_checks = :exit
7
+
8
+ # Override the parent class that the StimulusReflex ActionCable channel inherits from
9
+ # config.parent_channel = "ApplicationCable::Channel"
10
+ end
@@ -9,15 +9,14 @@ 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
15
  require "stimulus_reflex/sanity_checker"
16
16
  require "stimulus_reflex/broadcasters/broadcaster"
17
17
  require "stimulus_reflex/broadcasters/nothing_broadcaster"
18
18
  require "stimulus_reflex/broadcasters/page_broadcaster"
19
19
  require "stimulus_reflex/broadcasters/selector_broadcaster"
20
- require "generators/stimulus_reflex_generator"
21
20
 
22
21
  module StimulusReflex
23
22
  class Engine < Rails::Engine
@@ -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
- broadcaster: 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
- broadcaster: 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
- broadcaster: 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 :on_failed_sanity_checks, :parent_channel
18
+
19
+ def initialize
20
+ @on_failed_sanity_checks = :exit
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
@@ -43,13 +43,15 @@ class StimulusReflex::Reflex
43
43
  end
44
44
  end
45
45
 
46
- attr_reader :channel, :url, :element, :selectors, :method_name, :broadcaster, :permanent_attribute_name
46
+ attr_reader :channel, :url, :element, :selectors, :method_name, :broadcaster, :permanent_attribute_name, :reflex_id
47
+
48
+ alias_method :action_name, :method_name # for compatibility with controller libraries like Pundit that expect an action name
47
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
- def initialize(channel, url: nil, element: nil, selectors: [], method_name: nil, permanent_attribute_name: nil, params: {})
54
+ def initialize(channel, url: nil, element: nil, selectors: [], method_name: nil, permanent_attribute_name: nil, params: {}, reflex_id: nil)
53
55
  @channel = channel
54
56
  @url = url
55
57
  @element = element
@@ -58,6 +60,7 @@ class StimulusReflex::Reflex
58
60
  @params = params
59
61
  @permanent_attribute_name = permanent_attribute_name
60
62
  @broadcaster = StimulusReflex::PageBroadcaster.new(self)
63
+ @reflex_id = reflex_id
61
64
  self.params
62
65
  end
63
66
 
@@ -66,21 +69,26 @@ class StimulusReflex::Reflex
66
69
  uri = URI.parse(url)
67
70
  path = ActionDispatch::Journey::Router::Utils.normalize_path(uri.path)
68
71
  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
- )
72
+ mock_env = Rack::MockRequest.env_for(uri.to_s)
73
+
74
+ mock_env.merge!(
75
+ "rack.request.query_hash" => query_hash,
76
+ "rack.request.query_string" => uri.query,
77
+ "ORIGINAL_SCRIPT_NAME" => "",
78
+ "ORIGINAL_FULLPATH" => path,
79
+ Rack::SCRIPT_NAME => "",
80
+ Rack::PATH_INFO => path,
81
+ Rack::REQUEST_PATH => path,
82
+ Rack::QUERY_STRING => uri.query
82
83
  )
84
+
85
+ env = connection.env.merge(mock_env)
86
+ req = ActionDispatch::Request.new(env)
87
+
83
88
  path_params = Rails.application.routes.recognize_path_with_request(req, url, req.env[:extras] || {})
89
+ path_params[:controller] = path_params[:controller].force_encoding("UTF-8")
90
+ path_params[:action] = path_params[:action].force_encoding("UTF-8")
91
+
84
92
  req.env.merge(ActionDispatch::Http::Parameters::PARAMETERS_KEY => path_params)
85
93
  req.env["action_dispatch.request.parameters"] = req.parameters.merge(@params)
86
94
  req.tap { |r| r.session.send :load! }
@@ -112,10 +120,6 @@ class StimulusReflex::Reflex
112
120
  end
113
121
  end
114
122
 
115
- def url_params
116
- @url_params ||= Rails.application.routes.recognize_path_with_request(request, request.path, request.env[:extras] || {})
117
- end
118
-
119
123
  def process(name, *args)
120
124
  reflex_invoked = false
121
125
  result = run_callbacks(:process) {
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class StimulusReflex::SanityChecker
4
- NODE_VERSION_FORMAT = /(\d+\.\d+\.\d+.*):/
5
4
  JSON_VERSION_FORMAT = /(\d+\.\d+\.\d+.*)"/
6
5
 
7
6
  def self.check!
7
+ return if StimulusReflex.config.on_failed_sanity_checks == :ignore
8
+
8
9
  instance = new
9
10
  instance.check_caching_enabled
10
11
  instance.check_javascript_package_version
@@ -12,25 +13,32 @@ class StimulusReflex::SanityChecker
12
13
 
13
14
  def check_caching_enabled
14
15
  unless caching_enabled?
15
- puts <<~WARN
16
- WARNING: Stimulus Reflex requires caching to be enabled. Caching allows the session to be modified during ActionCable requests.
16
+ warn_and_exit <<~WARN
17
+ Stimulus Reflex requires caching to be enabled. Caching allows the session to be modified during ActionCable requests.
17
18
  To enable caching in development, run:
18
19
  rails dev:cache
19
20
  WARN
20
21
  end
22
+
23
+ unless not_null_store?
24
+ warn_and_exit <<~WARN
25
+ Stimulus Reflex requires caching to be enabled. Caching allows the session to be modified during ActionCable requests.
26
+ But your config.cache_store is set to :null_store, so it won't work.
27
+ WARN
28
+ end
21
29
  end
22
30
 
23
31
  def check_javascript_package_version
24
32
  if javascript_package_version.nil?
25
- puts <<~WARN
26
- WARNING: Can't locate the stimulus_reflex NPM package.
33
+ warn_and_exit <<~WARN
34
+ Can't locate the stimulus_reflex NPM package.
27
35
  Either add it to your package.json as a dependency or use "yarn link stimulus_reflex" if you are doing development.
28
36
  WARN
29
37
  end
30
38
 
31
39
  unless javascript_version_matches?
32
- puts <<~WARN
33
- WARNING: The Stimulus Reflex javascript package version (#{javascript_package_version}) does not match the Rubygem version (#{gem_version}).
40
+ warn_and_exit <<~WARN
41
+ The Stimulus Reflex javascript package version (#{javascript_package_version}) does not match the Rubygem version (#{gem_version}).
34
42
  To update the Stimulus Reflex npm package:
35
43
  yarn upgrade stimulus_reflex@#{gem_version}
36
44
  WARN
@@ -40,8 +48,11 @@ class StimulusReflex::SanityChecker
40
48
  private
41
49
 
42
50
  def caching_enabled?
43
- Rails.application.config.action_controller.perform_caching &&
44
- Rails.application.config.cache_store != :null_store
51
+ Rails.application.config.action_controller.perform_caching
52
+ end
53
+
54
+ def not_null_store?
55
+ Rails.application.config.cache_store != :null_store
45
56
  end
46
57
 
47
58
  def javascript_version_matches?
@@ -53,14 +64,11 @@ class StimulusReflex::SanityChecker
53
64
  end
54
65
 
55
66
  def javascript_package_version
56
- return @_js_version if defined?(@_js_version)
57
- @_js_version = find_javascript_package_version
67
+ @_js_version ||= find_javascript_package_version
58
68
  end
59
69
 
60
70
  def find_javascript_package_version
61
- if (match = search_file(yarn_lock_path, regex: /^stimulus_reflex/))
62
- match[NODE_VERSION_FORMAT, 1]
63
- elsif (match = search_file(yarn_link_path, regex: /version/))
71
+ if (match = search_file(package_json_path, regex: /version/))
64
72
  match[JSON_VERSION_FORMAT, 1]
65
73
  end
66
74
  end
@@ -70,11 +78,54 @@ class StimulusReflex::SanityChecker
70
78
  File.foreach(path).grep(regex).first
71
79
  end
72
80
 
73
- def yarn_lock_path
74
- Rails.root.join("yarn.lock")
81
+ def package_json_path
82
+ Rails.root.join("node_modules", "stimulus_reflex", "package.json")
75
83
  end
76
84
 
77
- def yarn_link_path
78
- Rails.root.join("node_modules", "stimulus_reflex", "package.json")
85
+ def initializer_path
86
+ @_initializer_path ||= Rails.root.join("config", "initializers", "stimulus_reflex.rb")
87
+ end
88
+
89
+ def warn_and_exit(text)
90
+ puts "WARNING:"
91
+ puts text
92
+ exit_with_info if StimulusReflex.config.on_failed_sanity_checks == :exit
93
+ end
94
+
95
+ def exit_with_info
96
+ puts
97
+
98
+ # bundle exec rails generate stimulus_reflex:config
99
+ if File.exist?(initializer_path)
100
+ puts <<~INFO
101
+ If you know what you are doing and you want to start the application anyway,
102
+ you can add the following directive to the StimulusReflex initializer,
103
+ which is located at #{initializer_path}
104
+
105
+ StimulusReflex.configure do |config|
106
+ config.on_failed_sanity_checks = :warn
107
+ end
108
+
109
+ INFO
110
+ else
111
+ puts <<~INFO
112
+ If you know what you are doing and you want to start the application anyway,
113
+ you can create a StimulusReflex initializer with the command:
114
+
115
+ bundle exec rails generate stimulus_reflex:config
116
+
117
+ Then open your initializer at
118
+
119
+ <RAILS_ROOT>/config/initializers/stimulus_reflex.rb
120
+
121
+ and then add the following directive:
122
+
123
+ StimulusReflex.configure do |config|
124
+ config.on_failed_sanity_checks = :warn
125
+ end
126
+
127
+ INFO
128
+ end
129
+ exit
79
130
  end
80
131
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StimulusReflex
4
- VERSION = "3.3.0.pre6"
4
+ VERSION = "3.4.0.pre3"
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.4.0-pre2",
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
+ }