readwise 0.3.0 → 0.5.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: 3430c0062c1b93b7f57032195dd68953f5747fe221104c3f9d27edac52bc6fa2
4
- data.tar.gz: 9436565954a21d0b34b8ae7896ded212801daf05512d1fb512207272547792c9
3
+ metadata.gz: 2132de7e8f9fdbc395fc94872968cee73d333c67723b2e81a7554b61105e1469
4
+ data.tar.gz: 4aca80570f7ea3d6d7045cfe0f3fe75c64d578e4ea2de05697d98e747215d74a
5
5
  SHA512:
6
- metadata.gz: e9d2cf349ca75d6b9a21dcf60c2c8ea2af9ddff6e3aace2ecb82c748b9a1845204e1ac7c9d72c23e5a6c4dbcd9bf682c455162d72ac56b62fb5348d32f958b6f
7
- data.tar.gz: 4fdcdc6cca2bb5a7881ee8beceb1c3096f348776839fed87c330d8bb91abd004d1ab75bff9cd9e9ce5d23b0d57c3f1ed55b34738ae9888c3fd42e72c94447dd2
6
+ metadata.gz: 35498d0c04b98eab247b3bf78bdf66dc51294f0538273c9515d8122b737c494421a4425f241d25577d77ec55a2ce01455e02fb91281a72769c753a7d74e9690c
7
+ data.tar.gz: 751bfaf4334fcf74cd6f16b685108a8df095a84366b91c76e1978447d839f28ff7234c136f933bd37eb18f7e1b0e437a40e84e7b17a4c5e3641a6d0995519268
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
@@ -0,0 +1,39 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ "main" ]
13
+ pull_request:
14
+ branches: [ "main" ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2']
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - name: Set up Ruby
30
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
31
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
32
+ uses: ruby/setup-ruby@v1
33
+ with:
34
+ ruby-version: ${{ matrix.ruby-version }}
35
+ # bundler-cache: true # runs 'bundle install' and caches installed gems automatically
36
+ - name: Install deps
37
+ run: bundle install
38
+ - name: Run tests
39
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -46,7 +46,7 @@ build-iPhoneSimulator/
46
46
 
47
47
  # for a library or gem, you might want to ignore these files since the code is
48
48
  # intended to run in multiple environments; otherwise, check them in:
49
- # Gemfile.lock
49
+ Gemfile.lock
50
50
  # .ruby-version
51
51
  # .ruby-gemset
52
52
 
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.5.0] - 2023-06-15
6
+ - Add methods to create/update highlights ([#8](https://github.com/joshbeckman/readwise-ruby/pull/12))
7
+ - Add methods to add/update/remove highlight tags
8
+ - Add method to get book
9
+
10
+ ## [0.4.0] - 2023-03-19
11
+
12
+ - Add `get_highlight` client method ([#10](https://github.com/andjosh/readwise-ruby/pull/10) from [@ajistrying](https://github.com/ajistrying))
13
+
5
14
  ## [0.3.0] - 2023-02-28
6
15
 
7
16
  - Change `Tag` from hash to struct ([#6](https://github.com/andjosh/readwise-ruby/pull/6) from [@ajistrying](https://github.com/ajistrying))
data/README.md CHANGED
@@ -33,11 +33,30 @@ books = client.export(book_ids: ['123']) # export specific highlights
33
33
 
34
34
  puts books.first.title # books are Readwise::Book structs
35
35
  puts books.first.highlights.map(&:text) # highlights are Readwise::Highlight structs
36
+
37
+ # create a highlight
38
+ create = Readwise::HighlightCreate.new(text: 'foobar', author: 'Joan')
39
+ highlight = client.create_highlight(highlight: create)
40
+
41
+ # update a highlight
42
+ update = Readwise::HighlightUpdate.new(text: 'foobaz', color: 'yellow')
43
+ updated = client.update_highlight(highlight: highlight, update: update)
44
+
45
+ # add a tag to a highlight
46
+ tag = Readwise::Tag.new(name: 'foobar')
47
+ added_tag = client.add_highlight_tag(highlight: highlight, tag: tag)
48
+
49
+ # update a tag on a highlight
50
+ added_tag.name = 'bing'
51
+ updated_tag = client.update_highlight_tag(highlight: highlight, tag: added_tag)
52
+
53
+ # remove a tag from a highlight
54
+ client.remove_highlight_tag(highlight: highlight, tag: added_tag)
36
55
  ```
37
56
 
38
57
  ## Development
39
58
 
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.
59
+ 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
60
 
42
61
  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
62
 
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,
@@ -8,17 +8,83 @@ module Readwise
8
8
  class Client
9
9
  class Error < StandardError; end
10
10
 
11
+ BASE_URL = "https://readwise.io/api/v2/"
12
+
11
13
  def initialize(token: nil)
12
14
  raise ArgumentError unless token
15
+
13
16
  @token = token.to_s
14
17
  end
15
18
 
16
19
  def create_highlight(highlight:)
17
- create_highlights([highlight])
20
+ create_highlights(highlights: [highlight]).first
18
21
  end
19
22
 
20
23
  def create_highlights(highlights: [])
21
- raise NotImplementedError
24
+ raise ArgumentError unless highlights.all? { |item| item.is_a?(Readwise::HighlightCreate) }
25
+ return [] unless highlights.any?
26
+
27
+ url = BASE_URL + 'highlights/'
28
+
29
+ payload = { highlights: highlights.map(&:serialize) }
30
+ res = post_readwise_request(url, payload: payload)
31
+
32
+ modified_ids = res.map { |book| book['modified_highlights'] }.flatten
33
+ modified_ids.map { |id| get_highlight(highlight_id: id) }
34
+ end
35
+
36
+ def get_highlight(highlight_id:)
37
+ url = BASE_URL + "highlights/#{highlight_id}"
38
+
39
+ res = get_readwise_request(url)
40
+
41
+ transform_highlight(res)
42
+ end
43
+
44
+ def update_highlight(highlight:, update:)
45
+ raise ArgumentError unless update.is_a?(Readwise::HighlightUpdate)
46
+
47
+ url = BASE_URL + "highlights/#{highlight.highlight_id}"
48
+
49
+ res = patch_readwise_request(url, payload: update.serialize)
50
+
51
+ transform_highlight(res)
52
+ end
53
+
54
+ def remove_highlight_tag(highlight:, tag:)
55
+ url = BASE_URL + "highlights/#{highlight.highlight_id}/tags/#{tag.tag_id}"
56
+
57
+ delete_readwise_request(url)
58
+ end
59
+
60
+ def add_highlight_tag(highlight:, tag:)
61
+ raise ArgumentError unless tag.is_a?(Readwise::Tag)
62
+
63
+ url = BASE_URL + "highlights/#{highlight.highlight_id}/tags"
64
+
65
+ payload = tag.serialize.select { |k, v| k == :name }
66
+ res = post_readwise_request(url, payload: payload)
67
+
68
+ transform_tag(res)
69
+ end
70
+
71
+ def update_highlight_tag(highlight:, tag:)
72
+ raise ArgumentError unless tag.is_a?(Readwise::Tag)
73
+
74
+ url = BASE_URL + "highlights/#{highlight.highlight_id}/tags/#{tag.tag_id}"
75
+
76
+ payload = tag.serialize.select { |k, v| k == :name }
77
+ res = patch_readwise_request(url, payload: payload)
78
+
79
+ transform_tag(res)
80
+ end
81
+
82
+ def get_book(book_id:)
83
+ url = BASE_URL + "books/#{book_id}"
84
+
85
+ res = get_readwise_request(url)
86
+
87
+ transform_book(res)
22
88
  end
23
89
 
24
90
  def export(updated_after: nil, book_ids: [])
@@ -38,52 +104,7 @@ module Readwise
38
104
  def export_page(page_cursor: nil, updated_after: nil, book_ids: [])
39
105
  parsed_body = get_export_page(page_cursor: page_cursor, updated_after: updated_after, book_ids: book_ids)
40
106
  results = parsed_body.dig('results').map do |item|
41
- Book.new(
42
- asin: item['asin'],
43
- author: item['author'],
44
- book_id: item['user_book_id'].to_s,
45
- category: item['category'],
46
- cover_image_url: item['cover_image_url'],
47
- note: item['document_note'],
48
- readable_title: item['readable_title'],
49
- readwise_url: item['readwise_url'],
50
- source: item['source'],
51
- source_url: item['source_url'],
52
- tags: item['book_tags'].map do |tag|
53
- Tag.new(
54
- tag_id: tag['id'].to_s,
55
- name: tag['name']
56
- )
57
- end,
58
- title: item['title'],
59
- unique_url: item['unique_url'],
60
- highlights: item['highlights'].map do |highlight|
61
- Highlight.new(
62
- book_id: highlight['book_id'].to_s,
63
- color: highlight['color'],
64
- created_at: highlight['created_at'],
65
- end_location: highlight['end_location'],
66
- external_id: highlight['external_id'],
67
- highlight_id: highlight['id'].to_s,
68
- highlighted_at: highlight['highlighted_at'],
69
- is_discard: highlight['is_discard'],
70
- is_favorite: highlight['is_favorite'],
71
- location: highlight['location'],
72
- location_type: highlight['location_type'],
73
- note: highlight['note'],
74
- readwise_url: highlight['readwise_url'],
75
- tags: highlight['tags'].map do |tag|
76
- Tag.new(
77
- tag_id: tag['id'].to_s,
78
- name: tag['name']
79
- )
80
- end,
81
- text: highlight['text'],
82
- updated_at: highlight['updated_at'],
83
- url: highlight['url'],
84
- )
85
- end
86
- )
107
+ transform_book(item)
87
108
  end
88
109
  {
89
110
  results: results,
@@ -96,16 +117,114 @@ module Readwise
96
117
  params['updatedAfter'] = updated_after if updated_after
97
118
  params['ids'] = book_ids if book_ids.any?
98
119
  params['pageCursor'] = page_cursor if page_cursor
99
- url = 'https://readwise.io/api/v2/export/?' + URI.encode_www_form(params)
100
- export_uri = URI.parse(url)
101
- export_req = Net::HTTP::Get.new(export_uri)
102
- export_req['Authorization'] = "Token #{@token}"
103
- export_res = Net::HTTP.start(export_uri.hostname, export_uri.port, use_ssl: true) do |http|
104
- http.request(export_req)
120
+ url = BASE_URL + 'export/?' + URI.encode_www_form(params)
121
+
122
+ get_readwise_request(url)
123
+
124
+ end
125
+
126
+ def transform_book(res)
127
+ highlights = (res['highlights'] || []).map { |highlight| transform_highlight(highlight) }
128
+ Book.new(
129
+ asin: res['asin'],
130
+ author: res['author'],
131
+ book_id: res['user_book_id'].to_s,
132
+ category: res['category'],
133
+ cover_image_url: res['cover_image_url'],
134
+ note: res['document_note'],
135
+ readable_title: res['readable_title'],
136
+ readwise_url: res['readwise_url'] || res['highlights_url'],
137
+ source: res['source'],
138
+ source_url: res['source_url'],
139
+ tags: (res['book_tags'] || res['tags'] || []).map { |tag| transform_tag(tag) },
140
+ title: res['title'],
141
+ unique_url: res['unique_url'],
142
+ highlights: highlights,
143
+ num_highlights: res['num_highlights'] || highlights.size,
144
+ )
145
+ end
146
+
147
+ def transform_highlight(res)
148
+ Highlight.new(
149
+ book_id: res['book_id'].to_s,
150
+ color: res['color'],
151
+ created_at: res['created_at'],
152
+ end_location: res['end_location'],
153
+ external_id: res['external_id'],
154
+ highlight_id: res['id'].to_s,
155
+ highlighted_at: res['highlighted_at'],
156
+ is_discard: res['is_discard'],
157
+ is_favorite: res['is_favorite'],
158
+ location: res['location'],
159
+ location_type: res['location_type'],
160
+ note: res['note'],
161
+ readwise_url: res['readwise_url'],
162
+ tags: res['tags'].map { |tag| transform_tag(tag) },
163
+ text: res['text'],
164
+ updated_at: res['updated_at'],
165
+ url: res['url'],
166
+ )
167
+ end
168
+
169
+ def transform_tag(res)
170
+ Tag.new(
171
+ tag_id: res['id'].to_s,
172
+ name: res['name'],
173
+ )
174
+ end
175
+
176
+ def get_readwise_request(url)
177
+ uri = URI.parse(url)
178
+ req = Net::HTTP::Get.new(uri)
179
+ req['Authorization'] = "Token #{@token}"
180
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
181
+ http.request(req)
182
+ end
183
+
184
+ raise Error, 'Get request failed' unless res.is_a?(Net::HTTPSuccess)
185
+
186
+ JSON.parse(res.body)
187
+ end
188
+
189
+ def patch_readwise_request(url, payload:)
190
+ uri = URI.parse(url)
191
+ req = Net::HTTP::Patch.new(uri)
192
+ req['Authorization'] = "Token #{@token}"
193
+ req['Content-Type'] = 'application/json'
194
+ req.body = payload.to_json
195
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
196
+ http.request(req)
197
+ end
198
+
199
+ raise Error, 'Patch request failed' unless res.is_a?(Net::HTTPSuccess)
200
+
201
+ JSON.parse(res.body)
202
+ end
203
+
204
+ def post_readwise_request(url, payload:)
205
+ uri = URI.parse(url)
206
+ req = Net::HTTP::Post.new(uri)
207
+ req['Authorization'] = "Token #{@token}"
208
+ req['Content-Type'] = 'application/json'
209
+ req.body = payload.to_json
210
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
211
+ http.request(req)
212
+ end
213
+
214
+ raise Error, 'Post request failed' unless res.is_a?(Net::HTTPSuccess)
215
+
216
+ JSON.parse(res.body)
217
+ end
218
+
219
+ def delete_readwise_request(url)
220
+ uri = URI.parse(url)
221
+ req = Net::HTTP::Delete.new(uri)
222
+ req['Authorization'] = "Token #{@token}"
223
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
224
+ http.request(req)
105
225
  end
106
- raise Error, 'Export request failed' unless export_res.is_a?(Net::HTTPSuccess)
107
226
 
108
- JSON.parse(export_res.body)
227
+ raise Error, 'Delete request failed' unless res.is_a?(Net::HTTPSuccess)
109
228
  end
110
229
  end
111
230
  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
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.3.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/readwise.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
37
  spec.require_paths = ["lib"]
38
38
 
39
- spec.add_development_dependency "bundler", "~> 2.4.4"
39
+ spec.add_development_dependency "bundler", "~> 2.2"
40
40
  spec.add_development_dependency "rake", "~> 10.0"
41
41
  spec.add_development_dependency "rspec", "~> 3.0"
42
42
  spec.add_development_dependency "rspec-file_fixtures", "~> 0.1.6"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: readwise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Beckman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-02-28 00:00:00.000000000 Z
11
+ date: 2023-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.4.4
19
+ version: '2.2'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.4.4
26
+ version: '2.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -74,11 +74,11 @@ extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
76
  - ".github/FUNDING.yml"
77
+ - ".github/workflows/ruby.yml"
77
78
  - ".gitignore"
78
79
  - CHANGELOG.md
79
80
  - CODE_OF_CONDUCT.md
80
81
  - Gemfile
81
- - Gemfile.lock
82
82
  - LICENSE.txt
83
83
  - README.md
84
84
  - Rakefile
@@ -114,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
114
  - !ruby/object:Gem::Version
115
115
  version: '0'
116
116
  requirements: []
117
- rubygems_version: 3.4.7
117
+ rubygems_version: 3.4.14
118
118
  signing_key:
119
119
  specification_version: 4
120
120
  summary: Readwise API client
data/Gemfile.lock DELETED
@@ -1,38 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- readwise (0.2.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- diff-lcs (1.5.0)
10
- rake (10.5.0)
11
- rspec (3.12.0)
12
- rspec-core (~> 3.12.0)
13
- rspec-expectations (~> 3.12.0)
14
- rspec-mocks (~> 3.12.0)
15
- rspec-core (3.12.0)
16
- rspec-support (~> 3.12.0)
17
- rspec-expectations (3.12.2)
18
- diff-lcs (>= 1.2.0, < 2.0)
19
- rspec-support (~> 3.12.0)
20
- rspec-file_fixtures (0.1.6)
21
- rspec (~> 3.0)
22
- rspec-mocks (3.12.2)
23
- diff-lcs (>= 1.2.0, < 2.0)
24
- rspec-support (~> 3.12.0)
25
- rspec-support (3.12.0)
26
-
27
- PLATFORMS
28
- ruby
29
-
30
- DEPENDENCIES
31
- bundler (~> 2.4.4)
32
- rake (~> 10.0)
33
- readwise!
34
- rspec (~> 3.0)
35
- rspec-file_fixtures (~> 0.1.6)
36
-
37
- BUNDLED WITH
38
- 2.4.4