mhtml 0.1.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +97 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/mhtml/document.rb +135 -0
- data/lib/mhtml/http_header.rb +103 -0
- data/lib/mhtml/root_document.rb +123 -0
- data/lib/mhtml/version.rb +3 -0
- data/lib/mhtml.rb +11 -0
- data/lib/string.rb +64 -0
- data/mhtml.gemspec +30 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7bd65b32102f1862282c6980695730474a60011f
|
4
|
+
data.tar.gz: 96c49f4a7a9e1132d93c89dc7cb27b03f746b179
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c2c2effc9cac20e3c6284f5c61137b553c8b0115bbc6540bd9c15ce97f2cefd541cba9cd9e1a0ede97010e2f7465074897233ee2d3e736172839f27dd29fc5d1
|
7
|
+
data.tar.gz: e4015b53919c0d6a29caf8c11df08ef6a8188eaea2459ca07460ff127df0d287b40eadc4e39e3c5985ef7c1d938ae35c69d5b8b7a0a5897ecf01d437f9167c68
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Mhtml
|
2
|
+
A ruby gem for parsing MHTML.
|
3
|
+
|
4
|
+
Uses the NodeJS C HTTP Parser under the hood (thanks to @cotag for the gem).
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'mhtml'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install mhtml
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
Two interfaces are provided - all at once, or chunked.
|
23
|
+
|
24
|
+
### All at once
|
25
|
+
For when you have all of the data in memory.
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
source = File.open('/file/path.mht').read
|
29
|
+
doc = Mhtml::RootDocument.new(source)
|
30
|
+
|
31
|
+
doc.headers.each { |h| puts h }
|
32
|
+
|
33
|
+
# body is decoded from printed quotable, and encoded according to charset header
|
34
|
+
puts doc.body
|
35
|
+
|
36
|
+
doc.sub_docs.each { |s| puts subdoc }
|
37
|
+
```
|
38
|
+
|
39
|
+
### Chunked
|
40
|
+
For when source data is being streamed, or when concerned about memory usage.
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
doc = Mhtml::RootDocument.new
|
44
|
+
|
45
|
+
doc.on_header { |h| handle_header(h) } # yields each header
|
46
|
+
|
47
|
+
# yields body, possibly in parts
|
48
|
+
doc.on_body do |b|
|
49
|
+
encoding = doc.encoding
|
50
|
+
handle_body(b)
|
51
|
+
end
|
52
|
+
|
53
|
+
doc.on_subdoc_begin { handle_subdoc_begin } # yields nil on each subdoc begin
|
54
|
+
doc.on_subdoc_header { |h| handle_subdoc_header(h) } # yields each subdoc header
|
55
|
+
doc.on_subdoc_body { |b| handle_subdoc_body(b) } # yields each subdoc's body, possibly in parts
|
56
|
+
doc.on_subdoc_complete { handle_subdoc_begin } # yields nil on each subdoc complete
|
57
|
+
|
58
|
+
File.open('/file/path.mht').read.scan(/.{128}/).each do |chunk|
|
59
|
+
doc << chunk
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
### Headers
|
64
|
+
The header class looks like this (portayed as a hash):
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# Content-Type: multipart/related; charset="windows-1252"; boundary="----=_NextPart_01C74319.B7EA56A0"
|
68
|
+
{
|
69
|
+
key: 'Content-Type',
|
70
|
+
values: [
|
71
|
+
{ key: nil, value: 'multipart/related' },
|
72
|
+
{ key: 'charset', value: 'windows-1252' },
|
73
|
+
{ key: 'boundary', value: '----=_NextPart_01C74319.B7EA56A0' }
|
74
|
+
]
|
75
|
+
}
|
76
|
+
```
|
77
|
+
|
78
|
+
## TODO
|
79
|
+
- Revisit spec fixtures - either use existing solution or break out to separate
|
80
|
+
gem
|
81
|
+
- Build up body of fixtures using MHTML from various sources
|
82
|
+
|
83
|
+
## Development
|
84
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
85
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
86
|
+
prompt that will allow you to experiment.
|
87
|
+
|
88
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
89
|
+
To release a new version, update the version number in `version.rb`, and then
|
90
|
+
run `bundle exec rake release`, which will create a git tag for the version,
|
91
|
+
push git commits and tags, and push the `.gem` file to
|
92
|
+
[rubygems.org](https://rubygems.org).
|
93
|
+
|
94
|
+
|
95
|
+
## Contributing
|
96
|
+
Bug reports and pull requests are welcome on GitHub at
|
97
|
+
https://github.com/benjineering/mhtml_rb.
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'http-parser'
|
2
|
+
|
3
|
+
module Mhtml
|
4
|
+
class Document
|
5
|
+
attr_reader :chunked, :parser
|
6
|
+
attr_accessor :headers, :body, :is_quoted_printable, :encoding
|
7
|
+
|
8
|
+
def initialize(str = nil)
|
9
|
+
@chunked = !str.is_a?(String)
|
10
|
+
@header_key = nil
|
11
|
+
@header_value_lines = nil
|
12
|
+
@is_quoted_printable = false
|
13
|
+
@encoding = nil
|
14
|
+
|
15
|
+
@request = HttpParser::Parser.new_instance { |inst| inst.type = :response }
|
16
|
+
|
17
|
+
@parser = HttpParser::Parser.new do |parser|
|
18
|
+
parser.on_header_field { |inst, data| handle_header_field(inst, data) }
|
19
|
+
parser.on_header_value { |inst, data| handle_header_value(inst, data) }
|
20
|
+
parser.on_body { |inst, data| handle_body(inst, data) }
|
21
|
+
parser.on_message_begin { |inst| handle_message_begin(inst) }
|
22
|
+
parser.on_message_complete { |inst| handle_message_complete(inst) }
|
23
|
+
end
|
24
|
+
|
25
|
+
@parser.parse(@request, Mhtml::STATUS_LINE)
|
26
|
+
|
27
|
+
unless @chunked
|
28
|
+
@headers = []
|
29
|
+
@body = ''
|
30
|
+
@parser.parse(@request, str)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def <<(chunk)
|
35
|
+
@parser.parse(@request, chunk)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(other)
|
39
|
+
@headers == other.headers &&
|
40
|
+
@body.gsub(/\r\n/, "\n").strip == other.body.gsub(/\r\n/, "\n").strip
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_header
|
44
|
+
@headers_proc = Proc.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_body
|
48
|
+
@body_proc = Proc.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def header(key)
|
52
|
+
header = nil
|
53
|
+
|
54
|
+
@headers.each do |h|
|
55
|
+
if h.key == key
|
56
|
+
header = h
|
57
|
+
break
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
header
|
62
|
+
end
|
63
|
+
|
64
|
+
# for testing only = no spec implemented
|
65
|
+
def to_s
|
66
|
+
@headers.join(LINE_BREAK) + Mhtml::DOUBLE_LINE_BREAK + @body
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def handle_header_field(inst, data)
|
72
|
+
maybe_create_header
|
73
|
+
@header_key = data
|
74
|
+
@header_value_lines = []
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_header_value(inst, data)
|
78
|
+
@header_value_lines << data
|
79
|
+
end
|
80
|
+
|
81
|
+
def handle_body(inst, data)
|
82
|
+
maybe_create_header
|
83
|
+
decoded = decode(data)
|
84
|
+
|
85
|
+
if @chunked
|
86
|
+
@body_proc.call(decoded) unless @body_proc.nil?
|
87
|
+
else
|
88
|
+
@body.force_encoding(@encoding) if @body.empty? && !@encoding.nil?
|
89
|
+
@body += decoded
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def handle_message_begin(inst)
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_message_complete(inst)
|
97
|
+
end
|
98
|
+
|
99
|
+
def maybe_create_header
|
100
|
+
unless @header_key.nil?
|
101
|
+
header = HttpHeader.new(@header_key, @header_value_lines)
|
102
|
+
@headers << header unless @chunked
|
103
|
+
|
104
|
+
if header.key == 'Content-Type'
|
105
|
+
boundary = header.value('boundary')
|
106
|
+
@boundary = boundary.value unless boundary.nil?
|
107
|
+
|
108
|
+
charset = header.value('charset')
|
109
|
+
|
110
|
+
unless charset.nil?
|
111
|
+
@encoding = Encoding.find(charset.value) rescue nil
|
112
|
+
end
|
113
|
+
|
114
|
+
elsif header.key == 'Content-Transfer-Encoding'
|
115
|
+
value = header.values.first
|
116
|
+
|
117
|
+
if !value.nil? && value.value == 'quoted-printable'
|
118
|
+
@is_quoted_printable = true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@headers_proc.call(header) unless @headers_proc.nil?
|
123
|
+
|
124
|
+
@header_key = nil
|
125
|
+
@header_value_lines = []
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def decode(str)
|
130
|
+
str = str.unpack1('M*') if @is_quoted_printable
|
131
|
+
str = str.force_encoding(@encoding) unless @encoding.nil?
|
132
|
+
str
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Mhtml
|
2
|
+
class HttpHeader
|
3
|
+
require 'string'
|
4
|
+
|
5
|
+
attr_accessor :key, :values
|
6
|
+
|
7
|
+
KEY_VALUE_SEP = ':'.freeze
|
8
|
+
VALUE_SEP = ';'.freeze
|
9
|
+
|
10
|
+
def initialize(key_or_hash, value_lines = nil)
|
11
|
+
if key_or_hash.is_a?(Hash)
|
12
|
+
@key = key_or_hash[:key]
|
13
|
+
@values = key_or_hash[:values]
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
@key = key_or_hash
|
18
|
+
@values = []
|
19
|
+
values_str = value_lines.join('')
|
20
|
+
|
21
|
+
values_str.split(VALUE_SEP).each do |val_str|
|
22
|
+
val_str.strip!
|
23
|
+
val = Value.new(val_str)
|
24
|
+
|
25
|
+
if val.nil?
|
26
|
+
raise "Invalid value:\n#{val_str}\n\nFrom string:\n#{val_str}"
|
27
|
+
end
|
28
|
+
|
29
|
+
@values << val
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
@key == other.key && @values == other.values
|
35
|
+
end
|
36
|
+
|
37
|
+
def value(key)
|
38
|
+
value = nil
|
39
|
+
|
40
|
+
@values.each do |v|
|
41
|
+
if v.key == key
|
42
|
+
value = v
|
43
|
+
break
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
value
|
48
|
+
end
|
49
|
+
|
50
|
+
# following methods are for debugging only - no spec implemented
|
51
|
+
def to_s
|
52
|
+
"#{@key}#{KEY_VALUE_SEP} #{@values.join(VALUE_SEP + ' ')}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def clone
|
56
|
+
vals = @values.collect { |v| v.clone }
|
57
|
+
HttpHeader.new(key: @key.clone, values: vals)
|
58
|
+
end
|
59
|
+
|
60
|
+
class Value
|
61
|
+
attr_reader :key, :value
|
62
|
+
|
63
|
+
# str examples:
|
64
|
+
# value
|
65
|
+
# key="value"
|
66
|
+
def initialize(str_or_hash)
|
67
|
+
if str_or_hash.is_a?(Hash)
|
68
|
+
@key = str_or_hash[:key]
|
69
|
+
@value = str_or_hash[:value]
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
str = str_or_hash
|
74
|
+
split_i = str.index('=')
|
75
|
+
@key = str[0, split_i].strip unless split_i.nil?
|
76
|
+
|
77
|
+
@value =
|
78
|
+
if split_i.nil?
|
79
|
+
str.strip
|
80
|
+
else
|
81
|
+
str[split_i + 1, str.length - 1].strip.strip_other('"')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def ==(other)
|
86
|
+
@key == other.key && @value == other.value
|
87
|
+
end
|
88
|
+
|
89
|
+
# following methods are for debugging only - no spec implemented
|
90
|
+
def to_s
|
91
|
+
if @key.nil?
|
92
|
+
@value
|
93
|
+
else
|
94
|
+
%Q[#{@key}="#{@value}"]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def clone
|
99
|
+
Value.new(key: @key.clone, value: @value.clone)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Mhtml
|
2
|
+
class RootDocument < Document
|
3
|
+
BOUNDARY_PREFIX = '--'.freeze
|
4
|
+
|
5
|
+
attr_accessor :boundary, :sub_docs
|
6
|
+
|
7
|
+
def initialize(str = nil)
|
8
|
+
@sub_docs = []
|
9
|
+
super(str)
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
super(other) && @boundary == other.boundary && @sub_docs == other.sub_docs
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_subdoc_begin
|
17
|
+
@subdoc_begin_proc = Proc.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_subdoc_header
|
21
|
+
@subdoc_header_proc = Proc.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_subdoc_body
|
25
|
+
@subdoc_body_proc = Proc.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_subdoc_complete
|
29
|
+
@subdoc_complete_proc = Proc.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def boundary_str
|
33
|
+
"#{Mhtml::LINE_BREAK}#{BOUNDARY_PREFIX}#{@boundary}#{Mhtml::LINE_BREAK}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def last_boundary_str
|
37
|
+
"#{Mhtml::LINE_BREAK}#{BOUNDARY_PREFIX}#{@boundary}#{BOUNDARY_PREFIX}#{Mhtml::LINE_BREAK}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# for testing only = no spec implemented
|
41
|
+
def to_s
|
42
|
+
doc_sep = Mhtml::DOUBLE_LINE_BREAK + BOUNDARY_PREFIX + @boundary +
|
43
|
+
Mhtml::LINE_BREAK
|
44
|
+
super + doc_sep + @sub_docs.join(doc_sep)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def handle_body(inst, data)
|
50
|
+
maybe_create_header
|
51
|
+
boundary = boundary_str
|
52
|
+
|
53
|
+
unless @split.nil?
|
54
|
+
data = @split + data
|
55
|
+
@split = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
parts = data.split(boundary)
|
59
|
+
|
60
|
+
unless @body_read
|
61
|
+
@body_read = parts.length > 1
|
62
|
+
super(inst, parts.shift)
|
63
|
+
end
|
64
|
+
|
65
|
+
parts.each_with_index do |part, i|
|
66
|
+
end_boundary_pos = part.rindex(last_boundary_str)
|
67
|
+
is_last_subdoc = !end_boundary_pos.nil?
|
68
|
+
part = part[0..(end_boundary_pos - 1)] if is_last_subdoc
|
69
|
+
|
70
|
+
if @chunked
|
71
|
+
is_last_part = i + 1 == parts.length
|
72
|
+
handle_chunked_body(part, is_last_part, is_last_subdoc)
|
73
|
+
else
|
74
|
+
@sub_docs << Document.new(part)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_chunked_body(chunk, is_last_part, is_last_subdoc)
|
80
|
+
if @chunked_sub_doc.nil?
|
81
|
+
create_chunked_subdoc
|
82
|
+
@subdoc_begin_proc.call unless @subdoc_begin_proc.nil?
|
83
|
+
end
|
84
|
+
|
85
|
+
if is_last_part
|
86
|
+
split_idx = chunk.rindex_of_split(boundary_str)
|
87
|
+
|
88
|
+
if split_idx.nil?
|
89
|
+
quoted_matches = chunk.match(/=[0-9A-F\r\n]{0,2}\Z/)
|
90
|
+
|
91
|
+
unless quoted_matches.nil?
|
92
|
+
split_idx = chunk.length - quoted_matches[0].length + 1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
unless split_idx.nil?
|
97
|
+
@split = chunk[split_idx..(chunk.length - 1)]
|
98
|
+
chunk = chunk[0..(split_idx - 1)]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@chunked_sub_doc << chunk
|
103
|
+
|
104
|
+
unless is_last_part && !is_last_subdoc
|
105
|
+
@sub_docs << @chunked_sub_doc
|
106
|
+
@chunked_sub_doc = nil
|
107
|
+
@subdoc_complete_proc.call unless @subdoc_complete_proc.nil?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def create_chunked_subdoc
|
112
|
+
@chunked_sub_doc = Document.new
|
113
|
+
|
114
|
+
@chunked_sub_doc.on_header do |header|
|
115
|
+
@subdoc_header_proc.call(header) unless @subdoc_header_proc.nil?
|
116
|
+
end
|
117
|
+
|
118
|
+
@chunked_sub_doc.on_body do |body|
|
119
|
+
@subdoc_body_proc.call(body) unless @subdoc_body_proc.nil?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/mhtml.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
module Mhtml
|
3
|
+
LINE_BREAK = "\r\n".freeze
|
4
|
+
DOUBLE_LINE_BREAK = "#{LINE_BREAK}#{LINE_BREAK}".freeze
|
5
|
+
STATUS_LINE = "HTTP/1.1 200 OK#{LINE_BREAK}".freeze
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'mhtml/document'
|
9
|
+
require 'mhtml/http_header'
|
10
|
+
require 'mhtml/root_document'
|
11
|
+
require 'mhtml/version'
|
data/lib/string.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
def each_index(x)
|
4
|
+
raise 'Block required' unless block_given?
|
5
|
+
return if empty? || x.nil?
|
6
|
+
|
7
|
+
i = 0
|
8
|
+
while true
|
9
|
+
i = index(x, i)
|
10
|
+
return if i.nil?
|
11
|
+
|
12
|
+
yield i
|
13
|
+
i += 1
|
14
|
+
|
15
|
+
return if i + 1 == length
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def strip_other(str)
|
20
|
+
start_i = 0
|
21
|
+
new_length = length
|
22
|
+
|
23
|
+
if start_with?(str)
|
24
|
+
start_i += str.length
|
25
|
+
new_length -= str.length
|
26
|
+
end
|
27
|
+
|
28
|
+
if end_with?(str)
|
29
|
+
new_length -= str.length
|
30
|
+
end
|
31
|
+
|
32
|
+
self[start_i, new_length]
|
33
|
+
end
|
34
|
+
|
35
|
+
def underscore
|
36
|
+
self.gsub(/::/, '/').
|
37
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
38
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
39
|
+
tr("-", "_").
|
40
|
+
downcase
|
41
|
+
end
|
42
|
+
|
43
|
+
def index_of_split(other)
|
44
|
+
last_idx = (other.length - 1)
|
45
|
+
|
46
|
+
(0..last_idx).step do |i|
|
47
|
+
part = other[i..last_idx]
|
48
|
+
return part.length - 1 if start_with?(part)
|
49
|
+
end
|
50
|
+
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def rindex_of_split(other)
|
55
|
+
last_idx = (other.length - 1)
|
56
|
+
|
57
|
+
(0..last_idx).step do |i|
|
58
|
+
part = other[0..(last_idx - i)]
|
59
|
+
return length - part.length if end_with?(part)
|
60
|
+
end
|
61
|
+
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
data/mhtml.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'mhtml/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'mhtml'
|
7
|
+
spec.version = Mhtml::VERSION
|
8
|
+
spec.authors = [ 'Ben Williams' ]
|
9
|
+
spec.email = [ '8enwilliams@gmail.com' ]
|
10
|
+
|
11
|
+
spec.summary = 'A Ruby gem for reading and extracting MHTML files'
|
12
|
+
spec.description = 'A Ruby gem for reading and extracting MHTML files'
|
13
|
+
spec.homepage = 'https://github.com/benjineering/mhtml_rb'
|
14
|
+
spec.licenses = [ 'MIT', 'GPL-2' ]
|
15
|
+
|
16
|
+
spec.metadata[ 'allowed_push_host' ] = 'https://rubygems.org'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
|
22
|
+
spec.require_paths = [ 'lib' ]
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
+
spec.add_development_dependency 'byebug'
|
28
|
+
|
29
|
+
spec.add_dependency 'http-parser'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mhtml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-09-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: http-parser
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: A Ruby gem for reading and extracting MHTML files
|
84
|
+
email:
|
85
|
+
- 8enwilliams@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- Gemfile
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- bin/console
|
97
|
+
- bin/setup
|
98
|
+
- lib/mhtml.rb
|
99
|
+
- lib/mhtml/document.rb
|
100
|
+
- lib/mhtml/http_header.rb
|
101
|
+
- lib/mhtml/root_document.rb
|
102
|
+
- lib/mhtml/version.rb
|
103
|
+
- lib/string.rb
|
104
|
+
- mhtml.gemspec
|
105
|
+
homepage: https://github.com/benjineering/mhtml_rb
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
- GPL-2
|
109
|
+
metadata:
|
110
|
+
allowed_push_host: https://rubygems.org
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.6.11
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: A Ruby gem for reading and extracting MHTML files
|
131
|
+
test_files: []
|