rwr-redux 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,105 @@
1
+ import expect, { spyOn } from 'expect';
2
+ import { createStore } from 'redux';
3
+
4
+ import subject from '../../src/integrations/redux-store';
5
+
6
+ const fakeReducer = function () {};
7
+ const validStore = function (initialState) {
8
+ return createStore(fakeReducer, initialState);
9
+ };
10
+
11
+ function resetConstructor() {
12
+ subject.registeredStores = {};
13
+ subject.mountedStores = {};
14
+ subject.defaultStore = null;
15
+
16
+ expect.restoreSpies();
17
+ }
18
+
19
+ describe('ReduxStore', function () {
20
+ before(function () {
21
+ resetConstructor();
22
+ });
23
+
24
+ afterEach(function () {
25
+ resetConstructor();
26
+ });
27
+
28
+ describe('.constructor', function () {
29
+ it('intializes empty registeredStores and mountedStores dictionaries', function () {
30
+ expect(subject.registeredStores).toEqual({});
31
+ expect(subject.mountedStores).toEqual({});
32
+ expect(subject.defaultStore).toEqual(null);
33
+ });
34
+ });
35
+
36
+ describe('#registerStore', function () {
37
+ it('throws an error when there is invalid store', function () {
38
+ expect(function () {
39
+ const invalidStore = {};
40
+ subject.registerStore('InvalidStore', invalidStore);
41
+ })
42
+ .toThrow(/Error when registering 'InvalidStore' store: must be a function./);
43
+ });
44
+
45
+ it('adds valid store to the storage', function () {
46
+ subject.registerStore('ValidStore', validStore);
47
+
48
+ expect(subject.registeredStores.ValidStore).toBe(validStore);
49
+ });
50
+ });
51
+
52
+ describe('#mountStore', function () {
53
+ it('throws an error when store is not a function', function () {
54
+ expect(function () {
55
+ subject.mountStore('InvalidStore', {});
56
+ })
57
+ .toThrow(/Error when mounting 'InvalidStore' store: must be a function./);
58
+ });
59
+ });
60
+
61
+ describe('#getStore', function () {
62
+ it('returns undefined if store is not found', function () {
63
+ expect(subject.getStore('FakeStore')).toBe(undefined);
64
+ });
65
+
66
+ it('returns store by name from mountedStores storage', function () {
67
+ subject.registerStore('ValidStore', validStore);
68
+ subject.mountStore('ValidStore', {});
69
+ expect(subject.getStore('ValidStore')).toEqual(validStore({}));
70
+ });
71
+
72
+ it('returns default store when store\'s name is not given', function () {
73
+ subject.registerStore('ValidStore', validStore);
74
+ subject.mountStore('ValidStore', {});
75
+
76
+ expect(subject.getStore()).toEqual(validStore({}));
77
+ });
78
+ });
79
+
80
+ describe('#integrationWrapper', function () {
81
+ const payload = { name: 'StoreName', props: { fake: 'props' } };
82
+
83
+ describe('mount', function () {
84
+ it('calls #mountStore', function () {
85
+ const { name, props } = payload;
86
+ const mountStoreSpy = spyOn(subject, 'mountStore');
87
+ subject.integrationWrapper.mount('', payload);
88
+
89
+ expect(mountStoreSpy.calls.length).toEqual(1);
90
+ expect(mountStoreSpy).toHaveBeenCalledWith(name, props);
91
+ });
92
+ });
93
+
94
+ describe('nodeRun', function () {
95
+ it('calls #mountStore', function () {
96
+ const { name, props } = payload;
97
+ const mountStoreSpy = spyOn(subject, 'mountStore');
98
+ subject.integrationWrapper.nodeRun(payload);
99
+
100
+ expect(mountStoreSpy.calls.length).toEqual(1);
101
+ expect(mountStoreSpy).toHaveBeenCalledWith(name, props);
102
+ });
103
+ });
104
+ });
105
+ });
@@ -0,0 +1,27 @@
1
+ require 'react_webpack_rails/redux_integration/services/redux_element'
2
+
3
+ module ReactWebpackRails
4
+ module ReduxIntegration
5
+ module Services
6
+ class ReduxContainer < ReduxElement
7
+ def result
8
+ super
9
+ end
10
+
11
+ def payload
12
+ { name: element_name, storeName: store_name }
13
+ end
14
+
15
+ def options
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def empty_result
22
+ { 'body' => '' }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ module ReactWebpackRails
2
+ module ReduxIntegration
3
+ module Services
4
+ class ReduxElement
5
+ def initialize(integration_name, element_name, base_options, path = nil)
6
+ @integration_name = integration_name
7
+ @element_name = element_name
8
+ @store_name = base_options[:store_name]
9
+ @server_side = base_options[:server_side]
10
+ @base_options = base_options
11
+ @path = path
12
+ end
13
+
14
+ def result
15
+ return empty_result unless server_side
16
+ JSON.parse(node_integration)
17
+ end
18
+
19
+ def options
20
+ base_options.except(:store_name, :server_side)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :integration_name, :element_name, :store_name, :server_side, :base_options, :path
26
+
27
+ def node_integration
28
+ NodeIntegrationRunner.new(integration_name, payload).run
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ require 'react_webpack_rails/redux_integration/services/redux_element'
2
+
3
+ module ReactWebpackRails
4
+ module ReduxIntegration
5
+ module Services
6
+ class ReduxRouter < ReduxElement
7
+ def result
8
+ super
9
+ end
10
+
11
+ def payload
12
+ { name: element_name, storeName: store_name, path: path }
13
+ end
14
+
15
+ def options
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def empty_result
22
+ { 'code' => 200, 'body' => '' }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  module ReactWebpackRails
2
2
  module ReduxIntegration
