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.
- checksums.yaml +5 -5
- data/.codeclimate.yml +27 -0
- data/.gitignore +30 -37
- data/.rubocop.yml +1159 -0
- data/.travis.yml +32 -0
- data/Appraisals +31 -0
- data/CHANGELOG.md +143 -0
- data/DOCS.md +1515 -0
- data/Gemfile +2 -5
- data/LICENSE +19 -0
- data/README.md +5 -33
- data/Rakefile +25 -6
- data/UPGRADING.md +24 -0
- data/component-name-lookup.md +145 -0
- data/dciy.toml +3 -0
- data/dciy_prepare.sh +8 -0
- data/dciy_run.sh +10 -0
- data/hyper-react.gemspec +24 -18
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
- data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
- data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
- data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +117 -0
- data/lib/hyper-react.rb +66 -4
- data/lib/rails-helpers/top_level_rails_component.rb +75 -0
- data/lib/react/api.rb +203 -0
- data/lib/react/callbacks.rb +41 -0
- data/lib/react/children.rb +30 -0
- data/lib/react/component.rb +177 -0
- data/lib/react/component/api.rb +69 -0
- data/lib/react/component/base.rb +13 -0
- data/lib/react/component/class_methods.rb +181 -0
- data/lib/react/component/dsl_instance_methods.rb +23 -0
- data/lib/react/component/params.rb +6 -0
- data/lib/react/component/props_wrapper.rb +78 -0
- data/lib/react/component/should_component_update.rb +99 -0
- data/lib/react/component/tags.rb +108 -0
- data/lib/react/config.rb +5 -0
- data/lib/react/config/client.rb.erb +19 -0
- data/lib/react/config/server.rb +23 -0
- data/lib/react/element.rb +150 -0
- data/lib/react/event.rb +76 -0
- data/lib/react/ext/hash.rb +9 -0
- data/lib/react/ext/opal-jquery/element.rb +26 -0
- data/lib/react/ext/string.rb +8 -0
- data/lib/react/hash.rb +13 -0
- data/lib/react/native_library.rb +87 -0
- data/lib/react/object.rb +15 -0
- data/lib/react/react-source-browser.rb +3 -0
- data/lib/react/react-source-server.rb +3 -0
- data/lib/react/react-source.rb +16 -0
- data/lib/react/ref_callback.rb +31 -0
- data/lib/react/rendering_context.rb +146 -0
- data/lib/react/server.rb +19 -0
- data/lib/react/state_wrapper.rb +23 -0
- data/lib/react/test.rb +16 -0
- data/lib/react/test/dsl.rb +17 -0
- data/lib/react/test/matchers/render_html_matcher.rb +56 -0
- data/lib/react/test/rspec.rb +15 -0
- data/lib/react/test/session.rb +37 -0
- data/lib/react/test/utils.rb +71 -0
- data/lib/react/top_level.rb +110 -0
- data/lib/react/top_level_render.rb +28 -0
- data/lib/react/validator.rb +136 -0
- data/lib/reactive-ruby/component_loader.rb +43 -0
- data/lib/reactive-ruby/isomorphic_helpers.rb +235 -0
- data/lib/reactive-ruby/rails.rb +8 -0
- data/lib/reactive-ruby/rails/component_mount.rb +48 -0
- data/lib/reactive-ruby/rails/controller_helper.rb +14 -0
- data/lib/reactive-ruby/rails/railtie.rb +20 -0
- data/lib/reactive-ruby/serializers.rb +15 -0
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +41 -0
- data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
- data/lib/reactive-ruby/version.rb +3 -0
- data/lib/reactrb/auto-import.rb +27 -0
- data/logo1.png +0 -0
- data/logo2.png +0 -0
- data/logo3.png +0 -0
- data/path_release_steps.md +9 -0
- data/spec/controller_helper_spec.rb +35 -0
- data/spec/index.html.erb +11 -0
- data/spec/react/callbacks_spec.rb +142 -0
- data/spec/react/children_spec.rb +132 -0
- data/spec/react/component/base_spec.rb +36 -0
- data/spec/react/component_spec.rb +1073 -0
- data/spec/react/dsl_spec.rb +323 -0
- data/spec/react/element_spec.rb +132 -0
- data/spec/react/event_spec.rb +39 -0
- data/spec/react/native_library_spec.rb +387 -0
- data/spec/react/observable_spec.rb +31 -0
- data/spec/react/opal_jquery_extensions_spec.rb +68 -0
- data/spec/react/param_declaration_spec.rb +253 -0
- data/spec/react/react_spec.rb +278 -0
- data/spec/react/refs_callback_spec.rb +65 -0
- data/spec/react/server_spec.rb +25 -0
- data/spec/react/state_spec.rb +52 -0
- data/spec/react/test/dsl_spec.rb +43 -0
- data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
- data/spec/react/test/rspec_spec.rb +62 -0
- data/spec/react/test/session_spec.rb +88 -0
- data/spec/react/test/utils_spec.rb +28 -0
- data/spec/react/top_level_component_spec.rb +103 -0
- data/spec/react/tutorial/tutorial_spec.rb +42 -0
- data/spec/react/validator_spec.rb +134 -0
- data/spec/reactive-ruby/component_loader_spec.rb +74 -0
- data/spec/reactive-ruby/isomorphic_helpers_spec.rb +157 -0
- data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +17 -0
- data/spec/reactive-ruby/rails/component_mount_spec.rb +64 -0
- data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +39 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/test_app/README.md +24 -0
- data/spec/test_app/Rakefile +6 -0
- data/spec/test_app/app/assets/config/manifest.js +3 -0
- data/spec/test_app/app/assets/images/.keep +0 -0
- data/spec/test_app/app/assets/javascripts/application.rb +7 -0
- data/spec/test_app/app/assets/javascripts/cable.js +13 -0
- data/spec/test_app/app/assets/javascripts/channels/.keep +0 -0
- data/spec/test_app/app/assets/javascripts/server_rendering.js +5 -0
- data/spec/test_app/app/assets/stylesheets/application.css +15 -0
- data/spec/test_app/app/channels/application_cable/channel.rb +4 -0
- data/spec/test_app/app/channels/application_cable/connection.rb +4 -0
- data/spec/test_app/app/controllers/application_controller.rb +3 -0
- data/spec/test_app/app/controllers/concerns/.keep +0 -0
- data/spec/test_app/app/helpers/application_helper.rb +2 -0
- data/spec/test_app/app/jobs/application_job.rb +2 -0
- data/spec/test_app/app/mailers/application_mailer.rb +4 -0
- data/spec/test_app/app/models/application_record.rb +3 -0
- data/spec/test_app/app/models/concerns/.keep +0 -0
- data/spec/test_app/app/views/components.rb +11 -0
- data/spec/test_app/app/views/components/hello_world.rb +11 -0
- data/spec/test_app/app/views/components/todo.rb +14 -0
- data/spec/test_app/app/views/layouts/application.html.erb +14 -0
- data/spec/test_app/app/views/layouts/explicit_layout.html.erb +0 -0
- data/spec/test_app/app/views/layouts/mailer.html.erb +13 -0
- data/spec/test_app/app/views/layouts/mailer.text.erb +1 -0
- data/spec/test_app/app/views/layouts/test_layout.html.erb +0 -0
- data/spec/test_app/bin/bundle +3 -0
- data/spec/test_app/bin/rails +4 -0
- data/spec/test_app/bin/rake +4 -0
- data/spec/test_app/bin/setup +38 -0
- data/spec/test_app/bin/update +29 -0
- data/spec/test_app/bin/yarn +11 -0
- data/spec/test_app/config.ru +5 -0
- data/spec/test_app/config/application.rb +45 -0
- data/spec/test_app/config/boot.rb +6 -0
- data/spec/test_app/config/cable.yml +10 -0
- data/spec/test_app/config/database.yml +25 -0
- data/spec/test_app/config/environment.rb +5 -0
- data/spec/test_app/config/environments/development.rb +54 -0
- data/spec/test_app/config/environments/production.rb +91 -0
- data/spec/test_app/config/environments/test.rb +42 -0
- data/spec/test_app/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/test_app/config/initializers/assets.rb +14 -0
- data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
- data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/test_app/config/initializers/inflections.rb +16 -0
- data/spec/test_app/config/initializers/mime_types.rb +4 -0
- data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/test_app/config/locales/en.yml +33 -0
- data/spec/test_app/config/puma.rb +56 -0
- data/spec/test_app/config/routes.rb +3 -0
- data/spec/test_app/config/secrets.yml +32 -0
- data/spec/test_app/config/spring.rb +6 -0
- data/spec/test_app/db/development.sqlite3 +0 -0
- data/spec/test_app/db/schema.rb +15 -0
- data/spec/test_app/db/seeds.rb +7 -0
- data/spec/test_app/db/test.sqlite3 +0 -0
- data/spec/test_app/lib/assets/.keep +0 -0
- data/spec/test_app/log/.keep +0 -0
- data/spec/test_app/package.json +5 -0
- data/spec/test_app/public/404.html +67 -0
- data/spec/test_app/public/422.html +67 -0
- data/spec/test_app/public/500.html +66 -0
- data/spec/test_app/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/test_app/public/apple-touch-icon.png +0 -0
- data/spec/test_app/public/favicon.ico +0 -0
- data/spec/vendor/es5-shim.min.js +7 -0
- data/spec/vendor/jquery-2.2.4.min.js +4 -0
- metadata +401 -61
- data/CODE_OF_CONDUCT.md +0 -49
- data/lib/react/version.rb +0 -3
File without changes
|
@@ -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
|
data/lib/hyper-react.rb
CHANGED
@@ -1,8 +1,70 @@
|
|
1
|
-
require '
|
2
|
-
|
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
|
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
|
-
|
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
|
data/lib/react/api.rb
ADDED
@@ -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
|