<commit_before>const assert = require('assert')

const { Recorder, Player } = require('transcript')

const FileSystem = require('b-tree/filesystem')
const WriteAheadOnly = require('b-tree/writeahead')
const Amalgamator = require('./amalgamate')

const Locker = require('./locker')
const Future = require('perhaps')
//

// Rotator and Locker are separate so we can unit test the MVCC locking logic
// synchrnously, separately.

//
class Rotator {
    // **TODO** No need to double checksum the write-ahead log. Starting to feel
    // like the write-ahead log should return an array of buffers, it should do
    // the partitioning for you, because we're doing it twice. Once in the log
    // and once it comes out of the log.
    constructor (destructible, { writeahead, mutations, version }, { size = 1024 * 1024 } = {}) {
        this.locker = new Locker({ mutations, version, size })
        this.destructible = destructible
        this.deferrable = destructible.durable($ => $(), { countdown: 1 }, 'deferrable')
        this.deferrable.destruct(() => this.locker.destroy())
        this.destructible.destruct(() => this.deferrable.decrement())
        this._destructible = {
            amalgamators: this.deferrable.durable('amalgamators')
        }
        this._amalgamators = new Map
        this._writeahead = writeahead
        this._zeroed = Future.resolve()
        this._rotated = Future.resolve()
        this._recorder = Recorder.create(() => '0')
        this.deferrable.durable($ => $(), 'rotate', this._rotate())
        writeahead.deferrable.increment()
        this.deferrable.destruct(() => {
            this.deferrable.ephemeral('shutdown', async () => {
                await this._destructible.amalgamators.done
                writeahead.deferrable.decrement()
            })
        })
    }

    static async open (writeahead) {
        const player = new Player(() => '0')
        const mutations = new Map
        let version = 0
        for await (const buffer of writeahead.get('commit')) {
            for (const entry of player.split(buffer)) {
                const mutation = {
                    version: JSON.parse(entry.parts.shift()),
                    completed: 0,
                    rolledback: false
                }
                mutations.set(mutation.version, mutation)
                version = Math.max(version, mutation.version)
            }
        }
        return { writeahead, mutations, version }
    }

    async open (name, { directory, handles, create, key, serializer, checksum, extractor }, options) {
        const open = {
            key: key,
            directory: directory,
            storage: await FileSystem.open({ handles, directory, create, serializer, checksum, extractor }),
            stages: [],
            options: {
                serializer: {
                    key: {
                        serialize: (key) => {
                            const [ value, version, order ] = key
                            assert(value != null)
                            const header = { version, order }
                            const buffer = Buffer.from(JSON.stringify(header))
                            return [ buffer ].concat(serializer.key.serialize(value))
                        },
                        deserialize: (parts) => {
                            const { version, order } = JSON.parse(parts[0].toString())
                            return [
                                serializer.key.deserialize(parts.slice(1)),
                                version, order
                            ]
                        }
                    },
                    parts: {
                        serialize: (parts) => {
                            const header = Buffer.from(JSON.stringify(parts[0]))
                            if (parts[0].method == 'insert') {
                                return [ header ].concat(serializer.parts.serialize(parts.slice(1)))
                            }
                            return [ header ].concat(serializer.key.serialize(parts[1]))
                        },
                        deserialize: (parts) => {
                            const header = JSON.parse(parts[0].toString())
                            if (header.method == 'insert') {
                                // TODO You do want to merge the arrays when you serialize, but do
                                // you want to do so when you deserialize? Can't the users parts be
                                // in array by themselves so you don't have to slice to pass them
                                // around? We had a bug where we where doing this here with key
                                // below which was a bug when the user was using an array key.
                                //
                                // The user will always use array parts, that is the parts
                                // deserializer always returns an array, but the key serializer can
                                // return a scalar.
                                return [ header ].concat(serializer.parts.deserialize(parts.slice(1)))
                            }
                            return [ header, serializer.key.deserialize(parts.slice(1)) ]
                        }
                    }
                },
                extractor: (parts) => {
                    if (parts[0].method == 'insert') {
                        return [
                            extractor(parts.slice(1)),
                            parts[0].version,
                            parts[0].order
                        ]
                    }
                    return [ parts[1], parts[0].version, parts[0].order ]
                }
            }
        }
        for await (const entries of WriteAheadOnly.wal.iterator(this._writeahead, [ 'locate' ], key)) {
            for (const entry of entries) {
                const key = entry.header.value
                const storage = await WriteAheadOnly.open({ writeahead: this._writeahead, key, ...open.options })
                open.stages.push({
                    name: `stage.${key[1]}`,
                    group: this.locker.latest,
                    count: 0,
                    appending: false,
                    storage: storage
                })
            }
        }
        open.stages.reverse()
        if (open.stages.length == 0) {
            open.stages.push({
                name: `stage.${this.locker.latest}`,
                group: this.locker.latest,
                count: 0,
                key: this.locker.latest,
                appending: true,
                storage: await WriteAheadOnly.open({
                    ...open.options,
                    writeahead: this._writeahead,
                    key: [ key, 0 ],
                    create: [ [ 'locate' ], key ]
                })
            })
        } else {
            open.stages[0].appending = true
        }
        const amalgamator = new Amalgamator(this._destructible.amalgamators.durable(name), this, open, options)
        this._amalgamators.set(amalgamator, { key, options: open.options })
        return amalgamator
    }

    advance () {
        this.locker.advance(this._writeahead.position)
    }

    commit (version) {
        return this._writeahead.write([{
            keys: [ 'commit' ],
            buffer: (this._recorder)([[ Buffer.from(JSON.stringify(version)) ]])
        }], true)
    }

    async _rotate (group) {
        for (;;) {
            const group = await this.locker.rotating.promise
            if (group == null) {
                break
            }
            this.deferrable.increment()
            await this._writeahead.rotate().promise
            for (const [ amalgamator, { key, options } ]  of this._amalgamators) {
                const storage = await WriteAheadOnly.open({
                    ...options,
                    writeahead: this._writeahead,
                    key: [ key, group ],
                    create: [ [ 'locate' ], key ]
                })
                amalgamator.rotate({
                    name: `stage.${group}`,
                    group: group,
                    count: 0,
                    key: group,
                    storage: storage,
                    appending: true
                })
            }
            this.locker.rotated()
            await this.locker.amalgamating.promise
            const mutator = { created: this.locker._completed  }
            for (const amalgamator of this._amalgamators.keys()) {
                await amalgamator.amalgamate(mutator)
            }
            this.locker.amalgamated()
            await this.locker.unstaging.promise
            for (const amalgamator of this._amalgamators.keys()) {
                this.destructible.progress()
                await amalgamator.unstage()
            }
            while (this._writeahead._logs.length != 1) {
                await this._writeahead.shift().promise
            }
            this.locker.unstaged()
            this.deferrable.decrement()
        }
    }

    _drain () {
        return ([ ...this._amalgamators.keys() ])
            .map(amalgamator => amalgamator.drain())
            .filter(drain => drain != null)
    }

