soda-core 0.0.1 → 0.0.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64b5b8109a7a40030971f07e90281ded5f2611c7f46eb02602c8d4ed73c57a20
4
- data.tar.gz: 69a55b5427f9b3c21207dced6b0b5a755a707d20e8eec69784cbfb49269d15d0
3
+ metadata.gz: 88762defaf2339fa04bbe565b9238d6edf1b6a3df2f68bd85c4bee62aa1e5397
4
+ data.tar.gz: b6b0934e5f9c7038859eed8dc28c8f6280223384e06f640c9ed907d8900f0392
5
5
  SHA512:
6
- metadata.gz: e6c98fe461e363df472afd2377d2ad311a7c76dfa883985ee1584da75bead837f7891fd97176774ceacccb5f91cae298a61000cd905d1ce1d6c049cac355f712
7
- data.tar.gz: 6ed06d08b7fc9a6d01cabcd3156d88a26dd259a1354b2f651afaa626fe23c9eb8abae1dc3b0d1183d6ec55d2dab42f90fb070c945573783b9e3be69ed5f124b1
6
+ metadata.gz: b1ccfad1b68e0f0e63dfd92e625a76896693b77a5314ae557a486af21a31a6f7c48c46f8495a3e8d4d088687bacff85636016181b51a2dcd24c6c29395372e79
7
+ data.tar.gz: 22a7dc79c110eae266bb0b4323ec474e22cc38ef02dd79bf58c1f9c26233fe0c8a10ace9906ec7bd99e17b3cc00a9fc4e245f6bd103dc2c98bd9d3876e455432
data/lib/soda.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require "aws-sdk-sqs"
2
+ require "erb"
2
3
  require "json"
3
4
  require "logger"
4
5
  require "securerandom"
5
6
  require "set"
7
+ require "yaml"
6
8
 
7
9
  require "soda/tools"
8
10
 
@@ -18,6 +20,7 @@ require "soda/retrier"
18
20
  require "soda/worker"
19
21
 
20
22
  module Soda
23
+ NAME = "Soda"
21
24
  DEFAULTS = {
22
25
  concurrency: 10,
23
26
  }
@@ -64,6 +67,40 @@ module Soda
64
67
  end
65
68
  end
66
69
 
70
+ def queues=(configs)
71
+ queues do |registry|
72
+ names = []
73
+ configs.each do |cfg|
74
+ # Find or create the queue.
75
+ queue = registry.select(cfg.delete(:name))
76
+
77
+ # Update the attributes
78
+ name = queue.name
79
+ url = cfg.delete(:url) || queue.url
80
+
81
+ registry.register(
82
+ name,
83
+ url,
84
+ queue.options.merge(cfg),
85
+ )
86
+
87
+ names << queue.name
88
+ end
89
+
90
+ # For queues that are not included in the command, set their weight to
91
+ # zero so they can still be accessed.
92
+ registry.each do |queue|
93
+ unless names.include?(queue.name)
94
+ registry.register(
95
+ queue.name,
96
+ queue.url,
97
+ queue.options.merge(weight: 0),
98
+ )
99
+ end
100
+ end
101
+ end
102
+ end
103
+
67
104
  def queue(name)
68
105
  queues.select(name).tap do |queue|
69
106
  yield(queue) if block_given?
data/lib/soda/cli.rb CHANGED
@@ -12,6 +12,8 @@ module Soda
12
12
  INT,
13
13
  ].freeze
14
14
 
15
+ DEFAULT_CONFIG = "config/soda.yml"
16
+
15
17
  def self.start
16
18
  new.run
17
19
  end
@@ -23,6 +25,21 @@ module Soda
23
25
  def run
24
26
  build_options
25
27
 
