hyper-react 0.99.6 → 1.0.0.lap21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +27 -0
  3. data/.gitignore +30 -37
  4. data/.rubocop.yml +1159 -0
  5. data/.travis.yml +32 -0
  6. data/Appraisals +31 -0
  7. data/CHANGELOG.md +143 -0
  8. data/DOCS.md +1515 -0
  9. data/Gemfile +2 -5
  10. data/LICENSE +19 -0
  11. data/README.md +5 -33
  12. data/Rakefile +25 -6
  13. data/UPGRADING.md +24 -0
  14. data/component-name-lookup.md +145 -0
  15. data/dciy.toml +3 -0
  16. data/dciy_prepare.sh +8 -0
  17. data/dciy_run.sh +10 -0
  18. data/hyper-react.gemspec +24 -18
  19. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
  20. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
  21. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
  22. data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
  23. data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  24. data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
  25. data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  26. data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  27. data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  28. data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +117 -0
  29. data/lib/hyper-react.rb +66 -4
  30. data/lib/rails-helpers/top_level_rails_component.rb +75 -0
  31. data/lib/react/api.rb +203 -0
  32. data/lib/react/callbacks.rb +41 -0
  33. data/lib/react/children.rb +30 -0
  34. data/lib/react/component.rb +177 -0
  35. data/lib/react/component/api.rb +69 -0
  36. data/lib/react/component/base.rb +13 -0
  37. data/lib/react/component/class_methods.rb +181 -0
  38. data/lib/react/component/dsl_instance_methods.rb +23 -0
  39. data/lib/react/component/params.rb +6 -0
  40. data/lib/react/component/props_wrapper.rb +78 -0
  41. data/lib/react/component/should_component_update.rb +99 -0
  42. data/lib/react/component/tags.rb +108 -0
  43. data/lib/react/config.rb +5 -0
  44. data/lib/react/config/client.rb.erb +19 -0
  45. data/lib/react/config/server.rb +23 -0
  46. data/lib/react/element.rb +150 -0
  47. data/lib/react/event.rb +76 -0
  48. data/lib/react/ext/hash.rb +9 -0
  49. data/lib/react/ext/opal-jquery/element.rb +26 -0
  50. data/lib/react/ext/string.rb +8 -0
  51. data/lib/react/hash.rb +13 -0
  52. data/lib/react/native_library.rb +87 -0
  53. data/lib/react/object.rb +15 -0
  54. data/lib/react/react-source-browser.rb +3 -0
  55. data/lib/react/react-source-server.rb +3 -0
  56. data/lib/react/react-source.rb +16 -0
  57. data/lib/react/ref_callback.rb +31 -0
  58. data/lib/react/rendering_context.rb +146 -0
  59. data/lib/react/server.rb +19 -0
  60. data/lib/react/state_wrapper.rb +23 -0
  61. data/lib/react/test.rb +16 -0
  62. data/lib/react/test/dsl.rb +17 -0
  63. data/lib/react/test/matchers/render_html_matcher.rb +56 -0
  64. data/lib/react/test/rspec.rb +15 -0
  65. data/lib/react/test/session.rb +37 -0
  66. data/lib/react/test/utils.rb +71 -0
  67. data/lib/react/top_level.rb +110 -0
  68. data/lib/react/top_level_render.rb +28 -0
  69. data/lib/react/validator.rb +136 -0
  70. data/lib/reactive-ruby/component_loader.rb +43 -0
  71. data/lib/reactive-ruby/isomorphic_helpers.rb +235 -0
  72. data/lib/reactive-ruby/rails.rb +8 -0
  73. data/lib/reactive-ruby/rails/component_mount.rb +48 -0
  74. data/lib/reactive-ruby/rails/controller_helper.rb +14 -0
  75. data/lib/reactive-ruby/rails/railtie.rb +20 -0
  76. data/lib/reactive-ruby/serializers.rb +15 -0
  77. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +41 -0
  78. data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
  79. data/lib/reactive-ruby/version.rb +3 -0
  80. data/lib/reactrb/auto-import.rb +27 -0
  81. data/logo1.png +0 -0
  82. data/logo2.png +0 -0
  83. data/logo3.png +0 -0
  84. data/path_release_steps.md +9 -0
  85. data/spec/controller_helper_spec.rb +35 -0
  86. data/spec/index.html.erb +11 -0
  87. data/spec/react/callbacks_spec.rb +142 -0
  88. data/spec/react/children_spec.rb +132 -0
  89. data/spec/react/component/base_spec.rb +36 -0
  90. data/spec/react/component_spec.rb +1073 -0
  91. data/spec/react/dsl_spec.rb +323 -0
  92. data/spec/react/element_spec.rb +132 -0
  93. data/spec/react/event_spec.rb +39 -0
  94. data/spec/react/native_library_spec.rb +387 -0
  95. data/spec/react/observable_spec.rb +31 -0
  96. data/spec/react/opal_jquery_extensions_spec.rb +68 -0
  97. data/spec/react/param_declaration_spec.rb +253 -0
  98. data/spec/react/react_spec.rb +278 -0
  99. data/spec/react/refs_callback_spec.rb +65 -0
  100. data/spec/react/server_spec.rb +25 -0
  101. data/spec/react/state_spec.rb +52 -0
  102. data/spec/react/test/dsl_spec.rb +43 -0
  103. data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
  104. data/spec/react/test/rspec_spec.rb +62 -0
  105. data/spec/react/test/session_spec.rb +88 -0
  106. data/spec/react/test/utils_spec.rb +28 -0
  107. data/spec/react/top_level_component_spec.rb +103 -0
  108. data/spec/react/tutorial/tutorial_spec.rb +42 -0
  109. data/spec/react/validator_spec.rb +134 -0
  110. data/spec/reactive-ruby/component_loader_spec.rb +74 -0
  111. data/spec/reactive-ruby/isomorphic_helpers_spec.rb +157 -0
  112. data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +17 -0
  113. data/spec/reactive-ruby/rails/component_mount_spec.rb +64 -0
  114. data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +39 -0
  115. data/spec/spec_helper.rb +55 -0
  116. data/spec/test_app/README.md +24 -0
  117. data/spec/test_app/Rakefile +6 -0
  118. data/spec/test_app/app/assets/config/manifest.js +3 -0
  119. data/spec/test_app/app/assets/images/.keep +0 -0
  120. data/spec/test_app/app/assets/javascripts/application.rb +7 -0
  121. data/spec/test_app/app/assets/javascripts/cable.js +13 -0
  122. data/spec/test_app/app/assets/javascripts/channels/.keep +0 -0
  123. data/spec/test_app/app/assets/javascripts/server_rendering.js +5 -0
  124. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  125. data/spec/test_app/app/channels/application_cable/channel.rb +4 -0
  126. data/spec/test_app/app/channels/application_cable/connection.rb +4 -0
  127. data/spec/test_app/app/controllers/application_controller.rb +3 -0
  128. data/spec/test_app/app/controllers/concerns/.keep +0 -0
  129. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  130. data/spec/test_app/app/jobs/application_job.rb +2 -0
  131. data/spec/test_app/app/mailers/application_mailer.rb +4 -0
  132. data/spec/test_app/app/models/application_record.rb +3 -0
  133. data/spec/test_app/app/models/concerns/.keep +0 -0
  134. data/spec/test_app/app/views/components.rb +11 -0
  135. data/spec/test_app/app/views/components/hello_world.rb +11 -0
  136. data/spec/test_app/app/views/components/todo.rb +14 -0
  137. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  138. data/spec/test_app/app/views/layouts/explicit_layout.html.erb +0 -0
  139. data/spec/test_app/app/views/layouts/mailer.html.erb +13 -0
  140. data/spec/test_app/app/views/layouts/mailer.text.erb +1 -0
  141. data/spec/test_app/app/views/layouts/test_layout.html.erb +0 -0
  142. data/spec/test_app/bin/bundle +3 -0
  143. data/spec/test_app/bin/rails +4 -0
  144. data/spec/test_app/bin/rake +4 -0
  145. data/spec/test_app/bin/setup +38 -0
  146. data/spec/test_app/bin/update +29 -0
  147. data/spec/test_app/bin/yarn +11 -0
  148. data/spec/test_app/config.ru +5 -0
  149. data/spec/test_app/config/application.rb +45 -0
  150. data/spec/test_app/config/boot.rb +6 -0
  151. data/spec/test_app/config/cable.yml +10 -0
  152. data/spec/test_app/config/database.yml +25 -0
  153. data/spec/test_app/config/environment.rb +5 -0
  154. data/spec/test_app/config/environments/development.rb +54 -0
  155. data/spec/test_app/config/environments/production.rb +91 -0
  156. data/spec/test_app/config/environments/test.rb +42 -0
  157. data/spec/test_app/config/initializers/application_controller_renderer.rb +8 -0
  158. data/spec/test_app/config/initializers/assets.rb +14 -0
  159. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  160. data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
  161. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  162. data/spec/test_app/config/initializers/inflections.rb +16 -0
  163. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  164. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  165. data/spec/test_app/config/locales/en.yml +33 -0
  166. data/spec/test_app/config/puma.rb +56 -0
  167. data/spec/test_app/config/routes.rb +3 -0
  168. data/spec/test_app/config/secrets.yml +32 -0
  169. data/spec/test_app/config/spring.rb +6 -0
  170. data/spec/test_app/db/development.sqlite3 +0 -0
  171. data/spec/test_app/db/schema.rb +15 -0
  172. data/spec/test_app/db/seeds.rb +7 -0
  173. data/spec/test_app/db/test.sqlite3 +0 -0
  174. data/spec/test_app/lib/assets/.keep +0 -0
  175. data/spec/test_app/log/.keep +0 -0
  176. data/spec/test_app/package.json +5 -0
  177. data/spec/test_app/public/404.html +67 -0
  178. data/spec/test_app/public/422.html +67 -0
  179. data/spec/test_app/public/500.html +66 -0
  180. data/spec/test_app/public/apple-touch-icon-precomposed.png +0 -0
  181. data/spec/test_app/public/apple-touch-icon.png +0 -0
  182. data/spec/test_app/public/favicon.ico +0 -0
  183. data/spec/vendor/es5-shim.min.js +7 -0
  184. data/spec/vendor/jquery-2.2.4.min.js +4 -0
  185. metadata +401 -61
  186. data/CODE_OF_CONDUCT.md +0 -49
  187. data/lib/react/version.rb +0 -3
