Skip to content

Commit

Permalink
Standardizes the plugin's interface. (#87)
Browse files Browse the repository at this point in the history
* Updates plugins to have the same reactotron interface.

* Changes the plugin interface to () => reactotron => {}.

* Removes the useless decorate plugin.

* Adds an npm run e2e for my own sanity.
  • Loading branch information
skellock committed Aug 23, 2016
1 parent 0e9b442 commit 6cd09ea
Show file tree
Hide file tree
Showing 23 changed files with 114 additions and 145 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"clean": "lerna clean",
"test": "lerna run test",
"copy-internal-deps": "lerna run copy-internal-deps",
"welcome": "./scripts/welcome.sh"
"welcome": "./scripts/welcome.sh",
"e2e": "npm run bootstrap && npm run build && npm run copy-internal-deps && npm run test"
},
"devDependencies": {
"lerna": "2.0.0-beta.24"
Expand Down
4 changes: 1 addition & 3 deletions packages/demo-react-native/App/Config/ReactotronConfig.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Reactotron from 'reactotron-react-native'
import decorate from 'reactotron-core-client/plugins/decorator'
import tronsauce from 'reactotron-apisauce'

Reactotron
Expand All @@ -8,6 +7,5 @@ Reactotron
onConnect: () => console.log('connected'),
onDisconnect: () => console.log('disconnected')
})
.use(decorate(console, 'tron'))
.use(tronsauce)
.use(tronsauce())
.connect()
34 changes: 18 additions & 16 deletions packages/reactotron-apisauce/src/index.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import RS from 'ramdasauce'

// apisauce uses axios, so let's deconstruct that format
const convertResponse = (source) => {
const url = RS.dotPath('config.url', source)
const method = RS.dotPath('config.method', source)
const requestData = RS.dotPath('config.data', source)
const requestHeaders = RS.dotPath('config.headers', source)
const duration = RS.dotPath('duration', source)
const status = RS.dotPath('status', source)
const body = RS.dotPath('data', source)
const responseHeaders = RS.dotPath('headers', source)
const request = { url, method, data: requestData, headers: requestHeaders }
const response = { body, status, headers: responseHeaders }

return [ request, response, duration ]
}

