motion 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54eb6afb0f21c0333d46c1a21602739146fa1e2a021d3ffda048c9763dc85e66
4
- data.tar.gz: c3d9476c0e34869f5d4e46121316dee4f2c78d91b0a9b51d05c14a0b4d4e63ac
3
+ metadata.gz: 579385926d481a6978aa354e54e7b63cdd92f8bb37dc60b49be0f1530371c0d5
4
+ data.tar.gz: 9d37a416852746fb7b75fd74a3ff972419baddf822b1283c42b86b18be3a0b64
5
5
  SHA512:
6
- metadata.gz: b17d8159ba7eae3b144a6380385e68eb9a2b49b0733926d9e285bf60c8f0767646d30841ee90d62d4b3a359f5c5f9beb62d4793c6d48dff6f1887c9fd7367a70
7
- data.tar.gz: 0c47ed993c63378f0d562e341a44539bda1578c34d42ae8449c1dfd9dedabf7481e9e5f272c0869416893533fe31b5901368ebcc2e53c0752db120afb0e84c50
6
+ metadata.gz: 0bcc311420b91f6479e1a8e9e45680e9854f5f719dc74ef7f25eeda5595e960f3d1082bf84d5b42c6e1bd01b61c24f788ef34df4322bee95d8162e4b732c08d1
7
+ data.tar.gz: d3d8ead03222c53a801116cc20197860cad5bd8aaa5534b728cc37a32ca00b231fbafdee1fa6e649f004f9b897eef1826a5b8c5d8317d1b3fd11da5586776b43
@@ -1,5 +1,5 @@
1
- import { createClient } from '@unabridged/motion';
2
- import consumer from './channels/consumer';
1
+ import { createClient } from '@unabridged/motion'
2
+ import consumer from './channels/consumer'
3
3
 
