fiveruns-dash-ruby 0.8.3 → 0.8.4
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.rdoc +5 -4
- data/Rakefile +1 -2
- data/lib/fiveruns/dash.rb +5 -4
- data/lib/fiveruns/dash/metric.rb +2 -1
- data/lib/fiveruns/dash/reporter.rb +9 -7
- data/lib/fiveruns/dash/store/file.rb +1 -1
- data/lib/fiveruns/dash/store/http.rb +4 -4
- data/lib/fiveruns/dash/trace.rb +4 -4
- data/lib/fiveruns/dash/update.rb +4 -4
- data/lib/fiveruns/json.rb +15 -0
- data/lib/fiveruns/json/add/core.rb +131 -0
- data/lib/fiveruns/json/add/rails.rb +55 -0
- data/lib/fiveruns/json/common.rb +352 -0
- data/lib/fiveruns/json/generator.rb +394 -0
- data/lib/fiveruns/json/pure.rb +71 -0
- data/lib/fiveruns/json/version.rb +8 -0
- data/test/collector_communication_test.rb +1 -1
- data/test/data_payload.bin.gz +0 -0
- data/test/json_test.rb +33 -0
- data/version.yml +1 -1
- metadata +15 -13
data/README.rdoc
CHANGED
@@ -18,10 +18,6 @@ See the Ruby support pages, http://support.fiveruns.com/faqs/dash/ruby, for info
|
|
18
18
|
|
19
19
|
The FiveRuns Development Team & Dash community
|
20
20
|
|
21
|
-
== Dependencies
|
22
|
-
|
23
|
-
* The json gem
|
24
|
-
|
25
21
|
== Platforms
|
26
22
|
|
27
23
|
This library has only been tested on OSX and Linux. The `ruby' recipe (which collects metrics on the Ruby process) currently relies on `ps' -- so will not work on Windows. We're actively looking for contributions to widen the number of platforms we support; please help!
|
@@ -40,6 +36,11 @@ Please join the dash-users Google group, http://groups.google.com/group/dash-use
|
|
40
36
|
|
41
37
|
You can also contact us via Twitter, Campfire, or email; see the main help page, http://support.fiveruns.com, for details.
|
42
38
|
|
39
|
+
== Note
|
40
|
+
|
41
|
+
This project contains a modified copy of the json_pure gem source code, which is distributed under
|
42
|
+
the Ruby license <http://www.ruby-lang.org/en/LICENSE.txt> by Florian Frank <flori@ping.de>.
|
43
|
+
|
43
44
|
== License
|
44
45
|
|
45
46
|
# (The FiveRuns License)
|
data/Rakefile
CHANGED
@@ -19,7 +19,6 @@ begin
|
|
19
19
|
s.description = "Provides an API to send metrics to the FiveRuns Dash service"
|
20
20
|
s.authors = ["FiveRuns Development Team"]
|
21
21
|
s.files = FileList['README.rdoc', 'Rakefile', 'version.yml', "{lib,test,recipes,examples}/**/*", ]
|
22
|
-
s.add_dependency 'json'
|
23
22
|
end
|
24
23
|
rescue LoadError
|
25
24
|
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
@@ -35,4 +34,4 @@ task :coverage do
|
|
35
34
|
FileUtils.cp_r 'coverage', ccout
|
36
35
|
end
|
37
36
|
system "open coverage/index.html" if PLATFORM['darwin']
|
38
|
-
end
|
37
|
+
end
|
data/lib/fiveruns/dash.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
|
2
|
+
|
3
|
+
module Fiveruns; end
|
4
|
+
|
5
|
+
# Pull in our forked copy of the pure JSON gem
|
6
|
+
require 'fiveruns/json'
|
3
7
|
|
4
8
|
require 'pathname'
|
5
9
|
require 'thread'
|
@@ -10,9 +14,6 @@ $:.unshift(File.dirname(__FILE__))
|
|
10
14
|
|
11
15
|
# NB: Pre-load ALL Dash files here so we do not accidentally
|
12
16
|
# use ActiveSupport's autoloading.
|
13
|
-
|
14
|
-
module Fiveruns; end
|
15
|
-
|
16
17
|
require 'dash/version'
|
17
18
|
require 'dash/util'
|
18
19
|
require 'dash/configuration'
|
data/lib/fiveruns/dash/metric.rb
CHANGED
@@ -47,8 +47,9 @@ module Fiveruns::Dash
|
|
47
47
|
Please set the :ar_total_time option when configuring Dash:
|
48
48
|
|
49
49
|
# Define an application-specific metric cooresponding to the total processing time for this app.
|
50
|
+
# You must mark this time so Dash can ignore any AR activity outside of the call stack.
|
50
51
|
Fiveruns::Dash.register_recipe :loader, :url => 'http://dash.fiveruns.com' do |recipe|
|
51
|
-
recipe.time :total_time, 'Load Time', :method => 'Loader::Engine#load'
|
52
|
+
recipe.time :total_time, 'Load Time', :method => 'Loader::Engine#load', :mark => true
|
52
53
|
end
|
53
54
|
|
54
55
|
# Pass the name of this custom metric to Dash so it will be used in the AR metric calculations.
|
@@ -51,7 +51,7 @@ module Fiveruns::Dash
|
|
51
51
|
def send_trace(trace)
|
52
52
|
if trace.data
|
53
53
|
payload = TracePayload.new(trace)
|
54
|
-
Fiveruns::Dash.logger.debug "Sending trace: #{payload.
|
54
|
+
Fiveruns::Dash.logger.debug "Sending trace: #{payload.to_fjson}"
|
55
55
|
Thread.new { Update.new(payload).store(*update_locations) }
|
56
56
|
else
|
57
57
|
Fiveruns::Dash.logger.debug "No trace to send"
|
@@ -81,7 +81,9 @@ module Fiveruns::Dash
|
|
81
81
|
%w(INT TERM).each do |sym|
|
82
82
|
TRAPS[sym] = Signal.trap(sym) do
|
83
83
|
stop
|
84
|
-
TRAPS[sym]
|
84
|
+
if TRAPS[sym] and TRAPS[sym].respond_to?(:call)
|
85
|
+
TRAPS[sym].call
|
86
|
+
end
|
85
87
|
end
|
86
88
|
end
|
87
89
|
end
|
@@ -134,7 +136,7 @@ module Fiveruns::Dash
|
|
134
136
|
def send_info_update
|
135
137
|
@info_update_sent ||= begin
|
136
138
|
payload = InfoPayload.new(@session.info, @started_at)
|
137
|
-
Fiveruns::Dash.logger.debug "Sending info: #{payload.
|
139
|
+
Fiveruns::Dash.logger.debug "Sending info: #{payload.to_fjson}"
|
138
140
|
result = Update.new(payload).store(*update_locations)
|
139
141
|
send_fake_info(payload)
|
140
142
|
result
|
@@ -148,7 +150,7 @@ module Fiveruns::Dash
|
|
148
150
|
Fiveruns::Dash.logger.debug "No exceptions for this interval"
|
149
151
|
else
|
150
152
|
payload = ExceptionsPayload.new(data)
|
151
|
-
Fiveruns::Dash.logger.debug "Sending exceptions: #{payload.
|
153
|
+
Fiveruns::Dash.logger.debug "Sending exceptions: #{payload.to_fjson}"
|
152
154
|
Update.new(payload).store(*update_locations)
|
153
155
|
end
|
154
156
|
else
|
@@ -162,7 +164,7 @@ module Fiveruns::Dash
|
|
162
164
|
if @info_update_sent
|
163
165
|
data = @session.data
|
164
166
|
payload = DataPayload.new(data)
|
165
|
-
Fiveruns::Dash.logger.debug "Sending data: #{payload.
|
167
|
+
Fiveruns::Dash.logger.debug "Sending data: #{payload.to_fjson}"
|
166
168
|
result = Update.new(payload).store(*update_locations)
|
167
169
|
send_fake_data(payload)
|
168
170
|
result
|
@@ -184,7 +186,7 @@ module Fiveruns::Dash
|
|
184
186
|
def send_fake_data(payload)
|
185
187
|
fake_host_count.times do |idx|
|
186
188
|
payload.params[:process_id] = Fiveruns::Dash.process_ids[idx+1]
|
187
|
-
Fiveruns::Dash.logger.debug "Sending data: #{payload.
|
189
|
+
Fiveruns::Dash.logger.debug "Sending data: #{payload.to_fjson}"
|
188
190
|
Update.new(payload).store(*update_locations)
|
189
191
|
end
|
190
192
|
end
|
@@ -194,7 +196,7 @@ module Fiveruns::Dash
|
|
194
196
|
fake_host_count.times do |idx|
|
195
197
|
payload.params[:mac] += idx.to_s
|
196
198
|
payload.params[:hostname] = host + idx.to_s
|
197
|
-
Fiveruns::Dash.logger.debug "Sending info: #{payload.
|
199
|
+
Fiveruns::Dash.logger.debug "Sending info: #{payload.to_fjson}"
|
198
200
|
Update.new(payload).store(*update_locations)
|
199
201
|
end
|
200
202
|
end
|
@@ -68,8 +68,8 @@ module Fiveruns::Dash::Store
|
|
68
68
|
end
|
69
69
|
case response.code.to_i
|
70
70
|
when 201
|
71
|
-
data = JSON.load(response.body)
|
72
|
-
set_trace_contexts(data)
|
71
|
+
# data = Fiveruns::JSON.load(response.body)
|
72
|
+
# set_trace_contexts(data)
|
73
73
|
true
|
74
74
|
when 400..499
|
75
75
|
Fiveruns::Dash.logger.warn "Could not access Dash service (#{response.code.to_i}, #{response.body.inspect})"
|
@@ -78,9 +78,9 @@ module Fiveruns::Dash::Store
|
|
78
78
|
Fiveruns::Dash.logger.debug "Received unknown response from Dash service (#{response.inspect})"
|
79
79
|
false
|
80
80
|
end
|
81
|
-
rescue JSON::ParserError => e
|
81
|
+
rescue Fiveruns::JSON::ParserError => e
|
82
82
|
puts response.body
|
83
|
-
Fiveruns::Dash.logger.error "Received non-
|
83
|
+
Fiveruns::Dash.logger.error "Received non-FiverunsJSON response (#{response.inspect})"
|
84
84
|
false
|
85
85
|
end
|
86
86
|
|
data/lib/fiveruns/dash/trace.rb
CHANGED
@@ -27,10 +27,10 @@ module Fiveruns::Dash
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def to_fjson
|
31
31
|
{ :context => context,
|
32
32
|
:data => (@data || {})
|
33
|
-
}.
|
33
|
+
}.to_fjson
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
@@ -51,11 +51,11 @@ module Fiveruns::Dash
|
|
51
51
|
@children ||= []
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
54
|
+
def to_fjson
|
55
55
|
{
|
56
56
|
:metrics => metrics,
|
57
57
|
:children => children,
|
58
|
-
}.
|
58
|
+
}.to_fjson
|
59
59
|
end
|
60
60
|
|
61
61
|
end
|
data/lib/fiveruns/dash/update.rb
CHANGED
@@ -27,7 +27,7 @@ module Fiveruns::Dash
|
|
27
27
|
response = http.post("/apps/#{token}/ping", multipart.to_s, "Content-Type" => multipart.content_type)
|
28
28
|
case response.code.to_i
|
29
29
|
when 201
|
30
|
-
data = JSON.load(response.body)
|
30
|
+
data = ::Fiveruns::JSON.load(response.body)
|
31
31
|
[:success, "Found application '#{data['name']}'"]
|
32
32
|
else
|
33
33
|
# Error message
|
@@ -133,8 +133,8 @@ module Fiveruns::Dash
|
|
133
133
|
{}
|
134
134
|
end
|
135
135
|
|
136
|
-
def
|
137
|
-
@data.
|
136
|
+
def to_fjson
|
137
|
+
@data.to_fjson
|
138
138
|
end
|
139
139
|
|
140
140
|
#######
|
@@ -146,7 +146,7 @@ module Fiveruns::Dash
|
|
146
146
|
end
|
147
147
|
|
148
148
|
def compressed
|
149
|
-
Zlib::Deflate.deflate(
|
149
|
+
Zlib::Deflate.deflate(to_fjson)
|
150
150
|
end
|
151
151
|
|
152
152
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Private copy of JSON for FiveRuns Dash as the current state of JSON/Ruby is
|
2
|
+
# nightmarish for library authors. ActiveSupport and JSON have incompatability
|
3
|
+
# issues and supporting/fixing them is not worth it.
|
4
|
+
#
|
5
|
+
# == Authors
|
6
|
+
#
|
7
|
+
# Florian Frank <mailto:flori@ping.de>
|
8
|
+
# FiveRuns Development Team
|
9
|
+
|
10
|
+
require 'fiveruns/json/version'
|
11
|
+
require 'fiveruns/json/common'
|
12
|
+
require 'fiveruns/json/generator'
|
13
|
+
require 'fiveruns/json/pure'
|
14
|
+
require 'fiveruns/json/add/core'
|
15
|
+
require 'fiveruns/json/add/rails'
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# This file contains implementations of ruby core's custom objects for
|
2
|
+
# serialisation/deserialisation.
|
3
|
+
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
class Time
|
7
|
+
def self.fjson_create(object)
|
8
|
+
if usec = object.delete('u') # used to be tv_usec -> tv_nsec
|
9
|
+
object['n'] = usec * 1000
|
10
|
+
end
|
11
|
+
if respond_to?(:tv_nsec)
|
12
|
+
at(*object.values_at('s', 'n'))
|
13
|
+
else
|
14
|
+
at(object['s'], object['n'] / 1000)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_fjson(*args)
|
19
|
+
{
|
20
|
+
'json_class' => self.class.name,
|
21
|
+
's' => tv_sec,
|
22
|
+
'n' => respond_to?(:tv_nsec) ? tv_nsec : tv_usec * 1000
|
23
|
+
}.to_fjson(*args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Date
|
28
|
+
def self.fjson_create(object)
|
29
|
+
civil(*object.values_at('y', 'm', 'd', 'sg'))
|
30
|
+
end
|
31
|
+
|
32
|
+
alias start sg unless method_defined?(:start)
|
33
|
+
|
34
|
+
def to_fjson(*args)
|
35
|
+
{
|
36
|
+
'json_class' => self.class.name,
|
37
|
+
'y' => year,
|
38
|
+
'm' => month,
|
39
|
+
'd' => day,
|
40
|
+
'sg' => start,
|
41
|
+
}.to_fjson(*args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class DateTime
|
46
|
+
def self.fjson_create(object)
|
47
|
+
args = object.values_at('y', 'm', 'd', 'H', 'M', 'S')
|
48
|
+
of_a, of_b = object['of'].split('/')
|
49
|
+
if of_b and of_b != '0'
|
50
|
+
args << Rational(of_a.to_i, of_b.to_i)
|
51
|
+
else
|
52
|
+
args << of_a
|
53
|
+
end
|
54
|
+
args << object['sg']
|
55
|
+
civil(*args)
|
56
|
+
end
|
57
|
+
|
58
|
+
alias start sg unless method_defined?(:start)
|
59
|
+
|
60
|
+
def to_fjson(*args)
|
61
|
+
{
|
62
|
+
'json_class' => self.class.name,
|
63
|
+
'y' => year,
|
64
|
+
'm' => month,
|
65
|
+
'd' => day,
|
66
|
+
'H' => hour,
|
67
|
+
'M' => min,
|
68
|
+
'S' => sec,
|
69
|
+
'of' => offset.to_s,
|
70
|
+
'sg' => start,
|
71
|
+
}.to_fjson(*args)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Range
|
76
|
+
def self.fjson_create(object)
|
77
|
+
new(*object['a'])
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_fjson(*args)
|
81
|
+
{
|
82
|
+
'json_class' => self.class.name,
|
83
|
+
'a' => [ first, last, exclude_end? ]
|
84
|
+
}.to_fjson(*args)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Struct
|
89
|
+
def self.fjson_create(object)
|
90
|
+
new(*object['v'])
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_fjson(*args)
|
94
|
+
klass = self.class.name
|
95
|
+
klass.empty? and raise Fiveruns::JSON::JSONError, "Only named structs are supported!"
|
96
|
+
{
|
97
|
+
'json_class' => klass,
|
98
|
+
'v' => values,
|
99
|
+
}.to_fjson(*args)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Exception
|
104
|
+
def self.fjson_create(object)
|
105
|
+
result = new(object['m'])
|
106
|
+
result.set_backtrace object['b']
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_fjson(*args)
|
111
|
+
{
|
112
|
+
'json_class' => self.class.name,
|
113
|
+
'm' => message,
|
114
|
+
'b' => backtrace,
|
115
|
+
}.to_fjson(*args)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Regexp
|
120
|
+
def self.fjson_create(object)
|
121
|
+
new(object['s'], object['o'])
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_fjson(*)
|
125
|
+
{
|
126
|
+
'json_class' => self.class.name,
|
127
|
+
'o' => options,
|
128
|
+
's' => source,
|
129
|
+
}.to_fjson
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# This file contains implementations of rails custom objects for
|
2
|
+
# serialisation/deserialisation.
|
3
|
+
|
4
|
+
require 'fiveruns/json'
|
5
|
+
|
6
|
+
class Object
|
7
|
+
def self.fjson_create(object)
|
8
|
+
obj = new
|
9
|
+
for key, value in object
|
10
|
+
next if key == 'json_class'
|
11
|
+
instance_variable_set "@#{key}", value
|
12
|
+
end
|
13
|
+
obj
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_fjson(*a)
|
17
|
+
result = {
|
18
|
+
'json_class' => self.class.name
|
19
|
+
}
|
20
|
+
instance_variables.inject(result) do |r, name|
|
21
|
+
r[name[1..-1]] = instance_variable_get name
|
22
|
+
r
|
23
|
+
end
|
24
|
+
result.to_fjson(*a)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Symbol
|
29
|
+
def to_fjson(*a)
|
30
|
+
to_s.to_fjson(*a)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Enumerable
|
35
|
+
def to_fjson(*a)
|
36
|
+
to_a.to_fjson(*a)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# class Regexp
|
41
|
+
# def to_json(*)
|
42
|
+
# inspect
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# The above rails definition has some problems:
|
47
|
+
#
|
48
|
+
# 1. { 'foo' => /bar/ }.to_json # => "{foo: /bar/}"
|
49
|
+
# This isn't valid FiverunsJSON, because the regular expression syntax is not
|
50
|
+
# defined in RFC 4627. (And unquoted strings are disallowed there, too.)
|
51
|
+
# Though it is valid Javascript.
|
52
|
+
#
|
53
|
+
# 2. { 'foo' => /bar/mix }.to_json # => "{foo: /bar/mix}"
|
54
|
+
# This isn't even valid Javascript.
|
55
|
+
|
@@ -0,0 +1,352 @@
|
|
1
|
+
module Fiveruns::JSON
|
2
|
+
class << self
|
3
|
+
# If _object_ is string-like parse the string and return the parsed result
|
4
|
+
# as a Ruby data structure. Otherwise generate a FiverunsJSON text from the Ruby
|
5
|
+
# data structure object and return it.
|
6
|
+
#
|
7
|
+
# The _opts_ argument is passed through to generate/parse respectively, see
|
8
|
+
# generate and parse for their documentation.
|
9
|
+
def [](object, opts = {})
|
10
|
+
if object.respond_to? :to_str
|
11
|
+
::Fiveruns::JSON.parse(object.to_str, opts => {})
|
12
|
+
else
|
13
|
+
::Fiveruns::JSON.generate(object, opts => {})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the FiverunsJSON parser class, that is used by FiverunsJSON. This might be either
|
18
|
+
# FiverunsJSON::Ext::Parser or FiverunsJSON::Pure::Parser.
|
19
|
+
attr_reader :parser
|
20
|
+
|
21
|
+
# Set the FiverunsJSON parser class _parser_ to be used by FiverunsJSON.
|
22
|
+
# def parser=(parser) # :nodoc:
|
23
|
+
# @parser = parser
|
24
|
+
# remove_const :Parser if const_defined? :Parser
|
25
|
+
# const_set :Parser, parser
|
26
|
+
# end
|
27
|
+
|
28
|
+
# Return the constant located at _path_. The format of _path_ has to be
|
29
|
+
# either ::A::B::C or A::B::C. In any case A has to be located at the top
|
30
|
+
# level (absolute namespace path?). If there doesn't exist a constant at
|
31
|
+
# the given path, an ArgumentError is raised.
|
32
|
+
def deep_const_get(path) # :nodoc:
|
33
|
+
path = path.to_s
|
34
|
+
path.split(/::/).inject(Object) do |p, c|
|
35
|
+
case
|
36
|
+
when c.empty? then p
|
37
|
+
when p.const_defined?(c) then p.const_get(c)
|
38
|
+
else raise ArgumentError, "can't find const #{path}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Set the module _generator_ to be used by FiverunsJSON.
|
44
|
+
def generator=(generator) # :nodoc:
|
45
|
+
@generator = generator
|
46
|
+
generator_methods = generator::GeneratorMethods
|
47
|
+
for const in generator_methods.constants
|
48
|
+
klass = deep_const_get(const)
|
49
|
+
modul = generator_methods.const_get(const)
|
50
|
+
klass.class_eval do
|
51
|
+
instance_methods(false).each do |m|
|
52
|
+
m.to_s == 'to_fjson' and remove_method m
|
53
|
+
end
|
54
|
+
include modul
|
55
|
+
end
|
56
|
+
end
|
57
|
+
self.state = generator::State
|
58
|
+
const_set :State, self.state
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the FiverunsJSON generator modul, that is used by FiverunsJSON. This might be
|
62
|
+
# either FiverunsJSON::Ext::Generator or FiverunsJSON::Pure::Generator.
|
63
|
+
attr_reader :generator
|
64
|
+
|
65
|
+
# Returns the FiverunsJSON generator state class, that is used by FiverunsJSON. This might
|
66
|
+
# be either FiverunsJSON::Ext::Generator::State or FiverunsJSON::Pure::Generator::State.
|
67
|
+
attr_accessor :state
|
68
|
+
|
69
|
+
# This is create identifier, that is used to decide, if the _json_create_
|
70
|
+
# hook of a class should be called. It defaults to 'json_class'.
|
71
|
+
attr_accessor :create_id
|
72
|
+
end
|
73
|
+
self.create_id = 'json_class'
|
74
|
+
|
75
|
+
NaN = (-1.0) ** 0.5
|
76
|
+
|
77
|
+
Infinity = 1.0/0
|
78
|
+
|
79
|
+
MinusInfinity = -Infinity
|
80
|
+
|
81
|
+
# The base exception for FiverunsJSON errors.
|
82
|
+
class JSONError < StandardError; end
|
83
|
+
|
84
|
+
# This exception is raised, if a parser error occurs.
|
85
|
+
class ParserError < ::Fiveruns::JSON::JSONError; end
|
86
|
+
|
87
|
+
# This exception is raised, if the nesting of parsed datastructures is too
|
88
|
+
# deep.
|
89
|
+
class NestingError < ParserError; end
|
90
|
+
|
91
|
+
# This exception is raised, if a generator or unparser error occurs.
|
92
|
+
class GeneratorError < ::Fiveruns::JSON::JSONError; end
|
93
|
+
# For backwards compatibility
|
94
|
+
UnparserError = GeneratorError
|
95
|
+
|
96
|
+
# If a circular data structure is encountered while unparsing
|
97
|
+
# this exception is raised.
|
98
|
+
class CircularDatastructure < GeneratorError; end
|
99
|
+
|
100
|
+
# This exception is raised, if the required unicode support is missing on the
|
101
|
+
# system. Usually this means, that the iconv library is not installed.
|
102
|
+
class MissingUnicodeSupport < ::Fiveruns::JSON::JSONError; end
|
103
|
+
|
104
|
+
module_function
|
105
|
+
|
106
|
+
# Parse the FiverunsJSON string _source_ into a Ruby data structure and return it.
|
107
|
+
#
|
108
|
+
# _opts_ can have the following
|
109
|
+
# keys:
|
110
|
+
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
|
111
|
+
# structures. Disable depth checking with :max_nesting => false, it defaults
|
112
|
+
# to 19.
|
113
|
+
# * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
|
114
|
+
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
|
115
|
+
# to false.
|
116
|
+
# * *create_additions*: If set to false, the Parser doesn't create
|
117
|
+
# additions even if a matchin class and create_id was found. This option
|
118
|
+
# defaults to true.
|
119
|
+
# def parse(source, opts = {})
|
120
|
+
# ::Fiveruns::JSON.parser.new(source, opts).parse
|
121
|
+
# end
|
122
|
+
|
123
|
+
# Parse the FiverunsJSON string _source_ into a Ruby data structure and return it.
|
124
|
+
# The bang version of the parse method, defaults to the more dangerous values
|
125
|
+
# for the _opts_ hash, so be sure only to parse trusted _source_ strings.
|
126
|
+
#
|
127
|
+
# _opts_ can have the following keys:
|
128
|
+
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
|
129
|
+
# structures. Enable depth checking with :max_nesting => anInteger. The parse!
|
130
|
+
# methods defaults to not doing max depth checking: This can be dangerous,
|
131
|
+
# if someone wants to fill up your stack.
|
132
|
+
# * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in
|
133
|
+
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
|
134
|
+
# to true.
|
135
|
+
# * *create_additions*: If set to false, the Parser doesn't create
|
136
|
+
# additions even if a matchin class and create_id was found. This option
|
137
|
+
# defaults to true.
|
138
|
+
# def parse!(source, opts = {})
|
139
|
+
# opts = {
|
140
|
+
# :max_nesting => false,
|
141
|
+
# :allow_nan => true
|
142
|
+
# }.update(opts)
|
143
|
+
# ::Fiveruns::JSON.parser.new(source, opts).parse
|
144
|
+
# end
|
145
|
+
|
146
|
+
# Unparse the Ruby data structure _obj_ into a single line FiverunsJSON string and
|
147
|
+
# return it. _state_ is
|
148
|
+
# * a FiverunsJSON::State object,
|
149
|
+
# * or a Hash like object (responding to to_hash),
|
150
|
+
# * an object convertible into a hash by a to_h method,
|
151
|
+
# that is used as or to configure a State object.
|
152
|
+
#
|
153
|
+
# It defaults to a state object, that creates the shortest possible FiverunsJSON text
|
154
|
+
# in one line, checks for circular data structures and doesn't allow NaN,
|
155
|
+
# Infinity, and -Infinity.
|
156
|
+
#
|
157
|
+
# A _state_ hash can have the following keys:
|
158
|
+
# * *indent*: a string used to indent levels (default: ''),
|
159
|
+
# * *space*: a string that is put after, a : or , delimiter (default: ''),
|
160
|
+
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
|
161
|
+
# * *object_nl*: a string that is put at the end of a FiverunsJSON object (default: ''),
|
162
|
+
# * *array_nl*: a string that is put at the end of a FiverunsJSON array (default: ''),
|
163
|
+
# * *check_circular*: true if checking for circular data structures
|
164
|
+
# should be done (the default), false otherwise.
|
165
|
+
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
166
|
+
# generated, otherwise an exception is thrown, if these values are
|
167
|
+
# encountered. This options defaults to false.
|
168
|
+
# * *max_nesting*: The maximum depth of nesting allowed in the data
|
169
|
+
# structures from which FiverunsJSON is to be generated. Disable depth checking
|
170
|
+
# with :max_nesting => false, it defaults to 19.
|
171
|
+
#
|
172
|
+
# See also the fast_generate for the fastest creation method with the least
|
173
|
+
# amount of sanity checks, and the pretty_generate method for some
|
174
|
+
# defaults for a pretty output.
|
175
|
+
def generate(obj, state = nil)
|
176
|
+
if state
|
177
|
+
state = State.from_state(state)
|
178
|
+
else
|
179
|
+
state = State.new
|
180
|
+
end
|
181
|
+
obj.to_fjson(state)
|
182
|
+
end
|
183
|
+
|
184
|
+
# :stopdoc:
|
185
|
+
# I want to deprecate these later, so I'll first be silent about them, and
|
186
|
+
# later delete them.
|
187
|
+
alias unparse generate
|
188
|
+
module_function :unparse
|
189
|
+
# :startdoc:
|
190
|
+
|
191
|
+
# Unparse the Ruby data structure _obj_ into a single line FiverunsJSON string and
|
192
|
+
# return it. This method disables the checks for circles in Ruby objects, and
|
193
|
+
# also generates NaN, Infinity, and, -Infinity float values.
|
194
|
+
#
|
195
|
+
# *WARNING*: Be careful not to pass any Ruby data structures with circles as
|
196
|
+
# _obj_ argument, because this will cause FiverunsJSON to go into an infinite loop.
|
197
|
+
def fast_generate(obj)
|
198
|
+
obj.to_fjson(nil)
|
199
|
+
end
|
200
|
+
|
201
|
+
# :stopdoc:
|
202
|
+
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
|
203
|
+
alias fast_unparse fast_generate
|
204
|
+
module_function :fast_unparse
|
205
|
+
# :startdoc:
|
206
|
+
|
207
|
+
# Unparse the Ruby data structure _obj_ into a FiverunsJSON string and return it. The
|
208
|
+
# returned string is a prettier form of the string returned by #unparse.
|
209
|
+
#
|
210
|
+
# The _opts_ argument can be used to configure the generator, see the
|
211
|
+
# generate method for a more detailed explanation.
|
212
|
+
def pretty_generate(obj, opts = nil)
|
213
|
+
state = ::Fiveruns::JSON.state.new(
|
214
|
+
:indent => ' ',
|
215
|
+
:space => ' ',
|
216
|
+
:object_nl => "\n",
|
217
|
+
:array_nl => "\n",
|
218
|
+
:check_circular => true
|
219
|
+
)
|
220
|
+
if opts
|
221
|
+
if opts.respond_to? :to_hash
|
222
|
+
opts = opts.to_hash
|
223
|
+
elsif opts.respond_to? :to_h
|
224
|
+
opts = opts.to_h
|
225
|
+
else
|
226
|
+
raise TypeError, "can't convert #{opts.class} into Hash"
|
227
|
+
end
|
228
|
+
state.configure(opts)
|
229
|
+
end
|
230
|
+
obj.to_fjson(state)
|
231
|
+
end
|
232
|
+
|
233
|
+
# :stopdoc:
|
234
|
+
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
|
235
|
+
alias pretty_unparse pretty_generate
|
236
|
+
module_function :pretty_unparse
|
237
|
+
# :startdoc:
|
238
|
+
|
239
|
+
# Load a ruby data structure from a FiverunsJSON _source_ and return it. A source can
|
240
|
+
# either be a string-like object, an IO like object, or an object responding
|
241
|
+
# to the read method. If _proc_ was given, it will be called with any nested
|
242
|
+
# Ruby object as an argument recursively in depth first order.
|
243
|
+
#
|
244
|
+
# This method is part of the implementation of the load/dump interface of
|
245
|
+
# Marshal and YAML.
|
246
|
+
def load(source, proc = nil)
|
247
|
+
if source.respond_to? :to_str
|
248
|
+
source = source.to_str
|
249
|
+
elsif source.respond_to? :to_io
|
250
|
+
source = source.to_io.read
|
251
|
+
else
|
252
|
+
source = source.read
|
253
|
+
end
|
254
|
+
result = parse(source, :max_nesting => false, :allow_nan => true)
|
255
|
+
recurse_proc(result, &proc) if proc
|
256
|
+
result
|
257
|
+
end
|
258
|
+
|
259
|
+
def recurse_proc(result, &proc)
|
260
|
+
case result
|
261
|
+
when Array
|
262
|
+
result.each { |x| recurse_proc x, &proc }
|
263
|
+
proc.call result
|
264
|
+
when Hash
|
265
|
+
result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
|
266
|
+
proc.call result
|
267
|
+
else
|
268
|
+
proc.call result
|
269
|
+
end
|
270
|
+
end
|
271
|
+
private :recurse_proc
|
272
|
+
module_function :recurse_proc
|
273
|
+
|
274
|
+
alias restore load
|
275
|
+
module_function :restore
|
276
|
+
|
277
|
+
# Dumps _obj_ as a FiverunsJSON string, i.e. calls generate on the object and returns
|
278
|
+
# the result.
|
279
|
+
#
|
280
|
+
# If anIO (an IO like object or an object that responds to the write method)
|
281
|
+
# was given, the resulting FiverunsJSON is written to it.
|
282
|
+
#
|
283
|
+
# If the number of nested arrays or objects exceeds _limit_ an ArgumentError
|
284
|
+
# exception is raised. This argument is similar (but not exactly the
|
285
|
+
# same!) to the _limit_ argument in Marshal.dump.
|
286
|
+
#
|
287
|
+
# This method is part of the implementation of the load/dump interface of
|
288
|
+
# Marshal and YAML.
|
289
|
+
def dump(obj, anIO = nil, limit = nil)
|
290
|
+
if anIO and limit.nil?
|
291
|
+
anIO = anIO.to_io if anIO.respond_to?(:to_io)
|
292
|
+
unless anIO.respond_to?(:write)
|
293
|
+
limit = anIO
|
294
|
+
anIO = nil
|
295
|
+
end
|
296
|
+
end
|
297
|
+
limit ||= 0
|
298
|
+
result = generate(obj, :allow_nan => true, :max_nesting => limit)
|
299
|
+
if anIO
|
300
|
+
anIO.write result
|
301
|
+
anIO
|
302
|
+
else
|
303
|
+
result
|
304
|
+
end
|
305
|
+
rescue ::Fiveruns::JSON::NestingError
|
306
|
+
raise ArgumentError, "exceed depth limit"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
module ::Kernel
|
311
|
+
# Outputs _objs_ to STDOUT as FiverunsJSON strings in the shortest form, that is in
|
312
|
+
# one line.
|
313
|
+
def fj(*objs)
|
314
|
+
objs.each do |obj|
|
315
|
+
puts ::Fiveruns::JSON::generate(obj, :allow_nan => true, :max_nesting => false)
|
316
|
+
end
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
|
320
|
+
# Ouputs _objs_ to STDOUT as FiverunsJSON strings in a pretty format, with
|
321
|
+
# indentation and over many lines.
|
322
|
+
def fjj(*objs)
|
323
|
+
objs.each do |obj|
|
324
|
+
puts ::Fiveruns::JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
|
325
|
+
end
|
326
|
+
nil
|
327
|
+
end
|
328
|
+
|
329
|
+
# If _object_ is string-like parse the string and return the parsed result as
|
330
|
+
# a Ruby data structure. Otherwise generate a FiverunsJSON text from the Ruby data
|
331
|
+
# structure object and return it.
|
332
|
+
#
|
333
|
+
# The _opts_ argument is passed through to generate/parse respectively, see
|
334
|
+
# generate and parse for their documentation.
|
335
|
+
def FJSON(object, opts = {})
|
336
|
+
# if object.respond_to? :to_str
|
337
|
+
# ::Fiveruns::JSON.parse(object.to_str, opts)
|
338
|
+
# else
|
339
|
+
::Fiveruns::JSON.generate(object, opts)
|
340
|
+
# end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
class ::Class
|
345
|
+
# Returns true, if this class can be used to create an instance
|
346
|
+
# from a serialised FiverunsJSON string. The class has to implement a class
|
347
|
+
# method _json_create_ that expects a hash as first parameter, which includes
|
348
|
+
# the required data.
|
349
|
+
def fjson_creatable?
|
350
|
+
respond_to?(:fjson_create)
|
351
|
+
end
|
352
|
+
end
|