rescuetime 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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