postjob 0.5.2 → 0.5.3

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
- 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