react_on_rails 5.2.0 → 6.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +31 -0
  5. data/PROJECTS.md +11 -2
  6. data/README.md +33 -203
  7. data/app/helpers/react_on_rails_helper.rb +32 -49
  8. data/docs/additional-reading/heroku-deployment.md +2 -35
  9. data/docs/additional-reading/node-server-rendering.md +30 -0
  10. data/docs/additional-reading/rails-assets.md +19 -0
  11. data/docs/additional-reading/rspec-configuration.md +38 -4
  12. data/docs/additional-reading/webpack-dev-server.md +16 -0
  13. data/docs/additional-reading/webpack.md +0 -10
  14. data/docs/api/javascript-api.md +37 -0
  15. data/docs/api/ruby-api-hot-reload-view-helpers.md +42 -0
  16. data/docs/api/ruby-api.md +3 -0
  17. data/docs/basics/generator.md +49 -0
  18. data/docs/{additional-reading → basics}/installation-overview.md +0 -0
  19. data/docs/basics/migrating-from-react-rails.md +17 -0
  20. data/docs/contributor-info/contributing.md +11 -0
  21. data/docs/{coding-style → contributor-info}/linters.md +0 -0
  22. data/lib/generators/USAGE +3 -95
  23. data/lib/generators/react_on_rails/base_generator.rb +10 -43
  24. data/lib/generators/react_on_rails/dev_tests_generator.rb +17 -1
  25. data/lib/generators/react_on_rails/install_generator.rb +0 -6
  26. data/lib/generators/react_on_rails/react_no_redux_generator.rb +3 -17
  27. data/lib/generators/react_on_rails/react_with_redux_generator.rb +4 -21
  28. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt +2 -2
  29. data/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt +3 -4
  30. data/lib/generators/react_on_rails/templates/base/base/client/app/bundles/HelloWorld/components/HelloWorldWidget.jsx.tt +1 -11
  31. data/lib/generators/react_on_rails/templates/base/base/client/node/package.json +10 -0
  32. data/lib/generators/react_on_rails/templates/base/base/client/node/server.js +82 -0
  33. data/lib/generators/react_on_rails/templates/base/base/client/package.json.tt +3 -20
  34. data/lib/generators/react_on_rails/templates/base/base/client/webpack.config.js +58 -0
  35. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +60 -26
  36. data/lib/generators/react_on_rails/templates/base/base/lib/tasks/assets.rake.tt +1 -4
  37. data/lib/generators/react_on_rails/templates/base/base/lib/tasks/load_test.rake +8 -0
  38. data/lib/generators/react_on_rails/templates/base/base/package.json.tt +0 -12
  39. data/lib/generators/react_on_rails/templates/dev_tests/spec/features/hello_world_spec.rb +1 -1
  40. data/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +1 -0
  41. data/lib/generators/react_on_rails/templates/no_redux/base/client/app/bundles/HelloWorld/containers/HelloWorld.jsx +1 -7
  42. data/lib/generators/react_on_rails/templates/{base/base/client/app/bundles/HelloWorld/startup/clientRegistration.jsx.tt → no_redux/base/client/app/bundles/HelloWorld/startup/HelloWorldApp.jsx.tt} +7 -1
  43. data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/startup/{HelloWorldAppClient.jsx.tt → HelloWorldApp.jsx.tt} +5 -1
  44. data/lib/generators/react_on_rails/templates/redux/base/client/app/bundles/HelloWorld/store/helloWorldStore.jsx +1 -5
  45. data/lib/react_on_rails.rb +2 -1
  46. data/lib/react_on_rails/configuration.rb +17 -5
  47. data/lib/react_on_rails/react_component/options.rb +76 -0
  48. data/lib/react_on_rails/server_rendering_pool.rb +9 -151
  49. data/lib/react_on_rails/server_rendering_pool/exec.rb +166 -0
  50. data/lib/react_on_rails/server_rendering_pool/node.rb +77 -0
  51. data/lib/react_on_rails/test_helper.rb +1 -6
  52. data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +4 -77
  53. data/lib/react_on_rails/test_helper/node_process_launcher.rb +12 -0
  54. data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +4 -28
  55. data/lib/react_on_rails/version.rb +1 -1
  56. data/lib/tasks/assets.rake +115 -0
  57. data/package.json +2 -1
  58. data/rakelib/example_type.rb +3 -10
  59. data/rakelib/examples_config.yml +2 -2
  60. data/react_on_rails.gemspec +1 -0
  61. metadata +41 -20
  62. data/lib/generators/react_on_rails/templates/base/base/REACT_ON_RAILS.md +0 -16
  63. data/lib/generators/react_on_rails/templates/base/base/client/REACT_ON_RAILS_CLIENT_README.md +0 -3
  64. data/lib/generators/react_on_rails/templates/base/base/client/webpack.client.base.config.js +0 -65
  65. data/lib/generators/react_on_rails/templates/base/base/client/webpack.client.rails.config.js +0 -53
  66. data/lib/generators/react_on_rails/templates/base/server_rendering/client/app/bundles/HelloWorld/startup/serverRegistration.jsx +0 -4
  67. data/lib/generators/react_on_rails/templates/base/server_rendering/client/webpack.server.rails.config.js +0 -39
  68. data/lib/generators/react_on_rails/templates/no_redux/base/client/app/bundles/HelloWorld/startup/HelloWorldAppClient.jsx.tt +0 -6
  69. data/lib/generators/react_on_rails/templates/no_redux/server_rendering/client/app/bundles/HelloWorld/startup/HelloWorldAppServer.jsx +0 -6
  70. data/lib/generators/react_on_rails/templates/redux/base/client/app/lib/middlewares/loggerMiddleware.js +0 -21
  71. data/lib/generators/react_on_rails/templates/redux/server_rendering/client/app/bundles/HelloWorld/startup/HelloWorldAppServer.jsx +0 -19
  72. data/lib/react_on_rails/test_helper/webpack_process_checker.rb +0 -54
