jobi 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/CHANGELOG.md +14 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +10 -0
  10. data/Gemfile.lock +45 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +132 -0
  13. data/Rakefile +6 -0
  14. data/bin/console +14 -0
  15. data/bin/jobi.log +31 -0
  16. data/bin/setup +8 -0
  17. data/examples/demo_app/.browserslistrc +1 -0
  18. data/examples/demo_app/.gitignore +35 -0
  19. data/examples/demo_app/.ruby-version +1 -0
  20. data/examples/demo_app/Gemfile +56 -0
  21. data/examples/demo_app/Gemfile.lock +237 -0
  22. data/examples/demo_app/README.md +24 -0
  23. data/examples/demo_app/Rakefile +6 -0
  24. data/examples/demo_app/app/assets/config/manifest.js +2 -0
  25. data/examples/demo_app/app/assets/images/.keep +0 -0
  26. data/examples/demo_app/app/assets/stylesheets/application.css +15 -0
  27. data/examples/demo_app/app/assets/stylesheets/home.scss +3 -0
  28. data/examples/demo_app/app/channels/application_cable/channel.rb +4 -0
  29. data/examples/demo_app/app/channels/application_cable/connection.rb +4 -0
  30. data/examples/demo_app/app/controllers/application_controller.rb +2 -0
  31. data/examples/demo_app/app/controllers/concerns/.keep +0 -0
  32. data/examples/demo_app/app/controllers/home_controller.rb +6 -0
  33. data/examples/demo_app/app/helpers/application_helper.rb +2 -0
  34. data/examples/demo_app/app/helpers/home_helper.rb +2 -0
  35. data/examples/demo_app/app/javascript/channels/consumer.js +6 -0
  36. data/examples/demo_app/app/javascript/channels/index.js +5 -0
  37. data/examples/demo_app/app/javascript/packs/application.js +17 -0
  38. data/examples/demo_app/app/jobs/application_job.rb +7 -0
  39. data/examples/demo_app/app/jobs/calculator_job.rb +14 -0
  40. data/examples/demo_app/app/mailers/application_mailer.rb +4 -0
  41. data/examples/demo_app/app/models/application_record.rb +3 -0
  42. data/examples/demo_app/app/models/concerns/.keep +0 -0
  43. data/examples/demo_app/app/views/layouts/application.html.erb +15 -0
  44. data/examples/demo_app/app/views/layouts/mailer.html.erb +13 -0
  45. data/examples/demo_app/app/views/layouts/mailer.text.erb +1 -0
  46. data/examples/demo_app/babel.config.js +72 -0
  47. data/examples/demo_app/bin/bundle +114 -0
  48. data/examples/demo_app/bin/rails +9 -0
  49. data/examples/demo_app/bin/rake +9 -0
  50. data/examples/demo_app/bin/setup +36 -0
  51. data/examples/demo_app/bin/spring +17 -0
  52. data/examples/demo_app/bin/webpack +18 -0
  53. data/examples/demo_app/bin/webpack-dev-server +18 -0
  54. data/examples/demo_app/bin/yarn +11 -0
  55. data/examples/demo_app/config.ru +5 -0
  56. data/examples/demo_app/config/application.rb +19 -0
  57. data/examples/demo_app/config/boot.rb +4 -0
  58. data/examples/demo_app/config/cable.yml +10 -0
  59. data/examples/demo_app/config/credentials.yml.enc +1 -0
  60. data/examples/demo_app/config/database.yml +25 -0
  61. data/examples/demo_app/config/environment.rb +5 -0
  62. data/examples/demo_app/config/environments/development.rb +62 -0
  63. data/examples/demo_app/config/environments/production.rb +112 -0
  64. data/examples/demo_app/config/environments/test.rb +48 -0
  65. data/examples/demo_app/config/initializers/application_controller_renderer.rb +8 -0
  66. data/examples/demo_app/config/initializers/assets.rb +14 -0
  67. data/examples/demo_app/config/initializers/backtrace_silencers.rb +7 -0
  68. data/examples/demo_app/config/initializers/content_security_policy.rb +30 -0
  69. data/examples/demo_app/config/initializers/cookies_serializer.rb +5 -0
  70. data/examples/demo_app/config/initializers/filter_parameter_logging.rb +4 -0
  71. data/examples/demo_app/config/initializers/inflections.rb +16 -0
  72. data/examples/demo_app/config/initializers/jobi.rb +8 -0
  73. data/examples/demo_app/config/initializers/mime_types.rb +4 -0
  74. data/examples/demo_app/config/initializers/wrap_parameters.rb +14 -0
  75. data/examples/demo_app/config/locales/en.yml +33 -0
  76. data/examples/demo_app/config/puma.rb +38 -0
  77. data/examples/demo_app/config/routes.rb +4 -0
  78. data/examples/demo_app/config/spring.rb +6 -0
  79. data/examples/demo_app/config/storage.yml +34 -0
  80. data/examples/demo_app/config/webpack/development.js +5 -0
  81. data/examples/demo_app/config/webpack/environment.js +3 -0
  82. data/examples/demo_app/config/webpack/production.js +5 -0
  83. data/examples/demo_app/config/webpack/test.js +5 -0
  84. data/examples/demo_app/config/webpacker.yml +96 -0
  85. data/examples/demo_app/db/seeds.rb +7 -0
  86. data/examples/demo_app/lib/assets/.keep +0 -0
  87. data/examples/demo_app/lib/tasks/.keep +0 -0
  88. data/examples/demo_app/log/.keep +0 -0
  89. data/examples/demo_app/package.json +15 -0
  90. data/examples/demo_app/postcss.config.js +12 -0
  91. data/examples/demo_app/public/404.html +67 -0
  92. data/examples/demo_app/public/422.html +67 -0
  93. data/examples/demo_app/public/500.html +66 -0
  94. data/examples/demo_app/public/apple-touch-icon-precomposed.png +0 -0
  95. data/examples/demo_app/public/apple-touch-icon.png +0 -0
  96. data/examples/demo_app/public/favicon.ico +0 -0
  97. data/examples/demo_app/public/robots.txt +1 -0
  98. data/examples/demo_app/storage/.keep +0 -0
  99. data/examples/demo_app/test/application_system_test_case.rb +5 -0
  100. data/examples/demo_app/test/channels/application_cable/connection_test.rb +11 -0
  101. data/examples/demo_app/test/controllers/.keep +0 -0
  102. data/examples/demo_app/test/controllers/home_controller_test.rb +7 -0
  103. data/examples/demo_app/test/fixtures/.keep +0 -0
  104. data/examples/demo_app/test/fixtures/files/.keep +0 -0
  105. data/examples/demo_app/test/helpers/.keep +0 -0
  106. data/examples/demo_app/test/integration/.keep +0 -0
  107. data/examples/demo_app/test/mailers/.keep +0 -0
  108. data/examples/demo_app/test/models/.keep +0 -0
  109. data/examples/demo_app/test/system/.keep +0 -0
  110. data/examples/demo_app/test/test_helper.rb +13 -0
  111. data/examples/demo_app/tmp/.keep +0 -0
  112. data/examples/demo_app/vendor/.keep +0 -0
  113. data/examples/demo_app/yarn.lock +7508 -0
  114. data/examples/normal_job.rb +39 -0
  115. data/jobi.gemspec +31 -0
  116. data/lib/jobi.rb +64 -0
  117. data/lib/jobi/clients/rabbitmq.rb +29 -0
  118. data/lib/jobi/config/rabbitmq.rb +58 -0
  119. data/lib/jobi/configuration.rb +66 -0
  120. data/lib/jobi/consumers/rabbitmq.rb +26 -0
  121. data/lib/jobi/job.rb +89 -0
  122. data/lib/jobi/message.rb +17 -0
  123. data/lib/jobi/runner.rb +25 -0
  124. data/lib/jobi/utils.rb +13 -0
  125. data/lib/jobi/version.rb +3 -0
  126. metadata +213 -0
