hyper-component 0.99.6 → 1.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -3
- data/Gemfile +4 -3
- data/Gemfile.lock +51 -36
- data/{misc/how-component-name-lookup-works.md → how-component-name-lookup-works.md} +1 -1
- data/hyper-component.gemspec +9 -8
- data/lib/hyper-component.rb +31 -43
- data/lib/hyperstack/component.rb +145 -0
- data/lib/hyperstack/component/auto-import.rb +44 -0
- data/lib/hyperstack/component/children.rb +40 -0
- data/lib/hyperstack/component/element.rb +129 -0
- data/lib/hyperstack/component/event.rb +78 -0
- data/lib/hyperstack/component/haml.rb +18 -0
- data/lib/hyperstack/component/isomorphic_helpers.rb +235 -0
- data/lib/hyperstack/component/jquery.rb +2 -0
- data/lib/hyperstack/component/native_library.rb +92 -0
- data/lib/hyperstack/component/react_api.rb +142 -0
- data/lib/hyperstack/component/server.rb +21 -0
- data/lib/hyperstack/component/version.rb +5 -0
- data/lib/hyperstack/ext/component/boolean.rb +14 -0
- data/lib/{react/ext/opal-jquery → hyperstack/ext/component}/element.rb +17 -12
- data/lib/{react/ext → hyperstack/ext/component}/hash.rb +0 -0
- data/lib/{react/to_key.rb → hyperstack/ext/component/number.rb} +0 -12
- data/lib/hyperstack/ext/component/object.rb +32 -0
- data/lib/{reactive-ruby → hyperstack/ext/component}/serializers.rb +0 -0
- data/lib/{react/ext → hyperstack/ext/component}/string.rb +0 -0
- data/lib/hyperstack/internal/component.rb +16 -0
- data/lib/hyperstack/internal/component/class_methods.rb +212 -0
- data/lib/hyperstack/internal/component/haml.rb +56 -0
- data/lib/hyperstack/internal/component/instance_methods.rb +92 -0
- data/lib/hyperstack/internal/component/props_wrapper.rb +125 -0
- data/lib/hyperstack/internal/component/rails.rb +11 -0
- data/lib/hyperstack/internal/component/rails/component_loader.rb +49 -0
- data/lib/hyperstack/internal/component/rails/component_mount.rb +52 -0
- data/lib/{reactive-ruby → hyperstack/internal/component}/rails/controller_helper.rb +0 -0
- data/lib/hyperstack/internal/component/rails/railtie.rb +24 -0
- data/lib/hyperstack/internal/component/rails/server_rendering/contextual_renderer.rb +52 -0
- data/lib/hyperstack/internal/component/rails/server_rendering/hyper_asset_container.rb +52 -0
- data/lib/hyperstack/internal/component/react_wrapper.rb +308 -0
- data/lib/hyperstack/internal/component/rendering_context.rb +165 -0
- data/lib/hyperstack/internal/component/should_component_update.rb +101 -0
- data/lib/hyperstack/internal/component/tags.rb +109 -0
- data/lib/hyperstack/internal/component/top_level_rails_component.rb +83 -0
- data/lib/hyperstack/internal/component/validator.rb +149 -0
- data/lib/react/react-source.rb +2 -2
- data/unmounting-objects.md +78 -0
- metadata +73 -85
- data/DOCS.md +0 -1515
- data/LICENSE +0 -19
- data/README.md +0 -49
- data/lib/hyper-component/jquery.rb +0 -2
- data/lib/rails-helpers/top_level_rails_component.rb +0 -79
- data/lib/react/api.rb +0 -272
- data/lib/react/callbacks.rb +0 -42
- data/lib/react/children.rb +0 -38
- data/lib/react/component.rb +0 -189
- data/lib/react/component/api.rb +0 -70
- data/lib/react/component/base.rb +0 -13
- data/lib/react/component/class_methods.rb +0 -175
- data/lib/react/component/dsl_instance_methods.rb +0 -23
- data/lib/react/component/params.rb +0 -6
- data/lib/react/component/props_wrapper.rb +0 -90
- data/lib/react/component/should_component_update.rb +0 -99
- data/lib/react/component/tags.rb +0 -116
- data/lib/react/config.rb +0 -5
- data/lib/react/element.rb +0 -167
- data/lib/react/event.rb +0 -76
- data/lib/react/native_library.rb +0 -87
- data/lib/react/object.rb +0 -15
- data/lib/react/ref_callback.rb +0 -31
- data/lib/react/rendering_context.rb +0 -149
- data/lib/react/server.rb +0 -19
- data/lib/react/state_wrapper.rb +0 -23
- data/lib/react/test.rb +0 -16
- data/lib/react/test/dsl.rb +0 -17
- data/lib/react/test/matchers/render_html_matcher.rb +0 -56
- data/lib/react/test/rspec.rb +0 -15
- data/lib/react/test/session.rb +0 -37
- data/lib/react/test/utils.rb +0 -71
- data/lib/react/top_level.rb +0 -110
- data/lib/react/top_level_render.rb +0 -30
- data/lib/react/validator.rb +0 -132
- data/lib/reactive-ruby/component_loader.rb +0 -43
- data/lib/reactive-ruby/isomorphic_helpers.rb +0 -233
- data/lib/reactive-ruby/rails.rb +0 -8
- data/lib/reactive-ruby/rails/component_mount.rb +0 -48
- data/lib/reactive-ruby/rails/railtie.rb +0 -20
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +0 -46
- data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +0 -46
- data/lib/reactive-ruby/version.rb +0 -5
- data/lib/reactrb/auto-import.rb +0 -27
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +0 -3
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +0 -5
- data/misc/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +0 -2
- data/misc/generators/reactive_ruby/test_app/templates/boot.rb.erb +0 -6
- data/misc/generators/reactive_ruby/test_app/templates/script/rails +0 -5
- data/misc/generators/reactive_ruby/test_app/templates/test_application.rb.erb +0 -13
- data/misc/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +0 -11
- data/misc/generators/reactive_ruby/test_app/templates/views/components/todo.rb +0 -14
- data/misc/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
- data/misc/generators/reactive_ruby/test_app/test_app_generator.rb +0 -121
- data/misc/hyperloop-logo-small-pink.png +0 -0
- data/misc/logo1.png +0 -0
- data/misc/logo2.png +0 -0
- data/misc/logo3.png +0 -0
- data/path_release_steps.md +0 -9
@@ -0,0 +1,44 @@
|
|
1
|
+
# rubocop:disable Style/FileName
|
2
|
+
# require 'reactrb/auto-import' to automatically
|
3
|
+
# import JS libraries and components when they are detected
|
4
|
+
if RUBY_ENGINE == 'opal'
|
5
|
+
# modifies const and method_missing so that they will attempt
|
6
|
+
# to auto import native libraries and components using Hyperstack::Component::NativeLibrary
|
7
|
+
class Object
|
8
|
+
class << self
|
9
|
+
alias _reactrb_original_const_missing const_missing
|
10
|
+
alias _reactrb_original_method_missing method_missing
|
11
|
+
|
12
|
+
def const_missing(const_name)
|
13
|
+
# Opal uses const_missing to initially define things,
|
14
|
+
# so we always call the original, and respond to the exception
|
15
|
+
_reactrb_original_const_missing(const_name)
|
16
|
+
rescue StandardError => e
|
17
|
+
Hyperstack::Internal::Component::NativeLibrary.import_const_from_native(Object, const_name, true) || raise(e)
|
18
|
+
end
|
19
|
+
|
20
|
+
def _reactrb_import_component_class(method)
|
21
|
+
Hyperstack::Internal::Component::NativeLibrary.import_const_from_native(self, method, false)
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method, *args, &block)
|
25
|
+
component_class = _reactrb_import_component_class(method)
|
26
|
+
_reactrb_original_method_missing(method, *args, &block) unless component_class
|
27
|
+
Hyperstack::Internal::Component::RenderingContext.render(component_class, *args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# The public NativeLibrary can't be used directly to
|
33
|
+
# import_const_from_native, because it is set to import from
|
34
|
+
# `window.NativeLibrary`. So we set up an internal class that won't
|
35
|
+
# have any prefix defined.
|
36
|
+
module Hyperstack
|
37
|
+
module Internal
|
38
|
+
module Component
|
39
|
+
class NativeLibrary < Hyperstack::Component::NativeLibrary
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
module Component
|
3
|
+
class Children
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(children)
|
7
|
+
@children = children
|
8
|
+
end
|
9
|
+
|
10
|
+
def render
|
11
|
+
each(&:render)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_proc
|
15
|
+
-> () { render }
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(&block)
|
19
|
+
return to_enum(__callee__) { length } unless block_given?
|
20
|
+
return [] unless length > 0
|
21
|
+
collection = []
|
22
|
+
%x{
|
23
|
+
React.Children.forEach(#{@children}, function(context){
|
24
|
+
#{
|
25
|
+
element = Element.new(`context`)
|
26
|
+
block.call(element)
|
27
|
+
collection << element
|
28
|
+
}
|
29
|
+
})
|
30
|
+
}
|
31
|
+
collection
|
32
|
+
end
|
33
|
+
|
34
|
+
def length
|
35
|
+
@length ||= `React.Children.count(#{@children})`
|
36
|
+
end
|
37
|
+
alias_method :size, :length
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'hyperstack/ext/component/string'
|
2
|
+
|
3
|
+
module Hyperstack
|
4
|
+
module Component
|
5
|
+
#
|
6
|
+
# Wraps the React Native element class
|
7
|
+
#
|
8
|
+
# adds the #on method to add event handlers to the element
|
9
|
+
#
|
10
|
+
# adds the #render method to place elements in the DOM and
|
11
|
+
# #delete (alias/deprecated #as_node) method to remove elements from the DOM
|
12
|
+
#
|
13
|
+
# handles the haml style class notation so that
|
14
|
+
# div.bar.blat becomes div(class: "bar blat")
|
15
|
+
# by using method missing
|
16
|
+
#
|
17
|
+
class Element
|
18
|
+
include Native
|
19
|
+
|
20
|
+
alias_native :element_type, :type
|
21
|
+
alias_native :props, :props
|
22
|
+
|
23
|
+
attr_reader :type
|
24
|
+
attr_reader :properties
|
25
|
+
attr_reader :block
|
26
|
+
|
27
|
+
attr_accessor :waiting_on_resources
|
28
|
+
|
29
|
+
def initialize(native_element, type = nil, properties = {}, block = nil)
|
30
|
+
@type = type
|
31
|
+
@properties = (`typeof #{properties} === 'undefined'` ? nil : properties) || {}
|
32
|
+
@block = block
|
33
|
+
@native = native_element
|
34
|
+
end
|
35
|
+
|
36
|
+
# Attach event handlers.
|
37
|
+
|
38
|
+
def on(*event_names, &block)
|
39
|
+
event_names.each { |event_name| merge_event_prop!(event_name, &block) }
|
40
|
+
@native = `React.cloneElement(#{@native}, #{@properties.shallow_to_n})`
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Render element into DOM in the current rendering context.
|
45
|
+
# Used for elements that are not yet in DOM, i.e. they are provided as children
|
46
|
+
# or they have been explicitly removed from the rendering context using the delete method.
|
47
|
+
|
48
|
+
def render(props = {}, &new_block)
|
49
|
+
if props.empty?
|
50
|
+
Hyperstack::Internal::Component::RenderingContext.render(self)
|
51
|
+
else
|
52
|
+
props = Hyperstack::Internal::Component::ReactWrapper.convert_props(props)
|
53
|
+
Hyperstack::Internal::Component::RenderingContext.render(
|
54
|
+
Element.new(`React.cloneElement(#{@native}, #{props.shallow_to_n})`,
|
55
|
+
type, @properties.merge(props), block)
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Delete (remove) element from rendering context, the element may later be added back in
|
61
|
+
# using the render method.
|
62
|
+
|
63
|
+
def delete
|
64
|
+
Hyperstack::Internal::Component::RenderingContext.delete(self)
|
65
|
+
end
|
66
|
+
# Deprecated version of delete method
|
67
|
+
alias as_node delete
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# built in events, events going to native components, and events going to reactrb
|
72
|
+
|
73
|
+
# built in events will have their event param translated to the Event wrapper
|
74
|
+
# and the name will camelcased and have on prefixed, so :click becomes onClick.
|
75
|
+
#
|
76
|
+
# events emitting from native components are assumed to have the same camel case and
|
77
|
+
# on prefixed.
|
78
|
+
#
|
79
|
+
# events emitting from reactrb components will just have on_ prefixed. So
|
80
|
+
# :play_button_pushed attaches to the :on_play_button_pushed param
|
81
|
+
#
|
82
|
+
# in all cases the default name convention can be overriden by wrapping in <...> brackets.
|
83
|
+
# So on("<MyEvent>") will attach to the "MyEvent" param.
|
84
|
+
|
85
|
+
def merge_event_prop!(event_name, &block)
|
86
|
+
if event_name =~ /^<(.+)>$/
|
87
|
+
merge_component_event_prop! event_name.gsub(/^<(.+)>$/, '\1'), &block
|
88
|
+
elsif Event::BUILT_IN_EVENTS.include?(name = "on#{event_name.event_camelize}")
|
89
|
+
merge_built_in_event_prop! name, &block
|
90
|
+
elsif event_name == :enter
|
91
|
+
merge_built_in_event_prop!('onKeyDown') { |evt| yield(evt) if evt.key_code == 13 }
|
92
|
+
elsif @type.instance_variable_get('@native_import')
|
93
|
+
merge_component_event_prop! name, &block
|
94
|
+
else
|
95
|
+
merge_component_event_prop! "on_#{event_name}", &block
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def merge_built_in_event_prop!(prop_name)
|
100
|
+
@properties.merge!(
|
101
|
+
prop_name => %x{
|
102
|
+
function(){
|
103
|
+
var react_event = arguments[0];
|
104
|
+
var all_args;
|
105
|
+
var other_args;
|
106
|
+
if (arguments.length > 1) {
|
107
|
+
all_args = Array.prototype.slice.call(arguments);
|
108
|
+
other_args = all_args.slice(1, arguments.length);
|
109
|
+
return #{yield(Event.new(`react_event`), *(`other_args`))};
|
110
|
+
} else {
|
111
|
+
return #{yield(Event.new(`react_event`))};
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
def merge_component_event_prop!(prop_name)
|
119
|
+
@properties.merge!(
|
120
|
+
prop_name => %x{
|
121
|
+
function(){
|
122
|
+
return #{yield(*Array(`arguments`))}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
module Component
|
3
|
+
class Event
|
4
|
+
include Native
|
5
|
+
alias_native :bubbles, :bubbles
|
6
|
+
alias_native :cancelable, :cancelable
|
7
|
+
alias_native :current_target, :currentTarget
|
8
|
+
alias_native :default_prevented, :defaultPrevented
|
9
|
+
alias_native :event_phase, :eventPhase
|
10
|
+
alias_native :is_trusted?, :isTrusted
|
11
|
+
alias_native :native_event, :nativeEvent
|
12
|
+
alias_native :target, :target
|
13
|
+
alias_native :timestamp, :timeStamp
|
14
|
+
alias_native :event_type, :type
|
15
|
+
alias_native :prevent_default, :preventDefault
|
16
|
+
alias_native :stop_propagation, :stopPropagation
|
17
|
+
# Clipboard
|
18
|
+
alias_native :clipboard_data, :clipboardData
|
19
|
+
# Keyboard
|
20
|
+
alias_native :alt_key, :altKey
|
21
|
+
alias_native :char_code, :charCode
|
22
|
+
alias_native :ctrl_key, :ctrlKey
|
23
|
+
alias_native :get_modifier_state, :getModifierState
|
24
|
+
alias_native :key, :key
|
25
|
+
alias_native :key_code, :keyCode
|
26
|
+
alias_native :locale, :locale
|
27
|
+
alias_native :location, :location
|
28
|
+
alias_native :meta_key, :metaKey
|
29
|
+
alias_native :repeat, :repeat
|
30
|
+
alias_native :shift_key, :shiftKey
|
31
|
+
alias_native :which, :which
|
32
|
+
# Focus
|
33
|
+
alias_native :related_target, :relatedTarget
|
34
|
+
# Mouse
|
35
|
+
# aliased above: alias_native :alt_key, :altKey
|
36
|
+
alias_native :button, :button
|
37
|
+
alias_native :buttons, :buttons
|
38
|
+
alias_native :client_x, :clientX
|
39
|
+
alias_native :client_y, :clientY
|
40
|
+
# aliased above: alias_native :ctrl_key, :ctrlKey
|
41
|
+
alias_native :get_modifier_state, :getModifierState
|
42
|
+
# aliased above: alias_native :meta_key, :metaKey
|
43
|
+
alias_native :page_x, :pageX
|
44
|
+
alias_native :page_y, :pageY
|
45
|
+
# aliased above: alias_native :related_target, :relatedTarget
|
46
|
+
alias_native :screen_x, :screen_x
|
47
|
+
alias_native :screen_y, :screen_y
|
48
|
+
# aliased above: alias_native :shift_key, :shift_key
|
49
|
+
# Touch
|
50
|
+
# aliased above: alias_native :alt_key, :altKey
|
51
|
+
alias_native :changed_touches, :changedTouches
|
52
|
+
# aliased above: alias_native :ctrl_key, :ctrlKey
|
53
|
+
# aliased above: alias_native :get_modifier_state, :getModifierState
|
54
|
+
# aliased above: alias_native :meta_key, :metaKey
|
55
|
+
# aliased above: alias_native :shift_key, :shiftKey
|
56
|
+
alias_native :target_touches, :targetTouches
|
57
|
+
alias_native :touches, :touches
|
58
|
+
# UI
|
59
|
+
alias_native :detail, :detail
|
60
|
+
alias_native :view, :view
|
61
|
+
# Wheel
|
62
|
+
alias_native :delta_mode, :deltaMode
|
63
|
+
alias_native :delta_x, :deltaX
|
64
|
+
alias_native :delta_y, :deltaY
|
65
|
+
alias_native :delta_z, :deltaZ
|
66
|
+
|
67
|
+
BUILT_IN_EVENTS = %w{onCopy onCut onPaste onKeyDown onKeyPress onKeyUp
|
68
|
+
onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick onDrag
|
69
|
+
onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop
|
70
|
+
onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver
|
71
|
+
onMouseUp onSelect onTouchCancel onTouchEnd onTouchMove onTouchStart onScroll onWheel}
|
72
|
+
|
73
|
+
def initialize(native_event)
|
74
|
+
@native = native_event
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'hyperstack/internal/component/haml'
|
2
|
+
# to allow for easier testing we include the internal mixins
|
3
|
+
# from hyperstack/internal/component/haml
|
4
|
+
# see spec/deprecated_features/haml_spec
|
5
|
+
module Hyperstack
|
6
|
+
module Internal
|
7
|
+
module Component
|
8
|
+
module Tags
|
9
|
+
include HAMLTagInstanceMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
module Component
|
14
|
+
class Element
|
15
|
+
include HAMLElementInstanceMethods
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require "hyperstack/internal/component"
|
2
|
+
|
3
|
+
module Hyperstack
|
4
|
+
module Component
|
5
|
+
module IsomorphicHelpers
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
if RUBY_ENGINE != 'opal'
|
11
|
+
def self.load_context(ctx, controller, name = nil)
|
12
|
+
@context = Context.new("#{controller.object_id}-#{Time.now.to_i}", ctx, controller, name)
|
13
|
+
@context.load_opal_context
|
14
|
+
::Rails.logger.debug "************************** React Server Context Initialized #{name} #{Time.now.to_f} *********************************************"
|
15
|
+
@context
|
16
|
+
end
|
17
|
+
else
|
18
|
+
def self.load_context(unique_id = nil, name = nil)
|
19
|
+
# can be called on the client to force re-initialization for testing purposes
|
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
|
27
|
+
log(message)
|
28
|
+
@context = Context.new(unique_id)
|
29
|
+
end
|
30
|
+
@context
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.context
|
35
|
+
@context
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.log(message, message_type = :info)
|
39
|
+
message = [message] unless message.is_a? Array
|
40
|
+
|
41
|
+
if (message_type == :info || message_type == :warning) && Hyperstack.env.production?
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
if message_type == :info
|
46
|
+
if on_opal_server?
|
47
|
+
style = 'background: #00FFFF; color: red'
|
48
|
+
else
|
49
|
+
style = 'background: #222; color: #bada55'
|
50
|
+
end
|
51
|
+
message = ["%c" + message[0], style]+message[1..-1]
|
52
|
+
`console.log.apply(console, message)`
|
53
|
+
elsif message_type == :warning
|
54
|
+
`console.warn.apply(console, message)`
|
55
|
+
else
|
56
|
+
`console.error.apply(console, message)`
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if RUBY_ENGINE != 'opal'
|
61
|
+
def self.on_opal_server?
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.on_opal_client?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
else
|
69
|
+
def self.on_opal_server?
|
70
|
+
`typeof Opal.global.document === 'undefined'`
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.on_opal_client?
|
74
|
+
!on_opal_server?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def log(*args)
|
79
|
+
IsomorphicHelpers.log(*args)
|
80
|
+
end
|
81
|
+
|
82
|
+
def on_opal_server?
|
83
|
+
self.class.on_opal_server?
|
84
|
+
end
|
85
|
+
|
86
|
+
def on_opal_client?
|
87
|
+
self.class.on_opal_client?
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.prerender_footers(controller = nil)
|
91
|
+
footer = Context.prerender_footer_blocks.collect { |block| block.call controller }.join("\n")
|
92
|
+
if RUBY_ENGINE != 'opal'
|
93
|
+
footer = (footer + @context.send_to_opal(:prerender_footers).to_s) if @context
|
94
|
+
footer = footer.html_safe
|
95
|
+
end
|
96
|
+
footer
|
97
|
+
end
|
98
|
+
|
99
|
+
class Context
|
100
|
+
attr_reader :controller
|
101
|
+
attr_reader :unique_id
|
102
|
+
|
103
|
+
def self.define_isomorphic_method(method_name, &block)
|
104
|
+
@@ctx_methods ||= {}
|
105
|
+
@@ctx_methods[method_name] = block
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.before_first_mount_blocks
|
109
|
+
@before_first_mount_blocks ||= []
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.prerender_footer_blocks
|
113
|
+
@prerender_footer_blocks ||= []
|
114
|
+
end
|
115
|
+
|
116
|
+
def initialize(unique_id, ctx = nil, controller = nil, cname = nil)
|
117
|
+
@unique_id = unique_id
|
118
|
+
@cname = cname
|
119
|
+
if RUBY_ENGINE != 'opal'
|
120
|
+
@controller = controller
|
121
|
+
@ctx = ctx
|
122
|
+
if defined? @@ctx_methods
|
123
|
+
@@ctx_methods.each do |method_name, block|
|
124
|
+
@ctx.attach("ServerSideIsomorphicMethod.#{method_name}", proc{|args| block.call(args.to_json)})
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
Hyperstack::Application::Boot.run(context: self)
|
129
|
+
self.class.before_first_mount_blocks.each { |block| block.call(self) }
|
130
|
+
end
|
131
|
+
|
132
|
+
def load_opal_context
|
133
|
+
send_to_opal(:load_context, @unique_id, @cname)
|
134
|
+
end
|
135
|
+
|
136
|
+
def eval(js)
|
137
|
+
@ctx.eval(js) if @ctx
|
138
|
+
end
|
139
|
+
|
140
|
+
def send_to_opal(method_name, *args)
|
141
|
+
return unless @ctx
|
142
|
+
args = [1] if args.length == 0
|
143
|
+
Hyperstack::Internal::Component::Rails::ComponentLoader.new(@ctx).load!
|
144
|
+
method_args = args.collect do |arg|
|
145
|
+
quarg = "#{arg}".tr('"', "'")
|
146
|
+
"\"#{quarg}\""
|
147
|
+
end.join(', ')
|
148
|
+
@ctx.eval("Opal.Hyperstack.$const_get('Component').$const_get('IsomorphicHelpers').$#{method_name}(#{method_args})")
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.register_before_first_mount_block(&block)
|
152
|
+
before_first_mount_blocks << block
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.register_prerender_footer_block(&block)
|
156
|
+
prerender_footer_blocks << block
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class IsomorphicProcCall
|
161
|
+
|
162
|
+
attr_reader :context
|
163
|
+
|
164
|
+
def result
|
165
|
+
@result.first if @result
|
166
|
+
end
|
167
|
+
|
168
|
+
def initialize(name, block, context, *args)
|
169
|
+
@name = name
|
170
|
+
@context = context
|
171
|
+
block.call(self, *args)
|
172
|
+
@result ||= send_to_server(*args)
|
173
|
+
end
|
174
|
+
|
175
|
+
def when_on_client(&block)
|
176
|
+
@result = [block.call] if IsomorphicHelpers.on_opal_client?
|
177
|
+
end
|
178
|
+
|
179
|
+
def send_to_server(*args)
|
180
|
+
if IsomorphicHelpers.on_opal_server?
|
181
|
+
method_string = "ServerSideIsomorphicMethod." + @name + "(" + args.to_json + ")"
|
182
|
+
@result = [JSON.parse(`eval(method_string)`)]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def when_on_server(&block)
|
187
|
+
@result = [block.call.to_json] unless IsomorphicHelpers.on_opal_client? || IsomorphicHelpers.on_opal_server?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
module ClassMethods
|
192
|
+
def on_opal_server?
|
193
|
+
IsomorphicHelpers.on_opal_server?
|
194
|
+
end
|
195
|
+
|
196
|
+
def on_opal_client?
|
197
|
+
IsomorphicHelpers.on_opal_client?
|
198
|
+
end
|
199
|
+
|
200
|
+
def log(*args)
|
201
|
+
IsomorphicHelpers.log(*args)
|
202
|
+
end
|
203
|
+
|
204
|
+
def controller
|
205
|
+
IsomorphicHelpers.context.controller
|
206
|
+
end
|
207
|
+
|
208
|
+
def before_first_mount(&block)
|
209
|
+
IsomorphicHelpers::Context.register_before_first_mount_block(&block)
|
210
|
+
end
|
211
|
+
|
212
|
+
def prerender_footer(&block)
|
213
|
+
IsomorphicHelpers::Context.register_prerender_footer_block(&block)
|
214
|
+
end
|
215
|
+
|
216
|
+
if RUBY_ENGINE != 'opal'
|
217
|
+
def isomorphic_method(name, &block)
|
218
|
+
IsomorphicHelpers::Context.send(:define_isomorphic_method, name) do |args_as_json|
|
219
|
+
IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *JSON.parse(args_as_json)).result
|
220
|
+
end
|
221
|
+
end
|
222
|
+
else
|
223
|
+
require 'json'
|
224
|
+
|
225
|
+
def isomorphic_method(name, &block)
|
226
|
+
self.class.send(:define_method, name) do | *args |
|
227
|
+
IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *args).result
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|