arstotzka 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +19 -8
- data/.gitignore +3 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +6 -3
- data/.yardopts +1 -0
- data/Dockerfile +8 -0
- data/README.md +2 -3
- data/Rakefile +2 -0
- data/arstotzka.gemspec +11 -8
- data/config/yardstick.rb +13 -0
- data/config/yardstick.yml +69 -0
- data/docker-compose.yml +14 -9
- data/lib/arstotzka/builder.rb +72 -0
- data/lib/arstotzka/class_methods.rb +9 -2
- data/lib/arstotzka/crawler.rb +56 -5
- data/lib/arstotzka/fetcher.rb +43 -14
- data/lib/arstotzka/reader.rb +44 -1
- data/lib/arstotzka/type_cast.rb +114 -1
- data/lib/arstotzka/version.rb +1 -1
- data/lib/arstotzka/wrapper.rb +47 -2
- data/spec/integration/readme/arstotzka_spec.rb +41 -0
- data/spec/integration/readme/my_parser_spec.rb +14 -13
- data/spec/integration/yard/arstotzka/crawler_spec.rb +9 -9
- data/spec/integration/yard/arstotzka/fetcher_spec.rb +4 -4
- data/spec/integration/yard/arstotzka/reader_spec.rb +7 -7
- data/spec/integration/yard/arstotzka/type_cast_spec.rb +55 -0
- data/spec/integration/yard/arstotzka/wrapper_spec.rb +30 -14
- data/spec/integration/yard/arstotzka_spec.rb +9 -9
- data/spec/lib/arstotzka/builder_spec.rb +18 -16
- data/spec/lib/arstotzka/crawler_spec.rb +21 -13
- data/spec/lib/arstotzka/fetcher_spec.rb +12 -10
- data/spec/lib/arstotzka/reader_spec.rb +16 -16
- data/spec/lib/arstotzka/wrapper_spec.rb +8 -2
- data/spec/lib/arstotzka_spec.rb +23 -23
- data/spec/support/models/car.rb +10 -0
- data/spec/support/models/car_collector.rb +20 -0
- data/spec/support/models/request.rb +9 -0
- data/spec/support/models/type_caster.rb +13 -0
- data/spec/support/shared_examples/wrapper.rb +2 -2
- metadata +65 -23
- data/spec/integration/readme/default_spec.rb +0 -39
data/lib/arstotzka/reader.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Arstotzka
|
4
|
+
# @api private
|
5
|
+
#
|
4
6
|
# Reads a value from a hash using the path as list of keys
|
5
7
|
class Reader
|
8
|
+
# Creates a new instance of Reader
|
9
|
+
#
|
6
10
|
# @param path [Array] path of keys broken down as array
|
7
11
|
# @param case_type [Symbol] Case of the keys
|
8
12
|
# - lower_camel: keys in the hash are lowerCamelCase
|
9
13
|
# - upper_camel: keys in the hash are UpperCamelCase
|
10
14
|
# - snake: keys in the hash are snake_case
|
15
|
+
#
|
16
|
+
# @return [Aristotzka::Reader]
|
11
17
|
def initialize(path:, case_type:)
|
12
18
|
@case_type = case_type
|
13
19
|
@path = path.map(&method(:change_case))
|
@@ -18,7 +24,7 @@ module Arstotzka
|
|
18
24
|
# @param hash [Hash] hash to be read
|
19
25
|
# @param index [Integer] Index of the key (in path) to be used
|
20
26
|
#
|
21
|
-
# @return Object The value fetched from the hash
|
27
|
+
# @return [Object] The value fetched from the hash
|
22
28
|
#
|
23
29
|
# @example
|
24
30
|
# hash = {
|
@@ -55,6 +61,8 @@ module Arstotzka
|
|
55
61
|
hash.key?(key) ? hash[key] : hash[key.to_sym]
|
56
62
|
end
|
57
63
|
|
64
|
+
# @private
|
65
|
+
#
|
58
66
|
# Checks if index is within path range
|
59
67
|
#
|
60
68
|
# @example
|
@@ -67,17 +75,52 @@ module Arstotzka
|
|
67
75
|
|
68
76
|
private
|
69
77
|
|
78
|
+
# @private
|
70
79
|
attr_reader :path, :case_type
|
71
80
|
|
81
|
+
# @private
|
82
|
+
#
|
83
|
+
# Checks if a hash contains or not the key
|
84
|
+
#
|
85
|
+
# if the key is not found, an execption is raised
|
86
|
+
#
|
87
|
+
# @raise Arstotzka::Exception::KeyNotFound
|
88
|
+
#
|
89
|
+
# @return [NilClass]
|
90
|
+
#
|
91
|
+
# @see #key?
|
72
92
|
def check_key!(hash, key)
|
73
93
|
return if key?(hash, key)
|
74
94
|
raise Exception::KeyNotFound
|
75
95
|
end
|
76
96
|
|
97
|
+
# @private
|
98
|
+
#
|
99
|
+
# Checks if a hash contains or not the key
|
100
|
+
#
|
101
|
+
# The check first happens using String key and,
|
102
|
+
# in case of not found, searches as symbol
|
103
|
+
#
|
104
|
+
# @param [Hash] hash Hash where the key will be found
|
105
|
+
# @param [String] key The key to be checked
|
106
|
+
#
|
107
|
+
# @return [Boolean]
|
108
|
+
#
|
109
|
+
# @see #check_key!
|
77
110
|
def key?(hash, key)
|
78
111
|
hash&.key?(key) || hash&.key?(key.to_sym)
|
79
112
|
end
|
80
113
|
|
114
|
+
# @private
|
115
|
+
#
|
116
|
+
# Transforms the key to have the correct case
|
117
|
+
#
|
118
|
+
# the possible case_types (instance attribute) are
|
119
|
+
# - lower_camel: for cammel case with first letter lowercase
|
120
|
+
# - upper_camel: for cammel case with first letter uppercase
|
121
|
+
# - snake: for snake case
|
122
|
+
#
|
123
|
+
# @param [String] key the key to be transformed
|
81
124
|
def change_case(key)
|
82
125
|
case case_type
|
83
126
|
when :lower_camel
|
data/lib/arstotzka/type_cast.rb
CHANGED
@@ -1,27 +1,140 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Arstotzka
|
4
|
+
# @api public
|
5
|
+
#
|
4
6
|
# Concern with all the type cast methods to be used by {Wrapper}
|
5
7
|
#
|
6
8
|
# Usage of typecast is defined by the configuration of {Builder} by the usage of
|
7
9
|
# option type
|
10
|
+
#
|
11
|
+
# TypeCast can also be extended to include more types
|
12
|
+
#
|
13
|
+
# Supported types:
|
14
|
+
# - integer
|
15
|
+
# - string
|
16
|
+
# - float
|
17
|
+
#
|
18
|
+
# @example (see #to_integer)
|
19
|
+
#
|
20
|
+
# @example (see #to_string)
|
21
|
+
#
|
22
|
+
# @example (see #to_float)
|
23
|
+
#
|
24
|
+
# @example Extending typecast
|
25
|
+
# class Car
|
26
|
+
# attr_reader :model, :maker
|
27
|
+
#
|
28
|
+
# def initialize(model:, maker:)
|
29
|
+
# @model = model
|
30
|
+
# @maker = maker
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# module Arstotzka
|
35
|
+
# module TypeCast
|
36
|
+
# def to_car(hash)
|
37
|
+
# Car.new(hash.symbolize_keys)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# class CarCollector
|
43
|
+
# include Arstotzka
|
44
|
+
#
|
45
|
+
# attr_reader :json
|
46
|
+
#
|
47
|
+
# expose :cars, full_path: 'cars.unit', type: :car
|
48
|
+
# def initialize(hash)
|
49
|
+
# @json = hash
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# hash = {
|
54
|
+
# cars: [{
|
55
|
+
# unit: { model: 'fox', maker: 'volkswagen' }
|
56
|
+
# }, {
|
57
|
+
# unit: { 'model' => 'focus', 'maker' => 'ford' }
|
58
|
+
# }]
|
59
|
+
# }
|
60
|
+
#
|
61
|
+
# model = CarCollector.new(hash)
|
62
|
+
#
|
63
|
+
# model.cars # returns [<Car>, <Car>]
|
64
|
+
# model.cars.map(&:model) # returns ['fox', 'focus']
|
8
65
|
module TypeCast
|
9
66
|
extend ActiveSupport::Concern
|
10
67
|
|
11
|
-
#
|
68
|
+
# Converts a value to integer
|
69
|
+
#
|
12
70
|
# @return [Integer]
|
71
|
+
#
|
72
|
+
# @example Casting to Integer
|
73
|
+
# class TypeCaster
|
74
|
+
# include Arstotzka
|
75
|
+
#
|
76
|
+
# expose :age, type: :integer, json: :@hash
|
77
|
+
#
|
78
|
+
# def initialize(hash)
|
79
|
+
# @hash = hash
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# hash = {
|
84
|
+
# age: '10',
|
85
|
+
# }
|
86
|
+
#
|
87
|
+
# TypeCaseter.new(age: '10').age
|
13
88
|
def to_integer(value)
|
14
89
|
value.to_i if value.present?
|
15
90
|
end
|
16
91
|
|
17
92
|
# converts value to string
|
93
|
+
#
|
18
94
|
# @return [String]
|
95
|
+
#
|
96
|
+
# @example Casting to String
|
97
|
+
# class TypeCaster
|
98
|
+
# include Arstotzka
|
99
|
+
#
|
100
|
+
# expose :payload, type: :string, json: :@hash
|
101
|
+
#
|
102
|
+
# def initialize(hash)
|
103
|
+
# @hash = hash
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# hash = {
|
108
|
+
# payload: { 'key' => 'value' },
|
109
|
+
# }
|
110
|
+
#
|
111
|
+
# model.TypeCaseter.new(hash)
|
112
|
+
#
|
113
|
+
# model.payload # returns '{"key"=>"value"}'
|
19
114
|
def to_string(value)
|
20
115
|
value.to_s
|
21
116
|
end
|
22
117
|
|
23
118
|
# converts value to float
|
119
|
+
#
|
24
120
|
# @return [Float]
|
121
|
+
#
|
122
|
+
# @example Casting to Float
|
123
|
+
# class TypeCaster
|
124
|
+
# include Arstotzka
|
125
|
+
#
|
126
|
+
# expose :price, type: :float, json: :@hash
|
127
|
+
#
|
128
|
+
# def initialize(hash)
|
129
|
+
# @hash = hash
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# hash = {
|
134
|
+
# price: '1.75'
|
135
|
+
# }
|
136
|
+
#
|
137
|
+
# TypeCaseter.new(price: '1.75').price # returns 1.75
|
25
138
|
def to_float(value)
|
26
139
|
value.to_f if value.present?
|
27
140
|
end
|
data/lib/arstotzka/version.rb
CHANGED
data/lib/arstotzka/wrapper.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Arstotzka
|
4
|
+
# @api private
|
5
|
+
#
|
4
6
|
# Class responsible for wrapping / parsing a value fetched
|
5
7
|
class Wrapper
|
6
8
|
include Arstotzka::TypeCast
|
7
9
|
|
10
|
+
# Returns a new instance of Wrapper
|
11
|
+
#
|
8
12
|
# @param clazz [Class] class to wrap the value
|
9
13
|
# @param type [String/Symbol] type to cast the value. The
|
10
14
|
# possible type_cast is defined by {TypeCast}
|
@@ -15,7 +19,9 @@ module Arstotzka
|
|
15
19
|
|
16
20
|
# wrap a value
|
17
21
|
#
|
18
|
-
# @
|
22
|
+
# @return [Object]
|
23
|
+
#
|
24
|
+
# @example Wrapping in a class
|
19
25
|
# class Person
|
20
26
|
# attr_reader :name
|
21
27
|
#
|
@@ -27,9 +33,23 @@ module Arstotzka
|
|
27
33
|
# wrapper = Arstotzka::Wrapper.new(clazz: Person)
|
28
34
|
# wrapper.wrap('John') # retruns Person.new('John')
|
29
35
|
#
|
30
|
-
# @example
|
36
|
+
# @example Casting type
|
31
37
|
# wrapper = Arstotzka::Wrapper.new(type: :integer)
|
32
38
|
# wrapper.wrap(['10', '20', '30']) # retruns [10, 20, 30]
|
39
|
+
#
|
40
|
+
# @example Casting and Wrapping
|
41
|
+
# class Request
|
42
|
+
# attr_reader :payload
|
43
|
+
#
|
44
|
+
# def initialize(payload)
|
45
|
+
# @payload = payload
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# wrapper = Arstotzka::Wrapper.new(type: :string, clazz: Request)
|
50
|
+
# request = wrapper.wrap(value)
|
51
|
+
#
|
52
|
+
# request.payload # returns '{"key"=>"value"}'
|
33
53
|
def wrap(value)
|
34
54
|
return wrap_array(value) if value.is_a?(Array)
|
35
55
|
wrap_element(value)
|
@@ -37,8 +57,14 @@ module Arstotzka
|
|
37
57
|
|
38
58
|
private
|
39
59
|
|
60
|
+
# @private
|
40
61
|
attr_reader :clazz, :type
|
41
62
|
|
63
|
+
# @private
|
64
|
+
#
|
65
|
+
# Wraps an element with a class and perform typecasting
|
66
|
+
#
|
67
|
+
# @return [Object]
|
42
68
|
def wrap_element(value)
|
43
69
|
value = cast(value) if type? && !value.nil?
|
44
70
|
return if value.nil?
|
@@ -46,14 +72,33 @@ module Arstotzka
|
|
46
72
|
clazz ? clazz.new(value) : value
|
47
73
|
end
|
48
74
|
|
75
|
+
# @private
|
76
|
+
#
|
77
|
+
# Wraps each element of the array
|
78
|
+
#
|
79
|
+
# @see #wrap_element
|
80
|
+
#
|
81
|
+
# @return [Arra]
|
49
82
|
def wrap_array(array)
|
50
83
|
array.map { |v| wrap v }
|
51
84
|
end
|
52
85
|
|
86
|
+
# @private
|
87
|
+
#
|
88
|
+
# Check if type was given
|
89
|
+
#
|
90
|
+
# @return [Boolean]
|
53
91
|
def type?
|
54
92
|
type.present? && type != :none
|
55
93
|
end
|
56
94
|
|
95
|
+
# @private
|
96
|
+
#
|
97
|
+
# Performs type casting
|
98
|
+
#
|
99
|
+
# @see Arstotzka::TypeCaster
|
100
|
+
#
|
101
|
+
# @return [Object]
|
57
102
|
def cast(value)
|
58
103
|
public_send("to_#{type}", value)
|
59
104
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Arstotzka do
|
6
|
+
describe 'default option' do
|
7
|
+
subject(:star_gazer) do
|
8
|
+
StarGazer.new(hash).favorite_star
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:hash) { {} }
|
12
|
+
|
13
|
+
context 'when node is not found' do
|
14
|
+
it 'returns the default before wrapping' do
|
15
|
+
expect(star_gazer.name).to eq('Sun')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'wraps the returned value in a class' do
|
19
|
+
expect(star_gazer).to be_a(Star)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when node is not missing' do
|
24
|
+
let(:hash) do
|
25
|
+
{
|
26
|
+
universe: {
|
27
|
+
star: { name: 'Antares' }
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns the value before wrapping' do
|
33
|
+
expect(star_gazer.name).to eq('Antares')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'wraps the returned value in a class' do
|
37
|
+
expect(star_gazer).to be_a(Star)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe MyParser do
|
6
|
-
subject { described_class.new(hash) }
|
6
|
+
subject(:parser) { described_class.new(hash) }
|
7
7
|
|
8
8
|
let(:hash) do
|
9
9
|
{
|
@@ -27,62 +27,63 @@ describe MyParser do
|
|
27
27
|
|
28
28
|
describe 'id' do
|
29
29
|
it 'returns the parsed id' do
|
30
|
-
expect(
|
30
|
+
expect(parser.id).to eq(10)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
describe 'name' do
|
35
35
|
it 'returns the parsed name' do
|
36
|
-
expect(
|
36
|
+
expect(parser.name).to eq('Robert')
|
37
37
|
end
|
38
38
|
|
39
39
|
context 'when person is missing' do
|
40
|
-
subject { described_class.new }
|
40
|
+
subject(:parser) { described_class.new }
|
41
41
|
|
42
42
|
it do
|
43
|
-
expect {
|
43
|
+
expect { parser.name }.not_to raise_error
|
44
44
|
end
|
45
45
|
|
46
46
|
it do
|
47
|
-
expect(
|
47
|
+
expect(parser.name).to be_nil
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
describe 'age' do
|
53
53
|
it do
|
54
|
-
expect(
|
54
|
+
expect(parser.age).to be_a(Integer)
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'returns the parsed age' do
|
58
|
-
expect(
|
58
|
+
expect(parser.age).to eq(22)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
62
|
describe '#total_money' do
|
63
63
|
it do
|
64
|
-
expect(
|
64
|
+
expect(parser.total_money).to be_a(Float)
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'summs all the balance in the accounts' do
|
68
|
-
expect(
|
68
|
+
expect(parser.total_money).to eq(1050.36)
|
69
69
|
end
|
70
70
|
|
71
71
|
context 'when there is a node missing' do
|
72
72
|
let(:hash) { {} }
|
73
|
+
|
73
74
|
it 'returns nil' do
|
74
|
-
expect(
|
75
|
+
expect(parser.total_money).to be_nil
|
75
76
|
end
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
79
80
|
describe '#total_owed' do
|
80
81
|
it do
|
81
|
-
expect(
|
82
|
+
expect(parser.total_owed).to be_a(Float)
|
82
83
|
end
|
83
84
|
|
84
85
|
it 'summs all the balance in the accounts' do
|
85
|
-
expect(
|
86
|
+
expect(parser.total_owed).to eq(550.84)
|
86
87
|
end
|
87
88
|
end
|
88
89
|
end
|
@@ -4,7 +4,7 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Arstotzka::Crawler do
|
6
6
|
describe 'yard' do
|
7
|
-
subject do
|
7
|
+
subject(:crawler) do
|
8
8
|
described_class.new(path: path, **options)
|
9
9
|
end
|
10
10
|
|
@@ -21,13 +21,13 @@ describe Arstotzka::Crawler do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'crawls to find the value' do
|
24
|
-
expect(
|
24
|
+
expect(crawler.value(hash)).to eq('John')
|
25
25
|
end
|
26
26
|
|
27
27
|
describe '#value' do
|
28
28
|
context 'when hash contains the path' do
|
29
29
|
it 'crawls to find the value' do
|
30
|
-
expect(
|
30
|
+
expect(crawler.value(hash)).to eq('John')
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -51,24 +51,24 @@ describe Arstotzka::Crawler do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'crawls to find the value' do
|
54
|
-
expect(
|
54
|
+
expect(crawler.value(hash)).to eq([['Rakhar']])
|
55
55
|
end
|
56
56
|
|
57
|
-
context '
|
57
|
+
context 'with default value' do
|
58
58
|
let(:options) { { compact: true, case_type: :snake, default: 'NO HERO' } }
|
59
59
|
|
60
60
|
it 'return default value for missed keys' do
|
61
|
-
expect(
|
61
|
+
expect(crawler.value(hash)).to eq([['NO HERO', 'Rakhar'], 'NO HERO'])
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
context '
|
66
|
-
subject do
|
65
|
+
context 'when block is given' do
|
66
|
+
subject(:crawler) do
|
67
67
|
described_class.new(path: path, **options) { |value| value&.to_sym }
|
68
68
|
end
|
69
69
|
|
70
70
|
it 'returns the post processed values' do
|
71
|
-
expect(
|
71
|
+
expect(crawler.value(hash)).to eq([[:Rakhar]])
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -5,7 +5,7 @@ require 'spec_helper'
|
|
5
5
|
describe Arstotzka::Fetcher do
|
6
6
|
describe 'yard' do
|
7
7
|
describe '#fetch' do
|
8
|
-
subject { described_class.new(hash, instance, path: path, **options) }
|
8
|
+
subject(:fetcher) { described_class.new(hash, instance, path: path, **options) }
|
9
9
|
|
10
10
|
let(:instance) { Account.new }
|
11
11
|
let(:path) { 'transactions' }
|
@@ -31,11 +31,11 @@ describe Arstotzka::Fetcher do
|
|
31
31
|
|
32
32
|
describe 'incoming transactions' do
|
33
33
|
it 'returns only the income payments' do
|
34
|
-
expect(
|
34
|
+
expect(fetcher.fetch.count).to eq(2)
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'returns Transactions' do
|
38
|
-
expect(
|
38
|
+
expect(fetcher.fetch.map(&:class).uniq).to eq([Transaction])
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'returns results wrapped in Transactions' do
|
@@ -43,7 +43,7 @@ describe Arstotzka::Fetcher do
|
|
43
43
|
Transaction.new(value: 1000.53, type: 'income'),
|
44
44
|
Transaction.new(value: 50.23, type: 'income')
|
45
45
|
]
|
46
|
-
expect(
|
46
|
+
expect(fetcher.fetch).to eq(expected)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -4,7 +4,7 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Arstotzka::Reader do
|
6
6
|
describe 'yard' do
|
7
|
-
subject { described_class.new(path: path, case_type: case_type) }
|
7
|
+
subject(:reader) { described_class.new(path: path, case_type: case_type) }
|
8
8
|
|
9
9
|
let(:path) { %w[person full_name] }
|
10
10
|
let(:case_type) { :snake }
|
@@ -23,7 +23,7 @@ describe Arstotzka::Reader do
|
|
23
23
|
|
24
24
|
context 'when using snake_case' do
|
25
25
|
it 'fetches the value using snake case key' do
|
26
|
-
expect(
|
26
|
+
expect(reader.read(hash, 1)).to eq('John')
|
27
27
|
end
|
28
28
|
|
29
29
|
context 'when key is missing' do
|
@@ -31,7 +31,7 @@ describe Arstotzka::Reader do
|
|
31
31
|
|
32
32
|
it do
|
33
33
|
expect do
|
34
|
-
|
34
|
+
reader.read(hash, 1)
|
35
35
|
end.to raise_error(Arstotzka::Exception::KeyNotFound)
|
36
36
|
end
|
37
37
|
end
|
@@ -46,7 +46,7 @@ describe Arstotzka::Reader do
|
|
46
46
|
{ maker: 'Ford', 'model' => 'Model A' },
|
47
47
|
{ maker: 'BMW', 'model' => 'Jetta' }
|
48
48
|
]
|
49
|
-
expect(
|
49
|
+
expect(reader.read(hash, 1)).to eq(expected)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -55,7 +55,7 @@ describe Arstotzka::Reader do
|
|
55
55
|
let(:path) { %w[person age] }
|
56
56
|
|
57
57
|
it 'fetches the value using uper camel case key' do
|
58
|
-
expect(
|
58
|
+
expect(reader.read(hash, 1)).to eq(23)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -63,13 +63,13 @@ describe Arstotzka::Reader do
|
|
63
63
|
describe '#ended?' do
|
64
64
|
context 'when the fetches have not ended' do
|
65
65
|
it do
|
66
|
-
expect(
|
66
|
+
expect(reader).not_to be_ended(1)
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
context 'when the fetches have ended' do
|
71
71
|
it do
|
72
|
-
expect(
|
72
|
+
expect(reader).to be_ended(2)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Arstotzka::TypeCast do
|
4
|
+
subject(:model) { TypeCaster.new(hash) }
|
5
|
+
|
6
|
+
let(:hash) do
|
7
|
+
{
|
8
|
+
age: '10',
|
9
|
+
payload: { 'key' => 'value' },
|
10
|
+
price: '1.75'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'yard' do
|
15
|
+
describe 'integer' do
|
16
|
+
it 'converts string to integer' do
|
17
|
+
expect(model.age).to eq(10)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'string' do
|
22
|
+
it 'converts value to string' do
|
23
|
+
expect(model.payload).to eq('{"key"=>"value"}')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'float' do
|
28
|
+
it 'converts value to string' do
|
29
|
+
expect(model.price).to eq(1.75)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'extending' do
|
34
|
+
subject(:model) { CarCollector.new(hash) }
|
35
|
+
|
36
|
+
let(:hash) do
|
37
|
+
{
|
38
|
+
cars: [{
|
39
|
+
unit: { model: 'fox', maker: 'volkswagen' }
|
40
|
+
}, {
|
41
|
+
unit: { 'model' => 'focus', 'maker' => 'ford' }
|
42
|
+
}]
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'converts each unit to be a car' do
|
47
|
+
expect(model.cars.first).to be_a(Car)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'converts using the given hash' do
|
51
|
+
expect(model.cars.map(&:model)).to eq(%w[fox focus])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|