hyper-react 0.10.0

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +27 -0
  3. data/.gitignore +36 -0
  4. data/.rubocop.yml +1159 -0
  5. data/.travis.yml +29 -0
  6. data/Appraisals +20 -0
  7. data/CHANGELOG.md +93 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE +19 -0
  10. data/README.md +121 -0
  11. data/Rakefile +33 -0
  12. data/UPGRADING.md +24 -0
  13. data/component-name-lookup.md +145 -0
  14. data/config.ru +25 -0
  15. data/gemfiles/opal_0.8_react_13.gemfile +13 -0
  16. data/gemfiles/opal_0.8_react_14.gemfile +13 -0
  17. data/gemfiles/opal_0.8_react_15.gemfile +13 -0
  18. data/gemfiles/opal_0.9_react_13.gemfile +13 -0
  19. data/gemfiles/opal_0.9_react_14.gemfile +13 -0
  20. data/gemfiles/opal_0.9_react_15.gemfile +13 -0
  21. data/hyper-react.gemspec +43 -0
  22. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +4 -0
  23. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
  24. data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
  25. data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  26. data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
  27. data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  28. data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  29. data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  30. data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +109 -0
  31. data/lib/hyper-react.rb +52 -0
  32. data/lib/rails-helpers/top_level_rails_component.rb +54 -0
  33. data/lib/react-sources/react-server.js +2 -0
  34. data/lib/react/api.rb +162 -0
  35. data/lib/react/callbacks.rb +42 -0
  36. data/lib/react/children.rb +30 -0
  37. data/lib/react/component.rb +139 -0
  38. data/lib/react/component/api.rb +50 -0
  39. data/lib/react/component/base.rb +9 -0
  40. data/lib/react/component/class_methods.rb +214 -0
  41. data/lib/react/component/dsl_instance_methods.rb +27 -0
  42. data/lib/react/component/params.rb +6 -0
  43. data/lib/react/component/props_wrapper.rb +83 -0
  44. data/lib/react/component/should_component_update.rb +98 -0
  45. data/lib/react/component/tags.rb +144 -0
  46. data/lib/react/element.rb +168 -0
  47. data/lib/react/event.rb +76 -0
  48. data/lib/react/ext/hash.rb +9 -0
  49. data/lib/react/ext/string.rb +8 -0
  50. data/lib/react/hash.rb +13 -0
  51. data/lib/react/native_library.rb +92 -0
  52. data/lib/react/object.rb +15 -0
  53. data/lib/react/observable.rb +29 -0
  54. data/lib/react/react-source.rb +9 -0
  55. data/lib/react/rendering_context.rb +142 -0
  56. data/lib/react/state.rb +190 -0
  57. data/lib/react/test.rb +16 -0
  58. data/lib/react/test/dsl.rb +17 -0
  59. data/lib/react/test/matchers/render_html_matcher.rb +49 -0
  60. data/lib/react/test/rspec.rb +15 -0
  61. data/lib/react/test/session.rb +46 -0
  62. data/lib/react/top_level.rb +132 -0
  63. data/lib/react/validator.rb +136 -0
  64. data/lib/reactive-ruby/component_loader.rb +49 -0
  65. data/lib/reactive-ruby/isomorphic_helpers.rb +197 -0
  66. data/lib/reactive-ruby/rails.rb +7 -0
  67. data/lib/reactive-ruby/rails/component_mount.rb +46 -0
  68. data/lib/reactive-ruby/rails/controller_helper.rb +15 -0
  69. data/lib/reactive-ruby/rails/railtie.rb +14 -0
  70. data/lib/reactive-ruby/serializers.rb +15 -0
  71. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +42 -0
  72. data/lib/reactive-ruby/version.rb +3 -0
  73. data/lib/reactrb/auto-import.rb +32 -0
  74. data/lib/reactrb/deep-compare.rb +24 -0
  75. data/lib/reactrb/new-event-name-convention.rb +11 -0
  76. data/lib/sources/react-latest.js +21169 -0
  77. data/lib/sources/react-v13.js +21645 -0
  78. data/lib/sources/react-v14.js +20821 -0
  79. data/lib/sources/react-v15.js +21170 -0
  80. data/logo1.png +0 -0
  81. data/logo2.png +0 -0
  82. data/logo3.png +0 -0
  83. data/path_release_steps.md +9 -0
  84. data/spec/controller_helper_spec.rb +34 -0
  85. data/spec/index.html.erb +10 -0
  86. data/spec/react/callbacks_spec.rb +106 -0
  87. data/spec/react/children_spec.rb +76 -0
  88. data/spec/react/component/base_spec.rb +32 -0
  89. data/spec/react/component_spec.rb +872 -0
  90. data/spec/react/dsl_spec.rb +296 -0
  91. data/spec/react/element_spec.rb +136 -0
  92. data/spec/react/event_spec.rb +24 -0
  93. data/spec/react/native_library_spec.rb +344 -0
  94. data/spec/react/observable_spec.rb +7 -0
  95. data/spec/react/opal_jquery_extensions_spec.rb +66 -0
  96. data/spec/react/param_declaration_spec.rb +258 -0
  97. data/spec/react/react_spec.rb +209 -0
  98. data/spec/react/state_spec.rb +55 -0
  99. data/spec/react/test/dsl_spec.rb +43 -0
  100. data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
  101. data/spec/react/test/rspec_spec.rb +62 -0
  102. data/spec/react/test/session_spec.rb +100 -0
  103. data/spec/react/test/utils_spec.rb +45 -0
  104. data/spec/react/top_level_component_spec.rb +96 -0
  105. data/spec/react/tutorial/tutorial_spec.rb +36 -0
  106. data/spec/react/validator_spec.rb +124 -0
  107. data/spec/reactive-ruby/component_loader_spec.rb +71 -0
  108. data/spec/reactive-ruby/isomorphic_helpers_spec.rb +155 -0
  109. data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +10 -0
  110. data/spec/reactive-ruby/rails/component_mount_spec.rb +66 -0
  111. data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +35 -0
  112. data/spec/spec_helper.rb +115 -0
  113. data/spec/support/react/spec_helpers.rb +64 -0
  114. data/spec/vendor/es5-shim.min.js +6 -0
  115. data/spec/vendor/jquery-2.2.4.min.js +4 -0
  116. metadata +387 -0