/**
* Sends an apisauce response to the server.
*/
export default config => {
export default () => reactotron => {
return {
features: {
apisauce: (source) => {
// apisauce uses axios, so let's deconstruct that format
const url = RS.dotPath('config.url', source)
const method = RS.dotPath('config.method', source)
const requestData = RS.dotPath('config.data', source)
const requestHeaders = RS.dotPath('config.headers', source)
const duration = RS.dotPath('duration', source)
const status = RS.dotPath('status', source)
const body = RS.dotPath('data', source)
const responseHeaders = RS.dotPath('headers', source)
const request = { url, method, data: requestData, headers: requestHeaders }
const response = { body, status, headers: responseHeaders }

config.ref.apiResponse(request, response, duration)
}
apisauce: (source) => reactotron.apiResponse(...convertResponse(source))
}
}
}
15 changes: 7 additions & 8 deletions packages/reactotron-apisauce/test/plugin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ test.cb('parses responses', t => {
// start listening
server.listen(port)

// captured these guys from our fake plugin
let request
let response
let duration

const plugin = createPlugin({
ref: {
apiResponse: (a, b, c) => {
request = a
response = b
duration = c
}
// create a fake plugin to receive the real plugin's functionality
const plugin = createPlugin()({
apiResponse: (a, b, c) => {
request = a
response = b
duration = c
}
})

Expand All @@ -51,6 +51,5 @@ test.cb('parses responses', t => {
t.true(duration > 0)
t.end()
})

})
})
43 changes: 30 additions & 13 deletions packages/reactotron-core-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,20 +380,37 @@ Sent from the client to server when it's time to report some performance details
Reactotron is extensible via plugins. You add plugins by calling the `use`
function on the the client.

A plugin is a function with 1 parameter: a configuration object. It returns an
object.
A plugin looks like this:

The inbound object has two keys:
```js
export default () => reactotron => {}
```

* A function that:
* returns a function with 1 parameter (reactotron) that:
* returns an object


### The 1st Function

You use the first function to configure your plugin. If you don't have any
configuration required for your plugin, just leave it empty like above.

### The 2nd function

The 2nd function gets called with the reactotron object. Among other things,
it contains (most importantly) a function called `send()`.

### The return object

* send - call this function to send messages
* ref - a reference to the client (danger... it's not bound properly, use send instead)
This contains hooks into reactotron. By naming the keys certain things, you're
able to hook into guts to do stuff. Most importantly `onCommand` to receive
events from the server and `features` to define extra functions on reactotron.

Should the object have certain keynames, then those functions will get invoked
at the right time.

```js
// counter-plugin.js
export default config => {
export default () => reactotron => {
let commandCounter = 0
return {
onCommand: command => {
Expand Down Expand Up @@ -426,24 +443,24 @@ Here's what a plugin can do.
onDisconnect: () => {},

// fires when the plugin is attached (this only happens once at initialization)
onPlugin: client => console.log('I have been attached to ', client),
onPlugin: reactotron => console.log('I have been attached to ', reactotron),

// This is an object (not a function). The keys are strings. The values are functions.
// Every entry in here will become a method on the Reactotron client object.
// Collisions are handled on a first-come first-serve basis.
//
// These functions are reserved (sorry): connect, configure, send, use,
// options, connected, plugins, and socket.
// These names are reserved:
// connect, configure, send, use, options, connected, plugins, and socket.
//
// Sorry.
//
// I went with this mixin approach because the interface feels nice from the
// calling code point-of-view.
features: {
// Reacotron.log('hello!')
// Reactotron.log('hello!')
log: (message) => send('log', { level: 'debug', message } ),

// Reacotron.warn('look out! falling rocks!')
// Reactotron.warn('look out! falling rocks!')
warn: (message) => send('log', { level: 'warn', message } ),
}

Expand Down
18 changes: 10 additions & 8 deletions packages/reactotron-core-client/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ const DEFAULTS = {
}

export const CorePlugins = [
logger,
benchmark,
stateResponses,
apiResponse
logger(),
benchmark(),
stateResponses(),
apiResponse()
]

// these are not for you.
Expand All @@ -41,6 +41,11 @@ export class Client {

startTimer = () => start()

constructor () {
// we will be invoking send from callbacks other than inside this file
this.send = this.send.bind(this)
}

/**
* Set the configuration options.
*/
Expand Down Expand Up @@ -123,10 +128,7 @@ export class Client {
if (typeof pluginCreator !== 'function') throw new Error('plugins must be a function')

// execute it immediately passing the send function
const plugin = pluginCreator({
send: this.send.bind(this),
ref: this
})
const plugin = pluginCreator.bind(this)(this)

// ensure we get an Object-like creature back
if (!R.is(Object, plugin)) throw new Error('plugins must return an object')
Expand Down
4 changes: 2 additions & 2 deletions packages/reactotron-core-client/src/plugins/api-response.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* Sends API request/response information.
*/
export default config => {
export default () => reactotron => {
return {
features: {
apiResponse: (request, response, duration) =>
config.send('api.response', { request, response, duration })
reactotron.send('api.response', { request, response, duration })
}
}
}
6 changes: 3 additions & 3 deletions packages/reactotron-core-client/src/plugins/benchmark.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* Runs small high-unscientific benchmarks for you.
*/
export default ({ send, ref }) => {
const { startTimer } = ref
export default () => reactotron => {
const { startTimer } = reactotron

const benchmark = title => {
const steps = []
Expand All @@ -11,7 +11,7 @@ export default ({ send, ref }) => {
steps.push({ title, time: 0 })
const stop = stopTitle => {
step(stopTitle)
send('benchmark.report', { title, steps })
reactotron.send('benchmark.report', { title, steps })
}
return { step, stop }
}
Expand Down
22 changes: 0 additions & 22 deletions packages/reactotron-core-client/src/plugins/decorator.js

This file was deleted.

10 changes: 5 additions & 5 deletions packages/reactotron-core-client/src/plugins/logger.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/**
* Provides 4 features for logging. log & debug are the same.
*/
export default ({ send }) => {
export default () => reactotron => {
return {
features: {
log: (message) => send('log', { level: 'debug', message }),
debug: (message) => send('log', { level: 'debug', message }),
warn: (message) => send('log', { level: 'warn', message }),
error: (message) => send('log', { level: 'error', message })
log: (message) => reactotron.send('log', { level: 'debug', message }),
debug: (message) => reactotron.send('log', { level: 'debug', message }),
warn: (message) => reactotron.send('log', { level: 'warn', message }),
error: (message) => reactotron.send('log', { level: 'error', message })
}
}
}
10 changes: 5 additions & 5 deletions packages/reactotron-core-client/src/plugins/state-responses.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/**
* Provides helper functions for send state responses.
*/
export default config => {
export default () => reactotron => {
return {
features: {
stateActionComplete: (name, action) =>
config.send('state.action.complete', { name, action }),
reactotron.send('state.action.complete', { name, action }),

stateValuesResponse: (path, value, valid = true) =>
config.send('state.values.response', { path, value, valid }),
reactotron.send('state.values.response', { path, value, valid }),

stateKeysResponse: (path, keys, valid = true) =>
config.send('state.keys.response', { path, keys, valid }),
reactotron.send('state.keys.response', { path, keys, valid }),

stateValuesChange: changes =>
config.send('state.values.change', { changes })
reactotron.send('state.values.change', { changes })
}
}
}
8 changes: 4 additions & 4 deletions packages/reactotron-core-client/test/api-response-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test('stateActionComplete', t => {
name = y.name
action = y.action
}
client.use(plugin)
client.use(plugin())
t.is(client.plugins.length, 1)
t.is(typeof client.stateActionComplete, 'function')
client.stateActionComplete('name', { action: 123 })
Expand All @@ -34,7 +34,7 @@ test('stateValuesResponse', t => {
value = y.value
valid = y.valid
}
client.use(plugin)
client.use(plugin())
t.is(client.plugins.length, 1)
t.is(typeof client.stateValuesResponse, 'function')
client.stateValuesResponse('user.password', 'password', false)
Expand All @@ -56,7 +56,7 @@ test('stateKeysResponse', t => {
keys = y.keys
valid = y.valid
}
client.use(plugin)
client.use(plugin())
t.is(client.plugins.length, 1)
t.is(typeof client.stateKeysResponse, 'function')
client.stateKeysResponse('user', ['name', 'password'], false)
Expand All @@ -74,7 +74,7 @@ test('stateValuesChange', t => {
type = x
changes = y.changes
}
client.use(plugin)
client.use(plugin())
t.is(client.plugins.length, 1)
t.is(typeof client.stateValuesChange, 'function')
client.stateValuesChange([{ path: 'a', value: 1 }, { path: 'b', value: 2 }])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test('adds benchmarks to a report', async t => {
}

// register
client.use(plugin)
client.use(plugin())

t.is(client.plugins.length, 1)
t.is(typeof client.benchmark, 'function')
Expand Down
28 changes: 0 additions & 28 deletions packages/reactotron-core-client/test/plugin-decorator-test.js

This file was deleted.

Loading

0 comments on commit 6cd09ea

Please sign in to comment.