postjob 0.5.2 → 0.5.3

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
- SHA256:
3
- metadata.gz: 4fcc6c18ad2be617abafbdfb1dc6258a435223a0fbd0110d86806b6da47fc5d6
4
- data.tar.gz: 98a5384f074100eb291d6357993969fea6da7b058177ba513f79329bc558d9c9
2
+ SHA1:
3
+ metadata.gz: ddb381c651727adfa2e654b8e9902510ede3dad3
4
+ data.tar.gz: e84c5cace159a3b39ce3cb34fc479d882eb4147a
5
5
  SHA512:
6
- metadata.gz: 212977ecccd8f11df6d0e4950155cc47f18c1d6c979b045109b5f981d5eb1e7a68c90aefae78708385458d102870f3d3c9357d8861fa9a54fd5f58c8db9f4f2b
7
- data.tar.gz: c31025b4869355c9dd13ed5e119c43133b9ae794eecaa2c12f92eb07c1e476f48c32cefa182757358b7236b64febe34283183d5f492a71116e86569adb36e724
6
+ metadata.gz: 7d05ff63ead66235f51259cf86ff4565fc2aa8cee2583dcf83bcfc3767b9685b6c91c100b9d86e230f888eb9ca28e4d1a5aae36a4d9b18bf25d23de9314ff703
7
+ data.tar.gz: de96874a8e293a2357bda35885f55aa14b152e9f849d796de98596edeec8a6481bea7c354a293dcaa02bd570ae94a58c96d0134a897a7e75273a04c5f26be48d
@@ -0,0 +1,46 @@
1
+ # This is a pretty generic association loader.
2
+ module Postjob::Queue::Associations
3
+ extend self
4
+
5
+ #
6
+ # returns a Hash workflow_id => [ associated ]
7
+ #
8
+ def load(entities, association, model, foreign_key, singular: false)
9
+ expect! entities => Array
10
+ expect! association => Symbol
11
+ expect! model => %w(postjobs events)
12
+ expect! foreign_key => Symbol
13
+
14
+ return entities if entities.empty?
15
+
16
+ entity_ids = entities.pluck(:id)
17
+
18
+ # Load all matching associated objects
19
+ scope = ::Postjob::Queue.search(model, foreign_key => entity_ids).order_by("id")
20
+
21
+ associated = ::Simple::SQL.all(scope, into: Hash)
22
+ associated_by_id = stable_group_by_key(associated, foreign_key)
23
+
24
+ # Distribute the associated objects amongst the entities
25
+ if singular
26
+ entities.each do |entity|
27
+ entity[association] = associated_by_id[entity[:id]].last
28
+ end
29
+ else
30
+ entities.each do |entity|
31
+ entity[association] = associated_by_id[entity[:id]]
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def stable_group_by_key(ary, key)
39
+ hsh = Hash.new { |h, k| h[k] = [] }
40
+ ary.each do |entity|
41
+ group = entity[key]
42
+ hsh[group] << entity
43
+ end
44
+ hsh
45
+ end
46
+ end
@@ -0,0 +1,99 @@
1
+ require_relative "./database_info"
2
+
3
+ module Postjob::Queue::Search
4
+ class ScopeBuilder
5
+ DatabaseInfo = ::Postjob::Queue::DatabaseInfo
6
+
7
+ SCHEMA_NAME = ::Postjob::Queue::SCHEMA_NAME
8
+
9
+ class << self
10
+ def build(model:, filter:, attributes:)
11
+ attributes ||= default_search_attributes(model)
12
+
13
+ builder(model).send(:scope, filter: filter, attributes: attributes)
14
+ end
15
+
16
+ private
17
+
18
+ # describes which attributes should be returned in addition to a model's columns.
19
+ EXTRA_SEARCH_ATTRIBUTES = {
20
+ "postjobs" => [
21
+ "workflow || COALESCE('@' || workflow_version, '') || args AS job",
22
+ "COALESCE((results->0)::varchar, error_message) AS result",
23
+ "(now() at time zone 'utc') - created_at AS age",
24
+ "updated_at - created_at AS runtime"
25
+ ]
26
+ }
27
+
28
+ def default_search_attributes(model)
29
+ DatabaseInfo.table_columns("#{SCHEMA_NAME}.#{model}") + (EXTRA_SEARCH_ATTRIBUTES[model] || [])
30
+ end
31
+
32
+ # Returns the name of the column used for dynamic filtering.
33
+ def dynamic_filter(model)
34
+ case model
35
+ when "postjobs" then "tags"
36
+ else "attributes"
37
+ end
38
+ end
39
+
40
+ def builder(model)
41
+ @builders ||= {}
42
+ @builders[model] ||= new(model: model, dynamic_filter: dynamic_filter(model))
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def initialize(model:, dynamic_filter:)
49
+ @model = model
50
+ @table_name = "#{SCHEMA_NAME}.#{model}"
51
+ @column_types = DatabaseInfo.column_types(@table_name)
52
+ @dynamic_filter = dynamic_filter
53
+ end
54
+
55
+ def scope(filter:, attributes:)
56
+ expect! filter => [Hash, nil]
57
+ expect! attributes => Array
58
+
59
+ # build Scope
60
+ scope = Simple::SQL::Scope.new("SELECT #{attributes.join(", ")} FROM #{@table_name}")
61
+ scope = apply_filters(scope, filter) if filter && !filter.empty?
62
+ scope
63
+ end
64
+
65
+ # apply the filter hash onto the passed in scope. This matches all filters
66
+ # with a name which is matching a column name against the column values.
67
+ # It matches every other filter value against an entry in the
68
+ # dynamic_filter column.
69
+ def apply_filters(scope, filter)
70
+ filter.each_key do |key|
71
+ expect! key => [Symbol, String]
72
+ end
73
+
74
+ converted_filters = filter.inject({}) do |hsh, (key, value)|
75
+ matches = convert_filter_value(value, key: key)
76
+ matches = matches.first if matches.length == 1
77
+ hsh.update key => matches
78
+ end
79
+
80
+ column_filters, tags_filters = converted_filters.partition { |key, _| !@column_types[key].nil? }
81
+
82
+ scope = scope.where(Hash[column_filters]) unless column_filters.empty?
83
+ scope = scope.where(@dynamic_filter => Hash[tags_filters]) unless tags_filters.empty?
84
+
85
+ scope
86
+ end
87
+
88
+ def convert_filter_value(value, key:)
89
+ value = Array(value)
90
+
91
+ case @column_types[key]
92
+ when "bigint" then value.map { |v| Integer(v) }
93
+ when "integer" then value.map { |v| Integer(v) }
94
+ when nil then value.map(&:to_s) + value.grep(/\A-?\d+\z/).map(&:to_i)
95
+ else value.map(&:to_s)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -1,120 +1,30 @@
1
- require_relative "./database_info"
1
+ require_relative "search/scope_builder"
2
2
 