@@ -6,6 +6,7 @@ require "react_on_rails/version_checker"
6
6
  require "react_on_rails/configuration"
7
7
  require "react_on_rails/server_rendering_pool"
8
8
  require "react_on_rails/engine"
9
+ require "react_on_rails/react_component/options"
9
10
  require "react_on_rails/version_syntax_converter"
10
11
  require "react_on_rails/test_helper"
11
12
  require "react_on_rails/git_utils"
@@ -13,5 +14,5 @@ require "react_on_rails/utils"
13
14
  require "react_on_rails/test_helper"
14
15
  require "react_on_rails/test_helper/webpack_assets_compiler"
15
16
  require "react_on_rails/test_helper/webpack_assets_status_checker"
16
- require "react_on_rails/test_helper/webpack_process_checker"
17
17
  require "react_on_rails/test_helper/ensure_assets_compiled"
18
+ require "react_on_rails/test_helper/node_process_launcher"
@@ -8,7 +8,7 @@ module ReactOnRails
8
8
 
9
9
  def self.setup_config_values
10
10
  if @configuration.webpack_generated_files.empty?
11
- files = ["client-bundle.js"]
11
+ files = ["webpack-bundle.js"]
12
12
  if @configuration.server_bundle_js_file.present?
13
13
  files << @configuration.server_bundle_js_file
14
14
  end
@@ -44,7 +44,6 @@ module ReactOnRails
44
44
 
45
45
  # generated_assets_dirs is deprecated
46
46
  generated_assets_dir: "",
47
-
48
47
  server_bundle_js_file: "",
49
48
  prerender: false,
50
49
  replay_console: true,
@@ -56,7 +55,11 @@ module ReactOnRails
56
55
  server_renderer_timeout: 20,
57
56
  skip_display_none: false,
58
57
  webpack_generated_files: [],
59
- rendering_extension: nil
58
+ rendering_extension: nil,
59
+ server_render_method: "",
60
+ symlink_non_digested_assets_regex: /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg)/,
61
+ npm_build_test_command: "",
62
+ npm_build_production_command: ""
60
63
  )
61
64
  end
62
65
 
@@ -66,7 +69,9 @@ module ReactOnRails
66
69
  :logging_on_server, :server_renderer_pool_size,
