harvesting 0.1.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.env.sample +10 -0
  3. data/.gitignore +13 -0
  4. data/.travis.yml +7 -2
  5. data/Dockerfile +6 -0
  6. data/README.md +173 -32
  7. data/RELEASE_NOTES.md +80 -0
  8. data/TODO.md +1 -1
  9. data/bin/console +1 -0
  10. data/bin/setup +5 -0
  11. data/docker-compose.yml +10 -0
  12. data/harvesting.gemspec +9 -6
  13. data/lib/harvesting.rb +20 -5
  14. data/lib/harvesting/client.rb +76 -15
  15. data/lib/harvesting/enumerable.rb +5 -2
  16. data/lib/harvesting/errors.rb +9 -0
  17. data/lib/harvesting/models/base.rb +93 -12
  18. data/lib/harvesting/models/client.rb +7 -1
  19. data/lib/harvesting/models/clients.rb +18 -0
  20. data/lib/harvesting/models/contact.rb +14 -1
  21. data/lib/harvesting/models/harvest_record.rb +18 -0
  22. data/lib/harvesting/models/harvest_record_collection.rb +43 -0
  23. data/lib/harvesting/models/invoice.rb +44 -0
  24. data/lib/harvesting/models/invoices.rb +17 -0
  25. data/lib/harvesting/models/line_item.rb +19 -0
  26. data/lib/harvesting/models/project.rb +28 -3
  27. data/lib/harvesting/models/project_task_assignment.rb +33 -0
  28. data/lib/harvesting/models/project_task_assignments.rb +18 -0
  29. data/lib/harvesting/models/project_user_assignment.rb +25 -0
  30. data/lib/harvesting/models/project_user_assignments.rb +19 -0
  31. data/lib/harvesting/models/projects.rb +6 -33
  32. data/lib/harvesting/models/task.rb +6 -3
  33. data/lib/harvesting/models/tasks.rb +6 -33
  34. data/lib/harvesting/models/time_entries.rb +6 -33
  35. data/lib/harvesting/models/time_entry.rb +22 -8
  36. data/lib/harvesting/models/user.rb +20 -6
  37. data/lib/harvesting/models/users.rb +18 -0
  38. data/lib/harvesting/version.rb +1 -1
  39. metadata +90 -21
  40. data/Gemfile.lock +0 -103
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba547e0b7559bb7e2fd813f033eea49bc081ba343eeb76f16a7fe6f5844d094b
4
- data.tar.gz: 619c571ee6951d6ba1680c8ef884b8f65029ca398039f080dbaad9c58a10033c
3
+ metadata.gz: 36525f3c1b0a053c53690e55831d69d402bdc9fb5709b68e0d8cfc885f9a5bf0
4
+ data.tar.gz: bb3c642047e046ebbc8a72448ac5c29358264f8609b34f2c7a746680e721a74c
5
5
  SHA512:
