code42 0.1.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/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +203 -0
- data/README.md +182 -0
- data/Rakefile +1 -0
- data/code42.gemspec +26 -0
- data/examples/Gemfile +6 -0
- data/examples/computers.rb +88 -0
- data/examples/diagnostic.rb +82 -0
- data/examples/orgs.rb +88 -0
- data/examples/parser.rb +51 -0
- data/examples/parser_test.rb +64 -0
- data/examples/users.rb +88 -0
- data/lib/code42.rb +46 -0
- data/lib/code42/attribute.rb +13 -0
- data/lib/code42/attribute_serializer.rb +129 -0
- data/lib/code42/client.rb +301 -0
- data/lib/code42/computer.rb +15 -0
- data/lib/code42/connection.rb +124 -0
- data/lib/code42/diagnostic.rb +8 -0
- data/lib/code42/error.rb +21 -0
- data/lib/code42/org.rb +38 -0
- data/lib/code42/ping.rb +14 -0
- data/lib/code42/resource.rb +58 -0
- data/lib/code42/role.rb +26 -0
- data/lib/code42/role_collection.rb +35 -0
- data/lib/code42/settings.rb +42 -0
- data/lib/code42/token.rb +31 -0
- data/lib/code42/token_validation.rb +10 -0
- data/lib/code42/user.rb +40 -0
- data/lib/code42/version.rb +3 -0
- data/spec/cassettes/Code42_Client/_create_org/returns_created_org.yml +47 -0
- data/spec/cassettes/Code42_Client/_create_user/returns_created_user.yml +44 -0
- data/spec/cassettes/Code42_Client/_create_user/when_sending_an_invalid_email/raises_an_exception.yml +37 -0
- data/spec/cassettes/Code42_Client/_find_org_by_name/returns_the_org_with_the_specified_name.yml +101 -0
- data/spec/cassettes/Code42_Client/_get_token/returns_valid_tokens.yml +38 -0
- data/spec/cassettes/Code42_Client/_get_token/when_providing_invalid_credentials/should_raise_an_exception.yml +37 -0
- data/spec/cassettes/Code42_Client/_org/when_ID_is_not_passed/returns_my_org.yml +54 -0
- data/spec/cassettes/Code42_Client/_org/when_ID_is_passed_in/returns_a_specific_org.yml +54 -0
- data/spec/cassettes/Code42_Client/_ping/returns_a_ping.yml +48 -0
- data/spec/cassettes/Code42_Client/_user/when_ID_is_not_passed/returns_my_user.yml +53 -0
- data/spec/cassettes/Code42_Client/_user/when_ID_is_passed_in/returns_a_specific_user.yml +53 -0
- data/spec/cassettes/Code42_Client/_user_roles/returns_an_enumerable.yml +50 -0
- data/spec/cassettes/Code42_Client/_validate_token/returns_a_valid_response.yml +83 -0
- data/spec/cassettes/Crashplan_Client/_create_org/returns_created_org.yml +47 -0
- data/spec/cassettes/Crashplan_Client/_create_user/returns_created_user.yml +44 -0
- data/spec/cassettes/Crashplan_Client/_create_user/when_sending_an_invalid_email/raises_an_exception.yml +37 -0
- data/spec/cassettes/Crashplan_Client/_find_org_by_name/returns_the_org_with_the_specified_name.yml +55 -0
- data/spec/cassettes/Crashplan_Client/_get_token/returns_valid_tokens.yml +38 -0
- data/spec/cassettes/Crashplan_Client/_get_token/when_providing_invalid_credentials/should_raise_an_exception.yml +37 -0
- data/spec/cassettes/Crashplan_Client/_org/when_ID_is_not_passed/returns_my_org.yml +54 -0
- data/spec/cassettes/Crashplan_Client/_org/when_ID_is_passed_in/returns_a_specific_org.yml +54 -0
- data/spec/cassettes/Crashplan_Client/_ping/returns_a_ping.yml +48 -0
- data/spec/cassettes/Crashplan_Client/_user/when_ID_is_not_passed/returns_my_user.yml +99 -0
- data/spec/cassettes/Crashplan_Client/_user/when_ID_is_passed_in/returns_a_specific_user.yml +53 -0
- data/spec/cassettes/Crashplan_Client/_user_roles/returns_an_enumerable.yml +50 -0
- data/spec/cassettes/Crashplan_Client/_validate_token/returns_a_valid_response.yml +128 -0
- data/spec/crashplan/client_spec.rb +135 -0
- data/spec/crashplan/connection_spec.rb +45 -0
- data/spec/crashplan/org_spec.rb +63 -0
- data/spec/crashplan/ping_spec.rb +14 -0
- data/spec/crashplan/resource_spec.rb +56 -0
- data/spec/crashplan/role_spec.rb +28 -0
- data/spec/crashplan/settings_spec.rb +118 -0
- data/spec/crashplan/token_spec.rb +33 -0
- data/spec/crashplan/user_spec.rb +21 -0
- data/spec/fixtures/auth/bad_password.json +1 -0
- data/spec/fixtures/authToken.json +10 -0
- data/spec/fixtures/org.1.json +36 -0
- data/spec/fixtures/org.create.json +36 -0
- data/spec/fixtures/org.my.json +36 -0
- data/spec/fixtures/ping.json +7 -0
- data/spec/fixtures/user.1.json +27 -0
- data/spec/fixtures/user.create.json +27 -0
- data/spec/fixtures/user.my.json +27 -0
- data/spec/fixtures/user_roles.json +32 -0
- data/spec/fixtures/validate_token.json +10 -0
- data/spec/spec_helper.rb +67 -0
- metadata +268 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'optparse'
|
6
|
+
require 'highline/import'
|
7
|
+
require 'code42'
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
class << self
|
11
|
+
def options
|
12
|
+
@options ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(args = [])
|
16
|
+
parse_options args
|
17
|
+
if args.count < 1 || args[0] =~ /^\d+$/
|
18
|
+
raise ArgumentError, 'Invalid arguments'
|
19
|
+
end
|
20
|
+
options.merge! parse_connection_string(args.shift)
|
21
|
+
options[:computer_id] = args.shift
|
22
|
+
options
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_options(args)
|
26
|
+
options[:ssl] = false
|
27
|
+
option_parser.parse! args
|
28
|
+
options
|
29
|
+
end
|
30
|
+
|
31
|
+
def help
|
32
|
+
option_parser.help
|
33
|
+
end
|
34
|
+
|
35
|
+
def option_parser
|
36
|
+
@option_parser ||= OptionParser.new do |opts|
|
37
|
+
opts.banner = "Usage: computers [options] [user@]address[:port] [computer_id]"
|
38
|
+
opts.on('-p', '--password [PASSWORD]', String, 'server password') do |password|
|
39
|
+
options[:password] = password
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('--[no-]ssl', 'Use SSL for network communication') do |ssl|
|
43
|
+
options[:ssl] = ssl
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_connection_string(connection_string)
|
49
|
+
m = connection_string.match /^((?<user>\w+)\@)?(?<host>[\w\.]+)(:(?<port>\d+))?$/
|
50
|
+
{
|
51
|
+
:user => m[:user],
|
52
|
+
:host => m[:host],
|
53
|
+
:port => (m[:port].to_i if m[:port])
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
begin
|
60
|
+
options = Parser.parse ARGV
|
61
|
+
rescue ArgumentError
|
62
|
+
abort Parser.help
|
63
|
+
end
|
64
|
+
|
65
|
+
if !options[:password]
|
66
|
+
options[:password] = ask('Enter password: ') { |q| q.echo = "*" }
|
67
|
+
end
|
68
|
+
|
69
|
+
if !options[:password]
|
70
|
+
options[:password] = ask('Enter password: ') { |q| q.echo = "*" }
|
71
|
+
end
|
72
|
+
|
73
|
+
client = Code42::Client.new(
|
74
|
+
:host => options[:host],
|
75
|
+
:username => options[:user],
|
76
|
+
:password => options[:password],
|
77
|
+
:https => options[:ssl],
|
78
|
+
:port => options[:port],
|
79
|
+
:api_root => '/api'
|
80
|
+
)
|
81
|
+
|
82
|
+
if options[:computer_id]
|
83
|
+
computer = client.computer(options[:computer_id].to_i, :incAll => true)
|
84
|
+
jj computer.serialize
|
85
|
+
else
|
86
|
+
computers = client.computers(:incAll => true)
|
87
|
+
jj computers.map(&:serialize)
|
88
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'optparse'
|
6
|
+
require 'highline/import'
|
7
|
+
require 'code42'
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
class << self
|
11
|
+
def options
|
12
|
+
@options ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(args = [])
|
16
|
+
parse_options args
|
17
|
+
if args.count < 1 || args[0] =~ /^\d+$/
|
18
|
+
raise ArgumentError, 'Invalid arguments'
|
19
|
+
end
|
20
|
+
options.merge! parse_connection_string(args.shift)
|
21
|
+
options
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_options(args)
|
25
|
+
options[:ssl] = false
|
26
|
+
option_parser.parse! args
|
27
|
+
options
|
28
|
+
end
|
29
|
+
|
30
|
+
def help
|
31
|
+
option_parser.help
|
32
|
+
end
|
33
|
+
|
34
|
+
def option_parser
|
35
|
+
@option_parser ||= OptionParser.new do |opts|
|
36
|
+
opts.banner = "Usage: diagnostic [options] [user@]address[:port]"
|
37
|
+
opts.on('-p', '--password [PASSWORD]', String, 'server password') do |password|
|
38
|
+
options[:password] = password
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on('--[no-]ssl', 'Use SSL for network communication') do |ssl|
|
42
|
+
options[:ssl] = ssl
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_connection_string(connection_string)
|
48
|
+
m = connection_string.match /^((?<user>\w+)\@)?(?<host>[\w\.]+)(:(?<port>\d+))?$/
|
49
|
+
{
|
50
|
+
:user => m[:user],
|
51
|
+
:host => m[:host],
|
52
|
+
:port => (m[:port].to_i if m[:port])
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
begin
|
59
|
+
options = Parser.parse ARGV
|
60
|
+
rescue ArgumentError
|
61
|
+
abort Parser.help
|
62
|
+
end
|
63
|
+
|
64
|
+
if !options[:user]
|
65
|
+
options[:user] = ask('Enter username: ')
|
66
|
+
end
|
67
|
+
|
68
|
+
if !options[:password]
|
69
|
+
options[:password] = ask('Enter password: ') { |q| q.echo = "*" }
|
70
|
+
end
|
71
|
+
|
72
|
+
client = Code42::Client.new(
|
73
|
+
:host => options[:host],
|
74
|
+
:username => options[:user],
|
75
|
+
:password => options[:password],
|
76
|
+
:https => options[:ssl],
|
77
|
+
:port => options[:port],
|
78
|
+
:api_root => '/api'
|
79
|
+
)
|
80
|
+
|
81
|
+
diagnostic = client.diagnostic
|
82
|
+
jj diagnostic.serialize
|
data/examples/orgs.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'code42'
|
6
|
+
require 'optparse'
|
7
|
+
require 'highline/import'
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
class << self
|
11
|
+
def options
|
12
|
+
@options ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(args = [])
|
16
|
+
parse_options args
|
17
|
+
if args.count < 1 || args[0] =~ /^\d+$/
|
18
|
+
raise ArgumentError, 'Invalid arguments'
|
19
|
+
end
|
20
|
+
options.merge! parse_connection_string(args.shift)
|
21
|
+
options[:org_id] = args.shift
|
22
|
+
options
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_options(args)
|
26
|
+
options[:ssl] = false
|
27
|
+
option_parser.parse! args
|
28
|
+
options
|
29
|
+
end
|
30
|
+
|
31
|
+
def help
|
32
|
+
option_parser.help
|
33
|
+
end
|
34
|
+
|
35
|
+
def option_parser
|
36
|
+
@option_parser ||= OptionParser.new do |opts|
|
37
|
+
opts.banner = "Usage: orgs [options] [user@]address[:port] [org_id]"
|
38
|
+
opts.on('-p', '--password [PASSWORD]', String, 'server password') do |password|
|
39
|
+
options[:password] = password
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('--[no-]ssl', 'Use SSL for network communication') do |ssl|
|
43
|
+
options[:ssl] = ssl
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_connection_string(connection_string)
|
49
|
+
m = connection_string.match /^((?<user>\w+)\@)?(?<host>[\w\.]+)(:(?<port>\d+))?$/
|
50
|
+
{
|
51
|
+
:user => m[:user],
|
52
|
+
:host => m[:host],
|
53
|
+
:port => (m[:port].to_i if m[:port])
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
begin
|
60
|
+
options = Parser.parse ARGV
|
61
|
+
rescue ArgumentError
|
62
|
+
abort Parser.help
|
63
|
+
end
|
64
|
+
|
65
|
+
if !options[:password]
|
66
|
+
options[:password] = ask('Enter password: ') { |q| q.echo = "*" }
|
67
|
+
end
|
68
|
+
|
69
|
+
if !options[:password]
|
70
|
+
options[:password] = ask('Enter password: ') { |q| q.echo = "*" }
|
71
|
+
end
|
72
|
+
|
73
|
+
client = Code42::Client.new(
|
74
|
+
:host => options[:host],
|
75
|
+
:username => options[:user],
|
76
|
+
:password => options[:password],
|
77
|
+
:https => options[:ssl],
|
78
|
+
:port => options[:port],
|
79
|
+
:api_root => '/api'
|
80
|
+
)
|
81
|
+
|
82
|
+
if options[:org_id]
|
83
|
+
org = client.org(options[:org_id].to_i, :incAll => true)
|
84
|
+
jj org.serialize
|
85
|
+
else
|
86
|
+
orgs = client.orgs(:incAll => true)
|
87
|
+
jj orgs.map(&:serialize)
|
88
|
+
end
|
data/examples/parser.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class Parser
|
4
|
+
class << self
|
5
|
+
def options
|
6
|
+
@options ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(args = [])
|
10
|
+
parse_options args
|
11
|
+
if args.count < 1 || args[0] =~ /^\d+$/
|
12
|
+
raise ArgumentError, 'Invalid arguments'
|
13
|
+
end
|
14
|
+
options.merge! parse_connection_string(args.shift)
|
15
|
+
options[:user_id] = args.shift
|
16
|
+
options
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_options(args)
|
20
|
+
options[:ssl] = false
|
21
|
+
option_parser.parse! args
|
22
|
+
options
|
23
|
+
end
|
24
|
+
|
25
|
+
def help
|
26
|
+
option_parser.help
|
27
|
+
end
|
28
|
+
|
29
|
+
def option_parser
|
30
|
+
@option_parser ||= OptionParser.new do |opts|
|
31
|
+
opts.banner = "Usage: users [options] [user@]address[:port] [user_id]"
|
32
|
+
opts.on('-p', '--password [PASSWORD]', String, 'server password') do |password|
|
33
|
+
options[:password] = password
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('--[no-]ssl', 'Use SSL for network communication') do |ssl|
|
37
|
+
options[:ssl] = ssl
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_connection_string(connection_string)
|
43
|
+
m = connection_string.match /^((?<user>\w+)\@)?(?<host>[\w\.]+)(:(?<port>\d+))?$/
|
44
|
+
{
|
45
|
+
:user => m[:user],
|
46
|
+
:host => m[:host],
|
47
|
+
:port => (m[:port].to_i if m[:port])
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'turn'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'parser'
|
6
|
+
|
7
|
+
describe Parser do
|
8
|
+
|
9
|
+
describe '.parse_options' do
|
10
|
+
it 'parses command line options' do
|
11
|
+
options = Parser.parse_options(["--no-ssl", "--password", "letmein"])
|
12
|
+
options[:password].must_equal 'letmein'
|
13
|
+
options[:ssl].must_equal false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ".parse_connection_string" do
|
18
|
+
it 'parses string in form user@host:port' do
|
19
|
+
options = Parser.parse_connection_string('user@host:1234')
|
20
|
+
options[:user].must_equal 'user'
|
21
|
+
options[:host].must_equal 'host'
|
22
|
+
options[:port].must_equal 1234
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'parses strings in form host:port' do
|
26
|
+
options = Parser.parse_connection_string('host:1234')
|
27
|
+
options[:user].must_equal nil
|
28
|
+
options[:host].must_equal 'host'
|
29
|
+
options[:port].must_equal 1234
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'parses strings in form user@host' do
|
33
|
+
options = Parser.parse_connection_string('user@host')
|
34
|
+
options[:user].must_equal 'user'
|
35
|
+
options[:host].must_equal 'host'
|
36
|
+
options[:port].must_equal nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'parses strings in form host' do
|
40
|
+
options = Parser.parse_connection_string('host')
|
41
|
+
options[:user].must_equal nil
|
42
|
+
options[:host].must_equal 'host'
|
43
|
+
options[:port].must_equal nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.parse' do
|
48
|
+
it 'parses arguments' do
|
49
|
+
options = Parser.parse ['-p', 'letmein', '--no-ssl', 'fred@localhost']
|
50
|
+
options[:password].must_equal 'letmein'
|
51
|
+
options[:ssl].must_equal false
|
52
|
+
options[:user].must_equal 'fred'
|
53
|
+
options[:host].must_equal 'localhost'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'raises an error when args is empty' do
|
57
|
+
lambda { Parser.parse([]) }.must_raise ArgumentError
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'raises an error when first arg is a number' do
|
61
|
+
lambda { Parser.parse(['1']) }.must_raise ArgumentError
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/examples/users.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'code42'
|
6
|
+
require 'optparse'
|
7
|
+
require 'highline/import'
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
class << self
|
11
|
+
def options
|
12
|
+
@options ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(args = [])
|
16
|
+
parse_options args
|
17
|
+
if args.count < 1 || args[0] =~ /^\d+$/
|
18
|
+
raise ArgumentError, 'Invalid arguments'
|
19
|
+
end
|
20
|
+
options.merge! parse_connection_string(args.shift)
|
21
|
+
options[:user_id] = args.shift
|
22
|
+
options
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_options(args)
|
26
|
+
options[:ssl] = false
|
27
|
+
option_parser.parse! args
|
28
|
+
options
|
29
|
+
end
|
30
|
+
|
31
|
+
def help
|
32
|
+
option_parser.help
|
33
|
+
end
|
34
|
+
|
35
|
+
def option_parser
|
36
|
+
@option_parser ||= OptionParser.new do |opts|
|
37
|
+
opts.banner = "Usage: users [options] [user@]address[:port] [user_id]"
|
38
|
+
opts.on('-p', '--password [PASSWORD]', String, 'server password') do |password|
|
39
|
+
options[:password] = password
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('--[no-]ssl', 'Use SSL for network communication') do |ssl|
|
43
|
+
options[:ssl] = ssl
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_connection_string(connection_string)
|
49
|
+
m = connection_string.match /^((?<user>\w+)\@)?(?<host>[\w\.]+)(:(?<port>\d+))?$/
|
50
|
+
{
|
51
|
+
:user => m[:user],
|
52
|
+
:host => m[:host],
|
53
|
+
:port => (m[:port].to_i if m[:port])
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
begin
|
60
|
+
options = Parser.parse ARGV
|
61
|
+
rescue ArgumentError
|
62
|
+
abort Parser.help
|
63
|
+
end
|
64
|
+
|
65
|
+
if !options[:user]
|
66
|
+
options[:user] = ask('Enter username: ')
|
67
|
+
end
|
68
|
+
|
69
|
+
if !options[:password]
|
70
|
+
options[:password] = ask('Enter password: ') { |q| q.echo = "*" }
|
71
|
+
end
|
72
|
+
|
73
|
+
client = Code42::Client.new(
|
74
|
+
:host => options[:host],
|
75
|
+
:username => options[:user],
|
76
|
+
:password => options[:password],
|
77
|
+
:https => options[:ssl],
|
78
|
+
:port => options[:port],
|
79
|
+
:api_root => '/api'
|
80
|
+
)
|
81
|
+
|
82
|
+
if options[:user_id]
|
83
|
+
user = client.user(options[:user_id].to_i, :incAll => true)
|
84
|
+
jj user.serialize
|
85
|
+
else
|
86
|
+
users = client.users(:incAll => true)
|
87
|
+
jj users.map(&:serialize)
|
88
|
+
end
|