yaks 0.4.4 → 0.5.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/.gitignore +2 -0
- data/CHANGELOG.md +44 -3
- data/README.md +90 -33
- data/Rakefile +10 -0
- data/bench/bench.rb +0 -1
- data/bench/bench_1000.rb +60 -0
- data/lib/yaks/breaking_changes.rb +22 -0
- data/lib/yaks/config/dsl.rb +114 -27
- data/lib/yaks/config.rb +39 -54
- data/lib/yaks/default_policy.rb +32 -14
- data/lib/yaks/format/collection_json.rb +4 -4
- data/lib/yaks/format/hal.rb +20 -3
- data/lib/yaks/format/json_api.rb +3 -3
- data/lib/yaks/format.rb +54 -9
- data/lib/yaks/fp/callable.rb +9 -0
- data/lib/yaks/fp/hash_updatable.rb +2 -0
- data/lib/yaks/fp/updatable.rb +2 -0
- data/lib/yaks/fp.rb +8 -0
- data/lib/yaks/mapper/link.rb +2 -2
- data/lib/yaks/mapper.rb +6 -6
- data/lib/yaks/primitivize.rb +2 -2
- data/lib/yaks/resource/link.rb +0 -4
- data/lib/yaks/runner.rb +90 -0
- data/lib/yaks/util.rb +4 -0
- data/lib/yaks/version.rb +1 -1
- data/lib/yaks.rb +3 -0
- data/spec/acceptance/acceptance_spec.rb +6 -1
- data/spec/json/confucius.collection.json +5 -16
- data/spec/json/plant_collection.collection.json +32 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/deep_eql.rb +14 -7
- data/spec/support/pet_mapper.rb +0 -2
- data/spec/unit/yaks/collection_mapper_spec.rb +24 -2
- data/spec/unit/yaks/config/dsl_spec.rb +6 -10
- data/spec/unit/yaks/config_spec.rb +40 -99
- data/spec/unit/yaks/default_policy_spec.rb +20 -0
- data/spec/unit/yaks/format/collection_json_spec.rb +41 -0
- data/spec/unit/yaks/format/hal_spec.rb +38 -3
- data/spec/unit/yaks/format/json_api_spec.rb +2 -2
- data/spec/unit/yaks/format_spec.rb +28 -3
- data/spec/unit/yaks/fp/callable_spec.rb +13 -0
- data/spec/unit/yaks/mapper_spec.rb +226 -126
- data/spec/unit/yaks/resource/link_spec.rb +2 -3
- data/spec/unit/yaks/resource_spec.rb +15 -0
- data/spec/unit/yaks/runner_spec.rb +260 -0
- data/spec/unit/yaks/util_spec.rb +7 -1
- data/yaks.gemspec +4 -1
- metadata +72 -15
- /data/spec/json/{hal_plant_collection.json → plant_collection.hal.json} +0 -0
data/spec/support/deep_eql.rb
CHANGED
@@ -39,19 +39,18 @@ module Matchers
|
|
39
39
|
end.join
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def add_failure_message(message)
|
43
43
|
diffs << "at %s, %s" % [stack_as_jsonpath, message]
|
44
44
|
@result = false
|
45
45
|
end
|
46
46
|
|
47
47
|
def compare(key)
|
48
|
-
#require 'pry' ; binding.pry
|
49
48
|
push key
|
50
49
|
if target[key] != expectation[key]
|
51
50
|
if [Hash, Array].any?{|klz| target[key].is_a? klz }
|
52
51
|
recurse(target[key], expectation[key])
|
53
52
|
else
|
54
|
-
|
53
|
+
add_failure_message begin
|
55
54
|
if expectation[key].class == target[key].class
|
56
55
|
"expected #{expectation[key].inspect}, got #{target[key].inspect}"
|
57
56
|
else
|
@@ -72,13 +71,19 @@ module Matchers
|
|
72
71
|
when Hash
|
73
72
|
if target.is_a?(Hash)
|
74
73
|
if target.class != expectation.class # e.g. HashWithIndifferentAccess
|
75
|
-
|
74
|
+
add_failure_message("expected #{expectation.class}, got #{target.class}")
|
75
|
+
end
|
76
|
+
(expectation.keys - target.keys).each do |key|
|
77
|
+
add_failure_message "Expected key #{key}"
|
78
|
+
end
|
79
|
+
(target.keys - expectation.keys).each do |key|
|
80
|
+
add_failure_message "Unexpected key #{key}"
|
76
81
|
end
|
77
82
|
(target.keys | expectation.keys).each do |key|
|
78
83
|
compare key
|
79
84
|
end
|
80
85
|
else
|
81
|
-
|
86
|
+
add_failure_message("expected Hash got #{@target.inspect}")
|
82
87
|
end
|
83
88
|
|
84
89
|
when Array
|
@@ -87,12 +92,12 @@ module Matchers
|
|
87
92
|
compare idx
|
88
93
|
end
|
89
94
|
else
|
90
|
-
|
95
|
+
add_failure_message("expected Array got #{@target.inspect}")
|
91
96
|
end
|
92
97
|
|
93
98
|
else
|
94
99
|
if target != expectation
|
95
|
-
|
100
|
+
add_failure_message("expected #{expectation.inspect}, got #{@target.inspect}")
|
96
101
|
end
|
97
102
|
end
|
98
103
|
|
@@ -102,10 +107,12 @@ module Matchers
|
|
102
107
|
def failure_message_for_should
|
103
108
|
diffs.join("\n")
|
104
109
|
end
|
110
|
+
alias failure_message failure_message_for_should
|
105
111
|
|
106
112
|
def failure_message_for_should_not
|
107
113
|
"expected #{@target.inspect} not to be in deep_eql with #{@expectation.inspect}"
|
108
114
|
end
|
115
|
+
alias failure_message_when_negated failure_message_for_should_not
|
109
116
|
end
|
110
117
|
end
|
111
118
|
|
data/spec/support/pet_mapper.rb
CHANGED
@@ -10,8 +10,7 @@ RSpec.describe Yaks::CollectionMapper do
|
|
10
10
|
{ item_mapper: item_mapper,
|
11
11
|
policy: policy,
|
12
12
|
env: {},
|
13
|
-
mapper_stack: []
|
14
|
-
}
|
13
|
+
mapper_stack: [] }
|
15
14
|
}
|
16
15
|
|
17
16
|
let(:collection) { [] }
|
@@ -33,6 +32,10 @@ RSpec.describe Yaks::CollectionMapper do
|
|
33
32
|
let(:item_mapper) { PetMapper }
|
34
33
|
|
35
34
|
it 'should map the members' do
|
35
|
+
stub(policy).derive_mapper_from_object(any_args) do
|
36
|
+
raise ":item_mapper was specified explicitly, should not be derived from object"
|
37
|
+
end
|
38
|
+
|
36
39
|
expect(mapper.call(collection)).to eql Yaks::CollectionResource.new(
|
37
40
|
type: 'pet',
|
38
41
|
links: [],
|
@@ -140,4 +143,23 @@ RSpec.describe Yaks::CollectionMapper do
|
|
140
143
|
end
|
141
144
|
end
|
142
145
|
|
146
|
+
context 'with an empty collection' do
|
147
|
+
|
148
|
+
context 'without an item_mapper specified' do
|
149
|
+
let(:context) { Yaks::Util.slice_hash(super(), :policy, :env) }
|
150
|
+
|
151
|
+
it 'should use a rel of "collection"' do
|
152
|
+
expect(mapper.([]).collection_rel).to eq 'collection'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'with an item_mapper specified' do
|
157
|
+
let(:context) { Yaks::Util.slice_hash(super(), :policy, :env, :item_mapper) }
|
158
|
+
|
159
|
+
it 'should derive the collection rel from the item mapper' do
|
160
|
+
expect(mapper.([]).collection_rel).to eq 'rel:the_types'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
143
165
|
end
|
@@ -43,7 +43,7 @@ RSpec.describe Yaks::Config::DSL do
|
|
43
43
|
|
44
44
|
describe '#format_options' do
|
45
45
|
configure { format_options :hal, singular_link: [:self] }
|
46
|
-
specify { expect(yaks_config.format_options[:hal].
|
46
|
+
specify { expect(yaks_config.format_options[:hal]).to eq(singular_link: [:self]) }
|
47
47
|
end
|
48
48
|
|
49
49
|
describe '#default_format' do
|
@@ -61,6 +61,11 @@ RSpec.describe Yaks::Config::DSL do
|
|
61
61
|
specify { expect(yaks_config.policy_options[:rel_template]).to eql 'rels:{rel}' }
|
62
62
|
end
|
63
63
|
|
64
|
+
describe '#json_serializer' do
|
65
|
+
configure { json_serializer { |i| "foo #{i}" } }
|
66
|
+
specify { expect(yaks_config.serializers[:json].call(7)).to eql 'foo 7' }
|
67
|
+
end
|
68
|
+
|
64
69
|
describe '#mapper_namespace' do
|
65
70
|
configure { mapper_namespace RSpec }
|
66
71
|
specify { expect(yaks_config.policy_options[:namespace]).to eql RSpec }
|
@@ -79,13 +84,4 @@ RSpec.describe Yaks::Config::DSL do
|
|
79
84
|
specify { expect(yaks_config.primitivize.call({:abc => Foo.new('hello')})).to eql 'abc' => 'hello' }
|
80
85
|
end
|
81
86
|
|
82
|
-
describe '#after' do
|
83
|
-
configure do
|
84
|
-
after {|x| x + 1}
|
85
|
-
after {|x| x + 10}
|
86
|
-
end
|
87
|
-
it 'should register the block' do
|
88
|
-
expect(yaks_config.steps.inject(0) {|memo, step| step.call(memo)}).to be 11
|
89
|
-
end
|
90
|
-
end
|
91
87
|
end
|
@@ -7,57 +7,63 @@ RSpec.describe Yaks::Config do
|
|
7
7
|
subject(:config) { described_class.new(&blk) }
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
default_format :json_api
|
10
|
+
describe '#initialize' do
|
11
|
+
context 'defaults' do
|
12
|
+
configure {}
|
13
|
+
|
14
|
+
its(:default_format) { should equal :hal }
|
15
|
+
its(:policy_class) { should < Yaks::DefaultPolicy }
|
16
|
+
its(:primitivize) { should be_a Yaks::Primitivize }
|
17
|
+
its(:serializers) { should eql({}) }
|
18
|
+
its(:hooks) { should eql([]) }
|
19
|
+
|
20
|
+
it 'should have empty format options' do
|
21
|
+
expect(config.format_options[:hal]).to eql({})
|
22
|
+
end
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
|
-
|
25
|
+
context 'with a default format' do
|
26
|
+
configure do
|
27
|
+
default_format :json_api
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
MyPolicy = Struct.new(:options)
|
31
|
-
configure do
|
32
|
-
policy MyPolicy
|
30
|
+
its(:default_format) { should equal :json_api }
|
33
31
|
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
context 'with a custom policy class' do
|
34
|
+
MyPolicy = Struct.new(:options)
|
35
|
+
configure do
|
36
|
+
policy MyPolicy
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
rel_template 'http://rel/foo'
|
39
|
+
its(:policy_class) { should equal MyPolicy }
|
40
|
+
its(:policy) { should be_a MyPolicy }
|
42
41
|
end
|
43
42
|
|
44
|
-
|
45
|
-
|
43
|
+
context 'with a rel template' do
|
44
|
+
configure do
|
45
|
+
rel_template 'http://rel/foo'
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
configure do
|
49
|
-
format_options :hal, plural_links: [:self, :profile]
|
48
|
+
its(:policy_options) { should eql(rel_template: 'http://rel/foo') }
|
50
49
|
end
|
51
50
|
|
52
|
-
|
53
|
-
|
51
|
+
context 'with format options' do
|
52
|
+
configure do
|
53
|
+
format_options :hal, plural_links: [:self, :profile]
|
54
|
+
end
|
55
|
+
|
56
|
+
specify do
|
57
|
+
expect(config.format_options[:hal]).to eql(plural_links: [:self, :profile])
|
58
|
+
end
|
54
59
|
end
|
55
60
|
end
|
56
61
|
|
57
|
-
describe '#
|
62
|
+
describe '#call' do
|
58
63
|
configure do
|
59
64
|
rel_template 'http://api.mysuperfriends.com/{rel}'
|
60
65
|
format_options :hal, plural_links: [:copyright]
|
66
|
+
skip :serialize
|
61
67
|
end
|
62
68
|
|
63
69
|
specify do
|
@@ -65,69 +71,4 @@ RSpec.describe Yaks::Config do
|
|
65
71
|
end
|
66
72
|
end
|
67
73
|
|
68
|
-
describe '#mapper_namespace' do
|
69
|
-
module MyMappers
|
70
|
-
class PetMapper < Yaks::Mapper
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
configure do
|
75
|
-
mapper_namespace MyMappers
|
76
|
-
end
|
77
|
-
|
78
|
-
specify do
|
79
|
-
expect(config.policy.derive_mapper_from_object(boingboing)).to eql(MyMappers::PetMapper)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
describe '#map_to_primitive' do
|
84
|
-
class TheMapper < Yaks::Mapper
|
85
|
-
attributes :a_date
|
86
|
-
end
|
87
|
-
|
88
|
-
TheModel = Struct.new(:a_date)
|
89
|
-
|
90
|
-
configure do
|
91
|
-
map_to_primitive Date do |object|
|
92
|
-
object.iso8601
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
let(:model) {
|
97
|
-
TheModel.new(Date.new(2014, 5, 6))
|
98
|
-
}
|
99
|
-
|
100
|
-
specify do
|
101
|
-
expect(config.serialize(model, mapper: TheMapper)).to eq({"a_date"=>"2014-05-06"})
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
describe '#format_class' do
|
106
|
-
configure do
|
107
|
-
default_format :collection_json
|
108
|
-
end
|
109
|
-
|
110
|
-
let(:rack_env) {
|
111
|
-
{ 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json' }
|
112
|
-
}
|
113
|
-
|
114
|
-
it 'should fall back to the default when no HTTP_ACCEPT key is present' do
|
115
|
-
expect(config.format_class({}, {})).to equal Yaks::Format::CollectionJson
|
116
|
-
end
|
117
|
-
|
118
|
-
it 'should detect format based on accept header' do
|
119
|
-
rack_env = { 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json' }
|
120
|
-
expect(config.format_class({}, rack_env)).to equal Yaks::Format::JsonApi
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'should know to pick the best match' do
|
124
|
-
rack_env = { 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json;q=0.7' }
|
125
|
-
expect(config.format_class({}, rack_env)).to equal Yaks::Format::Hal
|
126
|
-
end
|
127
|
-
|
128
|
-
it 'should fall back to the default when no mime type is recognized' do
|
129
|
-
rack_env = { 'HTTP_ACCEPT' => 'text/html, application/json' }
|
130
|
-
expect(config.format_class({}, rack_env)).to equal Yaks::Format::CollectionJson
|
131
|
-
end
|
132
|
-
end
|
133
74
|
end
|
@@ -26,6 +26,20 @@ RSpec.describe Yaks::DefaultPolicy do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
describe '#derive_type_from_collection' do
|
30
|
+
specify do
|
31
|
+
expect(
|
32
|
+
policy.derive_type_from_collection([Soy.new])
|
33
|
+
).to eql 'soy'
|
34
|
+
end
|
35
|
+
|
36
|
+
specify do
|
37
|
+
expect(
|
38
|
+
policy.derive_type_from_collection([])
|
39
|
+
).to be_nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
29
43
|
describe '#derive_mapper_from_association' do
|
30
44
|
let(:options) { { namespace: Namespace } }
|
31
45
|
|
@@ -47,4 +61,10 @@ RSpec.describe Yaks::DefaultPolicy do
|
|
47
61
|
end
|
48
62
|
end
|
49
63
|
|
64
|
+
describe '#serializer_for_format' do
|
65
|
+
specify {
|
66
|
+
expect(policy.serializer_for_format(Yaks::Format::JsonAPI).call('foo' => [1,2])).to eql "{\n \"foo\": [\n 1,\n 2\n ]\n}"
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
50
70
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::Format::CollectionJson do
|
4
|
+
context 'with the plant collection resource' do
|
5
|
+
include_context 'plant collection resource'
|
6
|
+
|
7
|
+
subject { Yaks::Primitivize.create.call(described_class.new.call(resource)) }
|
8
|
+
|
9
|
+
it { should deep_eql(load_json_fixture('plant_collection.collection')) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with a link without title' do
|
13
|
+
let(:resource) {
|
14
|
+
Yaks::Resource.new(
|
15
|
+
attributes: {foo: 'fooval', bar: 'barval'},
|
16
|
+
links: [Yaks::Resource::Link.new('the_rel', 'the_uri', {})]
|
17
|
+
)
|
18
|
+
}
|
19
|
+
|
20
|
+
subject {
|
21
|
+
Yaks::Primitivize.create.call(described_class.new.call(resource))
|
22
|
+
}
|
23
|
+
|
24
|
+
it 'should not render a name' do
|
25
|
+
should deep_eql(
|
26
|
+
"collection" => {
|
27
|
+
"version" => "1.0",
|
28
|
+
"items" => [
|
29
|
+
{
|
30
|
+
"data" => [
|
31
|
+
{ "name"=>"foo", "value"=>"fooval" },
|
32
|
+
{ "name"=>"bar", "value"=>"barval" }
|
33
|
+
],
|
34
|
+
"links" => [{"rel"=>"the_rel", "href"=>"the_uri"}]
|
35
|
+
}
|
36
|
+
]
|
37
|
+
}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,9 +1,44 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Yaks::Format::Hal do
|
4
|
-
|
4
|
+
context 'with the plant collection resource' do
|
5
|
+
include_context 'plant collection resource'
|
5
6
|
|
6
|
-
|
7
|
+
subject { Yaks::Primitivize.create.call(described_class.new.call(resource)) }
|
7
8
|
|
8
|
-
|
9
|
+
it { should deep_eql(load_json_fixture('plant_collection.hal')) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with multiple links on the same rel' do
|
13
|
+
let(:format) {
|
14
|
+
described_class.new(:plural_links => 'my_plural_rel')
|
15
|
+
}
|
16
|
+
|
17
|
+
let(:resource) {
|
18
|
+
Yaks::Resource.new(
|
19
|
+
attributes: {foo: 'fooval', bar: 'barval'},
|
20
|
+
links: [
|
21
|
+
Yaks::Resource::Link.new('my_plural_rel', 'the_uri1', {}),
|
22
|
+
Yaks::Resource::Link.new('my_plural_rel', 'the_uri2', {})
|
23
|
+
]
|
24
|
+
)
|
25
|
+
}
|
26
|
+
|
27
|
+
subject {
|
28
|
+
Yaks::Primitivize.create.call(format.call(resource))
|
29
|
+
}
|
30
|
+
|
31
|
+
it 'should render both links' do
|
32
|
+
should deep_eql(
|
33
|
+
'foo' => 'fooval',
|
34
|
+
'bar' => 'barval',
|
35
|
+
'_links' => {
|
36
|
+
"my_plural_rel" => [
|
37
|
+
{"href"=>"the_uri1"},
|
38
|
+
{"href"=>"the_uri2"}
|
39
|
+
]
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
9
44
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
# Mainly tested through the acceptance tests, here covering a few specific edge cases
|
4
|
-
RSpec.describe Yaks::Format::
|
5
|
-
let(:format) { Yaks::Format::
|
4
|
+
RSpec.describe Yaks::Format::JsonAPI do
|
5
|
+
let(:format) { Yaks::Format::JsonAPI.new }
|
6
6
|
|
7
7
|
context 'with no subresources' do
|
8
8
|
let(:resource) { Yaks::Resource.new(type: 'wizard', attributes: {foo: :bar}) }
|
@@ -2,11 +2,36 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Yaks::Format do
|
4
4
|
describe '.by_name' do
|
5
|
-
specify
|
6
|
-
|
5
|
+
specify do
|
6
|
+
expect(Yaks::Format.by_name(:hal)).to eql Yaks::Format::Hal
|
7
|
+
end
|
8
|
+
specify do
|
9
|
+
expect(Yaks::Format.by_name(:json_api)).to eql Yaks::Format::JsonAPI
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
describe '.by_mime_type' do
|
10
|
-
specify
|
14
|
+
specify do
|
15
|
+
expect(Yaks::Format.by_mime_type('application/hal+json')).to eql Yaks::Format::Hal
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.by_accept_header' do
|
20
|
+
specify do
|
21
|
+
expect(Yaks::Format.by_accept_header('application/hal+json;q=0.8, application/vnd.api+json')).to eql Yaks::Format::JsonAPI
|
22
|
+
end
|
23
|
+
specify do
|
24
|
+
expect(Yaks::Format.by_accept_header('application/hal+json;q=0.8, application/vnd.api+json;q=0.7')).to eql Yaks::Format::Hal
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '.mime_types' do
|
29
|
+
specify do
|
30
|
+
expect(Yaks::Format.mime_types).to eql(
|
31
|
+
collection_json: "application/vnd.collection+json",
|
32
|
+
hal: "application/hal+json",
|
33
|
+
json_api: "application/vnd.api+json"
|
34
|
+
)
|
35
|
+
end
|
11
36
|
end
|
12
37
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Yaks::FP::Callable do
|
4
|
+
it 'should delegate to_proc to method(:call)' do
|
5
|
+
obj = Class.new do
|
6
|
+
include Yaks::FP::Callable
|
7
|
+
|
8
|
+
def call(x) ; x * x ; end
|
9
|
+
end.new
|
10
|
+
|
11
|
+
expect([1,2,3].map(&obj)).to eql [1,4,9]
|
12
|
+
end
|
13
|
+
end
|