representors 0.0.5
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 +7 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +126 -0
- data/LICENSE.md +19 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/lib/representor_support/utilities.rb +39 -0
- data/lib/representors.rb +5 -0
- data/lib/representors/errors.rb +7 -0
- data/lib/representors/field.rb +108 -0
- data/lib/representors/options.rb +67 -0
- data/lib/representors/representor.rb +161 -0
- data/lib/representors/representor_builder.rb +64 -0
- data/lib/representors/representor_hash.rb +59 -0
- data/lib/representors/serialization.rb +4 -0
- data/lib/representors/serialization/deserializer_base.rb +29 -0
- data/lib/representors/serialization/deserializer_factory.rb +13 -0
- data/lib/representors/serialization/hal_deserializer.rb +44 -0
- data/lib/representors/serialization/hal_serializer.rb +91 -0
- data/lib/representors/serialization/hale_deserializer.rb +162 -0
- data/lib/representors/serialization/hale_serializer.rb +110 -0
- data/lib/representors/serialization/serialization_base.rb +27 -0
- data/lib/representors/serialization/serialization_factory_base.rb +54 -0
- data/lib/representors/serialization/serializer_base.rb +20 -0
- data/lib/representors/serialization/serializer_factory.rb +17 -0
- data/lib/representors/transition.rb +130 -0
- data/lib/representors/version.rb +4 -0
- data/spec/fixtures/complex_hal.json +92 -0
- data/spec/fixtures/complex_hale_document.json +81 -0
- data/spec/fixtures/drds_hash.rb +120 -0
- data/spec/fixtures/hale_spec_examples/basic.json +77 -0
- data/spec/fixtures/hale_spec_examples/complex_reference_objects.json +157 -0
- data/spec/fixtures/hale_spec_examples/data.json +17 -0
- data/spec/fixtures/hale_spec_examples/data_objects.json +96 -0
- data/spec/fixtures/hale_spec_examples/link_objects.json +18 -0
- data/spec/fixtures/hale_spec_examples/nested_ref.json +43 -0
- data/spec/fixtures/hale_spec_examples/reference_objects.json +89 -0
- data/spec/fixtures/hale_tutorial_examples/basic_links.json +85 -0
- data/spec/fixtures/hale_tutorial_examples/basic_links_with_orders.json +96 -0
- data/spec/fixtures/hale_tutorial_examples/basic_links_with_references.json +108 -0
- data/spec/fixtures/hale_tutorial_examples/embedded.json +182 -0
- data/spec/fixtures/hale_tutorial_examples/empty.json +1 -0
- data/spec/fixtures/hale_tutorial_examples/enctype.json +14 -0
- data/spec/fixtures/hale_tutorial_examples/final.json +141 -0
- data/spec/fixtures/hale_tutorial_examples/get_link.json +17 -0
- data/spec/fixtures/hale_tutorial_examples/get_link_with_data.json +29 -0
- data/spec/fixtures/hale_tutorial_examples/links.json +11 -0
- data/spec/fixtures/hale_tutorial_examples/links_only.json +3 -0
- data/spec/fixtures/hale_tutorial_examples/meta.json +208 -0
- data/spec/fixtures/hale_tutorial_examples/self_link.json +7 -0
- data/spec/fixtures/single_drd.rb +266 -0
- data/spec/lib/representors/complex_representor_spec.rb +288 -0
- data/spec/lib/representors/field_spec.rb +141 -0
- data/spec/lib/representors/representor_builder_spec.rb +223 -0
- data/spec/lib/representors/representor_spec.rb +285 -0
- data/spec/lib/representors/serialization/deserializer_factory_spec.rb +118 -0
- data/spec/lib/representors/serialization/hal_deserializer_spec.rb +34 -0
- data/spec/lib/representors/serialization/hal_serializer_spec.rb +171 -0
- data/spec/lib/representors/serialization/hale_deserializer_spec.rb +59 -0
- data/spec/lib/representors/serialization/hale_roundtrip_spec.rb +34 -0
- data/spec/lib/representors/serialization/hale_serializer_spec.rb +659 -0
- data/spec/lib/representors/serialization/serializer_factory_spec.rb +108 -0
- data/spec/lib/representors/transition_spec.rb +349 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/basic-hale.json +12 -0
- data/spec/support/hal_representor_shared.rb +206 -0
- data/spec/support/helpers.rb +8 -0
- data/tasks/benchmark.rake +75 -0
- data/tasks/complex_hal_document.json +98 -0
- data/tasks/test_specs.rake +37 -0
- data/tasks/yard.rake +22 -0
- metadata +232 -0
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
unless ENV['MUTANT']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
end
|
5
|
+
|
6
|
+
SPEC_DIR = File.expand_path("..", __FILE__)
|
7
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
8
|
+
require 'representors'
|
9
|
+
|
10
|
+
Dir["#{SPEC_DIR}/support/*.rb"].each { |f| require f }
|
11
|
+
|
12
|
+
def create_serializer(name)
|
13
|
+
Class.new(Representors::SerializerBase) do |klass|
|
14
|
+
klass.media_symbol name.to_sym
|
15
|
+
klass.media_type name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.expect_with :rspec do |c|
|
21
|
+
c.syntax = :expect
|
22
|
+
end
|
23
|
+
|
24
|
+
# Run specs in random order to surface order dependencies. If you find an
|
25
|
+
# order dependency and want to debug it, you can fix the order by providing
|
26
|
+
# the seed, which is printed after each run.
|
27
|
+
# --seed 1234
|
28
|
+
config.order = 'random' unless ENV['RANDOMIZE'] == 'false'
|
29
|
+
|
30
|
+
config.include Support::Helpers
|
31
|
+
config.include RepresentorSupport::Utilities
|
32
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
shared_examples_for 'can create a representor from a hal document' do
|
2
|
+
|
3
|
+
let(:semantics_field) {deserializer.to_representor.properties}
|
4
|
+
let(:transitions_field) {deserializer.to_representor.transitions}
|
5
|
+
let(:embedded_field) {deserializer.to_representor.embedded }
|
6
|
+
context "empty document" do
|
7
|
+
let(:document) { {}.to_json }
|
8
|
+
|
9
|
+
it "returns a hash with no attributes, links or embedded resources" do
|
10
|
+
expect(deserializer.to_representor.properties).to be_empty
|
11
|
+
expect(deserializer.to_representor.transitions).to be_empty
|
12
|
+
expect(deserializer.to_representor.embedded).to be_empty
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'Document with only properties' do
|
17
|
+
let(:original_hash) do
|
18
|
+
{
|
19
|
+
'title' => 'The Neverending Story',
|
20
|
+
'author' => 'Michael Ende',
|
21
|
+
'pages' => '396'
|
22
|
+
}
|
23
|
+
end
|
24
|
+
let(:document) { original_hash.to_json}
|
25
|
+
|
26
|
+
it 'return a representor with all the attributes of the document' do
|
27
|
+
expect(semantics_field).to eq(original_hash)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'Document with properties and links' do
|
33
|
+
let(:semantics) { { 'title' => 'The Neverending Story'}}
|
34
|
+
let(:transition_rel) { 'author'}
|
35
|
+
let(:transition_href) { '/mike'}
|
36
|
+
let(:document) do
|
37
|
+
{
|
38
|
+
'title' => 'The Neverending Story',
|
39
|
+
'_links' => {
|
40
|
+
'author' => {'href' => '/mike'}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'return a hash with all the attributes of the document' do
|
46
|
+
expect(semantics_field).to eq(semantics)
|
47
|
+
end
|
48
|
+
it 'Create a transition with the link' do
|
49
|
+
expect(transitions_field.first.rel).to eq(transition_rel)
|
50
|
+
expect(transitions_field.first.uri).to eq(transition_href)
|
51
|
+
end
|
52
|
+
it 'does not return any embedded resource' do
|
53
|
+
expect(embedded_field).to be_empty
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'Document with properties, links and embedded' do
|
59
|
+
let(:semantics) { { 'title' => 'The Neverending Story'}}
|
60
|
+
let(:transition_rel) { 'author'}
|
61
|
+
let(:transition_href) { '/mike'}
|
62
|
+
let(:embedded_book) { {'content' => 'A...'} }
|
63
|
+
let(:document) do
|
64
|
+
{
|
65
|
+
'title' => 'The Neverending Story',
|
66
|
+
'_links' => {
|
67
|
+
transition_rel => {'href' => transition_href}
|
68
|
+
},
|
69
|
+
'_embedded' => {
|
70
|
+
'embedded_book' => embedded_book
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
it 'Returns a hash with all the attributes of the document' do
|
75
|
+
expect(semantics_field).to eq(semantics)
|
76
|
+
end
|
77
|
+
it 'Creates a transition with the link' do
|
78
|
+
expect(transitions_field.first.rel).to eq(transition_rel)
|
79
|
+
expect(transitions_field.first.uri).to eq(transition_href)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'Creates an embedded resource with its data' do
|
83
|
+
expect(embedded_field['embedded_book'].properties).to eq(embedded_book)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
context 'Document with an embedded collection' do
|
89
|
+
let(:embedded_book1) { {'content' => 'A...'} }
|
90
|
+
let(:embedded_book2) { {'content' => 'When...'} }
|
91
|
+
let(:embedded_book3) { {'content' => 'Once upon...'} }
|
92
|
+
let(:embedded_books) { [ embedded_book1, embedded_book2, embedded_book3 ] }
|
93
|
+
let(:document) do
|
94
|
+
{
|
95
|
+
'_embedded' => {
|
96
|
+
'embedded_books' => [ embedded_book1, embedded_book2, embedded_book3 ]
|
97
|
+
}
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'Creates three embedded resources' do
|
102
|
+
expect(embedded_field['embedded_books'].size).to eq(embedded_books.size)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'Creates embedded resources with its data' do
|
106
|
+
embedded_books.each_with_index do |item, index|
|
107
|
+
expect(embedded_field['embedded_books'][index].properties).to eq(item)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'Document with only a self link and a title' do
|
113
|
+
let(:href) { '/example_resource'}
|
114
|
+
let(:title) { 'super!'}
|
115
|
+
let(:document) do
|
116
|
+
{ '_links' => {
|
117
|
+
'self' => { 'href' => href, 'title' => title}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'the transition has a "self" rel' do
|
123
|
+
expect(transitions_field.first.rel).to eq('self')
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'The transition has its href set properly' do
|
127
|
+
expect(transitions_field.first.uri).to eq(href)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'The transition has a title' do
|
131
|
+
expect(transitions_field.first['title']).to eq(title)
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'Document with an array of two links under items' do
|
137
|
+
let(:first_href) {'/example_resource'}
|
138
|
+
let(:second_href) {'/lotr_resource2'}
|
139
|
+
let(:rel) { 'items'}
|
140
|
+
let(:document) do
|
141
|
+
{ '_links' => {
|
142
|
+
rel => [{ 'href' => first_href, 'title' => 'resource1'},
|
143
|
+
{ 'href' => second_href, 'title' => 'resource2'}]
|
144
|
+
}
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'The representor has two links' do
|
149
|
+
expect(transitions_field.size).to eq(2)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'The transitions have a rel properly set' do
|
153
|
+
expect(transitions_field.all?{|link| link.rel == 'items'}).to eq(true)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'The transitions have a href properly set ' do
|
157
|
+
expect(transitions_field[0].uri).to eq(first_href)
|
158
|
+
expect(transitions_field[1].uri).to eq(second_href)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'Document with a link without a href' do
|
163
|
+
let(:document) do
|
164
|
+
{ '_links' => {
|
165
|
+
'self' => { 'title' => 'things'}
|
166
|
+
}
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'raises a DeserializationError' do
|
171
|
+
expect{transitions_field}.to raise_error(Representors::DeserializationError, "All links must contain the href attribute")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'Document where not all links have href' do
|
176
|
+
let(:link_properties) { { 'href' => '/example_resource'} }
|
177
|
+
let(:document) do
|
178
|
+
{ '_links' => {
|
179
|
+
'items' => [{ 'title' => 'resource1'},
|
180
|
+
{ 'href' => '/example_resource2', 'title' => 'resource2'}]
|
181
|
+
}
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'raises a DeserializationError' do
|
186
|
+
expect{transitions_field}.to raise_error(Representors::DeserializationError, "All links must contain the href attribute")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'Document with CURIEs' do
|
191
|
+
let(:link_properties) { { 'href' => '/example_resource'} }
|
192
|
+
let(:document) do
|
193
|
+
{ '_links' => {
|
194
|
+
'curies' => { 'href' => '/example_resource'}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'raises a DeserializationError' do
|
200
|
+
expect{transitions_field}.to raise_error(Representors::DeserializationError, "CURIE support not implemented for HAL")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'representors'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
ITERATIONS = 5
|
5
|
+
DESERIALIZATIONS = 10_000
|
6
|
+
HAL_BENCHMARK_FILE = 'complex_hal_document.json'
|
7
|
+
HALE_BENCHMARK_FILE = 'complex_hale_document.json'
|
8
|
+
|
9
|
+
|
10
|
+
def benchmark
|
11
|
+
benchmark_result = Benchmark.bm do |benchmarker|
|
12
|
+
1.upto(ITERATIONS) do |iteration|
|
13
|
+
benchmarker.report("Iteration #{iteration}") do
|
14
|
+
DESERIALIZATIONS.times do
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
benchmark_result
|
21
|
+
average_total_times = benchmark_result.map(&:total).inject(&:+) / ITERATIONS
|
22
|
+
average_operation_ms = (average_total_times * 1000) / DESERIALIZATIONS
|
23
|
+
|
24
|
+
puts "Processing #{DESERIALIZATIONS} objects took on average #{'%.4f' % average_total_times} seconds"
|
25
|
+
puts "It took #{'%.4f' % average_operation_ms} milliseconds to process each document or representor"
|
26
|
+
end
|
27
|
+
|
28
|
+
def benchmark_deserializer(format, document)
|
29
|
+
data = File.read( File.join(File.dirname(__FILE__), document))
|
30
|
+
puts "Deserializing #{format}:"
|
31
|
+
|
32
|
+
benchmark do
|
33
|
+
result = Representors::DeserializerFactory.build(format, data).to_representor
|
34
|
+
result.properties
|
35
|
+
result.transitions
|
36
|
+
result.embedded
|
37
|
+
end
|
38
|
+
puts "------------------"
|
39
|
+
puts " "
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def benchmark_serializer(format, document)
|
44
|
+
data = File.read( File.join(File.dirname(__FILE__), document))
|
45
|
+
representor = Representors::DeserializerFactory.build(format, data).to_representor
|
46
|
+
|
47
|
+
puts "Serializing #{format}:"
|
48
|
+
|
49
|
+
benchmark do
|
50
|
+
Representors::SerializerFactory.build(format, representor).to_media_type
|
51
|
+
end
|
52
|
+
puts "------------------"
|
53
|
+
puts " "
|
54
|
+
end
|
55
|
+
|
56
|
+
namespace :benchmark do
|
57
|
+
desc 'Benchmark deserializations'
|
58
|
+
task :deserializations do
|
59
|
+
benchmark_deserializer('application/hal+json', HAL_BENCHMARK_FILE )
|
60
|
+
benchmark_deserializer('application/vnd.hale+json', HALE_BENCHMARK_FILE )
|
61
|
+
end
|
62
|
+
|
63
|
+
desc 'Benchmark serializations'
|
64
|
+
task :serializations do
|
65
|
+
benchmark_serializer('application/hal+json', HAL_BENCHMARK_FILE )
|
66
|
+
benchmark_serializer('application/vnd.hale+json', HALE_BENCHMARK_FILE )
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'runs all benchmarks'
|
70
|
+
task :all do
|
71
|
+
Rake::Task['benchmark:deserializations'].invoke
|
72
|
+
Rake::Task['benchmark:serializations'].invoke
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
{
|
2
|
+
"_links":{
|
3
|
+
"self":{
|
4
|
+
"href":"http://example.org/api/user/ed"
|
5
|
+
},
|
6
|
+
"testrel":{
|
7
|
+
"href":"http://example.org/api/user/test",
|
8
|
+
"templated":true,
|
9
|
+
"type":"some-type",
|
10
|
+
"deprecation":"http://very-deprecated.com",
|
11
|
+
"name":"some-name",
|
12
|
+
"profile":"some-profile",
|
13
|
+
"title":"A Great Title",
|
14
|
+
"hreflang":"en-US"
|
15
|
+
},
|
16
|
+
"testrelarray":[
|
17
|
+
{
|
18
|
+
"href":"http://example.org/api/user/test1",
|
19
|
+
"templated":true,
|
20
|
+
"type":"some-type",
|
21
|
+
"deprecation":"http://very-deprecated.com",
|
22
|
+
"name":"some-name",
|
23
|
+
"profile":"some-profile",
|
24
|
+
"title":"A Great Title",
|
25
|
+
"hreflang":"en-US"
|
26
|
+
},
|
27
|
+
{
|
28
|
+
"href":"http://example.org/api/user/test2",
|
29
|
+
"templated":true,
|
30
|
+
"type":"some-type",
|
31
|
+
"deprecation":"http://very-deprecated.com",
|
32
|
+
"name":"some-name",
|
33
|
+
"profile":"some-profile",
|
34
|
+
"title":"A Great Title",
|
35
|
+
"hreflang":"en-US"
|
36
|
+
}
|
37
|
+
]
|
38
|
+
},
|
39
|
+
"some-property":123,
|
40
|
+
"_embedded":{
|
41
|
+
"embedded1":{
|
42
|
+
"_links":{
|
43
|
+
"self":{
|
44
|
+
"href":"http:/nice.com"
|
45
|
+
},
|
46
|
+
"testrel":{
|
47
|
+
"href":"http://example.org/api/user/test",
|
48
|
+
"templated":true,
|
49
|
+
"type":"some-type",
|
50
|
+
"deprecation":"http://very-deprecated.com",
|
51
|
+
"name":"some-name",
|
52
|
+
"profile":"some-profile",
|
53
|
+
"title":"A Great Title",
|
54
|
+
"hreflang":"en-US"
|
55
|
+
}
|
56
|
+
},
|
57
|
+
"some-property":1234
|
58
|
+
},
|
59
|
+
"embeddedarray":[
|
60
|
+
{
|
61
|
+
"_links":{
|
62
|
+
"self":{
|
63
|
+
"href":"http:/nice.com"
|
64
|
+
},
|
65
|
+
"testrel":{
|
66
|
+
"href":"http://example.org/api/user/test",
|
67
|
+
"templated":true,
|
68
|
+
"type":"some-type",
|
69
|
+
"deprecation":"http://very-deprecated.com",
|
70
|
+
"name":"some-name",
|
71
|
+
"profile":"some-profile",
|
72
|
+
"title":"A Great Title",
|
73
|
+
"hreflang":"en-US"
|
74
|
+
}
|
75
|
+
},
|
76
|
+
"some-property":1234
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"_links":{
|
80
|
+
"self":{
|
81
|
+
"href":"http:/nice.com"
|
82
|
+
},
|
83
|
+
"testrel":{
|
84
|
+
"href":"http://example.org/api/user/test",
|
85
|
+
"templated":true,
|
86
|
+
"type":"some-type",
|
87
|
+
"deprecation":"http://very-deprecated.com",
|
88
|
+
"name":"some-name",
|
89
|
+
"profile":"some-profile",
|
90
|
+
"title":"A Great Title",
|
91
|
+
"hreflang":"en-US"
|
92
|
+
}
|
93
|
+
},
|
94
|
+
"some-property":1234
|
95
|
+
}
|
96
|
+
]
|
97
|
+
}
|
98
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
|
5
|
+
def convert_to_representor(index, example)
|
6
|
+
representor = Representors::HaleDeserializer.new(example).to_representor
|
7
|
+
serialized_representor = Representors::Serialization::HaleSerializer.new(representor).to_media_type
|
8
|
+
if JSON.parse(serialized_representor) != JSON.parse(example)
|
9
|
+
puts example
|
10
|
+
puts "Example number #{index} can not be roundtriped!"
|
11
|
+
else
|
12
|
+
puts "Example number #{index} was roundtriped, HOORAY!"
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue JSON::ParserError
|
16
|
+
puts example
|
17
|
+
puts "Example number #{index} has a JSON error!"
|
18
|
+
rescue TypeError => e
|
19
|
+
puts example
|
20
|
+
puts "Example number #{index} breaks our code!"
|
21
|
+
puts e
|
22
|
+
end
|
23
|
+
|
24
|
+
#TODO: Test Hal spec also
|
25
|
+
desc "Rake tast to test the implementation against the specs"
|
26
|
+
task :test_specs do
|
27
|
+
|
28
|
+
hale_spec = ''
|
29
|
+
hale_spec << open('https://raw.githubusercontent.com/mdsol/hale/master/README.md').read
|
30
|
+
|
31
|
+
examples = hale_spec.scan(/```json(.*?)```/m).flatten
|
32
|
+
|
33
|
+
examples.each_with_index do |example, index|
|
34
|
+
convert_to_representor(index+1, example)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|