mqjob 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b8e339bb78dcea031b930f7fab3203e5358c0103e4a2c2357b5c321e19fcd311
4
+ data.tar.gz: eecac402fb51319ff008756bc7ebdf0a005a45b2816b1bf44cb92403726793d4
5
+ SHA512:
6
+ metadata.gz: 7ed6eb6e2c6642a4edfd0c73a2affc477a0b356150fb7c2d482561340534dd1febf22de73805e52d27948e204275ae188acda3310f3ab1d17ab0899ce253dd0a
7
+ data.tar.gz: ed2bd2477e35a535aeafc14271ec7b62ae5024e13ec781e56f09b46daf51cd307cb4fd425e0aca6126c6d6246a8e552eaf3fe5a7f589a31a628f8d2cd5e44dde
@@ -0,0 +1,51 @@
1
+ # ---> Ruby
2
+ *.gem
3
+ *.rbc
4
+ /.config
5
+ /coverage/
6
+ /InstalledFiles
7
+ /pkg/
8
+ /spec/reports/
9
+ /spec/examples.txt
10
+ /test/tmp/
11
+ /test/version_tmp/
12
+ /tmp/
13
+
14
+ # Used by dotenv library to load environment variables.
15
+ # .env
16
+
17
+ ## Specific to RubyMotion:
18
+ .dat*
19
+ .repl_history
20
+ build/
21
+ *.bridgesupport
22
+ build-iPhoneOS/
23
+ build-iPhoneSimulator/
24
+
25
+ ## Specific to RubyMotion (use of CocoaPods):
26
+ #
27
+ # We recommend against adding the Pods directory to your .gitignore. However
28
+ # you should judge for yourself, the pros and cons are mentioned at:
29
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
30
+ #
31
+ # vendor/Pods/
32
+
33
+ ## Documentation cache and generated files:
34
+ /.yardoc/
35
+ /_yardoc/
36
+ /doc/
37
+ /rdoc/
38
+
39
+ ## Environment normalization:
40
+ /.bundle/
41
+ /vendor/bundle
42
+ /lib/bundler/man/
43
+
44
+ # for a library or gem, you might want to ignore these files since the code is
45
+ # intended to run in multiple environments; otherwise, check them in:
46
+ Gemfile.lock
47
+ .ruby-version
48
+ .ruby-gemset
49
+
50
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
51
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://gems.ruby-china.com"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gem 'pulsar_sdk', github: 'archfish/pulsar_sdk'
6
+
7
+ gemspec
@@ -0,0 +1,118 @@
1
+ # Mqjob
2
+
3
+ A fast background processing framework for Ruby using MQ. MQ client was support as plugin. Current only implement Apache Pulsar. Name `Mqjob` was combine MQ and job.
4
+
5
+ Inspired By [Sneakers](https://github.com/jondot/sneakers).
6
+
7
+ ## Examples
8
+
9
+ - [initializer](examples/initializers.rb)
10
+ - [rake task](examples/mqjob.rake)
11
+ - [Single Job](examples/single_job.rb)
12
+ - [Multiple Job](examples/multiple_job.rb)
13
+ - Shell run `WORKERS=SingleJob,MultipleJob bundle exec rake mqjob:run`
14
+
15
+ ## API
16
+
17
+ ### Global Config
18
+
19
+ using in `Mqjob.configure`.
20
+
21
+ - client
22
+
23
+ An MQ client instance using in plugin. It should provide `producer` and `consumer` create api.
24
+
25
+ - plugin
26
+
27
+ Which implementing a specific MQ operations. Must implement basic interface:
28
+
29
+ ```ruby
30
+ def listen(topic, worker, opts = {}); end
31
+
32
+ def publish(topic, msg, opts = {}); end
33
+ ```
34
+
35
+ See [Pulsar](lib/plugin/pulsar.rb) for detail.
36
+
37
+ - daemonize
38
+
39
+ Config worker run to background.
40
+
41
+ - threads
42
+
43
+ How many Thread will create for job perform in process. It will init a thread pool.
44
+ IO-intensive tasks can be appropriately increased, and CPU-intensive tasks can be appropriately reduced.
45
+
46
+ - hooks
47
+
48
+ config `before_fork` and `after_fork`. NOT implement yet.
49
+ if you using ActiveRecord, set `wrap_perform` as follow avoid database connection broken.
50
+
51
+ ```ruby
52
+ Mqjob.configure do |config|
53
+ config.hooks = {
54
+ wrap_perform: lambda {|&b| ActiveRecord::Base.connection_pool.with_connection {b.call}}
55
+ }
56
+ end
57
+ ```
58
+
59
+ - subscription_mode
60
+
61
+ * exclusive
62
+
63
+ Same subscription name only one conumser can subscribe to a topic.
64
+
65
+ * failover
66
+
67
+ All subscribe to a topic only one can receive message, once the subscribe exit the remain pick one keep up.
68
+
69
+ * shared
70
+
71
+ All subscribe can receive message.
72
+
73
+ - logger
74
+
75
+ A log instance implement Ruby std logger interface.
76
+
77
+ ### Worker Config
78
+
79
+ - client
80
+
81
+ Using difference MQ client for this job. If connect to different types MQ you should config plugin at the same time.
82
+
83
+ - plugin
84
+
85
+ Using difference MQ client implement.
86
+
87
+ - prefetch
88
+
89
+ Config how many message per worker pull in one job cycle.
90
+
91
+ - subscription_mode
92
+
93
+ If set to `exclusive`, should provide subscription_name at the same time.
94
+
95
+ - subscription_name
96
+
97
+ Config subscription name, effects associated with subscription_mode.
98
+
99
+ - logger
100
+
101
+ Using difference logger for current worker.
102
+
103
+ - topic_type
104
+
105
+ * normal
106
+
107
+ Ordinary topic name. Default value.
108
+
109
+ * regex
110
+
111
+ The topic name is a regex, represent topics which match this regex. For example, `persistent://my-tenant/namespace2/topic_*` is all topics in namespace2 that match `/topic_*/`.
112
+
113
+ ## Note
114
+
115
+ - Thread pool will not clean `Thread.current` when thread give back to pool. If you want to use thread storage [RequestStore](https://github.com/steveklabnik/request_store) recommended, it build in support.
116
+ - database pool should not less than `threads` size.
117
+ - Maybe you should use connection pool to manage database connection. Like PgBouncer for PostgreSQL, Druid for MySQL.
118
+ - When topic_type is regex, message enqueue is not supported.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mqjob"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ Mqjob.configure {
14
+
15
+ }
16
+
17
+ require "irb"
18
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,124 @@
1
+ require 'logger'
2
+ require "mqjob/version"
3
+ require "mqjob/thread_pool"
4
+ require 'serverengine'
5
+ require 'mqjob/worker_group'
6
+ require 'mqjob/worker'
7
+ require 'plugin'
8
+ require 'concurrent/configuration'
9
+
10
+ module Mqjob
11
+ extend self
12
+
13
+ attr_reader :config
14
+
15
+ def configure(&block)
16
+ @config ||= Config.new
17
+
18
+ yield @config
19
+ end
20
+
21
+ def hooks
22
+ config&.hooks
23
+ end
24
+
25
+ def default_client
26
+ config&.client
27
+ end
28
+
29
+ # FIXME when job inherit from parent job it will not appear here!!
30
+ def registed_class
31
+ @registed_class ||= []
32
+ end
33
+
34
+ def regist_class(v)
35
+ @registed_class ||= []
36
+ @registed_class << v
37
+ @registed_class.uniq!
38
+ end
39
+
40
+ def logger
41
+ config.logger ||= ::Logger.new(STDOUT).tap do |logger|
42
+ logger.formatter = Formatter.new
43
+ end
44
+ end
45
+
46
+ class Config
47
+ attr_accessor :client,
48
+ :plugin,
49
+ :daemonize,
50
+ :threads,
51
+ :subscription_mode
52
+ attr_reader :logger, :hooks
53
+
54
+ def initialize(opts = {})
55
+ @hooks = Hooks.new(opts.delete(:hooks))
56
+ @plugin = :pulsar
57
+
58
+ assign_attributes(opts)
59
+
60
+ remove_empty_instance_variables!
61
+ end
62
+
63
+ def hooks=(v)
64
+ @hooks.update(v)
65
+ end
66
+
67
+ def logger=(v)
68
+ # fvcking Concurrent::Logging
69
+ unless v.respond_to?(:call)
70
+ v.class.class_eval <<-RUBY
71
+ def call(level, progname, message, &block)
72
+ add(level, message, progname, &block)
73
+ end
74
+ RUBY
75
+ end
76
+ @logger = v
77
+ Concurrent.global_logger = v
78
+ end
79
+
80
+ def assign_attributes(opts)
81
+ opts.each do |k, v|
82
+ method = "#{k}="
83
+ next unless self.respond_to?(method)
84
+ self.public_send method, v
85
+ end
86
+ end
87
+
88
+ private
89
+ def remove_empty_instance_variables!
90
+ instance_variables.each do |x|
91
+ remove_instance_variable(x) if instance_variable_get(x).nil?
92
+ end
93
+ end
94
+
95
+ class Hooks
96
+ attr_reader :before_fork, :after_fork, :wrap_perform
97
+
98
+ def initialize(opts)
99
+ update(opts)
100
+ end
101
+
102
+ def update(opts)
103
+ return if opts.nil? || opts.empty?
104
+
105
+ raise "hooks shuld be a Proc map!" unless opts.values.all?{|x| x.nil? || x.is_a?(Proc)}
106
+
107
+ @before_fork = opts[:before_fork]
108
+ @after_fork = opts[:after_fork]
109
+ @wrap_perform = opts[:wrap_perform]
110
+ end
111
+ end
112
+ end
113
+
114
+ class Formatter < ::Logger::Formatter
115
+ def call(severity, timestamp, progname, msg)
116
+ case msg
117
+ when ::StandardError
118
+ msg = [msg.message, msg&.backtrace].join(":\n")
119
+ end
120
+
121
+ super
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,44 @@
1
+ require 'concurrent/executors'
2
+
3
+ module Mqjob
4
+ class ThreadPool < ::Concurrent::FixedThreadPool
5
+ def initialize(num_threads, opts = {})
6
+ super
7
+ @job_finish = ConditionVariable.new
8
+ @job_mutex = Mutex.new
9
+ end
10
+
11
+ # NOTE 使用非缓冲线程池,防止消息丢失
12
+ def post(*args, &task)
13
+ wait
14
+
15
+ super
16
+ end
17
+
18
+ def ns_execute
19
+ super
20
+
21
+ @job_finish.signal
22
+ end
23
+
24
+ def shutdown
25
+ super
26
+
27
+ @job_finish.broadcast
28
+ end
29
+
30
+ def kill
31
+ super
32
+
33
+ @job_finish.broadcast
34
+ end
35
+
36
+ def wait
37
+ @job_mutex.synchronize do
38
+ while running? && (scheduled_task_count - completed_task_count >= max_length)
39
+ @job_finish.wait(@job_mutex, 0.05)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Mqjob
2
+ VERSION = "0.4.6"
3
+ end
@@ -0,0 +1,143 @@
1
+ module Mqjob
2
+ module Worker
3
+ SUBSCRIPTION_MODES = [:exclusive, :failover, :shared].freeze
4
+
5
+ def initialize(opts)
6
+ @pool = opts[:pool]
7
+ @topic = self.class.topic
8
+ @topic_opts = self.class.topic_opts
9
+
10
+ @mq = Plugin.client(@topic_opts[:client])
11
+ end
12
+
13
+ def ack!; :ack end
14
+ def reject!; :reject; end
15
+ def requeue!; :requeue; end
16
+
17
+ def do_work(cmd, msg)
18
+ @pool.post do
19
+ begin
20
+ wrap_perform = ::Mqjob.hooks&.wrap_perform
21
+
22
+ ::Mqjob.logger.debug(__method__){'Begin process'}
23
+
24
+ if wrap_perform.nil?
25
+ process_work(cmd, msg)
26
+ else
27
+ wrap_perform.call do
28
+ process_work(cmd, msg)
29
+ end
30
+ end
31
+
32
+ ::Mqjob.logger.debug(__method__){'Finish process'}
33
+ rescue => exp
34
+ ::Mqjob.logger.error(__method__){"message process error: #{exp.message}! cmd: #{cmd}, msg: #{msg}"}
35
+ ::Mqjob.logger.error(__method__){exp}
36
+ end
37
+ end
38
+ end
39
+
40
+ def run
41
+ @mq.listen(@topic, self, @topic_opts)
42
+ end
43
+
44
+ def stop
45
+ @mq.close_listen
46
+ end
47
+
48
+ def perform(msg); end
49
+
50
+ private
51
+ def process_work(cmd, msg)
52
+ RequestStore.clear! if Object.const_defined?(:RequestStore)
53
+
54
+ result = nil
55
+ begin
56
+ result = if respond_to?(:perform_full_msg)
57
+ ::Mqjob.logger.info(__method__){"perform_full_msg: #{msg.inspect}"}
58
+ perform_full_msg(cmd, msg)
59
+ else
60
+ ::Mqjob.logger.info(__method__){"perform: #{msg.payload.inspect}"}
61
+ perform(msg.payload)
62
+ end
63
+ rescue => exp
64
+ ::Mqjob.logger.error(__method__){exp}
65
+ result = :error
66
+ end
67
+
68
+ case result
69
+ when :error, :reject
70
+ ::Mqjob.logger.info(__method__) {"Redeliver messages! Current message id is: #{msg.message_id.inspect}"}
71
+ msg.nack
72
+ when :requeue
73
+ ::Mqjob.logger.info(__method__) {"Requeue! message id is: #{msg.message_id.inspect}"}
74
+ msg.ack
75
+ self.class.enqueue(msg.payload, in: 10)
76
+ else
77
+ ::Mqjob.logger.info(__method__) {"Acknowledge message!"}
78
+ msg.ack
79
+ end
80
+ end
81
+
82
+ def self.included(base)
83
+ base.extend ClassMethods
84
+ ::Mqjob.regist_class(base) if base.is_a? Class
85
+ end
86
+
87
+ module ClassMethods
88
+ attr_reader :topic_opts
89
+ attr_reader :topic
90
+
91
+ # client: MQ,
92
+ # plugin: :pulsar,
93
+ # prefetch: 1,
94
+ # subscription_mode: SUBSCRIPTION_MODES, # 不同类型需要不同配置参数,互斥模式下需要指定订阅名
95
+ # subscription_name
96
+ # logger: MyLogger
97
+ # topic_type [:normal, :regex] default normal
98
+ def from_topic(name, opts={})
99
+ @topic = name.respond_to?(:call) ? name.call : name
100
+ @topic_opts = opts
101
+
102
+ topic_type = @topic_opts[:topic_type]&.to_sym
103
+ @topic_opts[:topic_type] = topic_type || :normal
104
+
105
+ @topic_opts[:subscription_name] ||= (self.name.split('::') << 'Consumer').join
106
+ end
107
+
108
+ # opts
109
+ # in publish message in X seconds
110
+ # at publish message at specific time
111
+ # init_subscription Boolean 是否先初始化一个订阅
112
+ # perform_now Boolean 立即执行,通常用于测试环境减少流程
113
+ def enqueue(msg, opts={})
114
+ if topic_opts[:topic_type] != :normal
115
+ ::Mqjob.logger.error(__method__){
116
+ "message enqueue only support topic_type set to normal, but got 「#{topic_opts[:topic_type]}」! After action skipped!"
117
+ }
118
+ return false
119
+ end
120
+
121
+ if !opts[:perform_now]
122
+ @mq ||= Plugin.client(topic_opts[:client])
123
+ @mq.publish(topic, msg, topic_opts.merge(opts))
124
+ return true
125
+ end
126
+
127
+ begin
128
+ worker = self.new({})
129
+ if worker.respond_to?(:perform)
130
+ msg = JSON.parse(JSON.dump(msg))
131
+ ::Mqjob.logger.info('perform message now'){msg.inspect}
132
+ worker.send(:process_work, nil, OpenStruct.new(payload: msg))
133
+ else
134
+ ::Mqjob.logger.error('perform_now required 「perform」 method, 「perform_full_msg」not supported!')
135
+ end
136
+ rescue => exp
137
+ ::Mqjob.logger.error("#{self.name} perform_now") {exp}
138
+ end
139
+ true
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,71 @@
1
+ module Mqjob
2
+ module WorkerGroup
3
+ def initialize
4
+ @stoped = false
5
+ # 统一线程池,防止数据库连接池不够用,推荐设置为10
6
+ @pool = ::Mqjob::ThreadPool.new(threads)
7
+ end
8
+
9
+ def before_fork
10
+ ::Mqjob.hooks.before_fork&.call
11
+ end
12
+
13
+ def run
14
+ return if @stoped
15
+
16
+ ::Mqjob.hooks.after_fork&.call
17
+
18
+ workers.each do |worker|
19
+ worker.run
20
+ end
21
+ end
22
+
23
+ def stop
24
+ workers.each{|wc| wc.stop}
25
+
26
+ @stoped = true
27
+ end
28
+
29
+ def reload
30
+ puts "call #{__method__}"
31
+ end
32
+
33
+ def after_start
34
+ puts "call #{__method__}"
35
+ end
36
+
37
+ def workers
38
+ @workers ||= worker_classes.map do |wc|
39
+ wc.new(pool: @pool)
40
+ end
41
+ end
42
+
43
+ # 设置线程数并返回新类型
44
+ # opts
45
+ # threads 设置线程池大小
46
+ def self.configure(opts)
47
+ thread_size = opts[:threads]
48
+ raise "threads was required!" if thread_size.to_i.zero?
49
+ workers = Array(opts[:clazz])
50
+ raise "clazz was required!" if workers.empty?
51
+
52
+ md = Module.new
53
+ md_name = "WorkerGroup#{md.object_id}".tr('-', '_')
54
+
55
+ md.include(self)
56
+ md.class_eval <<-RUBY, __FILE__, __LINE__+1
57
+ def threads
58
+ #{thread_size}
59
+ end
60
+
61
+ def worker_classes
62
+ #{workers}
63
+ end
64
+
65
+ private :threads, :workers
66
+ RUBY
67
+
68
+ ::Mqjob.const_set(md_name, md)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,24 @@
1
+ require 'plugin/base'
2
+
3
+ module Plugin
4
+ extend self
5
+
6
+ def client(c, plugin: nil)
7
+ plugin = init_plugin(plugin)
8
+ c ||= Mqjob.default_client
9
+
10
+ Plugin.const_get(plugin).new(c)
11
+ end
12
+
13
+ def init_plugin(name)
14
+ name ||= Mqjob.config.plugin
15
+
16
+ Mqjob.logger.debug("#{self.name}::#{__method__}"){"select plugin: #{name}"}
17
+
18
+ require "plugin/#{name}"
19
+
20
+ name.to_s.capitalize
21
+ end
22
+
23
+ private :init_plugin
24
+ end
@@ -0,0 +1,15 @@
1
+ module Plugin
2
+ class Base
3
+ def initialize(client)
4
+ @client = client
5
+ @subscription_mode = ::Mqjob.config.subscription_mode
6
+ end
7
+
8
+ def listen(topic, worker, opts = {}); end
9
+
10
+ def publish(topic, msg, opts = {}); end
11
+
12
+ def close_listen; end
13
+ def close_publish; end
14
+ end
15
+ end
@@ -0,0 +1,92 @@
1
+ require 'pulsar_sdk'
2
+
3
+ module Plugin
4
+ class Pulsar < Base
5
+ def listen(topic, worker, opts = {})
6
+ create_consumer(topic, opts).listen do |cmd, msg|
7
+ ::Mqjob.logger.debug("#{self.class.name}::#{__method__}"){"receive msg: #{msg.payload}"}
8
+ worker.do_work(cmd, msg)
9
+ end
10
+ end
11
+
12
+ # opts
13
+ # in publish message in X seconds
14
+ # at publish message at specific time
15
+ # init_subscription Boolean
16
+ def publish(topic, msg, opts = {})
17
+ create_consumer(topic, opts) if opts[:init_subscription]
18
+
19
+ base_cmd = ::Pulsar::Proto::BaseCommand.new(
20
+ type: ::Pulsar::Proto::BaseCommand::Type::SEND,
21
+ send: ::Pulsar::Proto::CommandSend.new(
22
+ num_messages: 1
23
+ )
24
+ )
25
+
26
+ get_timestamp = lambda {|v| (v.to_f * 1000).floor}
27
+
28
+ deliver_at = case
29
+ when opts[:in]
30
+ Time.now.localtime + opts[:in].to_f
31
+ when opts[:at]
32
+ opts[:at]
33
+ else
34
+ Time.now.localtime
35
+ end
36
+
37
+ metadata = ::Pulsar::Proto::MessageMetadata.new(
38
+ deliver_at_time: get_timestamp.call(deliver_at)
39
+ )
40
+ p_msg = ::PulsarSdk::Producer::Message.new(msg, metadata)
41
+
42
+ create_producer(topic, opts).execute_async(base_cmd, p_msg)
43
+ end
44
+
45
+ def close_listen
46
+ @consumer&.close
47
+ end
48
+
49
+ def close_publish
50
+ @producer&.close
51
+ end
52
+
53
+ private
54
+ def create_consumer(topic, opts)
55
+ @consumer ||= begin
56
+ topic_type = :topic
57
+ if opts[:topic_type].to_sym == :regex
58
+ topic_type = :topics_pattern
59
+ elsif topic.is_a?(Array)
60
+ topic_type = :topics
61
+ end
62
+
63
+ consumer_opts = ::PulsarSdk::Options::Consumer.new(
64
+ topic_type => topic,
65
+ subscription_type: (opts[:subscription_mode] || @subscription_mode).to_s.capitalize.to_sym,
66
+ subscription_name: opts[:subscription_name],
67
+ prefetch: opts[:prefetch] || 1,
68
+ listen_wait: 0.1
69
+ )
70
+
71
+ ::Mqjob.logger.debug(__method__){consumer_opts.inspect}
72
+
73
+ @client.subscribe(consumer_opts)
74
+ end
75
+ end
76
+
77
+ def create_producer(topic, opts)
78
+ @producer ||= begin
79
+ producer_opts = ::PulsarSdk::Options::Producer.new(
80
+ topic: topic
81
+ )
82
+
83
+ @client.create_producer(producer_opts)
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ # NOTE
91
+ # ServerEngine会自动循环调用,所以这里使用无阻塞listen即可
92
+ # 如果这里listen阻塞会导致同组任务无法正常执行
@@ -0,0 +1,30 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "mqjob/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mqjob"
8
+ spec.version = Mqjob::VERSION
9
+ spec.authors = 'archfish'
10
+ spec.email = ["weihailang@gmail.com"]
11
+ spec.license = 'Apache License 2.0'
12
+
13
+ spec.summary = %q{A queue job base on Apache Pulsar}
14
+ spec.description = %q{A queue job base on Apache Pulsar}
15
+ spec.homepage = "https://github.com/archfish/mqjob"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples)/}) }
21
+ end
22
+
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_dependency 'serverengine', '~> 2.2'
27
+ spec.add_dependency 'concurrent-ruby', '~> 1.1'
28
+
29
+ spec.add_development_dependency "bundler", "> 1.17"
30
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mqjob
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.6
5
+ platform: ruby
6
+ authors:
7
+ - archfish
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: serverengine
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: concurrent-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.17'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.17'
55
+ description: A queue job base on Apache Pulsar
56
+ email:
57
+ - weihailang@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - README.md
65
+ - Rakefile
66
+ - bin/console
67
+ - bin/setup
68
+ - lib/mqjob.rb
69
+ - lib/mqjob/thread_pool.rb
70
+ - lib/mqjob/version.rb
71
+ - lib/mqjob/worker.rb
72
+ - lib/mqjob/worker_group.rb
73
+ - lib/plugin.rb
74
+ - lib/plugin/base.rb
75
+ - lib/plugin/pulsar.rb
76
+ - mqjob.gemspec
77
+ homepage: https://github.com/archfish/mqjob
78
+ licenses:
79
+ - Apache License 2.0
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.0.8
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A queue job base on Apache Pulsar
100
+ test_files: []