beeminder 0.2.9 → 0.2.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MGNjOWExYTVhMzY0ODc3NzRmYTg0ZmM3ZGRiYWI5MzRlZWY0ZTQwZA==
5
- data.tar.gz: !binary |-
6
- OTBkNmMyMzdmOGM5YTk3ZTJlYjI5ZjIxNmI0MTU2ZWEwYjIxYTFlNA==
2
+ SHA256:
3
+ metadata.gz: 93a795ea4da67f8f37467e4fba52f1ceaed16612f5ecccc8fb9bd11d0ea1a9ce
4
+ data.tar.gz: a9e21515c529f820ebf58a2278a3ca36e9b8a44eba59469366820c2f9e21763b
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NTcxNTBhNzE5Nzg2ZTUyMWI5OWZkNDZkMjY2OTk0Yjg5MmMwN2Q3MGNmZjhj
10
- OTcyODhkOWRjZWZmMGJjNWJhOTg2NjllNjQyYzg2NDMxZWUzZGNhYmFkMjdj
11
- ZmI1NjIxODg2ZjA3NzIwYjJhYTA4NmIxMGExMDk2YzY3ZTQ5Njg=
12
- data.tar.gz: !binary |-
13
- NDI1ZjdlZDhkZTEyMmFiZWU0MTk0NjA1NTY0MjNhMTZmNWE2NGFlOTBhNzVm
14
- MWE1YzhjNDhlODFkZTcyNmE1NjA2MzdhODc3NWE2N2UyZmE5NTJhZDU0ZjE1
15
- YTYyOTZkZWZhOWZkNDJhMzFhNDA1YmZjMjBlYTc1YmQ5NmM2OGU=
6
+ metadata.gz: 6269b4af7d7365c6d9e091f2734776af8402dcd5dfd852c19d63d8346417698d7785c526748b3283a40be1db68d7eb3739f588f1f520ec99f37c32382248d5fd
7
+ data.tar.gz: 9b313b7322ebc9dcc2876c141d6aec6bdf012ab016cdbc4e81361b98dcbba5a92efaa843dfe934f36ea58b3a6bc30378c11888f2dc4616203ec33a7691b84daa
data/beeminder.gemspec CHANGED
@@ -16,10 +16,10 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.require_paths = ["lib"]
18
18
 
19
- gem.add_dependency 'activesupport', ['>= 3.2', '< 5']
19
+ gem.add_dependency 'activesupport', ['>= 3.2', '< 7']
20
20
  gem.add_dependency 'chronic', '~> 0.7'
21
21
  gem.add_dependency 'json'
22
22
  gem.add_dependency 'highline', '~> 1.6'
23
- gem.add_dependency 'trollop', '~> 2'
23
+ gem.add_dependency 'optimist', '~> 3'
24
24
  gem.add_dependency 'tzinfo'
25
25
  end
data/bin/beemind CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  require 'chronic'
5
5
  require 'highline/import'
6
- require 'trollop'
6
+ require 'optimist'
7
7
  require 'yaml'
8
8
 
9
9
  # load library
@@ -18,7 +18,7 @@ else
18
18
  end
19
19
 
20
20
  usage = "usage: beemind goal value [comment]"
21
- opts = Trollop::options do
21
+ opts = Optimist::options do
22
22
  banner usage
23
23
 
24
24
  opt :config, "Path to config.", :type => :string, :default => "~/.beeminderrc"
@@ -27,7 +27,7 @@ opts = Trollop::options do
27
27
  opt :date, "Set a manual date. Uses Chronic syntax.", :type => :string, :default => "now"
28
28
  end
29
29
 
30
- Trollop::die usage if not (2..3).include?(ARGV.size) and not opts[:list]
30
+ Optimist::die usage if not (2..3).include?(ARGV.size) and not opts[:list]
31
31
  goal, value, comment = ARGV unless opts[:list]
32
32
 
33
33
  opts[:config] = File.expand_path opts[:config]
@@ -66,7 +66,7 @@ if opts[:list]
66
66
  end
67
67
  else
68
68
  date = Chronic.parse(opts[:date], :context => :past)
69
- Trollop::die "invalid date" if date.nil?
69
+ Optimist::die "invalid date" if date.nil?
70
70
 
71
71
  g = bee.goal goal
72
72
  dp = Beeminder::Datapoint.new :timestamp => date,
@@ -59,6 +59,18 @@ module Beeminder
59
59
 
60
60
  # @return [Beeminder::User] User that owns this goal.
61
61
  attr_reader :user
