reactrb 0.7.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.codeclimate.yml +6 -0
- data/.gitignore +33 -0
- data/.travis.yml +9 -0
- data/Gemfile +2 -0
- data/LICENSE +19 -0
- data/README.md +117 -0
- data/Rakefile +28 -0
- data/config.ru +16 -0
- data/example/examples/Gemfile +7 -0
- data/example/examples/app/basics.js.rb +42 -0
- data/example/examples/app/items.rb +11 -0
- data/example/examples/app/jquery.js +5 -0
- data/example/examples/app/nodes.rb +61 -0
- data/example/examples/app/react-router.js +6 -0
- data/example/examples/app/react_api_demo.rb +29 -0
- data/example/examples/app/rerendering.rb +72 -0
- data/example/examples/app/reuse.rb +59 -0
- data/example/examples/app/show.rb +52 -0
- data/example/examples/config.ru +38 -0
- data/example/rails-tutorial/.gitignore +17 -0
- data/example/rails-tutorial/Gemfile +51 -0
- data/example/rails-tutorial/README.rdoc +28 -0
- data/example/rails-tutorial/Rakefile +6 -0
- data/example/rails-tutorial/app/assets/images/.keep +0 -0
- data/example/rails-tutorial/app/assets/javascripts/application.rb +15 -0
- data/example/rails-tutorial/app/assets/stylesheets/application.css +15 -0
- data/example/rails-tutorial/app/controllers/application_controller.rb +6 -0
- data/example/rails-tutorial/app/controllers/concerns/.keep +0 -0
- data/example/rails-tutorial/app/controllers/home_controller.rb +6 -0
- data/example/rails-tutorial/app/helpers/application_helper.rb +2 -0
- data/example/rails-tutorial/app/mailers/.keep +0 -0
- data/example/rails-tutorial/app/models/.keep +0 -0
- data/example/rails-tutorial/app/models/concerns/.keep +0 -0
- data/example/rails-tutorial/app/views/components.rb +3 -0
- data/example/rails-tutorial/app/views/components/home/show.rb +47 -0
- data/example/rails-tutorial/app/views/layouts/application.html.erb +14 -0
- data/example/rails-tutorial/bin/bundle +3 -0
- data/example/rails-tutorial/bin/rails +8 -0
- data/example/rails-tutorial/bin/rake +8 -0
- data/example/rails-tutorial/bin/setup +29 -0
- data/example/rails-tutorial/bin/spring +15 -0
- data/example/rails-tutorial/config.ru +4 -0
- data/example/rails-tutorial/config/application.rb +26 -0
- data/example/rails-tutorial/config/boot.rb +3 -0
- data/example/rails-tutorial/config/database.yml +25 -0
- data/example/rails-tutorial/config/environment.rb +5 -0
- data/example/rails-tutorial/config/environments/development.rb +41 -0
- data/example/rails-tutorial/config/environments/production.rb +79 -0
- data/example/rails-tutorial/config/environments/test.rb +42 -0
- data/example/rails-tutorial/config/initializers/assets.rb +11 -0
- data/example/rails-tutorial/config/initializers/backtrace_silencers.rb +7 -0
- data/example/rails-tutorial/config/initializers/cookies_serializer.rb +3 -0
- data/example/rails-tutorial/config/initializers/filter_parameter_logging.rb +4 -0
- data/example/rails-tutorial/config/initializers/inflections.rb +16 -0
- data/example/rails-tutorial/config/initializers/mime_types.rb +4 -0
- data/example/rails-tutorial/config/initializers/session_store.rb +3 -0
- data/example/rails-tutorial/config/initializers/wrap_parameters.rb +14 -0
- data/example/rails-tutorial/config/locales/en.yml +23 -0
- data/example/rails-tutorial/config/routes.rb +59 -0
- data/example/rails-tutorial/config/secrets.yml +22 -0
- data/example/rails-tutorial/db/seeds.rb +7 -0
- data/example/rails-tutorial/lib/assets/.keep +0 -0
- data/example/rails-tutorial/lib/tasks/.keep +0 -0
- data/example/rails-tutorial/log/.keep +0 -0
- data/example/rails-tutorial/public/404.html +67 -0
- data/example/rails-tutorial/public/422.html +67 -0
- data/example/rails-tutorial/public/500.html +66 -0
- data/example/rails-tutorial/public/favicon.ico +0 -0
- data/example/rails-tutorial/public/robots.txt +5 -0
- data/example/rails-tutorial/test/controllers/.keep +0 -0
- data/example/rails-tutorial/test/fixtures/.keep +0 -0
- data/example/rails-tutorial/test/helpers/.keep +0 -0
- data/example/rails-tutorial/test/integration/.keep +0 -0
- data/example/rails-tutorial/test/mailers/.keep +0 -0
- data/example/rails-tutorial/test/models/.keep +0 -0
- data/example/rails-tutorial/test/test_helper.rb +10 -0
- data/example/rails-tutorial/vendor/assets/javascripts/.keep +0 -0
- data/example/rails-tutorial/vendor/assets/stylesheets/.keep +0 -0
- data/example/sinatra-tutorial/.DS_Store +0 -0
- data/example/sinatra-tutorial/Gemfile +5 -0
- data/example/sinatra-tutorial/README.md +8 -0
- data/example/sinatra-tutorial/_comments.json +42 -0
- data/example/sinatra-tutorial/app/example.rb +290 -0
- data/example/sinatra-tutorial/app/jquery.js +5 -0
- data/example/sinatra-tutorial/config.ru +58 -0
- data/example/sinatra-tutorial/public/base.css +62 -0
- data/example/todos/Gemfile +11 -0
- data/example/todos/README.md +37 -0
- data/example/todos/Rakefile +8 -0
- data/example/todos/app/application.rb +22 -0
- data/example/todos/app/components/app.react.rb +61 -0
- data/example/todos/app/components/footer.react.rb +31 -0
- data/example/todos/app/components/todo_item.react.rb +46 -0
- data/example/todos/app/components/todo_list.react.rb +25 -0
- data/example/todos/app/models/todo.rb +19 -0
- data/example/todos/config.ru +14 -0
- data/example/todos/index.html.haml +16 -0
- data/example/todos/spec/todo_spec.rb +28 -0
- data/example/todos/vendor/base.css +410 -0
- data/example/todos/vendor/bg.png +0 -0
- data/example/todos/vendor/jquery.js +4 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +4 -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/test_app_generator.rb +105 -0
- data/lib/rails-helpers/top_level_rails_component.rb +54 -0
- data/lib/react/api.rb +127 -0
- data/lib/react/callbacks.rb +42 -0
- data/lib/react/component.rb +269 -0
- data/lib/react/component/api.rb +50 -0
- data/lib/react/component/base.rb +9 -0
- data/lib/react/component/class_methods.rb +190 -0
- data/lib/react/component/props_wrapper.rb +82 -0
- data/lib/react/element.rb +77 -0
- data/lib/react/event.rb +76 -0
- data/lib/react/ext/hash.rb +9 -0
- data/lib/react/ext/string.rb +8 -0
- data/lib/react/native_library.rb +53 -0
- data/lib/react/observable.rb +29 -0
- data/lib/react/rendering_context.rb +109 -0
- data/lib/react/state.rb +140 -0
- data/lib/react/top_level.rb +97 -0
- data/lib/react/validator.rb +136 -0
- data/lib/reactive-ruby/component_loader.rb +45 -0
- data/lib/reactive-ruby/isomorphic_helpers.rb +196 -0
- data/lib/reactive-ruby/rails.rb +7 -0
- data/lib/reactive-ruby/rails/component_mount.rb +44 -0
- data/lib/reactive-ruby/rails/controller_helper.rb +13 -0
- data/lib/reactive-ruby/rails/railtie.rb +14 -0
- data/lib/reactive-ruby/serializers.rb +15 -0
- data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +42 -0
- data/lib/reactive-ruby/version.rb +3 -0
- data/lib/reactrb.rb +50 -0
- data/lib/sources/react-latest.js +21167 -0
- data/lib/sources/react-v13.js +21642 -0
- data/lib/sources/react-v14.js +20818 -0
- data/lib/sources/react-v15.js +21167 -0
- data/logo1.png +0 -0
- data/logo2.png +0 -0
- data/logo3.png +0 -0
- data/path_release_steps.md +9 -0
- data/reactrb.gemspec +43 -0
- data/spec/controller_helper_spec.rb +22 -0
- data/spec/index.html.erb +12 -0
- data/spec/react/callbacks_spec.rb +106 -0
- data/spec/react/component/base_spec.rb +36 -0
- data/spec/react/component_spec.rb +721 -0
- data/spec/react/dsl_spec.rb +161 -0
- data/spec/react/element_spec.rb +47 -0
- data/spec/react/event_spec.rb +24 -0
- data/spec/react/native_library_spec.rb +10 -0
- data/spec/react/observable_spec.rb +7 -0
- data/spec/react/param_declaration_spec.rb +286 -0
- data/spec/react/react_spec.rb +211 -0
- data/spec/react/state_spec.rb +26 -0
- data/spec/react/top_level_component_spec.rb +68 -0
- data/spec/react/tutorial/tutorial_spec.rb +35 -0
- data/spec/react/validator_spec.rb +128 -0
- data/spec/reactive-ruby/component_loader_spec.rb +68 -0
- data/spec/reactive-ruby/isomorphic_helpers_spec.rb +155 -0
- data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +9 -0
- data/spec/reactive-ruby/rails/component_mount_spec.rb +66 -0
- data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +35 -0
- data/spec/spec_helper.rb +109 -0
- data/spec/support/react/spec_helpers.rb +57 -0
- data/spec/vendor/es5-shim.min.js +6 -0
- data/spec/vendor/jquery-2.2.4.min.js +4 -0
- metadata +441 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
module React
|
|
2
|
+
|
|
3
|
+
class Validator
|
|
4
|
+
attr_accessor :errors
|
|
5
|
+
private :errors
|
|
6
|
+
|
|
7
|
+
def initialize(component_class)
|
|
8
|
+
@component_class = component_class
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.build(&block)
|
|
12
|
+
self.new.build(&block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def build(&block)
|
|
16
|
+
instance_eval(&block)
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def requires(name, options = {})
|
|
21
|
+
options[:required] = true
|
|
22
|
+
define_rule(name, options)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def optional(name, options = {})
|
|
26
|
+
options[:required] = false
|
|
27
|
+
define_rule(name, options)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def allow_undefined_props=(allow)
|
|
31
|
+
@allow_undefined_props = allow
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def undefined_props(props)
|
|
35
|
+
self.allow_undefined_props = true
|
|
36
|
+
props.reject { |name, value| rules[name] }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def validate(props)
|
|
40
|
+
self.errors = []
|
|
41
|
+
validate_undefined(props) unless allow_undefined_props?
|
|
42
|
+
props = coerce_native_hash_values(defined_props(props))
|
|
43
|
+
validate_required(props)
|
|
44
|
+
props.each do |name, value|
|
|
45
|
+
validate_types(name, value)
|
|
46
|
+
validate_allowed(name, value)
|
|
47
|
+
end
|
|
48
|
+
errors
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def default_props
|
|
52
|
+
rules
|
|
53
|
+
.select {|key, value| value.keys.include?("default") }
|
|
54
|
+
.inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def defined_props(props)
|
|
60
|
+
props.select { |name| rules.keys.include?(name) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def allow_undefined_props?
|
|
64
|
+
!!@allow_undefined_props
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def rules
|
|
68
|
+
@rules ||= { children: { required: false } }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def define_rule(name, options = {})
|
|
72
|
+
rules[name] = coerce_native_hash_values(options)
|
|
73
|
+
@component_class.define_param(name, options[:type]) unless name == :params
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def errors
|
|
77
|
+
@errors ||= []
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def validate_types(prop_name, value)
|
|
81
|
+
return unless klass = rules[prop_name][:type]
|
|
82
|
+
if !klass.is_a?(Array)
|
|
83
|
+
allow_nil = !!rules[prop_name][:allow_nil]
|
|
84
|
+
type_check("`#{prop_name}`", value, klass, allow_nil)
|
|
85
|
+
elsif klass.length > 0
|
|
86
|
+
validate_value_array(prop_name, value)
|
|
87
|
+
else
|
|
88
|
+
allow_nil = !!rules[prop_name][:allow_nil]
|
|
89
|
+
type_check("`#{prop_name}`", value, Array, allow_nil)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def type_check(prop_name, value, klass, allow_nil)
|
|
94
|
+
return if allow_nil && value.nil?
|
|
95
|
+
return if value.is_a?(klass)
|
|
96
|
+
return if klass.respond_to?(:_react_param_conversion) &&
|
|
97
|
+
klass._react_param_conversion(value, :validate_only)
|
|
98
|
+
errors << "Provided prop #{prop_name} could not be converted to #{klass}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def validate_allowed(prop_name, value)
|
|
102
|
+
return unless values = rules[prop_name][:values]
|
|
103
|
+
return if values.include?(value)
|
|
104
|
+
errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def validate_required(props)
|
|
108
|
+
(rules.keys - props.keys).each do |name|
|
|
109
|
+
next unless rules[name][:required]
|
|
110
|
+
errors << "Required prop `#{name}` was not specified"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def validate_undefined(props)
|
|
115
|
+
(props.keys - rules.keys).each do |prop_name|
|
|
116
|
+
errors << "Provided prop `#{prop_name}` not specified in spec"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def validate_value_array(name, value)
|
|
121
|
+
klass = rules[name][:type]
|
|
122
|
+
allow_nil = !!rules[name][:allow_nil]
|
|
123
|
+
value.each_with_index do |item, index|
|
|
124
|
+
type_check("`#{name}`[#{index}]", Native(item), klass[0], allow_nil)
|
|
125
|
+
end
|
|
126
|
+
rescue NoMethodError
|
|
127
|
+
errors << "Provided prop `#{name}` was not an Array"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def coerce_native_hash_values(hash)
|
|
131
|
+
hash.each do |key, value|
|
|
132
|
+
hash[key] = Native(value)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
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 react.rb components found in #{components}.rb" unless loaded?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def loaded?
|
|
26
|
+
!!v8_context.eval('Opal.React')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def components
|
|
32
|
+
# Make this configurable at some point
|
|
33
|
+
'components'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def opal(file)
|
|
37
|
+
Opal::Processor.load_asset_code(assets, file)
|
|
38
|
+
rescue # What exception is being caught here?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def assets
|
|
42
|
+
::Rails.application.assets
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
module React
|
|
2
|
+
module IsomorphicHelpers
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.extend(ClassMethods)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
if RUBY_ENGINE != 'opal'
|
|
8
|
+
def self.load_context(ctx, controller, name = nil)
|
|
9
|
+
puts "************************** React Server Context Initialized #{name} *********************************************"
|
|
10
|
+
@context = Context.new("#{controller.object_id}-#{Time.now.to_i}", ctx, controller, name)
|
|
11
|
+
end
|
|
12
|
+
else
|
|
13
|
+
def self.load_context(unique_id = nil, name = nil)
|
|
14
|
+
# can be called on the client to force re-initialization for testing purposes
|
|
15
|
+
if !unique_id || !@context || @context.unique_id != unique_id
|
|
16
|
+
if on_opal_server?
|
|
17
|
+
message = "************************ React Prerendering Context Initialized #{name} ***********************"
|
|
18
|
+
else
|
|
19
|
+
message = "************************ React Browser Context Initialized ****************************"
|
|
20
|
+
end
|
|
21
|
+
log(message)
|
|
22
|
+
@context = Context.new(unique_id)
|
|
23
|
+
end
|
|
24
|
+
@context
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.log(message, message_type = :info)
|
|
29
|
+
message = [message] unless message.is_a? Array
|
|
30
|
+
if message_type == :info
|
|
31
|
+
if on_opal_server?
|
|
32
|
+
style = 'background: #00FFFF; color: red'
|
|
33
|
+
else
|
|
34
|
+
style = 'background: #222; color: #bada55'
|
|
35
|
+
end
|
|
36
|
+
message = ["%c" + message[0], style]+message[1..-1]
|
|
37
|
+
`console.log.apply(console, message)`
|
|
38
|
+
elsif message_type == :warning
|
|
39
|
+
`console.warn.apply(console, message)`
|
|
40
|
+
else
|
|
41
|
+
`console.error.apply(console, message)`
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if RUBY_ENGINE != 'opal'
|
|
46
|
+
def self.on_opal_server?
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.on_opal_client?
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
def self.on_opal_server?
|
|
55
|
+
`typeof window.document === 'undefined'`
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.on_opal_client?
|
|
59
|
+
!on_opal_server?
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def log(*args)
|
|
64
|
+
IsomorphicHelpers.log(*args)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def on_opal_server?
|
|
68
|
+
self.class.on_opal_server?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def on_opal_client?
|
|
72
|
+
self.class.on_opal_client?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.prerender_footers
|
|
76
|
+
footer = Context.prerender_footer_blocks.collect { |block| block.call }.join("\n")
|
|
77
|
+
if RUBY_ENGINE != 'opal'
|
|
78
|
+
footer = (footer + "#{@context.send_to_opal(:prerender_footers)}") if @context
|
|
79
|
+
footer = footer.html_safe
|
|
80
|
+
end
|
|
81
|
+
footer
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class Context
|
|
85
|
+
attr_reader :controller
|
|
86
|
+
attr_reader :unique_id
|
|
87
|
+
|
|
88
|
+
def self.before_first_mount_blocks
|
|
89
|
+
@before_first_mount_blocks ||= []
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.prerender_footer_blocks
|
|
93
|
+
@prerender_footer_blocks ||= []
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def initialize(unique_id, ctx = nil, controller = nil, name = nil)
|
|
97
|
+
@unique_id = unique_id
|
|
98
|
+
if RUBY_ENGINE != 'opal'
|
|
99
|
+
@controller = controller
|
|
100
|
+
@ctx = ctx
|
|
101
|
+
ctx["ServerSideIsomorphicMethods"] = self
|
|
102
|
+
send_to_opal(:load_context, @unique_id, name)
|
|
103
|
+
end
|
|
104
|
+
self.class.before_first_mount_blocks.each { |block| block.call(self) }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def eval(js)
|
|
108
|
+
@ctx.eval(js) if @ctx
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def send_to_opal(method, *args)
|
|
112
|
+
return unless @ctx
|
|
113
|
+
args = [1] if args.length == 0
|
|
114
|
+
::ReactiveRuby::ComponentLoader.new(@ctx).load!
|
|
115
|
+
@ctx.eval("Opal.React.IsomorphicHelpers.$#{method}(#{args.collect { |arg| "'#{arg}'"}.join(', ')})")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.register_before_first_mount_block(&block)
|
|
119
|
+
before_first_mount_blocks << block
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def self.register_prerender_footer_block(&block)
|
|
123
|
+
prerender_footer_blocks << block
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
class IsomorphicProcCall
|
|
128
|
+
def result
|
|
129
|
+
@result.first if @result
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def initialize(name, block, *args)
|
|
133
|
+
@name = name
|
|
134
|
+
block.call(self, *args)
|
|
135
|
+
@result ||= send_to_server(*args) if IsomorphicHelpers.on_opal_server?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def when_on_client(&block)
|
|
139
|
+
@result = [block.call] if IsomorphicHelpers.on_opal_client?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def send_to_server(*args)
|
|
143
|
+
if IsomorphicHelpers.on_opal_server?
|
|
144
|
+
args_as_json = args.to_json
|
|
145
|
+
@result = [JSON.parse(`window.ServerSideIsomorphicMethods[#{@name}](#{args_as_json})`)]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def when_on_server(&block)
|
|
150
|
+
@result = [block.call.to_json] unless IsomorphicHelpers.on_opal_client? || IsomorphicHelpers.on_opal_server?
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
module ClassMethods
|
|
155
|
+
def on_opal_server?
|
|
156
|
+
IsomorphicHelpers.on_opal_server?
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def on_opal_client?
|
|
160
|
+
IsomorphicHelpers.on_opal_client?
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def log(*args)
|
|
164
|
+
IsomorphicHelpers.log(*args)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def controller
|
|
168
|
+
IsomorphicHelpers.context.controller
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def before_first_mount(&block)
|
|
172
|
+
React::IsomorphicHelpers::Context.register_before_first_mount_block &block
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def prerender_footer(&block)
|
|
176
|
+
React::IsomorphicHelpers::Context.register_prerender_footer_block &block
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
if RUBY_ENGINE != 'opal'
|
|
180
|
+
def isomorphic_method(name, &block)
|
|
181
|
+
React::IsomorphicHelpers::Context.send(:define_method, name) do |args_as_json|
|
|
182
|
+
React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, *JSON.parse(args_as_json)).result
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
else
|
|
186
|
+
require 'json'
|
|
187
|
+
|
|
188
|
+
def isomorphic_method(name, &block)
|
|
189
|
+
self.class.send(:define_method, name) do | *args |
|
|
190
|
+
React::IsomorphicHelpers::IsomorphicProcCall.new(name, block, *args).result
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
require 'action_view'
|
|
2
|
+
require 'react-rails'
|
|
3
|
+
require 'reactive-ruby/server_rendering/contextual_renderer'
|
|
4
|
+
require 'reactive-ruby/rails/component_mount'
|
|
5
|
+
require 'reactive-ruby/rails/railtie'
|
|
6
|
+
require 'reactive-ruby/rails/controller_helper'
|
|
7
|
+
require 'reactive-ruby/component_loader'
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
options = context_initializer_options(options, name) if options[:prerender]
|
|
12
|
+
props = serialized_props(props, name, controller)
|
|
13
|
+
super(top_level_name, props, options, &block) + footers
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def context_initializer_options(options, name)
|
|
19
|
+
options[:prerender] = {options[:prerender] => true} unless options[:prerender].is_a? Hash
|
|
20
|
+
existing_context_initializer = options[:prerender][:context_initializer]
|
|
21
|
+
|
|
22
|
+
options[:prerender][:context_initializer] = lambda do |ctx|
|
|
23
|
+
React::IsomorphicHelpers.load_context(ctx, controller, name)
|
|
24
|
+
existing_context_initializer.call ctx if existing_context_initializer
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
options
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def serialized_props(props, name, controller)
|
|
31
|
+
{ render_params: props, component_name: name,
|
|
32
|
+
controller: controller.class.name.gsub(/Controller$/,"") }.react_serializer
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def top_level_name
|
|
36
|
+
'React.TopLevelRailsComponent'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def footers
|
|
40
|
+
React::IsomorphicHelpers.prerender_footers #if options[:prerender]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'action_controller'
|
|
2
|
+
|
|
3
|
+
module ReactiveRuby
|
|
4
|
+
module Rails
|
|
5
|
+
class ActionController::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[0].is_a? Hash) ? args[0] : {}
|
|
9
|
+
render inline: "<%= react_component @component_name, @render_params, { prerender: !params[:no_prerender] } %>", layout: 'application'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|