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,14 @@
1
+ module Components
2
+ class Todo
3
+ include React::Component
4
+ export_component
5
+
6
+ params do
7
+ requires :todo
8
+ end
9
+
10
+ def render
11
+ li { "#{params[:todo]}" }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,117 @@
1
+ require 'rails/generators/rails/app/app_generator'
2
+
3
+ module ReactiveRuby
4
+ class TestAppGenerator < ::Rails::Generators::Base
5
+ def self.source_paths
6
+ paths = self.superclass.source_paths
7
+ paths << File.expand_path('../templates', __FILE__)
8
+ paths.flatten
9
+ end
10
+
11
+ def remove_existing_app
12
+ remove_dir(test_app_path) if File.directory?(test_app_path)
13
+ end
14
+
15
+ def generate_test_app
16
+ opts = options.dup
17
+ opts[:database] = 'sqlite3' if opts[:database].blank?
18
+ opts[:force] = true
19
+ opts[:skip_bundle] = true
20
+
21
+ puts "Generating Test Rails Application..."
22
+ invoke ::Rails::Generators::AppGenerator,
23
+ [ File.expand_path(test_app_path, destination_root) ], opts
24
+ end
25
+
26
+ def configure_test_app
27
+ template 'boot.rb.erb', "#{test_app_path}/config/boot.rb", force: true
28
+ template 'test_application.rb.erb', "#{test_app_path}/config/application.rb", force: true
29
+ template 'assets/javascripts/test_application.rb',
30
+ "#{test_app_path}/app/assets/javascripts/application.rb", force: true
31
+ template 'assets/javascripts/server_rendering.js',
32
+ "#{test_app_path}/app/assets/javascripts/server_rendering.js", force: true
33
+ template 'assets/javascripts/components.rb',
34
+ "#{test_app_path}/app/views/components.rb", force: true
35
+ template 'views/components/hello_world.rb',
36
+ "#{test_app_path}/app/views/components/hello_world.rb", force: true
37
+ template 'views/components/todo.rb',
38
+ "#{test_app_path}/app/views/components/todo.rb", force: true
39
+ template 'views/layouts/test_layout.html.erb',
40
+ "#{test_app_path}/app/views/layouts/test_layout.html.erb", force: true
41
+ template 'views/layouts/test_layout.html.erb',
42
+ "#{test_app_path}/app/views/layouts/explicit_layout.html.erb", force: true
43
+ end
44
+
45
+ def clean_superfluous_files
46
+ inside test_app_path do
47
+ remove_file '.gitignore'
48
+ remove_file 'doc'
49
+ remove_file 'Gemfile'
50
+ remove_file 'lib/tasks'
51
+ remove_file 'app/assets/images/rails.png'
52
+ remove_file 'app/assets/javascripts/application.js'
53
+ remove_file 'public/index.html'
54
+ remove_file 'public/robots.txt'
55
+ remove_file 'README.rdoc'
56
+ remove_file 'test'
57
+ remove_file 'vendor'
58
+ remove_file 'spec'
59
+ end
60
+ end
61
+
62
+ def configure_opal_rspec
63
+ inject_into_file "#{test_app_path}/config/application.rb",
64
+ after: /class Application < Rails::Application/, verbose: true do
65
+ %Q[
66
+ config.opal.method_missing = true
67
+ config.opal.optimized_operators = true
68
+ config.opal.arity_check = false
69
+ config.opal.const_missing = true
70
+ config.opal.dynamic_require_severity = :ignore
71
+ config.opal.enable_specs = true
72
+ config.opal.spec_location = 'spec-opal'
73
+ config.hyperloop.auto_config = false
74
+
75
+ config.react.server_renderer_options = {
76
+ files: ["server_rendering.js"]
77
+ }
78
+ config.react.server_renderer_directories = ["/app/assets/javascripts"]
79
+ ]
80
+ end
81
+ end
82
+
83
+ protected
84
+
85
+ def application_definition
86
+ @application_definition ||= begin
87
+ test_application_contents
88
+ end
89
+ end
90
+ alias :store_application_definition! :application_definition
91
+
92
+ private
93
+
94
+ def test_app_path
95
+ 'spec/test_app'
96
+ end
97
+
98
+ def test_application_path
99
+ File.expand_path("#{test_app_path}/config/application.rb",
100
+ destination_root)
101
+ end
102
+
103
+ def test_application_contents
104
+ return unless File.exists?(test_application_path) && !options[:pretend]
105
+ contents = File.read(test_application_path)
106
+ contents[(contents.index("module #{module_name}"))..-1]
107
+ end
108
+
109
+ def module_name
110
+ 'TestApp'
111
+ end
112
+
113
+ def gemfile_path
114
+ '../../../../Gemfile'
115
+ end
116
+ end
117
+ end
@@ -1,8 +1,70 @@
1
- require 'hyper-component'
2
- require 'react/version'
1
+ require 'hyperloop-config'
2
+ Hyperloop.import 'hyper-store'
3
+ Hyperloop.import 'react/react-source-browser', client_only: true
4
+ Hyperloop.import 'react/react-source-server', server_only: true
5
+ Hyperloop.import 'browser/delay', client_only: true
3
6
  Hyperloop.import 'hyper-react'
