process-query-language 0.1.0
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.
- data/LICENSE +2 -0
- data/README.md +54 -0
- data/lib/process-query-language.rb +34 -0
- data/lib/process-query-language/backend/process-status.rb +47 -0
- data/lib/process-query-language/collection.rb +42 -0
- data/lib/process-query-language/matcher.rb +56 -0
- data/lib/process-query-language/version.rb +6 -0
- metadata +101 -0
data/LICENSE
ADDED
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
Process Query Language
|
3
|
+
----------------------
|
4
|
+
|
5
|
+
PQL is a query language for processes running on a system. The syntax
|
6
|
+
is closely related to the one used in MongoDB. This is because at heart, PQL
|
7
|
+
uses Plucky. But instead of sending the queries to a MongoDB server, they
|
8
|
+
are intercepted and processed by PQL.
|
9
|
+
|
10
|
+
PQL extends the Process class with four new methods:
|
11
|
+
|
12
|
+
* find - find a process by PID
|
13
|
+
* where - find processes which match a query
|
14
|
+
* remove - remove (kill) processes
|
15
|
+
* count - count processes
|
16
|
+
|
17
|
+
These `where` method returns a Plucky::Query object. This object
|
18
|
+
can be used to further refine the query by chaining additional methods.
|
19
|
+
Consult the Plucky documentation to see which methods are available.
|
20
|
+
|
21
|
+
|
22
|
+
Supported fields
|
23
|
+
----------------
|
24
|
+
|
25
|
+
There is a limited number of fields which can be used in queries. The
|
26
|
+
following fields are supported:
|
27
|
+
|
28
|
+
:pid, :rss, :command
|
29
|
+
|
30
|
+
The following operators are supported
|
31
|
+
|
32
|
+
gt, gte, lt, lte, in, nin
|
33
|
+
|
34
|
+
Use of an unknown field or unknown operator will raise an exception.
|
35
|
+
|
36
|
+
|
37
|
+
Example
|
38
|
+
-------
|
39
|
+
|
40
|
+
This will print the number of unicorn rails workers which use more than
|
41
|
+
20MB of RSS:
|
42
|
+
|
43
|
+
require 'rubygems'
|
44
|
+
require 'process-query-language'
|
45
|
+
|
46
|
+
query = Process.where(:rss.gt => 20_000_000)
|
47
|
+
query.where(:command => /unicorn_rails.worker/)
|
48
|
+
puts query.count
|
49
|
+
|
50
|
+
|
51
|
+
License
|
52
|
+
-------
|
53
|
+
|
54
|
+
Copyright (c) 2010 by Tomas "wereHamster" Carnecky (tomas.carnecky@gmail.com)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
require 'plucky'
|
3
|
+
|
4
|
+
module ProcessQueryLanguage
|
5
|
+
|
6
|
+
autoload :Version, 'process-query-language/version'
|
7
|
+
autoload :Collection, 'process-query-language/collection'
|
8
|
+
autoload :Matcher, 'process-query-language/matcher'
|
9
|
+
|
10
|
+
module Backend
|
11
|
+
autoload :ProcessStatus, 'process-query-language/backend/process-status'
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
module Process
|
18
|
+
|
19
|
+
def self.pql(backend)
|
20
|
+
klass = ProcessQueryLanguage::Backend.const_get(backend)
|
21
|
+
@@pql = ProcessQueryLanguage::Collection.new(klass.new)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Only expose selected methods from the Plucky::Query object
|
25
|
+
%w{ find remove count where }.each do |method|
|
26
|
+
module_eval <<-eval
|
27
|
+
def Process.#{method}(*args)
|
28
|
+
@@pql ||= ProcessQueryLanguage::Collection.new(ProcessQueryLanguage::Backend::ProcessStatus.new)
|
29
|
+
Plucky::Query.new(@@pql).#{method}(*args)
|
30
|
+
end
|
31
|
+
eval
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module ProcessQueryLanguage
|
3
|
+
|
4
|
+
module Backend
|
5
|
+
|
6
|
+
# This backend uses the `ps` utility and parses its output. Because
|
7
|
+
# `ps` uses whitespace as the delimiter (and truncates too wide fields)
|
8
|
+
# except the last, we need to sort the fields so that :command is
|
9
|
+
# the last.
|
10
|
+
class ProcessStatus
|
11
|
+
|
12
|
+
FIELDS = %w{ pid rss command }.map(&:to_sym)
|
13
|
+
|
14
|
+
def scan(fields)
|
15
|
+
result = []
|
16
|
+
%x[ps -Aco #{FIELDS.join(',')}].lines.to_a[1..-1].each do |line|
|
17
|
+
values = line.split(' ')
|
18
|
+
process = {}
|
19
|
+
FIELDS.each_with_index do |value, i|
|
20
|
+
next unless fields.include?(FIELDS[i])
|
21
|
+
process[FIELDS[i]] = convert(FIELDS[i], values[i])
|
22
|
+
end
|
23
|
+
result << process
|
24
|
+
end
|
25
|
+
|
26
|
+
return result
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def convert(field, value)
|
32
|
+
case field
|
33
|
+
when :rss, :pid
|
34
|
+
return value.to_i
|
35
|
+
when :command
|
36
|
+
return value.to_s
|
37
|
+
else
|
38
|
+
return nil
|
39
|
+
raise Exception.new("Don't know how to convert #{field}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
module ProcessQueryLanguage
|
3
|
+
|
4
|
+
class Collection
|
5
|
+
|
6
|
+
def initialize(backend)
|
7
|
+
@backend = backend
|
8
|
+
end
|
9
|
+
|
10
|
+
def find(query, options)
|
11
|
+
query[:pid] = query.delete(:_id) if query[:_id]
|
12
|
+
|
13
|
+
keys = query.keys
|
14
|
+
keys = keys + options[:fields] if options[:fields]
|
15
|
+
|
16
|
+
matcher = ProcessQueryLanguage::Matcher.new(query)
|
17
|
+
result = []
|
18
|
+
@backend.scan(keys.uniq).each do |process|
|
19
|
+
if matcher.match(process)
|
20
|
+
result << process
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
return result
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_one(query, options)
|
28
|
+
find(query, options).first
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove(query)
|
32
|
+
find(query, { :fields => [ :pid ] }).each do |process|
|
33
|
+
Process.kill(process[:pid])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def insert
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
module ProcessQueryLanguage
|
3
|
+
|
4
|
+
class Matcher
|
5
|
+
|
6
|
+
attr_reader :pattern
|
7
|
+
|
8
|
+
# pattern: { a => { $gt => 100 }, b => { $in => [ 1,2,3 ] } }
|
9
|
+
def initialize(pattern)
|
10
|
+
@pattern = pattern
|
11
|
+
end
|
12
|
+
|
13
|
+
def apply(op, value, pattern)
|
14
|
+
case op
|
15
|
+
when "$gt"
|
16
|
+
return value > pattern
|
17
|
+
when "$gte"
|
18
|
+
return value >= pattern
|
19
|
+
when "$lt"
|
20
|
+
return value < pattern
|
21
|
+
when "$lte"
|
22
|
+
return value <= pattern
|
23
|
+
when "$in"
|
24
|
+
return pattern.include?(value)
|
25
|
+
when "$nin"
|
26
|
+
return !pattern.include?(value)
|
27
|
+
else
|
28
|
+
raise Exception.new("Unknown operator: #{op}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# object: { a => 200, b => 2 }
|
33
|
+
def match(object)
|
34
|
+
pattern.each do |key, value|
|
35
|
+
return false if object[key].nil?
|
36
|
+
|
37
|
+
case value
|
38
|
+
when Regexp
|
39
|
+
return false if !value.match(object[key])
|
40
|
+
when Hash
|
41
|
+
value.each do |op, value|
|
42
|
+
return false if !apply(op, object[key], value)
|
43
|
+
end
|
44
|
+
when object[key]
|
45
|
+
return false if object[key] != value
|
46
|
+
else
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: process-query-language
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tomas Carnecky
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-31 01:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: plucky
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: shoulda
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
description:
|
50
|
+
email:
|
51
|
+
- tomas.carnecky@gmail.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- lib/process-query-language/backend/process-status.rb
|
60
|
+
- lib/process-query-language/collection.rb
|
61
|
+
- lib/process-query-language/matcher.rb
|
62
|
+
- lib/process-query-language/version.rb
|
63
|
+
- lib/process-query-language.rb
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
has_rdoc: true
|
67
|
+
homepage: http://github.com/wereHamster/process-query-language
|
68
|
+
licenses: []
|
69
|
+
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Process query language (based on plucky)
|
100
|
+
test_files: []
|
101
|
+
|