3
3
  module Postjob::Queue
4
- class ScopeBuilder
5
- SCHEMA_NAME = ::Postjob::Queue::SCHEMA_NAME
6
-
7
- class << self
8
- def build(model:, filter:, attributes:)
9
- builder(model).send(:scope, filter: filter, attributes: attributes)
10
- end
11
-
12
- private
13
-
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
26
- end
27
-
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
35
- end
36
-
37
- def scope(filter:, attributes:)
4
+ module Search
5
+ # Builds a search scope (see Simple::SQL::Scope) for the passed in filter criteria.
6
+ #
7
+ # Parameters:
8
+ #
9
+ # - model: the name of the postjob model, e.g. "postjobs", "events", ..
10
+ # - filter: a Hash of filter values and other options. options are denoted
11
+ # by a Symbol key.
12
+ #
13
+ # Note that the search scope is unsorted.
14
+ def search(model, filter = {})
15
+ expect! model => String
38
16
  expect! filter => [Hash, nil]
39
- expect! attributes => Array
40
-
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
55
17
 
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
18
+ filter = filter ? filter.dup : {}
61
19
 
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?
20
+ root_only = filter.delete(:root_only)
21
+ attributes = filter.delete(:attributes)
66
22
 
23
+ scope = ScopeBuilder.build(model: model, filter: filter, attributes: attributes)
24
+ scope = scope.where("root_id=id") if root_only && model == "postjobs"
67
25
  scope
68
26
  end
69
-
70
- def convert_filter_value(value, key:)
71
- value = Array(value)
72
-
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)
78
- end
79
- end
80
- end
81
-
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]
94
-
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
27
  end
104
28
 
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] || [])
119
- end
29
+ extend Search
120
30
  end
data/lib/postjob/queue.rb CHANGED
@@ -15,6 +15,7 @@ end
15
15
  require_relative "queue/encoder"
16
16
  require_relative "queue/notifications"
17
17
  require_relative "queue/search"
18
+ require_relative "queue/associations"
18
19
  require_relative "queue/settings"
19
20
 
20
21
  module Postjob::Queue
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postjob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-26 00:00:00.000000000 Z
11
+ date: 2018-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -261,10 +261,12 @@ 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
+ - lib/postjob/queue/associations.rb
265
265
  - lib/postjob/queue/encoder.rb
266
266
  - lib/postjob/queue/notifications.rb
267
267
  - lib/postjob/queue/search.rb
268
+ - lib/postjob/queue/search/database_info.rb
269
+ - lib/postjob/queue/search/scope_builder.rb
268
270
  - lib/postjob/queue/settings.rb
269
271
  - lib/postjob/record.rb
270
272
  - lib/postjob/registry.rb
@@ -315,7 +317,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
315
317
  version: '0'
316
318
  requirements: []
317
319
  rubyforge_project:
318
- rubygems_version: 2.7.7
320
+ rubygems_version: 2.5.1
319
321
  signing_key:
320
322
  specification_version: 4
321
323
  summary: restartable, asynchronous, and distributed processes