format_parser 0.16.1 → 0.17.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: 80362b6ee92ad34550681e8c0447dcb93b11071beac25d743356a9c0b7a548f9
4
- data.tar.gz: c40fbcae9bd5417420ff5ea573c24f65462f3b0cf5269fc65024e6a237c98f45
3
+ metadata.gz: 45efce1fcb5395b66713b4704911473a6aaf6beab8dced2275299d05d42f051c
4
+ data.tar.gz: 760d1d686400e97cb58296bbf06862be7a19fedd6b2684075530c089d6685eac
5
5
  SHA512:
6
- metadata.gz: 7f20f7206574ea204393662e29251dc079ce615c593b21c0c4c35f3ba74a08fc9582e15443f23e45771ee2cb73b5b691ee6a31d37f5fb99e847d09d4c2e003cd
7
- data.tar.gz: 6a23e2bb7797a5834e0b2867096240a29ea899d2301f1496c94b2e9d35d29698dc8599bef7185785f7a26fc929bff1e9dda1f5d7a96bbfc37227b1a2d42b3c4b
6
+ metadata.gz: 0f3b357ff7b4121c4cad07a82ca94f13a4e92f3657e8984aa61a1c1a67f6144f726174a09b3b99c3c29b75dcd4c81970a5613e0e5a79e7c4eef52dbcdb779a50
7
+ data.tar.gz: 712e87ef7d506abcf7096d16c738058320dac9c1f2b2557ce08c6576748b37d3041bbe3d43260dcf846bfa7062a5de2840e30782e8c8edaabd15a6b19146d344
@@ -1,3 +1,11 @@
1
+ ## 0.17.0
2
+ * Remove parser factories. A parser should respond to `likely_match?` and `call`. If a parser has to be instantiated anew for
3
+ every call the parser should take care of instantiating itself.
4
+ * Add support for BMP files with core headers (older version of the BMP format)
5
+
6
+ ## 0.16.2
7
+ * All EXIF: Deal with EXIF orientations that get parsed as an array of [Orientation, nil] due to incorrect padding
8
+
1
9
  ## 0.16.1
2
10
  * All EXIF: Make sure the 0 orientation does not get silently treated as orientation 8, mislabling
3
11
  images which are not rotated as being rotated (orientation changed)
@@ -244,40 +244,25 @@ module FormatParser
244
244
 
245
245
  fitting_by_natures = assemble_parser_set[@parsers_per_nature, desired_natures]
246
246
  fitting_by_formats = assemble_parser_set[@parsers_per_format, desired_formats]
247
- factories = fitting_by_natures & fitting_by_formats
247
+ parsers = fitting_by_natures & fitting_by_formats
248
248
 
249
- if factories.empty?
249
+ if parsers.empty?
250
250
  raise ArgumentError, "No parsers provide both natures #{desired_natures.inspect} and formats #{desired_formats.inspect}"
251
251
  end
252
252
 
253
253
  # Order the parsers according to their priority value. The ones having a lower
254
254
  # value will sort higher and will be applied sooner
255
- factories_in_order_of_priority = factories.to_a.sort do |parser_factory_a, parser_factory_b|
256
- @parser_priorities[parser_factory_a] <=> @parser_priorities[parser_factory_b]
255
+ parsers_in_order_of_priority = parsers.to_a.sort do |parser_a, parser_b|
256
+ @parser_priorities[parser_a] <=> @parser_priorities[parser_b]
257
257
  end
258
258
 
259
259
  # If there is one parser that is more likely to match, place it first
260
- if first_match = factories_in_order_of_priority.find { |f| f.respond_to?(:likely_match?) && f.likely_match?(filename_hint) }
261
- factories_in_order_of_priority.delete(first_match)
262
- factories_in_order_of_priority.unshift(first_match)
260
+ if first_match = parsers_in_order_of_priority.find { |f| f.respond_to?(:likely_match?) && f.likely_match?(filename_hint) }
261
+ parsers_in_order_of_priority.delete(first_match)
262
+ parsers_in_order_of_priority.unshift(first_match)
263
263
  end
264
264
 
