opener-daemons 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d0b2126a8b6322257975cfa14b323e8adc2b764
4
+ data.tar.gz: 9d5cd048fed57092de5bca35d930f25cc7ec1ae5
5
+ SHA512:
6
+ metadata.gz: 2e557daafe0982331f859d5c6b0100b1c8cbfedcd8e2256215fc1a4175ceb4628a70c9edef86a962d19bccf769412ee8ea42c2721464fce3d0486689287dea31
7
+ data.tar.gz: e7d8736c85e18449ba7cf4192bd7840547c05bc12eedcb13651d9db74b302ca6f38e1a519a5a3b432eb114292df25126aeff28d5ccf422edc24f7f385e51bfcf
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in opener-daemons.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Wilco van Duinkerken
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # Opener::Daemons
2
+
3
+ This GEM is part of the OpeNER project, which is the NLP toolchain for the rest
4
+ of us. This particular GEM makes is possible that al OpeNER components can
5
+ actually be launched as deamons reading from and push to Amazon SQS queues.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'opener-daemons'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install opener-daemons
20
+
21
+
22
+ ## SQS
23
+
24
+ The Opener-daemon GEM uses Amazon SQS service as a message service. In order for
25
+ this to work properly you need an Amazon AWS account and you need to set the
26
+ following 3 environment variables in your shell:
27
+
28
+ ```
29
+ export AWS_ACCESS_KEY_ID='...'
30
+ export AWS_SECRET_ACCESS_KEY='...'
31
+ export AWS_REGION='...' #e.g. eu-west-1
32
+ ```
33
+
34
+ To see how you specify which queues to use, checkout the usage section below.
35
+
36
+ ## Implementation
37
+
38
+ This Gem is intended for use with other OpeNER components. In order to turn a
39
+ component in to a Daemon you have to do the following:
40
+
41
+ Add the opener-daemons gem to the gemspec of your component (or the the Gemfile
42
+ if your component is not a gem).
43
+
44
+ ```
45
+ gem.add_dependency 'opener-daemons'
46
+ ```
47
+
48
+ Create a file in the bin/ directory of the component. Following the OpeNER
49
+ naming conventions that will be something like this (e.g. the
50
+ language-identifier). This file provides you with the option to launch a daemon
51
+ from the command line.
52
+
53
+ touch bin/language-identifier-daemon
54
+
55
+ Then add the following lines and replace the language-identifier with your own
56
+ component:
57
+
58
+ ```ruby
59
+ #!/usr/bin/env ruby
60
+ require 'rubygems'
61
+ require 'opener/daemons'
62
+
63
+ exec_path = File.expand_path("exec/language-identifier.rb")
64
+ Opener::Daemons::Controller.new(:name=>"language-identifier",
65
+ :exec_path=>exec_path)
66
+ ```
67
+
68
+ After that you have to create a file that does the actual work in an "exec"
69
+ directory. From the root of your component do this:
70
+
71
+ ```
72
+ mkdir exec
73
+ touch exec/language-identifier.rb
74
+ ```
75
+
76
+ Then copy paste the following code into that file, replacing the
77
+ "language-identifier" parts with your own component.
78
+
79
+ ```ruby
80
+ require 'opener/daemons'
81
+ require 'opener/language_identifier'
82
+
83
+ options = Opener::OptParser.parse!(ARGV)
84
+ daemon = Opener::Daemon.new(Opener::LanguageIdentifier, options)
85
+ daemon.start
86
+ ```
87
+
88
+ Now you should be able to launch yourself a LanguageIdentifier daemon. Check out
89
+ the exact usage of the daemon by typing:
90
+
91
+ ```
92
+ bin/language-identifier-daemon -h
93
+ ```
94
+
95
+ ## Usage
96
+
97
+ Once you implemented the daemon you can use the -h option to get usage
98
+ information. It will look like this, with the "language-identifier" strings
99
+ replaced by your own component.
100
+
101
+ ```
102
+ Usage: language-identifier.rb <start|stop|restart> [options]
103
+
104
+ Specific options:
105
+ -i, --input INPUT_QUEUE_NAME Input queue name
106
+ -o, --output OUTPUT_QUEUE_NAME Output queue name
107
+ -b, --batch-size BATCH_SIZE Request x messages at once where x is between 1 and 10
108
+ -w, --workers NUMBER number of worker thread
109
+ -r, --readers NUMBER number of reader threads
110
+ -p, --writers NUMBER number of writer / pusher threads
111
+ --log FILENAME Filename and path of logfile. Defaults to STDOUT
112
+ --pid FILENAME Filename and path of pidfile. Defaults to /var/run/{filename}.rb
113
+ --pidpath DIRNAME Directory where to put the PID file. Is Overwritten by --pid if that option is present
114
+ --debug Turn on debug log level
115
+
116
+ Common options:
117
+ -h, --help Show this message
118
+ ```
119
+
120
+
121
+ ## Contributing
122
+
123
+ 1. Fork it ( http://github.com/<my-github-username>/opener-daemons/fork )
124
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
125
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
126
+ 4. Push to the branch (`git push origin my-new-feature`)
127
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,162 @@
1
+ #
2
+ # Original Idea by Charles Nutter
3
+ # Copied from: https://gist.github.com/ik5/448884
4
+ # Then adjusted.
5
+ #
6
+
7
+ require 'rubygems'
8
+ require 'opener/daemons'
9
+ require 'spoon'
10
+
11
+ module Opener
12
+ module Daemons
13
+ class Controller
14
+ attr_reader :name, :exec
15
+
16
+ def initialize(options={})
17
+ @exec = options.fetch(:exec_path)
18
+ @name = determine_name(options[:name])
19
+ read_commandline
20
+ end
21
+
22
+ def determine_name(name)
23
+ return identify(name) unless name.nil?
24
+ get_name_from_exec_path
25
+ end
26
+
27
+ def get_name_from_exec_path
28
+ File.basename(exec, ".rb")
29
+ end
30
+
31
+ def read_commandline
32
+ args = ARGV.dup
33
+ @options = Opener::Daemons::OptParser.parse!(args)
34
+
35
+ if ARGV[0] == 'start'
36
+ start
37
+ elsif ARGV[0] == 'stop'
38
+ stop
39
+ elsif ARGV[0] == 'restart'
40
+ stop
41
+ start
42
+ elsif ARGV[0] == '-h'
43
+ Opener::Daemons::OptParser.parse!(ARGV)
44
+ else
45
+ puts "Usage: #{name} <start|stop|restart> [options]"
46
+ puts "Or for help use: #{name} -h"
47
+ exit!
48
+ end
49
+ end
50
+
51
+ def pid_path
52
+ return @pid_path unless @pid_path.nil?
53
+ @pid_path = if @options[:pid]
54
+ File.expand_path(@options[:pid])
55
+ elsif @options[:pidpath]
56
+ File.expand_path(File.join(@options[:pidpath], "#{name}.pid"))
57
+ else
58
+ "/var/run/#{File.basename($0, ".rb")}.pid"
59
+ end
60
+ end
61
+
62
+ def create_pid(pid)
63
+ begin
64
+ open(pid_path, 'w') do |f|
65
+ f.puts pid
66
+ end
67
+ rescue => e
68
+ STDERR.puts "Error: Unable to open #{pid_path} for writing:\n\t" +
69
+ "(#{e.class}) #{e.message}"
70
+ exit!
71
+ end
72
+ end
73
+
74
+ def get_pid
75
+ pid = false
76
+ begin
77
+ open(pid_path, 'r') do |f|
78
+ pid = f.readline
79
+ pid = pid.to_s.gsub(/[^0-9]/,'')
80
+ end
81
+ rescue => e
82
+ STDOUT.puts "Info: Unable to open #{pid_path} for reading:\n\t" +
83
+ "(#{e.class}) #{e.message}"
84
+ end
85
+
86
+
87
+ if pid
88
+ return pid.to_i
89
+ else
90
+ return nil
91
+ end
92
+ end
93
+
94
+ def remove_pidfile
95
+ begin
96
+ File.unlink(pid_path)
97
+ rescue => e
98
+ STDERR.puts "ERROR: Unable to unlink #{path}:\n\t" +
99
+ "(#{e.class}) #{e.message}"
100
+ exit
101
+ end
102
+ end
103
+
104
+ def process_exists?
105
+ begin
106
+ pid = get_pid
107
+ return false unless pid
108
+ Process.kill(0, pid)
109
+ true
110
+ rescue Errno::ESRCH, TypeError # "PID is NOT running or is zombied
111
+ false
112
+ rescue Errno::EPERM
113
+ STDERR.puts "No permission to query #{pid}!";
114
+ false
115
+ rescue => e
116
+ STDERR.puts "Error: Unable to determine status for pid: #{pid}.\n\t" +
117
+ "(#{e.class}) #{e.message}"
118
+ false
119
+ end
120
+ end
121
+
122
+ def stop
123
+ begin
124
+ pid = get_pid
125
+ STDOUT.puts "Stopping pid: #{pid}"
126
+ while true do
127
+ Process.kill("TERM", pid)
128
+ Process.wait(pid)
129
+ sleep(0.1)
130
+ end
131
+ rescue Errno::ESRCH # no more process to kill
132
+ remove_pidfile
133
+ STDOUT.puts 'Stopped the process'
134
+ rescue => e
135
+ STDERR.puts "Unable to terminate process: (#{e.class}) #{e.message}"
136
+ end
137
+ end
138
+
139
+ def start
140
+ if process_exists?
141
+ STDERR.puts "Error: The process #{name} already running. Restarting the process"
142
+ stop
143
+ end
144
+
145
+ STDOUT.puts "Starting DAEMON"
146
+ pid = Spoon.spawnp exec, *ARGV
147
+ STDOUT.puts "Started DAEMON"
148
+ create_pid(pid)
149
+ begin
150
+ Process.setsid
151
+ rescue Errno::EPERM => e
152
+ STDERR.puts "Process.setsid not permitted on this platform, not critical. Continuing normal operations.\n\t (#{e.class}) #{e.message}"
153
+ end
154
+ File::umask(0)
155
+ end
156
+
157
+ def identify(string)
158
+ string.gsub(/\W/,"-").gsub("--","-")
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,135 @@
1
+ require 'thread'
2
+ require 'opener/daemons/sqs'
3
+
4
+ module Opener
5
+ module Daemons
6
+ class Daemon
7
+ attr_reader :batch_size,
8
+ :input_queue, :output_queue,
9
+ :input_buffer, :output_buffer,
10
+ :klass,
11
+ :logger
12
+
13
+ attr_accessor :threads, :thread_counts
14
+
15
+ def initialize(klass, options={})
16
+ @input_queue = Opener::Daemons::SQS.find(options.fetch(:input_queue))
17
+ @output_queue = Opener::Daemons::SQS.find(options.fetch(:output_queue))
18
+
19
+ @threads = {}
20
+ @threads[:writers] = []
21
+ @threads[:readers] = []
22
+ @threads[:workers] = []
23
+
24
+ @thread_counts = {}
25
+ @thread_counts[:workers] = options.fetch(:workers, 1)
26
+ @thread_counts[:readers] = options.fetch(:readers, 5)
27
+ @thread_counts[:writers] = options.fetch(:writers, 1)
28
+
29
+ @batch_size = options.fetch(:batch_size, 10)
30
+
31
+ @input_buffer = Queue.new
32
+ @output_buffer = Queue.new
33
+
34
+ @klass = klass
35
+
36
+ script_name = File.basename($0, ".rb")
37
+ @logger = Logger.new(options.fetch(:log, "#{script_name}.log"))
38
+ @logger.level = if options.fetch(:debug, false)
39
+ Logger::DEBUG
40
+ else
41
+ Logger::INFO
42
+ end
43
+ end
44
+
45
+ def buffer_new_messages
46
+ if input_buffer.size > buffer_size
47
+ #logger.debug "Maximum input buffer size reached"
48
+ return
49
+ end
50
+
51
+ if output_buffer.size > buffer_size
52
+ #logger.debug "Maximum output buffer size reached"
53
+ return
54
+ end
55
+
56
+ messages = input_queue.receive_messages(batch_size)
57
+
58
+ return if messages.nil?
59
+ messages.each do |message|
60
+ input_buffer << message
61
+ end
62
+ end
63
+
64
+ def start
65
+ Thread.abort_on_exception = true
66
+ #
67
+ # Load Readers
68
+ #
69
+ thread_counts[:readers].times do |t|
70
+ threads[:readers] << Thread.new do
71
+ logger.info "Producer #{t+1} ready for action..."
72
+ loop do
73
+ buffer_new_messages
74
+ end
75
+ end
76
+ end
77
+
78
+ #
79
+ # Load Workers
80
+ #
81
+ thread_counts[:workers].times do |t|
82
+ threads[:workers] << Thread.new do
83
+ logger.info "Worker #{t+1} launching..."
84
+ identifier = klass.new
85
+ loop do
86
+ message = input_buffer.pop
87
+
88
+ input = JSON.parse(message[:body])["input"]
89
+ begin
90
+ output = identifier.run(input)
91
+ rescue Exception => e
92
+ logger.error(e)
93
+ output = input
94
+ end
95
+ output_buffer.push({ :output=>output,
96
+ :handle=>message[:receipt_handle]})
97
+ end
98
+ end
99
+ end
100
+
101
+ #
102
+ # Load Writers
103
+ #
104
+ thread_counts[:writers].times do |t|
105
+ threads[:writers] << Thread.new do
106
+ logger.info "Pusher #{t+1} ready for action..."
107
+ loop do
108
+ message = output_buffer.pop
109
+
110
+ payload = {:input=>message[:output]}.to_json
111
+ output_queue.send_message(payload)
112
+ input_queue.delete_message(message[:handle])
113
+ end
114
+ end
115
+ end
116
+
117
+ reporter = Thread.new do
118
+ loop do
119
+ logger.debug "input buffer: #{input_buffer.size} \t output buffer: #{output_buffer.size}"
120
+ sleep(2)
121
+ end
122
+ end
123
+
124
+ threads[:writers].each(&:join)
125
+ threads[:readers].each(&:join)
126
+ threads[:workers].each(&:join)
127
+ end
128
+
129
+ def buffer_size
130
+ 4 * batch_size
131
+ end
132
+
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,79 @@
1
+ require 'optparse'
2
+
3
+ module Opener
4
+ module Daemons
5
+ class OptParser
6
+
7
+ def self.parse(args)
8
+ process(args, :parse)
9
+ end
10
+
11
+ def self.parse!(args)
12
+ process(args, :parse!)
13
+ end
14
+
15
+ def self.process(args, call)
16
+ options = {}
17
+
18
+ OptionParser.new do |opts|
19
+ opts.banner = "Usage: language-identifier.rb <start|stop|restart> [options]"
20
+ opts.separator ""
21
+ opts.separator "Specific options:"
22
+
23
+ opts.on("-i", "--input INPUT_QUEUE_NAME", "Input queue name") do |v|
24
+ options[:input_queue] = v
25
+ end
26
+
27
+ opts.on("-o", "--output OUTPUT_QUEUE_NAME", "Output queue name") do |v|
28
+ options[:output_queue] = v
29
+ end
30
+
31
+ opts.on("-b", "--batch-size BATCH_SIZE", Integer, "Request x messages at once where x is between 1 and 10") do |v|
32
+ options[:batch_size] = v
33
+ end
34
+
35
+ opts.on("-w", "--workers NUMBER", Integer, "number of worker thread") do |v|
36
+ options[:workers] = v
37
+ end
38
+
39
+ opts.on("-r", "--readers NUMBER", Integer, "number of reader threads") do |v|
40
+ options[:readers] = v
41
+ end
42
+
43
+ opts.on("-p", "--writers NUMBER", Integer, "number of writer / pusher threads") do |v|
44
+ options[:writers] = v
45
+ end
46
+
47
+ opts.on("--log FILENAME", "Filename and path of logfile. Defaults to STDOUT") do |v|
48
+ options[:log] = v
49
+ end
50
+
51
+ opts.on("--pid FILENAME", "Filename and path of pidfile. Defaults to /var/run/{basename of current script}.pid") do |v|
52
+ options[:pid] = v
53
+ end
54
+
55
+ opts.on("--pidpath DIRNAME", "Directory where to put the PID file. Is Overwritten by --pid if that option is present") do |v|
56
+ options[:pidpath] = v
57
+ end
58
+
59
+ opts.on("--debug", "Turn on debug log level") do |v|
60
+ options[:debug] = true
61
+ end
62
+
63
+ opts.separator ""
64
+
65
+ opts.separator "Common options:"
66
+
67
+ # No argument, shows at tail. This will print an options summary.
68
+ # Try it and see!
69
+ opts.on_tail("-h", "--help", "Show this message") do
70
+ puts opts
71
+ exit
72
+ end
73
+ end.send(call, args)
74
+
75
+ return options
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,33 @@
1
+ require 'aws-sdk-core'
2
+
3
+ module Opener
4
+ module Daemons
5
+ class SQS
6
+ attr_reader :sqs, :name, :url
7
+
8
+ def self.find(name)
9
+ new(name)
10
+ end
11
+
12
+ def initialize(name)
13
+ @sqs = Aws::SQS.new
14
+ @name = name
15
+ @url = sqs.get_queue_url(:queue_name=>name)[:queue_url]
16
+ end
17
+
18
+ def send_message(message)
19
+ sqs.send_message(:queue_url=>url, :message_body=>message)
20
+ end
21
+
22
+ def delete_message(handle)
23
+ sqs.delete_message(:queue_url=>url, :receipt_handle=>handle)
24
+ end
25
+
26
+ def receive_messages(limit)
27
+ result = sqs.receive_message(:queue_url=>url,
28
+ :max_number_of_messages=>limit)[:messages]
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ module Opener
2
+ module Daemons
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require "opener/daemons/version"
2
+ require "opener/daemons/sqs"
3
+ require "opener/daemons/daemon"
4
+ require "opener/daemons/opt_parser"
5
+ require "opener/daemons/controller"
6
+
7
+ module Opener
8
+ module Daemons
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'opener/daemons/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "opener-daemons"
8
+ spec.version = Opener::Daemons::VERSION
9
+ spec.authors = ["Wilco van Duinkerken"]
10
+ spec.email = ["wilco@sparkboxx.com"]
11
+ spec.summary = %q{Daemonize OpeNER components and make them read from an SQS queue. JRuby compatible.}
12
+ spec.description = spec.summary
13
+ spec.homepage = "http://opener-project.github.io"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+
22
+ spec.add_dependency 'aws-sdk-core'
23
+ spec.add_dependency 'spoon'
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.5"
26
+ spec.add_development_dependency "rake"
27
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opener-daemons
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Wilco van Duinkerken
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-core
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - '>='
23
+ - !ruby/object:Gem::Version
24
+ version: '0'
25
+ prerelease: false
26
+ type: :runtime
27
+ - !ruby/object:Gem::Dependency
28
+ name: spoon
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ prerelease: false
40
+ type: :runtime
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: '1.5'
53
+ prerelease: false
54
+ type: :development
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ prerelease: false
68
+ type: :development
69
+ description: Daemonize OpeNER components and make them read from an SQS queue. JRuby compatible.
70
+ email:
71
+ - wilco@sparkboxx.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - lib/opener/daemons.rb
82
+ - lib/opener/daemons/controller.rb
83
+ - lib/opener/daemons/daemon.rb
84
+ - lib/opener/daemons/opt_parser.rb
85
+ - lib/opener/daemons/sqs.rb
86
+ - lib/opener/daemons/version.rb
87
+ - opener-daemons.gemspec
88
+ homepage: http://opener-project.github.io
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.2.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Daemonize OpeNER components and make them read from an SQS queue. JRuby compatible.
112
+ test_files: []