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,41 @@
1
+ require 'hyperloop-config'
2
+ Hyperloop::Context
3
+ module React
4
+ module Callbacks
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def run_callback(name, *args)
10
+ self.class.callbacks_for(name).each do |callback|
11
+ if callback.is_a?(Proc)
12
+ instance_exec(*args, &callback)
13
+ else
14
+ send(callback, *args)
15
+ end
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+ def define_callback(callback_name)
21
+ wrapper_name = "_#{callback_name}_callbacks"
22
+ define_singleton_method(wrapper_name) do
23
+ Hyperloop::Context.set_var(self, "@#{wrapper_name}", force: true) { [] }
24
+ end
25
+ define_singleton_method(callback_name) do |*args, &block|
26
+ send(wrapper_name).concat(args)
27
+ send(wrapper_name).push(block) if block_given?
28
+ end
29
+ end
30
+
31
+ def callbacks_for(callback_name)
32
+ wrapper_name = "_#{callback_name}_callbacks"
33
+ if superclass.respond_to? :callbacks_for
34
+ superclass.callbacks_for(callback_name)
35
+ else
36
+ []
37
+ end + send(wrapper_name)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ module React
2
+ class Children
3
+ include Enumerable
4
+
5
+ def initialize(children)
6
+ @children = children
7
+ end
8
+
9
+ def each(&block)
10
+ return to_enum(__callee__) { length } unless block_given?
11
+ return [] unless length > 0
12
+ collection = []
13
+ %x{
14
+ React.Children.forEach(#{@children}, function(context){
15
+ #{
16
+ element = React::Element.new(`context`)
17
+ block.call(element)
18
+ collection << element
19
+ }
20
+ })
21
+ }
22
+ collection
23
+ end
24
+
25
+ def length
26
+ @length ||= `React.Children.count(#{@children})`
27
+ end
28
+ alias_method :size, :length
29
+ end
30
+ end
@@ -0,0 +1,177 @@
1
+ require 'react/ext/string'
2
+ require 'react/ext/hash'
3
+ require 'active_support/core_ext/class/attribute'
4
+ require 'react/callbacks'
5
+ require 'react/rendering_context'
6
+ require 'hyper-store'
7
+ require 'react/state_wrapper'
8
+ require 'react/component/api'
9
+ require 'react/component/class_methods'
10
+ require 'react/component/props_wrapper'
11
+ require 'native'
12
+
13
+ module Hyperloop
14
+ class Component
15
+ module Mixin
16
+ def self.included(base)
17
+ base.include(Hyperloop::Store::Mixin)
18
+ base.include(React::Component::API)
19
+ base.include(React::Callbacks)
20
+ base.include(React::Component::Tags)
21
+ base.include(React::Component::DslInstanceMethods)
22
+ base.include(React::Component::ShouldComponentUpdate)
23
+ base.class_eval do
24
+ class_attribute :initial_state
25
+ define_callback :before_mount
26
+ define_callback :after_mount
27
+ define_callback :before_receive_props
28
+ define_callback :before_update
29
+ define_callback :after_update
30
+ define_callback :before_unmount
31
+ define_callback :after_error
32
+ end
33
+ base.extend(React::Component::ClassMethods)
34
+ end
35
+
36
+ def self.deprecation_warning(message)
37
+ React::Component.deprecation_warning(name, message)
38
+ end
39
+
40
+ def deprecation_warning(message)
41
+ React::Component.deprecation_warning(self.class.name, message)
42
+ end
43
+
44
+ def initialize(native_element)
45
+ @native = native_element
46
+ init_store
47
+ end
48
+
49
+ def emit(event_name, *args)
50
+ if React::Event::BUILT_IN_EVENTS.include?(built_in_event_name = "on#{event_name.to_s.event_camelize}")
51
+ params[built_in_event_name].call(*args)
52
+ else
53
+ params["on_#{event_name}"].call(*args)
54
+ end
55
+ end
56
+
57
+ def component_will_mount
58
+ React::IsomorphicHelpers.load_context(true) if React::IsomorphicHelpers.on_opal_client?
59
+ React::State.set_state_context_to(self) { run_callback(:before_mount) }
60
+ end
61
+
62
+ def component_did_mount
63
+ React::State.set_state_context_to(self) do
64
+ run_callback(:after_mount)
65
+ React::State.update_states_to_observe
66
+ end
67
+ end
68
+
69
+ def component_will_receive_props(next_props)
70
+ # need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
71
+ # for now we are just using it to clear processed_params
72
+ React::State.set_state_context_to(self) { self.run_callback(:before_receive_props, next_props) }
73
+ end
74
+
75
+ def component_will_update(next_props, next_state)
76
+ React::State.set_state_context_to(self) { self.run_callback(:before_update, next_props, next_state) }
77
+ end
78
+
79
+ def component_did_update(prev_props, prev_state)
80
+ React::State.set_state_context_to(self) do
81
+ self.run_callback(:after_update, prev_props, prev_state)
82
+ React::State.update_states_to_observe
83
+ end
84
+ end
85
+
86
+ def component_will_unmount
87
+ React::State.set_state_context_to(self) do
88
+ self.run_callback(:before_unmount)
89
+ React::State.remove
90
+ end
91
+ end
92
+
93
+ def component_did_catch(error, info)
94
+ React::State.set_state_context_to(self) do
95
+ if self.class.callbacks_for(:after_error) == []
96
+ if `typeof error.$backtrace === "function"`
97
+ `console.error(error.$backtrace().$join("\n"))`
98
+ else
99
+ `console.error(error, info)`
100
+ end
101
+ else
102
+ self.run_callback(:after_error, error, info)
103
+ end
104
+ end
105
+ end
106
+
107
+ attr_reader :waiting_on_resources
108
+
109
+ def update_react_js_state(object, name, value)
110
+ if object
111
+ name = "#{object.class}.#{name}" unless object == self
112
+ # Date.now() has only millisecond precision, if several notifications of
113
+ # observer happen within a millisecond, updates may get lost.
114
+ # to mitigate this the Math.random() appends some random number
115
+ # this way notifactions will happen as expected by the rest of hyperloop
116
+ set_state(
117
+ '***_state_updated_at-***' => `Date.now() + Math.random()`,
118
+ name => value
119
+ )
120
+ else
121
+ set_state name => value
122
+ end
123
+ end
124
+
125
+ def set_state_synchronously?
126
+ @native.JS[:__opalInstanceSyncSetState]
127
+ end
128
+
129
+ def render
130
+ raise 'no render defined'
131
+ end unless method_defined?(:render)
132
+
133
+ def _render_wrapper
134
+ React::State.set_state_context_to(self, true) do
135
+ element = React::RenderingContext.render(nil) { render || '' }
136
+ @waiting_on_resources =
137
+ element.waiting_on_resources if element.respond_to? :waiting_on_resources
138
+ element
139
+ end
140
+ end
141
+
142
+ def watch(value, &on_change)
143
+ Observable.new(value, on_change)
144
+ end
145
+
146
+ def define_state(*args, &block)
147
+ React::State.initialize_states(self, self.class.define_state(*args, &block))
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ module React
154
+ module Component
155
+ def self.included(base)
156
+ # note this is turned off during old style testing: See the spec_helper
157
+ deprecation_warning base, "The module name React::Component has been deprecated. Use Hyperloop::Component::Mixin instead."
158
+ base.include Hyperloop::Component::Mixin
159
+ end
160
+ def self.deprecation_warning(name, message)
161
+ @deprecation_messages ||= []
162
+ message = "Warning: Deprecated feature used in #{name}. #{message}"
163
+ unless @deprecation_messages.include? message
164
+ @deprecation_messages << message
165
+ React::IsomorphicHelpers.log message, :warning
166
+ end
167
+ end
168
+ end
169
+ module ComponentNoNotice
170
+ def self.included(base)
171
+ base.include Hyperloop::Component::Mixin
172
+ end
173
+ end
174
+ end
175
+
176
+ module React
177
+ end
@@ -0,0 +1,69 @@
1
+ module React
2
+ module Component
3
+ module API
4
+ def dom_node
5
+ `ReactDOM.findDOMNode(#{self}.native)` # react >= v0.15.0
6
+ end
7
+
8
+ def mounted?
9
+ `(#{self}.is_mounted === undefined) ? false : #{self}.is_mounted`
10
+ end
11
+
12
+ def force_update!
13
+ `#{self}.native.forceUpdate()`
14
+ end
15
+
16
+ def set_props(prop, &block)
17
+ raise "set_props: setProps() is no longer supported by react"
18
+ end
19
+ alias :set_props! :set_props
20
+
21
+ def set_state(state, &block)
22
+ set_or_replace_state_or_prop(state, 'setState', &block)
23
+ end
24
+
25
+ def set_state!(state, &block)
26
+ set_or_replace_state_or_prop(state, 'setState', &block)
27
+ `#{self}.native.forceUpdate()`
28
+ end
29
+
30
+ private
31
+
32
+ def set_or_replace_state_or_prop(state_or_prop, method, &block)
33
+ raise "No native ReactComponent associated" unless @native
34
+ `var state_prop_n = #{state_or_prop.shallow_to_n}`
35
+ # the state object is initalized when the ruby component is instanciated
36
+ # this is detected by self.native.__opalInstanceInitializedState
37
+ # which is set in the netive component constructor in react/api.rb
38
+ # the setState update callback is not called when initalizing initial state
39
+ if block
40
+ %x{
41
+ if (#{@native}.__opalInstanceInitializedState === true) {
42
+ #{@native}[method](state_prop_n, function(){
43
+ #{block.call}
44
+ });
45
+ } else {
46
+ for (var sp in state_prop_n) {
47
+ if (state_prop_n.hasOwnProperty(sp)) {
48
+ #{@native}.state[sp] = state_prop_n[sp];
49
+ }
50
+ }
51
+ }
52
+ }
53
+ else
54
+ %x{
55
+ if (#{@native}.__opalInstanceInitializedState === true) {
56
+ #{@native}[method](state_prop_n);
57
+ } else {
58
+ for (var sp in state_prop_n) {
59
+ if (state_prop_n.hasOwnProperty(sp)) {
60
+ #{@native}.state[sp] = state_prop_n[sp];
61
+ }
62
+ }
63
+ }
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,13 @@
1
+ module React
2
+ module Component
3
+ class Base
4
+ def self.inherited(child)
5
+ # note this is turned off during old style testing: See the spec_helper
6
+ unless child.to_s == "React::Component::HyperTestDummy"
7
+ React::Component.deprecation_warning child, "The class name React::Component::Base has been deprecated. Use Hyperloop::Component instead."
8
+ end
9
+ child.include(ComponentNoNotice)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,181 @@
1
+ module React
2
+ module Component
3
+ # class level methods (macros) for components
4
+ module ClassMethods
5
+
6
+ def deprecation_warning(message)
7
+ React::Component.deprecation_warning(self, message)
8
+ end
9
+
10
+ def reactrb_component?
11
+ true
12
+ end
13
+
14
+ def backtrace(*args)
15
+ @dont_catch_exceptions = (args[0] == :none)
16
+ @backtrace_off = @dont_catch_exceptions || (args[0] == :off)
17
+ end
18
+
19
+ def append_backtrace(message_array, backtrace)
20
+ message_array << " #{backtrace[0]}"
21
+ backtrace[1..-1].each { |line| message_array << line }
22
+ end
23
+
24
+ def render(container = nil, params = {}, &block)
25
+ if container
26
+ container = container.type if container.is_a? React::Element
27
+ define_method :render do
28
+ React::RenderingContext.render(container, params) { instance_eval(&block) if block }
29
+ end
30
+ else
31
+ define_method(:render) { instance_eval(&block) }
32
+ end
33
+ end
34
+
35
+ # method missing will assume the method is a class name, and will treat this a render of
36
+ # of the component, i.e. Foo::Bar.baz === Foo::Bar().baz
37
+
38
+ def method_missing(name, *args, &children)
39
+ Object.method_missing(name, *args, &children) unless args.empty?
40
+ React::RenderingContext.render(
41
+ self, class: React::Element.haml_class_name(name), &children
42
+ )
43
+ end
44
+
45
+ def validator
46
+ @validator ||= Validator.new(props_wrapper)
47
+ end
48
+
49
+ def prop_types
50
+ if self.validator
51
+ {
52
+ _componentValidator: %x{
53
+ function(props, propName, componentName) {
54
+ var errors = #{validator.validate(Hash.new(`props`))};
55
+ return #{`errors`.count > 0 ? `new Error(#{"In component `#{name}`\n" + `errors`.join("\n")})` : `undefined`};
56
+ }
57
+ }
58
+ }
59
+ else
60
+ {}
61
+ end
62
+ end
63
+
64
+ def default_props
65
+ validator.default_props
66
+ end
67
+
68
+ def params(&block)
69
+ validator.build(&block)
70
+ end
71
+
72
+ def props_wrapper
73
+ @props_wrapper ||= Class.new(PropsWrapper)
74
+ end
75
+
76
+ def param(*args)
77
+ if args[0].is_a? Hash
78
+ options = args[0]
79
+ name = options.first[0]
80
+ default = options.first[1]
81
+ options.delete(name)
82
+ options.merge!({default: default})
83
+ else
84
+ name = args[0]
85
+ options = args[1] || {}
86
+ end
87
+ if options[:default]
88
+ validator.optional(name, options)
89
+ else
90
+ validator.requires(name, options)
91
+ end
92
+ end
93
+
94
+ def collect_other_params_as(name)
95
+ validator.allow_undefined_props = true
96
+ validator_in_lexical_scope = validator
97
+ props_wrapper.define_method(name) do
98
+ @_all_others ||= validator_in_lexical_scope.undefined_props(props)
99
+ end
100
+
101
+ validator_in_lexial_scope = validator
102
+ props_wrapper.define_method(name) do
103
+ @_all_others ||= validator_in_lexial_scope.undefined_props(props)
104
+ end
105
+ end
106
+
107
+ def define_state(*states, &block)
108
+ deprecation_warning "'define_state' is deprecated. Use the 'state' macro to declare states."
109
+ default_initial_value = (block && block.arity == 0) ? yield : nil
110
+ states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
111
+ states.each { |name| state(name => default_initial_value) } # was states_hash[name] = default_initial_value
112
+ states_hash.each { |name, value| state(name => value) }
113
+ end
114
+
115
+ def export_state(*states, &block)
116
+ deprecation_warning "'export_state' is deprecated. Use the 'state' macro to declare states."
117
+ default_initial_value = (block && block.arity == 0) ? yield : nil
118
+ states_hash = (states.last.is_a?(Hash)) ? states.pop : {}
119
+ states.each { |name| states_hash[name] = default_initial_value }
120
+ states_hash.each do |name, value|
121
+ state(name => value, scope: :class, reader: true)
122
+ singleton_class.define_method("#{name}!") do |*args|
123
+ mutate.__send__(name, *args)
124
+ end
125
+ end
126
+ end
127
+
128
+ def native_mixin(item)
129
+ native_mixins << item
130
+ end
131
+
132
+ def native_mixins
133
+ @native_mixins ||= []
134
+ end
135
+
136
+ def static_call_back(name, &block)
137
+ static_call_backs[name] = block
138
+ end
139
+
140
+ def static_call_backs
141
+ @static_call_backs ||= {}
142
+ end
143
+
144
+ def export_component(opts = {})
145
+ export_name = (opts[:as] || name).split('::')
146
+ first_name = export_name.first
147
+ Native(`Opal.global`)[first_name] = add_item_to_tree(
148
+ Native(`Opal.global`)[first_name],
149
+ [React::API.create_native_react_class(self)] + export_name[1..-1].reverse
150
+ ).to_n
151
+ end
152
+
153
+ def imports(component_name)
154
+ React::API.import_native_component(
155
+ self, React::API.eval_native_react_component(component_name)
156
+ )
157
+ define_method(:render) {} # define a dummy render method - will never be called...
158
+ rescue Exception => e # rubocop:disable Lint/RescueException : we need to catch everything!
159
+ raise "#{self} cannot import '#{component_name}': #{e.message}."
160
+ # rubocop:enable Lint/RescueException
161
+ ensure
162
+ self
163
+ end
164
+
165
+ def add_item_to_tree(current_tree, new_item)
166
+ if Native(current_tree).class != Native::Object || new_item.length == 1
167
+ new_item.inject { |a, e| { e => a } }
168
+ else
169
+ Native(current_tree)[new_item.last] = add_item_to_tree(
170
+ Native(current_tree)[new_item.last], new_item[0..-2]
171
+ )
172
+ current_tree
173
+ end
174
+ end
175
+
176
+ def to_n
177
+ React::API.class_eval('@@component_classes')[self]
178
+ end
179
+ end
180
+ end
181
+ end