3
- VERSION = '0.1.1'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -1,13 +1,34 @@
1
+ require 'react_webpack_rails/redux_integration/services/redux_container'
2
+ require 'react_webpack_rails/redux_integration/services/redux_router'
3
+
1
4
  module ReactWebpackRails
2
5
  module ReduxIntegration
3
6
  module ViewHelpers
4
7
  def redux_store(name, raw_props = {}, options = {})
5
- react_element('redux-store', { name: name, props: serialize_props(raw_props) }, options)
8
+ props = serialize_props(raw_props)
9
+
10
+ if server_side(options.delete(:server_side))
11
+ NodeIntegrationRunner.new('redux-store', name: name, props: props).run
12
+ end
13
+
14
+ react_element('redux-store', { name: name, props: props }, options)
6
15
  end
7
16
 
8
17
  def redux_container(name, options = {})
9
- store_name = options.delete(:store_name)
10
- react_element('redux-container', { name: name, storeName: store_name }, options)
18
+ container = Services::ReduxContainer.new('redux-container', name, options)
19
+
20
+ react_element('redux-container', container.payload, container.options) do
21
+ container.result['body'].html_safe
22
+ end
23
+ end
24
+
25
+ def redux_router(name, options = {})
26
+ router = Services::ReduxRouter.new('redux-router', name, options, request.path)
27
+ result = handle_response_code(router.result, name, request.path)
28
+
29
+ react_element('redux-router', router.payload, router.options) do
30
+ result
31
+ end
11
32
  end
12
33
 
13
34
  private
@@ -17,6 +38,21 @@ module ReactWebpackRails
17
38
  return props unless Rails.application.config.react.camelize_props
18
39
  ReactWebpackRails::Services::CamelizeKeys.call(props)
19
40
  end
41
+
42
+ def handle_response_code(result, name, path)
43
+ case result['code']
44
+ when 200
45
+ result['body'].html_safe
46
+ when 302
47
+ controller.redirect_to(result['redirectUri'])
48
+ else
49
+ raise ActionController::RoutingError, routing_error(name, path)
50
+ end
51
+ end
52
+
53
+ def routing_error(name, path)
54
+ "ReactWebpackRails::ReduxIntegration: No route found in #{name} router for #{path}."
55
+ end
20
56
  end
21
57
  end
