require 'test_helper' class GenericTest < MiniTest::Spec # one day, this file will contain all engine-independent test cases. one day... let (:new_album) { OpenStruct.new.extend(representer) } let (:album) { OpenStruct.new(:songs => ["Fuck Armageddon"]).extend(representer) } let (:song) { OpenStruct.new(:title => "Resist Stance") } let (:song_representer) { Module.new do include Representable::Hash; property :title end } describe "::collection" do representer! do collection :songs end it "initializes property with empty array" do new_album.from_hash({}) new_album.songs.must_equal [] # DISCUSS: do we really want this? end it "leaves properties untouched" do album.from_hash({}) # TODO: test property. album.songs.must_equal ["Fuck Armageddon"] # DISCUSS: do we really want this? end end describe "property with instance: { nil }" do # TODO: introduce :representable option? representer!(:inject => :song_representer) do property :song, :instance => lambda { |*| nil }, :extend => song_representer end let (:hit) { hit = OpenStruct.new(:song => song).extend(representer) } it "calls #to_hash on song instance, nothing else" do hit.to_hash.must_equal("song"=>{"title"=>"Resist Stance"}) end it "calls #from_hash on the existing song instance, nothing else" do song_id = hit.song.object_id hit.from_hash("song"=>{"title"=>"Suffer"}) hit.song.title.must_equal "Suffer" hit.song.object_id.must_equal song_id end end def self.for_formats(formats) formats.each do |format, cfg| mod, output, input = cfg yield format, mod, output, input end end for_formats( :hash => [Representable::Hash, {"song"=>{"title"=>"Resist Stance"}}, {"song"=>{"title"=>"Suffer"}}], :xml => [Representable::XML, "Resist Stance", "Suffer",], :yaml => [Representable::YAML, "---\nsong:\n title: Resist Stance\n", "---\nsong:\n title: Suffer\n"], ) do |format, mod, output, input| describe "[#{format}] property with parse_strategy: :sync" do # TODO: introduce :representable option? let (:format) { format } representer!(:module => mod, :name => :song_representer) do property :title self.representation_wrap = :song if format == :xml end representer!(:inject => :song_representer, :module => mod) do property :song, :parse_strategy => :sync, :extend => song_representer end let (:hit) { hit = OpenStruct.new(:song => song).extend(representer) } it "calls #to_hash on song instance, nothing else" do render(hit).must_equal_document(output) end it "calls #from_hash on the existing song instance, nothing else" do song_id = hit.song.object_id parse(hit, input) hit.song.title.must_equal "Suffer" hit.song.object_id.must_equal song_id end end end # FIXME: there's a bug with XML and the collection name! for_formats( :hash => [Representable::Hash, {"songs"=>[{"title"=>"Resist Stance"}]}, {"songs"=>[{"title"=>"Suffer"}]}], #:json => [Representable::JSON, "{\"song\":{\"name\":\"Alive\"}}", "{\"song\":{\"name\":\"You've Taken Everything\"}}"], :xml => [Representable::XML, "Resist Stance", "Suffer"], :yaml => [Representable::YAML, "---\nsongs:\n- title: Resist Stance\n", "---\nsongs:\n- title: Suffer\n"], ) do |format, mod, output, input| describe "[#{format}] collection with :parse_strategy: :sync" do # TODO: introduce :representable option? let (:format) { format } representer!(:module => mod, :name => :song_representer) do property :title self.representation_wrap = :song if format == :xml end representer!(:inject => :song_representer, :module => mod) do collection :songs, :parse_strategy => :sync, :extend => song_representer end let (:album) { OpenStruct.new(:songs => [song]).extend(representer) } it "calls #to_hash on song instances, nothing else" do render(album).must_equal_document(output) end it "calls #from_hash on the existing song instance, nothing else" do collection_id = album.songs.object_id song = album.songs.first song_id = song.object_id parse(album, input) album.songs.first.title.must_equal "Suffer" song.title.must_equal "Suffer" #album.songs.object_id.must_equal collection_id # TODO: don't replace! song.object_id.must_equal song_id end end end def render(object) AssertableDocument.new(object.send("to_#{format}"), format) end def parse(object, input) object.send("from_#{format}", input) end class AssertableDocument attr_reader :document def initialize(document, format) @document, @format = document, format end def must_equal_document(*args) return document.must_equal_xml(*args) if @format == :xml document.must_equal(*args) end end # Lonely Collection require "representable/hash/collection" for_formats( :hash => [Representable::Hash::Collection, [{"title"=>"Resist Stance"}], [{"title"=>"Suffer"}]], # :xml => [Representable::XML, "Resist Stance", "Suffer"], ) do |format, mod, output, input| describe "[#{format}] lonely collection with :parse_strategy: :sync" do # TODO: introduce :representable option? let (:format) { format } representer!(:module => Representable::Hash, :name => :song_representer) do property :title self.representation_wrap = :song if format == :xml end representer!(:inject => :song_representer, :module => mod) do items :parse_strategy => :sync, :extend => song_representer end let (:album) { [song].extend(representer) } it "calls #to_hash on song instances, nothing else" do render(album).must_equal_document(output) end it "calls #from_hash on the existing song instance, nothing else" do #collection_id = album.object_id song = album.first song_id = song.object_id parse(album, input) album.first.title.must_equal "Suffer" song.title.must_equal "Suffer" song.object_id.must_equal song_id end end end describe "mix :extend and inline representers" do representer! do rpr_module = Module.new do include Representable::Hash property :title end property :song, :extend => rpr_module do property :artist end end it do OpenStruct.new(:song => OpenStruct.new(:title => "The Fever And The Sound", :artist => "Strung Out")).extend(representer). to_hash. must_equal({"song"=>{"artist"=>"Strung Out", "title"=>"The Fever And The Sound"}}) end end end