@@ -0,0 +1,43 @@
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
@@ -0,0 +1,235 @@
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
+ is_production = React::Config.config[:environment] == 'production'
41
+
42
+ if (message_type == :info || message_type == :warning) && is_production
43
+ return
44
+ end
45
+
46
+ if message_type == :info
47
+ if on_opal_server?
48
+ style = 'background: #00FFFF; color: red'
49
+ else
50
+ style = 'background: #222; color: #bada55'
51
+ end
52
+ message = ["%c" + message[0], style]+message[1..-1]
53
+ `console.log.apply(console, message)`
54
+ elsif message_type == :warning
55
+ `console.warn.apply(console, message)`
56
+ else
57
+ `console.error.apply(console, message)`
58
+ end
59
+ end
60
+
61
+ if RUBY_ENGINE != 'opal'
62
+ def self.on_opal_server?
63
+ false
64
+ end
65
+
66
+ def self.on_opal_client?
67
+ false
68
+ end
69
+ else
70
+ def self.on_opal_server?
71
+ `typeof Opal.global.document === 'undefined'`
72
+ end
73
+
74
+ def self.on_opal_client?
75
+ !on_opal_server?
76
+ end
77
+ end
78
+
79
+ def log(*args)
80
+ IsomorphicHelpers.log(*args)
81
+ end
82
+
83
+ def on_opal_server?
84
+ self.class.on_opal_server?
85
+ end
86
+
87
+ def on_opal_client?
88
+ self.class.on_opal_client?
89
+ end
90
+
91
+ def self.prerender_footers(controller = nil)
92
+ footer = Context.prerender_footer_blocks.collect { |block| block.call controller }.join("\n")
93
+ if RUBY_ENGINE != 'opal'
94
+ footer = (footer + @context.send_to_opal(:prerender_footers).to_s) if @context
95
+ footer = footer.html_safe
96
+ end
97
+ footer
98
+ end
99
+
100
+ class Context
101
+ attr_reader :controller
102
+ attr_reader :unique_id
103
+
104
+ def self.define_isomorphic_method(method_name, &block)
105
+ @@ctx_methods ||= {}
106
+ @@ctx_methods[method_name] = block
107
+ end
108
+
109
+ def self.before_first_mount_blocks
110
+ @before_first_mount_blocks ||= []
111
+ end
112
+
113
+ def self.prerender_footer_blocks
114
+ @prerender_footer_blocks ||= []
115
+ end
116
+
117
+ def initialize(unique_id, ctx = nil, controller = nil, cname = nil)
118
+ @unique_id = unique_id
119
+ @cname = cname
120
+ if RUBY_ENGINE != 'opal'
121
+ @controller = controller
122
+ @ctx = ctx
123
+ if defined? @@ctx_methods
124
+ @@ctx_methods.each do |method_name, block|
125
+ @ctx.attach("ServerSideIsomorphicMethod.#{method_name}", proc{|args| block.call(args.to_json)})
126
+ end
127
+ end
128
+ end
129
+ Hyperloop::Application::Boot.run(context: self)
130
+ self.class.before_first_mount_blocks.each { |block| block.call(self) }
131
+ end
132
+
133
+ def load_opal_context
134
+ send_to_opal(:load_context, @unique_id, @cname)
135
+ end
136
+
137
+ def eval(js)
138
+ @ctx.eval(js) if @ctx
139
+ end
140
+
141
+ def send_to_opal(method_name, *args)
142
+ return unless @ctx
143
+ args = [1] if args.length == 0
144
+ ::ReactiveRuby::ComponentLoader.new(@ctx).load!
145
+ method_args = args.collect do |arg|
146
+ quarg = "#{arg}".tr('"', "'")
147
+ "\"#{quarg}\""
148
+ end.join(', ')
149
+ @ctx.eval("Opal.React.$const_get('IsomorphicHelpers').$#{method_name}(#{method_args})")
150
+ end
151
+
152
+ def self.register_before_first_mount_block(&block)
153
+ before_first_mount_blocks << block
154
+ end
155
+
156
+ def self.register_prerender_footer_block(&block)
157
+ prerender_footer_blocks << block
158
+ end
159
+ end
160
+
161
+ class IsomorphicProcCall
162
+
163
+ attr_reader :context
164
+
165
+ def result
166
+ @result.first if @result
167
+ end
168
+
169
+ def initialize(name, block, context, *args)
170
+ @name = name
171
+ @context = context
172
+ block.call(self, *args)
173
+ @result ||= send_to_server(*args)
174
+ end
175
+
176
+ def when_on_client(&block)
177
+ @result = [block.call] if IsomorphicHelpers.on_opal_client?
178
+ end
179
+
180
+ def send_to_server(*args)
181
+ if IsomorphicHelpers.on_opal_server?
182
+ method_string = "ServerSideIsomorphicMethod." + @name + "(" + args.to_json + ")"
183
+ @result = [JSON.parse(`eval(method_string)`)]
184
+ end
185
+ end
186
+
187
+ def when_on_server(&block)
188
+ @result = [block.call.to_json] unless IsomorphicHelpers.on_opal_client? || IsomorphicHelpers.on_opal_server?
189
+ end
190
+ end
191
+
192
+ module ClassMethods
193
+ def on_opal_server?
194
+ IsomorphicHelpers.on_opal_server?
195
+ end
196
+
197
+ def on_opal_client?
198
+ IsomorphicHelpers.on_opal_client?
199
+ end
200
+
201
+ def log(*args)
202
+ IsomorphicHelpers.log(*args)
203
+ end
204
+
205
+ def controller
206
+ IsomorphicHelpers.context.controller
207
+ end
208
+
209
+ def before_first_mount(&block)
210
+ React::IsomorphicHelpers::Context.register_before_first_mount_block(&block)
211
+ end
212
+
213
+ def prerender_footer(&block)
214
+ React::IsomorphicHelpers::Context.register_prerender_footer_block(&block)
215
+ end
216
+
217
+ if RUBY_ENGINE != 'opal'
218
+ def isomorphic_method(name, &block)
219
+ React::IsomorphicHelpers::Context.send(:define_isomorphic_method, name) do |args_as_json|
220
+ React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *JSON.parse(args_as_json)).result
221
+ end
222
+ end
223
+ else
224
+ require 'json'
225
+
226
+ def isomorphic_method(name, &block)
227
+ self.class.send(:define_method, name) do | *args |
228
+ React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, self, *args).result
229
+ end
230
+ end
231
+ end
232
+
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,8 @@
1
+ require 'action_view'
2
+ require 'react-rails'
3
+ require 'reactive-ruby/server_rendering/hyper_asset_container'
4
+ require 'reactive-ruby/server_rendering/contextual_renderer'
5
+ require 'reactive-ruby/rails/component_mount'
6
+ require 'reactive-ruby/rails/railtie'
7
+ require 'reactive-ruby/rails/controller_helper'
8
+ require 'reactive-ruby/component_loader'
@@ -0,0 +1,48 @@
1
+ module ReactiveRuby
2
+ module Rails
3
+ class ComponentMount < React::Rails::ComponentMount
4
+ attr_accessor :controller
5
+
6
+ def setup(controller)
7
+ self.controller = controller
8
+ end
9
+
10
+ def react_component(name, props = {}, options = {}, &block)
11
+ if options[:prerender] || [:on, 'on', true].include?(Hyperloop.prerendering)
12
+ options = context_initializer_options(options, name)
13
+ end
14
+ props = serialized_props(props, name, controller)
15
+ super(top_level_name, props, options, &block).gsub("\n","")
16
+ .gsub(/(<script>.*<\/script>)<\/div>$/,'</div>\1').html_safe +
17
+ footers
18
+ end
19
+
20
+ private
21
+
22
+ def context_initializer_options(options, name)
23
+ options[:prerender] = {options[:prerender] => true} unless options[:prerender].is_a? Hash
24
+ existing_context_initializer = options[:prerender][:context_initializer]
25
+
26
+ options[:prerender][:context_initializer] = lambda do |ctx|
27
+ React::IsomorphicHelpers.load_context(ctx, controller, name)
28
+ existing_context_initializer.call(ctx) if existing_context_initializer
29
+ end
30
+
31
+ options
32
+ end
33
+
34
+ def serialized_props(props, name, controller)
35
+ { render_params: props, component_name: name,
36
+ controller: controller.class.name.gsub(/Controller$/,"") }.react_serializer
37
+ end
38
+
39
+ def top_level_name
40
+ 'React.TopLevelRailsComponent'
41
+ end
42
+
43
+ def footers
44
+ React::IsomorphicHelpers.prerender_footers(controller)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ require 'action_controller'
2
+
3
+ module ActionController
4
+ # adds render_component helper to ActionControllers
5
+ class Base
6
+ def render_component(*args)
7
+ @component_name = (args[0].is_a? Hash) || args.empty? ? params[:action].camelize : args.shift
8
+ @render_params = args.shift || {}
9
+ options = args[0] || {}
10
+ render inline: '<%= react_component @component_name, @render_params %>',
11
+ layout: options.key?(:layout) ? options[:layout].to_s : :default
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module ReactiveRuby
2
+ module Rails
3
+ class Railtie < ::Rails::Railtie
4
+ config.before_configuration do |app|
5
+ app.config.assets.enabled = true
6
+ app.config.assets.paths << ::Rails.root.join('app', 'views').to_s
7
+ app.config.react.server_renderer = ReactiveRuby::ServerRendering::ContextualRenderer
8
+ app.config.react.view_helper_implementation = ReactiveRuby::Rails::ComponentMount
9
+ ReactiveRuby::ServerRendering::ContextualRenderer.asset_container_class = ReactiveRuby::ServerRendering::HyperAssetContainer
10
+ end
11
+ config.after_initialize do
12
+ class ::HyperloopController < ::ApplicationController
13
+ def action_missing(name)
14
+ render_component
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ [Bignum, FalseClass, Fixnum, Float, Integer, NilClass, String, Symbol, Time, TrueClass].each do |klass|
2
+ klass.send(:define_method, :react_serializer) do
3
+ as_json
4
+ end
5
+ end
6
+
7
+ BigDecimal.send(:define_method, :react_serializer) { as_json } rescue nil
8
+
9
+ Array.send(:define_method, :react_serializer) do
10
+ self.collect { |e| e.react_serializer }.as_json
11
+ end
12
+
13
+ Hash.send(:define_method, :react_serializer) do
14
+ Hash[*self.collect { |key, value| [key, value.react_serializer] }.flatten(1)].as_json
15
+ end
@@ -0,0 +1,41 @@
1
+ module ReactiveRuby
2
+ module ServerRendering
3
+ def self.context_instance_name
4
+ '@context'
5
+ end
6
+
7
+ def self.context_instance_for(context)
8
+ context.instance_variable_get(context_instance_name)
9
+ end
10
+
11
+ class ContextualRenderer < React::ServerRendering::BundleRenderer
12
+ def initialize(options = {})
13
+ super(options)
14
+ ComponentLoader.new(v8_context).load
15
+ end
16
+
17
+ def render(component_name, props, prerender_options)
18
+ if prerender_options.is_a?(Hash)
19
+ if !v8_runtime? && prerender_options[:context_initializer]
20
+ raise React::ServerRendering::PrerenderError.new(component_name, props, "you must use 'mini_racer' with the prerender[:context] option") unless v8_runtime?
21
+ else
22
+ prerender_options[:context_initializer].call v8_context
23
+ prerender_options = prerender_options[:static] ? :static : true
24
+ end
25
+ end
26
+
27
+ super(component_name, props, prerender_options)
28
+ end
29
+
30
+ private
31
+
32
+ def v8_runtime?
33
+ ExecJS.runtime.name == 'mini_racer (V8)'
34
+ end
35
+
36
+ def v8_context
37
+ @v8_context ||= ReactiveRuby::ServerRendering.context_instance_for(@context)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ require 'react/server_rendering/environment_container'
2
+ require 'react/server_rendering/manifest_container'
3
+ require 'react/server_rendering/webpacker_manifest_container'
4
+
5
+ module ReactiveRuby
6
+ module ServerRendering
7
+ class HyperTestAssetContainer
8
+ def find_asset(logical_path)
9
+ ::Rails.cache.read(logical_path)
10
+ end
11
+ end
12
+
13
+ class HyperAssetContainer
14
+ def initialize
15
+ @ass_containers = []
16
+ if assets_precompiled?
17
+ @ass_containers << React::ServerRendering::ManifestContainer.new if React::ServerRendering::ManifestContainer.compatible?
18
+ else
19
+ @ass_containers << React::ServerRendering::EnvironmentContainer.new if ::Rails.application.assets
20
+ end
21
+ if React::ServerRendering::WebpackerManifestContainer.compatible?
22
+ @ass_containers << React::ServerRendering::WebpackerManifestContainer.new
23
+ end
24
+ @ass_containers << HyperTestAssetContainer.new if ::Rails.env.test?
25
+ end
26
+
27
+ def find_asset(logical_path)
28
+ @ass_containers.each do |ass|
29
+ begin
30
+ asset = ass.find_asset(logical_path)
31
+ return asset if asset && asset != ''
32
+ rescue
33
+ next # no asset found, try the next container
34
+ end
35
+ end
36
+ raise "No asset found for #{logical_path}, tried: #{@ass_containers.map { |c| c.class.name }.join(', ')}"
37
+ end
38
+
39
+ private
40
+
41
+ def assets_precompiled?
42
+ !::Rails.application.config.assets.compile
43
+ end
44
+ end
45
+ end
46
+ end