rosetta-stone 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +9 -4
- data/lib/rosetta/deserializers.rb +11 -42
- data/lib/rosetta/deserializers/base.rb +43 -0
- data/lib/rosetta/deserializers/json.rb +40 -0
- data/lib/rosetta/serializers.rb +11 -43
- data/lib/rosetta/serializers/base.rb +44 -0
- data/lib/rosetta/serializers/csv.rb +43 -0
- data/lib/rosetta/translation.rb +13 -10
- data/lib/rosetta/version.rb +1 -1
- metadata +5 -3
- data/lib/rosetta/csv_serializer.rb +0 -41
- data/lib/rosetta/json_deserializer.rb +0 -38
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f49ad30a0b19d92be9bca2d6b43eca56f1843fe9b594d9c46c1401113304c15
|
|
4
|
+
data.tar.gz: 5989f92c630e155ea5da9a5191c6a06e5e41ac2f8854d347372f611b814a0b68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef07a8fbf976977caff7b8de46ff30df4ff500df9784bda69fb81e146d03123f7208434f5eddf8cacb57663d0113f5a352cd3ded94664ac09cd5f36eee4a8914
|
|
7
|
+
data.tar.gz: 12c85f29aebe211372dc362c221bc6f82e0ff8f64524060c2fb679e9ac7185893b0b2e9421e4f4a9dae7331b1095d71c2b5b8809d574d1ffd8531aa059475d74
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A lightweight format-to-format translator.
|
|
|
7
7
|
Add this line to your application's Gemfile:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem 'rosetta'
|
|
10
|
+
gem 'rosetta-stone'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
And then execute:
|
|
@@ -16,7 +16,7 @@ And then execute:
|
|
|
16
16
|
|
|
17
17
|
Or install it yourself as:
|
|
18
18
|
|
|
19
|
-
$ gem install rosetta
|
|
19
|
+
$ gem install rosetta-stone
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
@@ -64,7 +64,7 @@ Rosetta::Translation.new(deserializer, serializer).call("olleh") # => HELLO
|
|
|
64
64
|
When given `Symbol`s instead, they will try to lookup the registered serializers
|
|
65
65
|
and deserializers to find a match to use with the input.
|
|
66
66
|
By default, the only ones supplied with the gem are a JSON deserializer and a
|
|
67
|
-
CSV serializer.
|
|
67
|
+
CSV serializer. (`Rosetta::Deserializers::JSON` and `Rosetta::Serializers::CSV`).
|
|
68
68
|
See *Registering a Serializer / Deserializer* for more info on how to add your own.
|
|
69
69
|
|
|
70
70
|
You can also register `Translator`s, which are pipes connecting to specific
|
|
@@ -76,7 +76,7 @@ See *Registering a Translator* for more info on how to use them.
|
|
|
76
76
|
You can register a new serializer or deserializer by calling the `register`
|
|
77
77
|
methods of respectively `Rosetta::Serializers` or `Rosetta::Deserializers`.
|
|
78
78
|
The method takes the Symbol that's going to be used as shorthand for it as first
|
|
79
|
-
parameter and
|
|
79
|
+
parameter and either a callable object or a block for the serializer/deserializer.
|
|
80
80
|
|
|
81
81
|
Example:
|
|
82
82
|
```ruby
|
|
@@ -87,6 +87,11 @@ Rosetta::Deserializers.register(:mirror, deserializer)
|
|
|
87
87
|
Rosetta::Serializers.register(:BIG, serializer)
|
|
88
88
|
|
|
89
89
|
Rosetta.translate(from: :mirror, to: :big, "em ti si") # => "IS IT ME"
|
|
90
|
+
|
|
91
|
+
# Alternative
|
|
92
|
+
|
|
93
|
+
Rosetta::Deserializers.register(:BIG) { |input| input.upcase }
|
|
94
|
+
Rosetta::Deserializers.register(:mirror) { |input| input.reverse }
|
|
90
95
|
```
|
|
91
96
|
|
|
92
97
|
Whatever the Deserializer returns will be fed to the Serializer when
|
|
@@ -1,54 +1,23 @@
|
|
|
1
1
|
require 'rosetta/exceptions'
|
|
2
2
|
|
|
3
3
|
module Rosetta
|
|
4
|
-
|
|
4
|
+
module Deserializers
|
|
5
5
|
@registered = {}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def self.register(name, deserializer)
|
|
12
|
-
raise ExistingDeserializerError, <<-ERROR.strip if @registered.key? name
|
|
13
|
-
Deserializer #{name} is already registered.
|
|
14
|
-
ERROR
|
|
15
|
-
@registered[name] = deserializer
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
class Base
|
|
19
|
-
attr_reader :input
|
|
20
|
-
|
|
21
|
-
class << self
|
|
22
|
-
def inherited(new_serializer)
|
|
23
|
-
key = new_serializer.name.match(/^(.*?)(Deserializer)?$/)[1]
|
|
24
|
-
key = key.split("::").last
|
|
25
|
-
#NOTE: Similar to Rails's #underscore
|
|
26
|
-
#TODO: Extract in refinement?
|
|
27
|
-
key = key.scan(/[A-Z]+[a-z]*/).join('_').downcase.to_sym
|
|
28
|
-
Deserializers.register(key, new_serializer)
|
|
29
|
-
end
|
|
7
|
+
class << self
|
|
8
|
+
attr_reader :registered
|
|
30
9
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
34
|
-
alias_method :deserialize, :call
|
|
35
|
-
|
|
36
|
-
def to_proc
|
|
37
|
-
proc { |*args, &block| self.call(*args, &block) }
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def initialize(input)
|
|
42
|
-
@input = input.dup.freeze
|
|
10
|
+
def [](key)
|
|
11
|
+
registered[key]
|
|
43
12
|
end
|
|
44
13
|
|
|
45
|
-
def
|
|
46
|
-
raise
|
|
47
|
-
|
|
48
|
-
|
|
14
|
+
def register(name, deserializer, &block)
|
|
15
|
+
raise ExistingDeserializerError, <<-ERROR.strip if @registered.key? name
|
|
16
|
+
Deserializer #{name} is already registered.
|
|
17
|
+
ERROR
|
|
49
18
|
|
|
50
|
-
|
|
51
|
-
|
|
19
|
+
raise ArgumentError, "Can't take both deserializer object and block." if deserializer && block
|
|
20
|
+
@registered[name] = deserializer
|
|
52
21
|
end
|
|
53
22
|
end
|
|
54
23
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'rosetta/deserializers'
|
|
2
|
+
require 'rosetta/exceptions'
|
|
3
|
+
|
|
4
|
+
module Rosetta
|
|
5
|
+
module Deserializers
|
|
6
|
+
class Base
|
|
7
|
+
attr_reader :input
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def inherited(new_serializer)
|
|
11
|
+
key = new_serializer.name.match(/^(.*?)(Deserializer)?$/)[1]
|
|
12
|
+
key = key.split("::").last
|
|
13
|
+
#NOTE: Similar to Rails's #underscore
|
|
14
|
+
#TODO: Extract in refinement?
|
|
15
|
+
key = key.scan(/[A-Z]+[a-z]*/).join('_').downcase.to_sym
|
|
16
|
+
Deserializers.register(key, new_serializer)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call(input)
|
|
20
|
+
new(input).call
|
|
21
|
+
end
|
|
22
|
+
alias_method :deserialize, :call
|
|
23
|
+
|
|
24
|
+
def to_proc
|
|
25
|
+
proc { |*args, &block| self.call(*args, &block) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(input)
|
|
30
|
+
@input = input.dup.freeze
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
end
|
|
36
|
+
alias_method :deserialize, :call
|
|
37
|
+
|
|
38
|
+
def to_proc
|
|
39
|
+
proc { |*args, &block| self.call(*args, &block) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
require 'rosetta/element'
|
|
4
|
+
require 'rosetta/exceptions'
|
|
5
|
+
require 'rosetta/deserializers/base'
|
|
6
|
+
|
|
7
|
+
module Rosetta
|
|
8
|
+
module Deserializers
|
|
9
|
+
class JSON < Base
|
|
10
|
+
def call
|
|
11
|
+
validate_input!
|
|
12
|
+
input.map { |obj| Element.new(obj) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def validate_input!
|
|
18
|
+
raise DeserializationError, <<-ERROR.strip unless parsed_input = valid_json(@input)
|
|
19
|
+
JSON input is invalid
|
|
20
|
+
ERROR
|
|
21
|
+
raise DeserializationError, <<-ERROR.strip unless parsed_input.is_a? Array
|
|
22
|
+
JSON input must be an array
|
|
23
|
+
ERROR
|
|
24
|
+
raise DeserializationError, <<-ERROR.strip unless parsed_input.all? { |o| o.is_a? Hash }
|
|
25
|
+
JSON input must contain objects
|
|
26
|
+
ERROR
|
|
27
|
+
|
|
28
|
+
@input = parsed_input.freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
#HACK: Feels dirty but there's no JSON soft-parsing in ruby's json lib
|
|
32
|
+
def valid_json(json)
|
|
33
|
+
JSON(json)
|
|
34
|
+
#NOTE: Rescuing TypeError too in case json is not a String
|
|
35
|
+
rescue ::JSON::ParserError, TypeError
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/rosetta/serializers.rb
CHANGED
|
@@ -1,55 +1,23 @@
|
|
|
1
1
|
require 'rosetta/exceptions'
|
|
2
2
|
|
|
3
3
|
module Rosetta
|
|
4
|
-
|
|
4
|
+
module Serializers
|
|
5
5
|
@registered = {}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def self.register(name, serializer)
|
|
12
|
-
raise ExistingSerializerError, <<-ERROR.strip if @registered.key? name
|
|
13
|
-
Serializer #{name} is already registered.
|
|
14
|
-
ERROR
|
|
15
|
-
@registered[name] = serializer
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
class Base
|
|
19
|
-
attr_reader :elements
|
|
20
|
-
|
|
21
|
-
class << self
|
|
22
|
-
def inherited(new_serializer)
|
|
23
|
-
key = new_serializer.name.match(/^(.*?)(Serializer)?$/)[1]
|
|
24
|
-
key = key.split("::").last
|
|
25
|
-
#NOTE: Similar to Rails's #underscore
|
|
26
|
-
#TODO: Extract in refinement?
|
|
27
|
-
key = key.scan(/[A-Z]+[a-z]*/).join('_').downcase.to_sym
|
|
28
|
-
Serializers.register(key, new_serializer)
|
|
29
|
-
end
|
|
7
|
+
class << self
|
|
8
|
+
attr_reader :registered
|
|
30
9
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
end
|
|
34
|
-
alias_method :serialize, :call
|
|
35
|
-
|
|
36
|
-
def to_proc
|
|
37
|
-
proc { |*args, &block| self.call(*args, &block) }
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def initialize(elements)
|
|
42
|
-
@elements = elements.dup.freeze
|
|
43
|
-
validate_input!
|
|
10
|
+
def [](key)
|
|
11
|
+
@registered[key]
|
|
44
12
|
end
|
|
45
13
|
|
|
46
|
-
def
|
|
47
|
-
raise
|
|
48
|
-
|
|
49
|
-
|
|
14
|
+
def register(name, serializer, &block)
|
|
15
|
+
raise ExistingSerializerError, <<-ERROR.strip if @registered.key? name
|
|
16
|
+
Serializer #{name} is already registered.
|
|
17
|
+
ERROR
|
|
50
18
|
|
|
51
|
-
|
|
52
|
-
|
|
19
|
+
raise ArgumentError, "Can't take both serializer object and block." if serializer && block
|
|
20
|
+
@registered[name] = serializer
|
|
53
21
|
end
|
|
54
22
|
end
|
|
55
23
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'rosetta/serializers'
|
|
2
|
+
require 'rosetta/exceptions'
|
|
3
|
+
|
|
4
|
+
module Rosetta
|
|
5
|
+
module Serializers
|
|
6
|
+
class Base
|
|
7
|
+
attr_reader :elements
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def inherited(new_serializer)
|
|
11
|
+
key = new_serializer.name.match(/^(.*?)(Serializer)?$/)[1]
|
|
12
|
+
key = key.split("::").last
|
|
13
|
+
#NOTE: Similar to Rails's #underscore
|
|
14
|
+
#TODO: Extract in refinement?
|
|
15
|
+
key = key.scan(/[A-Z]+[a-z]*/).join('_').downcase.to_sym
|
|
16
|
+
Serializers.register(key, new_serializer)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call(elements)
|
|
20
|
+
new(elements).call
|
|
21
|
+
end
|
|
22
|
+
alias_method :serialize, :call
|
|
23
|
+
|
|
24
|
+
def to_proc
|
|
25
|
+
proc { |*args, &block| self.call(*args, &block) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(elements)
|
|
30
|
+
@elements = elements.dup.freeze
|
|
31
|
+
validate_input!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def call
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
end
|
|
37
|
+
alias_method :serialize, :call
|
|
38
|
+
|
|
39
|
+
def to_proc
|
|
40
|
+
proc { |*args, &block| self.call(*args, &block) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
|
|
3
|
+
require 'rosetta/exceptions'
|
|
4
|
+
require 'rosetta/serializers/base'
|
|
5
|
+
|
|
6
|
+
module Rosetta
|
|
7
|
+
module Serializers
|
|
8
|
+
class CSV < Base
|
|
9
|
+
def call
|
|
10
|
+
::CSV.generate do |csv|
|
|
11
|
+
csv << headers
|
|
12
|
+
elements.each do |element|
|
|
13
|
+
csv << headers.map { |header| serialize_value(element[header]) }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def headers
|
|
19
|
+
head, *_ = elements.map(&:properties).uniq
|
|
20
|
+
head
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate_input!
|
|
24
|
+
_, *others = elements.map(&:properties).uniq
|
|
25
|
+
|
|
26
|
+
raise SerializationError, <<-ERROR.strip unless others.none?
|
|
27
|
+
All objects need to share their structure to be serialized to CSV.
|
|
28
|
+
ERROR
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def serialize_value(value)
|
|
34
|
+
case value
|
|
35
|
+
when Array
|
|
36
|
+
value.join(',')
|
|
37
|
+
else
|
|
38
|
+
value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/rosetta/translation.rb
CHANGED
|
@@ -3,21 +3,24 @@ require 'rosetta/exceptions'
|
|
|
3
3
|
module Rosetta
|
|
4
4
|
class Translation
|
|
5
5
|
@registered = {}
|
|
6
|
-
|
|
7
6
|
attr_reader :serializer, :deserializer, :translator
|
|
8
7
|
alias_method :translator?, :translator
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
There already is a translator from #{source} to #{destination}.
|
|
13
|
-
ERROR
|
|
9
|
+
class << self
|
|
10
|
+
attr_reader :registered
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
def [](key)
|
|
13
|
+
@registered[key]
|
|
14
|
+
end
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
def register(source, destination, callable = nil, &block)
|
|
17
|
+
raise ExistingTranslatorError, <<-ERROR.strip if @registered.key? name
|
|
18
|
+
There already is a translator from #{source} to #{destination}.
|
|
19
|
+
ERROR
|
|
20
|
+
|
|
21
|
+
raise ArgumentError, "Can't take both callable object and block." if callable && block
|
|
22
|
+
@registered[source => destination] = callable || block
|
|
23
|
+
end
|
|
21
24
|
end
|
|
22
25
|
|
|
23
26
|
def initialize(deserializer, serializer)
|
data/lib/rosetta/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rosetta-stone
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jérémie Bonal
|
|
@@ -85,12 +85,14 @@ files:
|
|
|
85
85
|
- bin/console
|
|
86
86
|
- bin/setup
|
|
87
87
|
- lib/rosetta.rb
|
|
88
|
-
- lib/rosetta/csv_serializer.rb
|
|
89
88
|
- lib/rosetta/deserializers.rb
|
|
89
|
+
- lib/rosetta/deserializers/base.rb
|
|
90
|
+
- lib/rosetta/deserializers/json.rb
|
|
90
91
|
- lib/rosetta/element.rb
|
|
91
92
|
- lib/rosetta/exceptions.rb
|
|
92
|
-
- lib/rosetta/json_deserializer.rb
|
|
93
93
|
- lib/rosetta/serializers.rb
|
|
94
|
+
- lib/rosetta/serializers/base.rb
|
|
95
|
+
- lib/rosetta/serializers/csv.rb
|
|
94
96
|
- lib/rosetta/translation.rb
|
|
95
97
|
- lib/rosetta/version.rb
|
|
96
98
|
- rosetta.gemspec
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
require 'csv'
|
|
2
|
-
|
|
3
|
-
require 'rosetta/exceptions'
|
|
4
|
-
require 'rosetta/serializers'
|
|
5
|
-
|
|
6
|
-
module Rosetta
|
|
7
|
-
class CSVSerializer < Serializers::Base
|
|
8
|
-
def call
|
|
9
|
-
CSV.generate do |csv|
|
|
10
|
-
csv << headers
|
|
11
|
-
elements.each do |element|
|
|
12
|
-
csv << headers.map { |header| serialize_value(element[header]) }
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def headers
|
|
18
|
-
head, *_ = elements.map(&:properties).uniq
|
|
19
|
-
head
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def validate_input!
|
|
23
|
-
_, *others = elements.map(&:properties).uniq
|
|
24
|
-
|
|
25
|
-
raise SerializationError, <<-ERROR.strip unless others.none?
|
|
26
|
-
All objects need to share their structure to be serialized to CSV.
|
|
27
|
-
ERROR
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def serialize_value(value)
|
|
33
|
-
case value
|
|
34
|
-
when Array
|
|
35
|
-
value.join(',')
|
|
36
|
-
else
|
|
37
|
-
value
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
require 'json'
|
|
2
|
-
|
|
3
|
-
require 'rosetta/element'
|
|
4
|
-
require 'rosetta/exceptions'
|
|
5
|
-
require 'rosetta/deserializers'
|
|
6
|
-
|
|
7
|
-
module Rosetta
|
|
8
|
-
class JSONDeserializer < Deserializers::Base
|
|
9
|
-
def call
|
|
10
|
-
validate_input!
|
|
11
|
-
input.map { |obj| Element.new(obj) }
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def validate_input!
|
|
17
|
-
raise DeserializationError, <<-ERROR.strip unless parsed_input = valid_json(@input)
|
|
18
|
-
JSON input is invalid
|
|
19
|
-
ERROR
|
|
20
|
-
raise DeserializationError, <<-ERROR.strip unless parsed_input.is_a? Array
|
|
21
|
-
JSON input must be an array
|
|
22
|
-
ERROR
|
|
23
|
-
raise DeserializationError, <<-ERROR.strip unless parsed_input.all? { |o| o.is_a? Hash }
|
|
24
|
-
JSON input must contain objects
|
|
25
|
-
ERROR
|
|
26
|
-
|
|
27
|
-
@input = parsed_input.freeze
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
#HACK: Feels dirty but there's no JSON soft-parsing in ruby's json lib
|
|
31
|
-
def valid_json(json)
|
|
32
|
-
JSON(json)
|
|
33
|
-
#NOTE: Rescuing TypeError too in case json is not a String
|
|
34
|
-
rescue JSON::ParserError, TypeError
|
|
35
|
-
nil
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|