mqjob 0.4.6

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.
@@ -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: []