postjob 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5166951ca6511e2d599b44493288dc1ac43f712a
4
- data.tar.gz: f32f0a3255c782e5998d8c77b68b8e5656f356ae
2
+ SHA256:
3
+ metadata.gz: 4fcc6c18ad2be617abafbdfb1dc6258a435223a0fbd0110d86806b6da47fc5d6
4
+ data.tar.gz: 98a5384f074100eb291d6357993969fea6da7b058177ba513f79329bc558d9c9
5
5
  SHA512:
6
- metadata.gz: 40f67bc7082fc9df09bda3c4425297ffa7ce6ddaed0feef2bb849a3b9e5773a06a6b5349972c8f5c8f0d5c7cefb4cc7b1e0f64ce8da336d52fa0239a5e45fde1
7
- data.tar.gz: d5928e359abfc51dbeab4a1054d435dbba799ba5107296ccd2b65494e2c9853fca350ab88eba9f291ead1736026b74e04371d6830afad77a8b08bb60020a1ed8
6
+ metadata.gz: 212977ecccd8f11df6d0e4950155cc47f18c1d6c979b045109b5f981d5eb1e7a68c90aefae78708385458d102870f3d3c9357d8861fa9a54fd5f58c8db9f4f2b
7
+ data.tar.gz: c31025b4869355c9dd13ed5e119c43133b9ae794eecaa2c12f92eb07c1e476f48c32cefa182757358b7236b64febe34283183d5f492a71116e86569adb36e724
@@ -1,10 +1,14 @@
1
1
  module Postjob::CLI
2
2
  # Prints version info
3
3
  def version
4
- connect_to_database!
5
-
6
4
  gem_version = Gem.loaded_specs["postjob"].version
7
- puts "postjob/ruby: #{gem_version}"
8
- puts "postjob/queue: #{::Postjob::Queue.version}"
5
+ puts "This is postjob (ruby) #{gem_version}"
6
+
7
+ begin
8
+ connect_to_database!
9
+ puts "postjob/queue: #{::Postjob::Queue.version}"
10
+ rescue StandardError
11
+ Postjob.logger.warn "Cannot read postjob schema version. Database might not be configured or migrated."
12
+ end
9
13
  end
10
14
  end
data/lib/postjob/job.rb CHANGED
@@ -5,7 +5,7 @@ require_relative "./record"
5
5
  #
6
6
  class Postjob::Job < Postjob::Record
7
7
  def self.find(job_id)
8
- scope = Postjob::Queue.search(id: job_id)
8
+ scope = Postjob::Queue.search("postjobs", id: job_id)
9
9
  Simple::SQL.ask(scope, into: Postjob::Job)
10
10
  end
11
11
 
@@ -0,0 +1,23 @@
1
+ # This module holds cached database info, to be used by the search functions.
2
+ module Postjob::Queue::DatabaseInfo
3
+ extend self
4
+
5
+ # returns a Hash of column_name => column_type. Names are available both as
6
+ # Symbol and as String.
7
+ def column_types(table_name)
8
+ @column_types ||= {}
9
+ @column_types[table_name] ||= begin
10
+ column_info = ::Simple::SQL::Reflection.column_info(table_name)
11
+ hsh = {}
12
+ column_info.each do |column, rec|
13
+ hsh[column.to_sym] = hsh[column.to_s] = rec.data_type
14
+ end
15
+ hsh
16
+ end
17
+ end
18
+
19
+ def table_columns(table_name)
20
+ @table_columns ||= {}
21
+ @table_columns[table_name] ||= Simple::SQL::Reflection.columns(table_name)
22
+ end
23
+ end
@@ -1,82 +1,120 @@
1
+ require_relative "./database_info"
2
+
1
3
  module Postjob::Queue
2
- DEFAULT_ATTRIBUTES = [
3
- "workflow || COALESCE('@' || workflow_version, '') || args AS job",
4
- "COALESCE((results->0)::varchar, error_message) AS result",
5
- "(now() at time zone 'utc') - created_at AS age",
6
- "updated_at - created_at AS runtime"
7
- ]
8
-
9
- def default_attributes
10
- @default_attributes ||= begin
11
- column_names = Simple::SQL::Reflection.columns("#{SCHEMA_NAME}.postjobs")
12
- column_names + DEFAULT_ATTRIBUTES
13
- end
14
- end
4
+ class ScopeBuilder
5
+ SCHEMA_NAME = ::Postjob::Queue::SCHEMA_NAME
15
6
 