6
- metadata.gz: e67ebf3abc0d39326847c700cabc18ee9211457621fd2ff3aa1e9bad91bd5bee09fa1e5c06147f65f277d98bc1edd14bbdb9cd92db71f2ce2d302d6a91eab2b9
7
- data.tar.gz: 64044c30588913da4bd2abdd71b30cc890644a8db84bdc048c4a29d36d88d32a7b99e452b474c3727b653c44a4dab11588b1b937f7115877a85da38e28952045
6
+ metadata.gz: be1d66442a2b3bf3c0896055e15f8cdbdf506c8929814b1d5781c07676a36eab523e3182b155d593371aa91639fd55d86fb8689b0e1194f6f54ff091dd93c886
7
+ data.tar.gz: f0660def1ec97d8e02cb04dda6a52a14cd1fd08811a0372854632d076162ccc7990bcfa8e34906df3a439a7dcfc8d92bed60b25a78d531f1b045e9176062de1c
@@ -0,0 +1,10 @@
1
+ HARVEST_FIRST_NAME=<replace me with a test account>
2
+ HARVEST_LAST_NAME=<replace me with a test account>
3
+ HARVEST_ACCOUNT_ID=<replace me with a test account>
4
+ HARVEST_NON_ADMIN_ACCOUNT_ID=<replace me with a test account>
5
+ HARVEST_ACCESS_TOKEN=<replace me with a test account>
6
+ HARVEST_NON_ADMIN_ACCESS_TOKEN=<replace me with a test account>
7
+ HARVEST_ADMIN_FULL_NAME=<replace me with a test account>
8
+ HARVEST_NON_ADMIN_FULL_NAME=<replace me with a test account>
9
+ HARVEST_ADMIN_FIRST_NAME=<replace me with a test account>
10
+ HARVEST_ADMIN_LAST_NAME=<replace me with a test account>
data/.gitignore CHANGED
@@ -7,6 +7,14 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ # bundler
11
+ Gemfile.lock
12
+
13
+ # environment variables
14
+ .env
15
+ .env.*
16
+ !.env.sample
17
+
10
18
  # rspec failure tracking
11
19
  .rspec_status
12
20
 
@@ -18,3 +26,8 @@
18
26
 
19
27
  # ruby versions
20
28
  .ruby-version
29
+
30
+ .idea/
31
+ harvesting.iml
32
+
33
+ .DS_Store
@@ -1,5 +1,10 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4.1
5
- before_install: gem install bundler -v 1.16.1
4
+ - 2.5.8
5
+ - 2.6.6
6
+ - 2.7.1
7
+ env:
8
+ - HARVEST_FIRST_NAME=Aaron HARVEST_LAST_NAME=Burr HARVEST_ACCOUNT_ID=112341234 HARVEST_NON_ADMIN_ACCOUNT_ID=112341234 HARVEST_ACCESS_TOKEN=112341234 HARVEST_NON_ADMIN_ACCESS_TOKEN=112341234 HARVEST_ADMIN_FULL_NAME=112341234
9
+
10
+ before_install: gem install bundler -v 2.1.4
@@ -0,0 +1,6 @@
1
+ FROM ruby:2.4.1
2
+
3
+ RUN mkdir /gem
4
+ WORKDIR /gem
5
+
6
+ RUN gem install bundler -v 1.16.1
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Harvesting
2
2
 
