stimulus_reflex 3.4.0.pre0 → 3.4.0.pre5

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.

@@ -8,9 +8,10 @@ module StimulusReflex
8
8
 
9
9
  return unless page_html.present?
10
10
 
11
- document = Nokogiri::HTML(page_html)
11
+ document = Nokogiri::HTML.parse(page_html)
12
12
  selectors = selectors.select { |s| document.css(s).present? }
13
13
  selectors.each do |selector|
14
+ operations << [selector, :morph]
14
15
  html = document.css(selector).inner_html
15
16
  cable_ready[stream_name].morph(
16
17
  selector: selector,
@@ -32,5 +33,9 @@ module StimulusReflex
32
33
  def page?
33
34
  true
34
35
  end
36
+
37
+ def to_s
38
+ "Page"
39
+ end
35
40
  end
36
41
  end
@@ -11,6 +11,7 @@ module StimulusReflex
11
11
  fragment = Nokogiri::HTML.fragment(html)
12
12
  match = fragment.at_css(selector)
13
13
  if match.present?
14
+ operations << [selector, :morph]
14
15
  cable_ready[stream_name].morph(
15
16
  selector: selector,
16
17
  html: match.inner_html,
@@ -21,6 +22,7 @@ module StimulusReflex
21
22
  })
22
23
  )
23
24
  else
