rescuetime 0.3.2 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.rubocop.yml +4 -1
- data/.rubocop_todo.yml +32 -54
- data/.travis.yml +3 -1
- data/CHANGELOG.md +320 -0
- data/CODE_OF_CONDUCT.md +67 -6
- data/Gemfile +1 -3
- data/README.md +184 -41
- data/Rakefile +1 -1
- data/lib/rescuetime.rb +68 -3
- data/lib/rescuetime/client.rb +6 -13
- data/lib/rescuetime/collection.rb +32 -11
- 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 +56 -27
- data/lib/rescuetime/version.rb +3 -2
- data/rescuetime.gemspec +7 -9
- metadata +30 -43
data/lib/rescuetime/client.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
require 'csv'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
3
|
+
require 'rescuetime/core_extensions/string'
|
4
4
|
require 'rescuetime/query_buildable'
|
5
5
|
|
6
6
|
require 'rescuetime/requester'
|
@@ -64,7 +64,8 @@ module Rescuetime
|
|
64
64
|
#
|
65
65
|
# @return [Boolean]
|
66
66
|
def api_key?
|
67
|
-
|
67
|
+
key = CoreExtensions::String.new api_key.to_s
|
68
|
+
key.present?
|
68
69
|
end
|
69
70
|
|
70
71
|
# Returns true if the provided api key is valid. Performs a request to the
|
@@ -90,22 +91,14 @@ module Rescuetime
|
|
90
91
|
def valid_credentials?
|
91
92
|
return false unless api_key?
|
92
93
|
!activities.all.nil?
|
93
|
-
rescue Rescuetime::InvalidCredentialsError
|
94
|
+
rescue Rescuetime::Errors::InvalidCredentialsError
|
94
95
|
false
|
95
96
|
end
|
96
97
|
|
97
98
|
private
|
98
99
|
|
99
|
-
#
|
100
|
+
# Client-specific request parameters
|
100
101
|
#
|
101
|
-
# @param [#emtpy?] obj
|
102
|
-
# @return [Boolean]
|
103
|
-
# @since v0.3.2
|
104
|
-
def present?(obj)
|
105
|
-
blank = obj.respond_to?(:empty?) ? obj.empty? : obj.nil?
|
106
|
-
!blank
|
107
|
-
end
|
108
|
-
|
109
102
|
# @return [Hash]
|
110
103
|
def state
|
111
104
|
{ key: api_key }
|
@@ -1,4 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
|
1
5
|
require 'rescuetime/query_buildable'
|
6
|
+
require 'rescuetime/report_formatters'
|
2
7
|
|
3
8
|
module Rescuetime
|
4
9
|
# Represents a potential rescuetime collection. It holds the query information
|
@@ -8,7 +13,7 @@ module Rescuetime
|
|
8
13
|
include Enumerable
|
9
14
|
|
10
15
|
# Rescuetime Analytics API endpoint
|
11
|
-
HOST = 'https://www.rescuetime.com/anapi/data'
|
16
|
+
HOST = 'https://www.rescuetime.com/anapi/data'.freeze
|
12
17
|
|
13
18
|
# Returns a new Rescuetime collection. Default format is array.
|
14
19
|
#
|
@@ -34,7 +39,9 @@ module Rescuetime
|
|
34
39
|
#
|
35
40
|
# @see Rescuetime::Requester#get
|
36
41
|
def all
|
37
|
-
|
42
|
+
requester = Rescuetime::Requester
|
43
|
+
host = HOST
|
44
|
+
parse_response requester.get(host, params)
|
38
45
|
end
|
39
46
|
|
40
47
|
# Enumerates over the collection of Rescuetime report records. Performs
|
@@ -45,16 +52,19 @@ module Rescuetime
|
|
45
52
|
# @see #all
|
46
53
|
# @see http://ruby-doc.org/core/Enumerable.html Enumerable
|
47
54
|
def each(&block)
|
48
|
-
all.each
|
55
|
+
all.each(&block)
|
49
56
|
end
|
50
57
|
|
58
|
+
# Sets the report format to a valid type
|
59
|
+
#
|
51
60
|
# @param [#to_s] format desired report format (one of 'array' or 'csv')
|
52
61
|
# @return [Rescuetime::Collection]
|
53
62
|
#
|
54
|
-
#
|
63
|
+
# TODO: make chainable to the client
|
55
64
|
def format(format)
|
65
|
+
# Guard: fail if the passed format isn't on the whitelist
|
56
66
|
format = format.to_s
|
57
|
-
|
67
|
+
formatters.all.include?(format) || raise(Errors::InvalidFormatError)
|
58
68
|
|
59
69
|
@format = format
|
60
70
|
self
|
@@ -65,6 +75,19 @@ module Rescuetime
|
|
65
75
|
# Stores the query parameters to be set to the rescuetime host
|
66
76
|
attr_reader :params
|
67
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
|
+
#
|
68
91
|
# @param [String] body response body
|
69
92
|
# @return [Array, CSV]
|
70
93
|
def parse_response(body)
|
@@ -73,12 +96,10 @@ module Rescuetime
|
|
73
96
|
header_converters: :symbol,
|
74
97
|
converters: :all)
|
75
98
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
fail InvalidFormatError
|
81
|
-
end
|
99
|
+
format = @format.to_s.downcase
|
100
|
+
report_formatter = formatters.find(format)
|
101
|
+
|
102
|
+
report_formatter.format report
|
82
103
|
end
|
83
104
|
end
|
84
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
|