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

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.
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