representable 3.0.0 → 3.2.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 +5 -5
- data/.github/workflows/ci.yml +23 -0
- data/CHANGES.md +33 -0
- data/Gemfile +4 -12
- data/LICENSE +1 -1
- data/README.md +12 -13
- data/Rakefile +1 -6
- data/TODO +1 -3
- data/TODO-4.0.md +72 -0
- data/lib/representable/binding.rb +32 -12
- data/lib/representable/cached.rb +1 -1
- data/lib/representable/coercion.rb +8 -6
- data/lib/representable/config.rb +13 -3
- data/lib/representable/debug.rb +23 -15
- data/lib/representable/declarative.rb +12 -7
- data/lib/representable/decorator.rb +1 -1
- data/lib/representable/definition.rb +7 -3
- data/lib/representable/deserializer.rb +5 -4
- data/lib/representable/for_collection.rb +1 -1
- data/lib/representable/hash/allow_symbols.rb +9 -11
- data/lib/representable/hash/binding.rb +1 -0
- data/lib/representable/hash/collection.rb +4 -2
- data/lib/representable/hash.rb +9 -2
- data/lib/representable/hash_methods.rb +3 -2
- data/lib/representable/insert.rb +1 -1
- data/lib/representable/json/collection.rb +3 -0
- data/lib/representable/json.rb +8 -7
- data/lib/representable/object/binding.rb +5 -1
- data/lib/representable/object.rb +1 -1
- data/lib/representable/option.rb +19 -0
- data/lib/representable/pipeline.rb +3 -2
- data/lib/representable/pipeline_factories.rb +4 -2
- data/lib/representable/populator.rb +1 -1
- data/lib/representable/represent.rb +1 -0
- data/lib/representable/serializer.rb +3 -2
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml/binding.rb +19 -13
- data/lib/representable/xml/namespace.rb +122 -0
- data/lib/representable/xml.rb +12 -10
- data/lib/representable/yaml/binding.rb +1 -0
- data/lib/representable/yaml.rb +6 -2
- data/lib/representable.rb +19 -25
- data/representable.gemspec +8 -9
- data/test/as_test.rb +7 -7
- data/test/binding_test.rb +14 -14
- data/test/cached_test.rb +59 -49
- data/test/class_test.rb +9 -9
- data/test/coercion_test.rb +33 -22
- data/test/config/inherit_test.rb +14 -14
- data/test/config_test.rb +20 -20
- data/test/decorator_scope_test.rb +4 -4
- data/test/decorator_test.rb +33 -20
- data/test/default_test.rb +8 -8
- data/test/defaults_options_test.rb +3 -3
- data/test/definition_test.rb +38 -40
- data/test/{example.rb → examples/example.rb} +0 -1
- data/test/examples/object.rb +1 -5
- data/test/exec_context_test.rb +8 -8
- data/test/features_test.rb +6 -6
- data/test/filter_test.rb +8 -8
- data/test/for_collection_test.rb +10 -10
- data/test/generic_test.rb +13 -13
- data/test/getter_setter_test.rb +5 -5
- data/test/hash_bindings_test.rb +1 -1
- data/test/hash_test.rb +45 -23
- data/test/heritage_test.rb +16 -13
- data/test/if_test.rb +9 -9
- data/test/include_exclude_test.rb +14 -14
- data/test/inherit_test.rb +18 -18
- data/test/inline_test.rb +24 -24
- data/test/instance_test.rb +31 -31
- data/test/is_representable_test.rb +10 -10
- data/test/json_test.rb +29 -7
- data/test/lonely_test.rb +31 -31
- data/test/nested_test.rb +13 -13
- data/test/object_test.rb +9 -9
- data/test/option_test.rb +36 -0
- data/test/parse_pipeline_test.rb +3 -5
- data/test/pipeline_test.rb +50 -50
- data/test/populator_test.rb +18 -18
- data/test/prepare_test.rb +4 -4
- data/test/private_options_test.rb +2 -2
- data/test/reader_writer_test.rb +2 -2
- data/test/render_nil_test.rb +2 -2
- data/test/represent_test.rb +14 -14
- data/test/representable_test.rb +34 -36
- data/test/schema_test.rb +8 -11
- data/test/serialize_deserialize_test.rb +2 -2
- data/test/skip_test.rb +14 -14
- data/test/stringify_hash_test.rb +3 -3
- data/test/test_helper.rb +26 -14
- data/test/uncategorized_test.rb +10 -10
- data/test/user_options_test.rb +4 -4
- data/test/wrap_test.rb +19 -19
- data/test/xml_bindings_test.rb +0 -4
- data/test/xml_namespace_test.rb +186 -0
- data/test/xml_test.rb +103 -43
- data/test/yaml_test.rb +51 -26
- metadata +42 -35
- data/.travis.yml +0 -7
- data/lib/representable/TODO.getting_serious +0 -11
- data/lib/representable/autoload.rb +0 -10
- data/test/mongoid_test.rb +0 -31
data/representable.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.platform = Gem::Platform::RUBY
|
10
10
|
spec.authors = ["Nick Sutterer"]
|
11
11
|
spec.email = ["apotonick@gmail.com"]
|
12
|
-
spec.homepage = "https://github.com/
|
12
|
+
spec.homepage = "https://github.com/trailblazer/representable/"
|
13
13
|
spec.summary = %q{Renders and parses JSON/XML/YAML documents from and to Ruby objects. Includes plain properties, collections, nesting, coercion and more.}
|
14
14
|
spec.description = spec.summary
|
15
15
|
|
@@ -19,17 +19,16 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
spec.license = "MIT"
|
21
21
|
|
22
|
-
spec.required_ruby_version = '>=
|
22
|
+
spec.required_ruby_version = '>= 2.4.0'
|
23
23
|
|
24
|
-
spec.add_dependency "uber",
|
25
|
-
spec.add_dependency "declarative",
|
24
|
+
spec.add_dependency "uber", "< 0.2.0"
|
25
|
+
spec.add_dependency "declarative", "< 0.1.0"
|
26
|
+
spec.add_dependency "trailblazer-option", ">= 0.1.1", "< 0.2.0"
|
26
27
|
|
27
28
|
spec.add_development_dependency "rake"
|
28
|
-
spec.add_development_dependency "test_xml", "0.1.6"
|
29
|
+
spec.add_development_dependency "test_xml", ">= 0.1.6"
|
29
30
|
spec.add_development_dependency "minitest"
|
30
|
-
spec.add_development_dependency "mongoid"
|
31
31
|
spec.add_development_dependency "virtus"
|
32
|
-
spec.add_development_dependency "
|
33
|
-
|
34
|
-
spec.add_development_dependency "ruby-prof"
|
32
|
+
spec.add_development_dependency "dry-types"
|
33
|
+
spec.add_development_dependency "ruby-prof" if RUBY_ENGINE == "ruby" # mri
|
35
34
|
end
|
data/test/as_test.rb
CHANGED
@@ -7,8 +7,8 @@ class AsTest < MiniTest::Spec
|
|
7
7
|
# :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
|
8
8
|
) do |format, mod, input, output|
|
9
9
|
|
10
|
-
let
|
11
|
-
let
|
10
|
+
let(:song) { representer.prepare(Song.new("Revolution")) }
|
11
|
+
let(:format) { format }
|
12
12
|
|
13
13
|
|
14
14
|
describe "as: with :symbol" do
|
@@ -17,7 +17,7 @@ class AsTest < MiniTest::Spec
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it { render(song).must_equal_document output }
|
20
|
-
it { parse(song, input).name.must_equal "Wie Es Geht" }
|
20
|
+
it { _(parse(song, input).name).must_equal "Wie Es Geht" }
|
21
21
|
end
|
22
22
|
|
23
23
|
|
@@ -27,7 +27,7 @@ class AsTest < MiniTest::Spec
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it { render(song).must_equal_document({"Song" => "Revolution"}) }
|
30
|
-
it { parse(song, {"Song" => "Wie Es Geht"}).name.must_equal "Wie Es Geht" }
|
30
|
+
it { _(parse(song, {"Song" => "Wie Es Geht"}).name).must_equal "Wie Es Geht" }
|
31
31
|
end
|
32
32
|
|
33
33
|
|
@@ -37,7 +37,7 @@ class AsTest < MiniTest::Spec
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it { render(song, user_options:{volume: 1}).must_equal_document({"{:volume=>1}" => "Revolution"}) }
|
40
|
-
it { parse(song, {"{:volume=>1}" => "Wie Es Geht"}, user_options: {volume: 1}).name.must_equal "Wie Es Geht" }
|
40
|
+
it { _(parse(song, {"{:volume=>1}" => "Wie Es Geht"}, user_options: {volume: 1}).name).must_equal "Wie Es Geht" }
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -60,6 +60,6 @@ class AsXmlTest < MiniTest::Spec
|
|
60
60
|
|
61
61
|
it do
|
62
62
|
skip
|
63
|
-
representer.new(Album.new(Band.new("Offspring"))).to_xml.must_equal ""
|
63
|
+
_(representer.new(Album.new(Band.new("Offspring"))).to_xml).must_equal ""
|
64
64
|
end
|
65
|
-
end
|
65
|
+
end
|
data/test/binding_test.rb
CHANGED
@@ -2,45 +2,45 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class BindingTest < MiniTest::Spec
|
4
4
|
Binding = Representable::Binding
|
5
|
-
let
|
5
|
+
let(:render_nil_definition) { Representable::Definition.new(:song, :render_nil => true) }
|
6
6
|
|
7
7
|
describe "#skipable_empty_value?" do
|
8
|
-
let
|
8
|
+
let(:binding) { Binding.new(render_nil_definition) }
|
9
9
|
|
10
10
|
# don't skip when present.
|
11
|
-
it { binding.skipable_empty_value?("Disconnect, Disconnect").must_equal false }
|
11
|
+
it { _(binding.skipable_empty_value?("Disconnect, Disconnect")).must_equal false }
|
12
12
|
|
13
13
|
# don't skip when it's nil and render_nil: true
|
14
|
-
it { binding.skipable_empty_value?(nil).must_equal false }
|
14
|
+
it { _(binding.skipable_empty_value?(nil)).must_equal false }
|
15
15
|
|
16
16
|
# skip when nil and :render_nil undefined.
|
17
|
-
it { Binding.new(Representable::Definition.new(:song)).skipable_empty_value?(nil).must_equal true }
|
17
|
+
it { _(Binding.new(Representable::Definition.new(:song)).skipable_empty_value?(nil)).must_equal true }
|
18
18
|
|
19
19
|
# don't skip when nil and :render_nil undefined.
|
20
|
-
it { Binding.new(Representable::Definition.new(:song)).skipable_empty_value?("Fatal Flu").must_equal false }
|
20
|
+
it { _(Binding.new(Representable::Definition.new(:song)).skipable_empty_value?("Fatal Flu")).must_equal false }
|
21
21
|
end
|
22
22
|
|
23
23
|
|
24
24
|
describe "#default_for" do
|
25
|
-
let
|
26
|
-
let
|
25
|
+
let(:definition) { Representable::Definition.new(:song, :default => "Insider") }
|
26
|
+
let(:binding) { Binding.new(definition) }
|
27
27
|
|
28
28
|
# return value when value present.
|
29
|
-
it { binding.default_for("Black And Blue").must_equal "Black And Blue" }
|
29
|
+
it { _(binding.default_for("Black And Blue")).must_equal "Black And Blue" }
|
30
30
|
|
31
31
|
# return false when value false.
|
32
|
-
it { binding.default_for(false).must_equal false }
|
32
|
+
it { _(binding.default_for(false)).must_equal false }
|
33
33
|
|
34
34
|
# return default when value nil.
|
35
|
-
it { binding.default_for(nil).must_equal "Insider" }
|
35
|
+
it { _(binding.default_for(nil)).must_equal "Insider" }
|
36
36
|
|
37
37
|
# return nil when value nil and render_nil: true.
|
38
|
-
it { Binding.new(render_nil_definition).default_for(nil).
|
38
|
+
it { _(Binding.new(render_nil_definition).default_for(nil)).must_be_nil }
|
39
39
|
|
40
40
|
# return nil when value nil and render_nil: true, even when :default is set" do
|
41
|
-
it { Binding.new(Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")).default_for(nil).
|
41
|
+
it { _(Binding.new(Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")).default_for(nil)).must_be_nil }
|
42
42
|
|
43
43
|
# return nil if no :default
|
44
|
-
it { Binding.new(Representable::Definition.new(:song)).default_for(nil).
|
44
|
+
it { _(Binding.new(Representable::Definition.new(:song)).default_for(nil)).must_be_nil }
|
45
45
|
end
|
46
46
|
end
|
data/test/cached_test.rb
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
require "test_helper"
|
2
|
-
|
2
|
+
|
3
|
+
class Profiler
|
4
|
+
def self.profile(&block)
|
5
|
+
case RUBY_ENGINE
|
6
|
+
when "ruby"
|
7
|
+
require 'ruby-prof'
|
8
|
+
|
9
|
+
output = StringIO.new
|
10
|
+
profile_result = RubyProf.profile(&block)
|
11
|
+
printer = RubyProf::FlatPrinter.new(profile_result)
|
12
|
+
printer.print(output)
|
13
|
+
output.string
|
14
|
+
when "jruby"
|
15
|
+
require 'jruby/profiler'
|
16
|
+
|
17
|
+
output_stream = java.io.ByteArrayOutputStream.new
|
18
|
+
print_stream = java.io.PrintStream.new(output_stream)
|
19
|
+
profile_result = JRuby::Profiler.profile(&block)
|
20
|
+
printer = JRuby::Profiler::FlatProfilePrinter.new(profile_result)
|
21
|
+
printer.printProfile(print_stream)
|
22
|
+
output_stream.toString
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
3
26
|
|
4
27
|
class CachedTest < MiniTest::Spec
|
5
28
|
# TODO: also test with feature(Cached)
|
@@ -30,18 +53,18 @@ class CachedTest < MiniTest::Spec
|
|
30
53
|
|
31
54
|
|
32
55
|
describe "serialization" do
|
33
|
-
let
|
56
|
+
let(:album_hash) { {"name"=>"Louder And Even More Dangerous", "songs"=>[{"title"=>"Southbound:{:volume=>10}"}, {"title"=>"Jailbreak:{:volume=>10}"}]} }
|
34
57
|
|
35
|
-
let
|
36
|
-
let
|
37
|
-
let
|
38
|
-
let
|
58
|
+
let(:song) { Model::Song.new("Jailbreak") }
|
59
|
+
let(:song2) { Model::Song.new("Southbound") }
|
60
|
+
let(:album) { Model::Album.new("Live And Dangerous", [song, song2, Model::Song.new("Emerald")]) }
|
61
|
+
let(:representer) { AlbumRepresenter.new(album) }
|
39
62
|
|
40
63
|
it do
|
41
|
-
album2 = Model::Album.new("Louder And Even More Dangerous", [song2, song])
|
64
|
+
# album2 = Model::Album.new("Louder And Even More Dangerous", [song2, song])
|
42
65
|
|
43
66
|
# makes sure options are passed correctly.
|
44
|
-
representer.to_hash(user_options: {volume: 9}).must_equal({"name"=>"Live And Dangerous",
|
67
|
+
_(representer.to_hash(user_options: {volume: 9})).must_equal({"name"=>"Live And Dangerous",
|
45
68
|
"songs"=>[{"title"=>"Jailbreak:{:volume=>9}"}, {"title"=>"Southbound:{:volume=>9}"}, {"title"=>"Emerald:{:volume=>9}"}]}) # called in Deserializer/Serializer
|
46
69
|
|
47
70
|
# representer becomes reusable as it is stateless.
|
@@ -55,36 +78,31 @@ class CachedTest < MiniTest::Spec
|
|
55
78
|
it do
|
56
79
|
representer.to_hash
|
57
80
|
|
58
|
-
|
59
|
-
representer.to_hash
|
60
|
-
res = RubyProf.stop
|
61
|
-
|
62
|
-
printer = RubyProf::FlatPrinter.new(res)
|
63
|
-
|
64
|
-
data = StringIO.new
|
65
|
-
printer.print(data)
|
66
|
-
data = data.string
|
67
|
-
|
68
|
-
printer.print(STDOUT)
|
81
|
+
data = Profiler.profile { representer.to_hash }
|
69
82
|
|
70
83
|
# 3 songs get decorated.
|
71
|
-
data.must_match
|
84
|
+
_(data).must_match(/3\s*Representable::Function::Decorate#call/m)
|
85
|
+
# These weird Regexp bellow are a quick workaround to accomodate
|
86
|
+
# the different profiler result formats.
|
87
|
+
# - "3 <Class::Representable::Decorator>#prepare" -> At MRI Ruby
|
88
|
+
# - "3 Representable::Decorator.prepare" -> At JRuby
|
89
|
+
|
72
90
|
# 3 nested decorator is instantiated for 3 Songs, though.
|
73
|
-
data.must_match
|
91
|
+
_(data).must_match(/3\s*(<Class::)?Representable::Decorator\>?[\#.]prepare/m)
|
74
92
|
# no Binding is instantiated at runtime.
|
75
|
-
data.wont_match "Representable::Binding#initialize"
|
93
|
+
_(data).wont_match "Representable::Binding#initialize"
|
76
94
|
# 2 mappers for Album, Song
|
77
95
|
# data.must_match "2 Representable::Mapper::Methods#initialize"
|
78
96
|
# title, songs, 3x title, composer
|
79
|
-
data.must_match
|
80
|
-
data.wont_match "render_functions"
|
81
|
-
data.wont_match "Representable::Binding::Factories#render_functions"
|
97
|
+
_(data).must_match(/8\s*Representable::Binding[#\.]render_pipeline/m)
|
98
|
+
_(data).wont_match "render_functions"
|
99
|
+
_(data).wont_match "Representable::Binding::Factories#render_functions"
|
82
100
|
end
|
83
101
|
end
|
84
102
|
|
85
103
|
|
86
104
|
describe "deserialization" do
|
87
|
-
let
|
105
|
+
let(:album_hash) {
|
88
106
|
{
|
89
107
|
"name"=>"Louder And Even More Dangerous",
|
90
108
|
"songs"=>[
|
@@ -100,14 +118,14 @@ class CachedTest < MiniTest::Spec
|
|
100
118
|
|
101
119
|
AlbumRepresenter.new(album).from_hash(album_hash)
|
102
120
|
|
103
|
-
album.songs.size.must_equal 3
|
104
|
-
album.name.must_equal "Louder And Even More Dangerous"
|
105
|
-
album.songs[0].title.must_equal "Southbound"
|
106
|
-
album.songs[0].composer.name.must_equal "Lynott"
|
107
|
-
album.songs[1].title.must_equal "Jailbreak"
|
108
|
-
album.songs[1].composer.name.must_equal "Phil Lynott"
|
109
|
-
album.songs[2].title.must_equal "Emerald"
|
110
|
-
album.songs[2].composer.
|
121
|
+
_(album.songs.size).must_equal 3
|
122
|
+
_(album.name).must_equal "Louder And Even More Dangerous"
|
123
|
+
_(album.songs[0].title).must_equal "Southbound"
|
124
|
+
_(album.songs[0].composer.name).must_equal "Lynott"
|
125
|
+
_(album.songs[1].title).must_equal "Jailbreak"
|
126
|
+
_(album.songs[1].composer.name).must_equal "Phil Lynott"
|
127
|
+
_(album.songs[2].title).must_equal "Emerald"
|
128
|
+
_(album.songs[2].composer).must_be_nil
|
111
129
|
|
112
130
|
# TODO: test options.
|
113
131
|
end
|
@@ -116,25 +134,17 @@ class CachedTest < MiniTest::Spec
|
|
116
134
|
representer = AlbumRepresenter.new(Model::Album.new)
|
117
135
|
representer.from_hash(album_hash)
|
118
136
|
|
119
|
-
|
120
|
-
# puts "#{representer.class.representable_attrs.get(:songs).representer_module.representable_attrs.inspect}"
|
121
|
-
representer.from_hash(album_hash)
|
122
|
-
res = RubyProf.stop
|
123
|
-
|
124
|
-
printer = RubyProf::FlatPrinter.new(res)
|
125
|
-
|
126
|
-
data = StringIO.new
|
127
|
-
printer.print(data)
|
128
|
-
data = data.string
|
137
|
+
data = Profiler.profile { representer.from_hash(album_hash) }
|
129
138
|
|
130
139
|
# only 2 nested decorators are instantiated, Song, and Artist.
|
131
|
-
|
140
|
+
# Didn't like the regexp?
|
141
|
+
# MRI and JRuby has different output formats. See note above.
|
142
|
+
_(data).must_match(/5\s*(<Class::)?Representable::Decorator>?[#\.]prepare/)
|
132
143
|
# a total of 5 properties in the object graph.
|
133
|
-
data.wont_match "Representable::Binding#initialize"
|
134
|
-
|
144
|
+
_(data).wont_match "Representable::Binding#initialize"
|
135
145
|
|
136
|
-
data.wont_match "parse_functions" # no pipeline creation.
|
137
|
-
data.must_match
|
146
|
+
_(data).wont_match "parse_functions" # no pipeline creation.
|
147
|
+
_(data).must_match(/10\s*Representable::Binding[#\.]parse_pipeline/)
|
138
148
|
# three mappers for Album, Song, composer
|
139
149
|
# data.must_match "3 Representable::Mapper::Methods#initialize"
|
140
150
|
# # 6 deserializers as the songs collection uses 2.
|
data/test/class_test.rb
CHANGED
@@ -20,8 +20,8 @@ class ClassTest < BaseTest
|
|
20
20
|
|
21
21
|
it "creates fresh instance and doesn't extend" do
|
22
22
|
song = representer.prepare(OpenStruct.new).from_hash({"song" => {"__name__" => "Captured"}}).song
|
23
|
-
song.must_be_instance_of RepresentingSong
|
24
|
-
song.name.must_equal "Captured"
|
23
|
+
_(song).must_be_instance_of RepresentingSong
|
24
|
+
_(song.name).must_equal "Captured"
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -33,8 +33,8 @@ class ClassTest < BaseTest
|
|
33
33
|
|
34
34
|
it "creates fresh instance and doesn't extend" do
|
35
35
|
song = representer.prepare(OpenStruct.new).from_hash({"song" => {"__name__" => "Captured"}}).song
|
36
|
-
song.must_be_instance_of RepresentingSong
|
37
|
-
song.name.must_equal "Captured"
|
36
|
+
_(song).must_be_instance_of RepresentingSong
|
37
|
+
_(song.name).must_equal "Captured"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -54,7 +54,7 @@ class ClassTest < BaseTest
|
|
54
54
|
|
55
55
|
|
56
56
|
describe "lambda receiving fragment and args" do
|
57
|
-
let
|
57
|
+
let(:klass) { Class.new do
|
58
58
|
class << self
|
59
59
|
attr_accessor :args
|
60
60
|
end
|
@@ -69,13 +69,13 @@ class ClassTest < BaseTest
|
|
69
69
|
property :song, :class => lambda { |options| _klass.args=([options[:fragment],options[:user_options]]); _klass }
|
70
70
|
end
|
71
71
|
|
72
|
-
it { representer.prepare(OpenStruct.new).from_hash({"song" => {"name" => "Captured"}}, user_options: {volume: true}).song.class.args.
|
72
|
+
it { _(representer.prepare(OpenStruct.new).from_hash({"song" => {"name" => "Captured"}}, user_options: {volume: true}).song.class.args).
|
73
73
|
must_equal([{"name"=>"Captured"}, {:volume=>true}]) }
|
74
74
|
end
|
75
75
|
|
76
76
|
|
77
77
|
describe "collection: lambda receiving fragment and args" do
|
78
|
-
let
|
78
|
+
let(:klass) { Class.new do
|
79
79
|
class << self
|
80
80
|
attr_accessor :args
|
81
81
|
end
|
@@ -90,7 +90,7 @@ class ClassTest < BaseTest
|
|
90
90
|
collection :songs, :class => lambda { |options| _klass.args=([options[:fragment],options[:index],options[:user_options]]); _klass }
|
91
91
|
end
|
92
92
|
|
93
|
-
it { representer.prepare(OpenStruct.new).from_hash({"songs" => [{"name" => "Captured"}]}, user_options: {volume: true}).songs.first.class.args.
|
93
|
+
it { _(representer.prepare(OpenStruct.new).from_hash({"songs" => [{"name" => "Captured"}]}, user_options: {volume: true}).songs.first.class.args).
|
94
94
|
must_equal([{"name"=>"Captured"}, 0, {:volume=>true}]) }
|
95
95
|
end
|
96
96
|
|
@@ -109,7 +109,7 @@ class ClassTest < BaseTest
|
|
109
109
|
end
|
110
110
|
|
111
111
|
it "allows returning arbitrary objects in #from_hash" do
|
112
|
-
representer.prepare(OpenStruct.new).from_hash({"song" => 1}).song.must_equal [1,2,3,4]
|
112
|
+
_(representer.prepare(OpenStruct.new).from_hash({"song" => 1}).song).must_equal [1,2,3,4]
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
data/test/coercion_test.rb
CHANGED
@@ -1,52 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
require 'representable/coercion'
|
3
5
|
|
4
|
-
class
|
6
|
+
class CoercionTest < MiniTest::Spec
|
5
7
|
representer! do
|
6
8
|
include Representable::Coercion
|
7
9
|
|
8
10
|
property :title # no coercion.
|
9
|
-
property :length, :
|
11
|
+
property :length, type: Representable::Coercion::Types::Params::Float
|
10
12
|
|
11
|
-
property :band, :
|
12
|
-
property :founded, :
|
13
|
+
property :band, class: OpenStruct do
|
14
|
+
property :founded, type: Representable::Coercion::Types::Params::Integer
|
13
15
|
end
|
14
16
|
|
15
|
-
collection :songs, :
|
16
|
-
property :ok, :
|
17
|
+
collection :songs, class: OpenStruct do
|
18
|
+
property :ok, type: Representable::Coercion::Types::Params::Bool
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
|
-
let
|
21
|
-
|
22
|
-
|
22
|
+
let(:album) do
|
23
|
+
OpenStruct.new(title: 'Dire Straits', length: 41.34,
|
24
|
+
band: OpenStruct.new(founded: '1977'),
|
25
|
+
songs: [OpenStruct.new(ok: 1), OpenStruct.new(ok: 0)])
|
26
|
+
end
|
23
27
|
|
24
|
-
it
|
28
|
+
it do
|
29
|
+
_(album.extend(representer).to_hash).must_equal({ 'title' => 'Dire Straits',
|
30
|
+
'length' => 41.34,
|
31
|
+
'band' => { 'founded' => 1977 },
|
32
|
+
'songs' => [{ 'ok' => true }, { 'ok' => false }] })
|
33
|
+
end
|
25
34
|
|
26
35
|
it {
|
27
36
|
album = OpenStruct.new
|
28
37
|
album.extend(representer)
|
29
|
-
album.from_hash({
|
38
|
+
album.from_hash({ 'title' => 'Dire Straits',
|
39
|
+
'length' => '41.34',
|
40
|
+
'band' => { 'founded' => '1977' },
|
41
|
+
'songs' => [{ 'ok' => 1 }, { 'ok' => 0 }] })
|
30
42
|
|
31
43
|
# it
|
32
|
-
album.length.must_equal 41.34
|
33
|
-
album.band.founded.must_equal 1977
|
34
|
-
album.songs[0].ok.must_equal true
|
44
|
+
_(album.length).must_equal 41.34
|
45
|
+
_(album.band.founded).must_equal 1977
|
46
|
+
_(album.songs[0].ok).must_equal true
|
35
47
|
}
|
36
48
|
|
37
|
-
|
38
|
-
describe "with user :parse_filter and :render_filter" do
|
49
|
+
describe 'with user :parse_filter and :render_filter' do
|
39
50
|
representer! do
|
40
51
|
include Representable::Coercion
|
41
52
|
|
42
|
-
property :length, :
|
43
|
-
|
44
|
-
|
53
|
+
property :length, type: Representable::Coercion::Types::Params::Float,
|
54
|
+
parse_filter: ->(input, _options) { "#{input}.1" }, # happens BEFORE coercer.
|
55
|
+
render_filter: ->(fragment, *) { "#{fragment}.1" }
|
45
56
|
end
|
46
57
|
|
47
58
|
# user's :parse_filter(s) are run before coercion.
|
48
|
-
it { OpenStruct.new.extend(representer).from_hash(
|
59
|
+
it { _(OpenStruct.new.extend(representer).from_hash('length' => '1').length).must_equal 1.1 }
|
49
60
|
# user's :render_filter(s) are run before coercion.
|
50
|
-
it { OpenStruct.new(:
|
61
|
+
it { _(OpenStruct.new(length: 1).extend(representer).to_hash).must_equal({ 'length' => 1.1 }) }
|
51
62
|
end
|
52
|
-
end
|
63
|
+
end
|
data/test/config/inherit_test.rb
CHANGED
@@ -9,8 +9,8 @@ class ConfigInheritTest < MiniTest::Spec
|
|
9
9
|
|
10
10
|
child_def.merge!(:alias => property)
|
11
11
|
|
12
|
-
child_def[:alias].wont_equal parent_def[:alias]
|
13
|
-
child_def.object_id.wont_equal parent_def.object_id
|
12
|
+
_(child_def[:alias]).wont_equal parent_def[:alias]
|
13
|
+
_(child_def.object_id).wont_equal parent_def.object_id
|
14
14
|
end
|
15
15
|
# class Object
|
16
16
|
|
@@ -30,7 +30,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
it { Decorator.definitions.keys.must_equal ["title", "artist"] }
|
33
|
+
it { _(Decorator.definitions.keys).must_equal ["title", "artist"] }
|
34
34
|
|
35
35
|
# in inheriting Decorator
|
36
36
|
|
@@ -38,10 +38,10 @@ class ConfigInheritTest < MiniTest::Spec
|
|
38
38
|
property :location
|
39
39
|
end
|
40
40
|
|
41
|
-
it { InheritingDecorator.definitions.keys.must_equal ["title", "artist", "location"] }
|
41
|
+
it { _(InheritingDecorator.definitions.keys).must_equal ["title", "artist", "location"] }
|
42
42
|
it { assert_cloned(InheritingDecorator, Decorator, "title") }
|
43
43
|
it do
|
44
|
-
InheritingDecorator.representable_attrs.get(:artist).representer_module.object_id.wont_equal Decorator.representable_attrs.get(:artist).representer_module.object_id
|
44
|
+
_(InheritingDecorator.representable_attrs.get(:artist).representer_module.object_id).wont_equal Decorator.representable_attrs.get(:artist).representer_module.object_id
|
45
45
|
end
|
46
46
|
|
47
47
|
# in inheriting and including Decorator
|
@@ -51,7 +51,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
51
51
|
property :location
|
52
52
|
end
|
53
53
|
|
54
|
-
it { InheritingAndIncludingDecorator.definitions.keys.must_equal ["title", "artist", "genre", "location"] }
|
54
|
+
it { _(InheritingAndIncludingDecorator.definitions.keys).must_equal ["title", "artist", "genre", "location"] }
|
55
55
|
it { assert_cloned(InheritingAndIncludingDecorator, GenreModule, :genre) }
|
56
56
|
|
57
57
|
|
@@ -61,7 +61,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
61
61
|
property :title
|
62
62
|
end
|
63
63
|
|
64
|
-
it { Module.definitions.keys.must_equal ["title"] }
|
64
|
+
it { _(Module.definitions.keys).must_equal ["title"] }
|
65
65
|
|
66
66
|
|
67
67
|
# in module including module
|
@@ -72,7 +72,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
72
72
|
property :location
|
73
73
|
end
|
74
74
|
|
75
|
-
it { SubModule.definitions.keys.must_equal ["title", "location"] }
|
75
|
+
it { _(SubModule.definitions.keys).must_equal ["title", "location"] }
|
76
76
|
it { assert_cloned(SubModule, Module, :title) }
|
77
77
|
|
78
78
|
# including preserves order
|
@@ -84,7 +84,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
84
84
|
property :location
|
85
85
|
end
|
86
86
|
|
87
|
-
it { IncludingModule.definitions.keys.must_equal ["genre", "title", "location"] }
|
87
|
+
it { _(IncludingModule.definitions.keys).must_equal ["genre", "title", "location"] }
|
88
88
|
|
89
89
|
|
90
90
|
# included in class -------------------------------------------
|
@@ -93,7 +93,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
93
93
|
include IncludingModule
|
94
94
|
end
|
95
95
|
|
96
|
-
it { Class.definitions.keys.must_equal ["genre", "title", "location"] }
|
96
|
+
it { _(Class.definitions.keys).must_equal ["genre", "title", "location"] }
|
97
97
|
it { assert_cloned(Class, IncludingModule, :title) }
|
98
98
|
it { assert_cloned(Class, IncludingModule, :location) }
|
99
99
|
it { assert_cloned(Class, IncludingModule, :genre) }
|
@@ -105,7 +105,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
105
105
|
include IncludingModule
|
106
106
|
end
|
107
107
|
|
108
|
-
it { DefiningClass.definitions.keys.must_equal ["street_cred", "genre", "title", "location"] }
|
108
|
+
it { _(DefiningClass.definitions.keys).must_equal ["street_cred", "genre", "title", "location"] }
|
109
109
|
|
110
110
|
# in class
|
111
111
|
class RepresenterClass
|
@@ -113,7 +113,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
113
113
|
property :title
|
114
114
|
end
|
115
115
|
|
116
|
-
it { RepresenterClass.definitions.keys.must_equal ["title"] }
|
116
|
+
it { _(RepresenterClass.definitions.keys).must_equal ["title"] }
|
117
117
|
|
118
118
|
|
119
119
|
# in inheriting class
|
@@ -122,7 +122,7 @@ class ConfigInheritTest < MiniTest::Spec
|
|
122
122
|
property :location
|
123
123
|
end
|
124
124
|
|
125
|
-
it { InheritingClass.definitions.keys.must_equal ["title", "location"] }
|
125
|
+
it { _(InheritingClass.definitions.keys).must_equal ["title", "location"] }
|
126
126
|
it { assert_cloned(InheritingClass, RepresenterClass, :title) }
|
127
127
|
|
128
128
|
# in inheriting class and including
|
@@ -131,5 +131,5 @@ class ConfigInheritTest < MiniTest::Spec
|
|
131
131
|
include GenreModule
|
132
132
|
end
|
133
133
|
|
134
|
-
it { InheritingAndIncludingClass.definitions.keys.must_equal ["title", "location", "genre"] }
|
134
|
+
it { _(InheritingAndIncludingClass.definitions.keys).must_equal ["title", "location", "genre"] }
|
135
135
|
end
|