@@ -0,0 +1,39 @@
1
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ require 'jobi'
4
+
5
+ class NormalJob < Jobi::Job
6
+ options queue_name: :calculators,
7
+ ack: true,
8
+ consumers: 10
9
+
10
+ after_run :publish_result
11
+
12
+ def initialize(a:, b:)
13
+ @first = a
14
+ @second = b
15
+ end
16
+
17
+ def run
18
+ @sum = @first + @second
19
+ end
20
+
21
+ def publish_result
22
+ puts "publishing result: #{@sum}"
23
+ end
24
+ end
25
+
26
+ Jobi.configure do |config|
27
+ config.rabbitmq
28
+ config.act_as_publisher = true
29
+ config.act_as_consumer = true
30
+ config.jobs = ['NormalJob']
31
+ end
32
+
33
+ started_at = Time.now.to_f
34
+
35
+ (1..ENV['TIMES'].to_i).each do
36
+ NormalJob.run(a: 1, b: 2)
37
+ end
38
+
39
+ puts "took: #{Time.now.to_f - started_at}"
@@ -0,0 +1,31 @@
1
+ require_relative 'lib/jobi/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "jobi"
5
+ spec.version = Jobi::VERSION
6
+ spec.authors = ["Rudy Zidan"]
7
+ spec.email = ["rz.zidan@gmail.com"]
8
+
9
+ spec.summary = "A simple message brokers framework for Ruby."
10
+ spec.description = "Jobi provides a full interaction between your app/micro-service and message brokers."
11
+ spec.homepage = "https://www.github.com/Rudy-Zidan/jobi"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://www.github.com/Rudy-Zidan/jobi"
17
+ spec.metadata["changelog_uri"] = "https://www.github.com/Rudy-Zidan/jobi/blob/master/CHANGELOG.md"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency 'bunny', ">= 2.14.1"
29
+ spec.add_dependency 'logger'
30
+ spec.add_dependency 'rspec', "~> 3.0"
31
+ end
@@ -0,0 +1,64 @@
1
+ require 'jobi/version'
2
+ require 'logger'
3
+ require 'jobi/utils'
4
+ require 'jobi/job'
5
+ require 'jobi/configuration'
6
+ require 'jobi/config/rabbitmq'
7
+ require 'jobi/clients/rabbitmq'
8
+ require 'jobi/consumers/rabbitmq'
9
+
10
+ module Jobi
11
+ class Error < StandardError; end
12
+
13
+ class << self
14
+ include Utils
15
+
16
+ def configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def configure
21
+ yield(configuration)
22
+ configuration.setup_logger
23
+ start
24
+ end
25
+
26
+ def logger
27
+ configuration.logger
28
+ end
29
+
30
+ def session
31
+ @session ||= client_class.new(configuration.send(client_config_method))
32
+ end
33
+
34
+ def client_class_name
35
+ configuration.client.capitalize
36
+ end
37
+
38
+ def consumer?
39
+ configuration.act_as_consumer
40
+ end
41
+
42
+ def publisher?
43
+ configuration.act_as_publisher
44
+ end
45
+
46
+ private
47
+
48
+ def client_config_method
49
+ "#{client_class_name.downcase}_config"
50
+ end
51
+
52
+ def client_class
53
+ constantize("Jobi::Clients::#{client_class_name}")
54
+ end
55
+
56
+ def start
57
+ return unless consumer?
58
+
59
+ configuration.jobs.each do |job_klass|
60
+ constantize(job_klass).consume_messages
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,29 @@
1
+ require 'bunny'
2
+
3
+ module Jobi
4
+ module Clients
5
+ class Rabbitmq
6
+ def initialize(config = Jobi::Config::Rabbitmq.new)
7
+ @connection = Bunny.new(config.to_h)
8
+ @connection.start
9
+ end
10
+
11
+ def channel
12
+ @channel ||= @connection.create_channel
13
+ end
14
+
15
+ def default_exchange
16
+ @default_exchange ||= channel.default_exchange
17
+ end
18
+
19
+ def queue(name:, options: {})
20
+ default_exchange
21
+ channel.queue(name)
22
+ end
23
+
24
+ def publish(message:, queue:, options: {})
25
+ queue.publish(message)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,58 @@
1
+ module Jobi
2
+ module Config
3
+ class Rabbitmq
4
+ attr_accessor :host, :port, :user, :pass, :vhost,
5
+ :heartbeat, :automatically_recover,
6
+ :network_recovery_interval, :threaded, :ssl,
7
+ :continuation_timeout, :frame_max, :auth_mechanism
8
+
9
+ def initialize
10
+ setup_server_config
11
+ setup_network_config
12
+ setup_processing_config
13
+ end
14
+
15
+ def to_h
16
+ {
17
+ host: @host,
18
+ port: @port,
19
+ user: @user,
20
+ pass: @pass,
21
+ vhost: @vhost,
22
+ auth_mechanism: @auth_mechanism,
23
+ heartbeat: @heartbeat,
24
+ automatically_recover: @automatically_recover,
25
+ network_recovery_interval: @network_recovery_interval,
26
+ ssl: @ssl,
27
+ threaded: @threaded,
28
+ continuation_timeout: @continuation_timeout,
29
+ frame_max: @frame_max
30
+ }
31
+ end
32
+
33
+ private
34
+
35
+ def setup_server_config
36
+ @host = '127.0.0.1'
37
+ @port = '5672'
38
+ @user = 'guest'
39
+ @pass = 'guest'
40
+ @vhost = '/'
41
+ @auth_mechanism = 'PLAIN'
42
+ end
43
+
44
+ def setup_network_config
45
+ @heartbeat = :server
46
+ @automatically_recover = true
47
+ @network_recovery_interval = 5.0
48
+ @ssl = false
49
+ end
50
+
51
+ def setup_processing_config
52
+ @threaded = true
53
+ @continuation_timeout = 4000
54
+ @frame_max = 131_072
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,66 @@
1
+ module Jobi
2
+ class Configuration
3
+ attr_accessor :log_file, :act_as_publisher, :act_as_consumer, :jobs
4
+ attr_writer :log_level
5
+ attr_reader :logger, :rabbitmq_config, :client
6
+
7
+ def initialize
8
+ setup_client
9
+ setup_log_config
10
+ setup_logger
11
+ setup_pub_sub_config
12
+ setup_jobs
13
+ rabbitmq
14
+ end
15
+
16
+ def log_level
17
+ case @log_level
18
+ when :info
19
+ Logger::INFO
20
+ when :warn
21
+ Logger::WARN
22
+ when :debug
23
+ Logger::DEBUG
24
+ end
25
+ end
26
+
27
+ def rabbitmq(options = {})
28
+ @rabbitmq_config ||= Jobi::Config::Rabbitmq.new
29
+ @client = :rabbitmq
30
+
31
+ options.keys.each do |key|
32
+ @rabbitmq_config.send("#{key}=", options[key])
33
+ end
34
+ end
35
+
36
+ def setup_logger
37
+ @logger = if @log_file
38
+ Logger.new(@log_file)
39
+ else
40
+ Logger.new(STDOUT)
41
+ end
42
+
43
+ @logger.level = @log_level
44
+ end
45
+
46
+ private
47
+
48
+ def setup_log_config
49
+ @log_level = :info
50
+ @log_file = nil
51
+ end
52
+
53
+ def setup_client
54
+ @client = :rabbitmq
55
+ end
56
+
57
+ def setup_pub_sub_config
58
+ @act_as_publisher = true
59
+ @act_as_consumer = true
60
+ end
61
+
62
+ def setup_jobs
63
+ @jobs = []
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,26 @@
1
+ require 'jobi/runner'
2
+ require 'bunny'
3
+
4
+ module Jobi
5
+ module Consumers
6
+ class Rabbitmq
7
+ def initialize(queue:, ack:)
8
+ @queue = queue
9
+ @ack = ack
10
+ end
11
+
12
+ def consume!
13
+ @queue.subscribe(manual_ack: @ack) do |delivery_info, metadata, payload|
14
+ Jobi::Runner.new(payload: payload).run
15
+ acknowledge!(delivery_info.delivery_tag) if @ack
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def acknowledge!(delivery_tag)
22
+ @queue.channel.acknowledge(delivery_tag, false)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,89 @@
1
+ require 'jobi/utils'
2
+ require 'jobi/message'
3
+
4
+ module Jobi
5
+ class Job
6
+ class << self
7
+ include Utils
8
+
9
+ def options(queue_name:, ack: false, consumers: 5)
10
+ @queue_name = queue_name.to_s
11
+ @ack = ack
12
+ @consumers = consumers
13
+ end
14
+
15
+ def after_run(callback)
16
+ @after_run_callback = callback if callback
17
+ end
18
+
19
+ def run(**args)
20
+ return unless Jobi.publisher?
21
+
22
+ before_start(args)
23
+ start
24
+ rescue Error => e
25
+ Jobi.logger.error('Failed to process the job')
26
+ Jobi.logger.error(e)
27
+ end
28
+
29
+ def consume_messages
30
+ return unless Jobi.consumer?
31
+
32
+ join_queue
33
+
34
+ @consumer_threads = []
35
+ @consumers.times do
36
+ @consumer_threads << Thread.new { consumer.consume! }
37
+ end
38
+
39
+ @consumer_threads.join(&:join)
40
+ end
41
+
42
+ private
43
+
44
+ def before_start(args)
45
+ create_message(args: args)
46
+ log_job_info!
47
+ end
48
+
49
+ def start
50
+ join_queue
51
+ publish_message
52
+ @message.id
53
+ end
54
+
55
+ def consumer
56
+ class_const = constantize("Jobi::Consumers::#{Jobi.client_class_name}")
57
+ class_const.new(queue: @queue, ack: @ack)
58
+ end
59
+
60
+ def join_queue
61
+ @queue = Jobi.session
62
+ .queue(name: @queue_name)
63
+ end
64
+
65
+ def publish_message
66
+ Jobi.session
67
+ .publish(
68
+ message: Marshal.dump(@message),
69
+ queue: @queue
70
+ )
71
+ end
72
+
73
+ def create_message(args:)
74
+ @message = Message.new(
75
+ job_class: self,
76
+ args: args,
77
+ after_run: @after_run_callback
78
+ )
79
+ end
80
+
81
+ def log_job_info!
82
+ Jobi.logger.info("A job has been started with id: [#{@message.id}]")
83
+ Jobi.logger.debug("Queue: '#{@queue_name}'")
84
+ Jobi.logger.debug("Args: #{@message.args}")
85
+ Jobi.logger.debug("Job class: '#{@message.job_class}'")
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,17 @@
1
+ require 'jobi/utils'
2
+
3
+ module Jobi
4
+ class Message
5
+ include Utils
6
+
7
+ attr_reader :id, :job_class, :args, :started_at, :after_run
8
+
9
+ def initialize(job_class:, args:, after_run: nil)
10
+ @id = generate_job_id
11
+ @job_class = job_class
12
+ @args = args
13
+ @started_at = Time.now.to_f
14
+ @after_run = after_run
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ require 'jobi/utils'
2
+
3
+ module Jobi
4
+ class Runner
5
+ include Utils
6
+
7
+ def initialize(payload:)
8
+ @message = Marshal.load(payload)
9
+ end
10
+
11
+ def run
12
+ job = @message.job_class.new(**@message.args)
13
+ job.run
14
+ job.send(@message.after_run) if @message.after_run
15
+
16
+ Jobi.logger.info("Completed in: #{job_duration}")
17
+ end
18
+
19
+ private
20
+
21
+ def job_duration
22
+ Time.now.to_f - @message.started_at
23
+ end
24
+ end
25
+ end