representable 2.4.0.rc3 → 2.4.0.rc4
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/CHANGES.md +7 -2
- data/Rakefile +6 -0
- data/lib/representable.rb +21 -17
- data/lib/representable/binding.rb +3 -2
- data/lib/representable/definition.rb +1 -1
- data/lib/representable/deprecations.rb +31 -5
- data/lib/representable/deserializer.rb +24 -34
- data/lib/representable/hash/collection.rb +9 -2
- data/lib/representable/hash_methods.rb +2 -2
- data/lib/representable/parse_strategies.rb +6 -7
- data/lib/representable/pipeline.rb +12 -1
- data/lib/representable/pipeline_factories.rb +9 -2
- data/lib/representable/serializer.rb +3 -3
- data/lib/representable/version.rb +1 -1
- data/test-with-deprecations/as_test.rb +65 -0
- data/test-with-deprecations/benchmarking.rb +83 -0
- data/test-with-deprecations/binding_test.rb +46 -0
- data/test-with-deprecations/blaaaaaaaa_test.rb +69 -0
- data/test-with-deprecations/cached_test.rb +147 -0
- data/test-with-deprecations/class_test.rb +119 -0
- data/test-with-deprecations/coercion_test.rb +52 -0
- data/test-with-deprecations/config/inherit_test.rb +135 -0
- data/test-with-deprecations/config_test.rb +122 -0
- data/test-with-deprecations/decorator_scope_test.rb +28 -0
- data/test-with-deprecations/decorator_test.rb +96 -0
- data/test-with-deprecations/default_test.rb +34 -0
- data/test-with-deprecations/defaults_options_test.rb +93 -0
- data/test-with-deprecations/definition_test.rb +264 -0
- data/test-with-deprecations/example.rb +310 -0
- data/test-with-deprecations/examples/object.rb +31 -0
- data/test-with-deprecations/exec_context_test.rb +93 -0
- data/test-with-deprecations/features_test.rb +70 -0
- data/test-with-deprecations/filter_test.rb +57 -0
- data/test-with-deprecations/for_collection_test.rb +74 -0
- data/test-with-deprecations/generic_test.rb +116 -0
- data/test-with-deprecations/getter_setter_test.rb +21 -0
- data/test-with-deprecations/hash_bindings_test.rb +87 -0
- data/test-with-deprecations/hash_test.rb +160 -0
- data/test-with-deprecations/heritage_test.rb +62 -0
- data/test-with-deprecations/if_test.rb +79 -0
- data/test-with-deprecations/include_exclude_test.rb +88 -0
- data/test-with-deprecations/inherit_test.rb +159 -0
- data/test-with-deprecations/inline_test.rb +272 -0
- data/test-with-deprecations/instance_test.rb +266 -0
- data/test-with-deprecations/is_representable_test.rb +77 -0
- data/test-with-deprecations/json_test.rb +355 -0
- data/test-with-deprecations/lonely_test.rb +239 -0
- data/test-with-deprecations/mongoid_test.rb +31 -0
- data/test-with-deprecations/nested_test.rb +115 -0
- data/test-with-deprecations/object_test.rb +60 -0
- data/{test/---deserialize-pipeline_test.rb → test-with-deprecations/parse_pipeline_test.rb} +29 -2
- data/test-with-deprecations/parse_strategy_test.rb +279 -0
- data/{test → test-with-deprecations}/pass_options_test.rb +0 -0
- data/test-with-deprecations/pipeline_test.rb +277 -0
- data/test-with-deprecations/populator_test.rb +105 -0
- data/test-with-deprecations/prepare_test.rb +67 -0
- data/test-with-deprecations/private_options_test.rb +18 -0
- data/test-with-deprecations/reader_writer_test.rb +19 -0
- data/test-with-deprecations/realistic_benchmark.rb +115 -0
- data/test-with-deprecations/render_nil_test.rb +21 -0
- data/test-with-deprecations/represent_test.rb +88 -0
- data/test-with-deprecations/representable_test.rb +511 -0
- data/test-with-deprecations/schema_test.rb +148 -0
- data/test-with-deprecations/serialize_deserialize_test.rb +33 -0
- data/test-with-deprecations/skip_test.rb +81 -0
- data/test-with-deprecations/stringify_hash_test.rb +41 -0
- data/test-with-deprecations/test_helper.rb +135 -0
- data/test-with-deprecations/test_helper_test.rb +25 -0
- data/test-with-deprecations/uncategorized_test.rb +67 -0
- data/test-with-deprecations/user_options_test.rb +15 -0
- data/test-with-deprecations/wrap_test.rb +152 -0
- data/test-with-deprecations/xml_bindings_test.rb +62 -0
- data/test-with-deprecations/xml_test.rb +503 -0
- data/test-with-deprecations/yaml_test.rb +162 -0
- data/test/as_test.rb +3 -3
- data/test/cached_test.rb +2 -2
- data/test/class_test.rb +5 -5
- data/test/exec_context_test.rb +2 -2
- data/test/filter_test.rb +1 -1
- data/test/getter_setter_test.rb +4 -4
- data/test/if_test.rb +2 -2
- data/test/include_exclude_test.rb +88 -0
- data/test/instance_test.rb +15 -15
- data/test/lonely_test.rb +18 -2
- data/test/object_test.rb +4 -4
- data/test/parse_pipeline_test.rb +64 -0
- data/test/parse_strategy_test.rb +3 -3
- data/test/pipeline_test.rb +8 -12
- data/test/prepare_test.rb +2 -3
- data/test/reader_writer_test.rb +3 -3
- data/test/representable_test.rb +12 -48
- data/test/serialize_deserialize_test.rb +9 -9
- data/test/skip_test.rb +11 -11
- data/test/test_helper.rb +2 -0
- data/test/uncategorized_test.rb +10 -10
- data/test/user_options_test.rb +15 -0
- data/test/wrap_test.rb +1 -1
- metadata +65 -4
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class InheritTest < MiniTest::Spec
|
4
|
+
module SongRepresenter # it's important to have a global module so we can test if stuff gets overridden in the original module.
|
5
|
+
include Representable::Hash
|
6
|
+
property :name, :as => :title do
|
7
|
+
property :string, :as => :str
|
8
|
+
end
|
9
|
+
|
10
|
+
property :track, :as => :no
|
11
|
+
end
|
12
|
+
|
13
|
+
let (:song) { Song.new(Struct.new(:string).new("Roxanne"), 1) }
|
14
|
+
|
15
|
+
describe ":inherit plain property" do
|
16
|
+
representer! do
|
17
|
+
include SongRepresenter
|
18
|
+
|
19
|
+
property :track, :inherit => true, :getter => lambda { |*| "n/a" }
|
20
|
+
end
|
21
|
+
|
22
|
+
it { SongRepresenter.prepare(song).to_hash.must_equal({"title"=>{"str"=>"Roxanne"}, "no"=>1}) }
|
23
|
+
it { representer.prepare(song).to_hash.must_equal({"title"=>{"str"=>"Roxanne"}, "no"=>"n/a"}) } # as: inherited.
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ":inherit with empty inline representer" do
|
27
|
+
representer! do
|
28
|
+
include SongRepresenter
|
29
|
+
|
30
|
+
property :name, :inherit => true do # inherit as: title
|
31
|
+
# that doesn't make sense.
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it { SongRepresenter.prepare(Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
|
36
|
+
# the block doesn't override the inline representer.
|
37
|
+
it { representer.prepare( Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ":inherit with overriding inline representer" do
|
41
|
+
representer! do
|
42
|
+
include SongRepresenter
|
43
|
+
|
44
|
+
puts "passing block"
|
45
|
+
property :name, :inherit => true do # inherit as: title
|
46
|
+
property :string, :as => :s
|
47
|
+
property :length
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it { representer.prepare( Song.new(Struct.new(:string, :length).new("Believe It", 10), 1)).to_hash.must_equal({"title"=>{"s"=>"Believe It","length"=>10}, "no"=>1}) }
|
52
|
+
end
|
53
|
+
|
54
|
+
describe ":inherit with empty inline and options" do
|
55
|
+
representer! do
|
56
|
+
include SongRepresenter
|
57
|
+
|
58
|
+
property :name, inherit: true, as: :name do # inherit module, only.
|
59
|
+
# that doesn't make sense. but it should simply inherit the old nested properties.
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it { SongRepresenter.prepare(Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
|
64
|
+
it { representer.prepare( Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"name"=>{"str"=>"Believe It"}, "no"=>1}) }
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ":inherit with inline without block but options" do
|
68
|
+
representer! do
|
69
|
+
include SongRepresenter
|
70
|
+
|
71
|
+
property :name, :inherit => true, :as => :name # FIXME: add :getter or something else dynamic since this is double-wrapped.
|
72
|
+
end
|
73
|
+
|
74
|
+
it { SongRepresenter.prepare(Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"title"=>{"str"=>"Believe It"}, "no"=>1}) }
|
75
|
+
it { representer.prepare( Song.new(Struct.new(:string).new("Believe It"), 1)).to_hash.must_equal({"name"=>{"str"=>"Believe It"}, "no"=>1}) }
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
# no :inherit
|
81
|
+
describe "overwriting without :inherit" do
|
82
|
+
representer! do
|
83
|
+
include SongRepresenter
|
84
|
+
|
85
|
+
property :track, :representable => true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "replaces inherited property" do
|
89
|
+
representer.representable_attrs.size.must_equal 2
|
90
|
+
|
91
|
+
definition = representer.representable_attrs.get(:track) # TODO: find a better way to assert Definition identity.
|
92
|
+
# definition.keys.size.must_equal 2
|
93
|
+
definition[:representable]. must_equal true
|
94
|
+
definition.name.must_equal "track" # was "no".
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# decorator
|
100
|
+
describe ":inherit with decorator" do
|
101
|
+
representer!(:decorator => true) do
|
102
|
+
property :hit do
|
103
|
+
property :title, exec_context: :decorator
|
104
|
+
|
105
|
+
def title
|
106
|
+
"Cheap Transistor Radio"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
let (:inheriting) {
|
112
|
+
class InheritingDecorator < representer
|
113
|
+
include Representable::Debug
|
114
|
+
property :hit, :inherit => true do
|
115
|
+
include Representable::Debug
|
116
|
+
property :length
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
}
|
121
|
+
|
122
|
+
it { representer.new(OpenStruct.new(hit: OpenStruct.new(title: "I WILL BE OVERRIDDEN", :length => "2:59"))).to_hash.must_equal(
|
123
|
+
{"hit"=>{"title"=>"Cheap Transistor Radio"}}) }
|
124
|
+
|
125
|
+
# inheriting decorator inherits inline representer class (InlineRepresenter#title).
|
126
|
+
# inheriting decorator adds :length.
|
127
|
+
it { inheriting.new(OpenStruct.new(:hit => OpenStruct.new(:title => "Hole In Your Soul", :length => "2:59"))).to_hash.must_equal(
|
128
|
+
{"hit"=>{"title"=>"Cheap Transistor Radio", "length"=>"2:59"}}) }
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
# :inherit when property doesn't exist, yet.
|
133
|
+
describe ":inherit without inheritable property" do
|
134
|
+
representer! do
|
135
|
+
property :name, :inherit => true
|
136
|
+
end
|
137
|
+
|
138
|
+
it { representer.prepare(Song.new("The Beginning")).to_hash.must_equal({"name"=>"The Beginning"})}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
# class InheritancingTest < MiniTest::Spec
|
144
|
+
# class SongDecorator < Representable::Decorator
|
145
|
+
# include Representable::Hash
|
146
|
+
# property :album do
|
147
|
+
# # does have Hash.
|
148
|
+
# property :title
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
|
152
|
+
# class JsonSongDecorator < SongDecorator
|
153
|
+
# include Representable::XML
|
154
|
+
# end
|
155
|
+
|
156
|
+
# it do
|
157
|
+
# puts JsonSongDecorator.new(OpenStruct.new(:album => OpenStruct.new(:title => "Erotic Cakes", :tracks => nil))).to_xml
|
158
|
+
# end
|
159
|
+
# end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class InlineTest < MiniTest::Spec
|
4
|
+
let (:song) { Song.new("Alive") }
|
5
|
+
let (:request) { representer.prepare(OpenStruct.new(:song => song)) }
|
6
|
+
|
7
|
+
{
|
8
|
+
:hash => [Representable::Hash, {"song"=>{"name"=>"Alive"}}, {"song"=>{"name"=>"You've Taken Everything"}}],
|
9
|
+
:json => [Representable::JSON, "{\"song\":{\"name\":\"Alive\"}}", "{\"song\":{\"name\":\"You've Taken Everything\"}}"],
|
10
|
+
:xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song>/open_struct>"],
|
11
|
+
:yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
|
12
|
+
}.each do |format, cfg|
|
13
|
+
mod, output, input = cfg
|
14
|
+
|
15
|
+
describe "[#{format}] with :class" do
|
16
|
+
representer!(:module => mod) do
|
17
|
+
property :song, :class => Song do
|
18
|
+
property :name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
let (:format) { format }
|
23
|
+
|
24
|
+
it { render(request).must_equal_document output }
|
25
|
+
it { parse(request, input).song.name.must_equal "You've Taken Everything"}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
{
|
30
|
+
:hash => [Representable::Hash, {"songs"=>[{"name"=>"Alive"}]}, {"songs"=>[{"name"=>"You've Taken Everything"}]}],
|
31
|
+
:json => [Representable::JSON, "{\"songs\":[{\"name\":\"Alive\"}]}", "{\"songs\":[{\"name\":\"You've Taken Everything\"}]}"],
|
32
|
+
:xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song></open_struct>", { :as => :song }],
|
33
|
+
:yaml => [Representable::YAML, "---\nsongs:\n- name: Alive\n", "---\nsongs:\n- name: You've Taken Everything\n"],
|
34
|
+
}.each do |format, cfg|
|
35
|
+
mod, output, input, collection_options = cfg
|
36
|
+
collection_options ||= {}
|
37
|
+
|
38
|
+
describe "[#{format}] collection with :class" do
|
39
|
+
let (:request) { representer.prepare(OpenStruct.new(:songs => [song])) }
|
40
|
+
|
41
|
+
representer!(:module => mod) do
|
42
|
+
collection :songs, collection_options.merge(:class => Song) do
|
43
|
+
property :name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
let (:format) { format } # FIXME: why do we have to define this?
|
48
|
+
|
49
|
+
it { render(request).must_equal_document output }
|
50
|
+
it { parse(request, input).songs.first.name.must_equal "You've Taken Everything"}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "without :class" do
|
55
|
+
representer! do
|
56
|
+
property :song do
|
57
|
+
property :name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it { request.to_hash.must_equal({"song"=>{"name"=>"Alive"}}) }
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
for_formats(
|
66
|
+
:hash => [Representable::Hash, {}],
|
67
|
+
# :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song>/open_struct>"],
|
68
|
+
# :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
|
69
|
+
) do |format, mod, input|
|
70
|
+
|
71
|
+
describe "parsing [#{format}] where nested property missing" do
|
72
|
+
representer!(:module => mod) do
|
73
|
+
property :song do
|
74
|
+
property :name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "doesn't change represented object" do
|
79
|
+
request.send("from_#{format}", input).song.must_equal song
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
describe "inheriting from outer representer" do
|
86
|
+
let (:request) { Struct.new(:song, :requester).new(song, "Josephine") }
|
87
|
+
|
88
|
+
[false, true].each do |is_decorator| # test for module and decorator.
|
89
|
+
representer!(:decorator => is_decorator) do
|
90
|
+
property :requester
|
91
|
+
|
92
|
+
property :song, :class => Song do
|
93
|
+
property :name
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
let (:decorator) { representer.prepare(request) }
|
98
|
+
|
99
|
+
it { decorator.to_hash.must_equal({"requester"=>"Josephine", "song"=>{"name"=>"Alive"}}) }
|
100
|
+
it { decorator.from_hash({"song"=>{"name"=>"You've Taken Everything"}}).song.name.must_equal "You've Taken Everything"}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "object pollution" do
|
105
|
+
representer!(:decorator => true) do
|
106
|
+
property :song do
|
107
|
+
property :name
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "uses an inline decorator and doesn't alter represented" do
|
112
|
+
representer.prepare(Struct.new(:song).new(song)).to_hash
|
113
|
+
song.wont_be_kind_of Representable
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# TODO: should be in extend:/decorator: test.
|
118
|
+
# FIXME: this tests :getter{represented}+:extend - represented gets extended twice and the inline decorator overrides original config.
|
119
|
+
# for_formats(
|
120
|
+
# :hash => [Representable::Hash, {"album" => {"artist" => {"label"=>"Epitaph"}}}],
|
121
|
+
# # :xml => [Representable::XML, "<open_struct></open_struct>"],
|
122
|
+
# #:yaml => [Representable::YAML, "---\nlabel:\n label: Epitaph\n owner: Brett Gurewitz\n"]
|
123
|
+
# ) do |format, mod, output, input|
|
124
|
+
|
125
|
+
# module ArtistRepresenter
|
126
|
+
# include Representable::JSON
|
127
|
+
# property :label
|
128
|
+
# end
|
129
|
+
|
130
|
+
# describe ":getter with inline representer" do
|
131
|
+
# let (:format) { format }
|
132
|
+
|
133
|
+
# representer!(:module => mod) do
|
134
|
+
# self.representation_wrap = :album
|
135
|
+
|
136
|
+
# property :artist, :getter => lambda { |args| represented }, :extend => ArtistRepresenter
|
137
|
+
# end
|
138
|
+
|
139
|
+
# let (:album) { OpenStruct.new(:label => "Epitaph").extend(representer) }
|
140
|
+
|
141
|
+
# it "renders nested Album-properties in separate section" do
|
142
|
+
# render(album).must_equal_document output
|
143
|
+
# end
|
144
|
+
# end
|
145
|
+
# end
|
146
|
+
|
147
|
+
|
148
|
+
for_formats({
|
149
|
+
:hash => [Representable::Hash, {"album" => {"artist" => {"label"=>"Epitaph"}}}],
|
150
|
+
# :xml => [Representable::XML, "<open_struct></open_struct>"],
|
151
|
+
#:yaml => [Representable::YAML, "---\nlabel:\n label: Epitaph\n owner: Brett Gurewitz\n"]
|
152
|
+
}) do |format, mod, output, input|
|
153
|
+
|
154
|
+
class ArtistDecorator < Representable::Decorator
|
155
|
+
include Representable::JSON
|
156
|
+
property :label
|
157
|
+
end
|
158
|
+
|
159
|
+
describe ":getter with :decorator" do
|
160
|
+
let (:format) { format }
|
161
|
+
|
162
|
+
representer!(:module => mod) do
|
163
|
+
self.representation_wrap = "album"
|
164
|
+
|
165
|
+
property :artist, :getter => lambda { |args| represented }, :decorator => ArtistDecorator
|
166
|
+
end
|
167
|
+
|
168
|
+
let (:album) { OpenStruct.new(:label => "Epitaph").extend(representer) }
|
169
|
+
|
170
|
+
it "renders nested Album-properties in separate section" do
|
171
|
+
render(album).must_equal_document output
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
# test helper methods within inline representer
|
178
|
+
for_formats({
|
179
|
+
:hash => [Representable::Hash, {"song"=>{"name"=>"ALIVE"}}],
|
180
|
+
:xml => [Representable::XML, "<request>\n <song>\n <name>ALIVE</name>\n </song>\n</request>"],
|
181
|
+
:yaml => [Representable::YAML, "---\nsong:\n name: ALIVE\n"],
|
182
|
+
}) do |format, mod, output|
|
183
|
+
|
184
|
+
describe "helper method within inline representer [#{format}]" do
|
185
|
+
let (:format) { format }
|
186
|
+
|
187
|
+
representer!(:module => mod, :decorator => true) do
|
188
|
+
self.representation_wrap = :request if format == :xml
|
189
|
+
|
190
|
+
property :requester
|
191
|
+
property :song do
|
192
|
+
property :name, :exec_context => :decorator
|
193
|
+
|
194
|
+
define_method :name do
|
195
|
+
represented.name.upcase
|
196
|
+
end
|
197
|
+
|
198
|
+
self.representation_wrap = :song if format == :xml
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
let (:request) { representer.prepare(OpenStruct.new(:song => Song.new("Alive"))) }
|
203
|
+
|
204
|
+
it do
|
205
|
+
render(request).must_equal_document output
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
describe "include module in inline representers" do
|
212
|
+
representer! do
|
213
|
+
extension = Module.new do
|
214
|
+
include Representable::Hash
|
215
|
+
property :title
|
216
|
+
end
|
217
|
+
|
218
|
+
property :song do
|
219
|
+
include extension
|
220
|
+
property :artist
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
it do OpenStruct.new(:song => OpenStruct.new(:title => "The Fever And The Sound", :artist => "Strung Out")).extend(representer).
|
225
|
+
to_hash.
|
226
|
+
must_equal({"song"=>{"artist"=>"Strung Out", "title"=>"The Fever And The Sound"}})
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
# define method in inline representer
|
232
|
+
describe "define method in inline representer" do
|
233
|
+
Mod = Module.new do
|
234
|
+
include Representable::Hash
|
235
|
+
|
236
|
+
def song
|
237
|
+
"Object.new"
|
238
|
+
end
|
239
|
+
|
240
|
+
property :song do
|
241
|
+
property :duration
|
242
|
+
|
243
|
+
def duration
|
244
|
+
"6:53"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
it { Object.new.extend(Mod).to_hash.must_equal("song"=>{"duration"=>"6:53"}) }
|
250
|
+
end
|
251
|
+
|
252
|
+
# define method inline with Decorator
|
253
|
+
describe "define method inline with Decorator" do
|
254
|
+
dec = Class.new(Representable::Decorator) do
|
255
|
+
include Representable::Hash
|
256
|
+
|
257
|
+
def song
|
258
|
+
"Object.new"
|
259
|
+
end
|
260
|
+
|
261
|
+
property :song, :exec_context => :decorator do
|
262
|
+
property :duration, :exec_context => :decorator
|
263
|
+
|
264
|
+
def duration
|
265
|
+
"6:53"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
it { dec.new(Object.new).to_hash.must_equal("song"=>{"duration"=>"6:53"}) }
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class InstanceTest < BaseTest
|
4
|
+
Song = Struct.new(:id, :title)
|
5
|
+
Song.class_eval do
|
6
|
+
def self.find(id)
|
7
|
+
new(id, "Invincible")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "lambda { fragment } (new way of class: lambda { nil })" do
|
12
|
+
representer! do
|
13
|
+
property :title, :instance => lambda { |fragment, args| fragment }
|
14
|
+
end
|
15
|
+
|
16
|
+
it "skips creating new instance" do
|
17
|
+
object = Object.new
|
18
|
+
object.instance_eval do
|
19
|
+
def from_hash(hash, *args)
|
20
|
+
hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
song = OpenStruct.new.extend(representer).from_hash(hash = {"title" => object})
|
25
|
+
song.title.must_equal object
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# TODO: use *args in from_hash.
|
31
|
+
# DISCUSS: do we need parse_strategy?
|
32
|
+
describe "property with :instance" do
|
33
|
+
representer!(:inject => :song_representer) do
|
34
|
+
property :song,
|
35
|
+
:instance => lambda { |fragment, *args| fragment["id"] == song.id ? song : Song.find(fragment["id"]) },
|
36
|
+
:extend => song_representer
|
37
|
+
end
|
38
|
+
|
39
|
+
it( "xxx") { OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer).
|
40
|
+
from_hash("song" => {"id" => 1}).song.must_equal Song.new(1, "The Answer Is Still No") }
|
41
|
+
|
42
|
+
it { OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer).
|
43
|
+
from_hash("song" => {"id" => 2}).song.must_equal Song.new(2, "Invincible") }
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
describe "collection with :instance" do
|
48
|
+
representer!(:inject => :song_representer) do
|
49
|
+
collection :songs,
|
50
|
+
:instance => lambda { |fragment, i, *args|
|
51
|
+
fragment["id"] == songs[i].id ? songs[i] : Song.find(fragment["id"])
|
52
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
53
|
+
|
54
|
+
:extend => song_representer
|
55
|
+
end
|
56
|
+
|
57
|
+
it {
|
58
|
+
album= Struct.new(:songs).new(songs = [
|
59
|
+
Song.new(1, "The Answer Is Still No"),
|
60
|
+
Song.new(2, "")])
|
61
|
+
|
62
|
+
album.
|
63
|
+
extend(representer).
|
64
|
+
from_hash("songs" => [{"id" => 2},{"id" => 2, "title"=>"The Answer Is Still No"}]).songs.must_equal [
|
65
|
+
Song.new(2, "Invincible"), Song.new(2, "The Answer Is Still No")]
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "property with lambda receiving fragment and args" do
|
70
|
+
representer!(:inject => :song_representer) do
|
71
|
+
property :song, :instance => lambda { |fragment, args| Struct.new(:args, :id).new([fragment, args]) }, :extend => song_representer
|
72
|
+
end
|
73
|
+
|
74
|
+
it { OpenStruct.new(:song => Song.new(1, "The Answer Is Still No")).extend(representer).
|
75
|
+
from_hash({"song" => {"id" => 1}}, {:volume => 1}).song.args.must_equal([{"id"=>1}, {:volume=>1}]) }
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: raise and test instance:{nil}
|
79
|
+
# describe "property with instance: { nil }" do # TODO: introduce :representable option?
|
80
|
+
# representer!(:inject => :song_representer) do
|
81
|
+
# property :song, :instance => lambda { |*| nil }, :extend => song_representer
|
82
|
+
# end
|
83
|
+
|
84
|
+
# let (:hit) { hit = OpenStruct.new(:song => song).extend(representer) }
|
85
|
+
|
86
|
+
# it "calls #to_hash on song instance, nothing else" do
|
87
|
+
# hit.to_hash.must_equal("song"=>{"title"=>"Resist Stance"})
|
88
|
+
# end
|
89
|
+
|
90
|
+
# it "calls #from_hash on the existing song instance, nothing else" do
|
91
|
+
# song_id = hit.song.object_id
|
92
|
+
# hit.from_hash("song"=>{"title"=>"Suffer"})
|
93
|
+
# hit.song.title.must_equal "Suffer"
|
94
|
+
# hit.song.object_id.must_equal song_id
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
|
98
|
+
# lambda { |fragment, i, Context(binding: <..>, args: [..])| }
|
99
|
+
|
100
|
+
describe "sync" do
|
101
|
+
representer!(:inject => :song_representer) do
|
102
|
+
collection :songs,
|
103
|
+
:instance => lambda { |fragment, i, *args|
|
104
|
+
songs[i]
|
105
|
+
},
|
106
|
+
:extend => song_representer,
|
107
|
+
# :parse_strategy => :sync
|
108
|
+
:setter => lambda { |*| }
|
109
|
+
end
|
110
|
+
|
111
|
+
it {
|
112
|
+
album= Struct.new(:songs).new(songs = [
|
113
|
+
Song.new(1, "The Answer Is Still No"),
|
114
|
+
Song.new(2, "Invncble")])
|
115
|
+
|
116
|
+
album.
|
117
|
+
extend(representer).
|
118
|
+
from_hash("songs" => [{"title" => "The Answer Is Still No"}, {"title" => "Invincible"}])
|
119
|
+
|
120
|
+
album.songs.must_equal [
|
121
|
+
Song.new(1, "The Answer Is Still No"),
|
122
|
+
Song.new(2, "Invincible")]
|
123
|
+
|
124
|
+
songs.object_id.must_equal album.songs.object_id
|
125
|
+
songs[0].object_id.must_equal album.songs[0].object_id
|
126
|
+
songs[1].object_id.must_equal album.songs[1].object_id
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "update existing elements, only" do
|
131
|
+
representer!(:inject => :song_representer) do
|
132
|
+
collection :songs,
|
133
|
+
:instance => lambda { |fragment, i, *args|
|
134
|
+
|
135
|
+
#fragment["id"] == songs[i].id ? songs[i] : Song.find(fragment["id"])
|
136
|
+
songs.find { |s| s.id == fragment["id"] }
|
137
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
138
|
+
|
139
|
+
:extend => song_representer,
|
140
|
+
# :parse_strategy => :sync
|
141
|
+
:setter => lambda { |*| }
|
142
|
+
end
|
143
|
+
|
144
|
+
it("hooray") {
|
145
|
+
album= Struct.new(:songs).new(songs = [
|
146
|
+
Song.new(1, "The Answer Is Still No"),
|
147
|
+
Song.new(2, "Invncble")])
|
148
|
+
|
149
|
+
album.
|
150
|
+
extend(representer).
|
151
|
+
from_hash("songs" => [{"id" => 2, "title" => "Invincible"}]).
|
152
|
+
songs.must_equal [
|
153
|
+
Song.new(1, "The Answer Is Still No"),
|
154
|
+
Song.new(2, "Invincible")]
|
155
|
+
# TODO: check elements object_id!
|
156
|
+
|
157
|
+
songs.object_id.must_equal album.songs.object_id
|
158
|
+
songs[0].object_id.must_equal album.songs[0].object_id
|
159
|
+
songs[1].object_id.must_equal album.songs[1].object_id
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
describe "add incoming elements, only" do
|
165
|
+
representer!(:inject => :song_representer) do
|
166
|
+
collection :songs,
|
167
|
+
:instance => lambda { |fragment, i, *args|
|
168
|
+
songs << song=Song.new(2)
|
169
|
+
song
|
170
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
171
|
+
|
172
|
+
:extend => song_representer,
|
173
|
+
# :parse_strategy => :sync
|
174
|
+
:setter => lambda { |*| }
|
175
|
+
end
|
176
|
+
|
177
|
+
it {
|
178
|
+
album= Struct.new(:songs).new(songs = [
|
179
|
+
Song.new(1, "The Answer Is Still No")])
|
180
|
+
|
181
|
+
album.
|
182
|
+
extend(representer).
|
183
|
+
from_hash("songs" => [{"title" => "Invincible"}]).
|
184
|
+
songs.must_equal [
|
185
|
+
Song.new(1, "The Answer Is Still No"),
|
186
|
+
Song.new(2, "Invincible")]
|
187
|
+
|
188
|
+
songs.object_id.must_equal album.songs.object_id
|
189
|
+
songs[0].object_id.must_equal album.songs[0].object_id
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
# not sure if this must be a library strategy
|
195
|
+
describe "replace existing element" do
|
196
|
+
representer!(:inject => :song_representer) do
|
197
|
+
collection :songs,
|
198
|
+
:instance => lambda { |fragment, i, *args|
|
199
|
+
id = fragment.delete("replace_id")
|
200
|
+
replaced = songs.find { |s| s.id == id }
|
201
|
+
songs[songs.index(replaced)] = song=Song.new(3)
|
202
|
+
song
|
203
|
+
}, # let's not allow returning nil anymore. make sure we can still do everything as with nil. also, let's remove parse_strategy: sync.
|
204
|
+
|
205
|
+
:extend => song_representer,
|
206
|
+
# :parse_strategy => :sync
|
207
|
+
:setter => lambda { |*| }
|
208
|
+
end
|
209
|
+
|
210
|
+
it {
|
211
|
+
album= Struct.new(:songs).new(songs = [
|
212
|
+
Song.new(1, "The Answer Is Still No"),
|
213
|
+
Song.new(2, "Invincible")])
|
214
|
+
|
215
|
+
album.
|
216
|
+
extend(representer).
|
217
|
+
from_hash("songs" => [{"replace_id"=>2, "id" => 3, "title" => "Soulmate"}]).
|
218
|
+
songs.must_equal [
|
219
|
+
Song.new(1, "The Answer Is Still No"),
|
220
|
+
Song.new(3, "Soulmate")]
|
221
|
+
|
222
|
+
songs.object_id.must_equal album.songs.object_id
|
223
|
+
songs[0].object_id.must_equal album.songs[0].object_id
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
describe "replace collection" do
|
229
|
+
representer!(:inject => :song_representer) do
|
230
|
+
collection :songs,
|
231
|
+
:extend => song_representer, :class => Song
|
232
|
+
end
|
233
|
+
|
234
|
+
it {
|
235
|
+
album= Struct.new(:songs).new(songs = [
|
236
|
+
Song.new(1, "The Answer Is Still No")])
|
237
|
+
|
238
|
+
album.
|
239
|
+
extend(representer).
|
240
|
+
from_hash("songs" => [{"title" => "Invincible"}]).
|
241
|
+
songs.must_equal [
|
242
|
+
Song.new(nil, "Invincible")]
|
243
|
+
|
244
|
+
songs.object_id.wont_equal album.songs.object_id
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
describe "new syntax for instance: true" do
|
250
|
+
representer!(:inject => :song_representer) do
|
251
|
+
property :song, :pass_options => true,
|
252
|
+
:extend => song_representer, :instance => lambda { |fragment, args| args.binding.get(represented: args.represented) }
|
253
|
+
end
|
254
|
+
|
255
|
+
it "uses Binding#get instead of creating an instance, but deprecates" do
|
256
|
+
album= Struct.new(:song).new(song = Song.new(1, "The Answer Is Still No"))
|
257
|
+
|
258
|
+
album.
|
259
|
+
extend(representer).
|
260
|
+
from_hash("song" => {"title" => "Invincible"}).
|
261
|
+
song.must_equal Song.new(1, "Invincible")
|
262
|
+
|
263
|
+
album.song.object_id.must_equal song.object_id
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|