applebot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8abd1f581c359df890dca0e9ed9a458d6244a41d
4
+ data.tar.gz: 1af37a02a80c482bc8da15de976853215d4f203b
5
+ SHA512:
6
+ metadata.gz: e5eb30d8e6205b5d25347ea4c16953d6540ba276fa92ccab84ea659d019f2dfd3386c7cf06fddb461b98a2f5dac4c4bcbf3a1b2b6e7a12d5c837433135d3b41b
7
+ data.tar.gz: 8078823f751ddf30c2ffc578fd3273c151c9e01a735e8f1f719ada1dd7117e4ba0808436475dd3fc7721e58752fc62e6e41a047f8247bec527a6155f8437a9c1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ applebot (0.0.1)
5
+ activesupport (>= 3.2)
6
+ commander (~> 4.1.2)
7
+ terminal-table (>= 1.4.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (4.0.3)
13
+ i18n (~> 0.6, >= 0.6.4)
14
+ minitest (~> 4.2)
15
+ multi_json (~> 1.3)
16
+ thread_safe (~> 0.1)
17
+ tzinfo (~> 0.3.37)
18
+ atomic (1.1.14)
19
+ commander (4.1.6)
20
+ highline (~> 1.6.11)
21
+ dotenv (0.10.0)
22
+ highline (1.6.21)
23
+ i18n (0.6.9)
24
+ minitest (4.7.5)
25
+ multi_json (1.9.3)
26
+ rake (10.1.0)
27
+ terminal-table (1.4.5)
28
+ thread_safe (0.1.3)
29
+ atomic
30
+ tzinfo (0.3.38)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ applebot!
37
+ dotenv
38
+ rake
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013-2014 Turboprop Inc (https://usepropeller.com/)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # AppleBot - The Apple Robot
2
+
3
+ A CLI and Ruby library to manage Apple Developer Center and iTunes Connect tasks. Uses [CasperJS](http://casperjs.org/) for easy maintenance and flexibility.
4
+
5
+ ## Requirements
6
+
7
+ AppleBot requires [CasperJS](http://casperjs.org/), which can be installed using [Homebrew](http://brew.sh/):
8
+
9
+ ```
10
+ $ brew install casperjs --devel
11
+ ```
12
+
13
+ ## Installation
14
+
15
+ ```
16
+ $ gem install applebot
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Commands
22
+
23
+ These commands run using iTunes Connect:
24
+
25
+ - `app:create` - Creates an entry for a new app on iTunes Connect
26
+ - `app:update` - Creates an entry for a new update to an existing app on iTunes Connect
27
+ - `app:reject_binary` - Rejects the binary for a pending release (either new or an update)
28
+ - `app:remove_from_sale` - Removes the app from sale in all territories
29
+
30
+ These commands run using the Apple Developer Center:
31
+
32
+ - `app_id:create` - Creates a bundle identifier
33
+ - `app_id:delete` - Removes a bundle identifier
34
+ - `profile:create` - Creates a provisioning profile
35
+ - `profile:download` - Downloads an existing provisioning profile
36
+ - `profile:list` - Lists all provisioning profiles
37
+
38
+ These commands can run on either iTunes Connect or Apple Developer Center:
39
+
40
+ - `app_id:list` - Lists all bundle identifiers
41
+
42
+ #### JSON Manifests
43
+
44
+ Most commands take in options - you can either pass them individually, or use a JSON manifest file like this:
45
+
46
+ ```json
47
+ {
48
+ "name": "My new app",
49
+ "app_id": "com.usepropeller.mynewapp"
50
+ }
51
+ ```
52
+
53
+ For example, the following commands are equivalent usng this JSON manifest:
54
+
55
+ ```shell
56
+ $ applebot app_id:create --name 'My new app' --app_id 'com.usepropeller.mynewapp'
57
+ $ applebot app_id:create --manifest ./manifest.json
58
+ ```
59
+
60
+ ```ruby
61
+ > AppleBot.app_id.create(name: 'My new app', app_id: 'com.usepropeller.mynewapp')
62
+ > AppleBot.app_id.create(manifest './manifest.json')
63
+ ```
64
+
65
+ ### CLI
66
+
67
+ AppleBot installs an `applebot` command, which you can explore with `-h` flags:
68
+
69
+ ```shell
70
+ $ applebot -h
71
+
72
+ Commands:
73
+ app:create Create App
74
+ app:reject_binary Reject the binary of an pending release
75
+ app:remove_from_sale Remove app from sale
76
+ app:update Update App
77
+ app_id:create Create App ID
78
+ app_id:delete Delete App ID
79
+ app_id:list List App IDs
80
+ help Display global or [command] help documentation.
81
+ profile:create Create Provisioning Profile
82
+ profile:download Download a Provisioning Profile
83
+ profile:list List Provisioning Profiles
84
+
85
+ Global Options:
86
+ --manifest FILE.json Use a JSON file to load options for each command
87
+ --username USERNAME Username to login to Apple Service, or $APPLEBOT_USERNAME
88
+ --password PASSWORD Password to login to Apple Service, or $APPLEBOT_PASSWORD
89
+ --format FORMAT Output format - ['json', 'pretty']
90
+ --verbose Verbose output
91
+ ```
92
+
93
+ #### Authentication
94
+
95
+ For every command, you can pass `--username` and `--password` flags to enter you auth credentials; you can also set `APPLEBOT_USERNAME` and `APPLEBOT_PASSWORD` environment variables.
96
+
97
+ ### Ruby
98
+
99
+ The Ruby library uses an `AppleBot` module, and its methods map to the CLI commands:
100
+
101
+ ```ruby
102
+ > require 'applebot'
103
+ => true
104
+ > AppleBot.app.create(options: here)
105
+ ```
106
+
107
+ #### Authentication
108
+
109
+ The Ruby library has a few shortcuts for logging in to Apple services:
110
+
111
+ ```ruby
112
+ # pass as options
113
+ AppleBot.app.create(username: "username", password: "password")
114
+
115
+ # run in block
116
+ AppleBot.with_credentials(username: "username", password: "password") do
117
+ AppleBot.app.create(options)
118
+ end
119
+
120
+ # set globally
121
+ AppleBot.set_credentials(username: "username", password: "password")
122
+ ```
123
+
124
+
125
+ ### Output
126
+
127
+ The `:list` commands are meant to return some data. If you're using the Ruby library, you'll receive an `Array` when the command is done; if you're using the CLI, the last line will output a JSON object with one entry.
128
+
129
+ ```ruby
130
+ AppleBot.app_ids.all
131
+ => ["com.usepropeller.myapp"]
132
+ ```
133
+
134
+ ```bash
135
+ $ applebot app_ids:all
136
+ {"app_ids": ["com.usepropeller.myapp"]}
137
+ ```
138
+
139
+ If you're using any other command (which generally create side-effects), the end result will be `true` in Ruby, or exit code 0 on the CLI.
140
+
141
+ #### Verbose & Pretty Output
142
+
143
+ You can base a `--verbose` flag (or a `verbose: true` option in Ruby) to see all of the output as each script processes. There are two output formats, `json` and `pretty`, which you are set with either the `--format` flag or `format: 'format_string'` in Ruby.
144
+
145
+ ## Contact
146
+
147
+ [Clay Allsopp](http://clayallsopp.com/)
148
+ - [clay@usepropeller.com](mailto:clay@usepropeller.com)
149
+ - [@clayallsopp](https://twitter.com/clayallsopp)
150
+
151
+ ## License
152
+
153
+ AppleBot is available under the MIT license. See the [LICENSE](LICENSE) file for more info.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "bundler/setup"
3
+
4
+ require './lib/applebot.rb'
5
+
6
+ require 'dotenv'
7
+ Dotenv.load
8
+
9
+ task :console do
10
+ require 'irb'
11
+ ARGV.clear
12
+ IRB.start
13
+ end
data/applebot.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "applebot/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "applebot"
8
+ s.license = "MIT"
9
+ s.authors = ["Clay Allsopp"]
10
+ s.email = "clay@usepropeller.com"
11
+ s.homepage = AppleBot::WEBSITE
12
+ s.version = AppleBot::VERSION
13
+ s.summary = "AppleBot"
14
+ s.description = AppleBot::DESCRIPTION
15
+
16
+ s.add_dependency "commander", "~> 4.1.2"
17
+ s.add_dependency "activesupport", ">= 3.2"
18
+ s.add_dependency "terminal-table", ">= 1.4.0"
19
+
20
+ s.add_development_dependency "rake"
21
+ s.add_development_dependency "dotenv"
22
+
23
+ s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
data/bin/applebot ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'commander/import'
4
+
5
+ applebot = File.join(File.dirname(__FILE__), '..', 'lib', 'applebot')
6
+ require applebot
7
+
8
+ HighLine.track_eof = false # Fix for built-in Ruby
9
+ Signal.trap("INT") {} # Suppress backtrace when exiting command
10
+
11
+ program :name, 'AppleBot'
12
+ program :version, AppleBot::VERSION
13
+ program :description, AppleBot::DESCRIPTION
14
+
15
+ program :help, 'Author', 'Clay Allsopp <clay@usepropeller.com>'
16
+ program :help, 'Website', AppleBot::WEBSITE
17
+ program :help_formatter, :compact
18
+
19
+ default_command :help
20
+
21
+ global_option('--manifest FILE.json', 'Use a JSON file to load options for each command') { |file|
22
+ $manifest_file = file
23
+ }
24
+
25
+ global_option('--username USERNAME', 'Username to login to Apple Service, or $APPLEBOT_USERNAME') { |username|
26
+ $username = username
27
+ }
28
+
29
+ global_option('--password PASSWORD', 'Password to login to Apple Service, or $APPLEBOT_PASSWORD') { |password|
30
+ $password = password
31
+ }
32
+
33
+ global_option('--format FORMAT', "Output format - 'json' or 'pretty'") { |format|
34
+ $format = format
35
+ }
36
+
37
+ global_option('--verbose', "Verbose output") {|verbose|
38
+ $verbose = verbose
39
+ }
40
+
41
+ AppleBot.commands.each do |apple_command|
42
+ command apple_command.cli_command.to_sym do |c|
43
+ c.syntax = "applebot #{apple_command.cli_command} [options]"
44
+ c.description = apple_command.description
45
+ c.summary = apple_command.description
46
+
47
+ (apple_command.options.required + apple_command.options.optional).each do |apple_option|
48
+ c.option "--#{apple_option.key} VALUE", apple_option.cli_description
49
+ end
50
+
51
+ c.action do |args, options|
52
+ user_options = {
53
+ manifest: $manifest_file,
54
+ format: $format || 'pretty',
55
+ verbose: $verbose,
56
+ print_result: true
57
+ }.merge(options.__hash__)
58
+
59
+ credentials = {username: $username, password: $password}
60
+
61
+ ret_value = false
62
+ AppleBot.with_credentials(credentials) do |ab|
63
+ ret_value = ab.run_command(apple_command.ruby_method, user_options)
64
+ end
65
+ $username = nil
66
+ $password = nil
67
+ $manifest_file = nil
68
+ $format = nil
69
+ $verbose = nil
70
+ ret_value
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "Test App 9007",
3
+ "username": "clay+applestore@usepropeller.com",
4
+ "password": "B0nnyB3ar",
5
+ "app_id": "com.usepropeller.test.test9007"
6
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "username": "clay+applestore@usepropeller.com",
3
+ "password": "B0nnyB3ar",
4
+ "title": "Test App 9007",
5
+ "sku": "TEST_APP_9007",
6
+ "id": "com.usepropeller.test.test9007",
7
+ "price_tier": "free",
8
+ "app_version": "1.0.0",
9
+ "keywords": "this,thing,cool",
10
+ "support_url": "http://usepropeller.com",
11
+ "description": "My cool app",
12
+ "simulated_gambling": "none",
13
+ "horror_fear_themes": "none",
14
+ "primary_category": "book",
15
+ "secondary_category": "catalogs",
16
+ "first_name": "Clay",
17
+ "last_name": "Allsopp",
18
+ "email": "Clay.allsopp@gmail.com",
19
+ "phone": "9197937595",
20
+ "copyright": "Thing 2013",
21
+ "large_icon": "/Users/clayallsopp/Desktop/itunes_1.jpg",
22
+ "screenshots_35": "[\"/Users/clayallsopp/Projects/Apptory/iOS/profiles/apps/705/screenshots/products_35.png\"]",
23
+ "screenshots_4": "[\"/Users/clayallsopp/Projects/Apptory/iOS/profiles/apps/705/screenshots/products_4.png\"]"
24
+ }
@@ -0,0 +1,44 @@
1
+ module AppleBot
2
+ class CommandProxy
3
+ class_attribute :proxies
4
+ self.proxies = {}
5
+
6
+ attr_accessor :namespace, :commands
7
+
8
+ def self.for(command)
9
+ proxies[command.namespace] ||= new(command.namespace)
10
+ proxy = proxies[command.namespace]
11
+ proxy.add_command(command)
12
+ proxy
13
+ end
14
+
15
+ def initialize(namespace)
16
+ @namespace = namespace
17
+ @commands = []
18
+ end
19
+
20
+ def add_command(command)
21
+ return if @commands.include?(command.action)
22
+ @commands << command.action
23
+ define_singleton_method(command.action, ->(options = {}) {
24
+ AppleBot.run_command(command.file_name, options)
25
+ })
26
+ end
27
+
28
+ def attach_to(klass)
29
+ return if klass.respond_to?(self.namespace)
30
+ namespace = self.namespace
31
+ klass.define_singleton_method(namespace) {
32
+ return AppleBot::CommandProxy.proxies[namespace]
33
+ }
34
+ end
35
+
36
+ def inspect
37
+ s = self.to_s.gsub(">", " ")
38
+ s << "commands: #{@commands.join(', ')}"
39
+ s << ">"
40
+ s
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,74 @@
1
+ module AppleBot
2
+ mattr_accessor :commands
3
+
4
+ class Command < Struct.new(:file_name, :namespace, :action, :description, :options)
5
+ def initialize(*properties)
6
+ options = properties.delete_at -1
7
+ super(*properties)
8
+ self.options = CommandOptionSet.new(options['required'], options['optional'])
9
+ end
10
+
11
+ def self.load_all!
12
+ commands_file_path = File.join(AppleBot.phantom_scripts_path, '_commands.json')
13
+ commands = JSON.parse(IO.read(commands_file_path))
14
+ AppleBot.commands = commands.keys.map {|file|
15
+ command_config = commands[file]
16
+ args = [file] + [
17
+ command_config['namespace'], command_config['action'],
18
+ command_config['description'], command_config['options']
19
+ ]
20
+ Command.new(*args)
21
+ }
22
+ end
23
+
24
+ def cli_command
25
+ "#{namespace}:#{action}"
26
+ end
27
+
28
+ def ruby_method
29
+ "#{action}_#{namespace}"
30
+ end
31
+ end
32
+
33
+ class CommandOptionSet < Struct.new(:required, :optional)
34
+ def initialize(required, optional)
35
+ fix_batch_options = lambda { |set, extras|
36
+ set.map {|o|
37
+ if o['batch']
38
+ o['keys'].map {|k|
39
+ h = {
40
+ 'key' => k
41
+ }.merge(o)
42
+ h.delete 'keys'
43
+ h.delete 'batch'
44
+ h
45
+ }
46
+ else
47
+ o
48
+ end
49
+ }.flatten.map {|o|
50
+ o = o.merge(extras)
51
+ CommandOption.new(o['key'], o['description'], o['values'], o['default'], o['required'])
52
+ }
53
+ }
54
+ required = fix_batch_options.call(required, {'required' => true}) if required
55
+ required ||= []
56
+ optional = fix_batch_options.call(optional, {'required' => false}) if optional
57
+ optional ||= []
58
+ super(required, optional)
59
+ end
60
+ end
61
+
62
+ class CommandOption < Struct.new(:key, :description, :values, :default, :required)
63
+ def cli_description
64
+ s = ""
65
+ s << "REQUIRED " if required
66
+ s << "#{description}"
67
+ s.tap do |d|
68
+ d << " - VALUES: #{values}" if values
69
+ d << " - DEFAULT: #{default}" if default
70
+ end
71
+ end
72
+ end
73
+ end
74
+ AppleBot::Command.load_all!
@@ -0,0 +1,97 @@
1
+ module AppleBot
2
+ class AppleBotError < StandardError
3
+ class_attribute :message
4
+ self.message = "Unspecified AppleBot error occured"
5
+ attr_reader :output, :html
6
+
7
+ def initialize(error, output = [], message = nil)
8
+ super(self.class.message)
9
+ output ||= []
10
+ output = output.map(&:strip).reject(&:blank?)
11
+ @html = output.select {|n|
12
+ is_debug_html?(n)
13
+ }.map { |s|
14
+ JSON.parse(s)['html']
15
+ }.first
16
+
17
+ @output = output.reject {|n|
18
+ is_debug_html?(n)
19
+ }
20
+
21
+ full_backtrace = (@output.map { |o|
22
+ "#{o}:in `AppleBot'"
23
+ } + error.backtrace).compact
24
+ set_backtrace(full_backtrace)
25
+ end
26
+
27
+ def is_debug_html?(line)
28
+ line.include?('"event":"debug_html"')
29
+ end
30
+ end
31
+
32
+ class AppIdNotFound < AppleBotError
33
+ self.message = "Could not find App Bundle ID in the HTML"
34
+ end
35
+
36
+ class AppleMaintenanceMode < AppleBotError
37
+ self.message = "Apple is currently in maintenance mode"
38
+ end
39
+
40
+ class AuthenticationError < AppleBotError
41
+ self.message = "Provided Apple ID credentials were incorrect"
42
+ end
43
+
44
+ class MultipleProfileError < AppleBotError
45
+ self.message = "Attempted to re-create an existing profile"
46
+ end
47
+
48
+ class AppNameTakenError < AppleBotError
49
+ self.message = "The app name you entered is taken"
50
+ end
51
+
52
+ class SignalTermination < StandardError
53
+ def initialize(status)
54
+ super("The process was terminated with signal #{status.termsig} (#{Signal.signame(status.termsig)})")
55
+ end
56
+ end
57
+
58
+ MESSAGE_FRAGMENT_TO_ERROR = {
59
+ "could not find App ID in options" => AppIdNotFound,
60
+ 'class="maintenance"' => AppleMaintenanceMode,
61
+ 'iTunes Connect is temporarily unavailable' => AppleMaintenanceMode,
62
+ 'Your Apple ID or password was entered incorrectly' => AuthenticationError,
63
+ 'Multiple profiles found with the name' => MultipleProfileError,
64
+ 'The App Name you entered has already been used' => AppNameTakenError,
65
+ }
66
+
67
+ class AppleBotError
68
+ def self.for_output(output = [])
69
+ output ||= []
70
+
71
+ error_class = nil
72
+
73
+ output.each { |line|
74
+ MESSAGE_FRAGMENT_TO_ERROR.each { |message, klass|
75
+ if line.include?(message)
76
+ error_class = klass
77
+ break
78
+ end
79
+ }
80
+ break if error_class
81
+ }
82
+ error_class ||= AppleBotError
83
+
84
+ begin
85
+ # capture the Ruby-level stack trace
86
+ raise StandardError
87
+ rescue Exception => e
88
+ error_class.new(e, output)
89
+ end
90
+ end
91
+
92
+ def self.test
93
+ raise for_output(["something", "happened"])
94
+ end
95
+ end
96
+
97
+ end
@@ -0,0 +1,25 @@
1
+ module AppleBot
2
+ module_function
3
+
4
+ # ripped from mkmf source
5
+ def find_executable(bin, path = nil)
6
+ ext = ""
7
+ if File.expand_path(bin) == bin
8
+ return bin if File.executable?(bin)
9
+ ext and File.executable?(file = bin + ext) and return file
10
+ return nil
11
+ end
12
+ if path ||= ENV['PATH']
13
+ path = path.split(File::PATH_SEPARATOR)
14
+ else
15
+ path = %w[/usr/local/bin /usr/ucb /usr/bin /bin]
16
+ end
17
+ file = nil
18
+ path.each do |dir|
19
+ return file if File.executable?(file = File.join(dir, bin))
20
+ return file if ext and File.executable?(file << ext)
21
+ end
22
+ nil
23
+ end
24
+
25
+ end
@@ -0,0 +1,79 @@
1
+ module AppleBot
2
+ class Shell
3
+ def command(sys_command, verbose, format)
4
+ output = []
5
+ Open3.popen3(sys_command) do |stdin, stdout, stderr, wait_thr|
6
+ while line = (stdout.gets || stderr.gets)
7
+ output << line
8
+ if verbose === true
9
+ puts_format_line(line, format)
10
+ end
11
+ end
12
+ exit_status = wait_thr.value
13
+ output << JSON.generate(process_status: exit_status.inspect, thread: wait_thr.inspect)
14
+ if exit_status.termsig
15
+ raise SignalTermination.new(exit_status)
16
+ end
17
+ unless exit_status.success?
18
+ raise AppleBotError.for_output(output)
19
+ end
20
+ end
21
+
22
+ output.select {|o|
23
+ o.include?('"result":')
24
+ }.last
25
+ end
26
+
27
+ def puts_format_line(line, format)
28
+ json = JSON.parse(line)
29
+
30
+ case format.to_s
31
+ when 'json'
32
+ puts JSON.generate(json.except('normal_output'))
33
+ else
34
+ normal_output = json['normal_output']
35
+ return if normal_output.blank?
36
+ return if normal_output.include?("[phantom]")
37
+ puts(normal_output)
38
+ end
39
+ end
40
+
41
+ def result(output, format, print_result)
42
+ result = JSON.parse(output)['result']
43
+
44
+ case format.to_s
45
+ when 'pretty'
46
+ table(result).tap do |t|
47
+ puts t if print_result
48
+ end
49
+ true
50
+ else
51
+ result.tap do |json|
52
+ puts json if print_result
53
+ end
54
+ end
55
+ end
56
+
57
+ def table(data)
58
+ table = nil
59
+ if data.first && data.first[-1].is_a?(Hash)
60
+ headings = ['key'] + data.first[-1].keys
61
+ rows = data.to_a.map { |key_and_value|
62
+ row = []
63
+ headings.each do |heading|
64
+ if heading == 'key'
65
+ row << key_and_value[0]
66
+ else
67
+ row << key_and_value[1][heading]
68
+ end
69
+ end
70
+ row
71
+ }
72
+ table = Terminal::Table.new headings: headings, rows: rows
73
+ else
74
+ table = Terminal::Table.new rows: data.to_a
75
+ end
76
+ table
77
+ end
78
+ end
79
+ end