22
58
  end
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rwr-redux",
3
- "version": "0.1.0-alpha1",
4
- "description": "",
3
+ "version": "0.2.0",
4
+ "description": "Redux integration for react_webpack_rails",
5
5
  "main": "js/lib/index.js",
6
6
  "files": [
7
7
  "js/lib"
@@ -12,24 +12,29 @@
12
12
  "test": "mocha js/test --ui bdd --recursive --require babel-core/register"
13
13
  },
14
14
  "repository": {
15
- "type": "",
16
- "url": ""
15
+ "type": "git",
16
+ "url": "git+https://github.com/netguru/rwr-redux.git"
17
17
  },
18
- "keywords": [],
19
- "author": "",
20
- "license": "",
18
+ "keywords": [
19
+ "redux",
20
+ "react-redux",
21
+ "react",
22
+ "rails",
23
+ "react_webpack_rails",
24
+ "rwr"
25
+ ],
26
+ "author": "Kacper Goliński",
27
+ "license": "MIT",
21
28
  "bugs": {
22
- "url": ""
23
- },
24
- "homepage": "",
25
- "peerDependencies": {
26
- "react": "^0.14.0",
27
- "react-dom": "^0.14.0",
28
- "react-redux": "^4.4.0",
29
- "redux": "^3.3.1"
29
+ "url": "https://github.com/netguru/rwr-redux/issues"
30
30
  },
31
+ "homepage": "https://github.com/netguru/rwr-redux",
31
32
  "dependencies": {
32
- "react-webpack-rails": "^0.1.0"
33
+ "react-redux": "^4.4.2",
34
+ "react-router": "^2.0.1",
35
+ "react-router-redux": "^4.0.1",
36
+ "react-webpack-rails": "^0.3.1",
37
+ "redux": "^3.4.0"
33
38
  },