265
- factories_in_order_of_priority.map { |callable_or_class| instantiate_parser(callable_or_class) }
266
- end
267
-
268
- # Instantiates a parser object (an object that responds to `#call`) from a given class
269
- # or returns the parameter as is if it is callable by itself - i.e. if it is a Proc
270
- #
271
- # @param callable_or_responding_to_new[#call, #new] a callable or a Class/Module
272
- # @return [#call] a parser that can be called with an IO-ish argument
273
- def self.instantiate_parser(callable_or_responding_to_new)
274
- if callable_or_responding_to_new.respond_to?(:call)
275
- callable_or_responding_to_new
276
- elsif callable_or_responding_to_new.respond_to?(:new)
277
- callable_or_responding_to_new.new
278
- else
279
- raise ArgumentError, 'A parser should be either a class with an instance method #call or a Proc'
280
- end
265
+ parsers_in_order_of_priority
281
266
  end
282
267
 
283
268
  def self.string_to_lossy_utf8(str)
@@ -1,3 +1,3 @@
1
1
  module FormatParser
2
- VERSION = '0.16.1'
2
+ VERSION = '0.17.0'
3
3
  end
@@ -18,7 +18,7 @@ class FormatParser::AIFFParser
18
18
  'ANNO',
19
19
  ]
20
20
 
21
- def self.likely_match?(filename)
21
+ def likely_match?(filename)
22
22
  filename =~ /\.aiff?$/i
23
23
  end
24
24
 
@@ -84,5 +84,5 @@ class FormatParser::AIFFParser
84
84
  (sign == '1' ? -1.0 : 1.0) * (fraction.to_f / ((1 << 63) - 1)) * (2**exponent)
85
85
  end
86
86
 
87
- FormatParser.register_parser self, natures: :audio, formats: :aiff
87
+ FormatParser.register_parser new, natures: :audio, formats: :aiff
88
88
  end
@@ -4,9 +4,9 @@ class FormatParser::BMPParser
4
4
  include FormatParser::IOUtils
5
5
 
6
6
  VALID_BMP = 'BM'
7
- PERMISSIBLE_PIXEL_ARRAY_LOCATIONS = 40..512
7
+ PERMISSIBLE_PIXEL_ARRAY_LOCATIONS = 26..512
8
8
 
9
- def self.likely_match?(filename)
9
+ def likely_match?(filename)
10
10
  filename =~ /\.bmp$/i
11
11
  end
12
12
 
@@ -21,14 +21,41 @@ class FormatParser::BMPParser
21
21
  return unless PERMISSIBLE_PIXEL_ARRAY_LOCATIONS.cover?(pix_array_location)
22
22
 
23
23
  dib_header = safe_read(io, 40)
24
+ header_size = dib_header.unpack('V')[0]
25
+ case header_size
26
+ when 12 # OS21XBITMAPHEADER
27
+ parse_bitmap_core_header(dib_header)
28
+ else # More modern implementations
29
+ parse_modern_header(dib_header)
30
+ end
31
+ end
32
+
33
+ def parse_bitmap_core_header(dib_header)
34
+ _header_size, width, height, _num_color_planes, bit_depth = dib_header.unpack('VSSSS')
35
+
36
+ # In core bitmap format an unsigned int is used for dimensions,
37
+ # no inverse scan order is possible
38
+ data_order = :normal
39
+
40
+ FormatParser::Image.new(
41
+ format: :bmp,
42
+ width_px: width,
43
+ height_px: height,
44
+ color_mode: :rgb,
45
+ intrinsics: {
46
+ data_order: data_order,
47
+ bits_per_pixel: bit_depth
48
+ }
49
+ )
50
+ end
24
51
 
25
- _header_size, width, height, _planes, _bits_per_pixel,
52
+ def parse_modern_header(dib_header)
53
+ _header_size, width, height, _planes, bits_per_pixel,
26
54
  _compression_method, _image_size, horizontal_res,
27
55
  vertical_res, _n_colors, _i_colors = dib_header.unpack('Vl<2v2V2l<2V2')
28
56
 
29
57
  # There are cases where the height might by negative indicating the data
30
58
  # is ordered from top to bottom instead of bottom to top
31
- # http://www.dragonwins.com/domains/getteched/bmp/bmpfileformat.htm#The%20Image%20Header
32
59
  data_order = height < 0 ? :inverse : :normal
33
60
 