    drain () {
        throw new Error('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
        let drains = this._drain()
        if (drains.length != 0) {
            return (async () => {
                do {
                    for (const promise of drains) {
                        await promise
                    }
                    drains = this._drain()
                } while (drains.length != 0)
            }) ()
        }
        return null
    }

    done () {
        return this.destructible.done.promise
    }
}

module.exports = Rotator
<commit_msg>Explicitly check for deferrable destruction.<commit_after>const assert = require('assert')

const { Recorder, Player } = require('transcript')

const FileSystem = require('b-tree/filesystem')
const WriteAheadOnly = require('b-tree/writeahead')
const Amalgamator = require('./amalgamate')

const Locker = require('./locker')
const Future = require('perhaps')
//

// Rotator and Locker are separate so we can unit test the MVCC locking logic
// synchrnously, separately.

//
class Rotator {
    // **TODO** No need to double checksum the write-ahead log. Starting to feel
    // like the write-ahead log should return an array of buffers, it should do
    // the partitioning for you, because we're doing it twice. Once in the log
    // and once it comes out of the log.
    constructor (destructible, { writeahead, mutations, version }, { size = 1024 * 1024 } = {}) {
        this.locker = new Locker({ mutations, version, size })
        this.destructible = destructible
        this.deferrable = destructible.durable($ => $(), { countdown: 1 }, 'deferrable')
        this.deferrable.destruct(() => this.locker.destroy())
        this.destructible.destruct(() => this.deferrable.decrement())
        this._destructible = {
            amalgamators: this.deferrable.durable('amalgamators')
        }
        this._amalgamators = new Map
        this._writeahead = writeahead
        this._zeroed = Future.resolve()
        this._rotated = Future.resolve()
        this._recorder = Recorder.create(() => '0')
        this.deferrable.durable($ => $(), 'rotate', this._rotate())
        writeahead.deferrable.increment()
        this.deferrable.destruct(() => {
            this.deferrable.ephemeral('shutdown', async () => {
                await this._destructible.amalgamators.done
                writeahead.deferrable.decrement()
            })
        })
    }

    static async open (writeahead) {
        const player = new Player(() => '0')
        const mutations = new Map
        let version = 0
        for await (const buffer of writeahead.get('commit')) {
            for (const entry of player.split(buffer)) {
                const mutation = {
                    version: JSON.parse(entry.parts.shift()),
                    completed: 0,
                    rolledback: false
                }
                mutations.set(mutation.version, mutation)
                version = Math.max(version, mutation.version)
            }
        }
        return { writeahead, mutations, version }
    }

    async open (name, { directory, handles, create, key, serializer, checksum, extractor }, options) {
        const open = {
            key: key,
            directory: directory,
            storage: await FileSystem.open({ handles, directory, create, serializer, checksum, extractor }),
            stages: [],
            options: {
                serializer: {
                    key: {
                        serialize: (key) => {
                            const [ value, version, order ] = key
                            assert(value != null)
                            const header = { version, order }
                            const buffer = Buffer.from(JSON.stringify(header))
                            return [ buffer ].concat(serializer.key.serialize(value))
                        },
                        deserialize: (parts) => {
                            const { version, order } = JSON.parse(parts[0].toString())
                            return [
                                serializer.key.deserialize(parts.slice(1)),
                                version, order
                            ]
                        }
                    },
                    parts: {
                        serialize: (parts) => {
                            const header = Buffer.from(JSON.stringify(parts[0]))
                            if (parts[0].method == 'insert') {
                                return [ header ].concat(serializer.parts.serialize(parts.slice(1)))
                            }
                            return [ header ].concat(serializer.key.serialize(parts[1]))
                        },
                        deserialize: (parts) => {
                            const header = JSON.parse(parts[0].toString())
                            if (header.method == 'insert') {
                                // TODO You do want to merge the arrays when you serialize, but do
                                // you want to do so when you deserialize? Can't the users parts be
                                // in array by themselves so you don't have to slice to pass them
                                // around? We had a bug where we where doing this here with key
                                // below which was a bug when the user was using an array key.
                                //
                                // The user will always use array parts, that is the parts
                                // deserializer always returns an array, but the key serializer can
                                // return a scalar.
                                return [ header ].concat(serializer.parts.deserialize(parts.slice(1)))
                            }
                            return [ header, serializer.key.deserialize(parts.slice(1)) ]
                        }
                    }
                },
                extractor: (parts) => {
                    if (parts[0].method == 'insert') {
                        return [
                            extractor(parts.slice(1)),
                            parts[0].version,
                            parts[0].order
                        ]
                    }
                    return [ parts[1], parts[0].version, parts[0].order ]
                }
            }
        }
        for await (const entries of WriteAheadOnly.wal.iterator(this._writeahead, [ 'locate' ], key)) {
            for (const entry of entries) {
                const key = entry.header.value
                const storage = await WriteAheadOnly.open({ writeahead: this._writeahead, key, ...open.options })
                open.stages.push({
                    name: `stage.${key[1]}`,
                    group: this.locker.latest,
                    count: 0,
                    appending: false,
                    storage: storage
                })
            }
        }
        open.stages.reverse()
        if (open.stages.length == 0) {
            open.stages.push({
                name: `stage.${this.locker.latest}`,
                group: this.locker.latest,
                count: 0,
                key: this.locker.latest,
                appending: true,
                storage: await WriteAheadOnly.open({
                    ...open.options,
                    writeahead: this._writeahead,
                    key: [ key, 0 ],
                    create: [ [ 'locate' ], key ]
                })
            })
        } else {
            open.stages[0].appending = true
        }
        const amalgamator = new Amalgamator(this._destructible.amalgamators.durable(name), this, open, options)
        this._amalgamators.set(amalgamator, { key, options: open.options })
        return amalgamator
    }

    advance () {
        this.locker.advance(this._writeahead.position)
    }

    commit (version) {
        return this._writeahead.write([{
            keys: [ 'commit' ],
            buffer: (this._recorder)([[ Buffer.from(JSON.stringify(version)) ]])
        }], true)
    }

    async _rotate (group) {
        for (;;) {
            const group = await this.locker.rotating.promise
            if (group == null || this.deferrable.destroyed) {
                break
            }
            this.deferrable.increment()
            await this._writeahead.rotate().promise
            for (const [ amalgamator, { key, options } ]  of this._amalgamators) {
                const storage = await WriteAheadOnly.open({
                    ...options,
                    writeahead: this._writeahead,
                    key: [ key, group ],
                    create: [ [ 'locate' ], key ]
                })
                amalgamator.rotate({
                    name: `stage.${group}`,
                    group: group,
                    count: 0,
                    key: group,
                    storage: storage,
                    appending: true
                })
            }
            this.locker.rotated()
            await this.locker.amalgamating.promise
            const mutator = { created: this.locker._completed  }
            for (const amalgamator of this._amalgamators.keys()) {
                await amalgamator.amalgamate(mutator)
            }
            this.locker.amalgamated()
            await this.locker.unstaging.promise
            for (const amalgamator of this._amalgamators.keys()) {
                this.destructible.progress()
                await amalgamator.unstage()
            }
            while (this._writeahead._logs.length != 1) {
                await this._writeahead.shift().promise
            }
            this.locker.unstaged()
            this.deferrable.decrement()
        }
    }

    _drain () {
        return ([ ...this._amalgamators.keys() ])
            .map(amalgamator => amalgamator.drain())
            .filter(drain => drain != null)
    }

