rosetta-stone 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|