34
61
  FormatParser::Image.new(
@@ -39,10 +66,11 @@ class FormatParser::BMPParser
39
66
  intrinsics: {
40
67
  vertical_resolution: vertical_res,
41
68
  horizontal_resolution: horizontal_res,
42
- data_order: data_order
69
+ data_order: data_order,
70
+ bits_per_pixel: bits_per_pixel,
43
71
  }
44
72
  )
45
73
  end
46
74
 
47
- FormatParser.register_parser self, natures: :image, formats: :bmp
75
+ FormatParser.register_parser new, natures: :image, formats: :bmp
48
76
  end
@@ -7,7 +7,7 @@ class FormatParser::CR2Parser
7
7
  TIFF_HEADER = [0x49, 0x49, 0x2a, 0x00]
8
8
  CR2_HEADER = [0x43, 0x52, 0x02, 0x00]
9
9
 
10
- def self.likely_match?(filename)
10
+ def likely_match?(filename)
11
11
  filename =~ /\.cr2$/i
12
12
  end
13
13
 
@@ -44,5 +44,5 @@ class FormatParser::CR2Parser
44
44
  nil
45
45
  end
46
46
 
47
- FormatParser.register_parser self, natures: :image, formats: :cr2
47
+ FormatParser.register_parser new, natures: :image, formats: :cr2
48
48
  end
@@ -19,7 +19,7 @@ class FormatParser::DPXParser
19
19
 
20
20
  private_constant :ByteOrderHintIO
21
21
 
22
- def self.likely_match?(filename)
22
+ def likely_match?(filename)
23
23
  filename =~ /\.dpx$/i
24
24
  end
25
25
 
@@ -59,5 +59,5 @@ class FormatParser::DPXParser
59
59
  )
60
60
  end
61
61
 
62
- FormatParser.register_parser self, natures: :image, formats: :dpx
62
+ FormatParser.register_parser new, natures: :image, formats: :dpx
63
63
  end
@@ -42,7 +42,7 @@ module FormatParser::EXIFParser
42
42
 
43
43
  class EXIFResult < SimpleDelegator
44
44
  def rotated?
45
- __getobj__.orientation.to_i > 4
45
+ orientation.to_i > 4
46
46
  end
47
47
 
48
48
  def to_json(*maybe_coder)
@@ -52,7 +52,11 @@ module FormatParser::EXIFParser
52
52
  end
53
53
 
54
54
  def orientation
55
- __getobj__.orientation.to_i
55
+ # In some EXIF tags the value type is set oddly - it unpacks into multiple values,
56
+ # and it will look like this: [#<EXIFR::TIFF::Orientation:TopLeft(1)>, nil]
57
+ orientation_values = Array(__getobj__.orientation)
58
+ last_usable_value = orientation_values.compact[-1] # Use the last non-nil one
59
+ last_usable_value.to_i
56
60
  end
57
61
 
58
62
  def orientation_sym
@@ -162,4 +166,6 @@ module FormatParser::EXIFParser
162
166
  raw_exif_data ? EXIFResult.new(raw_exif_data) : nil
163
167
  end
164
168
  end
169
+
170
+ extend self
165
171
  end
@@ -1,7 +1,7 @@
1
1
  class FormatParser::FDXParser
2
2
  include FormatParser::IOUtils
3
3
 
4
- def self.likely_match?(filename)
4
+ def likely_match?(filename)
5
5
  filename =~ /\.fdx$/i
6
6
  end
7
7
 
@@ -30,5 +30,5 @@ class FormatParser::FDXParser
30
30
  end
31
31
  end
32
32
 
33
- FormatParser.register_parser self, natures: :document, formats: :fdx
33
+ FormatParser.register_parser new, natures: :document, formats: :fdx
34
34
  end
@@ -5,7 +5,7 @@ class FormatParser::FLACParser
5
5
  MAGIC_BYTE_STRING = 'fLaC'
6
6
  BLOCK_HEADER_BYTES = 4
7
7
 
8
- def self.likely_match?(filename)
8
+ def likely_match?(filename)
9
9
  filename =~ /\.flac$/i
10
10
  end
11
11
 
@@ -75,5 +75,5 @@ class FormatParser::FLACParser
75
75
  s.unpack('B*')[0].to_i(2)
76
76
  end
77
77
 
78
- FormatParser.register_parser self, natures: :audio, formats: :flac
78
+ FormatParser.register_parser new, natures: :audio, formats: :flac
79
79
  end