67
70
  :server_renderer_timeout, :raise_on_prerender_error,
68
71
  :skip_display_none, :generated_assets_dirs, :generated_assets_dir,
69
- :webpack_generated_files, :rendering_extension
72
+ :webpack_generated_files, :rendering_extension, :npm_build_test_command,
73
+ :npm_build_production_command,
74
+ :server_render_method, :symlink_non_digested_assets_regex
70
75
 
71
76
  def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil,
72
77
  trace: nil, development_mode: nil,
@@ -74,10 +79,14 @@ module ReactOnRails
74
79
  server_renderer_timeout: nil, raise_on_prerender_error: nil,
75
80
  skip_display_none: nil, generated_assets_dirs: nil,
76
81
  generated_assets_dir: nil, webpack_generated_files: nil,
77
- rendering_extension: nil)
82
+ rendering_extension: nil, npm_build_test_command: nil,
83
+ npm_build_production_command: nil,
84
+ server_render_method: nil, symlink_non_digested_assets_regex: nil)
78
85
  self.server_bundle_js_file = server_bundle_js_file
79
86
  self.generated_assets_dirs = generated_assets_dirs
80
87
  self.generated_assets_dir = generated_assets_dir
88
+ self.npm_build_test_command = npm_build_test_command
89
+ self.npm_build_production_command = npm_build_production_command
81
90
 
82
91
  self.prerender = prerender
83
92
  self.replay_console = replay_console
@@ -97,6 +106,9 @@ module ReactOnRails
97
106
 
98
107
  self.webpack_generated_files = webpack_generated_files
99
108
  self.rendering_extension = rendering_extension
109
+
110
+ self.server_render_method = server_render_method
111
+ self.symlink_non_digested_assets_regex = symlink_non_digested_assets_regex
100
112
  end
101
113
  end
102
114
  end
@@ -0,0 +1,76 @@
1
+ module ReactOnRails
2
+ module ReactComponent
3
+ class Options
4
+ NO_PROPS = {}.freeze
5
+ HIDDEN = "display:none".freeze
6
+
7
+ attr_reader :index
8
+
9
+ def initialize(name:, index:, options:)
10
+ @name = name
11
+ @index = index
12
+ @options = options
13
+ end
14
+
15
+ def props
16
+ options.fetch(:props) { NO_PROPS }
17
+ end
18
+
19
+ def name
20
+ @name.camelize
21
+ end
22
+
23
+ def dom_id
24
+ options.fetch(:id) { default_dom_id }
25
+ end
26
+
27
+ def html_options
28
+ options[:html_options].to_h
29
+ end
30
+
31
+ def prerender
32
+ retrieve_key(:prerender)
33
+ end
34
+
35
+ def trace
36
+ retrieve_key(:trace)
37
+ end
38
+
39
+ def replay_console
40
+ retrieve_key(:replay_console)
41
+ end
42
+
43
+ def raise_on_prerender_error
44
+ retrieve_key(:raise_on_prerender_error)
45
+ end
46
+
47
+ def data
48
+ {
49
+ component_name: name,
50
+ props: props,
51
+ trace: trace,
52
+ dom_id: dom_id
53
+ }
54
+ end
55
+
56
+ def style
57
+ return nil if ReactOnRails.configuration.skip_display_none
58
+ HIDDEN
59
+ end
60
+
61
+ private
62
+
63
+ attr_reader :options
64
+
65
+ def default_dom_id
66
+ "#{@name}-react-component-#{@index}"
67
+ end
68
+
69
+ def retrieve_key(key)
70
+ options.fetch(key) do
71
+ ReactOnRails.configuration.public_send(key)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,165 +1,23 @@
1
1
  require "connection_pool"
2
+ require_relative "server_rendering_pool/exec"
3
+ require_relative "server_rendering_pool/node"
2
4
 
3
5
  # Based on the react-rails gem.
4
6
  # None of these methods should be called directly.
5
7
  # See app/helpers/react_on_rails_helper.rb
6
8
  module ReactOnRails
