hyper-component 0.99.6 → 1.0.alpha1

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -3
  3. data/Gemfile +4 -3
  4. data/Gemfile.lock +51 -36
  5. data/{misc/how-component-name-lookup-works.md → how-component-name-lookup-works.md} +1 -1
  6. data/hyper-component.gemspec +9 -8
  7. data/lib/hyper-component.rb +31 -43
  8. data/lib/hyperstack/component.rb +145 -0
  9. data/lib/hyperstack/component/auto-import.rb +44 -0
  10. data/lib/hyperstack/component/children.rb +40 -0
  11. data/lib/hyperstack/component/element.rb +129 -0
  12. data/lib/hyperstack/component/event.rb +78 -0
  13. data/lib/hyperstack/component/haml.rb +18 -0
  14. data/lib/hyperstack/component/isomorphic_helpers.rb +235 -0
  15. data/lib/hyperstack/component/jquery.rb +2 -0
  16. data/lib/hyperstack/component/native_library.rb +92 -0
  17. data/lib/hyperstack/component/react_api.rb +142 -0
  18. data/lib/hyperstack/component/server.rb +21 -0
  19. data/lib/hyperstack/component/version.rb +5 -0
  20. data/lib/hyperstack/ext/component/boolean.rb +14 -0
  21. data/lib/{react/ext/opal-jquery → hyperstack/ext/component}/element.rb +17 -12
  22. data/lib/{react/ext → hyperstack/ext/component}/hash.rb +0 -0
  23. data/lib/{react/to_key.rb → hyperstack/ext/component/number.rb} +0 -12
  24. data/lib/hyperstack/ext/component/object.rb +32 -0
  25. data/lib/{reactive-ruby → hyperstack/ext/component}/serializers.rb +0 -0
  26. data/lib/{react/ext → hyperstack/ext/component}/string.rb +0 -0
  27. data/lib/hyperstack/internal/component.rb +16 -0
  28. data/lib/hyperstack/internal/component/class_methods.rb +212 -0
  29. data/lib/hyperstack/internal/component/haml.rb +56 -0
  30. data/lib/hyperstack/internal/component/instance_methods.rb +92 -0
  31. data/lib/hyperstack/internal/component/props_wrapper.rb +125 -0
  32. data/lib/hyperstack/internal/component/rails.rb +11 -0
  33. data/lib/hyperstack/internal/component/rails/component_loader.rb +49 -0
  34. data/lib/hyperstack/internal/component/rails/component_mount.rb +52 -0
  35. data/lib/{reactive-ruby → hyperstack/internal/component}/rails/controller_helper.rb +0 -0
  36. data/lib/hyperstack/internal/component/rails/railtie.rb +24 -0
  37. data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +52 -0
  38. data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +52 -0
  39. data/lib/hyperstack/internal/component/react_wrapper.rb +308 -0
  40. data/lib/hyperstack/internal/component/rendering_context.rb +165 -0
  41. data/lib/hyperstack/internal/component/should_component_update.rb +101 -0
  42. data/lib/hyperstack/internal/component/tags.rb +109 -0
  43. data/lib/hyperstack/internal/component/top_level_rails_component.rb +83 -0
  44. data/lib/hyperstack/internal/component/validator.rb +149 -0
  45. data/lib/react/react-source.rb +2 -2
  46. data/unmounting-objects.md +78 -0
  47. metadata +73 -85
  48. data/DOCS.md +0 -1515
  49. data/LICENSE +0 -19
  50. data/README.md +0 -49
  51. data/lib/hyper-component/jquery.rb +0 -2
  52. data/lib/rails-helpers/top_level_rails_component.rb +0 -79
  53. data/lib/react/api.rb +0 -272
  54. data/lib/react/callbacks.rb +0 -42
  55. data/lib/react/children.rb +0 -38
  56. data/lib/react/component.rb +0 -189
  57. data/lib/react/component/api.rb +0 -70
  58. data/lib/react/component/base.rb +0 -13
  59. data/lib/react/component/class_methods.rb +0 -175
  60. data/lib/react/component/dsl_instance_methods.rb +0 -23
  61. data/lib/react/component/params.rb +0 -6
  62. data/lib/react/component/props_wrapper.rb +0 -90
  63. data/lib/react/component/should_component_update.rb +0 -99
  64. data/lib/react/component/tags.rb +0 -116
  65. data/lib/react/config.rb +0 -5
  66. data/lib/react/element.rb +0 -167
  67. data/lib/react/event.rb +0 -76
  68. data/lib/react/native_library.rb +0 -87
  69. data/lib/react/object.rb +0 -15
  70. data/lib/react/ref_callback.rb +0 -31
  71. data/lib/react/rendering_context.rb +0 -149
  72. data/lib/react/server.rb +0 -19
  73. data/lib/react/state_wrapper.rb +0 -23
  74. data/lib/react/test.rb +0 -16
  75. data/lib/react/test/dsl.rb +0 -17
  76. data/lib/react/test/matchers/render_html_matcher.rb +0 -56
  77. data/lib/react/test/rspec.rb +0 -15
  78. data/lib/react/test/session.rb +0 -37
  79. data/lib/react/test/utils.rb +0 -71
  80. data/lib/react/top_level.rb +0 -110
  81. data/lib/react/top_level_render.rb +0 -30
  82. data/lib/react/validator.rb +0 -132
  83. data/lib/reactive-ruby/component_loader.rb +0 -43
  84. data/lib/reactive-ruby/isomorphic_helpers.rb +0 -233
  85. data/lib/reactive-ruby/rails.rb +0 -8
  86. data/lib/reactive-ruby/rails/component_mount.rb +0 -48
  87. data/lib/reactive-ruby/rails/railtie.rb +0 -20
  88. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +0 -46
  89. data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +0 -46
  90. data/lib/reactive-ruby/version.rb +0 -5
  91. data/lib/reactrb/auto-import.rb +0 -27
  92. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +0 -3
  93. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +0 -5
  94. data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +0 -2
  95. data/misc/generators/reactive_ruby/test_app/templates/boot.rb.erb +0 -6
  96. data/misc/generators/reactive_ruby/test_app/templates/script/rails +0 -5
  97. data/misc/generators/reactive_ruby/test_app/templates/test_application.rb.erb +0 -13
  98. data/misc/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +0 -11
  99. data/misc/generators/reactive_ruby/test_app/templates/views/components/todo.rb +0 -14
  100. data/misc/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  101. data/misc/generators/reactive_ruby/test_app/test_app_generator.rb +0 -121
  102. data/misc/hyperloop-logo-small-pink.png +0 -0
  103. data/misc/logo1.png +0 -0
  104. data/misc/logo2.png +0 -0
  105. data/misc/logo3.png +0 -0
  106. data/path_release_steps.md +0 -9
