Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standardizes the plugin's interface. #87

Merged
merged 4 commits into from
Aug 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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