rescuetime 0.3.2 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
- require 'faraday'
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
- present? api_key
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
- # Returns true if the project is present and non-blank
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
- parse_response Requester.get(HOST, params).body
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 &block
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
- # @todo: make chainable to the client
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
- fail InvalidFormatError unless %w(array csv).include? format
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
- case @format.intern
77
- when :array then report.to_a.map(&:to_hash)
78
- when :csv then report
79
- else
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,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