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.
@@ -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
- present? api_key
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
- # Returns true if the project is present and non-blank
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 &block
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
- # @todo: make chainable to the client
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
- fail InvalidFormatError unless %w(array csv).include? format
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
- case @format.intern
81
- when :array then report.to_a.map(&:to_hash)
82
- when :csv then report
83
- else
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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rescuetime
4
+ # Contains extensions to the core classes
5
+ # @since v0.4.0
6
+ module CoreExtensions
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rescuetime::CoreExtensions
4
+ # Contains extensions to the Object class
5
+ # @since v0.4.0
6
+ module Object
7
+ end
8
+ 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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rescuetime/core_extensions/object/blank'
4
+
5
+ module Rescuetime::CoreExtensions
6
+ # Represents an exteded Rescuetime::String object
7
+ class String < ::String
8
+ include Object::Blank
9
+ end
10
+ 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.gsub '/', '-'
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
- fail exception, message
84
+ raise exception, message
83
85
  end
84
86
  end
85
87
  end
@@ -1,88 +1,94 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rescuetime
2
- # Error class for rescuing all RescueTime errors
3
- class Error < StandardError; end
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
- # HTTP Errors
7
- # ===========
8
- # 4xx HTTP status code
9
- class ClientError < Error; end
10
- # HTTP status code 400
11
- class BadRequest < ClientError; end
12
- # HTTP status code 401
13
- class Unauthorized < ClientError; end
14
- # HTTP status code 403
15
- class Forbidden < ClientError; end
16
- # HTTP status code 404
17
- class NotFound < ClientError; end
18
- # HTTP status code 406
19
- class NotAcceptable < ClientError; end
20
- # HTTP status code 422
21
- class UnprocessableEntity < ClientError; end
22
- # HTTP status code 429
23
- class TooManyRequests < ClientError; end
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
- # 5xx HTTP status code
26
- class ServerError < Error; end
27
- # HTTP status code 500
28
- class InternalServerError < ServerError; end
29
- # HTTP status code 501
30
- class NotImplemented < ServerError; end
31
- # HTTP status code 502
32
- class BadGateway < ServerError; end
33
- # HTTP status code 503
34
- class ServiceUnavailable < ServerError; end
35
- # HTTP status code 504
36
- class GatewayTimeout < ServerError; end
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
- # Custom Errors
40
- # =============
42
+ ##
43
+ # Custom Errors
44
+ # =============
41
45
 
42
- # Raised when a method requires credentials but none are provided
43
- class MissingCredentialsError < Unauthorized
44
- def initialize(msg = 'No API key provided. Please provide a valid key.')
45
- super
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
- # Raised when a method requires credentials but credentials are invalid
50
- class InvalidCredentialsError < Unauthorized
51
- def initialize(msg = 'API key is invalid. Please provide a valid key.')
52
- super
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
- # Raised when a user-submitted query value is invalid
57
- class InvalidQueryError < BadRequest
58
- def initialize(msg = 'Likely a badly formatted or missing parameter')
59
- super
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
- # Raised when a user-submitted query value is invalid
64
- class InvalidFormatError < Error
65
- def initialize(msg = 'Invalid format. Please see docs for allowed formats.')
66
- super
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
- class Error
71
- # Collection of possible return status codes and corresponding Rescuetime
72
- # errors
73
- CODES = {
74
- 400 => Rescuetime::BadRequest,
75
- 401 => Rescuetime::Unauthorized,
76
- 403 => Rescuetime::Forbidden,
77
- 404 => Rescuetime::NotFound,
78
- 406 => Rescuetime::NotAcceptable,
79
- 422 => Rescuetime::UnprocessableEntity,
80
- 429 => Rescuetime::TooManyRequests,
81
- 500 => Rescuetime::InternalServerError,
82
- 501 => Rescuetime::NotImplemented,
83
- 502 => Rescuetime::BadGateway,
84
- 503 => Rescuetime::ServiceUnavailable,
85
- 504 => Rescuetime::GatewayTimeout
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