Skip to content

Commit fb9118e

Browse files
authored
Extendable base environment config and new API's (#884)
1 parent deafb46 commit fb9118e

34 files changed

+1364
-168
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ install:
2424
- yarn
2525
script:
2626
- yarn lint
27+
- yarn test
2728
- bundle exec rubocop
2829
- bundle exec rake test
2930
matrix:

lib/install/examples/vue/hello_vue.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint no-console: 0 */
22
// Run this example by adding <%= javascript_pack_tag 'hello_vue' %> (and
3-
// <%= stylesheet_pack_tag 'hello_vue' %> if you set extractStyles to true
4-
// in config/webpack/loaders/vue.js) to the head of your layout file,
3+
// <%= stylesheet_pack_tag 'hello_vue' %> if you have styles in your component)
4+
// to the head of your layout file,
55
// like app/views/layouts/application.html.erb.
66
// All it does is render <div>Hello Vue</div> at the bottom of the page.
77

lib/install/template.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Install Webpacker
22
copy_file "#{__dir__}/config/webpacker.yml", "config/webpacker.yml"
33

4-
say "Copying webpack core config and loaders"
4+
puts "Copying webpack core config"
55
directory "#{__dir__}/config/webpack", "config/webpack"
66

77
say "Copying .postcssrc.yml to app root directory"

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,18 @@
4242
"eslint-config-airbnb": "^14.1.0",
4343
"eslint-plugin-import": "^2.2.0",
4444
"eslint-plugin-jsx-a11y": "^4.0.0",
45-
"eslint-plugin-react": "^6.10.0"
45+
"eslint-plugin-react": "^6.10.0",
46+
"jest": "^21.2.1"
47+
},
48+
"jest": {
49+
"testRegex": "(/__tests__/.*|(\\.|/))\\.jsx?$",
50+
"roots": ["<rootDir>/package"]
4651
},
4752
"peerDependencies": {
4853
"coffeescript": ">= 1.12.7 || >= 2.x"
4954
},
5055
"scripts": {
51-
"test": "echo \"Error: no test specified\" && exit 1",
56+
"test": "jest",
5257
"lint": "eslint {package,lib}/"
5358
},
5459
"repository": {

package/asset_host.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
const config = require('./config')
22
const { resolve } = require('path')
33

4-
function removeOuterSlashes(string) {
5-
return string.replace(/^\/*/, '').replace(/\/*$/, '')
6-
}
4+
const removeOuterSlashes = string =>
5+
string.replace(/^\/*/, '').replace(/\/*$/, '')
76

8-
function formatPublicPath(host = '', path = '') {
7+
const formatPublicPath = (host = '', path = '') => {
98
let formattedHost = removeOuterSlashes(host)
109
if (formattedHost && !/^http/i.test(formattedHost)) {
1110
formattedHost = `//${formattedHost}`
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/* global test expect */
2+
3+
const ConfigList = require('../config_list')
4+
5+
test('new', () => {
6+
const list = new ConfigList()
7+
expect(list).toBeInstanceOf(ConfigList)
8+
expect(list).toBeInstanceOf(Array)
9+
})
10+
11+
test('set', () => {
12+
const list = new ConfigList()
13+
expect(list.set('key', 'value')).toEqual([{ key: 'key', value: 'value' }])
14+
})
15+
16+
test('get', () => {
17+
const list = new ConfigList()
18+
list.set('key', 'value')
19+
expect(list.get('key')).toEqual('value')
20+
})
21+
22+
test('append', () => {
23+
const list = new ConfigList()
24+
list.set('key', 'value')
25+
expect(list.append('key1', 'value1')).toEqual([
26+
{ key: 'key', value: 'value' },
27+
{ key: 'key1', value: 'value1' }
28+
])
29+
})
30+
31+
test('prepend', () => {
32+
const list = new ConfigList()
33+
list.set('key', 'value')
34+
expect(list.prepend('key1', 'value1')).toEqual([
35+
{ key: 'key1', value: 'value1' },
36+
{ key: 'key', value: 'value' }
37+
])
38+
})
39+
40+
test('insert without position', () => {
41+
const list = new ConfigList()
42+
list.set('key', 'value')
43+
44+
expect(list.insert('key1', 'value1')).toEqual([
45+
{ key: 'key', value: 'value' },
46+
{ key: 'key1', value: 'value1' }
47+
])
48+
49+
expect(list.insert('key2', 'value2')).toEqual([
50+
{ key: 'key', value: 'value' },
51+
{ key: 'key1', value: 'value1' },
52+
{ key: 'key2', value: 'value2' }
53+
])
54+
})
55+
56+
test('insert before an item', () => {
57+
const list = new ConfigList()
58+
list.set('key', 'value')
59+
list.set('key1', 'value1')
60+
61+
expect(list.insert('key2', 'value2', { before: 'key' })).toEqual([
62+
{ key: 'key2', value: 'value2' },
63+
{ key: 'key', value: 'value' },
64+
{ key: 'key1', value: 'value1' }
65+
])
66+
67+
expect(list.insert('key3', 'value3', { before: 'key2' })).toEqual([
68+
{ key: 'key3', value: 'value3' },
69+
{ key: 'key2', value: 'value2' },
70+
{ key: 'key', value: 'value' },
71+
{ key: 'key1', value: 'value1' }
72+
])
73+
})
74+
75+
test('insert after an item', () => {
76+
const list = new ConfigList()
77+
list.set('key', 'value')
78+
list.set('key1', 'value1')
79+
80+
expect(list.insert('key2', 'value2', { after: 'key' })).toEqual([
81+
{ key: 'key', value: 'value' },
82+
{ key: 'key2', value: 'value2' },
83+
{ key: 'key1', value: 'value1' }
84+
])
85+
86+
expect(list.insert('key3', 'value3', { after: 'key2' })).toEqual([
87+
{ key: 'key', value: 'value' },
88+
{ key: 'key2', value: 'value2' },
89+
{ key: 'key3', value: 'value3' },
90+
{ key: 'key1', value: 'value1' }
91+
])
92+
})
93+
94+
test('delete', () => {
95+
const list = new ConfigList()
96+
list.set('key', 'value')
97+
list.set('key1', 'value1')
98+
expect(list.delete('key')).toEqual([{ key: 'key1', value: 'value1' }])
99+
expect(list.delete('key1')).toEqual([])
100+
})
101+
102+
test('getIndex', () => {
103+
const list = new ConfigList()
104+
list.set('key', 'value')
105+
list.set('key1', 'value1')
106+
expect(list.getIndex('key')).toEqual(0)
107+
expect(list.getIndex('key2')).toEqual(-1)
108+
expect(() => list.getIndex('key2', true)).toThrow('Item key2 not found')
109+
})
110+
111+
test('values', () => {
112+
const list = new ConfigList()
113+
list.set('key', 'value')
114+
list.set('key1', 'value1')
115+
expect(list.values()).toEqual(['value', 'value1'])
116+
})
117+
118+
test('keys', () => {
119+
const list = new ConfigList()
120+
list.set('key', 'value')
121+
list.set('key1', 'value1')
122+
expect(list.keys()).toEqual(['key', 'key1'])
123+
})
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* global test expect */
2+
3+
const ConfigObject = require('../config_object')
4+
5+
test('new', () => {
6+
const object = new ConfigObject()
7+
expect(object).toBeInstanceOf(ConfigObject)
8+
expect(object).toBeInstanceOf(Object)
9+
})
10+
11+
test('set', () => {
12+
const object = new ConfigObject()
13+
expect(object.set('key', 'value')).toEqual({ key: 'value' })
14+
})
15+
16+
test('get', () => {
17+
const object = new ConfigObject()
18+
object.set('key', 'value')
19+
object.set('key1', 'value1')
20+
expect(object.get('key')).toEqual('value')
21+
})
22+
23+
test('delete', () => {
24+
const object = new ConfigObject()
25+
object.set('key', { key1: 'value' })
26+
expect(object.delete('key.key1')).toEqual({ key: {} })
27+
expect(object.delete('key')).toEqual({})
28+
})
29+
30+
test('toObject', () => {
31+
const object = new ConfigObject()
32+
object.set('key', 'value')
33+
object.set('key1', 'value1')
34+
expect(object.toObject()).toEqual({ key: 'value', key1: 'value1' })
35+
})
36+
37+
test('merge', () => {
38+
const object = new ConfigObject()
39+
object.set('foo', {})
40+
expect(object.merge({ key: 'foo', value: 'bar' })).toEqual(
41+
{ foo: {}, key: 'foo', value: 'bar' }
42+
)
43+
})

package/config_types/config_list.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @class
3+
* @extends { Array }
4+
*/
5+
class ConfigList extends Array {
6+
get(key) {
7+
const index = this.getIndex(key, true)
8+
return this[index].value
9+
}
10+
11+
set(key, value) {
12+
return this.add({ key, value })
13+
}
14+
15+
append(key, value) {
16+
return this.set(key, value)
17+
}
18+
19+
prepend(key, value) {
20+
return this.add({ key, value }, 'prepend')
21+
}
22+
23+
insert(key, value, pos = {}) {
24+
if (!(pos.before || pos.after)) return this.append(key, value)
25+
26+
const currentIndex = this.getIndex(key)
27+
if (currentIndex >= 0) this.splice(currentIndex, 1)
28+
29+
let newIndex = this.getIndex(pos.before || pos.after)
30+
if (pos.after) newIndex += 1
31+
32+
this.splice(newIndex, 0, { key, value })
33+
return this
34+
}
35+
36+
delete(key) {
37+
const index = this.getIndex(key, true)
38+
this.splice(index, 1)
39+
return this
40+
}
41+
42+
getIndex(key, shouldThrow = false) {
43+
const index = this.findIndex(entry =>
44+
(
45+
entry === key ||
46+
entry.key === key ||
47+
(entry.constructor && entry.constructor.name === key)
48+
)
49+
)
50+
51+
if (shouldThrow && index < 0) throw new Error(`Item ${key} not found`)
52+
return index
53+
}
54+
55+
add({ key, value }, strategy = 'append') {
56+
const index = this.getIndex(key)
57+
if (index >= 0) this.delete(key)
58+
59+
switch (strategy) {
60+
case 'prepend':
61+
this.unshift({ key, value })
62+
break
63+
default:
64+
this.push({ key, value })
65+
}
66+
67+
return this
68+
}
69+
70+
values() {
71+
return this.map(item => item.value)
72+
}
73+
74+
keys() {
75+
return this.map(item => item.key)
76+
}
77+
}
78+
79+
module.exports = ConfigList

package/config_types/config_object.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
const objectify = require('../utils/objectify')
2+
const deepAssign = require('../utils/deep_assign')
3+
const deepMerge = require('../utils/deep_merge')
4+
const { isStrPath, prettyPrint } = require('../utils/helpers')
5+
6+
/**
7+
* @class
8+
* @extends { Object }
9+
*/
10+
class ConfigObject extends Object {
11+
constructor(props = {}) {
12+
super()
13+
this.merge(props)
14+
}
15+
16+
get(key) {
17+
return isStrPath(key) ? objectify(key, this) : this[key]
18+
}
19+
20+
set(key, value) {
21+
Object.assign(this, deepAssign(this, key, value))
22+
return this
23+
}
24+
25+
delete(key) {
26+
let obj = this
27+
let propKey = key
28+
29+
if (isStrPath(key)) {
30+
const keys = key.split('.')
31+
propKey = keys.pop()
32+
const parentObjPath = keys.join('.')
33+
obj = objectify(parentObjPath, this)
34+
}
35+
36+
if (!obj) throw new Error(`Prop not found: ${key} in ${prettyPrint(obj)}`)
37+
delete obj[propKey]
38+
39+
return this
40+
}
41+
42+
toObject() {
43+
const object = {}
44+
Object.keys(this).forEach(key => (object[key] = this[key]))
45+
return object
46+
}
47+
48+
merge(config) {
49+
Object.assign(this, deepMerge(this, config))
50+
return this
51+
}
52+
}
53+
54+
module.exports = ConfigObject

package/config_types/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const ConfigObject = require('./config_object')
2+
const ConfigList = require('./config_list')
3+
4+
module.exports = {
5+
ConfigObject,
6+
ConfigList
7+
}

0 commit comments

Comments
 (0)