readwise 0.4.0 → 1.0.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
  SHA256:
3
- metadata.gz: 766eb82d68f06591b6e05211877532619182404c3aa29c24720de5cd1788ad0a
4
- data.tar.gz: 70b4affa8b2e46271fb1d690e00328be10e1fcbb5fd6a3d5ae08135947818671
3
+ metadata.gz: c3d14c72c58cb11dfcb79c0b3b232abb8b997dc230493d5e0765f186999accba
4
+ data.tar.gz: 037ac6d22c2d195607023e99eebbd71f2885996dd40e16d49f33bc403549ebd9
5
5
  SHA512:
6
- metadata.gz: f6b8cda492331d61352a83bc01732fee2d30191419b226327334daf4c00338afe6876bfb5b806318506bc42a5706f826225a2c917dcaf17a0172d9c12cc3cd08
7
- data.tar.gz: 7b2e849cc2bff772ebe84c7b93cb0b7bdf9c506c9bce80a79754554590a537717c39882f6b04faa8e43105b5eac20598fd89f1a39002746d8212557a4bdaf42d
6
+ metadata.gz: 6b25268acdd085ba3252f828a0f85051f492de64fd851e96a5a0e38bfcfb6e25070df8bc38d7fa94aa52c1433003d6dc8d57ca501d7261bef010e510b6711c13
7
+ data.tar.gz: 1d1c7e40a53214750405a866832427aadce2ad3dc432a4d9e4ab0610520400b1f5fb14d46d90712cc0219621eb2bef21c4bff40bb62def052e99409bfd0b0b86
data/.github/FUNDING.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  # These are supported funding model platforms
2
2
 