    drain () {
        throw new Error('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
        let drains = this._drain()
        if (drains.length != 0) {
            return (async () => {
                do {
                    for (const promise of drains) {
                        await promise
                    }
                    drains = this._drain()
                } while (drains.length != 0)
            }) ()
        }
        return null
    }

    done () {
        return this.destructible.done.promise
    }
}

module.exports = Rotator
<|endoftext|>


<commit_before>import forEach from 'lodash.foreach';
import hoistStatics from 'hoist-non-react-statics';
import invariant from 'invariant';
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';

import removeConnector from '../actions/remove-connector';

let ID_COUNTER = 0;

// getBundle is a function which receives the component props
// and retrieves a configuration set by the user
// e.g
// (props) => ({
//   fragments: {
//     pages: {
//       _id: 1,
//       title: 1
//     },
//     page: {
//       _id: 1,
//       title: 1,
//       user: {
//         _id: 1,
//         username: 1
//       }
//     }
//   },
//   variablesTypes: {
//     pages: {
//       order: 'String!',
//       sort: 'String!'
//     }
//   },
//   initialVariables: {
//     pages: {
//       order: 'asc',
//       sort: 'date'
//     }
//   },
//   mutations: {
//     addPage: [
//       {
//         type: 'APPEND', // INCREMENT || DECREMENT || APPEND || PREPEND
//         field: 'pages'
//       },
//       {
//         type: 'INCREMENT',
//         field: 'pagesCount'
//       },
//       {
//         type: 'INCREMENT',
//         field: ['pages', 'pagesCount']
//       }
//     ]
//   }
// })
export default function dataConnect (...args) {
  invariant(args.length, 'Relate: a dataConnect does not have arguments specified');

  const getReduxState = args.length > 1 && args[0];
  const getReduxDispatches = args.length > 2 && args[1];
  const getBundle = args[args.length - 1];

  // Connector id
  const CONNECTOR_ID = `connector_${ID_COUNTER++}`;

  return function wrapWithDataConnect (WrappedComponent) {
    class ConnectData extends Component {
      static propTypes = {
        relateConnectorData: PropTypes.object
      };

      static contextTypes = {
        fetchData: PropTypes.func.isRequired,
        store: PropTypes.any.isRequired
      };

      static defaultProps = {
        relateConnectorData: {}
      };

      constructor (props, context) {
        super(props, context);

        const initialBundle = getBundle && getBundle(this.props);

        // Relate connector info
        this.variablesTypes = initialBundle && initialBundle.variablesTypes || {};
        this.relate = {
          setVariables: ::this.setVariables,
          refresh: ::this.refresh,
          variables: initialBundle && initialBundle.initialVariables
        };

        // Fetch data
        if (initialBundle) {
          this.fetchData({
            fragments: initialBundle.fragments,
            variables: initialBundle.initialVariables,
            mutations: initialBundle.mutations
          });
        }

        // Set initial state
        this.state = {
          loading: true
        };
      }

      componentWillUnmount () {
        this.context.store.dispatch(removeConnector(CONNECTOR_ID));
      }

      setVariables (variables) {
        const bundle = getBundle && getBundle(this.props);
        this.relate.variables = variables;

        // Fetch data
        if (bundle) {
          this.fetchData({
            fragments: bundle.fragments,
            variables,
            mutations: bundle.mutations
          });
        }
      }

      getVariables (variables, fragments) {
        const resultVariables = {};
        if (variables) {
          forEach(variables, (vars, queryName) => {
            if (fragments[queryName]) { // if not in fragments, ignore
              resultVariables[queryName] = {};
              const queryVariablesTypes = this.variablesTypes[queryName];

              // No variables types defined for this query
              invariant(
                queryVariablesTypes,
                'Relate: Query to %s doesn\'t have variables types defined in %s!',
                queryName,
                WrappedComponent.displayName || 'a component'
              );

              // Check if every variable has a type
              forEach(vars, (value, variable) => {
                invariant(
                  queryVariablesTypes[variable],
                  'Relate: Query to %s does not have variable "%s" type defined in %s!',
                  queryName,
                  variable,
                  WrappedComponent.displayName || 'a component'
                );

                // add variable prepared for query e.g. {type: 'String', value: 'something'}
                resultVariables[queryName][variable] = {
                  type: queryVariablesTypes[variable],
                  value
                };
              });

              // Check if every required variable type is met
              forEach(queryVariablesTypes, (type, variable) => {
                invariant(
                  type.slice(-1) !== '!' || vars[variable],
                  'Relate: Query to %s requires the variable "%s" in %s!',
                  queryName,
                  variable,
                  WrappedComponent.displayName || 'a component'
                );
              });
            }
          });
        }
        return resultVariables;
      }

      refresh (props = this.props) {
        const bundle = getBundle && getBundle(props);

        // Fetch data
        if (bundle) {
          this.fetchData({
            fragments: bundle.fragments,
            variables: bundle.initialVariables,
            mutations: bundle.mutations
          });
        }
      }

      fetchData ({fragments, variables, mutations}) {
        const {fetchData} = this.context;

        if (fetchData && typeof fragments === 'object' && Object.keys(fragments).length) {
          fetchData({
            fragments,
            variables: this.getVariables(variables, fragments),
            ID: CONNECTOR_ID,
            mutations
          })
            .then(() => {
              this.setState({
                loading: false
              });
            });
        }
      }

      render () {
        const {relateConnectorData, ...otherProps} = this.props;
        return (
          <WrappedComponent
            {...otherProps}
            {...relateConnectorData}
            relate={this.relate}
            loading={this.state.loading}
          />
        );
      }
    }

    const Connected = connect(
      (state, props) => Object.assign(
        getReduxState && getReduxState(state, props) || {},
        {
          relateConnectorData: state.relateReducer[CONNECTOR_ID]
        }
      ),
      (dispatch) => Object.assign(
        getReduxDispatches && getReduxDispatches(dispatch) || {},
        {
          removeConnector: dispatch(removeConnector)
        }
      )
    )(ConnectData);

    return hoistStatics(Connected, WrappedComponent);
  };
}
<commit_msg>set loading when setting variables or refresh<commit_after>import forEach from 'lodash.foreach';
import hoistStatics from 'hoist-non-react-statics';
import invariant from 'invariant';
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';

import removeConnector from '../actions/remove-connector';

let ID_COUNTER = 0;

// getBundle is a function which receives the component props
// and retrieves a configuration set by the user
// e.g
// (props) => ({
//   fragments: {
//     pages: {
//       _id: 1,
//       title: 1
//     },
//     page: {
//       _id: 1,
//       title: 1,
//       user: {
//         _id: 1,
//         username: 1
//       }
//     }
//   },
//   variablesTypes: {
//     pages: {
//       order: 'String!',
//       sort: 'String!'
//     }
//   },
//   initialVariables: {
//     pages: {
//       order: 'asc',
//       sort: 'date'
//     }
//   },
//   mutations: {
//     addPage: [
//       {
//         type: 'APPEND', // INCREMENT || DECREMENT || APPEND || PREPEND
//         field: 'pages'
//       },
//       {
//         type: 'INCREMENT',
//         field: 'pagesCount'
//       },
//       {
//         type: 'INCREMENT',
//         field: ['pages', 'pagesCount']
//       }
//     ]
//   }
// })
export default function dataConnect (...args) {
  invariant(args.length, 'Relate: a dataConnect does not have arguments specified');

  const getReduxState = args.length > 1 && args[0];
  const getReduxDispatches = args.length > 2 && args[1];
  const getBundle = args[args.length - 1];

  // Connector id
  const CONNECTOR_ID = `connector_${ID_COUNTER++}`;

  return function wrapWithDataConnect (WrappedComponent) {
    class ConnectData extends Component {
      static propTypes = {
        relateConnectorData: PropTypes.object
      };

      static contextTypes = {
        fetchData: PropTypes.func.isRequired,
        store: PropTypes.any.isRequired
      };

      static defaultProps = {
        relateConnectorData: {}
      };

      constructor (props, context) {
        super(props, context);

        const initialBundle = getBundle && getBundle(this.props);

        // Relate connector info
        this.variablesTypes = initialBundle && initialBundle.variablesTypes || {};
        this.relate = {
          setVariables: ::this.setVariables,
          refresh: ::this.refresh,
          variables: initialBundle && initialBundle.initialVariables
        };

        // Fetch data
        if (initialBundle) {
          this.fetchData({
            fragments: initialBundle.fragments,
            variables: initialBundle.initialVariables,
            mutations: initialBundle.mutations
          });
        }

        // Set initial state
        this.state = {
          loading: true
        };
      }

      componentWillUnmount () {
        this.context.store.dispatch(removeConnector(CONNECTOR_ID));
      }

      getVariables (variables, fragments) {
        const resultVariables = {};
        if (variables) {
          forEach(variables, (vars, queryName) => {
            if (fragments[queryName]) { // if not in fragments, ignore
              resultVariables[queryName] = {};
              const queryVariablesTypes = this.variablesTypes[queryName];

              // No variables types defined for this query
              invariant(
                queryVariablesTypes,
                'Relate: Query to %s doesn\'t have variables types defined in %s!',
                queryName,
                WrappedComponent.displayName || 'a component'
              );

              // Check if every variable has a type
              forEach(vars, (value, variable) => {
                invariant(
                  queryVariablesTypes[variable],
                  'Relate: Query to %s does not have variable "%s" type defined in %s!',
                  queryName,
                  variable,
                  WrappedComponent.displayName || 'a component'
                );

                // add variable prepared for query e.g. {type: 'String', value: 'something'}
                resultVariables[queryName][variable] = {
                  type: queryVariablesTypes[variable],
                  value
                };
              });

              // Check if every required variable type is met
              forEach(queryVariablesTypes, (type, variable) => {
                invariant(
                  type.slice(-1) !== '!' || vars[variable],
                  'Relate: Query to %s requires the variable "%s" in %s!',
                  queryName,
                  variable,
                  WrappedComponent.displayName || 'a component'
                );
              });
            }
          });
        }
        return resultVariables;
      }

      setVariables (variables) {
        const bundle = getBundle && getBundle(this.props);
        this.relate.variables = variables;

        // Fetch data
        if (bundle) {
          this.setState({
            loading: true
          }, () => {
            this.fetchData({
              fragments: bundle.fragments,
              variables,
              mutations: bundle.mutations
            });
          });
        }
      }

      refresh (props = this.props) {
        const bundle = getBundle && getBundle(props);

        // Fetch data
        if (bundle) {
          this.setState({
            loading: true
          }, () => {
            this.fetchData({
              fragments: bundle.fragments,
              variables: bundle.initialVariables,
              mutations: bundle.mutations
            });
          });
        }
      }

      fetchData ({fragments, variables, mutations}) {
        const {fetchData} = this.context;

        if (fetchData && typeof fragments === 'object' && Object.keys(fragments).length) {
          fetchData({
            fragments,
            variables: this.getVariables(variables, fragments),
            ID: CONNECTOR_ID,
            mutations
          })
            .then(() => {
              this.setState({
                loading: false
              });
            });
        }
      }

      render () {
        const {relateConnectorData, ...otherProps} = this.props;
        return (
          <WrappedComponent
            {...otherProps}
            {...relateConnectorData}
            relate={this.relate}
            loading={this.state.loading}
          />
        );
      }
    }

    const Connected = connect(
      (state, props) => Object.assign(
        getReduxState && getReduxState(state, props) || {},
        {
          relateConnectorData: state.relateReducer[CONNECTOR_ID]
        }
      ),
      (dispatch) => Object.assign(
        getReduxDispatches && getReduxDispatches(dispatch) || {},
        {
          removeConnector: dispatch(removeConnector)
        }
      )
    )(ConnectData);

    return hoistStatics(Connected, WrappedComponent);
  };
}
<|endoftext|>


<commit_before>let routes = {
    "/gist/:gistID": handleGist,
};

function handleGist(gistID) {
    $("#file_drop").hide();
    let url = "https://api.github.com/gists/" + gistID;
    $("body").addClass("loading");
    $.getJSON(url, parseJSON);
}

function parseJSON(data) {
    let files = data.files;
    let result;
    for (let fileKey in files) {
        result = files[fileKey].content;
    }
    $("body").removeClass("loading");
    plotLog(parseLog(result));
}

let router = Router(routes);
router.init();<commit_msg>hide select<commit_after>let routes = {
    "/gist/:gistID": handleGist,
};

function handleGist(gistID) {
    $("#file_drop").hide();
    $("#file_select").hide();
    let url = "https://api.github.com/gists/" + gistID;
    $("body").addClass("loading");
    $.getJSON(url, parseJSON);
}

function parseJSON(data) {
    let files = data.files;
    let result;
    for (let fileKey in files) {
        result = files[fileKey].content;
    }
    $("body").removeClass("loading");
    plotLog(parseLog(result));
}

let router = Router(routes);
router.init();<|endoftext|>


<commit_before>Router.configure({
	layoutTemplate: 'layout',
	notFoundTemplate: 'notFound',
	loadingTemplate: 'loading',
});

webpagename = 'Openki - Course Organisation Platform - '  // global (document title init)

Router.map(function () {
	this.route('locationDetails',{							///////// locationdetails /////////
		path: 'locations/:_id',
		template: 'location_details',
		waitOn: function () {
			return Meteor.subscribe('locations');
		},
		data: function () {
			return Locations.findOne({_id: this.params._id})
		},
		onAfterAction: function() {
			var location = Locations.findOne({_id: this.params._id})
			if (!location) return; // wtf
			document.title = webpagename + 'Location: ' + location.name
		}
	})

	this.route('categorylist',{								///////// categories /////////
		waitOn: function () {
			return Meteor.subscribe('categories');     // TODO: Anchor tags don't work anyway
		},
		onAfterAction: function() {
			document.title = webpagename + 'Category list'
		}
	})

	this.route('pages', {									///////// static /////////
		path: 'page/:page_name',
		action: function() {
			this.render(this.params.page_name)
		},
		onAfterAction: function() {
			document.title = webpagename + '' + this.params.page_name
		}
	})

	this.route('proposeCourse', {							///////// propose /////////
		path: 'courses/propose',
		template: 'proposecourse',
		waitOn: function () {
			return Meteor.subscribe('categories');
		},
		onAfterAction: function() {
			document.title = webpagename + 'Propose new course'
		}
	})

	this.route('createCourse', {							///////// create /////////
		path: 'courses/create',
		template: 'createcourse',
		waitOn: function () {
			return Meteor.subscribe('categories');
		},
		onAfterAction: function() {
			document.title = webpagename + 'Create new course'
		}
	})

});

Router.map(function () {
	this.route('showEvent', {
		path: 'event/:_id',
		template: 'eventPage',
		waitOn: function () {
			return [
				Meteor.subscribe('categories'),
				Meteor.subscribe('event', this.params._id)
			]
		},
		data: function () {
			var event;
			var create = 'create' == this.params._id;
			if (create) {
				var propose = moment().add(1, 'week').startOf('hour');
				event = {
					new: true,
					startdate: propose.toDate(),
					enddate: moment(propose).add(2, 'hour').toDate(),
					course_id: this.params.query.courseId
				};
			} else {
				event = Events.findOne({_id: this.params._id});
				if (!event) return {};
			}

			return event;
		}
	})
});

<commit_msg>preset course name<commit_after>Router.configure({
	layoutTemplate: 'layout',
	notFoundTemplate: 'notFound',
	loadingTemplate: 'loading',
});

webpagename = 'Openki - Course Organisation Platform - '  // global (document title init)

Router.map(function () {
	this.route('locationDetails',{							///////// locationdetails /////////
		path: 'locations/:_id',
		template: 'location_details',
		waitOn: function () {
			return Meteor.subscribe('locations');
		},
		data: function () {
			return Locations.findOne({_id: this.params._id})
		},
		onAfterAction: function() {
			var location = Locations.findOne({_id: this.params._id})
			if (!location) return; // wtf
			document.title = webpagename + 'Location: ' + location.name
		}
	})

	this.route('categorylist',{								///////// categories /////////
		waitOn: function () {
			return Meteor.subscribe('categories');     // TODO: Anchor tags don't work anyway
		},
		onAfterAction: function() {
			document.title = webpagename + 'Category list'
		}
	})

	this.route('pages', {									///////// static /////////
		path: 'page/:page_name',
		action: function() {
			this.render(this.params.page_name)
		},
		onAfterAction: function() {
			document.title = webpagename + '' + this.params.page_name
		}
	})

	this.route('proposeCourse', {							///////// propose /////////
		path: 'courses/propose',
		template: 'proposecourse',
		waitOn: function () {
			return Meteor.subscribe('categories');
		},
		onAfterAction: function() {
			document.title = webpagename + 'Propose new course'
		}
	})

	this.route('createCourse', {							///////// create /////////
		path: 'courses/create',
		template: 'createcourse',
		waitOn: function () {
			return Meteor.subscribe('categories');
		},
		onAfterAction: function() {
			document.title = webpagename + 'Create new course'
		}
	})

});

Router.map(function () {
	this.route('showEvent', {
		path: 'event/:_id',
		template: 'eventPage',
		waitOn: function () {
			var subs = [
				Meteor.subscribe('categories'),
				Meteor.subscribe('event', this.params._id)
			]
			var courseId = this.params.query.courseId;
			if (courseId) {
				subs.push(Meteor.subscribe('courseDetails', courseId));
			}
			return subs;
		},
		data: function () {
			var event;
			var create = 'create' == this.params._id;
			if (create) {
				var propose = moment().add(1, 'week').startOf('hour');
				event = {
					new: true,
					startdate: propose.toDate(),
					enddate: moment(propose).add(2, 'hour').toDate(),
				};
				var course = Courses.findOne(this.params.query.courseId);
				if (course) {
					event.title = course.name,
					event.course_id = course._id;
				}
			} else {
				event = Events.findOne({_id: this.params._id});
				if (!event) return {};
			}

			return event;
		}
	})
});
<|endoftext|>


<commit_before>module.exports = {
  description: '',

  // locals: function(options) {
  //   // Return custom template variables here.
  //   return {
  //     foo: options.entity.options.foo
  //   };
  // }

  afterInstall: function(options) {
    return this.addBowerPackageToProject('add-to-homescreen');
  }
};
<commit_msg>Don't require app name for installation blueprint<commit_after>module.exports = {
  description: 'Installs ember-add-to-homescreen dependencies',

