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,155 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe React::IsomorphicHelpers do
4
+ describe 'code execution context' do
5
+ let(:klass) { Class.send(:include, described_class) }
6
+
7
+ describe 'module class methods', :opal do
8
+ it { expect(described_class).to_not be_on_opal_server }
9
+ it { expect(described_class).to be_on_opal_client }
10
+ end
11
+
12
+ describe 'included class methods', :opal do
13
+ it { expect(klass).to_not be_on_opal_server }
14
+ it { expect(klass).to be_on_opal_client }
15
+ end
16
+
17
+ describe 'included instance methods', :opal do
18
+ it { expect(klass.new).to_not be_on_opal_server }
19
+ it { expect(klass.new).to be_on_opal_client }
20
+ end
21
+
22
+ describe 'module class methods', :ruby do
23
+ it { is_expected.to_not be_on_opal_server }
24
+ it { is_expected.to_not be_on_opal_client }
25
+ end
26
+
27
+ describe 'included class methods', :ruby do
28
+ subject { klass }
29
+ it { is_expected.to_not be_on_opal_server }
30
+ it { is_expected.to_not be_on_opal_client }
31
+ end
32
+
33
+ describe 'included instance methods', :ruby do
34
+ subject { klass.new }
35
+ it { is_expected.to_not be_on_opal_server }
36
+ it { is_expected.to_not be_on_opal_client }
37
+ end
38
+ end
39
+
40
+ if ruby?
41
+ describe 'load_context', :ruby do
42
+ let(:v8_context) { TestV8Context.new }
43
+ let(:controller) { double('controller') }
44
+ let(:name) { double('name') }
45
+
46
+ it 'creates a context and sets a controller' do
47
+ context = described_class.load_context(v8_context, controller, name)
48
+ expect(context.controller).to eq(controller)
49
+ end
50
+
51
+ it 'creates a context and sets a unique_id' do
52
+ Timecop.freeze do
53
+ stamp = Time.now.to_i
54
+ context = described_class.load_context(v8_context, controller, name)
55
+ expect(context.unique_id).to eq("#{ controller.object_id }-#{ stamp }")
56
+ end
57
+ end
58
+ end
59
+
60
+ describe React::IsomorphicHelpers::Context do
61
+ class TestV8Context < Hash
62
+ def eval(args)
63
+ true
64
+ end
65
+ end
66
+
67
+ # Need to decouple/dry up this...
68
+ def test_context(files = nil)
69
+ js = ReactiveRuby::ServerRendering::ContextualRenderer::CONSOLE_POLYFILL.dup
70
+ js << Opal::Builder.build('opal').to_s
71
+ Array(files).each do |filename|
72
+ js << ::Rails.application.assets[filename].to_s
73
+ end
74
+ js = "#{React::ServerRendering::ExecJSRenderer::GLOBAL_WRAPPER}#{js}"
75
+ ctx = ExecJS.compile(js)
76
+ ctx = ReactiveRuby::ServerRendering.context_instance_for(ctx)
77
+ end
78
+
79
+ def react_context
80
+ test_context('components')
81
+ end
82
+
83
+ let(:v8_context) { TestV8Context.new }
84
+ let(:controller) { double('controller') }
85
+ let(:name) { double('name') }
86
+ before do
87
+ described_class.instance_variable_set :@before_first_mount_blocks, nil
88
+ end
89
+
90
+ describe '#initialize' do
91
+ it "sets the given V8 context's ServerSideIsomorphicMethods to itself" do
92
+ context = described_class.new('unique-id', v8_context, controller, name)
93
+ expect(v8_context['ServerSideIsomorphicMethods']).to eq(context)
94
+ end
95
+
96
+ it 'calls before mount callbacks' do
97
+ string = instance_double(String)
98
+ described_class.register_before_first_mount_block do
99
+ string.inspect
100
+ end
101
+ expect(string).to receive(:inspect).once
102
+ context = described_class.new('unique-id', v8_context, controller, name)
103
+ end
104
+ end
105
+
106
+ describe '#eval' do
107
+ it 'delegates to given context' do
108
+ context = described_class.new('unique-id', v8_context, controller, name)
109
+ js = 'true;'
110
+ expect(v8_context).to receive(:eval).with(js).once
111
+ context.eval(js)
112
+ end
113
+ end
114
+
115
+ describe '#send_to_opal' do
116
+ let(:opal_code) { Opal::Builder.new.build_str(ruby_code, __FILE__) }
117
+ let(:ruby_code) { %Q[
118
+ module React::IsomorphicHelpers
119
+ def self.greet(name)
120
+ "Hello, #\{name}!"
121
+ end
122
+
123
+ def self.valediction
124
+ 'Goodbye'
125
+ end
126
+ end
127
+ ]}
128
+
129
+ it 'raises an error when react cannot be loaded' do
130
+ context = described_class.new('unique-id', v8_context, controller, name)
131
+ context.instance_variable_set(:@ctx, test_context)
132
+ expect {
133
+ context.send_to_opal(:foo)
134
+ }.to raise_error(/No react.rb components found/)
135
+ end
136
+
137
+ it 'executes method with args inside opal rubyracer context' do
138
+ ctx = react_context
139
+ context = described_class.new('unique-id', ctx, controller, name)
140
+ ctx.eval(opal_code)
141
+ result = context.send_to_opal(:greet, 'world')
142
+ expect(result).to eq('Hello, world!')
143
+ end
144
+
145
+ it 'executes the method inside opal rubyracer context' do
146
+ ctx = react_context
147
+ context = described_class.new('unique-id', ctx, controller, name)
148
+ ctx.eval(opal_code)
149
+ result = context.send_to_opal(:valediction)
150
+ expect(result).to eq('Goodbye')
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ if ruby?
4
+ RSpec.describe 'test_app generator' do
5
+ it "does not interfer with asset precompilation" do
6
+ cmd = "cd spec/test_app; BUNDLE_GEMFILE=#{ENV['REAL_BUNDLE_GEMFILE']} bundle exec rake assets:precompile"
7
+ expect(system(cmd)).to be_truthy
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ if ruby?
4
+ RSpec.describe ReactiveRuby::Rails::ComponentMount do
5
+ let(:helper) { described_class.new }
6
+
7
+ before do
8
+ helper.setup(ActionView::TestCase::TestController.new)
9
+ end
10
+
11
+ describe '#react_component' do
12
+ it 'renders a div' do
13
+ html = helper.react_component('Components::HelloWorld')
14
+ expect(html).to match(/<div.*><\/div>/)
15
+ end
16
+
17
+ it 'accepts a pre-render option' do
18
+ html = helper.react_component('Components::HelloWorld', {}, prerender: true)
19
+ expect(html).to match(/<div.*><span.*>Hello, World!<\/span><\/div>/)
20
+ end
21
+
22
+ it 'sets data-react-class to React.TopLevelRailsComponent' do
23
+ html = helper.react_component('Components::HelloWorld')
24
+ top_level_class = 'React.TopLevelRailsComponent'
25
+ expect(attr_value(html, 'data-react-class')).to eq(top_level_class)
26
+ end
27
+
28
+ it 'sets component_name in data-react-props hash' do
29
+ html = helper.react_component('Components::HelloWorld')
30
+ props = react_props_for(html)
31
+
32
+ expect(props['component_name']).to eq('Components::HelloWorld')
33
+ end
34
+
35
+ it 'sets render_params in data-react-props hash' do
36
+ html = helper.react_component('Components::HelloWorld', {'foo' => 'bar'})
37
+ props = react_props_for(html)
38
+
39
+ expect(props['render_params']).to include({ 'foo' => 'bar' })
40
+ end
41
+
42
+ it 'sets controller in data-react-props hash' do
43
+ html = helper.react_component('Components::HelloWorld')
44
+ props = react_props_for(html)
45
+
46
+ expect(props['controller']).to eq('ActionView::TestCase::Test')
47
+ end
48
+
49
+ it 'passes additional options through as html attributes' do
50
+ html = helper.react_component('Components::HelloWorld', {},
51
+ { 'foo-bar' => 'biz-baz' })
52
+
53
+ expect(attr_value(html, 'foo-bar')).to eq('biz-baz')
54
+ end
55
+ end
56
+
57
+ def attr_value(html, attr)
58
+ matches = html.match(/#{attr}=["']((?:.(?!["']\s+(?:\S+)=|[>"']))+.)["']?/)
59
+ matches.captures.first
60
+ end
61
+
62
+ def react_props_for(html)
63
+ JSON.parse(CGI.unescapeHTML("#{attr_value(html, 'data-react-props')}"))
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ if ruby?
4
+ RSpec.describe ReactiveRuby::ServerRendering::ContextualRenderer do
5
+ let(:renderer) { described_class.new({}) }
6
+ let(:init) { Proc.new {} }
7
+ let(:options) { { context_initializer: init } }
8
+
9
+ describe '#render' do
10
+ it 'pre-renders HTML' do
11
+ result = renderer.render('Components.Todo',
12
+ { todo: 'finish reactive-ruby' },
13
+ options)
14
+ expect(result).to match(/<li.*>finish reactive-ruby<\/li>/)
15
+ expect(result).to match(/data-react-checksum/)
16
+ end
17
+
18
+ it 'accepts props as a string' do
19
+ result = renderer.render('Components.Todo',
20
+ { todo: 'finish reactive-ruby' }.to_json,
21
+ options)
22
+ expect(result).to match(/<li.*>finish reactive-ruby<\/li>/)
23
+ expect(result).to match(/data-react-checksum/)
24
+ end
25
+
26
+ it 'pre-renders static content' do
27
+ result = renderer.render('Components.Todo',
28
+ { todo: 'finish reactive-ruby' },
29
+ :static)
30
+ expect(result).to match(/<li.*>finish reactive-ruby<\/li>/)
31
+ expect(result).to_not match(/data-react-checksum/)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,115 @@
1
+ ENV["RAILS_ENV"] ||= 'test'
2
+
3
+ require 'opal'
4
+ require 'opal-rspec'
5
+ require 'opal-jquery'
6
+
7
+ def opal?
8
+ RUBY_ENGINE == 'opal'
9
+ end
10
+
11
+ def ruby?
12
+ !opal?
13
+ end
14
+
15
+
16
+ if RUBY_ENGINE == 'opal'
17
+ require File.expand_path('../vendor/jquery-2.2.4.min', __FILE__)
18
+ require 'react/react-source'
19
+ require 'hyper-react'
20
+ require 'react/test/rspec'
21
+
22
+ require File.expand_path('../support/react/spec_helpers', __FILE__)
23
+
24
+ module Opal
25
+ module RSpec
26
+ module AsyncHelpers
27
+ module ClassMethods
28
+ def rendering(title, &block)
29
+ klass = Class.new do
30
+ include React::Component
31
+
32
+ def self.block
33
+ @block
34
+ end
35
+
36
+ def self.name
37
+ "dummy class"
38
+ end
39
+
40
+ def render
41
+ instance_eval &self.class.block
42
+ end
43
+
44
+ def self.should_generate(opts={}, &block)
45
+ sself = self
46
+ @self.async(@title, opts) do
47
+ expect_component_to_eventually(sself, &block)
48
+ end
49
+ end
50
+
51
+ def self.should_immediately_generate(opts={}, &block)
52
+ sself = self
53
+ @self.it(@title, opts) do
54
+ element = build_element sself, {}
55
+ context = block.arity > 0 ? self : element
56
+ expect((element and context.instance_exec(element, &block))).to be(true)
57
+ end
58
+ end
59
+
60
+ end
61
+ klass.instance_variable_set("@block", block)
62
+ klass.instance_variable_set("@self", self)
63
+ klass.instance_variable_set("@title", "it can render #{title}")
64
+ klass
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ RSpec.configure do |config|
73
+ config.include React::SpecHelpers
74
+ config.filter_run_excluding :ruby
75
+ if `(React.version.search(/^0\.13/) === -1)`
76
+ config.filter_run_excluding :v13_only
77
+ else
78
+ config.filter_run_excluding :v13_exclude
79
+ end
80
+ end
81
+ end
82
+
83
+ if RUBY_ENGINE != 'opal'
84
+ begin
85
+ require File.expand_path('../test_app/config/environment', __FILE__)
86
+ rescue LoadError
87
+ puts 'Could not load test application. Please ensure you have run `bundle exec rake test_app`'
88
+ end
89
+ require 'rspec/rails'
90
+ require 'timecop'
91
+
92
+ Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
93
+
94
+ RSpec.configure do |config|
95
+ config.color = true
96
+ config.fail_fast = ENV['FAIL_FAST'] || false
97
+ config.fixture_path = File.join(File.expand_path(File.dirname(__FILE__)), "fixtures")
98
+ config.infer_spec_type_from_file_location!
99
+ config.mock_with :rspec
100
+ config.raise_errors_for_deprecations!
101
+
102
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
103
+ # examples within a transaction, comment the following line or assign false
104
+ # instead of true.
105
+ config.use_transactional_fixtures = true
106
+
107
+ config.before :each do
108
+ Rails.cache.clear
109
+ end
110
+
111
+ config.filter_run_including focus: true
112
+ config.filter_run_excluding opal: true
113
+ config.run_all_when_everything_filtered = true
114
+ end
115
+ end
@@ -0,0 +1,64 @@
1
+ if opal?
2
+ module React
3
+ module SpecHelpers
4
+ `var ReactTestUtils = React.addons.TestUtils`
5
+
6
+ def render_to_html(type, options = {})
7
+ element = React.create_element(type, options)
8
+ React.render_to_static_markup(element)
9
+ end
10
+
11
+ def renderToDocument(type, options = {})
12
+ element = React.create_element(type, options)
13
+ renderElementToDocument(element)
14
+ end
15
+
16
+ def renderElementToDocument(element)
17
+ instance = Native(`ReactTestUtils.renderIntoDocument(#{element.to_n})`)
18
+ instance.class.include(React::Component::API)
19
+ instance
20
+ end
21
+
22
+ def simulateEvent(event, element, params = {})
23
+ simulator = Native(`ReactTestUtils.Simulate`)
24
+ simulator[event.to_s].call(element.dom_node, params)
25
+ end
26
+
27
+ def isElementOfType(element, type)
28
+ `React.addons.TestUtils.isElementOfType(#{element.to_n}, #{type.cached_component_class})`
29
+ end
30
+
31
+ def build_element(type, options)
32
+ component = React.create_element(type, options)
33
+ element = `ReactTestUtils.renderIntoDocument(#{component.to_n})`
34
+ if `typeof React.findDOMNode === 'undefined'`
35
+ `$(element.getDOMNode())` # v0.12
36
+ else
37
+ `$(React.findDOMNode(element))` # v0.13
38
+ end
39
+ end
40
+
41
+ def expect_component_to_eventually(component_class, opts = {}, &block)
42
+ # Calls block after each update of a component until it returns true.
43
+ # When it does set the expectation to true. Uses the after_update
44
+ # callback of the component_class, then instantiates an element of that
45
+ # class The call back is only called on updates, so the call back is
46
+ # manually called right after the element is created. Because React.rb
47
+ # runs the callback inside the components context, we have to setup a
48
+ # lambda to get back to correct context before executing run_async.
49
+ # Because run_async can only be run once it is protected by clearing
50
+ # element once the test passes.
51
+ element = nil
52
+ check_block = lambda do
53
+ context = block.arity > 0 ? self : element
54
+ run_async do
55
+ element = nil; expect(true).to be(true)
56
+ end if element and context.instance_exec(element, &block)
57
+ end
58
+ component_class.after_update { check_block.call }
59
+ element = build_element component_class, opts
60
+ check_block.call
61
+ end
62
+ end
63
+ end
64
+ end