3
- github: andjosh
3
+ github: joshbeckman
data/CHANGELOG.md CHANGED
@@ -2,17 +2,29 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.0.0] - 2025-06-29
6
+ - Add Readwise API v3 support for Reader documents ([#13](https://github.com/joshbeckman/readwise-ruby/pull/13))
7
+ - Add `daily_review` client method and Review type ([#14](https://github.com/joshbeckman/readwise-ruby/pull/14))
8
+ - Add comprehensive document API methods (list, get, create, update, delete)
9
+ - Add support for document tags and metadata handling
10
+ - Add tests for document API functionality
11
+
12
+ ## [0.5.0] - 2023-06-15
13
+ - Add methods to create/update highlights ([#8](https://github.com/joshbeckman/readwise-ruby/pull/12))
14
+ - Add methods to add/update/remove highlight tags
15
+ - Add method to get book
16
+
5
17
  ## [0.4.0] - 2023-03-19
6
18
 
7
- - Add `get_highlight` client method ([#10](https://github.com/andjosh/readwise-ruby/pull/10) from [@ajistrying](https://github.com/ajistrying))
19
+ - Add `get_highlight` client method ([#10](https://github.com/joshbeckman/readwise-ruby/pull/10) from [@ajistrying](https://github.com/ajistrying))
8
20
 
9
21
  ## [0.3.0] - 2023-02-28
10
22
 
11
- - Change `Tag` from hash to struct ([#6](https://github.com/andjosh/readwise-ruby/pull/6) from [@ajistrying](https://github.com/ajistrying))
23
+ - Change `Tag` from hash to struct ([#6](https://github.com/joshbeckman/readwise-ruby/pull/6) from [@ajistrying](https://github.com/ajistrying))
12
24
 
13
25
  ## [0.2.0] - 2023-02-20
14
26
 
15
- - Add support for `Book.note` ([#4](https://github.com/andjosh/readwise-ruby/pull/4))
27
+ - Add support for `Book.note` ([#4](https://github.com/joshbeckman/readwise-ruby/pull/4))
16
28
 
17
29
  ## [0.1.0] - 2023-01-16
18
30
 
data/CODE_OF_CONDUCT.md CHANGED
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
55
55
  ## Enforcement
56
56
 
57
57
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at email@andjosh.com. All
58
+ reported by contacting the project team at josh@joshbeckman.org. All
59
59
  complaints will be reviewed and investigated and will result in a response that
60
60
  is deemed necessary and appropriate to the circumstances. The project team is
61
61
  obligated to maintain confidentiality with regard to the reporter of an incident.
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Readwise
2
2
 
3
- [Readwise](https://readwise.io/) is an application suite to store, revisit, and learn from your book and article highlights. This is a basic library to call the [Readwise API](https://readwise.io/api_deets) to read and write highlights.
3
+ [![Gem Version](https://badge.fury.io/rb/readwise.svg)](https://badge.fury.io/rb/readwise) [![Ruby](https://github.com/joshbeckman/readwise-ruby/actions/workflows/ruby.yml/badge.svg)](https://github.com/joshbeckman/readwise-ruby/actions/workflows/ruby.yml)
4
+
5
+ [Readwise](https://readwise.io/) is an application suite to store, revisit, and learn from your book and article highlights. This is a basic library to call the [Readwise API](https://readwise.io/api_deets) to read and write highlights, and manage Reader documents through the [Reader API](https://readwise.io/reader_api).
4
6
 
5
7
  This library is not at 100% coverage of the API, so if you need a method that is missing, open an issue or contribute changes!
6
8
 
@@ -24,6 +26,10 @@ Or install it yourself as:
24
26
 
25
27
  First, obtain an API access token from https://readwise.io/access_token.
26
28
 
29
+ ### Highlights API (V2)
30
+
31
+ The [V2 API](https://readwise.io/api_deets) provides access to your highlights and books:
32
+
27
33
  ```ruby
28
34
  client = Readwise::Client.new(token: token)
29
35
 
@@ -33,17 +39,118 @@ books = client.export(book_ids: ['123']) # export specific highlights
33
39
 
34
40
  puts books.first.title # books are Readwise::Book structs
35
41
  puts books.first.highlights.map(&:text) # highlights are Readwise::Highlight structs
42
+
43
+ # create a highlight
44
+ create = Readwise::HighlightCreate.new(text: 'foobar', author: 'Joan')
45
+ highlight = client.create_highlight(highlight: create)
46
+
47
+ # update a highlight
48
+ update = Readwise::HighlightUpdate.new(text: 'foobaz', color: 'yellow')
49
+ updated = client.update_highlight(highlight: highlight, update: update)
50
+
51
+ # add a tag to a highlight
52
+ tag = Readwise::Tag.new(name: 'foobar')
53
+ added_tag = client.add_highlight_tag(highlight: highlight, tag: tag)
54
+
55
+ # update a tag on a highlight
56
+ added_tag.name = 'bing'
57
+ updated_tag = client.update_highlight_tag(highlight: highlight, tag: added_tag)
58
+
59
+ # remove a tag from a highlight
60
+ client.remove_highlight_tag(highlight: highlight, tag: added_tag)
61
+
62
+ # get daily review highlights
63
+ daily_review = client.daily_review
64
+ puts daily_review.id
65
+ puts daily_review.url
66
+ puts daily_review.completed?
67
+ puts daily_review.highlights.size
68
+ puts daily_review.highlights.first.text
69
+ ```
70
+
71
+ ### Reader API (V3)
72
+
73
+ The [V3 API](https://readwise.io/reader_api) provides access to Readwise Reader functionality for managing documents (articles, PDFs, etc.):
74
+
75
+ ```ruby
76
+ # Get all documents
77
+ documents = client.get_documents
78
+
79
+ # Get documents with filters
80
+ documents = client.get_documents(
81
+ updated_after: '2023-01-01T00:00:00Z',
82
+ location: 'new', # 'new', 'later', 'archive', or 'feed'
83
+ category: 'article' # 'article', 'email', 'rss', 'highlight', 'note', 'pdf', 'epub', 'tweet', 'video'
84
+ )
85
+
86
+ # Get a specific document
87
+ document = client.get_document(document_id: '123456')
88
+
89
+ puts document.title
90
+ puts document.author
91
+ puts document.url
92
+ puts document.reading_progress
93
+ puts document.location
94
+ puts document.category
95
+
96
+ # Check document properties
97
+ puts document.read? # reading progress >= 85%
98
+ puts document.read?(threshold: 0.5) # custom threshold
99
+ puts document.parent? # is this a top-level document?
100
+ puts document.child? # is this a highlight/note of another document?
101
+
102
+ # Check location
103
+ puts document.in_new?
104
+ puts document.in_later?
105
+ puts document.in_archive?
106
+
107
+ # Check category
108
+ puts document.article?
109
+ puts document.pdf?
110
+ puts document.epub?
111
+ puts document.tweet?
112
+ puts document.video?
113
+ puts document.book?
114
+ puts document.email?
115
+ puts document.rss?
116
+ puts document.highlight?
117
+ puts document.note?
118
+
119
+ # Access timestamps
120
+ puts document.created_at_time
121
+ puts document.updated_at_time
122
+ puts document.published_date_time
123
+
124
+ # Create a new document
125
+ document_create = Readwise::DocumentCreate.new(
126
+ url: 'https://example.com/article',
127
+ title: 'My Article',
128
+ author: 'John Doe',
129
+ html: '<p>Article content</p>',
130
+ summary: 'A brief summary',
131
+ location: 'new', # 'new', 'later', 'archive', or 'feed'
132
+ category: 'article', # 'article', 'email', 'rss', etc.
133
+ tags: ['technology', 'programming'],
134
+ notes: 'My personal notes',
135
+ should_clean_html: true,
136
+ saved_using: 'api'
137
+ )
138
+
139
+ document = client.create_document(document: document_create)
140
+
141
+ # Create multiple documents
142
+ documents = client.create_documents(documents: [document_create1, document_create2])
36
143
  ```
37
144
 
38
145
  ## Development
39
146
 
40
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
147
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
41
148
 
42
149
  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).
43
150
 
44
151
  ## Contributing
45
152
 
46
- Bug reports and pull requests are welcome on GitHub at https://github.com/andjosh/readwise. 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.
153
+ Bug reports and pull requests are welcome on GitHub at https://github.com/joshbeckman/readwise-ruby. 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.
47
154
 
48
155
  ## License
49
156
 
@@ -51,4 +158,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
51
158
 
52
159
  ## Code of Conduct
53
160
 
54
- Everyone interacting in the Readwise project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/andjosh/readwise/blob/master/CODE_OF_CONDUCT.md).
161
+ Everyone interacting in the Readwise project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/joshbeckman/readwise-ruby/blob/main/CODE_OF_CONDUCT.md).
data/lib/readwise/book.rb CHANGED
@@ -10,6 +10,7 @@ module Readwise
10
10
  :cover_image_url,
11
11
  :highlights,
12
12
  :note,
13
+ :num_highlights,
13
14
  :readable_title,
14
15
  :readwise_url,
15
16
  :source,
@@ -3,34 +3,137 @@ require 'net/http'
3
3
  require_relative 'book'
4
4
  require_relative 'highlight'
5
5
  require_relative 'tag'
6
+ require_relative 'document'
7
+ require_relative 'review'
6
8
 
7
9
  module Readwise
8
10
  class Client
9
11
  class Error < StandardError; end
10
12
 
11
13
  BASE_URL = "https://readwise.io/api/v2/"
14
+ V3_BASE_URL = "https://readwise.io/api/v3/"
12
15
 
13
16
  def initialize(token: nil)
14
17
  raise ArgumentError unless token
18
+
15
19
  @token = token.to_s
16
20
  end
17
21
 
22
+ def create_document(document:)
23
+ raise ArgumentError unless document.is_a?(Readwise::DocumentCreate)
24
+
25
+ url = V3_BASE_URL + 'save/'
26
+
27
+ res = post_readwise_request(url, payload: document.serialize)
28
+ get_document(document_id: res['id'])
29
+ end
30
+
31
+ def create_documents(documents: [])
32
+ return [] unless documents.any?
33
+
34
+ documents.map do |document|
35
+ create_document(document: document)
36
+ end
37
+ end
38
+
39
+ def get_document(document_id:)
40
+ url = V3_BASE_URL + "list?id=#{document_id}"
41
+
42
+ res = get_readwise_request(url)
43
+
44
+ res['results'].map { |item| transform_document(item) }.first
45
+ end
46
+
47
+ def get_documents(updated_after: nil, location: nil, category: nil)
48
+ resp = documents_page(updated_after: updated_after, location: location, category: category)
49
+ next_page_cursor = resp[:next_page_cursor]
50
+ results = resp[:results]
51
+ while next_page_cursor
52
+ resp = documents_page(updated_after: updated_after, location: location, category: category, page_cursor: next_page_cursor)
53
+ results.concat(resp[:results])
54
+ next_page_cursor = resp[:next_page_cursor]
55
+ end
56
+ results.sort_by(&:created_at)
57
+ end
58
+
18
59
  def create_highlight(highlight:)
19
- create_highlights([highlight])
60
+ create_highlights(highlights: [highlight]).first
20
61
  end
21
62
 
22
63
  def create_highlights(highlights: [])
23
- raise NotImplementedError
64
+ raise ArgumentError unless highlights.all? { |item| item.is_a?(Readwise::HighlightCreate) }
65
+ return [] unless highlights.any?
66
+
67
+ url = BASE_URL + 'highlights/'
68
+
69
+ payload = { highlights: highlights.map(&:serialize) }
70
+ res = post_readwise_request(url, payload: payload)
71
+
72
+ modified_ids = res.map { |book| book['modified_highlights'] }.flatten
73
+ modified_ids.map { |id| get_highlight(highlight_id: id) }
24
74
  end
25
75
 
26
76
  def get_highlight(highlight_id:)
27
- url = BASE_URL + 'highlights/' + highlight_id
77
+ url = BASE_URL + "highlights/#{highlight_id}"
28
78
 
29
79
  res = get_readwise_request(url)
30
80
 
31
81
  transform_highlight(res)
32
82
  end
33
83
 
84
+ def update_highlight(highlight:, update:)
85
+ raise ArgumentError unless update.is_a?(Readwise::HighlightUpdate)
86
+
87
+ url = BASE_URL + "highlights/#{highlight.highlight_id}"
88
+
89
+ res = patch_readwise_request(url, payload: update.serialize)
90
+
91
+ transform_highlight(res)
92
+ end
93
+
94
+ def remove_highlight_tag(highlight:, tag:)
95
+ url = BASE_URL + "highlights/#{highlight.highlight_id}/tags/#{tag.tag_id}"
96
+
97
+ delete_readwise_request(url)
98
+ end
99
+
100
+ def add_highlight_tag(highlight:, tag:)
101
+ raise ArgumentError unless tag.is_a?(Readwise::Tag)
102
+
103
+ url = BASE_URL + "highlights/#{highlight.highlight_id}/tags"
104
+
105
+ payload = tag.serialize.select { |k, v| k == :name }
106
+ res = post_readwise_request(url, payload: payload)
107
+
108
+ transform_tag(res)
109
+ end
110
+
111
+ def update_highlight_tag(highlight:, tag:)
112
+ raise ArgumentError unless tag.is_a?(Readwise::Tag)
113
+
114
+ url = BASE_URL + "highlights/#{highlight.highlight_id}/tags/#{tag.tag_id}"
115
+
116
+ payload = tag.serialize.select { |k, v| k == :name }
117
+ res = patch_readwise_request(url, payload: payload)
118
+
119
+ transform_tag(res)
120
+ end
121
+
122
+ def get_book(book_id:)
123
+ url = BASE_URL + "books/#{book_id}"
124
+
125
+ res = get_readwise_request(url)
126
+
127
+ transform_book(res)
128
+ end
129
+
130
+ def daily_review
131
+ url = BASE_URL + 'review/'
132
+
133
+ res = get_readwise_request(url)
134
+ transform_review(res)
135
+ end
136
+
34
137
  def export(updated_after: nil, book_ids: [])
35
138
  resp = export_page(updated_after: updated_after, book_ids: book_ids)
36
139
  next_page_cursor = resp[:next_page_cursor]
@@ -45,25 +148,32 @@ module Readwise
45
148
 
46
149
  private
47
150
 
151
+ def documents_page(page_cursor: nil, updated_after:, location:, category:)
152
+ parsed_body = get_documents_page(page_cursor: page_cursor, updated_after: updated_after, location: location, category: category)
153
+ results = parsed_body.dig('results').map do |item|
154
+ transform_document(item)
155
+ end
156
+ {
157
+ results: results,
158
+ next_page_cursor: parsed_body.dig('nextPageCursor')
159
+ }
160
+ end
161
+
162
+ def get_documents_page(page_cursor: nil, updated_after:, location:, category:)
163
+ params = {}
164
+ params['updatedAfter'] = updated_after if updated_after
165
+ params['location'] = location if location
166
+ params['category'] = category if category
167
+ params['pageCursor'] = page_cursor if page_cursor
168
+ url = V3_BASE_URL + 'list/?' + URI.encode_www_form(params)
169
+
170
+ get_readwise_request(url)
171
+ end
172
+
48
173
  def export_page(page_cursor: nil, updated_after: nil, book_ids: [])
49
174
  parsed_body = get_export_page(page_cursor: page_cursor, updated_after: updated_after, book_ids: book_ids)
50
175
  results = parsed_body.dig('results').map do |item|
51
- Book.new(
52
- asin: item['asin'],
53
- author: item['author'],
54
- book_id: item['user_book_id'].to_s,
55
- category: item['category'],
56
- cover_image_url: item['cover_image_url'],
57
- note: item['document_note'],
58
- readable_title: item['readable_title'],
59
- readwise_url: item['readwise_url'],
60
- source: item['source'],
61
- source_url: item['source_url'],
62
- tags: item['book_tags'].map { |tag| transform_tag(tag) },
63
- title: item['title'],
64
- unique_url: item['unique_url'],
65
- highlights: item['highlights'].map { |highlight| transform_highlight(highlight) },
66
- )
176
+ transform_book(item)
67
177
  end
68
178
  {
69
179
  results: results,
@@ -79,7 +189,27 @@ module Readwise
79
189
  url = BASE_URL + 'export/?' + URI.encode_www_form(params)
80
190
 
81
191
  get_readwise_request(url)
192
+ end
82
193
 
194
+ def transform_book(res)
195
+ highlights = (res['highlights'] || []).map { |highlight| transform_highlight(highlight) }
196
+ Book.new(
197
+ asin: res['asin'],
198
+ author: res['author'],
199
+ book_id: res['user_book_id'].to_s,
200
+ category: res['category'],
201
+ cover_image_url: res['cover_image_url'],
202
+ note: res['document_note'],
203
+ readable_title: res['readable_title'],
204
+ readwise_url: res['readwise_url'] || res['highlights_url'],
205
+ source: res['source'],
206
+ source_url: res['source_url'],
207
+ tags: (res['book_tags'] || res['tags'] || []).map { |tag| transform_tag(tag) },
208
+ title: res['title'],
209
+ unique_url: res['unique_url'],
210
+ highlights: highlights,
211
+ num_highlights: res['num_highlights'] || highlights.size,
212
+ )
83
213
  end
84
214
 
85
215
  def transform_highlight(res)
@@ -104,13 +234,66 @@ module Readwise
104
234
  )
105
235
  end
106
236
 
237
+ def transform_document(res)
238
+ Document.new(
239
+ author: res['author'],
240
+ category: res['category'],
241
+ created_at: res['created_at'],
242
+ html: res['html'],
243
+ id: res['id'].to_s,
244
+ image_url: res['image_url'],
245
+ location: res['location'],
246
+ notes: res['notes'],
247
+ parent_id: res['parent_id'],
248
+ published_date: res['published_date'],
249
+ reading_progress: res['reading_progress'],
250
+ site_name: res['site_name'],
251
+ source: res['source'],
252
+ source_url: res['source_url'],
253
+ summary: res['summary'],
254
+ tags: transform_tags(res['tags']),
255
+ title: res['title'],
256
+ updated_at: res['updated_at'],
257
+ url: res['url'],
258
+ word_count: res['word_count'],
259
+ )
260
+ end
261
+
262
+ def transform_tags(res)
263
+ if res.is_a?(Array)
264
+ res.map { |tag| transform_tag(tag) }
265
+ elsif res.is_a?(Hash)
266
+ res.map do |tag_id, tag|
267
+ tag['id'] = tag_id
268
+ transform_tag(tag)
269
+ end
270
+ else
271
+ []
272
+ end
273
+ end
274
+
107
275
  def transform_tag(res)
276
+ if res.is_a?(String)
277
+ return Tag.new(name: res)
278
+ end
279
+
108
280
  Tag.new(
109
- tag_id: res['id'].to_s,
281
+ tag_id: res['id']&.to_s,
110
282
  name: res['name'],
111
283
  )
112
284
  end
113
285
 
286
+ def transform_review(res)
287
+ highlights = (res['highlights'] || []).map { |highlight| transform_highlight(highlight) }
288
+
289
+ Review.new(
290
+ id: res['review_id'],
291
+ url: res['review_url'],
292
+ completed: res['review_completed'],
293
+ highlights: highlights
294
+ )
295
+ end
296
+
114
297
  def get_readwise_request(url)
115
298
  uri = URI.parse(url)
116
299
  req = Net::HTTP::Get.new(uri)
@@ -118,10 +301,51 @@ module Readwise
118
301
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
119
302
  http.request(req)
120
303
  end
121
-
122
- raise Error, 'Export request failed' unless res.is_a?(Net::HTTPSuccess)
123
-
304
+
305
+ raise Error, "Get request failed with status code: #{res.code}" unless res.is_a?(Net::HTTPSuccess)
306
+
307
+ JSON.parse(res.body)
308
+ end
309
+
310
+ def patch_readwise_request(url, payload:)
311
+ uri = URI.parse(url)
312
+ req = Net::HTTP::Patch.new(uri)
313
+ req['Authorization'] = "Token #{@token}"
314
+ req['Content-Type'] = 'application/json'
315
+ req.body = payload.to_json
316
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
317
+ http.request(req)
318
+ end
319
+
320
+ raise Error, 'Patch request failed' unless res.is_a?(Net::HTTPSuccess)
321
+
124
322
  JSON.parse(res.body)
125
323
  end
324
+
325
+ def post_readwise_request(url, payload:)
326
+ uri = URI.parse(url)
327
+ req = Net::HTTP::Post.new(uri)
328
+ req['Authorization'] = "Token #{@token}"
329
+ req['Content-Type'] = 'application/json'
330
+ req.body = payload.to_json
331
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
332
+ http.request(req)
333
+ end
334
+
335
+ raise Error, 'Post request failed' unless res.is_a?(Net::HTTPSuccess)
336
+
337
+ JSON.parse(res.body)
338
+ end
339
+
340
+ def delete_readwise_request(url)
341
+ uri = URI.parse(url)
342
+ req = Net::HTTP::Delete.new(uri)
343
+ req['Authorization'] = "Token #{@token}"
344
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
345
+ http.request(req)
346
+ end
347
+
348
+ raise Error, 'Delete request failed' unless res.is_a?(Net::HTTPSuccess)
349
+ end
126
350
  end
127
351
  end
@@ -0,0 +1,140 @@
1
+ require 'time'
2
+
3
+ module Readwise
4
+ Document = Struct.new(
5
+ 'ReadwiseDocument',
6
+ :author,
7
+ :category, # One of: article, email, rss, highlight, note, pdf, epub, tweet or video.
8
+ # Default is guessed based on the URL, usually article.
9
+ :created_at,
10
+ :html,
11
+ :id,
12
+ :image_url,
13
+ :location, # One of: new, later, archive or feed. Default is new.
14
+ :notes,
15
+ :published_date,
16
+ :reading_progress,
17
+ :site_name,
18
+ :source,
19
+ :source_url,
20
+ :summary,
21
+ :tags,
22
+ :title,
23
+ :updated_at,
24
+ :url,
25
+ :word_count,
26
+ :parent_id, # both highlights and notes made in Reader are also considered Documents.
27
+ # Highlights and notes will have `parent_id` set, which is the Document id
28
+ # of the article/book/etc and highlight that they belong to, respectively.
29
+ keyword_init: true
30
+ ) do
31
+ def created_at_time
32
+ return unless created_at
33
+
34
+ Time.parse(created_at)
35
+ end
36
+
37
+ def updated_at_time
38
+ return unless updated_at
39
+
40
+ Time.parse(updated_at)
41
+ end
42
+
43
+ def published_date_time
44
+ return unless published_date
45
+
46
+ Time.at(published_date/1000)
47
+ end
48
+
49
+ def read?(threshold: 0.85)
50
+ reading_progress >= threshold
51
+ end
52
+
53
+ def parent?
54
+ parent_id.nil?
55
+ end
56
+
57
+ def child?
58
+ !parent?
59
+ end
60
+
61
+ def in_new?
62
+ location == 'new'
63
+ end
64
+
65
+ def in_later?
66
+ location == 'later'
67
+ end
68
+
69
+ def in_archive?
70
+ location == 'archive'
71
+ end
72
+
73
+ def pdf?
74
+ category == 'pdf'
75
+ end
76
+
77
+ def epub?
78
+ category == 'epub'
79
+ end
80
+
81
+ def tweet?
82
+ category == 'tweet'
83
+ end
84
+
85
+ def video?
86
+ category == 'video'
87
+ end
88
+
89
+ def article?
90
+ category == 'article'
91
+ end
92
+
93
+ def book?
94
+ category == 'book'
95
+ end
96
+
97
+ def email?
98
+ category == 'email'
99
+ end
100
+
101
+ def rss?
102
+ category == 'rss'
103
+ end
104
+
105
+ def highlight?
106
+ category == 'highlight'
107
+ end
108
+
109
+ def note?
110
+ category == 'note'
111
+ end
112
+
113
+ def serialize
114
+ to_h
115
+ end
116
+ end
117
+
118
+ DocumentCreate = Struct.new(
119
+ 'ReadwiseDocumentCreate',
120
+ :author,
121
+ :category, # One of: article, email, rss, highlight, note, pdf, epub, tweet or video.
122
+ # Default is guessed based on the URL, usually article.
123
+ :html,
124
+ :image_url,
125
+ :location, # One of: new, later, archive or feed. Default is new.
126
+ :notes,
127
+ :published_date,
128
+ :saved_using,
129
+ :should_clean_html,
130
+ :summary,
131
+ :tags,
132
+ :title,
133
+ :url,
134
+ keyword_init: true
135
+ ) do
136
+ def serialize
137
+ to_h.compact
138
+ end
139
+ end
140
+ end
@@ -39,5 +39,45 @@ module Readwise
39
39
 
40
40
  Time.parse(highlighted_at)
41
41
  end
42
+
43
+ def serialize
44
+ to_h
45
+ end
46
+ end
47
+
48
+ HighlightCreate = Struct.new(
49
+ 'ReadwiseHighlightCreate',
50
+ :author,
51
+ :category, # One of: books, articles, tweets or podcasts.
52
+ # (default: articles when source_url is provided, otherwise: books)
53
+ :highlight_url,
54
+ :highlighted_at,
55
+ :image_url,
56
+ :location,
57
+ :location_type, # One of: page, order or time_offset (default: order)
58
+ :note,
59
+ :source_type,
60
+ :source_url,
61
+ :text,
62
+ :title,
63
+ keyword_init: true
64
+ ) do
65
+ def serialize
66
+ to_h.compact
67
+ end
68
+ end
69
+
70
+ HighlightUpdate = Struct.new(
71
+ 'ReadwiseHighlightUpdate',
72
+ :color,
73
+ :location,
74
+ :note,
75
+ :text,
76
+ :url,
77
+ keyword_init: true
78
+ ) do
79
+ def serialize
80
+ to_h.compact
81
+ end
42
82
  end
43
83
  end
@@ -0,0 +1,17 @@
1
+ module Readwise
2
+ Review = Struct.new(
3
+ :id,
4
+ :url,
5
+ :completed,
6
+ :highlights,
7
+ keyword_init: true
8
+ ) do
9
+ def completed?
10
+ completed
11
+ end
12
+
13
+ def serialize
14
+ to_h.compact
15
+ end
16
+ end
17
+ end
data/lib/readwise/tag.rb CHANGED
@@ -4,5 +4,9 @@ module Readwise
4
4
  :tag_id,
5
5
  :name,
6
6
  keyword_init: true
7
- )
7
+ ) do
8
+ def serialize
9
+ to_h
10
+ end
11
+ end
8
12
  end
@@ -1,3 +1,3 @@
1
1
  module Readwise
2
- VERSION = "0.4.0"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/readwise.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'readwise/version'
2
- require 'readwise/client'
1
+ require_relative 'readwise/version'
2
+ require_relative 'readwise/client'
3
3
 
4
4
  module Readwise
5
5
  class Error < StandardError; end
data/readwise.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = "Readwise API client"
13
13
  spec.description = "Minimal Readwise API client and highlight parsing library"
14
- spec.homepage = "https://github.com/andjosh/readwise-ruby"
14
+ spec.homepage = "https://github.com/joshbeckman/readwise-ruby"
15
15
  spec.license = "MIT"
16
16
 
17
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.metadata["homepage_uri"] = spec.homepage
23
23
  spec.metadata["source_code_uri"] = spec.homepage
24
24
  spec.metadata["changelog_uri"] = spec.homepage + "/blob/main/CHANGELOG.md"
25
+ spec.metadata["funding_uri"] = "https://github.com/sponsors/joshbeckman"
25
26
  else
26
27
  raise "RubyGems 2.0 or newer is required to protect against " \
27
28
  "public gem pushes."
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: readwise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Beckman
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-03-19 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -87,19 +86,21 @@ files:
87
86
  - lib/readwise.rb
88
87
  - lib/readwise/book.rb
89
88
  - lib/readwise/client.rb
89
+ - lib/readwise/document.rb
90
90
  - lib/readwise/highlight.rb
91
+ - lib/readwise/review.rb
91
92
  - lib/readwise/tag.rb
92
93
  - lib/readwise/version.rb
93
94
  - readwise.gemspec
94
- homepage: https://github.com/andjosh/readwise-ruby
95
+ homepage: https://github.com/joshbeckman/readwise-ruby
95
96
  licenses:
96
97
  - MIT
97
98
  metadata:
98
99
  allowed_push_host: https://rubygems.org
99
- homepage_uri: https://github.com/andjosh/readwise-ruby
100
- source_code_uri: https://github.com/andjosh/readwise-ruby
101
- changelog_uri: https://github.com/andjosh/readwise-ruby/blob/main/CHANGELOG.md
102
- post_install_message:
100
+ homepage_uri: https://github.com/joshbeckman/readwise-ruby
101
+ source_code_uri: https://github.com/joshbeckman/readwise-ruby
102
+ changelog_uri: https://github.com/joshbeckman/readwise-ruby/blob/main/CHANGELOG.md
103
+ funding_uri: https://github.com/sponsors/joshbeckman
103
104
  rdoc_options: []
104
105
  require_paths:
105
106
  - lib
@@ -114,8 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
115
  - !ruby/object:Gem::Version
115
116
  version: '0'
116
117
  requirements: []
117
- rubygems_version: 3.4.7
118
- signing_key:
118
+ rubygems_version: 3.6.9
119
119
  specification_version: 4
120
120
  summary: Readwise API client
121
121
  test_files: []