  normalizeEntityName: function() {},

  afterInstall: function(options) {
    return this.addBowerPackageToProject('add-to-homescreen');
  }
};
<|endoftext|>


<commit_before>import { toString } from 'ember-utils';
import { assert, Error as EmberError } from 'ember-debug';
import { getPossibleMandatoryProxyValue, _getPath as getPath } from './property_get';
import {
  notifyPropertyChange
} from './property_events';

import {
  isPath
} from './path_cache';
import {
  isDescriptor,
  isDescriptorTrap,
  peekMeta,
  DESCRIPTOR,
  descriptorFor
} from './meta';
import { DESCRIPTOR_TRAP, EMBER_METAL_ES5_GETTERS, MANDATORY_SETTER } from 'ember/features';
/**
 @module @ember/object
*/
/**
  Sets the value of a property on an object, respecting computed properties
  and notifying observers and other listeners of the change. If the
  property is not defined but the object implements the `setUnknownProperty`
  method then that will be invoked as well.

  ```javascript
  import { set } from '@ember/object';
  set(obj, "name", value);
  ```

  @method set
  @static
  @for @ember/object
  @param {Object} obj The object to modify.
  @param {String} keyName The property key to set
  @param {Object} value The value to set
  @return {Object} the passed value.
  @public
*/
export function set(obj, keyName, value, tolerant) {
  assert(
    `Set must be called with three or four arguments; an object, a property key, a value and tolerant true/false`,
    arguments.length === 3 || arguments.length === 4
  );
  assert(`Cannot call set with '${keyName}' on an undefined object.`, obj && typeof obj === 'object' || typeof obj === 'function');
  assert(`The key provided to set must be a string or number, you passed ${keyName}`, typeof keyName === 'string' || (typeof keyName === 'number' && !isNaN(keyName)));
  assert(`'this' in paths is not supported`, typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);

  if (obj.isDestroyed) {
    assert(`calling set on destroyed object: ${toString(obj)}.${keyName} = ${toString(value)}`, tolerant);
    return;
  }

  if (isPath(keyName)) {
    return setPath(obj, keyName, value, tolerant);
  }

  if (EMBER_METAL_ES5_GETTERS) {
    let possibleDesc = descriptorFor(obj, keyName);

    if (possibleDesc !== undefined) { /* computed property */
      possibleDesc.set(obj, keyName, value);
      return value;
    }
  }

  let currentValue = getPossibleMandatoryProxyValue(obj, keyName);

  if (DESCRIPTOR_TRAP && isDescriptorTrap(currentValue)) {
    currentValue = currentValue[DESCRIPTOR];
  }

  if (isDescriptor(currentValue)) { /* computed property */
    currentValue.set(obj, keyName, value);
  } else if (currentValue === undefined && 'object' === typeof obj && !(keyName in obj) &&
    typeof obj.setUnknownProperty === 'function') { /* unknown property */
    obj.setUnknownProperty(keyName, value);
  } else if (currentValue === value) { /* no change */
  } else {
    let meta = peekMeta(obj);

    if (MANDATORY_SETTER) {
      setWithMandatorySetter(meta, obj, keyName, value);
    } else {
      obj[keyName] = value;
    }

    notifyPropertyChange(obj, keyName, meta);
  }

  return value;
}

if (MANDATORY_SETTER) {
  var setWithMandatorySetter = (meta, obj, keyName, value) => {
    if (meta !== undefined && meta.peekWatching(keyName) > 0) {
      makeEnumerable(obj, keyName);
      meta.writeValue(obj, keyName, value);
    } else {
      obj[keyName] = value;
    }
  };

  var makeEnumerable = (obj, key) => {
    let desc = Object.getOwnPropertyDescriptor(obj, key);

    if (desc && desc.set && desc.set.isMandatorySetter) {
      desc.enumerable = true;
      Object.defineProperty(obj, key, desc);
    }
  };
}

function setPath(root, path, value, tolerant) {
  let parts = path.split('.');
  let keyName = parts.pop();

  assert('Property set failed: You passed an empty path', keyName.trim().length > 0);

  let newPath = parts.join('.');

  let newRoot = getPath(root, newPath);

  if (newRoot) {
    return set(newRoot, keyName, value);
  } else if (!tolerant) {
    throw new EmberError(`Property set failed: object in path "${newPath}" could not be found or was destroyed.`);
  }
}

/**
  Error-tolerant form of `set`. Will not blow up if any part of the
  chain is `undefined`, `null`, or destroyed.

  This is primarily used when syncing bindings, which may try to update after
  an object has been destroyed.

  @method trySet
  @static
  @for @ember/object
  @param {Object} root The object to modify.
  @param {String} path The property path to set
  @param {Object} value The value to set
  @public
*/
export function trySet(root, path, value) {
  return set(root, path, value, true);
}
<commit_msg>Add code sample to `trySet`<commit_after>import { toString } from 'ember-utils';
import { assert, Error as EmberError } from 'ember-debug';
import { getPossibleMandatoryProxyValue, _getPath as getPath } from './property_get';
import {
  notifyPropertyChange
} from './property_events';

import {
  isPath
} from './path_cache';
import {
  isDescriptor,
  isDescriptorTrap,
  peekMeta,
  DESCRIPTOR,
  descriptorFor
} from './meta';
import { DESCRIPTOR_TRAP, EMBER_METAL_ES5_GETTERS, MANDATORY_SETTER } from 'ember/features';
/**
 @module @ember/object
*/
/**
  Sets the value of a property on an object, respecting computed properties
  and notifying observers and other listeners of the change. If the
  property is not defined but the object implements the `setUnknownProperty`
  method then that will be invoked as well.

  ```javascript
  import { set } from '@ember/object';
  set(obj, "name", value);
  ```

  @method set
  @static
  @for @ember/object
  @param {Object} obj The object to modify.
  @param {String} keyName The property key to set
  @param {Object} value The value to set
  @return {Object} the passed value.
  @public
*/
export function set(obj, keyName, value, tolerant) {
  assert(
    `Set must be called with three or four arguments; an object, a property key, a value and tolerant true/false`,
    arguments.length === 3 || arguments.length === 4
  );
  assert(`Cannot call set with '${keyName}' on an undefined object.`, obj && typeof obj === 'object' || typeof obj === 'function');
  assert(`The key provided to set must be a string or number, you passed ${keyName}`, typeof keyName === 'string' || (typeof keyName === 'number' && !isNaN(keyName)));
  assert(`'this' in paths is not supported`, typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);

  if (obj.isDestroyed) {
    assert(`calling set on destroyed object: ${toString(obj)}.${keyName} = ${toString(value)}`, tolerant);
    return;
  }

  if (isPath(keyName)) {
    return setPath(obj, keyName, value, tolerant);
  }

  if (EMBER_METAL_ES5_GETTERS) {
    let possibleDesc = descriptorFor(obj, keyName);

    if (possibleDesc !== undefined) { /* computed property */
      possibleDesc.set(obj, keyName, value);
      return value;
    }
  }

  let currentValue = getPossibleMandatoryProxyValue(obj, keyName);

  if (DESCRIPTOR_TRAP && isDescriptorTrap(currentValue)) {
    currentValue = currentValue[DESCRIPTOR];
  }

  if (isDescriptor(currentValue)) { /* computed property */
    currentValue.set(obj, keyName, value);
  } else if (currentValue === undefined && 'object' === typeof obj && !(keyName in obj) &&
    typeof obj.setUnknownProperty === 'function') { /* unknown property */
    obj.setUnknownProperty(keyName, value);
  } else if (currentValue === value) { /* no change */
  } else {
    let meta = peekMeta(obj);

    if (MANDATORY_SETTER) {
      setWithMandatorySetter(meta, obj, keyName, value);
    } else {
      obj[keyName] = value;
    }

    notifyPropertyChange(obj, keyName, meta);
  }

  return value;
}

if (MANDATORY_SETTER) {
  var setWithMandatorySetter = (meta, obj, keyName, value) => {
    if (meta !== undefined && meta.peekWatching(keyName) > 0) {
      makeEnumerable(obj, keyName);
      meta.writeValue(obj, keyName, value);
    } else {
      obj[keyName] = value;
    }
  };

  var makeEnumerable = (obj, key) => {
    let desc = Object.getOwnPropertyDescriptor(obj, key);

    if (desc && desc.set && desc.set.isMandatorySetter) {
      desc.enumerable = true;
      Object.defineProperty(obj, key, desc);
    }
  };
}

function setPath(root, path, value, tolerant) {
  let parts = path.split('.');
  let keyName = parts.pop();

  assert('Property set failed: You passed an empty path', keyName.trim().length > 0);

  let newPath = parts.join('.');

  let newRoot = getPath(root, newPath);

  if (newRoot) {
    return set(newRoot, keyName, value);
  } else if (!tolerant) {
    throw new EmberError(`Property set failed: object in path "${newPath}" could not be found or was destroyed.`);
  }
}

/**
  Error-tolerant form of `set`. Will not blow up if any part of the
  chain is `undefined`, `null`, or destroyed.

  This is primarily used when syncing bindings, which may try to update after
  an object has been destroyed.

  ```javascript
  import { trySet } from '@ember/object';

  let obj = { name: "Zoey" };
  trySet(obj, "contacts.twitter", "@emberjs");
  ```

  @method trySet
  @static
  @for @ember/object
  @param {Object} root The object to modify.
  @param {String} path The property path to set
  @param {Object} value The value to set
  @public
*/
export function trySet(root, path, value) {
  return set(root, path, value, true);
}
<|endoftext|>


<commit_before>var gulpfile = require('../utils/gulpfile');
var fs = require('fs');
var log = require('../utils/log');
var responseFile = require('../utils/file').responseFile;
/**
 * compress 字体压缩
 * @params params.src 需要解析的url地址
 */
exports.compress = function (req, res, compress, filename, params) {
    var fontfile = global.CONTENTPATH + "/fonts/" + filename;
    var src = params && params.src;
    if (!src) {
        //不带参数,直接返回字体文件

    }
    gulpfile.handleFonts(fontfile, src, compress, res);
}

/**
 * translate 字体转换
 */
exports.translate = function (req, res, compress, filename) {
    //判断字体文件类型
    var extName = filename.substr(filename.lastIndexOf(".") + 1).toLowerCase();
    var fontName = filename.substr(0, filename.lastIndexOf("."));
    var filepath = global.CONTENTPATH + "/fonts/" + extName + "/" + filename;
    //首先查看是否已存在
    fs.stat(filepath, function (err, stats) {
        if (err) {
            //文件不存在
            if (extName == "ttf") {
                res.writeHead(404, 'File Not Found!');
            } else if (extName == "eot") {
                //查找相应的ttf文件
                var ttfPath = global.CONTENTPATH + "/fonts/ttf/" + fontName + ".ttf";
                fs.stat(ttfPath, function (err2, stats2) {
                    if (err2) {
                        //ttf也不存在
                        res.writeHead(404, 'File Not Found!');
                    } else if (stats2.isFile()) {
                        //尝试转换
                        gulpfile.translateFonts(ttfPath, global.CONTENTPATH + "/fonts/eot/", filename, compress, res);
                    }
                });
            }
        } else if (stats.isFile()) {
            // 直接返回文件
            responseFile(req, res, filepath, stats, compress, filename);
            return;
        }
        res.writeHead(404, 'File Not Found!');
    });

}<commit_msg>bug fixed<commit_after>var gulpfile = require('../utils/gulpfile');
var fs = require('fs');
var log = require('../utils/log');
var responseFile = require('../utils/file').responseFile;
/**
 * compress 字体压缩
 * @params params.src 需要解析的url地址
 */
exports.compress = function (req, res, compress, filename, params) {
    var fontfile = global.CONTENTPATH + "/fonts/" + filename;
    var src = params && params.src;
    if (!src) {
        //不带参数,直接返回字体文件

    }
    gulpfile.handleFonts(fontfile, src, compress, res);
}

/**
 * translate 字体转换
 */
exports.translate = function (req, res, compress, filename) {
    //判断字体文件类型
    var extName = filename.substr(filename.lastIndexOf(".") + 1).toLowerCase();
    var fontName = filename.substr(0, filename.lastIndexOf("."));
    var filepath = global.CONTENTPATH + "/fonts/" + extName + "/" + filename;
    //首先查看是否已存在
    fs.stat(filepath, function (err, stats) {
        if (err) {
            //文件不存在
            if (extName == "ttf") {
                res.writeHead(404, 'File Not Found!');
            } else if (extName == "eot") {
                //查找相应的ttf文件
                var ttfPath = global.CONTENTPATH + "/fonts/ttf/" + fontName + ".ttf";
                fs.stat(ttfPath, function (err2, stats2) {
                    if (err2) {
                        //ttf也不存在
                        res.writeHead(404, 'File Not Found!');
                    } else if (stats2.isFile()) {
                        //尝试转换
                        gulpfile.translateFonts(ttfPath, global.CONTENTPATH + "/fonts/eot/", filename, compress, res);
                    }
                });
                return;
            }
        } else if (stats.isFile()) {
            // 直接返回文件
            responseFile(req, res, filepath, stats, compress, filename);
            return;
        }
        res.writeHead(404, 'File Not Found!');
    });

}<|endoftext|>


<commit_before>function errorToWarn(ruleValue) {
  if (ruleValue === 2 || ruleValue === 'error') {
    return 'warn'
  }
  if (Array.isArray(ruleValue)) {
    if (ruleValue[0] === 2 || ruleValue[0] === 'error') {
      ruleValue[0] = 'warn'
      return ruleValue
    }
  }
  return ruleValue
}

const standardJSRules = require('eslint-config-standard').rules
const standardJSRulesWarn = Object.keys(standardJSRules).reduce((acc, ruleName) => {
  if (ruleName.indexOf('standard') === 0) return acc

  const ruleValue = standardJSRules[ruleName]
  const ruleValueNew = errorToWarn(ruleValue)
  acc[ruleName] = ruleValueNew
  return acc
}, {})

module.exports = {
  root: true,

  parser: 'babel-eslint',

  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
      generators: true,
      legacyDecorators: true,
    },
  },

  plugins: ['import', 'flowtype', 'promise', 'react', 'no-copy-paste-default-export'],

  settings: {
    react: {
      // Force the react version, otherwise it will log an error to the console
      version: 'detect',
    },
  },

  env: {
    es6: true,
    browser: true,
    node: true,
  },

  globals: {
    document: false,
    navigator: false,
    window: false,
  },

  rules: {
    ...standardJSRulesWarn,

    // WARNINGS
    indent: [
      'warn',
      2,
      {
        SwitchCase: 1,
        flatTernaryExpressions: true,
      },
    ],
    'comma-dangle': ['warn', 'always-multiline'], // No risks, beacuse it will be transpiled
    'space-before-function-paren': ['warn', { anonymous: 'always', named: 'never' }],
    'key-spacing': ['warn', { beforeColon: false, afterColon: true, mode: 'minimum' }],
    'object-curly-spacing': ['warn', 'always'],
    'block-spacing': ['warn', 'always'],
    'padded-blocks': ['warn', { blocks: 'never', switches: 'never', classes: 'never' }],
    'no-shadow': 'warn',
    'import/order': ['warn', { groups: ['builtin', 'external'] }],
    'import/named': 'warn',
    'jsx-quotes': ['warn', 'prefer-double'],
    'prefer-template': 'warn',
    'no-useless-concat': 'warn',
    'no-implicit-coercion': 'warn',
    'no-bitwise': 'warn',
    'lines-between-class-members': ['warn', 'always', { exceptAfterSingleLine: true }],
    'array-element-newline': ['warn', 'consistent'],
    'prefer-object-spread': 'warn',
    'react/jsx-indent': ['warn', 2],
    'react/jsx-indent-props': ['warn', 2],
    'react/jsx-wrap-multilines': ['warn', { return: 'parens-new-line', arrow: 'parens-new-line' }],
    'react/jsx-tag-spacing': 'warn',
    'react/jsx-equals-spacing': ['warn', 'never'],
    'react/jsx-no-duplicate-props': ['warn', { ignoreCase: true }],
    'react/jsx-pascal-case': ['warn', { allowAllCaps: true, ignore: [] }],
    'react/jsx-uses-react': 'warn',
    'react/jsx-uses-vars': 'warn',
    'react/jsx-curly-spacing': ['warn', 'never'],
    'react/no-deprecated': 'warn',
    'react/no-danger-with-children': 'warn',
    'react/no-direct-mutation-state': 'warn',
    'react/no-is-mounted': 'warn',
    'react/require-render-return': 'warn',
    'react/style-prop-object': 'warn',
    'react/jsx-max-depth': ['warn', { max: 8 }],
    'react/jsx-max-props-per-line': ['warn', { maximum: 6 }],
    'react/self-closing-comp': ['warn', { component: true, html: true }],
    'react/jsx-first-prop-new-line': ['warn', 'multiline'],
    'react/jsx-no-bind': ['warn', { ignoreRefs: true }],
    'react/no-typos': 'warn',
    'react/jsx-props-no-multi-spaces': 'warn',
    'promise/no-callback-in-promise': 'warn',
    'promise/no-return-wrap': 'warn',

    // ERRORS
    'no-redeclare': 'error',
    'no-undef': 'error',
    'no-unexpected-multiline': 'error',
    'no-use-before-define': ['error', { functions: false, classes: true, variables: true }],
    'callback-return': ['error', ['callback', 'cb', 'next', 'done']],
    'handle-callback-err': 'error',
    'react/no-unused-prop-types': 'error',
    'react/jsx-key': 'error',
    'react/no-string-refs': 'error',
    'react/jsx-no-undef': 'error',
    'react/no-unescaped-entities': 'error',
    'react/jsx-no-target-blank': 'error',
    'react/no-unknown-property': 'error',
    'react/jsx-no-comment-textnodes': 'error',
    'react/react-in-jsx-scope': 'error',
    'promise/no-nesting': 'error',
    'no-copy-paste-default-export/default': 'error',

    // DISABLED ESLINT-CONFIG-STANDARD RULES
    // disable no-debugger because otherwise prettier removes it on save,
    // this is ok because debugger is stripped by uglify
    'no-debugger': 'off',
    // disable eslint-plugin-node rules because they error
    'node/no-deprecated-api': 'off',
    'node/process-exit-as-throw': 'off',

    // disable generator-star-spacing due to conflict with prettier, see https://github.com/accurat/accurapp/issues/51
    'generator-star-spacing': 'off',
  },
}
<commit_msg>💄 fixed according to comment<commit_after>function errorToWarn(ruleValue) {
  if (ruleValue === 2 || ruleValue === 'error') {
    return 'warn'
  }
  if (Array.isArray(ruleValue)) {
    if (ruleValue[0] === 2 || ruleValue[0] === 'error') {
      ruleValue[0] = 'warn'
      return ruleValue
    }
  }
  return ruleValue
}