16
- # Builds a search scope (see Simple::SQL::Scope) for the passed in filter criteria.
17
- #
18
- # Note that the search scope is unsorted.
19
- def search(filter = {})
20
- expect! filter => Hash
21
-
22
- # extract options
23
- filter = filter.dup
24
- root_only = filter.delete(:root_only) || false
25
- attributes = filter.delete(:attributes) || default_attributes
26
- expect! attributes => Array
27
-
28
- # build Scope
29
- scope = Simple::SQL::Scope.new("SELECT #{attributes.join(", ")} FROM #{SCHEMA_NAME}.postjobs")
30
- scope = scope.where("root_id=id") if root_only
31
- scope = apply_filters(scope, filter) if filter && !filter.empty?
32
- scope
33
- end
7
+ class << self
8
+ def build(model:, filter:, attributes:)
9
+ builder(model).send(:scope, filter: filter, attributes: attributes)
10
+ end
34
11
 
35
- private
12
+ private
36
13
 
37
- def apply_filters(scope, filter)
38
- filter.each_key do |key|
39
- expect! key => [Symbol, String]
14
+ # Returns the name of the column used for dynamic filtering.
15
+ def dynamic_filter(model)
16
+ case model
17
+ when "postjobs" then "tags"
18
+ else "attributes"
19
+ end
20
+ end
21
+
22
+ def builder(model)
23
+ @builders ||= {}
24
+ @builders[model] ||= new(model: model, dynamic_filter: dynamic_filter(model))
25
+ end
40
26
  end
41
27
 
42
- converted_filters = filter.inject({}) do |hsh, (key, value)|
43
- matches = convert_filter_value(value, key: key)
44
- matches = matches.first if matches.length == 1
45
- hsh.update key => matches
28
+ private
29
+
30
+ def initialize(model:, dynamic_filter:)
31
+ @model = model
32
+ @table_name = "#{SCHEMA_NAME}.#{model}"
33
+ @column_types = ::Postjob::Queue::DatabaseInfo.column_types(@table_name)
34
+ @dynamic_filter = dynamic_filter
46
35
  end
47
36
 
48
- column_filters, tags_filters = converted_filters.partition { |key, _| !column_types[key].nil? }
37
+ def scope(filter:, attributes:)
38
+ expect! filter => [Hash, nil]
39
+ expect! attributes => Array
49
40
 
50
- scope = scope.where(Hash[column_filters])
51
- scope = scope.where(tags: Hash[tags_filters])
52
- scope
53
- end
41
+ # build Scope
42
+ scope = Simple::SQL::Scope.new("SELECT #{attributes.join(", ")} FROM #{@table_name}")
43
+ scope = apply_filters(scope, filter) if filter && !filter.empty?
44
+ scope
45
+ end
46
+
47
+ # apply the filter hash onto the passed in scope. This matches all filters
48
+ # with a name which is matching a column name against the column values.
49
+ # It matches every other filter value against an entry in the
50
+ # dynamic_filter column.
51
+ def apply_filters(scope, filter)
52
+ filter.each_key do |key|
53
+ expect! key => [Symbol, String]
54
+ end
54
55
 
55
- class << self
56
- def column_types
57
- @column_types ||= _column_types
56
+ converted_filters = filter.inject({}) do |hsh, (key, value)|
57
+ matches = convert_filter_value(value, key: key)
58
+ matches = matches.first if matches.length == 1
59
+ hsh.update key => matches
60
+ end
61
+
62
+ column_filters, tags_filters = converted_filters.partition { |key, _| !@column_types[key].nil? }
63
+
64
+ scope = scope.where(Hash[column_filters]) unless column_filters.empty?
65
+ scope = scope.where(@dynamic_filter => Hash[tags_filters]) unless tags_filters.empty?
66
+
67
+ scope
58
68
  end
59
69
 
60
- private
70
+ def convert_filter_value(value, key:)
71
+ value = Array(value)
61
72
 
