hyper-component 1.0.alpha1.2 → 1.0.alpha1.7

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 +8 -16
  5. data/lib/hyper-component.rb +12 -1
  6. data/lib/hyperstack/component.rb +60 -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 -10
  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/ext/component/time.rb +5 -0
  20. data/lib/hyperstack/internal/component.rb +7 -1
  21. data/lib/hyperstack/internal/component/class_methods.rb +68 -7
  22. data/lib/hyperstack/internal/component/haml.rb +3 -12
  23. data/lib/hyperstack/internal/component/instance_methods.rb +38 -1
  24. data/lib/hyperstack/internal/component/props_wrapper.rb +29 -10
  25. data/lib/hyperstack/internal/component/rails/component_mount.rb +12 -0
  26. data/lib/hyperstack/internal/component/rails/controller_helper.rb +20 -1
  27. data/lib/hyperstack/internal/component/rails/railtie.rb +3 -2
  28. data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +5 -1
  29. data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +4 -2
  30. data/lib/hyperstack/internal/component/react_wrapper.rb +79 -62
  31. data/lib/hyperstack/internal/component/rendering_context.rb +95 -45
  32. data/lib/hyperstack/internal/component/rescue_wrapper.rb +40 -0
  33. data/lib/hyperstack/internal/component/should_component_update.rb +1 -1
  34. data/lib/hyperstack/internal/component/tags.rb +12 -3
  35. data/lib/hyperstack/internal/component/while_loading_wrapper.rb +29 -0
  36. data/lib/react/react-source.rb +2 -2
  37. metadata +56 -86
  38. data/Gemfile.lock +0 -363
@@ -0,0 +1,21 @@
1
+ module Hyperstack
2
+ module Component
3
+ module FreeRender
4
+ def self.included(base)
5
+ base.instance_eval do
6
+ alias :hyperstack_component_original_meth_missing method_missing
7
+ def method_missing(name, *args, &block)
8
+ if const_defined?(name) &&
9
+ (klass = const_get(name)) &&
10
+ ((klass.is_a?(Class) && klass.method_defined?(:render)) ||
11
+ Hyperstack::Internal::Component::Tags::HTML_TAGS.include?(klass))
12
+ render(klass, *args, &block)
13
+ else
14
+ hyperstack_component_original_meth_missing(name, *args, &block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -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('"', "'")
@@ -220,8 +225,6 @@ module Hyperstack
220
225
  end
221
226
  end
222
227
  else
223
- require 'json'
224
-
225
228
  def isomorphic_method(name, &block)
226
229
  self.class.send(:define_method, name) do | *args |
227
230
  IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *args).result
@@ -1,5 +1,5 @@
1
1
  module Hyperstack
2
2
  module Component
3
- VERSION = '1.0.alpha1.2' # '1.0.alpha1.2'
3
+ VERSION = '1.0.alpha1.7' # '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
@@ -0,0 +1,5 @@
1
+ class Time
2
+ def to_json
3
+ strftime("%FT%T.%3N%z").to_json
4
+ end
5
+ 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
@@ -42,18 +42,74 @@ module Hyperstack
42
42
  backtrace[1..-1].each { |line| message_array << line }
43
43
  end
44
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
+
45
50
  def render(container = nil, params = {}, &block)
46
51
  Tags.included(self)
47
52
  if container
48
- container = container.type if container.is_a? Hyperstack::Component::Element
49
- define_method :__hyperstack_component_render do
50
- 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
51
60
  end
52
61
  else
53
- define_method(:__hyperstack_component_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
67
+ end
68
+ end
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]
54
88
  end
55
89
  end
56
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
+
57
113
  # method missing will assume the method is a class name, and will treat this a render of
58
114
  # of the component, i.e. Foo::Bar.baz === Foo::Bar().baz
59
115
 
@@ -147,8 +203,10 @@ module Hyperstack
147
203
 
148
204
  alias other_params collect_other_params_as
149
205
  alias others collect_other_params_as
206
+ alias other collect_other_params_as
207
+ alias opts collect_other_params_as
150
208
 
151
- def triggers(name, opts = {})
209
+ def fires(name, opts = {})
152
210
  aka = opts[:alias] || "#{name}!"
153
211
  name = if name =~ /^<(.+)>$/
154
212
  name.gsub(/^<(.+)>$/, '\1')
@@ -161,6 +219,8 @@ module Hyperstack
161
219
  define_method(aka) { |*args| props[name]&.call(*args) }
162
220
  end
163
221
 
222
+ alias triggers fires
223
+
164
224
  def define_state(*states, &block)
165
225
  deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
166
226
  default_initial_value = (block && block.arity == 0) ? yield : nil
@@ -211,7 +271,8 @@ module Hyperstack
211
271
  ReactWrapper.import_native_component(
212
272
  self, ReactWrapper.eval_native_react_component(component_name)
213
273
  )
214
- 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...
215
276
  rescue Exception => e # rubocop:disable Lint/RescueException : we need to catch everything!
216
277
  raise "#{self} cannot import '#{component_name}': #{e.message}."
217
278
  # rubocop:enable Lint/RescueException
@@ -231,7 +292,7 @@ module Hyperstack
231
292
  end
232
293
 
233
294
  def to_n
234
- ReactWrapper.class_eval('@@component_classes')[self]
295
+ Hyperstack::Internal::Component::ReactWrapper.create_native_react_class(self)
235
296
  end
236
297
  end
237
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"\
@@ -27,10 +27,24 @@ module Hyperstack
27
27
  `ReactDOM.findDOMNode(#{self}.__hyperstack_component_native)` # react >= v0.15.0
28
28
  end
29
29
 
30
+ def jq_node
31
+ ::Element[dom_node]
32
+ end
33
+
30
34
  def mounted?
31
35
  `(#{self}.__hyperstack_component_is_mounted === undefined) ? false : #{self}.__hyperstack_component_is_mounted`
32
36
  end
33
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
+
34
48
  def force_update!
35
49
  `#{self}.__hyperstack_component_native.forceUpdate()`
36
50
  self
@@ -47,6 +61,29 @@ module Hyperstack
47
61
 
48
62
  private
49
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
+
50
87
  def set_or_replace_state_or_prop(state_or_prop, method, &block)
51
88
  raise "No native ReactComponent associated" unless @__hyperstack_component_native
52
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,18 +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
- return if param_accessor_style == :hyperstack
46
62
  if param_type == Proc
47
- define_method(meth_name.to_sym) do |*args, &block|
63
+ define_method(name.to_sym) do |*args, &block|
48
64
  props[name].call(*args, &block) if props[name]
49
65
  end
50
66
  else
51
- define_method(meth_name.to_sym) do
67
+ define_method(name.to_sym) do
52
68
  fetch_from_cache(name, param_type, props)
53
69
  end
54
70
  end
@@ -57,7 +73,10 @@ module Hyperstack
57
73
  def define_all_others(name)
58
74
  var_name = instance_var_name_for(name)
59
75
  param_definitions[name] = lambda do |props|
60
- @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
61
80
  end
62
81
  define_method(name.to_sym) do
63
82
  @_all_others_cache ||= yield(props)
@@ -71,7 +90,7 @@ module Hyperstack
71
90
 
72
91
  def initialize(component, incoming = nil)
73
92
  @component = component
74
- return if param_accessor_style == :legacy
93
+ #return if param_accessor_style == :legacy
75
94
  self.class.param_definitions.each_value do |initializer|
76
95
  instance_exec(incoming || props, &initializer)
77
96
  end