7
+ Hyperloop.import 'react_ujs'
4
8
 
5
- if RUBY_ENGINE != 'opal'
9
+ if RUBY_ENGINE == 'opal'
10
+ module Hyperloop
11
+ class Component
12
+ end
13
+ end
14
+
15
+ if `Opal.global.React === undefined || Opal.global.React.version === undefined`
16
+ raise [
17
+ "No React.js Available",
18
+ "",
19
+ "A global `React` must be defined before requiring 'hyper-react'",
20
+ "",
21
+ "To USE THE BUILT-IN SOURCE: ",
22
+ " add 'require \"react/react-source-browser\"' immediately before the 'require \"hyper-react\" directive.",
23
+ "IF USING WEBPACK:",
24
+ " add 'react' to your webpack manifest."
25
+ ].join("\n")
26
+ end
27
+ require 'react/hash'
28
+ require 'react/top_level'
29
+ require 'react/top_level_render'
30
+ require 'react/observable'
31
+ require 'react/validator'
32
+ require 'react/component'
33
+ require 'react/component/dsl_instance_methods'
34
+ require 'react/component/should_component_update'
35
+ require 'react/component/tags'
36
+ require 'react/component/base'
37
+ require 'react/element'
38
+ require 'react/event'
39
+ require 'react/api'
40
+ require 'react/rendering_context'
41
+ require 'react/state'
42
+ require 'react/object'
43
+ require 'react/ext/opal-jquery/element'
44
+ require 'reactive-ruby/isomorphic_helpers'
45
+ require 'rails-helpers/top_level_rails_component'
46
+ require 'reactive-ruby/version'
47
+ module Hyperloop
48
+ class Component
49
+ def self.inherited(child)
50
+ child.include(Mixin)
51
+ end
52
+ end
53
+ end
54
+ React::Component.deprecation_warning(
55
+ 'component.rb',
56
+ "Requiring 'hyper-react' is deprecated. Use gem 'hyper-component', and require 'hyper-component' instead."
57
+ ) unless defined? Hyperloop::Component::VERSION
58
+ else
6
59
  require 'opal'
7
- Opal.append_path(File.expand_path('../', __FILE__).untaint)
60
+
61
+ require 'hyper-store'
62
+ require 'opal-activesupport'
63
+ require 'reactive-ruby/version'
64
+ require 'reactive-ruby/rails' if defined?(Rails)
65
+ require 'reactive-ruby/isomorphic_helpers'
66
+ require 'reactive-ruby/serializers'
67
+
68
+ Opal.append_path File.expand_path('../', __FILE__).untaint
69
+ require 'react/react-source'
8
70
  end
