yourkarma 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +28 -0
  7. data/Rakefile +2 -0
  8. data/bin/yourkarma +8 -0
  9. data/lib/yourkarma.rb +5 -0
  10. data/lib/yourkarma/benchmarker.rb +36 -0
  11. data/lib/yourkarma/cli.rb +195 -0
  12. data/lib/yourkarma/client.rb +61 -0
  13. data/lib/yourkarma/device.rb +35 -0
  14. data/lib/yourkarma/version.rb +3 -0
  15. data/spec/cassettes/.keep +0 -0
  16. data/spec/cassettes/CLI/output/connected_to_a_hotspot/.yml +86 -0
  17. data/spec/cassettes/CLI/output/connected_to_a_hotspot_without_an_assigned_WAN_IP_address/.yml +45 -0
  18. data/spec/cassettes/CLI/output/connected_to_the_internet/.yml +86 -0
  19. data/spec/cassettes/CLI/output/connected_to_the_internet_with_a_slow_connection/.yml +45 -0
  20. data/spec/cassettes/CLI/status_code/connected_to_a_hotspot/.yml +86 -0
  21. data/spec/cassettes/CLI/status_code/connected_to_a_hotspot_without_an_assigned_WAN_IP_address/.yml +45 -0
  22. data/spec/cassettes/CLI/status_code/connected_to_the_internet/.yml +86 -0
  23. data/spec/cassettes/CLI/status_code/connected_to_the_internet_with_a_slow_connection/.yml +45 -0
  24. data/spec/features/cli_spec.rb +77 -0
  25. data/spec/spec_helper.rb +22 -0
  26. data/spec/support/have_line_matching_matcher.rb +5 -0
  27. data/spec/support/vcr.rb +12 -0
  28. data/spec/units/benchmarker_spec.rb +50 -0
  29. data/spec/units/cli_argument_parser_spec.rb +26 -0
  30. data/spec/units/cli_reporter_spec.rb +61 -0
  31. data/spec/units/cli_spec.rb +41 -0
  32. data/spec/units/client_spec.rb +54 -0
  33. data/spec/units/device_spec.rb +21 -0
  34. data/what_karma.gemspec +30 -0
  35. metadata +167 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2a01341231ba782a9acafe01f78d90cdc20c2a4d
