module React module IsomorphicHelpers def self.included(base) base.extend(ClassMethods) end if RUBY_ENGINE != 'opal' def self.load_context(ctx, controller, name = nil) puts "************************** React Server Context Initialized #{name} *********************************************" @context = Context.new("#{controller.object_id}-#{Time.now.to_i}", ctx, controller, name) end else def self.load_context(unique_id = nil, name = nil) # can be called on the client to force re-initialization for testing purposes if !unique_id or !@context or @context.unique_id != unique_id if on_opal_server? message = "************************ React Prerendering Context Initialized #{name} ***********************" else message = "************************ React Browser Context Initialized ****************************" end log(message) @context = Context.new(unique_id) end @context end end def self.log(message, message_type = :info) message = [message] unless message.is_a? Array if message_type == :info if on_opal_server? style = 'background: #00FFFF; color: red' else style = 'background: #222; color: #bada55' end message = ["%c" + message[0], style]+message[1..-1] `console.log.apply(console, message)` elsif message_type == :warning `console.warn.apply(console, message)` else `console.error.apply(console, message)` end end if RUBY_ENGINE != 'opal' def self.on_opal_server? false end def self.on_opal_client? false end else def self.on_opal_server? `typeof window.document === 'undefined'` end def self.on_opal_client? !on_opal_server? end end def log(*args) IsomorphicHelpers.log(*args) end def on_opal_server? IsomorphicHelpers.on_opal_server? end def on_opal_client? IsomorphicHelpers.on_opal_client? end def self.prerender_footers footer = Context.prerender_footer_blocks.collect { |block| block.call }.join("\n") if RUBY_ENGINE != 'opal' footer = (footer + "#{@context.send_to_opal(:prerender_footers)}") if @context footer = footer.html_safe end footer end class Context attr_reader :controller attr_reader :unique_id def self.before_first_mount_blocks @before_first_mount_blocks ||= [] end def self.prerender_footer_blocks @prerender_footer_blocks ||= [] end def initialize(unique_id, ctx = nil, controller = nil, name = nil) @unique_id = unique_id if RUBY_ENGINE != 'opal' @controller = controller @ctx = ctx ctx["ServerSideIsomorphicMethods"] = self send_to_opal(:load_context, @unique_id, name) end self.class.before_first_mount_blocks.each { |block| block.call(self) } end def eval(js) @ctx.eval(js) if @ctx end def send_to_opal(method, *args) args = [1] if args.length == 0 if @ctx unless @ctx.eval('Opal.React') @ctx.eval(Opal::Processor.load_asset_code(::Rails.application.assets, 'components')) rescue nil raise "No opal-react components found in the components.rb file" unless @ctx.eval('Opal.React') end @ctx.eval("Opal.React.IsomorphicHelpers.$#{method}(#{args.collect { |arg| "'#{arg}'"}.join(', ')})") end end def self.register_before_first_mount_block(&block) before_first_mount_blocks << block end def self.register_prerender_footer_block(&block) prerender_footer_blocks << block end end class IsomorphicProcCall def result @result.first if @result end def initialize(name, block, *args) @name = name block.call(self, *args) @result ||= send_to_server(*args) if IsomorphicHelpers.on_opal_server? end def when_on_client(&block) @result = [block.call] if IsomorphicHelpers.on_opal_client? end def send_to_server(*args) if IsomorphicHelpers.on_opal_server? args_as_json = args.to_json @result = [JSON.parse(`window.ServerSideIsomorphicMethods[#{@name}](#{args_as_json})`)] end end def when_on_server(&block) @result = [block.call.to_json] unless IsomorphicHelpers.on_opal_client? or IsomorphicHelpers.on_opal_server? end end module ClassMethods def on_opal_server? IsomorphicHelpers.on_opal_server? end def on_opal_client? IsomorphicHelpers.on_opal_client? end def log(*args) IsomorphicHelpers.log(*args) end def controller IsomorphicHelpers.context.controller end def before_first_mount(&block) React::IsomorphicHelpers::Context.register_before_first_mount_block &block end def prerender_footer(&block) React::IsomorphicHelpers::Context.register_prerender_footer_block &block end if RUBY_ENGINE != 'opal' def isomorphic_method(name, &block) React::IsomorphicHelpers::Context.send(:define_method, name) do |args_as_json| React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, *JSON.parse(args_as_json)).result end end else require 'json' def isomorphic_method(name, &block) self.class.send(:define_method, name) do | *args | React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, *args).result end end end end end end