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