hyper-component 1.0.alpha1.1 → 1.0.alpha1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/Gemfile +5 -0
  4. data/hyper-component.gemspec +6 -9
  5. data/lib/hyper-component.rb +10 -1
  6. data/lib/hyperstack/component.rb +78 -10
  7. data/lib/hyperstack/component/children.rb +1 -1
  8. data/lib/hyperstack/component/element.rb +93 -21
  9. data/lib/hyperstack/component/event.rb +1 -1
  10. data/lib/hyperstack/component/free_render.rb +21 -0
  11. data/lib/hyperstack/component/isomorphic_helpers.rb +13 -8
  12. data/lib/hyperstack/component/version.rb +1 -1
  13. data/lib/hyperstack/component/while_loading.rb +27 -0
  14. data/lib/hyperstack/ext/component/array.rb +20 -0
  15. data/lib/hyperstack/ext/component/element.rb +22 -8
  16. data/lib/hyperstack/ext/component/enumerable.rb +18 -0
  17. data/lib/hyperstack/ext/component/kernel.rb +7 -0
  18. data/lib/hyperstack/ext/component/string.rb +1 -1
  19. data/lib/hyperstack/internal/component.rb +7 -1
  20. data/lib/hyperstack/internal/component/class_methods.rb +84 -8
  21. data/lib/hyperstack/internal/component/haml.rb +3 -12
  22. data/lib/hyperstack/internal/component/instance_methods.rb +38 -5
  23. data/lib/hyperstack/internal/component/props_wrapper.rb +29 -9
  24. data/lib/hyperstack/internal/component/rails/component_mount.rb +12 -0
  25. data/lib/hyperstack/internal/component/rails/controller_helper.rb +20 -1
  26. data/lib/hyperstack/internal/component/rails/railtie.rb +3 -2
  27. data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +5 -1
  28. data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +4 -2
  29. data/lib/hyperstack/internal/component/react_wrapper.rb +79 -62
  30. data/lib/hyperstack/internal/component/rendering_context.rb +95 -45
  31. data/lib/hyperstack/internal/component/rescue_wrapper.rb +40 -0
  32. data/lib/hyperstack/internal/component/should_component_update.rb +1 -1
  33. data/lib/hyperstack/internal/component/tags.rb +12 -3
  34. data/lib/hyperstack/internal/component/top_level_rails_component.rb +4 -0
  35. data/lib/hyperstack/internal/component/while_loading_wrapper.rb +29 -0
  36. data/lib/react/react-source.rb +2 -2
  37. metadata +50 -81
  38. data/Gemfile.lock +0 -363
@@ -18,16 +18,22 @@ module Hyperstack
18
18
  def self.load_context(unique_id = nil, name = nil)
19
19
  # can be called on the client to force re-initialization for testing purposes
20
20
  if !unique_id || !@context || @context.unique_id != unique_id
21
- if on_opal_server?
22
- `console.history = []` rescue nil
23
- message = "************************ React Prerendering Context Initialized #{name} ***********************"
24
- else
25
- message = "************************ React Browser Context Initialized ****************************"
26
- end
21
+ message =
22
+ if on_opal_server?
23
+ `console.history = []` rescue nil
24
+ "************************ React Prerendering Context Initialized #{name} ***********************"
25
+ else
26
+ '************************ React Browser Context Initialized ****************************'
27
+ end
28
+
27
29
  log(message)
30
+
28
31
  @context = Context.new(unique_id)
29
32
  end
30
- @context
33
+
34
+ # True is returned here because this method is evaluated by MiniRacer,
35
+ # and can cause TypeError: Converting circular structure to JSON to raise
36
+ true
31
37
  end
32
38
  end
33
39
 
@@ -139,7 +145,6 @@ module Hyperstack
139
145
 
140
146
  def send_to_opal(method_name, *args)
141
147
  return unless @ctx
142
- args = [1] if args.length == 0
143
148
  Hyperstack::Internal::Component::Rails::ComponentLoader.new(@ctx).load!
144
149
  method_args = args.collect do |arg|
145
150
  quarg = "#{arg}".tr('"', "'")
@@ -1,5 +1,5 @@
1
1
  module Hyperstack
2
2
  module Component
3
- VERSION = '1.0.alpha1.1' # '1.0.alpha1.1'
3
+ VERSION = '1.0.alpha1.6' # '1.0.alpha1.5'
4
4
  end
5
5
  end
