toggl_cache 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0d9815ad14776766ab2dbcbf4ee4dc059625d071
4
- data.tar.gz: 03c21e7d27ee0fc6835074749a6c07d28b9aaf35
3
+ metadata.gz: e5c97e1c35e17dd0e4e74f0194eefb1b9d100199
4
+ data.tar.gz: c4b6bfe066da7c539256d39dc98c00539795e7ec
5
5
  SHA512:
6
- metadata.gz: '0668183f52a17e6928f592e7cd78cc5088b89c6286f927edbf1a44268820a90fd21feb04698ced8f2caa005b858245b57a09113ea97db9c45ab856038fc51c1d'
7
- data.tar.gz: 9e59068942a8e45c1811ce82d3c1dc025b0d2dbb9b647b180c52291a89a5d5d6f1563d12f77a7b4c4961d5b8eb2664f1c789ca29de110cd5c2973ba88457d819
6
+ metadata.gz: d4f4ce467a89a23278e731de36b6a2e0df90d6164eb742a87e496f9c42667c232d7764df84b0a0c2b90a0ad0780d640fd65a7715c098ca70f29a04864fa34f41
7
+ data.tar.gz: 167afe2ed80546b63b0007e69dab6bd1d5ce2fd7a49deff03cf975a4578bbe16194c0cfcaf9e1d5b88812cc1ad9714230f5368a9b7ad6930c01fdb387c1f02f1
data/Gemfile CHANGED
@@ -23,4 +23,5 @@ group :test do
23
23
  gem 'coveralls', require: false
24
24
  gem 'simplecov', require: false
25
25
  gem 'simplecov-json', require: false
26
+ gem "codeclimate-test-reporter", "~> 1.0.0"
26
27
  end
data/HISTORY.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # History
2
2
 
3
+ ## 2017-03-25 - 0.2.0
4
+
5
+ Synchronizing deleted records and old changes.
6
+
7
+ Approach #1: calculate aggregates over periods and perform a full sync over the period if there is a diff.
8
+
9
+ - Pros: more efficient, smarter, also provides a synchronization correctness control.
10
+ - Cons: harder to implement.
11
+
12
+ Approach #2: brute-force, i.e. simply clear the content for a given period and perform a full sync.
13
+
14
+ - Pros: simpler to implement.
15
+ - Cons: synchronization is currently to long.
16
+
17
+ Trying approach #2. Optimizing synchronization:
18
+
19
+ - [X] processing fetched records once a page has been fetched, not at the end.
20
+ - [ ] perform requests in parallel.
21
+
22
+ The approach #2 is not applicable. Reviewing Toggl API's documentation, the rate limit is about 1 request per second, so parallelizing does not seem to be an option.
23
+
24
+ NB: performing a full synchronization leading to about 40K reports takes about 0.65 hour.
25
+
26
+ The approach #1 has been implemented. The algorithm first checks each full year. If a difference is detected, it checks each month of the year. For each month with a difference, a sync is done by clearing the cache for the month and fetching the reports.
27
+
28
+ The implementation has been done in `TogglCache.sync_check_and_fix`.
29
+
3
30
  ## 2017-03-18 - 0.1.1
4
31
 
5
32
  - Changed default period of import to one week from now, instead of one day.
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # TogglCache
2
2
 
3
- Fetches reports data from Toggl API and caches them in a MongoDB store.
3
+ Fetches reports data from Toggl API and caches them in a PostgreSQL database.
4
4
 
5
5
  This allows you to build applications performing complex operations on a large number of Toggl content without the Toggl API latency. You may also use it to backup your precious data!
6
6
 