34
39
  "devDependencies": {
35
40
  "babel-cli": "^6.4.0",
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ['react@netguru.co']
11
11
 
12
12
  spec.summary = 'Redux integration for react_webpack_rails'
13
- spec.description = ''
14
- spec.homepage = ''
13
+ spec.description = 'Redux integration for react_webpack_rails'
14
+ spec.homepage = 'https://github.com/netguru/rwr-redux'
15
15
  spec.license = 'MIT'
16
16
 
17
17
  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'bundler', '~> 1.10'
26
26
  spec.add_development_dependency 'rake', '~> 10.0'
27
27
  spec.add_development_dependency 'rspec', '~> 3.3'
28
+ spec.add_development_dependency 'pry'
28
29
 
29
30
  spec.add_dependency 'react_webpack_rails', '>= 0.1.0'
30
31
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rwr-redux
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kacper Goliński
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2016-03-17 00:00:00.000000000 Z
12
+ date: 2016-04-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '3.3'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: react_webpack_rails
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -67,7 +81,7 @@ dependencies:
67
81
  - - ">="
68
82
  - !ruby/object:Gem::Version
69
83
  version: 0.1.0
70
- description: ''
84
+ description: Redux integration for react_webpack_rails
71
85
  email:
72
86
  - react@netguru.co
73
87
  executables: []
@@ -75,7 +89,9 @@ extensions: []
75
89
  extra_rdoc_files: []
76
90
  files:
77
91
  - ".babelrc"
92
+ - ".eslintrc"
78
93
  - ".gitignore"
94
+ - ".rubocop.yml"
79
95
  - CHANGELOG.md
80
96
  - Gemfile
81
97
  - LICENSE.txt
@@ -83,19 +99,30 @@ files:
83
99
  - Rakefile
84
100
  - bin/console
85
101
  - bin/setup
102
+ - docs/rails-redux-router.md
86
103
  - js/.eslintrc
87
104
  - js/src/index.js
105
+ - js/src/integrations/redux-container.js
106
+ - js/src/integrations/redux-router.js
107
+ - js/src/integrations/redux-store.js
88
108
  - js/src/utils/validators.js
89
109
  - js/src/version.js
90
- - js/test/index.js
110
+ - js/test/helpers/redux-router.js
111
+ - js/test/index.spec.js
112
+ - js/test/integrations/redux-container.spec.js
113
+ - js/test/integrations/redux-router.spec.js
114
+ - js/test/integrations/redux-store.spec.js
91
115
  - lib/react_webpack_rails/redux_integration/engine.rb
92
116
  - lib/react_webpack_rails/redux_integration/railtie.rb
117
+ - lib/react_webpack_rails/redux_integration/services/redux_container.rb
118
+ - lib/react_webpack_rails/redux_integration/services/redux_element.rb
119
+ - lib/react_webpack_rails/redux_integration/services/redux_router.rb
93
120
  - lib/react_webpack_rails/redux_integration/version.rb
94
121
  - lib/react_webpack_rails/redux_integration/view_helpers.rb
95
122
  - lib/rwr-redux.rb
96
123
  - package.json
97
124
  - redux_integration.gemspec
98
- homepage: ''
125
+ homepage: https://github.com/netguru/rwr-redux
99
126
  licenses:
100
127
  - MIT
101
128
  metadata: {}
@@ -1,188 +0,0 @@
1
- import expect, { spyOn } from 'expect';
2
- import { createStore } from 'redux'
3
- import React from 'react';
4
- import ReactDOM from 'react-dom';
5
- import ReactDOMServer from 'react-dom/server';
6
-
7
- import subject from '../src/index';
8
-
9
- class AppContainer extends React.Component {
10
- render() {
11
- return <div>AppContainer</div>;
12
- }
13
- }
14
-
15
- const fakeReducer = function() {};
16
- const validStore = function(initialState) {
17
- return createStore(fakeReducer, initialState)
18
- };
19
-
20
- describe('RWRRedux', function () {
21
- afterEach(function() {
22
- subject.registeredStores = {};
23
- subject.mountedStores = {};
24
- subject.containers = {};
25
- expect.restoreSpies();
26
- });
27
-
28
- describe('.version', function () {
29
- it('is present', function () {
30
- expect(subject.version).toNotEqual(undefined);
31
- });
32
- });
33
-
34
- describe('.constructor', function() {
35
- it('intializes empty stores, mountedStores and containers dictionaries', function () {
36
- expect(subject.registeredStores).toEqual({});
37
- expect(subject.mountedStores).toEqual({});
38
- expect(subject.containers).toEqual({});
39
- });
40
- });
41
-
42
- describe('#registerStore', function() {
43
- it('throws an error', function() {
44
- expect(function() {
45
- const invalidStore = {};
46
- subject.registerStore('InvalidStore', invalidStore);
47
- })
48
- .toThrow(/Error when registering 'InvalidStore' store: must be a function./);
49
- });
50
-
51
- it('adds valid store to the storage', function() {
52
- subject.registerStore('ValidStore', validStore);
53
-
54
- expect(subject.registeredStores.ValidStore).toBe(validStore);
55
- });
56
- });
57
-
58
- describe('#mountStore', function() {
59
- it('throws an error when store is not a function', function(){
60
- expect(function() {
61
- subject.mountStore('InvalidStore', {});
62
- })
63
- .toThrow(/Error when mounting 'InvalidStore' store: must be a function./);
64
- });
65
-
66
- it('throws an error when store does not returns valid object', function(){
67
- subject.registerStore('InvalidStore', function() { return 'store' });
68
- expect(function() {
69
- subject.mountStore('InvalidStore', {});
70
- })
71
- .toThrow(/Error when mounting 'InvalidStore' store: must be a valid Redux store./);
72
- });
73
-
74
- it('adds store to mountedStores storage and save as defaultStore', function() {
75
- subject.registerStore('ValidStore', validStore);
76
- const initialState = {};
77
- subject.mountStore('ValidStore', initialState);
78
- const storeObject = validStore(initialState);
79
-
80
- expect(subject.mountedStores.ValidStore).toEqual(storeObject);
81
- expect(subject.defaultStore).toEqual(storeObject)
82
- });
83
- });
84
-
85
- describe('#getStore', function() {
86
- it('returns undefined if store is not found', function() {
87
- expect(subject.getStore('FakeStore')).toBe(undefined);
88
- });
89
-
90
- it('returns store by name from mountedStores storage', function() {
91
- subject.registerStore('ValidStore', validStore);
92
- subject.mountStore('ValidStore', {});
93
- expect(subject.getStore('ValidStore')).toEqual(validStore({}));
94
- });
95
-
96
- it('returns default store when store\'s name is not given', function() {
97
- subject.registerStore('ValidStore', validStore);
98
- subject.mountStore('ValidStore', {});
99
-
100
- expect(subject.getStore()).toEqual(validStore({}));
101
- });
102
- });
103
-
104
- describe('#registerContainer', function() {
105
- it('adds container to the storage', function() {
106
- subject.registerContainer('AppContainer', AppContainer);
107
-
108
- expect(subject.containers.AppContainer).toEqual(AppContainer);
109
- });
110
- });
111
-
112
- describe('#getContainer', function() {
113
- it('returns container by name', function() {
114
- subject.registerContainer('AppContainer', AppContainer);
115
- expect(subject.getContainer('AppContainer')).toEqual(AppContainer);
116
- });
117
- });
118
-
119
- describe('#createContainer', function () {
120
- it('creates redux container', function() {
121
- subject.registerContainer('AppContainer', AppContainer);
122
- const container = subject.createContainer('AppContainer');
123
-
124
- expect(React.isValidElement(container)).toBe(true);
125
- expect(container.type).toBe(AppContainer);
126
- });
127
- });
128
-
129
- describe('#createRootComponent', function() {
130
- it('creates redux root component', function() {
131
- subject.registerStore('ValidStore', validStore);
132
- const initialState = { fake: 'state' };
133
- subject.mountStore('ValidStore', initialState);
134
- subject.registerContainer('AppContainer', AppContainer);
135
- const rootComponent = subject.createRootComponent('AppContainer', 'ValidStore');
136
-
137
- expect(React.isValidElement(rootComponent)).toBe(true);
138
- });
139
- });
140
-
141
- describe('#renderContainer', function() {
142
- it('calls #createRootComponent and ReactDOM.render functions', function() {
143
- const subjectSpy = spyOn(subject, 'createRootComponent');
144
- const reactSpy = spyOn(ReactDOM, 'render');
145
-
146
- subject.renderContainer('ContainerName', 'node', 'StoreName');
147
-
148
- expect(subjectSpy.calls.length).toEqual(1);
149
- expect(subjectSpy).toHaveBeenCalledWith('ContainerName', 'StoreName');
150
- expect(reactSpy.calls.length).toEqual(1);
151
- });
152
- });
153
-
154
- describe('#unmountContainer', function() {
155
- const node = { nodeType: 1, nodeName: 'DIV' };
156
- const unmountSpy = spyOn(ReactDOM, 'unmountComponentAtNode');
157
-
158
- subject.unmountContainer(node);
159
-
160
- expect(unmountSpy.calls.length).toEqual(1);
161
- expect(unmountSpy).toHaveBeenCalledWith(node);
162
- });
163
-
164
- describe('#renderContainerToString', function() {
165
- it('calls #createRootComponent and ReactDOM.renderToString', function() {
166
- const subjectSpy = spyOn(subject, 'createRootComponent');
167
- const reactSpy = spyOn(ReactDOMServer, 'renderToString');
168
-
169
- subject.renderContainerToString('ContainerName', 'StoreName');
170
-
171
- expect(subjectSpy.calls.length).toEqual(1);
172
- expect(subjectSpy).toHaveBeenCalledWith('ContainerName', 'StoreName');
173
- expect(reactSpy.calls.length).toEqual(1);
174
- });
175
- });
176
-
177
- describe('#storeIntegrationWrapper.mount', function() {
178
- it('calls #mountStore function', function() {
179
- const mountStoreSpy = spyOn(subject, 'mountStore');
180
- const payload = { name: 'StoreName', props: { fake: 'props' } };
181
-
182
- subject.storeIntegrationWrapper.mount('', payload)
183
-
184
- expect(mountStoreSpy.calls.length).toEqual(1);
185
- expect(mountStoreSpy).toHaveBeenCalledWith(payload.name, payload.props);
186
- });
187
- });
188
- });