@@ -4,7 +4,7 @@ class FormatParser::GIFParser
4
4
  HEADERS = ['GIF87a', 'GIF89a'].map(&:b)
5
5
  NETSCAPE_AND_AUTHENTICATION_CODE = 'NETSCAPE2.0'
6
6
 
7
- def self.likely_match?(filename)
7
+ def likely_match?(filename)
8
8
  filename =~ /\.gif$/i
9
9
  end
10
10
 
@@ -48,5 +48,5 @@ class FormatParser::GIFParser
48
48
  )
49
49
  end
50
50
 
51
- FormatParser.register_parser self, natures: :image, formats: :gif, priority: 0
51
+ FormatParser.register_parser new, natures: :image, formats: :gif, priority: 0
52
52
  end
@@ -17,6 +17,10 @@ class FormatParser::JPEGParser
17
17
  filename =~ /\.jpe?g$/i
18
18
  end
19
19
 
20
+ def self.call(io)
21
+ new.call(io)
22
+ end
23
+
20
24
  def call(io)
21
25
  @buf = FormatParser::IOConstraint.new(io)
22
26
  @width = nil
@@ -11,7 +11,7 @@ class FormatParser::MOOVParser
11
11
  'm4a ' => :m4a,
12
12
  }
13
13
 
14
- def self.likely_match?(filename)
14
+ def likely_match?(filename)
15
15
  filename =~ /\.(mov|m4a|ma4|mp4|aac)$/i
16
16
  end
17
17
 
@@ -87,5 +87,5 @@ class FormatParser::MOOVParser
87
87
  maybe_atom_size >= minimum_ftyp_atom_size && maybe_ftyp_atom_signature == 'ftyp'
88
88
  end
89
89
 
90
- FormatParser.register_parser self, natures: :video, formats: FTYP_MAP.values, priority: 1
90
+ FormatParser.register_parser new, natures: :video, formats: FTYP_MAP.values, priority: 1
91
91
  end
@@ -53,7 +53,7 @@ class FormatParser::MP3Parser
53
53
  end
54
54
  end
55
55
 
56
- def self.likely_match?(filename)
56
+ def likely_match?(filename)
57
57
  filename =~ /\.mp3$/i
58
58
  end
59
59
 
@@ -297,5 +297,5 @@ class FormatParser::MP3Parser
297
297
  attrs
298
298
  end
299
299
 
300
- FormatParser.register_parser self, natures: :audio, formats: :mp3, priority: 99
300
+ FormatParser.register_parser new, natures: :audio, formats: :mp3, priority: 99
301
301
  end
@@ -6,7 +6,7 @@ class FormatParser::OggParser
6
6
  # Maximum size of an Ogg page
7
7
  MAX_POSSIBLE_PAGE_SIZE = 65307
8
8
 
9
- def self.likely_match?(filename)
9
+ def likely_match?(filename)
10
10
  filename =~ /\.ogg$/i
11
11
  end
12
12
 
@@ -218,5 +218,5 @@ class FormatParser::OggParser
218
218
  0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
219
219
  ].freeze
220
220
 
221
- FormatParser.register_parser self, natures: :audio, formats: :ogg
221
+ FormatParser.register_parser new, natures: :audio, formats: :ogg
222
222
  end
@@ -9,7 +9,7 @@ class FormatParser::PDFParser
9
9
  #
10
10
  PDF_MARKER = /%PDF-1\.[0-8]{1}/
11
11
 
12
- def self.likely_match?(filename)
12
+ def likely_match?(filename)
13
13
  filename =~ /\.(pdf|ai)$/i
14
14
  end
15
15
 
@@ -21,5 +21,5 @@ class FormatParser::PDFParser
21
21
  FormatParser::Document.new(format: :pdf)
22
22
  end
23
23
 
24
- FormatParser.register_parser self, natures: :document, formats: :pdf, priority: 1
24
+ FormatParser.register_parser new, natures: :document, formats: :pdf, priority: 1
25
25
  end
@@ -15,7 +15,7 @@ class FormatParser::PNGParser
15
15
  6 => true,
16
16
  }
17
17
 
18
- def self.likely_match?(filename)
18
+ def likely_match?(filename)
19
19
  filename =~ /\.png$/i
20
20
  end
21
21
 
