howami 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: c8ecc8d9b5061e6e73462466cc9bd93267ad5edd
4
+ data.tar.gz: 382fd3b19a1adc3c60393e04321f5e964f514306
5
+ SHA512:
6
+ metadata.gz: ea647e2f91f55fecf8c77e88cfe93ac362f849975d599cf33475ebc1222bcdbe53459a0a430ccd4c908b94d0c1d15e2edaf180514307422120c1149ffc99999f
7
+ data.tar.gz: 7051440a383be7d19f956d99aa09635dcb2f2dc5ffafc4144f4054390370cb69bdcf56023d86deeaa731f8f95e54d6d574793a55623dee93714c94b1266ceaa5
@@ -0,0 +1,8 @@
1
+ .DS_Store
2
+ results.html
3
+ pkg
4
+ html
5
+ .rvmrc
6
+ .ruby-version
7
+ Gemfile.lock
8
+ .bundle
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 2.0.0
6
+
@@ -0,0 +1,7 @@
1
+ ## Contributing
2
+
3
+ 1. Fork it
4
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
5
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
6
+ 4. Push to the branch (`git push origin my-new-feature`)
7
+ 5. Create new Pull Request
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in howami.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Matthew Rothenberg
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,36 @@
1
+ # howami ![fitbit-logo](http://f.cl.ly/items/2w0f0c0j1J1H1V171g2w/fitbit-32-blk-onwhite.jpg)
2
+
3
+ All unix people know the `whoami` command.
4
+
5
+ $ whoami
6
+ mroth
7
+
8
+ But what if you want to keep track of _how_ you are?
9
+
10
+ $ howami
11
+ Current weight: 138.8 pounds (12.97 % body fat)
12
+ Activity level: 2765 steps thus far today
13
+ Sleep last night: 5 hours, 41 min.
14
+
15
+ `howami` uses the Fitbit API to give you a quick CLI glance at your vital stats as synchronized from your nerdy quantified-self tracking devices.
16
+
17
+ [![Build Status](https://travis-ci.org/mroth/howami.png?branch=master)](https://travis-ci.org/mroth/howami)
18
+ [![Dependency Status](https://gemnasium.com/mroth/howami.png)](https://gemnasium.com/mroth/howami)
19
+
20
+ ## Installation
21
+
22
+ Just install it via rubygems:
23
+
24
+ $ gem install howami
25
+
26
+ Done.
27
+
28
+ ## Usage
29
+
30
+ Do `howami --auth` to complete login via the Fitbit API. At any point after that, you can do `howami` to see your stats.
31
+
32
+ ## What devices?
33
+
34
+ Since people keep asking, I personally use a [Fitbit Aria](http://www.fitbit.com/aria) scale to weigh myself, and a [Fitbit Flex](http://www.fitbit.com/flex) for activity/sleep tracking.
35
+
36
+ <sub>(And I've lost 22lbs. at time of writing this!)</sub>
@@ -0,0 +1,19 @@
1
+ = howami - DESCRIBE YOUR GEM
2
+
3
+ Author:: YOUR NAME (YOUR EMAIL)
4
+ Copyright:: Copyright (c) 2013 YOUR NAME
5
+
6
+
7
+ DESCRIBE YOUR GEM HERE
8
+
9
+ == Links
10
+
11
+ * {Source on Github}[LINK TO GITHUB]
12
+ * RDoc[LINK TO RDOC.INFO]
13
+
14
+ == Install
15
+
16
+ == Examples
17
+
18
+ == Contributing
19
+
@@ -0,0 +1,66 @@
1
+ def dump_load_path
2
+ puts $LOAD_PATH.join("\n")
3
+ found = nil
4
+ $LOAD_PATH.each do |path|
5
+ if File.exists?(File.join(path,"rspec"))
6
+ puts "Found rspec in #{path}"
7
+ if File.exists?(File.join(path,"rspec","core"))
8
+ puts "Found core"
9
+ if File.exists?(File.join(path,"rspec","core","rake_task"))
10
+ puts "Found rake_task"
11
+ found = path
12
+ else
13
+ puts "!! no rake_task"
14
+ end
15
+ else
16
+ puts "!!! no core"
17
+ end
18
+ end
19
+ end
20
+ if found.nil?
21
+ puts "Didn't find rspec/core/rake_task anywhere"
22
+ else
23
+ puts "Found in #{path}"
24
+ end
25
+ end
26
+ require 'bundler'
27
+ require 'rake/clean'
28
+
29
+ begin
30
+ require 'rspec/core/rake_task'
31
+ rescue LoadError
32
+ dump_load_path
33
+ raise
34
+ end
35
+
36
+ require 'cucumber'
37
+ require 'cucumber/rake/task'
38
+ gem 'rdoc' # we need the installed RDoc gem, not the system one
39
+ require 'rdoc/task'
40
+
41
+ include Rake::DSL
42
+
43
+ Bundler::GemHelper.install_tasks
44
+
45
+
46
+ RSpec::Core::RakeTask.new do |t|
47
+ # Put spec opts in a file named .rspec in root
48
+ end
49
+
50
+
51
+ CUKE_RESULTS = 'results.html'
52
+ CLEAN << CUKE_RESULTS
53
+ Cucumber::Rake::Task.new(:features) do |t|
54
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty --no-source -x"
55
+ t.fork = false
56
+ end
57
+
58
+ Rake::RDocTask.new do |rd|
59
+
60
+ rd.main = "README.rdoc"
61
+
62
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
63
+ end
64
+
65
+ task :default => [:spec,:features]
66
+
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'methadone'
5
+ require 'howami.rb'
6
+
7
+ class App
8
+ include Methadone::Main
9
+ include Methadone::CLILogging
10
+ include Howami
11
+
12
+ main do # Add args you want: |like,so|
13
+ # your program code here
14
+ # You can access CLI options via
15
+ # the options Hash
16
+
17
+ if options[:auth]
18
+ Authentication.new.do_interactive_auth
19
+ else
20
+ unless Configuration.has_valid_credentials?
21
+ fatal "You need to authenticate first, please run `howami --auth`"
22
+ exit 1
23
+ end
24
+ fit_obj = Fitbit.new
25
+ Howami::cli_run!(fit_obj)
26
+ end
27
+
28
+ end
29
+
30
+ # supplemental methods here
31
+ # def do_auth
32
+ # Howami::Authentication.new.do_interactive_auth
33
+ # end
34
+
35
+ # Declare command-line interface here
36
+
37
+ # description "one line description of your app"
38
+ #
39
+ # Accept flags via:
40
+ # on("--flag VAL","Some flag")
41
+ # options[flag] will contain VAL
42
+ #
43
+ # Specify switches via:
44
+ # on("--[no-]switch","Some switch")
45
+ on("--auth","Authenticate howami to the Fitbit API")
46
+ #
47
+ # Or, just call OptionParser methods on opts
48
+ #
49
+ # Require an argument
50
+ # arg :some_arg
51
+ #
52
+ # # Make an argument optional
53
+ # arg :optional_arg, :optional
54
+
55
+ version Howami::VERSION
56
+
57
+ use_log_level_option
58
+
59
+ go!
60
+ end
@@ -0,0 +1,27 @@
1
+ Feature: My bootstrapped app kinda works
2
+ In order to get going on coding my awesome app
3
+ I want to have aruba and cucumber setup
4
+ So I don't have to do it myself
5
+
6
+ Scenario: displaying help
7
+ When I get help for "howami"
8
+ Then the exit status should be 0
9
+ And the banner should be present
10
+ And the banner should document that this app takes options
11
+ And the following options should be documented:
12
+ |--version|
13
+ |--auth|
14
+ And the banner should document that this app takes no arguments
15
+
16
+ Scenario: graceful error message when not authenticated
17
+ Given howami is not authenticated
18
+ When I run `howami`
19
+ Then the output should contain "You need to authenticate first"
20
+ And the exit status should be 1
21
+
22
+ Scenario: clean configuring the app
23
+ When I run `howami --auth` interactively
24
+ And I type "6uaeuvtk7m5fe3q4o75invkbpf"
25
+ Then the output should contain "Go to http://www.fitbit.com/oauth/authorize"
26
+
27
+ Scenario: overwrites authentication when already authenticated
@@ -0,0 +1,7 @@
1
+ require 'howami'
2
+
3
+ Given(/^howami is not authenticated$/) do
4
+ XDG_CONFIG_HOME = ENV['XDG_CONFIG_HOME'] || File.join(ENV['HOME'], '.config')
5
+ CREDENTIALS_STORE = File.join( XDG_CONFIG_HOME, "howami", "credentials.yml")
6
+ FileUtils.rm CREDENTIALS_STORE if File.exist? CREDENTIALS_STORE
7
+ end
@@ -0,0 +1,20 @@
1
+ require 'aruba/cucumber'
2
+ require 'methadone/cucumber'
3
+
4
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
5
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
6
+
7
+ Before do
8
+ # Using "announce" causes massive warnings on 1.9.2
9
+ @puts = true
10
+ @original_rubylib = ENV['RUBYLIB']
11
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
12
+
13
+ @original_home = ENV['HOME']
14
+ ENV['HOME'] = File.expand_path( current_dir )
15
+ end
16
+
17
+ After do
18
+ ENV['RUBYLIB'] = @original_rubylib
19
+ ENV['HOME'] = @original_home
20
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'howami/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "howami"
8
+ gem.version = Howami::VERSION
9
+ gem.authors = ["Matthew Rothenberg"]
10
+ gem.email = ["mrothenberg@gmail.com"]
11
+ gem.description = %q{CLI tool to show a summary of Fitbit health statistics.}
12
+ gem.summary = %q{CLI tool to show a summary of Fitbit health statistics.}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency('rdoc')
21
+ gem.add_development_dependency('aruba', '~> 0.5.2')
22
+ gem.add_development_dependency('rake', '~> 10.0.3')
23
+ gem.add_development_dependency('rspec', '~> 2.13.0')
24
+ gem.add_development_dependency('fakefs')
25
+ gem.add_development_dependency('vcr', '~> 2.4.0')
26
+ gem.add_development_dependency('webmock', '~> 1.9.3')
27
+
28
+ gem.add_dependency('methadone', '~> 1.2.6')
29
+ gem.add_dependency('fitgem', '~> 0.6.1')
30
+ gem.add_dependency('colored')
31
+ end
@@ -0,0 +1,13 @@
1
+ require "howami/version"
2
+ require "howami/configuration"
3
+ require "howami/credentials"
4
+ require "howami/authentication"
5
+ require "howami/fitbit"
6
+
7
+ module Howami
8
+ def self.cli_run!(fit_obj)
9
+ puts "Current weight: #{fit_obj.weight_str} (#{fit_obj.fat_str} body fat)"
10
+ puts "Activity level: #{fit_obj.steps_str} thus far today"
11
+ puts "Sleep last night: #{fit_obj.sleep_str}"
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ require "methadone"
2
+ require "fitgem"
3
+
4
+ module Howami
5
+ class Authentication
6
+ include Methadone::CLILogging
7
+
8
+ def initialize
9
+ @client = Fitgem::Client.new({:consumer_key => CONSUMER_KEY, :consumer_secret => CONSUMER_SECRET})
10
+ end
11
+
12
+ def fetch_request_token!
13
+ @request_token = @client.request_token
14
+ end
15
+
16
+ def do_interactive_auth
17
+ fetch_request_token!
18
+
19
+ auth_url = "http://www.fitbit.com/oauth/authorize?oauth_token=#{@request_token.token}"
20
+ puts "Go to #{auth_url} and then enter the verifier code below and hit Enter"
21
+ verifier = gets.chomp
22
+ debug "Parsed user verifier as: #{verifier}"
23
+
24
+ access_token = self.get_access_token_with_verifier(verifier)
25
+ debug "Received user token is: "+access_token.token
26
+ debug "Received user secret is: "+access_token.secret
27
+
28
+ Configuration.store_credentials(access_token.token, access_token.secret)
29
+ end
30
+
31
+ def get_access_token_with_verifier(verifier)
32
+ @client.authorize(@request_token.token, @request_token.secret, { :oauth_verifier => verifier })
33
+ end
34
+
35
+ def self.authenticated_client
36
+ raise "No valid credentials stored!" unless Configuration.has_valid_credentials?
37
+ @auth_client ||= Fitgem::Client.new({
38
+ :consumer_key => CONSUMER_KEY,
39
+ :consumer_secret => CONSUMER_SECRET,
40
+ :token => Configuration.get_credentials[:user_token],
41
+ :secret => Configuration.get_credentials[:user_secret],
42
+ :user_id => "-"
43
+ })
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ require "methadone"
2
+
3
+ module Howami
4
+ class Configuration
5
+ include Methadone::CLILogging
6
+
7
+ XDG_CONFIG_HOME = ENV['XDG_CONFIG_HOME'] || File.join(ENV['HOME'], '.config')
8
+ CREDENTIALS_STORE = File.join( XDG_CONFIG_HOME, "howami", "credentials.yml")
9
+
10
+ def self.has_valid_credentials?
11
+ self.has_stored_credentials?
12
+ end
13
+
14
+ def self.store_credentials(user_token, user_secret)
15
+ #create proper XDG directory if doesn't exist
16
+ FileUtils.mkdir_p( File.dirname(CREDENTIALS_STORE) )
17
+
18
+ #store
19
+ File.open(CREDENTIALS_STORE, 'w') do |f|
20
+ f.puts({ :user_token => user_token, :user_secret => user_secret }.to_yaml)
21
+ end
22
+ end
23
+
24
+ def self.get_credentials
25
+ return nil if not self.has_stored_credentials?
26
+ YAML.load_file(CREDENTIALS_STORE)
27
+ end
28
+
29
+ protected
30
+ def self.has_stored_credentials?
31
+ File.exist? CREDENTIALS_STORE
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,4 @@
1
+ module Howami
2
+ CONSUMER_KEY = 'b116074974f44c12934d472fde4aac69'
3
+ CONSUMER_SECRET = 'f62a08f63b954b4bbd0f0de235f27edd'
4
+ end
@@ -0,0 +1,63 @@
1
+ require "methadone"
2
+
3
+ module Howami
4
+ class Fitbit
5
+ include Methadone::CLILogging
6
+
7
+ attr_reader :client
8
+
9
+ def initialize
10
+ @client = Authentication.authenticated_client
11
+ retrieve_values!
12
+ end
13
+
14
+ def retrieve_values!
15
+ @weights = @client.data_by_time_range('/body/log/weight', {:base_date => Date.today.to_s, :period => '1w'})['weight']
16
+ @fats = @client.data_by_time_range('/body/log/fat', {:base_date => Date.today.to_s, :period => '1w'})['fat']
17
+ @sleeps = @client.sleep_on_date('today')
18
+ @activities = @client.activities_on_date('today')
19
+ @weight_lbl = weight_lbl_value_187_workaround
20
+ end
21
+
22
+ # this method breaks the fitgem gem in ruby 1.8.7, so we just work around it and dont
23
+ # offer any i18n support for default units in 1.8.7, just assume AMERICA FUCK YEAH
24
+ def weight_lbl_value_187_workaround
25
+ return "pounds" if RUBY_VERSION =~ /^1\.8/
26
+ @client.label_for_measurement(:weight)
27
+ end
28
+
29
+ def weight_str
30
+ if @weights.last
31
+ return "#{@weights.last['weight']} #{@weight_lbl}"
32
+ else
33
+ return "* No weight data recorded in past 7 days."
34
+ end
35
+ end
36
+
37
+ def fat_str
38
+ if @fats.last
39
+ return "#{@fats.last['fat']} %"
40
+ else
41
+ return "* No body fat data recorded in past 7 days."
42
+ end
43
+ end
44
+
45
+ def sleep_str
46
+ return "no sleep data." if @sleeps['sleep'].count == 0
47
+
48
+ sleep_total_min = @sleeps['summary']['totalMinutesAsleep'].to_i
49
+ sleep_hours = sleep_total_min / 60
50
+ sleep_remainder_min = sleep_total_min % 60
51
+ return "#{sleep_hours} hours, #{sleep_remainder_min} min."
52
+ end
53
+
54
+ def steps
55
+ @activities['summary']['steps']
56
+ end
57
+
58
+ def steps_str
59
+ "#{self.steps} steps"
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module Howami
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'fakefs/safe'
3
+ include Howami
4
+
5
+ describe Authentication do
6
+
7
+ describe ".new" do
8
+ it "should be instantiated with any args" do
9
+ lambda { Authentication.new() }.should_not raise_error
10
+ lambda { Authentication.new( "moo ") }.should raise_error(ArgumentError)
11
+ end
12
+ end
13
+
14
+ describe ".authenticated_client" do
15
+ # it "should return a working fitgem client ready for action" √
16
+ it "should throw an error if proper credentials arent stored" do
17
+ FakeFS do
18
+ wipe_credentials!
19
+ lambda { Authentication.authenticated_client }.should raise_error
20
+ end
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,229 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.fitbit.com/1/user/-/body/log/weight/date/2013-05-10/1w.json
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - fitgem gem v0.6.1 (OAuth gem v0.4.7)
12
+ Accept-Language:
13
+ - en_US
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - '*/*'
18
+ Authorization:
19
+ - <OAUTH_AUTH_HEADER>
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Server:
26
+ - Apache-Coyote/1.1
27
+ Expires:
28
+ - Thu, 01 Jan 1970 00:00:00 GMT
29
+ Cache-Control:
30
+ - no-store, no-cache, must-revalidate
31
+ Pragma:
32
+ - no-cache
33
+ Set-Cookie:
34
+ - JSESSIONID=5908CBD729C81753C9D9824971D28B92.fitbit1; Path=/
35
+ Content-Type:
36
+ - application/json;charset=UTF-8
37
+ Content-Language:
38
+ - en-US
39
+ Content-Length:
40
+ - '728'
41
+ Date:
42
+ - Fri, 10 May 2013 14:59:45 GMT
43
+ body:
44
+ encoding: UTF-8
45
+ string: '{"weight":[{"bmi":22.01,"date":"2013-05-03","logId":1367578013000,"time":"10:46:53","weight":140.56},{"bmi":21.91,"date":"2013-05-04","logId":1367659147000,"time":"09:19:07","weight":139.9},{"bmi":22.1,"date":"2013-05-05","logId":1367743206000,"time":"08:40:06","weight":141.11},{"bmi":21.81,"date":"2013-05-06","logId":1367829101000,"time":"08:31:41","weight":139.28},{"bmi":21.89,"date":"2013-05-07","logId":1367913082000,"time":"07:51:22","weight":139.79},{"bmi":21.93,"date":"2013-05-08","logId":1368003855000,"time":"09:04:15","weight":140.08},{"bmi":21.84,"date":"2013-05-09","logId":1368089755000,"time":"08:55:55","weight":139.5},{"bmi":21.73,"date":"2013-05-10","logId":1368170956000,"time":"07:29:16","weight":138.8}]}'
46
+ http_version:
47
+ recorded_at: Fri, 10 May 2013 14:59:46 GMT
48
+ - request:
49
+ method: get
50
+ uri: http://api.fitbit.com/1/user/-/body/log/fat/date/2013-05-10/1w.json
51
+ body:
52
+ encoding: US-ASCII
53
+ string: ''
54
+ headers:
55
+ User-Agent:
56
+ - fitgem gem v0.6.1 (OAuth gem v0.4.7)
57
+ Accept-Language:
58
+ - en_US
59
+ Accept-Encoding:
60
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
61
+ Accept:
62
+ - '*/*'
63
+ Authorization:
64
+ - <OAUTH_AUTH_HEADER>
65
+ response:
66
+ status:
67
+ code: 200
68
+ message: OK
69
+ headers:
70
+ Server:
71
+ - Apache-Coyote/1.1
72
+ Expires:
73
+ - Thu, 01 Jan 1970 00:00:00 GMT
74
+ Cache-Control:
75
+ - no-store, no-cache, must-revalidate
76
+ Pragma:
77
+ - no-cache
78
+ Set-Cookie:
79
+ - JSESSIONID=6B48DC79806B3131CC24091D8BD4F499.fitbit1; Path=/
80
+ Content-Type:
81
+ - application/json;charset=UTF-8
82
+ Content-Language:
83
+ - en-US
84
+ Content-Length:
85
+ - '600'
86
+ Date:
87
+ - Fri, 10 May 2013 14:59:44 GMT
88
+ body:
89
+ encoding: UTF-8
90
+ string: '{"fat":[{"date":"2013-05-03","fat":13.39,"logId":1367578013000,"time":"10:46:53"},{"date":"2013-05-04","fat":12.5,"logId":1367659147000,"time":"09:19:07"},{"date":"2013-05-05","fat":13.37,"logId":1367743206000,"time":"08:40:06"},{"date":"2013-05-06","fat":13.15,"logId":1367829101000,"time":"08:31:41"},{"date":"2013-05-07","fat":12.81,"logId":1367913082000,"time":"07:51:22"},{"date":"2013-05-08","fat":12.95,"logId":1368003855000,"time":"09:04:15"},{"date":"2013-05-09","fat":12.94,"logId":1368089755000,"time":"08:55:55"},{"date":"2013-05-10","fat":12.97,"logId":1368170956000,"time":"07:29:16"}]}'
91
+ http_version:
92
+ recorded_at: Fri, 10 May 2013 14:59:46 GMT
93
+ - request:
94
+ method: get
95
+ uri: http://api.fitbit.com/1/user/-/sleep/date/2013-05-10.json
96
+ body:
97
+ encoding: US-ASCII
98
+ string: ''
99
+ headers:
100
+ User-Agent:
101
+ - fitgem gem v0.6.1 (OAuth gem v0.4.7)
102
+ Accept-Language:
103
+ - en_US
104
+ Accept-Encoding:
105
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
106
+ Accept:
107
+ - '*/*'
108
+ Authorization:
109
+ - <OAUTH_AUTH_HEADER>
110
+ response:
111
+ status:
112
+ code: 200
113
+ message: OK
114
+ headers:
115
+ Server:
116
+ - Apache-Coyote/1.1
117
+ Expires:
118
+ - Thu, 01 Jan 1970 00:00:00 GMT
119
+ Cache-Control:
120
+ - no-store, no-cache, must-revalidate
121
+ Pragma:
122
+ - no-cache
123
+ Set-Cookie:
124
+ - JSESSIONID=D3FEACFED7A7120A8CA8A284EF9918C1.fitbit1; Path=/
125
+ Content-Type:
126
+ - application/json;charset=UTF-8
127
+ Content-Language:
128
+ - en-US
129
+ Content-Length:
130
+ - '6999'
131
+ Date:
132
+ - Fri, 10 May 2013 14:59:44 GMT
133
+ body:
134
+ encoding: UTF-8
135
+ string: '{"sleep":[{"awakeningsCount":7,"duration":11100000,"efficiency":91,"isMainSleep":true,"logId":35846905,"minuteData":[{"dateTime":"03:50:00","value":"3"},{"dateTime":"03:51:00","value":"3"},{"dateTime":"03:52:00","value":"3"},{"dateTime":"03:53:00","value":"3"},{"dateTime":"03:54:00","value":"3"},{"dateTime":"03:55:00","value":"3"},{"dateTime":"03:56:00","value":"2"},{"dateTime":"03:57:00","value":"1"},{"dateTime":"03:58:00","value":"1"},{"dateTime":"03:59:00","value":"1"},{"dateTime":"04:00:00","value":"1"},{"dateTime":"04:01:00","value":"1"},{"dateTime":"04:02:00","value":"1"},{"dateTime":"04:03:00","value":"1"},{"dateTime":"04:04:00","value":"1"},{"dateTime":"04:05:00","value":"1"},{"dateTime":"04:06:00","value":"1"},{"dateTime":"04:07:00","value":"1"},{"dateTime":"04:08:00","value":"1"},{"dateTime":"04:09:00","value":"1"},{"dateTime":"04:10:00","value":"1"},{"dateTime":"04:11:00","value":"1"},{"dateTime":"04:12:00","value":"1"},{"dateTime":"04:13:00","value":"1"},{"dateTime":"04:14:00","value":"1"},{"dateTime":"04:15:00","value":"1"},{"dateTime":"04:16:00","value":"1"},{"dateTime":"04:17:00","value":"1"},{"dateTime":"04:18:00","value":"1"},{"dateTime":"04:19:00","value":"1"},{"dateTime":"04:20:00","value":"1"},{"dateTime":"04:21:00","value":"1"},{"dateTime":"04:22:00","value":"1"},{"dateTime":"04:23:00","value":"1"},{"dateTime":"04:24:00","value":"1"},{"dateTime":"04:25:00","value":"1"},{"dateTime":"04:26:00","value":"1"},{"dateTime":"04:27:00","value":"1"},{"dateTime":"04:28:00","value":"1"},{"dateTime":"04:29:00","value":"1"},{"dateTime":"04:30:00","value":"1"},{"dateTime":"04:31:00","value":"1"},{"dateTime":"04:32:00","value":"1"},{"dateTime":"04:33:00","value":"1"},{"dateTime":"04:34:00","value":"1"},{"dateTime":"04:35:00","value":"1"},{"dateTime":"04:36:00","value":"1"},{"dateTime":"04:37:00","value":"1"},{"dateTime":"04:38:00","value":"1"},{"dateTime":"04:39:00","value":"1"},{"dateTime":"04:40:00","value":"1"},{"dateTime":"04:41:00","value":"1"},{"dateTime":"04:42:00","value":"1"},{"dateTime":"04:43:00","value":"1"},{"dateTime":"04:44:00","value":"2"},{"dateTime":"04:45:00","value":"1"},{"dateTime":"04:46:00","value":"1"},{"dateTime":"04:47:00","value":"1"},{"dateTime":"04:48:00","value":"1"},{"dateTime":"04:49:00","value":"1"},{"dateTime":"04:50:00","value":"1"},{"dateTime":"04:51:00","value":"1"},{"dateTime":"04:52:00","value":"1"},{"dateTime":"04:53:00","value":"1"},{"dateTime":"04:54:00","value":"1"},{"dateTime":"04:55:00","value":"1"},{"dateTime":"04:56:00","value":"1"},{"dateTime":"04:57:00","value":"1"},{"dateTime":"04:58:00","value":"1"},{"dateTime":"04:59:00","value":"1"},{"dateTime":"05:00:00","value":"1"},{"dateTime":"05:01:00","value":"1"},{"dateTime":"05:02:00","value":"1"},{"dateTime":"05:03:00","value":"1"},{"dateTime":"05:04:00","value":"1"},{"dateTime":"05:05:00","value":"1"},{"dateTime":"05:06:00","value":"1"},{"dateTime":"05:07:00","value":"1"},{"dateTime":"05:08:00","value":"1"},{"dateTime":"05:09:00","value":"1"},{"dateTime":"05:10:00","value":"1"},{"dateTime":"05:11:00","value":"1"},{"dateTime":"05:12:00","value":"1"},{"dateTime":"05:13:00","value":"1"},{"dateTime":"05:14:00","value":"1"},{"dateTime":"05:15:00","value":"1"},{"dateTime":"05:16:00","value":"1"},{"dateTime":"05:17:00","value":"1"},{"dateTime":"05:18:00","value":"1"},{"dateTime":"05:19:00","value":"1"},{"dateTime":"05:20:00","value":"1"},{"dateTime":"05:21:00","value":"1"},{"dateTime":"05:22:00","value":"1"},{"dateTime":"05:23:00","value":"1"},{"dateTime":"05:24:00","value":"1"},{"dateTime":"05:25:00","value":"1"},{"dateTime":"05:26:00","value":"1"},{"dateTime":"05:27:00","value":"1"},{"dateTime":"05:28:00","value":"1"},{"dateTime":"05:29:00","value":"1"},{"dateTime":"05:30:00","value":"1"},{"dateTime":"05:31:00","value":"1"},{"dateTime":"05:32:00","value":"1"},{"dateTime":"05:33:00","value":"1"},{"dateTime":"05:34:00","value":"1"},{"dateTime":"05:35:00","value":"1"},{"dateTime":"05:36:00","value":"1"},{"dateTime":"05:37:00","value":"1"},{"dateTime":"05:38:00","value":"1"},{"dateTime":"05:39:00","value":"1"},{"dateTime":"05:40:00","value":"1"},{"dateTime":"05:41:00","value":"1"},{"dateTime":"05:42:00","value":"1"},{"dateTime":"05:43:00","value":"1"},{"dateTime":"05:44:00","value":"1"},{"dateTime":"05:45:00","value":"1"},{"dateTime":"05:46:00","value":"1"},{"dateTime":"05:47:00","value":"1"},{"dateTime":"05:48:00","value":"1"},{"dateTime":"05:49:00","value":"1"},{"dateTime":"05:50:00","value":"1"},{"dateTime":"05:51:00","value":"1"},{"dateTime":"05:52:00","value":"1"},{"dateTime":"05:53:00","value":"1"},{"dateTime":"05:54:00","value":"1"},{"dateTime":"05:55:00","value":"1"},{"dateTime":"05:56:00","value":"1"},{"dateTime":"05:57:00","value":"1"},{"dateTime":"05:58:00","value":"1"},{"dateTime":"05:59:00","value":"1"},{"dateTime":"06:00:00","value":"1"},{"dateTime":"06:01:00","value":"1"},{"dateTime":"06:02:00","value":"1"},{"dateTime":"06:03:00","value":"2"},{"dateTime":"06:04:00","value":"2"},{"dateTime":"06:05:00","value":"2"},{"dateTime":"06:06:00","value":"2"},{"dateTime":"06:07:00","value":"2"},{"dateTime":"06:08:00","value":"2"},{"dateTime":"06:09:00","value":"1"},{"dateTime":"06:10:00","value":"2"},{"dateTime":"06:11:00","value":"2"},{"dateTime":"06:12:00","value":"1"},{"dateTime":"06:13:00","value":"1"},{"dateTime":"06:14:00","value":"1"},{"dateTime":"06:15:00","value":"1"},{"dateTime":"06:16:00","value":"1"},{"dateTime":"06:17:00","value":"1"},{"dateTime":"06:18:00","value":"1"},{"dateTime":"06:19:00","value":"1"},{"dateTime":"06:20:00","value":"1"},{"dateTime":"06:21:00","value":"1"},{"dateTime":"06:22:00","value":"1"},{"dateTime":"06:23:00","value":"1"},{"dateTime":"06:24:00","value":"1"},{"dateTime":"06:25:00","value":"1"},{"dateTime":"06:26:00","value":"1"},{"dateTime":"06:27:00","value":"2"},{"dateTime":"06:28:00","value":"1"},{"dateTime":"06:29:00","value":"1"},{"dateTime":"06:30:00","value":"1"},{"dateTime":"06:31:00","value":"2"},{"dateTime":"06:32:00","value":"1"},{"dateTime":"06:33:00","value":"1"},{"dateTime":"06:34:00","value":"1"},{"dateTime":"06:35:00","value":"1"},{"dateTime":"06:36:00","value":"1"},{"dateTime":"06:37:00","value":"1"},{"dateTime":"06:38:00","value":"1"},{"dateTime":"06:39:00","value":"1"},{"dateTime":"06:40:00","value":"1"},{"dateTime":"06:41:00","value":"1"},{"dateTime":"06:42:00","value":"1"},{"dateTime":"06:43:00","value":"1"},{"dateTime":"06:44:00","value":"1"},{"dateTime":"06:45:00","value":"1"},{"dateTime":"06:46:00","value":"2"},{"dateTime":"06:47:00","value":"2"},{"dateTime":"06:48:00","value":"1"},{"dateTime":"06:49:00","value":"1"},{"dateTime":"06:50:00","value":"2"},{"dateTime":"06:51:00","value":"2"},{"dateTime":"06:52:00","value":"2"},{"dateTime":"06:53:00","value":"1"},{"dateTime":"06:54:00","value":"2"}],"minutesAfterWakeup":1,"minutesAsleep":161,"minutesAwake":16,"minutesToFallAsleep":7,"startTime":"2013-05-10T03:50:00.000","timeInBed":185}],"summary":{"totalMinutesAsleep":161,"totalSleepRecords":1,"totalTimeInBed":185}}'
136
+ http_version:
137
+ recorded_at: Fri, 10 May 2013 14:59:46 GMT
138
+ - request:
139
+ method: get
140
+ uri: http://api.fitbit.com/1/user/-/profile.json
141
+ body:
142
+ encoding: US-ASCII
143
+ string: ''
144
+ headers:
145
+ User-Agent:
146
+ - fitgem gem v0.6.1 (OAuth gem v0.4.7)
147
+ Accept-Language:
148
+ - en_US
149
+ Accept-Encoding:
150
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
151
+ Accept:
152
+ - '*/*'
153
+ Authorization:
154
+ - <OAUTH_AUTH_HEADER>
155
+ response:
156
+ status:
157
+ code: 200
158
+ message: OK
159
+ headers:
160
+ Server:
161
+ - Apache-Coyote/1.1
162
+ Expires:
163
+ - Thu, 01 Jan 1970 00:00:00 GMT
164
+ Cache-Control:
165
+ - no-store, no-cache, must-revalidate
166
+ Pragma:
167
+ - no-cache
168
+ Set-Cookie:
169
+ - JSESSIONID=1B679215CCC4D720D8F0D2E0908C189C.fitbit1; Path=/
170
+ Content-Type:
171
+ - application/json;charset=UTF-8
172
+ Content-Language:
173
+ - en-US
174
+ Content-Length:
175
+ - '615'
176
+ Date:
177
+ - Fri, 10 May 2013 14:59:44 GMT
178
+ body:
179
+ encoding: UTF-8
180
+ string: '{"user":{"avatar":"http://cache.fitbit.com/DA2F8E91-BDC1-5BD1-1209-8AA23C6E2AB3_profile_100_square.jpg","city":"Brooklyn","country":"US","dateOfBirth":"1979-08-04","displayName":"Matthew","distanceUnit":"en_US","encodedId":"24DQFP","foodsLocale":"en_US","fullName":"Matthew
181
+ Rothenberg","gender":"MALE","glucoseUnit":"en_US","height":67.00787401574803,"heightUnit":"en_US","locale":"en_US","memberSince":"2013-01-17","nickname":"mroth","offsetFromUTCMillis":-14400000,"state":"NY","strideLengthRunning":0,"strideLengthWalking":0,"timezone":"America/New_York","waterUnit":"en_US","weight":138.8,"weightUnit":"en_US"}}'
182
+ http_version:
183
+ recorded_at: Fri, 10 May 2013 14:59:46 GMT
184
+ - request:
185
+ method: get
186
+ uri: http://api.fitbit.com/1/user/-/activities/date/2013-05-10.json
187
+ body:
188
+ encoding: US-ASCII
189
+ string: ''
190
+ headers:
191
+ User-Agent:
192
+ - fitgem gem v0.6.1 (OAuth gem v0.4.7)
193
+ Accept-Language:
194
+ - en_US
195
+ Accept-Encoding:
196
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
197
+ Accept:
198
+ - '*/*'
199
+ Authorization:
200
+ - <OAUTH_AUTH_HEADER>
201
+ response:
202
+ status:
203
+ code: 200
204
+ message: OK
205
+ headers:
206
+ Server:
207
+ - Apache-Coyote/1.1
208
+ Expires:
209
+ - Thu, 01 Jan 1970 00:00:00 GMT
210
+ Cache-Control:
211
+ - no-store, no-cache, must-revalidate
212
+ Pragma:
213
+ - no-cache
214
+ Set-Cookie:
215
+ - JSESSIONID=ECEDFFC89CE6BC7B3B89AF82D8DCEF61.fitbit1; Path=/
216
+ Content-Type:
217
+ - application/json;charset=UTF-8
218
+ Content-Language:
219
+ - en-US
220
+ Content-Length:
221
+ - '624'
222
+ Date:
223
+ - Fri, 10 May 2013 15:54:14 GMT
224
+ body:
225
+ encoding: UTF-8
226
+ string: '{"activities":[],"goals":{"activeScore":1000,"caloriesOut":2817,"distance":5,"steps":10000},"summary":{"activeScore":207,"activityCalories":277,"caloriesBMR":761,"caloriesOut":986,"distances":[{"activity":"total","distance":0.68},{"activity":"tracker","distance":0.68},{"activity":"loggedActivities","distance":0},{"activity":"veryActive","distance":0},{"activity":"moderatelyActive","distance":0.23},{"activity":"lightlyActive","distance":0.44},{"activity":"sedentaryActive","distance":0}],"fairlyActiveMinutes":13,"lightlyActiveMinutes":79,"marginalCalories":154,"sedentaryMinutes":437,"steps":1546,"veryActiveMinutes":0}}'
227
+ http_version:
228
+ recorded_at: Fri, 10 May 2013 15:54:17 GMT
229
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'fakefs/safe'
3
+ include Howami
4
+
5
+ describe Configuration do
6
+
7
+ before(:each) do
8
+ FakeFS.activate!
9
+ end
10
+ after(:each) do
11
+ FakeFS::FileSystem.clear
12
+ FakeFS::deactivate!
13
+ end
14
+
15
+ describe ".has_valid_credentials?" do
16
+ it "should return false when no tokens are stored" do
17
+ wipe_credentials!
18
+ Configuration.has_valid_credentials?.should be_false
19
+ end
20
+ it "should return false if tokens are invalid"
21
+ it "should return true if stored tokens are valid"
22
+ end
23
+
24
+ describe ".get_credentials" do
25
+ it "should return the credentials" do
26
+ Configuration.store_credentials( 'aaa', 'bbb' )
27
+ Configuration.get_credentials().should_not be(nil)
28
+ end
29
+ it "should not throw an exception if no credentials are stored in fs" do
30
+ lambda { Configuration.get_credentials() }.should_not raise_error(Errno::ENOENT)
31
+ end
32
+ it "should return nil if no credentials are stored in fs" do
33
+ Configuration.get_credentials().should be(nil)
34
+ end
35
+ end
36
+
37
+ describe ".store_credentials" do
38
+ it "should require credentials to be passed" do
39
+ lambda { Configuration.store_credentials() }.should raise_error(ArgumentError)
40
+ end
41
+ it "should verify the credentials passed are of proper format"
42
+ it "should store the credentials to a file" do
43
+ File.exist?(Configuration::CREDENTIALS_STORE).should be_false
44
+ Configuration.store_credentials( 'aaa', 'bbb' )
45
+ File.exist?(Configuration::CREDENTIALS_STORE).should be_true
46
+ end
47
+ it "should persist the credentials in a way that they match upon retrieval" do
48
+ Configuration.store_credentials( 'aaa', 'bbb' )
49
+ Configuration.get_credentials()[:user_token].should eq 'aaa'
50
+ Configuration.get_credentials()[:user_secret].should eq 'bbb'
51
+ end
52
+ it "should overwrite credentials if stored a second time" do
53
+ Configuration.store_credentials( 'aaa', 'aaa' )
54
+ Configuration.store_credentials( 'bbb', 'bbb' )
55
+ Configuration.get_credentials()[:user_token].should eq 'bbb'
56
+ Configuration.get_credentials()[:user_secret].should eq 'bbb'
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+ include Howami
3
+
4
+ describe Fitbit, :vcr => { :cassette_name => "fitbit" } do
5
+ #use a vcr cassette so we can assume a full populated and authenticated user record
6
+
7
+ # for testing on machines that dont have a valid condif (e.g. travis-ci)
8
+ # generate an invalid configuration and let us rely upon the VCR cassettes
9
+ # we don't use fakefs with this because it seems to screw up VCR
10
+ before(:all) do
11
+ FAKED_CONFIG_LOL = false
12
+ if !File.exist? Howami::Configuration::CREDENTIALS_STORE
13
+ Configuration.store_credentials( 'abc', 'xyz' )
14
+ FAKED_CONFIG_LOL = true
15
+ end
16
+ end
17
+ after(:all) do
18
+ FileUtils.rm Howami::Configuration::CREDENTIALS_STORE if FAKED_CONFIG_LOL
19
+ end
20
+
21
+ describe ".new" do
22
+ it "should be instantiated without any args" do
23
+ lambda { Fitbit.new() }.should_not raise_error
24
+ lambda { Fitbit.new( "moo ") }.should raise_error(ArgumentError)
25
+ end
26
+
27
+ it "should retrieve all the values it needs on instantiation"
28
+ end
29
+
30
+ context "output rendering" do
31
+ before(:each) { @fb = Fitbit.new }
32
+
33
+ describe "#weight_str" do
34
+ it "returns a nicely formatted string with the proper weight" do
35
+ @fb.weight_str.should be_kind_of(String)
36
+ @fb.weight_str.should match(/\d\d\d\.\d pounds/)
37
+ end
38
+ it "returns a nice error message if there is no weight from past week" do
39
+ @fb.instance_variable_set("@weights",[])
40
+ @fb.weight_str.should match(/No weight data recorded in past 7 days./)
41
+ end
42
+ it "adds an indicator if measurement is more than 24hrs old"
43
+ end
44
+ describe "#fat_str" do
45
+ it "returns a nicely formatted string with the current body fat" do
46
+ @fb.fat_str.should match(/\d\d\.\d\d %/)
47
+ end
48
+ it "returns a nice error message if there is no body fat measurement from past week" do
49
+ @fb.instance_variable_set("@fats",[])
50
+ @fb.fat_str.should match(/No body fat data recorded in past 7 days./)
51
+ end
52
+ end
53
+
54
+ describe "#sleep_str" do
55
+ it "returns a nicely formatted string with the previous nights sleep" do
56
+ @fb.sleep_str.should match(/\d hours, \d+ min./)
57
+ end
58
+ it "returns a nice error message if there is no sleep data for day" do
59
+ @fb.instance_variable_set("@sleeps", {"sleep"=>[], "summary"=>{"totalMinutesAsleep"=>0, "totalSleepRecords"=>0, "totalTimeInBed"=>0}} )
60
+ @fb.sleep_str.should match(/no sleep data./)
61
+ end
62
+ end
63
+
64
+ describe "#steps" do
65
+ it "returns the amount of steps that day as an integer" do
66
+ @fb.steps.should be_kind_of(Integer)
67
+ end
68
+ end
69
+
70
+ describe "#steps_str" do
71
+ it "returns a nicely formatted string with the current amount of daily steps" do
72
+ @fb.steps_str.should match(/\d+ steps/)
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,24 @@
1
+ require 'rspec'
2
+ require 'howami'
3
+ require 'vcr'
4
+
5
+ RSpec.configure do |config|
6
+ config.color_enabled = true
7
+ config.formatter = 'documentation'
8
+ # config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ end
10
+
11
+ VCR.configure do |c|
12
+ c.cassette_library_dir = 'spec/cassettes'
13
+ c.hook_into :webmock
14
+ c.configure_rspec_metadata!
15
+ c.default_cassette_options = { :record => :none, :match_requests_on => [:method, :uri] }
16
+ c.filter_sensitive_data('<OAUTH_AUTH_HEADER>') do |interaction|
17
+ interaction.request.headers['Authorization'].first
18
+ end
19
+ end
20
+
21
+ def wipe_credentials!
22
+ cred_store = Howami::Configuration::CREDENTIALS_STORE
23
+ FileUtils.rm cred_store if File.exist? cred_store
24
+ end
metadata ADDED
@@ -0,0 +1,216 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: howami
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Rothenberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aruba
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.5.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 10.0.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 10.0.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 2.13.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: fakefs
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: vcr
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 2.4.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 2.4.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 1.9.3
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.9.3
111
+ - !ruby/object:Gem::Dependency
112
+ name: methadone
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 1.2.6
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: 1.2.6
125
+ - !ruby/object:Gem::Dependency
126
+ name: fitgem
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: 0.6.1
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: 0.6.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: colored
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: CLI tool to show a summary of Fitbit health statistics.
154
+ email:
155
+ - mrothenberg@gmail.com
156
+ executables:
157
+ - howami
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - .gitignore
162
+ - .travis.yml
163
+ - CONTRIBUTING.md
164
+ - Gemfile
165
+ - LICENSE.txt
166
+ - README.md
167
+ - README.rdoc
168
+ - Rakefile
169
+ - bin/howami
170
+ - features/howami.feature
171
+ - features/step_definitions/howami_steps.rb
172
+ - features/support/env.rb
173
+ - howami.gemspec
174
+ - lib/howami.rb
175
+ - lib/howami/authentication.rb
176
+ - lib/howami/configuration.rb
177
+ - lib/howami/credentials.rb
178
+ - lib/howami/fitbit.rb
179
+ - lib/howami/version.rb
180
+ - spec/authentication_spec.rb
181
+ - spec/cassettes/fitbit.yml
182
+ - spec/configuration_spec.rb
183
+ - spec/fitbit_spec.rb
184
+ - spec/spec_helper.rb
185
+ homepage: ''
186
+ licenses: []
187
+ metadata: {}
188
+ post_install_message:
189
+ rdoc_options: []
190
+ require_paths:
191
+ - lib
192
+ required_ruby_version: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - '>='
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ required_rubygems_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ requirements: []
203
+ rubyforge_project:
204
+ rubygems_version: 2.0.0
205
+ signing_key:
206
+ specification_version: 4
207
+ summary: CLI tool to show a summary of Fitbit health statistics.
208
+ test_files:
209
+ - features/howami.feature
210
+ - features/step_definitions/howami_steps.rb
211
+ - features/support/env.rb
212
+ - spec/authentication_spec.rb
213
+ - spec/cassettes/fitbit.yml
214
+ - spec/configuration_spec.rb
215
+ - spec/fitbit_spec.rb
216
+ - spec/spec_helper.rb