const standardJSRules = require('eslint-config-standard').rules
const standardJSRulesWarn = Object.keys(standardJSRules).reduce((acc, ruleName) => {
  if (ruleName.indexOf('standard') === 0) return acc

  const ruleValue = standardJSRules[ruleName]
  const ruleValueNew = errorToWarn(ruleValue)
  acc[ruleName] = ruleValueNew
  return acc
}, {})

module.exports = {
  root: true,

  parser: 'babel-eslint',

  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
      generators: true,
      legacyDecorators: true,
    },
  },

  plugins: ['import', 'flowtype', 'promise', 'react', 'no-copy-paste-default-export'],

  settings: {
    react: {
      // Force the react version, otherwise it will log an error to the console
      version: 'detect',
    },
  },

  env: {
    es6: true,
    browser: true,
    node: true,
  },

  globals: {
    document: false,
    navigator: false,
    window: false,
  },

  rules: {
    ...standardJSRulesWarn,

    // WARNINGS
    indent: [
      'warn',
      2,
      {
        SwitchCase: 1,
        flatTernaryExpressions: true,
      },
    ],
    'comma-dangle': ['warn', 'always-multiline'], // No risks, beacuse it will be transpiled
    'space-before-function-paren': ['warn', { anonymous: 'always', named: 'never' }],
    'key-spacing': ['warn', { beforeColon: false, afterColon: true, mode: 'minimum' }],
    'object-curly-spacing': ['warn', 'always'],
    'block-spacing': ['warn', 'always'],
    'padded-blocks': ['warn', { blocks: 'never', switches: 'never', classes: 'never' }],
    'no-shadow': 'warn',
    'import/order': ['warn', { groups: ['builtin', 'external'] }],
    'import/named': 'warn',
    'jsx-quotes': ['warn', 'prefer-double'],
    'prefer-template': 'warn',
    'no-useless-concat': 'warn',
    'no-implicit-coercion': 'warn',
    'no-bitwise': 'warn',
    'lines-between-class-members': ['warn', 'always', { exceptAfterSingleLine: true }],
    'array-element-newline': ['warn', 'consistent'],
    'prefer-object-spread': 'warn',
    'react/jsx-indent': ['warn', 2],
    'react/jsx-indent-props': ['warn', 2],
    'react/jsx-wrap-multilines': ['warn', { return: 'parens-new-line', arrow: 'parens-new-line' }],
    'react/jsx-tag-spacing': 'warn',
    'react/jsx-equals-spacing': ['warn', 'never'],
    'react/jsx-no-duplicate-props': ['warn', { ignoreCase: true }],
    'react/jsx-pascal-case': ['warn', { allowAllCaps: true, ignore: [] }],
    'react/jsx-uses-react': 'warn',
    'react/jsx-uses-vars': 'warn',
    'react/jsx-curly-spacing': ['warn', 'never'],
    'react/no-deprecated': 'warn',
    'react/no-danger-with-children': 'warn',
    'react/no-direct-mutation-state': 'warn',
    'react/no-is-mounted': 'warn',
    'react/require-render-return': 'warn',
    'react/style-prop-object': 'warn',
    'react/jsx-max-depth': ['warn', { max: 8 }],
    'react/jsx-max-props-per-line': ['warn', { maximum: 6 }],
    'react/self-closing-comp': ['warn', { component: true, html: true }],
    'react/jsx-first-prop-new-line': ['warn', 'multiline'],
    'react/jsx-no-bind': ['warn', { ignoreRefs: true }],
    'react/no-typos': 'warn',
    'react/jsx-props-no-multi-spaces': 'warn',
    'promise/no-callback-in-promise': 'warn',
    'promise/no-return-wrap': 'warn',

    // ERRORS
    'no-redeclare': 'error',
    'no-undef': 'error',
    'no-unexpected-multiline': 'error',
    'no-use-before-define': ['error', { functions: false, classes: true, variables: true }],
    'callback-return': ['error', ['callback', 'cb', 'next', 'done']],
    'handle-callback-err': 'error',
    'react/no-unused-prop-types': 'error',
    'react/jsx-key': 'error',
    'react/no-string-refs': 'error',
    'react/jsx-no-undef': 'error',
    'react/no-unescaped-entities': 'error',
    'react/jsx-no-target-blank': 'error',
    'react/no-unknown-property': 'error',
    'react/jsx-no-comment-textnodes': 'error',
    'react/react-in-jsx-scope': 'error',
    'promise/no-nesting': 'error',
    'no-copy-paste-default-export/default': 'error',

    // DISABLED ESLINT-CONFIG-STANDARD RULES
    // disable no-debugger because otherwise prettier removes it on save,
    // this is ok because debugger is stripped by uglify
    'no-debugger': 'off',
    // disable eslint-plugin-node rules because they error
    'node/no-deprecated-api': 'off',
    'node/process-exit-as-throw': 'off',

    // aligned to prettier, see https://github.com/accurat/accurapp/issues/51
    'generator-star-spacing': ['warn', 'after'],
  },
}
<|endoftext|>