3
+ [![Build Status](https://travis-ci.org/fastruby/harvesting.svg?branch=main)](https://travis-ci.org/fastruby/harvesting)
4
+ [![Code Climate](https://codeclimate.com/github/fastruby/harvesting/badges/gpa.svg)](https://codeclimate.com/github/fastruby/harvesting)
5
+ [![codecov](https://codecov.io/gh/fastruby/harvesting/branch/main/graph/badge.svg)](https://codecov.io/gh/fastruby/harvesting)
6
+
3
7
  A Ruby gem to interact with the Harvest API v2.0 and forward.
4
8
 
5
9
  ## Installation
@@ -18,6 +22,10 @@ Or install it yourself as:
18
22
 
19
23
  $ gem install harvesting
20
24
 
25
+ ## Documentation
26
+
27
+ The API is documented [here](https://www.rubydoc.info/github/fastruby/harvesting)
28
+
21
29
  ## Usage
22
30
 
23
31
  In order to start using this gem you will need your personal token and an
@@ -35,72 +43,205 @@ to these environment variables:
35
43
 
36
44
  That means that you could build a client like this:
37
45
 
38
- # $ export HARVEST_ACCESS_TOKEN=abc
39
- # $ export HARVEST_ACCOUNT_ID=12345678
40
- client = Harvesting::Client.new
41
- client.me
42
- > => #<Harvesting::Models::User:0x007ff8830658f0 @attributes={"id"=>2108614, "first_name"=>"Ernesto", "last_name"=>"Tagwerker", ... >
46
+ ```ruby
47
+ # $ export HARVEST_ACCESS_TOKEN=abc
48
+ # $ export HARVEST_ACCOUNT_ID=12345678
49
+ client = Harvesting::Client.new
50
+ client.me
51
+ # => #<Harvesting::Models::User:0x007ff8830658f0 @attributes={"id"=>2108614, "first_name"=>"Ernesto", "last_name"=>"Tagwerker", ... >
52
+ ```
43
53
 
44
54
  If you don't specify a valid combination of token and account id, your code will
45
55
  raise this error:
46
56
 
47
- client = Harvesting::Client.new(access_token: "foo", account_id: "bar")
48
- client.me
49
- > Harvesting::AuthenticationError: {"error":"invalid_token","error_description":"The access token provided is expired, revoked, malformed or invalid for other reasons."}
57
+ ```ruby
58
+ client = Harvesting::Client.new(access_token: "foo", account_id: "bar")
59
+ client.me
60
+ # Harvesting::AuthenticationError: {"error":"invalid_token","error_description":"The access token provided is expired, revoked, malformed or invalid for other reasons."}
61
+ ```
50
62
 
51
63
  If your personal token and account id are valid, you should see something like
52
64
  this:
53
65
 
54
- client = Harvesting::Client.new(access_token: "<your token here>", account_id: "<your account id here>")
55
- user = client.me
56
- > => #<Harvesting::Models::User:0x007ff8830658f0 @attributes={"id"=>2108614, "first_name"=>"Ernesto", "last_name"=>"Tagwerker", ... >
66
+ ```ruby
67
+ client = Harvesting::Client.new(access_token: "<your token here>", account_id: "<your account id here>")
68
+ user = client.me
69
+ # => #<Harvesting::Models::User:0x007ff8830658f0 @attributes={"id"=>2108614, "first_name"=>"Ernesto", "last_name"=>"Tagwerker", ... >
57
70
 
58
- user.id
59
- > => 2108614
71
+ user.id
72
+ # => 2108614
73
+ ```
60
74
 
61
75
  ### Clients
62
76
 
63
- client.clients
64
- > => [#<Harvesting::Models::Client:0x007ff718d65fd0 @attributes={"id"=>6760580, "name"=>"Toto", "is_active"=>true, "address"=>"" ... >
77
+ ```ruby
78
+ client.clients
79
+ # => #<Harvesting::Models::Clients:0x007ff718d65fd0>
65
80
 
66
- client = client.clients.first
67
- > => #<Harvesting::Models::Client:0x007ff718cf5fc8 @attributes={"id"=>6760580, "name"=>"Toto",
68
- ... >
81
+ client = client.clients.first
82
+ # => #<Harvesting::Models::Client:0x007ff718cf5fc8 @attributes={"id"=>6760580, "name"=>"Toto",
83
+ # ... >
84
+ ```
69
85
 
70
86
  ### Time Entries
71
87
 
72
- time_entries = client.time_entries
73
- > => #<Harvesting::Models::TimeEntries:0x007ff71913e3a0 @attributes={"per_page"=>100, "total_pages"=>1, "total_entries"=>14, "next_page"=>nil, "previous_page"=>nil, "page"=>1, "links"=>{"first"=>"https://api.harvestapp.com/v2/time_entries?limit=1&page=1&per_page=100", "next"=>nil, "previous"=>nil, "last"=>"https://api.harvestapp.com/v2/time_entries?limit=1&page=1&per_page=100"}}, ... >
88
+ ```ruby
89
+ time_entries = client.time_entries
90
+ # => #<Harvesting::Models::TimeEntries:0x007ff71913e3a0 @attributes={"per_page"=>100, "total_pages"=>1, "total_entries"=>14, "next_page"=>nil, "previous_page"=>nil, "page"=>1, "links"=>{"first"=>"https://api.harvestapp.com/v2/time_entries?limit=1&page=1&per_page=100", "next"=>nil, "previous"=>nil, "last"=>"https://api.harvestapp.com/v2/time_entries?limit=1&page=1&per_page=100"}}, ... >
74
91
 
75
- entry = time_entries.first
76
- > => #<Harvesting::Models::TimeEntry:0x007ff71913dfe0 @attributes={"id"=>792860513, "spent_date"=>"2018-05-14", "hours"=>1.0, "notes"=>"hacked the things", "is_locked"=>false, "locked_reason"=>nil, "is_closed"=>false, "is_billed"=>false, "timer_started_at"=>nil, "started_time"=>nil, "ended_time"=>nil, "is_running"=>false, "billable"=>true, "budgeted"=>false, "billable_rate"=>nil, "cost_rate ... >
92
+ entry = time_entries.first
93
+ # => #<Harvesting::Models::TimeEntry:0x007ff71913dfe0 @attributes={"id"=>792860513, "spent_date"=>"2018-05-14", "hours"=>1.0, "notes"=>"hacked the things", "is_locked"=>false, "locked_reason"=>nil, "is_closed"=>false, "is_billed"=>false, "timer_started_at"=>nil, "started_time"=>nil, "ended_time"=>nil, "is_running"=>false, "billable"=>true, "budgeted"=>false, "billable_rate"=>nil, "cost_rate ... >
94
+ ```
77
95
 
78
96
  ### Tasks
79
97
 
80
- tasks = client.tasks
81
- > => #<Harvesting::Models::Tasks:0x007ff718897990 @attributes={"per_page"=>100, "total_pages"=>1, "total_entries"=>6, "next_page"=>nil, "previous_page"=>nil, "page"=>1, "links"=>{"first"=>"https://api.harvestapp.com/v2/tasks?page=1&per_page=100", "next"=>nil, ... >
98
+ ```ruby
99
+ tasks = client.tasks
100
+ # => #<Harvesting::Models::Tasks:0x007ff718897990 @attributes={"per_page"=>100, "total_pages"=>1, "total_entries"=>6, "next_page"=>nil, "previous_page"=>nil, "page"=>1, "links"=>{"first"=>"https://api.harvestapp.com/v2/tasks?page=1&per_page=100", "next"=>nil, ... >
101
+ ```
82
102
 
83
103
  ### Projects
84
104
 
85
- projects = client.projects
86
- > => #<Harvesting::Models::Projects:0x007ff718e1c8e8 @attributes={"per_page"=>100, "total_pages"=>1, "total_entries"=>1, "next_page"=>nil, "previous_page"=>nil, "page"=>1, "links"=>{"first"=>"https://api.harvestapp.com/v2/projects?page=1&per_page=100", ... >
105
+ ```ruby
106
+ projects = client.projects
107
+ # => #<Harvesting::Models::Projects:0x007ff718e1c8e8 @attributes={"per_page"=>100, "total_pages"=>1, "total_entries"=>1, "next_page"=>nil, "previous_page"=>nil, "page"=>1, "links"=>{"first"=>"https://api.harvestapp.com/v2/projects?page=1&per_page=100", ... >
108
+
109
+ project = projects.first
110
+ # => #<Harvesting::Models::Project:0x007ff718e1c618 @attributes={"id"=>17367712, "name"=>"Foo", "code"=>"", "is_active"=>true, "is_billable"=>true, "is_fixed_fee"=>false, "bill_by"=>"none", "budget"=>nil, "budget_by"=>"none", "budget_is_monthly"=>false, "notify_when_over_budget"=>false, "over_budget_notification_percentage"=>80.0, "show_budget_to_all"=>false, "created_at"=>"2018-05-13T03:30:06Z", ... >
111
+ ```
112
+
113
+ ### Invoices
114
+ ```ruby
115
+ invoices = client.invoices
116
+ # => #<Harvesting::Models::Invoices:0x00007fc8905671f0 @models={}, @attributes={"per_page"=>100, "total_pages"=>1, "total_entries"=>3, "next_page"=>nil, "previous_page"=>nil, "page"=>1, "links"=>{"first"=>"https://api.harvestapp.com/v2/invoices?page=1&per_page=100", ... >
117
+
118
+ invoice = invoices.first
119
+ # => #<Harvesting::Models::Invoice:0x00007f8c37eb6d18 @models={}, @attributes={"id"=>23831208, "client_key"=>"73688e97a43ed497ace45939eb76db6b18427b80", "number"=>"3", "purchase_order"=>"", "amount"=>750.0, "due_amount"=>750.0, "tax"=>nil, "tax_amount"=>0.0, "tax2"=>nil, "tax2_amount"=>0.0, "discount"=>nil, "discount_amount"=>0.0, "subject"=>"", "notes"=>"", "state"=>"draft", "period_start"=>nil, ... >
120
+ ```
121
+
122
+ An invoice can have many line items:
123
+ ```ruby
124
+ line_items = invoice.line_items
125
+ # => [#<Harvesting::Models::LineItem:0x00007f92617ce8e0 @models={}, @attributes={"id"=>109677268, "kind"=>"Service", "description"=>"", "quantity"=>3.0, "unit_price"=>250.0, "amount"=>750.0, "taxed"=>false, "taxed2"=>false, "project"=>{"id"=>24566828, "name"=>"Harvest Billing Automation", "code"=>""}}, ... >]
126
+ ```
127
+
128
+ You can filter invoices by various attributes. E.g. `client.invoices(state: "draft")` only returns invoices in a draft state.
129
+
130
+ ### Nested Attributes
131
+
132
+ The Harvest v2 API embeds some data in JSON objects. You can access nested attributes quite naturally.
133
+ For example, to access the user id for a time entry instance, `entry`, use:
134
+
135
+ ```ruby
136
+ entry.user.id
137
+ ```
138
+
139
+ Or to access the name of the client on a project instance, `project`:
140
+ ```ruby
141
+ project.client.name
142
+ ```
143
+
144
+ ## Tips
145
+
146
+ ### Deleting All Items
147
+
148
+ When you need to delete all items, care needs to be taken, because the API uses pagination. The following code will only delete data from _every other_ page.
87
149
 
88
- project = projects.first
89
- > => #<Harvesting::Models::Project:0x007ff718e1c618 @attributes={"id"=>17367712, "name"=>"Foo", "code"=>"", "is_active"=>true, "is_billable"=>true, "is_fixed_fee"=>false, "bill_by"=>"none", "budget"=>nil, "budget_by"=>"none", "budget_is_monthly"=>false, "notify_when_over_budget"=>false, "over_budget_notification_percentage"=>80.0, "show_budget_to_all"=>false, "created_at"=>"2018-05-13T03:30:06Z", ... >
150
+ ```ruby
151
+ # WARNING - only deletes every other page
152
+ client.time_entries.each do |time_entry|
153
+ time_entry.delete
154
+ end
155
+ ```
156
+
157
+ While iterating over items from the first page, all of those items will be deleted. This will result in moving items from the second page onto the first page. If there are only two pages, then the second page will be empty. If there are more than two pages, then the second page will now contain items which would have previously appeared on the third page. Deleting those items will move the items from the fourth page on to the third page, and so on.
158
+
159
+ Instead you need to make sure you get access to all of the time entry objects before you try to delete any of them. The easiest way to do this is to convert the `Enumerable` instance into an `Array`, by calling `#to_a`, before you iterate over it.
160
+
161
+ ```ruby
162
+ # GOOD - This should do what you want
163
+ client.time_entries.to_a.each do |time_entry|
164
+ time_entry.delete
165
+ end
166
+ ```
90
167
 
91
168
  ## Roadmap
92
169
 
93
- There are many things to be developed for this gem. For now they are tracked here: [TODO.md](https://github.com/ombulabs/harvesting/blob/master/TODO.md)
170
+ There are many things to be developed for this gem. For now they are tracked here: [TODO.md](https://github.com/fastruby/harvesting/blob/main/TODO.md)
171
+
172
+ ## Releases
173
+
174
+ You can find more info [here](RELEASE_NOTES.md)
94
175
 
95
176
  ## Development
96
177
 
97
178
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
98
179
 
99
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
180
+ ./bin/setup
181
+ rake
182
+
183
+ To install this gem onto your local machine, run `bundle exec rake install`.
184
+
185
+ ### Using Docker
186
+
187
+ This setup allows you to create a completely isolated development environment for working on this gem. The version of ruby used for development and the gems that it depends on will remain inside the container.
188
+
189
+ After checking out the repo, run `docker-compose build` to create the `gem` container. Running `docker-compose run gem bash` will get you a bash session inside the container. From there, you can follow the instructions in the "Without Docker" section above.
190
+
191
+ ### Running Tests
192
+
193
+ The tests in this project use the [VCR gem](https://github.com/vcr/vcr) to record and playback all interactions with the Harvest API. This allows you to run the test suite without having an account at Harvest for testing.
194
+
195
+ If you add a test that requires making an additional API call, then you'll need to make adjustments to the `.env` file to provide account details that are required by the test suite.
196
+
197
+ *WARNING*: The test suite is destructive. It deletes all entities from the account before it runs. _DO NOT_ run it against a Harvest account which contains information that you need to preserve. Instead, create a Harvest account for testing.
198
+
199
+ If you need to refresh the VCR cassettes, the easiest way is to delete all of the files located under [`fixtures/vcr_cassettes`](fixtures/vcr_cassettes). The next time the test suite is run, VCR will make actual calls against the Harvest API and record the responses into updated cassette files.
200
+
201
+ Effort has been taken to ensure that private information is excluded from the recorded cassettes. To adjust this further, add additional `filter_sensitive_data` calls to [`spec/spec_helper.rb`](spec/spec_helper.rb).
202
+
203
+ ### Models
204
+
205
+ The models in this project reflect the Harvest v2 API endpoints:
206
+
207
+ * client
208
+ * contact
209
+ * task
210
+ * task_assignment
211
+ * project
212
+ * user
213
+ * time_entry
214
+
215
+ There are also models for the Harvest v2 API collection endpoints:
216
+
217
+ * clients
218
+ * contacts
219
+ * tasks
220
+ * task_assignments
221
+ * projects
222
+ * users
223
+ * time_entries
224
+
225
+ These collection models handle the Harvest v2 API pagination automatically, making it easy to enumerate through all the instances of each type.
226
+
227
+ The models try to reduce code duplication through base class helper functions to automatically define accessors for the attributes included in each type returned by the Harvest v2 API.
228
+ The `Harvesting::Base::attributed` method will define accessors for each simple attribute included in an array passed as an argument. Data is returned from these accessors as strings.
229
+
230
+ Some data is returned from Harvest as nested JSON (e.g. time_entry.project.name). A base class helper to expose this using the available models
231
+ is also present. The `Harvesting::Base::modeled` method will define accessors for each object attribute included in options. Both the name of the attribute and the model to use in accessing that data is supplied.
232
+ Data is returned from these accessors as model objects.
233
+
234
+ NOTE: Nesting model objects requires that the nested model types be defined before the nesting model type. E.g. if `project` contains a nested `client`, then `client` must be defined *before* `project` in the `harvesting.rb` include list.
235
+
236
+ ## Releases
237
+
238
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
239
+
240
+ We follow semantic versioning for version numbers: [https://semver.org](https://semver.org)
100
241
 
101
242
  ## Contributing
102
243
 
103
- Bug reports and pull requests are welcome on GitHub at https://github.com/ombulabs/harvesting. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
244
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/fastruby/harvesting](https://github.com/fastruby/harvesting). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
104
245
 
105
246
  ## License
106
247
 
@@ -108,4 +249,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
108
249
 
109
250
  ## Code of Conduct
110
251
 
111
- Everyone interacting in the Harvesting project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ombulabs/harvesting/blob/master/CODE_OF_CONDUCT.md).
252
+ Everyone interacting in the Harvesting project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fastruby/harvesting/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,80 @@
1
+ # RELEASE NOTES
2
+
3
+ ### main
4
+
5
+ **Notes**
6
+
7
+ **Bug Fixes**
8
+
9
+ ### Version 0.5.1 - January 14, 2021
10
+
11
+ **Notes**
12
+ - Add support for Codecov so that we can track code coverage over time in the
13
+ library: https://github.com/fastruby/harvesting/pull/59
14
+
15
+ **Bug Fixes**
16
+ - Fix incompatibility with Ruby 3.0: https://github.com/fastruby/harvesting/pull/64
17
+ - Relax `http` dependency declaration so that we can `bundle install` with more
18
+ modern versions of that gem: https://github.com/fastruby/harvesting/pull/63
19
+ - Fix issue when trying to remove an entity that is not removable:
20
+ https://github.com/fastruby/harvesting/pull/61
21
+
22
+ ### Version 0.5.0 - September 3, 2020
23
+
24
+ **Notes**
25
+ - Changed behavior of `client.clients` so that it returns an instance of `Harvesting::Models::Clients` instead of an `Array`: https://github.com/fastruby/harvesting/pull/39
26
+
27
+ **Bug Fixes**
28
+ - Add support for Harvesting::RateLimitExceeded instead of a JSON::ParserError: https://github.com/fastruby/harvesting/pull/57
29
+ - Add support for RequestNotFound instead of a JSON::ParserError: https://github.com/fastruby/harvesting/pull/54
30
+
31
+ ### Version 0.4.0 - June 6, 2020
32
+
33
+ **Notes**
34
+ - Added Ruby 2.5.1 to version matrix in Travis: https://github.com/fastruby/harvesting/pull/31
35
+ - Associated time entries for project: https://github.com/fastruby/harvesting/pull/32
36
+ - Add require forwardable in havest_record_collection model: https://github.com/fastruby/harvesting/pull/40
37
+ - Add syntax highlighting to readme examples: https://github.com/fastruby/harvesting/pull/41
38
+ - Rename the client key as harvest_client to avoid confusion: https://github.com/fastruby/harvesting/pull/43
39
+ - Update rake requirement from ~> 10.0 to ~> 13.0: https://github.com/fastruby/harvesting/pull/44
40
+ - Bump ffi from 1.9.23 to 1.12.2: https://github.com/fastruby/harvesting/pull/45
41
+ - Ability to supply filter options to the invoice end point and a model for line items on an invoice.: https://github.com/fastruby/harvesting/pull/46
42
+
43
+ **Bug Fixes**
44
+
45
+ - Complete pending test: https://github.com/fastruby/harvesting/pull/28
46
+ - Fixed Code Climate link: https://github.com/fastruby/harvesting/pull/38
47
+
48
+
49
+ ### Version 0.3.0 - Jan 22, 2019
50
+
51
+ **Notes**
52
+
53
+ - Support for users: https://github.com/fastruby/harvesting/pull/9
54
+ - Support for fetching single records: https://github.com/fastruby/harvesting/pull/14 and https://github.com/fastruby/harvesting/pull/22
55
+ - Nested models make it easier to access data: https://github.com/fastruby/harvesting/pull/15
56
+ - Better architecture for collections: https://github.com/fastruby/harvesting/pull/18
57
+
58
+ **Bug Fixes**
59
+
60
+ - Correct pagination support: https://github.com/fastruby/harvesting/pull/17
61
+ - Added documentation: https://github.com/fastruby/harvesting/pull/30
62
+
63
+ ### Version 0.2.0 - Oct 18, 2018
64
+
65
+ **Notes**
66
+
67
+ - More documentation in README.md
68
+ - Adds ability to access user associated with time entries: https://github.com/fastruby/harvesting/pull/3
69
+ - Adds support for Docker for development: https://github.com/fastruby/harvesting/pull/1
70
+
71
+ **Bug Fixes**
72
+
73
+ - Fixes issues with specs: https://github.com/fastruby/harvesting/pull/2
74
+ - Fixed https://github.com/fastruby/harvesting/issues/6 with https://github.com/fastruby/harvesting/pull/7
75
+
76
+ ## Version 0.1.0 - Sep 04, 2018
77
+
78
+ **Notes**
79
+
80
+ - Initial release
data/TODO.md CHANGED
@@ -1,2 +1,2 @@
1
- * Implement methods like `project.time_entries` or `user.time_entries`
1
+ * Implement methods like `user.time_entries`
2
2
  * Implement methods like `project.time_entries.create(attribute: value)`
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
+ require "dotenv/load"
4
5
  require "harvesting"
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
data/bin/setup CHANGED
@@ -6,3 +6,8 @@ set -vx
6
6
  bundle install
7
7
 
8
8
  # Do any other automated setup that you need to do here
9
+
10
+ if [ ! -f .env ]; then
11
+ cp .env.sample .env
12
+ echo "Make sure that .env has valid values."
13
+ fi
@@ -0,0 +1,10 @@
1
+ version: '3'
2
+ services:
3
+ gem:
4
+ build: .
5
+ volumes:
6
+ - .:/gem
7
+ - bundler:/usr/local/bundle
8
+
9
+ volumes:
10
+ bundler:
@@ -6,12 +6,12 @@ require "harvesting/version"
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "harvesting"
8
8
  spec.version = Harvesting::VERSION
9
- spec.authors = ["Ernesto Tagwerker"]
10
- spec.email = ["ernesto+github@ombulabs.com"]
9
+ spec.authors = ["Ernesto Tagwerker", "M. Scott Ford"]
10
+ spec.email = ["ernesto+github@ombulabs.com", "scott@mscottford.com"]
11
11
 
12
12
  spec.summary = %q{Ruby wrapper for the Harvest API v2.0}
13
13
  spec.description = %q{Interact with the Harvest API v2.0 from your Ruby application}
14
- spec.homepage = "https://github.com/ombulabs/harvesting"
14
+ spec.homepage = "https://github.com/fastruby/harvesting"
15
15
  spec.license = "MIT"
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
@@ -20,13 +20,16 @@ Gem::Specification.new do |spec|
20
20
  spec.bindir = "exe"
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
- spec.add_dependency "http", "~> 3.3", ">= 3.3"
23
+ spec.add_dependency "http", ">= 3.3", "< 5.0"
24
24
 
25
- spec.add_development_dependency "bundler", "~> 1.16"
26
- spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "bundler", ">= 2.0", "< 3.0"
26
+ spec.add_development_dependency "rake", "~> 13.0"
27
27
  spec.add_development_dependency "rspec", "~> 3.0"
28
28
  spec.add_development_dependency "guard-rspec", "~> 4.7", ">= 4.7"
29
29
  spec.add_development_dependency "byebug", "~> 10.0", ">= 10.0"
30
30
  spec.add_development_dependency "vcr", "~> 4.0", ">= 4.0"
31
31
  spec.add_development_dependency "webmock", "~> 3.4", ">= 3.4"
32
+ spec.add_development_dependency "dotenv", "~> 2.5", ">= 2.5"
33
+ spec.add_development_dependency "simplecov", "~> 0.19.0"
34
+ spec.add_development_dependency "codecov", "~> 0.2.9"
32
35
  end