62
+
63
+ # @return [Array<Integer, Float, Float>] All road settings over time
64
+ attr_accessor :roadall
65
+
66
+ # @return [true|false] Whether the goal is currently frozen and therefore must be restarted before continuing to accept data.
67
+ attr_reader :frozen
68
+
69
+ # @return [Numeric] Where you are with respect to the yellow brick road (2 or more = above the road, 1 = top lane, -1 = bottom lane, -2 or less = below the road).
70
+ attr_reader :lane
71
+
72
+ # @return [Numeric] Good side of the road. I.e., the side of the road (+1/-1 = above/below) that makes you say “yay”.
73
+ attr_reader :yaw
62
74
 
63
75
  def initialize user, name_or_info
64
76
  @user = user
@@ -79,7 +91,7 @@ module Beeminder
79
91
  # Reload data from Beeminder.
80
92
  def reload
81
93
  info = @user.get "users/me/goals/#{@slug}.json"
82
- parse_info info
94
+ _parse_info info
83
95
  end
84
96
 
85
97
  # List of datapoints.
@@ -108,8 +120,9 @@ module Beeminder
108
120
  "secret" => @secret || false,
109
121
  "datapublic" => @datapublic || false,
110
122
  }
123
+ data['roadall'] = @roadall if @roadall
111
124
 
112
- @user.put "users/me/goals/#{@slug}.json", data
125
+ @user.put_document "users/me/goals/#{@slug}.json", data
113
126
  end
114
127
 
115
128
  # Send new road setting to Beeminder.
@@ -126,6 +139,20 @@ module Beeminder
126
139
 
127
140
  @user.post "users/me/goals/#{@slug}/dial_road.json", dials
128
141
  end
142
+
143
+ # Schedule a break.
144
+ # Adds two new entries to `roadall` reflecting the break.
145
+ # Use #update to actually update the goal.
146
+ #
147
+ # @param start_time [Time] when to start the break -- must be after the akrasia horizon
148
+ # @param end_time [Time] when to end the break
149
+ # @param rate [Float] the slope of the road during the break
150
+ def schedule_break start_time, end_time, rate = 0.0
151
+ check_break start_time, end_time
152
+
153
+ roadall.insert(-2, [start_time.to_i, nil, roadall.last.last])
154
+ roadall.insert(-2, [end_time.to_i, nil, rate])
155
+ end
129
156
 
130
157
  # Add one or more datapoints to the goal.
131
158
  #
@@ -172,6 +199,14 @@ module Beeminder
172
199
 
173
200
  private
174
201
 
202
+ def check_break start_time, end_time
203
+ akrasia_horizon = user.akrasia_horizon
204
+ fail ArgumentError, "break start can't be before the akrasia horizon (#{akrasia_horizon})" \
205
+ if start_time < akrasia_horizon
206
+ fail ArgumentError, 'break must start before it ends' \
207
+ unless end_time > start_time
208
+ end
209
+
175
210
  def _parse_info info
176
211
  # set variables
177
212
  info.each do |k,v|
@@ -184,6 +219,9 @@ module Beeminder
184
219
  @losedate = DateTime.strptime(@losedate.to_s, '%s').in_time_zone(@user.timezone) unless @losedate.nil?
185
220
  @updated_at = DateTime.strptime(@updated_at.to_s, '%s').in_time_zone(@user.timezone)
186
221
  @curdate = DateTime.strptime(@curdate.to_s, '%s').in_time_zone(@user.timezone) unless @curdate.nil?
222
+
223
+ # reported data is sometimes malformed like this
224
+ roadall.last[0] = nil if !roadall.nil? && roadall.last[0] == 0
187
225
  end
188
226
  end
189
227
 
@@ -127,6 +127,14 @@ module Beeminder
127
127
  _connection :put, cmd, data
128
128
  end
129
129
 
130
+ # Send PUT request with a JSON document to API.
131
+ #
132
+ # @param cmd [String] the API command, like `users/#{user.name}.json`
133
+ # @param data [Hash] data to send
134
+ def put_document cmd, data = {}
135
+ _connection :put_json, cmd, data
136
+ end
137
+
130
138
  # Converts time object to one with user's timezone.
131
139
  #
132
140
  # @param time [Date|DateTime|Time] Time to convert.
@@ -140,6 +148,15 @@ module Beeminder
140
148
  }
141
149
  end
142
150
 
151
+ # Returns the current akrasia horizon.
152
+ #
153
+ # @return [Time] The first instant changes can be made for.
154
+ def akrasia_horizon
155
+ Time.use_zone(@timezone) do
156
+ -8.days.ago.beginning_of_day
157
+ end
158
+ end
159
+
143
160
  private
144
161
 
145
162
  # Establish HTTPS connection to API.
@@ -151,7 +168,7 @@ module Beeminder
151
168
  http = Net::HTTP.new(url.host, url.port)
152
169
  http.read_timeout = 8640