4
+ data.tar.gz: 1edbb26ae1729447750964fb557ae781ba2718d6
5
+ SHA512:
6
+ metadata.gz: 58a1e7efb614a6b5b617e1982b2dba09c3dbaf0988b1cb690272de2119a5274bae2fb45ec3d4c682ef67a462330cb96e14db1fa85e009bcf49facd27a976475d
7
+ data.tar.gz: c9ca35b5db6487a8c4516a8622bdd13d0de695f09d01707fca6ed7c27f8c8cdd8443b2fda0cc19e9ed8f8961b97814691bc0a277dd895b56bd0f3b2814969c9f
data/.gitignore ADDED
@@ -0,0 +1,22 @@
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
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yourkarma.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Greg Sterndale
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.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # yourkarma
2
+ ## Karma hotspot status client & CLI
3
+
4
+ Determine connectivity, battery and connection speed for your device.
5
+
6
+ ## Installation
7
+
8
+ $ gem install yourkarma
9
+
10
+ ## Usage
11
+
12
+ $ yourkarma
13
+
14
+ ## TODO
15
+
16
+ - Bar charts for connection speed and battery
17
+ - Option to poll until connected
18
+ - Display available data from yourkarma.com/dashboard
19
+ - Display updates/news from yourkarma.com/dashboard
20
+ - Display connected user count
21
+
22
+ ## Contributing
23
+
24
+ 1. Fork it ( https://github.com/[my-github-username]/yourkarma/fork )
25
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
26
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
27
+ 4. Push to the branch (`git push origin my-new-feature`)
28
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/yourkarma ADDED
@@ -0,0 +1,8 @@
1
+ #! /usr/bin/ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'yourkarma'
7
+
8
+ exit YourKarma::CLI.run(STDOUT, ARGV)
data/lib/yourkarma.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "yourkarma/version"
2
+ require "yourkarma/cli"
3
+
4
+ module YourKarma
5
+ end
@@ -0,0 +1,36 @@
1
+ require 'uri'
2
+ require 'timeout'
3
+ require 'net/http'
4
+ require 'benchmark'
5
+
6
+ module YourKarma
7
+ class Benchmarker
8
+ class ConnectionError < StandardError; end
9
+
10
+ HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
11
+ Errno::ETIMEDOUT, EOFError, Net::HTTPBadResponse,
12
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError]
13
+
14
+ DEFAULT_OPTIONS = {
15
+ timeout: 10,
16
+ benchmark: 'http://yourkarma.com/dashboard'
17
+ }
18
+
19
+ attr_accessor :options
20
+
21
+ def initialize(options = {})
22
+ self.options = DEFAULT_OPTIONS.merge(options)
23
+ end
24
+
25
+ def benchmark
26
+ uri = URI(options[:benchmark])
27
+ Timeout::timeout(options[:timeout]) do
28
+ return Benchmark.realtime do
29
+ Net::HTTP.get_response(uri)
30
+ end
31
+ end
32
+ rescue *HTTP_ERRORS => e
33
+ raise ConnectionError
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,195 @@
1
+ require 'optparse'
2
+ require_relative 'client'
3
+ require_relative 'device'
4
+ require_relative 'benchmarker'
5
+
6
+ module YourKarma
7
+ class CLI
8
+ attr_accessor :options
9
+
10
+ def self.run(io, arguments)
11
+ options = { io: io }
12
+ options.merge! ArgumentParser.new.parse(arguments)
13
+ self.new(options).run
14
+ rescue ArgumentParser::InvalidOption => e
15
+ options[:io].puts e
16
+ 1
17
+ end
18
+
19
+ def initialize(options = {})
20
+ self.options = options
21
+ end
22
+
23
+ def run
24
+ reporter = Reporter.new(options)
25
+ attrs = Client.new(options).get
26
+ device = Device.new(attrs)
27
+ benchmark = Benchmarker.new(options).benchmark
28
+ reporter.report_on(device, benchmark)
29
+ rescue Client::ConnectionError => e
30
+ reporter.report_connectivity_failure e.message
31
+ rescue Benchmarker::ConnectionError
32
+ reporter.report_benchmark_failure_on(device)
33
+ end
34
+
35
+ class Reporter
36
+ DEFAULT_OPTIONS = {
37
+ io: STDOUT
38
+ }
39
+ attr_accessor :options
40
+
41
+ def initialize(options = {})
42
+ self.options = DEFAULT_OPTIONS.merge(options)
43
+ end
44
+
45
+ def report_on(device, benchmark)
46
+ write "Connected to '#{device.ssid}' and online!"
47
+ if options[:verbose]
48
+ ceil = (benchmark * 10).ceil / 10.0
49
+ write "Benchmark took less than #{ceil} seconds."
50
+ end
51
+ 0
52
+ end
53
+
54
+ def report_connectivity_failure(message)
55
+ write "Not connected to a Karma hotspot."
56
+ write message if options[:verbose]
57
+ 1
58
+ end
59
+
60
+ def report_benchmark_failure_on(device)
61
+ if device.valid_ipaddress?
62
+ write "Connected to '#{device.ssid}' and online, but the tubes are slow!"
63
+ else
64
+ write "Connected to '#{device.ssid}' and acquiring IP address."
65
+ end
66
+ 1
67
+ end
68
+
69
+ private
70
+
71
+ def write(string)
72
+ options[:io].puts string
73
+ end
74
+ end
75
+
76
+ class ArgumentParser
77
+ class InvalidOption < StandardError
78
+ end
79
+
80
+ DEFAULT_OPTIONS = {
81
+ verbose: false
82
+ }
83
+
84
+ def parse(arguments)
85
+ options = DEFAULT_OPTIONS.dup
86
+ optparse = OptionParser.new do |opts|
87
+ opts.banner = <<-BANNER.gsub(/^\s{4}/, '')
88
+ Karma hotspot status
89
+ Usage: yourkarma [options]
90
+ Example: yourkarma --verbose
91
+ BANNER
92
+
93
+ opts.on('-u', '--url=URL', "Karma hotspot URL") do |url|
94
+ options[:url] = url
95
+ end
96
+ opts.on('-t', '--timeout=TIMEOUT', "Time to wait for HTTP response", Float) do |timeout|
97
+ options[:timeout] = timeout
98
+ end
99
+ opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
100
+ options[:verbose] = true
101
+ end
102
+ opts.on('-h', '--help', 'Display this screen') do
103
+ raise InvalidOption, opts
104
+ end
105
+ end
106
+
107
+ optparse.parse!(arguments)
108
+
109
+ options
110
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
111
+ raise InvalidOption, optparse
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ # require 'json'
118
+ # require 'uri'
119
+ # require 'net/http'
120
+ # require 'timeout'
121
+ # require 'benchmark'
122
+ # require 'pp'
123
+ #
124
+ # uri = URI('http://hotspot.yourkarma.com/api/status.json')
125
+ #
126
+ # VERBOSE = true
127
+ # VERY_VERBOSE = false
128
+ # TIMEOUT = 10
129
+ #
130
+ # def failure(message, exception, context = nil)
131
+ # if VERBOSE
132
+ # puts exception.class if VERY_VERBOSE
133
+ # puts exception
134
+ # puts context if context && VERY_VERBOSE
135
+ # end
136
+ # puts message
137
+ # exit 1
138
+ # end
139
+ #
140
+ # HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
141
+ # Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
142
+ # SocketError]
143
+ #
144
+ # begin
145
+ # response = nil
146
+ # Timeout::timeout(TIMEOUT) do
147
+ # response = Net::HTTP.get_response(uri)
148
+ # end
149
+ # parsed = JSON::load(response.body)
150
+ # rescue Timeout::Error => e
151
+ # failure "Request to Karma hotspot timed out.", e
152
+ # rescue *HTTP_ERRORS => e
153
+ # failure "Not connected to a Karma hotspot.", e
154
+ # rescue JSON::ParserError
155
+ # failure "Unexpected response from #{uri}.", e, parsed
156
+ # end
157
+ #
158
+ # begin
159
+ # message = "Network not connected."
160
+ #
161
+ # device = parsed.fetch "device"
162
+ # wifi = device.fetch "wifiinterface"
163
+ # ssid = wifi.fetch "ssid"
164
+ #
165
+ # message = "Unable to determine battery status."
166
+ #
167
+ # charging = device.fetch "charging"
168
+ # battery = device.fetch "batterypower"
169
+ # print "Battery #{battery}%. "
170
+ # puts charging ? "Charging." : "Not charging."
171
+ #
172
+ # message = "Connected to '#{ssid}', but WAN IP address is not assigned."
173
+ #
174
+ # wan = device.fetch "waninterface"
175
+ # ip_address = wan.fetch "ipaddress"
176
+ # rescue KeyError => e
177
+ # failure message, e, parsed
178
+ # end
179
+ #
180
+ # begin
181
+ # benchmark = nil
182
+ # Timeout::timeout(TIMEOUT) do
183
+ # benchmark = Benchmark.realtime do
184
+ # Net::HTTP.get_response(URI('https://www.google.com'))
185
+ # end
186
+ # end
187
+ # benchmark = (benchmark * 10).ceil / 10.0
188
+ # rescue TimeoutError => e
189
+ # failure "Connected to '#{ssid}' and online, but the tubes are slow!", e, parsed
190
+ # rescue *HTTP_ERRORS => e
191
+ # failure "Connected to '#{ssid}' and WAN IP address has be assigned, but request to Google failed.", e, parsed
192
+ # end
193
+ #
194
+ # puts "Connected to '#{ssid}' and online!"
195
+ # puts "Request to Google took less than #{benchmark} seconds." if VERBOSE
@@ -0,0 +1,61 @@
1
+ require 'uri'
2
+ require 'timeout'
3
+ require 'net/http'
4
+
5
+ module YourKarma
6
+ class Client
7
+ class ConnectionError < StandardError; end
8
+ class BadResponseError < StandardError; end
9
+
10
+ HTTP_ERRORS = [Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
11
+ Errno::ETIMEDOUT, EOFError, Net::HTTPBadResponse,
12
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError]
13
+
14
+ DEFAULT_OPTIONS = {
15
+ timeout: 10,
16
+ url: "http://hotspot.yourkarma.com/api/status.json",
17
+ }
18
+
19
+ attr_accessor :options
20
+
21
+ def initialize(options = DEFAULT_OPTIONS)
22
+ self.options = DEFAULT_OPTIONS.merge(options)
23
+ end
24
+
25
+ def get
26
+ uri = URI(options[:url])
27
+ response = nil
28
+ Timeout::timeout(options[:timeout]) do
29
+ response = Net::HTTP.get_response(uri)
30
+ end
31
+ JSON::load(response.body).fetch("device")
32
+ rescue *HTTP_ERRORS => e
33
+ raise ConnectionError, e.message
34
+ rescue JSON::ParserError => e
35
+ raise BadResponseError, e.message
36
+ end
37
+ end
38
+ end
39
+
40
+ # {
41
+ # "device"=>{
42
+ # "name"=>"IMW-C918W",
43
+ # "swversion"=>"R4855",
44
+ # "hwversion"=>"R06",
45
+ # "uptime"=>"P0Y0M0DT0H14M41S",
46
+ # "batterypower"=>100,
47
+ # "charging"=>false,
48
+ # "waninterface"=>{
49
+ # "macaddress"=>"001E312C42D0",
50
+ # "ipaddress"=>"74.60.178.162",
51
+ # "bsid"=>"00:00:02:06:04:30",
52
+ # "rssi"=>-59,
53
+ # "cinr"=>20,
54
+ # "connectionduration"=>"P0Y0M0DT0H9M54S"
55
+ # },
56
+ # "wifiinterface"=>{
57
+ # "ssid"=>"Karma Wi-Fi",
58
+ # "users"=>2
59
+ # }
60
+ # }
61
+ # }
@@ -0,0 +1,35 @@
1
+ require 'json'
2
+
3
+ module YourKarma
4
+ class Device
5
+ IPADDRESS_PATTERN = /^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/
6
+
7
+ attr_accessor *%i(name swversion hwversion uptime batterypower charging
8
+ macaddress ipaddress bsid rssi cinr connectionduration
9
+ ssid users)
10
+
11
+ def initialize(attrs = {})
12
+ self.attributes = attrs
13
+ end
14
+
15
+ def waninterface=(attrs)
16
+ self.attributes = attrs
17
+ end
18
+
19
+ def wifiinterface=(attrs)
20
+ self.attributes = attrs
21
+ end
22
+
23
+ def valid_ipaddress?
24
+ self.ipaddress && self.ipaddress.match(IPADDRESS_PATTERN)
25
+ end
26
+
27
+ private
28
+
29
+ def attributes=(attrs)
30
+ attrs.each_pair do |name, value|
31
+ self.public_send("#{name}=", value)
32
+ end
33
+ end
34
+ end
35
+ end