readwise 0.3.0 → 0.5.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
  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