@@ -75,5 +75,5 @@ class FormatParser::PNGParser
75
75
  end
76
76
 
77
77
  # Give it priority 1 since priority 0 is reserved for JPEG, our most popular
78
- FormatParser.register_parser self, natures: :image, formats: :png, priority: 1
78
+ FormatParser.register_parser new, natures: :image, formats: :png, priority: 1
79
79
  end
@@ -3,7 +3,7 @@ class FormatParser::PSDParser
3
3
 
4
4
  PSD_HEADER = [0x38, 0x42, 0x50, 0x53]
5
5
 
6
- def self.likely_match?(filename)
6
+ def likely_match?(filename)
7
7
  filename =~ /\.psd$/i # Maybe also PSB at some point
8
8
  end
9
9
 
@@ -23,5 +23,5 @@ class FormatParser::PSDParser
23
23
  )
24
24
  end
25
25
 
26
- FormatParser.register_parser self, natures: :image, formats: :psd
26
+ FormatParser.register_parser new, natures: :image, formats: :psd
27
27
  end
@@ -5,7 +5,7 @@ class FormatParser::TIFFParser
5
5
  MAGIC_LE = [0x49, 0x49, 0x2A, 0x0].pack('C4')
6
6
  MAGIC_BE = [0x4D, 0x4D, 0x0, 0x2A].pack('C4')
7
7
 
8
- def self.likely_match?(filename)
8
+ def likely_match?(filename)
9
9
  filename =~ /\.tiff?$/i
10
10
  end
11
11
 
@@ -43,5 +43,5 @@ class FormatParser::TIFFParser
43
43
  safe_read(io, 2) == 'CR'
44
44
  end
45
45
 
46
- FormatParser.register_parser self, natures: :image, formats: :tif
46
+ FormatParser.register_parser new, natures: :image, formats: :tif
47
47
  end
@@ -1,7 +1,7 @@
1
1
  class FormatParser::WAVParser
2
2
  include FormatParser::IOUtils
3
3
 
4
- def self.likely_match?(filename)
4
+ def likely_match?(filename)
5
5
  filename =~ /\.wav$/i
6
6
  end
7
7
 
@@ -99,5 +99,5 @@ class FormatParser::WAVParser
99
99
  )
100
100
  end
101
101
 
102
- FormatParser.register_parser self, natures: :audio, formats: :wav
102
+ FormatParser.register_parser new, natures: :audio, formats: :wav
103
103
  end
@@ -5,7 +5,7 @@ class FormatParser::ZIPParser
5
5
  include OfficeFormats
6
6
  include FormatParser::IOUtils
7
7
 
8
- def self.likely_match?(filename)
8
+ def likely_match?(filename)
9
9
  filename =~ /\.(zip|docx|keynote|numbers|pptx|xlsx)$/i
10
10
  end
11
11
 
@@ -58,5 +58,5 @@ class FormatParser::ZIPParser
58
58
  end
59
59
  end
60
60
 
61
- FormatParser.register_parser self, natures: [:archive, :document], formats: :zip, priority: 2
61
+ FormatParser.register_parser new, natures: [:archive, :document], formats: :zip, priority: 2
62
62
  end
@@ -17,6 +17,7 @@ describe FormatParser::BMPParser do
17
17
  expect(parsed.intrinsics[:vertical_resolution]).to eq(2834)
18
18
  expect(parsed.intrinsics[:horizontal_resolution]).to eq(2834)
19
19
  expect(parsed.intrinsics[:data_order]).to eq(:normal)
20
+ expect(parsed.intrinsics[:bits_per_pixel]).to eq(24)
20
21
  end
21
22
 
22
23
  it 'parses a BMP file with negative height_px values (divergent scan order)' do
@@ -35,6 +36,7 @@ describe FormatParser::BMPParser do
35
36
  expect(parsed.intrinsics[:vertical_resolution]).to eq(2835)
36
37
  expect(parsed.intrinsics[:horizontal_resolution]).to eq(2835)
37
38
  expect(parsed.intrinsics[:data_order]).to eq(:inverse)
39
+ expect(parsed.intrinsics[:bits_per_pixel]).to eq(24)
38
40
  end
39
41
 
40
42
  it 'parses a BMP where the pixel array location is other than 54' do
@@ -52,6 +54,32 @@ describe FormatParser::BMPParser do
52
54
  expect(parsed.intrinsics).not_to be_nil
