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