@@ -0,0 +1,16 @@
1
+ require 'react/test/session'
2
+ require 'react/test/dsl'
3
+
4
+ module React
5
+ module Test
6
+ class << self
7
+ def current_session
8
+ @current_session ||= Session.new
9
+ end
10
+
11
+ def reset_session!
12
+ @current_session = nil
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'react/test'
2
+
3
+ module React
4
+ module Test
5
+ module DSL
6
+ def component
7
+ React::Test.current_session
8
+ end
9
+
10
+ Session::DSL_METHODS.each do |method|
11
+ define_method method do |*args, &block|
12
+ component.public_send(method, *args, &block)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ module React
2
+ module Test
3
+ module Matchers
4
+ class RenderHTMLMatcher
5
+ def initialize(expected)
6
+ @expected = expected
7
+ @params = {}
8
+ end
9
+
10
+ def with_params(params)
11
+ @params = params
12
+ self
13
+ end
14
+
15
+ def matches?(component)
16
+ @component = component
17
+ @actual = render_to_html
18
+ @expected == @actual
19
+ end
20
+
21
+ def failure_message
22
+ failure_string
23
+ end
24
+
25
+ def negative_failure_message
26
+ failure_string(:negative)
27
+ end
28
+
29
+ private
30
+
31
+ def render_to_html
32
+ element = React.create_element(@component, @params)
33
+ React.render_to_static_markup(element)
34
+ end
35
+
36
+ def failure_string(negative = false)
37
+ str = "expected '#{@component.name}' with params '#{@params}' to "
38
+ str = str + "not " if negative
39
+ str = str + "render '#{@expected}', but '#{@actual}' was rendered."
40
+ str
41
+ end
42
+ end
43
+
44
+ def render(*args)
45
+ RenderHTMLMatcher.new(*args)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ require 'react/test/dsl'
2
+ require 'react/test/matchers/render_html_matcher'
3
+
4
+ RSpec.configure do |config|
5
+ config.include React::Test::DSL, type: :component
6
+ config.include React::Test::Matchers, type: :component
7
+
8
+ config.after do
9
+ React::Test.reset_session!
10
+ end
11
+
12
+ config.before do
13
+ # nothing yet
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ module React
2
+ module Test
3
+ class Session
4
+ DSL_METHODS = %i[mount instance native element update_params
5
+ force_update! html].freeze
6
+
7
+ def mount(component_klass, params = {})
8
+ @element = React.create_element(component_klass, params)
9
+ instance
10
+ end
11
+
12
+ def instance
13
+ unless @instance
14
+ @native = Native(`React.addons.TestUtils.renderIntoDocument(#{element.to_n})`)
15
+ @instance = `#{@native.to_n}._getOpalInstance()`
16
+ end
17
+ @instance
18
+ end
19
+
20
+ def native
21
+ @native
22
+ end
23
+
24
+ def element
25
+ @element
26
+ end
27
+
28
+ def update_params(params)
29
+ cloned_element = React::Element.new(`React.cloneElement(#{self.element.to_n}, #{params.to_n})`)
30
+ prev_container = `#{self.instance.dom_node}.parentNode`
31
+ React.render(cloned_element, prev_container)
32
+ nil
33
+ end
34
+
35
+ def force_update!
36
+ native.force_update!
37
+ end
38
+
39
+ def html
40
+ # How can we get the current ReactElement w/o violating private APIs?
41
+ elem = Native(native[:_reactInternalInstance][:_currentElement])
42
+ React.render_to_static_markup(elem)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,132 @@
1
+ require "native"
2
+ require 'active_support'
3
+ require 'react/component/tags'
4
+ require 'react/component/base'
5
+
6
+ module React
7
+
8
+ ATTRIBUTES = %w(accept acceptCharset accessKey action allowFullScreen allowTransparency alt
9
+ async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
10
+ className cols colSpan content contentEditable contextMenu controls coords
11
+ crossOrigin data dateTime defer dir disabled download draggable encType form
12
+ formAction formEncType formMethod formNoValidate formTarget frameBorder height
13
+ hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
14
+ marginHeight marginWidth max maxLength media mediaGroup method min multiple
15
+ muted name noValidate open pattern placeholder poster preload radioGroup
16
+ readOnly rel required role rows rowSpan sandbox scope scrolling seamless
17
+ selected shape size sizes span spellCheck src srcDoc srcSet start step style
18
+ tabIndex target title type useMap value width wmode dangerouslySetInnerHTML) +
19
+ #SVG ATTRIBUTES
20
+ %w(clipPath cx cy d dx dy fill fillOpacity fontFamily
21
+ fontSize fx fy gradientTransform gradientUnits markerEnd
22
+ markerMid markerStart offset opacity patternContentUnits
23
+ patternUnits points preserveAspectRatio r rx ry spreadMethod
24
+ stopColor stopOpacity stroke strokeDasharray strokeLinecap
25
+ strokeOpacity strokeWidth textAnchor transform version
26
+ viewBox x1 x2 x xlinkActuate xlinkArcrole xlinkHref xlinkRole
27
+ xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace y1 y2 y)
28
+ HASH_ATTRIBUTES = %w(data aria)
29
+ HTML_TAGS = React::Component::Tags::HTML_TAGS
30
+
31
+ def self.html_tag?(name)
32
+ tags = HTML_TAGS
33
+ %x{
34
+ for(var i = 0; i < tags.length; i++) {
35
+ if(tags[i] === name)
36
+ return true;
37
+ }
38
+ return false;
39
+ }
40
+ end
41
+
42
+ def self.html_attr?(name)
43
+ attrs = ATTRIBUTES
44
+ %x{
45
+ for(var i = 0; i < attrs.length; i++) {
46
+ if(attrs[i] === name)
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+ end
52
+
53
+ def self.create_element(type, properties = {}, &block)
54
+ React::API.create_element(type, properties, &block)
55
+ end
56
+
57
+ def self.render(element, container)
58
+ container = `container.$$class ? container[0] : container`
59
+ if !(`typeof ReactDOM === 'undefined'`)
60
+ component = Native(`ReactDOM.render(#{element.to_n}, container, function(){#{yield if block_given?}})`) # v0.15+
61
+ elsif !(`typeof React.renderToString === 'undefined'`)
62
+ component = Native(`React.render(#{element.to_n}, container, function(){#{yield if block_given?}})`)
63
+ else
64
+ raise "render is not defined. In React >= v15 you must import it with ReactDOM"
65
+ end
66
+
67
+ component.class.include(React::Component::API)
68
+ component
69
+ end
70
+
71
+ def self.is_valid_element(element)
72
+ element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
73
+ end
74
+
75
+ def self.render_to_string(element)
76
+ if !(`typeof ReactDOMServer === 'undefined'`)
77
+ React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
78
+ elsif !(`typeof React.renderToString === 'undefined'`)
79
+ React::RenderingContext.build { `React.renderToString(#{element.to_n})` }
80
+ else
81
+ raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
82
+ end
83
+ end
84
+
85
+ def self.render_to_static_markup(element)
86
+ if !(`typeof ReactDOMServer === 'undefined'`)
87
+ React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
88
+ elsif !(`typeof React.renderToString === 'undefined'`)
89
+ React::RenderingContext.build { `React.renderToStaticMarkup(#{element.to_n})` }
90
+ else
91
+ raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
92
+ end
93
+ end
94
+
95
+ def self.unmount_component_at_node(node)
96
+ if !(`typeof ReactDOM === 'undefined'`)
97
+ `ReactDOM.unmountComponentAtNode(node.$$class ? node[0] : node)` # v0.15+
98
+ elsif !(`typeof React.renderToString === 'undefined'`)
99
+ `React.unmountComponentAtNode(node.$$class ? node[0] : node)`
100
+ else
101
+ raise "unmountComponentAtNode is not defined. In React >= v15 you must import it with ReactDOM"
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ Element.instance_eval do
108
+ def self.find(selector)
109
+ selector = begin
110
+ selector.dom_node
111
+ rescue
112
+ selector
113
+ end if `#{selector}.$dom_node !== undefined`
114
+ `$(#{selector})`
115
+ end
116
+
117
+ def self.[](selector)
118
+ find(selector)
119
+ end
120
+
121
+ define_method :render do |container = nil, params = {}, &block|
122
+ if `#{self.to_n}._reactrb_component_class === undefined`
123
+ `#{self.to_n}._reactrb_component_class = #{Class.new(React::Component::Base)}`
124
+ end
125
+ klass = `#{self.to_n}._reactrb_component_class`
126
+ klass.class_eval do
127
+ render(container, params, &block)
128
+ end
129
+
130
+ React.render(React.create_element(`#{self.to_n}._reactrb_component_class`), self)
131
+ end
132
+ end if Object.const_defined?('Element')
@@ -0,0 +1,136 @@
1
+ module React
2
+ class Validator
3
+ attr_accessor :errors
4
+ attr_reader :props_wrapper
5
+ private :errors, :props_wrapper
6
+
7
+ def initialize(props_wrapper = Class.new(Component::PropsWrapper))
8
+ @props_wrapper = props_wrapper
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
+ props_wrapper.define_param(name, options[:type])
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,49 @@
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
+ if Opal::Processor.respond_to?(:load_asset_code)
38
+ Opal::Processor.load_asset_code(assets, file)
39
+ else
40
+ Opal::Sprockets.load_asset(file, assets)
41
+ end
42
+ rescue # What exception is being caught here?
43
+ end
44
+
45
+ def assets
46
+ ::Rails.application.assets
47
+ end
48
+ end
49
+ end