representable 3.2.0 → 3.3.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/.github/workflows/ci.yml +17 -10
- data/.github/workflows/ci_jruby.yml +21 -0
- data/.github/workflows/ci_truffleruby.yml +19 -0
- data/CHANGES.md +5 -0
- data/Gemfile +1 -1
- data/README.md +0 -1
- data/lib/representable/hash/binding.rb +1 -0
- data/lib/representable/json.rb +14 -2
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml/binding.rb +0 -7
- data/lib/representable/xml.rb +7 -0
- data/representable.gemspec +1 -3
- data/test/as_test.rb +8 -13
- data/test/benchmarking.rb +20 -25
- data/test/binding_test.rb +13 -5
- data/test/cached_test.rb +55 -35
- data/test/class_test.rb +46 -33
- data/test/coercion_test.rb +29 -19
- data/test/config/inherit_test.rb +12 -17
- data/test/config_test.rb +5 -7
- data/test/decorator_scope_test.rb +13 -11
- data/test/decorator_test.rb +11 -11
- data/test/default_test.rb +2 -2
- data/test/defaults_options_test.rb +37 -9
- data/test/definition_test.rb +10 -12
- data/test/examples/example.rb +43 -38
- data/test/examples/object.rb +7 -6
- data/test/exec_context_test.rb +18 -23
- data/test/features_test.rb +29 -10
- data/test/filter_test.rb +29 -20
- data/test/for_collection_test.rb +12 -15
- data/test/generic_test.rb +15 -22
- data/test/getter_setter_test.rb +11 -6
- data/test/hash_bindings_test.rb +21 -12
- data/test/hash_test.rb +53 -38
- data/test/heritage_test.rb +0 -1
- data/test/if_test.rb +25 -21
- data/test/include_exclude_test.rb +24 -16
- data/test/inherit_test.rb +114 -27
- data/test/inline_test.rb +42 -33
- data/test/instance_test.rb +151 -109
- data/test/is_representable_test.rb +19 -17
- data/test/json_test.rb +48 -46
- data/test/lonely_test.rb +35 -33
- data/test/nested_test.rb +16 -19
- data/test/object_test.rb +6 -6
- data/test/option_test.rb +10 -8
- data/test/parse_pipeline_test.rb +21 -15
- data/test/pipeline_test.rb +144 -108
- data/test/populator_test.rb +18 -15
- data/test/prepare_test.rb +15 -17
- data/test/private_options_test.rb +3 -2
- data/test/reader_writer_test.rb +4 -4
- data/test/realistic_benchmark.rb +21 -27
- data/test/render_nil_test.rb +1 -1
- data/test/represent_test.rb +14 -18
- data/test/representable_test.rb +86 -53
- data/test/schema_test.rb +51 -21
- data/test/serialize_deserialize_test.rb +14 -14
- data/test/skip_test.rb +39 -28
- data/test/stringify_hash_test.rb +29 -31
- data/test/test_helper.rb +31 -25
- data/test/test_helper_test.rb +7 -7
- data/test/uncategorized_test.rb +20 -15
- data/test/user_options_test.rb +12 -3
- data/test/wrap_test.rb +27 -17
- data/test/xml_assertions.rb +29 -0
- data/test/xml_bindings_test.rb +5 -8
- data/test/xml_namespace_test.rb +46 -39
- data/test/xml_test.rb +109 -88
- data/test/yaml_test.rb +73 -51
- metadata +7 -32
data/test/skip_test.rb
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class SkipParseTest <
|
|
3
|
+
class SkipParseTest < Minitest::Spec
|
|
4
4
|
representer! do
|
|
5
|
-
property :title, skip_parse:
|
|
5
|
+
property :title, skip_parse: ->(options) { options[:user_options][:skip?] and options[:fragment] == "skip me" }
|
|
6
6
|
property :band,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
skip_parse: ->(options) { options[:user_options][:skip?] and options[:fragment]["name"].nil? }, class: OpenStruct do
|
|
8
|
+
property :name
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
collection :airplays,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
skip_parse: ->(options) {
|
|
13
|
+
options[:user_options][:skip?] and options[:fragment]["station"].nil?
|
|
14
|
+
}, class: OpenStruct do
|
|
15
|
+
property :station
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
@@ -18,11 +20,13 @@ class SkipParseTest < MiniTest::Spec
|
|
|
18
20
|
|
|
19
21
|
# do parse.
|
|
20
22
|
it do
|
|
21
|
-
song.from_hash(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
song.from_hash(
|
|
24
|
+
{
|
|
25
|
+
"title" => "Victim Of Fate",
|
|
26
|
+
"band" => {"name" => "Mute 98"},
|
|
27
|
+
"airplays" => [{"station" => "JJJ"}]
|
|
28
|
+
}, user_options: {skip?: true}
|
|
29
|
+
)
|
|
26
30
|
|
|
27
31
|
_(song.title).must_equal "Victim Of Fate"
|
|
28
32
|
_(song.band.name).must_equal "Mute 98"
|
|
@@ -33,11 +37,13 @@ class SkipParseTest < MiniTest::Spec
|
|
|
33
37
|
let(:airplay) { OpenStruct.new(station: "JJJ") }
|
|
34
38
|
|
|
35
39
|
it do
|
|
36
|
-
song.from_hash(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
song.from_hash(
|
|
41
|
+
{
|
|
42
|
+
"title" => "skip me",
|
|
43
|
+
"band" => {},
|
|
44
|
+
"airplays" => [{}, {"station" => "JJJ"}, {}]
|
|
45
|
+
}, user_options: {skip?: true}
|
|
46
|
+
)
|
|
41
47
|
|
|
42
48
|
_(song.title).must_be_nil
|
|
43
49
|
_(song.band).must_be_nil
|
|
@@ -45,17 +51,17 @@ class SkipParseTest < MiniTest::Spec
|
|
|
45
51
|
end
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
class SkipRenderTest <
|
|
54
|
+
class SkipRenderTest < Minitest::Spec
|
|
49
55
|
representer! do
|
|
50
56
|
property :title
|
|
51
57
|
property :band,
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
skip_render: ->(options) { options[:user_options][:skip?] and options[:input].name == "Rancid" } do
|
|
59
|
+
property :name
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
collection :airplays,
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
skip_render: ->(options) { options[:user_options][:skip?] and options[:input].station == "Radio Dreyeckland" } do
|
|
64
|
+
property :station
|
|
59
65
|
end
|
|
60
66
|
end
|
|
61
67
|
|
|
@@ -63,19 +69,24 @@ class SkipRenderTest < MiniTest::Spec
|
|
|
63
69
|
let(:skip_song) { OpenStruct.new(title: "Time Bomb", band: OpenStruct.new(name: "Rancid")).extend(representer) }
|
|
64
70
|
|
|
65
71
|
# do render.
|
|
66
|
-
it { _(song.to_hash(user_options: {
|
|
72
|
+
it { _(song.to_hash(user_options: {skip?: true})).must_equal({"title" => "Black Night", "band" => {"name"=>"Time Again"}}) }
|
|
67
73
|
# skip.
|
|
68
|
-
it { _(skip_song.to_hash(user_options: {
|
|
74
|
+
it { _(skip_song.to_hash(user_options: {skip?: true})).must_equal({"title"=>"Time Bomb"}) }
|
|
69
75
|
|
|
70
76
|
# do render all collection items.
|
|
71
77
|
it do
|
|
72
78
|
song = OpenStruct.new(airplays: [OpenStruct.new(station: "JJJ"), OpenStruct.new(station: "ABC")]).extend(representer)
|
|
73
|
-
_(song.to_hash(user_options: {
|
|
79
|
+
_(song.to_hash(user_options: {skip?: true})).must_equal({"airplays"=>[{"station"=>"JJJ"}, {"station"=>"ABC"}]})
|
|
74
80
|
end
|
|
75
81
|
|
|
76
82
|
# skip middle item.
|
|
77
83
|
it do
|
|
78
|
-
song = OpenStruct.new(
|
|
79
|
-
|
|
84
|
+
song = OpenStruct.new(
|
|
85
|
+
airplays: [
|
|
86
|
+
OpenStruct.new(station: "JJJ"), OpenStruct.new(station: "Radio Dreyeckland"),
|
|
87
|
+
OpenStruct.new(station: "ABC")
|
|
88
|
+
]
|
|
89
|
+
).extend(representer)
|
|
90
|
+
_(song.to_hash(user_options: {skip?: true})).must_equal({"airplays"=>[{"station"=>"JJJ"}, {"station"=>"ABC"}]})
|
|
80
91
|
end
|
|
81
|
-
end
|
|
92
|
+
end
|
data/test/stringify_hash_test.rb
CHANGED
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class StringifyHashTest <
|
|
4
|
-
describe "#from_hash" do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
include Representable::Hash::AllowSymbols
|
|
9
|
-
|
|
10
|
-
property :title
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
representer!(:inject => :song_representer) do
|
|
14
|
-
include Representable::Hash::AllowSymbols
|
|
15
|
-
|
|
16
|
-
property :song, :extend => song_representer, :class => OpenStruct
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "parses symbols, too" do
|
|
20
|
-
_(OpenStruct.new.extend(representer).from_hash({:song => {:title => "Der Optimist"}}).song.title).must_equal "Der Optimist"
|
|
21
|
-
end
|
|
3
|
+
class StringifyHashTest < Minitest::Spec
|
|
4
|
+
describe "#from_hash" do
|
|
5
|
+
representer!(:name => :song_representer) do
|
|
6
|
+
include Representable::Hash
|
|
7
|
+
include Representable::Hash::AllowSymbols
|
|
22
8
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
9
|
+
property :title
|
|
10
|
+
end
|
|
26
11
|
|
|
27
|
-
describe "with :wrap" do
|
|
28
12
|
representer!(:inject => :song_representer) do
|
|
29
13
|
include Representable::Hash::AllowSymbols
|
|
30
14
|
|
|
31
|
-
self.representation_wrap = :album
|
|
32
15
|
property :song, :extend => song_representer, :class => OpenStruct
|
|
33
16
|
end
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
18
|
+
it "parses symbols, too" do
|
|
19
|
+
_(OpenStruct.new.extend(representer).from_hash({:song => {:title => "Der Optimist"}}).song.title).must_equal "Der Optimist"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "still parses strings" do
|
|
23
|
+
_(OpenStruct.new.extend(representer).from_hash({"song" => {"title" => "Der Optimist"}}).song.title).must_equal "Der Optimist"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "with :wrap" do
|
|
27
|
+
representer!(:inject => :song_representer) do
|
|
28
|
+
include Representable::Hash::AllowSymbols
|
|
40
29
|
|
|
41
|
-
|
|
30
|
+
self.representation_wrap = :album
|
|
31
|
+
property :song, :extend => song_representer, :class => OpenStruct
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "parses symbols, too" do
|
|
35
|
+
_(OpenStruct.new.extend(representer).from_hash({:album => {:song => {:title => "Der Optimist"}}}).song.title).must_equal "Der Optimist"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/test/test_helper.rb
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "ostruct"
|
|
2
|
+
require "representable"
|
|
3
3
|
|
|
4
|
-
require
|
|
5
|
-
require 'test_xml/mini_test'
|
|
4
|
+
require "minitest/autorun"
|
|
6
5
|
|
|
7
6
|
require "representable/debug"
|
|
8
|
-
require
|
|
7
|
+
require "minitest/assertions"
|
|
8
|
+
require "xml_assertions"
|
|
9
9
|
|
|
10
|
-
module
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
module Minitest::Assertions
|
|
11
|
+
include XmlAssertions
|
|
12
|
+
|
|
13
|
+
# Arguments read (asserted, expected), the assertion order used throughout this suite.
|
|
14
|
+
def assert_equal_xml(subject, text)
|
|
15
|
+
assert_equal text.gsub("\n", "").gsub(/(\s\s+)/, ""), subject.gsub("\n", "").gsub(/(\s\s+)/, "")
|
|
13
16
|
end
|
|
14
17
|
end
|
|
15
|
-
String.infect_an_assertion :assert_equal_xml, :must_xml
|
|
16
18
|
|
|
17
19
|
# TODO: delete all that in 4.0:
|
|
18
20
|
class Album
|
|
19
21
|
attr_accessor :songs, :best_song
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
def initialize(songs = nil, best_song = nil)
|
|
21
24
|
@songs = songs
|
|
22
25
|
@best_song = best_song
|
|
23
26
|
end
|
|
@@ -29,7 +32,8 @@ end
|
|
|
29
32
|
|
|
30
33
|
class Song
|
|
31
34
|
attr_accessor :name, :track # never change this, track rendered with Rails#to_json.
|
|
32
|
-
|
|
35
|
+
|
|
36
|
+
def initialize(name = nil, track = nil)
|
|
33
37
|
@name = name
|
|
34
38
|
@track = track
|
|
35
39
|
end
|
|
@@ -47,14 +51,14 @@ end
|
|
|
47
51
|
|
|
48
52
|
module AssertJson
|
|
49
53
|
module Assertions
|
|
50
|
-
def assert_json(expected, actual, msg=nil)
|
|
54
|
+
def assert_json(expected, actual, msg = nil)
|
|
51
55
|
msg = message(msg, "") { diff expected, actual }
|
|
52
56
|
assert_equal(expected.split("").sort, actual.split("").sort, msg)
|
|
53
57
|
end
|
|
54
58
|
end
|
|
55
59
|
end
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
Minitest::Spec.class_eval do
|
|
58
62
|
include AssertJson::Assertions
|
|
59
63
|
include XmlHelper
|
|
60
64
|
|
|
@@ -66,7 +70,7 @@ MiniTest::Spec.class_eval do
|
|
|
66
70
|
end
|
|
67
71
|
|
|
68
72
|
def render(object, *args)
|
|
69
|
-
AssertableDocument.new(object.send("to_#{format}", *args), format)
|
|
73
|
+
AssertableDocument.new(object.send("to_#{format}", *args), format, self)
|
|
70
74
|
end
|
|
71
75
|
|
|
72
76
|
def parse(object, input, *args)
|
|
@@ -76,17 +80,20 @@ MiniTest::Spec.class_eval do
|
|
|
76
80
|
class AssertableDocument
|
|
77
81
|
attr_reader :document
|
|
78
82
|
|
|
79
|
-
def initialize(document, format)
|
|
80
|
-
@document
|
|
83
|
+
def initialize(document, format, test)
|
|
84
|
+
@document = document
|
|
85
|
+
@format = format
|
|
86
|
+
@test = test
|
|
81
87
|
end
|
|
82
88
|
|
|
83
|
-
def must_equal_document(*
|
|
84
|
-
return
|
|
85
|
-
|
|
89
|
+
def must_equal_document(expected, *rest)
|
|
90
|
+
return @test.assert_xml_equal(document, expected, *rest) if @format == :xml
|
|
91
|
+
|
|
92
|
+
@test.assert_equal(expected, document, *rest)
|
|
86
93
|
end
|
|
87
94
|
end
|
|
88
95
|
|
|
89
|
-
def self.representer!(options={}, &block)
|
|
96
|
+
def self.representer!(options = {}, &block)
|
|
90
97
|
fmt = options # we need that so the 2nd call to ::let(within a ::describe) remembers the right format.
|
|
91
98
|
|
|
92
99
|
name = options[:name] || :representer
|
|
@@ -119,7 +126,7 @@ MiniTest::Spec.class_eval do
|
|
|
119
126
|
end
|
|
120
127
|
|
|
121
128
|
module TestMethods
|
|
122
|
-
def representer_for(modules=[Representable::Hash], &block)
|
|
129
|
+
def representer_for(modules = [Representable::Hash], &block)
|
|
123
130
|
Module.new do
|
|
124
131
|
extend TestMethods
|
|
125
132
|
include(*modules)
|
|
@@ -127,17 +134,16 @@ MiniTest::Spec.class_eval do
|
|
|
127
134
|
end
|
|
128
135
|
end
|
|
129
136
|
|
|
130
|
-
|
|
137
|
+
alias representer! representer_for
|
|
131
138
|
end
|
|
132
139
|
include TestMethods
|
|
133
140
|
end
|
|
134
141
|
|
|
135
|
-
class BaseTest <
|
|
142
|
+
class BaseTest < Minitest::Spec
|
|
136
143
|
let(:new_album) { OpenStruct.new.extend(representer) }
|
|
137
144
|
let(:album) { OpenStruct.new(:songs => ["Fuck Armageddon"]).extend(representer) }
|
|
138
145
|
let(:song) { OpenStruct.new(:title => "Resist Stance") }
|
|
139
|
-
let(:song_representer) { Module.new
|
|
140
|
-
|
|
146
|
+
let(:song_representer) { Module.new { include Representable::Hash; property :title } }
|
|
141
147
|
end
|
|
142
148
|
|
|
143
149
|
Band = Struct.new(:id, :name) do
|
data/test/test_helper_test.rb
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class TestHelperTest <
|
|
3
|
+
class TestHelperTest < Minitest::Spec
|
|
4
4
|
describe "#assert_json" do
|
|
5
5
|
it "tests for equality" do
|
|
6
6
|
assert_json "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}", "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}"
|
|
7
7
|
end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
it "allows different key orders" do
|
|
10
10
|
assert_json "{\"songs\":{\"one\":\"65\",\"two\":\"Emo Boy\"}}", "{\"songs\":{\"two\":\"Emo Boy\",\"one\":\"65\"}}"
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
it "complains when expected hash is subset" do
|
|
14
|
-
assert_raises
|
|
14
|
+
assert_raises Minitest::Assertion do
|
|
15
15
|
assert_json "{\"songs\":{\"one\":\"65\"}}", "{\"songs\":{\"two\":\"Emo Boy\",\"one\":\"65\"}}"
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
it "complains when source hash is subset" do
|
|
20
|
-
assert_raises
|
|
20
|
+
assert_raises Minitest::Assertion do
|
|
21
21
|
assert_json "{\"songs\":{\"two\":\"Emo Boy\",\"one\":\"65\"}}", "{\"songs\":{\"one\":\"65\"}}"
|
|
22
22
|
end
|
|
23
23
|
end
|
data/test/uncategorized_test.rb
CHANGED
|
@@ -1,36 +1,42 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class StopWhenIncomingObjectFragmentIsNilTest <
|
|
3
|
+
class StopWhenIncomingObjectFragmentIsNilTest < Minitest::Spec
|
|
4
4
|
Album = Struct.new(:id, :songs)
|
|
5
5
|
Song = Struct.new(:title)
|
|
6
6
|
|
|
7
7
|
representer!(decorator: true) do
|
|
8
8
|
property :id
|
|
9
|
-
collection :songs, class: Song, parse_pipeline: ->(
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
collection :songs, class: Song, parse_pipeline: ->(_input, _options) { # TODO: test if :doc is set for parsing. test if options are ok and contain :user_options!
|
|
10
|
+
Representable::Pipeline[*parse_functions.insert(3, Representable::StopOnNil)]
|
|
11
|
+
} do
|
|
12
12
|
property :title
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
it do
|
|
17
17
|
album = Album.new
|
|
18
|
-
_(
|
|
18
|
+
_(
|
|
19
|
+
representer.new(album).from_hash(
|
|
20
|
+
{
|
|
21
|
+
"id" => 1,
|
|
22
|
+
"songs" => [{"title"=>"Walkie Talkie"}]
|
|
23
|
+
}
|
|
24
|
+
).songs
|
|
25
|
+
).must_equal [Song.new("Walkie Talkie")]
|
|
19
26
|
end
|
|
20
27
|
|
|
21
28
|
it do
|
|
22
29
|
album = Album.new(2, ["original"])
|
|
23
|
-
_(representer.new(album).from_hash({"id"=>1, "songs"=>nil}).songs).must_equal ["original"]
|
|
30
|
+
_(representer.new(album).from_hash({"id" => 1, "songs" => nil}).songs).must_equal ["original"]
|
|
24
31
|
end
|
|
25
|
-
|
|
26
32
|
end
|
|
27
33
|
|
|
28
|
-
class RenderPipelineOptionTest <
|
|
34
|
+
class RenderPipelineOptionTest < Minitest::Spec
|
|
29
35
|
Album = Struct.new(:id, :songs)
|
|
30
|
-
NilToNA = ->(input,
|
|
36
|
+
NilToNA = ->(input, _options) { input.nil? ? "n/a" : input }
|
|
31
37
|
|
|
32
38
|
representer!(decorator: true) do
|
|
33
|
-
property :id, render_pipeline: ->(
|
|
39
|
+
property :id, render_pipeline: ->(_input, options) do
|
|
34
40
|
Representable::Pipeline[*render_functions.insert(2, options[:options][:user_options][:function])]
|
|
35
41
|
end
|
|
36
42
|
end
|
|
@@ -43,15 +49,14 @@ class RenderPipelineOptionTest < MiniTest::Spec
|
|
|
43
49
|
_(decorator.to_hash(user_options: {function: NilToNA})).must_equal({"id"=>"n/a"})
|
|
44
50
|
_(decorator.to_hash(user_options: {function: nil})).must_equal({"id"=>"n/a"}) # non-sense function is not applied.
|
|
45
51
|
end
|
|
46
|
-
|
|
47
52
|
end
|
|
48
53
|
|
|
49
|
-
class ParsePipelineOptionTest <
|
|
54
|
+
class ParsePipelineOptionTest < Minitest::Spec
|
|
50
55
|
Album = Struct.new(:id, :songs)
|
|
51
|
-
NilToNA = ->(input,
|
|
56
|
+
NilToNA = ->(input, _options) { input.nil? ? "n/a" : input }
|
|
52
57
|
|
|
53
58
|
representer!(decorator: true) do
|
|
54
|
-
property :id, parse_pipeline: ->(
|
|
59
|
+
property :id, parse_pipeline: ->(_input, options) do
|
|
55
60
|
Representable::Pipeline[*parse_functions.insert(3, options[:options][:user_options][:function])].extend(Representable::Pipeline::Debug)
|
|
56
61
|
end
|
|
57
62
|
end
|
|
@@ -64,4 +69,4 @@ class ParsePipelineOptionTest < MiniTest::Spec
|
|
|
64
69
|
_(decorator.from_hash({"id"=>nil}, user_options: {function: NilToNA}).id).must_equal "n/a"
|
|
65
70
|
_(decorator.from_hash({"id"=>nil}, user_options: {function: "nonsense"}).id).must_equal "n/a" # non-sense function is not applied.
|
|
66
71
|
end
|
|
67
|
-
end
|
|
72
|
+
end
|
data/test/user_options_test.rb
CHANGED
|
@@ -8,8 +8,17 @@ class UserOptionsTest < Minitest::Spec
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
it { _(Song.new("Run With It").extend(representer).to_hash).must_equal({}) }
|
|
11
|
-
it {
|
|
11
|
+
it {
|
|
12
|
+
_(Song.new("Run With It").extend(representer).to_hash(user_options: {visible: true})).must_equal({"title"=>"Run With It"})
|
|
13
|
+
}
|
|
12
14
|
it { _(Song.new("Run With It").extend(representer).from_hash("title"=>"Business Conduct").title).must_equal "Run With It" }
|
|
13
|
-
it {
|
|
15
|
+
it {
|
|
16
|
+
_(
|
|
17
|
+
Song.new("Run With It").extend(representer).from_hash(
|
|
18
|
+
{"title"=>"Business Conduct"},
|
|
19
|
+
user_options: {visible: true}
|
|
20
|
+
).title
|
|
21
|
+
).must_equal "Business Conduct"
|
|
22
|
+
}
|
|
14
23
|
end
|
|
15
|
-
# Representable.deprecations=false
|
|
24
|
+
# Representable.deprecations=false
|
data/test/wrap_test.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class WrapTest <
|
|
3
|
+
class WrapTest < Minitest::Spec
|
|
4
4
|
class HardcoreBand
|
|
5
5
|
include Representable::Hash
|
|
6
6
|
end
|
|
@@ -27,16 +27,15 @@ class WrapTest < MiniTest::Spec
|
|
|
27
27
|
for_formats(
|
|
28
28
|
:hash => [Representable::Hash, {"Blink182"=>{"genre"=>"Pop"}}, {"Blink182"=>{"genre"=>"Poppunk"}}],
|
|
29
29
|
:json => [Representable::JSON, "{\"Blink182\":{\"genre\":\"Pop\"}}", "{\"Blink182\":{\"genre\":\"Poppunk\"}}"],
|
|
30
|
-
:xml => [Representable::XML, "<Blink182><genre>Pop</genre></Blink182>", "<Blink182><genre>Poppunk</genre></Blink182>"]
|
|
30
|
+
:xml => [Representable::XML, "<Blink182><genre>Pop</genre></Blink182>", "<Blink182><genre>Poppunk</genre></Blink182>"]
|
|
31
31
|
# :yaml => [Representable::YAML, "---\nBlink182:\n"], # TODO: fix YAML.
|
|
32
32
|
) do |format, mod, output, input|
|
|
33
|
-
|
|
34
33
|
describe "[#{format}] dynamic wrap" do
|
|
35
34
|
let(:band) { representer.prepare(Struct.new(:name, :genre).new("Blink", "Pop")) }
|
|
36
35
|
let(:format) { format }
|
|
37
36
|
|
|
38
37
|
representer!(:module => mod) do
|
|
39
|
-
self.representation_wrap =
|
|
38
|
+
self.representation_wrap = ->(number:, **) { "#{name}#{number}" }
|
|
40
39
|
property :genre
|
|
41
40
|
end
|
|
42
41
|
|
|
@@ -47,8 +46,7 @@ class WrapTest < MiniTest::Spec
|
|
|
47
46
|
end
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
class HashDisableWrapTest < MiniTest::Spec
|
|
49
|
+
class HashDisableWrapTest < Minitest::Spec
|
|
52
50
|
Band = Struct.new(:name, :label)
|
|
53
51
|
Album = Struct.new(:band)
|
|
54
52
|
Label = Struct.new(:name)
|
|
@@ -80,7 +78,6 @@ class HashDisableWrapTest < MiniTest::Spec
|
|
|
80
78
|
_(band.from_hash({band: {"name"=>"Social Distortion"}}, wrap: :band).name).must_equal "Social Distortion"
|
|
81
79
|
end
|
|
82
80
|
|
|
83
|
-
|
|
84
81
|
class AlbumDecorator < Representable::Decorator
|
|
85
82
|
include Representable::Hash
|
|
86
83
|
|
|
@@ -89,12 +86,20 @@ class HashDisableWrapTest < MiniTest::Spec
|
|
|
89
86
|
property :band, decorator: BandDecorator, wrap: false, class: Band
|
|
90
87
|
end
|
|
91
88
|
|
|
92
|
-
|
|
93
89
|
let(:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
|
|
94
90
|
|
|
95
91
|
# band has wrap turned off per property definition, however, label still has wrap.
|
|
96
92
|
it "renders" do
|
|
97
|
-
_(album.to_hash).must_equal(
|
|
93
|
+
_(album.to_hash).must_equal(
|
|
94
|
+
{
|
|
95
|
+
"albums" => {
|
|
96
|
+
"band" => {
|
|
97
|
+
"name" => "Social Distortion",
|
|
98
|
+
"label" => {"important"=>{"name"=>"Epitaph"}}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
)
|
|
98
103
|
end
|
|
99
104
|
|
|
100
105
|
it "parses" do
|
|
@@ -102,8 +107,7 @@ class HashDisableWrapTest < MiniTest::Spec
|
|
|
102
107
|
end
|
|
103
108
|
end
|
|
104
109
|
|
|
105
|
-
|
|
106
|
-
class XMLDisableWrapTest < MiniTest::Spec
|
|
110
|
+
class XMLDisableWrapTest < Minitest::Spec
|
|
107
111
|
Band = Struct.new(:name, :label)
|
|
108
112
|
Album = Struct.new(:band)
|
|
109
113
|
Label = Struct.new(:name)
|
|
@@ -123,11 +127,10 @@ class XMLDisableWrapTest < MiniTest::Spec
|
|
|
123
127
|
let(:band) { BandDecorator.prepare(Band.new("Social Distortion")) }
|
|
124
128
|
|
|
125
129
|
it do
|
|
126
|
-
band.to_xml
|
|
127
|
-
band.to_xml(wrap: "combo")
|
|
130
|
+
assert_xml_equal(band.to_xml, "<bands><name>Social Distortion</name></bands>")
|
|
131
|
+
assert_xml_equal(band.to_xml(wrap: "combo"), "<combo><name>Social Distortion</name></combo>")
|
|
128
132
|
end
|
|
129
133
|
|
|
130
|
-
|
|
131
134
|
class AlbumDecorator < Representable::Decorator
|
|
132
135
|
include Representable::XML
|
|
133
136
|
|
|
@@ -136,17 +139,24 @@ class XMLDisableWrapTest < MiniTest::Spec
|
|
|
136
139
|
property :band, decorator: BandDecorator, wrap: "po", class: Band
|
|
137
140
|
end
|
|
138
141
|
|
|
139
|
-
|
|
140
142
|
let(:album) { AlbumDecorator.prepare(Album.new(Band.new("Social Distortion", Label.new("Epitaph")))) }
|
|
141
143
|
|
|
142
144
|
# band has wrap turned of per property definition, however, label still has wrap.
|
|
143
145
|
it "rendersxx" do
|
|
144
146
|
skip
|
|
145
|
-
_(album.to_xml).must_equal(
|
|
147
|
+
_(album.to_xml).must_equal(
|
|
148
|
+
{
|
|
149
|
+
"albums" => {
|
|
150
|
+
"band" => {
|
|
151
|
+
"name" => "Social Distortion",
|
|
152
|
+
"label" => {"important"=>{"name"=>"Epitaph"}}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
)
|
|
146
157
|
end
|
|
147
158
|
|
|
148
159
|
# it "parses" do
|
|
149
160
|
# album.from_hash({"albums" => {"band" => {"name"=>"Rvivr"}}}).band.name.must_equal "Rvivr"
|
|
150
161
|
# end
|
|
151
162
|
end
|
|
152
|
-
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "nokogiri"
|
|
2
|
+
|
|
3
|
+
# Order-insensitive XML comparison: attributes are sorted by name and sibling
|
|
4
|
+
# elements by their own canonical form, then both documents are compared as
|
|
5
|
+
# strings.
|
|
6
|
+
module XmlAssertions
|
|
7
|
+
def self.canonicalize(xml, indent = 0)
|
|
8
|
+
node = xml.is_a?(String) ? Nokogiri::XML.parse(xml).root : xml
|
|
9
|
+
return "(unparsable xml)\n#{xml}" if node.nil?
|
|
10
|
+
|
|
11
|
+
pad = " " * indent
|
|
12
|
+
attrs = node.attribute_nodes.map { |a| " #{a.name}=#{a.value.inspect}" }.sort.join
|
|
13
|
+
children = node.elements
|
|
14
|
+
|
|
15
|
+
if children.empty?
|
|
16
|
+
"#{pad}<#{node.name}#{attrs}>#{node.text.strip}</#{node.name}>\n"
|
|
17
|
+
else
|
|
18
|
+
body = children.map { |child| canonicalize(child, indent + 1) }.sort.join
|
|
19
|
+
"#{pad}<#{node.name}#{attrs}>\n#{body}#{pad}</#{node.name}>\n"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Arguments read (asserted, expected), the assertion order used throughout this suite.
|
|
24
|
+
def assert_xml_equal(actual, expected, msg = nil)
|
|
25
|
+
assert_equal XmlAssertions.canonicalize(expected),
|
|
26
|
+
XmlAssertions.canonicalize(actual),
|
|
27
|
+
msg
|
|
28
|
+
end
|
|
29
|
+
end
|
data/test/xml_bindings_test.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "representable/xml/hash"
|
|
3
3
|
|
|
4
|
-
class XMLBindingTest <
|
|
4
|
+
class XMLBindingTest < Minitest::Spec
|
|
5
5
|
module SongRepresenter
|
|
6
6
|
include Representable::XML
|
|
7
7
|
property :name
|
|
@@ -19,9 +19,6 @@ class XMLBindingTest < MiniTest::Spec
|
|
|
19
19
|
@song = SongWithRepresenter.new("Thinning the Herd")
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
22
|
describe "AttributeBinding" do
|
|
26
23
|
describe "with plain text items" do
|
|
27
24
|
before do
|
|
@@ -35,7 +32,7 @@ class XMLBindingTest < MiniTest::Spec
|
|
|
35
32
|
it "inserts with #write" do
|
|
36
33
|
parent = Nokogiri::XML::Node.new("song", @doc)
|
|
37
34
|
@property.write(parent, "The Gargoyle", "name")
|
|
38
|
-
assert_xml_equal("<song name=\"The Gargoyle\" />"
|
|
35
|
+
assert_xml_equal(parent.to_s, "<song name=\"The Gargoyle\" />")
|
|
39
36
|
end
|
|
40
37
|
end
|
|
41
38
|
end
|
|
@@ -52,7 +49,7 @@ class XMLBindingTest < MiniTest::Spec
|
|
|
52
49
|
it "inserts with #write" do
|
|
53
50
|
parent = Nokogiri::XML::Node.new("song", @doc)
|
|
54
51
|
@property.write(parent, "The Gargoyle", "song")
|
|
55
|
-
assert_xml_equal("<song>The Gargoyle</song>"
|
|
52
|
+
assert_xml_equal(parent.to_s, "<song>The Gargoyle</song>")
|
|
56
53
|
end
|
|
57
54
|
end
|
|
58
55
|
end
|