7
- class ServerRenderingPool
8
- def self.reset_pool
9
- options = { size: ReactOnRails.configuration.server_renderer_pool_size,
10
- timeout: ReactOnRails.configuration.server_renderer_pool_size }
11
- @js_context_pool = ConnectionPool.new(options) { create_js_context }
12
- end
13
-
14
- def self.reset_pool_if_server_bundle_was_modified
15
- return unless ReactOnRails.configuration.development_mode
16
- file_mtime = File.mtime(ReactOnRails::Utils.default_server_bundle_js_file_path)
17
- @server_bundle_timestamp ||= file_mtime
18
- return if @server_bundle_timestamp == file_mtime
19
- ReactOnRails::ServerRenderingPool.reset_pool
20
- @server_bundle_timestamp = file_mtime
21
- end
22
-
23
- # js_code: JavaScript expression that returns a string.
24
- # Returns a Hash:
25
- # html: string of HTML for direct insertion on the page by evaluating js_code
26
- # consoleReplayScript: script for replaying console
27
- # hasErrors: true if server rendering errors
28
- # Note, js_code does not have to be based on React.
29
- # js_code MUST RETURN json stringify Object
30
- # Calling code will probably call 'html_safe' on return value before rendering to the view.
31
- def self.server_render_js_with_console_logging(js_code)
32
- if trace_react_on_rails?
33
- @file_index ||= 1
34
- trace_messsage(js_code, "tmp/server-generated-#{@file_index % 10}.js")
35
- @file_index += 1
36
- end
37
- json_string = eval_js(js_code)
38
- result = JSON.parse(json_string)
39
-
40
- if ReactOnRails.configuration.logging_on_server
41
- console_script = result["consoleReplayScript"]
42
- console_script_lines = console_script.split("\n")
43
- console_script_lines = console_script_lines[2..-2]
44
- re = /console\.log\.apply\(console, \["\[SERVER\] (?<msg>.*)"\]\);/
45
- if console_script_lines
46
- console_script_lines.each do |line|
47
- match = re.match(line)
48
- Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match
49
- end
50
- end
51
- end
52
- result
53
- end
54
-
9
+ module ServerRenderingPool
55
10
  class << self
56
- private
57
-
58
- def trace_messsage(js_code, file_name = "tmp/server-generated.js", force = false)
59
- return unless trace_react_on_rails? || force
60
- # Set to anything to print generated code.
61
- puts "Z" * 80
62
- puts "react_renderer.rb: 92"
63
- puts "wrote file #{file_name}"
64
- File.write(file_name, js_code)
65
- puts "Z" * 80
66
- end
67
-
68
- def trace_react_on_rails?
69
- ENV["TRACE_REACT_ON_RAILS"].present?
70
- end
71
-
72
- def eval_js(js_code)
73
- @js_context_pool.with do |js_context|
74
- result = js_context.eval(js_code)
75
- js_context.eval("console.history = []")
76
- result
77
- end
78
- end
79
-
80
- def create_js_context
81
- server_js_file = ReactOnRails::Utils.default_server_bundle_js_file_path
82
- if server_js_file.present? && File.exist?(server_js_file)
83
- bundle_js_code = File.read(server_js_file)
84
- base_js_code = <<-JS
85
- #{console_polyfill}
86
- #{execjs_timer_polyfills}
87
- #{bundle_js_code};
88
- JS
89
- file_name = "tmp/base_js_code.js"
90
- begin
91
- trace_messsage(base_js_code, file_name)
92
- ExecJS.compile(base_js_code)
93
- rescue => e
94
- msg = "ERROR when compiling base_js_code! "\
95
- "See file #{file_name} to "\
96
- "correlate line numbers of error. Error is\n\n#{e.message}"\
97
- "\n\n#{e.backtrace.join("\n")}"
98
- puts msg
99
- Rails.logger.error(msg)
100
- trace_messsage(base_js_code, file_name, true)
101
- raise e
102
- end
103
- else
104
- if server_js_file.present?
105
- msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be "\
106
- "read. You may set the server_bundle_js_file in your configuration to be \"\" to "\
107
- "avoid this warning"
108
- Rails.logger.warn msg
109
- puts msg
110
- end
111
- ExecJS.compile("")
112
- end
113
- end
114
-
115
- def execjs_timer_polyfills
116
- <<-JS
117
- function getStackTrace () {
118
- var stack;
119
- try {
120
- throw new Error('');
121
- }
122
- catch (error) {
123
- stack = error.stack || '';
124
- }
125
- stack = stack.split('\\n').map(function (line) { return line.trim(); });
126
- return stack.splice(stack[0] == 'Error' ? 2 : 1);
127
- }
128
-
129
- function setInterval() {
130
- #{undefined_for_exec_js_logging('setInterval')}
131
- }
132
-
133
- function setTimeout() {
134
- #{undefined_for_exec_js_logging('setTimeout')}
135
- }
136
- JS
137
- end
138
-
139
- def undefined_for_exec_js_logging(function_name)
140
- if trace_react_on_rails?
141
- "console.error('#{function_name} is not defined for execJS. See "\
142
- "https://github.com/sstephenson/execjs#faq. Note babel-polyfill may call this.');\n"\
143
- " console.error(getStackTrace().join('\\n'));"
11
+ def pool
12
+ if ReactOnRails.configuration.server_render_method == "NodeJS"
13
+ ServerRenderingPool::Node
144
14
  else