28
+ logger.info("🥤 %s v%s" % [Soda::NAME, Soda::VERSION])
29
+
30
+ if rails?
31
+ if Rails::VERSION::MAJOR >= 5
32
+ require "./config/application.rb"
33
+ require "./config/environment.rb"
34
+ require "soda/rails"
35
+ require "soda/extensions/active_job"
36
+
37
+ logger.info("Loaded Rails v%s application." % ::Rails.version)
38
+ else
39
+ raise "Not compatible with Rails v%s!" % Rails.version
40
+ end
41
+ end
42
+
26
43
  manager = Manager.new
27
44
  manager.start
28
45
 
@@ -70,12 +87,20 @@ module Soda
70
87
  parser = build_option_parser(opts)
71
88
  parser.parse!(argv)
72
89
 
90
+ if File.exists?(default_config = File.expand_path(DEFAULT_CONFIG))
91
+ opts[:config] ||= default_config
92
+ end
93
+
94
+ if (file = opts.delete(:config_file))
95
+ parse_config_file(opts, opts.delete(:config))
96
+ end
97
+
73
98
  if (req = opts.delete(:require))
74
99
  require(req)
75
100
  end
76
101
 
77
- if (queues_opt = opts.delete(:queues))
78
- parse_queues(queues_opt)
102
+ if (queues = opts.delete(:queues))
103
+ Soda.queues = queues
79
104
  end
80
105
 
81
106
  options = Soda.options
@@ -88,46 +113,38 @@ module Soda
88
113
  opts.merge!(require: val)
89
114
  end
90
115
 
91
- o.on("-q", "--queues [PATH]", "Queue to listen to, with optional weights") do |val|
92
- opts.merge!(queues: opts.fetch(:queues, []).push(val.split(/\,+/)))
116
+ o.on("-q", "--queue QUEUE[,WEIGHT]", "Queue to listen to, with optional weights") do |val|
117
+ name, weight = val.split(/,/)
118
+ opts.merge!(queues: opts.fetch(:queues, []).push(name: name, weight: weight))
119
+ end
120
+
121
+ o.on("-c", "--concurrency [INT]", "Number of processor threads") do |val|
122
+ opts.merge!(concurrency: Integer(val))
93
123
  end
94
124
  end
95
125
  end
96
126
 
97
- def parse_queues(opt)
98
- Soda.queues do |registry|
99
- opt.each do |name, weight|
100
- # Find or create the queue.
101
- queue = registry.select(name)
102
-
103
- if weight
104
- # Replace the queue with the same one, except mutate the options to
105
- # include the specified weight.
106
- registry.register(
107
- queue.name,
108
- queue.url,
109
- queue.options.merge(weight: weight.to_i),
110
- )
111
- end
112
- end
127
+ def parse_config_file(opts = {}, file)
128
+ path = File.expand_path(file)
113
129
 
114
- # For queues that are not included in the command, set their weight to
115
- # zero so they can still be accessed.
116
- names = opt.map(&:first)
117
- registry.each do |queue|
118
- unless names.include?(queue.name)
119
- registry.register(
120
- queue.name,
121
- queue.url,
122
- queue.options.merge(weight: 0),
123
- )
124
- end
125
- end
130
+ unless File.exists?(path)
131
+ raise "File does not exist: %s"
126
132
  end
133
+
134
+ opts.merge!(
135
+ deep_symbolize_keys(
136
+ YAML.load(
137
+ ERB.new(File.read(path)).result,
138
+ ),
139
+ ),
140
+ )
127
141
  end
128
142
 
129
- def options
130
- Soda.options
143
+ def rails?
144
+ require "rails"
145
+ defined?(::Rails)
146
+ rescue LoadError
147
+ false
131
148
  end
132
149
  end
133
150
  end
data/lib/soda/client.rb CHANGED
@@ -1,11 +1,27 @@
1
1
  module Soda
2
2
  class Client
3
+ DEFAULTS = {
4
+ "retry" => true,
5
+ "delay" => 0,
6
+ }
7
+
8
+ class << self
9
+ def push(*args)
10
+ new.push(*args)
11
+ end
12
+
13
+ def middleware
14
+ Soda.client_middleware
15
+ end
16
+ end
17
+
3
18
  def push(item)