62
- def _column_types
63
- column_info = ::Simple::SQL::Reflection.column_info("#{SCHEMA_NAME}.postjobs")
64
- hsh = {}
65
- column_info.each do |column, rec|
66
- hsh[column.to_sym] = hsh[column.to_s] = rec.data_type
73
+ case @column_types[key]
74
+ when "bigint" then value.map { |v| Integer(v) }
75
+ when "integer" then value.map { |v| Integer(v) }
76
+ when nil then value.map(&:to_s) + value.grep(/\A-?\d+\z/).map(&:to_i)
77
+ else value.map(&:to_s)
67
78
  end
68
- hsh
69
79
  end
70
80
  end
71
81
 
72
- def convert_filter_value(value, key:)
73
- value = Array(value)
82
+ # Builds a search scope (see Simple::SQL::Scope) for the passed in filter criteria.
83
+ #
84
+ # Parameters:
85
+ #
86
+ # - model: the name of the postjob model, e.g. "postjobs", "events", ..
87
+ # - filter: a Hash of filter values and other options. options are denoted
88
+ # by a Symbol key.
89
+ #
90
+ # Note that the search scope is unsorted.
91
+ def search(model, filter = {})
92
+ expect! model => String
93
+ expect! filter => [Hash, nil]
74
94
 
75
- case Postjob::Queue.column_types[key]
76
- when "bigint" then value.map { |v| Integer(v) }
77
- when "integer" then value.map { |v| Integer(v) }
78
- when nil then value.map(&:to_s) + value.grep(/\A-?\d+\z/).map(&:to_i)
79
- else value.map(&:to_s)
80
- end
95
+ filter = filter ? filter.dup : {}
96
+
97
+ root_only = filter.delete(:root_only)
98
+ attributes = filter.delete(:attributes) || default_search_attributes(model)
99
+
100
+ scope = ScopeBuilder.build(model: model, filter: filter, attributes: attributes)
101
+ scope = scope.where("root_id=id") if root_only && model == "postjobs"
102
+ scope
103
+ end
104
+
105
+ private
106
+
107
+ # describes which attributes should be returned in addition to a model's columns.
108
+ EXTRA_SEARCH_ATTRIBUTES = {
109
+ "postjobs" => [
110
+ "workflow || COALESCE('@' || workflow_version, '') || args AS job",
111
+ "COALESCE((results->0)::varchar, error_message) AS result",
112
+ "(now() at time zone 'utc') - created_at AS age",
113
+ "updated_at - created_at AS runtime"
114
+ ]
115
+ }
116
+
117
+ def default_search_attributes(model)
118
+ DatabaseInfo.table_columns("#{SCHEMA_NAME}.#{model}") + (EXTRA_SEARCH_ATTRIBUTES[model] || [])
81
119
  end
82
120
  end
@@ -14,14 +14,14 @@ end
14
14
 
15
15
  # A worker worker_session
16
16
  class Postjob::WorkerSession < Postjob::Record
17
- HOST_ID_STORE = ".postjob.host_id"
17
+ HOST_ID_PATH = ".postjob.host_id"
18
18
 
19
19
  class << self
20
20
  # Starts a worker session.
21
21
  def start!(workflows_with_versions)
22
22
  worker_session = nil
23
23
 
24
- AtomicStore.with(HOST_ID_STORE) do |host_id|
24
+ AtomicStore.with(HOST_ID_PATH) do |host_id|
25
25
  host_id ||= ::Postjob::Host.register
26
26
  Postjob.logger.debug "Starting worker_session w/host_id #{host_id.inspect}"
27
27
  worker_session = ::Postjob::Queue.start_worker_session(workflows_with_versions, host_id: host_id)
@@ -27,7 +27,7 @@ describe "Postjob::Queue.search" do
27
27
 
28
28
  context "attributes" do
29
29
  it "returns a specific set of attributes" do
30
- scope = Postjob::Queue.search(id: id)
30
+ scope = Postjob::Queue.search("postjobs", id: id)
31
31
  result = Simple::SQL.ask scope, into: Hash
32
32
 
