format_parser 0.11.0 → 0.12.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 +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +9 -1
- data/lib/format_parser/version.rb +1 -1
- data/lib/parsers/jpeg_parser.rb +7 -4
- data/lib/remote_io.rb +14 -5
- data/spec/remote_io_spec.rb +21 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f66a8afff2b85b57587c1cc7914dda25005b024284104bf3b0324b653a3ceff
|
4
|
+
data.tar.gz: 7e31d48b97e0dedfe5965b6ae524f1a14bbdc751ac50df11666aee3d4a18e5af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 850a9f04a7006a52016d087c479a5b22048396c55ed51766a8d8c50f4ec66385ad565c3a006687428c762d16fd3aae5aa4f1d250ed1926bde07c0a61387f77d4
|
7
|
+
data.tar.gz: 6afa6ca1706cac48d9ea915bcd0c7ea4900d517eef195880cd0124621c0a7af4a1759ab999ede643dcce8a1066f126f59f030dcc963272c95b130bd397150975
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
## 0.12.0
|
2
|
+
* Relay upstream status from `RemoteIO` in the `status_code` attribute (returns an `Integer`)
|
3
|
+
|
1
4
|
## 0.11.0
|
2
5
|
* Add `Image#display_width_px` and `Image#display_height_px` for EXIF/aspect corrected display dimensions, and provide
|
3
6
|
those values from a few parsers already. Also make full EXIF data available for JPEG/TIFF in `intrinsics[:exif]`
|
data/README.md
CHANGED
@@ -35,7 +35,7 @@ and [dimensions,](https://github.com/sstephenson/dimensions) borrowing from them
|
|
35
35
|
|
36
36
|
## Basic usage
|
37
37
|
|
38
|
-
Pass an IO object that responds to `read` and `seek` to `FormatParser` and the first confirmed match will be returned.
|
38
|
+
Pass an IO object that responds to `read` and `seek` to `FormatParser.parse` and the first confirmed match will be returned.
|
39
39
|
|
40
40
|
```ruby
|
41
41
|
match = FormatParser.parse(File.open("myimage.jpg", "rb"))
|
@@ -46,6 +46,14 @@ match.display_height_px #=> 240
|
|
46
46
|
match.orientation #=> :top_left
|
47
47
|
```
|
48
48
|
|
49
|
+
You can also use `parse_http` passing a URL or `parse_file_at` passing a path:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
match = FormatParser.parse_http('https://upload.wikimedia.org/wikipedia/commons/b/b4/Mardin_1350660_1350692_33_images.jpg')
|
53
|
+
match.nature #=> :image
|
54
|
+
match.format #=> :jpg
|
55
|
+
```
|
56
|
+
|
49
57
|
If you would rather receive all potential results from the gem, call the gem as follows:
|
50
58
|
|
51
59
|
```ruby
|
data/lib/parsers/jpeg_parser.rb
CHANGED
@@ -17,6 +17,7 @@ class FormatParser::JPEGParser
|
|
17
17
|
@buf = FormatParser::IOConstraint.new(io)
|
18
18
|
@width = nil
|
19
19
|
@height = nil
|
20
|
+
@exif_data = nil
|
20
21
|
scan
|
21
22
|
end
|
22
23
|
|
@@ -119,6 +120,7 @@ class FormatParser::JPEGParser
|
|
119
120
|
# the second time around. What we care about, rather, is the EXIF data only. So we will
|
120
121
|
# pry it out of the APP1 frame and parse it as the TIFF segment - which is what EXIFR
|
121
122
|
# does under the hood.
|
123
|
+
marker_length_at = @buf.pos
|
122
124
|
app1_frame_content_length = read_short - 2
|
123
125
|
|
124
126
|
# If there is certainly not enough data in this APP1 to begin with, bail out.
|
@@ -132,10 +134,7 @@ class FormatParser::JPEGParser
|
|
132
134
|
|
133
135
|
# If we could not find the magic Exif\0 string at the start of the marker,
|
134
136
|
# seek to the start of the next marker and return
|
135
|
-
unless maybe_exif_magic_str == EXIF_MAGIC_STRING
|
136
|
-
safe_skip(@buf, app1_frame_content_length - EXIF_MAGIC_STRING.bytesize)
|
137
|
-
return
|
138
|
-
end
|
137
|
+
return unless maybe_exif_magic_str == EXIF_MAGIC_STRING
|
139
138
|
|
140
139
|
# ...and only then read the marker contents and parse it as EXIF
|
141
140
|
exif_data = safe_read(@buf, app1_frame_content_length - EXIF_MAGIC_STRING.bytesize)
|
@@ -146,6 +145,10 @@ class FormatParser::JPEGParser
|
|
146
145
|
rescue EXIFR::MalformedTIFF
|
147
146
|
# Not a JPEG or the Exif headers contain invalid data, or
|
148
147
|
# an APP1 marker was detected in a file that is not a JPEG
|
148
|
+
ensure
|
149
|
+
# Reposition the file pointer to where the next marker will begin,
|
150
|
+
# regardless whether we did find usable EXIF or not
|
151
|
+
@buf.seek(marker_length_at + 2 + app1_frame_content_length)
|
149
152
|
end
|
150
153
|
|
151
154
|
def read_frame
|
data/lib/remote_io.rb
CHANGED
@@ -4,14 +4,23 @@
|
|
4
4
|
# tweaks using `Faraday.default_connection = ...` these will
|
5
5
|
# take effect for these RemoteIO objects as well
|
6
6
|
class FormatParser::RemoteIO
|
7
|
+
class UpstreamError < StandardError
|
8
|
+
# @return Integer
|
9
|
+
attr_reader :status_code
|
10
|
+
def initialize(status_code, message)
|
11
|
+
@status_code = status_code
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
7
16
|
# Represents a failure that might be retried
|
8
17
|
# (like a 5xx response or a timeout)
|
9
|
-
class IntermittentFailure <
|
18
|
+
class IntermittentFailure < UpstreamError
|
10
19
|
end
|
11
20
|
|
12
21
|
# Represents a failure that should not be retried
|
13
22
|
# (like a 4xx response or a DNS resolution error)
|
14
|
-
class InvalidRequest <
|
23
|
+
class InvalidRequest < UpstreamError
|
15
24
|
end
|
16
25
|
|
17
26
|
# @param uri[URI, String] the remote URL to obtain
|
@@ -76,7 +85,7 @@ class FormatParser::RemoteIO
|
|
76
85
|
# Figure out of the server supports content ranges, if it doesn't we have no
|
77
86
|
# business working with that server
|
78
87
|
range_header = response.headers['Content-Range']
|
79
|
-
raise InvalidRequest, "No range support at #{@uri}" unless range_header
|
88
|
+
raise InvalidRequest.new(response.status, "No range support at #{@uri}") unless range_header
|
80
89
|
|
81
90
|
# "Content-Range: bytes 0-0/307404381" is how the response header is structured
|
82
91
|
size = range_header[/\/(\d+)$/, 1].to_i
|
@@ -95,10 +104,10 @@ class FormatParser::RemoteIO
|
|
95
104
|
return
|
96
105
|
when 500..599
|
97
106
|
FormatParser::Measurometer.increment_counter('format_parser.RemoteIO.upstream50x_errors', 1)
|
98
|
-
raise IntermittentFailure, "Server at #{@uri} replied with a #{response.status} and we might want to retry"
|
107
|
+
raise IntermittentFailure.new(response.status, "Server at #{@uri} replied with a #{response.status} and we might want to retry")
|
99
108
|
else
|
100
109
|
FormatParser::Measurometer.increment_counter('format_parser.RemoteIO.invalid_request_errors', 1)
|
101
|
-
raise InvalidRequest, "Server at #{@uri} replied with a #{response.status} and refused our request"
|
110
|
+
raise InvalidRequest.new(response.status, "Server at #{@uri} replied with a #{response.status} and refused our request")
|
102
111
|
end
|
103
112
|
end
|
104
113
|
end
|
data/spec/remote_io_spec.rb
CHANGED
@@ -35,6 +35,27 @@ describe FormatParser::RemoteIO do
|
|
35
35
|
expect { rio.read(100) }.to raise_error(/replied with a 403 and refused/)
|
36
36
|
end
|
37
37
|
|
38
|
+
it 'returns nil on a 416 response' do
|
39
|
+
rio = described_class.new('https://images.invalid/img.jpg')
|
40
|
+
|
41
|
+
fake_resp = double(headers: {}, status: 416, body: 'You stepped off the ledge of the range')
|
42
|
+
expect(Faraday).to receive(:get).with('https://images.invalid/img.jpg', nil, range: 'bytes=100-199').and_return(fake_resp)
|
43
|
+
|
44
|
+
rio.seek(100)
|
45
|
+
expect(rio.read(100)).to be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'sets the status_code of the exception on a 4xx response from upstream' do
|
49
|
+
rio = described_class.new('https://images.invalid/img.jpg')
|
50
|
+
|
51
|
+
fake_resp = double(headers: {}, status: 403, body: 'Please log in')
|
52
|
+
expect(Faraday).to receive(:get).with('https://images.invalid/img.jpg', nil, range: 'bytes=100-199').and_return(fake_resp)
|
53
|
+
|
54
|
+
rio.seek(100)
|
55
|
+
# rubocop: disable Lint/AmbiguousBlockAssociation
|
56
|
+
expect { rio.read(100) }.to raise_error { |e| expect(e.status_code).to eq(403) }
|
57
|
+
end
|
58
|
+
|
38
59
|
it 'returns a nil when the range cannot be satisfied and the response is 416' do
|
39
60
|
rio = described_class.new('https://images.invalid/img.jpg')
|
40
61
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: format_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Noah Berman
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-05-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ks
|