153
170
  http.use_ssl = true
154
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE # FIXME: actually verify
171
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER # was VERIFY_NONE for some reason
155
172
 
156
173
  # FIXME Sanity check for wrong timestamp. Source of bug unknown, but at least we can prevent screwing up someone's graph.
157
174
  unless data["timestamp"].nil?
@@ -162,21 +179,25 @@ module Beeminder
162
179
 
163
180
  json = ""
164
181
  http.start do |http|
165
- req = case type
166
- when :post
167
- Net::HTTP::Post.new(url.path)
168
- req.set_form_data(data)
169
- when :get
170
- Net::HTTP::Get.new(url.path + "?" + data.to_query)
171
- when :delete
172
- Net::HTTP::Delete.new(url.path + "?" + data.to_query)
173
- when :put
174
- Net::HTTP::Put.new(url.path)
175
- req.set_form_data(data)
176
- else
177
- raise "invalid connection type"
178
- end
179
-
182
+ case type
183
+ when :post
184
+ req = Net::HTTP::Post.new(url.path)
185
+ req.set_form_data(data)
186
+ when :get
187
+ req = Net::HTTP::Get.new(url.path + "?" + data.to_query)
188
+ when :delete
189
+ req = Net::HTTP::Delete.new(url.path + "?" + data.to_query)
190
+ when :put
191
+ req = Net::HTTP::Put.new(url.path)
192
+ req.set_form_data(data)
193
+ when :put_json
194
+ req = Net::HTTP::Put.new(url.path)
195
+ req.body = data.to_json
196
+ req.content_type = 'application/json'
197
+ else
198
+ raise "invalid connection type"
199
+ end
200
+
180
201
  res = http.request(req)
181
202
  if not res.is_a? Net::HTTPSuccess
182
203
  raise "request failed: #{res.code} / #{res.body}"
@@ -1,3 +1,3 @@
1
1
  module Beeminder
2
- VERSION = "0.2.9"
2
+ VERSION = "0.2.13"
3
3
  end
data/lib/beeminder.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'active_support'
3
4
  require 'active_support/core_ext'
4
5
  require 'date'
5
6
  require 'json'
metadata CHANGED
@@ -1,103 +1,103 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beeminder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.2.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - muflax
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-20 00:00:00.000000000 Z
11
+ date: 2022-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.2'
20
- - - <
20
+ - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '5'
22
+ version: '7'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ! '>='
27
+ - - ">="
28
28
  - !ruby/object:Gem::Version
29
29
  version: '3.2'
30
- - - <
30
+ - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5'
32
+ version: '7'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: chronic
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ~>
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0.7'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ~>
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0.7'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: json
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ! '>='
51
+ - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ! '>='
58
+ - - ">="
59
59
  - !ruby/object:Gem::Version
60
60
  version: '0'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: highline
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ~>
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '1.6'
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
- - - ~>
72
+ - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '1.6'
75
75
  - !ruby/object:Gem::Dependency
76
- name: trollop
76
+ name: optimist
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - ~>
79
+ - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '2'
81
+ version: '3'
82
82
  type: :runtime
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - ~>
86
+ - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '2'
88
+ version: '3'
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: tzinfo
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ! '>='
93
+ - - ">="
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
96
  type: :runtime
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - ! '>='
100
+ - - ">="
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  description: Convenient access to Beeminder's API.
@@ -108,7 +108,7 @@ executables:
108
108
  extensions: []
109
109
  extra_rdoc_files: []
110
110
  files:
111
- - .gitignore
111
+ - ".gitignore"
112
112
  - CHANGES.md
113
113
  - Gemfile
114
114
  - LICENSE.txt
@@ -124,24 +124,23 @@ files:
124
124
  homepage: https://github.com/beeminder/beeminder-gem
125
125
  licenses: []
126
126
  metadata: {}
127
- post_install_message:
127
+ post_install_message:
128
128
  rdoc_options: []
129
129
  require_paths:
130
130
  - lib
131
131
  required_ruby_version: !ruby/object:Gem::Requirement
132
132
  requirements:
133
- - - ! '>='
133
+ - - ">="
134
134
  - !ruby/object:Gem::Version
135
135
  version: '0'
136
136
  required_rubygems_version: !ruby/object:Gem::Requirement
137
137
  requirements:
138
- - - ! '>='
138
+ - - ">="
139
139
  - !ruby/object:Gem::Version
140
140
  version: '0'
141
141
  requirements: []
142
- rubyforge_project:
143
- rubygems_version: 2.2.2
144
- signing_key:
142
+ rubygems_version: 3.2.32
143
+ signing_key:
145
144
  specification_version: 4
146
145
  summary: access Beeminder API
147
146
  test_files: []