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,53 @@
|
|
|
1
|
+
module React
|
|
2
|
+
class NativeLibrary
|
|
3
|
+
def self.renames_and_exclusions
|
|
4
|
+
@renames_and_exclusions ||= {}
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.libraries
|
|
8
|
+
@libraries ||= []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.const_missing(name)
|
|
12
|
+
if renames_and_exclusions.has_key? name
|
|
13
|
+
if native_name = renames_and_exclusions[name]
|
|
14
|
+
native_name
|
|
15
|
+
else
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
libraries.each do |library|
|
|
20
|
+
native_name = "#{library}.#{name}"
|
|
21
|
+
native_component = `eval(#{native_name})` rescue nil
|
|
22
|
+
React::API.import_native_component(name, native_component) and return name if native_component and `native_component != undefined`
|
|
23
|
+
end
|
|
24
|
+
name
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.method_missing(n, *args, &block)
|
|
29
|
+
name = n
|
|
30
|
+
if name =~ /_as_node$/
|
|
31
|
+
node_only = true
|
|
32
|
+
name = name.gsub(/_as_node$/, "")
|
|
33
|
+
end
|
|
34
|
+
unless name = const_get(name)
|
|
35
|
+
return super
|
|
36
|
+
end
|
|
37
|
+
React::RenderingContext.build_or_render(node_only, name, *args, &block)
|
|
38
|
+
rescue
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.imports(library)
|
|
42
|
+
libraries << library
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.rename(rename_list={})
|
|
46
|
+
renames_and_exclusions.merge!(rename_list.invert)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.exclude(*exclude_list)
|
|
50
|
+
renames_and_exclusions.merge(Hash[exclude_list.map {|k| [k, nil]}])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module React
|
|
2
|
+
class Observable
|
|
3
|
+
def initialize(value, on_change = nil, &block)
|
|
4
|
+
@value = value
|
|
5
|
+
@on_change = on_change || block
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def method_missing(method_sym, *args, &block)
|
|
9
|
+
@value.send(method_sym, *args, &block).tap { |result| @on_change.call @value }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def respond_to?(method, *args)
|
|
13
|
+
if [:call, :to_proc].include? method
|
|
14
|
+
true
|
|
15
|
+
else
|
|
16
|
+
@value.respond_to? method, *args
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call(new_value)
|
|
21
|
+
@on_change.call new_value
|
|
22
|
+
@value = new_value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_proc
|
|
26
|
+
lambda { |arg = @value| @on_change.call arg }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module React
|
|
2
|
+
class RenderingContext
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :waiting_on_resources
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.build_or_render(node_only, name, *args, &block)
|
|
8
|
+
if node_only
|
|
9
|
+
React::RenderingContext.build { React::RenderingContext.render(name, *args, &block) }.to_n
|
|
10
|
+
else
|
|
11
|
+
React::RenderingContext.render(name, *args, &block)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.render(name, *args, &block)
|
|
16
|
+
remove_nodes_from_args(args)
|
|
17
|
+
@buffer = [] unless @buffer
|
|
18
|
+
if block
|
|
19
|
+
element = build do
|
|
20
|
+
saved_waiting_on_resources = waiting_on_resources
|
|
21
|
+
self.waiting_on_resources = nil
|
|
22
|
+
result = block.call
|
|
23
|
+
# Todo figure out how children rendering should happen, probably should have special method that pushes children into the buffer
|
|
24
|
+
# i.e. render_child/render_children that takes Element/Array[Element] and does the push into the buffer
|
|
25
|
+
if !name && ( # !name means called from outer render so we check that it has rendered correctly
|
|
26
|
+
(@buffer.count > 1) || # should only render one element
|
|
27
|
+
(@buffer.count == 1 && @buffer.last != result) || # it should return that element
|
|
28
|
+
(@buffer.count == 0 && !(result.is_a?(String) || (result.respond_to?(:acts_as_string?) && result.acts_as_string?) || result.is_a?(Element))) #for convience we will also convert the return value to a span if its a string
|
|
29
|
+
)
|
|
30
|
+
raise "a components render method must generate and return exactly 1 element or a string"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@buffer << result.to_s if result.is_a? String || (result.respond_to?(:acts_as_string?) && result.acts_as_string?) # For convience we push the last return value on if its a string
|
|
34
|
+
@buffer << result if result.is_a?(Element) && @buffer.count == 0
|
|
35
|
+
if name
|
|
36
|
+
buffer = @buffer.dup
|
|
37
|
+
React.create_element(name, *args) { buffer }.tap do |element|
|
|
38
|
+
element.waiting_on_resources = saved_waiting_on_resources || !!buffer.detect { |e| e.waiting_on_resources if e.respond_to?(:waiting_on_resources) }
|
|
39
|
+
end
|
|
40
|
+
elsif @buffer.last.is_a? React::Element
|
|
41
|
+
@buffer.last.tap { |element| element.waiting_on_resources ||= saved_waiting_on_resources }
|
|
42
|
+
else
|
|
43
|
+
@buffer.last.to_s.span.tap { |element| element.waiting_on_resources = saved_waiting_on_resources }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
elsif name.is_a? React::Element
|
|
47
|
+
element = name
|
|
48
|
+
# I BELIEVE WAITING ON RESOURCES SHOULD ALREADY BE SET
|
|
49
|
+
else
|
|
50
|
+
element = React.create_element(name, *args)
|
|
51
|
+
element.waiting_on_resources = waiting_on_resources
|
|
52
|
+
end
|
|
53
|
+
@buffer << element
|
|
54
|
+
self.waiting_on_resources = nil
|
|
55
|
+
element
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.build(&block)
|
|
59
|
+
current = @buffer
|
|
60
|
+
@buffer = []
|
|
61
|
+
return_val = yield @buffer
|
|
62
|
+
@buffer = current
|
|
63
|
+
return_val
|
|
64
|
+
#ensure
|
|
65
|
+
# @buffer = current
|
|
66
|
+
# return_val
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.as_node(element)
|
|
70
|
+
@buffer.delete(element)
|
|
71
|
+
element
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class << self; alias_method :delete, :as_node; end
|
|
75
|
+
|
|
76
|
+
def self.replace(e1, e2)
|
|
77
|
+
@buffer[@buffer.index(e1)] = e2
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.remove_nodes_from_args(args)
|
|
81
|
+
args[0].each do |key, value|
|
|
82
|
+
value.as_node if value.is_a?(Element) rescue nil
|
|
83
|
+
end if args[0] && args[0].is_a?(Hash)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class ::Object
|
|
88
|
+
alias_method :old_method_missing, :method_missing
|
|
89
|
+
|
|
90
|
+
["span", "para", "td", "th", "while_loading"].each do |tag|
|
|
91
|
+
define_method(tag) do | *args, &block |
|
|
92
|
+
args.unshift(tag)
|
|
93
|
+
return self.method_missing(*args, &block) if self.is_a? React::Component
|
|
94
|
+
React::RenderingContext.render(*args) { self.to_s }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def para(*args, &block)
|
|
99
|
+
args.unshift("p")
|
|
100
|
+
return self.method_missing(*args, &block) if self.is_a? React::Component
|
|
101
|
+
React::RenderingContext.render(*args) { self.to_s }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def br
|
|
105
|
+
return self.method_missing(*["br"]) if self.is_a? React::Component
|
|
106
|
+
React::RenderingContext.render("span") { React::RenderingContext.render(self.to_s); React::RenderingContext.render("br") }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/react/state.rb
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module React
|
|
2
|
+
class StateWrapper < BasicObject
|
|
3
|
+
def initialize(native, from)
|
|
4
|
+
@state_hash = Hash.new(`#{native}.state`)
|
|
5
|
+
@from = from
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def [](state)
|
|
9
|
+
@state_hash[state]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def []=(state, new_value)
|
|
13
|
+
@state_hash[state] = new_value
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def method_missing(method, *args)
|
|
17
|
+
if match = method.match(/^(.+)\!$/)
|
|
18
|
+
if args.count > 0
|
|
19
|
+
current_value = State.get_state(@from, match[1])
|
|
20
|
+
State.set_state(@from, $1, args[0])
|
|
21
|
+
current_value
|
|
22
|
+
else
|
|
23
|
+
current_state = State.get_state(@from, match[1])
|
|
24
|
+
State.set_state(@from, $1, current_state)
|
|
25
|
+
Observable.new(current_state) do |update|
|
|
26
|
+
State.set_state(@from, $1, update)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
State.get_state(@from, method)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class State
|
|
36
|
+
class << self
|
|
37
|
+
attr_reader :current_observer
|
|
38
|
+
|
|
39
|
+
def initialize_states(object, initial_values) # initialize objects' name/value pairs
|
|
40
|
+
states[object].merge!(initial_values || {})
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get_state(object, name, current_observer = @current_observer)
|
|
44
|
+
# get current value of name for object, remember that the current object depends on this state, current observer can be overriden with last param
|
|
45
|
+
new_observers[current_observer][object] << name if current_observer && !new_observers[current_observer][object].include?(name)
|
|
46
|
+
states[object][name]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def set_state2(object, name, value) # set object's name state to value, tell all observers it has changed. Observers must implement update_react_js_state
|
|
50
|
+
object_needs_notification = object.respond_to? :update_react_js_state
|
|
51
|
+
observers_by_name[object][name].dup.each do |observer|
|
|
52
|
+
observer.update_react_js_state(object, name, value)
|
|
53
|
+
object_needs_notification = false if object == observer
|
|
54
|
+
end
|
|
55
|
+
object.update_react_js_state(nil, name, value) if object_needs_notification
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def set_state(object, name, value, delay=nil)
|
|
59
|
+
states[object][name] = value
|
|
60
|
+
if delay
|
|
61
|
+
@delayed_updates ||= []
|
|
62
|
+
@delayed_updates << [object, name, value]
|
|
63
|
+
@delayed_updater ||= after(0.001) do
|
|
64
|
+
delayed_updates = @delayed_updates
|
|
65
|
+
@delayed_updates = []
|
|
66
|
+
@delayed_updater = nil
|
|
67
|
+
delayed_updates.each do |object, name, value|
|
|
68
|
+
set_state2(object, name, value)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
set_state2(object, name, value)
|
|
73
|
+
end
|
|
74
|
+
value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def will_be_observing?(object, name, current_observer)
|
|
78
|
+
current_observer && new_observers[current_observer][object].include?(name)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def is_observing?(object, name, current_observer)
|
|
82
|
+
current_observer && observers_by_name[object][name].include?(current_observer)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def update_states_to_observe(current_observer = @current_observer) # should be called after the last after_render callback, currently called after components render method
|
|
86
|
+
raise "update_states_to_observer called outside of watch block" unless current_observer
|
|
87
|
+
current_observers[current_observer].each do |object, names|
|
|
88
|
+
names.each do |name|
|
|
89
|
+
observers_by_name[object][name].delete(current_observer)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
observers = current_observers[current_observer] = new_observers[current_observer]
|
|
93
|
+
new_observers.delete(current_observer)
|
|
94
|
+
observers.each do |object, names|
|
|
95
|
+
names.each do |name|
|
|
96
|
+
observers_by_name[object][name] << current_observer
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def remove # call after component is unmounted
|
|
102
|
+
raise "remove called outside of watch block" unless @current_observer
|
|
103
|
+
current_observers[@current_observer].each do |object, names|
|
|
104
|
+
names.each do |name|
|
|
105
|
+
observers_by_name[object][name].delete(@current_observer)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
current_observers.delete(@current_observer)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def set_state_context_to(observer) # wrap all execution that may set or get states in a block so we know which observer is executing
|
|
112
|
+
if `typeof window.reactive_ruby_timing !== 'undefined'`
|
|
113
|
+
@nesting_level = (@nesting_level || 0) + 1
|
|
114
|
+
start_time = Time.now.to_f
|
|
115
|
+
observer_name = (observer.class.respond_to?(:name) ? observer.class.name : observer.to_s) rescue "object:#{observer.object_id}"
|
|
116
|
+
end
|
|
117
|
+
saved_current_observer = @current_observer
|
|
118
|
+
@current_observer = observer
|
|
119
|
+
return_value = yield
|
|
120
|
+
return_value
|
|
121
|
+
ensure
|
|
122
|
+
@current_observer = saved_current_observer
|
|
123
|
+
@nesting_level = [0, @nesting_level - 1].max if `typeof window.reactive_ruby_timing !== 'undefined'`
|
|
124
|
+
return_value
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def states
|
|
128
|
+
@states ||= Hash.new { |h, k| h[k] = {} }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
[:new_observers, :current_observers, :observers_by_name].each do |method_name|
|
|
132
|
+
define_method(method_name) do
|
|
133
|
+
instance_variable_get("@#{method_name}") or
|
|
134
|
+
instance_variable_set("@#{method_name}", Hash.new { |h, k| h[k] = Hash.new { |h, k| h[k] = [] } })
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require "native"
|
|
2
|
+
require 'active_support'
|
|
3
|
+
require 'react/component/base'
|
|
4
|
+
|
|
5
|
+
module React
|
|
6
|
+
HTML_TAGS = %w(a abbr address area article aside audio b base bdi bdo big blockquote body br
|
|
7
|
+
button canvas caption cite code col colgroup data datalist dd del details dfn
|
|
8
|
+
dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
|
|
9
|
+
h6 head header hr html i iframe img input ins kbd keygen label legend li link
|
|
10
|
+
main map mark menu menuitem meta meter nav noscript object ol optgroup option
|
|
11
|
+
output p param picture pre progress q rp rt ruby s samp script section select
|
|
12
|
+
small source span strong style sub summary sup table tbody td textarea tfoot th
|
|
13
|
+
thead time title tr track u ul var video wbr)
|
|
14
|
+
ATTRIBUTES = %w(accept acceptCharset accessKey action allowFullScreen allowTransparency alt
|
|
15
|
+
async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
|
|
16
|
+
className cols colSpan content contentEditable contextMenu controls coords
|
|
17
|
+
crossOrigin data dateTime defer dir disabled download draggable encType form
|
|
18
|
+
formAction formEncType formMethod formNoValidate formTarget frameBorder height
|
|
19
|
+
hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
|
|
20
|
+
marginHeight marginWidth max maxLength media mediaGroup method min multiple
|
|
21
|
+
muted name noValidate open pattern placeholder poster preload radioGroup
|
|
22
|
+
readOnly rel required role rows rowSpan sandbox scope scrolling seamless
|
|
23
|
+
selected shape size sizes span spellCheck src srcDoc srcSet start step style
|
|
24
|
+
tabIndex target title type useMap value width wmode dangerouslySetInnerHTML)
|
|
25
|
+
|
|
26
|
+
def self.create_element(type, properties = {}, &block)
|
|
27
|
+
React::API.create_element(type, properties, &block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.render(element, container)
|
|
31
|
+
container = `container.$$class ? container[0] : container`
|
|
32
|
+
if !(`typeof ReactDOM === 'undefined'`)
|
|
33
|
+
component = Native(`ReactDOM.render(#{element.to_n}, container, function(){#{yield if block_given?}})`) # v0.15+
|
|
34
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
35
|
+
component = Native(`React.render(#{element.to_n}, container, function(){#{yield if block_given?}})`)
|
|
36
|
+
else
|
|
37
|
+
raise "render is not defined. In React >= v15 you must import it with ReactDOM"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
component.class.include(React::Component::API)
|
|
41
|
+
component
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.is_valid_element(element)
|
|
45
|
+
element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.render_to_string(element)
|
|
49
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
50
|
+
React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
|
|
51
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
52
|
+
React::RenderingContext.build { `React.renderToString(#{element.to_n})` }
|
|
53
|
+
else
|
|
54
|
+
raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.render_to_static_markup(element)
|
|
59
|
+
if !(`typeof ReactDOMServer === 'undefined'`)
|
|
60
|
+
React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
|
|
61
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
62
|
+
React::RenderingContext.build { `React.renderToStaticMarkup(#{element.to_n})` }
|
|
63
|
+
else
|
|
64
|
+
raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.unmount_component_at_node(node)
|
|
69
|
+
if !(`typeof ReactDOM === 'undefined'`)
|
|
70
|
+
`ReactDOM.unmountComponentAtNode(node.$$class ? node[0] : node)` # v0.15+
|
|
71
|
+
elsif !(`typeof React.renderToString === 'undefined'`)
|
|
72
|
+
`React.unmountComponentAtNode(node.$$class ? node[0] : node)`
|
|
73
|
+
else
|
|
74
|
+
raise "unmountComponentAtNode is not defined. In React >= v15 you must import it with ReactDOM"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
Element.instance_eval do
|
|
81
|
+
|
|
82
|
+
class ::Element::DummyContext < React::Component::Base
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.find(selector)
|
|
86
|
+
selector = selector.dom_node if selector.respond_to? :dom_node rescue selector
|
|
87
|
+
`$(#{selector})`
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.[](selector)
|
|
91
|
+
find(selector)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def render(&block)
|
|
95
|
+
React.render(React::RenderingContext.render(nil) {::Element::DummyContext.new.instance_eval &block}, self)
|
|
96
|
+
end
|
|
97
|
+
end if Object.const_defined?("Element")
|