33
33
  expected_keys = [
@@ -47,7 +47,7 @@ describe "Postjob::Queue.search" do
47
47
  end
48
48
 
49
49
  it "with attributes: argument it only returns the specified attributes" do
50
- scope = Postjob::Queue.search(attributes: %w(id))
50
+ scope = Postjob::Queue.search("postjobs", attributes: %w(id))
51
51
  result = Simple::SQL.ask scope, into: Hash
52
52
 
53
53
  expected_keys = [
@@ -64,7 +64,7 @@ describe "Postjob::Queue.search" do
64
64
  end
65
65
 
66
66
  it "returns all entries" do
67
- scope = Postjob::Queue.search
67
+ scope = Postjob::Queue.search("postjobs")
68
68
  result = Simple::SQL.all(scope, into: OpenStruct)
69
69
  expect(result.length).to eq(2)
70
70
  end
@@ -72,46 +72,46 @@ describe "Postjob::Queue.search" do
72
72
 
73
73
  describe "filtering" do
74
74
  it "can find a job by id" do
75
- scope = Postjob::Queue.search(id: id)
75
+ scope = Postjob::Queue.search("postjobs", id: id)
76
76
  result = Simple::SQL.ask scope, into: Hash
77
77
  expect(result[:id]).to eq(id)
78
78
  end
79
79
 
80
80
  it "positive filters by tags" do
81
- scope = Postjob::Queue.search("initial" => "yay")
81
+ scope = Postjob::Queue.search("postjobs", "initial" => "yay")
82
82
  result = Simple::SQL.all scope, into: OpenStruct
83
83
  expect(result.length).to eq(1)
84
84
  expect(result.first.id).to eq(id)
85
85
  end
86
86
 
87
87
  it "negative filters by tags" do
88
- scope = Postjob::Queue.search("initial" => "nay")
88
+ scope = Postjob::Queue.search("postjobs", "initial" => "nay")
89
89
  result = Simple::SQL.all(scope)
90
90
  expect(result.length).to eq(0)
91
91
  end
92
92
 
93
93
  it "filter tags against empty array" do
94
- scope = Postjob::Queue.search("initial" => [])
94
+ scope = Postjob::Queue.search("postjobs", "initial" => [])
95
95
  result = Simple::SQL.all(scope)
96
96
  expect(result.length).to eq(0)
97
97
  end
98
98
 
99
99
  it "filter tags against multiple matches" do
100
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ])
100
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ])
101
101
  result = Simple::SQL.all(scope)
102
102
  expect(result.length).to eq(1)
103
103
  end
104
104
 
105
105
  it "filter tags against a combination of multiple matches" do
106
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ], "multi" => "manyay")
106
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ], "multi" => "manyay")
107
107
  result = Simple::SQL.all(scope)
108
108
  expect(result.length).to eq(1)
109
109
 
110
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ], "multi" => "nay")
110
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ], "multi" => "nay")
111
111
  result = Simple::SQL.all(scope)
112
112
  expect(result.length).to eq(0)
113
113
 
114
- scope = Postjob::Queue.search("initial" => [ "yay", "nay" ], "multi" => ["nay", "manyay"])
114
+ scope = Postjob::Queue.search("postjobs", "initial" => [ "yay", "nay" ], "multi" => ["nay", "manyay"])
115
115
  result = Simple::SQL.all(scope)
116
116
  expect(result.length).to eq(1)
117
117
  end
data/spec/spec_helper.rb CHANGED
@@ -40,6 +40,6 @@ RSpec.configure do |config|
40
40
  config.after {}
41
41
 
42
42
  config.before(:all) do
43
- FileUtils.rm_rf ::Postjob::WorkerSession::HOST_ID_STORE
43
+ FileUtils.rm_rf ::Postjob::WorkerSession::HOST_ID_PATH
44
44
  end
45
45
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postjob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -261,6 +261,7 @@ files:
261
261
  - lib/postjob/migrations/018_heartbeat.sql
262
262
  - lib/postjob/migrations/019_heartbeat_indices.sql
263
263
  - lib/postjob/queue.rb
264
+ - lib/postjob/queue/database_info.rb
264
265
  - lib/postjob/queue/encoder.rb
265
266
  - lib/postjob/queue/notifications.rb
266
267
  - lib/postjob/queue/search.rb
@@ -314,7 +315,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
314
315
  version: '0'
315
316
  requirements: []
316
317
  rubyforge_project:
317
- rubygems_version: 2.5.1
318
+ rubygems_version: 2.7.7
318
319
  signing_key:
319
320
  specification_version: 4
320
321
  summary: restartable, asynchronous, and distributed processes