podgraph 0.0.3 → 0.0.5
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.
- data/NEWS +25 -3
- data/README.rdoc +20 -20
- data/Rakefile +19 -16
- data/TODO +3 -5
- data/bin/podgraph +32 -51
- data/lib/podgraph/meta.rb +9 -0
- data/lib/podgraph/posterous.rb +20 -28
- data/lib/podgraph/trestle.rb +216 -0
- data/test/.document +0 -0
- data/test/helper.rb +3 -0
- data/test/helper_trestle.rb +37 -0
- data/test/rake_git.rb +36 -0
- data/test/simple.html +2 -1
- data/test/{ts_mime.rb → test_mime.rb} +30 -29
- metadata +37 -46
data/NEWS
CHANGED
@@ -1,12 +1,34 @@
|
|
1
1
|
-*- text -*-
|
2
2
|
|
3
|
+
0.0.5
|
4
|
+
-----
|
5
|
+
|
6
|
+
Wed Apr 20 21:58:27 EEST 2011
|
7
|
+
|
8
|
+
- If no input file is specified, read stdin.
|
9
|
+
|
10
|
+
- Repackaged under RVM in fedora.
|
11
|
+
|
12
|
+
0.0.4
|
13
|
+
-----
|
14
|
+
|
15
|
+
Tue Apr 19 19:01:14 EEST 2011
|
16
|
+
|
17
|
+
- Fixed one RE to support https:// images.
|
18
|
+
|
19
|
+
- Dropped activesupport dependency.
|
20
|
+
|
21
|
+
- Injected falsework's naive template.
|
22
|
+
|
3
23
|
0.0.3
|
24
|
+
-----
|
4
25
|
|
5
|
-
-
|
6
|
-
requires i18n (
|
7
|
-
|
26
|
+
- added missing activesupport dependency (btw, activesupport 3.0.0
|
27
|
+
requires i18n (run 'gem install i18n', for some reason this dependency
|
28
|
+
isn't listed in the gem).
|
8
29
|
|
9
30
|
0.0.2
|
31
|
+
-----
|
10
32
|
|
11
33
|
- nailed the mail gem to explicit version 2.1.3, because the current
|
12
34
|
2.2.0 introduced some weird bugs.
|
data/README.rdoc
CHANGED
@@ -1,41 +1,41 @@
|
|
1
|
-
-*- text -*-
|
2
1
|
= Name
|
3
2
|
|
4
|
-
podgraph -- Create an email from
|
5
|
-
|
3
|
+
podgraph -- Create an email from XHTML + inline images and send it to
|
4
|
+
posterous.com.
|
6
5
|
|
7
6
|
= Synopsis
|
8
7
|
|
9
|
-
podgraph [-Sv] [-m ARG] file.html
|
8
|
+
podgraph [-Sv] [-m ARG] [file.html]
|
10
9
|
|
11
10
|
= Description
|
12
11
|
|
13
|
-
It
|
14
|
-
from the file, constructs an email and delivers it
|
12
|
+
It is a simple program that reads a XHTML file (or stdin), extracts
|
13
|
+
subject and body from the file, constructs an email and delivers it to
|
14
|
+
local MTA.
|
15
15
|
|
16
16
|
The options are as follows:
|
17
17
|
|
18
18
|
-v Be more verbose.
|
19
19
|
-S Don't send, just dump the mail to stdout.
|
20
|
-
-m ARG 1 of modes:
|
20
|
+
-m ARG 1 of modes: related, mixed.
|
21
21
|
|
22
|
-
The file is supposed to be a XHTML
|
22
|
+
The file is supposed to be a XHTML generated by python's docutils from
|
23
23
|
reStructuredText. The idea is:
|
24
24
|
|
25
|
-
1.
|
26
|
-
|
25
|
+
1. Write a post (for example, for posterous.com) in reStructuredText in
|
26
|
+
Emacs.
|
27
27
|
|
28
|
-
2.
|
28
|
+
2. Convert it with help of docutils to the XHTML.
|
29
29
|
|
30
|
-
3. Finally,
|
31
|
-
|
30
|
+
3. Finally, create a proper MIME mail from the raw XHTML and send it to
|
31
|
+
posterous.com.
|
32
32
|
|
33
|
-
Step 3 is what podgraph does
|
33
|
+
<em>Step 3 is what podgraph does.</em>
|
34
34
|
|
35
35
|
== Features
|
36
36
|
|
37
|
-
*
|
38
|
-
|
37
|
+
* Generates mail with inline images if it can found links to local
|
38
|
+
images in the XHTML file.
|
39
39
|
|
40
40
|
* Charset of 'text/html' portion of a mail is always UTF-8.
|
41
41
|
|
@@ -46,7 +46,7 @@ or >= 1 if en error occurs.
|
|
46
46
|
|
47
47
|
= Examples
|
48
48
|
|
49
|
-
Create a config.yaml file in the directory with your
|
49
|
+
Create a config.yaml file in the directory with your writings. For
|
50
50
|
example:
|
51
51
|
|
52
52
|
% cd ~/lib/writing/posterous
|
@@ -64,7 +64,7 @@ To give podgraph a chance to find a subject for the mail, write .rest
|
|
64
64
|
file as:
|
65
65
|
|
66
66
|
This is a subject
|
67
|
-
|
67
|
+
-----------------
|
68
68
|
|
69
69
|
My usual reStructuredText.
|
70
70
|
|
@@ -86,8 +86,8 @@ Also, you can preview the mail without sending it:
|
|
86
86
|
% podgraph -S 0001.html
|
87
87
|
|
88
88
|
If you have links to local images in your .rest file (and
|
89
|
-
correspondingly in .html) then podgraph will include images in the mail
|
90
|
-
inline images. But if you don't want this and want images to become a
|
89
|
+
correspondingly in .html) then podgraph will include images in the mail
|
90
|
+
as inline images. But if you don't want this and want images to become a
|
91
91
|
typical gallery on posterous.com, run:
|
92
92
|
|
93
93
|
% podgraph -m mixed 0001.html
|
data/Rakefile
CHANGED
@@ -1,39 +1,42 @@
|
|
1
|
-
# -*- ruby -*-
|
2
|
-
|
3
1
|
require 'rake'
|
4
2
|
require 'rake/gempackagetask'
|
5
3
|
require 'rake/clean'
|
6
4
|
require 'rake/rdoctask'
|
7
5
|
require 'rake/testtask'
|
8
6
|
|
7
|
+
require_relative 'lib/podgraph/meta'
|
8
|
+
require_relative 'test/rake_git'
|
9
|
+
|
9
10
|
spec = Gem::Specification.new do |s|
|
10
|
-
s.name =
|
11
|
+
s.name = Podgraph::Meta::NAME
|
12
|
+
s.version = Podgraph::Meta::VERSION
|
11
13
|
s.summary = 'Creates a MIME mail from a XHTML source and delivers it to Posterous.com.'
|
12
|
-
s.
|
13
|
-
s.author =
|
14
|
-
s.email =
|
14
|
+
s.description = 'Adequately scans XHTML for local inline images and appends them to the mail.'
|
15
|
+
s.author = Podgraph::Meta::AUTHOR
|
16
|
+
s.email = Podgraph::Meta::EMAIL
|
17
|
+
s.homepage = Podgraph::Meta::HOMEPAGE
|
15
18
|
s.platform = Gem::Platform::RUBY
|
16
|
-
s.required_ruby_version = '>= 1.9'
|
17
|
-
s.files =
|
18
|
-
s.executables = [
|
19
|
-
|
20
|
-
s.test_files = FileList['test/
|
19
|
+
s.required_ruby_version = '>= 1.9.2'
|
20
|
+
s.files = git_ls('.')
|
21
|
+
s.executables = [s.name]
|
22
|
+
|
23
|
+
s.test_files = FileList['test/test_*.rb']
|
21
24
|
s.rdoc_options << '-m' << 'Podgraph'
|
25
|
+
|
26
|
+
s.add_dependency('mail', '~> 2.2.17')
|
27
|
+
s.add_development_dependency('git', '>= 1.2.5')
|
22
28
|
end
|
23
|
-
spec.add_dependency('mail', '= 2.1.3')
|
24
|
-
spec.add_dependency('activesupport', '>= 3.0.0')
|
25
29
|
|
26
30
|
Rake::GemPackageTask.new(spec).define
|
27
31
|
|
28
|
-
task :
|
32
|
+
task default: [:repackage]
|
29
33
|
|
30
34
|
Rake::RDocTask.new('doc') do |rd|
|
31
35
|
rd.main = "Podgraph"
|
32
|
-
rd.rdoc_dir = 'doc'
|
33
36
|
rd.rdoc_files.include("lib/**/*.rb")
|
34
37
|
end
|
35
38
|
|
36
39
|
Rake::TestTask.new do |t|
|
37
|
-
t.test_files = FileList['test/
|
40
|
+
t.test_files = FileList['test/test_*.rb']
|
38
41
|
t.verbose = true
|
39
42
|
end
|
data/TODO
CHANGED
data/bin/podgraph
CHANGED
@@ -1,76 +1,57 @@
|
|
1
1
|
#!/usr/bin/env ruby19
|
2
2
|
# -*- ruby -*-
|
3
3
|
|
4
|
-
# Copyright (c) 2010 Alexander Gromnitsky <mailto:alexander.gromnitsky@gmail.com>.
|
5
|
-
#
|
6
|
-
# $Id: podgraph 128 2010-04-24 07:16:01Z alex $
|
7
|
-
|
8
4
|
require_relative '../lib/podgraph/posterous'
|
9
5
|
|
10
|
-
$conf = {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
$conf = {}
|
7
|
+
u = Trestle.new $conf
|
8
|
+
|
9
|
+
$conf[:send?] = true
|
10
|
+
$conf[:mailconfig] = 'config.yaml'
|
11
|
+
$conf[:modes] = %w(related mixed)
|
12
|
+
$conf[:mode] = 'related'
|
13
|
+
$conf[:to] = nil
|
14
|
+
$conf[:from] = nil
|
15
|
+
$conf[:banner] = "#{File.basename($0)} [options] [file.html]\nType 'ri #{Podgraph::Meta::NAME.capitalize}' for the help."
|
16
|
+
$conf[:input] = nil
|
19
17
|
|
20
|
-
def
|
18
|
+
def mailconfig_load()
|
21
19
|
begin
|
22
|
-
myconf = YAML.load_file($conf[:
|
20
|
+
myconf = YAML.load_file($conf[:mailconfig])
|
23
21
|
rescue
|
24
|
-
abort("cannot parse #{$conf[:
|
22
|
+
abort("cannot parse #{$conf[:mailconfig]} in the current directory")
|
25
23
|
end
|
26
24
|
%w(to from).each { |i|
|
27
|
-
|
25
|
+
Trestle.errx(1, "missing #{i} in #{$conf[:mailconfig]}") if ! myconf.key?(i.to_sym)
|
28
26
|
}
|
29
27
|
$conf.merge!(myconf)
|
30
28
|
end
|
31
29
|
|
32
|
-
def cl_parse(myargs)
|
33
|
-
o = OptionParser.new()
|
34
|
-
o.banner = "#{File.basename($PROGRAM_NAME)} #{$conf[:prog_ver]}.
|
35
|
-
Create an email from a XHTML and inline images and send it to posterous.com.
|
36
|
-
http://podgraph.posterous.com/
|
37
30
|
|
38
|
-
|
39
|
-
o.separator ""
|
40
|
-
o.on('-v', 'Be more verbose.') { |v| Podgraph::cfg[:verbose] += 1 }
|
41
|
-
o.on('-S', "Don't send, just dump the mail to stdout.") { |v| $conf[:send?] = false }
|
42
|
-
o.on('-m ARG', "1 of modes: Podgraph::#{$conf[:modes]}.",
|
43
|
-
$conf[:modes]) { |v| $conf[:mode] = v }
|
31
|
+
# ---
|
44
32
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
config_load()
|
33
|
+
u.config_parse(['foobar']) {|src|
|
34
|
+
o = u.cl_parse(src) # create an OptionParser object
|
35
|
+
o.on('-c ARG', "Use another configuration file instead of",
|
36
|
+
$conf[:mailconfig]) { |v| $conf[:mailconfig] = v }
|
37
|
+
o.on('-S', "Don't send, just dump the mail to stdout") { |v| $conf[:send?] = false }
|
38
|
+
o.on('-m ARG', "Select mode: #{$conf[:modes].join(', ')}") { |v| $conf[:mode] = v }
|
39
|
+
u.cl_parse(src, o) # run cl parser
|
40
|
+
}
|
55
41
|
|
56
|
-
|
57
|
-
|
58
|
-
unless ARGV.size >= 1
|
59
|
-
abort("Usage: #{File.basename($PROGRAM_NAME)} [options] filename.html
|
60
|
-
Type \"#{File.basename($PROGRAM_NAME)} -h\" for the help.")
|
61
|
-
end
|
42
|
+
ARGV.size < 1 ? $conf[:input] = STDIN : $conf[:input] = ARGV[0]
|
43
|
+
mailconfig_load
|
62
44
|
|
63
|
-
Podgraph::veputs(2, "cfg #{Podgraph::cfg}")
|
64
45
|
begin
|
65
|
-
p = Podgraph::Posterous.new(
|
66
|
-
|
67
|
-
mail = p.generate
|
46
|
+
p = Podgraph::Posterous.new(u, $conf[:input], $conf[:to], $conf[:from], $conf[:mode])
|
47
|
+
u.veputs(2, "o: #{p.o}".to_s.encode('koi8-u'))
|
48
|
+
mail = p.generate
|
68
49
|
rescue
|
69
|
-
|
50
|
+
Trestle.errx(1, "HTML parsing failed: #{$!}")
|
70
51
|
end
|
71
52
|
|
72
53
|
if ! $conf[:send?]
|
73
|
-
puts mail.to_s
|
54
|
+
puts mail.to_s
|
74
55
|
exit 0
|
75
56
|
end
|
76
57
|
|
@@ -78,5 +59,5 @@ begin
|
|
78
59
|
mail.delivery_method :sendmail
|
79
60
|
mail.deliver
|
80
61
|
rescue
|
81
|
-
|
62
|
+
Trestle.errx(1, "cannot send mail: #{$!}")
|
82
63
|
end
|
data/lib/podgraph/posterous.rb
CHANGED
@@ -1,26 +1,13 @@
|
|
1
|
-
# $Id: posterous.rb 128 2010-04-24 07:16:01Z alex $
|
2
|
-
|
3
|
-
gem 'mail', '= 2.1.3'
|
4
1
|
require 'mail'
|
5
|
-
require 'active_support/core_ext/module/attribute_accessors'
|
6
|
-
|
7
2
|
require 'rexml/document'
|
8
3
|
require 'yaml'
|
9
4
|
require 'optparse'
|
10
5
|
|
6
|
+
require_relative 'trestle'
|
7
|
+
include Podgraph
|
8
|
+
|
11
9
|
# :include: ../../README.rdoc
|
12
10
|
module Podgraph
|
13
|
-
|
14
|
-
VERSION = '0.0.3'
|
15
|
-
mattr_accessor :cfg
|
16
|
-
|
17
|
-
self.cfg = Hash.new()
|
18
|
-
cfg[:verbose] = 0
|
19
|
-
|
20
|
-
def self.veputs(level, s)
|
21
|
-
puts(s) if cfg[:verbose] >= level
|
22
|
-
end
|
23
|
-
|
24
11
|
# Reads XHTML file, analyses it, finds images, checks if they can be inlined,
|
25
12
|
# generates multipart/relative or multipart/mixed MIME mail.
|
26
13
|
class Posterous
|
@@ -28,12 +15,15 @@ module Podgraph
|
|
28
15
|
# some options for mail generator; change with care
|
29
16
|
attr_accessor :o
|
30
17
|
|
18
|
+
# a Trestle object
|
19
|
+
attr_accessor :trestle
|
20
|
+
|
31
21
|
# Analyses _filename_. It must be a XHTML file.
|
32
22
|
# _to_, _from_ are email.
|
33
23
|
# _mode_ is 1 of 'related' or 'mixed' string.
|
34
|
-
def initialize(filename, to, from, mode)
|
24
|
+
def initialize(trestle, filename, to, from, mode)
|
35
25
|
@o = Hash.new()
|
36
|
-
@o[:user_agent] =
|
26
|
+
@o[:user_agent] = Podgraph::Meta::NAME + ?/ + Podgraph::Meta::VERSION
|
37
27
|
@o[:subject] = ''
|
38
28
|
@o[:body] = []
|
39
29
|
@o[:attachment] = []
|
@@ -42,13 +32,15 @@ module Podgraph
|
|
42
32
|
@o[:to] = to
|
43
33
|
@o[:from] = from
|
44
34
|
|
45
|
-
|
35
|
+
@trestle = trestle
|
36
|
+
|
37
|
+
fp = (filename == STDIN ? STDIN : File.new(filename))
|
46
38
|
begin
|
47
39
|
make(fp)
|
48
40
|
rescue
|
49
41
|
raise $!
|
50
42
|
ensure
|
51
|
-
fp.close
|
43
|
+
fp.close unless fp == STDIN
|
52
44
|
end
|
53
45
|
end
|
54
46
|
|
@@ -65,7 +57,7 @@ module Podgraph
|
|
65
57
|
if i.name == 'img'
|
66
58
|
if (src = i.attributes['src']) =~ /^\s*$/
|
67
59
|
raise '<img> tag with missing or empty src attribute'
|
68
|
-
elsif src =~ /\s*(
|
60
|
+
elsif src =~ /\s*(https?|s?ftp):\/\//
|
69
61
|
# we are ignoring URL's
|
70
62
|
return
|
71
63
|
else
|
@@ -77,7 +69,7 @@ module Podgraph
|
|
77
69
|
i.attributes['src'] = random
|
78
70
|
@o[:a_marks][src] = random # save an act of the replacement
|
79
71
|
|
80
|
-
@o.rehash() #
|
72
|
+
@o.rehash() # is this really necessary?
|
81
73
|
end
|
82
74
|
end
|
83
75
|
end
|
@@ -90,10 +82,10 @@ module Podgraph
|
|
90
82
|
next
|
91
83
|
end
|
92
84
|
|
93
|
-
|
85
|
+
@trestle.veputs(2, "node: #{i.name}")
|
94
86
|
img_collect.call(i, @o[:attachment])
|
95
87
|
i.each_recursive { |j|
|
96
|
-
|
88
|
+
@trestle.veputs(2, "node recursive: #{j.name}")
|
97
89
|
img_collect.call(j, @o[:attachment])
|
98
90
|
}
|
99
91
|
|
@@ -109,11 +101,12 @@ module Podgraph
|
|
109
101
|
m = Mail.new()
|
110
102
|
m.from(@o[:from])
|
111
103
|
m.to(@o[:to])
|
112
|
-
m.
|
104
|
+
m.transport_encoding = Mail::Encodings.get_encoding('8bit')
|
105
|
+
# m.content_transfer_encoding('8bit')
|
113
106
|
m.subject(@o[:subject])
|
114
107
|
m.headers({'User-Agent' => @o[:user_agent]})
|
115
108
|
|
116
|
-
|
109
|
+
@trestle.veputs(2, "Body lines=#{@o[:body].size}, bytes=#{@o[:body].to_s.bytesize}")
|
117
110
|
if @o[:attachment].size == 0
|
118
111
|
m.content_disposition('inline')
|
119
112
|
m.content_type('text/html; charset="UTF-8"')
|
@@ -123,7 +116,6 @@ module Podgraph
|
|
123
116
|
m.content_type('Multipart/Related')
|
124
117
|
end
|
125
118
|
m.html_part = Mail::Part.new {
|
126
|
-
content_transfer_encoding('8bit')
|
127
119
|
content_type('text/html; charset=UTF-8')
|
128
120
|
}
|
129
121
|
m.html_part.body = @o[:body]
|
@@ -147,7 +139,7 @@ module Podgraph
|
|
147
139
|
|
148
140
|
@o[:a_marks].each { |k, v|
|
149
141
|
if cid.key?(k)
|
150
|
-
|
142
|
+
@trestle.veputs(2, "mark #{k} = #{v}; -> to #{cid[k]}")
|
151
143
|
# replace marks with corresponding content-id
|
152
144
|
m.html_part.body.raw_source.sub!(v, "cid:#{cid[k][1..-1]}")
|
153
145
|
else
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# :erb:
|
2
|
+
require 'yaml'
|
3
|
+
require 'shellwords.rb'
|
4
|
+
require 'optparse'
|
5
|
+
require 'pp'
|
6
|
+
require 'open4'
|
7
|
+
|
8
|
+
require_relative 'meta'
|
9
|
+
|
10
|
+
# :include: ../../README.rdoc
|
11
|
+
module Podgraph
|
12
|
+
|
13
|
+
class Trestle
|
14
|
+
|
15
|
+
# Execute _cmd_ and return a list [exit_status, stderr,
|
16
|
+
# stdout]. Very handy.
|
17
|
+
def self.cmd_run(cmd)
|
18
|
+
so = sr = ''
|
19
|
+
status = Open4::popen4(cmd) { |pid, stdin, stdout, stderr|
|
20
|
+
so = stdout.read
|
21
|
+
sr = stderr.read
|
22
|
+
}
|
23
|
+
[status.exitstatus, sr, so]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return a directory with program libraries.
|
27
|
+
def self.gem_libdir
|
28
|
+
t = ["#{File.dirname(File.expand_path($0))}/../lib/#{Podgraph::Meta::NAME}",
|
29
|
+
"#{Gem.dir}/gems/#{Podgraph::Meta::NAME}-#{Podgraph::Meta::VERSION}/lib/#{Podgraph::Meta::NAME}",
|
30
|
+
"lib/#{Podgraph::Meta::NAME}"]
|
31
|
+
t.each {|i| return i if File.readable?(i) }
|
32
|
+
fail "all paths are invalid: #{t}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Analogue to shell command +which+.
|
36
|
+
def self.in_path?(file)
|
37
|
+
return true if file =~ %r%\A/% and File.exist? file
|
38
|
+
|
39
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path|
|
40
|
+
File.exist? File.join(path, file)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Print an error message _t_ and exit if _ec_ > 0.
|
45
|
+
def self.errx(ec, t)
|
46
|
+
STDERR.puts File.basename($0) + ' error: ' + t.to_s
|
47
|
+
exit ec if ec > 0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Print a warning.
|
51
|
+
def self.warnx(t)
|
52
|
+
STDERR.puts File.basename($0) + ' warning: ' + t.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
# #veputs uses this to decide to put a newline or not to put.
|
56
|
+
NNL_MARK = '__NNL__'
|
57
|
+
|
58
|
+
# Use this in your CL options to check if modifying some variable is
|
59
|
+
# not an idempotent act.
|
60
|
+
attr_reader :cl_opt_protect
|
61
|
+
|
62
|
+
# [conf] Typically must be a reference to some global variable.
|
63
|
+
def initialize(conf)
|
64
|
+
@conf = conf
|
65
|
+
@conf[:verbose] = 0
|
66
|
+
@conf[:banner] = "Usage: #{File.basename($0)} [options]"
|
67
|
+
@conf[:config] = Meta::NAME + '.yaml'
|
68
|
+
@conf[:config_dirs] = [ENV['HOME']+'/.'+Meta::NAME,
|
69
|
+
File.absolute_path("#{File.dirname(File.expand_path($0))}/../etc"),
|
70
|
+
'/usr/etc', '/usr/local/etc', '/etc',
|
71
|
+
"#{Gem.dir}/gems/#{Meta::NAME}-#{Meta::VERSION}/etc"
|
72
|
+
]
|
73
|
+
@conf[:config_env] = [Meta::NAME.upcase + '_CONF']
|
74
|
+
|
75
|
+
@cl_parsing_times = 0 # not used
|
76
|
+
@cl_opt_protect = false
|
77
|
+
end
|
78
|
+
|
79
|
+
# [level] A verbose level.
|
80
|
+
# [t] A string to print.
|
81
|
+
#
|
82
|
+
# Don't print _t_ with a newline if it contains NNL_MARK at the end.
|
83
|
+
def veputs(level, t)
|
84
|
+
t = t.dup
|
85
|
+
nnl = nil
|
86
|
+
if t.match(/#{NNL_MARK}$/)
|
87
|
+
t.sub!(/#{$&}/, '')
|
88
|
+
nnl = 1
|
89
|
+
end
|
90
|
+
|
91
|
+
if @conf[:verbose] >= level
|
92
|
+
nnl ? print(t) : puts(t)
|
93
|
+
STDOUT.flush
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Run all configuration parsing in a batch.
|
98
|
+
#
|
99
|
+
# [rvars] A list of variable names which must be in the
|
100
|
+
# configuration file.
|
101
|
+
#
|
102
|
+
# If no block is given, only standard CL options will be analysed.
|
103
|
+
def config_parse(rvars, &block)
|
104
|
+
cb = ->(b, src) {
|
105
|
+
if b
|
106
|
+
block.call src
|
107
|
+
else
|
108
|
+
# very basic default options
|
109
|
+
cl_parse(src, nil, true)
|
110
|
+
end
|
111
|
+
}
|
112
|
+
|
113
|
+
# 1. parse env
|
114
|
+
@conf[:config_env].each {|i|
|
115
|
+
# puts '0 run:'
|
116
|
+
cb.call(block_given?, ENV[i].shellsplit) if ENV.key?(i)
|
117
|
+
}
|
118
|
+
|
119
|
+
# 2. parse CL in case of '--config' option
|
120
|
+
# puts "\n1 run"
|
121
|
+
@cl_opt_protect = true
|
122
|
+
cb.call(block_given?, ARGV.dup)
|
123
|
+
@cl_opt_protect = false
|
124
|
+
|
125
|
+
# 3. load the configuration file & do the final CL parsing
|
126
|
+
begin
|
127
|
+
# puts "\n2 run"
|
128
|
+
r = config_flat_load(rvars)
|
129
|
+
rescue
|
130
|
+
Trestle.errx(1, "cannot load config: #{$!}")
|
131
|
+
end
|
132
|
+
veputs(1, "Loaded config: #{r}")
|
133
|
+
cb.call(block_given?, ARGV)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Load a config file immediately if it contains '/' in its name,
|
137
|
+
# otherwise search through several dirs for it.
|
138
|
+
#
|
139
|
+
# [rvars] a list of requied variables in the config
|
140
|
+
#
|
141
|
+
# Return a loaded filename or nil on error.
|
142
|
+
def config_flat_load(rvars)
|
143
|
+
p = ->(f) {
|
144
|
+
if File.readable?(f)
|
145
|
+
begin
|
146
|
+
myconf = YAML.load_file(f)
|
147
|
+
rescue
|
148
|
+
abort("cannot parse #{f}: #{$!}")
|
149
|
+
end
|
150
|
+
rvars.each { |i|
|
151
|
+
fail "missing or nil '#{i}' in #{f}" if ! myconf.key?(i.to_sym) || ! myconf[i.to_sym]
|
152
|
+
}
|
153
|
+
@conf.merge!(myconf)
|
154
|
+
return @conf[:config]
|
155
|
+
end
|
156
|
+
return nil
|
157
|
+
}
|
158
|
+
|
159
|
+
if @conf[:config].index('/')
|
160
|
+
return p.call(@config[:config])
|
161
|
+
else
|
162
|
+
@conf[:config_dirs].each {|dir|
|
163
|
+
return dir+'/'+@conf[:config] if p.call(dir + '/' + @conf[:config])
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
return nil
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
# Parses CL-like options.
|
172
|
+
#
|
173
|
+
# [src] An array of options (usually +ARGV+).
|
174
|
+
#
|
175
|
+
# If _o_ is non nil function parses _src_ immediately, otherwise it
|
176
|
+
# only creates +OptionParser+ object and return it (if _simple_ is
|
177
|
+
# false).
|
178
|
+
def cl_parse(src, o = nil, simple = false)
|
179
|
+
if ! o then
|
180
|
+
# puts "NEW o (#{cl_opt_protect})" + src.to_s
|
181
|
+
o = OptionParser.new
|
182
|
+
o.banner = @conf[:banner]
|
183
|
+
o.on('-v', 'Be more verbose.') { |i|
|
184
|
+
# puts "cl_parsing_times "+cl_parsing_times.to_s
|
185
|
+
@conf[:verbose] += 1 unless cl_opt_protect
|
186
|
+
}
|
187
|
+
o.on('-V', 'Show version & exit.') { |i|
|
188
|
+
puts Meta::VERSION
|
189
|
+
exit 0
|
190
|
+
}
|
191
|
+
o.on('--config NAME', "Set a config name (default is #{@conf[:config]})") {|i|
|
192
|
+
@conf[:config] = i
|
193
|
+
}
|
194
|
+
o.on('--config-dirs', 'Show possible config locations') {
|
195
|
+
@conf[:config_dirs].each { |j|
|
196
|
+
f = j + '/' + @conf[:config]
|
197
|
+
puts (File.readable?(f) ? '* ' : ' ') + f
|
198
|
+
}
|
199
|
+
exit 0
|
200
|
+
}
|
201
|
+
|
202
|
+
return o if ! simple
|
203
|
+
end
|
204
|
+
|
205
|
+
begin
|
206
|
+
o.parse!(src)
|
207
|
+
@cl_parsing_times += 1
|
208
|
+
rescue
|
209
|
+
Trestle.errx(1, $!.to_s)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
end # trestle
|
214
|
+
end
|
215
|
+
|
216
|
+
# Don't remove this: falsework/0.2.2/naive/2010-12-26T04:50:00+02:00
|
data/test/.document
ADDED
File without changes
|
data/test/helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# :erb:
|
2
|
+
# Various staff for minitest. Include this file into your 'helper.rb'.
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
include FileUtils
|
6
|
+
|
7
|
+
require_relative '../lib/podgraph/trestle'
|
8
|
+
include Podgraph
|
9
|
+
|
10
|
+
# don't run tests automatically if they were invoked as 'gem check -t ...'
|
11
|
+
if $0 =~ /gem/
|
12
|
+
require 'minitest/unit'
|
13
|
+
else
|
14
|
+
require 'minitest/autorun'
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return the right directory for (probably executable) _c_.
|
18
|
+
def cmd(c)
|
19
|
+
case File.basename(Dir.pwd)
|
20
|
+
when Meta::NAME.downcase
|
21
|
+
# test probably is executed from the Rakefile
|
22
|
+
Dir.chdir('test')
|
23
|
+
when 'test'
|
24
|
+
# we are in the test directory, there is nothing special to do
|
25
|
+
else
|
26
|
+
# tests were invoked by 'gem check -t podgraph'
|
27
|
+
begin
|
28
|
+
Dir.chdir(Trestle.gem_libdir + '/../../test')
|
29
|
+
rescue
|
30
|
+
raise "running tests from '#{Dir.pwd}' isn't supported: #{$!}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
'../bin/' + c
|
35
|
+
end
|
36
|
+
|
37
|
+
# Don't remove this: falsework/0.2.2/naive/2010-12-26T04:50:00+02:00
|
data/test/rake_git.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*-ruby-*-
|
3
|
+
# :erb:
|
4
|
+
|
5
|
+
# This is a helper for your Rakefile. Read the comments for each
|
6
|
+
# function.
|
7
|
+
|
8
|
+
require 'git'
|
9
|
+
require 'pp'
|
10
|
+
|
11
|
+
# Return a list of files in a git repository _repdir_.
|
12
|
+
#
|
13
|
+
# Add this to your gem spec:
|
14
|
+
#
|
15
|
+
# spec = Gem::Specification.new {|i|
|
16
|
+
# i.files = git_ls('.')
|
17
|
+
# }
|
18
|
+
#
|
19
|
+
# What it does is just collecting the list of the files from the git
|
20
|
+
# repository. The idea is to use that list for the gem spec. No more
|
21
|
+
# missing or redundant files in gems!
|
22
|
+
def git_ls(repdir, ignore_some = true)
|
23
|
+
ignore = ['/?\.gitignore$']
|
24
|
+
|
25
|
+
r = []
|
26
|
+
g = Git.open repdir
|
27
|
+
g.ls_files.each {|i, v|
|
28
|
+
next if ignore_some && ignore.index {|ign| i.match(/#{ign}/) }
|
29
|
+
r << i
|
30
|
+
}
|
31
|
+
r
|
32
|
+
end
|
33
|
+
|
34
|
+
pp git_ls('.') if __FILE__ == $0
|
35
|
+
|
36
|
+
# Don't remove this: falsework/0.2.2/naive/2010-12-26T04:50:00+02:00
|
data/test/simple.html
CHANGED
@@ -1,65 +1,65 @@
|
|
1
|
-
|
2
|
-
require 'digest/md5'
|
3
|
-
|
1
|
+
require_relative 'helper'
|
4
2
|
require_relative '../lib/podgraph/posterous'
|
5
3
|
|
6
|
-
class TestMime <
|
4
|
+
class TestMime < MiniTest::Unit::TestCase
|
7
5
|
def setup
|
8
6
|
@testdir = File.expand_path(File.dirname(__FILE__))
|
9
7
|
@to = 'alex@goliard'
|
10
8
|
@from = 'alexander.gromnitsky@gmail.com'
|
9
|
+
@conf = {}
|
10
|
+
@u = Trestle.new @conf
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_broken_subject
|
14
|
-
e =
|
15
|
-
p = Podgraph::Posterous.new(@testdir+'/nosubject.html', @to,
|
14
|
+
e = assert_raises(RuntimeError) {
|
15
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/nosubject.html', @to,
|
16
16
|
@from, 'related')
|
17
17
|
}
|
18
18
|
assert_match(/cannot extract the subject/, e.message)
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_missing_input_file
|
22
|
-
e =
|
23
|
-
p = Podgraph::Posterous.new('something_completly_unreliable.html',
|
22
|
+
e = assert_raises(Errno::ENOENT) {
|
23
|
+
p = Podgraph::Posterous.new(@u, 'something_completly_unreliable.html',
|
24
24
|
@to, @from, 'related')
|
25
25
|
}
|
26
26
|
assert_match(/no such file or directory/i, e.message)
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_empty_input_file
|
30
|
-
e =
|
31
|
-
p = Podgraph::Posterous.new(@testdir+'/empty.html',
|
30
|
+
e = assert_raises(RuntimeError) {
|
31
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/empty.html',
|
32
32
|
@to, @from, 'related')
|
33
33
|
}
|
34
34
|
assert_match(/cannot extract the subject/, e.message)
|
35
35
|
end
|
36
36
|
|
37
37
|
def test_invalid_input_file_01
|
38
|
-
e =
|
39
|
-
p = Podgraph::Posterous.new(@testdir+'/garbage_01.html',
|
38
|
+
e = assert_raises(RuntimeError) {
|
39
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/garbage_01.html',
|
40
40
|
@to, @from, 'related')
|
41
41
|
}
|
42
42
|
assert_match(/cannot extract the subject/, e.message)
|
43
43
|
end
|
44
44
|
|
45
45
|
def test_invalid_input_file_02
|
46
|
-
|
47
|
-
p = Podgraph::Posterous.new(@testdir+'/garbage_02.html',
|
46
|
+
assert_raises(REXML::ParseException) {
|
47
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/garbage_02.html',
|
48
48
|
@to, @from, 'related')
|
49
49
|
}
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_invalid_input_file_03
|
53
|
-
e =
|
54
|
-
p = Podgraph::Posterous.new(@testdir+'/garbage_03.html',
|
53
|
+
e = assert_raises(RuntimeError) {
|
54
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/garbage_03.html',
|
55
55
|
@to, @from, 'related')
|
56
56
|
}
|
57
57
|
assert_match(/body is empty or filled with nonsence/, e.message)
|
58
58
|
end
|
59
59
|
|
60
60
|
def test_invalid_input_file_04
|
61
|
-
e =
|
62
|
-
p = Podgraph::Posterous.new(@testdir+'/garbage_04.html',
|
61
|
+
e = assert_raises(RuntimeError) {
|
62
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/garbage_04.html',
|
63
63
|
@to, @from, 'related')
|
64
64
|
mail = p.generate()
|
65
65
|
}
|
@@ -69,25 +69,26 @@ class TestMime < Test::Unit::TestCase
|
|
69
69
|
|
70
70
|
# NOTE: run
|
71
71
|
#
|
72
|
-
#
|
72
|
+
# ../bin/podgraph -S simple.html | grep -v -e ^To: -e '^Date:' -e ^Message-ID: -e '^user-agent:'|md5
|
73
73
|
#
|
74
|
-
# to get a new hash for this test
|
74
|
+
# to get a new hash for this test.
|
75
75
|
def test_simple
|
76
|
-
p = Podgraph::Posterous.new(@testdir+'/simple.html', @to, @from, 'related')
|
77
|
-
mail = p.generate
|
76
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/simple.html', @to, @from, 'related')
|
77
|
+
mail = p.generate.to_s
|
78
78
|
mail.sub!(/^Date: .+$\n/, '')
|
79
|
+
mail.sub!(/^To: .+$\n/, '')
|
79
80
|
mail.sub!(/^Message-ID: .+$\n/, '')
|
80
81
|
mail.sub!(/^user-agent: .+$\n/, '')
|
81
82
|
# p mail
|
82
83
|
# puts mail
|
83
|
-
assert_equal('
|
84
|
-
Digest::MD5.hexdigest("
|
84
|
+
assert_equal('c7ac3a5797496efd323c7017c4516cb1',
|
85
|
+
Digest::MD5.hexdigest(mail + "\n"))
|
85
86
|
end
|
86
87
|
|
87
88
|
def test_related
|
88
89
|
p = mail = nil
|
89
90
|
Dir.chdir(@testdir) do
|
90
|
-
p = Podgraph::Posterous.new(@testdir+'/related.html', @to, @from, 'related')
|
91
|
+
p = Podgraph::Posterous.new(@u, @testdir+'/related.html', @to, @from, 'related')
|
91
92
|
mail = p.generate()
|
92
93
|
end
|
93
94
|
assert_equal(mail.multipart?, true)
|
@@ -98,7 +99,7 @@ class TestMime < Test::Unit::TestCase
|
|
98
99
|
|
99
100
|
# html
|
100
101
|
assert_equal(mail.parts[0].content_type, "text/html; charset=UTF-8")
|
101
|
-
|
102
|
+
refute_equal(mail.parts[0].content_disposition, "inline")
|
102
103
|
assert_equal(mail.parts[1].content_disposition, "inline")
|
103
104
|
assert_equal(mail.parts[2].content_disposition, "inline")
|
104
105
|
|
@@ -112,7 +113,7 @@ class TestMime < Test::Unit::TestCase
|
|
112
113
|
def test_mixed
|
113
114
|
p = mail = nil
|
114
115
|
Dir.chdir(@testdir) do
|
115
|
-
p = Podgraph::Posterous.new('related.html', @to, @from, 'mixed')
|
116
|
+
p = Podgraph::Posterous.new(@u, 'related.html', @to, @from, 'mixed')
|
116
117
|
mail = p.generate()
|
117
118
|
end
|
118
119
|
assert_equal(mail.multipart?, true)
|
@@ -124,8 +125,8 @@ class TestMime < Test::Unit::TestCase
|
|
124
125
|
# html
|
125
126
|
assert_equal(mail.parts[0].content_type, "text/html; charset=UTF-8")
|
126
127
|
assert_equal(mail.parts[0].content_disposition, "inline")
|
127
|
-
|
128
|
-
|
128
|
+
refute_equal(mail.parts[1].content_disposition, "inline")
|
129
|
+
refute_equal(mail.parts[2].content_disposition, "inline")
|
129
130
|
end
|
130
131
|
|
131
132
|
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: podgraph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 3
|
9
|
-
version: 0.0.3
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.5
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Alexander Gromnitsky
|
@@ -14,38 +10,31 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date:
|
18
|
-
default_executable:
|
13
|
+
date: 2011-04-18 00:00:00 Z
|
19
14
|
dependencies:
|
20
15
|
- !ruby/object:Gem::Dependency
|
21
16
|
name: mail
|
22
17
|
prerelease: false
|
23
18
|
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
24
20
|
requirements:
|
25
|
-
- -
|
21
|
+
- - ~>
|
26
22
|
- !ruby/object:Gem::Version
|
27
|
-
|
28
|
-
- 2
|
29
|
-
- 1
|
30
|
-
- 3
|
31
|
-
version: 2.1.3
|
23
|
+
version: 2.2.17
|
32
24
|
type: :runtime
|
33
25
|
version_requirements: *id001
|
34
26
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
27
|
+
name: git
|
36
28
|
prerelease: false
|
37
29
|
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
38
31
|
requirements:
|
39
32
|
- - ">="
|
40
33
|
- !ruby/object:Gem::Version
|
41
|
-
|
42
|
-
|
43
|
-
- 0
|
44
|
-
- 0
|
45
|
-
version: 3.0.0
|
46
|
-
type: :runtime
|
34
|
+
version: 1.2.5
|
35
|
+
type: :development
|
47
36
|
version_requirements: *id002
|
48
|
-
description:
|
37
|
+
description: Adequately scans XHTML for local inline images and appends them to the mail.
|
49
38
|
email: alexander.gromnitsky@gmail.com
|
50
39
|
executables:
|
51
40
|
- podgraph
|
@@ -54,33 +43,38 @@ extensions: []
|
|
54
43
|
extra_rdoc_files: []
|
55
44
|
|
56
45
|
files:
|
57
|
-
- lib/podgraph/posterous.rb
|
58
|
-
- bin/podgraph
|
59
46
|
- LICENSE
|
60
|
-
-
|
47
|
+
- NEWS
|
61
48
|
- README.rdoc
|
49
|
+
- Rakefile
|
62
50
|
- TODO
|
63
|
-
-
|
51
|
+
- bin/podgraph
|
52
|
+
- lib/podgraph/meta.rb
|
53
|
+
- lib/podgraph/posterous.rb
|
54
|
+
- lib/podgraph/trestle.rb
|
55
|
+
- test/.document
|
56
|
+
- test/blue.png
|
57
|
+
- test/config.yaml
|
58
|
+
- test/empty.html
|
59
|
+
- test/garbage_01.html
|
60
|
+
- test/garbage_02.html
|
61
|
+
- test/garbage_03.html
|
62
|
+
- test/garbage_04.html
|
63
|
+
- test/helper.rb
|
64
|
+
- test/helper_trestle.rb
|
64
65
|
- test/mechanical-turk/1.html
|
65
66
|
- test/mechanical-turk/2.html
|
66
|
-
- test/mechanical-turk/sun.jpg
|
67
67
|
- test/mechanical-turk/3.html
|
68
|
-
- test/mechanical-turk/config.yaml
|
69
68
|
- test/mechanical-turk/Baby-Bunnie.jpg
|
69
|
+
- test/mechanical-turk/config.yaml
|
70
|
+
- test/mechanical-turk/sun.jpg
|
70
71
|
- test/nosubject.html
|
71
|
-
- test/
|
72
|
-
- test/empty.html
|
72
|
+
- test/rake_git.rb
|
73
73
|
- test/related.html
|
74
|
-
- test/config.yaml
|
75
|
-
- test/blue.png
|
76
|
-
- test/garbage_01.html
|
77
|
-
- test/garbage_02.html
|
78
74
|
- test/simple.html
|
79
|
-
- test/
|
80
|
-
- test/garbage_04.html
|
75
|
+
- test/test_mime.rb
|
81
76
|
- test/yellow.png
|
82
|
-
|
83
|
-
homepage:
|
77
|
+
homepage: http://github.com/gromnitsky/podgraph
|
84
78
|
licenses: []
|
85
79
|
|
86
80
|
post_install_message:
|
@@ -90,26 +84,23 @@ rdoc_options:
|
|
90
84
|
require_paths:
|
91
85
|
- lib
|
92
86
|
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
93
88
|
requirements:
|
94
89
|
- - ">="
|
95
90
|
- !ruby/object:Gem::Version
|
96
|
-
|
97
|
-
- 1
|
98
|
-
- 9
|
99
|
-
version: "1.9"
|
91
|
+
version: 1.9.2
|
100
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
101
94
|
requirements:
|
102
95
|
- - ">="
|
103
96
|
- !ruby/object:Gem::Version
|
104
|
-
segments:
|
105
|
-
- 0
|
106
97
|
version: "0"
|
107
98
|
requirements: []
|
108
99
|
|
109
100
|
rubyforge_project:
|
110
|
-
rubygems_version: 1.
|
101
|
+
rubygems_version: 1.7.2
|
111
102
|
signing_key:
|
112
103
|
specification_version: 3
|
113
104
|
summary: Creates a MIME mail from a XHTML source and delivers it to Posterous.com.
|
114
105
|
test_files:
|
115
|
-
- test/
|
106
|
+
- test/test_mime.rb
|