constable 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +39 -8
- data/bin/constable-convert +47 -0
- data/bin/constable-identify +18 -17
- data/bin/constabled +94 -25
- data/lib/constable/version.rb +1 -1
- metadata +5 -4
data/README.md
CHANGED
@@ -8,6 +8,33 @@ anything quite as powerful will be generated using the service, but if you do
|
|
8
8
|
make something nice please let me know!
|
9
9
|
|
10
10
|
|
11
|
+
Up and running fast
|
12
|
+
-------------------
|
13
|
+
|
14
|
+
I've provided a Vagrant setup that will get you up and running fast. Install
|
15
|
+
the necessary gems using bundler:
|
16
|
+
|
17
|
+
cd /path/to/checkout/of/constable
|
18
|
+
bundle
|
19
|
+
|
20
|
+
Ask Vagrant to bring up the server components for you:
|
21
|
+
|
22
|
+
cd /path/to/checkout/of/constable
|
23
|
+
bundle exec vagrant up
|
24
|
+
|
25
|
+
Ask Constable to do some work:
|
26
|
+
|
27
|
+
cd /path/to/checkout/of/constable
|
28
|
+
bundle exec ./bin/constable-identify -- /path/to/input.png -verbose
|
29
|
+
|
30
|
+
That's it, you just used ImageMagick as a service.
|
31
|
+
|
32
|
+
Of course, if you want to use it as a real service on your network you'll
|
33
|
+
probably want to set it up a little differently. See the following sections on
|
34
|
+
Installing and Usage for information on how to setup your own ImageMagick
|
35
|
+
service.
|
36
|
+
|
37
|
+
|
11
38
|
Installing
|
12
39
|
----------
|
13
40
|
|
@@ -31,9 +58,9 @@ and I'll pull your changes.
|
|
31
58
|
|
32
59
|
Run the server, somewhere that has ImageMagick installed:
|
33
60
|
|
34
|
-
constabled --broker=stomp://mq.yourdomain.com:61613
|
61
|
+
$ constabled --broker=stomp://mq.yourdomain.com:61613
|
35
62
|
|
36
|
-
Use the services on the command line. You don't need
|
63
|
+
Use the services on the command line. You don't need to have ImageMagick
|
37
64
|
installed on your client machines, just constable.
|
38
65
|
|
39
66
|
Command names are based on ImageMagick command names, prefixed with
|
@@ -41,16 +68,20 @@ Command names are based on ImageMagick command names, prefixed with
|
|
41
68
|
and will give you a decent explanation of what they do and their options if
|
42
69
|
you ask for it.
|
43
70
|
|
44
|
-
The commands are designed to take their input
|
45
|
-
results on standard output. It's up to you what you do with the
|
46
|
-
|
47
|
-
|
71
|
+
The commands are designed to take their input just like ImageMagick and return
|
72
|
+
results on standard output. It's up to you what you do with the output;
|
73
|
+
write it to a file, pipe it to another process, that's not handled by Constable.
|
74
|
+
A caveat of this is that you must construct your Constable invocations such that
|
75
|
+
ImageMagick would write to standard output rather than a file.
|
48
76
|
|
49
77
|
A brief example of what interacting with Constable looks like, here
|
50
78
|
identifying some image file I had lying around on disk:
|
51
79
|
|
52
|
-
$
|
53
|
-
|
80
|
+
$ constable-identify --broker=stomp://mq.yourdomain.com:61613 -- input_file
|
81
|
+
input_file JPEG 640x480 DirectClass 87kb 0.050u 0:01
|
82
|
+
|
83
|
+
$ constable-convert --broker=stomp://mq.yourdomain.com:61613 -- \
|
84
|
+
-resize 50% input.png jpg:- > output.jpg
|
54
85
|
|
55
86
|
I explicitly state the broker in the above commands but if you leave out that
|
56
87
|
option it'll default to stomp://localhost:61613 ie it expects a broker running
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'stomp'
|
5
|
+
require 'json'
|
6
|
+
require "getoptlong"
|
7
|
+
|
8
|
+
options = {}
|
9
|
+
argv = GetoptLong.new(
|
10
|
+
[ "--broker", "-b", GetoptLong::OPTIONAL_ARGUMENT ]
|
11
|
+
)
|
12
|
+
argv.each do |option, value|
|
13
|
+
options['broker'] = value if option == '--broker'
|
14
|
+
end
|
15
|
+
options['broker'] ||= 'stomp://127.0.0.1:61613'
|
16
|
+
|
17
|
+
parameters = ARGV.join ' '
|
18
|
+
command = {
|
19
|
+
command: File.basename($0).gsub(/^constable-/, ''),
|
20
|
+
parameters: parameters,
|
21
|
+
files: {}
|
22
|
+
}
|
23
|
+
ARGV.each do |file_name|
|
24
|
+
if File.exists? file_name
|
25
|
+
file = File.read file_name
|
26
|
+
data = Base64.encode64 file
|
27
|
+
command[:files][file_name] = data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
command_json = JSON.generate command
|
32
|
+
|
33
|
+
results = '/queue/constable.results-' + Process.pid.to_s + '-' + Time.now.to_i.to_s
|
34
|
+
client = Stomp::Client.new options['broker']
|
35
|
+
client.publish '/queue/constable', command_json, 'reply-to' => results
|
36
|
+
|
37
|
+
client.subscribe results, 'auto-delete' => true do |message|
|
38
|
+
results = JSON.parse message.body
|
39
|
+
if results['exit_code'] == 0
|
40
|
+
print results['stdout']
|
41
|
+
else
|
42
|
+
print results['stderr']
|
43
|
+
end
|
44
|
+
client.close
|
45
|
+
end
|
46
|
+
|
47
|
+
client.join
|
data/bin/constable-identify
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
|
3
|
-
image = STDIN.read
|
4
|
-
if image.to_s == ''
|
5
|
-
puts "Give me some input!"
|
6
|
-
exit 1
|
7
|
-
end
|
8
|
-
|
9
3
|
require 'base64'
|
10
|
-
encoded_input = Base64.encode64 image
|
11
|
-
|
12
4
|
require 'stomp'
|
13
5
|
require 'json'
|
14
6
|
require "getoptlong"
|
@@ -22,8 +14,26 @@ argv.each do |option, value|
|
|
22
14
|
end
|
23
15
|
options['broker'] ||= 'stomp://127.0.0.1:61613'
|
24
16
|
|
17
|
+
parameters = ARGV.join ' '
|
18
|
+
command = {
|
19
|
+
command: File.basename($0).gsub(/^constable-/, ''),
|
20
|
+
parameters: parameters,
|
21
|
+
files: {}
|
22
|
+
}
|
23
|
+
ARGV.each do |file_name|
|
24
|
+
if File.exists? file_name
|
25
|
+
file = File.read file_name
|
26
|
+
data = Base64.encode64 file
|
27
|
+
command[:files][file_name] = data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
command_json = JSON.generate command
|
32
|
+
|
25
33
|
results = '/queue/constable.results-' + Process.pid.to_s + '-' + Time.now.to_i.to_s
|
26
34
|
client = Stomp::Client.new options['broker']
|
35
|
+
client.publish '/queue/constable', command_json, 'reply-to' => results
|
36
|
+
|
27
37
|
client.subscribe results, 'auto-delete' => true do |message|
|
28
38
|
results = JSON.parse message.body
|
29
39
|
if results['exit_code'] == 0
|
@@ -34,13 +44,4 @@ client.subscribe results, 'auto-delete' => true do |message|
|
|
34
44
|
client.close
|
35
45
|
end
|
36
46
|
|
37
|
-
parameters = ARGV.join ' '
|
38
|
-
command = {
|
39
|
-
command: 'identify',
|
40
|
-
input: encoded_input,
|
41
|
-
parameters: parameters
|
42
|
-
}
|
43
|
-
command_json = JSON.generate command
|
44
|
-
|
45
|
-
client.publish '/queue/constable', command_json, 'reply-to' => results
|
46
47
|
client.join
|
data/bin/constabled
CHANGED
@@ -10,38 +10,98 @@ require 'getoptlong'
|
|
10
10
|
|
11
11
|
class Job
|
12
12
|
module WorkingEnvironment
|
13
|
-
def with_temp_file
|
14
|
-
temp_file = Tempfile.new serial
|
15
|
-
yield temp_file
|
16
|
-
ensure
|
17
|
-
temp_file.close!
|
18
|
-
end
|
19
|
-
|
20
13
|
def serial
|
21
14
|
@job_id ||= [
|
22
15
|
File.basename($0), Time.now.to_i, rand(1_000_000)
|
23
16
|
].join '-'
|
24
17
|
end
|
25
18
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
19
|
+
def working_directory
|
20
|
+
@working_directory ||= %x[mktemp -d -t #{serial}-XXXXXX].strip
|
21
|
+
end
|
22
|
+
|
23
|
+
def clean_up
|
24
|
+
%x[ test -d "#{@working_directory}" && rm -rf "#{@working_directory}"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def write_files
|
28
|
+
files.each_pair do |file_name, content|
|
29
|
+
puts "Decoding #{file_name}"
|
30
|
+
decoded_content = Base64.decode64 content
|
31
|
+
puts "Writing #{file_name} content"
|
32
|
+
file = write_file decoded_content, file_name.split(/\./)[-1]
|
33
|
+
puts "Wrote #{file_name} to #{file.path}"
|
34
|
+
rewrite_file_parameter file_name, file.path
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
35
|
-
def
|
38
|
+
def write_file content, ext = nil
|
39
|
+
hash = Digest::SHA1.hexdigest content
|
40
|
+
ext = ".#{ext}" if ext && ext[0] != '.'
|
41
|
+
file = File.open File.join(working_directory, "#{hash}#{ext}"), 'w+'
|
42
|
+
file.print content
|
43
|
+
file.close
|
44
|
+
file
|
45
|
+
end
|
46
|
+
|
47
|
+
# FIXME: We can still end up with parameters we don't know about being sent
|
48
|
+
# here, including shell escaped... insane.
|
49
|
+
#
|
50
|
+
# Figure out some way to generate a whitelist of ImageMagick commands and
|
51
|
+
# parameters.
|
52
|
+
|
53
|
+
def rewrite_file_parameter file_name, actual_file
|
54
|
+
file_map[actual_file] = file_name
|
55
|
+
puts "Rewriting parameter #{file_name} to #{actual_file}"
|
56
|
+
parameters.gsub! file_name, actual_file
|
57
|
+
end
|
58
|
+
|
59
|
+
def file_map
|
60
|
+
@file_map ||= {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def unwrap_file_parameters result
|
64
|
+
file_map.each_pair do |local_file, remote_file|
|
65
|
+
puts "Rewriting parameter #{local_file} to #{remote_file}"
|
66
|
+
result.gsub! local_file, remote_file
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def files
|
71
|
+
options['files']
|
72
|
+
end
|
73
|
+
|
74
|
+
def parameters
|
75
|
+
options['parameters']
|
76
|
+
end
|
77
|
+
|
78
|
+
def hostname
|
79
|
+
%x[hostname].strip
|
80
|
+
end
|
81
|
+
|
82
|
+
def run_command command
|
83
|
+
write_files
|
84
|
+
command_line = [ command, parameters ].join ' '
|
36
85
|
puts "Running #{command_line}"
|
37
|
-
stdin, stdout, stderr
|
86
|
+
stdin, stdout, stderr = Open3.popen3 command_line
|
87
|
+
output = stdout.readlines.join
|
88
|
+
errors = stderr.readlines.join
|
89
|
+
unwrap_file_parameters command_line
|
90
|
+
unwrap_file_parameters output
|
38
91
|
{
|
39
|
-
stdout
|
40
|
-
stderr
|
41
|
-
|
42
|
-
|
43
|
-
|
92
|
+
:stdout => output,
|
93
|
+
:stderr => errors,
|
94
|
+
:command_line => command_line,
|
95
|
+
:exit_code => $?.exitstatus,
|
96
|
+
:pid => $?.pid,
|
97
|
+
:working_directory => working_directory,
|
98
|
+
:processor => {
|
99
|
+
:hostname => hostname,
|
100
|
+
:pid => Process.pid
|
101
|
+
}
|
44
102
|
}
|
103
|
+
ensure
|
104
|
+
clean_up
|
45
105
|
end
|
46
106
|
end
|
47
107
|
|
@@ -56,13 +116,22 @@ class Job
|
|
56
116
|
end
|
57
117
|
|
58
118
|
def execute
|
59
|
-
|
60
|
-
run_command "identify #{parameters} #{file.path}"
|
61
|
-
end
|
119
|
+
run_command :identify
|
62
120
|
end
|
121
|
+
end
|
63
122
|
|
64
|
-
|
65
|
-
|
123
|
+
class ConvertCommand
|
124
|
+
include WorkingEnvironment
|
125
|
+
|
126
|
+
attr_accessor :options
|
127
|
+
private :options, :options=
|
128
|
+
|
129
|
+
def initialize options
|
130
|
+
self.options = options
|
131
|
+
end
|
132
|
+
|
133
|
+
def execute
|
134
|
+
run_command :convert
|
66
135
|
end
|
67
136
|
end
|
68
137
|
|
data/lib/constable/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: constable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-10-03 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: stomp
|
16
|
-
requirement: &
|
16
|
+
requirement: &70206086170300 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70206086170300
|
25
25
|
description: Installing ImageMagick and RMagick everywhere sucks, right? So install
|
26
26
|
it in one place and have everything else use that one install.
|
27
27
|
email:
|
@@ -34,6 +34,7 @@ extra_rdoc_files: []
|
|
34
34
|
files:
|
35
35
|
- lib/constable/version.rb
|
36
36
|
- lib/constable.rb
|
37
|
+
- bin/constable-convert
|
37
38
|
- bin/constable-identify
|
38
39
|
- bin/constabled
|
39
40
|
- README.md
|