subserver 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ module Subserver
2
+ class MessageLogger
3
+
4
+ def call(message)
5
+ start = Time.now
6
+ logger.info("start")
7
+ yield
8
+ logger.info("done: #{elapsed(start)} sec")
9
+ rescue Exception
10
+ logger.info("fail: #{elapsed(start)} sec")
11
+ raise
12
+ end
13
+
14
+ private
15
+
16
+ def elapsed(start)
17
+ (Time.now - start).round(3)
18
+ end
19
+
20
+ def logger
21
+ Subserver.logger
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module Subserver
3
+ module Middleware
4
+ class ActiveRecord
5
+
6
+ def initialize
7
+ # With Rails 5+ we must use the Reloader **always**.
8
+ # The reloader handles code loading and db connection management.
9
+ if defined?(::Rails) && ::Rails::VERSION::MAJOR >= 5
10
+ raise ArgumentError, "Rails 5 no longer needs or uses the ActiveRecord middleware."
11
+ end
12
+ end
13
+
14
+ def call(*args)
15
+ yield
16
+ ensure
17
+ ::ActiveRecord::Base.clear_active_connections!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+ module Subserver
3
+ # Middleware is code configured to run before/after
4
+ # a message is processed. It is patterned after Rack
5
+ # middleware.
6
+ #
7
+ # To modify middleware for the server, just call
8
+ # with another block:
9
+ #
10
+ # Subserver.configure do |config|
11
+ # config.middleware do |chain|
12
+ # chain.add MyServerHook
13
+ # chain.remove ActiveRecord
14
+ # end
15
+ # end
16
+ #
17
+ # To insert immediately preceding another entry:
18
+ #
19
+ # Subserver.configure do |config|
20
+ # config.middleware do |chain|
21
+ # chain.insert_before ActiveRecord, MyServerHook
22
+ # end
23
+ # end
24
+ #
25
+ # To insert immediately after another entry:
26
+ #
27
+ # Subserver.configure do |config|
28
+ # config.middleware do |chain|
29
+ # chain.insert_after ActiveRecord, MyServerHook
30
+ # end
31
+ # end
32
+ #
33
+ # This is an example of a minimal server middleware:
34
+ #
35
+ # class MyServerHook
36
+ # def call(subscriber_class, message)
37
+ # puts "Before work"
38
+ # yield
39
+ # puts "After work"
40
+ # end
41
+ # end
42
+ #
43
+ #
44
+ module Middleware
45
+ class Chain
46
+ include Enumerable
47
+ attr_reader :entries
48
+
49
+ def initialize_copy(copy)
50
+ copy.instance_variable_set(:@entries, entries.dup)
51
+ end
52
+
53
+ def each(&block)
54
+ entries.each(&block)
55
+ end
56
+
57
+ def initialize
58
+ @entries = []
59
+ yield self if block_given?
60
+ end
61
+
62
+ def remove(klass)
63
+ entries.delete_if { |entry| entry.klass == klass }
64
+ end
65
+
66
+ def add(klass, *args)
67
+ remove(klass) if exists?(klass)
68
+ entries << Entry.new(klass, *args)
69
+ end
70
+
71
+ def prepend(klass, *args)
72
+ remove(klass) if exists?(klass)
73
+ entries.insert(0, Entry.new(klass, *args))
74
+ end
75
+
76
+ def insert_before(oldklass, newklass, *args)
77
+ i = entries.index { |entry| entry.klass == newklass }
78
+ new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
79
+ i = entries.index { |entry| entry.klass == oldklass } || 0
80
+ entries.insert(i, new_entry)
81
+ end
82
+
83
+ def insert_after(oldklass, newklass, *args)
84
+ i = entries.index { |entry| entry.klass == newklass }
85
+ new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
86
+ i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
87
+ entries.insert(i+1, new_entry)
88
+ end
89
+
90
+ def exists?(klass)
91
+ any? { |entry| entry.klass == klass }
92
+ end
93
+
94
+ def retrieve
95
+ map(&:make_new)
96
+ end
97
+
98
+ def clear
99
+ entries.clear
100
+ end
101
+
102
+ def invoke(*args)
103
+ chain = retrieve.dup
104
+ traverse_chain = lambda do
105
+ if chain.empty?
106
+ yield
107
+ else
108
+ chain.shift.call(*args, &traverse_chain)
109
+ end
110
+ end
111
+ traverse_chain.call
112
+ end
113
+ end
114
+
115
+ class Entry
116
+ attr_reader :klass
117
+
118
+ def initialize(klass, *args)
119
+ @klass = klass
120
+ @args = args
121
+ end
122
+
123
+ def make_new
124
+ @klass.new(*@args)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,20 @@
1
+ require "google/cloud/pubsub"
2
+
3
+ module Subserver
4
+ module Pubsub
5
+ def self.client
6
+ defined?(@client) ? @client : initialize_client
7
+ end
8
+
9
+ def self.initialize_client
10
+ @client = Google::Cloud::Pubsub.new(
11
+ project_id: options[:project_id],
12
+ credentials: File.expand_path(options[:credentials])
13
+ )
14
+ end
15
+
16
+ def self.options
17
+ Subserver.options
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,57 @@
1
+ module Subserver
2
+ class Rails < ::Rails::Engine
3
+ # We need to setup this up before any application configuration which might
4
+ # change Subserver middleware.
5
+ #
6
+ # This hook happens after `Rails::Application` is inherited within
7
+ # config/application.rb and before config is touched, usually within the
8
+ # class block. Definitely before config/environments/*.rb and
9
+ # config/initializers/*.rb.
10
+ config.before_configuration do
11
+ if ::Rails::VERSION::MAJOR < 5 && defined?(::ActiveRecord)
12
+ Subserver.middleware do |chain|
13
+ require 'subserver/middleware/active_record'
14
+ chain.add Subserver::Middleware::ActiveRecord
15
+ end
16
+ end
17
+ end
18
+
19
+ config.after_initialize do
20
+ # This hook happens after all initializers are run, just before returning
21
+ # from config/environment.rb back to subserver/cli.rb.
22
+ # We have to add the reloader after initialize to see if cache_classes has
23
+ # been turned on.
24
+ #
25
+ Subserver.configure do |_|
26
+
27
+ Subserver.options[:subscriber_dir] = ::Rails.root.join('app', 'subscribers')
28
+
29
+ if ::Rails::VERSION::MAJOR >= 5
30
+ Subserver.options[:reloader] = Subserver::Rails::Reloader.new
31
+ end
32
+ end
33
+ end
34
+
35
+ class Reloader
36
+ def initialize(app = ::Rails.application)
37
+ @app = app
38
+ end
39
+
40
+ def call
41
+ @app.reloader.wrap do
42
+ yield
43
+ end
44
+ end
45
+
46
+ def inspect
47
+ "#<Subserver::Rails::Reloader @app=#{@app.class.name}>"
48
+ end
49
+ end
50
+ end if defined?(::Rails)
51
+ end
52
+
53
+ if defined?(::Rails) && ::Rails::VERSION::MAJOR < 4
54
+ $stderr.puts("**************************************************")
55
+ $stderr.puts("WARNING: Subserver is not supported on Rails < 4. Please Update to a newer version of Rails.")
56
+ $stderr.puts("**************************************************")
57
+ end
@@ -0,0 +1,25 @@
1
+ module Subserver
2
+ module Subscriber
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ def logger
9
+ Subserver.logger
10
+ end
11
+
12
+ module ClassMethods
13
+ @subserver_options
14
+
15
+ def subserver_options(opts={})
16
+ (@subserver_options ||= Subserver.default_subscriber_options).merge!(opts)
17
+ end
18
+
19
+ def get_subserver_options
20
+ @subserver_options
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require 'socket'
3
+ require 'securerandom'
4
+ require 'subserver/exception_handler'
5
+
6
+ module Subserver
7
+ ##
8
+ # This module is part of Subserver core and not intended for extensions.
9
+ #
10
+ module Util
11
+ include ExceptionHandler
12
+
13
+ EXPIRY = 60 * 60 * 24
14
+
15
+ def watchdog(last_words)
16
+ yield
17
+ rescue Exception => ex
18
+ handle_exception(ex, { context: last_words })
19
+ raise ex
20
+ end
21
+
22
+ def safe_thread(name, &block)
23
+ Thread.new do
24
+ Thread.current['subserver_label'] = name
25
+ watchdog(name, &block)
26
+ end
27
+ end
28
+
29
+ def logger
30
+ Subserver.logger
31
+ end
32
+
33
+ def hostname
34
+ ENV['DYNO'] || Socket.gethostname
35
+ end
36
+
37
+ def process_nonce
38
+ @@process_nonce ||= SecureRandom.hex(6)
39
+ end
40
+
41
+ def identity
42
+ @@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
43
+ end
44
+
45
+ def fire_event(event, options={})
46
+ reverse = options[:reverse]
47
+ reraise = options[:reraise]
48
+
49
+ arr = Subserver.options[:lifecycle_events][event]
50
+ arr.reverse! if reverse
51
+ arr.each do |block|
52
+ begin
53
+ block.call
54
+ rescue => ex
55
+ handle_exception(ex, { context: "Exception during Subserver lifecycle event.", event: event })
56
+ raise ex if reraise
57
+ end
58
+ end
59
+ arr.clear
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Subserver
3
+ VERSION = "0.1.0"
4
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: subserver
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Scott Hill
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: google-cloud-pubsub
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.31'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.31.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.31'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.31.0
33
+ description: Subserver is a background server process for processing messages from
34
+ Google Pub/Sub.
35
+ email:
36
+ executables:
37
+ - subserver
38
+ extensions: []
39
+ extra_rdoc_files: []
40
+ files:
41
+ - CHANGELOG.md
42
+ - CONTRIBUTING.md
43
+ - LICENSE
44
+ - README.md
45
+ - bin/subserver
46
+ - lib/subserver.rb
47
+ - lib/subserver/cli.rb
48
+ - lib/subserver/exception_handler.rb
49
+ - lib/subserver/health.rb
50
+ - lib/subserver/launcher.rb
51
+ - lib/subserver/listener.rb
52
+ - lib/subserver/logging.rb
53
+ - lib/subserver/manager.rb
54
+ - lib/subserver/message_logger.rb
55
+ - lib/subserver/middleware/active_record.rb
56
+ - lib/subserver/middleware/chain.rb
57
+ - lib/subserver/pubsub.rb
58
+ - lib/subserver/rails.rb
59
+ - lib/subserver/subscriber.rb
60
+ - lib/subserver/util.rb
61
+ - lib/subserver/version.rb
62
+ homepage: https://github.com/lifechurch/subserver/
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.3.1
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.5.2.3
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Simple background server process for Google Pub/Sub.
86
+ test_files: []