Skip to content

Commit ecb87fd

Browse files
joshbeckmanclaude
andcommitted
fix: improve CLI validation and code organization
- Move document categories and locations to shared constants file - Add URL validation for --url option using URI parsing - Add ISO 8601 date validation for --published-date option - Add comprehensive tests for new validation features - Add .rspec_status to gitignore 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent e3943e9 commit ecb87fd

File tree

4 files changed

+53
-7
lines changed

4 files changed

+53
-7
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,5 @@ Gemfile.lock
5555

5656
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
5757
# .rubocop-https?--*
58+
59+
.rspec_status

lib/readwise/cli/document/create_command.rb

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
require_relative '../base_command'
2+
require_relative '../../constants'
3+
require 'uri'
4+
require 'date'
25

36
module Readwise
47
class CLI
58
module Document
69
class CreateCommand < BaseCommand
7-
CATEGORIES = %w[article email rss highlight note pdf epub tweet video].freeze
810
def banner
911
"Usage: readwise document create [options]"
1012
end
@@ -27,6 +29,10 @@ def add_options(opts)
2729
end
2830

2931
opts.on("-u", "--url=URL", "Source URL (defaults to https://example.com/<filename>)") do |url|
32+
unless valid_url?(url)
33+
puts "Error: Invalid URL format. Please provide a valid URL."
34+
exit 1
35+
end
3036
options[:url] = url
3137
end
3238

@@ -38,17 +44,17 @@ def add_options(opts)
3844
options[:notes] = notes
3945
end
4046

41-
opts.on("--location=LOCATION", "Document location: new, later, archive, feed (default: new)") do |location|
42-
unless %w[new later archive feed].include?(location)
43-
puts "Error: Invalid location. Must be one of: new, later, archive, feed"
47+
opts.on("--location=LOCATION", "Document location: #{Readwise::Constants::DOCUMENT_LOCATIONS.join(', ')} (default: new)") do |location|
48+
unless Readwise::Constants::DOCUMENT_LOCATIONS.include?(location)
49+
puts "Error: Invalid location. Must be one of: #{Readwise::Constants::DOCUMENT_LOCATIONS.join(', ')}"
4450
exit 1
4551
end
4652
options[:location] = location
4753
end
4854

49-
opts.on("--category=CATEGORY", "Document category: #{CATEGORIES.join(', ')}") do |category|
50-
unless CATEGORIES.include?(category)
51-
puts "Error: Invalid category. Must be one of: #{CATEGORIES.join(', ')}"
55+
opts.on("--category=CATEGORY", "Document category: #{Readwise::Constants::DOCUMENT_CATEGORIES.join(', ')}") do |category|
56+
unless Readwise::Constants::DOCUMENT_CATEGORIES.include?(category)
57+
puts "Error: Invalid category. Must be one of: #{Readwise::Constants::DOCUMENT_CATEGORIES.join(', ')}"
5258
exit 1
5359
end
5460
options[:category] = category
@@ -63,6 +69,10 @@ def add_options(opts)
6369
end
6470

6571
opts.on("--published-date=DATE", "Published date (ISO 8601 format)") do |date|
72+
unless valid_iso8601_date?(date)
73+
puts "Error: Invalid date format. Please provide a valid ISO 8601 date (e.g., 2023-12-25T10:30:00Z)."
74+
exit 1
75+
end
6676
options[:published_date] = date
6777
end
6878

@@ -104,6 +114,20 @@ def run(args)
104114

105115
private
106116

117+
def valid_url?(url)
118+
uri = URI.parse(url)
119+
uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
120+
rescue URI::InvalidURIError
121+
false
122+
end
123+
124+
def valid_iso8601_date?(date)
125+
DateTime.iso8601(date)
126+
true
127+
rescue Date::Error
128+
false
129+
end
130+
107131
def build_document_params(html_content, html_file)
108132
document_params = {
109133
html: html_content,

lib/readwise/constants.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Readwise
2+
module Constants
3+
DOCUMENT_CATEGORIES = %w[article email rss highlight note pdf epub tweet video].freeze
4+
DOCUMENT_LOCATIONS = %w[new later archive feed].freeze
5+
end
6+
end

spec/cli_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,19 @@ def run_cli_without_token(*args)
100100
expect(status.success?).to be false
101101
expect(stdout).to include('Error: Invalid category. Must be one of: article, email, rss, highlight, note, pdf, epub, tweet, video')
102102
end
103+
104+
it 'shows error for invalid URL' do
105+
stdout, _stderr, status = run_cli('document', 'create', '--url=invalid-url', '--html-file', temp_file.path)
106+
107+
expect(status.success?).to be false
108+
expect(stdout).to include('Error: Invalid URL format. Please provide a valid URL.')
109+
end
110+
111+
it 'shows error for invalid date format' do
112+
stdout, _stderr, status = run_cli('document', 'create', '--published-date=invalid-date', '--html-file', temp_file.path)
113+
114+
expect(status.success?).to be false
115+
expect(stdout).to include('Error: Invalid date format. Please provide a valid ISO 8601 date')
116+
end
103117
end
104118
end

0 commit comments

Comments
 (0)