25
+ operations << [selector, :inner_html]
24
26
  cable_ready[stream_name].inner_html(
25
27
  selector: selector,
26
28
  html: fragment.to_html,
@@ -40,6 +42,10 @@ module StimulusReflex
40
42
  @morphs ||= []
41
43
  end
42
44
 
45
+ def append_morph(selectors, html)
46
+ morphs << [selectors, html]
47
+ end
48
+
43
49
  def to_sym
44
50
  :selector
45
51
  end
@@ -47,5 +53,9 @@ module StimulusReflex
47
53
  def selector?
48
54
  true
49
55
  end
56
+
57
+ def to_s
58
+ "Selector"
59
+ end
50
60
  end
51
61
  end
@@ -14,11 +14,14 @@ module StimulusReflex
14
14
  end
15
15
 
16
16
  class Configuration
17
- attr_accessor :exit_on_failed_sanity_checks, :parent_channel
17
+ attr_accessor :on_failed_sanity_checks, :parent_channel, :logging
18
+
19
+ DEFAULT_LOGGING = proc { "[#{session_id}] #{operation_counter.magenta} #{reflex_info.green} -> #{selector.cyan} via #{mode} Morph (#{operation.yellow})" }
18
20
 
19
21
  def initialize
20
- @exit_on_failed_sanity_checks = true
22
+ @on_failed_sanity_checks = :exit
21
23
  @parent_channel = "ApplicationCable::Channel"
24
+ @logging = DEFAULT_LOGGING
22
25
  end
23
26
  end
24
27
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusReflex
4
+ class Logger
5
+ attr_accessor :reflex, :current_operation
6
+
7
+ def initialize(reflex)
8
+ @reflex = reflex
9
+ @current_operation = 1
10
+ end
11
+
12
+ def print
13
+ return unless config_logging.instance_of?(Proc)
14
+
15
+ puts
16
+ reflex.broadcaster.operations.each do
17
+ puts instance_eval(&config_logging) + "\e[0m"
18
+ @current_operation += 1
19
+ end
20
+ puts
21
+ end
22
+
23
+ private
24
+
25
+ def config_logging
26
+ return @config_logging if @config_logging
27
+
28
+ StimulusReflex.config.logging.binding.eval("using StimulusReflex::Utils::Colorize")
29
+ @config_logging = StimulusReflex.config.logging
30
+ end
31
+
32
+ def session_id_full
33
+ session = reflex.request&.session
34
+ session.nil? ? "-" : session.id
35
+ end
36
+
37
+ def session_id
38
+ session_id_full.to_s[0..7]
39
+ end
40
+
41
+ def reflex_info
42
+ reflex.class.to_s + "#" + reflex.method_name
43
+ end
44
+
45
+ def reflex_id_full
46
+ reflex.reflex_id
47
+ end
48
+
49
+ def reflex_id
50
+ reflex_id_full[0..7]
51
+ end
52
+
53
+ def mode
54
+ reflex.broadcaster.to_s
55
+ end
56
+
57
+ def selector
58
+ reflex.broadcaster.operations[current_operation - 1][0]
59
+ end
60
+
61
+ def operation
62
+ reflex.broadcaster.operations[current_operation - 1][1].to_s
63
+ end
64
+
65
+ def operation_counter
66
+ current_operation.to_s + "/" + reflex.broadcaster.operations.size.to_s
67
+ end
68
+
69
+ def connection_id_full
70
+ identifier = reflex.connection&.connection_identifier
71
+ identifier.empty? ? "-" : identifier
72
+ end
73
+
74
+ def connection_id
75
+ connection_id_full[0..7]
76
+ end
77
+
78
+ def timestamp
79
+ Time.now.strftime("%Y-%m-%d %H:%M:%S")
80
+ end
81
+
82
+ def method_missing method
83
+ return send(method.to_sym) if private_instance_methods.include?(method.to_sym)
84
+
85
+ reflex.connection.identifiers.each do |identifier|
86
+ ident = reflex.connection.send(identifier)
87
+ return ident.send(method) if ident.respond_to?(:attributes) && ident.attributes.key?(method.to_s)
88
+ end
89
+ "-"
90
+ end
91
+
92
+ def respond_to_missing? method
93
+ return true if private_instance_methods.include?(method.to_sym)
94
+
95
+ reflex.connection.identifiers.each do |identifier|
96
+ ident = reflex.connection.send(identifier)
97
+ return true if ident.respond_to?(:attributes) && ident.attributes.key?(method.to_s)
98
+ end
99
+ false
100
+ end
101
+
102
+ def private_instance_methods
103
+ StimulusReflex::Logger.private_instance_methods(false)
104
+ end
105
+ end
106
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ClientAttributes = Struct.new(:reflex_id, :reflex_controller, :xpath, :c_xpath, :permanent_attribute_name, keyword_init: true)
4
+
3
5
  class StimulusReflex::Reflex
4
6
  include ActiveSupport::Rescuable
5
7
  include ActiveSupport::Callbacks
@@ -43,23 +45,25 @@ class StimulusReflex::Reflex
43
45
  end
44
46
  end
45
47
 
46
- attr_reader :channel, :url, :element, :selectors, :method_name, :broadcaster, :permanent_attribute_name
48
+ attr_reader :channel, :url, :element, :selectors, :method_name, :broadcaster, :client_attributes, :logger
47
49
 
48
50
  alias_method :action_name, :method_name # for compatibility with controller libraries like Pundit that expect an action name
49
51
 
50
52
  delegate :connection, :stream_name, to: :channel
51
53
  delegate :flash, :session, to: :request
52
54
  delegate :broadcast, :broadcast_message, to: :broadcaster
55
+ delegate :reflex_id, :reflex_controller, :xpath, :c_xpath, :permanent_attribute_name, to: :client_attributes
53
56
 
54
- def initialize(channel, url: nil, element: nil, selectors: [], method_name: nil, permanent_attribute_name: nil, params: {})
57
+ def initialize(channel, url: nil, element: nil, selectors: [], method_name: nil, params: {}, client_attributes: {})
55
58
  @channel = channel
56
59
  @url = url
57
60
  @element = element
58
61
  @selectors = selectors
59
62
  @method_name = method_name
60
63
  @params = params
61
- @permanent_attribute_name = permanent_attribute_name
62
64
  @broadcaster = StimulusReflex::PageBroadcaster.new(self)
65
+ @logger = StimulusReflex::Logger.new(self)
66
+ @client_attributes = ClientAttributes.new(client_attributes)
63
67
  self.params
64
68
  end
65
69
 
@@ -104,7 +108,7 @@ class StimulusReflex::Reflex
104
108
  else
105
109
  raise StandardError.new("Cannot call :selector morph after :nothing morph") if broadcaster.nothing?
106
110
  @broadcaster = StimulusReflex::SelectorBroadcaster.new(self) unless broadcaster.selector?
107
- broadcaster.morphs << [selectors, html]
111
+ broadcaster.append_morph(selectors, html)
108
112
  end
109
113
  end
110
114
 
@@ -3,10 +3,21 @@
3
3
  class StimulusReflex::SanityChecker
4
4
  JSON_VERSION_FORMAT = /(\d+\.\d+\.\d+.*)"/
5
5
 
6
- def self.check!
7
- instance = new
8
- instance.check_caching_enabled
9
- instance.check_javascript_package_version
6
+ class << self
7
+ def check!
8
+ return if StimulusReflex.config.on_failed_sanity_checks == :ignore
9
+ return if called_by_generate_config?
10
+
11
+ instance = new
12
+ instance.check_caching_enabled
13
+ instance.check_javascript_package_version
14
+ end
15
+
16
+ private
17
+
18
+ def called_by_generate_config?
19
+ ARGV.include? "stimulus_reflex:config"
20
+ end
10
21
  end
11
22
 
12
23
  def check_caching_enabled
@@ -80,19 +91,50 @@ class StimulusReflex::SanityChecker
80
91
  Rails.root.join("node_modules", "stimulus_reflex", "package.json")
81
92
  end
82
93
 
94
+ def initializer_path
95
+ @_initializer_path ||= Rails.root.join("config", "initializers", "stimulus_reflex.rb")
96
+ end
97
+
83
98
  def warn_and_exit(text)
84
99
  puts "WARNING:"
85
100
  puts text
86
- exit_with_info if StimulusReflex.config.exit_on_failed_sanity_checks
101
+ exit_with_info if StimulusReflex.config.on_failed_sanity_checks == :exit
87
102
  end
88
103
 
89
104
  def exit_with_info
90
105
  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
106
+
107
+ # bundle exec rails generate stimulus_reflex:config
108
+ if File.exist?(initializer_path)
109
+ puts <<~INFO
110
+ If you know what you are doing and you want to start the application anyway,
111
+ you can add the following directive to the StimulusReflex initializer,
112
+ which is located at #{initializer_path}
113
+
114
+ StimulusReflex.configure do |config|
115
+ config.on_failed_sanity_checks = :warn
116
+ end
117
+
118
+ INFO
119
+ else
120
+ puts <<~INFO
121
+ If you know what you are doing and you want to start the application anyway,
122
+ you can create a StimulusReflex initializer with the command:
123
+
124
+ bundle exec rails generate stimulus_reflex:config
125
+
126
+ Then open your initializer at
127
+
128
+ <RAILS_ROOT>/config/initializers/stimulus_reflex.rb
129
+
130
+ and then add the following directive:
131
+
132
+ StimulusReflex.configure do |config|
133
+ config.on_failed_sanity_checks = :warn
134
+ end
135
+
136
+ INFO
137
+ end
138
+ exit false
97
139
  end
98
140
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusReflex
4
+ module Utils
5
+ module Colorize
6
+ COLORS = {
7
+ red: "31",
8
+ green: "32",
9
+ yellow: "33",
10
+ blue: "34",
11
+ magenta: "35",
12
+ cyan: "36",
13
+ white: "37"
14
+ }
15
+
16
+ refine String do
17
+ COLORS.each do |name, code|
18
+ define_method(name) { "\e[#{code}m#{self}\e[0m" }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StimulusReflex
4
- VERSION = "3.4.0.pre0"
4
+ VERSION = "3.4.0.pre5"
5
5
  end
@@ -8,7 +8,7 @@ namespace :stimulus_reflex do
8
8
  task install: :environment do
9
9
  system "bundle exec rails webpacker:install:stimulus"
10
10
  gem_version = StimulusReflex::VERSION.gsub(".pre", "-pre")
11
- system "yarn add cable_ready stimulus_reflex@#{gem_version}"
11
+ system "yarn add stimulus_reflex@#{gem_version}"
12
12
  main_folder = defined?(Webpacker) ? Webpacker.config.source_path.to_s.gsub("#{Rails.root}/", "") : "app/javascript"
13
13
 
14
14
  FileUtils.mkdir_p Rails.root.join("#{main_folder}/controllers"), verbose: true
@@ -60,11 +60,12 @@ namespace :stimulus_reflex do
60
60
  lines.delete_at 1
61
61
  lines.insert 1, " adapter: redis\n"
62
62
  lines.insert 2, " url: <%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %>\n"
63
- lines.insert 3, " channel_prefix: " + Rails.application.class.module_parent.to_s.underscore + "_development\n"
63
+ lines.insert 3, " channel_prefix: " + File.basename(Rails.root.to_s).underscore + "_development\n"
64
64
  File.open(filepath, "w") { |f| f.write lines.join }
65
65
  end
66
66
 
67
67
  system "bundle exec rails generate stimulus_reflex example"
68
+ puts "Generating default StimulusReflex configuration file into your application config/initializers directory"
68
69
  system "bundle exec rails generate stimulus_reflex:config"
69
70
  system "rails dev:cache" unless Rails.root.join("tmp", "caching-dev.txt").exist?
70
71
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stimulus_reflex",
3
- "version": "3.3.0",
3
+ "version": "3.4.0-pre4",
4
4
  "description": "Build reactive applications with the Rails tooling you already know and love.",
5
5
  "keywords": [
6
6
  "ruby",
@@ -28,34 +28,28 @@
28
28
  },
29
29
  "license": "MIT",
30
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",
31
+ "main": "./javascript/stimulus_reflex.js",
32
+ "module": "./javascript/stimulus_reflex.js",
35
33
  "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"
34
+ "postinstall": "node ./javascript/scripts/post_install.js",
35
+ "prettier-standard:check": "yarn run prettier-standard --check ./javascript/*.js ./javascript/**/*.js",
36
+ "prettier-standard:format": "yarn run prettier-standard ./javascript/*.js ./javascript/**/*.js",
37
+ "test": "yarn run mocha --require @babel/register --require esm ./javascript/test"
43
38
  },