@@ -1,15 +0,0 @@
1
- require 'react/test/dsl'
2
- require 'react/test/matchers/render_html_matcher'
3
-
4
- RSpec.configure do |config|
5
- config.include React::Test::DSL, type: :component
6
- config.include React::Test::Matchers, type: :component
7
-
8
- config.after do
9
- React::Test.reset_session!
10
- end
11
-
12
- config.before do
13
- # nothing yet
14
- end
15
- end
@@ -1,37 +0,0 @@
1
- module React
2
- module Test
3
- class Session
4
- DSL_METHODS = %i[mount instance update_params html].freeze
5
-
6
- def mount(component_klass, params = {})
7
- @element = React.create_element(component_klass, params)
8
- instance
9
- end
10
-
11
- def instance
12
- unless @instance
13
- @container = `document.createElement('div')`
14
- @instance = React.render(@element, @container)
15
- end
16
- @instance
17
- end
18
-
19
- def update_params(params, &block)
20
- cloned_element = React::Element.new(`React.cloneElement(#{@element.to_n}, #{params.to_n})`)
21
- React.render(cloned_element, @container, &block)
22
- nil
23
- end
24
-
25
- def html
26
- html = `#@container.innerHTML`
27
- %x{
28
- var REGEX_REMOVE_ROOT_IDS = /\s?data-reactroot="[^"]*"/g;
29
- var REGEX_REMOVE_IDS = /\s?data-reactid="[^"]+"/g;
30
- html = html.replace(REGEX_REMOVE_ROOT_IDS, '');
31
- html = html.replace(REGEX_REMOVE_IDS, '');
32
- }
33
- return html
34
- end
35
- end
36
- end
37
- end
@@ -1,71 +0,0 @@
1
- module React
2
- module Test
3
- class Utils
4
- def self.render_component_into_document(component, args = {})
5
- element = React.create_element(component, args)
6
- render_into_document(element)
7
- end
8
-
9
- def self.render_into_document(element)
10
- raise "You should pass a valid React::Element" unless React.is_valid_element?(element)
11
- dom_el = `document.body.querySelector('div[data-react-class="React.TopLevelRailsComponent"]').appendChild(document.createElement('div'))`
12
- React.render(element, dom_el)
13
- end
14
-
15
- def self.simulate_click(element)
16
- # element must be a component or a dom node or a element
17
- el = if `typeof element.nodeType !== "undefined"`
18
- element
19
- elsif element.respond_to? :dom_node
20
- element.dom_node
21
- elsif element.is_a? React::Element
22
- `ReactDOM.findDOMNode(#{element.to_n}.native)`
23
- else
24
- element
25
- end
26
- %x{
27
- var evob = new MouseEvent('click', {
28
- view: window,
29
- bubbles: true,
30
- cancelable: true
31
- });
32
- el.dispatchEvent(evob);
33
- }
34
- end
35
-
36
- def self.simulate_keydown(element, key_name = "Enter")
37
- # element must be a component or a dom node or a element
38
- el = if `typeof element.nodeType !== "undefined"`
39
- element
40
- elsif element.respond_to? :dom_node
41
- element.dom_node
42
- elsif element.is_a? React::Element
43
- `ReactDOM.findDOMNode(#{element.to_n}.native)`
44
- else
45
- element
46
- end
47
- %x{
48
- var evob = new KeyboardEvent('keydown', { key: key_name, bubbles: true, cancelable: true });
49
- el.dispatchEvent(evob);
50
- }
51
- end
52
-
53
- def self.simulate_submit(element)
54
- # element must be a component or a dom node or a element
55
- el = if `typeof element.nodeType !== "undefined"`
56
- element
57
- elsif element.respond_to? :dom_node
58
- element.dom_node
59
- elsif element.is_a? React::Element
60
- `ReactDOM.findDOMNode(#{element.to_n}.native)`
61
- else
62
- element
63
- end
64
- %x{
65
- var evob = new Event('submit', { bubbles: true, cancelable: true });
66
- el.dispatchEvent(evob);
67
- }
68
- end
69
- end
70
- end
71
- end
@@ -1,110 +0,0 @@
1
- require "native"
2
- require 'active_support/core_ext/object/try'
3
- require 'react/component/tags'
4
- require 'react/component/base'
5
-
6
- module React
7
-
8
- ATTRIBUTES = %w(accept acceptCharset accessKey action allowFullScreen allowTransparency alt
9
- async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
10
- className cols colSpan content contentEditable contextMenu controls coords
11
- crossOrigin data dateTime defer dir disabled download draggable encType form
12
- formAction formEncType formMethod formNoValidate formTarget frameBorder height
13
- hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
14
- marginHeight marginWidth max maxLength media mediaGroup method min multiple
15
- muted name noValidate open pattern placeholder poster preload radioGroup
16
- readOnly rel required role rows rowSpan sandbox scope scrolling seamless
17
- selected shape size sizes span spellCheck src srcDoc srcSet start step style
18
- tabIndex target title type useMap value width wmode dangerouslySetInnerHTML) +
19
- #SVG ATTRIBUTES
20
- %w(clipPath cx cy d dx dy fill fillOpacity fontFamily
21
- fontSize fx fy gradientTransform gradientUnits markerEnd
22
- markerMid markerStart offset opacity patternContentUnits
23
- patternUnits points preserveAspectRatio r rx ry spreadMethod
24
- stopColor stopOpacity stroke strokeDasharray strokeLinecap
25
- strokeOpacity strokeWidth textAnchor transform version
26
- viewBox x1 x2 x xlinkActuate xlinkArcrole xlinkHref xlinkRole
27
- xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace y1 y2 y)
28
- HASH_ATTRIBUTES = %w(data aria)
29
- HTML_TAGS = React::Component::Tags::HTML_TAGS
30
-
31
- def self.html_tag?(name)
32
- tags = HTML_TAGS
33
- %x{
34
- for(var i = 0; i < tags.length; i++) {
35
- if(tags[i] === name)
36
- return true;
37
- }
38
- return false;
39
- }
40
- end
41
-
42
- def self.html_attr?(name)
43
- attrs = ATTRIBUTES
44
- %x{
45
- for(var i = 0; i < attrs.length; i++) {
46
- if(attrs[i] === name)
47
- return true;
48
- }
49
- return false;
50
- }
51
- end
52
-
53
- def self.create_element(type, properties = {}, &block)
54
- React::API.create_element(type, properties, &block)
55
- end
56
-
57
- def self.render(element, container)
58
- %x{
59
- console.error(
60
- "Warning: Using deprecated behavior of `React.render`,",
61
- "require \"react/top_level_render\" to get the correct behavior."
62
- );
63
- }
64
- container = `container.$$class ? container[0] : container`
65
- if !(`typeof ReactDOM === 'undefined'`)
66
- component = Native(`ReactDOM.render(#{element.to_n}, container, function(){#{yield if block_given?}})`) # v0.15+
67
- else
68
- raise "render is not defined. In React >= v15 you must import it with ReactDOM"
69
- end
70
-
71
- component.class.include(React::Component::API)
72
- component
73
- end
74
-
75
- def self.is_valid_element(element)
76
- %x{ console.error("Warning: `is_valid_element` is deprecated in favor of `is_valid_element?`."); }
77
- element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
78
- end
79
-
80
- def self.is_valid_element?(element)
81
- element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
82
- end
83
-
84
- def self.render_to_string(element)
85
- %x{ console.error("Warning: `React.render_to_string` is deprecated in favor of `React::Server.render_to_string`."); }
86
- if !(`typeof ReactDOMServer === 'undefined'`)
87
- React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
88
- else
89
- raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
90
- end
91
- end
92
-
93
- def self.render_to_static_markup(element)
94
- %x{ console.error("Warning: `React.render_to_static_markup` is deprecated in favor of `React::Server.render_to_static_markup`."); }
95
- if !(`typeof ReactDOMServer === 'undefined'`)
96
- React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
97
- else
98
- raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
99
- end
100
- end
101
-
102
- def self.unmount_component_at_node(node)
103
- if !(`typeof ReactDOM === 'undefined'`)
104
- `ReactDOM.unmountComponentAtNode(node.$$class ? node[0] : node)` # v0.15+
105
- else
106
- raise "unmountComponentAtNode is not defined. In React >= v15 you must import it with ReactDOM"
107
- end
108
- end
109
-
110
- end
@@ -1,30 +0,0 @@
1
- module React
2
- def self.render(element, container)
3
- raise "ReactDOM.render is not defined. In React >= v15 you must import it with ReactDOM" if (`typeof ReactDOM === 'undefined'`)
4
-
5
- container = `container.$$class ? container[0] : container`
6
-
7
- if block_given?
8
- cb = %x{
9
- function(){
10
- setTimeout(function(){
11
- #{yield}
12
- }, 0)
13
- }
14
- }
15
- native = `ReactDOM.render(#{element.to_n}, container, cb)`
16
- else
17
- native = `ReactDOM.render(#{element.to_n}, container)`
18
- end
19
-
20
- return unless `#{native} !== null`
21
-
22
- if `#{native}.__opalInstance !== undefined && #{native}.__opalInstance !== null`
23
- `#{native}.__opalInstance`
24
- elsif `ReactDOM.findDOMNode !== undefined && #{native}.nodeType === undefined`
25
- `ReactDOM.findDOMNode(#{native})`
26
- else
27
- native
28
- end
29
- end
30
- end
@@ -1,132 +0,0 @@
1
- module React
2
- class Validator
3
- attr_accessor :errors
4
- attr_reader :props_wrapper
5
- private :errors, :props_wrapper
6
-
7
- def initialize(props_wrapper = Class.new(Component::PropsWrapper))
8
- @props_wrapper = props_wrapper
9
- end
10
-
11
- def self.build(&block)
12
- self.new.build(&block)
13
- end
14
-
15
- def build(&block)
16
- instance_eval(&block)
17
- self
18
- end
19
-
20
- def requires(name, options = {})
21
- options[:required] = true
22
- define_rule(name, options)
23
- end
24
-
25
- def optional(name, options = {})
26
- options[:required] = false
27
- define_rule(name, options)
28
- end
29
-
30
- def all_other_params(name)
31
- @allow_undefined_props = true
32
- props_wrapper.define_all_others(name) { |props| props.reject { |name, value| rules[name] } }
33
- end
34
-
35
- def validate(props)
36
- self.errors = []
37
- validate_undefined(props) unless allow_undefined_props?
38
- props = coerce_native_hash_values(defined_props(props))
39
- validate_required(props)
40
- props.each do |name, value|
41
- validate_types(name, value)
42
- validate_allowed(name, value)
43
- end
44
- errors
45
- end
46
-
47
- def default_props
48
- rules
49
- .select {|key, value| value.keys.include?("default") }
50
- .inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
51
- end
52
-
53
- private
54
-
55
- def defined_props(props)
56
- props.select { |name| rules.keys.include?(name) }
57
- end
58
-
59
- def allow_undefined_props?
60
- !!@allow_undefined_props
61
- end
62
-
63
- def rules
64
- @rules ||= { children: { required: false } }
65
- end
66
-
67
- def define_rule(name, options = {})
68
- rules[name] = coerce_native_hash_values(options)
69
- props_wrapper.define_param(name, options[:type])
70
- end
71
-
72
- def errors
73
- @errors ||= []
74
- end
75
-
76
- def validate_types(prop_name, value)
77
- return unless klass = rules[prop_name][:type]
78
- if !klass.is_a?(Array)
79
- allow_nil = !!rules[prop_name][:allow_nil]
80
- type_check("`#{prop_name}`", value, klass, allow_nil)
81
- elsif klass.length > 0
82
- validate_value_array(prop_name, value)
83
- else
84
- allow_nil = !!rules[prop_name][:allow_nil]
85
- type_check("`#{prop_name}`", value, Array, allow_nil)
86
- end
87
- end
88
-
89
- def type_check(prop_name, value, klass, allow_nil)
90
- return if allow_nil && value.nil?
91
- return if value.is_a?(klass)
92
- return if klass.respond_to?(:_react_param_conversion) &&
93
- klass._react_param_conversion(value, :validate_only)
94
- errors << "Provided prop #{prop_name} could not be converted to #{klass}"
95
- end
96
-
97
- def validate_allowed(prop_name, value)
98
- return unless values = rules[prop_name][:values]
99
- return if values.include?(value)
100
- errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value"
101
- end
102
-
103
- def validate_required(props)
104
- (rules.keys - props.keys).each do |name|
105
- next unless rules[name][:required]
106
- errors << "Required prop `#{name}` was not specified"
107
- end
108
- end
109
-
110
- def validate_undefined(props)
111
- (props.keys - rules.keys).each do |prop_name|
112
- errors << "Provided prop `#{prop_name}` not specified in spec"
113
- end
114
- end
115
-
116
- def validate_value_array(name, value)
117
- klass = rules[name][:type]
118
- allow_nil = !!rules[name][:allow_nil]
119
- value.each_with_index do |item, index|
120
- type_check("`#{name}`[#{index}]", Native(item), klass[0], allow_nil)
121
- end
122
- rescue NoMethodError
123
- errors << "Provided prop `#{name}` was not an Array"
124
- end
125
-
126
- def coerce_native_hash_values(hash)
127
- hash.each do |key, value|
128
- hash[key] = Native(value)
129
- end
130
- end
131
- end
132
- end
@@ -1,43 +0,0 @@
1
- module ReactiveRuby
2
- class ComponentLoader
3
- attr_reader :v8_context
4
- private :v8_context
5
-
6
- def initialize(v8_context)
7
- unless v8_context
8
- raise ArgumentError.new('Could not obtain ExecJS runtime context')
9
- end
10
- @v8_context = v8_context
11
- end
12
-
13
- def load(file = components)
14
- return true if loaded?
15
- !!v8_context.eval(opal(file))
16
- end
17
-
18
- def load!(file = components)
19
- return true if loaded?
20
- self.load(file)
21
- ensure
22
- raise "No HyperReact components found in #{components}" unless loaded?
23
- end
24
-
25
- def loaded?
26
- !!v8_context.eval('Opal.React !== undefined')
27
- rescue ::ExecJS::Error
28
- false
29
- end
30
-
31
- private
32
-
33
- def components
34
- opts = ::Rails.configuration.react.server_renderer_options
35
- return opts[:files].first.gsub(/.js$/,'') if opts && opts[:files]
36
- 'components'
37
- end
38
-
39
- def opal(file)
40
- Opal::Sprockets.load_asset(file)
41
- end
42
- end
43
- end
@@ -1,233 +0,0 @@
1
- require "react/config"
2
-
3
- module React
4
- module IsomorphicHelpers
5
- def self.included(base)
6
- base.extend(ClassMethods)
7
- end
8
-
9
- if RUBY_ENGINE != 'opal'
10
- def self.load_context(ctx, controller, name = nil)
11
- @context = Context.new("#{controller.object_id}-#{Time.now.to_i}", ctx, controller, name)
12
- @context.load_opal_context
13
- ::Rails.logger.debug "************************** React Server Context Initialized #{name} #{Time.now.to_f} *********************************************"
14
- @context
15
- end
16
- else
17
- def self.load_context(unique_id = nil, name = nil)
18
- # can be called on the client to force re-initialization for testing purposes
19
- if !unique_id || !@context || @context.unique_id != unique_id
20
- if on_opal_server?
21
- `console.history = []` rescue nil
22
- message = "************************ React Prerendering Context Initialized #{name} ***********************"
23
- else
24
- message = "************************ React Browser Context Initialized ****************************"
25
- end
26
- log(message)
27
- @context = Context.new(unique_id)
28
- end
29
- @context
30
- end
31
- end
32
-
33
- def self.context
34
- @context
35
- end
36
-
37
- def self.log(message, message_type = :info)
38
- message = [message] unless message.is_a? Array
39
-
40
- if (message_type == :info || message_type == :warning) && Hyperloop.env.production?
41
- return
42
- end
43
-
44
- if message_type == :info
45
- if on_opal_server?
46
- style = 'background: #00FFFF; color: red'
47
- else
48
- style = 'background: #222; color: #bada55'
49
- end
50
- message = ["%c" + message[0], style]+message[1..-1]
51
- `console.log.apply(console, message)`
52
- elsif message_type == :warning
53
- `console.warn.apply(console, message)`
54
- else
55
- `console.error.apply(console, message)`
56
- end
57
- end
58
-
59
- if RUBY_ENGINE != 'opal'
60
- def self.on_opal_server?
61
- false
62
- end
63
-
64
- def self.on_opal_client?
65
- false
66
- end
67
- else
68
- def self.on_opal_server?
69
- `typeof Opal.global.document === 'undefined'`
70
- end
71
-
72
- def self.on_opal_client?
73
- !on_opal_server?
74
- end
75
- end
76
-
77
- def log(*args)
78
- IsomorphicHelpers.log(*args)
79
- end
80
-
81
- def on_opal_server?
82
- self.class.on_opal_server?
83
- end
84
-
85
- def on_opal_client?
86
- self.class.on_opal_client?
87
- end
88
-
89
- def self.prerender_footers(controller = nil)
90
- footer = Context.prerender_footer_blocks.collect { |block| block.call controller }.join("\n")
91
- if RUBY_ENGINE != 'opal'
92
- footer = (footer + @context.send_to_opal(:prerender_footers).to_s) if @context
93
- footer = footer.html_safe
94
- end
95
- footer
96
- end
97
-
98
- class Context
99
- attr_reader :controller
100
- attr_reader :unique_id
101
-
102
- def self.define_isomorphic_method(method_name, &block)
103
- @@ctx_methods ||= {}
104
- @@ctx_methods[method_name] = block
105
- end
106
-
107
- def self.before_first_mount_blocks
108
- @before_first_mount_blocks ||= []
109
- end
110
-
111
- def self.prerender_footer_blocks
112
- @prerender_footer_blocks ||= []
113
- end
114
-
115
- def initialize(unique_id, ctx = nil, controller = nil, cname = nil)
116
- @unique_id = unique_id
117
- @cname = cname
118
- if RUBY_ENGINE != 'opal'
119
- @controller = controller
120
- @ctx = ctx
121
- if defined? @@ctx_methods
122
- @@ctx_methods.each do |method_name, block|
123
- @ctx.attach("ServerSideIsomorphicMethod.#{method_name}", proc{|args| block.call(args.to_json)})
124
- end
125
- end
126
- end
127
- Hyperloop::Application::Boot.run(context: self)
128
- self.class.before_first_mount_blocks.each { |block| block.call(self) }
129
- end
130
-
131
- def load_opal_context
132
- send_to_opal(:load_context, @unique_id, @cname)
133
- end
134
-
135
- def eval(js)
136
- @ctx.eval(js) if @ctx
137
- end
138
-
139
- def send_to_opal(method_name, *args)
140
- return unless @ctx
141
- args = [1] if args.length == 0
142
- ::ReactiveRuby::ComponentLoader.new(@ctx).load!
143
- method_args = args.collect do |arg|
144
- quarg = "#{arg}".tr('"', "'")
145
- "\"#{quarg}\""
146
- end.join(', ')
147
- @ctx.eval("Opal.React.$const_get('IsomorphicHelpers').$#{method_name}(#{method_args})")
148
- end
149
-
150
- def self.register_before_first_mount_block(&block)
151
- before_first_mount_blocks << block
152
- end
153
-
154
- def self.register_prerender_footer_block(&block)
155
- prerender_footer_blocks << block
156
- end
157
- end
158
-
159
- class IsomorphicProcCall
160
-
161
- attr_reader :context
162
-
163
- def result
164
- @result.first if @result
165
- end
166
-
167
- def initialize(name, block, context, *args)
168
- @name = name
169
- @context = context
170
- block.call(self, *args)
171
- @result ||= send_to_server(*args)
172
- end
173
-
174
- def when_on_client(&block)
175
- @result = [block.call] if IsomorphicHelpers.on_opal_client?
176
- end
177
-
178
- def send_to_server(*args)
179
- if IsomorphicHelpers.on_opal_server?
180
- method_string = "ServerSideIsomorphicMethod." + @name + "(" + args.to_json + ")"
181
- @result = [JSON.parse(`eval(method_string)`)]
182
- end
183
- end
184
-
185
- def when_on_server(&block)
186
- @result = [block.call.to_json] unless IsomorphicHelpers.on_opal_client? || IsomorphicHelpers.on_opal_server?
187
- end
188
- end
189
-
190
- module ClassMethods
191
- def on_opal_server?
192
- IsomorphicHelpers.on_opal_server?
193
- end
194
-
195
- def on_opal_client?
196
- IsomorphicHelpers.on_opal_client?
197
- end
198
-
199
- def log(*args)
200
- IsomorphicHelpers.log(*args)
201
- end
202
-
203
- def controller
204
- IsomorphicHelpers.context.controller
205
- end
206
-
207
- def before_first_mount(&block)
208
- React::IsomorphicHelpers::Context.register_before_first_mount_block(&block)
209
- end
210
-
211
- def prerender_footer(&block)
212
- React::IsomorphicHelpers::Context.register_prerender_footer_block(&block)
213
- end
214
-
215
- if RUBY_ENGINE != 'opal'
216
- def isomorphic_method(name, &block)
217
- React::IsomorphicHelpers::Context.send(:define_isomorphic_method, name) do |args_as_json|
218
- React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *JSON.parse(args_as_json)).result
219
- end
220
- end
221
- else
222
- require 'json'
223
-
224
- def isomorphic_method(name, &block)
225
- self.class.send(:define_method, name) do | *args |
226
- React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *args).result
227
- end
228
- end
229
- end
230
-
231
- end
232
- end
233
- end