statscloud 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cfc4b9f7ca3ff6dfc74d26c74805c53562ef11ae1842c043ff76fa802025867e
4
+ data.tar.gz: 8bd02234c81c1537ed5b03b5eb507d31e23cf0140ebf03343445af1d143904c5
5
+ SHA512:
6
+ metadata.gz: 1036da28708e37ced51549361dc0f200ba3ab9a4386ad9b5ce6b0228d77ed56e25b3ff491ff9d80f449ce9806c42be70da2655ababfd629b93066fd06a13889d
7
+ data.tar.gz: 09800276336b3e3087c332d27facf0e57118ba3175c141af87b5467a21a7c434ae4d3d9a9b2fa38acb9186bf0f0d957109bd1439556eb6a0a2f819e580226e98
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.idea
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /log/
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,72 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+
4
+ Layout/ExtraSpacing:
5
+ Exclude:
6
+ - 'statscloud.gemspec'
7
+
8
+ Layout/LeadingBlankLines:
9
+ Exclude:
10
+ - 'statscloud.gemspec'
11
+
12
+ Layout/SpaceAroundOperators:
13
+ Exclude:
14
+ - 'statscloud.gemspec'
15
+
16
+ Layout/SpaceInsideBlockBraces:
17
+ Exclude:
18
+ - 'Gemfile'
19
+
20
+ Metrics/BlockLength:
21
+ Exclude:
22
+ - 'spec/**/*'
23
+ - 'test/**/*'
24
+ - 'statscloud.gemspec'
25
+
26
+ Metrics/AbcSize:
27
+ Exclude:
28
+ - 'lib/statscloud/helpers/socketio_helper.rb'
29
+
30
+ Metrics/MethodLength:
31
+ Exclude:
32
+ - 'lib/statscloud/helpers/socketio_helper.rb'
33
+
34
+ Style/Documentation:
35
+ Exclude:
36
+ - 'spec/**/*'
37
+ - 'test/**/*'
38
+
39
+ Style/ExpandPathArguments:
40
+ Exclude:
41
+ - 'statscloud.gemspec'
42
+
43
+ Style/HashSyntax:
44
+ EnforcedStyle: ruby19
45
+
46
+ Style/MutableConstant:
47
+ Exclude:
48
+ - 'lib/statscloud/version.rb'
49
+
50
+ Style/PercentLiteralDelimiters:
51
+ Exclude:
52
+ - 'statscloud.gemspec'
53
+
54
+ Style/StringLiterals:
55
+ Exclude:
56
+ - 'Gemfile'
57
+ - 'Rakefile'
58
+ - 'bin/console'
59
+ - 'lib/statscloud/version.rb'
60
+ - 'spec/spec_helper.rb'
61
+ - 'spec/statscloud_client_spec.rb'
62
+ - 'statscloud.gemspec'
63
+
64
+ Style/FrozenStringLiteralComment:
65
+ Exclude:
66
+ - 'lib/templates/statscloud/statscloud.rb'
67
+ Style/UnneededPercentQ:
68
+ Exclude:
69
+ - 'statscloud.gemspec'
70
+
71
+ Metrics/LineLength:
72
+ Max: 120
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in statscloud.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,89 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ statscloud (1.0.0)
5
+ activesupport (>= 5.0.0.1)
6
+ crc32 (~> 1.0.1)
7
+ eventmachine (~> 1.2)
8
+ fileutils
9
+ leon (~> 1.1)
10
+ logger (~> 1.2)
11
+ rest-client (~> 2.0.2)
12
+ statscloud.io-ruby-socket.io-client-simple (~> 1.2.1.pre.2)
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ activesupport (5.2.1)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 0.7, < 2)
20
+ minitest (~> 5.1)
21
+ tzinfo (~> 1.1)
22
+ concurrent-ruby (1.0.5)
23
+ crc32 (1.0.1)
24
+ diff-lcs (1.3)
25
+ domain_name (0.5.20180417)
26
+ unf (>= 0.0.5, < 1.0.0)
27
+ event_emitter (0.2.6)
28
+ eventmachine (1.2.7)
29
+ fileutils (1.1.0)
30
+ http-cookie (1.0.3)
31
+ domain_name (~> 0.5)
32
+ httparty (0.16.2)
33
+ multi_xml (>= 0.5.2)
34
+ i18n (1.1.1)
35
+ concurrent-ruby (~> 1.0)
36
+ json (2.1.0)
37
+ leon (1.1.2)
38
+ logger (1.2.8)
39
+ mime-types (3.2.2)
40
+ mime-types-data (~> 3.2015)
41
+ mime-types-data (3.2018.0812)
42
+ minitest (5.11.3)
43
+ multi_xml (0.6.0)
44
+ netrc (0.11.0)
45
+ rake (10.5.0)
46
+ rest-client (2.0.2)
47
+ http-cookie (>= 1.0.2, < 2.0)
48
+ mime-types (>= 1.16, < 4.0)
49
+ netrc (~> 0.8)
50
+ rspec (3.8.0)
51
+ rspec-core (~> 3.8.0)
52
+ rspec-expectations (~> 3.8.0)
53
+ rspec-mocks (~> 3.8.0)
54
+ rspec-core (3.8.0)
55
+ rspec-support (~> 3.8.0)
56
+ rspec-expectations (3.8.2)
57
+ diff-lcs (>= 1.2.0, < 2.0)
58
+ rspec-support (~> 3.8.0)
59
+ rspec-mocks (3.8.0)
60
+ diff-lcs (>= 1.2.0, < 2.0)
61
+ rspec-support (~> 3.8.0)
62
+ rspec-support (3.8.0)
63
+ statscloud.io-ruby-socket.io-client-simple (1.2.1.pre.2)
64
+ event_emitter
65
+ httparty
66
+ json
67
+ websocket-client-simple (~> 0.3.0)
68
+ thread_safe (0.3.6)
69
+ tzinfo (1.2.5)
70
+ thread_safe (~> 0.1)
71
+ unf (0.1.4)
72
+ unf_ext
73
+ unf_ext (0.0.7.5)
74
+ websocket (1.2.8)
75
+ websocket-client-simple (0.3.0)
76
+ event_emitter
77
+ websocket
78
+
79
+ PLATFORMS
80
+ ruby
81
+
82
+ DEPENDENCIES
83
+ bundler (~> 1.16)
84
+ rake (~> 10.0)
85
+ rspec (~> 3.0)
86
+ statscloud!
87
+
88
+ BUNDLED WITH
89
+ 1.16.3
data/LICENSE.txt ADDED
@@ -0,0 +1 @@
1
+ (c) Copyright 2018 Agilium Labs LLC, all rights reserved
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # StatsCloud
2
+ StatsCloud module
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'statscloud'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install statscloud
18
+
19
+ ## Usage
20
+
21
+ ### Rails
22
+
23
+ 1. Run generator for creatinig initializer file, statscloud.io configuration file `.statscloud.yml` and directory for saving your metrics configurations.
24
+ ```ruby
25
+ rails g stats_cloud:install
26
+ ```
27
+ 2. Set up your configuration files (you can read more about StatsCloud configuration [here](https://medium.com/@roman.kisilenko/software-application-monitoring-how-to-or-how-not-to-let-your-production-fail-9481dd0ef6de)).
28
+
29
+ 3. You can change `statscloud.rb` intializer file if you want to start StatsCloud service with a different environment or add some tags to your StatsmeterClient.
30
+
31
+ #### `with_environment('env')`
32
+ This method allows you to record metrics from different environments (like `test`, `development`, `production`) separately, so
33
+ ```ruby
34
+ StatsCloud.with_environment('production').start
35
+ ```
36
+ would start StatsCloud service at `production` environment. Without this method, the environment would be chosen from the configuration file. If you don't configure it service would try to find env in the RAILS_ENV and if it is also missing it would set the value as the `default`.
37
+
38
+ #### `with_tags(array[string])`
39
+ Also, you can start service with sending some tags to the cluster. Tags are used to group metrics. By default, the host name is used as a tag, which allows you to track metrics for both the application as a whole and for each host individually. In the cloud, an array of tags `['region', 'server_name']` can be used, which allows you to track metrics for the application as a whole, separate regions or separate servers.
40
+ ```ruby
41
+ StatsCloud.with_tags(['region', 'server_name']).start
42
+ ```
43
+ You can combine these methods calling them as a chain.
44
+
45
+ 4. Use StatsCLoud.meter with `record_event` method for recording one event or `record_events` to send multiple events to cluster from any place of you application.
46
+
47
+ ```ruby
48
+ StatsCloud.meter.record_event('event', 1)
49
+ StatsCloud.meter.record_events({name: 'gauge', measurement: 123}, {name: 'counter'})
50
+ ```
51
+ ### Ruby
52
+ 1. Install `statscloud` gem:
53
+
54
+ ```ruby
55
+ gem install statscloud
56
+ ```
57
+
58
+ 2. Require `statscloud` in the project and set up your application structure, with `.statscloud.yml` configuration file (at the root of the project) and metrics configs (read more about StatsCloud configuration [here](https://medium.com/@roman.kisilenko/software-application-monitoring-how-to-or-how-not-to-let-your-production-fail-9481dd0ef6de)).
59
+
60
+ 3. Customize statscloud run with `with_environment` and `with_tags` methods and start it where you need it.
61
+
62
+ ```ruby
63
+ StatsCloud.with_environment('test').with_tags(['usa', 'server_1']).start
64
+ ```
65
+ 4. Send metrics via StatsCloud.meter
66
+
67
+ ```ruby
68
+ meter = StatsCloud.meter
69
+ meter.record_event('event', 1)
70
+ meter.record_events({name: 'gauge', measurement: 123}, {name: 'counter'});
71
+ ```
72
+
73
+ 5. Stop work of StatsCloud service when 'time is over'.
74
+ ```ruby
75
+ StatsCloud.stop
76
+ ```
77
+
78
+ The full version of pure ruby example takes the form:
79
+ ```ruby
80
+ require 'statscloud' # import gem
81
+
82
+ # start the module, returns a thread
83
+ StatsCloud.start
84
+
85
+ # track events (remember that you have to wait for the socket connection to the cluster before record any metrics)
86
+ StatsCloud.meter.record_event('numeric', 123)
87
+ StatsCloud.meter.record_events({name: 'gauge', measurement: 123}, {name: 'counter'})
88
+
89
+ # stop the module
90
+ StatsCloud.stop
91
+
92
+ ```
93
+
94
+ ## License
95
+
96
+ (c) Copyright 2018 Agilium Labs LLC, all rights reserved.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "statscloud"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+
5
+ module StatsCloud
6
+ # Rails generator for statscloud configuration.
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path('../../templates/statscloud', __dir__)
9
+
10
+ # Creates rails initializer for simultaneous start with rails server.
11
+ def create_initializer_file
12
+ copy_file 'statscloud_template.rb', File.join('config', 'initializers', 'statscloud.rb')
13
+ end
14
+
15
+ # Creates directory for saving metrics config files.
16
+ def create_config_statscloud_directory
17
+ empty_directory File.join('config', 'statscloud')
18
+ end
19
+
20
+ # Creates statscloud configuration file.
21
+ def create_config_file
22
+ copy_file '.statscloud_template.yml', '.statscloud.yml'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './helpers/parsed_response_helper'
4
+ require_relative './helpers/logger_helper'
5
+ require 'rest-client'
6
+
7
+ module StatsCloud
8
+ # statscloud.io cluster-api service client.
9
+ class ClusterClient
10
+ include StatsCloud::LoggerHelper
11
+
12
+ # Constructs cluster-api service client instance.
13
+ #
14
+ # @param [+String+] env
15
+ # cluster api environment
16
+ #
17
+ # @param [+String+] host
18
+ # cluster api host.
19
+ #
20
+ # @api public
21
+ def initialize(env, host = 'https://cluster-api-v1.statscloud.statscloud.io')
22
+ @environment = env
23
+ @host = host
24
+ end
25
+
26
+ # Schedules cluster instance to be retrieved.
27
+ #
28
+ # @param [+String+] token
29
+ # authorization token.
30
+ # @param [+String+] app
31
+ # cluster name.
32
+ #
33
+ # @api public
34
+ def get_cluster(token, app)
35
+ url = "#{host}/users/current/clusters/#{app}/#{environment}"
36
+ response = http_client.get(url, headers(token))
37
+ get_parsed_response(response)
38
+ rescue StandardError => error
39
+ logger.error error
40
+ end
41
+
42
+ # Schedules cluster to be (re)deployed.
43
+ #
44
+ # @param [+String+] token
45
+ # authorization token.
46
+ # @param [+String+] app
47
+ # cluster name.
48
+ # @param [+String+] configuration
49
+ # cluster configuration
50
+ #
51
+ # @api public
52
+ def deploy_cluster(token, app, configuration)
53
+ url = "#{host}/users/current/clusters/#{app}/#{environment}"
54
+ body = body_hash_parameters(configuration)
55
+ response = http_client.put(url, body.to_json, headers(token))
56
+ get_parsed_response(response)
57
+ rescue StandardError => error
58
+ logger.error error
59
+ end
60
+
61
+ # Schedules cluster to be undeployed.
62
+ # @param [+String+] token
63
+ # authorization token.
64
+ # @param [+String+] app
65
+ # cluster name.
66
+ #
67
+ # @api public
68
+ def undeploy_cluster(token, app)
69
+ url = "#{host}/users/current/clusters/#{app}/#{environment}/undeploy"
70
+ response = http_client.post(url, nil, headers(token))
71
+ get_parsed_response(response)
72
+ rescue StandardError => error
73
+ logger.error error
74
+ end
75
+
76
+ private
77
+
78
+ attr_reader :host, :environment
79
+
80
+ def headers(token)
81
+ default_headers.merge(auth_headers(token))
82
+ end
83
+
84
+ def default_headers
85
+ {
86
+ content_type: :json,
87
+ accept: :json
88
+ }
89
+ end
90
+
91
+ def auth_headers(token)
92
+ {
93
+ 'auth-token' => token
94
+ }
95
+ end
96
+
97
+ def body_hash_parameters(configuration)
98
+ {
99
+ configuration: configuration.to_json
100
+ }
101
+ end
102
+
103
+ def get_parsed_response(response)
104
+ StatsCloud::ParsedResponseHelper.new(response)
105
+ end
106
+
107
+ def http_client
108
+ RestClient
109
+ end
110
+
111
+ def logger
112
+ @logger ||= ::StatsCloud.logger
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StatsCloud
4
+ # This helper works to gather and merge the configuration from files.
5
+ module AssetsHelper
6
+ private
7
+
8
+ def asset_selector_matches(filename, asset_selector)
9
+ includes = asset_selector[:include]
10
+ excludes = asset_selector[:exclude]
11
+ process_match(includes, filename) && !process_match(excludes, filename)
12
+ end
13
+
14
+ def collect_statscloud_assets(config, source_mappings, dir = '.')
15
+ asset_selector = get_checked_asset_selector(config)
16
+ process_files(dir, config, source_mappings, asset_selector)
17
+ end
18
+
19
+ def join_configs(config, extra_config, source_mappings, file_name)
20
+ process_array_fields(config, extra_config, source_mappings, file_name)
21
+ process_object_fields(config, extra_config, source_mappings, file_name)
22
+ process_value_fields(config, extra_config)
23
+ end
24
+
25
+ def get_checked_asset_selector(config)
26
+ asset_selector = config[:assetSelector] || {
27
+ include: ['.*\.statscloud.yml'], exclude: []
28
+ }
29
+ asset_selector[:include] = put_to_array_if_needed(asset_selector[:include])
30
+ asset_selector[:exclude] = put_to_array_if_needed(asset_selector[:exclude])
31
+ asset_selector
32
+ end
33
+
34
+ def get_config_from_file(file)
35
+ return ::YAML.load_file(file) if File.exist?(file)
36
+ {}
37
+ end
38
+
39
+ def process_files(dir, config, source_mappings, asset_selector)
40
+ entries = Dir.entries(dir)
41
+ entries.reduce({}) do |_config, entry|
42
+ file_name = File.join(dir, entry)
43
+ if it_directory?(file_name, entry)
44
+ collect_statscloud_assets(config, source_mappings, file_name)
45
+ elsif it_config_file?(file_name, asset_selector)
46
+ extra_config = get_config_from_file(file_name)
47
+ join_configs(config, extra_config, source_mappings, file_name)
48
+ end
49
+ end
50
+ end
51
+
52
+ def process_match(list, filename)
53
+ list.reduce(false) do |match, pattern|
54
+ match || Regexp.new(pattern).match(filename)
55
+ end
56
+ end
57
+
58
+ def process_array_fields(config, extra_config, source_mappings, file_name)
59
+ array_fields.each do |array_field|
60
+ next unless it_not_empty_array?(extra_config, array_field)
61
+ config[array_field] ||= []
62
+ source_mappings[array_field.to_sym].push(source_map_index(config, extra_config, array_field, file_name))
63
+ config[array_field] = config[array_field].concat(extra_config[array_field])
64
+ end
65
+ end
66
+
67
+ def process_object_fields(config, extra_config, source_mappings, file_name)
68
+ object_fields.each do |object_field|
69
+ next unless extra_config[object_field]&.kind_of?(Hash)
70
+ source_mappings[object_field.to_sym].push(
71
+ fields: extra_config[object_field].keys,
72
+ sourceFile: file_name
73
+ )
74
+ config[object_field] = (config[object_field] || {}).merge(extra_config[object_field])
75
+ end
76
+ end
77
+
78
+ def process_value_fields(config, extra_config)
79
+ value_fields.each do |field|
80
+ config[field] = extra_config[field] if config[field].nil? && !extra_config[field].nil?
81
+ end
82
+ end
83
+
84
+ def put_to_array_if_needed(object)
85
+ object.is_a?(Array) ? object : [object]
86
+ end
87
+
88
+ def it_directory?(file_name, entry)
89
+ File.directory?(file_name) && not_required_directory.exclude?(entry)
90
+ end
91
+
92
+ def it_config_file?(file_name, asset_selector)
93
+ File.stat(file_name).file? && asset_selector_matches(file_name, asset_selector)
94
+ end
95
+
96
+ def it_not_empty_array?(extra_config, field)
97
+ extra_config[field]&.kind_of?(Array) && extra_config[field].length.positive?
98
+ end
99
+
100
+ def source_map_index(config, extra_config, array_field, file_name)
101
+ start_index = config[array_field].length
102
+ {
103
+ startIndex: start_index,
104
+ endIndex: start_index + extra_config[array_field].length - 1,
105
+ sourceFile: file_name
106
+ }
107
+ end
108
+
109
+ def not_required_directory
110
+ %w[. ..]
111
+ end
112
+
113
+ def array_fields
114
+ %w[alerts dashboards]
115
+ end
116
+
117
+ def object_fields
118
+ %w[metrics admins]
119
+ end
120
+
121
+ def value_fields
122
+ %w[token application environment endpoint assetSelector propagateErrors
123
+ passwordProtect retention flushIntervalInSeconds diskSize]
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StatsCloud
4
+ # This helper works with events.
5
+ module EventHelper
6
+ private
7
+
8
+ # Byte size for measurement.
9
+ MEASUREMENT_SIZE = 4
10
+
11
+ def get_binary_length(size)
12
+ size + MEASUREMENT_SIZE
13
+ end
14
+
15
+ def get_plain_length(name, measurement)
16
+ name.length + (measurement ? MEASUREMENT_SIZE + 1 : 0) + 1
17
+ end
18
+
19
+ def get_event_name(event)
20
+ event[:name].to_s
21
+ end
22
+
23
+ def get_event_measurement(event)
24
+ event[:measurement]&.to_s
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module StatsCloud
6
+ # This helper works to help log errors and info about StatsCloud service work.
7
+ module LoggerHelper
8
+ # Log information about work to standart ouput
9
+ def logger
10
+ file_utils.mkdir_p 'log'
11
+ @logger ||= logger_servise.new(File.join('log', 'statscloud.log')).tap do |log|
12
+ log.progname = 'StatsCloud'
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def logger_servise
19
+ Logger
20
+ end
21
+
22
+ def file_utils
23
+ FileUtils
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'active_support/all'
5
+
6
+ module StatsCloud
7
+ # This helper works to parse response from the server to convenient object.
8
+ class ParsedResponseHelper
9
+ # RestClient json-parsed response body.
10
+ #
11
+ # Type: *Hash*
12
+ attr_reader :body
13
+
14
+ # RestClient response code.
15
+ #
16
+ # Type: *Integer*
17
+ attr_reader :code
18
+
19
+ # Creates new helper object with parsed response body as json.
20
+ #
21
+ # @param [+RestClient::Response+] response
22
+ # response from the cluster.
23
+ def initialize(response)
24
+ @code = response&.code
25
+ @body = parse_json(response.body)
26
+ end
27
+
28
+ private
29
+
30
+ def parse_json(json, default = nil)
31
+ JSON.parse(json).with_indifferent_access
32
+ rescue JSON::ParserError
33
+ default
34
+ end
35
+ end
36
+ end