@@ -0,0 +1,75 @@
1
+ module React
2
+ class TopLevelRailsComponent
3
+ include Hyperloop::Component::Mixin
4
+
5
+ def self.search_path
6
+ @search_path ||= [Object]
7
+ end
8
+
9
+ export_component
10
+
11
+ param :component_name
12
+ param :controller
13
+ param :render_params
14
+
15
+ backtrace :off
16
+
17
+ def render
18
+ paths_searched = []
19
+ component = nil
20
+ if params.component_name.start_with?('::')
21
+ # if absolute path of component is given, look it up and fail if not found
22
+ paths_searched << params.component_name
23
+ component = begin
24
+ Object.const_get(params.component_name)
25
+ rescue NameError
26
+ nil
27
+ end
28
+ else
29
+ # if relative path is given, look it up like this
30
+ # 1) we check each path + controller-name + component-name
31
+ # 2) if we can't find it there we check each path + component-name
32
+ # if we can't find it we just try const_get
33
+ # so (assuming controller name is Home)
34
+ # ::Foo::Bar will only resolve to some component named ::Foo::Bar
35
+ # but Foo::Bar will check (in this order) ::Home::Foo::Bar, ::Components::Home::Foo::Bar, ::Foo::Bar, ::Components::Foo::Bar
36
+ self.class.search_path.each do |scope|
37
+ paths_searched << "#{scope.name}::#{params.controller}::#{params.component_name}"
38
+ component = begin
39
+ scope.const_get(params.controller, false).const_get(params.component_name, false)
40
+ rescue NameError
41
+ nil
42
+ end
43
+ break if component != nil
44
+ end
45
+ unless component
46
+ self.class.search_path.each do |scope|
47
+ paths_searched << "#{scope.name}::#{params.component_name}"
48
+ component = begin
49
+ scope.const_get(params.component_name, false)
50
+ rescue NameError
51
+ nil
52
+ end
53
+ break if component != nil
54
+ end
55
+ end
56
+ end
57
+ return React::RenderingContext.render(component, params.render_params) if component && component.method_defined?(:render)
58
+ raise "Could not find component class '#{params.component_name}' for params.controller '#{params.controller}' in any component directory. Tried [#{paths_searched.join(", ")}]"
59
+ end
60
+ end
61
+ end
62
+
63
+ class Module
64
+ def add_to_react_search_path(replace_search_path = nil)
65
+ if replace_search_path
66
+ React::TopLevelRailsComponent.search_path = [self]
67
+ elsif !React::TopLevelRailsComponent.search_path.include? self
68
+ React::TopLevelRailsComponent.search_path << self
69
+ end
70
+ end
71
+ end
72
+
73
+ module Components
74
+ add_to_react_search_path
75
+ end
@@ -0,0 +1,203 @@
1
+ require 'react/native_library'
2
+
3
+ module React
4
+ # Provides the internal mechanisms to interface between reactrb and native components
5
+ # the code will attempt to create a js component wrapper on any rb class that has a
6
+ # render (or possibly _render_wrapper) method. The mapping between rb and js components
7
+ # is kept in the @@component_classes hash.
8
+
9
+ # Also provides the mechanism to build react elements
10
+
11
+ # TOOO - the code to deal with components should be moved to a module that will be included
12
+ # in a class which will then create the JS component for that class. That module will then
13
+ # be included in React::Component, but can be used by any class wanting to become a react
14
+ # component (but without other DSL characteristics.)
15
+ class API
16
+ @@component_classes = {}
17
+
18
+ def self.import_native_component(opal_class, native_class)
19
+ opal_class.instance_variable_set("@native_import", true)
20
+ @@component_classes[opal_class] = native_class
21
+ end
22
+
23
+ def self.eval_native_react_component(name)
24
+ component = `eval(name)`
25
+ raise "#{name} is not defined" if `#{component} === undefined`
26
+ is_component_class = `#{component}.prototype !== undefined` &&
27
+ (`!!#{component}.prototype.isReactComponent` ||
28
+ `!!#{component}.prototype.render`)
29
+ is_functional_component = `typeof #{component} === "function"`
30
+ unless is_component_class || is_functional_component
31
+ raise 'does not appear to be a native react component'
32
+ end
33
+ component
34
+ end
35
+
36
+ def self.native_react_component?(name = nil)
37
+ return false unless name
38
+ eval_native_react_component(name)
39
+ rescue
40
+ nil
41
+ end
42
+
43
+ def self.create_native_react_class(type)
44
+ raise "Provided class should define `render` method" if !(type.method_defined? :render)
45
+ render_fn = (type.method_defined? :_render_wrapper) ? :_render_wrapper : :render
46
+ # this was hashing type.to_s, not sure why but .to_s does not work as it Foo::Bar::View.to_s just returns "View"
47
+ @@component_classes[type] ||= %x{
48
+ class extends React.Component {
49
+ constructor(props) {
50
+ super(props);
51
+ this.mixins = #{type.respond_to?(:native_mixins) ? type.native_mixins : `[]`};
52
+ this.statics = #{type.respond_to?(:static_call_backs) ? type.static_call_backs.to_n : `{}`};
53
+ this.state = {};
54
+ this.__opalInstanceInitializedState = false;
55
+ this.__opalInstanceSyncSetState = true;
56
+ this.__opalInstance = #{type.new(`this`)};
57
+ this.__opalInstanceInitializedState = true;
58
+ this.__opalInstanceSyncSetState = false;
59
+ }
60
+ static get displayName() {
61
+ return #{type.name};
62
+ }
63
+ static get defaultProps() {
64
+ return #{type.respond_to?(:default_props) ? type.default_props.to_n : `{}`};
65
+ }
66
+ static get propTypes() {
67
+ return #{type.respond_to?(:prop_types) ? type.prop_types.to_n : `{}`};
68
+ }
69
+ componentWillMount() {
70
+ if (#{type.method_defined? :component_will_mount}) {
71
+ this.__opalInstanceSyncSetState = true;
72
+ this.__opalInstance.$component_will_mount();
73
+ this.__opalInstanceSyncSetState = false;
74
+ }
75
+ }
76
+ componentDidMount() {
77
+ this.__opalInstance.is_mounted = true
78
+ if (#{type.method_defined? :component_did_mount}) {
79
+ this.__opalInstanceSyncSetState = false;
80
+ this.__opalInstance.$component_did_mount();
81
+ }
82
+ }
83
+ componentWillReceiveProps(next_props) {
84
+ if (#{type.method_defined? :component_will_receive_props}) {
85
+ this.__opalInstanceSyncSetState = true;
86
+ this.__opalInstance.$component_will_receive_props(Opal.Hash.$new(next_props));
87
+ this.__opalInstanceSyncSetState = false;
88
+ }
89
+ }
90
+ shouldComponentUpdate(next_props, next_state) {
91
+ if (#{type.method_defined? :should_component_update?}) {
92
+ this.__opalInstanceSyncSetState = false;
93
+ return this.__opalInstance["$should_component_update?"](Opal.Hash.$new(next_props), Opal.Hash.$new(next_state));
94
+ } else { return true; }
95
+ }
96
+ componentWillUpdate(next_props, next_state) {
97
+ if (#{type.method_defined? :component_will_update}) {
98
+ this.__opalInstanceSyncSetState = false;
99
+ this.__opalInstance.$component_will_update(Opal.Hash.$new(next_props), Opal.Hash.$new(next_state));
100
+ }
101
+ }
102
+ componentDidUpdate(prev_props, prev_state) {
103
+ if (#{type.method_defined? :component_did_update}) {
104
+ this.__opalInstanceSyncSetState = false;
105
+ this.__opalInstance.$component_did_update(Opal.Hash.$new(prev_props), Opal.Hash.$new(prev_state));
106
+ }
107
+ }
108
+ componentWillUnmount() {
109
+ this.__opalInstance.is_mounted = false;
110
+ if (#{type.method_defined? :component_will_unmount}) {
111
+ this.__opalInstanceSyncSetState = false;
112
+ this.__opalInstance.$component_will_unmount();
113
+ }
114
+ }
115
+ componentDidCatch(error, info) {
116
+ if (#{type.method_defined? :component_did_catch}) {
117
+ this.__opalInstanceSyncSetState = false;
118
+ this.__opalInstance.$component_did_catch(error, Opal.Hash.$new(info));
119
+ }
120
+ }
121
+ render() {
122
+ this.__opalInstanceSyncSetState = false;
123
+ return this.__opalInstance.$send(render_fn).$to_n();
124
+ }
125
+ }
126
+ }
127
+ end
128
+
129
+ def self.create_element(type, properties = {}, &block)
130
+ params = []
131
+
132
+ # Component Spec, Normal DOM, String or Native Component
133
+ ncc = @@component_classes[type]
134
+ if ncc
135
+ params << ncc
136
+ elsif type.is_a?(Class)
137
+ params << create_native_react_class(type)
138
+ elsif block_given? || React::Component::Tags::HTML_TAGS.include?(type)
139
+ params << type
140
+ elsif type.is_a?(String)
141
+ return React::Element.new(type)
142
+ else
143
+ raise "#{type} not implemented"
144
+ end
145
+
146
+ # Convert Passed in properties
147
+ properties = convert_props(properties)
148
+ params << properties.shallow_to_n
149
+
150
+ # Children Nodes
151
+ if block_given?
152
+ a = [yield].flatten
153
+ %x{
154
+ for(var i=0, l=a.length; i<l; i++) {
155
+ params.push(a[i].$to_n());
156
+ }
157
+ }
158
+ end
159
+ React::Element.new(`React.createElement.apply(null, #{params})`, type, properties, block)
160
+ end
161
+
162
+ def self.clear_component_class_cache
163
+ @@component_classes = {}
164
+ end
165
+
166
+ def self.convert_props(properties)
167
+ raise "Component parameters must be a hash. Instead you sent #{properties}" unless properties.is_a? Hash
168
+ props = {}
169
+ properties.each do |key, value|
170
+ if key == "class" || key == "class_name"
171
+ props["className"] = value
172
+ elsif ["style", "dangerously_set_inner_HTML"].include? key
173
+ props[lower_camelize(key)] = value.to_n
174
+ elsif key == 'ref' && value.is_a?(Proc)
175
+ props[key] = %x{
176
+ function(dom_node){
177
+ if (dom_node.__opalInstance !== undefined && dom_node.__opalInstance !== null) {
178
+ #{ value.call(`dom_node.__opalInstance`) };
179
+ } else if(ReactDOM.findDOMNode !== undefined && dom_node.nodeType === undefined) {
180
+ #{ value.call(`ReactDOM.findDOMNode(dom_node)`) };
181
+ } else {
182
+ #{ value.call(`dom_node`) };
183
+ }
184
+ }
185
+ }
186
+ elsif React::HASH_ATTRIBUTES.include?(key) && value.is_a?(Hash)
187
+ value.each { |k, v| props["#{key}-#{k.tr('_', '-')}"] = v.to_n }
188
+ else
189
+ props[React.html_attr?(lower_camelize(key)) ? lower_camelize(key) : key] = value
190
+ end
191
+ end
192
+ props
193
+ end
194
+
195
+ private
196
+
197
+ def self.lower_camelize(snake_cased_word)
198
+ words = snake_cased_word.split('_')
199
+ result = [words.first]
200
+ result.concat(words[1..-1].map {|word| word[0].upcase + word[1..-1] }).join('')
201
+ end
202
+ end
203
+ end