145
- ""
15
+ ServerRenderingPool::Exec
146
16
  end
147
17
  end
148
18
 
149
- # Reimplement console methods for replaying on the client
150
- def console_polyfill
151
- <<-JS
152
- var console = { history: [] };
153
- ['error', 'log', 'info', 'warn'].forEach(function (level) {
154
- console[level] = function () {
155
- var argArray = Array.prototype.slice.call(arguments);
156
- if (argArray.length > 0) {
157
- argArray[0] = '[SERVER] ' + argArray[0];
158
- }
159
- console.history.push({level: level, arguments: argArray});
160
- };
161
- });
162
- JS
19
+ def method_missing(sym, *args, &block)
20
+ pool.send sym, *args, &block
163
21
  end
164
22
  end
165
23
  end
@@ -0,0 +1,166 @@
1
+ module ReactOnRails
2
+ module ServerRenderingPool
3
+ # This implementation of the rendering pool uses ExecJS to execute javasript code
4
+ class Exec
5
+ def self.reset_pool
6
+ options = {
7
+ size: ReactOnRails.configuration.server_renderer_pool_size,
8
+ timeout: ReactOnRails.configuration.server_renderer_timeout
9
+ }
10
+ @js_context_pool = ConnectionPool.new(options) { create_js_context }
11
+ end
12
+
13
+ def self.reset_pool_if_server_bundle_was_modified
14
+ return unless ReactOnRails.configuration.development_mode
15
+ file_mtime = File.mtime(ReactOnRails::Utils.default_server_bundle_js_file_path)
16
+ @server_bundle_timestamp ||= file_mtime
17
+ return if @server_bundle_timestamp == file_mtime
18
+ ReactOnRails::ServerRenderingPool.reset_pool
19
+ @server_bundle_timestamp = file_mtime
20
+ end
21
+
22
+ # js_code: JavaScript expression that returns a string.
23
+ # Returns a Hash:
24
+ # html: string of HTML for direct insertion on the page by evaluating js_code
25
+ # consoleReplayScript: script for replaying console
26
+ # hasErrors: true if server rendering errors
27
+ # Note, js_code does not have to be based on React.
28
+ # js_code MUST RETURN json stringify Object
29
+ # Calling code will probably call 'html_safe' on return value before rendering to the view.
30
+ def self.server_render_js_with_console_logging(js_code)
31
+ if trace_react_on_rails?
32
+ @file_index ||= 1
33
+ trace_messsage(js_code, "tmp/server-generated-#{@file_index % 10}.js")
34
+ @file_index += 1
35
+ end
36
+ json_string = eval_js(js_code)
37
+ result = JSON.parse(json_string)
38
+
39
+ if ReactOnRails.configuration.logging_on_server
40
+ console_script = result["consoleReplayScript"]
41
+ console_script_lines = console_script.split("\n")
42
+ console_script_lines = console_script_lines[2..-2]
43
+ re = /console\.log\.apply\(console, \["\[SERVER\] (?<msg>.*)"\]\);/
44
+ if console_script_lines
45
+ console_script_lines.each do |line|
46
+ match = re.match(line)
47
+ Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match
48
+ end
49
+ end
50
+ end
51
+ result
52
+ end
53
+
54
+ class << self
55
+ private
56
+
57
+ def trace_messsage(js_code, file_name = "tmp/server-generated.js", force = false)
58
+ return unless trace_react_on_rails? || force
59
+ # Set to anything to print generated code.
60
+ puts "Z" * 80
61
+ puts "react_renderer.rb: 92"
62
+ puts "wrote file #{file_name}"
63
+ File.write(file_name, js_code)
64
+ puts "Z" * 80
65
+ end
66
+
67
+ def trace_react_on_rails?
68
+ ENV["TRACE_REACT_ON_RAILS"].present?
69
+ end
70
+
71
+ def eval_js(js_code)
72
+ @js_context_pool.with do |js_context|
73
+ result = js_context.eval(js_code)
74
+ js_context.eval("console.history = []")
75
+ result
76
+ end
77
+ end
78
+
79
+ def create_js_context
80
+ server_js_file = ReactOnRails::Utils.default_server_bundle_js_file_path
81
+ if server_js_file.present? && File.exist?(server_js_file)
82
+ bundle_js_code = File.read(server_js_file)
83
+ base_js_code = <<-JS
84
+ #{console_polyfill}
85
+ #{execjs_timer_polyfills}
86
+ #{bundle_js_code};
87
+ JS
88
+ file_name = "tmp/base_js_code.js"
89
+ begin
90
+ trace_messsage(base_js_code, file_name)
91
+ ExecJS.compile(base_js_code)
92
+ rescue => e
93
+ msg = "ERROR when compiling base_js_code! "\
94
+ "See file #{file_name} to "\
95
+ "correlate line numbers of error. Error is\n\n#{e.message}"\
96
+ "\n\n#{e.backtrace.join("\n")}"
97
+ puts msg
98
+ Rails.logger.error(msg)
99
+ trace_messsage(base_js_code, file_name, true)
100
+ raise e
101
+ end
102
+ else
103
+ if server_js_file.present?
104
+ msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be "\
105
+ "read. You may set the server_bundle_js_file in your configuration to be \"\" to "\
106
+ "avoid this warning"
107
+ Rails.logger.warn msg
108
+ puts msg
109
+ end
110
+ ExecJS.compile("")
111
+ end
112
+ end
113
+
114
+ def execjs_timer_polyfills
115
+ <<-JS
116
+ function getStackTrace () {
117
+ var stack;
118
+ try {
119
+ throw new Error('');
120
+ }
121
+ catch (error) {
122
+ stack = error.stack || '';
123
+ }
124
+ stack = stack.split('\\n').map(function (line) { return line.trim(); });
125
+ return stack.splice(stack[0] == 'Error' ? 2 : 1);
126
+ }
127
+
128
+ function setInterval() {
129
+ #{undefined_for_exec_js_logging('setInterval')}
130
+ }
131
+
132
+ function setTimeout() {
133
+ #{undefined_for_exec_js_logging('setTimeout')}
134
+ }
135
+ JS
136
+ end
137
+
138
+ def undefined_for_exec_js_logging(function_name)
139
+ if trace_react_on_rails?
140
+ "console.error('#{function_name} is not defined for execJS. See "\
141
+ "https://github.com/sstephenson/execjs#faq. Note babel-polyfill may call this.');\n"\
142
+ " console.error(getStackTrace().join('\\n'));"
143
+ else
144
+ ""
145
+ end
146
+ end
147
+
148
+ # Reimplement console methods for replaying on the client
149
+ def console_polyfill
150
+ <<-JS
151
+ var console = { history: [] };
152
+ ['error', 'log', 'info', 'warn'].forEach(function (level) {
153
+ console[level] = function () {
154
+ var argArray = Array.prototype.slice.call(arguments);
155
+ if (argArray.length > 0) {
156
+ argArray[0] = '[SERVER] ' + argArray[0];
157
+ }
158
+ console.history.push({level: level, arguments: argArray});
159
+ };
160
+ });
161
+ JS
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end