1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
require "test_helper"
class Profiler
def self.profile(&block)
case RUBY_ENGINE
when "ruby"
require 'ruby-prof'
output = StringIO.new
profile_result = RubyProf.profile(&block)
printer = RubyProf::FlatPrinter.new(profile_result)
printer.print(output)
output.string
when "jruby"
require 'jruby/profiler'
output_stream = java.io.ByteArrayOutputStream.new
print_stream = java.io.PrintStream.new(output_stream)
profile_result = JRuby::Profiler.profile(&block)
printer = JRuby::Profiler::FlatProfilePrinter.new(profile_result)
printer.printProfile(print_stream)
output_stream.toString
end
end
end
class CachedTest < Minitest::Spec
# TODO: also test with feature(Cached)
module Model
Song = Struct.new(:title, :composer)
Album = Struct.new(:name, :songs, :artist)
Artist = Struct.new(:name, :hidden_taste)
end
class SongRepresenter < Representable::Decorator
include Representable::Hash
feature Representable::Cached
property :title, render_filter: lambda { |input, options| "#{input}:#{options[:options][:user_options]}" }
property :composer, class: Model::Artist do
property :name
end
end
class AlbumRepresenter < Representable::Decorator
include Representable::Hash
include Representable::Cached
property :name
collection :songs, decorator: SongRepresenter, class: Model::Song
end
describe "serialization" do
let(:album_hash) { {"name"=>"Louder And Even More Dangerous", "songs"=>[{"title"=>"Southbound:{:volume=>10}"}, {"title"=>"Jailbreak:{:volume=>10}"}]} }
let(:song) { Model::Song.new("Jailbreak") }
let(:song2) { Model::Song.new("Southbound") }
let(:album) { Model::Album.new("Live And Dangerous", [song, song2, Model::Song.new("Emerald")]) }
let(:representer) { AlbumRepresenter.new(album) }
it do
# album2 = Model::Album.new("Louder And Even More Dangerous", [song2, song])
# makes sure options are passed correctly.
representer.to_hash(user_options: {volume: 9}).must_equal({"name"=>"Live And Dangerous",
"songs"=>[{"title"=>"Jailbreak:{:volume=>9}"}, {"title"=>"Southbound:{:volume=>9}"}, {"title"=>"Emerald:{:volume=>9}"}]}) # called in Deserializer/Serializer
# representer becomes reusable as it is stateless.
# representer.update!(album2)
# makes sure options are passed correctly.
# representer.to_hash(volume:10).must_equal(album_hash)
end
# profiling
it do
representer.to_hash
data = Profiler.profile { representer.to_hash }
# 3 songs get decorated.
data.must_match(/3\s*Representable::Function::Decorate#call/m)
# These weird Regexp bellow are a quick workaround to accomodate
# the different profiler result formats.
# - "3 <Class::Representable::Decorator>#prepare" -> At MRI Ruby
# - "3 Representable::Decorator.prepare" -> At JRuby
# 3 nested decorator is instantiated for 3 Songs, though.
data.must_match(/3\s*(<Class::)?Representable::Decorator\>?[\#.]prepare/m)
# no Binding is instantiated at runtime.
data.wont_match "Representable::Binding#initialize"
# 2 mappers for Album, Song
# data.must_match "2 Representable::Mapper::Methods#initialize"
# title, songs, 3x title, composer
data.must_match(/8\s*Representable::Binding[#\.]render_pipeline/m)
data.wont_match "render_functions"
data.wont_match "Representable::Binding::Factories#render_functions"
end
end
describe "deserialization" do
let(:album_hash) {
{
"name"=>"Louder And Even More Dangerous",
"songs"=>[
{"title"=>"Southbound", "composer"=>{"name"=>"Lynott"}},
{"title"=>"Jailbreak", "composer"=>{"name"=>"Phil Lynott"}},
{"title"=>"Emerald"}
]
}
}
it do
album = Model::Album.new
AlbumRepresenter.new(album).from_hash(album_hash)
album.songs.size.must_equal 3
album.name.must_equal "Louder And Even More Dangerous"
album.songs[0].title.must_equal "Southbound"
album.songs[0].composer.name.must_equal "Lynott"
album.songs[1].title.must_equal "Jailbreak"
album.songs[1].composer.name.must_equal "Phil Lynott"
album.songs[2].title.must_equal "Emerald"
album.songs[2].composer.must_be_nil
# TODO: test options.
end
it "xxx" do
representer = AlbumRepresenter.new(Model::Album.new)
representer.from_hash(album_hash)
data = Profiler.profile { representer.from_hash(album_hash) }
# only 2 nested decorators are instantiated, Song, and Artist.
# Didn't like the regexp?
# MRI and JRuby has different output formats. See note above.
data.must_match(/5\s*(<Class::)?Representable::Decorator>?[#\.]prepare/)
# a total of 5 properties in the object graph.
data.wont_match "Representable::Binding#initialize"
data.wont_match "parse_functions" # no pipeline creation.
data.must_match(/10\s*Representable::Binding[#\.]parse_pipeline/)
# three mappers for Album, Song, composer
# data.must_match "3 Representable::Mapper::Methods#initialize"
# # 6 deserializers as the songs collection uses 2.
# data.must_match "6 Representable::Deserializer#initialize"
# # one populater for every property.
# data.must_match "5 Representable::Populator#initialize"
# printer.print(STDOUT)
end
end
end
|