TokiCLI 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 24126edaca561eab0623a449445132dddf6697a0
4
+ data.tar.gz: bec8ef8903fd53c79b8ef96696dc04fde69e9e7b
5
+ SHA512:
6
+ metadata.gz: 985a22eda651b23467d18db8e7198edd24c5454275a09958e31b4d38a74f9a4d44031e30033302dd7cba00a3485b0b7301470464f605ce94170d2e1f7a8ab2f3
7
+ data.tar.gz: 8168f8fa325678e6e4d332476ee0d889a9819bbcf9a917e0cf89d7442b29c5295350f049a0f1ad5f19168f69193fb723df3ff0354767d00dab3247970d71dbac
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ token
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in TokiCLI.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Eric Dejonckheere
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ # TokiCLI
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ $ gem install TokiCLI
8
+
9
+ ## Usage
10
+
11
+ TODO: Write usage instructions here
12
+
13
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'TokiCLI/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "TokiCLI"
8
+ spec.version = TokiCLI::VERSION
9
+ spec.authors = ["Eric Dejonckheere"]
10
+ spec.email = ["eric@aya.io"]
11
+ spec.summary = %q{Toki command-line client}
12
+ spec.description = %q{Toki command-line client: read your Toki data from the local database or from the App.net backup channel}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = '>= 1.9.3'
22
+
23
+ spec.add_dependency "thor", ">= 0.18"
24
+ spec.add_dependency "rest-client", ">= 1.6"
25
+ spec.add_dependency "amalgalite", ">= 1.3"
26
+ spec.add_dependency "terminal-table", ">= 1.4"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.6"
29
+ spec.add_development_dependency "rake"
30
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # Toki command-line client
4
+ # Author: Eric Dejonckheere (http://app.net/ericd)
5
+ # Toki by: Keita Kobayashi (http://app.net/keita)
6
+
7
+ $PROGRAM_NAME = 'toki'
8
+ require_relative '../lib/TokiCLI'
9
+
10
+ TokiCLI::App.start ARGV
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+ require 'ostruct'
3
+ require 'thor'
4
+ require 'amalgalite'
5
+ require 'terminal-table/import'
6
+ require_relative "TokiCLI/version"
7
+ require_relative "TokiCLI/app"
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ module TokiCLI
3
+ class App < Thor
4
+ package_name "TokiCLI"
5
+ %w{get_messages search_messages get_channels module status view}.each {|r| require_relative "#{r}"}
6
+
7
+ desc "total", "Shows the total usage of all apps"
8
+ option :adn, aliases: '-a', type: :boolean, desc: 'Select ADN channel as source (toki total -a)'
9
+ def total
10
+ if options[:adn]
11
+ toki = total_adn
12
+ else
13
+ toki = total_db
14
+ end
15
+ get_total(toki, options)
16
+ end
17
+
18
+ desc "auth", "Input your App.net token for authorization"
19
+ def auth
20
+ puts Status.paste_token
21
+ token = STDIN.gets.chomp!
22
+ unless token.nil?
23
+ save_token(token)
24
+ else
25
+ abort(Status.no_token)
26
+ end
27
+ puts Status.done
28
+ end
29
+
30
+ desc "log APP", "Shows the complete log for an app"
31
+ option :adn, aliases: '-a', type: :boolean, desc: 'Select ADN channel as source (toki total -a)'
32
+ def log(*args)
33
+ abort(Status.specify_name) if args.empty?
34
+ if options[:adn]
35
+ toki = total_adn
36
+ else
37
+ toki = total_db
38
+ end
39
+ asked = args[0]
40
+ synced = toki.get_content(options)
41
+ app_data = toki.get_app(asked, synced)
42
+ v = TokiCLI::View.new
43
+ puts v.app_table(asked, app_data)
44
+ puts "\n"
45
+ end
46
+
47
+ private
48
+
49
+ def total_adn
50
+ clear
51
+ puts Status.get_all
52
+ init_toki_adn
53
+ end
54
+
55
+ def total_db
56
+ clear
57
+ puts "getting data from db\n"
58
+ init_toki_db
59
+ end
60
+
61
+ def get_total(toki, options)
62
+ begin
63
+ apps = get_all(toki, options)
64
+ rescue Interrupt
65
+ puts Status.canceled
66
+ exit
67
+ rescue => e
68
+ puts Status.error(e)
69
+ exit
70
+ end
71
+ clear
72
+ puts TokiCLI::View.new.total_table(apps)
73
+ puts "\n"
74
+ end
75
+
76
+ def init_toki_db
77
+ TokiCLI::Toki.new(nil, nil)
78
+ end
79
+
80
+ def init_toki_adn(channel_id = get_channel_id)
81
+ TokiCLI::Toki.new("#{get_token}", channel_id)
82
+ end
83
+
84
+ def save_token(token)
85
+ File.write(Dir.home + '/.toki_token', token)
86
+ end
87
+ def get_token
88
+ if File.exist?(Dir.home + '/.toki_token')
89
+ File.read(Dir.home + '/.toki_token').chomp
90
+ else
91
+ abort Status.run_auth
92
+ end
93
+ end
94
+ def get_channel_id #TODO return ch['counts']['messages'] too
95
+ channels = ADNChannels::GetChannels.new(get_token).get_channels
96
+ channel_id = ''
97
+ channels.each do |ch|
98
+ if ch['type'] == 'us.kkob.toki.sync-b'
99
+ channel_id = ch['id']
100
+ break
101
+ end
102
+ end
103
+ return channel_id.to_i unless channel_id == ''
104
+ puts Status.no_channel
105
+ exit
106
+ end
107
+ def clear
108
+ puts "\e[H\e[2J"
109
+ end
110
+ def get_all(t, options)
111
+ t.all_data(t.get_content(options))
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,22 @@
1
+ require 'json'
2
+ require 'rest_client'
3
+ module ADNChannels
4
+ class GetChannels
5
+ def initialize(token)
6
+ @base_url = 'http://api.app.net'
7
+ @token = token
8
+ end
9
+ def get_channels
10
+ args = {:count => 200, :before_id => nil}
11
+ channels = []
12
+ loop do
13
+ @url = "#{@base_url}/users/me/channels?access_token=#{@token}&include_machine=1&include_message_annotations=1&include_deleted=0&include_html=0&count=#{args[:count]}&before_id=#{args[:before_id]}"
14
+ resp = JSON.parse(RestClient.get(@url))
15
+ resp['data'].each { |m| channels << m }
16
+ break unless resp['meta']['more']
17
+ args = {:count => 200, :before_id => resp['meta']['min_id']}
18
+ end
19
+ channels
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+ require 'rest_client'
3
+ module ADNChannels
4
+ class GetMessages
5
+ def initialize(token)
6
+ @base_url = 'http://api.app.net'
7
+ @token = token
8
+ end
9
+ def get_messages(channel_id)
10
+ args = {:count => 200, :before_id => nil}
11
+ messages = []
12
+ @index = 1
13
+ puts "Downloading synced objects (~1MB/page):\n\n"
14
+ loop do
15
+ puts "--Downloading from page #{@index}...\n"
16
+ @url = "#{@base_url}/channels/#{channel_id}/messages?access_token=#{@token}&include_machine=1&include_message_annotations=1&include_deleted=0&include_html=0&count=#{args[:count]}&before_id=#{args[:before_id]}"
17
+ begin
18
+ data = RestClient.get(@url)
19
+ rescue Interrupt
20
+ puts TokiCLI::Status.canceled
21
+ exit
22
+ end
23
+ resp = JSON.parse(data)
24
+ resp['data'].each { |m| messages << m }
25
+ break unless resp['meta']['more']
26
+ @index += 1
27
+ args = {:count => 200, :before_id => resp['meta']['min_id']}
28
+ end
29
+ messages
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+ module TokiCLI
3
+ class Toki
4
+ def initialize(token, channel_id)
5
+ @token = token
6
+ @channel_id = channel_id
7
+ end
8
+ def get_content(options)
9
+ synced = []
10
+ if options[:adn]
11
+ messages = get_adn_data
12
+ messages.reverse.each do |obj|
13
+ synced << make_obj_from_adn(obj)
14
+ end
15
+ else
16
+ content = get_db_data
17
+ content.each do |obj|
18
+ synced << make_obj_from_db(obj)
19
+ end
20
+ end
21
+ synced
22
+ end
23
+ def get_app(app, synced)
24
+ name_regex = /#{app.downcase}/
25
+ app_data = {}
26
+ totals = {}
27
+ synced.each do |obj|
28
+ next unless obj.app[:name].downcase =~ name_regex
29
+ unless totals[:app].nil?
30
+ totals[:app] = totals[:app] + obj.app[:total]
31
+ else
32
+ totals[:app] = obj.app[:total]
33
+ end
34
+ human = epoch_to_human(obj.app[:total])
35
+ app_data[obj.msg[:id]] = {
36
+ from: Time.at(obj.app[:active_from]).strftime("%Y/%m/%d %Hh:%Mm:%Ss"),
37
+ to: Time.at(obj.app[:active_to]).strftime("%Y/%m/%d %Hh:%Mm:%Ss"),
38
+ duration: "#{human[:hours]}h #{human[:minutes]}m #{human[:seconds]}s"
39
+ }
40
+ end
41
+ human = epoch_to_human(totals[:app])
42
+ app_data['total'] = "#{human[:hours]}h #{human[:minutes]}m #{human[:seconds]}s"
43
+ app_data
44
+ end
45
+ def all_data(synced)
46
+ totals = {}
47
+ synced.each do |obj|
48
+ unless totals[obj.app[:name]].nil?
49
+ totals[obj.app[:name]] = totals[obj.app[:name]] + obj.app[:total]
50
+ else
51
+ totals[obj.app[:name]] = obj.app[:total]
52
+ end
53
+ end
54
+ bundles = {}
55
+ sorted = totals.sort_by{ |k,v| v }
56
+ sorted.each do |name, total|
57
+ human = epoch_to_human(total)
58
+ if human[:hours] < 1
59
+ bundles[name] = "#{human[:minutes]}m #{human[:seconds]}s"
60
+ else
61
+ bundles[name] = "#{human[:hours]}h #{human[:minutes]}m #{human[:seconds]}s"
62
+ end
63
+ end
64
+ bundles
65
+ end
66
+
67
+ private
68
+
69
+ def epoch_to_human(epoch)
70
+ hours = epoch / 3600
71
+ minutes = (epoch / 60 - hours * 60)
72
+ seconds = (epoch - (minutes * 60 + hours * 3600))
73
+ {hours: hours, minutes: minutes, seconds: seconds}
74
+ end
75
+
76
+ def get_adn_data
77
+ channels = ADNChannels::GetMessages.new(@token) #ericd
78
+ channels.get_messages(@channel_id)
79
+ end
80
+
81
+ def get_db_data
82
+ db = open_db
83
+ data = db.execute("SELECT * FROM KKAppActivity")
84
+ data
85
+ end
86
+
87
+
88
+
89
+ def open_db
90
+ tmp_path = "#{Dir.home}/temp/toki/"
91
+ db_path = "#{Dir.home}/Library/Containers/us.kkob.Toki/Data/Documents/toki_data.sqlite3"
92
+ FileUtils.mkdir_p(tmp_path)
93
+ FileUtils.copy(db_path, "#{tmp_path}/toki_data.bak")
94
+ Amalgalite::Database.new(db_path)
95
+ end
96
+
97
+ def make_obj_from_adn(obj)
98
+ msg = {}
99
+ usr = {}
100
+ app = {}
101
+ d = obj['annotations'][0]['value']['d']
102
+ msg[:id] = obj['id']
103
+ msg[:thread] = obj['thread_id']
104
+ msg[:date] = parsed_date obj['created_at']
105
+ usr[:username] = obj['user']['username']
106
+ usr[:name] = obj['user']['name']
107
+ usr[:id] = obj['user']['id']
108
+ app[:id] = d['id']
109
+ app[:uuid] = d['UUID']
110
+ app[:name] = d['bundleIdentifier']
111
+ app[:active_to] = d['activeTo']
112
+ app[:active_from] = d['activeFrom']
113
+ app[:total] = d['totalSeconds']
114
+ OpenStruct.new(app: app, msg: msg, usr: usr)
115
+ end
116
+
117
+ def make_obj_from_db(obj)
118
+ # content is an array:
119
+ # id (INTEGER) 0, bundleIdentifier (VARCHAR) 1, activeFrom (INTEGER) 2, activeTo (INTEGER) 3, totalSeconds (INTEGER) 4, UUID (VARCHAR) 5, synced (INTEGER) 6, availableToSync (INTEGER) 7
120
+ msg = {}
121
+ usr = {}
122
+ app = {}
123
+ msg[:id] = rand(100000000)
124
+ msg[:thread] = msg[:id]
125
+ msg[:date] = Time.now
126
+ usr[:username] = ENV['USERNAME']
127
+ usr[:name] = usr[:username]
128
+ usr[:id] = obj[5]
129
+ app[:id] = obj[0]
130
+ app[:uuid] = obj[5]
131
+ app[:name] = obj[1]
132
+ app[:active_to] = obj[3]
133
+ app[:active_from] = obj[2]
134
+ app[:total] = obj[4]
135
+ OpenStruct.new(app: app, msg: msg, usr: usr)
136
+ end
137
+
138
+ def parsed_date(string)
139
+ "#{string[0...10]} #{string[11...19]}"
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,23 @@
1
+ require 'json'
2
+ require 'rest_client'
3
+ module ADNChannels
4
+ class SearchMessages
5
+ def initialize(token)
6
+ @base_url = 'http://api.app.net'
7
+ @token = token
8
+ end
9
+ def search_annotation(channel_id, query)
10
+ args = {:count => 200, :before_id => nil}
11
+ messages = []
12
+ loop do
13
+ @url = "#{@base_url}/channels/messages/search?query=#{query}&channel_ids=#{channel_id}&access_token=#{@token}&include_machine=1&include_message_annotations=1&include_deleted=0&count=#{args[:count]}&before_id=#{args[:before_id]}"
14
+ resp = JSON.parse(RestClient.get(@url))
15
+ resp['data'].each { |m| messages << m }
16
+ break unless resp['meta']['more']
17
+ args = {:count => 200, :before_id => resp['meta']['min_id']}
18
+ end
19
+ messages
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ module TokiCLI
3
+ class Status
4
+ def self.get_all
5
+ "Toki_CLI is crawling your Toki App.net channel to get your data.\nIt could take a while, please be patient.\n\n"
6
+ end
7
+ def self.canceled
8
+ "\n\nCanceled.\n\n"
9
+ end
10
+ def self.error(e)
11
+ "\n\nStopped. Error: \n#{e}\n\n"
12
+ end
13
+ def self.no_channel
14
+ "\n\nCan't find your Toki App.net channel, sorry.\n\n"
15
+ end
16
+ def self.paste_token
17
+ "\nPlease paste your App.net token:\n\n"
18
+ end
19
+ def self.no_token
20
+ "\nOops, I didn't get the token. Please try again.\n"
21
+ end
22
+ def self.done
23
+ "\nDone.\n"
24
+ end
25
+ def self.specify_name
26
+ "\nPlease specify the name of the app (or part of it).\n\n"
27
+ end
28
+ def self.run_auth
29
+ "\nPlease run `toki auth` first.\n\n"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module TokiCLI
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ module TokiCLI
3
+ class View
4
+ def total_table(list)
5
+ table = init_table
6
+ table.title = "Your apps monitored by Toki"
7
+ list.each do |k,v|
8
+ table << ["#{k}", "#{v}"]
9
+ end
10
+ table
11
+ end
12
+ def app_table(asked, app_data)
13
+ table = init_table
14
+ table.title = "Toki time tracking for '#{asked}'"
15
+ table.headings = ['From', 'To', 'Duration']
16
+ @index = 0
17
+ @length = app_data.length - 1
18
+ app_data.each do |id, log|
19
+ if id == 'total'
20
+ @total = log
21
+ next
22
+ end
23
+ @index += 1
24
+ table << ["#{log[:from]}", "#{log[:to]}", "#{log[:duration]}"]
25
+ table << :separator unless @index == @length
26
+ end
27
+ table << :separator
28
+ table << [{ :value => "Total: #{@total}", :colspan => 3, :alignment => :center }]
29
+ table
30
+ end
31
+
32
+ private
33
+
34
+ def init_table
35
+ Terminal::Table.new do |t|
36
+ t.style = { :width => 75 }
37
+ end
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: TokiCLI
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Eric Dejonckheere
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: amalgalite
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: terminal-table
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: 'Toki command-line client: read your Toki data from the local database
98
+ or from the App.net backup channel'
99
+ email:
100
+ - eric@aya.io
101
+ executables:
102
+ - toki
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - TokiCLI.gemspec
112
+ - bin/toki
113
+ - lib/TokiCLI.rb
114
+ - lib/TokiCLI/app.rb
115
+ - lib/TokiCLI/get_channels.rb
116
+ - lib/TokiCLI/get_messages.rb
117
+ - lib/TokiCLI/module.rb
118
+ - lib/TokiCLI/search_messages.rb
119
+ - lib/TokiCLI/status.rb
120
+ - lib/TokiCLI/version.rb
121
+ - lib/TokiCLI/view.rb
122
+ homepage: ''
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 1.9.3
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.2.2
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Toki command-line client
146
+ test_files: []
147
+ has_rdoc: