rescuetime 0.3.1 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d14aafe98892e8e11f57043050fe2906c6f73f45
4
- data.tar.gz: a8d7bc8a5acd04b0352341173835133e10ef06fd
3
+ metadata.gz: 6464e2b8b391cfa2b334288ba6cdbd81fe7728f6
4
+ data.tar.gz: e293998bd6552509d6225422b1a7f029a26dcde3
5
5
  SHA512:
6
- metadata.gz: fa1aea8ef22994a34faf16834107719fc69373a0ae4f09e78ac86871fcf05527e92dad6d9ee106c3de6bf5addbb717066d4fd97ef4e3be394eb5d1cc140041ed
7
- data.tar.gz: 2a8ba29a4bbdd16d967b8f2d6fc8737fd6cc30a7f0776357949817a66c6fcc89ca1579b8292b2169ad11313b75b1e4e32038fc8ff50d5fc8487f6b20b39c10f2
6
+ metadata.gz: 4e022e5eefb8a858051f8c5264d394562ba6939381beb5b9072807f2dad2a70f9d8047ada933a2be9172ba050ed5b71ed41e51934e1b2a8ade83bd67d7a645c2
7
+ data.tar.gz: 8c09915abb016f0680d9b48c1f45362ccf6312fc970f1e58d38feeb25e15afda6cfeba1d5f18a877712ac7b29bce2e65f39cdf14ebea57caf79f26fbcdc5df33
data/.rubocop.yml ADDED
@@ -0,0 +1 @@
1
+ inherit_from: .rubocop_todo.yml
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,80 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2016-05-22 00:42:00 -0400 using RuboCop version 0.40.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ Lint/AmbiguousOperator:
11
+ Exclude:
12
+ - 'lib/rescuetime/collection.rb'
13
+
14
+ # Offense count: 1
15
+ # Configuration parameters: CountComments.
16
+ Metrics/MethodLength:
17
+ Max: 13
18
+
19
+ # Offense count: 1
20
+ # Cop supports --auto-correct.
21
+ Performance/StringReplacement:
22
+ Exclude:
23
+ - 'lib/rescuetime/date_parser.rb'
24
+
25
+ # Offense count: 1
26
+ # Cop supports --auto-correct.
27
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
28
+ # SupportedStyles: always, conditionals
29
+ Style/AndOr:
30
+ Exclude:
31
+ - 'lib/rescuetime/query_buildable.rb'
32
+
33
+ # Offense count: 5
34
+ # Cop supports --auto-correct.
35
+ # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
36
+ # SupportedStyles: aligned, indented
37
+ Style/MultilineMethodCallIndentation:
38
+ Enabled: false
39
+
40
+ # Offense count: 9
41
+ # Cop supports --auto-correct.
42
+ Style/MutableConstant:
43
+ Exclude:
44
+ - 'lib/rescuetime/collection.rb'
45
+ - 'lib/rescuetime/date_parser.rb'
46
+ - 'lib/rescuetime/errors.rb'
47
+ - 'lib/rescuetime/query_buildable.rb'
48
+ - 'lib/rescuetime/requester.rb'
49
+ - 'lib/rescuetime/version.rb'
50
+ - 'spec/sample_secret.rb'
51
+ - 'spec/secret.rb'
52
+
53
+ # Offense count: 3
54
+ # Cop supports --auto-correct.
55
+ Style/NestedParenthesizedCalls:
56
+ Exclude:
57
+ - 'spec/rescuetime/query_buildable_spec.rb'
58
+
59
+ # Offense count: 1
60
+ # Cop supports --auto-correct.
61
+ Style/ParallelAssignment:
62
+ Exclude:
63
+ - 'spec/rescuetime/query_buildable_spec.rb'
64
+
65
+ # Offense count: 1
66
+ # Cop supports --auto-correct.
67
+ Style/RedundantSelf:
68
+ Exclude:
69
+ - 'lib/rescuetime/query_buildable.rb'
70
+
71
+ # Offense count: 10
72
+ # Cop supports --auto-correct.
73
+ # Configuration parameters: EnforcedStyle, SupportedStyles.
74
+ # SupportedStyles: only_raise, only_fail, semantic
75
+ Style/SignalException:
76
+ Exclude:
77
+ - 'lib/rescuetime/collection.rb'
78
+ - 'lib/rescuetime/date_parser.rb'
79
+ - 'lib/rescuetime/query_buildable.rb'
80
+ - 'lib/rescuetime/requester.rb'
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rubocop', require: false
4
+
3
5
  group :test do
4
6
  gem 'codeclimate-test-reporter', '~> 0.4.7'
5
7
  gem 'simplecov', '~> 0.9.2'
data/README.md CHANGED
@@ -146,7 +146,7 @@ require 'rescuetime'
146
146
 
147
147
  ### Finding Answers (Documentation)
148
148
 
149
- For more details, please see [official gem documentation](http://www.rubydoc.info/gems/rescuetime/0.1.0) or [read the wiki](https://github.com/leesharma/rescuetime/wiki).
149
+ For more details, please see [official gem documentation](http://www.rubydoc.info/gems/rescuetime) or [read the wiki](https://github.com/leesharma/rescuetime/wiki).
150
150
 
151
151
  ### Defaults
152
152
 
@@ -164,7 +164,7 @@ The `Rescuetime::Client#activities` action has the following defaults:
164
164
 
165
165
  There are a number of exceptions that extend from the custom Rescuetime::Error class:
166
166
 
167
- * * **Rescuetime::MissingCredentialsError** is raised when a request is attempted by a client with no credentials. Try setting credentials with `@client.api_key = <YOUR_API_KEY>`.
167
+ * **Rescuetime::MissingCredentialsError** is raised when a request is attempted by a client with no credentials. Try setting credentials with `@client.api_key = <YOUR_API_KEY>`.
168
168
  * **Rescuetime::InvalidCredentialsError** is raised when a request is attempted by a client with invalid credentials. Double-check your API key and fix your client with `@client.api_key = <VALID_API_KEY>`.
169
169
  * **Rescuetime::InvalidQueryError** is raised if you enter an invalid value for any of the RescueTime query methods (or if the server returns a bad query error)
170
170
  * **Rescuetime::InvalidFormatError** is raised if you pass a disallowed format to the client
data/Rakefile CHANGED
@@ -3,9 +3,18 @@
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
5
 
6
- desc 'Run all the tests in spec'
7
6
  RSpec::Core::RakeTask.new(:spec)
8
7
  task test: :spec
9
8
 
10
- desc 'Default: run specs'
11
- task default: :spec
9
+ defaults = [:spec]
10
+
11
+ begin
12
+ require 'rubocop/rake_task'
13
+ RuboCop::RakeTask.new(:rubocop)
14
+ # defaults << :rubocop
15
+ rescue LoadError
16
+ puts 'Rubocop is not installed; please install it for code quality checks.'
17
+ end
18
+
19
+ desc "Run default actions: #{defaults}"
20
+ task default: defaults
@@ -14,31 +14,101 @@ module Rescuetime
14
14
  class Client
15
15
  include QueryBuildable
16
16
 
17
- # @!attribute [rw] api_key
18
- # @since v0.1.0
17
+ # Represents the client's Rescuetime API key, required for successful
18
+ # requests
19
+ #
20
+ # @return [String, nil] Rescuetime API key
19
21
  attr_accessor :api_key
20
22
 
21
- # @since v0.1.0
22
- def initialize(key = nil, **opts)
23
- @api_key = key || opts[:api_key]
23
+ # Creates a new Rescuetime client. The API key can be passed as either a
24
+ # parameter or option (with "key" taking priority over ":api_key")
25
+ #
26
+ # @example Basic Use
27
+ # Rescuetime::Client.new
28
+ # # => #<Rescuetime::Client:0x007f938489b6c8 @api_key=nil>
29
+ #
30
+ # @example Setting the API Key
31
+ # Rescuetime::Client.new
32
+ # # => #<Rescuetime::Client:0x007f938489b6c8 @api_key=nil>
33
+ #
34
+ # Rescuetime::Client.new('12345')
35
+ # # => #<Rescuetime::Client:0x007f938489b6c8 @api_key="12345">
36
+ #
37
+ # Rescuetime::Client.new(api_key: 12345')
38
+ # # => #<Rescuetime::Client:0x007f938489b6c8 @api_key="12345">
39
+ #
40
+ # Rescuetime::Client.new('123', api_key: '456')
41
+ # # => #<Rescuetime::Client:0x007f938489b6c8 @api_key="123">
42
+ #
43
+ # @param [String, nil] key API key
44
+ # @param [String] api_key Rescuetime API key
45
+ # @return [Rescuetime::Client]
46
+ def initialize(key = nil, api_key: nil)
47
+ @api_key = key || api_key
24
48
  end
25
49
 
26
- # @since v0.1.0
50
+ # Returns true if the API key is present.
51
+ #
52
+ # @example Basic Use
53
+ # client = Rescuetime::Client.new
54
+ # client.api_key?
55
+ # # => false
56
+ #
57
+ # client.api_key = ''
58
+ # client.api_key?
59
+ # # => false
60
+ #
61
+ # client.api_key = '12345'
62
+ # client.api_key?
63
+ # # => true
64
+ #
65
+ # @return [Boolean]
27
66
  def api_key?
28
- !!@api_key && !@api_key.empty?
67
+ present? api_key
29
68
  end
30
69
 
31
- # @since v0.2.0
70
+ # Returns true if the provided api key is valid. Performs a request to the
71
+ # Rescuetime API.
72
+ #
73
+ # @example Basic Use
74
+ # # Assuming that INVALID_KEY is an invalid Rescuetime API key
75
+ # # and VALID_KEY is a valid one
76
+ #
77
+ # client = Rescuetime::Client
78
+ # client.valid_credentials?
79
+ # # => false
80
+ #
81
+ # client.api_key = INVALID_KEY
82
+ # client.valid_credentials? # Performs a request to the Rescuetime API
83
+ # # => false
84
+ #
85
+ # client.api_key = VALID_KEY
86
+ # client.valid_credentials? # Performs a request to the Rescuetime API
87
+ # # => true
88
+ #
89
+ # @return [Boolean]
32
90
  def valid_credentials?
33
91
  return false unless api_key?
34
- !!activities.all rescue false
92
+ !activities.all.nil?
93
+ rescue Rescuetime::InvalidCredentialsError
94
+ false
35
95
  end
36
96
 
37
97
  private
38
98
 
39
- # @since v0.3.0
99
+ # Returns true if the project is present and non-blank
100
+ #
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
+ # @return [Hash]
40
110
  def state
41
- { key: @api_key }
111
+ { key: api_key }
42
112
  end
43
113
  end
44
114
  end
@@ -1,46 +1,79 @@
1
1
  require 'rescuetime/query_buildable'
2
2
 
3
3
  module Rescuetime
4
+ # Represents a potential rescuetime collection. It holds the query information
5
+ # and performs the query lazily, when #all or an Enumerable method is called.
4
6
  class Collection
5
7
  include QueryBuildable
6
8
  include Enumerable
7
9
 
10
+ # Rescuetime Analytics API endpoint
8
11
  HOST = 'https://www.rescuetime.com/anapi/data'
9
12
 
13
+ # Returns a new Rescuetime collection. Default format is array.
14
+ #
15
+ # @param [Array<Hash>] terms a list of parameter hashes
16
+ # @return [Rescuetime::Collection] a new Rescuetime collection
10
17
  def initialize(*terms)
11
18
  @params = terms.reduce({}, :merge)
12
19
  @format = :array
13
20
  end
14
21
 
22
+ # Appends new terms onto the Rescuetime collection. In the case of duplicate
23
+ # keys, the later keys overwrite the former keys.
24
+ #
25
+ # @param [Hash] terms new terms to add to collection params
26
+ # @return [Hash] collection params with terms merged in
15
27
  def <<(terms)
16
- @params.merge! terms
28
+ @params = params.merge terms
17
29
  end
18
30
 
31
+ # Performs the rescuetime query and returns an array or csv response.
32
+ #
33
+ # @return [Array, CSV]
34
+ #
35
+ # @see Rescuetime::Requester#get
19
36
  def all
20
- parse_response Requester.get(HOST, @params).body
37
+ parse_response Requester.get(HOST, params).body
21
38
  end
22
39
 
40
+ # Enumerates over the collection of Rescuetime report records. Performs
41
+ # the query.
42
+ #
43
+ # @return [Array, CSV]
44
+ #
45
+ # @see #all
46
+ # @see http://ruby-doc.org/core/Enumerable.html Enumerable
23
47
  def each(&block)
24
48
  all.each &block
25
49
  end
26
50
 
27
- # TODO: Chainable to client
51
+ # @param [#to_s] format desired report format (one of 'array' or 'csv')
52
+ # @return [Rescuetime::Collection]
53
+ #
54
+ # @todo: make chainable to the client
28
55
  def format(format)
29
- fail InvalidFormatError unless %w(array csv).include? format.to_s
30
- @format = format.to_sym
56
+ format = format.to_s
57
+ fail InvalidFormatError unless %w(array csv).include? format
58
+
59
+ @format = format
31
60
  self
32
61
  end
33
62
 
34
63
  private
35
64
 
36
- # @since v0.1.0
65
+ # Stores the query parameters to be set to the rescuetime host
66
+ attr_reader :params
67
+
68
+ # @param [String] body response body
69
+ # @return [Array, CSV]
37
70
  def parse_response(body)
38
71
  report = CSV.new(body,
39
- headers: true,
40
- header_converters: :symbol,
41
- converters: :all)
72
+ headers: true,
73
+ header_converters: :symbol,
74
+ converters: :all)
42
75
 
43
- case @format
76
+ case @format.intern
44
77
  when :array then report.to_a.map(&:to_hash)
45
78
  when :csv then report
46
79
  else
@@ -0,0 +1,86 @@
1
+ module Rescuetime
2
+ # Responsible for formatting user-inputted dates into proper Rescuetime format
3
+ # @since v0.3.2
4
+ class DateParser
5
+ # Valid string formats for date parsing. The key is a human-readable pattern
6
+ # (ex. 'yyyy-mm-dd'), and the value is the corresponding regular expression.
7
+ DATE_FORMATS = {
8
+ 'yyyy-mm-dd' => /\d{4}-\d{2}-\d{2}/,
9
+ '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}/},
11
+ 'mm-dd or mm/dd' => %r{\d{2}[-\/]\d{2}}
12
+ }
13
+
14
+ class << self
15
+ # Returns a date as a string in the correct Rescuetime API format
16
+ #
17
+ # Allowed date formats:
18
+ # - Object that responds to #strftime
19
+ # - String in "YYYY-MM-DD" format
20
+ # - String in "YYYY/MM/DD" format
21
+ # - String in "MM-DD-YYYY" format
22
+ # - String in "MM/DD/YYYY" format
23
+ # - String in "MM-DD" format (defaults to current year)
24
+ # - String in "MM/DD" format (defaults to current year)
25
+ #
26
+ # @example Basic Use
27
+ # parser = Rescuetime::DateParser
28
+ # parser.parse '12-25' # assuming the current year is 2015
29
+ # #=> '2015-12-25'
30
+ #
31
+ # parser.parse '2015/12/25'
32
+ # #=> '2015-12-25'
33
+ #
34
+ # parser.parse Time.parse('2015-12-25')
35
+ # #=> '2015-12-25'
36
+ #
37
+ # parser.parse 'Dec. 25, 2015'
38
+ # #=> Rescuetime::InvalidQueryError: Invalid date entered. Please
39
+ # # see docs for allowed formats.
40
+ #
41
+ # @param [#strftime, String] date a date to be formatted
42
+ # @return [String] a date string in the YYYY-MM-DD format
43
+ #
44
+ # @raise [Rescuetime::InvalidQueryError] if the date format is invalid
45
+ def parse(date)
46
+ if date.respond_to? :strftime
47
+ date.strftime '%Y-%m-%d'
48
+ else
49
+ reformat_string(date)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Reformats a date string to follow the YYYY-MM-DD format
56
+ #
57
+ # @param [String] date A string representation of a date
58
+ # @param [Hash] formatted_as A hash of date formats and patterns
59
+ # @return [String] a string date in YYYY-MM-DD format
60
+ #
61
+ # @raise [Rescuetime::InvalidQueryError] if the date format is invalid
62
+ def reformat_string(date, formatted_as: DATE_FORMATS)
63
+ case date
64
+ when formatted_as['yyyy-mm-dd'] then date
65
+ when formatted_as['yyyy/mm/dd'] then date.gsub '/', '-'
66
+ when formatted_as['mm-dd-yyyy or mm/dd/yyyy']
67
+ month, day, year = date.scan(/\d+/)
68
+ "#{year}-#{month}-#{day}"
69
+ when formatted_as['mm-dd or mm/dd']
70
+ year = Time.now.year
71
+ month, day = date.scan(/\d+/)
72
+ "#{year}-#{month}-#{day}"
73
+ else
74
+ fail_date_format
75
+ end
76
+ end
77
+
78
+ # Raises Rescuetime::InvalidQueryError
79
+ # @raise [Rescuetime::InvalidQueryError]
80
+ def fail_date_format(exception: Rescuetime::InvalidQueryError)
81
+ message = 'Invalid date entered. Please see docs for allowed formats.'
82
+ fail exception, message
83
+ end
84
+ end
85
+ end
86
+ end
@@ -38,7 +38,7 @@ module Rescuetime
38
38
  ##
39
39
  # Custom Errors
40
40
  # =============
41
- # class CredentialsError < Error; end
41
+
42
42
  # Raised when a method requires credentials but none are provided
43
43
  class MissingCredentialsError < Unauthorized
44
44
  def initialize(msg = 'No API key provided. Please provide a valid key.')
@@ -68,6 +68,8 @@ module Rescuetime
68
68
  end
69
69
 
70
70
  class Error
71
+ # Collection of possible return status codes and corresponding Rescuetime
72
+ # errors
71
73
  CODES = {
72
74
  400 => Rescuetime::BadRequest,
73
75
  401 => Rescuetime::Unauthorized,
@@ -1,3 +1,5 @@
1
+ require 'rescuetime/date_parser'
2
+
1
3
  module Rescuetime
2
4
  # The Rescuetime::Reportable module contains client methods relating to
3
5
  # generating requests to fetch reports on the /data endpoint of the
@@ -5,85 +7,370 @@ module Rescuetime
5
7
  #
6
8
  # @since v0.1.0
7
9
  module QueryBuildable
10
+ # Parameters that are included by default in every query
8
11
  BASE_PARAMS = { format: 'csv',
9
12
  operation: 'select',
10
13
  version: 0 }
11
14
 
12
- # @return [Rescuetime::Collection]
13
- # @since v0.3.0
15
+ # Valid values for the :order and :interval parameters
16
+ # @see #order_by
17
+ VALID = {
18
+ order_by: %w(time rank member),
19
+ interval: %w(minute hour day week month)
20
+ }
21
+
22
+ # Returns a Rescuetime overview report, grouping activities into their
23
+ # top-level categories
24
+ #
25
+ # Defaults:
26
+ # - order: rank
27
+ # - date range: current day
28
+ #
29
+ # @example Basic Use
30
+ # client = Rescuetime::Client.new
31
+ # client.overview
32
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
33
+ #
34
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying kind
35
+ # as an overview report
36
+ #
37
+ # @see #categories Category report (alternative)
38
+ # @see #activities Activity report (alternative)
39
+ # @see #productivity Productivity report (alternative)
40
+ # @see #efficiency Efficiency report (alternative)
41
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
42
+ # (see: restrict_kind)
14
43
  def overview
15
44
  add_to_query restrict_kind: 'overview'
16
45
  end
17
46
 
18
- # @return [Rescuetime::Collection]
19
- # @since v0.3.0
47
+ # Returns a Rescuetime category report, grouping activities into their
48
+ # second-level (sub-)categories
49
+ #
50
+ # Defaults:
51
+ # - order: rank
52
+ # - date range: current day
53
+ #
54
+ # @example Basic Use
55
+ # client = Rescuetime::Client.new
56
+ # client.categories
57
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
58
+ #
59
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying kind
60
+ # as a category report
61
+ #
62
+ # @see #overview Overview report (alternative)
63
+ # @see #activities Activity report (alternative)
64
+ # @see #productivity Productivity report (alternative)
65
+ # @see #efficiency Efficiency report (alternative)
66
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
67
+ # (see: restrict_kind)
20
68
  def categories
21
69
  add_to_query restrict_kind: 'category'
22
70
  end
23
71
 
24
- # @return [Rescuetime::Collection]
25
- # @since v0.1.0
72
+ # Returns a Rescuetime activity report, grouping activities by their
73
+ # specific application, website, or activity
74
+ #
75
+ # Defaults:
76
+ # - order: rank
77
+ # - date range: current day
78
+ #
79
+ # @example Basic Use
80
+ # client = Rescuetime::Client.new
81
+ # client.activities
82
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
83
+ #
84
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying kind
85
+ # as a activity report
86
+ #
87
+ # @see #overview Overview report (alternative)
88
+ # @see #categories Category report (alternative)
89
+ # @see #productivity Productivity report (alternative)
90
+ # @see #efficiency Efficiency report (alternative)
91
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
92
+ # (see: restrict_kind)
26
93
  def activities
27
94
  add_to_query restrict_kind: 'activity'
28
95
  end
29
96
 
30
- # @return [Rescuetime::Collection]
31
- # @since v0.2.0
97
+ # Returns a Rescuetime productivity report, featuring productivity
98
+ # calculations
99
+ #
100
+ # Defaults:
101
+ # - order: rank
102
+ # - date range: current day
103
+ #
104
+ # @example Basic Use
105
+ # client = Rescuetime::Client.new
106
+ # client.productivity
107
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
108
+ #
109
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying kind
110
+ # as a productivity report
111
+ #
112
+ # @see #overview Overview report (alternative)
113
+ # @see #categories Category report (alternative)
114
+ # @see #activities Activity report (alternative)
115
+ # @see #efficiency Efficiency report (alternative)
116
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
117
+ # (see: restrict_kind)
32
118
  def productivity
33
119
  add_to_query restrict_kind: 'productivity'
34
120
  end
35
121
 
36
- # @return [Rescuetime::Collection]
37
- # @since v0.2.0
122
+ # Returns a Rescuetime efficiency report, featuring efficiency calculations.
123
+ # Defaults to chronological (time) order.
124
+ #
125
+ # Defaults:
126
+ # - order: time
127
+ # - date range: current day
128
+ #
129
+ # @example Basic Use
130
+ # client = Rescuetime::Client.new
131
+ # client.efficiency
132
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
133
+ #
134
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying kind
135
+ # as a efficiency report
136
+ #
137
+ # @see #overview Overview report (alternative)
138
+ # @see #categories Category report (alternative)
139
+ # @see #activities Activity report (alternative)
140
+ # @see #productivity Productivity report (alternative)
141
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
142
+ # (see: restrict_kind)
38
143
  def efficiency
39
- add_to_query restrict_kind: 'efficiency', perspective: 'interval'
144
+ add_to_query restrict_kind: 'efficiency',
145
+ perspective: 'interval'
40
146
  end
41
147
 
42
- # @return [Rescuetime::Collection]
43
- # @since v0.3.0
44
- def order_by(order, opts = {})
45
- fail(InvalidQueryError, "#{order} is not a valid order") unless
46
- VALID[:order_by].include? order
47
- fail(InvalidQueryError, "#{opts[:interval]} is not a valid interval") if
48
- opts[:interval] && !VALID[:interval].include?(opts[:interval])
148
+ # Specifies the ordering and the interval of the returned Rescuetime report.
149
+ # For example, the results can be ordered by time, activity rank, or member;
150
+ # The results can be returned in intervals spanning a month, a week, a day,
151
+ # an hour, or 5-minutes.
152
+ #
153
+ # Efficiency reports default to :time order; everything else defaults to
154
+ # :rank order.
155
+ #
156
+ # @example Basic Use
157
+ # client = Rescuetime::Client.new
158
+ # client.order_by 'time' # interval is not required
159
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
160
+ #
161
+ # client.order_by 'rank', interval: 'hour'
162
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
163
+ #
164
+ # client.order_by :rank, interval: :hour # Symbols are also valid
165
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
166
+ #
167
+ # @example Invalid Values Raise Errors
168
+ # client = Rescuetime::Client.new
169
+ # client.order_by 'invalid'
170
+ # # => Rescuetime::InvalidQueryError: invalid is not a valid order
171
+ #
172
+ # client.order_by 'time', interval: 'invalid'
173
+ # # => Rescuetime::InvalidQueryError: invalid is not a valid interval
174
+ #
175
+ # @param [#to_s] order an order for the results (ex. 'time')
176
+ # @param [#intern, nil] interval a chunking interval for results
177
+ # (ex. 'month'). defaults to nil
178
+ #
179
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying order
180
+ # and interval (if set)
181
+ #
182
+ # @raise [Rescuetime::InvalidQueryError] if either order or interval are
183
+ # invalid
184
+ #
185
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
186
+ # (see: perspective,
187
+ # resolution_time)
188
+ # @see Rescuetime::QueryBuildable::VALID List of valid values
189
+ def order_by(order, interval: nil)
190
+ # set order and intervals as symbols
191
+ order = order.to_s
192
+ interval = interval ? interval.to_s : nil
49
193
 
50
- add_to_query perspective: (order.to_s == 'time' ? 'interval' : order),
51
- resolution_time: opts[:interval] || opts['interval']
194
+ # guards against invalid order or interval
195
+ unless valid_order? order
196
+ fail InvalidQueryError, "#{order} is not a valid order"
197
+ end
198
+
199
+ unless valid_interval? interval
200
+ fail InvalidQueryError, "#{interval} is not a valid interval"
201
+ end
202
+
203
+ add_to_query perspective: (order == 'time' ? 'interval' : order),
204
+ resolution_time: interval
52
205
  end
53
206
 
54
- # @return [Rescuetime::Collection]
207
+ # Limits the Rescuetime report to a specific single date. To specify a date
208
+ # range, use #from and #to.
209
+ #
210
+ # @example Basic Use
211
+ # client = Rescuetime::Client.new
212
+ # client.date Date.parse('1776-07-04')
213
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
214
+ #
215
+ # @example Valid Date Formats
216
+ # client.date Time.parse('1776-07-04') # responds to #strftime
217
+ # client.date '1776-07-04'
218
+ # client.date '1776/07/04'
219
+ # client.date '07-04-1776'
220
+ # client.date '07/04/1776'
221
+ # client.date '07-04' # defaults to current year
222
+ # client.date '07/04' # defaults to current year
223
+ #
224
+ # @param [#strftime, String] date a valid date-like object
225
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying
226
+ # report date
227
+ #
228
+ # @raise [Rescuetime::InvalidQueryError] if the date format is invalid
229
+ #
230
+ # @see #from
231
+ # @see #to
232
+ # @see Rescuetime::DateParser
233
+ # @see Rescuetime::DateParser::DATE_FORMATS Valid date formats
234
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
235
+ # (see: restrict_begin,
236
+ # restrict_end)
55
237
  # @since v0.3.0
56
238
  def date(date)
57
239
  add_to_query restrict_end: to_formatted_s(date),
58
240
  restrict_begin: to_formatted_s(date)
59
241
  end
60
242
 
61
- # @return [Rescuetime::Collection]
243
+ # Limits the Rescuetime report to a specific start date. If the end date is
244
+ # not specified, it defaults to today.
245
+ #
246
+ # @example Basic Use
247
+ # client = Rescuetime::Client.new
248
+ # client.from 3.days.ago
249
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
250
+ #
251
+ # @example With a specified end date
252
+ # client.from(2.weeks.ago).to(1.week.ago)
253
+ # client.from(2.weeks.ago) # defaults to ending today
254
+ #
255
+ # @param [#strftime, String] date a valid date-like object
256
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying
257
+ # report date
258
+ #
259
+ # @raise [Rescuetime::InvalidQueryError] if the date format is invalid
260
+ #
261
+ # @see #to
262
+ # @see #date
263
+ # @see Rescuetime::DateParser
264
+ # @see Rescuetime::DateParser::DATE_FORMATS Valid date formats
265
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
266
+ # (see: restrict_begin)
62
267
  # @since v0.3.0
63
268
  def from(date)
64
269
  add_to_query restrict_begin: to_formatted_s(date)
65
270
  end
66
271
 
67
- # @return [Rescuetime::Collection]
272
+ # Limits the Rescuetime report to a specific end date. Requires #from to
273
+ # be set before the query is sent, but it doesn't matter if that happens
274
+ # before or after the call to #to.
275
+ #
276
+ # @example Basic Use
277
+ # client = Rescuetime::Client.new
278
+ # client.from(3.days.ago).to(1.day.ago)
279
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
280
+ #
281
+ # @example Requires a specified start date
282
+ # client.from(2.weeks.ago).to(1.week.ago).all # valid
283
+ # #=> [ ... ]
284
+ #
285
+ # client.to(1.week.ago).from(2.weeks.ago).all # valid
286
+ # #=> [ ... ]
287
+ #
288
+ # client.to(1.week.ago).all # invalid!
289
+ # #=> Rescuetime::InvalidQueryError
290
+ #
291
+ # @param [#strftime, String] date a valid date-like object
292
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying
293
+ # report date
294
+ #
295
+ # @raise [Rescuetime::InvalidQueryError] if the date format is invalid
296
+ #
297
+ # @see #from
298
+ # @see #date
299
+ # @see Rescuetime::DateParser
300
+ # @see Rescuetime::DateParser::DATE_FORMATS Valid date formats
301
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
302
+ # (see: restrict_end)
68
303
  # @since v0.3.0
69
304
  def to(date)
70
305
  add_to_query restrict_end: to_formatted_s(date)
71
306
  end
72
307
 
73
- # @return [Rescuetime::Collection]
308
+ # Limits the Rescuetime report to specific activities and documents.
309
+ # The name option limits the results to those where name is an exact match;
310
+ # this can be used on the overview, activity, and category report. The
311
+ # document option limits the specific document title; this is only available
312
+ # on the activity report.
313
+ #
314
+ # If a value is passed for an unsupported report, it will be ignored.
315
+ #
316
+ # To see the category and document names for your account, please look at
317
+ # your rescuetime dashboard or generated rescuetime report.
318
+ #
319
+ # :name can be used on:
320
+ # - Overview report
321
+ # - Category report
322
+ # - Activity report
323
+ #
324
+ # :document can be used on:
325
+ # - Activity report when :name is set
326
+ #
327
+ # @example Basic Use
328
+ # client = Rescuetime::Client.new
329
+ # client.activities.where name: 'github.com',
330
+ # document: 'leesharma/rescuetime'
331
+ # #=> #<Rescuetime::Collection:0x007f93841681a8>
332
+ #
333
+ # @example Valid reports
334
+ # client.overview.where name: 'Utilities'
335
+ # client.categories.where name: 'Intelligence'
336
+ # client.activities.where name: 'github.com'
337
+ # client.activities.where name: 'github.com', document: 'rails/rails'
338
+ #
339
+ # @example Invalid reports
340
+ # client.productivity.where name: 'Intelligence' # Invalid!
341
+ # client.efficiency.where name: 'Intelligence' # Invalid!
342
+ # client.activities.where document: 'rails/rails' # Invalid!
343
+ #
344
+ # @param [String] name Rescuetime category name, valid on overview,
345
+ # category, and activity reports
346
+ # @param [String] document Specific document name, valid on activity
347
+ # reports when :name is set
348
+ #
349
+ # @return [Rescuetime::Collection] a Rescuetime Collection specifying
350
+ # category name and (optionally) document
351
+ # @raises [ArgumentError] if name is not set
352
+ #
353
+ # @see #overview
354
+ # @see #activities
355
+ # @see #categories
356
+ # @see https://www.rescuetime.com/apidoc#paramlist Rescuetime API docs
357
+ # (see: restrict_thing,
358
+ # restrict_thingy)
74
359
  # @since v0.3.0
75
- def where(options)
76
- add_to_query restrict_thing: options[:name],
77
- restrict_thingy: options[:document]
360
+ def where(name: nil, document: nil)
361
+ # Stand-in for required keyword arguments
362
+ name or fail ArgumentError, 'missing keyword: name'
363
+
364
+ add_to_query restrict_thing: name,
365
+ restrict_thingy: document
78
366
  end
79
367
 
80
368
  private
81
369
 
82
- VALID = {
83
- order_by: [:time, :rank, :member],
84
- interval: [:minute, :hour, :day, :week, :month]
85
- }
86
-
370
+ # Adds terms to the Rescuetime collection query
371
+ #
372
+ # @param [Hash] terms a set of terms to add to the query
373
+ # @return [Rescuetime::Collection]
87
374
  def add_to_query(**terms)
88
375
  if self.is_a? Rescuetime::Collection
89
376
  self << terms
@@ -93,22 +380,55 @@ module Rescuetime
93
380
  end
94
381
  end
95
382
 
383
+ # Returns a list of valid orders
384
+ #
385
+ # @return [Array<String>] a list of valid order arguments
386
+ # @since v0.3.2
387
+ def valid_orders
388
+ VALID[:order_by]
389
+ end
390
+
391
+ # Returns a list of valid intervals
392
+ #
393
+ # @return [Array<String>] a list of valid interval arguments
394
+ # @since v0.3.2
395
+ def valid_intervals
396
+ VALID[:interval]
397
+ end
398
+
399
+ # Returns true if the given order is valid
400
+ #
401
+ # @param [#to_s] order order to check for validity
402
+ # @return [Boolean] true if the order is valid
403
+ # @since v0.3.2
404
+ def valid_order?(order)
405
+ valid_orders.include? order.to_s
406
+ end
407
+
408
+ # Returns true if the given interval is valid.
409
+ #
410
+ # @param [#to_s] interval interval to check for validity
411
+ # @return [Boolean] true if the interval is valid
412
+ # @since v0.3.2
413
+ def valid_interval?(interval)
414
+ return true if interval.nil? # intervals aren't required
415
+ valid_intervals.include? interval.to_s
416
+ end
417
+
418
+ # Returns a string in the Rescuetime-date format ("YYYY-MM-DD")
419
+ #
420
+ # @param [#strftime, String] date
421
+ # @param [#parse] date_parser defaults to
422
+ # Rescuetime::DateParser
423
+ # @return [String] a string in 'YYYY-MM-DD' format
424
+ #
425
+ # @raise [Rescuetime::InvalidQueryError] if the date format is invalid
426
+ #
427
+ # @see Rescuetime::DateParser
428
+ # @see Rescuetime::DateParser::DATE_FORMATS Valid date formats
96
429
  # @since v0.3.0
97
- def to_formatted_s(date)
98
- case
99
- when date.respond_to?(:strftime) then date.strftime('%Y-%m-%d')
100
- when date =~ /\d{4}-\d{2}-\d{2}/ then date
101
- when date =~ %r{\d{4}\/\d{2}\/\d{2}} then date.gsub '/', '-'
102
- when date =~ %r{\d{2}[-\/]\d{2}[-\/]\d{4}/}
103
- date_chunks = date.scan(/\d+/)
104
- "#{ date_chunks[2] }-#{ date_chunks[0] }-#{ date_chunks[1] }"
105
- when date =~ %r{\d{2}[-\/]\d{2}}
106
- date_chunks = date.scan(/\d+/)
107
- "#{ Time.now.year }-#{ date_chunks[0] }-#{ date_chunks[1] }"
108
- else
109
- fail InvalidQueryError,
110
- 'Invalid date entered. Please see docs for allowed formats.'
111
- end
430
+ def to_formatted_s(date, date_parser: Rescuetime::DateParser)
431
+ date_parser.parse(date)
112
432
  end
113
433
  end
114
434
  end
@@ -4,37 +4,96 @@ module Rescuetime
4
4
  #
5
5
  # @since v0.2.0
6
6
  class Requester
7
+ # Contains bodies of error responses recieved from rescuetime.com. If one
8
+ # of these responses is recieved, an error will be raised.
9
+ INVALID = {
10
+ key_not_found: '"error":"# key not found","messages":"key not found"',
11
+ query: '"error": "# query error",'\
12
+ '"messages": "Error: Likely a badly formatted or missing parameter"'
13
+ }
14
+
7
15
  class << self
8
- # @since v0.2.0
9
- def get(host, params)
10
- fail(Rescuetime::MissingCredentialsError) unless
11
- params[:key] && !params[:key].to_s.empty?
16
+ # Performs the GET request to the specified host. Before making the
17
+ # request, it checks for API key presence and raises an exception if it
18
+ # is not present. All other exceptions require the request to go through.
19
+ #
20
+ # @param [String] host request host
21
+ # @param [Hash] params request parameters
22
+ # @param [#get] http HTTP requester
23
+ # @return [Faraday::Response]
24
+ #
25
+ # @raise [Rescuetime::MissingCredentialsError] if no api key is present
26
+ # @raise [Rescuetime::InvalidCredentialsError] if api key is incorrect
27
+ # @raise [Rescuetime::InvalidQueryError] if query is badly formed
28
+ # @raise [Rescuetime::Error] if response HTTP status is
29
+ # not 200
30
+ #
31
+ # @see Rescuetime::Client#api_key=
32
+ def get(host, params, http: Faraday)
33
+ # Fail if no api key is provided
34
+ unless params[:key] && !params[:key].to_s.empty?
35
+ fail(Rescuetime::MissingCredentialsError)
36
+ end
12
37
 
13
- response = Faraday.get(host,
14
- params.delete_if { |_, v| !v || v.to_s.empty? })
38
+ req_params = params.delete_if { |_, v| !v || v.to_s.empty? }
39
+ response = http.get host, req_params
15
40
 
16
41
  fail_or_return response
17
42
  end
18
43
 
19
44
  private
20
45
 
21
- INVALID = {
22
- key_not_found: '"error":"# key not found","messages":"key not found"',
23
- query: '"error": "# query error",'\
24
- '"messages": "Error: Likely a badly formatted or missing parameter"'
25
- }
26
-
46
+ # Checks for an error in the Rescuetime response. If an error was recieved
47
+ # raise the appropriate Rescuetime::Error. Otherwise, return the response.
48
+ #
49
+ # @param [Faraday::Response] response HTTP response from rescuetime.com
50
+ # @return [Faraday::Response] 200 HTTP response from rescuetime.com
51
+ #
52
+ # @raise [Rescuetime::InvalidCredentialsError] if api key is incorrect
53
+ # @raise [Rescuetime::InvalidQueryError] if query is badly formed
54
+ # @raise [Rescuetime::Error] an error that varies based on the
55
+ # response status
27
56
  def fail_or_return(response)
28
- fail Rescuetime::InvalidCredentialsError if
29
- response.body =~ /#{INVALID[:key_not_found]}/
30
- fail Rescuetime::InvalidQueryError if
31
- response.body =~ /#{INVALID[:query]}/
57
+ # match the response body to known error messages with 200 status
58
+ case response.body
59
+ when key_not_found? then fail Rescuetime::InvalidCredentialsError
60
+ when invalid_query? then fail Rescuetime::InvalidQueryError
61
+ end
32
62
 
63
+ # check the response status for errors
33
64
  error = Rescuetime::Error::CODES[response.status.to_i]
34
65
  fail(error) if error
35
66
 
36
67
  response
37
68
  end
69
+
70
+ # Returns lambda that returns true if the response body states that the
71
+ # api key was not found. For use in a case statement.
72
+ #
73
+ # This check is required because rescuetime.com returns a staus of 200
74
+ # with this error.
75
+ #
76
+ # @return [Lambda] a lambda that returns true if the api key was not found
77
+ #
78
+ # @see Rescuetime::Requester::INVALID
79
+ # @since v0.3.2
80
+ def key_not_found?
81
+ ->(body) { body =~ /#{INVALID[:key_not_found]}/ }
82
+ end
83
+
84
+ # Returns lambda that returns true if the response body states that the
85
+ # query was invalid. For user in a case statement.
86
+ #
87
+ # This check is required because rescuetime.com returns a staus of 200
88
+ # with this error.
89
+ #
90
+ # @return [Lambda] a lambda that returns true if the query was invalid
91
+ #
92
+ # @see Rescuetime::Requester::INVALID
93
+ # @since v0.3.2
94
+ def invalid_query?
95
+ ->(body) { body =~ /#{INVALID[:query]}/ }
96
+ end
38
97
  end
39
98
  end
40
99
  end
@@ -1,5 +1,5 @@
1
- # Contains Rescuetime::VERSION, the gem version number
1
+ # lib/rescuetime/version.rb
2
2
  module Rescuetime
3
3
  # rescuetime gem version number
4
- VERSION = '0.3.1'
4
+ VERSION = '0.3.2'
5
5
  end
data/rescuetime.gemspec CHANGED
@@ -31,4 +31,5 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency 'rspec-its'
32
32
  spec.add_development_dependency 'vcr', '~> 2.9', '>= 2.9.3'
33
33
  spec.add_development_dependency 'webmock', '~> 1.21', '>= 1.21.0'
34
+ spec.add_development_dependency 'timecop', '~> 0.8.0'
34
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rescuetime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Sharma
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-05-04 00:00:00.000000000 Z
11
+ date: 2016-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -132,6 +132,20 @@ dependencies:
132
132
  - - ">="
133
133
  - !ruby/object:Gem::Version
134
134
  version: 1.21.0
135
+ - !ruby/object:Gem::Dependency
136
+ name: timecop
137
+ requirement: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: 0.8.0
142
+ type: :development
143
+ prerelease: false
144
+ version_requirements: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: 0.8.0
135
149
  description: Ruby interface for the RescueTime Data Analytics API.
136
150
  email:
137
151
  - lee@leesharma.com
@@ -141,8 +155,9 @@ extra_rdoc_files: []
141
155
  files:
142
156
  - ".gitignore"
143
157
  - ".rspec"
158
+ - ".rubocop.yml"
159
+ - ".rubocop_todo.yml"
144
160
  - ".travis.yml"
145
- - CHANGELOG.md
146
161
  - CODE_OF_CONDUCT.md
147
162
  - CONTRIBUTING.md
148
163
  - Gemfile
@@ -154,6 +169,7 @@ files:
154
169
  - lib/rescuetime.rb
155
170
  - lib/rescuetime/client.rb
156
171
  - lib/rescuetime/collection.rb
172
+ - lib/rescuetime/date_parser.rb
157
173
  - lib/rescuetime/errors.rb
158
174
  - lib/rescuetime/query_buildable.rb
159
175
  - lib/rescuetime/requester.rb
@@ -179,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
195
  version: '0'
180
196
  requirements: []
181
197
  rubyforge_project:
182
- rubygems_version: 2.4.5
198
+ rubygems_version: 2.4.8
183
199
  signing_key:
184
200
  specification_version: 4
185
201
  summary: Ruby interface for RescueTime
data/CHANGELOG.md DELETED
@@ -1,13 +0,0 @@
1
- # Changelog
2
-
3
- ## v0.1.0 (April 15, 2015)
4
-
5
- Initial gem release
6
-
7
- ### Features
8
-
9
- * Rescuetime has a version number
10
- * `Rescuetime::Client` exists
11
- * `Rescuetime::Client#api_key?` returns existence of api key
12
- * `Rescuetime::Client#api_key=` overwrites api key
13
- * `Rescuetime::Client#activities` returns list of activities