53
55
  end
54
56
 
57
+ it 'parses various BMP headers' do
58
+ bmp_path = fixtures_dir + '/BMP/test_v5header.bmp'
59
+ parsed = subject.call(File.open(bmp_path, 'rb'))
60
+
61
+ expect(parsed).not_to be_nil
62
+ expect(parsed.nature).to eq(:image)
63
+ expect(parsed.format).to eq(:bmp)
64
+ expect(parsed.color_mode).to eq(:rgb)
65
+ expect(parsed.width_px).to eq(40)
66
+ expect(parsed.height_px).to eq(27)
67
+ expect(parsed.intrinsics[:bits_per_pixel]).to eq(24)
68
+ expect(parsed.intrinsics[:data_order]).to eq(:normal)
69
+
70
+ bmp_path = fixtures_dir + '/BMP/test_coreheader.bmp'
71
+ parsed = subject.call(File.open(bmp_path, 'rb'))
72
+
73
+ expect(parsed).not_to be_nil
74
+ expect(parsed.nature).to eq(:image)
75
+ expect(parsed.format).to eq(:bmp)
76
+ expect(parsed.color_mode).to eq(:rgb)
77
+ expect(parsed.width_px).to eq(40)
78
+ expect(parsed.height_px).to eq(27)
79
+ expect(parsed.intrinsics[:bits_per_pixel]).to eq(24)
80
+ expect(parsed.intrinsics[:data_order]).to eq(:normal)
81
+ end
82
+
55
83
  it 'refuses to parse a BMP where the pixel array location is very large' do
56
84
  junk_data = [
57
85
  'BM',
@@ -1,15 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FormatParser::EXIFParser do
4
- let(:subject) do
5
- Object.new.tap { |o| o.extend FormatParser::EXIFParser }
6
- end
7
-
8
4
  describe 'is able to correctly parse orientation for all the TIFF EXIF examples from FastImage' do
9
5
  Dir.glob(fixtures_dir + '/exif-orientation-testimages/tiff-*/*.tif').each do |tiff_path|
10
6
  filename = File.basename(tiff_path)
11
7
  it "is able to parse #{filename}" do
12
- result = subject.exif_from_tiff_io(File.open(tiff_path, 'rb'))
8
+ result = described_class.exif_from_tiff_io(File.open(tiff_path, 'rb'))
13
9
  expect(result).not_to be_nil
14
10
  expect(result.orientation_sym).to be_kind_of(Symbol)
15
11
  # Filenames in this dir correspond with the orientation of the file
@@ -18,6 +14,14 @@ describe FormatParser::EXIFParser do
18
14
  end
19
15
  end
20
16
 
17
+ it 'is able to deal with an orientation tag which a tuple value for orientation' do
18
+ path = fixtures_dir + '/EXIF/double_orientation.exif.bin'
19
+ exif_data = File.open(path, 'rb') do |f|
20
+ described_class.exif_from_tiff_io(f)
21
+ end
22
+ expect(exif_data.orientation).to eq(1)
23
+ end
24
+
21
25
  describe 'IOExt' do
22
26
  it 'supports readbyte' do
23
27
  io = FormatParser::EXIFParser::IOExt.new(StringIO.new('hello'))
@@ -2,8 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  describe FormatParser::ZIPParser do
4
4
  it 'provides filename hints' do
5
- expect(FormatParser::ZIPParser).to be_likely_match('file.zip')
6
- expect(FormatParser::ZIPParser).not_to be_likely_match('file.tif')
5
+ expect(subject).to be_likely_match('file.zip')
6
+ expect(subject).not_to be_likely_match('file.tif')
7
7
  end
8
8
 
9
9
  it 'parses a ZIP archive with Zip64 extra fields (due to the number of files)' do
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.16.1
4
+ version: 0.17.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: 2019-08-06 00:00:00.000000000 Z
12
+ date: 2019-09-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ks
@@ -303,8 +303,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
303
303
  - !ruby/object:Gem::Version
304
304
  version: '0'
305
305
  requirements: []
306
- rubyforge_project:
307
- rubygems_version: 2.7.6
306
+ rubygems_version: 3.0.6
308
307
  signing_key:
309
308
  specification_version: 4
310
309
  summary: A library for efficient parsing of file metadata