scout 4.0.2 → 5.0.2
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/README +18 -28
- data/Rakefile +41 -41
- data/bin/scout +2 -1
- data/lib/scout.rb +2 -1
- data/lib/scout/command.rb +52 -50
- data/lib/scout/command/install.rb +9 -6
- data/lib/scout/command/run.rb +19 -2
- data/lib/scout/command/test.rb +36 -14
- data/lib/scout/plugin.rb +17 -0
- data/lib/scout/plugin_options.rb +80 -0
- data/lib/scout/server.rb +165 -88
- metadata +18 -17
data/README
CHANGED
@@ -6,32 +6,32 @@ Scout by Highgroove Studios
|
|
6
6
|
|
7
7
|
The easier way to monitor servers and web applications.
|
8
8
|
|
9
|
-
Scout makes monitoring and reporting on your web applications
|
10
|
-
and simple as possible.
|
9
|
+
Scout makes monitoring and reporting on your web applications
|
10
|
+
as flexible and simple as possible.
|
11
11
|
|
12
|
-
Scout is a product of Highgroove Studios.
|
13
|
-
for more information.
|
12
|
+
Scout is a product of Highgroove Studios.
|
13
|
+
Please visit http://scoutapp.com for more information.
|
14
14
|
|
15
15
|
== Installing
|
16
16
|
|
17
|
-
|
17
|
+
Install the Scout gem:
|
18
18
|
|
19
19
|
$ sudo gem install scout
|
20
20
|
|
21
|
-
|
21
|
+
Then simply run:
|
22
22
|
|
23
23
|
$ scout
|
24
24
|
|
25
|
-
to run the installation wizard. You'll need your
|
25
|
+
to run the installation wizard. You'll need your server key to continue. Get the server key from your account at http://scoutapp.com
|
26
26
|
|
27
27
|
== Running the Scout Client
|
28
28
|
|
29
29
|
The Scout client has several modes of operation and commands. The normal, intended usage is through a scheduled interval with no output.
|
30
30
|
|
31
31
|
Normal checkin with server:
|
32
|
-
$ scout [OPTIONS]
|
32
|
+
$ scout [OPTIONS] SERVER_KEY
|
33
33
|
... OR ...
|
34
|
-
$ scout [OPTIONS] run
|
34
|
+
$ scout [OPTIONS] run SERVER_KEY
|
35
35
|
|
36
36
|
Install:
|
37
37
|
$ scout
|
@@ -41,36 +41,26 @@ Install:
|
|
41
41
|
Local plugin testing:
|
42
42
|
$ scout [OPTIONS] test PATH_TO_PLUGIN [PLUGIN_OPTIONS]
|
43
43
|
|
44
|
-
Clone a client setup:
|
45
|
-
$ scout [OPTIONS] clone CLIENT_KEY NEW_CLIENT_NAME
|
46
44
|
|
47
|
-
|
48
|
-
this client by the server.
|
45
|
+
SERVER_KEY is the identification key assigned by your account at http://scoutapp.com
|
49
46
|
|
50
47
|
PATH_TO_PLUGIN is the file system path to a Ruby file
|
51
48
|
that contains a Scout plugin.
|
52
49
|
|
53
|
-
PLUGIN_OPTIONS
|
54
|
-
|
55
|
-
options will be used for the plugin run.
|
50
|
+
PLUGIN_OPTIONS are one or more options in the form:
|
51
|
+
key1=val1 key2=val2
|
52
|
+
These options will be used for the plugin run.
|
56
53
|
|
57
|
-
NEW_CLIENT_NAME is name you wish to use for the new
|
58
|
-
client the server creates.
|
59
54
|
|
55
|
+
== Setting up in cron
|
60
56
|
|
61
|
-
|
57
|
+
Configure Scout to run every minute. Typically, this will look like:
|
62
58
|
|
63
|
-
|
64
|
-
|
65
|
-
An example usage, running:
|
66
|
-
|
67
|
-
server1 $ scout clone CLIENT_KEY 'My New Client'
|
68
|
-
|
69
|
-
Will create a new client called 'My New Client' on the Scout server with the same plugins as an already existing client (with the CLIENT_KEY specified). It will also return the new system crontab line:
|
70
|
-
|
71
|
-
*/30 * * * * deploy /usr/bin/scout NEW_CLIENT_KEY
|
59
|
+
* * * * * deploy /usr/bin/scout SERVER_KEY
|
72
60
|
|
61
|
+
It's often helpful to log the output to a file. To do so:
|
73
62
|
|
63
|
+
* * * * * deploy /usr/bin/scout SERVER_KEY > /path/to/anywhere/scout.out 2>&1
|
74
64
|
|
75
65
|
|
76
66
|
For additional help, please visit http://scoutapp.com
|
data/Rakefile
CHANGED
@@ -7,8 +7,8 @@ require "net/ssh"
|
|
7
7
|
require "rubygems"
|
8
8
|
require "rubyforge"
|
9
9
|
|
10
|
-
dir
|
11
|
-
lib
|
10
|
+
dir = File.dirname(__FILE__)
|
11
|
+
lib = File.join(dir, "lib", "scout.rb")
|
12
12
|
version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
|
13
13
|
history = File.read("CHANGELOG").split(/^(===.*)/)
|
14
14
|
changes ||= history[0..2].join.strip
|
@@ -19,50 +19,50 @@ need_zip = true
|
|
19
19
|
task :default => [:test]
|
20
20
|
|
21
21
|
Rake::TestTask.new do |test|
|
22
|
-
test.libs
|
22
|
+
test.libs << "test"
|
23
23
|
test.test_files = [ "test/scout_test.rb" ]
|
24
|
-
test.verbose
|
24
|
+
test.verbose = true
|
25
25
|
end
|
26
26
|
|
27
27
|
Rake::RDocTask.new do |rdoc|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
rdoc.main = "README"
|
29
|
+
rdoc.rdoc_dir = "doc/html"
|
30
|
+
rdoc.title = "Scout Client Documentation"
|
31
|
+
rdoc.rdoc_files.include( "README", "INSTALL",
|
32
|
+
"TODO", "CHANGELOG",
|
33
|
+
"AUTHORS", "COPYING",
|
34
|
+
"LICENSE", "lib/" )
|
35
35
|
end
|
36
36
|
|
37
37
|
spec = Gem::Specification.new do |spec|
|
38
|
-
|
39
|
-
|
38
|
+
spec.name = "scout"
|
39
|
+
spec.version = version
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
spec.platform = Gem::Platform::RUBY
|
42
|
+
spec.summary = "Scout makes monitoring and reporting on your web applications as flexible and simple as possible."
|
43
43
|
|
44
44
|
# TODO: test suite
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
spec.executables
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
# spec.test_suite_file = "test/ts_all.rb"
|
46
|
+
spec.files = Dir.glob("{bin,lib}/**/*.rb") +
|
47
|
+
Dir.glob("{data,vendor}/**/*") +
|
48
|
+
%w[Rakefile]
|
49
|
+
spec.executables = ["scout"]
|
50
|
+
|
51
|
+
spec.has_rdoc = true
|
52
|
+
spec.extra_rdoc_files = %w[ AUTHORS COPYING README INSTALL TODO CHANGELOG
|
53
53
|
LICENSE ]
|
54
|
-
|
55
|
-
|
54
|
+
spec.rdoc_options << "--title" << "Scout Client Documentation" <<
|
55
|
+
"--main" << "README"
|
56
|
+
|
57
|
+
spec.require_path = "lib"
|
56
58
|
|
57
|
-
spec.require_path = "lib"
|
58
|
-
|
59
59
|
spec.add_dependency "elif"
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
spec.author = "Highgroove Studios"
|
62
|
+
spec.email = "scout@highgroove.com"
|
63
|
+
spec.rubyforge_project = "scout"
|
64
|
+
spec.homepage = "http://scoutapp.com"
|
65
|
+
spec.description = <<END_DESC
|
66
66
|
Scout makes monitoring and reporting on your web applications as flexible and simple as possible.
|
67
67
|
|
68
68
|
Scout is a product of Highgroove Studios.
|
@@ -70,8 +70,8 @@ END_DESC
|
|
70
70
|
end
|
71
71
|
|
72
72
|
Rake::GemPackageTask.new(spec) do |pkg|
|
73
|
-
|
74
|
-
|
73
|
+
pkg.need_zip = need_tar
|
74
|
+
pkg.need_tar = need_zip
|
75
75
|
end
|
76
76
|
|
77
77
|
desc "Publishes to Scout Gem Server and Rubyforge"
|
@@ -85,12 +85,12 @@ task :publish_rubyforge => [:package] do
|
|
85
85
|
puts "Logging in"
|
86
86
|
forge.login
|
87
87
|
|
88
|
-
release
|
88
|
+
release = forge.userconfig
|
89
89
|
release["release_changes"] = File.read(File.join(dir, "CHANGELOG"))
|
90
|
-
release["preformatted"]
|
90
|
+
release["preformatted"] = true
|
91
91
|
|
92
92
|
package = "pkg/#{spec.name}-#{version}"
|
93
|
-
files
|
93
|
+
files = %W[#{package}.tgz #{package}.zip #{package}.gem].compact
|
94
94
|
|
95
95
|
puts "Releasing #{spec.name}-#{version}"
|
96
96
|
forge.add_release(spec.rubyforge_project, spec.name, version, *files)
|
@@ -98,9 +98,9 @@ end
|
|
98
98
|
|
99
99
|
desc "Upload current documentation to Scout Gem Server and RubyForge"
|
100
100
|
task :upload_docs => [:rdoc] do
|
101
|
-
|
102
|
-
|
103
|
-
|
101
|
+
sh "scp -r doc/html/* " +
|
102
|
+
"deploy@gems.scoutapp.com:/var/www/gems/docs"
|
103
|
+
|
104
104
|
config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
105
105
|
host = "#{config["username"]}@rubyforge.org"
|
106
106
|
|
@@ -112,5 +112,5 @@ end
|
|
112
112
|
|
113
113
|
desc "Add new files to Subersion"
|
114
114
|
task :svn_add do
|
115
|
-
|
115
|
+
system "svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add"
|
116
116
|
end
|
data/bin/scout
CHANGED
data/lib/scout.rb
CHANGED
data/lib/scout/command.rb
CHANGED
@@ -9,7 +9,7 @@ module Scout
|
|
9
9
|
def self.user
|
10
10
|
@user ||= ENV["USER"] || ENV["USERNAME"] || "root"
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def self.program_name
|
14
14
|
@program_name ||= File.basename($PROGRAM_NAME)
|
15
15
|
end
|
@@ -17,7 +17,7 @@ module Scout
|
|
17
17
|
def self.program_path
|
18
18
|
@program_path ||= File.expand_path($PROGRAM_NAME)
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def self.usage
|
22
22
|
@usage
|
23
23
|
end
|
@@ -25,9 +25,10 @@ module Scout
|
|
25
25
|
def self.parse_options(argv)
|
26
26
|
options = { }
|
27
27
|
|
28
|
-
|
28
|
+
op = OptionParser.new do |opts|
|
29
29
|
opts.banner = "Usage:"
|
30
30
|
|
31
|
+
opts.separator "--------------------------------------------------------------------------"
|
31
32
|
opts.separator " Normal checkin with server:"
|
32
33
|
opts.separator " #{program_name} [OPTIONS] CLIENT_KEY"
|
33
34
|
opts.separator " ... OR ..."
|
@@ -39,47 +40,32 @@ module Scout
|
|
39
40
|
opts.separator " Local plugin testing:"
|
40
41
|
opts.separator " #{program_name} [OPTIONS] test " +
|
41
42
|
"PATH_TO_PLUGIN [PLUGIN_OPTIONS]"
|
42
|
-
opts.separator "
|
43
|
-
opts.separator "
|
44
|
-
|
45
|
-
opts.separator ""
|
46
|
-
opts.separator "CLIENT_KEY is the indentification key assigned to"
|
47
|
-
opts.separator "this client by the server."
|
48
|
-
opts.separator ""
|
49
|
-
opts.separator "PATH_TO_PLUGIN is the file system path to a Ruby file"
|
50
|
-
opts.separator "that contains a Scout plugin."
|
51
|
-
opts.separator ""
|
52
|
-
opts.separator "PLUGIN_OPTIONS can be the code for a Ruby Hash or the"
|
53
|
-
opts.separator "path to a YAML options file containing defaults. These"
|
54
|
-
opts.separator "options will be used for the plugin run."
|
55
|
-
opts.separator ""
|
56
|
-
opts.separator "NEW_CLIENT_NAME is name you wish to use for the new"
|
57
|
-
opts.separator "client the server creates."
|
58
|
-
opts.separator ""
|
43
|
+
opts.separator "[PLUGIN_OPTIONS] format: opt1=val1 opt2=val2 opt2=val3 ..."
|
44
|
+
opts.separator "Plugin will use internal defaults if options aren't provided."
|
45
|
+
opts.separator " "
|
59
46
|
opts.separator "Note: This client is meant to be installed and"
|
60
47
|
opts.separator "invoked through cron or any other scheduler."
|
61
|
-
opts.separator ""
|
48
|
+
opts.separator " "
|
62
49
|
opts.separator "Specific Options:"
|
63
|
-
|
50
|
+
opts.separator "--------------------------------------------------------------------------"
|
64
51
|
opts.on( "-s", "--server SERVER", String,
|
65
52
|
"The URL for the server to report to." ) do |url|
|
66
53
|
options[:server] = url
|
67
54
|
end
|
68
55
|
|
69
|
-
opts.separator ""
|
70
|
-
|
71
56
|
opts.on( "-d", "--data DATA", String,
|
72
57
|
"The data file used to track history." ) do |file|
|
73
58
|
options[:history] = file
|
74
59
|
end
|
75
60
|
opts.on( "-l", "--level LEVEL",
|
76
61
|
Logger::SEV_LABEL.map { |l| l.downcase },
|
77
|
-
"The level of logging to report." ) do |level|
|
62
|
+
"The level of logging to report. Use -ldebug for most detail." ) do |level|
|
78
63
|
options[:level] = level
|
79
64
|
end
|
80
65
|
|
66
|
+
opts.separator " "
|
81
67
|
opts.separator "Common Options:"
|
82
|
-
|
68
|
+
opts.separator "--------------------------------------------------------------------------"
|
83
69
|
opts.on( "-h", "--help",
|
84
70
|
"Show this message." ) do
|
85
71
|
puts opts
|
@@ -89,31 +75,46 @@ module Scout
|
|
89
75
|
"Turn on logging to STDOUT" ) do |bool|
|
90
76
|
options[:verbose] = bool
|
91
77
|
end
|
92
|
-
|
78
|
+
|
93
79
|
opts.on( "-V", "--version",
|
94
80
|
"Display the current version") do |version|
|
95
81
|
puts Scout::VERSION
|
96
82
|
exit
|
97
83
|
end
|
98
84
|
|
99
|
-
|
100
|
-
|
101
|
-
@usage = opts.to_s
|
102
|
-
rescue
|
103
|
-
puts opts
|
104
|
-
exit
|
85
|
+
opts.on( "-F", "--force", "Force checkin to Scout server regardless of last checkin time") do |bool|
|
86
|
+
options[:force] = bool
|
105
87
|
end
|
88
|
+
|
89
|
+
opts.separator " "
|
90
|
+
opts.separator "Examples: "
|
91
|
+
opts.separator "--------------------------------------------------------------------------"
|
92
|
+
opts.separator "1. Normal run (replace w/your own key):"
|
93
|
+
opts.separator " scout 6ecad322-0d17-4cb8-9b2c-a12c4541853f"
|
94
|
+
opts.separator "2. Normal run with logging to standard out (replace w/your own key):"
|
95
|
+
opts.separator " scout --verbose 6ecad322-0d17-4cb8-9b2c-a12c4541853f"
|
96
|
+
opts.separator "3. Test a plugin:"
|
97
|
+
opts.separator " scout test my_plugin.rb foo=18 bar=42"
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
begin
|
102
|
+
op.parse!(argv)
|
103
|
+
@usage = op.to_s
|
104
|
+
rescue
|
105
|
+
puts op
|
106
|
+
exit
|
106
107
|
end
|
107
|
-
|
108
108
|
options
|
109
109
|
end
|
110
110
|
private_class_method :parse_options
|
111
|
-
|
111
|
+
|
112
112
|
def self.dispatch(argv)
|
113
|
+
# capture help command
|
114
|
+
argv.push("--help") if argv.first == 'help'
|
113
115
|
options = parse_options(argv)
|
114
116
|
command = if name_or_key = argv.shift
|
115
|
-
if cls = Scout::Command.const_get(name_or_key.capitalize)
|
116
|
-
rescue nil
|
117
|
+
if cls = (Scout::Command.const_get(name_or_key.capitalize) rescue nil)
|
117
118
|
cls.new(options, argv)
|
118
119
|
else
|
119
120
|
Run.new(options, [name_or_key] + argv)
|
@@ -121,9 +122,9 @@ module Scout
|
|
121
122
|
else
|
122
123
|
Install.new(options, argv)
|
123
124
|
end
|
124
|
-
command.
|
125
|
+
command.run
|
125
126
|
end
|
126
|
-
|
127
|
+
|
127
128
|
def initialize(options, args)
|
128
129
|
@server = options[:server] || "https://scoutapp.com/"
|
129
130
|
@history = options[:history] ||
|
@@ -132,23 +133,24 @@ module Scout
|
|
132
133
|
"client_history.yaml" )
|
133
134
|
@verbose = options[:verbose] || false
|
134
135
|
@level = options[:level] || "info"
|
135
|
-
|
136
|
+
@force = options[:force] || false
|
137
|
+
|
136
138
|
@args = args
|
137
139
|
end
|
138
|
-
|
140
|
+
|
139
141
|
attr_reader :server, :history
|
140
|
-
|
142
|
+
|
141
143
|
def config_dir
|
142
144
|
return @config_dir if defined? @config_dir
|
143
145
|
@config_dir = File.dirname(history)
|
144
146
|
FileUtils.mkdir_p(@config_dir) # ensure dir exists
|
145
147
|
@config_dir
|
146
148
|
end
|
147
|
-
|
149
|
+
|
148
150
|
def verbose?
|
149
151
|
@verbose
|
150
152
|
end
|
151
|
-
|
153
|
+
|
152
154
|
def log
|
153
155
|
return @log if defined? @log
|
154
156
|
@log = if verbose?
|
@@ -160,15 +162,15 @@ module Scout
|
|
160
162
|
nil
|
161
163
|
end
|
162
164
|
end
|
163
|
-
|
165
|
+
|
164
166
|
def level
|
165
167
|
Logger.const_get(@level.upcase) rescue Logger::INFO
|
166
168
|
end
|
167
|
-
|
169
|
+
|
168
170
|
def user
|
169
171
|
@user ||= Command.user
|
170
172
|
end
|
171
|
-
|
173
|
+
|
172
174
|
def program_name
|
173
175
|
@program_name ||= Command.program_name
|
174
176
|
end
|
@@ -176,11 +178,11 @@ module Scout
|
|
176
178
|
def program_path
|
177
179
|
@program_path ||= Command.program_path
|
178
180
|
end
|
179
|
-
|
181
|
+
|
180
182
|
def usage
|
181
183
|
@usage ||= Command.usage
|
182
184
|
end
|
183
|
-
|
185
|
+
|
184
186
|
def create_pid_file_or_exit
|
185
187
|
pid_file = File.join(config_dir, "scout_client_pid.txt")
|
186
188
|
begin
|
@@ -228,7 +230,7 @@ module Scout
|
|
228
230
|
retry
|
229
231
|
end
|
230
232
|
end
|
231
|
-
|
233
|
+
|
232
234
|
self
|
233
235
|
end
|
234
236
|
end
|
@@ -4,23 +4,25 @@ module Scout
|
|
4
4
|
class Command
|
5
5
|
class Install < Command
|
6
6
|
def run
|
7
|
+
create_pid_file_or_exit
|
8
|
+
|
7
9
|
abort usage unless $stdin.tty?
|
8
10
|
|
9
11
|
puts <<-END_INTRO.gsub(/^ {8}/, "")
|
10
12
|
=== Scout Installation Wizard ===
|
11
13
|
|
12
|
-
You need the
|
14
|
+
You need the Server Key displayed in the Server Settings tab.
|
13
15
|
It looks like:
|
14
16
|
|
15
17
|
6ecad322-0d17-4cb8-9b2c-a12c4541853f
|
16
18
|
|
17
|
-
Enter the
|
19
|
+
Enter the Server Key:
|
18
20
|
END_INTRO
|
19
21
|
key = gets.to_s.strip
|
20
22
|
|
21
23
|
puts "\nAttempting to contact the server..."
|
22
24
|
begin
|
23
|
-
Scout::Server.new(server, key, history, log) { |scout| scout.
|
25
|
+
Scout::Server.new(server, key, history, log) { |scout| scout.fetch_plan }
|
24
26
|
|
25
27
|
puts <<-END_SUCCESS.gsub(/^ {10}/, "")
|
26
28
|
Success!
|
@@ -31,14 +33,14 @@ module Scout
|
|
31
33
|
(usually located at /etc/crontab):
|
32
34
|
|
33
35
|
****** START CRONTAB SAMPLE ******
|
34
|
-
|
36
|
+
* * * * * #{user} #{program_path} #{key}
|
35
37
|
****** END CRONTAB SAMPLE ******
|
36
38
|
|
37
39
|
If you are using this current user's crontab
|
38
40
|
(using crontab -e to edit):
|
39
41
|
|
40
42
|
****** START CRONTAB SAMPLE ******
|
41
|
-
|
43
|
+
* * * * * #{program_path} #{key}
|
42
44
|
****** END CRONTAB SAMPLE ******
|
43
45
|
|
44
46
|
For help setting up Scout with crontab, please visit:
|
@@ -47,9 +49,10 @@ module Scout
|
|
47
49
|
|
48
50
|
END_SUCCESS
|
49
51
|
rescue SystemExit
|
52
|
+
puts $!.message
|
50
53
|
puts <<-END_ERROR.gsub(/^ {10}/, "")
|
51
54
|
|
52
|
-
|
55
|
+
Failed.
|
53
56
|
For more help, please visit:
|
54
57
|
|
55
58
|
http://scoutapp.com/help
|
data/lib/scout/command/run.rb
CHANGED
@@ -5,8 +5,25 @@ module Scout
|
|
5
5
|
class Run < Command
|
6
6
|
def run
|
7
7
|
key = @args.first
|
8
|
-
|
9
|
-
|
8
|
+
# too much external logic of command doing things to server ... should be moved into server class
|
9
|
+
@scout = Scout::Server.new(server, key, history, log)
|
10
|
+
@scout.load_history
|
11
|
+
@scout.fetch_plan
|
12
|
+
|
13
|
+
|
14
|
+
if @scout.new_plan || @scout.time_to_checkin? || @force
|
15
|
+
if @scout.new_plan
|
16
|
+
log.info("Now checking in with new plugin plan") if log
|
17
|
+
elsif @scout.time_to_checkin?
|
18
|
+
log.info("It is time to checkin") if log
|
19
|
+
elsif @force
|
20
|
+
log.info("overriding checkin schedule with --force and checking in now.") if log
|
21
|
+
end
|
22
|
+
create_pid_file_or_exit
|
23
|
+
@scout.run_plugins_by_plan
|
24
|
+
@scout.save_history
|
25
|
+
else
|
26
|
+
log.info "Not time to checkin yet. Next checkin in #{@scout.next_checkin}. Override by passing --force to the scout command" if log
|
10
27
|
end
|
11
28
|
end
|
12
29
|
end
|
data/lib/scout/command/test.rb
CHANGED
@@ -6,22 +6,43 @@ module Scout
|
|
6
6
|
class Command
|
7
7
|
class Test < Command
|
8
8
|
def run
|
9
|
-
|
10
|
-
|
9
|
+
create_pid_file_or_exit
|
10
|
+
plugin, *provided_options = @args
|
11
11
|
# read the plugin_code from the file specified
|
12
12
|
plugin_code = File.read(plugin)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
|
14
|
+
options_for_run = {}
|
15
|
+
|
16
|
+
# deal with embedded options yaml
|
17
|
+
if options_yaml = Scout::Plugin.extract_options_yaml_from_code(plugin_code)
|
18
|
+
options=Scout::PluginOptions.from_yaml(options_yaml)
|
19
|
+
|
20
|
+
if options.error
|
21
|
+
puts "Problem parsing option definition in the plugin code (ignoring and continuing):"
|
22
|
+
puts options_yaml
|
23
|
+
else
|
24
|
+
puts "== Plugin options: "
|
25
|
+
puts options.to_s
|
26
|
+
options.select{|o|o.has_default?}.each{|o|options_for_run[o.name]=o.default}
|
27
|
+
end
|
28
|
+
else
|
29
|
+
puts "== This plugin doesn't have option metadata."
|
30
|
+
end
|
31
|
+
|
32
|
+
# provided_options are what the user gave us in the command line. Here, we merge them into
|
33
|
+
# the defaults we've already established (if any) for this run.
|
34
|
+
provided_options.each do |e|
|
35
|
+
if e.include?('=')
|
36
|
+
k,v=e.split('=',2)
|
37
|
+
options_for_run[k]=v
|
38
|
+
else
|
39
|
+
puts "ERROR: Option '#{e}' is no good -- provided options should be in the format name=value."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
if options_for_run.any?
|
43
|
+
puts "== Running plugin with: #{options_for_run.to_a.map{|a| "#{a.first}=#{a.last}"}.join('; ') }"
|
23
44
|
else
|
24
|
-
|
45
|
+
puts "== You haven't provided any options for running this plugin."
|
25
46
|
end
|
26
47
|
|
27
48
|
Scout::Server.new(nil, nil, history, log) do |scout|
|
@@ -30,8 +51,9 @@ module Scout
|
|
30
51
|
'plugin_id' => 1,
|
31
52
|
'name' => "Local Plugin",
|
32
53
|
'code' => plugin_code,
|
33
|
-
'options' =>
|
54
|
+
'options' => options_for_run,
|
34
55
|
'path' => plugin )
|
56
|
+
puts "== Output:"
|
35
57
|
scout.show_checkin(:pp)
|
36
58
|
end
|
37
59
|
end
|
data/lib/scout/plugin.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby -wKU
|
2
2
|
|
3
|
+
|
3
4
|
module Scout
|
5
|
+
|
4
6
|
class Plugin
|
7
|
+
|
8
|
+
EMBEDDED_OPTIONS_REGEX = /OPTIONS ?= ?<<-?([A-Z_]+)(.*)\1/m
|
9
|
+
|
5
10
|
class << self
|
6
11
|
attr_accessor :last_defined
|
7
12
|
|
@@ -32,6 +37,18 @@ module Scout
|
|
32
37
|
needs.push(*libraries.flatten)
|
33
38
|
end
|
34
39
|
end
|
40
|
+
|
41
|
+
# true if the code seems to have embedded options
|
42
|
+
def has_embedded_options?(code)
|
43
|
+
code =~ EMBEDDED_OPTIONS_REGEX
|
44
|
+
end
|
45
|
+
|
46
|
+
# extracts the internal YAML, if any, and returns the YAML string.
|
47
|
+
# returns nil if no embedded options.
|
48
|
+
def extract_options_yaml_from_code(code)
|
49
|
+
code =~ EMBEDDED_OPTIONS_REGEX
|
50
|
+
return $2
|
51
|
+
end
|
35
52
|
end
|
36
53
|
|
37
54
|
# Creates a new Scout Plugin to run.
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Scout
|
6
|
+
# a data structure of an individual plugin option
|
7
|
+
class PluginOption
|
8
|
+
attr_reader :name, :notes, :default, :advanced, :password, :required
|
9
|
+
def initialize(name, h)
|
10
|
+
@name=name
|
11
|
+
@notes=h['notes'] || ''
|
12
|
+
@default=h['default'] || ''
|
13
|
+
@attributes=h['attributes'] || ''
|
14
|
+
@advanced = @attributes.include?('advanced')
|
15
|
+
@password = @attributes.include?('password')
|
16
|
+
@required = @attributes.include?('required')
|
17
|
+
end
|
18
|
+
|
19
|
+
# convenience -- for nicer syntax
|
20
|
+
def advanced?; @advanced; end
|
21
|
+
def password?; @password; end
|
22
|
+
def required?; @required; end
|
23
|
+
def has_default?; default != '';end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
required_string = required? ? " (required). " : ""
|
27
|
+
default_string = default == '' ? '' : " Default: #{default}. "
|
28
|
+
"'#{name}'#{required_string}#{default_string}#{notes}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# A collection of pluginOption
|
33
|
+
# Create: opts=PluginOptions.from_yaml(yaml_string)
|
34
|
+
# Check if there were any problems -- opts.error -- should be nil.
|
35
|
+
#
|
36
|
+
# A valid options yaml looks like this:
|
37
|
+
# max_swap_used:
|
38
|
+
# notes: If swap is larger than this amount, an alert is generated. Amount should be in MB.
|
39
|
+
# default: 2048 # 2 GB
|
40
|
+
# max_swap_ratio:
|
41
|
+
# notes: If swap used over memory used is larger than this amount, an alert is generated
|
42
|
+
# default: 3
|
43
|
+
# attributes: required advanced
|
44
|
+
class PluginOptions < Array
|
45
|
+
|
46
|
+
attr_accessor :error
|
47
|
+
|
48
|
+
# Should be valid YAML, a hash of hashes ... if not, will be caught in the rescue below
|
49
|
+
def self.from_yaml(string)
|
50
|
+
options_array=[]
|
51
|
+
error=nil
|
52
|
+
|
53
|
+
items=YAML.load(string)
|
54
|
+
items.each_pair {|name, hash| options_array.push(PluginOption.new(name,hash)) }
|
55
|
+
rescue
|
56
|
+
error="Invalid Plugin Options"
|
57
|
+
ensure
|
58
|
+
res=PluginOptions.new(options_array)
|
59
|
+
res.error=error
|
60
|
+
return res
|
61
|
+
end
|
62
|
+
|
63
|
+
def advanced
|
64
|
+
select{|o|o.advanced? }
|
65
|
+
end
|
66
|
+
|
67
|
+
def regular
|
68
|
+
select{|o|!o.advanced? }
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
res=[]
|
73
|
+
each_with_index do |opt,i|
|
74
|
+
res.push "#{i+1}. #{opt.to_s}"
|
75
|
+
end
|
76
|
+
res.join("\n")
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
data/lib/scout/server.rb
CHANGED
@@ -34,7 +34,10 @@ module Scout
|
|
34
34
|
# We consider the interval close enough at this point.
|
35
35
|
#
|
36
36
|
RUN_DELTA = 30
|
37
|
-
|
37
|
+
|
38
|
+
attr_reader :new_plan
|
39
|
+
attr_reader :directives
|
40
|
+
|
38
41
|
# Creates a new Scout Server connection.
|
39
42
|
def initialize(server, client_key, history_file, logger = nil)
|
40
43
|
@server = server
|
@@ -42,56 +45,116 @@ module Scout
|
|
42
45
|
@history_file = history_file
|
43
46
|
@history = Hash.new
|
44
47
|
@logger = logger
|
45
|
-
|
48
|
+
@plugin_plan = []
|
49
|
+
@directives = {} # take_snapshots
|
50
|
+
@new_plan = false
|
51
|
+
|
52
|
+
# the block is only passed for install and test, since we split plan retrieval outside the lockfile for run
|
46
53
|
if block_given?
|
47
54
|
load_history
|
48
55
|
yield self
|
49
56
|
save_history
|
50
57
|
end
|
51
58
|
end
|
52
|
-
|
53
|
-
# Prepares a check-in data structure to hold Plugin generated data.
|
54
|
-
def prepare_checkin
|
55
|
-
@checkin = { :reports => Array.new,
|
56
|
-
:alerts => Array.new,
|
57
|
-
:errors => Array.new,
|
58
|
-
:summaries => Array.new }
|
59
|
-
end
|
60
|
-
|
61
|
-
def show_checkin(printer = :p)
|
62
|
-
send(printer, @checkin)
|
63
|
-
end
|
64
|
-
|
65
|
-
#
|
66
|
-
# Loads the history file from disk. If the file does not exist,
|
67
|
-
# it creates one.
|
59
|
+
|
68
60
|
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
61
|
+
# Retrieves the Plugin Plan from the server. This is the list of plugins
|
62
|
+
# to execute, along with all options.
|
63
|
+
#
|
64
|
+
# This method has a couple of side effects:
|
65
|
+
# 1) it sets the @plugin plan with either A) whatever is in history, B) the results of the /plan retrieval
|
66
|
+
# 2) it sets @checkin_to = true IF so directed by the scout server
|
67
|
+
def fetch_plan
|
68
|
+
url = urlify(:plan)
|
69
|
+
info "Pinging server at #{url}..."
|
70
|
+
headers = Hash.new
|
71
|
+
if @history["plan_last_modified"] and @history["old_plugins"]
|
72
|
+
headers["If-Modified-Since"] = @history["plan_last_modified"]
|
73
|
+
end
|
74
|
+
get(url, "Could not retrieve plan from server.", headers) do |res|
|
75
|
+
if res.is_a? Net::HTTPNotModified
|
76
|
+
info "Plan not modified. Will reuse saved plan."
|
77
|
+
@plugin_plan = Array(@history["old_plugins"])
|
78
|
+
@directives = @history["directives"] || Hash.new
|
79
|
+
else
|
80
|
+
info "plan has been modified. Will run the new plan now."
|
81
|
+
begin
|
82
|
+
body = res.body
|
83
|
+
if res["Content-Encoding"] == "gzip" and body and not body.empty?
|
84
|
+
body = Zlib::GzipReader.new(StringIO.new(body)).read
|
85
|
+
end
|
86
|
+
|
87
|
+
body_as_hash = JSON.parse(body)
|
88
|
+
@plugin_plan = Array(body_as_hash["plugins"])
|
89
|
+
@directives = body_as_hash["directives"].is_a?(Hash) ? body_as_hash["directives"] : Hash.new
|
90
|
+
|
91
|
+
@history["plan_last_modified"] = res["last-modified"]
|
92
|
+
@history["old_plugins"] = @plugin_plan
|
93
|
+
@history["directives"] = @directives
|
94
|
+
|
95
|
+
info "Plan loaded. (#{@plugin_plan.size} plugins: " +
|
96
|
+
"#{@plugin_plan.map { |p| p['name'] }.join(', ')})" +
|
97
|
+
". Directives: #{@directives.to_a.map{|a| "#{a.first}:#{a.last}"}.join(", ")}"
|
98
|
+
|
99
|
+
@new_plan = true # used in determination if we should checkin this time or not
|
100
|
+
rescue Exception
|
101
|
+
fatal "Plan from server was malformed."
|
102
|
+
exit
|
103
|
+
end
|
74
104
|
end
|
75
|
-
info "History file created."
|
76
105
|
end
|
77
|
-
debug "Loading history file..."
|
78
|
-
@history = File.open(@history_file) { |file| YAML.load(file) }
|
79
|
-
info "History file loaded."
|
80
106
|
end
|
81
|
-
|
82
|
-
#
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
107
|
+
|
108
|
+
# uses values from history and current time to determine if we should checkin at this time
|
109
|
+
def time_to_checkin?
|
110
|
+
@history['last_checkin'] == nil ||
|
111
|
+
@directives['interval'] == nil ||
|
112
|
+
(Time.now.to_i - Time.at(@history['last_checkin']).to_i).abs+15 > @directives['interval'].to_i*60
|
113
|
+
rescue
|
114
|
+
debug "Failed to calculate time_to_checkin. @history['last_checkin']=#{@history['last_checkin']}. "+
|
115
|
+
"@directives['interval']=#{@directives['interval']}. Time.now.to_i=#{Time.now.to_i}"
|
116
|
+
return true
|
87
117
|
end
|
88
|
-
|
89
|
-
#
|
118
|
+
|
119
|
+
# uses values from history and current time to determine if we should ping the server at this time
|
120
|
+
def time_to_ping?
|
121
|
+
return true if
|
122
|
+
@history['last_ping'] == nil ||
|
123
|
+
@directives['ping_interval'] == nil ||
|
124
|
+
(Time.now.to_i - Time.at(@history['last_ping']).to_i).abs+15 > @directives['ping_interval'].to_i*60
|
125
|
+
rescue
|
126
|
+
debug "Failed to calculate time_to_ping. @history['last_ping']=#{@history['last_ping']}. "+
|
127
|
+
"@directives['ping_interval']=#{@directives['ping_interval']}. Time.now.to_i=#{Time.now.to_i}"
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
|
131
|
+
# returns a human-readable representation of the next checkin, i.e., 5min 30sec
|
132
|
+
def next_checkin
|
133
|
+
secs= @directives['interval'].to_i*60 - (Time.now.to_i - Time.at(@history['last_checkin']).to_i).abs
|
134
|
+
minutes=(secs.to_f/60).floor
|
135
|
+
secs=secs%60
|
136
|
+
"#{minutes}min #{secs} sec"
|
137
|
+
rescue
|
138
|
+
"[next scout invocation]"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Runs all plugins from the given plan. Calls process_plugin on each plugin.
|
142
|
+
# @plugin_execution_plan is populated by calling fetch_plan
|
90
143
|
def run_plugins_by_plan
|
91
144
|
prepare_checkin
|
92
|
-
|
93
|
-
|
145
|
+
@plugin_plan.each do |plugin|
|
146
|
+
begin
|
147
|
+
process_plugin(plugin)
|
148
|
+
rescue Exception
|
149
|
+
@checkin[:errors] << build_report(
|
150
|
+
plugin['id'],
|
151
|
+
:subject => "Exception: #{$!.message}.",
|
152
|
+
:body => $!.backtrace
|
153
|
+
)
|
154
|
+
error("Encountered an error: #{$!.message}")
|
155
|
+
end
|
94
156
|
end
|
157
|
+
take_snapshot if @directives['take_snapshots']
|
95
158
|
checkin
|
96
159
|
end
|
97
160
|
|
@@ -105,7 +168,7 @@ module Scout
|
|
105
168
|
# set memory and last_run information in the history file.
|
106
169
|
#
|
107
170
|
def process_plugin(plugin)
|
108
|
-
info "Processing the #{plugin['name']} plugin:"
|
171
|
+
info "Processing the '#{plugin['name']}' plugin:"
|
109
172
|
id_and_name = "#{plugin['id']}-#{plugin['name']}".sub(/\A-/, "")
|
110
173
|
last_run = @history["last_runs"][id_and_name] ||
|
111
174
|
@history["last_runs"][plugin['name']]
|
@@ -126,6 +189,7 @@ module Scout
|
|
126
189
|
rescue Exception
|
127
190
|
raise if $!.is_a? SystemExit
|
128
191
|
error "Plugin would not compile: #{$!.message}"
|
192
|
+
@checkin[:errors] << build_report(plugin['id'],:subject => "Plugin would not compile", :body=>$!.message)
|
129
193
|
return
|
130
194
|
end
|
131
195
|
debug "Loading plugin..."
|
@@ -140,13 +204,19 @@ module Scout
|
|
140
204
|
Timeout.timeout(timeout, PluginTimeoutError) do
|
141
205
|
data = job.run
|
142
206
|
end
|
143
|
-
rescue Timeout::Error
|
207
|
+
rescue Timeout::Error, PluginTimeoutError
|
144
208
|
error "Plugin took too long to run."
|
209
|
+
@checkin[:errors] << build_report(plugin['id'],
|
210
|
+
:subject => "Plugin took too long to run",
|
211
|
+
:body=>"Execution timed out.")
|
145
212
|
return
|
146
213
|
rescue Exception
|
147
214
|
raise if $!.is_a? SystemExit
|
148
215
|
error "Plugin failed to run: #{$!.class}: #{$!.message}\n" +
|
149
216
|
"#{$!.backtrace.join("\n")}"
|
217
|
+
@checkin[:errors] << build_report(plugin['id'],
|
218
|
+
:subject => "Plugin failed to run",
|
219
|
+
:body=>"#{$!.class}: #{$!.message}\n#{$!.backtrace.join("\n")}")
|
150
220
|
end
|
151
221
|
info "Plugin completed its run."
|
152
222
|
|
@@ -168,7 +238,7 @@ module Scout
|
|
168
238
|
@history["memory"][id_and_name] = data[:memory]
|
169
239
|
else
|
170
240
|
@checkin[:errors] << build_report(
|
171
|
-
|
241
|
+
plugin['id'],
|
172
242
|
:subject => "Plugin would not load."
|
173
243
|
)
|
174
244
|
end
|
@@ -189,57 +259,62 @@ module Scout
|
|
189
259
|
error "Unable to remove plugin."
|
190
260
|
end
|
191
261
|
end
|
192
|
-
info "Plugin #{plugin['name']} processing complete."
|
262
|
+
info "Plugin '#{plugin['name']}' processing complete."
|
193
263
|
end
|
194
|
-
|
195
|
-
|
196
|
-
#
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
264
|
+
|
265
|
+
|
266
|
+
# captures a list of processes running at this moment
|
267
|
+
def take_snapshot
|
268
|
+
info "Taking a process snapshot"
|
269
|
+
ps=%x(ps aux).split("\n")[1..-1].join("\n") # get rid of the header line
|
270
|
+
@checkin[:snapshot]=ps
|
271
|
+
rescue Exception
|
272
|
+
error "unable to capture processes on this server. #{$!.message}"
|
273
|
+
return nil
|
274
|
+
end
|
275
|
+
|
276
|
+
# Prepares a check-in data structure to hold Plugin generated data.
|
277
|
+
def prepare_checkin
|
278
|
+
@checkin = { :reports => Array.new,
|
279
|
+
:alerts => Array.new,
|
280
|
+
:errors => Array.new,
|
281
|
+
:summaries => Array.new,
|
282
|
+
:snapshot => '' }
|
283
|
+
end
|
284
|
+
|
285
|
+
def show_checkin(printer = :p)
|
286
|
+
send(printer, @checkin)
|
287
|
+
end
|
288
|
+
|
289
|
+
#
|
290
|
+
# Loads the history file from disk. If the file does not exist,
|
291
|
+
# it creates one.
|
292
|
+
#
|
293
|
+
def load_history
|
294
|
+
unless File.exist? @history_file
|
295
|
+
create_blank_history
|
205
296
|
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
plugin_execution_plan = Array(JSON.parse(body)["plugins"])
|
217
|
-
if res["Last-Modified"]
|
218
|
-
@history["last_modified_for_plugins"] = res["last-modified"]
|
219
|
-
@history["old_plugins"] = plugin_execution_plan
|
220
|
-
end
|
221
|
-
info "Plan loaded. (#{plugin_execution_plan.size} plugins: " +
|
222
|
-
"#{plugin_execution_plan.map { |p| p['name'] }.join(', ')})"
|
223
|
-
rescue Exception
|
224
|
-
fatal "Plan from server was malformed."
|
225
|
-
exit
|
226
|
-
end
|
227
|
-
end
|
228
|
-
plugin_execution_plan.each do |plugin|
|
229
|
-
begin
|
230
|
-
yield plugin if block_given?
|
231
|
-
rescue RuntimeError
|
232
|
-
@checkin[:errors] << build_report(
|
233
|
-
plugin_id['id'],
|
234
|
-
:subject => "Exception: #{$!.message}.",
|
235
|
-
:body => $!.backtrace
|
236
|
-
)
|
237
|
-
end
|
238
|
-
end
|
297
|
+
debug "Loading history file..."
|
298
|
+
@history = File.open(@history_file) { |file| YAML.load(file) }
|
299
|
+
info "History file loaded."
|
300
|
+
end
|
301
|
+
|
302
|
+
# creates a blank history file
|
303
|
+
def create_blank_history
|
304
|
+
debug "Creating empty history file..."
|
305
|
+
File.open(@history_file, "w") do |file|
|
306
|
+
YAML.dump({"last_runs" => Hash.new, "memory" => Hash.new}, file)
|
239
307
|
end
|
308
|
+
info "History file created."
|
309
|
+
end
|
310
|
+
|
311
|
+
# Saves the history file to disk.
|
312
|
+
def save_history
|
313
|
+
debug "Saving history file..."
|
314
|
+
File.open(@history_file, "w") { |file| YAML.dump(@history, file) }
|
315
|
+
info "History file saved."
|
240
316
|
end
|
241
|
-
|
242
|
-
|
317
|
+
|
243
318
|
private
|
244
319
|
|
245
320
|
def build_report(plugin_id, fields)
|
@@ -293,8 +368,9 @@ module Scout
|
|
293
368
|
when Net::HTTPSuccess, Net::HTTPNotModified
|
294
369
|
response_handler[response] unless response_handler.nil?
|
295
370
|
else
|
371
|
+
error = "Server says: #{response['x-scout-msg']}" if response['x-scout-msg']
|
296
372
|
fatal error
|
297
|
-
|
373
|
+
raise SystemExit.new(error)
|
298
374
|
end
|
299
375
|
rescue Timeout::Error
|
300
376
|
fatal "Request timed out."
|
@@ -306,6 +382,7 @@ module Scout
|
|
306
382
|
end
|
307
383
|
|
308
384
|
def checkin
|
385
|
+
@history['last_checkin'] = Time.now.to_i # might have to save the time of invocation and use here to prevent drift
|
309
386
|
io = StringIO.new
|
310
387
|
gzip = Zlib::GzipWriter.new(io)
|
311
388
|
gzip << @checkin.to_json
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Highgroove Studios
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-23 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -46,18 +46,12 @@ files:
|
|
46
46
|
- lib/scout/command/test.rb
|
47
47
|
- lib/scout/command.rb
|
48
48
|
- lib/scout/plugin.rb
|
49
|
+
- lib/scout/plugin_options.rb
|
49
50
|
- lib/scout/server.rb
|
50
51
|
- lib/scout.rb
|
51
52
|
- data/cacert.pem
|
52
53
|
- data/gpl-2.0.txt
|
53
54
|
- data/lgpl-2.1.txt
|
54
|
-
- vendor/json_pure/CHANGES
|
55
|
-
- vendor/json_pure/GPL
|
56
|
-
- vendor/json_pure/README
|
57
|
-
- vendor/json_pure/RUBY
|
58
|
-
- vendor/json_pure/Rakefile
|
59
|
-
- vendor/json_pure/TODO
|
60
|
-
- vendor/json_pure/VERSION
|
61
55
|
- vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log
|
62
56
|
- vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat
|
63
57
|
- vendor/json_pure/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat
|
@@ -93,6 +87,7 @@ files:
|
|
93
87
|
- vendor/json_pure/benchmarks/parser_benchmark.rb
|
94
88
|
- vendor/json_pure/bin/edit_json.rb
|
95
89
|
- vendor/json_pure/bin/prettify_json.rb
|
90
|
+
- vendor/json_pure/CHANGES
|
96
91
|
- vendor/json_pure/data/example.json
|
97
92
|
- vendor/json_pure/data/index.html
|
98
93
|
- vendor/json_pure/data/prototype.js
|
@@ -106,26 +101,30 @@ files:
|
|
106
101
|
- vendor/json_pure/ext/json/ext/parser/parser.rl
|
107
102
|
- vendor/json_pure/ext/json/ext/parser/unicode.c
|
108
103
|
- vendor/json_pure/ext/json/ext/parser/unicode.h
|
104
|
+
- vendor/json_pure/GPL
|
109
105
|
- vendor/json_pure/install.rb
|
110
|
-
- vendor/json_pure/lib/json/Array.xpm
|
111
|
-
- vendor/json_pure/lib/json/FalseClass.xpm
|
112
|
-
- vendor/json_pure/lib/json/Hash.xpm
|
113
|
-
- vendor/json_pure/lib/json/Key.xpm
|
114
|
-
- vendor/json_pure/lib/json/NilClass.xpm
|
115
|
-
- vendor/json_pure/lib/json/Numeric.xpm
|
116
|
-
- vendor/json_pure/lib/json/String.xpm
|
117
|
-
- vendor/json_pure/lib/json/TrueClass.xpm
|
118
106
|
- vendor/json_pure/lib/json/add/core.rb
|
119
107
|
- vendor/json_pure/lib/json/add/rails.rb
|
108
|
+
- vendor/json_pure/lib/json/Array.xpm
|
120
109
|
- vendor/json_pure/lib/json/common.rb
|
121
110
|
- vendor/json_pure/lib/json/editor.rb
|
122
111
|
- vendor/json_pure/lib/json/ext.rb
|
112
|
+
- vendor/json_pure/lib/json/FalseClass.xpm
|
113
|
+
- vendor/json_pure/lib/json/Hash.xpm
|
123
114
|
- vendor/json_pure/lib/json/json.xpm
|
115
|
+
- vendor/json_pure/lib/json/Key.xpm
|
116
|
+
- vendor/json_pure/lib/json/NilClass.xpm
|
117
|
+
- vendor/json_pure/lib/json/Numeric.xpm
|
124
118
|
- vendor/json_pure/lib/json/pure/generator.rb
|
125
119
|
- vendor/json_pure/lib/json/pure/parser.rb
|
126
120
|
- vendor/json_pure/lib/json/pure.rb
|
121
|
+
- vendor/json_pure/lib/json/String.xpm
|
122
|
+
- vendor/json_pure/lib/json/TrueClass.xpm
|
127
123
|
- vendor/json_pure/lib/json/version.rb
|
128
124
|
- vendor/json_pure/lib/json.rb
|
125
|
+
- vendor/json_pure/Rakefile
|
126
|
+
- vendor/json_pure/README
|
127
|
+
- vendor/json_pure/RUBY
|
129
128
|
- vendor/json_pure/tests/fixtures/fail1.json
|
130
129
|
- vendor/json_pure/tests/fixtures/fail10.json
|
131
130
|
- vendor/json_pure/tests/fixtures/fail11.json
|
@@ -163,8 +162,10 @@ files:
|
|
163
162
|
- vendor/json_pure/tests/test_json_generate.rb
|
164
163
|
- vendor/json_pure/tests/test_json_rails.rb
|
165
164
|
- vendor/json_pure/tests/test_json_unicode.rb
|
165
|
+
- vendor/json_pure/TODO
|
166
166
|
- vendor/json_pure/tools/fuzz.rb
|
167
167
|
- vendor/json_pure/tools/server.rb
|
168
|
+
- vendor/json_pure/VERSION
|
168
169
|
- Rakefile
|
169
170
|
- AUTHORS
|
170
171
|
- COPYING
|