rescuetime 0.3.3 → 0.4.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/.rubocop_todo.yml +32 -54
- data/Gemfile +1 -3
- data/README.md +133 -38
- data/Rakefile +1 -1
- data/lib/rescuetime.rb +68 -3
- data/lib/rescuetime/client.rb +7 -11
- data/lib/rescuetime/collection.rb +27 -10
- data/lib/rescuetime/configuration.rb +22 -0
- data/lib/rescuetime/core_extensions.rb +8 -0
- data/lib/rescuetime/core_extensions/object.rb +8 -0
- data/lib/rescuetime/core_extensions/object/blank.rb +49 -0
- data/lib/rescuetime/core_extensions/string.rb +10 -0
- data/lib/rescuetime/date_parser.rb +12 -10
- data/lib/rescuetime/errors.rb +79 -73
- data/lib/rescuetime/formatters.rb +48 -0
- data/lib/rescuetime/formatters/array_formatter.rb +23 -0
- data/lib/rescuetime/formatters/base_formatter.rb +46 -0
- data/lib/rescuetime/formatters/csv_formatter.rb +23 -0
- data/lib/rescuetime/query_buildable.rb +18 -16
- data/lib/rescuetime/report_formatters.rb +83 -0
- data/lib/rescuetime/requester.rb +43 -22
- data/lib/rescuetime/version.rb +3 -2
- data/rescuetime.gemspec +1 -0
- metadata +12 -2
data/lib/rescuetime/client.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rescuetime/core_extensions/string'
|
1
4
|
require 'rescuetime/query_buildable'
|
2
5
|
|
3
6
|
require 'rescuetime/requester'
|
@@ -61,7 +64,8 @@ module Rescuetime
|
|
61
64
|
#
|
62
65
|
# @return [Boolean]
|
63
66
|
def api_key?
|
64
|
-
|
67
|
+
key = CoreExtensions::String.new api_key.to_s
|
68
|
+
key.present?
|
65
69
|
end
|
66
70
|
|
67
71
|
# Returns true if the provided api key is valid. Performs a request to the
|
@@ -87,22 +91,14 @@ module Rescuetime
|
|
87
91
|
def valid_credentials?
|
88
92
|
return false unless api_key?
|
89
93
|
!activities.all.nil?
|
90
|
-
rescue Rescuetime::InvalidCredentialsError
|
94
|
+
rescue Rescuetime::Errors::InvalidCredentialsError
|
91
95
|
false
|
92
96
|
end
|
93
97
|
|
94
98
|
private
|
95
99
|
|
96
|
-
#
|
100
|
+
# Client-specific request parameters
|
97
101
|
#
|
98
|
-
# @param [#emtpy?] obj
|
99
|
-
# @return [Boolean]
|
100
|
-
# @since v0.3.2
|
101
|
-
def present?(obj)
|
102
|
-
blank = obj.respond_to?(:empty?) ? obj.empty? : obj.nil?
|
103
|
-
!blank
|
104
|
-
end
|
105
|
-
|
106
102
|
# @return [Hash]
|
107
103
|
def state
|
108
104
|
{ key: api_key }
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'csv'
|
2
4
|
|
3
5
|
require 'rescuetime/query_buildable'
|
6
|
+
require 'rescuetime/report_formatters'
|
4
7
|
|
5
8
|
module Rescuetime
|
6
9
|
# Represents a potential rescuetime collection. It holds the query information
|
@@ -10,7 +13,7 @@ module Rescuetime
|
|
10
13
|
include Enumerable
|
11
14
|
|
12
15
|
# Rescuetime Analytics API endpoint
|
13
|
-
HOST = 'https://www.rescuetime.com/anapi/data'
|
16
|
+
HOST = 'https://www.rescuetime.com/anapi/data'.freeze
|
14
17
|
|
15
18
|
# Returns a new Rescuetime collection. Default format is array.
|
16
19
|
#
|
@@ -49,16 +52,19 @@ module Rescuetime
|
|
49
52
|
# @see #all
|
50
53
|
# @see http://ruby-doc.org/core/Enumerable.html Enumerable
|
51
54
|
def each(&block)
|
52
|
-
all.each
|
55
|
+
all.each(&block)
|
53
56
|
end
|
54
57
|
|
58
|
+
# Sets the report format to a valid type
|
59
|
+
#
|
55
60
|
# @param [#to_s] format desired report format (one of 'array' or 'csv')
|
56
61
|
# @return [Rescuetime::Collection]
|
57
62
|
#
|
58
|
-
#
|
63
|
+
# TODO: make chainable to the client
|
59
64
|
def format(format)
|
65
|
+
# Guard: fail if the passed format isn't on the whitelist
|
60
66
|
format = format.to_s
|
61
|
-
|
67
|
+
formatters.all.include?(format) || raise(Errors::InvalidFormatError)
|
62
68
|
|
63
69
|
@format = format
|
64
70
|
self
|
@@ -69,6 +75,19 @@ module Rescuetime
|
|
69
75
|
# Stores the query parameters to be set to the rescuetime host
|
70
76
|
attr_reader :params
|
71
77
|
|
78
|
+
# Returns a new collection of available report formatters (using the
|
79
|
+
# Rescuetime::ReportFormatters class)
|
80
|
+
#
|
81
|
+
# @param [Class] formatter_collection defaults to
|
82
|
+
# Rescuetime::ReportFormatters
|
83
|
+
# @return [Rescuetime::ReportFormatter]
|
84
|
+
# @since v0.4.0
|
85
|
+
def formatters(formatter_collection: Rescuetime::ReportFormatters)
|
86
|
+
@formatters ||= formatter_collection.new
|
87
|
+
end
|
88
|
+
|
89
|
+
# Parses a response from the string response body to the desired format.
|
90
|
+
#
|
72
91
|
# @param [String] body response body
|
73
92
|
# @return [Array, CSV]
|
74
93
|
def parse_response(body)
|
@@ -77,12 +96,10 @@ module Rescuetime
|
|
77
96
|
header_converters: :symbol,
|
78
97
|
converters: :all)
|
79
98
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
fail InvalidFormatError
|
85
|
-
end
|
99
|
+
format = @format.to_s.downcase
|
100
|
+
report_formatter = formatters.find(format)
|
101
|
+
|
102
|
+
report_formatter.format report
|
86
103
|
end
|
87
104
|
end
|
88
105
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rescuetime
|
4
|
+
# Represents a set of Rescuetime configuration settings
|
5
|
+
#
|
6
|
+
# @since v0.4.0
|
7
|
+
class Configuration
|
8
|
+
# Contains user-specified formatter paths. Defaults to [] when a new
|
9
|
+
# configuration is created.
|
10
|
+
#
|
11
|
+
# @see Rescuetime.configure
|
12
|
+
attr_accessor :formatter_paths
|
13
|
+
|
14
|
+
# Creates a new Rescuetime configuration, defaulting to no formatter paths
|
15
|
+
#
|
16
|
+
# @return [Rescuetime::Configuration] a new rescuetime configuration
|
17
|
+
# @see Rescuetime.configure
|
18
|
+
def initialize
|
19
|
+
@formatter_paths = []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rescuetime::CoreExtensions
|
4
|
+
module Object
|
5
|
+
# Includes methods that check the presence or blankness of an object.
|
6
|
+
# @since v0.4.0
|
7
|
+
module Blank
|
8
|
+
# Returns true if the associated object is empty or falsey. Based on
|
9
|
+
# Rails' ActiveSupport method Object#blank?
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# module Rescuetime
|
13
|
+
# # ...
|
14
|
+
#
|
15
|
+
# def format(report)
|
16
|
+
# # Guard: report presence
|
17
|
+
# report.extend CoreExtensions::Object::Blank
|
18
|
+
# report.blank? && return false
|
19
|
+
# # ...
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @return [Boolean] true if the object is empty or falsey
|
24
|
+
# @see #present?
|
25
|
+
def blank?
|
26
|
+
respond_to?(:empty?) ? !!empty? : !self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns true if an object is truthy and is not empty (is not blank).
|
30
|
+
# Based on Rails' ActiveSupport method Object#present?
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# module Rescuetime
|
34
|
+
# # ...
|
35
|
+
#
|
36
|
+
# def api_key?
|
37
|
+
# api_key.extend CoreExtensions::Object::Blank
|
38
|
+
# api_key.present?
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# @return [Boolean] true if the object is not blank
|
43
|
+
# @see #blank?
|
44
|
+
def present?
|
45
|
+
!blank?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rescuetime
|
2
4
|
# Responsible for formatting user-inputted dates into proper Rescuetime format
|
3
5
|
# @since v0.3.2
|
@@ -7,9 +9,9 @@ module Rescuetime
|
|
7
9
|
DATE_FORMATS = {
|
8
10
|
'yyyy-mm-dd' => /\d{4}-\d{2}-\d{2}/,
|
9
11
|
'yyyy/mm/dd' => %r{\d{4}\/\d{2}\/\d{2}},
|
10
|
-
'mm-dd-yyyy or mm/dd/yyyy' => %r{\d{2}[-\/]\d{2}[-\/]\d{4}
|
12
|
+
'mm-dd-yyyy or mm/dd/yyyy' => %r{\d{2}[-\/]\d{2}[-\/]\d{4}},
|
11
13
|
'mm-dd or mm/dd' => %r{\d{2}[-\/]\d{2}}
|
12
|
-
}
|
14
|
+
}.freeze
|
13
15
|
|
14
16
|
class << self
|
15
17
|
# Returns a date as a string in the correct Rescuetime API format
|
@@ -35,13 +37,13 @@ module Rescuetime
|
|
35
37
|
# #=> '2015-12-25'
|
36
38
|
#
|
37
39
|
# parser.parse 'Dec. 25, 2015'
|
38
|
-
# #=> Rescuetime::InvalidQueryError: Invalid date entered. Please
|
40
|
+
# #=> Rescuetime::Errors::InvalidQueryError: Invalid date entered. Please
|
39
41
|
# # see docs for allowed formats.
|
40
42
|
#
|
41
43
|
# @param [#strftime, String] date a date to be formatted
|
42
44
|
# @return [String] a date string in the YYYY-MM-DD format
|
43
45
|
#
|
44
|
-
# @raise [Rescuetime::InvalidQueryError] if the date format is invalid
|
46
|
+
# @raise [Rescuetime::Errors::InvalidQueryError] if the date format is invalid
|
45
47
|
def parse(date)
|
46
48
|
if date.respond_to? :strftime
|
47
49
|
date.strftime '%Y-%m-%d'
|
@@ -58,11 +60,11 @@ module Rescuetime
|
|
58
60
|
# @param [Hash] formatted_as A hash of date formats and patterns
|
59
61
|
# @return [String] a string date in YYYY-MM-DD format
|
60
62
|
#
|
61
|
-
# @raise [Rescuetime::InvalidQueryError] if the date format is invalid
|
63
|
+
# @raise [Rescuetime::Errors::InvalidQueryError] if the date format is invalid
|
62
64
|
def reformat_string(date, formatted_as: DATE_FORMATS)
|
63
65
|
case date
|
64
66
|
when formatted_as['yyyy-mm-dd'] then date
|
65
|
-
when formatted_as['yyyy/mm/dd'] then date.
|
67
|
+
when formatted_as['yyyy/mm/dd'] then date.tr '/', '-'
|
66
68
|
when formatted_as['mm-dd-yyyy or mm/dd/yyyy']
|
67
69
|
month, day, year = date.scan(/\d+/)
|
68
70
|
"#{year}-#{month}-#{day}"
|
@@ -75,11 +77,11 @@ module Rescuetime
|
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
78
|
-
# Raises Rescuetime::InvalidQueryError
|
79
|
-
# @raise [Rescuetime::InvalidQueryError]
|
80
|
-
def fail_date_format(exception: Rescuetime::InvalidQueryError)
|
80
|
+
# Raises Rescuetime::Errors::InvalidQueryError
|
81
|
+
# @raise [Rescuetime::Errors::InvalidQueryError]
|
82
|
+
def fail_date_format(exception: Rescuetime::Errors::InvalidQueryError)
|
81
83
|
message = 'Invalid date entered. Please see docs for allowed formats.'
|
82
|
-
|
84
|
+
raise exception, message
|
83
85
|
end
|
84
86
|
end
|
85
87
|
end
|
data/lib/rescuetime/errors.rb
CHANGED
@@ -1,88 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rescuetime
|
2
|
-
#
|
3
|
-
|
4
|
+
# Contains all Rescuetime error classes
|
5
|
+
module Errors
|
6
|
+
# Error class for rescuing all RescueTime errors
|
7
|
+
class Error < StandardError; end
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
9
|
+
##
|
10
|
+
# HTTP Errors
|
11
|
+
# ===========
|
12
|
+
# 4xx HTTP status code
|
13
|
+
class ClientError < Error; end
|
14
|
+
# HTTP status code 400
|
15
|
+
class BadRequest < ClientError; end
|
16
|
+
# HTTP status code 401
|
17
|
+
class Unauthorized < ClientError; end
|
18
|
+
# HTTP status code 403
|
19
|
+
class Forbidden < ClientError; end
|
20
|
+
# HTTP status code 404
|
21
|
+
class NotFound < ClientError; end
|
22
|
+
# HTTP status code 406
|
23
|
+
class NotAcceptable < ClientError; end
|
24
|
+
# HTTP status code 422
|
25
|
+
class UnprocessableEntity < ClientError; end
|
26
|
+
# HTTP status code 429
|
27
|
+
class TooManyRequests < ClientError; end
|
24
28
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
# 5xx HTTP status code
|
30
|
+
class ServerError < Error; end
|
31
|
+
# HTTP status code 500
|
32
|
+
class InternalServerError < ServerError; end
|
33
|
+
# HTTP status code 501
|
34
|
+
class NotImplemented < ServerError; end
|
35
|
+
# HTTP status code 502
|
36
|
+
class BadGateway < ServerError; end
|
37
|
+
# HTTP status code 503
|
38
|
+
class ServiceUnavailable < ServerError; end
|
39
|
+
# HTTP status code 504
|
40
|
+
class GatewayTimeout < ServerError; end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
##
|
43
|
+
# Custom Errors
|
44
|
+
# =============
|
41
45
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
+
# Raised when a method requires credentials but none are provided
|
47
|
+
class MissingCredentialsError < Unauthorized
|
48
|
+
def initialize(msg = 'No API key provided. Please provide a valid key.')
|
49
|
+
super
|
50
|
+
end
|
46
51
|
end
|
47
|
-
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
# Raised when a method requires credentials but credentials are invalid
|
54
|
+
class InvalidCredentialsError < Unauthorized
|
55
|
+
def initialize(msg = 'API key is invalid. Please provide a valid key.')
|
56
|
+
super
|
57
|
+
end
|
53
58
|
end
|
54
|
-
end
|
55
59
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
+
# Raised when a user-submitted query value is invalid
|
61
|
+
class InvalidQueryError < BadRequest
|
62
|
+
def initialize(msg = 'Likely a badly formatted or missing parameter')
|
63
|
+
super
|
64
|
+
end
|
60
65
|
end
|
61
|
-
end
|
62
66
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
+
# Raised when a user-submitted query value is invalid
|
68
|
+
class InvalidFormatError < Error
|
69
|
+
def initialize(msg = 'Invalid format. Please see docs for allowed formats.')
|
70
|
+
super
|
71
|
+
end
|
67
72
|
end
|
68
|
-
end
|
69
73
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
74
|
+
# Error class for rescuing all RescueTime errors
|
75
|
+
class Error
|
76
|
+
# Collection of possible return status codes and corresponding Rescuetime
|
77
|
+
# errors
|
78
|
+
CODES = {
|
79
|
+
400 => Rescuetime::Errors::BadRequest,
|
80
|
+
401 => Rescuetime::Errors::Unauthorized,
|
81
|
+
403 => Rescuetime::Errors::Forbidden,
|
82
|
+
404 => Rescuetime::Errors::NotFound,
|
83
|
+
406 => Rescuetime::Errors::NotAcceptable,
|
84
|
+
422 => Rescuetime::Errors::UnprocessableEntity,
|
85
|
+
429 => Rescuetime::Errors::TooManyRequests,
|
86
|
+
500 => Rescuetime::Errors::InternalServerError,
|
87
|
+
501 => Rescuetime::Errors::NotImplemented,
|
88
|
+
502 => Rescuetime::Errors::BadGateway,
|
89
|
+
503 => Rescuetime::Errors::ServiceUnavailable,
|
90
|
+
504 => Rescuetime::Errors::GatewayTimeout
|
91
|
+
}.freeze
|
92
|
+
end
|
87
93
|
end
|
88
94
|
end
|