<commit_before>var fs = require('fs');

module.exports = function(conn) {
  return {
    list: function(businessId, callback) {
      conn.get('/businesses/'+businessId+'/audits', callback);
    },

    create: function(businessId, audit, callback) {
      conn.post('/businesses/'+businessId+'/audits', audit, callback);
    },

    edit: function(businessId, auditId, audit, callback) {
      conn.put('/businesses/'+businessId+'/audits/'+auditId, audit, callback);
    },

    find: function(businessId, auditId, callback) {
      conn.get('/businesses/'+businessId+'/audits/'+auditId, callback);
    },

    delete: function(businessId, auditId, callback){
      conn.delete('/businesses/'+businessId+'/audits/'+auditId, callback);
    },

    addFailure: function(businessId, auditId, failure, callback) {
      conn.post(
        '/businesses/' + businessId + '/audits/' + auditId + '/failures',
        failure,
        callback
      );
    },

    addDocument: function(businessId, auditId, docType, file, callback) {
      conn.postMultipart(
        '/businesses/' + businessId + '/audits/' + auditId + '/documents',
        {docType: docType, document: fs.createReadStream(file.path)},
        callback
      );
    },

    deleteDocument: function(businessId, auditId, docId, callback) {
      conn.delete(
        '/businesses/' +
          businessId +
          '/audits/' +
          auditId +
          '/documents/' +
          docId,
        callback
      );
    }
  };
};
<commit_msg>Explicitly specify filename in upload request<commit_after>var fs = require('fs');

