format_parser 0.11.0 → 0.12.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: ab01944664740856d704875f0a74d1b0540379c8e351176e38d3b5bf119e813f
4
- data.tar.gz: b0736ffd074eb3fb49586d52799dd687c72f0a09b6e2d4109b1cbd26a8eac293
3
+ metadata.gz: 5f66a8afff2b85b57587c1cc7914dda25005b024284104bf3b0324b653a3ceff
4
+ data.tar.gz: 7e31d48b97e0dedfe5965b6ae524f1a14bbdc751ac50df11666aee3d4a18e5af
5
5
  SHA512:
6
- metadata.gz: 570e9fcef6a08ad4e800c84d8452d985900f8442d9071477e6cd465b533677f7c39b6ae51c8f94d0ab0e90305ce790c395c3ce54ecda46800921b3311adc23b8
7
- data.tar.gz: 0662cc268f0fc1fc61ac97896811e505f1ae251a2a15fe9027de2414107adb1414b6ce4f3a1111be1215e8e48323330ce8b08b98e521bf7d9ec4a26c8d29f01d
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
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '0.11.0'
2
+ VERSION = '0.12.0'
3
3
  end
@@ -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 < StandardError
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 < StandardError
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
@@ -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.11.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-04-30 00:00:00.000000000 Z
12
+ date: 2018-05-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ks