44
39
  "peerDependencies": {
45
- "@rails/actioncable": ">= 6.0",
46
- "cable_ready": ">= 4.3.0",
47
40
  "stimulus": ">= 1.1"
48
41
  },
42
+ "dependencies": {
43
+ "@rails/actioncable": ">= 6.0",
44
+ "cable_ready": ">= 4.3.0"
45
+ },
49
46
  "devDependencies": {
50
47
  "@babel/core": "^7.6.2",
51
48
  "@babel/preset-env": "^7.6.2",
52
49
  "@babel/register": "^7.6.2",
53
- "@rails/actioncable": "^6.0.3-3",
54
50
  "assert": "^2.0.0",
55
- "cable_ready": "^4.4.0-pre2",
56
51
  "esm": "^3.2.25",
57
52
  "jsdom": "^16.0.1",
58
- "microbundle": "^0.12.3",
59
53
  "mocha": "^8.0.1",
60
54
  "prettier-standard": "^16.1.0",
61
55
  "stimulus": "^1.1.1"
@@ -23,14 +23,14 @@ Gem::Specification.new do |gem|
23
23
  "source_code_uri" => gem.homepage
24
24
  }
25
25
 
26
- gem.files = Dir["lib/**/*", "bin/*", "[A-Z]*"]
26
+ gem.files = Dir["app/**/*", "lib/**/*", "bin/*", "[A-Z]*"]
27
27
  gem.test_files = Dir["test/**/*.rb"]
28
28
 
29
29
  gem.add_dependency "rack"
30
30
  gem.add_dependency "nokogiri"
31
31
  gem.add_dependency "rails", ">= 5.2"
32
- gem.add_dependency "cable_ready", ">= 4.3.0"
33
32
  gem.add_dependency "redis"
33
+ gem.add_dependency "cable_ready", ">= 4.3.0"
34
34
 
35
35
  gem.add_development_dependency "bundler", "~> 2.0"
36
36
  gem.add_development_dependency "pry-nav"
@@ -0,0 +1,15 @@
1
+ require_relative "../test_helper"
2
+
3
+ class StimulusReflex::BroadcasterTest < ActiveSupport::TestCase
4
+ setup do
5
+ @reflex = Minitest::Mock.new
6
+ @reflex.expect :stream_name, "TestStream"
7
+ end
8
+
9
+ test "raises a NotImplementedError if called directly" do
10
+ broadcaster = StimulusReflex::Broadcaster.new(@reflex)
11
+
12
+ assert_raises(NotImplementedError) { broadcaster.broadcast }
13
+ assert_raises(NotImplementedError) { broadcaster.broadcast_message(subject: "Test") }
14
+ end
15
+ end