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 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 ot have ImageMagick
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 on standard input and return
45
- results on standard output. It's up to you what you do with the input and
46
- output; write it to a file, pipe it to another process, that's not handled by
47
- Constable (and I have no plans to handle it).
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
- $ cat input_file | constable-identify --broker=stomp://mq.yourdomain.com:61613
53
- constabled-164829495-102948483-1939485.jpg JPEG 640x480 DirectClass 87kb 0.050u 0:01
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
@@ -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 using_file_with_content encoded_content, &block
27
- decoded_content = Base64.decode64 encoded_content
28
- with_temp_file do |file|
29
- file.print decoded_content
30
- file.flush
31
- yield file
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 run_command command_line
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, command = Open3.popen3 command_line
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: stdout.readlines.join,
40
- stderr: stderr.readlines.join,
41
- exit_code: command.value.exitstatus,
42
- pid: command.value.pid,
43
- command_line: command_line
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
- using_file_with_content options['input'] do |file|
60
- run_command "identify #{parameters} #{file.path}"
61
- end
119
+ run_command :identify
62
120
  end
121
+ end
63
122
 
64
- def parameters
65
- '-verbose' if options['parameters'] == '-verbose'
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
 
@@ -1,3 +1,3 @@
1
1
  module Constable
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
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.1.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-09-30 00:00:00.000000000Z
12
+ date: 2011-10-03 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: stomp
16
- requirement: &70227556077520 !ruby/object:Gem::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: *70227556077520
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