4
4
  export default createClient({
5
5
 
@@ -11,7 +11,7 @@ export default createClient({
11
11
  // Motion can log information about the lifecycle of components to the
12
12
  // browser's console. It is recommended to turn this feature off outside of
13
13
  // development.
14
- logging: process.env["RAILS_ENV"] === "development",
14
+ logging: process.env.RAILS_ENV === 'development'
15
15
 
16
16
  // This function will be called for every motion, and the return value will be
17
17
  // made available at `Motion::Event#extra_data`:
@@ -30,8 +30,8 @@ export default createClient({
30
30
  // The data attributes used by Motion can be customized, but these values must
31
31
  // also be updated in the Ruby initializer:
32
32
  //
33
- // keyAttribute: "data-motion-key",
34
- // stateAttribute: "data-motion-state",
35
- // motionAttribute: "data-motion",
33
+ // keyAttribute: 'data-motion-key',
34
+ // stateAttribute: 'data-motion-state',
35
+ // motionAttribute: 'data-motion',
36
36
 
37
- });
37
+ })
data/lib/motion.rb CHANGED
@@ -19,41 +19,43 @@ module Motion
19
19
  autoload :Serializer, "motion/serializer"
20
20
  autoload :TestHelpers, "motion/test_helpers"
21
21
 
22
- def self.configure(&block)
23
- raise AlreadyConfiguredError if @config
24
-
25
- @config = Configuration.new(&block)
26
- end
27
-
28
- def self.config
29
- @config ||= Configuration.default
30
- end
31
-
32
- singleton_class.alias_method :configuration, :config
33
-
34
- def self.serializer
35
- @serializer ||= Serializer.new
36
- end
37
-
38
- def self.markup_transformer
39
- @markup_transformer ||= MarkupTransformer.new
40
- end
41
-
42
- def self.build_renderer_for(websocket_connection)
43
- config.renderer_for_connection_proc.call(websocket_connection)
44
- end
45
-
46
- def self.notify_error(error, message)
47
- config.error_notification_proc&.call(error, message)
48
- end
49
-
50
- # This method only exists for testing. Changing configuration while Motion is
51
- # in use is not supported. It is only safe to call this method when no
52
- # components are currently mounted.
53
- def self.reset_internal_state_for_testing!(new_configuration = nil)
54
- @config = new_configuration
55
- @serializer = nil
56
- @markup_transformer = nil
22
+ class << self
23
+ def configure(&block)
24
+ raise AlreadyConfiguredError if @config
25
+
26
+ @config = Configuration.new(&block)
27
+ end
28
+
29
+ def config
30
+ @config ||= Configuration.default
31
+ end
32
+
33
+ alias_method :configuration, :config
34
+
35
+ def serializer
36
+ @serializer ||= Serializer.new
37
+ end
38
+
39
+ def markup_transformer
40
+ @markup_transformer ||= MarkupTransformer.new
41
+ end
42
+
43
+ def build_renderer_for(websocket_connection)
44
+ config.renderer_for_connection_proc.call(websocket_connection)
45
+ end
46
+
47
+ def notify_error(error, message)
48
+ config.error_notification_proc&.call(error, message)
49
+ end
50
+
51
+ # This method only exists for testing. Changing configuration while Motion
52
+ # is in use is not supported. It is only safe to call this method when no
53
+ # components are currently mounted.
54
+ def reset_internal_state_for_testing!(new_configuration = nil)
55
+ @config = new_configuration
56
+ @serializer = nil
57
+ @markup_transformer = nil
58
+ end
57
59
  end
58
60
  end
59
61
 
@@ -66,7 +66,7 @@ module Motion
66
66
 
67
67
  def synchronize
68
68
  component_connection.if_render_required do |component|
69
- transmit(renderer.render(component))
69
+ transmit(renderer.render(component, layout: nil))
70
70
  end
71
71
 
72
72
  streaming_from component_connection.broadcasts,
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/concern"
4
- require "active_support/core_ext/class/attribute"
5
4
  require "active_support/core_ext/object/to_param"
6
5
  require "active_support/core_ext/hash/except"
7
6
 
@@ -12,6 +11,9 @@ module Motion
12
11
  module Broadcasts
13
12
  extend ActiveSupport::Concern
14
13
 
14
+ DEFAULT = {}.freeze
15
+ private_constant :DEFAULT
16
+
15
17
  # Analogous to `module_function` (available on both class and instance)
16
18
  module ModuleFunctions
17
19
  def stream_from(broadcast, handler)
@@ -37,14 +39,6 @@ module Motion
37
39
  end
38
40
  end
39
41
 
40
- included do
41
- class_attribute :_broadcast_handlers,
42
- instance_reader: false,
43
- instance_writer: false,
44
- instance_predicate: false,
45
- default: {}.freeze
46
- end
47
-
48
42
  class_methods do
49
43
  include ModuleFunctions
50
44
 
@@ -56,6 +50,15 @@ module Motion
56
50
  serialize_broadcasting([name, model])
57
51
  end
58
52
 
53
+ attr_writer :_broadcast_handlers
54
+
55
+ def _broadcast_handlers
56
+ return @_broadcast_handlers if defined?(@_broadcast_handlers)
57
+ return superclass._broadcast_handlers if superclass.respond_to?(:_broadcast_handlers)
58
+
59
+ DEFAULT
60
+ end
61
+
59
62
  private
60
63
 
61
64
  # This definition is copied from ActionCable::Channel::Broadcasting
@@ -105,8 +105,7 @@ module Motion
105
105
 
106
106
  run_callbacks(:action, &block)
107
107
  ensure
108
- # `@_action_callback_context = nil` would still appear in the state
109
- remove_instance_variable(:@_action_callback_context)
108
+ @_action_callback_context = nil
110
109
  end
111
110
  end
112
111
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/concern"
4
- require "active_support/core_ext/class/attribute"
5
4
  require "active_support/core_ext/hash/except"
6
5
 
7
6
  require "motion"
@@ -11,6 +10,9 @@ module Motion
11
10
  module Motions
12
11
  extend ActiveSupport::Concern
13
12
 
13
+ DEFAULT = {}.freeze
14
+ private_constant :DEFAULT
15
+
14
16
  # Analogous to `module_function` (available on both class and instance)
15
17
  module ModuleFunctions
16
18
  def map_motion(motion, handler = motion)
@@ -28,16 +30,17 @@ module Motion
28
30
  end
29
31
  end
30
32
 
31
- included do
32
- class_attribute :_motion_handlers,
33
- instance_reader: false,
34
- instance_writer: false,
35
- instance_predicate: false,
36
- default: {}.freeze
37
- end
38
-
39
33
  class_methods do
40
34
  include ModuleFunctions
35
+
36
+ attr_writer :_motion_handlers
37
+
38
+ def _motion_handlers
39
+ return @_motion_handlers if defined?(@_motion_handlers)
40
+ return superclass._motion_handlers if superclass.respond_to?(:_motion_handlers)
41
+
42
+ DEFAULT
43
+ end
41
44
  end
42
45
 
43
46
  include ModuleFunctions
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/concern"
4
- require "active_support/core_ext/class/attribute"
5
4
  require "active_support/core_ext/hash/except"
6
5
 
7
6
  require "motion"
@@ -11,6 +10,9 @@ module Motion
11
10
  module PeriodicTimers
12
11
  extend ActiveSupport::Concern
13
12
 
13
+ DEFAULT = {}.freeze
14
+ private_constant :DEFAULT
15
+
14
16
  # Analogous to `module_function` (available on both class and instance)
15
17
  module ModuleFunctions
16
18
  def every(interval, handler, name: handler)
@@ -32,16 +34,17 @@ module Motion
32
34
  end
33
35
  end
34
36
 
35
- included do
36
- class_attribute :_periodic_timers,
37
- instance_reader: false,
38
- instance_writer: false,
39
- instance_predicate: false,
40
- default: {}.freeze
41
- end
42
-
43
37
  class_methods do
44
38
  include ModuleFunctions
39
+
40
+ attr_writer :_periodic_timers
41
+
42
+ def _periodic_timers
43
+ return @_periodic_timers if defined?(@_periodic_timers)
44
+ return superclass._periodic_timers if superclass.respond_to?(:_periodic_timers)
45
+
46
+ DEFAULT
47
+ end
45
48
  end
46
49
 
47
50
  include ModuleFunctions
@@ -5,27 +5,37 @@ require "motion"
5
5
  module Motion
6
6
  module Component
7
7
  module Rendering
8
- # Use the presence/absence of the ivar instead of true/false to avoid
9
- # extra serialized state (Note that in this scheme, the presence of
10
- # the ivar will never be serialized).
11
- RERENDER_MARKER_IVAR = :@__awaiting_forced_rerender__
12
- private_constant :RERENDER_MARKER_IVAR
13
-
14
- # Some changes to Motion's state are specifically supported during render.
15
- ALLOWED_NEW_IVARS_DURING_RENDER = %i[
16
- @_broadcast_handlers
17
- @_stable_instance_identifier_for_callbacks
18
- @_motion_handlers
19
- @_periodic_timers
8
+ STATE_EXCLUDED_IVARS = %i[
9
+ @_action_callback_context
10
+ @_awaiting_forced_rerender
11
+ @_routes
12
+
13
+ @view_context
14
+ @lookup_context
15
+ @view_renderer
16
+ @view_flow
17
+ @virtual_path
18
+ @variant
19
+ @current_template
20
+ @output_buffer
21
+
22
+ @helpers
23
+ @controller
24
+ @request
25
+ @tag_builder
26
+
27
+ @asset_resolver_strategies
28
+ @assets_environment
20
29
  ].freeze
21
- private_constant :ALLOWED_NEW_IVARS_DURING_RENDER
30
+
31
+ private_constant :STATE_EXCLUDED_IVARS
22
32
 
23
33
  def rerender!
24
- instance_variable_set(RERENDER_MARKER_IVAR, true)
34
+ @_awaiting_forced_rerender = true
25
35
  end
26
36
 
27
37
  def awaiting_forced_rerender?
28
- instance_variable_defined?(RERENDER_MARKER_IVAR)
38
+ @_awaiting_forced_rerender
29
39
  end
30
40
 
31
41
  # * This can be overwritten.
@@ -43,10 +53,10 @@ module Motion
43
53
  _run_action_callbacks(context: :render) {
44
54
  _clear_awaiting_forced_rerender!
45
55
 
46
- view_context.capture { _without_new_instance_variables { super } }
56
+ view_context.capture { super }
47
57
  }
48
58
 
49
- raise RenderAborted, self if html == false
59
+ raise RenderAborted, self unless html
50
60
 
51
61
  Motion.markup_transformer.add_state_to_html(self, html)
52
62
  end
@@ -54,21 +64,19 @@ module Motion
54
64
  private
55
65
 
56
66
  def _clear_awaiting_forced_rerender!
57
- return unless awaiting_forced_rerender?
58
-
59
- remove_instance_variable(RERENDER_MARKER_IVAR)
67
+ @_awaiting_forced_rerender = false
60
68
  end
61
69
 
62
- def _without_new_instance_variables
63
- existing_instance_variables = instance_variables
70
+ def marshal_dump
71
+ (instance_variables - STATE_EXCLUDED_IVARS)
72
+ .map { |ivar| [ivar, instance_variable_get(ivar)] }
73
+ .to_h
74
+ end
64
75
 
65
- yield
66
- ensure
67
- (
68
- instance_variables -
69
- existing_instance_variables -
70
- ALLOWED_NEW_IVARS_DURING_RENDER
71
- ).each(&method(:remove_instance_variable))
76
+ def marshal_load(instance_variables)
77
+ instance_variables.each do |ivar, value|
78
+ instance_variable_set(ivar, value)
79
+ end
72
80
  end
73
81
  end
74
82
  end
@@ -41,7 +41,7 @@ module Motion
41
41
  end
42
42
 
43
43
  def process_motion(motion, event = nil)
44
- timing("Proccessed #{motion}") do
44
+ timing("Processed #{motion}") do
45
45
  component.process_motion(motion, event)
46
46
  end
47
47
 
@@ -53,7 +53,7 @@ module Motion
53
53
  end
54
54
 
55
55
  def process_broadcast(broadcast, message)
56
- timing("Proccessed broadcast to #{broadcast}") do
56
+ timing("Processed broadcast to #{broadcast}") do
57
57
  component.process_broadcast broadcast, message
58
58
  end
59
59
 
@@ -65,7 +65,7 @@ module Motion
65
65
  end
66
66
 
67
67
  def process_periodic_timer(timer)
68
- timing("Proccessed periodic timer #{timer}") do
68
+ timing("Processed periodic timer #{timer}") do
69
69
  component.process_periodic_timer timer
70
70
  end
71
71
 
data/lib/motion/event.rb CHANGED
@@ -18,7 +18,7 @@ module Motion
18
18
  raw["type"]
19
19
  end
20
20
 
21
- alias name type
21
+ alias_method :name, :type
22
22
 
23
23
  def details
24
24
  raw.fetch("details", {})
@@ -34,14 +34,12 @@ module Motion
34
34
  @target = Motion::Element.from_raw(raw["target"])
35
35
  end
36
36
 
37
- def current_target
38
- return @current_target if defined?(@current_target)
37
+ def element
38
+ return @element if defined?(@element)
39
39
 
40
- @current_target = Motion::Element.from_raw(raw["currentTarget"])
40
+ @element = Motion::Element.from_raw(raw["element"])
41
41
  end
42
42
 
43
- alias element current_target
44
-
45
43
  def form_data
46
44
  element&.form_data
47
45
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "digest"
4
+ require "lz4-ruby"
4
5
  require "active_support/message_encryptor"
5
6
 
6
7
  require "motion"
@@ -37,7 +38,7 @@ module Motion
37
38
  end
38
39
 
39
40
  def serialize(component)
40
- state = dump(component)
41
+ state = deflate(dump(component))
41
42
  state_with_revision = "#{revision}#{NULL_BYTE}#{state}"
42
43
 
43
44
  [
@@ -49,7 +50,7 @@ module Motion
49
50
  def deserialize(serialized_component)
50
51
  state_with_revision = decrypt_and_verify(serialized_component)
51
52
  serialized_revision, state = state_with_revision.split(NULL_BYTE, 2)
52
- component = load(state)
53
+ component = load(inflate(state))
53
54
 
54
55
  if revision == serialized_revision
55
56
  component
@@ -70,6 +71,14 @@ module Motion
70
71
  Marshal.load(state)
71
72
  end
72
73
 
74
+ def deflate(dumped_component)
75
+ LZ4.compress(dumped_component)
76
+ end
77
+
78
+ def inflate(deflated_state)
79
+ LZ4.uncompress(deflated_state)
80
+ end
81
+
73
82
  def encrypt_and_sign(cleartext)
74
83
  encryptor.encrypt_and_sign(cleartext)
75
84
  end
@@ -16,14 +16,18 @@ module Motion
16
16
  component.motions.include?(motion_name.to_s)
17
17
  end
18
18
 
19
- def run_motion(component, motion_name)
19
+ def run_motion(component, motion_name, event = motion_event)
20
20
  if block_given?
21
21
  c = component.dup
22
- c.process_motion(motion_name.to_s)
22
+ c.process_motion(motion_name.to_s, event)
23
23
  yield c
24
24
  else
25
- component.process_motion(motion_name.to_s)
25
+ component.process_motion(motion_name.to_s, event)
26
26
  end
27
27
  end
28
+
29
+ def motion_event(attributes = {})
30
+ Motion::Event.new(ActiveSupport::JSON.decode(attributes.to_json))
31
+ end
28
32
  end
29
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motion
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alec Larsen
8
8
  - Drew Ulmer
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-07-16 00:00:00.000000000 Z
12
+ date: 2021-03-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -31,14 +31,28 @@ dependencies:
31
31
  requirements:
32
32
  - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: '5.2'
34
+ version: '5.1'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: '5.2'
41
+ version: '5.1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: lz4-ruby
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 0.3.3
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 0.3.3
42
56
  description: |
43
57
  Motion extends Github's `view_component` to allow you to build reactive,
44
58
  real-time frontend UI components in your Rails application using pure Ruby.
@@ -88,7 +102,7 @@ metadata:
88
102
  source_code_uri: https://github.com/unabridged/motion
89
103
  post_install_message: |
90
104
  Friendly reminder: When updating the motion gem, don't forget to update the
91
- NPM package as well (`bin/yarn add '@unabridged/motion@0.4.0'`).
105
+ NPM package as well (`bin/yarn add '@unabridged/motion@0.5.0'`).
92
106
  rdoc_options: []
93
107
  require_paths:
94
108
  - lib
@@ -96,15 +110,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
110
  requirements:
97
111
  - - ">="
98
112
  - !ruby/object:Gem::Version
99
- version: 2.3.0
113
+ version: 2.5.0
100
114
  required_rubygems_version: !ruby/object:Gem::Requirement
101
115
  requirements:
102
116
  - - ">="
103
117
  - !ruby/object:Gem::Version
104
118
  version: '0'
105
119
  requirements: []
106
- rubygems_version: 3.0.3
107
- signing_key:
120
+ rubygems_version: 3.1.2
121
+ signing_key:
108
122
  specification_version: 4
109
123
  summary: Reactive frontend UI components for Rails in pure Ruby.
110
124
  test_files: []