@@ -0,0 +1,27 @@
1
+ module Hyperstack
2
+ module Component
3
+ module WhileLoading
4
+ def __hyperstack_component_rescue_wrapper(child)
5
+ Hyperstack::Internal::Component::WhileLoadingWrapper(child: self, children_elements: child)
6
+ end
7
+
8
+ def resources_loading?
9
+ @__hyperstack_while_loading_waiting_on_resources
10
+ end
11
+
12
+ def resources_loaded?
13
+ !@__hyperstack_while_loading_waiting_on_resources
14
+ end
15
+
16
+ if Hyperstack::Component::IsomorphicHelpers.on_opal_client?
17
+ %x{
18
+ function onError(event) {
19
+ if (event.message.match(/^Uncaught NotQuiet: /)) event.preventDefault();
20
+ }
21
+
22
+ window.addEventListener('error', onError);
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ # from Rails 6.0 activesupport. Remove once defined by Opal activesupport
2
+ class Array
3
+ # Removes and returns the elements for which the block returns a true value.
4
+ # If no block is given, an Enumerator is returned instead.
5
+ #
6
+ # numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7
+ # odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
8
+ # numbers # => [0, 2, 4, 6, 8]
9
+ def extract!
10
+ return to_enum(:extract!) { size } unless block_given?
11
+
12
+ extracted_elements = []
13
+
14
+ reject! do |element|
15
+ extracted_elements << element if yield(element)
16
+ end
17
+
18
+ extracted_elements
19
+ end
20
+ end
@@ -1,11 +1,9 @@
1
1
  Element.instance_eval do
2
2
  def self.find(selector)
3
- selector = begin
4
- selector.dom_node
5
- rescue
6
- selector
7
- end if `#{selector}.$dom_node !== undefined`
8
- `$(#{selector})`
3
+ if `typeof #{selector}['$respond_to?'] == 'function'` && selector.respond_to?(:dom_node)
4
+ selector = selector.dom_node
5
+ end
6
+ `jQuery(#{selector})`
9
7
  end
10
8
 
11
9
  def self.[](selector)
@@ -37,7 +35,7 @@ Element.instance_eval do
37
35
  # see react-rails documentation for more details
38
36
 
39
37
  %x{
40
- $.fn.mount_components = function() {
38
+ jQuery.fn.mount_components = function() {
41
39
  this.each(function(e) { ReactRailsUJS.mountComponents(e[0]) })
42
40
  return this;
43
41
  }
@@ -45,4 +43,20 @@ Element.instance_eval do
45
43
  Element.expose :mount_components
46
44
  end
47
45
 
48
- DOM = Element
46
+ module Hyperstack
47
+ module Internal
48
+ module Component
49
+ module InstanceMethods
50
+ def set_jq(var)
51
+ ->(val) { set(var).call(jQ[val]) }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ class Object
59
+ def jQ
60
+ Element
61
+ end
62
+ end
@@ -0,0 +1,18 @@
1
+ # from Rails 6.0 activesupport. Remove once defined by Opal activesupport
2
+ module Enumerable
3
+ INDEX_WITH_DEFAULT = Object.new
4
+ private_constant :INDEX_WITH_DEFAULT
5
+ def index_with(default = INDEX_WITH_DEFAULT)
6
+ if block_given?
7
+ result = {}
8
+ each { |elem| result[elem] = yield(elem) }
9
+ result
10
+ elsif default != INDEX_WITH_DEFAULT
11
+ result = {}
12
+ each { |elem| result[elem] = default }
13
+ result
14
+ else
15
+ to_enum(:index_with) { size if respond_to?(:size) }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module Kernel
2
+ def pause(s, &block)
3
+ Promise.new.tap { |p| after(s) { p.resolve(*(block && [block.call])) }}
4
+ end
5
+ alias busy_sleep sleep
6
+ alias sleep pause
7
+ end
@@ -5,4 +5,4 @@ class String
5
5
  return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
6
6
  })`
7
7
  end
8
- end
8
+ end
@@ -1,7 +1,13 @@
1
1
  require 'hyperstack-config'
2
2
 
3
3
  module Hyperstack
4
-
5
4
  define_setting :prerendering, :off if RUBY_ENGINE != 'opal'
6
5
 
6
+ module Internal
7
+ module Component
8
+ class << self
9
+ attr_accessor :after_error_args
10
+ end
11
+ end
12
+ end
7
13
  end
@@ -20,6 +20,10 @@ module Hyperstack
20
20
  true
21
21
  end
22
22
 
23
+ def allow_deprecated_render_definition?
24
+ false
25
+ end
26
+
23
27
  def mounted_components
24
28
  Hyperstack::Component.mounted_components.select { |c| c.class <= self }
25
29
  end
@@ -38,17 +42,74 @@ module Hyperstack
38
42
  backtrace[1..-1].each { |line| message_array << line }
39
43
  end
40
44
 
45
+ def before_receive_props(*args, &block)
46
+ deprecation_warning "'before_receive_props' is deprecated. Use the 'before_new_params' macro instead."
47
+ before_new_params(*args, &block)
48
+ end
49
+
41
50
  def render(container = nil, params = {}, &block)
51
+ Tags.included(self)
42
52
  if container
43
- container = container.type if container.is_a? Hyperstack::Component::Element
44
- define_method :render do
45
- RenderingContext.render(container, params) { instance_eval(&block) if block }
53
+ container = container.element_type if container.is_a? Hyperstack::Component::Element
54
+ define_method(:__hyperstack_component_render) do
55
+ __hyperstack_component_select_wrappers do
56
+ RenderingContext.render(container, params) do
57
+ instance_eval(&block) if block
58
+ end
59
+ end
46
60
  end
47
61
  else
48
- define_method(:render) { instance_eval(&block) }
62
+ define_method(:__hyperstack_component_render) do
63
+ __hyperstack_component_select_wrappers do
64
+ instance_eval(&block)
65
+ end
66
+ end
49
67
  end
50
68
  end
51
69
 
70
+ # def while_loading(*args, &block)
71
+ # __hyperstack_component_after_render_hook do |element|
72
+ # element.while_loading(*args) { |*aargs| instance_exec(*aargs, &block) }
73
+ # end
74
+ # end
75
+
76
+ def on(*args, &block)
77
+ __hyperstack_component_after_render_hook do |element|
78
+ element.on(*args) { |*aargs| instance_exec(*aargs, &block) }
79
+ end
80
+ end
81
+
82
+ def rescues(*klasses, &block)
83
+ klasses = [StandardError] if klasses.empty?
84
+ __hyperstack_component_rescue_hook do |found, *args|
85
+ next [found, *args] if found || !klasses.detect { |klass| args[0].is_a? klass }
86
+ instance_exec(*args, &block)
87
+ [true, *args]
88
+ end
89
+ end
90
+
91
+ def before_render(*args, &block)
92
+ before_mount(*args, &block)
93
+ before_update(*args, &block)
94
+ end
95
+
96
+ def after_render(*args, &block)
97
+ after_mount(*args, &block)
98
+ after_update(*args, &block)
99
+ end
100
+
101
+ # [:while_loading, :on].each do |method|
102
+ # define_method(method) do |*args, &block|
103
+ # @@alias_id ||= 0
104
+ # @@alias_id += 1
105
+ # alias_name = "__hyperstack_attach_post_render_hook_#{@@alias_id}"
106
+ # alias_method alias_name, :__hyperstack_attach_post_render_hook
107
+ # define_method(:__hyperstack_attach_post_render_hook) do |element|
108
+ # send(alias_name, element.while_loading(*args) { instance_eval(&block) })
109
+ # end
110
+ # end
111
+ # end
112
+
52
113
  # method missing will assume the method is a class name, and will treat this a render of
53
114
  # of the component, i.e. Foo::Bar.baz === Foo::Bar().baz
54
115
 
@@ -125,6 +186,10 @@ module Hyperstack
125
186
  options[:default] ||= nil
126
187
  options[:allow_nil] = true unless options.key?(:allow_nil)
127
188
  end
189
+ if name == :class
190
+ name = :className
191
+ options[:alias] ||= :Class
192
+ end
128
193
  if options[:default]
129
194
  validator.optional(name, options)
130
195
  else
@@ -138,14 +203,24 @@ module Hyperstack
138
203
 
139
204
  alias other_params collect_other_params_as
140
205
  alias others collect_other_params_as
206
+ alias other collect_other_params_as
207
+ alias opts collect_other_params_as
141
208
 
142
- def triggers(name, opts = {})
209
+ def fires(name, opts = {})
143
210
  aka = opts[:alias] || "#{name}!"
144
- name = name =~ /^<(.+)>$/ ? name.gsub(/^<(.+)>$/, '\1') : "on_#{name}"
211
+ name = if name =~ /^<(.+)>$/
212
+ name.gsub(/^<(.+)>$/, '\1')
213
+ elsif Hyperstack::Component::Event::BUILT_IN_EVENTS.include?("on#{name.event_camelize}")
214
+ "on#{name.event_camelize}"
215
+ else
216
+ "on_#{name}"
217
+ end
145
218
  validator.event(name)
146
219
  define_method(aka) { |*args| props[name]&.call(*args) }
147
220
  end
148
221
 
222
+ alias triggers fires
223
+
149
224
  def define_state(*states, &block)
150
225
  deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
151
226
  default_initial_value = (block && block.arity == 0) ? yield : nil
@@ -196,7 +271,8 @@ module Hyperstack
196
271
  ReactWrapper.import_native_component(
197
272
  self, ReactWrapper.eval_native_react_component(component_name)
198
273
  )
199
- define_method(:render) {} # define a dummy render method - will never be called...
274
+ render {}
275
+ #define_method(:render) {} # define a dummy render method - will never be called...
200
276
  rescue Exception => e # rubocop:disable Lint/RescueException : we need to catch everything!
201
277
  raise "#{self} cannot import '#{component_name}': #{e.message}."
202
278
  # rubocop:enable Lint/RescueException
@@ -216,7 +292,7 @@ module Hyperstack
216
292
  end
217
293
 
218
294
  def to_n
219
- ReactWrapper.class_eval('@@component_classes')[self]
295
+ Hyperstack::Internal::Component::ReactWrapper.create_native_react_class(self)
220
296
  end
221
297
  end
222
298
  end
@@ -27,7 +27,9 @@ module Hyperstack
27
27
  Hyperstack::Internal::Component::RenderingContext.replace(
28
28
  self,
29
29
  Hyperstack::Internal::Component::RenderingContext.build do
30
- Hyperstack::Internal::Component::RenderingContext.render(type, build_new_properties(class_name, args), &new_block)
30
+ Hyperstack::Internal::Component::RenderingContext.render(
31
+ element_type, @properties, args, class: haml_class_name(class_name), &new_block
32
+ )
31
33
  end
32
34
  )
33
35
  end
@@ -39,17 +41,6 @@ module Hyperstack
39
41
  def haml_class_name(class_name)
40
42
  class_name.gsub(/__|_/, '__' => '_', '_' => '-')
41
43
  end
42
-
43
- private
44
-
45
- def build_new_properties(class_name, args)
46
- class_name = haml_class_name(class_name)
47
- new_props = @properties.dup
48
- new_props[:className] = "\
49
- #{class_name} #{new_props[:className]} #{args.delete(:class)} #{args.delete(:className)}\
50
- ".split(' ').uniq.join(' ')
51
- new_props.merge! args
52
- end
53
44
  end
54
45
  end
55
46
  end
@@ -9,7 +9,7 @@ module Hyperstack
9
9
  end
10
10
 
11
11
  def params
12
- if @__hyperstack_component_params_wrapper.param_accessor_style == :hyperstack
12
+ if [:hyperstack, :accessors].include? @__hyperstack_component_params_wrapper.param_accessor_style
13
13
  raise "params are now directly accessible via instance variables.\n"\
14
14
  ' to access the legacy behavior add `param_accessor_style = :legacy` '\
15
15
  "to your component class\n"\
@@ -23,18 +23,28 @@ module Hyperstack
23
23
  Hash.new(`#{@__hyperstack_component_native}.props`)
24
24
  end
25
25
 
26
- def refs
27
- Hash.new(`#{@__hyperstack_component_native}.refs`)
28
- end
29
-
30
26
  def dom_node
31
27
  `ReactDOM.findDOMNode(#{self}.__hyperstack_component_native)` # react >= v0.15.0
32
28
  end
33
29
 
30
+ def jq_node
31
+ ::Element[dom_node]
32
+ end
33
+
34
34
  def mounted?
35
35
  `(#{self}.__hyperstack_component_is_mounted === undefined) ? false : #{self}.__hyperstack_component_is_mounted`
36
36
  end
37
37
 
38
+ def pluralize(count, singular, plural = nil)
39
+ word = if (count == 1 || count =~ /^1(\.0+)?$/)
40
+ singular
41
+ else
42
+ plural || singular.pluralize
43
+ end
44
+
45
+ "#{count || 0} #{word}"
46
+ end
47
+
38
48
  def force_update!
39
49
  `#{self}.__hyperstack_component_native.forceUpdate()`
40
50
  self
@@ -51,6 +61,29 @@ module Hyperstack
51
61
 
52
62
  private
53
63
 
64
+ # can be overriden by the Router include
65
+ def __hyperstack_router_wrapper(&block)
66
+ ->() { instance_eval(&block) }
67
+ end
68
+
69
+ # can be overriden by including WhileLoading include
70
+ def __hyperstack_component_rescue_wrapper(child)
71
+ if self.class.callbacks?(:__hyperstack_component_rescue_hook)
72
+ Hyperstack::Internal::Component::RescueWrapper(child: self, children_elements: child)
73
+ else
74
+ child.call
75
+ end
76
+ end
77
+
78
+ def __hyperstack_component_select_wrappers(&block)
79
+ RescueWrapper.after_error_args = nil
80
+ __hyperstack_component_run_post_render_hooks(
81
+ __hyperstack_component_rescue_wrapper(
82
+ __hyperstack_router_wrapper(&block)
83
+ )
84
+ )
85
+ end
86
+
54
87
  def set_or_replace_state_or_prop(state_or_prop, method, &block)
55
88
  raise "No native ReactComponent associated" unless @__hyperstack_component_native
56
89
  `var state_prop_n = #{state_or_prop.shallow_to_n}`
@@ -9,7 +9,7 @@ module Hyperstack
9
9
  def instance_var_name_for(name)
10
10
  case Hyperstack.naming_convention
11
11
  when :camelize_params
12
- name.camelize
12
+ fix_suffix(name.camelize)
13
13
  when :prefix_params
14
14
  "_#{name}"
15
15
  else
@@ -17,6 +17,17 @@ module Hyperstack
17
17
  end
18
18
  end
19
19
 
20
+ def fix_suffix(name)
21
+ return unless name
22
+ if name =~ /\?$/
23
+ name[0..-2] + '_q'
24
+ elsif name =~ /\!$/
25
+ name[0..-2] + '_b'
26
+ else
27
+ name
28
+ end
29
+ end
30
+
20
31
  def param_accessor_style(style = nil)
21
32
  @param_accessor_style = style if style
22
33
  @param_accessor_style ||=
@@ -37,17 +48,23 @@ module Hyperstack
37
48
  end
38
49
 
39
50
  def define_param(name, param_type, aka = nil)
40
- meth_name = aka || name
41
- var_name = aka || instance_var_name_for(name)
42
- param_definitions[name] = lambda do |props|
43
- @component.instance_variable_set :"@#{var_name}", fetch_from_cache(name, param_type, props)
51
+ if param_accessor_style != :legacy || aka
52
+ meth_name = aka || name
53
+ var_name = fix_suffix(aka) || instance_var_name_for(name)
54
+ param_definitions[name] = lambda do |props|
55
+ @component.instance_variable_set :"@#{var_name}", val = fetch_from_cache(name, param_type, props)
56
+ next unless param_accessor_style == :accessors
57
+ `#{@component}[#{"$#{meth_name}"}] = function() { return #{val} }`
58
+ # @component.define_singleton_method(name) { val } if param_accessor_style == :accessors
59
+ end
60
+ return if %i[hyperstack accessors].include? param_accessor_style
44
61
  end
45
62
  if param_type == Proc
46
- define_method(meth_name.to_sym) do |*args, &block|
63
+ define_method(name.to_sym) do |*args, &block|
47
64
  props[name].call(*args, &block) if props[name]
48
65
  end
49
66
  else
50
- define_method(meth_name.to_sym) do
67
+ define_method(name.to_sym) do
51
68
  fetch_from_cache(name, param_type, props)
52
69
  end
53
70
  end
@@ -56,7 +73,10 @@ module Hyperstack
56
73
  def define_all_others(name)
57
74
  var_name = instance_var_name_for(name)
58
75
  param_definitions[name] = lambda do |props|
59
- @component.instance_variable_set :"@#{var_name}", yield(props)
76
+ @component.instance_variable_set :"@#{var_name}", val = yield(props)
77
+ next unless param_accessor_style == :accessors
78
+ `#{@component}[#{"$#{name}"}] = function() { return #{val} }`
79
+ # @component.define_singleton_method(name) { val } if param_accessor_style == :accessors
60
80
  end
61
81
  define_method(name.to_sym) do
62
82
  @_all_others_cache ||= yield(props)
@@ -70,7 +90,7 @@ module Hyperstack
70
90
 
71
91
  def initialize(component, incoming = nil)
72
92
  @component = component
73
- return if param_accessor_style == :legacy
93
+ #return if param_accessor_style == :legacy
74
94
  self.class.param_definitions.each_value do |initializer|
75
95
  instance_exec(incoming || props, &initializer)
76
96
  end