From 50325d0f8fc13b293773c2b554febdc1792e158e Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Wed, 21 Jan 2015 09:50:19 -0400 Subject: [PATCH] Make use of resin-cli-visuals and get rid of local ui module --- lib/actions/app.coffee | 10 +- lib/actions/auth.coffee | 6 +- lib/actions/device.coffee | 15 +- lib/actions/environment-variables.coffee | 6 +- lib/actions/examples.coffee | 6 +- lib/actions/keys.coffee | 8 +- lib/actions/os.coffee | 4 +- lib/ui/index.coffee | 3 - lib/ui/patterns/patterns.coffee | 19 --- lib/ui/widgets/table/table-helpers.coffee | 83 --------- .../widgets/table/table-helpers.spec.coffee | 157 ------------------ lib/ui/widgets/table/table.coffee | 23 --- lib/ui/widgets/table/table.spec.coffee | 42 ----- lib/ui/widgets/widgets.coffee | 99 ----------- package.json | 1 + 15 files changed, 29 insertions(+), 453 deletions(-) delete mode 100644 lib/ui/index.coffee delete mode 100644 lib/ui/patterns/patterns.coffee delete mode 100644 lib/ui/widgets/table/table-helpers.coffee delete mode 100644 lib/ui/widgets/table/table-helpers.spec.coffee delete mode 100644 lib/ui/widgets/table/table.coffee delete mode 100644 lib/ui/widgets/table/table.spec.coffee delete mode 100644 lib/ui/widgets/widgets.coffee diff --git a/lib/actions/app.coffee b/lib/actions/app.coffee index fd18ebea..a8933196 100644 --- a/lib/actions/app.coffee +++ b/lib/actions/app.coffee @@ -1,7 +1,7 @@ _ = require('lodash-contrib') async = require('async') resin = require('resin-sdk') -ui = require('../ui') +visuals = require('resin-cli-visuals') commandOptions = require('./command-options') exports.create = @@ -39,7 +39,7 @@ exports.create = return callback(null, deviceType) else deviceTypes = resin.models.device.getSupportedDeviceTypes() - ui.widgets.select('Select a type', deviceTypes, callback) + visuals.widgets.select('Select a type', deviceTypes, callback) (type, callback) -> resin.models.application.create(params.name, type, callback) @@ -62,7 +62,7 @@ exports.list = action: (params, options, done) -> resin.models.application.getAll (error, applications) -> return done(error) if error? - console.log ui.widgets.table.horizontal applications, [ + console.log visuals.widgets.table.horizontal applications, [ 'ID' 'Name' 'Device Display Name' @@ -84,7 +84,7 @@ exports.info = action: (params, options, done) -> resin.models.application.get params.id, (error, application) -> return done(error) if error? - console.log ui.widgets.table.vertical application, [ + console.log visuals.widgets.table.vertical application, [ 'ID' 'Name' 'Device Display Name' @@ -122,7 +122,7 @@ exports.remove = options: [ commandOptions.yes ] permission: 'user' action: (params, options, done) -> - ui.patterns.remove 'application', options.yes, (callback) -> + visuals.patterns.remove 'application', options.yes, (callback) -> resin.models.application.remove(params.id, callback) , done diff --git a/lib/actions/auth.coffee b/lib/actions/auth.coffee index 6919419d..f75a7084 100644 --- a/lib/actions/auth.coffee +++ b/lib/actions/auth.coffee @@ -2,7 +2,7 @@ _ = require('lodash-contrib') url = require('url') async = require('async') resin = require('resin-sdk') -ui = require('../ui') +visuals = require('resin-cli-visuals') helpers = require('../helpers/helpers') exports.login = @@ -26,7 +26,7 @@ exports.login = if params.credentials? return helpers.parseCredentials(params.credentials, callback) else - return ui.widgets.login(callback) + return visuals.widgets.login(callback) (credentials, callback) -> resin.auth.login(credentials, callback) @@ -70,7 +70,7 @@ exports.signup = async.waterfall([ (callback) -> - ui.widgets.register(callback) + visuals.widgets.register(callback) (credentials, callback) -> resin.auth.register credentials, (error, token) -> diff --git a/lib/actions/device.coffee b/lib/actions/device.coffee index 2c214b4b..1d07d637 100644 --- a/lib/actions/device.coffee +++ b/lib/actions/device.coffee @@ -4,7 +4,7 @@ resin = require('resin-sdk') os = require('os') fs = require('fs') progressStream = require('progress-stream') -ui = require('../ui') +visuals = require('resin-cli-visuals') commandOptions = require('./command-options') exports.list = @@ -21,7 +21,7 @@ exports.list = action: (params, options, done) -> resin.models.device.getAllByApplication options.application, (error, devices) -> return done(error) if error? - console.log ui.widgets.table.horizontal devices, [ + console.log visuals.widgets.table.horizontal devices, [ 'ID' 'Name' 'Device Display Name' @@ -46,7 +46,7 @@ exports.info = action: (params, options, done) -> resin.models.device.get params.id, (error, device) -> return done(error) if error? - console.log ui.widgets.table.vertical device, [ + console.log visuals.widgets.table.vertical device, [ 'ID' 'Name' 'Device Display Name' @@ -80,7 +80,7 @@ exports.remove = options: [ commandOptions.yes ] permission: 'user' action: (params, options, done) -> - ui.patterns.remove 'device', options.yes, (callback) -> + visuals.patterns.remove 'device', options.yes, (callback) -> resin.models.device.remove(params.id, callback) , done @@ -118,7 +118,7 @@ exports.rename = (callback) -> if not _.isEmpty(params.name) return callback(null, params.name) - ui.widgets.ask('How do you want to name this device?', callback) + visuals.widgets.ask('How do you want to name this device?', callback) (name, callback) -> resin.models.device.rename(params.id, name, callback) @@ -184,7 +184,8 @@ exports.init = if options.yes return callback(null, true) else - ui.widgets.confirm("This will completely erase #{params.device}. Are you sure you want to continue?", callback) + confirmMessage = "This will completely erase #{params.device}. Are you sure you want to continue?" + visuals.widgets.confirm(confirmMessage, callback) (confirmed, callback) -> return done() if not confirmed @@ -198,7 +199,7 @@ exports.init = time: 500 if not options.quiet - progressBar = new ui.widgets.Progress('Writing device OS', imageFileSize) + progressBar = new visuals.widgets.Progress('Writing device OS', imageFileSize) progress.on 'progress', (status) -> progressBar.tick(status.delta) diff --git a/lib/actions/environment-variables.coffee b/lib/actions/environment-variables.coffee index 47578eb5..5ed3d283 100644 --- a/lib/actions/environment-variables.coffee +++ b/lib/actions/environment-variables.coffee @@ -1,6 +1,6 @@ _ = require('lodash-contrib') resin = require('resin-sdk') -ui = require('../ui') +visuals = require('resin-cli-visuals') commandOptions = require('./command-options') exports.list = @@ -36,7 +36,7 @@ exports.list = if not options.verbose environmentVariables = _.reject(environmentVariables, resin.models.environmentVariables.isSystemVariable) - console.log(ui.widgets.table.horizontal(environmentVariables)) + console.log(visuals.widgets.table.horizontal(environmentVariables)) return done() exports.remove = @@ -57,7 +57,7 @@ exports.remove = options: [ commandOptions.yes ] permission: 'user' action: (params, options, done) -> - ui.patterns.remove 'environment variable', options.yes, (callback) -> + visuals.patterns.remove 'environment variable', options.yes, (callback) -> resin.models.environmentVariables.remove(params.id, callback) , done diff --git a/lib/actions/examples.coffee b/lib/actions/examples.coffee index dc261031..159327c8 100644 --- a/lib/actions/examples.coffee +++ b/lib/actions/examples.coffee @@ -4,7 +4,7 @@ path = require('path') _ = require('lodash') gitCli = require('git-cli') resin = require('resin-sdk') -ui = require('../ui') +visuals = require('resin-cli-visuals') examplesData = require('../data/examples.json') exports.list = @@ -26,7 +26,7 @@ exports.list = example.author ?= 'Unknown' return example - console.log ui.widgets.table.horizontal examplesData, [ + console.log visuals.widgets.table.horizontal examplesData, [ 'ID' 'Display Name' 'Repository' @@ -53,7 +53,7 @@ exports.info = example.id = id example.author ?= 'Unknown' - console.log ui.widgets.table.vertical example, [ + console.log visuals.widgets.table.vertical example, [ 'ID' 'Display Name' 'Description' diff --git a/lib/actions/keys.coffee b/lib/actions/keys.coffee index be0548c2..4a9fdc1d 100644 --- a/lib/actions/keys.coffee +++ b/lib/actions/keys.coffee @@ -4,7 +4,7 @@ async = require('async') fs = require('fs') resin = require('resin-sdk') helpers = require('../helpers/helpers') -ui = require('../ui') +visuals = require('resin-cli-visuals') commandOptions = require('./command-options') exports.list = @@ -20,7 +20,7 @@ exports.list = action: (params, options, done) -> resin.models.key.getAll (error, keys) -> return done(error) if error? - console.log ui.widgets.table.horizontal keys, [ 'ID', 'Title' ] + console.log visuals.widgets.table.horizontal keys, [ 'ID', 'Title' ] return done() exports.info = @@ -37,7 +37,7 @@ exports.info = resin.models.key.get params.id, (error, key) -> return done(error) if error? key.public_key = '\n' + _.str.chop(key.public_key, resin.settings.get('sshKeyWidth')).join('\n') - console.log(ui.widgets.table.vertical(key, [ 'ID', 'Title', 'Public Key' ])) + console.log(visuals.widgets.table.vertical(key, [ 'ID', 'Title', 'Public Key' ])) return done() exports.remove = @@ -56,7 +56,7 @@ exports.remove = options: [ commandOptions.yes ] permission: 'user' action: (params, options, done) -> - ui.patterns.remove 'key', options.yes, (callback) -> + visuals.patterns.remove 'key', options.yes, (callback) -> resin.models.key.remove(params.id, callback) , done diff --git a/lib/actions/os.coffee b/lib/actions/os.coffee index 725658b9..393433a2 100644 --- a/lib/actions/os.coffee +++ b/lib/actions/os.coffee @@ -3,7 +3,7 @@ async = require('async') path = require('path') mkdirp = require('mkdirp') resin = require('resin-sdk') -ui = require('../ui') +visuals = require('resin-cli-visuals') exports.download = signature: 'os download ' @@ -81,7 +81,7 @@ exports.download = resin.models.os.download osParams, outputFile, callback, (state) -> return if options.quiet - bar ?= new ui.widgets.Progress('Downloading device OS', state.total) + bar ?= new visuals.widgets.Progress('Downloading device OS', state.total) return if bar.complete or not state? diff --git a/lib/ui/index.coffee b/lib/ui/index.coffee deleted file mode 100644 index fe90ba5e..00000000 --- a/lib/ui/index.coffee +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = - widgets: require('./widgets/widgets') - patterns: require('./patterns/patterns') diff --git a/lib/ui/patterns/patterns.coffee b/lib/ui/patterns/patterns.coffee deleted file mode 100644 index ec884afe..00000000 --- a/lib/ui/patterns/patterns.coffee +++ /dev/null @@ -1,19 +0,0 @@ -async = require('async') -fs = require('fs') -widgets = require('../widgets/widgets') -ProgressBar = require('progress') - -exports.remove = (name, confirmAttribute, deleteFunction, outerCallback) -> - async.waterfall([ - - (callback) -> - if confirmAttribute - return callback(null, true) - - widgets.confirmRemoval(name, callback) - - (confirmed, callback) -> - return callback() if not confirmed - deleteFunction(callback) - - ], outerCallback) diff --git a/lib/ui/widgets/table/table-helpers.coffee b/lib/ui/widgets/table/table-helpers.coffee deleted file mode 100644 index c69585a3..00000000 --- a/lib/ui/widgets/table/table-helpers.coffee +++ /dev/null @@ -1,83 +0,0 @@ -_ = require('lodash') -_.str = require('underscore.string') - -KEY_DISPLAY_MAP = - app_name: 'Name' - last_seen_time: 'Last Seen' - ip_address: 'IP Address' - id: 'ID' - uuid: 'UUID' - -startsWithLetter = (string) -> - firstLetter = _.first(string) - return /[a-z|A-Z]/.test(firstLetter) - -renameObjectKey = (object, key, newKey) -> - return if key is newKey - object[newKey] = object[key] - delete object[key] - -exports.getKeyName = (key) -> - nameFromMap = KEY_DISPLAY_MAP[key] - return nameFromMap if nameFromMap? - - # Prevent modifying a value that is part of - # the map values. - # This is really an heuristic, as making sure - # the client actually refers to that value means - # converting the value to lowercase-underscore-cased - # and do the check, but that seems overkill. - if _.values(KEY_DISPLAY_MAP).indexOf(key) isnt -1 - return key - - key = _.str.humanize(key) - return _.str.titleize(key) - -isReallyEmpty = (value) -> - - # For some reason, _.isEmpty returns - # true for numbers and booleans - return false if _.isNumber(value) or _.isBoolean(value) - - return _.isEmpty(value) - -exports.prepareObject = (object) -> - object = _.omit object, (value, key) -> - return not startsWithLetter(key) - - for key, value of object - if _.isObject(value) and not _.isArray(value) - object[key] = exports.prepareObject(value) - - newKeyName = exports.getKeyName(key) - renameObjectKey(object, key, newKeyName) - - object = _.omit object, (value, key) -> - return isReallyEmpty(value) - - return object - -exports.processTableContents = (contents) -> - return if not contents? - - # Allows us to simplify the algorithm by not - # concerning about different input types - if not _.isArray(contents) - contents = [ contents ] - - return _.map(contents, exports.prepareObject) - -isRealObject = (object) -> - return false if _.isArray(object) or _.isFunction(object) - return _.isObject(object) - -exports.getDefaultContentsOrdering = (contents) -> - return if _.isEmpty(contents) - firstContentEntry = _.first(contents) - return if not isRealObject(firstContentEntry) - return _.keys(firstContentEntry) - -exports.normaliseOrdering = (ordering, contents) -> - if not _.isEmpty(ordering) - return _.map(ordering, exports.getKeyName) - return exports.getDefaultContentsOrdering(contents) diff --git a/lib/ui/widgets/table/table-helpers.spec.coffee b/lib/ui/widgets/table/table-helpers.spec.coffee deleted file mode 100644 index 9f59abe8..00000000 --- a/lib/ui/widgets/table/table-helpers.spec.coffee +++ /dev/null @@ -1,157 +0,0 @@ -expect = require('chai').expect -sinon = require('sinon') -_ = require('lodash') -tableHelpers = require('./table-helpers') - -OBJECTS = - application: - device: null - id: 162 - user: - __deferred: [] - __id: 24 - app_name: 'HelloResin' - git_repository: 'git@git.staging.resin.io:jviotti/helloresin.git' - commit: '1234' - device_type: 'raspberry-pi' - __metadata: - uri: '/ewa/application(162)' - basic: - Hello: 'world' - Hey: 'there' - __private: true - _option: false - $data: [ 1, 2, 3 ] - recursive: - hello: 'world' - Hey: - __private: true - value: - $option: false - data: 'There' - _option: false - __something: - value: 'ok' - nested: - $data: [ 1, 2, 3 ] - valid: - One: 'one' - Two: 'two' - Three: 'three' - -describe 'Table Helpers:', -> - - describe '#getKeyName()', -> - - it 'should return titleized names', -> - expect(tableHelpers.getKeyName('hello world')).to.equal('Hello World') - expect(tableHelpers.getKeyName('foo Bar')).to.equal('Foo Bar') - - it 'should return custom names from the map', -> - expect(tableHelpers.getKeyName('last_seen_time')).to.equal('Last Seen') - expect(tableHelpers.getKeyName('app_name')).to.equal('Name') - - it 'should remove underscores', -> - expect(tableHelpers.getKeyName('git_repository')).to.equal('Git Repository') - expect(tableHelpers.getKeyName('device_type')).to.equal('Device Type') - expect(tableHelpers.getKeyName('is_web_accessible')).to.equal('Is Web Accessible') - - describe '#prepareObject()', -> - - it 'should get rid of keys not starting with letters', -> - expect(tableHelpers.prepareObject(OBJECTS.basic)).to.deep.equal - Hello: 'world' - Hey: 'there' - - it 'should get rid of keys not starting with letters recursively', -> - expect(tableHelpers.prepareObject(OBJECTS.recursive)).to.deep.equal - Hello: 'world' - Hey: - Value: - Data: 'There' - - it 'should do proper key renamings', -> - expect(tableHelpers.prepareObject(OBJECTS.application)).to.deep.equal - ID: 162 - Name: 'HelloResin' - 'Git Repository': 'git@git.staging.resin.io:jviotti/helloresin.git' - Commit: '1234' - 'Device Type': 'raspberry-pi' - - it 'should not remove not empty arrays', -> - object = { Array: [ 1, 2, 3 ] } - expect(tableHelpers.prepareObject(object)).to.deep.equal(object) - - it 'should preserve false fields', -> - object = { Test: false } - expect(tableHelpers.prepareObject(object)).to.deep.equal(object) - - describe '#processTableContents()', -> - - checkIfArray = (input) -> - result = tableHelpers.processTableContents(input, _.identity) - expect(result).to.be.an.instanceof(Array) - - it 'should always return an array', -> - checkIfArray(OBJECTS.basic) - checkIfArray([ OBJECTS.basic ]) - checkIfArray([ 'contents' ]) - - it 'should get rid of keys not starting with letters', -> - result = tableHelpers.processTableContents(OBJECTS.basic, _.identity) - expect(result).to.deep.equal [ - { - Hello: 'world' - Hey: 'there' - } - ] - - it 'should allow a null/undefined map function without corrupting the data', -> - for map in [ null, undefined ] - result = tableHelpers.processTableContents([ OBJECTS.valid ], map) - expect(result).to.deep.equal([ OBJECTS.valid ]) - - describe '#getDefaultContentsOrdering()', -> - - it 'should return undefined if no contents', -> - expect(tableHelpers.getDefaultContentsOrdering()).to.be.undefined - - it 'should return undefined if contents is empty', -> - expect(tableHelpers.getDefaultContentsOrdering([])).to.be.undefined - - it 'should return undefined if contents is not an array of objects', -> - inputs = [ - [ 1, 2, 3 ] - [ '1', '2', '3' ] - [ _.identity ] - ] - - for input in inputs - expect(tableHelpers.getDefaultContentsOrdering(input)).to.be.undefined - - it 'should return an array containing all the object keys', -> - result = tableHelpers.getDefaultContentsOrdering([ OBJECTS.valid ]) - for key, value of OBJECTS.valid - expect(result.indexOf(key)).to.not.equal(-1) - - describe '#normaliseOrdering()', -> - - it 'should return titleized words if ordering is not empty', -> - ordering = [ 'one', 'two', 'three' ] - result = tableHelpers.normaliseOrdering(ordering, {}) - expect(result).to.deep.equal([ 'One', 'Two', 'Three' ]) - - it 'should return an array containing all the object keys', -> - result = tableHelpers.normaliseOrdering(null, [ OBJECTS.valid ]) - for key, value of OBJECTS.valid - expect(result.indexOf(key)).to.not.equal(-1) - - it 'should not give precendence to names from the map', -> - ordering = [ 'id', 'ip_address', 'name' ] - result = tableHelpers.normaliseOrdering(ordering, {}) - expect(result).to.deep.equal([ 'ID', 'IP Address', 'Name' ]) - - it 'should preverse a string that is the result of a map lookup', -> - ordering = [ 'ID', 'IP Address' ] - result = tableHelpers.normaliseOrdering(ordering, {}) - expect(result).to.deep.equal(ordering) diff --git a/lib/ui/widgets/table/table.coffee b/lib/ui/widgets/table/table.coffee deleted file mode 100644 index 737760e1..00000000 --- a/lib/ui/widgets/table/table.coffee +++ /dev/null @@ -1,23 +0,0 @@ -cliff = require('cliff') -tableHelpers = require('./table-helpers') - -# TODO: Maybe there is a (sane) way to test this, given -# that the result is not automatically printed by cliff? -exports.horizontal = (contents, ordering, colours) -> - return if not contents? - contents = tableHelpers.processTableContents(contents) - ordering = tableHelpers.normaliseOrdering(ordering, contents) - return cliff.stringifyObjectRows(contents, ordering, colours) - -exports.vertical = (contents, ordering) -> - return if not contents? - contents = tableHelpers.processTableContents(contents) - ordering = tableHelpers.normaliseOrdering(ordering, contents) - - # TODO: Add some kind of separator to make clear - # when we're printing another content item - result = [] - for item in contents - for next in ordering - result.push("#{next}: #{item[next]}") - return result.join('\n') diff --git a/lib/ui/widgets/table/table.spec.coffee b/lib/ui/widgets/table/table.spec.coffee deleted file mode 100644 index f76d047e..00000000 --- a/lib/ui/widgets/table/table.spec.coffee +++ /dev/null @@ -1,42 +0,0 @@ -expect = require('chai').expect -table = require('./table') - -OBJECTS = - valid: - One: 'one' - Two: 'two' - Three: 'three' - -describe 'Table:', -> - - describe '#vertical()', -> - - it 'should return a string respecting the ordering', -> - ordering = [ 'One', 'Two', 'Three' ] - result = table.vertical(OBJECTS.valid, null, ordering).split('\n') - expected = [ - 'One: one' - 'Two: two' - 'Three: three' - ] - - expect(result).to.deep.equal(expected) - - it 'should be able to print everything without explicit ordering', -> - result = table.vertical(OBJECTS.valid, null).split('\n') - expected = [ - 'One: one' - 'Two: two' - 'Three: three' - ] - - for line in expected - expect(result.indexOf(line)).to.not.equal(-1) - - it 'should return undefined if contents does not exist', -> - expect(table.vertical(undefined)).to.be.undefined - - describe '#horizontal()', -> - - it 'should return undefined if contents does not exist', -> - expect(table.horizontal(undefined)).to.be.undefined diff --git a/lib/ui/widgets/widgets.coffee b/lib/ui/widgets/widgets.coffee deleted file mode 100644 index 245baa1c..00000000 --- a/lib/ui/widgets/widgets.coffee +++ /dev/null @@ -1,99 +0,0 @@ -_ = require('lodash') -inquirer = require('inquirer') -ProgressBar = require('progress') - -exports.table = require('./table/table') - -exports.register = (callback) -> - inquirer.prompt([ - { - type: 'input' - name: 'email' - message: 'Email' - } - { - type: 'input' - name: 'username' - message: 'Username' - } - { - type: 'password' - name: 'password' - message: 'Password' - validate: (input) -> - if input.length < 8 - return 'Password should be 8 characters long' - - return true - } - ], _.partial(callback, null)) - -exports.login = (callback) -> - inquirer.prompt([ - { - type: 'input' - name: 'username' - message: 'Username' - } - { - type: 'password' - name: 'password' - message: 'Password' - } - ], _.partial(callback, null)) - -exports.select = (message, list, callback) -> - inquirer.prompt [ - { - type: 'list' - name: 'option' - message: message or 'Select an option' - choices: list - } - ], (response) -> - return callback(null, response.option) - -exports.confirmRemoval = (name, callback) -> - inquirer.prompt [ - { - type: 'confirm' - name: 'confirmed' - message: "Are you sure you want to delete the #{name}?" - default: false - } - ], (response) -> - return callback(null, response.confirmed) - -exports.confirm = (message, callback) -> - inquirer.prompt [ - { - type: 'confirm' - name: 'confirmed' - message: message - default: false - } - ], (response) -> - return callback(null, response.confirmed) - -exports.ask = (question, callback) -> - inquirer.prompt [ - { - type: 'input' - name: 'answer' - message: question - validate: (input) -> - return _.isString(input) and not _.isEmpty(input) - } - ], (response) -> - return callback(null, response.answer) - -exports.Progress = class Progress extends ProgressBar - constructor: (message, size) -> - message = "#{message} [:bar] :percent :etas" - options = - complete: '=' - incomplete: ' ' - width: 40 - total: size - - super(message, options) diff --git a/package.json b/package.json index 43fa88cb..084d931d 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "progress-bar": "~0.1.1", "progress-stream": "^0.5.0", "resin-sdk": "git+ssh://git@bitbucket.org/rulemotion/resin-sdk.git", + "resin-cli-visuals": "git+ssh://git@bitbucket.org/rulemotion/resin-cli-visuals.git", "underscore.string": "~2.4.0", "yeoman-environment": "^1.2.0" }