module.exports = function(conn) {
  return {
    list: function(businessId, callback) {
      conn.get('/businesses/'+businessId+'/audits', callback);
    },

    create: function(businessId, audit, callback) {
      conn.post('/businesses/'+businessId+'/audits', audit, callback);
    },

    edit: function(businessId, auditId, audit, callback) {
      conn.put('/businesses/'+businessId+'/audits/'+auditId, audit, callback);
    },

    find: function(businessId, auditId, callback) {
      conn.get('/businesses/'+businessId+'/audits/'+auditId, callback);
    },

    delete: function(businessId, auditId, callback){
      conn.delete('/businesses/'+businessId+'/audits/'+auditId, callback);
    },

    addFailure: function(businessId, auditId, failure, callback) {
      conn.post(
        '/businesses/' + businessId + '/audits/' + auditId + '/failures',
        failure,
        callback
      );
    },

    addDocument: function(businessId, auditId, docType, file, callback) {
      conn.postMultipart(
        '/businesses/' + businessId + '/audits/' + auditId + '/documents',
        {
          docType: docType,
          document: fs.createReadStream(file.path),
          filename: file.name
        },
        callback
      );
    },

    deleteDocument: function(businessId, auditId, docId, callback) {
      conn.delete(
        '/businesses/' +
          businessId +
          '/audits/' +
          auditId +
          '/documents/' +
          docId,
        callback
      );
    }
  };
};
<|endoftext|>