4
- copy = normalize!(item)
5
- mw = Soda.client_middleware
19
+ copy = normalize!(item)
6
20
 
7
- mw.use(item["klass"], copy, copy["queue"]) do
8
- Soda.queue(copy["queue"]) do |queue|
21
+ self.class.middleware.use(item["klass"], copy, copy["queue"]) do
22
+ jid = copy["id"]
23
+ jid.tap do
24
+ queue = Soda.queue(copy["queue"])
9
25
  queue.push_in(copy["delay"], Soda.dump_json(copy))
10
26
  end
11
27
  end
@@ -14,19 +30,20 @@ module Soda
14
30
  private
15
31
 
16
32
  def normalize!(item)
17
- item.dup.tap do |copy|
18
- copy.keys.each do |key|
19
- copy.merge!(String(key) => copy[key])
33
+ item = DEFAULTS.merge(item)
34
+ item.tap do
35
+ item.keys.each do |key|
36
+ item.merge!(String(key) => item.delete(key))
20
37
  end
21
38
 
22
39
  id = SecureRandom.base64(10)
23
- klass = copy["klass"].to_s
24
- delay = Integer(copy["delay"]) || 0
25
- queue = copy["queue"] || Soda.default_queue!.name
40
+ klass = item["klass"].to_s
41
+ delay = item["delay"].to_i
42
+ queue = item["queue"] || Soda.default_queue!.name
26
43
 
27
44
  # TODO: add validation
28
45
  #
