hyper-react 0.99.6 → 1.0.0.lap21

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.
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