howami 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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