29
- copy.merge!(
46
+ item.merge!(
30
47
  "id" => id,
31
48
  "klass" => klass,
32
49
  "delay" => delay,
@@ -0,0 +1,27 @@
1
+ module ActiveJob
2
+ module QueueAdapters
3
+ class SodaAdapter
4
+ def enqueue(job)
5
+ enqueue_at(job, Time.now)
6
+ end
7
+
8
+ def enqueue_at(job, ts)
9
+ job.provider_job_id = ::Soda::Client.push(
10
+ "klass" => JobWrapper,
11
+ "wrapped" => job.class,
12
+ "queue" => job.queue_name,
13
+ "delay" => [0, (ts - Time.now).to_i].max,
14
+ "args" => [job.serialize],
15
+ )
16
+ end
17
+
18
+ class JobWrapper
19
+ include ::Soda::Worker
20
+
21
+ def perform(data = {})
22
+ Base.execute(data.merge("provider_job_id" => id))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/soda/fetcher.rb CHANGED
@@ -37,7 +37,7 @@ module Soda
37
37
  start = now
38
38
  logger.debug(%(fetching from "%s") % queue.name)
39
39
 
40
- queue.pop.tap do |msgs|
40
+ (queue.pop || []).tap do |msgs|
41
41
  logger.debug(%(fetched %d message(s) from "%s" (%fms)) % [msgs.count, queue.name, (now - start)])
42
42
  end
43
43
  end
@@ -45,7 +45,7 @@ module Soda
45
45
  yield
46
46
  else
47
47
  entry = copy.shift
48
- inst = entry.klass.new(*entry.args)
48
+ inst = entry.build
49
49
 
50
50
  inst.call(*args) do
51
51
  traverse(copy, args) { yield }
@@ -2,6 +2,12 @@ module Soda
2
2
  class Processor
3
3
  include Tools
4
4
 
5
+ class << self
6
+ def middleware
7
+ Soda.server_middleware
8
+ end
9
+ end
10
+
5
11
  def initialize(manager)
6
12
  @manager = manager
7
13
  @retrier = Retrier.new
@@ -31,7 +37,7 @@ module Soda
31
37
 
32
38
  def run
33
39
  until stopped?
34
- msgs = fetch
40
+ msgs = fetch || []
35
41
  msgs.each(&method(:process))
36
42
  end
37
43
  rescue Exception => ex
@@ -50,7 +56,9 @@ module Soda
50
56
  def process(msg)
51
57
  if (job_hash = parse_job(msg.str))
52
58
  job_logger.with(job_hash) do
53
- execute(job_hash, msg)
59
+ reloader.wrap do
60
+ execute(job_hash, msg)
61
+ end
54
62
  end
55
63
  else
56
64
  # We can't process the work because the JSON is invalid, so we have to
@@ -69,8 +77,7 @@ module Soda
69
77
  worker = constantize(klass)
70
78
 
71
79
  retrier.retry(job_hash, msg) do
72
- middleware = Soda.server_middleware
73
- middleware.use(worker, job_hash, queue.name, msg) do
80
+ self.class.middleware.use(worker, job_hash, queue.name, msg) do
74
81
  instance = worker.new(job_hash)
75
82
  instance.perform(*job_hash["args"])
76
83
  end
@@ -98,5 +105,21 @@ module Soda
98
105
  def constantize(str)
99
106
  Object.const_get(str)
100
107
  end
108
+
109
+ # To support Rails reloading from the CLI context, the following code find
110
+ # the Rails reloader or stubs a no-op one.
111
+ class StubReloader
112
+ def wrap; yield; end
113
+ end
114
+
115
+ def reloader
116
+ @reloader ||=
117
+ if defined?(::Soda::Rails)
118
+ application = ::Rails.application
119
+ application.reloader
120
+ else
121
+ StubReloader.new
122
+ end
123
+ end
101
124
  end
102
125
  end
data/lib/soda/rails.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Soda
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
data/lib/soda/retrier.rb CHANGED
@@ -24,7 +24,7 @@ module Soda
24
24
  if ret.is_a?(Numeric)
25
25
  ret < msg.receive_count
26
26
  else
27
- ret
27
+ !!ret
28
28
  end
29
29
  end
30
30
 
data/lib/soda/tools.rb CHANGED
@@ -19,5 +19,24 @@ module Soda
19
19
  def now
20
20
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
21
21
  end
22
+
23
+ def deep_symbolize_keys(hash)
24
+ transform_value = -> (value) {
25
+ case value
26
+ when Hash
27
+ deep_symbolize_keys(value)
28
+ when Array
29
+ value.map { |val| transform_value.call(val) }
30
+ else
31
+ value
32
+ end
33
+ }
34
+
35
+ {}.tap do |memo|
36
+ hash.each do |key, value|
37
+ memo.merge!(key.to_sym => transform_value.call(value))
38
+ end
39
+ end
40
+ end
22
41
  end
23
42
  end
data/lib/soda/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Soda
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.8".freeze
3
3
  end
data/lib/soda/worker.rb CHANGED
@@ -28,6 +28,7 @@ module Soda
28
28
  )
29
29
  end
30
30
  end
31
+ alias_method :perform_at, :perform_in
31
32
 
32
33
  private
33
34
 
@@ -70,6 +71,12 @@ module Soda
70
71
  @options = options
71
72
  end
72
73
 
74
+ %i[id].each do |method|
75
+ define_method(method) do
76
+ options.fetch(String(method))
77
+ end
78
+ end
79
+
73
80
  private
74
81
 
75
82
  attr_reader :options
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: soda-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Portes Chaikin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-15 00:00:00.000000000 Z
11
+ date: 2020-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-sqs
@@ -63,6 +63,7 @@ files:
63
63
  - lib/soda.rb
64
64
  - lib/soda/cli.rb
65
65
  - lib/soda/client.rb
66
+ - lib/soda/extensions/active_job.rb
66
67
  - lib/soda/fetcher.rb
67
68
  - lib/soda/logger.rb
68
69
  - lib/soda/manager.rb
@@ -70,6 +71,7 @@ files:
70
71
  - lib/soda/processor.rb
71
72
  - lib/soda/queue.rb
72
73
  - lib/soda/queues/registry.rb
74
+ - lib/soda/rails.rb
73
75
  - lib/soda/retrier.rb
74
76
  - lib/soda/tools.rb
75
77
  - lib/soda/version.rb