rwr-redux 0.1.1 → 0.2.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.
@@ -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
- });