toggl_cache 0.1.1 → 0.2.0

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: 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