7
7
  [![Build Status](https://travis-ci.org/rchampourlier/toggl_cache.svg)](https://travis-ci.org/rchampourlier/toggl_cache)
8
- [![Code Climate](https://codeclimate.com/github/rchampourlier/toggl_cache/badges/gpa.svg)](https://codeclimate.com/github/rchampourlier/toggl_cache)
9
- [![Coverage Status](https://coveralls.io/repos/github/rchampourlier/toggl_cache/badge.svg?branch=master)](https://coveralls.io/github/rchampourlier/toggl_cache?branch=master)
8
+ [![Code Climate](https://codeclimate.com/repos/58d7ff3b88ccb7027b000baa/badges/182b308109bf20bd9dbf/gpa.svg)](https://codeclimate.com/repos/58d7ff3b88ccb7027b000baa/feed)
9
+ [![Test Coverage](https://codeclimate.com/repos/58d7ff3b88ccb7027b000baa/badges/182b308109bf20bd9dbf/coverage.svg)](https://codeclimate.com/repos/58d7ff3b88ccb7027b000baa/coverage)
10
10
 
11
11
  ## Installation
12
12
 
@@ -39,6 +39,12 @@ client = TogglCache::Client.new(
39
39
  TogglCache.sync_reports(client, 'TOGGL-WORKSPACE-ID')
40
40
  ```
41
41
 
42
+ ## The CLI
43
+
44
+ ```
45
+ ruby lib/toggl_cli.rb [batch|help]
46
+ ```
47
+
42
48
  ## Contributing
43
49
 
44
50
  1. Fork it ( https://github.com/rchampourlier/toggl_cache/fork )
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
@@ -10,28 +10,26 @@ module TogglAPI
10
10
  API_URL = "https://toggl.com/reports/api/v2"
11
11
 
12
12
  # @param params [Hash]: Toggl API params
13
- # - date_since
14
- # - date_until
13
+ # - since
14
+ # - until
15
15
  # - workspace_id
16
16
  # - ... more params available, see Toggl API documentation for details
17
17
  def fetch_reports(params)
18
+ raise "Must give a block" unless block_given?
18
19
  page = 1
19
- all_results = []
20
+ current_total = 0
20
21
  loop do
21
22
  results_raw = fetch_reports_details_raw(
22
23
  params.merge(page: page)
23
24
  )
24
- results = results_raw["data"]
25
+ yield(results_raw["data"])
25
26
 
26
- all_results += results
27
- break if all_results.count == results_raw["total_count"]
27
+ current_total += results_raw["data"].count
28
+ break if current_total == results_raw["total_count"]
28
29
  page += 1
29
30
  end
30
- all_results
31
31
  end
32
32
 
33
- private
34
-
35
33
  def fetch_reports_details_raw(params)
36
34
  fetch_reports_raw(api_url(:details), params)
37
35
  end
@@ -40,6 +38,8 @@ module TogglAPI
40
38
  fetch_reports_raw(api_url(:summary), params)
41
39
  end
42
40
 
41
+ private
42
+
43
43
  # @param url [String]
44
44
  # @param params [Hash]: Toggl API params
45
45
  def fetch_reports_raw(url, params)
@@ -5,8 +5,10 @@ require "active_support/inflector"
5
5
  module TogglCache
6
6
  module Data
7
7
 
8
- # Superclass for repositories. Simply provide some shared
9
- # methods.
8
+ # Repository for Toggl reports.
9
+ #
10
+ # TODO: should be used through instances
11
+ # TODO: #table should be private
10
12
  class ReportRepository
11
13
 
12
14
  MAPPED_REPORT_ATTRIBUTES = %w(
@@ -22,7 +24,7 @@ module TogglCache
22
24
  # It inserts a new issue row with the specified data.
23
25
  # If the issue already exists (unicity key is `id`)
24
26
  # the row is updated instead.
25
- def self.create_or_update(report)
27
+ def create_or_update(report)
26
28
  id = report["id"].to_s
27
29
  if exist_with_id?(id)
28
30
  update_where({ id: id }, row(report: report))
@@ -31,39 +33,65 @@ module TogglCache
31
33
  end
32
34
  end
33
35
 
34
- def self.find_by_id(id)
36
+ def find_by_id(id)
35
37
  table.where(id: id).first
36
38
  end
37
39
 
38
- def self.exist_with_id?(id)
40
+ def exist_with_id?(id)
39
41
  table.where(id: id).count != 0
40
42
  end
41
43
 
42
- def self.delete_where(where_data)
44
+ def delete_where(where_data)
43
45
  table.where(where_data).delete
44
46
  end
45
47
 
46
- def self.update_where(where_data, values)
48
+ def update_where(where_data, values)
47
49
  table.where(where_data).update(values)
48
50
  end
49
51
 
50
- def self.first_where(where_data)
52
+ def first(by: :start)
53
+ table.order(by).first
54
+ end
55
+
56
+ def first_where(where_data)
51
57
  table.where(where_data).first
52
58
  end
53
59
 
54
- def self.index
60
+ def index
55
61
  table.entries
56
62
  end
57
63
 
58
- def self.count
64
+ def count
59
65
  table.count
60
66
  end
61
67
 
62
- def self.table
68
+ # Returns reports whose `start` time is within the specified range.
69
+ #
70
+ # @param since: [Time]
71
+ # @param until: [Time]
72
+ def starting(time_since:, time_until:)
73
+ table.where("start >= ? AND start <= ?", time_since, time_until).entries
74
+ end
75
+
76
+ def delete_starting(time_since:, time_until:)
77
+ table.where("start >= ? AND start <= ?", time_since, time_until).delete
78
+ end
79
+
80
+ # @param pid [Integer]
81
+ # @param tid [Integer] optional
82
+ def where(project_id:, task_id: nil)
83
+ where_criteria = { pid: project_id }
84
+ where_criteria[:tid] = tid if task_id
85
+ table.where(where_criteria).entries
86
+ end
87
+
88
+ private
89
+
90
+ def table
63
91
  DB[:toggl_cache_reports]
64
92
  end
65
93
 
66
- def self.row(report:, insert_created_at: false, insert_updated_at: true)
94
+ def row(report:, insert_created_at: false, insert_updated_at: true)
67
95
  new_report = map_report_attributes(report: report)
68
96
  new_report = add_timestamps(
69
97
  report: new_report,
@@ -73,7 +101,7 @@ module TogglCache
73
101
  new_report
74
102
  end
75
103
 
76
- def self.map_report_attributes(report:)
104
+ def map_report_attributes(report:)
77
105
  new_report = report.select { |k, _| MAPPED_REPORT_ATTRIBUTES.include?(k) }
78
106
  new_report = new_report.merge(
79
107
  duration: report["dur"] / 1_000,
@@ -85,7 +113,7 @@ module TogglCache
85
113
  new_report
86
114
  end
87
115
 
88
- def self.add_timestamps(report:, insert_created_at:, insert_updated_at:)
116
+ def add_timestamps(report:, insert_created_at:, insert_updated_at:)
89
117
  new_report = {}.merge(report)
90
118
  new_report["created_at"] = Time.now if insert_created_at
91
119
  new_report["updated_at"] = Time.now if insert_updated_at
data/lib/toggl_cache.rb CHANGED
@@ -20,17 +20,130 @@ module TogglCache
20
20
  # The fetched reports either update the already
21
21
  # existing ones, or create new ones.
22
22
  #
23
- # @param client [TogglAPI::Client] a configured client
24
- # @param workspace_id [String] Toggl workspace ID (mandatory)
25
23
  # @param date_since [Date] Date since when to fetch
26
24
  # the reports.
27
- def self.sync_reports(client: default_client,
28
- date_since: default_date_since)
29
- reports = fetch_reports(
25
+ # @param date_until [Date] Date until when to fetch. Defaults to `Time.now`.
26
+ # @param client [TogglAPI::Client] a configured client
27
+ def self.sync_reports(
28
+ date_since: default_date_since,
29
+ date_until: Time.now,
30
+ logger: default_logger,
31
+ client: default_client
32
+ )
33
+ logger.info "Syncing reports from #{date_since} to #{date_until}."
34
+ clear_cache(
35
+ time_since: Time.parse("#{date_since} 00:00:00Z"),
36
+ time_until: Time.parse("#{date_until} 23:59:59Z")
37
+ )
38
+ fetch_reports(
30
39
  client: client,
31
- date_since: date_since
40
+ date_since: date_since,
41
+ date_until: date_until
42
+ ) do |reports|
43
+ process_reports(reports)
44
+ end
45
+ end
46
+
47
+ # Performs a full synchronization check, from the time of the first
48
+ # report in the cache to now. Proceeds by comparing reports total
49
+ # duration from Toggl (using the Reports API) and the total contained
50
+ # in the cache. If a difference is detected, proceeds monthly and
51
+ # clear and reconstructs the cache for the invovled month.
52
+ #
53
+ # TODO: enable detecting a change in project/task level aggregates.
54
+ def self.sync_check_and_fix(logger: default_logger)
55
+ reports = TogglCache::Data::ReportRepository.new
56
+ first_report = reports.first
57
+
58
+ year_start = first_report[:start].year
59
+ year_end = Time.now.year
60
+ month_start = first_report[:start].month
61
+ month_end = Time.now.month
62
+
63
+ (year_start..year_end).each do |year|
64
+ year_toggl = TogglCache.toggl_total(year: year)
65
+ year_cache = TogglCache.cache_total(year: year)
66
+ if year_toggl == year_cache
67
+ logger.info "Checked total for #{year}: ✅ (#{year_toggl})"
68
+ next
69
+ end
70
+ logger.info "Checked total for #{year}: ❌ (Toggl: #{year_toggl}, cache: #{year_cache})"
71
+ (1..12).each do |month|
72
+ next if year == year_start && month < month_start
73
+ next if year == year_end && month > month_end
74
+ month_toggl = TogglCache.toggl_total(year: year, month: month)
75
+ month_cache = TogglCache.cache_total(year: year, month: month)
76
+ if month_toggl == month_cache
77
+ logger.info "Checked total for #{year}/#{month}: ✅ (#{month_toggl})"
78
+ else
79
+ logger.info "Checked total for #{year}/#{month}: ❌ (Toggl: #{month_toggl}, cache: #{month_cache})"
80
+ TogglCache.clear_cache(year: year, month: month)
81
+ TogglCache.sync_reports_for_month(year: year, month: month)
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # An easy-to-use method to sync reports for a given month. Simply
88
+ # performs a call to `sync_reports`.
89
+ #
90
+ # @param year [Integer]
91
+ # @param month [Integer
92
+ def self.sync_reports_for_month(year:, month:)
93
+ date_since = Date.civil(year, month, 1)
94
+ date_until = Date.civil(year, month, -1)
95
+ sync_reports(
96
+ date_since: date_since,
97
+ date_until: date_until
98
+ )
99
+ end
100
+
101
+ # Remove TogglCache's reports between the specified dates.
102
+ def self.clear_cache(time_since:, time_until:)
103
+ logger.info "Clearing cache from #{date_since} to #{date_until}."
104
+ reports = Data::ReportRepository.new
105
+ reports.delete_starting(
106
+ time_since: time_since,
107
+ time_until: time_until
108
+ )
109
+ end
110
+
111
+ # Remove TogglCache's reports for the specified month.
112
+ def self.clear_cache_for_month(year:, month:, logger: default_logger)
113
+ date_since = Date.civil(year, month, 1)
114
+ date_until = Date.civil(year, month, -1)
115
+ clear_cache(
116
+ time_since: Time.parse("#{date_since} 00:00:00Z"),
117
+ time_until: Time.parse("#{date_until} 23:59:59Z")
32
118
  )
33
- process_reports(reports)
119
+ end
120
+
121
+ # Returns the total duration from Toggl (using Reports API)
122
+ # for the specified year or month.
123
+ def self.toggl_total(year:, month: nil)
124
+ reports_client = TogglAPI::ReportsClient.new
125
+ date_since = month ? Date.civil(year, month, 1) : Date.civil(year, 1, 1)
126
+ date_until = month ? Date.civil(year, month, -1) : Date.civil(year, 12, -1)
127
+ total_grand = reports_client.fetch_reports_summary_raw(
128
+ since: date_since.to_s,
129
+ until: date_until.to_s,
130
+ workspace_id: ENV["TOGGL_WORKSPACE_ID"]
131
+ )["total_grand"]
132
+ total_grand ? total_grand / 3600 / 1000 : 0
133
+ end
134
+
135
+ # Returns the total duration in TogglCache reports
136
+ # for the specified year or month.
137
+ def self.cache_total(year:, month: nil)
138
+ reports = TogglCache::Data::ReportRepository.new
139
+ date_since = month ? Date.civil(year, month, 1).to_s : Date.civil(year, 1, 1)
140
+ date_until = month ? Date.civil(year, month, -1).to_s : Date.civil(year, 12, -1)
141
+ time_since = Time.parse("#{date_since} 00:00:00Z")
142
+ time_until = Time.parse("#{date_until} 23:59:59Z")
143
+ reports.starting(
144
+ time_since: time_since,
145
+ time_until: time_until
146
+ ).inject(0) { |sum, r| sum + r[:duration] } / 3600
34
147
  end
35
148
 
36
149
  # Fetch from Toggl
@@ -47,32 +160,37 @@ module TogglCache
47
160
  client: default_client,
48
161
  workspace_id: default_workspace_id,
49
162
  date_since:,
50
- date_until: Time.now
163
+ date_until: Time.now,
164
+ &block
51
165
  )
166
+ raise "You must give a block to process fetched records" unless block_given?
52
167
  if date_since && date_until.year > date_since.year
53
- fetch_reports(
54
- client: client,
55
- workspace_id: workspace_id,
56
- date_since: date_since,
57
- date_until: Date.new(date_since.year, 12, 31)
58
- ) + fetch_reports(
59
- client: client,
60
- workspace_id: workspace_id,
61
- date_since: Date.new(date_since.year + 1, 1, 1),
62
- date_until: date_until
63
- )
168
+ [
169
+ [date_since, Date.new(date_since.year, 12, 31)],
170
+ [Date.new(date_since.year + 1, 1, 1), date_until]
171
+ ].each do |dates|
172
+ fetch_reports(
173
+ client: client,
174
+ workspace_id: workspace_id,
175
+ date_since: dates.first,
176
+ date_until: dates.last,
177
+ &block
178
+ )
179
+ end
64
180
  else
65
181
  options = {
66
182
  workspace_id: workspace_id, until: date_until.strftime("%Y-%m-%d")
67
183
  }
68
184
  options[:since] = date_since.strftime("%Y-%m-%d") unless date_since.nil?
69
- client.fetch_reports(options)
185
+ client.fetch_reports(options, &block)
70
186
  end
71
187
  end
72
188
 
73
- def self.process_reports(reports)
189
+ def self.process_reports(reports, logger: default_logger)
190
+ logger.debug "Processing #{reports.count} Toggl reports"
191
+ repository = Data::ReportRepository.new
74
192
  reports.each do |report|
75
- Data::ReportRepository.create_or_update(report)
193
+ repository.create_or_update(report)
76
194
  end
77
195
  end
78
196
 
data/lib/toggl_cli.rb CHANGED
@@ -3,7 +3,6 @@ require "thor"
3
3
  require "tty-pager"
4
4
  require "tty-progressbar"
5
5
  require "tty-spinner"
6
- # require "tty-table"
7
6
  require File.expand_path("../../config/boot", __FILE__)
8
7
  require "toggl_api/client"
9
8
  require "toggl_cache/data/report_repository"
@@ -48,7 +47,7 @@ class TogglCLI < Thor
48
47
  say("MOVING!!!")
49
48
  end
50
49
  end
51
- # TogglCache::Data::ReportRepository.create_or_update(report)
50
+ TogglCache::Data::ReportRepository.new.create_or_update(report)
52
51
  end
53
52
 
54
53
  private
@@ -112,10 +111,9 @@ class TogglCLI < Thor
112
111
  end
113
112
 
114
113
  def find_reports(project_id, task_id)
114
+ reports = TogglCache::Data::ReportRepository.new
115
115
  with_spinner("Fetching matching reports...") do
116
- where_criteria = { pid: project_id.to_i }
117
- where_criteria[:tid] = task_id if task_id
118
- TogglCache::Data::ReportRepository.table.where(where_criteria).entries
116
+ reports.where(project_id: project_id&.to_i, task_id: task_id&.to_i)
119
117
  end
120
118
  end
121
119
 
data/scripts/sync.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Usage:
4
4
  #
5
- # - Syncs reports since 1 month:
5
+ # - Syncs reports since 31 days::
6
6
  # ruby scripts/sync.rb
7
7
  #
8
8
  # - Sync reports since the specified date (use Ruby-parseable
@@ -16,5 +16,11 @@ require 'bundler/setup'
16
16
  $LOAD_PATH.unshift File.expand_path('../..', __FILE__)
17
17
  require 'config/boot'
18
18
 
19
- date_since = Time.parse(ARGV[0]) unless ARGV[0].nil? || ARGV[0].empty?
19
+ date_since = (
20
+ if ARGV[0].nil? || ARGV[0].empty?
21
+ Time.now - 31 * 24 * 3600
22
+ else
23
+ Time.parse(ARGV[0])
24
+ end
25
+ )
20
26
  TogglCache.sync_reports(date_since: date_since)
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Check that reports stored in TogglCache match report summaries
5
+ # provided by toggl Reports API.
6
+ #
7
+ # Usage:
8
+ #
9
+ # ruby scripts/sync_check_and_fix.rb
10
+
11
+ # Load dependencies
12
+ require "rubygems"
13
+ require "bundler/setup"
14
+ require "logger"
15
+
16
+ $LOAD_PATH.unshift File.expand_path("../..", __FILE__)
17
+ require "config/boot"
18
+
19
+ TogglCache.sync_check_and_fix
data/spec/spec_helper.rb CHANGED
@@ -35,7 +35,7 @@ RSpec.configure do |config|
35
35
  end
36
36
 
37
37
  config.after(:each) do
38
- TogglCache::Data::ReportRepository.delete_where("TRUE")
38
+ TogglCache::Data::ReportRepository.new.delete_where("TRUE")
39
39
  end
40
40
 
41
41
  config.after(:all) do
@@ -4,8 +4,13 @@ require "toggl_api/reports_client"
4
4
 
5
5
  describe TogglAPI::ReportsClient do
6
6
  let(:options) { {} }
7
- let(:expected_api_call_url) { "https://toggl_api_token:api_token@toggl.com/reports/api/v2/details?page=1&user_agent=TogglAPI" }
8
-
7
+ let(:expected_api_call_url) { "https://toggl.com/reports/api/v2/details?page=1&user_agent=TogglAPI" }
8
+ let(:expected_headers) do
9
+ {
10
+ "Authorization" => "Basic #{["toggl_api_token:api_token"].pack('m').delete("\r\n")}",
11
+ "Content-Type" => "application/json"
12
+ }
13
+ end
9
14
  let(:api_response_status) { 200 }
10
15
  let(:api_response_body) do
11
16
  {
@@ -20,24 +25,25 @@ describe TogglAPI::ReportsClient do
20
25
 
21
26
  before do
22
27
  stub_request(:get, expected_api_call_url)
23
- .with(headers: { "Content-Type" => "application/json" })
28
+ .with(headers: expected_headers)
24
29
  .to_return(status: api_response_status, body: api_response_body, headers: {})
25
30
  end
26
31
 
27
- describe "#fetch_reports(params)" do
32
+ describe "#fetch_reports(params, &block)" do
28
33
 
29
34
  context "only 1 page" do
30
35
  let(:total_count) { 50 }
31
36
 
32
37
  it "fetches the data only once" do
33
- described_class.new.fetch_reports(options)
38
+ described_class.new.fetch_reports(options) {}
34
39
  # It will fail if it fetches several times because
35
40
  # the second request, with page=2, is not mocked.
36
41
  end
37
42
 
38
- it "returns the received data" do
39
- results = described_class.new.fetch_reports(options)
40
- expect(results.count).to eq(50)
43
+ it "passes the received data to the specified block" do
44
+ results = described_class.new.fetch_reports(options) do |reports|
45
+ expect(reports.count).to eq(50)
46
+ end
41
47
  end
42
48
  end
43
49
 
@@ -53,18 +59,16 @@ describe TogglAPI::ReportsClient do
53
59
  data: data_page2
54
60
  }.to_json
55
61
  stub_request(:get, expected_api_call_url.gsub('page=1', 'page=2'))
56
- .with(headers: { "Content-Type" => "application/json" })
62
+ .with(headers: expected_headers)
57
63
  .to_return(status: api_response_status, body: api_response_body_page2, headers: {})
58
64
  end
59
65
 
60
66
  it "fetches the additional pages" do
61
- results = described_class.new.fetch_reports(options)
62
- expect(results.count).to eq(60)
63
- end
64
-
65
- it "returns the merged data" do
66
- results = described_class.new.fetch_reports(options)
67
- expect(results.inject(0) { |s, i| s + i["id"] }).to eq(50 * 1 + 10 * 2)
67
+ total_count = 0
68
+ results = described_class.new.fetch_reports(options) do |reports|
69
+ total_count += reports.count
70
+ end
71
+ expect(total_count).to eq(60)
68
72
  end
69
73
  end
70
74
 
@@ -72,7 +76,7 @@ describe TogglAPI::ReportsClient do
72
76
  let(:api_response_status) { 500 }
73
77
 
74
78
  it "raises a TogglAPI::Error" do
75
- expect { described_class.new.fetch_reports(options) }
79
+ expect { described_class.new.fetch_reports(options) {} }
76
80
  .to raise_error(TogglAPI::Error)
77
81
  end
78
82
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toggl_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Romain Champourlier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-18 00:00:00.000000000 Z
11
+ date: 2017-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -204,6 +204,7 @@ files:
204
204
  - lib/toggl_cache/version.rb
205
205
  - lib/toggl_cli.rb
206
206
  - scripts/sync.rb
207
+ - scripts/sync_check_and_fix.rb
207
208
  - spec/spec_helper.rb
208
209
  - spec/unit/toggl_api/reports_client_spec.rb
209
210
  - toggl_cache.gemspec